1. はじめに
オンチェーン清算スナイプBotは、単に「動く」だけでは十分ではありません。
相場環境やネットワーク状態は常に変化し、設計段階で想定した通りの挙動が続くとは限らないからです。
本記事では、このBotの開発プロジェクトを技術的な観点から時系列で整理します。
ゴールは、**「検知 → 記録 → 評価 → 実トレード」**というフィードバックループを確立すること。
このループが回れば、実運用の中で戦略を継続的に改善でき、運用の安定化とリスク管理を両立できます。
また、実トレードへ移行する前に、検知ロジックや市場インタラクションの精度をデータで裏付けることが可能になります。
2. 安定運用基盤の構築
開発の第一歩は、戦略ロジックよりも運用基盤の安定化から始めました。
戦略の優劣も、稼働環境が不安定では正しく評価できないためです。
単一プロセス起動
fcntl
ロック+PIDファイルで同一Botの多重起動を防止。- 旧プロセスが残っている場合は起動拒否し、運用事故を未然に防ぎます。
自己診断
- 起動時にgitハッシュ、設定ハッシュ、ポート番号、CollectorRegistryのIDをログに出力。
- 稼働中のBotのバージョンや設定差異を即座に把握でき、トラブルシューティングを効率化。
統一CollectorRegistry
- Prometheusの**
CollectorRegistry
を単一インスタンス**として運用。 - モジュールごとに別レジストリを持つ構成を避け、
/metrics
の重複定義や空応答といった不具合を防止。
主要メトリクスの統合
- ビルド情報:稼働中のコードと設定の特定に使用。
- 心拍(heartbeat):処理ループや外部接続が生きているかを定期的に計測。
- 影響分解(impact/micro_move):後述の検知・執行分析に利用。
- エラー類:例外発生やデータ不整合などを計測・可視化。
これらを1つのレジストリ上で一元管理することで、監視・分析の基盤を強固にしました。
3. 清算推定ロジックの進化
戦略の心臓部となる清算推定ロジックは、最初から複合的な条件で構築しました。
対象はBTC、情報源はすべて公開データに限定しています。取引所の内部APIや非公開フィードに依存せず、将来的な移植性・再現性を担保しました。
複合検知
- Tradesクラスタ
300ms窓内での取引群を集約し、特定方向の成約が連続するパターンを抽出。 - BBO(Best Bid/Offer)崩落
最良気配が急激に崩れる現象を検知。 - OI(Open Interest)Z-score
建玉残高の異常変動を統計的に評価。 - Mark価格ジャンプ
マークプライスの急変を補助指標として追加。
これらをスコアリングして、単一の指標では見逃す可能性のある清算イベントも拾えるようにしています。
過検知抑止
過検知(false positive)を減らすため、以下の工夫を実装しました。
- 単調時計:
time.monotonic_ns()
を利用し、システム時間変更の影響を排除。 - ヒステリシス窓:開始と終了の閾値を分け、クラスタの開閉を安定化。
- 重複排除キー:取引ID、方向、価格バケット、時間バケットでイベントを一意化。
- スコア上限:異常値によるスコアの暴走を防止(上限0.99)。
シリーズ集約
清算は単発で終わらない場合があります。そこで、
- 20%清算 → 30秒以内 → 全量清算
といった連鎖的な清算を1つのシリーズとして扱う仕様を導入しました。 - さらに1シリーズ1回のみ実行する制約(
series_exec_triggered
)を加え、同一現象に対する多重トリガーを防ぎます。
4. 保存と日次レポート基盤
検知結果や実行シミュレーションのデータは、後から分析可能な形で確実に残す必要があります。
ここでは「壊れにくさ」と「分析しやすさ」を両立する設計を採用しました。
NDJSON追記方式
- ファイル種別:
events_*.ndjson
:検知イベントexec_*.ndjson
:シャドー実行イベントshadow_pnl_*.ndjson
:+1秒後のPnL測定結果
- 1行1JSONの追記方式で保存。途中書き込みやプロセス停止でもファイルが壊れにくい。
- 書き込み時には
flush + fsync
でディスク反映を保証。
影響分解の可視化
impact_bp
:VWAPと直前best価格の差(板厚起因の影響)。micro_move_bp
:直前bestと直前mid価格の差(タイミング起因の影響)。- 上記をNDJSONとPrometheusメトリクスの両方に記録し、リアルタイム監視と後日分析を両立。
日次レポート(JST)
scripts/daily_kpi_report.py
でJST 00:00–24:00のデータを集計し、
MarkdownとParquet形式でreports/daily/
に出力します。
集計するKPIは以下の通り:
- count/fill率
- slip P50/P90
- +1s PnL(mid/mark) 平均・中央値・勝率
- 理由内訳(executed / stale_book / crossed_or_empty 等)
- impact/micro のP50/P90
壊れ行カウント
- NDJSONの読み込み時にJSONパース失敗行を検出し、
report_bad_lines_total{kind}
メトリクスとして記録。 - 分析結果に欠損が出た際、即原因特定が可能。
この設計により、
- 検知ロジックの精度評価
- 執行タイミングの最適化
- リスク要因(板厚 or タイミング)の切り分け
が日次単位で回せるようになっています。
次はこの仕組みを活用したシャドー執行による学習ループや、LIVE試験運転の安全装置について解説します。
5. シャドー執行による学習サイクル
実トレードを始める前に、市場板の挙動を精緻に把握し、執行戦略を磨き上げるために導入したのがシャドー(仮想)執行です。
これは実際には注文を出さず、過去の注文簿データやリアルタイムのL2データをもとに、あたかも注文を出したかのように板を「歩かせる」シミュレーションです。
IOC風板歩き
- 市場に出すと仮定した注文サイズに応じて、板を順に食い進める。
- 約定価格の加重平均からVWAP(加重平均約定価格)を算出。
- 実行前のmid価格との差から**スリッページ(bp)**を計算。
ガード類
実環境に近い条件を再現しつつ、異常なシナリオを除外するためのガードを実装。
- 鮮度ガード:板スナップショットのタイムスタンプが100ms以内。
- クロス/空板検知:bid >= ask または片側の板が欠落している場合は不成立扱い。
- Notional cap:注文の名目金額上限を優先、過剰な板歩きを防止。
理由ラベルの付与
シャドー執行の結果を分類することで、後の分析に活かせるようにしました。
executed
:条件を満たして板歩き完了。stale_book
:板の鮮度が基準を満たさない。crossed_or_empty
:板がクロスまたは片側欠落。insufficient_liquidity
:板厚不足で要求サイズが満たせない。
影響分解ヒストグラム
スリッページを2つの要因に分解し、Prometheusメトリクスで分布を可視化。
shadow_exec_impact_bp_histogram
:VWAPと直前best価格の差(板厚起因)。shadow_exec_micro_move_bp_histogram
:直前bestと直前mid価格の差(タイミング起因)。
これにより、「板の薄さ」と「発注タイミング」のどちらが損益に大きく影響しているかを定量的に評価できます。
清算スナイプbotに限らず、botのシャドー執行の鮮度ガードについては思うところがあって、現在は板スナップショット100ms以内を基準にしてるけど、これだとWebSocket遅延やGCポーズで一時的にズレることがある。連続何回か超えた場合のみ排除の方が安全かも。GCトリガーを増やす要因も排除していく。
— よだか(夜鷹/yodaka) (@yodakablog) August 9, 2025
6. 実運用の安全装置
シャドー執行で十分なデータと知見を得た後は、極小ロットから実トレードを安全に始めるための仕組みが必要になります。
そこで、LIVE実行専用の安全ガードを複数実装しました。
LIVEフラグ
- **
LIVE_EXEC_ENABLED
**でON/OFFを切り替え(デフォルトはOFF)。 - 実トレード開始は必ず明示的にONにするオペレーションフローを徹底。
キルスイッチ
急な市場変動や戦略劣化に備え、自動でLIVE実行を停止する条件を用意。
- 連敗N回:
LIVE_MAX_CONSEC_LOSSES
(例:5連敗でOFF)。 - 日次PnL下限:
LIVE_DAILY_PNL_FLOOR_BP
(例:日次合計PnLが-10bpでOFF)。 - 最小間隔:
LIVE_MIN_INTERVAL_MS
(例:3000ms、注文間隔を制限)。
メトリクス露出
LIVE実行の状態や実績もPrometheusで監視可能に。
live_exec_enabled
:現在のLIVE実行フラグ状態。live_exec_consec_losses
:現在の連敗数。live_exec_total{reason}
:実行結果の理由別カウント(executed / rate_limited / kill_* など)。live_exec_pnl_bp_histogram
:LIVE実行のPnL分布(bp単位)。
これらの安全装置と可視化により、**「試す→監視する→即座に止める」**というリスク管理が可能になり、実運用への移行リスクを最小限に抑えています。
次のセクションでは、この安全装置の運用方針と、KPI判定によるLIVE解禁プロセスについて詳しく解説します。
7. データ突合の信頼性
検知イベントと実行結果を正確に対応付けることは、分析や戦略改善に不可欠です。そこで導入したのが**exec_id
**です。
exec_idの役割
- 生成タイミング:シャドー実行(
exec
イベント)生成時にUUID形式で一意のexec_id
を発行。 - 記録範囲:
exec_*.ndjson
(実行イベント)shadow_pnl_*.ndjson
(+1秒後のPnL計測)
に同じexec_id
を付与して保存。
- 突合方法:日次レポート作成時に
exec_id
でexec
とshadow_pnl
をjoin。exec_id
がない古いデータはseries_id
でフォールバック。
実運用での利点
実トレードに移行した後も、exec_id
をそのまま活用することで:
- 検知イベント ↔ 実注文のフィル結果 ↔ PnL計算
といった一貫したトレーサビリティを確保。 - フィードの遅延やイベントの順序入れ替わりがあっても、データの紐付けが正確に行える。
この仕組みにより、分析精度が向上し、戦略の改善サイクルをより速く回すことが可能になっています。
8. 現在の運転モードと短期目標
運転モード
現時点では、量取り優先モードで稼働しています。
- LIVE=OFF:実トレードは行わず、シャドー実行によるデータ収集に専念。
- 緩和パラメータ:
EXEC_MIN_SCORE = 0.83
EXEC_ALPHA = 0.15
EXEC_COOLDOWN_MS = 4000
これにより、日次で十分なサンプルを確保しつつ、検知ロジックや影響分解の傾向を観測しています。
短期目標
- サンプル確保:1日あたり**≥50 exec**を目標。
- KPI合格ライン(2〜3日連続でクリアが条件):
count_exec ≥ 50
stale_book + crossed_or_empty < 25%
slip_bp P50 ≤ 2bp
、P90 ≤ 6bp
pnl_mid_1s
勝率 ≥ 52%、平均 ≥ 0.5bp
これらの条件を満たした時点で、LIVE=ONに切り替え、極小ロットでの試験運転に移行します。
この段階でも、キルスイッチ(連敗・日次PnL下限・最小間隔)は常時有効にし、リスクを最小化した状態で運用を始める予定です。
次のセクションでは、このKPI判定後のLIVE解禁プロセスと、その後の戦略チューニング計画について紹介します。
9. 今後の補強ポイント
現状のBotは「観測 → 記録 → 評価 → 実トレード」のループを回せる状態にありますが、実トレードの安定性と安全性をさらに高めるため、以下の機能を追加予定です。
JST日次リセット
- 目的:運用日単位でのリスク管理を明確にするため。
- 内容:JST 00:00時点で、
_consec_losses
(連敗カウンタ)と_daily_pnl_bp
(日次PnL合計)を自動的に0にリセット。 - 効果:日をまたいだ連敗や損失が残らず、1日ごとにクリーンな状態でスタートできる。
露出上限ガード
LIVE_MAX_NOTIONAL_USD
:保有ポジションの総名目額上限。LIVE_MAX_PARALLEL
:同時に保有できるポジション数の上限。- 効果:発注頻度やロットを上げた際でも、過剰なポジション保有を防ぎ、過大リスクを抑制。
Stalenessスロットル
- 目的:板データの鮮度が悪化したときの誤発注防止。
- 内容:
orderbook_staleness_ms
が基準値(例:150ms)を連続して超えた場合、30秒間シャドー/ライブ実行を停止。 - 効果:フィード遅延や接続不安定時の誤作動を防ぐ。
実トレードAPI接続時のPnL計算改善
- 現状:シャドー実行時は+1秒後のmid/mark価格を用いた近似PnL。
- 改善:実注文APIと接続後は、実際のfill価格・fillサイズに基づいてPnLを計算。
- 効果:実運用時の損益計測精度を向上させ、戦略評価を正確化。
10. まとめ
今回の開発フェーズで、観測 → 記録 → 評価 → 実トレードというBot運用の基本サイクルがほぼ完成しました。
これにより、次のステップとして安全に極小ロットでの試験運転へ移行できる状態になっています。
特に技術的な観点では、以下の4つのポイントを押さえられたことが大きな成果です。
- 安定稼働のためのインフラ構築
- 単一プロセス起動、自己診断、統一CollectorRegistry、主要メトリクス統合。
- 精度を高めた清算検知ロジック
- 公開情報ベースの複合検知+過検知抑止+シリーズ集約。
- 壊れにくいデータ保存と自動集計
- NDJSON保存方式と日次レポート基盤、影響分解の可視化。
- リスクを抑えた安全装置
- LIVE実行フラグ、キルスイッチ、メトリクス可視化。
この構成により、開発サイクルを加速しつつ、運用リスクを最小化する仕組みが整いました。
次のフェーズでは、実ロット試験運転で得られるフィードバックをもとに、戦略パラメータの最適化とリスク管理強化を同時に進めていきます。
11. 極小ロット試験運転の手順
KPI合格ラインを2〜3日連続でクリアしたら、いよいよ実トレードを小さく解禁します。
このフェーズでは安全装置をフル稼働させ、影響を最小限に抑えた状態で市場に接続します。
事前チェック
- KPI判定(2〜3日連続で以下を満たす)
count_exec ≥ 50
stale_book + crossed_or_empty < 25%
slip_bp P50 ≤ 2bp, P90 ≤ 6bp
pnl_mid_1s
勝率 ≥ 52%、平均 ≥ 0.5bp
- メトリクス確認
live_exec_enabled
が0(OFF)であること。orderbook_staleness_ms
の95%タイルが閾値内。
起動手順
LIVE_EXEC_ENABLED=1 \ LIVE_MAX_CONSEC_LOSSES=5 \ LIVE_DAILY_PNL_FLOOR_BP=-10 \ LIVE_MIN_INTERVAL_MS=3000 \ METRICS_PORT=8000 \ python scripts/start_corrected_btc_sniper_robust.py > /tmp/btc_sniper.log 2>&1 &
運用時の監視
- リアルタイム
watch -n 2 "curl -s localhost:8000/metrics | grep -E 'live_exec_enabled|live_exec_consec_losses|live_exec_total{reason=\"executed\"}'"
- 日次レポート
- 実トレード分は
exec_id
でPnLと検知イベントを突合。 - 勝率・滑り分布・理由内訳をシャドー実行時と比較。
- 実トレード分は
停止条件
- 自動OFF
- 連敗回数が
LIVE_MAX_CONSEC_LOSSES
に達したとき。 - 日次PnL合計が
LIVE_DAILY_PNL_FLOOR_BP
を下回ったとき。
- 連敗回数が
- 手動OFF
- 市場環境が急変、または監視で異常値を確認した場合。
12. パラメータ調整計画
極小ロット試験の目的は戦略パラメータを安全に最適化することです。
特にα
(板厚に対する取得比率)、TTL
(注文有効時間)、score
(検知スコア閾値)の3要素が中心になります。
調整の流れ
- 初期設定(試験開始時)
α = 0.15
TTL = 300ms
score = 0.85
- A/Bテスト方式での検証
- 1日単位で1つのパラメータだけ変更。
- 各パターンで**≥100 exec**分のデータを取得。
- 評価指標
- 滑りP50/P90(bp)
- 勝率・平均PnL(bp)
- 実行成功率(executed割合)
impact_bp
/micro_move_bp
の支配要因
具体例
- impact優勢(板厚起因が大きい)
→α
を下げる(例:0.15 → 0.10)、TTL
短縮。 - micro_move優勢(タイミング起因が大きい)
→score
閾値を上げる(例:0.85 → 0.90)、cooldown
延長。
フィードバックループ
- 実行 → 記録:execイベントとPnL、理由ラベルを保存。
- 日次評価:KPI達成度と影響分解を分析。
- パラメータ更新:翌日の設定に反映。
- 繰り返し:条件が安定して勝てる範囲まで調整を継続。
このフェーズを経て、安定してKPIを満たせるようになれば、ロットの段階的引き上げや対象市場の拡大に進むことができます。(できない場合は開発見送り)
次回の記事では、パラメータチューニングの実測例とロット拡大時のリスク管理について掘り下げていく予定です。
付録:今後掘り下げていきたいことリスト(8項)
- レイテンシ分解メトリクス:
md→decide→prelat→req→ack
をヒスト化。 - Reorder Buffer:
event_ts
で120–200ms整列→300msクラスタへ。 - ガス/インクルージョンをPnLへ内生化:
gas_cost_usd
とinclusion_delay_ms
を影響分解に追加。 - 外因キル:
rpc_error_rate/gas_p95/block_time_p95/oi_stale
で全体cooldown。 - インタリーブA/B:
arm
タグで同時期比較、日替わりはやめる。 - ULID exec_id:時系列デバッグを容易に。
- Wilson区間 or SPRT:KPI合格の統計根拠を明文化(最低500 exec推奨)。
- 露出上限の自動縮退:
gas↑
やstaleness↑
でα↓/score↑/interval↑
を自動適用。