"""Unit tests for store_builder.py.""" import datetime as dt import numpy as np import pytest from libs.backtest.scenarios.price_gen import PriceRegime from libs.backtest.scenarios.scenarios import SCENARIO_REGISTRY, STEADY_BULL, NO_SIGNAL from libs.backtest.scenarios.store_builder import _get_trading_dates, build_synthetic_store from libs.backtest.snapshot_store import SnapshotStore @pytest.mark.unit class TestGetTradingDates: def test_returns_correct_count(self): dates = _get_trading_dates(50) assert len(dates) == 50 def test_dates_are_weekdays(self): dates = _get_trading_dates(30) for d in dates: assert d.weekday() < 5, f"Non-weekday in trading dates: {d}" def test_dates_are_sorted_ascending(self): dates = _get_trading_dates(30) assert dates == sorted(dates) def test_starts_on_or_after_start_date(self): start = dt.date(2024, 1, 2) dates = _get_trading_dates(20, start=start) assert dates[0] >= start @pytest.mark.unit class TestBuildSyntheticStore: def test_returns_snapshot_store(self): store = build_synthetic_store(STEADY_BULL, rng=np.random.default_rng(42)) assert isinstance(store, SnapshotStore) def test_store_has_candidates(self): store = build_synthetic_store(STEADY_BULL, rng=np.random.default_rng(42)) exec_dates = store.all_execution_dates() assert len(exec_dates) > 0, "No candidates generated" def test_store_has_macro_data(self): store = build_synthetic_store(STEADY_BULL, rng=np.random.default_rng(42)) trading_days = store.all_trading_days() assert len(trading_days) > 0 def test_store_has_bar_data(self): store = build_synthetic_store(STEADY_BULL, rng=np.random.default_rng(42)) trading_days = store.all_trading_days() assert len(trading_days) > 0 def test_spy_and_qqq_in_bars(self): store = build_synthetic_store(STEADY_BULL, rng=np.random.default_rng(42)) trading_days = store.all_trading_days() some_day = trading_days[10] assert store.get_bar("SPY", some_day) is not None assert store.get_bar("QQQ", some_day) is not None def test_macro_has_required_keys(self): store = build_synthetic_store(STEADY_BULL, rng=np.random.default_rng(42)) trading_days = store.all_trading_days() # Pick a date past rolling window warm-up late_date = trading_days[60] macro = store.get_macro_for_date(late_date) for key in ("VIXCLS", "macro_vix", "macro_hy_spread", "spy_close", "qqq_close"): assert key in macro, f"Missing macro key: {key}" def test_seed_is_deterministic(self): store_a = build_synthetic_store(STEADY_BULL, rng=np.random.default_rng(123)) store_b = build_synthetic_store(STEADY_BULL, rng=np.random.default_rng(123)) # Same seed → same exec dates assert store_a.all_execution_dates() == store_b.all_execution_dates() def test_no_signal_scenario_produces_store(self): """no_signal scenario should assemble without error.""" store = build_synthetic_store(NO_SIGNAL, rng=np.random.default_rng(1)) assert isinstance(store, SnapshotStore) def test_scenario_seed_used_when_rng_none(self): """If rng=None, scenario.seed is used for determinism.""" from dataclasses import replace seeded_scenario = replace(STEADY_BULL, seed=77) store_a = build_synthetic_store(seeded_scenario, rng=None) store_b = build_synthetic_store(seeded_scenario, rng=None) assert store_a.all_execution_dates() == store_b.all_execution_dates() def test_candidate_exec_dates_align_with_macro_dates(self): """All candidate exec_dates should exist in trading days (have macro).""" store = build_synthetic_store(STEADY_BULL, rng=np.random.default_rng(42)) macro_trading_days = set(store.all_trading_days()) for exec_date in store.all_execution_dates(): assert exec_date in macro_trading_days, f"exec_date {exec_date} not in trading days" def test_all_registered_scenarios_can_build(self): """Smoke-test: every scenario in SCENARIO_REGISTRY builds without error.""" for name, scenario in SCENARIO_REGISTRY.items(): try: store = build_synthetic_store(scenario, rng=np.random.default_rng(0)) assert isinstance(store, SnapshotStore), f"Bad store for {name}" except Exception as exc: pytest.fail(f"build_synthetic_store failed for scenario '{name}': {exc}")