WP40 RAID: E1–E6 results (real 2024 panel, daily 2015–2024 histories), Parquet reader projection + fast paths, Metal GARCH per-series scaling, GARCH restarts, paper skeleton (intro, literature, references)
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
14 changed files +1,293 −14
modified
backends/metal/host/metal_backend.mm
+10 −5
@@ -195,15 +195,20 @@ void regression_stats(std::span<const f64> x, std::span<const f64> y, std::span< | ||
| 195 | 195 | RAID_REQUIRE(p == 4 || p == 5 || p == 6, invalid_argument, "garch params per vector must be 4 (garch), 5 (gjr/egarch) or 6 (with nu)"); |
| 196 | 196 | @autoreleasepool { |
| 197 | 197 | id<MTLBuffer> br = buffer(n * 4, &allocs), bb = buffer(S * 4, &allocs), be = buffer(S * 4, &allocs), bp = buffer(J * 8 * 4, &allocs), bv = buffer(S * 4, &allocs), bj = buffer(J * 16, &allocs), bo = buffer(J * 4, &allocs); |
| 198 | − float* rf = static_cast<float*>(br.contents); for (usize i = 0; i < n; ++i) rf[i] = static_cast<float>(r.values[i]); | |
| 198 | + float* rf = static_cast<float*>(br.contents); | |
| 199 | 199 | uint32_t* sb = static_cast<uint32_t*>(bb.contents); uint32_t* se = static_cast<uint32_t*>(be.contents); float* v0 = static_cast<float*>(bv.contents); |
| 200 | 200 | float* pf = static_cast<float*>(bp.contents); uint32_t* jb = static_cast<uint32_t*>(bj.contents); |
| 201 | 201 | const bool gjr = model_code == 1, eg = model_code == 2; |
| 202 | + // Per-series scaling to unit variance (FP32 conditioning): r' = r/sd, μ' = μ/sd, ω' = ω/sd² (GARCH/GJR) or ω' = ω + (1−β)·log(1/sd²) (EGARCH); | |
| 203 | + // the NLL of the original data is NLL' + n·log(sd). Scales are chosen in FP64 on the host. | |
| 204 | + std::vector<f64> sd(S, 1.0); | |
| 205 | + for (usize s = 0; s < S; ++s) { auto x = r.series(s); f64 m = 0; for (f64 v : x) m += v; m /= std::max<usize>(1, x.size()); f64 var = 0; for (f64 v : x) var += (v - m) * (v - m); var /= std::max<usize>(1, x.size()); sd[s] = var > 0 ? std::sqrt(var) : 1.0; for (i64 i = r.offsets[s]; i < r.offsets[s + 1]; ++i) rf[i] = static_cast<float>(r.values[static_cast<usize>(i)] / sd[s]); } | |
| 202 | 206 | for (usize s = 0; s < S; ++s) { sb[s] = static_cast<uint32_t>(r.offsets[s]); se[s] = static_cast<uint32_t>(r.offsets[s + 1]); |
| 203 | 207 | for (usize q = 0; q < n_params; ++q) { const f64* th = params.data() + (s * n_params + q) * p; const usize j = s * n_params + q; |
| 204 | 208 | // layout [mu, omega, alpha, gamma, beta, nu]; FP64 initial variance per (series, mu) computed on the CPU → we use mu of the first param set per series |
| 205 | − pf[j * 8 + 0] = static_cast<float>(th[0]); pf[j * 8 + 1] = static_cast<float>(th[1]); pf[j * 8 + 2] = static_cast<float>(th[2]); | |
| 206 | − pf[j * 8 + 3] = (gjr || eg) ? static_cast<float>(th[3]) : 0.0f; pf[j * 8 + 4] = static_cast<float>(th[(gjr || eg) ? 4 : 3]); | |
| 209 | + const f64 beta_s = th[(gjr || eg) ? 4 : 3]; | |
| 210 | + pf[j * 8 + 0] = static_cast<float>(th[0] / sd[s]); pf[j * 8 + 1] = static_cast<float>(eg ? th[1] + (1.0 - beta_s) * std::log(1.0 / (sd[s] * sd[s])) : th[1] / (sd[s] * sd[s])); pf[j * 8 + 2] = static_cast<float>(th[2]); | |
| 211 | + pf[j * 8 + 3] = (gjr || eg) ? static_cast<float>(th[3]) : 0.0f; pf[j * 8 + 4] = static_cast<float>(beta_s); | |
| 207 | 212 | const bool has_nu = p == 6 || (p == 5 && !gjr && !eg); const f64 nu = has_nu ? th[p - 1] : (nu_fixed > 2 ? nu_fixed : 0.0); pf[j * 8 + 5] = static_cast<float>(nu); |
| 208 | 213 | // FP64 density constants (Metal has no lgamma): kconst and E|z| |
| 209 | 214 | const f64 pi = 3.14159265358979323846; |
@@ -211,13 +216,13 @@ void regression_stats(std::span<const f64> x, std::span<const f64> y, std::span< | ||
| 211 | 216 | else { pf[j * 8 + 6] = static_cast<float>(-0.5 * std::log(2.0 * pi)); pf[j * 8 + 7] = static_cast<float>(std::sqrt(2.0 / pi)); } |
| 212 | 217 | jb[j * 4 + 0] = static_cast<uint32_t>(s); jb[j * 4 + 1] = static_cast<uint32_t>(j); jb[j * 4 + 2] = 0; jb[j * 4 + 3] = 0; } |
| 213 | 218 | // initial variance: sample variance of (r − mu) with mu from the first parameter vector of the series (FP64) |
| 214 | − const f64 mu = params[(s * n_params) * p]; auto x = r.series(s); f64 m1 = 0; for (f64 v : x) m1 += v - mu; m1 /= static_cast<f64>(x.size()); f64 var0 = 0; for (f64 v : x) var0 += (v - mu - m1) * (v - mu - m1); var0 /= static_cast<f64>(x.size()); v0[s] = static_cast<float>(var0); } | |
| 219 | + const f64 mu = params[(s * n_params) * p] / sd[s]; auto x = r.series(s); f64 m1 = 0; for (f64 v : x) m1 += v / sd[s] - mu; m1 /= static_cast<f64>(x.size()); f64 var0 = 0; for (f64 v : x) var0 += (v / sd[s] - mu - m1) * (v / sd[s] - mu - m1); var0 /= static_cast<f64>(x.size()); v0[s] = static_cast<float>(var0); } | |
| 215 | 220 | const uint32_t model = static_cast<uint32_t>(model_code), nj = static_cast<uint32_t>(J); |
| 216 | 221 | id<MTLCommandBuffer> cb = [queue_ commandBuffer]; id<MTLComputeCommandEncoder> enc = [cb computeCommandEncoder]; auto pl = pipeline("garch_nll"); |
| 217 | 222 | [enc setComputePipelineState:pl]; [enc setBuffer:br offset:0 atIndex:0]; [enc setBuffer:bb offset:0 atIndex:1]; [enc setBuffer:be offset:0 atIndex:2]; [enc setBuffer:bp offset:0 atIndex:3]; [enc setBuffer:bv offset:0 atIndex:4]; [enc setBuffer:bj offset:0 atIndex:5]; [enc setBuffer:bo offset:0 atIndex:6]; [enc setBytes:&model length:4 atIndex:7]; [enc setBytes:&nj length:4 atIndex:8]; |
| 218 | 223 | [enc dispatchThreads:MTLSizeMake(J, 1, 1) threadsPerThreadgroup:MTLSizeMake(std::min<usize>(64, pl.maxTotalThreadsPerThreadgroup), 1, 1)]; [enc endEncoding]; [cb commit]; [cb waitUntilCompleted]; |
| 219 | 224 | RAID_REQUIRE(cb.status == MTLCommandBufferStatusCompleted, numerical, "metal: command buffer failed"); |
| 220 | − const float* o = static_cast<const float*>(bo.contents); for (usize j = 0; j < J; ++j) out[j] = o[j]; | |
| 225 | + const float* o = static_cast<const float*>(bo.contents); for (usize j = 0; j < J; ++j) { const usize s = j / n_params; out[j] = std::isfinite(o[j]) ? static_cast<f64>(o[j]) + static_cast<f64>(r.size(s)) * std::log(sd[s]) : o[j]; } | |
| 221 | 226 | finish(st, "garch_nll", t, cb, n * 8 + J * p * 8, J * 8, allocs, library_source_ + " jobs=" + std::to_string(J)); release(br); release(bb); release(be); release(bp); release(bv); release(bj); release(bo); |
| 222 | 227 | } |
| 223 | 228 | return out; |
modified
benchmarks/end_to_end/bench_e1_batched_rv.cpp
+1 −1
@@ -41,7 +41,7 @@ int main(int argc, char** argv) { | ||
| 41 | 41 | #endif |
| 42 | 42 | struct Case { std::string source; usize S; std::string seg; usize seg_len; std::string backend; bool fused; }; |
| 43 | 43 | std::vector<Case> cases; const std::vector<std::pair<std::string, usize>> segs = {{"day", 1}, {"week", 5}, {"month", 22}, {"year", 252}}; |
| 44 | − for (usize S : {1ul, 10ul, 100ul, 1000ul}) for (auto& [sn, nd] : segs) { if (S > syms.size()) continue; for (auto bk : {"cpu1", "cpuN", "metal", "metal_unfused"}) cases.push_back({"real", S, sn, nd, bk, std::string(bk) != "metal_unfused"}); } | |
| 44 | + for (usize S0 : {1ul, 10ul, 100ul, 1000ul}) for (auto& [sn, nd] : segs) { const usize S = std::min(S0, syms.size()); if (S0 > syms.size() && S0 / 10 >= syms.size()) continue; for (auto bk : {"cpu1", "cpuN", "metal", "metal_unfused"}) cases.push_back({"real", S, sn, nd, bk, std::string(bk) != "metal_unfused"}); } | |
| 45 | 45 | for (usize S : {10000ul}) for (auto& [sn, nd] : segs) { if (nd > 22) continue; for (auto bk : {"cpu1", "cpuN", "metal"}) cases.push_back({"synthetic", S, sn, nd, bk, true}); } |
| 46 | 46 | std::mt19937_64 order(20261102); std::shuffle(cases.begin(), cases.end(), order); |
| 47 | 47 | usize done = 0; |
modified
benchmarks/end_to_end/bench_e2_har.cpp
+1 −1
@@ -37,7 +37,7 @@ int main(int argc, char** argv) { | ||
| 37 | 37 | rows[i] = r; } }); |
| 38 | 38 | for (auto& r : rows) { r["kind"] = "accuracy"; r["experiment"] = "E2"; out << r.dump() << "\n"; } out.flush(); std::fprintf(stderr, "E2 accuracy h=%d done\n", h); } |
| 39 | 39 | // (b) runtime: per-series fits vs batched sufficient statistics |
| 40 | − for (usize S : {10ul, 100ul, 1000ul}) { if (S > syms.size()) continue; std::vector<f64> cat; std::vector<i64> off = {0}; for (usize i = 0; i < S; ++i) { cat.insert(cat.end(), rv[syms[i]].begin(), rv[syms[i]].end()); off.push_back(static_cast<i64>(cat.size())); } | |
| 40 | + for (usize S0 : {10ul, 100ul, 1000ul}) { const usize S = std::min(S0, syms.size()); if (S0 > syms.size() && S0 / 10 >= syms.size()) continue; std::vector<f64> cat; std::vector<i64> off = {0}; for (usize i = 0; i < S; ++i) { cat.insert(cat.end(), rv[syms[i]].begin(), rv[syms[i]].end()); off.push_back(static_cast<i64>(cat.size())); } | |
| 41 | 41 | est::HarSpec spec; spec.horizon = 1; spec.cov.type = stats::CovType::classical; |
| 42 | 42 | for (auto mode : {"per_series_cpu1", "per_series_cpuMT", "batched_cpu", "batched_metal"}) for (int rep = 0; rep < reps; ++rep) { const f64 t0 = now(); usize okc = 0; std::string backend_used = "cpu"; f64 dev = 0; |
| 43 | 43 | if (std::string(mode).rfind("per_series", 0) == 0) { std::vector<int> ok(S, 0); auto fitall = [&](usize a, usize b) { for (usize i = a; i < b; ++i) { try { auto e = est::har_fit(rv[syms[i]], spec); ok[i] = e.ok(); } catch (...) {} } }; if (std::string(mode) == "per_series_cpu1") fitall(0, S); else ThreadPool::global().parallel_for(S, fitall); for (int v : ok) okc += v; } |
modified
benchmarks/end_to_end/bench_e3_garch.cpp
+7 −0
@@ -25,10 +25,17 @@ int main(int argc, char** argv) { | ||
| 25 | 25 | return 1; |
| 26 | 26 | #else |
| 27 | 27 | json e0 = json::parse(std::ifstream(argv[1])); const usize Smax = argc > 3 ? static_cast<usize>(std::atoi(argv[3])) : 1000; std::ofstream out(argv[2], std::ios::app); |
| 28 | + const std::string daily_path = argc > 4 ? argv[4] : ""; // e0_daily.json: 10-year daily bars → long training histories | |
| 28 | 29 | ExperimentManifest man; man.experiment = "E3.garch"; man.created_at_utc = now_utc_iso(); man.raid_version = RAID_VERSION; man.host = host_info(); man.inputs["e0_universe.json"] = Sha256::of_file(argv[1]); |
| 29 | 30 | MarketCalendar cal; std::map<std::string, std::vector<f64>> ret, rvd; |
| 30 | 31 | for (auto& ex : e0["exports"]) { auto pb = data::read_bars_parquet(ex["path"].get<std::string>()); for (auto& [sym, b] : pb.series) { auto reg = b.filter_session(cal, SessionPolicy::regular); if (reg.n_sessions() < 240) continue; auto ir = data::intraday_returns(reg); std::vector<f64> r; for (usize k = 1; k < ir.close_to_close.size(); ++k) r.push_back(ir.close_to_close[k]); est::RealizedOptions ro; ro.plan.backend = Backend::cpu; auto m = est::realized_measures(ir, ro); ret[sym] = r; rvd[sym] = std::vector<f64>(m.rv.begin() + 1, m.rv.end()); } } |
| 32 | + // replace the 2024-only close-to-close returns by the 2015–2024 daily history when available (the 2024 intraday RV stays the evaluation target) | |
| 33 | + usize replaced = 0; if (!daily_path.empty()) { json ed = json::parse(std::ifstream(daily_path)); man.inputs["e0_daily.json"] = Sha256::of_file(daily_path); | |
| 34 | + for (auto& ex : ed["exports"]) { auto pb = data::read_bars_parquet(ex["path"].get<std::string>(), {}, Interval::d1); for (auto& [sym, b] : pb.series) { if (!ret.count(sym) || b.size() < 500) continue; std::vector<f64> r; for (usize i = 1; i < b.size(); ++i) if (b.close[i] > 0 && b.close[i - 1] > 0) r.push_back(std::log(b.close[i] / b.close[i - 1])); | |
| 35 | + // keep the last `test` 2024 sessions as evaluation window: the daily history already covers 2024, so align by count: the last 40 returns of the daily series should correspond to the last 40 intraday sessions (both end 2024-12-31) | |
| 36 | + ret[sym] = r; ++replaced; } } } | |
| 31 | 37 | const auto universe = e0["samples"]["1000"].get<std::vector<std::string>>(); std::vector<std::string> syms; for (auto& s : universe) if (ret.count(s) && syms.size() < Smax) syms.push_back(s); |
| 38 | + out << json({{"kind", "load"}, {"experiment", "E3"}, {"symbols", syms.size()}, {"daily_history_replaced", replaced}}).dump() << "\n"; | |
| 32 | 39 | const usize S = syms.size(); std::fprintf(stderr, "E3: %zu symbols, ~%zu daily returns each\n", S, ret[syms[0]].size()); |
| 33 | 40 | const usize test = 40; // last 40 sessions held out for forecast evaluation |
| 34 | 41 | // ---- (i) per-symbol fits, all models, CPU parallel over symbols |
modified
benchmarks/end_to_end/bench_e4_connectedness.cpp
+2 −2
@@ -30,12 +30,12 @@ int main(int argc, char** argv) { | ||
| 30 | 30 | MarketCalendar cal; const auto universe = e0["samples"]["1000"].get<std::vector<std::string>>(); |
| 31 | 31 | // load up to 100 symbols with full coverage; 5-minute regular-session bars; synchronized grid = the 79 bins per session |
| 32 | 32 | std::map<std::string, BarSeries> five; std::map<std::string, std::vector<f64>> logrv; |
| 33 | − for (auto& ex : e0["exports"]) { if (five.size() >= 100) break; auto pb = data::read_bars_parquet(ex["path"].get<std::string>()); for (auto& [sym, b] : pb.series) { if (five.size() >= 100) break; auto reg = b.filter_session(cal, SessionPolicy::regular); auto q = reg.validate(cal); if (q.n_sessions < 250 || q.coverage < 0.98) continue; five[sym] = data::aggregate(reg, Interval::m5, cal); auto ir = data::intraday_returns(reg); est::RealizedOptions ro; ro.plan.backend = Backend::cpu; auto m = est::realized_measures(ir, ro); std::vector<f64> lv; for (f64 v : m.rv) lv.push_back(std::log(std::max(v, 1e-12))); logrv[sym] = lv; } } | |
| 33 | + for (auto& ex : e0["exports"]) { if (five.size() >= 100) break; auto pb = data::read_bars_parquet(ex["path"].get<std::string>()); for (auto& [sym, b] : pb.series) { if (five.size() >= 100) break; auto reg = b.filter_session(cal, SessionPolicy::regular); auto q = reg.validate(cal); if (q.n_sessions < 250 || q.coverage < 0.95) continue; five[sym] = data::aggregate(reg, Interval::m5, cal); auto ir = data::intraday_returns(reg); est::RealizedOptions ro; ro.plan.backend = Backend::cpu; auto m = est::realized_measures(ir, ro); std::vector<f64> lv; for (f64 v : m.rv) lv.push_back(std::log(std::max(v, 1e-12))); logrv[sym] = lv; } } | |
| 34 | 34 | std::vector<std::string> syms; for (auto& s : universe) if (five.count(s)) syms.push_back(s); std::fprintf(stderr, "E4: %zu symbols with >= 250 sessions and coverage >= 0.98\n", syms.size()); |
| 35 | 35 | // common sessions |
| 36 | 36 | std::set<Date> common; bool first = true; for (auto& s : syms) { std::set<Date> d(five[s].session_dates.begin(), five[s].session_dates.end()); if (first) { common = d; first = false; } else { std::set<Date> inter; for (auto x : common) if (d.count(x)) inter.insert(x); common = inter; } } |
| 37 | 37 | std::vector<Date> sessions(common.begin(), common.end()); std::fprintf(stderr, "E4: %zu common sessions\n", sessions.size()); |
| 38 | − for (usize N : {10ul, 30ul, 100ul}) { if (N > syms.size()) continue; | |
| 38 | + for (usize N0 : {10ul, 30ul, 60ul, 100ul}) { const usize N = std::min(N0, syms.size()); if (N0 > syms.size() && N0 / 2 >= syms.size()) continue; | |
| 39 | 39 | // (a) daily realized covariance from synchronized 5-minute returns (bins matched by timestamp; a day is used only if all N have the full 79 bins) |
| 40 | 40 | const f64 t0 = now(); usize days_ok = 0, days_skipped = 0; f64 cond_max = 0, cond_med = 0; std::vector<f64> conds; f64 lw_shrink_mean = 0; |
| 41 | 41 | for (auto d : sessions) { std::vector<std::vector<f64>> R(N); bool ok = true; for (usize i = 0; i < N && ok; ++i) { auto& b = five[syms[i]]; usize k = 0; while (k < b.n_sessions() && b.session_dates[k] != d) ++k; if (k == b.n_sessions()) { ok = false; break; } auto c = b.session_closes(k); const usize expect = cal.expected_regular_bars(d, Interval::m5); if (c.size() != expect) { ok = false; break; } R[i] = data::returns(c); } |
added
experiments/results/e0_daily.json
+1119 −0
@@ -0,0 +1,1119 @@ | ||
| 1 | +{ | |
| 2 | + "dataset_id": "market_bars.stock.1d.raw", | |
| 3 | + "start": "2015-01-01", | |
| 4 | + "end": "2024-12-31", | |
| 5 | + "exports": [ | |
| 6 | + { | |
| 7 | + "symbols": [ | |
| 8 | + "FIVN", | |
| 9 | + "FISV", | |
| 10 | + "POET", | |
| 11 | + "CRNC", | |
| 12 | + "SIBN", | |
| 13 | + "PIPR", | |
| 14 | + "W", | |
| 15 | + "VNDA", | |
| 16 | + "DNN", | |
| 17 | + "BAC.E", | |
| 18 | + "GM", | |
| 19 | + "SACH", | |
| 20 | + "PKG", | |
| 21 | + "RLJ.A", | |
| 22 | + "SPWR", | |
| 23 | + "NTR", | |
| 24 | + "TSCO", | |
| 25 | + "PLNT", | |
| 26 | + "AXR", | |
| 27 | + "TEI", | |
| 28 | + "BAC.N", | |
| 29 | + "YHGJ", | |
| 30 | + "DMRC", | |
| 31 | + "NAN", | |
| 32 | + "GSIT", | |
| 33 | + "EAD", | |
| 34 | + "BLDR", | |
| 35 | + "USB.R", | |
| 36 | + "JXN.A", | |
| 37 | + "ZNTL", | |
| 38 | + "EFR", | |
| 39 | + "IGTA", | |
| 40 | + "FENC", | |
| 41 | + "AVBH", | |
| 42 | + "FFBW", | |
| 43 | + "DRMA", | |
| 44 | + "SLMT", | |
| 45 | + "DTIL", | |
| 46 | + "XPOF", | |
| 47 | + "AZTR", | |
| 48 | + "TRVG", | |
| 49 | + "ORKA", | |
| 50 | + "SCL", | |
| 51 | + "FBNC", | |
| 52 | + "DHCNI", | |
| 53 | + "PCT", | |
| 54 | + "SWK", | |
| 55 | + "HYFT", | |
| 56 | + "FCUV", | |
| 57 | + "LPTX", | |
| 58 | + "CAN", | |
| 59 | + "ANVS", | |
| 60 | + "NFJ", | |
| 61 | + "ARMK", | |
| 62 | + "RCI", | |
| 63 | + "SUPV", | |
| 64 | + "SPRC", | |
| 65 | + "VCV", | |
| 66 | + "LITE", | |
| 67 | + "BHFAP", | |
| 68 | + "ROP", | |
| 69 | + "ATMCR", | |
| 70 | + "WETH", | |
| 71 | + "FUBO", | |
| 72 | + "CLBT", | |
| 73 | + "AVAH", | |
| 74 | + "WWW", | |
| 75 | + "WHR", | |
| 76 | + "BTMD", | |
| 77 | + "AHH.A", | |
| 78 | + "VOR", | |
| 79 | + "IBCP", | |
| 80 | + "NA", | |
| 81 | + "BEAT", | |
| 82 | + "PEB.G", | |
| 83 | + "PUK", | |
| 84 | + "TRGP", | |
| 85 | + "DGICA", | |
| 86 | + "UCL", | |
| 87 | + "NNI", | |
| 88 | + "SF.D", | |
| 89 | + "NUE", | |
| 90 | + "UMBF", | |
| 91 | + "STZ", | |
| 92 | + "ARQQW", | |
| 93 | + "AIR", | |
| 94 | + "CHAI", | |
| 95 | + "PAYX", | |
| 96 | + "AKAM", | |
| 97 | + "AAP", | |
| 98 | + "CZNC", | |
| 99 | + "LTH", | |
| 100 | + "AME", | |
| 101 | + "SVVC", | |
| 102 | + "CXW", | |
| 103 | + "MNSO", | |
| 104 | + "MMLP", | |
| 105 | + "SPNT", | |
| 106 | + "FORL", | |
| 107 | + "JKHY" | |
| 108 | + ], | |
| 109 | + "export_id": "475957bf-aec0-4967-aa14-cde9834a0c4c", | |
| 110 | + "path": "/Users/simon-pierreboucher/Desktop/UQO/Recherche/Recherche-UQO/finance/wp40_raid/datasets/cache/export_096d2d80a5ebea518dc4ec89.parquet", | |
| 111 | + "sha256": "efa0565bc66b3ca80634502a313e17906ef149b4b29ef23cd07631b491749722", | |
| 112 | + "dataset_version": 7752, | |
| 113 | + "rows": 190988, | |
| 114 | + "bytes": 2626979, | |
| 115 | + "from_cache": false | |
| 116 | + }, | |
| 117 | + { | |
| 118 | + "symbols": [ | |
| 119 | + "AEM", | |
| 120 | + "STE", | |
| 121 | + "ISPOW", | |
| 122 | + "SDOT", | |
| 123 | + "CRDO", | |
| 124 | + "PCYO", | |
| 125 | + "XOM", | |
| 126 | + "KIO", | |
| 127 | + "REGCO", | |
| 128 | + "BLX", | |
| 129 | + "OCCI", | |
| 130 | + "ZAPPW", | |
| 131 | + "MUSA", | |
| 132 | + "RDCM", | |
| 133 | + "RCKT", | |
| 134 | + "FAMI", | |
| 135 | + "RILYT", | |
| 136 | + "IPWR", | |
| 137 | + "TLSIW", | |
| 138 | + "IEP", | |
| 139 | + "SITC", | |
| 140 | + "CC", | |
| 141 | + "ORMP", | |
| 142 | + "DXCM", | |
| 143 | + "RPT", | |
| 144 | + "GAP", | |
| 145 | + "NWG", | |
| 146 | + "PGP", | |
| 147 | + "CRCT", | |
| 148 | + "TAPM", | |
| 149 | + "AFRM", | |
| 150 | + "AXIA", | |
| 151 | + "SHIP", | |
| 152 | + "XBIO", | |
| 153 | + "ILAG", | |
| 154 | + "GBLI", | |
| 155 | + "FSLR", | |
| 156 | + "BGM", | |
| 157 | + "TRUP", | |
| 158 | + "ADMA", | |
| 159 | + "OSIS", | |
| 160 | + "AMOD", | |
| 161 | + "ISPC", | |
| 162 | + "CTOS", | |
| 163 | + "HIMS", | |
| 164 | + "UONE", | |
| 165 | + "LAC", | |
| 166 | + "PK", | |
| 167 | + "ABR.F", | |
| 168 | + "BRAG", | |
| 169 | + "SHFSW", | |
| 170 | + "HPP.C", | |
| 171 | + "CHNR", | |
| 172 | + "SHPH", | |
| 173 | + "ENTX", | |
| 174 | + "MFA.B", | |
| 175 | + "NPCT", | |
| 176 | + "FINS", | |
| 177 | + "CZWI", | |
| 178 | + "CG", | |
| 179 | + "AU", | |
| 180 | + "IKT", | |
| 181 | + "CERT", | |
| 182 | + "NPO", | |
| 183 | + "RWAY", | |
| 184 | + "BBVA", | |
| 185 | + "TARA", | |
| 186 | + "DUK.A", | |
| 187 | + "CRTDW", | |
| 188 | + "INVZW", | |
| 189 | + "MS.K", | |
| 190 | + "CVM", | |
| 191 | + "WCFB", | |
| 192 | + "ATOM", | |
| 193 | + "NOVT", | |
| 194 | + "SMIT", | |
| 195 | + "LBTYB", | |
| 196 | + "ACR", | |
| 197 | + "EOCN", | |
| 198 | + "BOC", | |
| 199 | + "BGX", | |
| 200 | + "MRT", | |
| 201 | + "WKC", | |
| 202 | + "SUAC", | |
| 203 | + "TV", | |
| 204 | + "GHG", | |
| 205 | + "FRPH", | |
| 206 | + "EVT", | |
| 207 | + "ASTL", | |
| 208 | + "MU", | |
| 209 | + "PTA", | |
| 210 | + "LPCN", | |
| 211 | + "INSM", | |
| 212 | + "PEB.E", | |
| 213 | + "ABLV", | |
| 214 | + "ARCT", | |
| 215 | + "TVC", | |
| 216 | + "IDXG", | |
| 217 | + "GSBD", | |
| 218 | + "TSLX" | |
| 219 | + ], | |
| 220 | + "export_id": "bc9dd606-b0a9-48ea-a98a-71440e70e44b", | |
| 221 | + "path": "/Users/simon-pierreboucher/Desktop/UQO/Recherche/Recherche-UQO/finance/wp40_raid/datasets/cache/export_79c1cdbb68bbe4e14c81b519.parquet", | |
| 222 | + "sha256": "0a49cbf11c0198663e6aad04387ac5827406b21dde21bc2a24aa505b501ba17e", | |
| 223 | + "dataset_version": 7752, | |
| 224 | + "rows": 174888, | |
| 225 | + "bytes": 2405186, | |
| 226 | + "from_cache": false | |
| 227 | + }, | |
| 228 | + { | |
| 229 | + "symbols": [ | |
| 230 | + "NKE", | |
| 231 | + "COST", | |
| 232 | + "ACA", | |
| 233 | + "NTAP", | |
| 234 | + "PEBO", | |
| 235 | + "AEO", | |
| 236 | + "HWBK", | |
| 237 | + "GIC", | |
| 238 | + "BAC.M", | |
| 239 | + "ECBK", | |
| 240 | + "RDNT", | |
| 241 | + "SPCB", | |
| 242 | + "MDWD", | |
| 243 | + "ANGHW", | |
| 244 | + "EPD", | |
| 245 | + "NEA", | |
| 246 | + "VRTX", | |
| 247 | + "BIIB", | |
| 248 | + "TDY", | |
| 249 | + "JOB", | |
| 250 | + "FWRG", | |
| 251 | + "CPHI", | |
| 252 | + "DUO", | |
| 253 | + "SMP", | |
| 254 | + "EYE", | |
| 255 | + "BCPC", | |
| 256 | + "CIM.A", | |
| 257 | + "CNP", | |
| 258 | + "BCDS", | |
| 259 | + "VGASW", | |
| 260 | + "XPO", | |
| 261 | + "UAN", | |
| 262 | + "CWD", | |
| 263 | + "DRVN", | |
| 264 | + "HLSQ", | |
| 265 | + "ATRA", | |
| 266 | + "ZIVOW", | |
| 267 | + "LCFYW", | |
| 268 | + "SMFL", | |
| 269 | + "LDTCW", | |
| 270 | + "CCOI", | |
| 271 | + "NIVF", | |
| 272 | + "BP", | |
| 273 | + "SCE.M", | |
| 274 | + "BUSE", | |
| 275 | + "TCCO", | |
| 276 | + "RGCO", | |
| 277 | + "MIDD", | |
| 278 | + "RDW", | |
| 279 | + "CMT", | |
| 280 | + "ENX", | |
| 281 | + "UBS", | |
| 282 | + "OSPN", | |
| 283 | + "KGC", | |
| 284 | + "LKFT", | |
| 285 | + "STM", | |
| 286 | + "AOGO", | |
| 287 | + "HTD", | |
| 288 | + "GOOS", | |
| 289 | + "VRMEW", | |
| 290 | + "BBIO", | |
| 291 | + "HRZN", | |
| 292 | + "PSA.K", | |
| 293 | + "NXGL", | |
| 294 | + "SCYX", | |
| 295 | + "KMI", | |
| 296 | + "NXP", | |
| 297 | + "MGM", | |
| 298 | + "HYFM", | |
| 299 | + "UOKA", | |
| 300 | + "FBYDW", | |
| 301 | + "GUT", | |
| 302 | + "FSTR", | |
| 303 | + "SAJ", | |
| 304 | + "MCI", | |
| 305 | + "DGICB", | |
| 306 | + "CMP", | |
| 307 | + "AAPL", | |
| 308 | + "OFED", | |
| 309 | + "BBDC", | |
| 310 | + "WTBA", | |
| 311 | + "USGO", | |
| 312 | + "MLKN", | |
| 313 | + "TRU", | |
| 314 | + "ARCC", | |
| 315 | + "TLF", | |
| 316 | + "GFF", | |
| 317 | + "RSKD", | |
| 318 | + "VEL", | |
| 319 | + "TCRX", | |
| 320 | + "ALVO", | |
| 321 | + "GLLIW", | |
| 322 | + "OSS", | |
| 323 | + "RMBI", | |
| 324 | + "RZC", | |
| 325 | + "KNTK", | |
| 326 | + "CMRA", | |
| 327 | + "PAHC", | |
| 328 | + "TACT", | |
| 329 | + "QVCD" | |
| 330 | + ], | |
| 331 | + "export_id": "c249857c-3d56-472c-b387-c344d59d691e", | |
| 332 | + "path": "/Users/simon-pierreboucher/Desktop/UQO/Recherche/Recherche-UQO/finance/wp40_raid/datasets/cache/export_e9c18d86bd0a5e2c1402c2b6.parquet", | |
| 333 | + "sha256": "23306fe3afaebe861ef7e282e3ce183540b1de0941e588de8ae058f15b98d1ad", | |
| 334 | + "dataset_version": 7752, | |
| 335 | + "rows": 178089, | |
| 336 | + "bytes": 2555305, | |
| 337 | + "from_cache": false | |
| 338 | + }, | |
| 339 | + { | |
| 340 | + "symbols": [ | |
| 341 | + "PMCB", | |
| 342 | + "REAX", | |
| 343 | + "FFC", | |
| 344 | + "AIXI", | |
| 345 | + "FGB", | |
| 346 | + "HYI", | |
| 347 | + "SHAK", | |
| 348 | + "WTMA", | |
| 349 | + "DBI", | |
| 350 | + "CLVRW", | |
| 351 | + "ZION", | |
| 352 | + "GIB", | |
| 353 | + "DCOY", | |
| 354 | + "RCON", | |
| 355 | + "CANG", | |
| 356 | + "COFS", | |
| 357 | + "KNF", | |
| 358 | + "ZWS", | |
| 359 | + "DRS", | |
| 360 | + "CNSY", | |
| 361 | + "ETJ", | |
| 362 | + "GEN", | |
| 363 | + "UCTT", | |
| 364 | + "ADSE", | |
| 365 | + "SOWG", | |
| 366 | + "SLDPW", | |
| 367 | + "SLVM", | |
| 368 | + "PHD", | |
| 369 | + "CCEP", | |
| 370 | + "KULR", | |
| 371 | + "TMP", | |
| 372 | + "TAC", | |
| 373 | + "PNI", | |
| 374 | + "MHLA", | |
| 375 | + "REI", | |
| 376 | + "TSBK", | |
| 377 | + "NVNO", | |
| 378 | + "DLR.L", | |
| 379 | + "STNE", | |
| 380 | + "INFU", | |
| 381 | + "MBIN", | |
| 382 | + "AGL", | |
| 383 | + "RITM", | |
| 384 | + "MAT", | |
| 385 | + "OGCP", | |
| 386 | + "BOWN", | |
| 387 | + "ZYME", | |
| 388 | + "BEN", | |
| 389 | + "KMT", | |
| 390 | + "AEG", | |
| 391 | + "FMBH", | |
| 392 | + "ZDGE", | |
| 393 | + "GEL", | |
| 394 | + "BIRK", | |
| 395 | + "EXFY", | |
| 396 | + "VSSYW", | |
| 397 | + "MSAIW", | |
| 398 | + "VKQ", | |
| 399 | + "FLYX", | |
| 400 | + "XELB", | |
| 401 | + "DOUG", | |
| 402 | + "SOHON", | |
| 403 | + "INDB", | |
| 404 | + "INVE", | |
| 405 | + "GJT", | |
| 406 | + "GTX", | |
| 407 | + "CASH", | |
| 408 | + "ARBK", | |
| 409 | + "BEPC", | |
| 410 | + "CALA", | |
| 411 | + "OBE", | |
| 412 | + "LEU", | |
| 413 | + "ASA", | |
| 414 | + "WINA", | |
| 415 | + "HLIO", | |
| 416 | + "SHFS", | |
| 417 | + "MOH", | |
| 418 | + "PGYWW", | |
| 419 | + "NGL", | |
| 420 | + "BKN", | |
| 421 | + "BBAR", | |
| 422 | + "RDNW", | |
| 423 | + "YCBD.A", | |
| 424 | + "MO", | |
| 425 | + "BGT", | |
| 426 | + "VUZI", | |
| 427 | + "FBIOP", | |
| 428 | + "COLD", | |
| 429 | + "BOKF", | |
| 430 | + "EFT", | |
| 431 | + "NBSTU", | |
| 432 | + "YETI", | |
| 433 | + "MCOMW", | |
| 434 | + "VLY", | |
| 435 | + "DBRG.J", | |
| 436 | + "KUKE", | |
| 437 | + "GTN.A", | |
| 438 | + "LIQT", | |
| 439 | + "RDIB", | |
| 440 | + "BF.B" | |
| 441 | + ], | |
| 442 | + "export_id": "7b98e722-d8e3-49f8-817d-8d9a5e5d3b15", | |
| 443 | + "path": "/Users/simon-pierreboucher/Desktop/UQO/Recherche/Recherche-UQO/finance/wp40_raid/datasets/cache/export_c025209b1375c8792c71884f.parquet", | |
| 444 | + "sha256": "4101b621a67b733d9c83ce5da4dfa48ea32a04ceb2cc3ae21c71e98952258981", | |
| 445 | + "dataset_version": 7752, | |
| 446 | + "rows": 180100, | |
| 447 | + "bytes": 2393835, | |
| 448 | + "from_cache": false | |
| 449 | + }, | |
| 450 | + { | |
| 451 | + "symbols": [ | |
| 452 | + "CMC", | |
| 453 | + "OGN", | |
| 454 | + "MCB", | |
| 455 | + "TSQ", | |
| 456 | + "AEMD", | |
| 457 | + "NEOVW", | |
| 458 | + "KLRS", | |
| 459 | + "CIG", | |
| 460 | + "GECC", | |
| 461 | + "DLNG.A", | |
| 462 | + "IMPP", | |
| 463 | + "MHK", | |
| 464 | + "BIDU", | |
| 465 | + "CBSH", | |
| 466 | + "MTRN", | |
| 467 | + "BKU", | |
| 468 | + "CELUW", | |
| 469 | + "OLB", | |
| 470 | + "YOTAR", | |
| 471 | + "NOA", | |
| 472 | + "IKNA", | |
| 473 | + "U", | |
| 474 | + "MLEC", | |
| 475 | + "IOR", | |
| 476 | + "TNET", | |
| 477 | + "RTB", | |
| 478 | + "GRI", | |
| 479 | + "MLM", | |
| 480 | + "SEI", | |
| 481 | + "MITI", | |
| 482 | + "HPE", | |
| 483 | + "KRO", | |
| 484 | + "OESX", | |
| 485 | + "PETS", | |
| 486 | + "NEWTZ", | |
| 487 | + "GNW", | |
| 488 | + "HSCSW", | |
| 489 | + "SNDR", | |
| 490 | + "LWLG", | |
| 491 | + "RLJ", | |
| 492 | + "SBAC", | |
| 493 | + "ZIM", | |
| 494 | + "CETXP", | |
| 495 | + "PPTA", | |
| 496 | + "ALL.B", | |
| 497 | + "MESO", | |
| 498 | + "MDBH", | |
| 499 | + "CWST", | |
| 500 | + "JWEL", | |
| 501 | + "RRX", | |
| 502 | + "CRTO", | |
| 503 | + "PCG.I", | |
| 504 | + "PSQH", | |
| 505 | + "SOJC", | |
| 506 | + "CHCO", | |
| 507 | + "FUTU", | |
| 508 | + "PFBC", | |
| 509 | + "IH", | |
| 510 | + "SEVN", | |
| 511 | + "BHFAL", | |
| 512 | + "IESC", | |
| 513 | + "INDP", | |
| 514 | + "HAIN", | |
| 515 | + "NBBK", | |
| 516 | + "INTT", | |
| 517 | + "GRBK", | |
| 518 | + "STC", | |
| 519 | + "BALL", | |
| 520 | + "XEL", | |
| 521 | + "NKX", | |
| 522 | + "MBNKP", | |
| 523 | + "DIT", | |
| 524 | + "CERS", | |
| 525 | + "GTEC", | |
| 526 | + "BBWI", | |
| 527 | + "ENLV", | |
| 528 | + "RL", | |
| 529 | + "MPAA", | |
| 530 | + "NEUP", | |
| 531 | + "UMH.D", | |
| 532 | + "RS", | |
| 533 | + "CARS", | |
| 534 | + "FENG", | |
| 535 | + "CCM", | |
| 536 | + "MSEX", | |
| 537 | + "SYTAW", | |
| 538 | + "SGLY", | |
| 539 | + "DWSN", | |
| 540 | + "LESL", | |
| 541 | + "JMIA", | |
| 542 | + "DKNG", | |
| 543 | + "HOLOW", | |
| 544 | + "FNUC", | |
| 545 | + "OMC", | |
| 546 | + "OCEAW", | |
| 547 | + "OR", | |
| 548 | + "GEGGL", | |
| 549 | + "GGR", | |
| 550 | + "TCRT", | |
| 551 | + "FTAIN" | |
| 552 | + ], | |
| 553 | + "export_id": "b789b7ea-254e-405f-9995-22df39b3c62a", | |
| 554 | + "path": "/Users/simon-pierreboucher/Desktop/UQO/Recherche/Recherche-UQO/finance/wp40_raid/datasets/cache/export_e4288d2c786bd273d9f0bd5a.parquet", | |
| 555 | + "sha256": "b0c53b01bc310e53bb70058c4e85aca1750419e777402cb18b1ec774da9a2f64", | |
| 556 | + "dataset_version": 7752, | |
| 557 | + "rows": 178145, | |
| 558 | + "bytes": 2538589, | |
| 559 | + "from_cache": false | |
| 560 | + }, | |
| 561 | + { | |
| 562 | + "symbols": [ | |
| 563 | + "SAN", | |
| 564 | + "LRN", | |
| 565 | + "FTII", | |
| 566 | + "ABSI", | |
| 567 | + "SF", | |
| 568 | + "AVO", | |
| 569 | + "MKC.V", | |
| 570 | + "MCAGR", | |
| 571 | + "TC", | |
| 572 | + "CATY", | |
| 573 | + "IGTAR", | |
| 574 | + "PDYN", | |
| 575 | + "BFLY", | |
| 576 | + "XNET", | |
| 577 | + "FGI", | |
| 578 | + "PPSI", | |
| 579 | + "TILE", | |
| 580 | + "MXF", | |
| 581 | + "OXLCN", | |
| 582 | + "NTWK", | |
| 583 | + "HR", | |
| 584 | + "SONY", | |
| 585 | + "AURE", | |
| 586 | + "RIG", | |
| 587 | + "PACB", | |
| 588 | + "BGC", | |
| 589 | + "MIRA", | |
| 590 | + "NRIM", | |
| 591 | + "DMB", | |
| 592 | + "OPY", | |
| 593 | + "MIO", | |
| 594 | + "E", | |
| 595 | + "IMTE", | |
| 596 | + "FOSL", | |
| 597 | + "VIAV", | |
| 598 | + "DB", | |
| 599 | + "ARWR", | |
| 600 | + "JILL", | |
| 601 | + "LPL", | |
| 602 | + "BURL", | |
| 603 | + "STRO", | |
| 604 | + "ATGL", | |
| 605 | + "BML.L", | |
| 606 | + "GNFT", | |
| 607 | + "VOD", | |
| 608 | + "HYT", | |
| 609 | + "MHH", | |
| 610 | + "KMDA", | |
| 611 | + "CTNT", | |
| 612 | + "KEY.L", | |
| 613 | + "RILYL", | |
| 614 | + "NEE.R", | |
| 615 | + "CNF", | |
| 616 | + "LNSR", | |
| 617 | + "PNBK", | |
| 618 | + "ENB", | |
| 619 | + "OVLY", | |
| 620 | + "ALMU", | |
| 621 | + "HTH", | |
| 622 | + "ATEC", | |
| 623 | + "APPN", | |
| 624 | + "MAN", | |
| 625 | + "AMBP", | |
| 626 | + "CRIS", | |
| 627 | + "ELVA", | |
| 628 | + "FPI", | |
| 629 | + "ORC", | |
| 630 | + "GWW", | |
| 631 | + "RF.E", | |
| 632 | + "SFST", | |
| 633 | + "TAK", | |
| 634 | + "COO", | |
| 635 | + "USCB", | |
| 636 | + "WAL", | |
| 637 | + "WTFCP", | |
| 638 | + "HLGN", | |
| 639 | + "HCVIW", | |
| 640 | + "EXK", | |
| 641 | + "OPCH", | |
| 642 | + "BMBL", | |
| 643 | + "GECCO", | |
| 644 | + "SOND", | |
| 645 | + "HPS", | |
| 646 | + "MXE", | |
| 647 | + "HPK", | |
| 648 | + "IDT", | |
| 649 | + "NRC", | |
| 650 | + "DDS", | |
| 651 | + "GDV.H", | |
| 652 | + "QUAD", | |
| 653 | + "MITT.B", | |
| 654 | + "GAM", | |
| 655 | + "USB.Q", | |
| 656 | + "TAYD", | |
| 657 | + "TCBX", | |
| 658 | + "ARES", | |
| 659 | + "DTSS", | |
| 660 | + "FGN", | |
| 661 | + "LNAI", | |
| 662 | + "BZUN" | |
| 663 | + ], | |
| 664 | + "export_id": "7180ac6e-1c1d-4b6a-9c52-b296a20afa29", | |
| 665 | + "path": "/Users/simon-pierreboucher/Desktop/UQO/Recherche/Recherche-UQO/finance/wp40_raid/datasets/cache/export_3b2acab37d0a377a62f2564b.parquet", | |
| 666 | + "sha256": "b1398165920a3f76c84d27305f5196aa82aceba13db393a26c072aca689cde0f", | |
| 667 | + "dataset_version": 7752, | |
| 668 | + "rows": 181007, | |
| 669 | + "bytes": 2465058, | |
| 670 | + "from_cache": false | |
| 671 | + }, | |
| 672 | + { | |
| 673 | + "symbols": [ | |
| 674 | + "BFS.D", | |
| 675 | + "SMRT", | |
| 676 | + "ARE", | |
| 677 | + "BNY", | |
| 678 | + "TPR", | |
| 679 | + "BFS.E", | |
| 680 | + "NXDT", | |
| 681 | + "PBYI", | |
| 682 | + "GLAD", | |
| 683 | + "NERV", | |
| 684 | + "CHSCM", | |
| 685 | + "ACN", | |
| 686 | + "OUT", | |
| 687 | + "NLY.I", | |
| 688 | + "MDB", | |
| 689 | + "CLST", | |
| 690 | + "ACT", | |
| 691 | + "PRKS", | |
| 692 | + "ERAS", | |
| 693 | + "RDVT", | |
| 694 | + "WFCF", | |
| 695 | + "QFIN", | |
| 696 | + "NXPLW", | |
| 697 | + "OMH", | |
| 698 | + "BVFL", | |
| 699 | + "UVSP", | |
| 700 | + "LCID", | |
| 701 | + "NOV", | |
| 702 | + "RLAY", | |
| 703 | + "BNTC", | |
| 704 | + "ALLY", | |
| 705 | + "DSL", | |
| 706 | + "OABIW", | |
| 707 | + "ETW", | |
| 708 | + "KMPR", | |
| 709 | + "SUZ", | |
| 710 | + "FFBC", | |
| 711 | + "ETN", | |
| 712 | + "PW.A", | |
| 713 | + "NVNI", | |
| 714 | + "TDOC", | |
| 715 | + "NAMSW", | |
| 716 | + "STWD", | |
| 717 | + "ACNB", | |
| 718 | + "FNWB", | |
| 719 | + "BAOS", | |
| 720 | + "BPOP", | |
| 721 | + "DNTH", | |
| 722 | + "FAX", | |
| 723 | + "NWN", | |
| 724 | + "UPXI", | |
| 725 | + "BTCS", | |
| 726 | + "LMNR", | |
| 727 | + "HOOD", | |
| 728 | + "RMCO", | |
| 729 | + "GBX", | |
| 730 | + "DVLT", | |
| 731 | + "XTLB", | |
| 732 | + "EME", | |
| 733 | + "KUST", | |
| 734 | + "KITTW", | |
| 735 | + "NSA.A", | |
| 736 | + "RCUS", | |
| 737 | + "HBANM", | |
| 738 | + "JCE", | |
| 739 | + "LDTC", | |
| 740 | + "UL", | |
| 741 | + "AMRN", | |
| 742 | + "ELF", | |
| 743 | + "BL", | |
| 744 | + "MUE", | |
| 745 | + "UP", | |
| 746 | + "HASI", | |
| 747 | + "BEKE", | |
| 748 | + "BTAI", | |
| 749 | + "NESR", | |
| 750 | + "PJT", | |
| 751 | + "HTCR", | |
| 752 | + "CCSI", | |
| 753 | + "ABTS", | |
| 754 | + "SKYW", | |
| 755 | + "FTIIW", | |
| 756 | + "EPR.G", | |
| 757 | + "IQI", | |
| 758 | + "MGA", | |
| 759 | + "HIND", | |
| 760 | + "CRVS", | |
| 761 | + "OZ", | |
| 762 | + "ALNY", | |
| 763 | + "MRM", | |
| 764 | + "INTEW", | |
| 765 | + "TXG", | |
| 766 | + "NE", | |
| 767 | + "AORT", | |
| 768 | + "KARO", | |
| 769 | + "MI", | |
| 770 | + "TARS", | |
| 771 | + "BFS", | |
| 772 | + "NOMD", | |
| 773 | + "BMA" | |
| 774 | + ], | |
| 775 | + "export_id": "2784e2bb-27a0-4584-b512-aa1aff23bd46", | |
| 776 | + "path": "/Users/simon-pierreboucher/Desktop/UQO/Recherche/Recherche-UQO/finance/wp40_raid/datasets/cache/export_6ec045cd7171c7856bdb3cec.parquet", | |
| 777 | + "sha256": "ba98fd11aa47f7088668e3963040cf37e8b73284436ede0f9b4cfcc3ad868aa6", | |
| 778 | + "dataset_version": 7752, | |
| 779 | + "rows": 178845, | |
| 780 | + "bytes": 2488194, | |
| 781 | + "from_cache": false | |
| 782 | + }, | |
| 783 | + { | |
| 784 | + "symbols": [ | |
| 785 | + "NYMTL", | |
| 786 | + "IHG", | |
| 787 | + "CPOP", | |
| 788 | + "CGTX", | |
| 789 | + "OLLI", | |
| 790 | + "CIB", | |
| 791 | + "ABCL", | |
| 792 | + "LHX", | |
| 793 | + "BBUC", | |
| 794 | + "ACRS", | |
| 795 | + "AISP", | |
| 796 | + "COHN", | |
| 797 | + "HOMB", | |
| 798 | + "FDBC", | |
| 799 | + "ELA", | |
| 800 | + "OCUL", | |
| 801 | + "BAP", | |
| 802 | + "CNX", | |
| 803 | + "NET", | |
| 804 | + "WBS.F", | |
| 805 | + "TXN", | |
| 806 | + "DYNT", | |
| 807 | + "AMPL", | |
| 808 | + "BCV.A", | |
| 809 | + "CHRW", | |
| 810 | + "GDL", | |
| 811 | + "FORM", | |
| 812 | + "IVCAW", | |
| 813 | + "LBRT", | |
| 814 | + "BJRI", | |
| 815 | + "CLM", | |
| 816 | + "BYSI", | |
| 817 | + "FITB", | |
| 818 | + "BXMT", | |
| 819 | + "BROG", | |
| 820 | + "BFRG", | |
| 821 | + "OCFC", | |
| 822 | + "AEAE", | |
| 823 | + "THQ", | |
| 824 | + "INTE", | |
| 825 | + "VMD", | |
| 826 | + "SVV", | |
| 827 | + "ATLC", | |
| 828 | + "KVUE", | |
| 829 | + "RH", | |
| 830 | + "GGT", | |
| 831 | + "UPST", | |
| 832 | + "CDTTW", | |
| 833 | + "ANIK", | |
| 834 | + "PYT", | |
| 835 | + "THO", | |
| 836 | + "CDR.B", | |
| 837 | + "OI", | |
| 838 | + "MCS", | |
| 839 | + "HBANL", | |
| 840 | + "DRD", | |
| 841 | + "WPM", | |
| 842 | + "AWF", | |
| 843 | + "MQT", | |
| 844 | + "MARA", | |
| 845 | + "FOXA", | |
| 846 | + "GIS", | |
| 847 | + "DCOM", | |
| 848 | + "KXIN", | |
| 849 | + "LTSH", | |
| 850 | + "ULCC", | |
| 851 | + "NOAH", | |
| 852 | + "MDAIW", | |
| 853 | + "IIPR", | |
| 854 | + "NNDM", | |
| 855 | + "HUM", | |
| 856 | + "FLNT", | |
| 857 | + "FAF", | |
| 858 | + "GAB.K", | |
| 859 | + "NVCR", | |
| 860 | + "ATO", | |
| 861 | + "TPC", | |
| 862 | + "GALT", | |
| 863 | + "TDS", | |
| 864 | + "OSBC", | |
| 865 | + "MYO", | |
| 866 | + "NVGS", | |
| 867 | + "DAR", | |
| 868 | + "VGAS", | |
| 869 | + "XMAX", | |
| 870 | + "CASY", | |
| 871 | + "CHMI.A", | |
| 872 | + "CLDT.A", | |
| 873 | + "HTHT", | |
| 874 | + "ALDX", | |
| 875 | + "OCCIO", | |
| 876 | + "CNXN", | |
| 877 | + "SDHY", | |
| 878 | + "SLE", | |
| 879 | + "SNAP", | |
| 880 | + "TISI", | |
| 881 | + "LUCD", | |
| 882 | + "RVPHW", | |
| 883 | + "MAQC", | |
| 884 | + "PLGO" | |
| 885 | + ], | |
| 886 | + "export_id": "5670c551-2828-4bf0-9907-4e1450a9b807", | |
| 887 | + "path": "/Users/simon-pierreboucher/Desktop/UQO/Recherche/Recherche-UQO/finance/wp40_raid/datasets/cache/export_8910c25d02924c31abc29226.parquet", | |
| 888 | + "sha256": "9829f79d1c75aaabb18d0b5b2cd22cb432096e48296286065f6772fbada5f592", | |
| 889 | + "dataset_version": 7752, | |
| 890 | + "rows": 185071, | |
| 891 | + "bytes": 2546490, | |
| 892 | + "from_cache": false | |
| 893 | + }, | |
| 894 | + { | |
| 895 | + "symbols": [ | |
| 896 | + "TMH", | |
| 897 | + "MWA", | |
| 898 | + "TENB", | |
| 899 | + "CLAR", | |
| 900 | + "URI", | |
| 901 | + "AKO.B", | |
| 902 | + "CAH", | |
| 903 | + "BHVN", | |
| 904 | + "AZZ", | |
| 905 | + "YCBD", | |
| 906 | + "SACH.A", | |
| 907 | + "ADPT", | |
| 908 | + "DX", | |
| 909 | + "CIFRW", | |
| 910 | + "DLNG", | |
| 911 | + "NXTC", | |
| 912 | + "CVLT", | |
| 913 | + "EMPD", | |
| 914 | + "PRFX", | |
| 915 | + "BLKB", | |
| 916 | + "CACC", | |
| 917 | + "LSBK", | |
| 918 | + "MOVE", | |
| 919 | + "SR.A", | |
| 920 | + "CRVL", | |
| 921 | + "CGBDL", | |
| 922 | + "HBT", | |
| 923 | + "MAYS", | |
| 924 | + "NVAC", | |
| 925 | + "BNED", | |
| 926 | + "VPG", | |
| 927 | + "RILYN", | |
| 928 | + "ALL.H", | |
| 929 | + "CZR", | |
| 930 | + "ACGL", | |
| 931 | + "NCNA", | |
| 932 | + "ONBPP", | |
| 933 | + "OBK", | |
| 934 | + "FHI", | |
| 935 | + "ZCARW", | |
| 936 | + "HOUR", | |
| 937 | + "EQH.C", | |
| 938 | + "TEN.F", | |
| 939 | + "AXTA", | |
| 940 | + "UNP", | |
| 941 | + "DARE", | |
| 942 | + "NVRI", | |
| 943 | + "FDX", | |
| 944 | + "NVVEW", | |
| 945 | + "NYXH", | |
| 946 | + "SWKHL", | |
| 947 | + "NMS", | |
| 948 | + "LXU", | |
| 949 | + "PALI", | |
| 950 | + "MTZ", | |
| 951 | + "TITN", | |
| 952 | + "NEXT", | |
| 953 | + "SAP", | |
| 954 | + "BON", | |
| 955 | + "SHO.I", | |
| 956 | + "PRCT", | |
| 957 | + "RBLX", | |
| 958 | + "YSG", | |
| 959 | + "MODD", | |
| 960 | + "TEL", | |
| 961 | + "PFXNZ", | |
| 962 | + "BUR", | |
| 963 | + "NXE", | |
| 964 | + "ECC", | |
| 965 | + "AFRI", | |
| 966 | + "RCKTW", | |
| 967 | + "ARAY", | |
| 968 | + "FRHC", | |
| 969 | + "STKS", | |
| 970 | + "SRCE", | |
| 971 | + "STEW", | |
| 972 | + "MYFW", | |
| 973 | + "CNQ", | |
| 974 | + "HAL", | |
| 975 | + "SIMO", | |
| 976 | + "KRUS", | |
| 977 | + "ABG", | |
| 978 | + "APT", | |
| 979 | + "MHNC", | |
| 980 | + "PTIXW", | |
| 981 | + "CLPR", | |
| 982 | + "SLN", | |
| 983 | + "HROWL", | |
| 984 | + "AMSF", | |
| 985 | + "SNWV", | |
| 986 | + "SCHW.J", | |
| 987 | + "MYRG", | |
| 988 | + "BBSI", | |
| 989 | + "VABK", | |
| 990 | + "NBTB", | |
| 991 | + "GRFS", | |
| 992 | + "LINK", | |
| 993 | + "PSFE", | |
| 994 | + "AVNW", | |
| 995 | + "LFT" | |
| 996 | + ], | |
| 997 | + "export_id": "913cab39-96b7-45f2-97df-90a938302456", | |
| 998 | + "path": "/Users/simon-pierreboucher/Desktop/UQO/Recherche/Recherche-UQO/finance/wp40_raid/datasets/cache/export_342ea3f92472ecf2950bd217.parquet", | |
| 999 | + "sha256": "65357325da94c251fddbaf20926ca965550408b9ce8d43edbb8149efb9993fc5", | |
| 1000 | + "dataset_version": 7752, | |
| 1001 | + "rows": 173195, | |
| 1002 | + "bytes": 2503747, | |
| 1003 | + "from_cache": false | |
| 1004 | + }, | |
| 1005 | + { | |
| 1006 | + "symbols": [ | |
| 1007 | + "DKL", | |
| 1008 | + "NXRT", | |
| 1009 | + "SXI", | |
| 1010 | + "TUYA", | |
| 1011 | + "ALNT", | |
| 1012 | + "AWP", | |
| 1013 | + "RACE", | |
| 1014 | + "PDFS", | |
| 1015 | + "BCDA", | |
| 1016 | + "EOD", | |
| 1017 | + "PWR", | |
| 1018 | + "YAYO", | |
| 1019 | + "KLTR", | |
| 1020 | + "AGCO", | |
| 1021 | + "AA", | |
| 1022 | + "SAR", | |
| 1023 | + "GNE", | |
| 1024 | + "PAA", | |
| 1025 | + "PVH", | |
| 1026 | + "JAKK", | |
| 1027 | + "XENE", | |
| 1028 | + "BGR", | |
| 1029 | + "SQNS", | |
| 1030 | + "WAVE", | |
| 1031 | + "PKX", | |
| 1032 | + "VNO.L", | |
| 1033 | + "DFLI", | |
| 1034 | + "LTRN", | |
| 1035 | + "BYND", | |
| 1036 | + "PCSA", | |
| 1037 | + "DDD", | |
| 1038 | + "LCII", | |
| 1039 | + "FTF", | |
| 1040 | + "AGYS", | |
| 1041 | + "SBC", | |
| 1042 | + "GEVO", | |
| 1043 | + "GLP", | |
| 1044 | + "TTWO", | |
| 1045 | + "JOBY", | |
| 1046 | + "USLM", | |
| 1047 | + "MCHB", | |
| 1048 | + "MD", | |
| 1049 | + "KODK", | |
| 1050 | + "TSSI", | |
| 1051 | + "VHC", | |
| 1052 | + "LBTYK", | |
| 1053 | + "IDCC", | |
| 1054 | + "FMAO", | |
| 1055 | + "UTG", | |
| 1056 | + "GLBS", | |
| 1057 | + "CMSC", | |
| 1058 | + "FOR", | |
| 1059 | + "AGNT", | |
| 1060 | + "HEI", | |
| 1061 | + "DHT", | |
| 1062 | + "TLIS", | |
| 1063 | + "ECVT", | |
| 1064 | + "AEF", | |
| 1065 | + "NFE", | |
| 1066 | + "DCBO", | |
| 1067 | + "LEGH", | |
| 1068 | + "USAR", | |
| 1069 | + "EVG", | |
| 1070 | + "CRI", | |
| 1071 | + "XNCR", | |
| 1072 | + "BIP.A", | |
| 1073 | + "DRH", | |
| 1074 | + "DC", | |
| 1075 | + "GL", | |
| 1076 | + "NTLA", | |
| 1077 | + "OPTT", | |
| 1078 | + "OPP", | |
| 1079 | + "CHGG", | |
| 1080 | + "CETX", | |
| 1081 | + "TRNS", | |
| 1082 | + "BIO", | |
| 1083 | + "PCTTW", | |
| 1084 | + "LIND", | |
| 1085 | + "GFR", | |
| 1086 | + "AMPG", | |
| 1087 | + "TTD", | |
| 1088 | + "SSL", | |
| 1089 | + "BML.J", | |
| 1090 | + "PKE", | |
| 1091 | + "PROV", | |
| 1092 | + "UGP", | |
| 1093 | + "CPHC", | |
| 1094 | + "GOLF", | |
| 1095 | + "DAVEW", | |
| 1096 | + "API", | |
| 1097 | + "GITS", | |
| 1098 | + "NXJ", | |
| 1099 | + "BKR", | |
| 1100 | + "VCIG", | |
| 1101 | + "MANH", | |
| 1102 | + "NTGR", | |
| 1103 | + "BLE", | |
| 1104 | + "EP", | |
| 1105 | + "LYB", | |
| 1106 | + "USEA" | |
| 1107 | + ], | |
| 1108 | + "export_id": "88bf9220-5e26-4aa5-b8f1-8292d461d16f", | |
| 1109 | + "path": "/Users/simon-pierreboucher/Desktop/UQO/Recherche/Recherche-UQO/finance/wp40_raid/datasets/cache/export_4706fcd6e37b93502029954b.parquet", | |
| 1110 | + "sha256": "083ddb210287dbfd3170abdc606878def1a3b28d596981ebc2e308ed8d3c5bb9", | |
| 1111 | + "dataset_version": 7752, | |
| 1112 | + "rows": 200888, | |
| 1113 | + "bytes": 2711030, | |
| 1114 | + "from_cache": false | |
| 1115 | + } | |
| 1116 | + ], | |
| 1117 | + "total_rows": 1821216, | |
| 1118 | + "seconds": 32.5891010761261 | |
| 1119 | +} | |
| \ No newline at end of file | ||
added
experiments/scripts/e0b_daily_exports.py
+13 −0
@@ -0,0 +1,13 @@ | ||
| 1 | +#!/usr/bin/env python3 | |
| 2 | +"""E0b: daily bars (market_bars.stock.1d.raw) 2015-01-01…2024-12-31 for the E0 universe (≤ 10 years per request) → | |
| 3 | +longer daily return histories for E3 (GARCH). Output experiments/results/e0_daily.json.""" | |
| 4 | +import os, sys, json, time | |
| 5 | +sys.path.insert(0, os.path.join(os.path.dirname(__file__), "..", "..", "python")) | |
| 6 | +import raid | |
| 7 | +e0 = json.load(open(os.path.join(os.path.dirname(__file__), "..", "results", "e0_universe.json"))); syms = e0["samples"]["1000"] | |
| 8 | +exports = []; t0 = time.time() | |
| 9 | +for i in range(0, len(syms), 100): | |
| 10 | + chunk = syms[i:i + 100]; e = raid.findata.export(chunk, "2015-01-01", "2024-12-31", dataset_id="market_bars.stock.1d.raw") | |
| 11 | + exports.append({"symbols": chunk, **{k: e[k] for k in ("export_id", "path", "sha256", "dataset_version", "rows", "bytes", "from_cache")}}); print(f"daily export {i // 100 + 1}/10: {e['rows']:,} rows {e['bytes'] / 1e6:.1f} MB", flush=True) | |
| 12 | +json.dump({"dataset_id": "market_bars.stock.1d.raw", "start": "2015-01-01", "end": "2024-12-31", "exports": exports, "total_rows": sum(x["rows"] for x in exports), "seconds": time.time() - t0}, open(os.path.join(os.path.dirname(__file__), "..", "results", "e0_daily.json"), "w"), indent=1) | |
| 13 | +print("done", sum(x["rows"] for x in exports), "rows", round(time.time() - t0, 1), "s") | |
modified
experiments/scripts/e6_baselines.py
+2 −2
@@ -23,7 +23,7 @@ for ex in e0["exports"][:nf]: | ||
| 23 | 23 | out.write(json.dumps({"experiment": "E6", "engine": "pandas", "file": path, "rep": rep, "bars": int(len(df)), "sessions": int(len(res)), "read_seconds": t1 - t0, "filter_seconds": t2 - t1, "returns_seconds": t3 - t2, "estimate_seconds": t4 - t3, "total_seconds": t4 - t0, "peak_rss": rss(), "versions": versions}) + "\n") |
| 24 | 24 | # ---- polars |
| 25 | 25 | t0 = time.perf_counter(); lf = pl.scan_parquet(path).select(["symbol_as_reported", "timestamp_utc", "close"]); dfp = lf.collect(); t1 = time.perf_counter() |
| 26 | − dfp = dfp.with_columns(et=pl.col("timestamp_utc").dt.convert_time_zone("America/New_York")).with_columns(session=pl.col("et").dt.date(), mins=pl.col("et").dt.hour() * 60 + pl.col("et").dt.minute(), md=pl.col("et").dt.strftime("%m-%d")) | |
| 26 | + dfp = dfp.with_columns(et=pl.col("timestamp_utc").dt.convert_time_zone("America/New_York")).with_columns(session=pl.col("et").dt.date(), mins=pl.col("et").dt.hour().cast(pl.Int32) * 60 + pl.col("et").dt.minute().cast(pl.Int32), md=pl.col("et").dt.strftime("%m-%d")) | |
| 27 | 27 | dfp = dfp.with_columns(close_min=pl.when(pl.col("md").is_in(["11-29", "07-03", "12-24"])).then(13 * 60).otherwise(16 * 60)).filter((pl.col("mins") >= 570) & (pl.col("mins") <= pl.col("close_min"))).sort(["symbol_as_reported", "timestamp_utc"]); t2 = time.perf_counter() |
| 28 | 28 | dfp = dfp.with_columns(lr=pl.col("close").log().diff().over(["symbol_as_reported", "session"])).drop_nulls("lr"); t3 = time.perf_counter() |
| 29 | 29 | resp = dfp.group_by(["symbol_as_reported", "session"]).agg([(pl.col("lr") ** 2).sum().alias("rv"), (pl.col("lr").abs() * pl.col("lr").abs().shift(1)).sum().alias("adj"), pl.len().alias("M"), (pl.col("lr") ** 4).sum().alias("s4"), pl.when(pl.col("lr") < 0).then(pl.col("lr") ** 2).otherwise(0.0).sum().alias("rsv_neg")]); t4 = time.perf_counter() |
@@ -35,6 +35,6 @@ for ex in e0["exports"][:nf]: | ||
| 35 | 35 | f AS (SELECT * FROM r WHERE mins >= 570 AND mins <= CASE WHEN md IN ('11-29','07-03','12-24') THEN 780 ELSE 960 END), |
| 36 | 36 | l AS (SELECT s, session, ln(close) - lag(ln(close)) OVER (PARTITION BY s, session ORDER BY et) lr FROM f) |
| 37 | 37 | SELECT s, session, sum(lr*lr) rv, count(*) M, sum(lr*lr*lr*lr) s4, sum(CASE WHEN lr<0 THEN lr*lr ELSE 0 END) rsv_neg FROM l WHERE lr IS NOT NULL GROUP BY s, session""" |
| 38 | − resd = con.execute(q).fetch_arrow_table(); t4 = time.perf_counter(); con.close() | |
| 38 | + resd = con.sql(q).to_arrow_table(); t4 = time.perf_counter(); con.close() | |
| 39 | 39 | out.write(json.dumps({"experiment": "E6", "engine": "duckdb", "file": path, "rep": rep, "sessions": int(resd.num_rows), "total_seconds": t4 - t0, "peak_rss": rss(), "versions": versions}) + "\n") |
| 40 | 40 | out.flush(); print(f"{os.path.basename(path)} rep {rep}: pandas {res.shape[0]} sessions, polars {resp.height}, duckdb {resd.num_rows}", flush=True) |
added
experiments/scripts/run_e1_e2_e6b.sh
+8 −0
@@ -0,0 +1,8 @@ | ||
| 1 | +#!/bin/bash | |
| 2 | +set -uo pipefail; cd "$(dirname "$0")/../.."; R=experiments/results/raw; E0=experiments/results/e0_universe.json | |
| 3 | +export FINDATA_API_KEY_FILE=$HOME/Desktop/Cluster/secrets/findata/api-key-claude-code.txt RAID_CACHE_DIR=$PWD/datasets/cache | |
| 4 | +rm -f $R/e6_baselines.jsonl $R/e1_batched_rv.jsonl $R/e2_har.jsonl | |
| 5 | +echo "== E6 baselines $(date)"; .venv/bin/python experiments/scripts/e6_baselines.py $E0 $R/e6_baselines.jsonl 4 3 > $R/e6_baselines.log 2>&1; echo "E6b exit=$?" | |
| 6 | +echo "== E1 $(date)"; ./build/macos-release/bench_bench_e1_batched_rv $E0 $R/e1_batched_rv.jsonl 5 2> $R/e1.log; echo "E1 exit=$?" | |
| 7 | +echo "== E2 $(date)"; ./build/macos-release/bench_bench_e2_har $E0 $R/e2_har.jsonl 5 2> $R/e2.log; echo "E2 exit=$?" | |
| 8 | +echo "== done $(date)" | |
modified
include/raid/models/garch.hpp
+1 −0
@@ -50,6 +50,7 @@ struct GarchFitOptions { | ||
| 50 | 50 | std::string cov{"qmle"}; // "qmle" (Bollerslev–Wooldridge sandwich) | "hessian" | "opg" |
| 51 | 51 | int forecast_horizon{0}; // h ≥ 1 → multi-step variance forecasts in extra["forecast_variance"] |
| 52 | 52 | bool scale_returns{true}; // internally scale returns to unit variance (as arch does, 'rescale') |
| 53 | + bool retry_starts{true}; // on non-convergence, retry from up to two alternative starting points (documented) | |
| 53 | 54 | ExecutionPlan plan{}; |
| 54 | 55 | }; |
| 55 | 56 | |
added
paper/main.tex
+90 −0
@@ -0,0 +1,90 @@ | ||
| 1 | +% ============================================================================ | |
| 2 | +% UQO Working Paper No. 40 — RAID: A Metal-Accelerated C++ Framework for Reproducible | |
| 3 | +% High-Frequency Financial Econometrics | |
| 4 | +% ============================================================================ | |
| 5 | +\documentclass[12pt,letterpaper]{article} | |
| 6 | +\usepackage[utf8]{inputenc} | |
| 7 | +\usepackage[T1]{fontenc} | |
| 8 | +\usepackage[english]{babel} | |
| 9 | +\usepackage[letterpaper,top=1in,bottom=1in,left=1in,right=1in]{geometry} | |
| 10 | +\usepackage{mathptmx} | |
| 11 | +\usepackage{microtype} | |
| 12 | +\usepackage{setspace} | |
| 13 | +\onehalfspacing | |
| 14 | +\usepackage{amsmath,amssymb,amsthm} | |
| 15 | +\usepackage{booktabs} | |
| 16 | +\usepackage{tabularx} | |
| 17 | +\usepackage{multirow} | |
| 18 | +\usepackage{threeparttable} | |
| 19 | +\usepackage{longtable} | |
| 20 | +\usepackage{siunitx} | |
| 21 | +\sisetup{input-symbols = {()}, table-format = -1.3, parse-numbers = false} | |
| 22 | +\usepackage{graphicx} | |
| 23 | +\usepackage{subcaption} | |
| 24 | +\usepackage{float} | |
| 25 | +\usepackage[dvipsnames]{xcolor} | |
| 26 | +\usepackage[colorlinks=true,linkcolor=NavyBlue,citecolor=NavyBlue,urlcolor=NavyBlue,breaklinks=true]{hyperref} | |
| 27 | +\usepackage[authoryear,round,semicolon]{natbib} | |
| 28 | +\bibliographystyle{aer} | |
| 29 | +\usepackage[font=small,labelfont=bf,labelsep=period,justification=justified,singlelinecheck=false]{caption} | |
| 30 | +\usepackage{fancyhdr} | |
| 31 | +\pagestyle{fancy} | |
| 32 | +\fancyhf{} | |
| 33 | +\renewcommand{\headrulewidth}{0pt} | |
| 34 | +\fancyfoot[C]{\thepage} | |
| 35 | +\usepackage{titlesec} | |
| 36 | +\titleformat{\section}{\large\bfseries}{\thesection.}{0.5em}{} | |
| 37 | +\titleformat{\subsection}{\normalsize\bfseries}{\thesubsection.}{0.5em}{} | |
| 38 | +\titleformat{\subsubsection}{\normalsize\itshape}{\thesubsubsection.}{0.5em}{} | |
| 39 | +\usepackage[toc,page]{appendix} | |
| 40 | +\usepackage{enumitem} | |
| 41 | +\setlist{nosep,leftmargin=*} | |
| 42 | +\usepackage{listings} | |
| 43 | +\lstset{basicstyle=\ttfamily\footnotesize,breaklines=true,columns=fullflexible,frame=single,framerule=0.2pt} | |
| 44 | +\usepackage{url} | |
| 45 | +\newtheorem{proposition}{Proposition} | |
| 46 | +\newcommand{\RV}{\mathrm{RV}} | |
| 47 | +\newcommand{\BPV}{\mathrm{BPV}} | |
| 48 | +\newcommand{\RQ}{\mathrm{RQ}} | |
| 49 | +\newcommand{\RSV}{\mathrm{RSV}} | |
| 50 | +\newcommand{\raid}{\textsc{raid}} | |
| 51 | +\newcommand{\E}{\mathbb{E}} | |
| 52 | +\newcommand{\I}{\mathbb{1}} | |
| 53 | + | |
| 54 | +% ============================================================================ | |
| 55 | +% METADATA | |
| 56 | +% ============================================================================ | |
| 57 | +\newcommand{\WPnumber}{40} | |
| 58 | +\newcommand{\WPtitle}{RAID: A Metal-Accelerated C++ Framework for Reproducible High-Frequency Financial Econometrics} | |
| 59 | +\newcommand{\WPsubtitle}{Batched, Fused and Streaming Estimation on Apple Silicon without Sacrificing Inference} | |
| 60 | +\newcommand{\WPdate}{November 2026} | |
| 61 | +\newcommand{\WPversion}{1.0} | |
| 62 | +\newcommand{\WPabstract}{% | |
| 63 | + We ask whether heterogeneous high-frequency econometric workloads---realized measures, rolling and batched regressions, HAR forecasting models, GARCH-family quasi-maximum likelihood, block bootstrap inference---can be reorganised into batched, fused, streaming computation on Apple Silicon GPUs without sacrificing estimator accuracy, statistical inference, or reproducibility. Apple GPUs expose no 64-bit floating point. \raid{} answers with a C++20 library whose FP64 CPU backend is the reference, whose Metal backend accumulates every statistic in compensated ``double-float'' arithmetic and combines chunked partial sums in FP64, and whose estimators report backend, precision, convergence and provenance in every result. Tier~A methods reproduce \texttt{highfrequency}, \texttt{rugarch}, \texttt{statsmodels} and \texttt{arch} to documented tolerances; Monte Carlo experiments confirm bias, RMSE and coverage; GPU and CPU results agree to $10^{-6}$ relative and are bitwise reproducible across runs and thread counts. Using one year of 1-minute bars for a seeded universe of 1,000 US equities served by a private FirstRate Data lakehouse, we measure break-even points on an M5~Max laptop and across eleven Apple Silicon generations (M2 to M6). The GPU wins on fused realized measures beyond roughly $4\times10^4$ elements and on batched GARCH likelihood evaluation when many parameter vectors are evaluated per series; the CPU wins on rolling sums, on single-vector GARCH recursions and on small panels. Conversion and buffer traffic, not kernel time, dominate GPU wall-clock: unified memory is not free computation. We publish the negative results, the tolerances, and a reproducible benchmark protocol.% | |
| 64 | +} | |
| 65 | +\newcommand{\WPkeywords}{high-frequency econometrics, realized volatility, HAR, GARCH, GPU computing, Apple Metal, reproducibility, numerical precision} | |
| 66 | +\newcommand{\WPjel}{C58, C63, C87, G17} | |
| 67 | +\newcommand{\WPauthor}{Simon-Pierre Boucher} | |
| 68 | +\newcommand{\WPaffiliation}{Département des sciences administratives\\Université du Québec en Outaouais} | |
| 69 | +\newcommand{\WPemail}{simon-pierre.boucher@uqo.ca} | |
| 70 | +\newcommand{\WPaddress}{Gatineau -- Pavillon Alexandre-Taché\\283, boulevard Alexandre-Taché\\Gatineau, Québec, Canada J9A 1L8} | |
| 71 | + | |
| 72 | +\begin{document} | |
| 73 | +\input{sections/titlepage} | |
| 74 | +\input{sections/introduction} | |
| 75 | +\input{sections/literature} | |
| 76 | +\input{sections/architecture} | |
| 77 | +\input{sections/estimators} | |
| 78 | +\input{sections/metal} | |
| 79 | +\input{sections/correctness} | |
| 80 | +\input{sections/setup} | |
| 81 | +\input{sections/microbenchmarks} | |
| 82 | +\input{sections/endtoend} | |
| 83 | +\input{sections/applications} | |
| 84 | +\input{sections/threats} | |
| 85 | +\input{sections/reproducibility} | |
| 86 | +\input{sections/conclusion} | |
| 87 | +\newpage | |
| 88 | +\bibliography{references} | |
| 89 | +\input{appendices/appendix} | |
| 90 | +\end{document} | |
added
paper/sections/introduction.tex
+18 −0
@@ -0,0 +1,18 @@ | ||
| 1 | +\section{Introduction} | |
| 2 | +\label{sec:intro} | |
| 3 | + | |
| 4 | +High-frequency financial econometrics is computationally repetitive in a particular way. The same small estimator---a sum of squared returns, a bipower product, a four-regressor regression, a GARCH recursion---is evaluated on thousands of series, thousands of days, and thousands of bootstrap or Monte Carlo replicas. The established toolchains that practitioners trust for correctness (\texttt{highfrequency} in R, \citealp{BoudtKleenSjorup2022}; \texttt{rugarch}, \citealp{Ghalanos2024rugarch}; \texttt{arch} and \texttt{statsmodels} in Python, \citealp{Sheppard2025arch,Seabold2010}) are organised around one series at a time and double-precision CPU arithmetic. Meanwhile the hardware on most researchers' desks has changed: Apple Silicon machines couple a multicore CPU with a wide GPU in a single unified memory space. The GPU, however, has no 64-bit floating-point unit \citep{AppleMetal2026}, and econometric estimators are not obviously tolerant of single precision. | |
| 5 | + | |
| 6 | +This paper studies whether that hardware can be used for high-frequency econometrics \emph{without} giving up what makes the established toolchains trustworthy: estimator accuracy, valid inference and reproducibility. We frame the question as three falsifiable research questions and build the software needed to answer them. | |
| 7 | + | |
| 8 | +\begin{description}[leftmargin=1.2em] | |
| 9 | +\item[RQ1 (break-even).] At what data and batch sizes does Metal beat an optimised multicore CPU once I/O, shader compilation, synchronisation and host--device transfer are counted? | |
| 10 | +\item[RQ2 (batching and inference).] Does batched estimation raise throughput while preserving coverage, convergence and the numerical agreement with FP64 references? | |
| 11 | +\item[RQ3 (pipeline).] Can an Arrow/Parquet-to-estimator pipeline materially reduce copies, memory pressure and end-to-end latency relative to the columnar engines researchers use today? | |
| 12 | +\end{description} | |
| 13 | + | |
| 14 | +\raid{} (Research Architecture for Intraday Data) is the instrument: an open C++20 library with (i) an FP64 CPU backend that serves as the reference implementation of every method, (ii) an optional Metal backend in which every accumulator is carried as a compensated double-float pair \citep{Dekker1971,Neumaier1974} and chunked partial sums are combined in FP64 on the CPU, (iii) a method registry that pins the exact convention of each estimator and the reference software it is compared with, (iv) nanobind Python bindings and a command-line tool, and (v) a secure client for the FinData~OS lakehouse that serves FirstRate Data 1-minute bars with dataset versions, request identifiers and checksums. | |
| 15 | + | |
| 16 | +Our contributions are deliberately engineering and measurement contributions, not new econometric methodology. First, a precision strategy for FP32-only GPUs that keeps realized measures within $10^{-6}$ of FP64, regression sufficient statistics within $10^{-6}$ and GARCH likelihoods within $2\times10^{-5}$, with bitwise reproducibility. Second, a validation protocol: every Tier~A method is compared with at least one established implementation under identical conventions, with tolerances recorded in the registry and reproduced by the test suite. Third, a benchmark protocol with randomised order, warm-ups, repeated measurements, bootstrap confidence intervals of medians, explicit cold and cached shader compilation, and manifests recording hardware, compiler, load and thermal state. Fourth, measured answers to RQ1--RQ3 on an M5~Max laptop and on eleven Apple Silicon generations, including the cases where the CPU wins. | |
| 17 | + | |
| 18 | +The remainder of the paper is organised as follows. Section~\ref{sec:lit} positions the work relative to the econometric and systems literatures. Section~\ref{sec:arch} describes the architecture and API, Section~\ref{sec:est} the estimator definitions and conventions, Section~\ref{sec:metal} the Metal execution and precision strategy, Section~\ref{sec:correct} the statistical correctness evidence. Sections~\ref{sec:setup}--\ref{sec:apps} present the experimental setup, microbenchmarks, end-to-end benchmarks and financial applications. Sections~\ref{sec:threats}--\ref{sec:concl} discuss threats to validity, reproducibility and licensing, and conclude. | |
added
paper/sections/literature.tex
+10 −0
@@ -0,0 +1,10 @@ | ||
| 1 | +\section{Related literature} | |
| 2 | +\label{sec:lit} | |
| 3 | + | |
| 4 | +\paragraph{Realized measures and their software.} The realized variance of \citet{AndersenBollerslevDieboldLabys2003} and \citet{BarndorffNielsenShephard2002}, bipower variation \citep{BarndorffNielsenShephard2004,BarndorffNielsenShephard2006}, realized semivariance \citep{BarndorffNielsenKinnebrockShephard2010}, realized quarticity and jump proxies \citep{AndersenBollerslevDiebold2007,HuangTauchen2005} are the building blocks of Tier~A. Their finite-sample constants and sampling conventions vary across implementations; \citet{LiuPattonSheppard2015} document how much the choice of sampling scheme matters. Noise-robust estimators---two-scale \citep{ZhangMyklandAitSahalia2005}, realized kernels \citep{BarndorffNielsenHansenLundeShephard2008}, pre-averaging \citep{JacodLiMyklandPodolskijVetter2009}---require tick or quote data and are outside the admissible inputs of this paper, which uses 1-minute OHLCV bars only. The reference implementation for our comparisons is the \texttt{highfrequency} package \citep{BoudtKleenSjorup2022}. | |
| 5 | + | |
| 6 | +\paragraph{HAR and GARCH.} The HAR model of \citet{Corsi2009} and its HARQ extension \citep{BollerslevPattonQuaedvlieg2016}, the signed-jump variants of \citet{PattonSheppard2015}, and the GARCH family \citep{Engle1982,Bollerslev1986,Nelson1991,GlostenJagannathanRunkle1993} with quasi-maximum likelihood inference \citep{BollerslevWooldridge1992,FiorentiniCalzolariPanattoni1996} are the forecasting and conditional-variance workhorses we implement. We compare with \texttt{rugarch} \citep{Ghalanos2024rugarch} and \texttt{arch} \citep{Sheppard2025arch}. Forecast losses follow \citet{Patton2011}; the horse race of \citet{HansenLunde2005} and the comparison framework of \citet{DieboldMariano2002,HansenLundeNason2011} motivate the loss-based evaluation, although model selection is not the object of this paper. Realized GARCH \citep{HansenHuangShek2012}, HEAVY \citep{NoureldinShephardSheppard2012}, DCC \citep{Engle2002} and GARCH-MIDAS \citep{EngleGhyselsSohn2013} are listed in the registry as not implemented. | |
| 7 | + | |
| 8 | +\paragraph{Inference.} Heteroskedasticity-robust \citep{White1980}, HAC \citep{NeweyWest1987,NeweyWest1994} and cluster-robust \citep{CameronGelbachMiller2011} covariances are implemented with the \texttt{statsmodels} conventions. Dependent-data resampling uses the stationary bootstrap \citep{PolitisRomano1994}; the wild bootstrap for realized volatility \citep{GoncalvesMeddahi2009} is a natural extension we do not pursue. Connectedness measures follow \citet{DieboldYilmaz2012,DieboldYilmaz2014} with the generalized FEVD of \citet{KoopPesaranPotter1996} and \citet{PesaranShin1998}; covariance shrinkage follows \citet{LedoitWolf2004}. | |
| 9 | + | |
| 10 | +\paragraph{Numerical computing and GPUs in economics.} Compensated summation goes back to \citet{Kahan1965}, \citet{Dekker1971} and \citet{Neumaier1974}; \citet{Higham1993} gives the error analysis we rely on for the double-float accumulators. \citet{AldrichFernandezVillaverdeGallantRubioRamirez2011} introduced GPU computing to dynamic equilibrium models; the econometric literature on GPUs has concentrated on Monte Carlo and Bayesian computation rather than on the estimators studied here. Our systems dependencies are Eigen \citep{Eigen2025} with Apple Accelerate BLAS/LAPACK \citep{Blackford2002}, Apache Arrow and Parquet \citep{Arrow2025}, nanobind \citep{Nanobind2025}, and the Metal Shading Language \citep{AppleMetal2026}. The comparison engines for the pipeline experiment are pandas, Polars and DuckDB on top of NumPy \citep{Harris2020} and SciPy \citep{Virtanen2020}. | |
modified
src/models/garch_fit.cpp
+11 −3
@@ -45,7 +45,14 @@ template <class F> Mat num_hessian(const F& f, const Vec& x, f64 h0 = 1e-4) { | ||
| 45 | 45 | } |
| 46 | 46 | } // namespace |
| 47 | 47 | |
| 48 | +static Estimate garch_fit_once(std::span<const f64> r0, const GarchFitOptions& opt, int start_variant); | |
| 48 | 49 | Estimate garch_fit(std::span<const f64> r0, const GarchFitOptions& opt) { |
| 50 | + // Up to 3 starting-value variants (documented in the registry): 0 = default, 1 = low persistence (0.80), 2 = high persistence (0.985) / opposite EGARCH γ sign. | |
| 51 | + Estimate best; bool have = false; int tried = 0; | |
| 52 | + for (int v = 0; v < 3; ++v) { Estimate e = garch_fit_once(r0, opt, v); ++tried; if (!have || (e.ok() && (!best.ok() || e.loss < best.loss))) { best = e; have = true; } if (best.ok() && opt.retry_starts == false) break; if (best.ok()) break; } | |
| 53 | + best.extra["start_variants_tried"] = tried; return best; | |
| 54 | +} | |
| 55 | +static Estimate garch_fit_once(std::span<const f64> r0, const GarchFitOptions& opt, int start_variant) { | |
| 49 | 56 | const auto t0 = std::chrono::steady_clock::now(); |
| 50 | 57 | Estimate e; e.model = "garch." + to_string(opt.model) + "." + to_string(opt.dist); e.backend = Backend::cpu; e.precision = Precision::fp64; |
| 51 | 58 | e.names = garch_param_names(opt.model, opt.dist); e.n_obs = r0.size(); |
@@ -59,9 +66,10 @@ Estimate garch_fit(std::span<const f64> r0, const GarchFitOptions& opt) { | ||
| 59 | 66 | // starting values (on the scaled series): μ = mean, persistence 0.95, α = 0.05 (GJR γ = 0.05), ν = 8 |
| 60 | 67 | std::vector<f64> th0(p); th0[0] = stats::mean(std::span<const f64>(r)); |
| 61 | 68 | const f64 v = stats::variance(std::span<const f64>(r), 0); |
| 62 | − if (opt.model == GarchModel::egarch) { th0[2] = 0.1; th0[3] = -0.05; th0[4] = 0.95; th0[1] = (1.0 - th0[4]) * std::log(v); } | |
| 63 | − else if (opt.model == GarchModel::garch) { th0[2] = 0.05; th0[3] = 0.90; th0[1] = v * (1 - 0.95); } | |
| 64 | − else { th0[2] = 0.03; th0[3] = 0.05; th0[4] = 0.90; th0[1] = v * (1 - 0.03 - 0.90 - 0.025); } | |
| 69 | + const f64 pers = start_variant == 1 ? 0.80 : (start_variant == 2 ? 0.985 : 0.95); | |
| 70 | + if (opt.model == GarchModel::egarch) { th0[2] = 0.1; th0[3] = start_variant == 2 ? 0.05 : -0.05; th0[4] = pers; th0[1] = (1.0 - th0[4]) * std::log(v); } | |
| 71 | + else if (opt.model == GarchModel::garch) { th0[2] = 0.05; th0[3] = pers - 0.05; th0[1] = v * (1 - pers); } | |
| 72 | + else { th0[2] = 0.03; th0[3] = 0.05; th0[4] = pers - 0.055; th0[1] = v * (1 - pers); } | |
| 65 | 73 | if (opt.dist == GarchDist::student_t) th0[p - 1] = 8.0; |
| 66 | 74 | auto f_u = [&](const Vec& u) { auto th = garch_from_unconstrained(std::span<const f64>(u.data(), static_cast<usize>(u.size())), opt.model, opt.dist); return garch_nll(r, th, opt.model, opt.dist); }; |
| 67 | 75 | auto u0v = garch_to_unconstrained(th0, opt.model, opt.dist); |
| 68 | 76 | |