65 lines
1.8 KiB
Python
65 lines
1.8 KiB
Python
"""凱利準則(Kelly Criterion)工具。"""
|
||
|
||
from __future__ import annotations
|
||
|
||
from dataclasses import dataclass
|
||
|
||
|
||
@dataclass(frozen=True)
|
||
class KellyResult:
|
||
"""凱利投注建議結果。"""
|
||
|
||
decimal_odds: float
|
||
win_probability: float
|
||
raw_kelly_fraction: float
|
||
fractional_kelly_factor: float
|
||
risk_tolerance_factor: float
|
||
final_fraction: float
|
||
stake_fraction: float
|
||
|
||
|
||
def calculate_kelly_fraction(
|
||
decimal_odds: float,
|
||
true_prob: float,
|
||
*,
|
||
bankroll: float,
|
||
fractional_kelly_factor: float = 1.0,
|
||
risk_tolerance_factor: float = 1.0,
|
||
) -> KellyResult:
|
||
"""依凱利準則估算下注比例與建議金額。
|
||
|
||
凱利公式:
|
||
f* = (b * p - q) / b
|
||
其中 b = odds - 1,p 為勝率,q = 1 - p。
|
||
"""
|
||
|
||
if decimal_odds <= 1:
|
||
raise ValueError('decimal_odds 必須大於 1')
|
||
if bankroll <= 0:
|
||
raise ValueError('bankroll 必須大於 0')
|
||
if not 0 <= true_prob <= 1:
|
||
raise ValueError('true_prob 需介於 0 到 1')
|
||
if not 0 <= fractional_kelly_factor <= 5:
|
||
raise ValueError('fractional_kelly_factor 須介於 0 到 5')
|
||
if not 0 <= risk_tolerance_factor <= 2:
|
||
raise ValueError('risk_tolerance_factor 須介於 0 到 2')
|
||
|
||
b = decimal_odds - 1
|
||
raw_kelly = (b * true_prob - (1 - true_prob)) / b
|
||
final_fraction = raw_kelly * fractional_kelly_factor * risk_tolerance_factor
|
||
# 保守處理:避免負值與超過總資金比例(100%)的極端輸出。
|
||
final_fraction = max(0.0, min(final_fraction, 1.0))
|
||
|
||
return KellyResult(
|
||
decimal_odds=decimal_odds,
|
||
win_probability=true_prob,
|
||
raw_kelly_fraction=raw_kelly,
|
||
fractional_kelly_factor=fractional_kelly_factor,
|
||
risk_tolerance_factor=risk_tolerance_factor,
|
||
final_fraction=final_fraction,
|
||
stake_fraction=final_fraction,
|
||
)
|
||
|
||
|
||
__all__ = ['KellyResult', 'calculate_kelly_fraction']
|