You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
90 lines
3.0 KiB
Python
90 lines
3.0 KiB
Python
"""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 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
|