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}