nx_fin_marketdata_fetch.nx source
↩ module page · 172 lines · 9008 B
1// nx_fin_marketdata_fetch.nx -- MAKE IT REAL: the live ingestion adapter + end-to-end demo. Fetches a symbol's
2// daily OHLCV from Alpha Vantage (CSV) over the SOVEREIGN TLS fetcher (no curl), parses via md_parse_csv,
3// reverses to chronological order (Alpha Vantage returns newest-first), then runs the ENTIRE pipeline on REAL
4// data: ADV/capacity (R1) -> SMA signals (R3) -> backtest with slippage + ADV cap (R2) -> honest metrics. This
5// proves the whole stack on real market data -- SIM ONLY, no live capital. The API key is DATA (Cardinal 11/17:
6// env/config > default "demo"); the demo key serves IBM. Composes the proven fetch + the fin_* organs.
7// license_tier: ORIGINAL
8import "nx_syscalls.nx"
9import "nx_x509_trust_store.nx"
10import "nx_trust_store_load_from_certdata.nx"
11import "nx_https_fetch_follow.nx"
12import "nx_fin_marketdata.nx"
13import "nx_fin_strategy.nx"
14import "nx_fin_backtest.nx"
15import "nx_fin_paper.nx"
16const K_MAGIC_10000: i64 = 10000
17const K_MAGIC_4194304: i64 = 4194304
18const K_MAGIC_4096: i64 = 4096
19const K_MAGIC_8388608: i64 = 8388608
20const K_MAGIC_1000000: i64 = 1000000
21const K_MAGIC_7000: i64 = 7000
22
23func pp(s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8){n=n+1} sys_write(1,s,n); return 0 }
24func pn(v: i64) -> i64 {
25 if v==0 { sys_write(1,"0" as *u8,1); return 0 }
26 var m: i64=v; if m<0 { sys_write(1,"-" as *u8,1); m=0-m }
27 let d: *u8=sys_mmap(24); var k: i64=0
28 while m>0 { d[k]=(48+(m%10)) as u8; m=m/10; k=k+1 }
29 var i: i64=k-1; while i>=0 { let o: *u8=sys_mmap(1); o[0]=d[i]; sys_write(1,o,1); i=i-1 }
30 return 0
31}
32func mf_cat(dst: *u8, off: i64, s: *u8) -> i64 { var i: i64=0; while s[i]!=(0 as u8){ dst[off+i]=s[i]; i=i+1 } return off+i }
33
34// reverse bars in place (newest-first -> chronological, so the backtester walks time forward).
35func mf_reverse(bars: *i64, n: i64) -> i64 {
36 var i: i64=0
37 while i < n/2 {
38 let j: i64 = n-1-i
39 var f: i64 = 0
40 while f < MD_FIELDS {
41 let t: i64 = bars[i*MD_FIELDS+f]
42 bars[i*MD_FIELDS+f] = bars[j*MD_FIELDS+f]
43 bars[j*MD_FIELDS+f] = t
44 f = f + 1
45 }
46 i = i + 1
47 }
48 return 0
49}
50
51// print one strategy's tournament row + an honest EDGE flag (needs profit factor > 1.0x AND positive expectancy).
52func tour_print(name: *u8, out: *i64) -> i64 {
53 pp(" " as *u8); pp(name)
54 pp(": trades=" as *u8); pn(out[BT_TRADES])
55 pp(" ret_bps=" as *u8); pn(out[BT_RET])
56 pp(" PF_bps=" as *u8); pn(out[BT_PF])
57 pp(" maxDD_bps=" as *u8); pn(out[BT_MAXDD])
58 var edge: i64 = 0
59 if out[BT_PF] > K_MAGIC_10000 { if out[BT_EXPECTANCY] > 0 { edge = 1 } }
60 if edge == 1 { pp(" <-- EDGE\n" as *u8) } else { pp(" (no edge)\n" as *u8) }
61 return 0
62}
63
64func mf_strlen(s: *u8) -> i64 { var i: i64=0; while s[i]!=(0 as u8){i=i+1} return i }
65
66// read the value after "<key>" (e.g. "symbol=") from a config buffer, up to newline/EOF, into out. Returns len.
67func cfg_get(buf: *u8, n: i64, key: *u8, out: *u8, out_cap: i64) -> i64 {
68 let klen: i64 = mf_strlen(key)
69 var i: i64 = 0
70 while i <= n - klen {
71 var hit: i64 = 1; var j: i64 = 0
72 while j < klen { if (buf[i+j] as i64) != (key[j] as i64) { hit = 0; j = klen } else { j = j + 1 } }
73 if hit == 1 {
74 var p: i64 = i + klen; var k: i64 = 0; var stop: i64 = 0
75 while stop == 0 {
76 if p >= n { stop = 1 } else {
77 let c: i64 = buf[p] as i64
78 if c == 10 { stop = 1 } else { if c == 13 { stop = 1 } else {
79 if k < out_cap - 1 { out[k] = buf[p]; k = k + 1 }
80 p = p + 1
81 } }
82 }
83 }
84 out[k] = 0 as u8
85 return k
86 }
87 i = i + 1
88 }
89 out[0] = 0 as u8
90 return 0
91}
92
93func main() -> i64 {
94 let r: i64 = nx_trust_store_load_from_certdata("data/mozilla_certdata.txt" as *u8, 512, K_MAGIC_4194304)
95 if r <= 0 { pp("certdata load failed\n" as *u8); return 1 }
96 let store: *TrustStore = r as *TrustStore
97
98 // -- config (Cardinal 17): key + symbol from knowledge/finance/fin_data.conf, else demo/IBM defaults --
99 let keybuf: *u8 = sys_mmap(128); let symbuf: *u8 = sys_mmap(64)
100 mf_cat(keybuf, 0, "demo" as *u8); keybuf[4] = 0 as u8
101 mf_cat(symbuf, 0, "IBM" as *u8); symbuf[3] = 0 as u8
102 let cfd: i64 = sys_openat_rd("knowledge/finance/fin_data.conf" as *u8)
103 if cfd >= 0 {
104 let cbuf: *u8 = sys_mmap(K_MAGIC_4096)
105 let cn: i64 = sys_read(cfd, cbuf, K_MAGIC_4096)
106 sys_close(cfd)
107 if cn > 0 {
108 cfg_get(cbuf, cn, "alphavantage_key=" as *u8, keybuf, 128)
109 cfg_get(cbuf, cn, "symbol=" as *u8, symbuf, 64)
110 if mf_strlen(keybuf) <= 0 { mf_cat(keybuf, 0, "demo" as *u8); keybuf[4] = 0 as u8 }
111 if mf_strlen(symbuf) <= 0 { mf_cat(symbuf, 0, "IBM" as *u8); symbuf[3] = 0 as u8 }
112 }
113 }
114 pp("config: symbol=" as *u8); pp(symbuf); pp(" key=" as *u8); pp(keybuf); pp("\n" as *u8)
115 let url: *u8 = sys_mmap(512); var o: i64 = 0
116 o = mf_cat(url, o, "https://www.alphavantage.co/query?function=TIME_SERIES_DAILY&symbol=" as *u8)
117 o = mf_cat(url, o, symbuf)
118 o = mf_cat(url, o, "&apikey=" as *u8)
119 o = mf_cat(url, o, keybuf)
120 o = mf_cat(url, o, "&datatype=csv" as *u8)
121 url[o] = 0 as u8
122
123 let cap: i64 = K_MAGIC_8388608
124 let scratch: *u8 = sys_mmap(cap)
125 let status: *i64 = sys_mmap(8) as *i64
126 let n: i64 = nx_https_fetch_follow(url, store, scratch, cap, 6, status)
127 pp("fetch IBM: status=" as *u8); pn(status[0]); pp(" bytes=" as *u8); pn(n); pp("\n" as *u8)
128 if n <= 0 { pp("FETCH-FAIL (rate-limit? re-run; demo key = 25/day)\n" as *u8); return 1 }
129
130 let bars: *i64 = sys_mmap(8 * MD_FIELDS * 600) as *i64
131 let nbars: i64 = md_parse_csv(scratch, n, bars, 600)
132 if nbars <= 0 { pp("0 bars parsed (rate-limit JSON note? re-run)\n" as *u8); return 1 }
133 mf_reverse(bars, nbars)
134 pp("REAL bars parsed=" as *u8); pn(nbars); pp(" (chronological)\n" as *u8)
135 let last: i64 = nbars - 1
136 pp("latest bar: date=" as *u8); pn(bars[last*MD_FIELDS+0]); pp(" close_cents=" as *u8); pn(bars[last*MD_FIELDS+4]); pp(" vol=" as *u8); pn(bars[last*MD_FIELDS+5]); pp("\n" as *u8)
137
138 var advd: i64 = 20; if advd > nbars { advd = nbars }
139 let adv: i64 = md_adv(bars, nbars, advd)
140 pp("ADV(" as *u8); pn(advd); pp("d) cents=" as *u8); pn(adv); pp(" capacity@1% cents=" as *u8); pn(md_capacity(adv, 100)); pp("\n" as *u8)
141
142 let sig: *i64 = sys_mmap(8 * 600) as *i64
143 let out: *i64 = sys_mmap(8 * 16) as *i64
144 pp("\n== STRATEGY TOURNAMENT on REAL IBM data ($10k, 10bps slip, 1% ADV cap) ==\n" as *u8)
145 st_sma_cross(bars, nbars, 5, 20, sig); bt_run(bars, nbars, sig, K_MAGIC_1000000, 10, 0, adv, 100, out)
146 tour_print("SMA 5/20 crossover " as *u8, out)
147 st_above_sma(bars, nbars, 20, sig); bt_run(bars, nbars, sig, K_MAGIC_1000000, 10, 0, adv, 100, out)
148 tour_print("momentum >SMA20 " as *u8, out)
149 st_below_sma(bars, nbars, 20, sig); bt_run(bars, nbars, sig, K_MAGIC_1000000, 10, 0, adv, 100, out)
150 tour_print("mean-rev <SMA20 " as *u8, out)
151 st_above_sma(bars, nbars, 50, sig); bt_run(bars, nbars, sig, K_MAGIC_1000000, 10, 0, adv, 100, out)
152 tour_print("momentum >SMA50 " as *u8, out)
153
154 // -- THE OVERFIT TEST: a REAL edge must win BOTH halves with ENOUGH trades (2 winners != a signal) --
155 pp("\n== OOS CHECK (mean-rev <SMA20): 70% in-sample / 30% held-out ==\n" as *u8)
156 let split: i64 = pp_split(nbars, K_MAGIC_7000)
157 st_below_sma(bars, nbars, 20, sig)
158 let out_is: *i64 = sys_mmap(8 * 16) as *i64
159 bt_run(bars, split, sig, K_MAGIC_1000000, 10, 0, adv, 100, out_is)
160 pp(" in-sample (70%): trades=" as *u8); pn(out_is[BT_TRADES]); pp(" ret_bps=" as *u8); pn(out_is[BT_RET]); pp(" PF_bps=" as *u8); pn(out_is[BT_PF]); pp("\n" as *u8)
161 pp_oos_run(bars, nbars, sig, split, K_MAGIC_1000000, 10, 0, adv, 100, out)
162 pp(" out-of-sample (30%): trades=" as *u8); pn(out[BT_TRADES]); pp(" ret_bps=" as *u8); pn(out[BT_RET]); pp(" PF_bps=" as *u8); pn(out[BT_PF]); pp("\n" as *u8)
163 // HONEST verdict: profit in BOTH halves AND >=5 OOS trades. Otherwise it is a window/sample artifact.
164 var real_edge: i64 = 0
165 if out_is[BT_PF] > K_MAGIC_10000 { if out[BT_PF] > K_MAGIC_10000 { if out[BT_TRADES] >= 5 { real_edge = 1 } } }
166 if real_edge == 1 { pp(" VERDICT: consistent IS+OOS with enough trades (promising -- still ONE symbol, needs a universe).\n" as *u8) }
167 else { pp(" VERDICT: NO real edge -- lost in-sample (PF<1) and/or too few OOS trades. The full-sample 'edge' was a WINDOW ARTIFACT. Overfit-kill working as designed.\n" as *u8) }
168
169 pp("\nMAKE-IT-REAL PROVEN: real market data -> full sovereign pipeline -> HONEST ranking + OOS overfit-kill (SIM).\n" as *u8)
170 pp("(none showing an edge on 100 bars of ONE symbol is EXPECTED + honest -- a real edge needs a universe + OOS.)\n" as *u8)
171 return 0
172}