code wiki / (root) / nx_fin_advstats.nx

nx_fin_advstats.nx source

↩ module page · 50 lines · 2630 B

1// nx_fin_advstats.nx -- advanced backtest statistics (the Lopez de Prado rigor that separates a real edge from 2// the luckiest of many tries). Core = the DEFLATED SHARPE RATIO: the multiple-testing correction. When you test 3// T strategies (our tournament does), the BEST Sharpe is inflated by selection; the deflated Sharpe subtracts the 4// expected-max-under-null haircut (trial_std * sqrt(2*ln T)) before testing significance. Composes me_isqrt 5// (nx_fin_metrics). i64 fixed-point (Sharpe in MILLI = SR*1000); sqrt(2 ln T) via a data table (avoids integer 6// ln). This moves us from BEHIND to PARITY on advanced stats (PBO / CPCV are the next refinements). 7// license_tier: ORIGINAL 8import "nx_syscalls.nx" 9import "nx_fin_metrics.nx" 10const K_MAGIC_1177: i64 = 1177 11const K_MAGIC_1665: i64 = 1665 12const K_MAGIC_2036: i64 = 2036 13const K_MAGIC_2354: i64 = 2354 14const K_MAGIC_2634: i64 = 2634 15const K_MAGIC_3035: i64 = 3035 16const K_MAGIC_3723: i64 = 3723 17 18// classic Sharpe significance t-stat (x1000) = SR * sqrt(N-1). sr_milli = SR*1000, N = sample size. 19func as_sharpe_tstat_milli(sr_milli: i64, n: i64) -> i64 { 20 if n <= 1 { return 0 } 21 return sr_milli * me_isqrt(n - 1) 22} 23 24// expected max Sharpe under the null from n_trials strategies = sqrt(2*ln T) x1000 (data table, Cardinal 11). 25// The multiple-testing hurdle: the more strategies you try, the higher the best must clear to be real. 26func as_max_z_milli(n_trials: i64) -> i64 { 27 if n_trials <= 1 { return 0 } 28 if n_trials <= 2 { return K_MAGIC_1177 } 29 if n_trials <= 4 { return K_MAGIC_1665 } 30 if n_trials <= 8 { return K_MAGIC_2036 } 31 if n_trials <= 16 { return K_MAGIC_2354 } 32 if n_trials <= 32 { return K_MAGIC_2634 } 33 if n_trials <= 100 { return K_MAGIC_3035 } 34 return K_MAGIC_3723 // ~T=1000 35} 36 37// DEFLATED Sharpe (milli) = best-of-T Sharpe minus the multiple-testing haircut (trial_std * sqrt(2 ln T)). 38func as_deflated_sr_milli(best_sr_milli: i64, trial_std_milli: i64, n_trials: i64) -> i64 { 39 let haircut: i64 = trial_std_milli * as_max_z_milli(n_trials) / 1000 40 return best_sr_milli - haircut 41} 42 43// is the DEFLATED Sharpe significant? deflated-SR t-stat > threshold (2000 = t 2.0 ~ 95%). This is the honest 44// gate the tournament's "winner" must pass -- it kills the luckiest-of-many-strategies illusion. 45func as_deflated_significant(best_sr_milli: i64, trial_std_milli: i64, n_trials: i64, n: i64, thresh_milli: i64) -> i64 { 46 let dsr: i64 = as_deflated_sr_milli(best_sr_milli, trial_std_milli, n_trials) 47 if dsr <= 0 { return 0 } 48 if as_sharpe_tstat_milli(dsr, n) > thresh_milli { return 1 } 49 return 0 50}