code wiki / (root) / nx_fin_strategy.nx

nx_fin_strategy.nx source

↩ module page · 88 lines · 3540 B

1// nx_fin_strategy.nx -- R3: the SIGNAL generator (produces the long/flat signals the R2 backtester consumes). 2// v1 = SMA crossover (fast-SMA > slow-SMA -> long, else flat), computed with data THROUGH bar i-1 so there is 3// NO look-ahead by construction. Pure, deterministic, i64 (cents). Single-responsibility: it GENERATES signals; 4// R2 simulates them; R4 sizes them. Composes R1 (MD_FIELDS / close). Built to also compose the fundamental 5// organs (nx_fin_mgmt_grade, nx_fin_zscore) as quality GATES on entries in later versions -- microcap edge = 6// (technical trigger) AND (real business). license_tier: ORIGINAL 7import "nx_syscalls.nx" 8import "nx_fin_marketdata.nx" 9 10// simple moving average of CLOSE over `period` bars ending at index `end` (inclusive), in cents. Uses only 11// bars <= end (no look-ahead). Returns 0 if not enough history. 12func st_sma(bars: *i64, end: i64, period: i64) -> i64 { 13 if period <= 0 { return 0 } 14 if end < period - 1 { return 0 } 15 var sum: i64 = 0; var k: i64 = 0 16 while k < period { sum = sum + bars[(end-k)*MD_FIELDS+4]; k = k + 1 } 17 return sum / period 18} 19 20// write long/flat signals[i] in {0,1}: 1 iff fast-SMA > slow-SMA measured THROUGH bar i-1 (the decision for bar 21// i uses only prior data -> no look-ahead). Not-enough-history -> 0. Returns nbars. 22func st_sma_cross(bars: *i64, nbars: i64, fast: i64, slow: i64, signals: *i64) -> i64 { 23 var i: i64 = 0 24 while i < nbars { 25 var s: i64 = 0 26 let end: i64 = i - 1 27 if end >= slow - 1 { 28 let f: i64 = st_sma(bars, end, fast) 29 let sl: i64 = st_sma(bars, end, slow) 30 if f > sl { s = 1 } 31 } 32 signals[i] = s 33 i = i + 1 34 } 35 return nbars 36} 37 38// momentum: long when the last close is ABOVE its `period` SMA (trend-following). Decided through bar i-1. 39func st_above_sma(bars: *i64, nbars: i64, period: i64, signals: *i64) -> i64 { 40 var i: i64 = 0 41 while i < nbars { 42 var s: i64 = 0 43 let end: i64 = i - 1 44 if end >= period - 1 { 45 if bars[end*MD_FIELDS+4] > st_sma(bars, end, period) { s = 1 } 46 } 47 signals[i] = s 48 i = i + 1 49 } 50 return nbars 51} 52 53// mean-reversion: long when the last close is BELOW its `period` SMA (buy the dip). Decided through bar i-1. 54func st_below_sma(bars: *i64, nbars: i64, period: i64, signals: *i64) -> i64 { 55 var i: i64 = 0 56 while i < nbars { 57 var s: i64 = 0 58 let end: i64 = i - 1 59 if end >= period - 1 { 60 if bars[end*MD_FIELDS+4] < st_sma(bars, end, period) { s = 1 } 61 } 62 signals[i] = s 63 i = i + 1 64 } 65 return nbars 66} 67 68// highest close over the `period` bars ending at index `end` (inclusive). 0 if not enough history. 69func st_donchian_high(bars: *i64, end: i64, period: i64) -> i64 { 70 if end < period - 1 { return 0 } 71 var mx: i64 = 0; var i: i64 = end - period + 1 72 while i <= end { let c: i64 = bars[i*MD_FIELDS+4]; if c > mx { mx = c } i = i + 1 } 73 return mx 74} 75// Donchian breakout (Turtle-style trend-follow): long when the last close breaks above the highest close of the 76// PRIOR `period` bars (decided through bar i-1 -> no look-ahead). 77func st_donchian_signal(bars: *i64, nbars: i64, period: i64, signals: *i64) -> i64 { 78 var i: i64 = 0 79 while i < nbars { 80 var s: i64 = 0 81 if i - 2 >= period - 1 { 82 if bars[(i-1)*MD_FIELDS+4] > st_donchian_high(bars, i - 2, period) { s = 1 } 83 } 84 signals[i] = s 85 i = i + 1 86 } 87 return nbars 88}