コンテンツにスキップ

ケリー基準と分数ケリー

ケリー基準は、勝率と損益比が検証済みの売買ルールに対して、1トレードでリスクにさらす口座資金の割合を決めるための考え方です。トレードの期待値を建玉サイズへ変換し、長期の対数成長率を最大化する賭け率を求めます。裁量の思いつきエントリーには向かず、同一ルールで十分なサンプルがある系統的な売買を前提にします。

定義

ケリー基準は、各回の資金を一定割合で賭ける反復ゲームにおいて、長期の期待対数成長率を最大化する賭け率を求める式です。

勝つ確率を pp、負ける確率を q=1pq=1-p、勝ったときに賭け金 1 に対して得る純利益倍率を bb とします。資金のうち ff を賭けると、1回後の資金倍率は次になります。

{1+bf勝ち1f負け\begin{cases} 1 + bf & \text{勝ち} \\ 1 - f & \text{負け} \end{cases}

期待対数成長率は、

g(f)=plog(1+bf)+qlog(1f)g(f) = p\log(1+bf) + q\log(1-f)

これを ff で微分して 0 と置きます。

dgdf=pb1+bfq1f=0\frac{dg}{df} = \frac{pb}{1+bf} - \frac{q}{1-f} = 0

整理すると、

pb(1f)=q(1+bf)pb(1-f) = q(1+bf) pbpbf=q+qbfpb - pbf = q + qbf pbq=bf(p+q)pb - q = bf(p+q)

p+q=1p+q=1 なので、

f=bpqbf^* = \frac{bp-q}{b}

同値な形として、

f=pqbf^* = p - \frac{q}{b}

となります。

  • f>0f^* > 0 のときだけ賭ける
  • f=0f^* = 0 は期待値がない
  • f<0f^* < 0 はその方向に賭けてはいけない
  • b=1b=1 のイーブン勝負では f=2p1f^*=2p-1

例として、勝率 p=0.55p=0.55、損益比 b=1b=1 なら、

f=2×0.551=0.10f^* = 2 \times 0.55 - 1 = 0.10

つまりフルケリーでは口座資金の 10% をリスクにさらします。これは理論上の最適値であり、実運用では大きすぎます。

FX への置き換え

FX では ff を「必要証拠金の割合」や「レバレッジ倍率」として扱ってはいけません。ff損切りにかかったときに失う口座資金の割合 と定義します。

USD/JPY のロングで次の条件を考えます。

  • エントリー: 150.00
  • 損切り: 149.60
  • 利確: 150.50
  • ストップ幅: 40 pips
  • 利確幅: 50 pips
  • 往復コスト、スリッページ見込み: 1 pip

このとき、勝ちの純 pips と負けの純 pips は保守的に次で置きます。

W=501=49W = 50 - 1 = 49 L=40+1=41L = 40 + 1 = 41

損益比は、

b=WL=49411.195b = \frac{W}{L} = \frac{49}{41} \approx 1.195

検証済み勝率を p=0.53p=0.53q=0.47q=0.47 とすると、

f=1.195×0.530.471.1950.137f^* = \frac{1.195 \times 0.53 - 0.47}{1.195} \approx 0.137

フルケリーは約 13.7% になります。これは USD/JPY の実運用では過大です。勝率推定の誤差、スプレッド拡大、連敗、経済指標、日銀と財務省関連ヘッドライン、米金利急変を考えると、この値をそのまま使ってはいけません。

分数ケリー

実運用ではフルケリーを縮小します。

fα=αff_{\alpha} = \alpha f^*
  • フルケリー: α=1.0\alpha=1.0
  • ハーフケリー: α=0.5\alpha=0.5
  • クオーターケリー: α=0.25\alpha=0.25

上の例では、

方式計算リスク
フルケリー1.00×13.7%1.00 \times 13.7\%13.7%
ハーフケリー0.50×13.7%0.50 \times 13.7\%6.85%
クオーターケリー0.25×13.7%0.25 \times 13.7\%3.43%

それでも大きい値です。FX ではさらに固定上限を置きます。

fuse=min(αf,fcap)f_{\text{use}} = \min(\alpha f^*, f_{\text{cap}})

実務上は、検証済みシステムでも fcapf_{\text{cap}} を 0.25% から 2% 程度に置きます。裁量要素が強いなら 0.25% から 0.5% を上限にします。

ハーフケリーの意味

ハーフケリーは「利益を半分にする」方法ではありません。小さいリターン近似では、期待対数成長率を次で近似できます。

g(f)μf12σ2f2g(f) \approx \mu f - \frac{1}{2}\sigma^2 f^2

このとき最適比率は、

f=μσ2f^* = \frac{\mu}{\sigma^2}

分数ケリー f=αff=\alpha f^* を代入すると、最大成長率に対する比はおおよそ、

g(αf)g(f)=2αα2\frac{g(\alpha f^*)}{g(f^*)} = 2\alpha - \alpha^2

したがって、

方式成長率の近似比ポジションサイズ
フルケリー100%100%
ハーフケリー75%50%
クオーターケリー43.75%25%

ハーフケリーは、理論上の成長率を大きく残しながら、ドローダウンと推定誤差への感度を下げます。クオーターケリーはさらに保守的で、FX ではこちらを基準にするほうが現実的です。

破産確率

ケリー基準は「フルケリーなら破産しない」という意味ではありません。各回で資金の一部だけを失う設定なら、数学的に資金が完全なゼロへ到達しにくいだけで、実務上の破産は簡単に起きます。

実務上の破産は次で定義します。

  • 口座資金が初期資金の 50% を下回る
  • 最大ドローダウンが 30% を超える
  • 必要証拠金維持率を割る
  • 精神的にルールを続けられない損失に達する

固定額ベットの単純なイーブン勝負では、勝率 p>qp>q、破産ラインまで mm 単位ある場合、究極破産確率は近似的に次で見ます。

Pruin(qp)mP_{\text{ruin}} \approx \left(\frac{q}{p}\right)^m

ただしこれは USD/JPY の実運用には直接使いません。FX では損益幅が一定でなく、スリッページがあり、取引が独立でなく、同じドル円テーマで連敗が連続します。

より実用的には、検証済みトレード列をブートストラップし、次をモンテカルロで確認します。

  • 最大ドローダウン分布
  • 連敗長
  • 証拠金維持率の最低値
  • 破産ライン到達確率
  • 月次と年次の損益分布
  • スプレッド拡大時の悪化
  • 指標前後を除外した場合の変化

USD/JPY での使いどころ

USD/JPY は流動性が高く、BIS 2025年調査でも USD/JPY は主要通貨ペアの一つです。ただし、流動性が高いことは「損失が小さい」ことを意味しません。USD/JPY は米金利、日本国債利回り、日銀政策、財務省の為替介入観測、米雇用統計、CPI、FOMC で急変しやすい通貨ペアです。

ケリー基準を使うなら、次のように限定します。

  • 同一ルールで十分なサンプル数がある
  • エントリー前に損切り幅と利確幅が決まっている
  • 勝率 pp と損益比 bb をコスト控除後で推定している
  • 東京時間、ロンドン時間、NY時間を分けて検証している
  • 指標トレードと通常トレードを混ぜていない
  • ロングとショートを分けて集計している
  • 介入警戒局面を別 regime として扱っている

USD/JPY のロット計算では、JPY 口座なら次で概算できます。

risk_jpy=equity_jpy×fuse\text{risk\_jpy} = \text{equity\_jpy} \times f_{\text{use}}

USD/JPY の 1 pip を 0.01 円とすると、USD 建て通貨数量 NN の 1 pip 価値は、

pip_value_jpy=N×0.01\text{pip\_value\_jpy} = N \times 0.01

ストップ幅を SS pips とすると、

N=risk_jpyS×0.01N = \frac{\text{risk\_jpy}}{S \times 0.01}

例として、口座資金 1,000,000 円、使用リスク fuse=1%f_{\text{use}}=1\%、ストップ幅 40 pips なら、

risk_jpy=1,000,000×0.01=10,000\text{risk\_jpy} = 1,000,000 \times 0.01 = 10,000 N=10,00040×0.01=25,000N = \frac{10,000}{40 \times 0.01} = 25,000

したがって、約 25,000 USD のポジションになります。1万通貨単位なら 2.5 ロット、10万通貨単位なら 0.25 ロットです。

必要証拠金は別に確認します。USD/JPY が 150 円、レバレッジ 25倍なら、25,000 USD の円建て想定元本は、

25,000×150=3,750,00025,000 \times 150 = 3,750,000 \text{円}

必要証拠金は、

3,750,00025=150,000\frac{3,750,000}{25} = 150,000 \text{円}

損失リスクは 10,000 円、必要証拠金は 150,000 円であり、両者は別物です。

Python 実装スケッチ

from __future__ import annotations
import numpy as np
import pandas as pd
def kelly_binary(p: float, b: float) -> float:
"""
Binary Kelly fraction.
p: win probability after costs.
b: net win / net loss, e.g. 49 pips / 41 pips.
returns: full Kelly fraction.
"""
if not 0 <= p <= 1:
raise ValueError("p must be in [0, 1]")
if b <= 0:
raise ValueError("b must be positive")
q = 1.0 - p
return (b * p - q) / b
def fractional_kelly(
p: float,
b: float,
fraction: float = 0.25,
cap: float = 0.01,
) -> float:
"""
Conservative tradable fraction.
fraction=0.5 is half Kelly.
fraction=0.25 is quarter Kelly.
cap is the absolute risk cap per trade.
"""
full = kelly_binary(p, b)
scaled = fraction * full
return max(0.0, min(scaled, cap))
def estimate_binary_params_from_trades(r_multiples: pd.Series) -> tuple[float, float]:
"""
Estimate p and b from R-multiple trade results.
Example:
+1.2 means +1.2R
-1.0 means -1.0R
-1.3 means slippage beyond stop
This is a rough binary approximation. For fat-tailed results,
optimize expected log growth directly instead.
"""
wins = r_multiples[r_multiples > 0]
losses = r_multiples[r_multiples < 0]
if len(wins) == 0 or len(losses) == 0:
raise ValueError("Need both winning and losing trades")
p = len(wins) / len(r_multiples)
avg_win = wins.mean()
avg_loss = -losses.mean()
b = avg_win / avg_loss
return float(p), float(b)
def expected_log_growth(f: float, r_multiples: np.ndarray) -> float:
"""
Empirical expected log growth for general R-multiple outcomes.
f is the fraction of equity risked per 1R.
If a trade returns -1R, equity multiplier is 1 - f.
If a trade returns +1.5R, equity multiplier is 1 + 1.5f.
"""
multipliers = 1.0 + f * r_multiples
if np.any(multipliers <= 0):
return -np.inf
return float(np.mean(np.log(multipliers)))
def kelly_empirical_grid(
r_multiples: pd.Series,
max_fraction: float = 0.10,
steps: int = 10_001,
) -> tuple[float, float]:
"""
Grid-search Kelly fraction from empirical R-multiple distribution.
This avoids forcing every trade into a binary win/loss model.
Use out-of-sample and walk-forward validation before trusting it.
"""
r = r_multiples.dropna().to_numpy(dtype=float)
if len(r) == 0:
raise ValueError("No trades")
# Keep f below the level that would make the worst historical loss fatal.
worst = r.min()
feasible_max = max_fraction
if worst < 0:
feasible_max = min(feasible_max, 0.999 / abs(worst))
grid = np.linspace(0.0, feasible_max, steps)
growth = np.array([expected_log_growth(f, r) for f in grid])
idx = int(np.argmax(growth))
return float(grid[idx]), float(growth[idx])
def usdjpy_units_for_jpy_account(
equity_jpy: float,
risk_fraction: float,
stop_pips: float,
pip_size: float = 0.01,
) -> float:
"""
Position size for USD/JPY with a JPY-denominated account.
Returns USD units.
For USD/JPY, 1 pip is usually 0.01 JPY.
"""
if equity_jpy <= 0:
raise ValueError("equity_jpy must be positive")
if not 0 < risk_fraction < 1:
raise ValueError("risk_fraction must be in (0, 1)")
if stop_pips <= 0:
raise ValueError("stop_pips must be positive")
risk_jpy = equity_jpy * risk_fraction
units = risk_jpy / (stop_pips * pip_size)
return float(units)
def margin_required_jpy(
usdjpy_price: float,
usd_units: float,
leverage: float,
) -> float:
"""
Approximate margin requirement in JPY.
"""
if usdjpy_price <= 0 or usd_units <= 0 or leverage <= 0:
raise ValueError("inputs must be positive")
notional_jpy = usdjpy_price * usd_units
return float(notional_jpy / leverage)
def monte_carlo_ruin_probability(
r_multiples: pd.Series,
risk_fraction: float,
n_trades: int = 300,
n_paths: int = 20_000,
ruin_level: float = 0.5,
seed: int = 42,
) -> dict[str, float]:
"""
Bootstrap trade outcomes and estimate practical ruin/drawdown risk.
ruin_level=0.5 means equity falling to 50% of initial equity.
"""
rng = np.random.default_rng(seed)
r = r_multiples.dropna().to_numpy(dtype=float)
if len(r) == 0:
raise ValueError("No trades")
samples = rng.choice(r, size=(n_paths, n_trades), replace=True)
multipliers = 1.0 + risk_fraction * samples
# A path with any non-positive multiplier is immediately ruined.
invalid = np.any(multipliers <= 0, axis=1)
equity = np.cumprod(np.maximum(multipliers, 1e-12), axis=1)
running_peak = np.maximum.accumulate(equity, axis=1)
drawdown = 1.0 - equity / running_peak
min_equity = equity.min(axis=1)
max_dd = drawdown.max(axis=1)
ruined = invalid | (min_equity <= ruin_level)
return {
"ruin_probability": float(np.mean(ruined)),
"median_terminal_equity": float(np.median(equity[:, -1])),
"p05_terminal_equity": float(np.quantile(equity[:, -1], 0.05)),
"p95_terminal_equity": float(np.quantile(equity[:, -1], 0.95)),
"median_max_drawdown": float(np.median(max_dd)),
"p95_max_drawdown": float(np.quantile(max_dd, 0.95)),
}
if __name__ == "__main__":
p = 0.53
b = 49 / 41
full = kelly_binary(p, b)
half = fractional_kelly(p, b, fraction=0.5, cap=0.02)
quarter = fractional_kelly(p, b, fraction=0.25, cap=0.01)
print({"full": full, "half_capped": half, "quarter_capped": quarter})
units = usdjpy_units_for_jpy_account(
equity_jpy=1_000_000,
risk_fraction=quarter,
stop_pips=40,
)
margin = margin_required_jpy(
usdjpy_price=150.0,
usd_units=units,
leverage=25.0,
)
print({"usd_units": units, "margin_jpy": margin})

落とし穴

勝率を過大評価する

ケリー基準は pp に極端に敏感です。勝率 52% と 55% では推奨リスクが大きく変わります。バックテストの勝率をそのまま使わないようにします。

保守的には、推定勝率を 50% 側へ縮小します。

padj=0.5+λ(p^0.5)p_{\text{adj}} = 0.5 + \lambda(\hat{p} - 0.5)

ここで 0<λ<10 < \lambda < 1 とします。例えば λ=0.5\lambda=0.5 なら、推定勝率の優位性を半分しか信用しません。

損益比を美化する

利確 50 pips、損切り 40 pips でも、実際の bb50/4050/40 ではありません。スプレッド、滑り、約定拒否、週明けギャップ、指標直後の飛びを入れます。

b=average net winaverage net lossb = \frac{\text{average net win}}{\text{average net loss}}

平均損失には、通常の損切りだけでなく、滑った損切りを含めます。

トレードが独立していない

ケリー基準の基本形は、同じゲームを独立に繰り返す前提が強いものです。USD/JPY ではこの前提が崩れやすくなります。

  • 米金利テーマで同じ方向に連敗する
  • 日銀イベント前後で regime が変わる
  • 東京時間の逆張りと NY 時間の順張りを混ぜる
  • 同じシグナルが短時間に連発する
  • クロス円と同時に持つと実質的に JPY リスクが重複する

相関したトレードを独立トレードとして扱うと、破産確率を過小評価します。

証拠金とリスクを混同する

必要証拠金が少ないことは、リスクが小さいことを意味しません。USD/JPY で 25倍レバレッジを使えるとしても、25倍で張る必要はありません。

管理すべき順序は次のとおりです。

  1. 損切り幅を決める
  2. 口座資金に対する許容損失 fusef_{\text{use}} を決める
  3. 通貨数量を計算する
  4. 必要証拠金と維持率を確認する
  5. 指標、週末、流動性低下時の追加リスクを確認する

フルケリーを安全だと誤解する

フルケリーは長期の対数成長率を最大化しますが、短期の最大ドローダウンを最小化しません。ドローダウン制約があるなら、分数ケリーか、明示的なドローダウン制約付き最適化を使います。

破産確率を平均値で隠す

期待値が正でも、連敗で運用停止になるなら実務では失敗です。次の条件を先に決めます。

  • 最大許容ドローダウン
  • 月間損失停止ライン
  • 連敗時の縮小ルール
  • 指標前後の取引停止ルール
  • スプレッドが通常時の何倍なら停止するか

実運用ルール例

USD/JPY のシステムにケリー基準を使うなら、次の手順に固定します。

  1. 過去トレードを R 倍率で保存する
  2. コスト控除後の ppbb を推定する
  3. 外れ値を消さずに、滑った損失を含める
  4. ロング、ショート、時間帯、指標有無で分けて推定する
  5. pp を保守的に 50% 側へ縮小する
  6. bb も保守的に下げる
  7. クオーターケリーを計算する
  8. 1トレード上限でさらにキャップする
  9. モンテカルロで破産確率と最大ドローダウンを確認する
  10. 実運用後は月次で再推定し、悪化したらサイズを落とす

最終的な使用リスクは次の形にします。

fuse=min(0.25fadj,ftrade cap,fportfolio cap)f_{\text{use}} = \min( 0.25 f^*_{\text{adj}}, f_{\text{trade cap}}, f_{\text{portfolio cap}} )

ここで、

  • fadjf^*_{\text{adj}}: 保守的な ppbb で計算したフルケリー
  • ftrade capf_{\text{trade cap}}: 1トレードの絶対上限
  • fportfolio capf_{\text{portfolio cap}}: 同時保有ポジション全体の上限

参考