// raid — command-line tool: devices | validate | estimate | data | benchmark (Phase 1: devices, validate, estimate on CSV/Parquet). #include #include #include #include #include #include #include #include #include "raid/core/types.hpp" #include "raid/core/error.hpp" #include "raid/core/manifest.hpp" #include "raid/core/hash.hpp" #include "raid/backend/backend.hpp" #include "raid/data/io.hpp" #include "raid/data/calendar.hpp" #include "raid/data/returns.hpp" #include "raid/estimators/realized.hpp" #include "raid/estimators/ols.hpp" #include "raid/models/garch.hpp" #include "raid/estimators/har.hpp" #ifdef RAID_WITH_FINDATA #include "raid/data/findata.hpp" #endif using namespace raid; using json = nlohmann::json; namespace { void usage() { std::fprintf(stderr, "raid %s — Research Architecture for Intraday Data (UQO WP 40)\n" " raid devices list backends/devices (JSON)\n" " raid validate [--session regular|extended] quality report per symbol (JSON)\n" " raid estimate realized [--backend auto|cpu|metal] [--bpv-no-fs] per-session realized measures\n" " raid estimate garch [--model garch|gjr|egarch] [--dist gaussian|student_t] [--horizon H] [--returns daily|intraday]\n" " raid estimate ols [--cov classical|hc0..hc3|hac|cluster] [--lags L]\n" " raid estimate har [--horizon h] [--log] [--harq] [--window W --step S] daily RV from 1m bars → HAR fit (+ rolling forecasts)\n" " raid data ... FinData access (Phase 2)\n" " raid benchmark ... benchmark harness (Phase 3+)\n", RAID_VERSION); } std::string arg(const std::vector& a, const std::string& key, const std::string& def = "") { for (usize i = 0; i + 1 < a.size(); ++i) if (a[i] == key) return a[i + 1]; return def; } bool flag(const std::vector& a, const std::string& key) { for (auto& s : a) if (s == key) return true; return false; } std::map load_bars(const std::string& path) { if (path.size() > 4 && path.substr(path.size() - 4) == ".csv") return data::read_bars_csv(path); #ifdef RAID_WITH_ARROW if (path.size() > 8 && path.substr(path.size() - 8) == ".parquet") return data::read_bars_parquet(path).series; if (path.size() > 6 && path.substr(path.size() - 6) == ".arrow") return data::read_bars_arrow_ipc(path).series; #endif throw RaidError(ErrorCode::io, "unsupported input format (csv" #ifdef RAID_WITH_ARROW ", parquet, arrow" #endif "): " + path); } int cmd_devices() { json out = json::array(); for (auto& d : backend::list_devices()) out.push_back(d.to_json()); json j = {{"devices", out}, {"host", host_info()}}; std::cout << j.dump(1) << "\n"; return 0; } int cmd_validate(const std::vector& a) { if (a.empty()) { usage(); return 2; } auto bars = load_bars(a[0]); MarketCalendar cal; const auto policy = arg(a, "--session", "regular") == "extended" ? SessionPolicy::extended : SessionPolicy::regular; json out = json::array(); for (auto& [sym, b] : bars) { auto bb = b; auto st = bb.normalize(); auto q = bb.validate(cal, policy); q.duplicates = st.duplicates_removed; q.unsorted = st.reordered; out.push_back(q.to_json()); } std::cout << json({{"file", a[0]}, {"sha256", Sha256::of_file(a[0])}, {"reports", out}}).dump(1) << "\n"; return 0; } int cmd_estimate(const std::vector& a) { if (a.size() < 2) { usage(); return 2; } const std::string what = a[0], path = a[1]; ExecutionPlan plan; plan.backend = backend_from_string(arg(a, "--backend", "auto")); if (what == "realized") { auto bars = load_bars(path); MarketCalendar cal; json out = json::object(); est::RealizedOptions ro; ro.plan = plan; ro.bpv_finite_sample = !flag(a, "--bpv-no-fs"); for (auto& [sym, b0] : bars) { auto b = b0; b.normalize(); auto reg = b.filter_session(cal, SessionPolicy::regular); auto ir = data::intraday_returns(reg); auto m = est::realized_measures(ir, ro); json rows = json::array(); for (usize k = 0; k < m.sessions.size(); ++k) rows.push_back({{"session", m.sessions[k].iso()}, {"n", m.n_returns[k]}, {"rv", m.rv[k]}, {"bpv", m.bpv[k]}, {"rsv_neg", m.rsv_neg[k]}, {"rsv_pos", m.rsv_pos[k]}, {"rq", m.rq[k]}, {"jump", m.jump[k]}, {"tripv", m.tripv[k]}, {"ret", m.daily_return[k]}}); out[sym] = {{"measures", rows}, {"backend", std::string(to_string(m.stats.backend))}, {"precision", std::string(to_string(m.stats.precision))}, {"wall_seconds", m.stats.wall_seconds}, {"note", m.stats.note}, {"warnings", m.warnings}}; } std::cout << out.dump(1) << "\n"; return 0; } if (what == "garch") { auto bars = load_bars(path); MarketCalendar cal; json out = json::object(); models::GarchFitOptions go; const std::string mdl = arg(a, "--model", "garch"); go.model = mdl == "gjr" ? models::GarchModel::gjr : (mdl == "egarch" ? models::GarchModel::egarch : models::GarchModel::garch); go.dist = arg(a, "--dist", "gaussian") == "student_t" ? models::GarchDist::student_t : models::GarchDist::gaussian; go.forecast_horizon = std::stoi(arg(a, "--horizon", "0")); go.plan = plan; const bool intraday = arg(a, "--returns", "daily") == "intraday"; for (auto& [sym, b0] : bars) { auto b = b0; b.normalize(); auto reg = b.filter_session(cal, SessionPolicy::regular); auto ir = data::intraday_returns(reg); std::vector r; if (intraday) r = ir.r; else for (f64 v : ir.close_to_close) if (std::isfinite(v)) r.push_back(v); try { auto e = models::garch_fit(r, go); out[sym] = e.to_json(); } catch (const RaidError& ex) { out[sym] = {{"error", ex.what()}}; } } std::cout << out.dump(1) << "\n"; return 0; } if (what == "har") { auto bars = load_bars(path); MarketCalendar cal; json out = json::object(); est::HarSpec s; s.horizon = std::stoi(arg(a, "--horizon", "1")); s.log = flag(a, "--log"); s.harq = flag(a, "--harq"); for (auto& [sym, b0] : bars) { auto b = b0; b.normalize(); auto reg = b.filter_session(cal, SessionPolicy::regular); auto ir = data::intraday_returns(reg); est::RealizedOptions ro; ro.plan = plan; auto m = est::realized_measures(ir, ro); try { auto e = est::har_fit(m.rv, s, m.rq); json j = e.to_json(); j["forecast_next"] = est::har_forecast(e, m.rv, s, m.rq); j["n_sessions"] = m.rv.size(); if (!arg(a, "--window").empty()) { auto r = est::har_rolling(m.rv, s, static_cast(std::stoul(arg(a, "--window"))), static_cast(std::stoul(arg(a, "--step", "1"))), m.rq); auto l = est::forecast_loss(r.actual, r.forecast); j["rolling"] = {{"n_fits", r.n_fits}, {"mse", l.mse}, {"qlike", l.qlike}, {"seconds", r.seconds}}; } out[sym] = j; } catch (const RaidError& ex) { out[sym] = {{"error", ex.what()}}; } } std::cout << out.dump(1) << "\n"; return 0; } if (what == "ols") { std::ifstream f(path); RAID_REQUIRE(f.good(), io, "cannot open " + path); std::string line; std::getline(f, line); std::vector names; { usize s = 0; for (usize i = 0; i <= line.size(); ++i) if (i == line.size() || line[i] == ',') { names.push_back(line.substr(s, i - s)); s = i + 1; } } std::vector> rows; while (std::getline(f, line)) { if (line.empty()) continue; std::vector r; usize s = 0; for (usize i = 0; i <= line.size(); ++i) if (i == line.size() || line[i] == ',') { r.push_back(std::stod(line.substr(s, i - s))); s = i + 1; } rows.push_back(r); } const usize n = rows.size(), k = names.size() - 1; Eigen::MatrixXd X(n, k + 1); Eigen::VectorXd y(n); for (usize i = 0; i < n; ++i) { y[static_cast(i)] = rows[i][0]; X(static_cast(i), 0) = 1.0; for (usize j = 0; j < k; ++j) X(static_cast(i), static_cast(j + 1)) = rows[i][j + 1]; } est::OlsOptions oo; oo.cov.type = stats::covtype_from_string(arg(a, "--cov", "classical")); oo.cov.hac_lags = std::stoi(arg(a, "--lags", "-1")); oo.names = {"const"}; for (usize j = 1; j < names.size(); ++j) oo.names.push_back(names[j]); std::cout << est::ols(X, y, oo).estimate.to_json().dump(1) << "\n"; return 0; } usage(); return 2; } } // namespace int main(int argc, char** argv) { std::vector a(argv + 1, argv + argc); if (a.empty()) { usage(); return 2; } try { const std::string cmd = a[0]; a.erase(a.begin()); if (cmd == "devices") return cmd_devices(); if (cmd == "validate") return cmd_validate(a); if (cmd == "estimate") return cmd_estimate(a); #ifdef RAID_WITH_FINDATA if (cmd == "data") return raid::findata::cli_main(a); #endif if (cmd == "benchmark") { std::fprintf(stderr, "benchmark: use the bench_* executables (Phase 3+)\n"); return 2; } usage(); return 2; } catch (const RaidError& e) { std::fprintf(stderr, "raid error: %s\n", e.what()); return 1; } catch (const std::exception& e) { std::fprintf(stderr, "error: %s\n", e.what()); return 1; } }