| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| export function kellyFraction(price, pWin) { |
| if (!price || price <= 0 || price >= 1 || !pWin) return 0; |
| const pLose = 1 - pWin; |
| const odds = 1 / price - 1; |
| const k = (odds * pWin - pLose) / odds; |
| return Math.min(Math.max(k, 0), 0.25); |
| } |
|
|
| const SPREAD_ILLIQUID_THRESHOLD = 0.05; |
| const KELLY_CONSERVATIVE_FACTOR = 0.25; |
| const MAX_BANKROLL_FRACTION = 0.25; |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| export function suggestSize({ yesPrice, noPrice, spread = 0, signal, bankroll = 1000 }) { |
| const illiquid = (spread ?? 0) > SPREAD_ILLIQUID_THRESHOLD; |
|
|
| if (!signal || signal.edgePoints == null) { |
| return { |
| outcome: null, |
| fraction: 0, |
| amountEur: 0, |
| edgeNet: 0, |
| illiquid, |
| note: illiquid |
| ? `Mercado ilíquido (spread ${Math.round((spread ?? 0) * 100)}¢). Compra desaconsejada.` |
| : 'Sin señal IA disponible. No se puede calcular sugerencia.', |
| }; |
| } |
|
|
| const rawEdge = Math.abs(signal.edgePoints) / 100; |
| const netEdge = rawEdge - (spread ?? 0); |
| const outcome = signal.edgePoints > 0 ? 'YES' : 'NO'; |
| const price = outcome === 'YES' ? yesPrice : noPrice; |
|
|
| if (netEdge <= 0.005 || !price || price <= 0 || price >= 1) { |
| return { |
| outcome: null, |
| fraction: 0, |
| amountEur: 0, |
| edgeNet: netEdge, |
| illiquid, |
| note: illiquid |
| ? `Mercado ilíquido (spread ${Math.round((spread ?? 0) * 100)}¢).` |
| : `Sin edge neto tras spread (${(rawEdge * 100).toFixed(1)}pp − ${((spread ?? 0) * 100).toFixed(1)}pp). No apostar.`, |
| }; |
| } |
|
|
| |
| const pWin = Math.max(0, Math.min(0.99, price + netEdge)); |
| |
| const k = kellyFraction(price, pWin) * KELLY_CONSERVATIVE_FACTOR; |
| const fraction = Math.min(MAX_BANKROLL_FRACTION, Math.max(0, k)); |
| const amountEur = Math.round(bankroll * fraction); |
|
|
| return { |
| outcome, |
| fraction, |
| amountEur, |
| edgeNet: netEdge, |
| illiquid, |
| note: illiquid |
| ? `Mercado ilíquido (spread ${Math.round((spread ?? 0) * 100)}¢). Compra desaconsejada.` |
| : `Kelly conservador (¼) sobre edge neto ${(netEdge * 100).toFixed(1)}pp: €${amountEur} en ${outcome}.`, |
| }; |
| } |
|
|