"""Unit tests for libs/backtest/splits.py.""" from __future__ import annotations import datetime as dt import pytest from libs.backtest.calendar import get_trading_days class TestGenerateWalkForwardWindows: def test_basic(self): from libs.backtest.splits import generate_walk_forward_windows dates = [dt.date(2026, 1, 1) + dt.timedelta(days=i) for i in range(400)] windows = generate_walk_forward_windows(dates, train_days=252, test_days=63, step_days=63) assert len(windows) > 0 for w in windows: assert w.train_start < w.train_end assert w.test_start <= w.test_end assert w.train_end < w.test_start def test_no_windows_when_not_enough_data(self): from libs.backtest.splits import generate_walk_forward_windows dates = [dt.date(2026, 1, 1) + dt.timedelta(days=i) for i in range(10)] windows = generate_walk_forward_windows(dates, train_days=252, test_days=63) assert windows == [] def test_window_indices(self): from libs.backtest.splits import generate_walk_forward_windows dates = [dt.date(2026, 1, 1) + dt.timedelta(days=i) for i in range(400)] windows = generate_walk_forward_windows(dates, train_days=100, test_days=50, step_days=50) for i, w in enumerate(windows): assert w.window_index == i def test_repr(self): from libs.backtest.splits import WalkForwardWindow w = WalkForwardWindow( 0, dt.date(2026, 1, 1), dt.date(2026, 6, 1), dt.date(2026, 6, 2), dt.date(2026, 9, 1), ) assert "WalkForwardWindow" in repr(w) class TestGenerateRobustnessWindows: def test_generates_expected_horizons_and_step_spacing(self): from libs.backtest.splits import generate_robustness_windows dates = [dt.date(2026, 1, 1) + dt.timedelta(days=i) for i in range(200)] windows = generate_robustness_windows(dates, horizons_days=[21, 63, 126], step_days=21) assert set(windows.keys()) == {21, 63, 126} assert all(len(windows[horizon]) > 0 for horizon in (21, 63, 126)) for horizon, horizon_windows in windows.items(): assert [window.window_index for window in horizon_windows] == list(range(len(horizon_windows))) assert all(window.start <= window.end for window in horizon_windows) assert all(window.horizon_days == horizon for window in horizon_windows) for previous, current in zip(horizon_windows, horizon_windows[1:]): assert (current.start - previous.start).days == 21 def test_skips_horizons_without_enough_data(self): from libs.backtest.splits import generate_robustness_windows dates = [dt.date(2026, 1, 1) + dt.timedelta(days=i) for i in range(40)] windows = generate_robustness_windows(dates, horizons_days=[21, 63], step_days=21) assert 21 in windows assert len(windows[21]) > 0 assert 63 not in windows or windows[63] == [] class TestSplitByYear: def test_groups_by_year(self): from libs.backtest.splits import split_by_year dates = ( [dt.date(2024, 12, i) for i in range(1, 10)] + [dt.date(2025, 1, i) for i in range(1, 10)] ) by_year = split_by_year(dates) assert 2024 in by_year assert 2025 in by_year assert all(d.year == 2024 for d in by_year[2024]) assert all(d.year == 2025 for d in by_year[2025]) def test_empty(self): from libs.backtest.splits import split_by_year assert split_by_year([]) == {} class TestSplitByRegime: def test_basic_grouping(self): from libs.backtest.splits import split_by_regime dates = [dt.date(2026, 1, i) for i in range(1, 11)] regime_map = { dt.date(2026, 1, 1): "bull", dt.date(2026, 1, 2): "bull", dt.date(2026, 1, 3): "bear", } grouped = split_by_regime(dates, regime_map) assert "bull" in grouped assert "bear" in grouped assert "unknown" in grouped # dates without regime def test_custom_default(self): from libs.backtest.splits import split_by_regime dates = [dt.date(2026, 1, 5)] grouped = split_by_regime(dates, {}, default_regime="neutral") assert "neutral" in grouped