code wiki / (root) / nx_fin_risk_engine.nx

nx_fin_risk_engine.nx source

↩ module page · 52 lines · 2950 B

1// nx_fin_risk_engine.nx -- R4: POSITION SIZING + drawdown circuit breaker = the real answer to "don't blow up". 2// All i64 (cents / basis-points), NO floats. Sizers: (1) fractional-KELLY from the backtest's edge stats, 3// (2) fixed-RISK-per-trade (the "1% rule": size so a stop-out costs a bounded % of equity), (3) Kelly-fraction -> 4// shares. Plus a max-DRAWDOWN circuit breaker that HALTS sizing once equity has fallen past a limit, and an 5// ADV cap (composes R1 md_capacity) so every size respects the scaling wall. Single-responsibility: sizes ONE 6// position; portfolio-level diversification is nx_fin_portfolio. license_tier: ORIGINAL 7import "nx_syscalls.nx" 8import "nx_fin_marketdata.nx" 9const K_MAGIC_10000: i64 = 10000 10 11// optimal Kelly fraction (bps of equity) from win-rate + avg win/loss. f* = p - (1-p)/b, b = avg_win/avg_loss. 12// No edge (f*<0) or no loss estimate -> 0 (refuse to size). Full Kelly is aggressive -> temper with re_frac_kelly. 13func re_kelly_bps(win_rate_bps: i64, avg_win_cents: i64, avg_loss_cents: i64) -> i64 { 14 if avg_loss_cents <= 0 { return 0 } 15 if avg_win_cents <= 0 { return 0 } 16 let b_bps: i64 = avg_win_cents*K_MAGIC_10000/avg_loss_cents // win/loss ratio, x10000 17 if b_bps <= 0 { return 0 } 18 let lose_bps: i64 = K_MAGIC_10000 - win_rate_bps 19 let f: i64 = win_rate_bps - (lose_bps*K_MAGIC_10000/b_bps) 20 if f < 0 { return 0 } 21 return f 22} 23 24// apply a safety fraction to Kelly (half-Kelly = 5000 bps). Both in bps. 25func re_frac_kelly(kelly_bps: i64, frac_bps: i64) -> i64 { return kelly_bps*frac_bps/K_MAGIC_10000 } 26 27// shares such that a stop-out (entry -> stop) costs exactly risk_budget_bps of equity (the "1% rule"). 28func re_size_by_risk(equity_cents: i64, risk_budget_bps: i64, entry_cents: i64, stop_cents: i64) -> i64 { 29 let risk_per_share: i64 = entry_cents - stop_cents 30 if risk_per_share <= 0 { return 0 } 31 let risk_budget: i64 = equity_cents*risk_budget_bps/K_MAGIC_10000 32 return risk_budget/risk_per_share 33} 34 35// shares from a Kelly (or any) equity fraction: (equity * frac_bps/10000) / price. 36func re_size_by_kelly(equity_cents: i64, frac_bps: i64, price_cents: i64) -> i64 { 37 if price_cents <= 0 { return 0 } 38 let dollars: i64 = equity_cents*frac_bps/K_MAGIC_10000 39 return dollars/price_cents 40} 41 42// circuit breaker: 1 = trading allowed (current drawdown within limit), 0 = HALT (breaker tripped). 43func re_dd_ok(current_dd_bps: i64, limit_bps: i64) -> i64 { if current_dd_bps >= limit_bps { return 0 } return 1 } 44 45// cap a share count by the ADV scaling wall (composes R1 md_capacity). adv_cents<=0 disables the cap. 46func re_cap_shares(shares: i64, price_cents: i64, adv_cents: i64, participation_bps: i64) -> i64 { 47 if adv_cents <= 0 { return shares } 48 if price_cents <= 0 { return 0 } 49 let cap_shares: i64 = md_capacity(adv_cents, participation_bps)/price_cents 50 if cap_shares < shares { return cap_shares } 51 return shares 52}