code wiki / (root) / nx_fin_pbo.nx

nx_fin_pbo.nx source

↩ module page · 48 lines · 2604 B

1// nx_fin_pbo.nx -- Probability of Backtest Overfitting (Bailey/Lopez de Prado CSCV). Completes the overfit- 2// detection suite (deflated-Sharpe was the multiple-testing correction; PBO is the combinatorial one). Given an 3// M-strategy x S-group performance matrix, enumerate every C(S, S/2) train/test split (train = set bits of a 4// popcount-S/2 mask, test = the rest); for each split pick the BEST in-sample strategy and ask whether it lands 5// in the BOTTOM HALF out-of-sample. PBO = fraction of splits where it does. High PBO => your "winner" is likely 6// luck. Pure integer combinatorics -- no transcendental functions, exact + gate-checkable. license_tier: ORIGINAL 7import "nx_syscalls.nx" 8const K_MAGIC_1000000000: i64 = 1000000000 9 10func pb_popcount(x: i64) -> i64 { var c: i64 = 0; var v: i64 = x; while v > 0 { c = c + (v & 1); v = v >> 1 } return c } 11 12// PBO in permille (0..1000). perf flat = perf[strategy*S + group]. m strategies, s groups (s even). 13func pb_pbo_permille(perf: *i64, m: i64, s: i64) -> i64 { 14 let half: i64 = s / 2 15 var overfit: i64 = 0; var ncombos: i64 = 0 16 var total: i64 = 1; var pe: i64 = 0 17 while pe < s { total = total * 2; pe = pe + 1 } // 2^s (avoid << : unconfirmed .nx operator) 18 var mask: i64 = 0 19 while mask < total { 20 if pb_popcount(mask) == half { 21 let oos: *i64 = sys_mmap(8*m) as *i64 22 var best_is_val: i64 = 0 - K_MAGIC_1000000000; var best_idx: i64 = 0 23 var mi: i64 = 0 24 while mi < m { 25 var is_sum: i64 = 0; var oos_sum: i64 = 0; var g: i64 = 0 26 while g < s { 27 let val: i64 = perf[mi*s + g] 28 if ((mask >> g) & 1) == 1 { is_sum = is_sum + val } else { oos_sum = oos_sum + val } 29 g = g + 1 30 } 31 oos[mi] = oos_sum 32 if is_sum > best_is_val { best_is_val = is_sum; best_idx = mi } 33 mi = mi + 1 34 } 35 let best_oos: i64 = oos[best_idx] 36 var nhigher: i64 = 0; var k: i64 = 0 37 while k < m { if oos[k] > best_oos { nhigher = nhigher + 1 } k = k + 1 } 38 if 2*nhigher >= m { overfit = overfit + 1 } // best-IS strategy fell to the bottom half OOS 39 ncombos = ncombos + 1 40 } 41 mask = mask + 1 42 } 43 if ncombos <= 0 { return 0 } 44 return overfit * 1000 / ncombos 45} 46 47// overfit-likely iff PBO >= threshold permille (500 = 50%). 48func pb_overfit_likely(pbo_permille: i64, thresh_permille: i64) -> i64 { if pbo_permille >= thresh_permille { return 1 } return 0 }