メインコンテンツまでスキップ

バックテスト内部仕様

このページは、atc backtest が内部で何をしているかを、実装ベースで説明します。

対象コード:

  • src/atc/cli/backtest.py
  • src/atc/execution/execution_manager.py
  • src/atc/features/feature_engine.py

0. この文書でいう「イベント」とは

ATCにおけるイベントは、src/atc/core/event_models.pyCanonicalEvent です。
1件のイベントは「市場または内部状態の1回の更新」を表します。

主要フィールド:

  • event_type: 何の更新か(例: TRADE, TICKER, ORDERBOOK_SNAPSHOT
  • exchange_ts_utc: 取引所時刻(UTC)
  • recv_ts_utc: 受信時刻(UTC)
  • symbol: 対象銘柄(例: ETH_JPY
  • payload: イベント固有データ(約定価格、bid/ask、板スナップショットなど)

戦略は、このイベント列を1件ずつ処理して TargetPositionIntent を返します。

0.2 イベントはどこから来るか(取得元と使用データ)

ATCでバックテストに使うイベントは、主に次の2系統です。

EventType主な取得元具体的な入力データCanonical化コードバックテストで主に使うフィールド
TRADEGMOヒストリカルCSVsymbol,side,size,price,timestampsrc/atc/data/ingest_historical.pyprice, side, size, exchange_ts_utc
TRADEGMO Public WS trades約定メッセージ(side/size/price/timestamp)src/atc/data/ingest_ws_public.py + src/atc/exchange/gmo/ws_public.pyprice, side, size, exchange_ts_utc
TICKERGMO Public WS tickerbest bid/ask, last, timestamp同上bid, ask, last
ORDERBOOK_SNAPSHOTGMO Public WS orderbooksbids/asks スナップショット同上上位板価格・サイズ(imbalance/depth/microprice計算)

補足:

  • ヒストリカルCSVのURLパターン:
    https://api.coin.z.com/data/trades/{SYMBOL}/{YYYY}/{MM}/{YYYYMMDD}_{SYMBOL}.csv.gz
  • 保存フロー: raw -> canonical -> derived
  • 通常のヒストリカルバックテストでは TRADE が中心です(板情報はWS収集データがある期間のみ利用可能)。

0.1 処理フロー(全体)

1. 入力と初期状態

atc backtest は次を受け取ります。

  • 期間: --start, --end(JST営業日)
  • 戦略: --strategy
  • 実行制約:
  • --order-latency-sec(既定0.3秒)
  • --min-order-interval-sec(既定300秒)
  • 比較基準:
  • --benchmark-hold-eth(既定10.0)

初期ポジションと初期現金は次で指定します。

  • --initial-position-eth
  • --initial-cash-jpy

2. イベントごとの処理順序

各イベントで次を実行します。

  1. FeatureEngine.update(event)
  • mid, spread, logret_1s/5s/30s, rv_5s/30s, trade_imbalance_1s などを更新
  1. 保留中limit注文の約定判定
  • 注文有効化時刻(submit + latency)以降の TRADE を見て fill 判定
  • 買い: trade_price <= limit_price
  • 売り: trade_price >= limit_price
  1. 戦略評価(該当イベントタイプのみ)
  • on_event()TargetPositionIntent を返す
  1. リスク判定
  • RiskManager.allow_target(...) が拒否した注文は採用しない
  1. 発注制約
  • min_order_interval_sec 未満なら throttled_min_interval
  • 既存保留注文があれば置換キャンセルして新規注文を保留
  1. 損益更新
  • equity の差分を strategy_pnl_step として累積
  • benchmark差分: benchmark_hold_eth * (mid_t - mid_{t-1})
  • alpha_step = strategy_step - benchmark_step

3. セッション強制クローズ(任意)

--enforce-session-close をONにした場合のみ、以下が有効です。

  • 新規建て抑制: 05:49:00 以降(既定10分)
  • 強制クローズ窓: 05:55:00 以降
  • デッドライン: 05:59:50 以降
  • fallback: 未解消なら market close(許可時)

関連引数:

  • --no-new-entry-minutes
  • --force-close-start-jst
  • --close-deadline-jst
  • --allow-market-fallback / --no-market-fallback

4. 出力JSONの読み方

主なフィールド:

  • strategy_total_pnl_jpy: 戦略の累積損益
  • benchmark_total_pnl_jpy: 固定保有の累積損益
  • alpha_total_pnl_jpy: 超過損益
  • metrics.alpha_sharpe
  • metrics.alpha_max_dd
  • orders, fills, metrics.fill_rate, metrics.turnover

強制クローズ関連:

  • forced_close_orders
  • forced_close_fills
  • market_fallback_count
  • deadline_breach_flag

4.1 Postgres への結果同期(任意)

ATC_POSTGRES_WRITE_ENABLED=true のとき、run_backtest_cli は JSON ファイル出力後に src/atc/storage/postgres_sink.py 経由で backtest_runs へ upsert します。

設計方針:

  • ファイル出力を主系とし、DB同期は副系
  • DB同期失敗は backtest 自体を失敗させない(例外を吸収)
  • Dashboardは保存済みレコードのみを比較対象にする

5. 価格可視化(overlay)

--plot を付けると、注文マーカー付きチャートを生成します。

  • HTML: インタラクティブ確認向け
  • PNG: レポート添付向け
uv run atc backtest \
--symbol ETH_JPY \
--start 2026-01-01 \
--end 2026-01-31 \
--strategy ml_hold10_softmax_overlay_derisk_only_aggressive \
--initial-position-eth 10 \
--benchmark-hold-eth 10 \
--target-abs-qty-eth 10 \
--max-abs-position-eth 10 \
--min-order-interval-sec 300 \
--plot --plot-format both --plot-interval 5m

6. 再現性を壊しやすいポイント

  1. 初期条件の不一致
  • initial_position_ethbenchmark_hold_eth が揃っていない比較
  1. 実行制約の差
  • order_latency_sec / min_order_interval_sec の違い
  1. セッション制約の混在
  • enforce_session_close のON/OFFが実験間で一致していない
  1. 学習モデルの参照先違い
  • 環境変数や latest モデルが別ファイルを指している