Bot 開発ログ

🛠️開発記録#282(2025/8/16)本日の作業ログ

今回は“机上戦→実戦移行”のレバーに手がかかったかなという感じです。
特にオンチェーン系は「怖いから止まる仕組みを作った」ので、今後もスモールステップを意識して開発を進めていきます。

MMbot 開発ログ

0. 目的と現状サマリー

  • 目的:Binance Japanでのマーケットメイクによる安定収益化
  • 達成度(稼ぐ観点)40% ±5%
    • 土台(可観測性・接続・レイテンシ計測)は強化済み。
    • 残りは α検証→Paper→最小Live の収益化フェーズと、自動停止の実戦確認
  • 最新実測
    • /metricsGET 200(HEADは405仕様、正常)
    • mm_lat_total_agg_seconds_bucket20行出力(REG統一OKのサイン)
    • Prometheus:スタック起動済み(scrape確認=up{job="mm-bot"} は最終チェック段階)
    • p99:録画ルール未適用(これから)

1. 技術基盤(完了)

  • 依存prometheus-client を正規導入(Docker単体検証でも import OK)
  • 設定ConfigShim 導入で .get / [] / attr を提供(Runnerでは self.cfg = ConfigShim(cfg)/互換 self.config = self.cfg
  • 接続BinanceJapanAdapter 生成→await connect()ready() 確認を _init_components() 先頭に固定
  • 在庫InventoryManager 互換化(get_all_positions(symbol|なし)get_position()/戻り値dictの両対応)
  • 責務分離:監視・計測は 副作用なし、戦略/実行/リスクは別層に戻す方針(Monitorへの一時的HotfixにはTODOを付与)

2. メトリクス / 監視(現状)

  • REG統一:Exporter start_http_server(..., registry=REG)、全メトリクス registry=REG 明示
  • 系列のブートストラップRunner.start() 冒頭で LAT.observe(0.0)(必ず系列が出る)
  • 出力確認mm_lat_total_agg_seconds_bucket20行(秒ヒストグラム)
  • Prometheus:スタック起動済み。次の確認
    • up{job="mm-bot"} == 1
    • mm_lat_total_agg_seconds_bucketseries > 0
  • 録画ルール(未反映)
    • mm_total_p99_ms:5m/1m*1000でms化)
    • mm_total_obs_rps:5m

3. レイテンシ / 実行品質

  • レイテンシ分解md→decide→prelat→req→ack を計測設計済み
  • 重点指標
    • Cancel RTT の p95/p99(逆選択コストに直結)
    • Fill率、Reject率、実効スプレッド/スリッページ
  • p99ガード(設計)p99>250ms(連続2分)disable_live

4. Paper / Live(現状と設計)

  • Paper:実装中(改善ポイント)
    • キュー位置モデル(先頭距離×確率)
    • 部分約定(10–20%注入)
    • 高遅延時のキャンセル失敗イベント
  • Live:最小サイズ(1×)の前提条件を定義
    • Paper 24–48hEV>0, fill≥35%, reject≤3%
    • 最小Live 1日:ネットPnL ≥ 0、停止条件が一度以上正常発火

5. P/L分解(ダッシュボード方針)

最上段に “日次EV(Go/No-Go)” を配置。下段に分解の4枚:

  1. Spread PnL + Rebate
  2. Adverse Selection(mid移動×注文方向)
  3. Slippage / Fees(実効コスト)
  4. 実行品質:Cancel RTT p95/p99, Fill率, Reject率

6. 主要な修正・決定ログ(要点)

  • prometheus-clientPoetry→Docker に確実反映(venv混線回避、system site-packages運用)
  • REG不一致 を解消(Exporter/定義をREGで統一)
  • 二重登録 回避のため Monitor をシングルトン化(重複登録エラー対策)
  • Runner の属性名・初期化順を統一(self.cfg/self.inventory/adapter.connect() 先行)

7. リスク / 自動停止(設計→演習へ)

  • 停止p99>250ms(2m) / reject>3% / 日次DD>1Rdisable_live
  • 再開p99<200ms(5m) & reject<2%
  • Alert運用
    • Warning:p99>250ms(5m) & ObsRPS≥0.10
    • Critical:p99>350ms(1m) or p99>300ms(5m) & ObsRPS≥0.10
    • Alertmanager Webhook or サイドカーで /emit disable_live 連動

8. 既知の課題・残タスク

  • Prometheus:scrape確認(up/series) と録画ルール反映
  • Paper:現実寄りFillモデル を導入(先頭距離/部分約定/キャンセル失敗)
  • P/Lパネル:分解4枚の追加(PromQL or 日次CSV)
  • 自動キル:疑似負荷で Warning→Critical→停止 の演習を各2回

9. 今日〜48hの実行プラン(最短で“稼ぐ”に近づける)

T+0.5h

  • Prom:up{job="mm-bot"}series>0 を確認 → 録画ルールをReload
  • /api/v1/querymm_total_p99_ms:5m, mm_total_obs_rps:5m非空になるまで待機(~45–90s)

T+1–3h

  • ダッシュボードに P/L分解4枚 を追加(暫定でCSVでも可)
  • Paper に 先頭距離×確率 を入れて再テスト

T+24–48h

  • Paper連続運転(24–48h)→ KPI 判定
  • 疑似遅延/Reject注入で 停止→復帰 フローを2回通す

Go条件を満たしたら 最小Live(1×, 1日) へ。


10. 受け入れ基準(Checkリスト)

  • /metrics GET 200 & mm_lat_total_agg_seconds_bucket > 0
  • Prom:up==1 & series>0
  • mm_total_p99_ms:5m / mm_total_obs_rps:5m 非空
  • Paper 24–48h:EV>0, fill≥35%, reject≤3%
  • 最小Live 1日:ネットPnL ≥ 0、停止発火ログ有り

CEX/DEX Arbbot 開発ログ

1) 概要・目的

  • 目的:裁定ボットを実測ベースで運用最適化し、“最初の1ドル”を獲る。
  • 運用方針(三段フィルタ)
    通知 38bps → 人間ゲート 46bps(ラダー 45bps は30分TAP≥46=0時のみ1本) → pre-net≥+10bps


2) 現状サマリ

  • watcher:UP(NET_EDGE_MIN_BPS=38
  • killswitch:tail版でUP(履歴無視・現行末尾から監視)
  • TAP:直近観測例 42.5 / 45.2(p95はデータ希薄時 None)
  • 取引履歴:11本(勝率 0.000, 平均PnL −0.01283$/10USD
  • 実測メディアン:fees 20bps, slip 3.15bps(→ BE≈23bps、目安 gross≥33bps

3) 実装・パッチ(完了)

  • 判定系decide_exec.py(OK/OK_LADDER/SKIP)、preok.sh(fx_bp可、pre-net算出)
  • 一括実行go_one.py(判定→不足提示→実行→ログ)、
    exec_once.shflockで二重起動防止、exec_log.jsonlに固定スキーマ追記)
  • ラダーladder_ok.py(30分窓でTAP≥46=0判定、exit code), ラダー1本後は実行ラインを46に復帰
  • 不足可視化need_cf.pyneed_delta_px / need_cex_fill_min を即出力)
  • 監視status_now.py(tail killswitch対応), tap_follower.sh(CSV直書き・多重起動防止)
  • 復旧safe_restart.sh(watcher→1秒→killswitch_tail 起動)

ログスキーマ{ts, tap, pre_net, fx_bp, ladder, dq, df, cb, cf}


4) 直近の学び

  • pre-net不足が主因:例)DF=190.16, MID=190 だと need_delta_px≈0.627 USDT(gross=33bps相当)が必要。
    つまり CEX_BID ≥ need_cex_fill_min≈190.787 になった瞬間だけGO。
  • ラダー45は通知には有効だが、利得面では“あと一歩”帯。pre-net条件を満たす場面が少ない。

5) 運用Runbook(現行)

  1. トリガdecide_exec.pypreok.sh DEX_Q CEX_BOOK(ok=True)
    NO-GO時は need_cf.py DEX_FILL MID で不足数値化
  2. 実行go_one.py DEX_Q DEX_FILL CEX_BOOK CEX_FILL MIDtail -n1 data/exec_log.jsonl
  3. ラダーladder_ok.py && go_one.py …1本だけ/終了後は実行ライン46へ)
  4. 5本サマリ:win率>0.60 & 平均PnL>0 で継続/未達なら実行ラインのみ±1bps(通知38は据え置き)
    • 直近30分TAP p95≥53 → +1, p95≤44 & 希薄 → −1(ラダー条件と重複しないこと)

6) 詰まり・課題(現状)

  • pre-net不足:TAPは出るが gross差が足りず + コスト(20+3bps)。
  • サンプル数が少ない:11本では統計的に弱い(過調整リスク)。
  • 狩場が狭い:SOL中心だとTAP母集団が小さい時間帯が多い。

7) 今日〜今週のアクション(具体)

今日(90分)

  1. ラダー1本ladder_ok=True かつ preok.sh ok=True(=pre-net≥+10)を満たす瞬間だけ実行
    • 目安:CEX_BID ≥ need_cex_fill_minscripts/need_cf.py DEX_FILL MID
  2. 5本集めて即判定:合格なら継続/未達なら 実行ライン±1bps(通知38は据え置き)
  3. ログ確認tail -n1 data/exec_log.jsonl(空値なし・固定スキーマで入っていること)

今週

  • 監視銘柄の拡張(5〜10銘柄)+ベストBID採用(マルチCEX比較)
  • ラダーはデータ採取目的に限定(無駄打ち防止、pre-net閾値を+10→+12へ厳格化も検討)
  • 日次事実同期fees_med + max(slip_p95, slip_med+2) + 15 を再計算(通知は±2bpsまでの見直し)
  • 勝ち条件の抽出exec_log.jsonl に tap_age / depth / σ / fx_bp を追加 → 日次で“勝ちパターン”可視化

8) KPI / 受入れ基準

  • 短期KPI:二重ゲートで 新規5本(NO-GOは不足数値を残す)
  • 合格基準win_rate>0.60 & avg_pnl>0(未達なら実行ライン±1bps)
  • ログ:各約定で {ts,tap,pre_net,fx_bp,ladder,dq,df,cb,cf} が1行記録

人間ゲート(human gate)

意味

  • 通知(NET_EDGE_MIN_BPS=38)よりも厳しい実行ラインを、人が運用ポリシーとして設定している閾値。現在は 46bps(ラダー時は 45bps 条件付き)。
  • 実行するか否かを最終判断する“人側の安全弁”。過剰エントリを防ぐ。

使い方

  1. TAP ≥ 46bps(=人間ゲートを超えた通知)
  2. かつ pre-net ≥ +10bps(質ゲート)
    GO
  • データが希薄/過密なときに ±1bps だけ微調整(直近30分TAPのp95に連動)。通知38は据え置き、触るのは実行ラインのみ。

実測メディアン(observed medians)

意味

  • ログから得た“実際の取引/市場環境”の中央値。しきい値の根拠とBE(損益分岐)の見積もりに使う。
  • 例:
    • fees_med = 20 bps
    • slip_med = 3.15 bps
      BE ≈ fees + slip ≈ 23 bps

役割

  • 通知ラインの算出:fees_med + slip_med + 15 ≒ 38bps(※通知は多めでOK)
  • 実運用の安全幅:pre-net閾値(+10bps)や人間ゲート微調整の“現実合わせ”の材料。

ラダー(ladder)

意味

  • 本命(TAP≥46)不在のときにだけ使える、条件付きの“1本限定”ショートカット
  • ルール:直近30分に TAP≥46 が 0件なら 45bps を実行ラインとして 1本だけ許容(ladder_ok.py が True のとき)。終了したら必ず46bpsに復帰。

ただし必須条件は維持

  • ラダーでも pre-net ≥ +10bps は必須(=質は落とさない)。

狙い

  • 機会が薄い時間帯の データ採取/“もう一歩”を拾うための限定運用。無駄打ち防止のため「1本限定+復帰」を徹底。

参考:関連指標の式(ここでの定義)

  • gross_bps((CEX_FILL − DEX_FILL) / mid) * 1e4
  • pre-netgross_bps − fees_bps − slip_bps − fx_bp
  • BE(損益分岐)fees_med + slip_med(現状 ≈ 23 bps
  • 目安pre-net ≥ +10 bpsgross ≥ 33 bps 目安

例(mid≈190, DEX_FILL=190.16)

  • gross≥33bpsに必要な価格差 ≈ 0.627 USDTCEX_BID ≥ 190.787 を満たした瞬間のみGO。

まとめ

  • 人間ゲート=“撃つ/見送る”を決める運用上の最終閾値(46bps/ラダー45bps)。
  • 実測メディアン=現場でのコストや滑りの代表値。閾値やBEの根拠。
  • ラダー=本命不在時の条件付き1本(直近30分でTAP≥46=0のときだけ、45bps+pre-net≥+10bps)。終了後は実行ライン46へ復帰。

オンチェーン清算スナイプbot(Hyperliquid)開発ログ

概要(Summary)

  • 目的:清算前後の短期優位を検知→低ノッチで影/実執行、**keep%↑&p95↓&drop≤10%**で“稼げる”状態へ。
  • 現状到達度(目安):≈ 46%(動作・可視化は整備、keep% 低位/drop未計測がボトルネック)。
  • 最新実測(代表)
    • JOIN=1163 / KEEP=374 / KEEP%=32.2
    • p95はヒスト算出で管理(例:計測ケースにより 25–300ms を再現、混線解消済)
    • bad_order=0 / pair_block=0 / faded=0(メトリクス型と判定バグ解消後)

タイムライン(主な出来事)

  • P0: 観測再構築:単一Registry+メトリクス鯖統一(:8019)。series_id正規化、ts正規化(sec/µs混在→ms)。
  • P1: 合議路の整流:2-of-4合議、JOIN/CLOSE窓、_process_confirms()実装、KEEP冪等化(JOINあたり1回)。
  • P2: 診断強化consensus_eval_total{ltK|pair_block|join}alive_kinds_per_eval、miss理由、align_ms_current
  • P3: REPLAYブートrefreshのみで片翼心拍(JOIN水増しなし)→ K=2到達率↑
  • P4: ペアゲート:実データ準拠に再設計(trade_cluster×oi_z等)。REQUIRE_PAIR=ENV切替
  • P5: バグ潰し
    • pair_blockがREQUIRE_PAIR=0でも増える→環境変数正規化で修正。
    • faded_before_keepの巨大値→Counterに統一で解消。
    • Registry不一致(default vs get_registry)→self.m経由に統一。

現行アーキテクチャ(要点)

  • データtrades / l2Book / activeAssetCtx を整流→series_key(coin, side, slot)に集約。
  • 同期評価SyncEvaluator が同一 ts_ms/series_key で複数kindを評価(FIRE/REFRの二段閾値)。
  • 合議:K=2/N=4、JOIN→時間依存KEEP2段確認ヒープkeep_wait_ms計測)。
  • SLOconsensus_latency_ms(slot_start→JOIN)、p95はヒスト算出一本化。
  • 監視eval_total / alive_kinds / keep_wait_ms / miss_breakdown / align_ms_current

直近の実測と読み

  • alive_kinds 分布1種:877 / 2種:1143 / 3種:25ltK(2種不足)が主要因
  • KEEP%=32%:JOIN→KEEPの間でTTL/窓の不整合が残る公算。keep_wait_msピークと照合して最適化へ。
  • p95:計測混線は解消。ALIGN依存(例:ALIGN=300 → p95≲300–340ms)で再現可能。

修正済み(Done)

  • Registry統一(get_registry)、self.m一本化
  • KEEP冪等化(JOIN1件に対しKEEP最大1回)
  • 引数順ガード(score/ts_ms自動スワップ+bad_order可視化)
  • ペアゲートのENV整備&False判定の厳密化(“0/false/no/off/空”→False)
  • miss理由の型&計測修正(Counter統一、exactly-one方針に寄せた実装)

未完・優先タスク(Next)

  1. TTL/窓の整合(即効)
    • まずセットAで固定:ALIGN=300 / JOIN_WINDOW=600 / KEEP_CONFIRM=250 / TTL=2000
    • ルール:TTL ≥ KEEP×2 + 100ms。**KEEP% 60–75%**を先に確保。
  2. keep_wait_ms 分布→KEEP窓微調整
    • ピーク+αで KEEP_CONFIRM_MS を調整(±50ms単位)、JOIN_WINDOW_MSも追従。
  3. drop 計測ON(影/実4カウンタ)
    • shadow/live_exec_attempt_totalshadow/live_fill_success_totaldrop = 1 - (live/shadow)
    • 目標 ≤10%
  4. ペアゲート再導入(段階)
    • OFFで alive_pair_total を観測→トップ3–5をPAIR_SET_JSONに注入→ON→KPIを二分探索。
  5. αトリガの見直し(頻度差の是正)
    • oi_zが希薄なら FIRE閾値を一段緩めtrade_clusterは据え置き(REPLAYでKPI比較)。

推奨パラメータ・検証セット

  • セットA(安定寄り)
    ALIGN=300 / JOIN_WINDOW=600 / KEEP_CONFIRM=250 / TTL=2000
  • セットB(俊敏寄り)
    ALIGN=80 / JOIN_WINDOW=180 / KEEP_CONFIRM=120 / TTL=2000
  • セットC(攻め)
    ALIGN=50 / JOIN_WINDOW=120 / KEEP_CONFIRM=100 / TTL=1800

手順:AでKEEP≥60%→keep_wait_msで窓を詰める→p95見ながらB/Cに攻める。


ヘルス&観測コマンド(抜粋)

  • 2行ヘルス
sleep 3 && curl -s http://127.0.0.1:8019/metrics | awk '
/^consensus_join_total /{j=$2}
/^consensus_keep_total /{k=$2}
/^consensus_latency_ms_count /{c=$2}
END{printf "join=%s keep=%s keep%%=%s p95_cnt=%s\n",
(j?j:0),(k?k:0),(j? sprintf("%.1f",k/j*100):"NA"),(c?c:0)}'
  • p95(ヒスト算出)
PORT=8019; curl -s http://127.0.0.1:$PORT/metrics > /tmp/m.txt && python - <<'PY'
import re
t=open("/tmp/m.txt").read().splitlines()
B=[(float(re.search(r'le="([^"]+)"',l).group(1)),float(l.split()[-1]))
   for l in t if l.startswith("consensus_latency_ms_bucket")]
B.sort()
total=[l for l in t if l.startswith("consensus_latency_ms_count")]
print("p95=NA" if not B or not total else f"p95={[le for le,c in B if c>=float(total[0].split()[-1])*0.95][0]} ms")
PY
  • miss内訳・分布
curl -s http://127.0.0.1:8019/metrics | grep -E \
'(consensus_miss_total|consensus_eval_total|alive_kinds_per_eval_bucket|keep_wait_ms_bucket|align_ms_current)' | head -120

“稼ぐ”までのGo/No-Go(運用基準)

  • GoKEEP≥70% / p95≤350–400ms / drop≤10% / 手数料込みEV>05営業日連続、MaxDD許容内。
  • No-Go:上記いずれか未達/miss理由が増勢/Registry混線・ENV未反映が判明した場合。

今日のアクション(最短)

  1. セットAでREPLAY_MODE=OFFで再計測 → 2行ログ+keep_wait_ms_bucketのピークを共有。
  2. TTL/窓を±50msで合わせる(ピーク+α)→ KEEP%が60%台に乗るか確認。
  3. 影/実の4カウンタを配線→ drop(≤10%)を可視化。

補録:用語解説(この文脈での意味)

この文脈での用語を実装寄りにサクッと整理します。各項目は「一言で → なぜ必要 → どう測る/設定」の順です。


低ノッチ(ていノッチ)

一言で超小さい建玉で試す運用モード。EXEC_NOTIONAL_CAP_USD=50 などで上限を縛る。
なぜ:バグやモデル誤差で吹っ飛ばないように、学習中の損失を最小化するため。
どう測る/設定.env で上限/日次DD/連敗停止をセット。

EXEC_NOTIONAL_CAP_USD=50
DAILY_LOSS_LIMIT_USD=...
MAX_CONSEC_LOSS=...

影/実(かげ/じつ)

一言で影=シャドー(擬似約定)実=ライブ(実注文)
なぜ実運用の前に、同じ条件で fill のしやすさ・スリップを比較するため。
どう測る/設定:4カウンタで drop を出す。

shadow_exec_attempt_total / shadow_fill_success_total
live_exec_attempt_total   / live_fill_success_total
drop = 1 - (live_fill_rate / shadow_fill_rate)  # 目標 ≤ 10%

合議回路(K-of-N)

一言で:複数シグナルを多数決で束ねるロジック(例:K=2, N=4)。
なぜ:単独シグナルの騒音を下げ、誤発火を抑えるため。
どう測る/設定

  • JOIN(合議成立)→ consensus_join_total
  • KEEP(一定時間後の再確認成功)→ consensus_keep_total
  • 生存種数alive_kinds_per_eval ヒスト
  • ケース別consensus_eval_total{case="ltK|pair_block|join"}

2段確認ヒープ(時間依存KEEP)

一言で:JOIN後に、時間をずらして2回 KEEP確認する実装。min-heap で (confirm_at, step_idx, n_steps, key, join_ts) を保持。
なぜ次のtick頼みにせず、時間で確定させる(イベントが来なくてもKEEP判定が動く)。
どう測る/設定

  • 遅延 → KEEP_CONFIRM_MS(例 250ms)
  • 結果 → keep_wait_ms(JOIN→KEEPの待ち時間)
  • 失敗 → consensus_miss_total{reason="faded_before_keep"}(JOINごと最大1回)

FIRE/REFR(二段閾値)

一言でFIRE=発火(JOIN寄与)REFR=リフレッシュ(TTL延命)の2段しきい。
なぜ:JOINの質はFIREで担保
し、連続性はREFRで維持して fade を防ぐ。
どう測る/設定

  • 例)BBO_COLLAPSE_BP FIRE=6 / REFR=4OI_Z FIRE=1.8 / REFR=1.6
  • FIREは add_signal_to_series、REFRは refresh_signal

TTL/窓の不整合

一言でTTL_MSKEEP_CONFIRM_MS / JOIN_WINDOW_MS と噛み合わず、KEEP前にシグナルが失効する状態。
なぜ:fade が増えて KEEP% が上がらない
どう直す(ルール)

  • 必ず TTL_MS ≥ KEEP_CONFIRM_MS×2 + 100ms
  • keep_wait_ms のピーク帯を見て KEEP_CONFIRM_MS / JOIN_WINDOW_MS を合わせる
  • まずは安定セット:ALIGN=300 / JOIN=600 / KEEP=250 / TTL=2000

ALIGN依存(p95の意味)

一言で:p95(slot_start→JOIN)は理論上 ALIGN_MS を超えない
なぜ:スロットの起点からJOINまでを測る設計だから。
どう測る/設定

  • p95はヒストグラムから算出consensus_latency_ms_bucket
  • align_ms_current をメトリクスで併記
  • p95 > ALIGN が出たら、環境/Registry混線を疑って再起動

引数順ガード

一言でadd_signal_to_series(key, kind, score, ts_ms)引数入れ違い自動検知&スワップ
なぜts_msscore の逆転はJOINゼロの元凶。
どう測る/設定

  • 自動スワップ時に bad_add_signal_order_totalinc()
  • ヘルスで bad_order=0 に収束しているか確認

ペアゲート

一言で:生存種がKに達しても、指定ペアが同居していなければJOINを拒否するゲート。
なぜ脆いJOIN(ノイズ)を減らし KEEP% を上げるため。
どう測る/設定

  • ENV:REQUIRE_PAIR=1, PAIR_SET_JSON='[["trade_cluster","oi_z"], ...]'
  • 観測:alive_pair_total{a,b} をまず集計 → 上位3–5ペアを採用
  • 影響:拒否は consensus_miss_total{reason="pair_gate_blocked"} に出る
  • 段階的にON/OFF(JOINが枯れたら1ペア戻す)

使いどころの目安(クイック導線)

  • KEEP%が低い → まず TTL/窓の整合(セットA)→ keep_wait_ms で微調整
  • p95が高い/揺れるALIGN依存で設計(p95算出一本化+align併記)
  • JOINがゼロ → FIRE/REFRを差し戻し+REPLAYブート(refreshのみ)で K=2 到達率 を上げる
  • ノイズJOINが多いペアゲートを実観測準拠でON

ショート系bot 開発ログ

0) ハイライト(要点)

  • 可観測性/信頼性:Prometheusマルチプロセス集約が安定。**堅牢版 metrics-serve(WSGI, SIGTERM対応, epoch方式, ジャニター内蔵)**で即終了問題を解消。
  • メトリクスshort_req_ack_ms / short_cancel_msbucket/sum/count が multiproc で出力。metrics-sanity で集約内容を直接検査可能。
  • Circuit BreakerCLOSED→OPEN→HALF_OPEN→CLOSED の遷移を JSON永続化+CLI(error-sim, health-sim-recover)で確認。カテゴリ別運用(network/rate/auth)。
  • Canary運用:フラグ/上限/安定サンプリング(trace_id 5%)/Smoke/Status/Runbook/Recording&Alert/Preflight 一式を整備。
  • 執行:SELL post_only の致命傷価格バグを修正(best_bid以上に配置)。価格/数量量子化を導入。dry-runで req→ack ≈ 10–11ms、cancel ≈ 11ms(短時間計測)。
  • REPLAYreplay_ndjson_into_handlers.py自己完結化(同一プロセスで /metrics 提供、ScoringEngine+SyncEvaluator 通電)。
    代表値:JOIN=1143, KEEP=374 (32.7%), p95=1000ms, ltK=877, pair_block=25
  • 運用衛生:epochディレクトリ+currentシンボリックリンク方式、ジャニター(自動/手動)+mp_db_files_total/mp_stale_marked_total で見える化。

1) 時系列ログ(抜粋)

8/13

  • /metrics空問題の恒久対策の方針固め:metrics-serve 常駐metrics-diag/metrics-sanity、multiprocess対応。
  • SELL post_only 価格決定の致命傷を特定 → best_bid以上に修正。量子化関数導入。

8/14

  • ヒストグラム未出力の原因分解:PROMETHEUS_MULTIPROC_DIR 不一致/タイマー未経路/高カーディナラベルを修復。
    → ラベルを order_type, experiment に整理、client_order_id を削除
  • metrics-serve を集約レジストリで公開、writer側はHTTP公開しない。metrics-sanityで直読み検証。
    → 30回dry-runで short_req_ack_ms_* / short_cancel_ms_* が出力されることを確認。

8/15

  • Circuit Breakercircuit_manager.py(HALF_OPEN採用)+api_wrappers統合。
  • 状態永続化circuit_store.jsonfcntl.flock)でプロセス間共有。CLI を永続化対応に。
  • 古いGauge残留の恒久対策:ジャニターmetrics-janitor)+ mark_process_dead、multiprocess Gaugeモードmax / livesum)。

8/16

  • 堅牢版 metrics-serve へ差し替え:WSGIブロッキング、シグナル安全終了、epoch方式・ジャニター内蔵。即終了問題を解消。
  • Canary一式(CNR-1〜7):フラグ/上限/Preflight/Entrance Gate/安定サンプラー/Smoke/Status/Runbook/Rules まで整備。

8/17

  • REPLAY自己完結:HTTPサーバ同一プロセス化、ScoringEngine/SyncEvaluator通電、alive_signals/score取扱い修正。
  • REPLAY実測JOIN=1143, KEEP=374 (32.7%), p95=1000ms, ltK=877, pair_block=25 を /metrics で確認。

2) 領域別サマリ

可観測性/メトリクス

  • 実装:multiprocess対応、epochディレクトリrun-{ts})+current symlink、ジャニター(自動/手動, dry-run安全弁)、mp_db_files_total/mp_stale_marked_total
  • 確認metrics-sanityで短命CLIの観測サンプル数(例:short_req_ack_ms 240, short_cancel_ms 30)を集約側で確認。

Circuit Breaker

  • 実装:カテゴリ別(network/rate/auth)、HALF_OPEN、cooldown後 probe 許可、JSON永続化(壁時計)、/metrics 反映
  • CLIerror-sim(致命×NでOPEN)、health-sim-recover(probe注入→HALF_OPEN→CLOSED)。
  • 注意点:古いmpファイル残留で状態が反映されない事象 → ジャニター/epoch切替で解消。

Canary/運用ガード

  • 実装preflight、Entrance Gate、trace_id 5% サンプラー、canary-smokecanary-status、Recording/Alert、Runbook。
  • 推奨運用(整備済み情報):ratio 1–2%、max_notional 50USDT、BTCUSDT、reject<1%、p99 SLOを連続達成。

執行/性能

  • 価格決定:SELL post_only が best_bid以上で配置されるよう修正。価格/数量の量子化導入。
  • 短時間実測:req→ack ≈ 10–11ms、cancel ≈ 11ms(dry-run)。p99 SLOは24h連続は未確認

REPLAY/α検証の土台

  • 自己完結REPLAY:同一プロセスで /metrics 提供、consensus_* 系メトリクスでJOIN/KEEP/レイテンシを可視化。
  • 実測抜粋JOIN=1143, KEEP=374 (32.7%), p95=1000ms, ltK=877, pair_block=25

3) 現在の数値(代表)

  • req→ack:240サンプル / 合計≈2651ms(平均≈11.05ms)
  • cancel:30サンプル / 合計≈337ms(平均≈11.22ms)
  • REPLAYJOIN=1143, KEEP=374 (32.7%), p95=1000msltK=877, pair_block=25
  • CB:2→1→0 の往復確認(JSON永続化/メトリクスとも整合、古いmpを掃除した状態で)

4) 未完・課題の棚卸し(ログ上で残っているもの)

  • 24h連続SLOの実績未確認req→ack p99<250ms / cancel p99<300ms / reject<1% / Circuit OPEN=024h連続で満たした証跡が未取得。
  • 収益系メトリクスの本配線pnl_usdt_total / fees_usdt_total / win_rate / max_drawdown / exposure など通電・ダッシュボードが未完了(設計・名称は合意済み)。
  • EVのネット証明:手数料/滑り/FR込みの EV_bps を実トレードで連続提示する仕組みは準備中。
  • REPLAYと実運用の乖離検証:REPLAYのKEEP%と生環境の差分トラッキングの仕組み(ログ上、方針は明記済み)。

5) コマンド備忘(ログで使用頻度が高いもの)

# multiprocクリーン & serve起動(epoch方式, 例)
pkill -f metrics-serve || true
rm -rf /tmp/bsb_mp && mkdir -p /tmp/bsb_mp
PYTHONPATH=src METRICS_ENABLED=true \
python -m src.cli metrics-serve --port 9523 --mp-root /tmp/bsb_mp --mode wsgi &

# writerドライラン(ヒストグラム出力)
for i in {1..30}; do \
  PROMETHEUS_MULTIPROC_DIR=/tmp/bsb_mp/current PYTHONPATH=src METRICS_ENABLED=true \
  python -m src.cli engine-dryrun --symbol BTCUSDT --hold 1 --bypass-canary >/dev/null 2>&1; \
done

# ヒストグラム確認
curl -s localhost:9523/metrics | grep -E 'short_req_ack_ms_(bucket|sum|count)|short_cancel_ms_'

# CBドリル(OPEN→HALF_OPEN→CLOSED)
PYTHONPATH=src METRICS_ENABLED=true python -m src.cli error-sim --category network --fatal 3
sleep 16
PYTHONPATH=src METRICS_ENABLED=true python -m src.cli health-sim-recover --category network --ok 3

# ジャニター(手動, ドライラン)
PROMETHEUS_MULTIPROC_DIR=/tmp/bsb_mp/current PYTHONPATH=src \
python -m src.cli metrics-janitor --dry-run

6) Go/No-Go(ログに基づく運用基準の固定・再掲)

  • Go:Preflight合格(Testnet)/p99 SLOを24h連続満たす/アラート演習済み/Canary1–2%で reject<1%、CIRCUIT OPEN=0。
  • No-Goshort_error_total 上昇継続/CIRCUIT OPEN断続発生/p99 SLO超過>10分/日/収益メトリクス未整備で効果測定不能。

付録

「ジャニター」とは?

この文脈での“ジャニター(janitor)”は、Prometheus の multiprocess 用メトリクスファイル(*.db)を定期的に掃除する仕組みのことです。目的は、短命プロセスが残した古い Gauges などの“亡霊系列”を除去して、/metrics の値が過去に引っ張られないようにすること。

  • 実装の要点
    • 常駐版: metrics-serve 内のスレッドが、PROMETHEUS_MULTIPROC_DIR を60秒ごとに走査し、最終更新がTTL超のファイルの PID を mark_process_dead(pid) でマーク。Collectorが次回スクレイプで除去。
    • 手動版: metrics-janitor CLI(--ttl-sec, --dry-run あり)で即時掃除。
    • 安全弁: epoch 方式(run-<ts>current symlink)、自分自身のPID除外、(環境に応じて)/proc/<pid> 存在確認、例外はログに出す。
    • 監視: /metricsmp_db_files_total{type}mp_stale_marked_total を出して“掃除が効いているか”を可視化。

前提として認識している「ショート戦略ロジック」の骨子

開発ログと現在の配線から見えているコア構造です(提案ではなく、現状の前提)。

1) マーケットと運用枠

  • 市場: Bybit Perp(メイン)/Binance(バックアップ)。
  • モード: Testnet → Canary → 本番。Canary は ratio 1–2%、max_notional=50 USDT, BTCUSDT
  • 口座: One-way / Cross / 1x。
  • ガード: Preflight・Smoke・Entrance Gate(露出/比率/シンボル許可/ヘルス)・Kill Switch・Circuit Breaker(OPEN→HALF_OPEN→CLOSED, JSON永続化)。

2) シグナル層 → 合議(JOIN)→ 確認(KEEP)

  • シグナル種(kind): 価格/出来高/ボラのブレイク、OFI(成行フロー不均衡)、ティック・インバランス等(実装/候補を含む)。
  • 合議(JOIN): K=2 を基本に、時間窓で合致したら成立。
    • 時間設計:
      • ALIGN_MS(整列待ち)
      • JOIN_WINDOW_MS(K揃えの窓)※場合により EARLY/LATE 分割で「早着×2」を優先
      • KEEP_CONFIRM_MS(成立後の短期確認)
      • TTL_MS(全体の寿命)
  • メトリクス: consensus_join_total / keep_total / latency_ms_* / eval_case{join,ltK,pair_block,...} で JOIN/KEEP の質と遅延を記録。

3) 執行ポリシ(SELLショート)

  • 初弾: post_only LIMITbest_bid + n*tickceil_to_tick で量子化、best_ask を上限クリップ)。
  • 置換: ~150ms 間隔 × 最大6回(post_only 継続)。
  • IOC切替: 充足しない/200ms 経過などで IOC(taker)へ降格。
  • キャンセル目標: cancel p99 < 300ms
  • 量子化: 価格=ceil_to_tick、数量=floor_to_step。最小ロット/ティック/手数料は Instrument(spec) を単一点管理。

4) 可観測性とSLO

  • レイテンシ: short_req_ack_ms / short_cancel_ms の Histogram(multiprocess 集約)。
  • 健全性: short_error_total、Circuit 状態、reject率。
  • 運用: 24h 連続req→ack p99<250ms / cancel p99<300ms / reject<1% / Circuit OPEN=0 を満たすことを Go 条件に。
  • (収益系は通電準備中): pnl_usdt_total / fees_usdt_total / win_rate / max_drawdown / exposure / EV_bps_* をダッシュに載せる前提。

5) Canary 一式(CNR-1〜7)

  • フラグ/上限/比率PreflightEntrance Gatetrace_id 安定サンプリング(5%)Smoke(即キャンセル)StatusRecording/Alert ルールRunbook
  • 運用は「Smoke→Canary」の順で、SLOとアラート演習(Firing→Resolved)を一往復してから本運転。

6) REPLAY(自己完結)の土台

  • replay_ndjson_into_handlers.py同一プロセスで /metrics 提供しながら、ScoringEngine + SyncEvaluator を通電。
  • 代表値: JOIN=1143, KEEP=374 (32.7%), p95 ≈ 1000ms, ltK=877。**原因内訳(不足種/ペアゲート等)**がメトリクスで追える。

-Bot, 開発ログ