from __future__ import annotations import importlib.util from pathlib import Path import sys import pandas as pd _SCRIPT_PATH = Path(__file__).resolve().parents[2] / "scripts" / "ensemble_overlay_backtest.py" _SPEC = importlib.util.spec_from_file_location("ensemble_overlay_backtest", _SCRIPT_PATH) assert _SPEC and _SPEC.loader _MODULE = importlib.util.module_from_spec(_SPEC) sys.modules[_SPEC.name] = _MODULE _SPEC.loader.exec_module(_MODULE) build_ensemble_returns = _MODULE.build_ensemble_returns summarize_ensemble = _MODULE.summarize_ensemble def test_build_ensemble_returns_uses_lagged_rolling_sharpe() -> None: dates = pd.date_range("2024-01-01", periods=6, freq="B") returns = pd.DataFrame( { "date": dates, "a": [0.0, 0.01, 0.015, 0.02, -0.02, -0.02], "b": [0.0, -0.01, -0.015, -0.02, 0.02, 0.02], } ) ensemble, weights = build_ensemble_returns(returns, window=3) # Before enough history, fallback is equal weight. assert weights.loc[0, "a"] == 0.5 assert weights.loc[1, "b"] == 0.5 # Once trailing Sharpe is available, positive trailing performer gets all the weight. assert weights.loc[4, "a"] == 1.0 assert weights.loc[4, "b"] == 0.0 assert ensemble.loc[4, "ensemble_return"] == returns.loc[4, "a"] def test_summarize_ensemble_reports_yearly_returns() -> None: dates = pd.to_datetime(["2024-12-30", "2024-12-31", "2025-01-02"]) ensemble = pd.DataFrame({"date": dates, "ensemble_return": [0.0, 0.01, 0.02]}) summary = summarize_ensemble(ensemble, initial_equity=100.0) assert round(summary["total_return_pct"], 4) == 3.02 assert "2024" in summary["yearly_returns_pct"] assert "2025" in summary["yearly_returns_pct"]