Files
sanguo_vnpy_v2/tests/portfolio/test_runner_curves.py
T

93 lines
3.1 KiB
Python

"""runner_backtest 基准对齐/回撤/扩展指标 纯函数单测(B1)。"""
import pytest
from sanguo_portfolio.runner_backtest import (
_align_benchmark,
_compute_extended_metrics,
_extract_drawdown,
)
EQ = [
{"date": "2024-01-01", "equity": 100.0},
{"date": "2024-01-02", "equity": 110.0},
{"date": "2024-01-03", "equity": 99.0},
{"date": "2024-01-04", "equity": 120.0},
{"date": "2024-01-05", "equity": 90.0},
]
BD = [
{"date": "2024-01-01", "close": 200.0},
{"date": "2024-01-02", "close": 220.0},
{"date": "2024-01-03", "close": 210.0},
{"date": "2024-01-04", "close": 260.0},
{"date": "2024-01-05", "close": 208.0},
]
def test_drawdown_series():
dd = _extract_drawdown(EQ)
vals = [p["drawdown"] for p in dd]
assert vals[0] == 0.0 and vals[1] == 0.0 and vals[3] == 0.0
assert vals[2] == pytest.approx(-10.0)
assert vals[4] == pytest.approx(-25.0)
def test_align_benchmark_normalizes_and_matches_length():
bench = _align_benchmark(BD, EQ)
assert len(bench) == len(EQ)
assert bench[0]["benchmark"] == pytest.approx(1.0)
assert bench[4]["benchmark"] == pytest.approx(1.04)
def test_align_benchmark_ffill_missing_dates():
bench = _align_benchmark(BD[:2], EQ)
# 后 3 天无基准数据 → 前向填充 1.1
assert [p["benchmark"] for p in bench] == [1.0, 1.1, 1.1, 1.1, 1.1]
def test_align_benchmark_empty_inputs():
assert _align_benchmark([], EQ) == []
assert _align_benchmark(BD, []) == []
def test_extended_metrics_values():
bench = _align_benchmark(BD, EQ)
m = _compute_extended_metrics(EQ, bench)
assert m["benchmark_return"] == pytest.approx(4.0)
assert m["excess_return"] == pytest.approx(-14.0) # -10% 策略 - +4% 基准
assert m["beta"] == pytest.approx(1.0874, abs=1e-3)
assert "annual_volatility" in m
assert "sortino" in m
assert "calmar" in m
def test_extended_metrics_short_series():
assert _compute_extended_metrics([{"date": "d", "equity": 1.0}], []) == {}
assert _compute_extended_metrics([], []) == {}
def test_holdings_curve_aggregates_daily():
"""每日持仓聚合:count=非零标的数,value=市值合计;零持仓行剔除。"""
import pandas as pd
from sanguo_portfolio.runner_backtest import _extract_holdings_curve
df = pd.DataFrame([
{"date": "2024-01-01", "code": "600000", "amount": 100, "value": 1000.0},
{"date": "2024-01-01", "code": "000001", "amount": 200, "value": 2000.0},
{"date": "2024-01-01", "code": "510300", "amount": 0, "value": 0.0}, # 已清仓剔除
{"date": "2024-01-02", "code": "600000", "amount": 100, "value": 1100.0},
])
curve = _extract_holdings_curve(df)
assert curve == [
{"date": "2024-01-01", "count": 2, "value": 3000.0},
{"date": "2024-01-02", "count": 1, "value": 1100.0},
]
def test_holdings_curve_empty_inputs():
from sanguo_portfolio.runner_backtest import _extract_holdings_curve
assert _extract_holdings_curve(None) == []
import pandas as pd
assert _extract_holdings_curve(pd.DataFrame()) == []
assert _extract_holdings_curve(pd.DataFrame({"code": ["600000"]})) == [] # 无 date 列