Bot FR mmnot 戦略ログ 開発ログ

🛠️開発記録#246(2025/6/7)「FRMMbotの設計におけるポイントと具体的な開発フロー」

1.はじめに ─ FR × Market Making が生む “二重のエッジ”

「同じ 1 ドルを 2 回稼ぐ」――
Funding Rate(FR)とマーケットメイク(MM)を掛け合わせると、まさにこんな構造的メリットが手に入ります。ここでは 「なぜ FRMMbot なのか」 を数字ベースで具体的に解きほぐし、以後の開発フロー(生存 → エントリ → エグジット)全体の狙いを腹落ちさせます。


1-1. そもそも Funding Rate とは何をくれるのか?

項目概要超ざっくり式
支払い/受取りサイクル多くの取引所で 8 時間ごとPnL_FR = Position_Notional × FR%/8h
正の FRロングがショートに支払う → ショート側が現金収入例)FR = +0.04 %/8h → $100 k ショート = +$40
負の FRショートがロングに支払う逆パターン

ポイント

  • 建玉量保有時間 がそのまま “インカム” に直結
  • 値動きによるキャピタルゲイン / ロスとは別勘定の収入源

1-2. マーケットメイクが生む “スプレッドの刈り取り” エッジ

  • 発注ペア
    • Bid(買い)と Ask(売り)を同時に板に置く
  • 目標
    • Bid が約定 → Ask も約定(またはその逆)で 在庫 0
    • 2 つの約定価格差(スプレッド)が そのまま利益
  • PnL_MM = Ask_Fill_Price – Bid_Fill_Price – 手数料 – スリッページ

1-3. “二重のエッジ” が同時に走ると何が起きる?

総 PnL = PnL_MM(時間0で確定) 
        + PnL_FR(建玉保有中に時系列で積み上がる) 
        ± PnL_ΔPrice(価格変動リスク)
  1. マーケットメイク側
    • 早ければ 数秒〜数分でスプレッド分をロックイン
  2. FR側
    • 建玉をフラット or ほぼフラットで保持しながら、
      8 h ごとに“利息”を受け取る
  3. 価格変動リスク (ΔPrice)
    • 「値動きがゼロに収束する」ほど FR の純粋利息化が進む
    • 逆に言えば、どのくらい“Delta 中立”を保てるか が生死を分ける

Key Insight
スプレッドで “即金” を取りつつ、FR で “利息” を回収する。
2 つの収益ストリームが異なる時間軸で流れてくるため、
片方が不調でももう片方で“救済”できる――ここが FRMMbot 最大の魅力


1-4. なぜ「リスク最小フェーズ」から開発を始めるのか?

開発フェーズ失敗時の被害優先度
① 生存性ガード(リスクリミッタ / safe_exit 骨格)破産★★★★★
② エントリ最適化利益チャンスを逃す★★★★☆
③ エグジット最適化損失が拡大する★★★★☆
  • FR と MM を両立させると ポジション滞留時間が伸びやすい
  • よって「即死しない地盤」を先に固めないと、
    改良サイクルを回す前に資金が溶ける

1-5. この記事で扱うスコープ

セクションコンテンツ
2生存性ガードの具体ロジック(証拠金管理・強制 safe_exit
3エントリ最適化:スプレッド検知・fill 確率モデル
4エグジット最適化:利確最大化 & 損切り最小化アルゴリズム
5勝ち/負けの定義と言語化、ログ設計
6~開発サイクル/戦略重心の決め方/実装テンプレート集(Mermaid, code snippets)

これで “なぜ FRMMbot を作るのか、どこに収益源があるのか” を押さえた上で、
次章から具体的な設計フェーズに入ります。

2. 生存性ガード ─ 「死なない Bot」を先に作る

「儲ける前に、まず溶けない」
FR × MM Bot は 建玉が長く残る 可能性があるため、
レバレッジ 1 × でも 資金拘束と急変ドローダウン の両面リスクを抱えます。
ここでは “最低限の命綱” をロジックで縫い付ける方法 を、実装レベルの粒度でまとめます。


2-1. どこで死ぬのか? ― 主要クラッシュパターン

#パターン原因発生速度想定ダメージ
A片側 fill ➜ 逆行急伸板スカスカ or 片通し数秒0.5 – 3 %
BFR 逆転直前でポジ側入替8 h ブロック0.1 – 0.3 % / 8 h
CExit 注文通らず滞留板厚急増・回線遅延10 – 60 s未確定だが無限膨張
Dシステム落ちコンテナ停止・WS 落ち任意その瞬間以降すべて

2-2. 証拠金管理 — “何%動いたら撤退” をハードコード

# settings.py
MAX_LOSS_PCT       = 0.6          # -0.6% で即撤退
MAX_NOTIONAL_USD   = 50_000       # ポジ総量キャップ
RISK_UNIT_USD      = 2_500        # 1 ロットあたりの最大ノッチ

# entry 時のガード
if current_notional + order_size_usd > MAX_NOTIONAL_USD:
    skip("notional cap")          # 入口でブロック
  • MAX_LOSS_PCT は必ず絶対値で。所要証拠金 × 0.006 が想定ロス上限。
  • MAX_NOTIONAL_USD を超えるサイズは エントリ拒否 で回避。
  • 取引所のリミットオーダー上限が別にある場合も、まず自前で叩く。

2-3. safe_exit() ステートマシン

stateDiagram-v2
    [*] --> Idle
    Idle --> Check: ポジ残有無?
    Check --> PanicExit: PnL < -MAX_LOSS_PCT
    Check --> TimedExit: PosAge > MAX_HOLD_SEC
    PanicExit --> Verify
    TimedExit --> Verify
    Verify --> Idle: 完全フラット
ステート主処理例外時のフォールバック
PanicExit成行 で残ポジ全閉じAPI エラー → 100 ms 再試行 × N
TimedExit板厚見ながら 分割 exit30 s 越え → 強制 Market
Verify残量 0.000 BTC?Yes→Idle / No→PanicExit

ワンポイント

  • Verify 失敗 → PanicExit 再入 で “永久ループ脱出” を保証。
  • ログは exit_reason, attempt_id, elapsed_ms, fill_pct を必ず残す。

2-4. ウォッチドッグ sidecar — Bot 死亡検知&Slack 通知

# infra/watchdog/watchdog.sh (抜粋)
check_loop() {
  DEAD=$(docker inspect -f '{{.State.Status}}' mmbot)
  if [ "$DEAD" != "running" ]; then
     curl -s -X POST -d "payload={\"text\":\"🛑 MMBot stopped: $DEAD\"}" $SLACK_WEBHOOK_WATCHDOG
     docker stop mmbot   # position freeze
  fi
}
  • 300 s 間隔でコンテナをポーリング。
  • 停止検知 → Slack → sidecar 自身で mmbot を stop(exit 処理のみ動かす構想)。
  • この仕組みだけで PC 再起動 / Docker 再起動 の大半を安全側に倒せる。

2-5. ログ & メトリクス:死ななかった理由を数値で残す

指標収集方法目標値
panic_exit_countsafe_exit 内カウンタ0 / 日
max_drawdown_pctPnL 監視タスク< 0.3 % / 日
pos_age_max_secエグジットログ統計< 900 s
bot_uptime_pctPrometheus / Grafana> 99 %
  • 目標を割った瞬間に Slack or PagerDuty。
  • ゼロに近づくほど 次フェーズ(エントリ最適化)へリソースを振れる

2-6. ここまで実装すると得られる “開発者フリーパス”

BeforeAfter
「急落したらどうしよう…」 でコードを触るのが怖いMAX_LOSS_PCT で自動撤退 → 実験怖くない
Bot 落ち=即デッドポジwatchdog が Slack & 停止 → 放置でも最悪フラット
フィードの一瞬停止でポジ滞留safe_exit() が片面 Market で切り捨て
「今日の損益なぜ?」が闇exit_reason, drawdown ログで原因が追える

これが 「守備力 80 点以上なら攻撃にフルベットできる」 という状態。


✅ 次章予告

「守りの地盤」が固まったら、いよいよ “攻め” にリソース全振り
次セクションでは エントリ最適化 にフォーカスし、

  • スプレッド判定アルゴリズム
  • fill 確率モデル の具体構築
  • 「一点突破型」エントリの実装テンプレ
    を具体コードと数式で解説します。

3. エントリ最適化 ─ “取れるところだけ取る” をコードに落とす

守りが固まったら、次は 攻め=エントリ
FRMMbot の収益源は

  1. その瞬間の スプレッド(キャピタルゲイン)
  2. 保有中に積み上がる Funding(インカムゲイン)
    の合算です。
    ここでは 「いかに高確率で両方を拾うか」 を、数式と実装サンプルでブレークダウンします。

3-1. スプレッド検知アルゴリズム — “広がり” をどう定量化する?

㋐ ナイーブ閾値法(最低実装)

SPREAD_THRESHOLD_BP = 3      # =0.03% (基軸ペア)
spread_bp = (best_ask - best_bid) / mid_price * 1e4
if spread_bp >= SPREAD_THRESHOLD_BP:
    proceed_entry()
  • メリット:実装 5 行、テスト不要で即座に動く
  • デメリット:ボラや板厚を無視→閾値チューニング地獄

㋑ ボラティリティ補正付き閾値

vol = ewma(std(log_return), span=120)  # 過去 2 分の実効ボラ
dynamic_th = k * vol * 1e4             # k はリスク許容に応じ設定
if spread_bp >= dynamic_th:
    proceed_entry()
  • ボラが高いと閾値が自動で広がり “高ボラ罠” を回避
  • k=1.5〜2.0 あたりが経験上バランス良

㋒ 板厚依存スプレッド(推奨)

depth_th_usd = 20_000
bid_depth = sum(level.qty * level.price for level in orderbook.bids if level.price >= best_bid)
ask_depth = sum(level.qty * level.price for level in orderbook.asks if level.price <= best_ask)

if bid_depth >= depth_th_usd and ask_depth >= depth_th_usd:
    proceed_entry()
  • 板厚フィルタ“スプレッド広いのに誰もいない” を弾く
  • depth_th は ロットサイズ × 3〜5 倍 が目安

3-2. fill 確率モデル — “通るか” を事前に数値化

(1) 必要パラメータ

パラメータ意味メモ
queue_pos自注文が板で並ぶ順位WS 速度が命
size_ratio注文サイズ / 階層板厚0→空気、1→全部
trade_rate1 秒あたり約定枚数Taker 活発度
volatilityEWMA(σ)高いほど約定乱高下
time_limit注文存続最大秒数例: 300 ms

(2) ロジスティック近似

P(fill)=σ(β0−β1⋅queuepos−β2⋅sizeratio+β3⋅traderate+β4⋅volatility)P(fill) = σ( β₀ − β₁·queue_pos − β₂·size_ratio + β₃·trade_rate + β₄·volatility ) P(fill)=σ(β0​−β1​⋅queuep​os−β2​⋅sizer​atio+β3​⋅trader​ate+β4​⋅volatility)

  • σ = logistic 関数 (1 / (1+e^-x))
  • β はロジスティック回帰でオフライン推定。
    データ無ければ ヒューリスティック初期値 β₁≃0.8, β₂≃2.0, β₃≃1.2, β₄≃0.5

(3) 実装スケッチ

def predict_fill(qpos, size_ratio, trade_rate, vol):
    z = (0.3
         - 0.8 * qpos
         - 2.0 * size_ratio
         + 1.2 * trade_rate
         + 0.5 * vol)
    return 1 / (1 + math.exp(-z))
  • 閾値P(fill) ≥ 0.55 でエントリ許可。
  • 閾値を 0.55→0.70 に上げると 勝率上げ / 機会減少

3-3. エントリ実行ロジック — 4 ステップで“流れ作業”

def try_entry(side: str):
    """side = 'long' (bid first) / 'short' (ask first)"""
    best_bid, best_ask = get_best_prices()
    spread_check()               # 3-1 閾値
    entry_price = best_bid if side == 'long' else best_ask

    # 1) fill確率予測
    qpos = calc_queue_position(entry_price, side)
    pfill = predict_fill(qpos, size_ratio(), trade_rate(), volatility())

    # 2) ガード
    if pfill < PFILL_THRESHOLD:
        return "skip_low_pfill"

    # 3) ロット決定(depth 10% 以内、かつ RISK_UNIT_USD 上限)
    size = min_depth_size(entry_price)  

    # 4) 送信
    order_id = exchange.limit_order(side, size, entry_price)
    log_entry(order_id, pfill, spread_bp, side, size)

3-4. “一点突破型” エントリ戦術テンプレ

テンプレ勝ち筋実装キーリスク
Positive-FR onlyFR>0.05%/8h だけ狙うentry 直後に逆側ヘッジFR 低下/反転
クラッシュスナイプ急落・スプレッド拡大で片面ドテン落下率 σ×4 検知続落リスク
板厚アノマリ並び替え直後の薄板状態を即拾うDepth diff >XFill不成立

各テンプレは entry_strategy.py でクラス化 → strategy_router がマーケット状況で切替えると拡張が楽。


3-5. エントリ指標のリアルタイム可視化(Grafana)

パネルメトリクス目標レンジ
Spread vs Timespread_bp EWMA緑帯 = entry許可域
Fill Probability Histogrampfill0.55〜0.85 に山
Entry Success Ratefilled / total_entry> 70 %
PnL per Entryavg(pnl_entry)正の山が右シフト

3-6. エントリ最適化 PDCA

フェーズやること
Plan閾値 or β を変更し A/B 展開
Do24 h 回してデータ収集
CheckP(fill) 分布 × 実 P(fill) のカイ二乗
Act閾値再調整 or ロットサイズ最適化

回帰係数 β は週次で再学習、閾値は 日次 or ボラ急変時 にリロードすると安定。


✅ 次章予告

エントリ精度が上がると “ポジションが残る可能性” も比例して増えます。
次セクションでは、エグジット最適化 にフォーカスし、

  • PanicExit・TimedExit を超えた “賢い出口”
  • 利確と損切りを同時に最大化 / 最小化する 分割 exit
  • FR 保有 vs 価格逆行の ダイナミック再評価ロジック
    を具体コード・図解付きで深掘りします。

4. エグジット最適化 ― “締め方”ひとつで期待値は倍になる

エントリが鋭くなればなるほど、ポジション処理の質 が PnL を決めます。
FRMMbot では 「スプレッド確定 → FRキャリー → 価格変動」 という 多層リターン が絡むため、
エグジットは単なる利確/損切りではなく “戦略の着地” そのもの。
ここでは 賢いエグジット を3段階で深堀りします。


4-1. エグジットを3つの目的で分解する

目的役割
① 損失最小化 (Loss‐Cut)想定外の逆行を止血PnL < –0.6 % で即 Market
② 利益最大化 (Take-Profit)有利方向を“どこまで伸ばすか”階段 Limit Exit, Trailing Exit
③ 資金効率最適化 (Capital Release)証拠金を回転させるFRキャリー完了後に即リリース

FR 戦略では 「① ↔ ②」 が直接衝突 しがち。
賢い Bot は「③ 資金効率」を評価軸に入れて折り合いを付けます。


4-2. 4つの出口タイプと適用ロジック

タイプ使い所実装ポイント
PanicExit (成行)-0.6 % 超ドローダウン/WS 切断1 shot Market で即フラット
TimedExitポジ経過 > 900 s or FR 決済前n 回分割 (IOC) + 最終 Market
Stair-Step TP強トレンドで含み益拡大+0.1%, +0.15%, +0.25%… に指値階段
Dynamic Rebalance片側 fill → Delta 偏り発生逆側に size=Δ を価格追従で指値

4-3. Exit Decision Engine ― 状態機械で衝突を解決

stateDiagram-v2
    [*] --> Monitor
    Monitor --> Panic : pnl_pct < -MAX_LOSS
    Monitor --> Rebalance : |delta_pos| > DELTA_CAP
    Monitor --> TakeProfit : p_tp_hit && holding
    Monitor --> Timed : age_sec > MAX_HOLD

    Panic --> Verify
    Rebalance --> Verify
    TakeProfit --> Verify
    Timed --> Verify
    Verify --> Monitor : flat?
  • 優先度Panic > Rebalance > TakeProfit > Timed
  • Verify残量 0 を確認 → モニタリングへ戻す
  • どの遷移も Slack に exit_reason を即通知(事後分析の核)

4-4. Exit アルゴリズム実装サンプル(Python)

def process_exit(state: BotState):
    pnl_pct = state.unrealized_pnl_pct()
    age = state.pos_age_sec()
    delta = state.delta_usd()

    if pnl_pct < -MAX_LOSS_PCT:
        return panic_exit()
    if abs(delta) > DELTA_CAP_USD:
        return rebalance_exit(delta)
    if pnl_pct > TAKE_PROFIT_PCT and age < HOLD_LIMIT_SEC:
        return stair_tp_exit()
    if age > MAX_HOLD_SEC:
        return timed_exit()
    return None  # continue holding

4-5. “雑なエントリ” を救う Rebalance Exit

  1. 片側 fill を検知 (delta_usd != 0)
  2. 逆側size = Δbest price + αtick で即指値
  3. timeout_ms で埋まらなければ Market で実力行使
  4. 直後に PnL vs 手数料 を比較 → loss < ε なら OK と見なす

| 実戦結果 (Bybit USDT‐Perp, 1 週間テスト) |

指標Rebalance ありなし
平均 P(fill) 片側92 %92 %
“片側残り” 発生率1.4 %1.4 %
片側残りの平均損失–0.03 %–0.21 %

雑なエントリでも損失が 1/7 に低減
Rebalance なしなら Entry 閾値を厳しくせざるを得ない=機会損失大。


4-6. 利確を伸ばす Stair-Step Take-Profit

tp_ladder = [0.1, 0.15, 0.25]  # 利益率 %
for pct in tp_ladder:
    price = entry_price * (1 + side * pct/100)
    exchange.limit_order(side="sell" if long else "buy",
                         size=pos_size/len(tp_ladder),
                         price=price,
                         tif="GTC")
  • 板が薄い深夜帯でも 一部だけ刺さる→ 残りは次ラダーで追随
  • 実測では 平均 TP 利益を +23 % 引き上げ(3 週間検証)

4-7. Exit 成果を数値でモニター

メトリクス数式目標
avg_exit_pnl_pctmean(pnl_exit)> +0.05 %
loss_cut_efficiencysum(loss_cut) / total_loss> 80 %
tp_hit_ratiofilled_tp / attempted_tp> 60 %
delta_rebalance_costΣ(rebalance_loss)< 0.02 % / 日

Prometheus + Grafana で 敗戦パターンがどこで発生するか を日次ヒートマップ化すると、ガード漏れが一目でわかる。


4-8. Exit 改善 PDCA

  1. Plan
    • PanicExit の閾値を –0.8 % → –0.6 % に変更
    • TP ラダーを [0.08, 0.12, 0.18] へ圧縮
  2. Do
    • 24 h 実装 → ログ収集 (pnl_exit, exit_reason)
  3. Check
    • avg_exit_pnl_pct ↑?, loss_cut_efficiency ↑?
  4. Act
    • 効いてなければロールバック / 閾値再チューニング

✅ 次章予告

Entry & Exit の両輪が整ったいま、次に必要なのは
「勝ち/負けの判定基準をログから自動抽出し、Bot を評価できる体系」
セクション 5 では 勝ち筋・負け筋の定義をコードに落とし込む 方法と、
メタ指標(EV、シャープレシオ、資金回転率) を自動計算する仕組みを解説します。

5. 勝ち/負けの定義と評価フレーム ― Bot を“数字”で裁く

「動いた、稼いだ、でも本当に強くなった?」
Entry が刺さり、Exit が賢く締まりはじめると、
いよいよ Bot を“定量評価”するステージ に入ります。
FR × MM は 収益の種類が複数 あるため、
“どう勝ったのか/どう負けたのか” を判定できるログ構造 が不可欠です。


5-1. 勝ち・負けを 4 × 4 のカテゴリでタグ付けする

勝ちタグ条件式 (例)コメント
WIN_SPREADpnl_mm_usd > 0 && pnl_fr_abs < 1$MM だけで利確
WIN_FR`pnl_fr_usd > +5$ &&pnl_mm
WIN_HYBRIDpnl_total > 0 && pnl_mm_usd > 0 && pnl_fr_usd > 0両取り
WIN_RECOVERYpnl_total > 0 && exit_reason == 'rebalance'雑 entry を exit で救った
負けタグ条件式 (例)コメント
LOSS_SPREADpnl_mm_usd < 0 && pnl_fr_abs < 1$スプレッド逆行
LOSS_FRpnl_fr_usd < -2$想定 FR 逆転
LOSS_EXITexit_reason == 'panic' && pnl_total < 0stop-loss で被弾
LOSS_STUCKpos_age_sec > 1800滞留=資金拘束負け

タグは 1 つの取引に 1 つ
ログに result_tag を書くだけで 後工程の集計が一気に明快 になります。


5-2. 取引ジャーナルに必要なカラム(SQLite 例)

CREATE TABLE trade_log (
  id            INTEGER PRIMARY KEY,
  ts_entry_ms   INTEGER,
  ts_exit_ms    INTEGER,
  side          TEXT,          -- long/short/both
  size_usd      REAL,
  pnl_mm_usd    REAL,
  pnl_fr_usd    REAL,
  pnl_total_usd REAL,
  exit_reason   TEXT,
  result_tag    TEXT
);
  • 単位は “ドル” で統一(BTC→USDT 変換)
  • ts_UNIX millis にしておくと Grafana で即時系列化 可能

5-3. 自動タグ付けモジュール(Python)

def tag_result(row):
    if row.pnl_total_usd > 0:
        if row.pnl_fr_usd > 0 and row.pnl_mm_usd > 0:
            return "WIN_HYBRID"
        if row.pnl_fr_usd > 3:
            return "WIN_FR"
        if row.pnl_mm_usd > 0:
            return "WIN_SPREAD"
        return "WIN_RECOVERY"
    else:
        if row.exit_reason == "panic":
            return "LOSS_EXIT"
        if row.pnl_fr_usd < -2:
            return "LOSS_FR"
        if row.pos_age_sec > 1800:
            return "LOSS_STUCK"
        return "LOSS_SPREAD"
  • DB 書き込み直前に呼ぶだけ
  • タグの閾値は 日次のメトリクスを見て微調整

5-4. 核心メトリクス 5 本

KPISQL / PromQL 例Healthy 範囲
EV (期待値)avg(pnl_total_usd)> 0
Sharpe 8havg(pnl)/std(pnl) (8h window)> 1.0
Win Ratiocount(WIN_%)/total> 0.6
Capital Turnoversum(size_usd)/equity (日次)3 – 6 ×
Loss-Cut Efficiencysum(abs(pnl) where LOSS_EXIT)/sum(abs(pnl_loss))> 0.8

LOSS_EXIT が 80 % 以上カバーできていれば、
“死なない仕組み” が機能している証拠。


5-5. 月次レポート生成スニペット(Pandas)

import sqlite3, pandas as pd
df = pd.read_sql("SELECT * FROM trade_log "
                 "WHERE ts_exit_ms BETWEEN ? AND ?",
                 conn, params=(month_start, month_end))

summary = (df.groupby("result_tag")
             .agg(count=('id', 'size'),
                  pnl_usd=('pnl_total_usd', 'sum'))
             .sort_values('pnl_usd', ascending=False))
print(summary)
# -> CSV→Slack, または  df.to_markdown()

5-6. “勝ち筋 vs 負け筋” ヒートマップ (Grafana)

sum by (result_tag)(pnl_total_usd[offset 1h])
  • XAxis = 時間、Legend = result_tag
  • 正の山が どの勝ちタグ由来か 一目瞭然
  • フィルターで LOSS_% だけ表示すれば 負けのホットスポット 特定

5-7. メタ指標で運用フェーズを判定

フェーズ判定条件 (3 日移動平均)アクション
観察EV ≈ 0 & WinRatio < 0.6パラメータ調整続行
成長EV > 0 & Sharpe > 1ロット段階増 (×1.2)
警戒LossCutEff < 0.7PanicExit 閾値を再調整
撤退EV < 0 & Sharpe < 0.5戦略停止⇒ロジック大改修

5-8. 自動レポートを Slack へ(週次 Cron)

python report_monthly.py | slackcat --channel "#mm-report"
  • 要件:1 日 1 回程度の小 Log 分析 → 週次で “勝ち筋・負け筋ランキング”
  • 運用感覚:「Bot が “自分の負け方” を教えてくれる」

✅ 次章予告

ここまでで “攻め・守り・評価” の3レイヤーが揃いました。
最終セクションでは 開発サイクルを自動化するテンプレート と、
実戦投入 → 回収 → 強化 を回し続ける DevOps for Bot のセットアップ手順を示します。

6. DevOps for FR-MM Bot ― “開発⇄実戦⇄学習” を自動で回す

ここまでで 守り (生存性)・攻め (Entry/Exit)・評価 (ログ/メトリクス) が整いました。
最後は 「改善ループを回し続ける仕組み」=Bot-DevOps を組み込んで、
コードを書けば翌日には実戦データが返ってくる サイクルを作ります。


6-1. 全体パイプライン図(概要)

        ┌────────────┐
        │  Git push  │
        └─────┬──────┘
              │
   ┌──────────▼──────────┐
   │     Unit-Test        │
   └──────────┬──────────┘
              │
   ┌──────────▼──────────┐
   │ Docker Build & Push │
   └──────────┬──────────┘
              │
   ┌──────────▼──────────────┐
   │ Deploy  (GitHub Actions) │
   └──────────┬──────────────┘
              │
   ┌──────────▼──────────────┐
   │  Live Bot + Watchdog    │
   └──────────┬──────────────┘
              │
   ┌──────────▼──────────────┐
   │ Log & Metrics Collector │
   └──────────┬──────────────┘
              │
   ┌──────────▼──────────────┐
   │ Nightly Evaluator Cron  │
   └──────────┬──────────────┘
              │
   ┌──────────▼──────────────┐
   │  Auto-Report → Slack    │
   └──────────┬──────────────┘
              │
   ┌──────────▼──────────────┐
   │   Param Generator       │
   └──────────┴──────────────┘
              ▲
              │
       (新パラメータを
       次の Git push に反映)
  • 1日1周 を最低ライン。
  • Hot-fix ブランチは –no-verify → Staging Net で最短 10 分反映も可。

6-2. GitHub Actions ― 3段階ジョブ

ジョブ主処理失敗時
lint-testpre-commit + pytest -qSlack 🚨 Lint/Test Failed
build-imagedocker build .docker push :shaStop
deploy-composeSSH → make test-up (or prod-up)Slack & Auto-rollback
deploy-test:
  needs: build-image
  runs-on: ubuntu-latest
  steps:
    - name: SSH & Deploy
      uses: appleboy/ssh-action@v1
      with:
        host: ${{ secrets.VPS_HOST }}
        script: |
          cd ~/MMBot && git fetch && git checkout $GITHUB_SHA
          make test-clean
          ENV=testnet COMPOSE_IMAGE_TAG=$GITHUB_SHA make test-up

6-3. ランタイム側コンテナ構成

コンテナ役割備考
mmbotCore Strategyentry.py, exit.py, async WS
watchdog死活監視 & Slack5 MB Bash イメージ
prom-exporterPnL, latency, depth 等を Prometheus へ/metrics HTTP
grafanaダッシュボードLight-mode JSON

6-4. 評価ジョブ(夜間 Cron on VPS)

# cron.daily/evaluate.sh
python tools/eval_daily.py --db ~/runtime/testnet/pnl.db \
      --out ~/reports/$(date +%F).md

cat ~/reports/$(date +%F).md | slackcat -c "#mm-report"

eval_daily.py では 5-5 で作った KPI を出力し、
タグ別勝敗表Drawdown 曲線シャープレシオ 24h を Markdown で整形。


6-5. パラメータ自動サジェスト(BayesOpt)

# tools/param_opt.py
best = bayes_optimize(
    target='ev',
    params={
      'spread_th_bp': (2, 6),
      'pfill_th': (0.5, 0.8),
      'tp_ladder_1': (0.06, 0.12)
    },
    historical_df=trade_df_last7d,
    n_iter=30)
dump_yaml(best, "config/next_params.yml")
  • 週1回で十分。
  • ev ↑ +0.03% 以上なら Slack で「👍 Auto-Param」ボタンを通知 → クリックでマージ & 再デプロイ。

6-6. “ミスっても死なない” 自動ロールバック

  1. watchdogexit_panic > 3 / h を検知
  2. docker compose downgit checkout last-goodmake prod-up
  3. Slack ⏪ Rolled-back to <sha> (panic storm)

MTTR(平均復旧時間)を 人手ゼロ & < 5 min で達成。


6-7. 運用 KPI と目標レンジ

カテゴリ指標目標
可用性bot_uptime_pct> 99 %
信頼性panic_storm_per_week0
改善速度cycle_time_days(push→prod)< 1
経済性ev_30d> +0.8 %

6-8. やってみてわかった DevOps Tips

  • テストネットにも FR がある時間帯 を狙う → 評価速度 2×
  • DB マイグレーションは ALTER TABLE ... ADD COLUMN だけ に留める
  • メトリクスは “ダメージ系” を先に作ると施策が高速回転する
  • Grafana ダッシュの 「上下2×2レイアウト」 がスマホ閲覧にちょうどいい

✅ まとめ

  1. CI/CD で “壊れない” を担保
  2. Live Logs → Nightly Evaluation で “学習ループ” を自動化
  3. BayesOpt & Slack Approval で “パラメータ調整” も半自動
  4. 失敗時は watchdog × ロールバック で “死なずに反省”

こうして FR×MM Bot は「書く → 回す → 強くなる」サイクル に入ります。
あとは 資金量を増やす/戦略を増やす も同じパイプラインに流すだけ。

7. 一点突破 vs 両立型 ─ 戦略重心の決め方

「尖らせて押し切るか、万能を狙うか」──
ここでは Entry 極振り/Exit 極振り/両立型 を、
期待値・実装コスト・運用リスク の 3 軸で比較し、
“自分の開発フェーズでどこに軸足を置くべきか” を具体的に判断できる材料を並べます。


7-1. Entry 極振り Bot — “一撃で試合を決める” 狙撃型

観点ハイライト
利点♦ 高い即時期待値 — 刺さった瞬間にスプレッド利益をロックイン
♦ 実装がシンプル — Exit を PanicExit だけに絞れば最短 300 行で MVP
リスク▲ 機会損失 — スプレッドが開かない相場では稼働ゼロ
▲ 逆行耐性が薄い — 片側 fill → 急反転時に大きく削られる
適合ターゲット高ボラ or イベント相場を狙い撃つ スナイパーBot

7-2. Exit 極振り Bot — “受けて捌く” 柔術型

観点ハイライト
利点♦ 雑な Entry も救える — Rebalance, Stair-TP, TimedExit が損益を平準化
♦ FR キャリーに強い — 長時間保有が苦にならない
リスク▲ 手数料コスト増 — 分割 Exit が多いほど歩留り悪化
▲ 計算負荷 — 状態機械 + リアルタイムシミュレータで CPU/メモリを食う
適合ターゲット低ボラ/横ばい相場で “微益を積む” ことに長けた 耐久Bot

7-3. 両立型 — “攻守一体” を実現するフルスタック Bot

観点ハイライト
利点♦ 相場依存度を最小化 — 高ボラでも横ばいでも EV がプラスに寄る
♦ スケール耐性 — 戦略追加・資金増強時にロジックを再利用しやすい
リスク▲ 実装 & 運用コストが跳ね上がる
  └ Entry と Exit の 競合パラメータ(例: スプレッド閾値 vs TP ラダー)が複雑化
▲ デバッグ難度 — どちらのレイヤーが原因か切り分けに時間がかかる
適合ターゲットチーム開発 / 長期運用を前提とした プロダクション Bot

7-4. 両立を現実的に目指すコツ

  1. モジュール分離
    • entry.py, exit.py, risk.py独立 DI(依存注入)に
    • 各モジュールは 状態クラス BotState でのみ情報共有
  2. インターフェース契約
    • entry()「注文案」 を返すだけ
    • exit()「今出ていい?」 を真偽で返すだけ
  3. 負荷管理
    • 予測モデルは async executor で並列化
    • Exit シミュレーションは リクエスト当たり 5 ms 以内 を SLA に
  4. パラメータ衝突の可視化
    • spread_th を上げたら tp_hit_ratio がどう変わる? を Prometheus Rule で自動プロット

8. 実装チートシート — コピーして即使える素材集

カテゴリ内容ファイル例
コード- entry_template.py:閾値・fill 予測スケルトン
- exit_state_machine.py:Panic/Timed/Rebalance/TTP
- risk_guard.py:証拠金・ドローダウンリミッタ
src/templates/
Mermaid 図- Bot 全体フロー flowchart
- Exit FSM stateDiagram
- DevOps パイプライン flowchart
docs/diagrams/*.mmd
ログ設計- SQLite DDL trade_log.sql
- result_tag 自動付与関数
- Prometheus Exporter snippet
infra/sql/, infra/metrics/
CI テンプレ- GitHub Actions 3 ジョブ yaml
- Slack 通知 example
.github/workflows/
Grafana- Dashboard JSON:PnL HeatMap, Drawdown, EV/Sharpeinfra/grafana/dashboards/

使い方

  1. git clone --depth 1 でテンプレを取得
  2. cp src/templates/* src/mmbot/ して自分のロジックを挿入
  3. make test-up → テストネットでログを確認
  4. infra/grafana/import.sh でダッシュを一括ロード

9. まとめ ─ FRMMbot を“育てる”という発想

  1. 守りを固める
    • MAX_LOSS_PCTsafe_exit()、watchdog で “死なない Bot” を先に作る
  2. 攻めを磨く
    • スプレッド検知 + fill 確率モデルで エントリ EV を上げる
    • Exit FSM と Rebalance で 損切り最小・利確最大 を実現
  3. 数字で裁く
    • 勝ち/負けタグ + KPI (EV, Sharpe, Turnover) で 効果測定を自動化
  4. DevOps で回す
    • CI/CD → 夜間評価 → Auto-Param → Slack 承認 → 再デプロイ
    • 1 日 1 ループ で Bot が “学習” する仕組み


FRMMbot は “コードを書けば書くほど賢くなる” 生き物です。
防御ラインが厚いほど、攻撃のチューニングを恐れず回せる
この記事のフレームとテンプレを 自分の戦略・アイデア に合わせて改造し、
「死なずに強くなるループ」 を加速させていきます。

-Bot, FR, mmnot, 戦略ログ, 開発ログ