from __future__ import annotations import json import tempfile import unittest from pathlib import Path from src.backtest.compare import build_rows class CompareTests(unittest.TestCase): def test_build_rows_includes_cost_label_and_relative_columns(self) -> None: temp_dir = tempfile.TemporaryDirectory() self.addCleanup(temp_dir.cleanup) root = Path(temp_dir.name) backtests_root = root / "outputs" / "backtests" result_dir = backtests_root / "demo_strategy" result_dir.mkdir(parents=True, exist_ok=True) (result_dir / "summary.json").write_text( json.dumps( { "cumulative_return": 0.20, "annual_return": 0.05, "max_drawdown": -0.10, "annual_volatility": 0.20, "sharpe": 0.25, "calmar": 0.50, "turnover": 12.0, "rebalance_count": 8, "cash_days_ratio": 0.10, } ), encoding="utf-8", ) (result_dir / "benchmark_summary.json").write_text( json.dumps( { "equal_weight": {"cumulative_return": 0.15}, "hs300": {"cumulative_return": 0.08}, "chinext50": {"cumulative_return": 0.30}, } ), encoding="utf-8", ) frame = build_rows( backtests_root=backtests_root, strategy_configs={ "demo_strategy": { "name": "demo_strategy", "top_n": 1, "rebalance_frequency": "every_5_days", "risk_penalty_multiplier": 0.5, "commission_bps": 5.0, "slippage_bps": 5.0, } }, cost_scenarios={10.0: "optimistic", 15.0: "base", 20.0: "conservative"}, ) self.assertEqual(len(frame.index), 1) row = frame.iloc[0] self.assertEqual(row["cost_scenario"], "optimistic") self.assertAlmostEqual(row["vs_equal_weight_cum"], 0.05) self.assertAlmostEqual(row["vs_hs300_cum"], 0.12) self.assertAlmostEqual(row["vs_chinext50_cum"], -0.10) if __name__ == "__main__": unittest.main()