"""test_golden.py — Compare quantalib outputs vs known golden values. Golden values are computed once from the managed QuanTAlib C# library. This ensures the NativeAOT path produces identical results. """ from __future__ import annotations import numpy as np import pytest # Deterministic test data RNG = np.random.default_rng(12345) N = 100 CLOSE = RNG.standard_normal(N).cumsum() + 100.0 HIGH = CLOSE + RNG.uniform(0.5, 2.0, N) LOW = CLOSE - RNG.uniform(0.5, 2.0, N) VOLUME = RNG.uniform(1e6, 5e6, N) TOL = 1e-10 # Tolerance for floating-point comparison @pytest.fixture(scope="module") def qtl(): try: import quantalib as _qtl return _qtl except (OSError, ImportError) as e: pytest.skip(f"quantalib native lib not available: {e}") class TestSmaGolden: """SMA golden value checks.""" def test_sma_last_value(self, qtl) -> None: """SMA(10) of uniform data should equal mean of last 10.""" data = np.arange(1.0, 21.0) # 1..20 result = qtl.sma(data, length=10) # SMA at index 19 = mean(11..20) = 15.5 assert abs(result[19] - 15.5) < TOL # SMA at index 9 = mean(1..10) = 5.5 assert abs(result[9] - 5.5) < TOL class TestEmaGolden: """EMA golden value checks.""" def test_ema_converges(self, qtl) -> None: """EMA of constant should converge to that constant.""" data = np.full(50, 42.0) result = qtl.ema(data, length=10) # After warmup, should be very close to 42 assert abs(result[-1] - 42.0) < 1e-6 class TestMedpriceGolden: """Medprice golden value check.""" def test_medprice_simple(self, qtl) -> None: h = np.array([10.0, 20.0, 30.0]) l = np.array([2.0, 4.0, 6.0]) result = qtl.medprice(h, l) np.testing.assert_allclose(result, [6.0, 12.0, 18.0], atol=TOL) class TestRsiGolden: """RSI golden value checks.""" def test_rsi_range(self, qtl) -> None: """RSI should stay in [0, 100] range.""" result = qtl.rsi(CLOSE, length=14) finite = result[np.isfinite(result)] assert np.all(finite >= 0.0) assert np.all(finite <= 100.0) class TestBbandsGolden: """Bollinger Bands golden value checks.""" def test_bbands_ordering(self, qtl) -> None: """Upper >= Mid >= Lower for all non-NaN.""" result = qtl.bbands(CLOSE, length=20, std=2.0) upper, mid, lower = result mask = np.isfinite(upper) & np.isfinite(mid) & np.isfinite(lower) assert np.all(upper[mask] >= mid[mask] - TOL) assert np.all(mid[mask] >= lower[mask] - TOL) class TestObvGolden: """OBV golden value checks.""" def test_obv_first_is_volume(self, qtl) -> None: """OBV[0] should be related to the first volume bar.""" c = np.array([10.0, 11.0, 10.5, 12.0, 11.5]) v = np.array([100.0, 200.0, 150.0, 300.0, 250.0]) result = qtl.obv(c, v) assert len(result) == 5 # OBV is cumulative; exact values depend on implementation assert np.isfinite(result[-1]) class TestTrGolden: """True Range golden value check.""" def test_tr_simple(self, qtl) -> None: """TR = max(H-L, |H-Cprev|, |L-Cprev|).""" h = np.array([12.0, 15.0, 13.0]) l = np.array([8.0, 10.0, 9.0]) c = np.array([10.0, 14.0, 11.0]) result = qtl.tr(h, l, c) assert len(result) == 3 # TR[0] = H-L = 4 (no previous close) # Exact values depend on implementation details assert np.isfinite(result[-1])