nx_fin_strategy_gate.nx source
↩ module page · 72 lines · 4013 B
1// nx_fin_strategy_gate.nx -- R3 GATE: proves the SMA signal generator on known bars, then runs the FULL LOOP
2// R1(md_parse_csv) -> R3(st_sma_cross) -> R2(bt_run) end-to-end. The crossover buys into a top on this fixture
3// and takes a loss -> the loop HONESTLY reports a losing strategy (that is the whole point of the truth engine).
4// Exits 0 iff all pass. license_tier: ORIGINAL
5import "nx_gate.nx"
6import "nx_fin_marketdata.nx"
7import "nx_fin_backtest.nx"
8import "nx_fin_strategy.nx"
9
10func chk(name: *u8, got: i64, want: i64, st: *i64) -> i64 {
11 if got == want { st[0] = st[0] + 1; gw(" PASS " as *u8); gw(name); gw("\n" as *u8) }
12 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) }
13 return 0
14}
15
16func main() -> i64 {
17 let st: *i64 = sys_mmap(16) as *i64
18 st[0] = 0; st[1] = 0
19
20 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
21 var n: i64 = 0; while csv[n] != (0 as u8) { n = n + 1 }
22 let bars: *i64 = sys_mmap(8 * MD_FIELDS * 16) as *i64
23 let nbars: i64 = md_parse_csv(csv, n, bars, 16)
24 chk("parsed 5 bars" as *u8, nbars, 5, st)
25
26 // -- SMA values (closes 1000,1100,1050,1200,1150) --
27 chk("sma(end=2,p=2) = 1075" as *u8, st_sma(bars, 2, 2), 1075, st)
28 chk("sma(end=2,p=3) = 1050" as *u8, st_sma(bars, 2, 3), 1050, st)
29 chk("sma(end=3,p=2) = 1125" as *u8, st_sma(bars, 3, 2), 1125, st)
30 chk("sma(end=3,p=3) = 1116" as *u8, st_sma(bars, 3, 3), 1116, st)
31 chk("sma no-history (end=0,p=3) = 0" as *u8, st_sma(bars, 0, 3), 0, st)
32
33 // -- signals: fast=2, slow=3, decided through bar i-1 (no look-ahead) --
34 let sig: *i64 = sys_mmap(8 * 8) as *i64
35 st_sma_cross(bars, nbars, 2, 3, sig)
36 chk("signal[0] = 0 (no history)" as *u8, sig[0], 0, st)
37 chk("signal[2] = 0 (no history)" as *u8, sig[2], 0, st)
38 chk("signal[3] = 1 (fast>slow)" as *u8, sig[3], 1, st)
39 chk("signal[4] = 1 (fast>slow)" as *u8, sig[4], 1, st)
40
41 // -- FULL LOOP: R3 signals -> R2 backtester --
42 let out: *i64 = sys_mmap(8 * 16) as *i64
43 bt_run(bars, nbars, sig, 1000000, 0, 0, 0, 0, out)
44 chk("loop n_trades = 1" as *u8, out[BT_TRADES], 1, st)
45 chk("loop n_losses = 1 (HONEST: strategy lost)" as *u8, out[BT_LOSSES], 1, st)
46 chk("loop net_pnl = -41650 cents" as *u8, out[BT_NET], 0 - 41650, st)
47 chk("loop final_equity = 958350 cents" as *u8, out[BT_FINAL_EQ], 958350, st)
48
49 // momentum (above SMA) / mean-reversion (below SMA), period 2, decided through bar i-1
50 let sig2: *i64 = sys_mmap(8 * 8) as *i64
51 st_above_sma(bars, nbars, 2, sig2)
52 chk("above_sma[2]=1 (1100>sma1050)" as *u8, sig2[2], 1, st)
53 chk("above_sma[3]=0 (1050<sma1075)" as *u8, sig2[3], 0, st)
54 chk("above_sma[4]=1 (1200>sma1125)" as *u8, sig2[4], 1, st)
55 let sig3: *i64 = sys_mmap(8 * 8) as *i64
56 st_below_sma(bars, nbars, 2, sig3)
57 chk("below_sma[3]=1 (1050<sma1075)" as *u8, sig3[3], 1, st)
58 chk("below_sma[4]=0 (1200>sma1125)" as *u8, sig3[4], 0, st)
59
60 // Donchian breakout: highest-close over the prior window; long on a break above it
61 chk("donchian_high(end=1,p=2) = 1100" as *u8, st_donchian_high(bars, 1, 2), 1100, st)
62 chk("donchian_high(end=3,p=2) = 1200" as *u8, st_donchian_high(bars, 3, 2), 1200, st)
63 let sig4: *i64 = sys_mmap(8 * 8) as *i64
64 st_donchian_signal(bars, nbars, 2, sig4)
65 chk("donchian[3]=0 (1050 < prior-high 1100)" as *u8, sig4[3], 0, st)
66 chk("donchian[4]=1 (1200 > prior-high 1100 = breakout)" as *u8, sig4[4], 1, st)
67
68 gw("nx_fin_strategy_gate: PASS=" as *u8); gn(st[0]); gw(" FAIL=" as *u8); gn(st[1]); gw("\n" as *u8)
69 if st[1] == 0 { gw("R3 nx_fin_strategy: GREEN (full R1->R3->R2 loop proven)\n" as *u8); return 0 }
70 gw("R3 nx_fin_strategy: RED\n" as *u8)
71 return 1
72}