import sys, os _VNPY_SRC = os.path.abspath(os.path.join(os.path.dirname(__file__), "..", "..", "vnpy_v4.4.0")) sys.path.insert(0, _VNPY_SRC) import pandas as pd import numpy as np import empyrical from sanguo_backtest.metrics import compute_metrics, MetricsResult, BENCHMARK_SYMBOL def _make_daily(returns): idx = pd.date_range("2024-01-01", periods=len(returns), freq="B") return pd.DataFrame({"return": returns}, index=idx) def test_compute_metrics_scalars_match_empyrical(): np.random.seed(42) strat = pd.Series(np.random.normal(0.001, 0.02, 100), index=pd.date_range("2024-01-01", periods=100, freq="B")) bench = pd.Series(np.random.normal(0.0005, 0.015, 100), index=strat.index) daily_df = pd.DataFrame({"return": strat.values}, index=strat.index) res = compute_metrics(daily_df, bench) assert isinstance(res, MetricsResult) # 标量口径与 empyrical 直接计算一致 assert abs(res.scalars["alpha"] - empyrical.alpha(strat, bench)) < 1e-9 assert abs(res.scalars["beta"] - empyrical.beta(strat, bench)) < 1e-9 assert abs(res.scalars["sharpe_ratio"] - empyrical.sharpe_ratio(strat)) < 1e-9 assert abs(res.scalars["sortino_ratio"] - empyrical.sortino_ratio(strat)) < 1e-9 assert abs(res.scalars["max_drawdown"] - empyrical.max_drawdown(strat)) < 1e-9 assert abs(res.scalars["annual_volatility"] - empyrical.annual_volatility(strat)) < 1e-9 def test_compute_metrics_has_all_required_scalars(): strat = pd.Series([0.01, -0.005, 0.02, 0.0], index=pd.date_range("2024-01-01", periods=4, freq="B")) bench = pd.Series([0.005, 0.001, 0.01, -0.002], index=strat.index) res = compute_metrics(pd.DataFrame({"return": strat.values}, index=strat.index), bench) required = {"total_return","annual_return","alpha","beta","sharpe_ratio", "sortino_ratio","information_ratio","annual_volatility","max_drawdown", "benchmark_return","benchmark_volatility"} assert required.issubset(res.scalars.keys()) def test_compute_metrics_series_keys_and_length(): strat = pd.Series(np.random.normal(0, 0.01, 50), index=pd.date_range("2024-01-01", periods=50, freq="B")) bench = pd.Series(np.random.normal(0, 0.01, 50), index=strat.index) res = compute_metrics(pd.DataFrame({"return": strat.values}, index=strat.index), bench) for key in ["equity_curve","benchmark_curve","alpha","beta","drawdown"]: assert key in res.series assert len(res.series[key]) == 50 assert res.series["drawdown"].max() <= 1e-9 # 回撤 <= 0 def test_benchmark_symbol_map(): assert BENCHMARK_SYMBOL["hs300"] == "sh000300" assert BENCHMARK_SYMBOL["zz500"] == "sz000905"