🗄️ 歴史的文書(アーカイブ) — この文書は過去の研究フェーズの記録であり、現在の結論・手法を反映していません。現在の研究状況は解説セクションを参照してください。
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 ゲートを重ねる:
| # | 緩和策 | パラメータ | 目的 |
|---|---|---|---|
| 1 | Quote offset inside spread | QUOTE_OFFSET_BPS_FROM_INSIDE = 1.5 | top-of-book からわずかに内側にクォート → 約定確率を下げ、priority-gaining な informed flow を避ける |
| 2 | Directional gate | ob_imbalance_top5 >= 0.10(BUY 側)/ ≤ -0.10(SELL 側) | 板が自分に有利な方に偏っているときだけクォート |
| 3 | Confirming-trade gate | MIN_CONFIRMING_TRADES = 2 within CONFIRMING_TRADE_WINDOW_SEC = 2.0 | 同方向の aggressor trade が 2 本以上確認できたときだけクォート |
| 4 | Adverse-trade pullback | ADVERSE_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— 新規戦略(classMmDirectionalZeroFee)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 -v→ 17/17 passeduv run pytest -q→ 400 passed, 17 skipped(383 → +17、回帰なし)uv run ruff check src tests→ all checks passed
1-day sanity check(2026-03-18, S3 stream, initial/max 10 ETH)
| metric | mm_zero_fee (baseline) | mm_directional (new) | delta |
|---|---|---|---|
| fills | 1,571 | 22 | ~71× 減 |
| strategy_total_pnl_jpy | -¥340,721 | -¥2,440 | ~140× 改善 |
| alpha_total_pnl_jpy | -¥135,616 | +¥202,664 | 符号反転 |
| alpha_sharpe | — | 17.08 | — |
| maker_quote_submissions | 124,302 | 2,412 | ~50× 減 |
| turnover (ETH) | 7,870 | 110 | ~72× 減 |
| fill_rate | — | 1.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 解除後に再送出されるのを防ぐ
次のステップ
- 62-day 完走後:v2 leaderboard に 18 戦略目として取り込み、Phase 1.5-v2-final leaderboard を公開
- パラメータスイープ(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 で - ML-アシスト directional gate(Phase 5+):
ob_imbalance_top5の閾値判定をml_direction_gbdt(Phase 4 計画)のスコアで置換