SPB Git

spb/wp40_uqo

Public MIT
4.6 KB · 55 lines cpp
1#include <catch2/catch_test_macros.hpp>2#include <catch2/matchers/catch_matchers_floating_point.hpp>3#include "raid/estimators/realized.hpp"4#include "raid/models/garch.hpp"5#include "raid/core/rng.hpp"6#include "raid/data/io.hpp"7#include "raid/data/returns.hpp"8#include "raid/data/resample.hpp"9#include <cmath>10using namespace raid; using Catch::Matchers::WithinAbs; using Catch::Matchers::WithinRel;1112TEST_CASE("Realized measures on a known segment", "[realized]") {13  std::vector<f64> r = {0.01, -0.02, 0.015, -0.005, 0.0}; std::vector<i64> off = {0, 5};14  backend::Batch b{r, off}; est::RealizedOptions o; o.plan.backend = Backend::cpu; auto m = est::realized_measures_batch(b, o);15  CHECK_THAT(m.rv[0], WithinRel(0.0001 + 0.0004 + 0.000225 + 0.000025, 1e-14));16  CHECK_THAT(m.rsv_neg[0], WithinRel(0.0004 + 0.000025, 1e-14)); CHECK_THAT(m.rsv_pos[0], WithinRel(0.0001 + 0.000225, 1e-14));17  const double adj = 0.01 * 0.02 + 0.02 * 0.015 + 0.015 * 0.005 + 0.0;18  CHECK_THAT(m.bpv[0], WithinRel((M_PI / 2.0) * (5.0 / 4.0) * adj, 1e-13));19  CHECK_THAT(m.rq[0], WithinRel((5.0 / 3.0) * (1e-8 + 1.6e-7 + 5.0625e-8 + 6.25e-10), 1e-12));20  CHECK_THAT(m.daily_return[0], WithinAbs(0.0, 1e-15)); CHECK(m.stats.backend == Backend::cpu);21  CHECK_THAT(est::mu_p(1.0), WithinRel(est::kMu1, 1e-14)); CHECK_THAT(est::mu_p(4.0 / 3.0), WithinRel(0.8308, 1e-3));22}23TEST_CASE("Intraday returns respect session boundaries and the synthetic fixture validates", "[realized][data]") {24  auto bars = data::read_bars_csv(std::string(RAID_SOURCE_DIR) + "/datasets/fixtures/synthetic_bars_1m.csv");25  REQUIRE(bars.count("SYN1")); auto& b = bars["SYN1"]; MarketCalendar cal; auto q = b.validate(cal);26  CHECK(q.n_sessions == 5); CHECK_FALSE(q.has_errors()); CHECK(q.coverage > 0.95);27  auto reg = b.filter_session(cal, SessionPolicy::regular); auto ir = data::intraday_returns(reg);28  CHECK(ir.session_offsets.size() == 6); CHECK(static_cast<usize>(ir.session_offsets[1]) == reg.session_closes(0).size() - 1);29  CHECK(std::isnan(ir.overnight[0])); CHECK(std::isfinite(ir.overnight[1]));30  auto agg = data::aggregate(reg, Interval::m5, cal); CHECK(agg.n_sessions() == 5); CHECK(agg.session_closes(0).size() <= 79);31  CHECK_THAT(agg.close[static_cast<usize>(agg.session_offsets[1]) - 1], WithinRel(reg.close[static_cast<usize>(reg.session_offsets[1]) - 1], 1e-15));32  f64 v5 = 0; for (usize i = 0; i < agg.session_closes(0).size(); ++i) v5 += agg.volume[i]; f64 v1 = 0; for (usize i = 0; i < reg.session_closes(0).size(); ++i) v1 += reg.volume[i]; CHECK_THAT(v5, WithinRel(v1, 1e-12));33}34TEST_CASE("GARCH likelihood, transforms and fit on simulated data", "[garch]") {35  using namespace models; Rng rng(2024, 0); const usize n = 3000; std::vector<f64> r(n); f64 s2 = 0.04 / (1 - 0.05 - 0.9), e = 0;36  for (usize t = 0; t < n; ++t) { s2 = 0.04 + 0.05 * e * e + 0.9 * s2; e = std::sqrt(s2) * rng.normal(); r[t] = 0.01 + e; }37  std::vector<f64> th = {0.01, 0.04, 0.05, 0.9};38  CHECK(garch_params_valid(th, GarchModel::garch, GarchDist::gaussian)); std::vector<f64> bad = {0.0, 0.04, 0.5, 0.6}; CHECK_FALSE(garch_params_valid(bad, GarchModel::garch, GarchDist::gaussian));39  auto u = garch_to_unconstrained(th, GarchModel::garch, GarchDist::gaussian); auto back = garch_from_unconstrained(u, GarchModel::garch, GarchDist::gaussian);40  for (usize j = 0; j < 4; ++j) CHECK_THAT(back[j], WithinRel(th[j], 1e-8));41  const f64 nll = garch_nll(r, th, GarchModel::garch, GarchDist::gaussian); CHECK(std::isfinite(nll)); std::vector<f64> neg = {0.0, -1.0, 0.05, 0.9}; CHECK(std::isinf(garch_nll(r, neg, GarchModel::garch, GarchDist::gaussian)));42  GarchFitOptions o; o.forecast_horizon = 5; auto fit = garch_fit(r, o);43  REQUIRE(fit.ok()); CHECK(fit.loss <= nll + 1e-6);44  CHECK_THAT(fit.coefficients[3], WithinAbs(0.9, 0.06)); CHECK_THAT(fit.coefficients[2], WithinAbs(0.05, 0.03)); CHECK(fit.inference->std_errors[3] > 0);45  auto fc = fit.extra["forecast_variance"].get<std::vector<f64>>(); CHECK(fc.size() == 5); CHECK(fc[0] > 0);46  // GJR & EGARCH & t fits must at least converge on this series47  o.model = GarchModel::gjr; o.forecast_horizon = 0; CHECK(garch_fit(r, o).ok());48  o.model = GarchModel::egarch; auto ef = garch_fit(r, o); REQUIRE(ef.ok()); CHECK_THAT(ef.coefficients[4], WithinAbs(0.95, 0.1));49  o.model = GarchModel::garch; o.dist = GarchDist::student_t; auto tf = garch_fit(r, o); CHECK(tf.ok()); CHECK(tf.coefficients.back() > 2.0);50}51TEST_CASE("Non-convergence is never reported as success", "[garch]") {52  using namespace models; std::vector<f64> r(100, 0.0); r[50] = 1.0;  // degenerate series53  GarchFitOptions o; o.max_iter = 1; auto fit = garch_fit(r, o); CHECK_FALSE(fit.ok());54}55
Files · 1dca98c3de92f594407f852f003c4b15bf6ed04d
Menu

Theme