Bot

開発記録#162(2025/4/1)「論文ベースのbot開発フローpart.24 最終パフォーマンスチェック― バックテストと実運用を⼀括検証 ―」

2025年4月1日

初回公開:2025‑04‑01 / 最終更新:2025‑04‑24

前回の記事に引き続き、今回も仮想通貨botの開発状況をまとめていきます。

本記事では「暗号通貨のパンプ&ダンプスキームの検出」に関する論文をベースにbot開発の過程をまとめていきます。

1. 運用ルールの確立

  • Botの稼働スケジュール設計
  • 障害発生時の対応フロー策定
  • 異常検出やリスク回避の優先順位決定

2. 最終パフォーマンスチェック←今ここ

  • シミュレーション結果と実運用結果の比較
  • 取引成功率、利益率、エラー率の再評価
  • 調整が必要なパラメータの特定

3. デプロイ後の安定性確認

  • Podの状態、リソース使用率の監視
  • APIエラーやデータ欠損の確認
  • Slack通知やログ記録の精度確認

1.運用ルールの確立 (Botの稼働スケジュールや障害対応フローの策定)
2.最終パフォーマンスチェック (シミュレーションと実運用の比較検証)
3.デプロイ後の安定性確認 (サーバー監視とログの確認)

Yodaka

次のステップとして、2.最終パフォーマンスチェック(シミュレーションと実運用の比較検証)に進みます。

目的

  1. シミュレーション結果と実運用結果を同じ指標で比較し、再現性を確認
  2. ズレの要因を特定して パラメータ/ロジックを最終調整
  3. 本番リリース前に “数値で安心” を取る

0. イントロ

前回(part.23)では 「暗号通貨のパンプ&ダンプスキーム検出」 の論文に基づき、検知ロジックと運用ルールを実装しました。今回の part.24 では、その 検知 Bot を例に「バックテスト vs. 実運用」のパフォーマンスを数値で突き合わせ、最終調整へ進む手順を解説します。


1. ステップ概要

準備        →  比較        →  調整          →  次フェーズ
CSV 生成       指標の差確認     パラメータ修正     監視&デプロイ

2. データ準備

2‑1 ファイル構成

results/
├── backtest_result.csv      # バックテスト取引ログ
├── live_trade_log.csv       # 実運用ログ
└── performance_comparison.py# 比較スクリプト(§3)

2‑2 CSV 列仕様

timestampsymbolsidesizepricepnlbalance

2‑3 backtester_export.py(全文:バックテスト結果を保存)

#!/usr/bin/env python3
"""
backtester_export.py
────────────────────────────────────────────────────────
バックテストで得た取引ログ (list[dict] など) を
CSV ファイル `./results/backtest_result.csv` として保存するユーティリティ

❏ 使い方
$ python backtester_export.py
  └─ ./results/backtest_result.csv が生成される
"""
from pathlib import Path
from datetime import datetime, timedelta
import pandas as pd
import random

# ① ダミーログを用意   ※実際はここをバックテストの生ログに置き換える
trade_log = []
base_time = datetime(2025, 3, 30, 10, 0, 0)
balance   = 10_000   # スタート残高 USDT

for i in range(50):
    ts    = base_time + timedelta(minutes=i * 30)
    side  = 'buy' if i % 2 == 0 else 'sell'
    price = 70_000 + random.uniform(-300, 300)
    size  = 0.01
    pnl   = random.uniform(-30, 50)
    balance += pnl
    trade_log.append({
        "timestamp": ts.strftime("%Y-%m-%d %H:%M:%S"),
        "symbol"   : "BTCUSDT",
        "side"     : side,
        "size"     : size,
        "price"    : round(price, 2),
        "pnl"      : round(pnl, 2),
        "balance"  : round(balance, 2)
    })

# ② DataFrame へ変換
df = pd.DataFrame(trade_log)

# ③ 保存ディレクトリを保証して CSV 出力
out_dir = Path("./results")
out_dir.mkdir(parents=True, exist_ok=True)

csv_path = out_dir / "backtest_result.csv"
df.to_csv(csv_path, index=False)
print(f"✅ バックテスト結果を保存しました → {csv_path}")

2‑4 live_log_export.py(全文:実運用ログを整形)

#!/usr/bin/env python3
"""
live_log_export.py
────────────────────────────────────────────────────────
Bot が吐いたテキストログ (./logs/live_trade.log) をパースし、
./results/live_trade_log.csv に整形保存するユーティリティ。

❏ 想定ログフォーマット
[YYYY-MM-DD HH:MM] Trade executed: SYMBOL SIDE SIZE @ PRICE, PnL: PNL, Balance: BAL
"""
from pathlib import Path
import re
import pandas as pd

# ① 入出力パス
log_path = Path("./logs/live_trade.log")
out_dir  = Path("./results")
out_dir.mkdir(parents=True, exist_ok=True)
csv_path = out_dir / "live_trade_log.csv"

# ② 正規表現パターン
a = (r"\[(?P<ts>.*?)\] Trade executed: "
     r"(?P<sym>\w+) (?P<side>\w+) (?P<size>[\d.]+) @ (?P<price>[\d.]+), "
     r"PnL: (?P<pnl>-?[\d.]+), Balance: (?P<bal>[\d.]+)")
pat = re.compile(a)

rows = []
with log_path.open() as f:
    for line in f:
        if (m := pat.search(line)):
            rows.append(dict(
                timestamp=m["ts"], symbol=m["sym"], side=m["side"],
                size=float(m["size"]), price=float(m["price"]),
                pnl=float(m["pnl"]), balance=float(m["bal"])))

if not rows:
    raise RuntimeError(f"❌ パースできる行が見つかりません: {log_path}")

pd.DataFrame(rows).to_csv(csv_path, index=False)
print(f"✅ 実運用ログを CSV に変換しました → {csv_path}")

簡易チェックリスト

  • 列構成:timestamp, symbol, side, size, price, pnl, balance
  • 欠損ゼロ:df.isnull().sum()
  • 重複ゼロ:df.duplicated().sum()

3. 比較スクリプト performance_comparison.py

import pandas as pd

# ------------------------------------------------------------
def load_data():
    """バックテストと実運用 CSV を読み込む"""
    bt = pd.read_csv('./results/backtest_result.csv')
    lv = pd.read_csv('./results/live_trade_log.csv')
    return bt, lv

# ------------------------------------------------------------
def calculate_metrics(df):
    """主要 4 指標を返す"""
    n   = len(df)
    win = df[df['pnl'] > 0]
    w%  = len(win) / n * 100 if n else 0
    pnl = df['pnl'].sum()
    dd  = (df['balance'].cummax() - df['balance']).max()
    return {
        'trades'       : n,
        'win_rate'     : round(w% , 2),
        'pnl_total'    : round(pnl, 2),
        'max_drawdown' : round(dd , 2)
    }

# ------------------------------------------------------------
def compare():
    bt, lv = load_data()
    bt_m   = calculate_metrics(bt)
    lv_m   = calculate_metrics(lv)

    print("\n📈 最終パフォーマンス比較")
    print(f"{'指標':<15} | {'シミュレーション':<12} | {'実運用':<12}")
    print("-" * 46)
    for k in bt_m:
        print(f"{k:<15} | {bt_m[k]:<12} | {lv_m[k]:<12}")

# ------------------------------------------------------------
if __name__ == "__main__":
    compare()

4. 実行 & 出力例

$ python performance_comparison.py
📈 最終パフォーマンス比較
指標             | シミュレーション  | 実運用        
----------------------------------------------
trades           | 128              | 116           
win_rate         | 58.59            | 51.72         
pnl_total        | 2650.5           | 1780.1        
max_drawdown     | 420.25           | 690.8         

5. 差分を読む:原因 × 改善

指標代表的原因代表的改善策
tradesBot 停止 / スケジュール漏れスケジューラ確認・例外復帰処理
win_rate遅延 / トリガー劣化スリッページ補正・閾値再設定
pnl_total手数料 / 成行偏重指値+監視発注 / 多段利確
max_drawdownSL 設計が甘いATR 等で動的 StopLoss

6. 最終調整サンプル

# config.yaml before → after
RSI_OVERSOLD       : 30 → 35
RSI_OVERBOUGHT     : 70 → 65
ATR_THRESHOLD      :100 → 80
STOP_LOSS_PERCENT  :4.2 → 3.0
TAKE_PROFIT_PERCENT:6.8 → 5.5
# トレンドフィルターを追加
df['ma_fast'] = df['close'].rolling(5).mean()
df['ma_slow'] = df['close'].rolling(20).mean()

手順

  1. 調整を反映 → 再バックテスト (backtester_export.py)
  2. backtest_result.csv を更新
  3. performance_comparison.py を再実行 → 数値確認

7. まとめ & 次のアクション

  • 比較 → 評価 → 調整 のステップ 2 を完了
  • 指標が許容範囲なら 監視設計 (Slack 通知・Pod 監視など) を実装
  • 次回 part.25 は デプロイ後の安定性確認 を掘り下げます。

📌 フローを自動化する場合は CI で CSV 出力 → 比較 → Slack レポートを回すと便利です。


関連
開発記録#163(2025/4/2)「論文ベースのbot開発フローpart.25」

続きを見る

👇ラジオで話したこと

こんにちは、Yodakaです。
今回のラジオでは、「仮想通貨Botって、作った通りにちゃんと動いてるの?」というテーマでお話します。

というのも──Botを動かし始める前に「シミュレーションで勝ってた戦略」が、本番環境でもちゃんと勝ってるのか?って、確認しないと怖いですよね。

これを 「最終パフォーマンスチェック」 と呼んで、Bot開発の第2ステップに位置づけています。


🧩 概要:バックテスト vs 実運用の比較

Botって、最初は「過去のチャートでどう動くか」っていう**シミュレーション(バックテスト)**を通して設計します。でも実際にマーケットで動かすと──

  • サーバー落ちてた!
  • 注文通ってなかった!
  • 約定(=注文が通ること)してなかった!

……なんてことが、しょっちゅうあるんですね。

だから「シミュレーション通りに動いてたのか?」をあとから数字で見比べるのが超重要になります。


🗂️ 1. データを準備する

まずは、**「シミュレーション結果」と「実運用の結果」**を、それぞれCSVファイルという表形式のデータにまとめます。

  • backtest_result.csv(バックテストの記録)
  • live_trade_log.csv(本番Botのログ)

この2つができれば、あとはPythonでスクリプトを1本書いて比べるだけ。


🧪 2. どんな指標で比べるの?

比較する項目は、主に以下の4つです:

指標名意味
tradesトレードの回数(Botが何回売買したか)
win_rate勝率(利益が出た割合)
pnl_total合計の利益(損益)
max_drawdown最大ドローダウン(口座が一番減った瞬間)

例えば「シミュレーションでは120回トレードしてたのに、実運用では80回しかしてない」とか、「勝率が7割だったのに実運用では5割しかない」など、ズレをあぶり出します


🧰 3. 比較スクリプトの一部(ざっくり)

def calculate_metrics(df):
    total = len(df)
    wins = df[df['pnl'] > 0]
    return {
        'trades': total,
        'win_rate': len(wins) / total * 100,
        ...
    }

こんな感じで、「勝ってるトレードの数」や「トレード回数」をカウントして指標を作ります。


⚠️ 4. ズレの原因と対策

ズレが出たときの原因と、それに対して何を調整すればいいかの例を紹介します。

ズレ内容よくある原因対策
トレード数が少ないBotが止まっていたログで停止原因を調査
勝率が下がってる市場の動きが予想外だったスリッページ補正を導入
PnLが低い注文が成行で高コスト指値で発注するよう修正
DDが大きい損切りラインが甘いStopLossを強化

🔧 5. 最後にやること(最終調整)

もしズレがあれば、以下のようにパラメータを微調整します:

  • RSIの基準値を見直す
  • 利確や損切りの幅を変える
  • トレンドフィルター(移動平均線)を導入する

そして、またバックテストして改善したかを確認。
これを**「数値で検証 → 修正 → 再検証」**とループして、精度を高めていきます。


✅ まとめ

最終パフォーマンスチェックは、言ってみれば「Botの健康診断」です。

頭の中で「勝てる!」と思って作った戦略でも、本番のマーケットは予測不能なことばかり。
だからこそ、「ズレてないか?」を数値で確認する習慣が大事です。


次回は、このBotをいよいよ「本番環境で安定稼働させる」ためのステップ──モニタリングと通知の整備についてお話しします。

ではまた、次回の放送でお会いしましょう!

-Bot