今回は“机上戦→実戦移行”のレバーに手がかかったかなという感じです。
特にオンチェーン系は「怖いから止まる仕組みを作った」ので、今後もスモールステップを意識して開発を進めていきます。
MMbot 開発ログ
0. 目的と現状サマリー
- 目的:Binance Japanでのマーケットメイクによる安定収益化
- 達成度(稼ぐ観点):40% ±5%
- 土台(可観測性・接続・レイテンシ計測)は強化済み。
- 残りは α検証→Paper→最小Live の収益化フェーズと、自動停止の実戦確認。
- 最新実測
/metrics
:GET 200(HEADは405仕様、正常)mm_lat_total_agg_seconds_bucket
:20行出力(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_bucket
が 20行(秒ヒストグラム) - Prometheus:スタック起動済み。次の確認:
up{job="mm-bot"} == 1
mm_lat_total_agg_seconds_bucket
の series > 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–48h:
EV>0
,fill≥35%
,reject≤3%
- 最小Live 1日:ネットPnL ≥ 0、停止条件が一度以上正常発火
- Paper 24–48h:
5. P/L分解(ダッシュボード方針)
最上段に “日次EV(Go/No-Go)” を配置。下段に分解の4枚:
- Spread PnL + Rebate
- Adverse Selection(mid移動×注文方向)
- Slippage / Fees(実効コスト)
- 実行品質:Cancel RTT p95/p99, Fill率, Reject率
6. 主要な修正・決定ログ(要点)
prometheus-client
を Poetry→Docker に確実反映(venv混線回避、system site-packages運用)- REG不一致 を解消(Exporter/定義をREGで統一)
- 二重登録 回避のため Monitor をシングルトン化(重複登録エラー対策)
- Runner の属性名・初期化順を統一(
self.cfg
/self.inventory
/adapter.connect()
先行)
7. リスク / 自動停止(設計→演習へ)
- 停止:
p99>250ms(2m)
/reject>3%
/日次DD>1R
→ disable_live - 再開:
p99<200ms(5m)
&reject<2%
- Alert運用:
- Warning:
p99>250ms(5m)
& ObsRPS≥0.10 - Critical:
p99>350ms(1m)
orp99>300ms(5m)
& ObsRPS≥0.10 - Alertmanager Webhook or サイドカーで
/emit disable_live
連動
- Warning:
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/query
:mm_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。
やはり、過度に欲張らずに「最初の1ドルを取りに行く」つもりで開発をした方が良いな。そこからスケールさせるようにした方が結果的に余分な作業が減る。
— よだか(夜鷹/yodaka) (@yodakablog) August 16, 2025
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.sh
(flockで二重起動防止、exec_log.jsonl
に固定スキーマ追記) - ラダー:
ladder_ok.py
(30分窓でTAP≥46=0判定、exit code), ラダー1本後は実行ラインを46に復帰 - 不足可視化:
need_cf.py
(need_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(現行)
- トリガ:
decide_exec.py
→preok.sh DEX_Q CEX_BOOK(ok=True)
NO-GO時はneed_cf.py DEX_FILL MID
で不足数値化 - 実行:
go_one.py DEX_Q DEX_FILL CEX_BOOK CEX_FILL MID
→tail -n1 data/exec_log.jsonl
- ラダー:
ladder_ok.py && go_one.py …
(1本だけ/終了後は実行ライン46へ) - 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本:
ladder_ok=True
かつpreok.sh ok=True
(=pre-net≥+10)を満たす瞬間だけ実行- 目安:CEX_BID ≥ need_cex_fill_min(
scripts/need_cf.py DEX_FILL MID
)
- 目安:CEX_BID ≥ need_cex_fill_min(
- 5本集めて即判定:合格なら継続/未達なら 実行ライン±1bps(通知38は据え置き)
- ログ確認:
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 条件付き)。- 実行するか否かを最終判断する“人側の安全弁”。過剰エントリを防ぐ。
使い方
- TAP ≥ 46bps(=人間ゲートを超えた通知)
- かつ 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-net =
gross_bps − fees_bps − slip_bps − fx_bp
- BE(損益分岐) ≈
fees_med + slip_med
(現状 ≈ 23 bps)- 目安:pre-net ≥ +10 bps ⇔ gross ≥ 33 bps 目安
例(mid≈190, DEX_FILL=190.16)
- gross≥33bpsに必要な価格差 ≈
0.627 USDT
→ CEX_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→時間依存KEEP(2段確認ヒープ+
keep_wait_ms
計測)。 - SLO:
consensus_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種:25
→ ltK(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)
- TTL/窓の整合(即効)
- まずセットAで固定:
ALIGN=300 / JOIN_WINDOW=600 / KEEP_CONFIRM=250 / TTL=2000
- ルール:TTL ≥ KEEP×2 + 100ms。**KEEP% 60–75%**を先に確保。
- まずセットAで固定:
- keep_wait_ms 分布→KEEP窓微調整
- ピーク+αで
KEEP_CONFIRM_MS
を調整(±50ms単位)、JOIN_WINDOW_MS
も追従。
- ピーク+αで
- drop 計測ON(影/実4カウンタ)
shadow/live_exec_attempt_total
とshadow/live_fill_success_total
→drop = 1 - (live/shadow)
- 目標 ≤10%。
- ペアゲート再導入(段階)
- OFFで
alive_pair_total
を観測→トップ3–5をPAIR_SET_JSONに注入→ON→KPIを二分探索。
- OFFで
- αトリガの見直し(頻度差の是正)
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(運用基準)
- Go:
KEEP≥70%
/p95≤350–400ms
/drop≤10%
/ 手数料込みEV>0 を5営業日連続、MaxDD許容内。 - No-Go:上記いずれか未達/miss理由が増勢/Registry混線・ENV未反映が判明した場合。
今日のアクション(最短)
- セットAでREPLAY_MODE=OFFで再計測 → 2行ログ+
keep_wait_ms_bucket
のピークを共有。 - TTL/窓を±50msで合わせる(ピーク+α)→ KEEP%が60%台に乗るか確認。
- 影/実の4カウンタを配線→ drop(≤10%)を可視化。
補録:用語解説(この文脈での意味)
この文脈での用語を実装寄りにサクッと整理します。各項目は「一言で → なぜ必要 → どう測る/設定」の順です。
bot開発の本筋とは全然関係ないのだけれど、開発ログをChatGPTに生成させる過程で色々と不可解な言葉が出てくるの興味深い。
表現しようとしていることは分からないでもないが、中には「ん?私の認識している概念とズレているな」というものも散見される。
結局自分で勉強するしかないのです。 pic.twitter.com/7taUXWEruk
— よだか(夜鷹/yodaka) (@yodakablog) August 16, 2025
低ノッチ(ていノッチ)
一言で:超小さい建玉で試す運用モード。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=4
、OI_Z FIRE=1.8 / REFR=1.6
- FIREは
add_signal_to_series
、REFRはrefresh_signal
。
TTL/窓の不整合
一言で:TTL_MS
が KEEP_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_ms
と score
の逆転はJOINゼロの元凶。
どう測る/設定:
- 自動スワップ時に
bad_add_signal_order_total
をinc()
- ヘルスで
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_ms
の bucket/sum/count が multiproc で出力。metrics-sanity
で集約内容を直接検査可能。 - Circuit Breaker:CLOSED→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(短時間計測)。
- REPLAY:
replay_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 Breaker:
circuit_manager.py
(HALF_OPEN採用)+api_wrappers
統合。 - 状態永続化:
circuit_store.json
(fcntl.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 反映。
- CLI:
error-sim
(致命×NでOPEN)、health-sim-recover
(probe注入→HALF_OPEN→CLOSED)。 - 注意点:古いmpファイル残留で状態が反映されない事象 → ジャニター/epoch切替で解消。
Canary/運用ガード
- 実装:
preflight
、Entrance Gate、trace_id 5% サンプラー、canary-smoke
、canary-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)
- REPLAY:
JOIN=1143
,KEEP=374 (32.7%)
, p95=1000ms、ltK=877
,pair_block=25
- CB:2→1→0 の往復確認(JSON永続化/メトリクスとも整合、古いmpを掃除した状態で)
4) 未完・課題の棚卸し(ログ上で残っているもの)
- 24h連続SLOの実績未確認:
req→ack p99<250ms / cancel p99<300ms / reject<1% / Circuit OPEN=0
を24h連続で満たした証跡が未取得。 - 収益系メトリクスの本配線:
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-Go:
short_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>
存在確認、例外はログに出す。 - 監視:
/metrics
にmp_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 LIMIT
を best_bid + n*tick(ceil_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)
- フラグ/上限/比率、Preflight、Entrance Gate、trace_id 安定サンプリング(5%)、Smoke(即キャンセル)、Status、Recording/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
。**原因内訳(不足種/ペアゲート等)**がメトリクスで追える。
【個人で刺す勝ち筋メモ】
1、“速度戦”を避ける
2、コストと露出を数字で締める
3、勝てる時間・銘柄でαを積む— よだか(夜鷹/yodaka) (@yodakablog) August 16, 2025