feat: add allocation backtest evidence

This commit is contained in:
Windows
2026-09-11 15:08:37 +08:00
parent ff71a1a724
commit 13bb4f257d
5 changed files with 528 additions and 35 deletions
@@ -0,0 +1,70 @@
from datetime import date, timedelta
from decimal import Decimal
from app.service.allocation_backtest_service import (
AllocationBacktestEngine,
AllocationBacktestService,
BacktestObservation,
)
def observation(day: int, cash: str, equity: str, liquid: bool = True) -> BacktestObservation:
return BacktestObservation(
trade_date=date(2026, 1, 1) + timedelta(days=day),
returns_pct={
"cash_management_etf": Decimal(cash),
"equity_etf": Decimal(equity),
},
liquidity_observed=liquid,
)
def test_backtest_compares_static_and_dynamic_performance() -> None:
observations = [observation(0, "0", "10"), observation(1, "0", "-10")]
result = AllocationBacktestEngine.run(
observations,
{"cash_management_etf": 50, "bond_etf": 0, "equity_etf": 50},
{
observations[1].trade_date: {
"cash_management_etf": 100,
"bond_etf": 0,
"equity_etf": 0,
}
},
)
assert result.observation_count == 2
assert result.static is not None and result.dynamic is not None
assert result.static.total_return_pct == Decimal("-0.2500")
assert result.dynamic.total_return_pct == Decimal("5.0000")
assert result.dynamic_rebalance_count == 1
assert result.liquidity_history_coverage_pct == Decimal("100")
assert result.status == "insufficient_history"
def test_rolling_metrics_and_adequate_history_are_ready() -> None:
observations = [observation(day, "1", "2") for day in range(20)]
metrics = AllocationBacktestService._rolling_metrics(observations)
assert {item.asset_class for item in metrics} == {"cash_management_etf", "equity_etf"}
assert all(item.product_count == 20 for item in metrics)
result = AllocationBacktestEngine.run(
observations,
{"cash_management_etf": 50, "bond_etf": 0, "equity_etf": 50},
{},
)
assert result.status == "ready"
assert result.limitations == ("历史观察不足 120 个交易日,动态优化无法覆盖完整窗口。",)
def test_backtest_reports_liquidity_coverage_limit() -> None:
observations = [observation(day, "0", "0", day < 10) for day in range(20)]
result = AllocationBacktestEngine.run(
observations,
{"cash_management_etf": 100, "bond_etf": 0, "equity_etf": 0},
{},
)
assert result.status == "partial"
assert result.liquidity_history_coverage_pct == Decimal("50")
assert any("80%" in limitation for limitation in result.limitations)