"""Position sizing utilities. Provides various sizing methods beyond the basic risk-percentage approach. """ from __future__ import annotations from enum import Enum class SizingMethod(str, Enum): FIXED_RISK = "fixed_risk" FIXED_AMOUNT = "fixed_amount" KELLY = "kelly" def fixed_risk_size( balance: float, entry_price: float, stop_loss: float, risk_pct: float = 0.02, ) -> float: """Position size = (balance * risk%) / |entry - SL|.""" price_risk = abs(entry_price - stop_loss) if price_risk == 0: return 0.0 return round((balance * risk_pct) / price_risk, 8) def fixed_amount_size( amount_usd: float, entry_price: float, ) -> float: """Fixed USD amount converted to asset quantity.""" if entry_price <= 0: return 0.0 return round(amount_usd / entry_price, 8) def kelly_size( win_rate: float, avg_win: float, avg_loss: float, balance: float, entry_price: float, fraction: float = 0.5, ) -> float: """Half-Kelly sizing for conservative edge exploitation. Kelly% = W - (1-W)/R where R = avg_win/avg_loss We use fraction (default 0.5) of full Kelly for safety. """ if avg_loss == 0 or entry_price <= 0: return 0.0 r = avg_win / avg_loss kelly_pct = win_rate - (1 - win_rate) / r kelly_pct = max(0.0, min(kelly_pct * fraction, 0.1)) # cap at 10% return round((balance * kelly_pct) / entry_price, 8)