Files
group_fqcd_jr/tests/unit/service/test_allocation_backtest_service.py
T
张胜宇 9675df8453 chore(sync): zsy_developcc 全量同步至 qyqy_develop(W26 口径)
- 分支内容对齐 qyqy_develop b6ec3aa,树完全一致(同步后 git diff 为空)
- 覆盖本轮全部交付:客服 Agent 重构(安全路由 / 五出口 / 记忆与画像 / RAG 全链路)
  + 开发文档 62 份编号体系(D1.1 v1.17 索引)
  + 新增 D2.10-客服Agent端到端答辩文档-2026-09-21.html
- 基线:e239eb7(2026-09-17 品牌口径统一快照),本提交为其直接后继
2026-09-21 21:26:30 +08:00

71 lines
2.5 KiB
Python

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)