FX Bot のドローダウン管理と自動停止の仕組み


はじめに

自動売買 Bot で最も重要なのは「勝つこと」ではなく「大負けしないこと」です。

どんな優秀な戦略でも、相場環境の変化で連敗することがあります。そのとき Bot が暴走して資金を溶かさないよう、多層のリスク管理 を実装しています。

この記事では、私の Bot に実装しているドローダウン管理の全容を公開します。

リスク管理の全体像

┌─────────────────────────────────────────┐
│ Layer 4: 取引所側 (OCO注文)              │ ← Bot停止でもSL執行
├─────────────────────────────────────────┤
│ Layer 3: 最大ドローダウン制限 (10%)       │ ← 全ポジション強制決済
├─────────────────────────────────────────┤
│ Layer 2: ボラティリティガード             │ ← 異常相場で取引停止
├─────────────────────────────────────────┤
│ Layer 1: ポジションサイズ管理 (2%ルール)  │ ← 1トレードの損失上限
└─────────────────────────────────────────┘

Layer 1: ポジションサイズ管理

1トレードの最大リスク = 資金の 2%

def calculate_position_size(
    balance: float,
    risk_pct: float,   # 0.02 (2%)
    sl_pips: float,    # ATRベースの損切り幅
    pip_value: float,  # 1pipあたりの価値
) -> int:
    risk_amount = balance * risk_pct
    size = risk_amount / (sl_pips * pip_value)
    return int(size)  # 切り捨て (安全側)

例: 残高 100,000円、SL 30pips の場合

  • リスク金額: 100,000 × 2% = 2,000円
  • ポジションサイズ: 2,000 / (30 × 0.01) = 6,666 → 6,000通貨

これにより、1回の負けで資金の 2% 以上は失わない設計です。

Layer 2: ボラティリティガード

相場が荒れているときはエントリーしない仕組み:

def check_volatility_guard(self, candles: list) -> bool:
    """バーレンジが平均の5倍を超えたらクールダウン"""
    ranges = [c.high - c.low for c in candles[-20:]]
    avg_range = sum(ranges) / len(ranges)
    current_range = candles[-1].high - candles[-1].low

    if current_range > avg_range * 5:
        self.cooldown_until = now() + timedelta(hours=6)
        return False  # 取引禁止

    if now() < self.cooldown_until:
        return False  # クールダウン中

    return True  # 取引OK

なぜ必要か: 重要指標発表時 (雇用統計、FOMC等) はスプレッドも広がり、テクニカル指標が機能しない。このタイミングでエントリーすると大きな損失リスクがある。

Layer 3: 最大ドローダウン制限

ピーク残高からの下落率が 10% に達したら 全ポジション強制決済 + Bot 停止:

def check_max_drawdown(self) -> bool:
    """最大DD 10%で強制停止"""
    current_balance = self.get_balance()
    peak = self.peak_balance

    if current_balance > peak:
        self.peak_balance = current_balance
        return True

    drawdown = (peak - current_balance) / peak
    if drawdown >= 0.10:
        self.emergency_close_all()
        self.notify("最大ドローダウン10%到達。全ポジション決済、Bot停止。")
        self.status = "critical"
        return False

    return True

10% の根拠:

  • 10% 損失 → 元に戻すには 11.1% の利益が必要 (回復可能)
  • 20% 損失 → 25% の利益が必要 (厳しい)
  • 50% 損失 → 100% の利益が必要 (ほぼ不可能)

損失が小さいうちに止めることが重要です。

Layer 4: 取引所側の OCO 注文

Bot がクラッシュしても損切りが執行されるよう、注文と同時にサーバーサイドの OCO (SL+TP) を設定:

async def place_order_with_oco(self, signal):
    # メイン注文
    order = await self.broker.create_order(
        symbol="USD_JPY",
        side=signal.side,
        size=signal.size,
    )

    # OCO (SL + TP) をサーバーサイドで設定
    await self.broker.set_stop_loss(order.id, signal.sl_price)
    await self.broker.set_take_profit(order.id, signal.tp_price)

これにより:

  • Bot プロセスが死んでも → SL/TP は取引所が執行
  • サーバーが落ちても → SL/TP は取引所が執行
  • ネットワーク断でも → SL/TP は取引所が執行

実際の動作例

ケース: 連敗時

Trade #1: -2% (SL)
Trade #2: -2% (SL)
Trade #3: +4% (TP)
Trade #4: -2% (SL)
Trade #5: -2% (SL)
→ 累計ドローダウン: -4% (まだ継続)

ケース: 急変時

[12:00] 相場急変、バーレンジが平均の8倍
→ ボラティリティガード発動、6時間エントリー禁止
→ 既存ポジションのSLは取引所側で管理 (Bot関与なし)
[18:00] クールダウン解除、通常運用再開

ケース: 最大DD到達

ピーク残高: ¥100,000
現在残高:   ¥89,500
DD: 10.5% > 10%
→ 全ポジション成行決済
→ Bot ステータス: critical
→ LINE通知: "最大ドローダウン到達。手動確認してください。"
→ 手動で原因確認後にのみ再起動

まとめ

防御対象方法
L11トレードの損失ポジションサイズ制限 (2%)
L2異常相場での損失ボラティリティガード
L3連敗による資金崩壊最大DD制限 (10%)
L4Bot/サーバー障害OCO注文 (取引所側)

自動売買は「利益を出す仕組み」ではなく「損失を限定する仕組みの上に利益を乗せる」ものです。まずリスク管理を実装してから戦略を開発することをおすすめします。


免責事項: この記事は投資助言ではありません。コード例は説明目的であり、実際の運用は自己責任で行ってください。