nx_fin_backtest_gate.nx source
↩ module page · 59 lines · 3450 B
1// nx_fin_backtest_gate.nx -- R2 GATE: proves the backtester on KNOWN bars + a KNOWN signal, with EVERY metric
2// hand-computed (no network, no false-green). Integrates R1 (md_parse_csv -> bars) into R2 (bt_run). The two
3// trades are +50 (win) and -50 (loss) per share so the gate PROVES the engine reports losses honestly -- not the
4// "no-loss" fantasy. Exits 0 iff all pass. license_tier: ORIGINAL
5import "nx_gate.nx"
6import "nx_fin_marketdata.nx"
7import "nx_fin_backtest.nx"
8
9func chk(name: *u8, got: i64, want: i64, st: *i64) -> i64 {
10 if got == want { st[0] = st[0] + 1; gw(" PASS " as *u8); gw(name); gw("\n" as *u8) }
11 else { st[1] = st[1] + 1; gw(" FAIL " as *u8); gw(name); gw(" got=" as *u8); gn(got); gw(" want=" as *u8); gn(want); gw("\n" as *u8) }
12 return 0
13}
14
15func main() -> i64 {
16 let st: *i64 = sys_mmap(16) as *i64
17 st[0] = 0; st[1] = 0
18
19 // -- position sizing + the ADV/capacity cap (the scaling wall) --
20 chk("size cash-bound (no cap)" as *u8, bt_position_size(1000000, 1000, 0, 0), 1000, st)
21 chk("size capacity-capped @10bps ADV" as *u8, bt_position_size(1000000, 1000, 100000000, 10), 100, st)
22 chk("size loose cap @100% -> cash-bound" as *u8, bt_position_size(1000000, 1000, 100000000, 10000), 1000, st)
23
24 // -- backtest on KNOWN bars (closes 1000,1100,1050,1200,1150 cents) + signal [1,1,0,1,0] --
25 let csv: *u8 = "Date,Open,High,Low,Close,Volume\n2024-01-02,10.00,10.00,10.00,10.00,1000000\n2024-01-03,11.00,11.00,11.00,11.00,1000000\n2024-01-04,10.50,10.50,10.50,10.50,1000000\n2024-01-05,12.00,12.00,12.00,12.00,1000000\n2024-01-06,11.50,11.50,11.50,11.50,1000000\n" as *u8
26 var n: i64 = 0; while csv[n] != (0 as u8) { n = n + 1 }
27 let bars: *i64 = sys_mmap(8 * MD_FIELDS * 16) as *i64
28 let nbars: i64 = md_parse_csv(csv, n, bars, 16)
29 chk("parsed 5 bars (R1->R2)" as *u8, nbars, 5, st)
30
31 let sig: *i64 = sys_mmap(8 * 8) as *i64
32 sig[0]=1; sig[1]=1; sig[2]=0; sig[3]=1; sig[4]=0
33 let out: *i64 = sys_mmap(8 * 16) as *i64
34 bt_run(bars, nbars, sig, 1000000, 0, 0, 0, 0, out) // start $10,000; no slippage/commission/cap
35
36 chk("n_trades = 2" as *u8, out[BT_TRADES], 2, st)
37 chk("n_wins = 1" as *u8, out[BT_WINS], 1, st)
38 chk("n_losses = 1 (HONEST: reports the loss)" as *u8, out[BT_LOSSES], 1, st)
39 chk("gross_win cents (+50 x 1000sh)" as *u8, out[BT_GROSS_WIN], 50000, st)
40 chk("gross_loss cents (-50 x 875sh)" as *u8, out[BT_GROSS_LOSS], 43750, st)
41 chk("net_pnl cents" as *u8, out[BT_NET], 6250, st)
42 chk("expectancy/trade cents" as *u8, out[BT_EXPECTANCY], 3125, st)
43 chk("win_rate bps (50%)" as *u8, out[BT_WINRATE], 5000, st)
44 chk("profit_factor bps (1.142x)" as *u8, out[BT_PF], 11428, st)
45 chk("max_drawdown bps (8.52%)" as *u8, out[BT_MAXDD], 852, st)
46 chk("return bps (0.625%)" as *u8, out[BT_RET], 62, st)
47 chk("final_equity cents" as *u8, out[BT_FINAL_EQ], 1006250, st)
48
49 // -- costs are wired: 100bps slippage must REDUCE net pnl --
50 let out2: *i64 = sys_mmap(8 * 16) as *i64
51 bt_run(bars, nbars, sig, 1000000, 100, 0, 0, 0, out2)
52 var slip_less: i64 = 0; if out2[BT_NET] < 6250 { slip_less = 1 }
53 chk("slippage 100bps reduces net pnl" as *u8, slip_less, 1, st)
54
55 gw("nx_fin_backtest_gate: PASS=" as *u8); gn(st[0]); gw(" FAIL=" as *u8); gn(st[1]); gw("\n" as *u8)
56 if st[1] == 0 { gw("R2 nx_fin_backtest: GREEN\n" as *u8); return 0 }
57 gw("R2 nx_fin_backtest: RED\n" as *u8)
58 return 1
59}