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}