「このストラテジーで破産する確率は何%か?」——自動売買を本番稼働させる前に、この問いに定量的に答えられるだろうか。
Kelly基準の簡易式で計算した破産確率が0%だったとしても、それは「勝率とリスクリワード比が正確で、かつ固定である」という非現実的な仮定の上に成り立っている。実際の相場では連敗が重なり、バックテストの数値から乖離することは日常的に起きる。
本記事では、Kelly簡易式の限界を示し、モンテカルロシミュレーション(MC)による破産確率推定を10,000試行で実装した過程を記録する。結果として全9通貨ペアの破産確率0.00%を確認し、Practice Mode(少額実弾テスト)への移行判断に使用した。
Kelly簡易式の限界
Kelly基準の破産確率簡易式
ケリー基準(Kelly Criterion)には、理論上の破産確率の近似式がある。
破産確率 ≈ (1 - Edge / Odds)^(Capital / UnitRisk)
- Edge: 期待値の優位性(勝率 × 平均利益 - 敗率 × 平均損失)
- Odds: リスクリワード比
- Capital: 現在の口座残高
- UnitRisk: 1トレードあたりのリスク額
この式は「毎回同じ金額をベットし、勝率とRR比が完全に固定」という前提で成り立つ。
簡易式の問題点
- 勝率は一定ではない: バックテストで65%の勝率が出ても、ある月は50%に落ちることがある
- RR比も変動する: SL/TPの固定値とATR(Average True Range)ベースの動的値で結果が変わる
- 連敗の分布を無視: 実際の連敗は理論値よりクラスター化しやすい(市場環境の影響)
- ポジションサイズの動的変化: フラクショナルケリーでロットサイズが口座残高に連動する場合、簡易式の前提が崩れる
モンテカルロシミュレーションによる検証
MCシミュレーションとは
モンテカルロシミュレーション(MC)とは、乱数を使って多数の「仮想的な未来」を生成し、統計的な結論を導く手法だ。
今回は以下のアプローチを取った。
入力:
- バックテスト結果の全トレード履歴(勝敗・損益額)
- 初期口座残高
- ロットサイジングルール(フラクショナルケリー)
シミュレーション:
- トレード履歴からランダムに1トレードを復元抽出(ブートストラップ)
- 口座残高を更新
- 「破産」の定義: 口座残高が初期資金の20%以下に到達
- これを10,000回繰り返す
出力:
- 破産確率 = 破産した試行数 / 10,000
- 最大ドローダウン分布(中央値・95パーセンタイル・最大値)
- 最終残高分布(中央値・最悪ケース)
ブートストラップ法を選んだ理由
トレード結果の分布を「正規分布」と仮定するのは危険だ。FXのリターン分布はファットテール(裾が厚い)、つまり極端な損益が正規分布の想定よりも頻繁に発生する。
ブートストラップ法(実際のトレード結果からランダムに復元抽出する方法)なら、分布の仮定を置かずにシミュレーションできる。バックテストで実際に発生した損益パターンをそのまま使うため、ファットテールも自然に反映される。
実装のポイント
def run_monte_carlo(
trade_results: list[float], # 各トレードの損益(pips or 金額)
initial_capital: float,
num_simulations: int = 10_000,
num_trades_per_sim: int = 500,
ruin_threshold: float = 0.2, # 残高が初期資金の20%以下で「破産」
) -> dict:
ruin_count = 0
max_drawdowns = []
for _ in range(num_simulations):
capital = initial_capital
peak = capital
max_dd = 0.0
for _ in range(num_trades_per_sim):
# ブートストラップ: 実績から1トレードをランダム抽出
trade_pnl = random.choice(trade_results)
# フラクショナルケリーで実際のロットサイズに変換
position_size = calc_kelly_position(capital, ...)
capital += trade_pnl * position_size
# ドローダウン更新
peak = max(peak, capital)
dd = (peak - capital) / peak
max_dd = max(max_dd, dd)
# 破産判定
if capital <= initial_capital * ruin_threshold:
ruin_count += 1
break
max_drawdowns.append(max_dd)
return {
"ruin_probability": ruin_count / num_simulations,
"max_dd_median": np.median(max_drawdowns),
"max_dd_95th": np.percentile(max_drawdowns, 95),
}
10,000試行を選んだ根拠
統計的に有意な結果を得るために必要な試行数は、検出したい確率の逆数の10倍程度だ。破産確率1%を検出したいなら最低1,000試行、0.1%なら10,000試行が必要になる。
10,000試行あれば破産確率0.01%(1万回に1回)レベルまで検出でき、実用上十分な精度が得られる。計算時間も数秒で完了する。
結果:全9通貨ペアで破産確率0.00%
EUR_JPY、GBP_JPY、AUD_JPY、CAD_JPY、CHF_JPY、NZD_JPY、EUR_USD、GBP_USD、AUD_USDの全9通貨ペアについてMCシミュレーションを実行した結果、すべてのペアで破産確率が0.00%(10,000試行中0回)となった。
ただしCHF_JPYについては、バックテスト自体がマイナスリターン(-7,000円)だったため、Practice Mode(少額実弾テスト)からDry-Run(仮想取引モード)に戻す判断を行った。MCで破産確率0%であっても、期待リターンがマイナスの戦略を実弾で走らせる意味はない。
Kelly簡易式 vs MC:何が違ったか
| 項目 | Kelly簡易式 | MCシミュレーション |
|---|---|---|
| 勝率の扱い | 固定値(バックテスト平均) | 実績分布からランダム抽出 |
| RR比の扱い | 固定値 | 各トレードの実績損益を使用 |
| 連敗の考慮 | 独立試行を仮定 | 自然に反映(実績の偏りを含む) |
| ポジションサイズ | 固定 | フラクショナルケリーで動的変化 |
| 計算結果 | 0.00% | 0.00%(ただし分布の幅が見える) |
今回は両方とも0.00%で差異がなかったが、MCの本当の価値は「破産しなかった場合のドローダウン分布」が見えることだ。95パーセンタイルのドローダウンが25%なのか50%なのかで、精神的な許容度の判断が大きく変わる。
Go/No-Go判断への活用
MCシミュレーションの結果は、Practice Mode移行のGo/No-Go判断基準として明文化した。
Go条件(すべて満たすこと):
- MC破産確率 ≤ 1.0%(10,000試行)
- MC 95パーセンタイル最大ドローダウン ≤ 30%
- バックテスト期待リターン > 0
- バックテスト期間 ≥ 2年(統計的有意性の確保)
結果: 5ペア中4ペアがすべてのGo条件を満たし、Practice Mode移行を決定。CHF_JPY(条件3未達)はDry-Run戻し。
学んだこと
1. 簡易式とMCの使い分け
Kelly簡易式は「この戦略に基本的なエッジがあるか」の直感的な確認には使える。しかし本番稼働の判断材料にするなら、MCシミュレーションで分布を見るべきだ。
2. 破産確率0%でも安心できない
「破産しない」と「快適に運用できる」は全く別の問題だ。最大ドローダウンが50%あれば、口座の半分が溶ける局面が来る。MCで破産確率だけでなく、ドローダウン分布の95パーセンタイルまで確認することで、精神的に耐えられるかどうかの判断ができる。
3. バックテストがマイナスなら、どんな統計検定も無意味
CHF_JPYのように、バックテスト自体がマイナスリターンの戦略は、MC破産確率が0%であっても実弾投入すべきではない。統計的に「破産しない」ことと「利益が出る」ことは独立した問題だ。
まとめ
破産確率の検証で重要なのは以下の3点だ。
- Kelly簡易式の限界を知る: 固定勝率・固定RR比の仮定は非現実的。実運用判断にはMCが必要
- ブートストラップ法で分布を仮定しない: 正規分布を仮定するとファットテール(極端な損益)を過小評価する
- 破産確率だけでなくドローダウン分布を見る: 95パーセンタイルの最大DDが運用の快適さを決める
MCシミュレーションは実装コストが低い割に、戦略の信頼性判断に大きな影響を与える。本番稼働前の「最後の安全チェック」として、すべてのストラテジーに適用することを推奨する。