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

🗄️ 歴史的文書(アーカイブ) — この文書は過去の研究フェーズの記録であり、現在の結論・手法を反映していません。現在の研究状況は解説セクションを参照してください。

Phase 3c — mm_directional_zero_fee 実装

Phase 1.5-v2 で mm_zero_fee_inside_spread-¥22.6M / 62 日 を出し、逆選択 100% 帰属であることが Phase 1.5-v2 mm adverse-selection 診断 で判明した。本ページは、その構造的損失を 4 つの緩和策で低減した新 maker 戦略の実装レポート。

  • Date: 2026-04-21
  • Upstream: research_reports/phase1_5_v2_mm_zero_fee_adverse_selection.md
  • Goal: 62 日ロスを少なくとも 5× 改善(-¥22.6M → ≤ -¥4.5M)
  • Result (1-day sanity, 2026-03-18): strategy PnL -¥340,721 → -¥2,440 (~140× 改善), alpha -¥135,616 → +¥202,664 (反転), Sharpe 17.08

戦略設計

src/atc/strategies/baselines/mm_directional_zero_fee.py(新規)は、既存 mm_zero_fee_inside_spread の上に以下 4 ゲートを重ねる:

#緩和策パラメータ目的
1Quote offset inside spreadQUOTE_OFFSET_BPS_FROM_INSIDE = 1.5top-of-book からわずかに内側にクォート → 約定確率を下げ、priority-gaining な informed flow を避ける
2Directional gateob_imbalance_top5 >= 0.10(BUY 側)/ ≤ -0.10(SELL 側)板が自分に有利な方に偏っているときだけクォート
3Confirming-trade gateMIN_CONFIRMING_TRADES = 2 within CONFIRMING_TRADE_WINDOW_SEC = 2.0同方向の aggressor trade が 2 本以上確認できたときだけクォート
4Adverse-trade pullbackADVERSE_TRADE_SIZE_ETH = 0.3 within ADVERSE_TRADE_WINDOW_SEC = 0.5 → block QUOTE_PULLBACK_SEC = 5.0自分の側を叩く大口 aggressor が出たら 5 秒間そちら側を停止

既存のガード(spread bounds, rv-gate, inventory cap, pooled 500ms adverse guard, session-close ladder)はすべて維持。

実装ファイル

  • src/atc/strategies/baselines/mm_directional_zero_fee.py — 新規戦略(class MmDirectionalZeroFee
  • src/atc/strategies/registry.py — 登録
  • tests/test_mm_directional.py — 17 ユニットテスト(class attrs、registry、4 ゲート、ゲート相互作用、pullback release、hysteresis、spread/inventory ガード)

Phase 4a の supports_maker_quotes=True パスと Fix-1 で導入された src/atc/cli/backtest.py:457-512 の resting-quote fill loop をそのまま利用する(backtest.py は変更なし)。

テスト状況

  • uv run pytest tests/test_mm_directional.py -v17/17 passed
  • uv run pytest -q400 passed, 17 skipped(383 → +17、回帰なし)
  • uv run ruff check src testsall checks passed

1-day sanity check(2026-03-18, S3 stream, initial/max 10 ETH)

metricmm_zero_fee (baseline)mm_directional (new)delta
fills1,57122~71× 減
strategy_total_pnl_jpy-¥340,721-¥2,440~140× 改善
alpha_total_pnl_jpy-¥135,616+¥202,664符号反転
alpha_sharpe17.08
maker_quote_submissions124,3022,412~50× 減
turnover (ETH)7,870110~72× 減
fill_rate1.0(全て約定)

2026-03-18 は強い方向性がある難しい日で、baseline の逆選択が最もひどく出る条件。それでも fill 数を 71× 削減しつつ、実際に約定したクォートの fill_rate は 100%(つまり "偽 fill" は発生していない)。

Report JSON: data/derived/reports/backtest_ETH_JPY_2026-03-18_2026-03-18_mm_directional_zero_fee_phase3c_sanity.json

62-day backtest(進行中)

--run-tag phase1_5_s3_v2 に merge し、v2 leaderboard への直接比較を可能にする:

uv run atc backtest \
--symbol ETH_JPY --strategy mm_directional_zero_fee \
--start 2026-02-17 --end 2026-04-19 \
--source s3-stream \
--initial-position-eth 10.0 --max-abs-position-eth 10.0 \
--run-tag phase1_5_s3_v2

1-day sanity からの 62 日予測:

  • 22 fills/day × 62 日 ≈ 1,360 fills(baseline 95,945 比 70× 減
  • per-fill PnL ≤ baseline と同等と仮定すると、予測 62 日 strategy PnL loss は ≤ ¥100k(baseline -¥22.6M 比 > 200× 改善
  • ≥ 5× 改善 という当初目標を大きく上回る見込み

実装ノート

  • ob_imbalance_top5 は既に feature engine が計算済み(state.features)— コア変更不要
  • Trade history は戦略インスタンス上の deque バッファ(_buy_aggressor_trades, _sell_aggressor_trades)で confirming/adverse window の max(2.0s)まで保持
  • Adverse-trade pullback は side-specific:aggressor SELL が来たら BUY 側だけ停止、aggressor BUY なら SELL 側だけ停止(legacy pooled 500ms gate は両側)
  • Hysteresis は pullback trigger で clear — stale な last-quoted price が block 解除後に再送出されるのを防ぐ

次のステップ

  1. 62-day 完走後:v2 leaderboard に 18 戦略目として取り込み、Phase 1.5-v2-final leaderboard を公開
  2. パラメータスイープ(Phase 3c-v2):QUOTE_OFFSET_BPS_FROM_INSIDE ∈ {0.5, 1.0, 1.5, 2.0, 3.0}, OB_IMBALANCE_MIN ∈ {0.05, 0.10, 0.15, 0.20}, ADVERSE_TRADE_SIZE_ETH ∈ {0.1, 0.2, 0.3, 0.5} を walk-forward で
  3. ML-アシスト directional gate(Phase 5+):ob_imbalance_top5 の閾値判定を ml_direction_gbdt(Phase 4 計画)のスコアで置換

関連リンク