Files
group_fqcd_jr/tests/unit/service/test_allocation_backtest_service.py
T

52 lines
2.0 KiB
Python

from datetime import date, timedelta
from decimal import Decimal
from app.service.allocation_backtest_service import AllocationBacktestService, BacktestPrice
def series(start: str, daily_change_pct: str, turnover: str | None) -> list[BacktestPrice]:
value = Decimal("100")
start_date = date.fromisoformat(start)
points: list[BacktestPrice] = []
for offset in range(150):
value *= Decimal("1") + Decimal(daily_change_pct) / Decimal("100")
points.append(BacktestPrice(
trade_date=start_date + timedelta(days=offset),
close_price=value,
turnover_amount=Decimal(turnover) if turnover is not None else None,
))
return points
def test_walk_forward_backtest_compares_dynamic_and_static_without_lookahead() -> None:
result = AllocationBacktestService.run(
{
"cash_management_etf": series("2026-01-01", "0.01", "50000000"),
"bond_etf": series("2026-01-01", "0.03", "8000000"),
"equity_etf": series("2026-01-01", "0.08", "10000000"),
},
{"cash_management_etf": 15, "bond_etf": 45, "equity_etf": 40},
return_target_lower_pct=Decimal("6"),
max_drawdown_pct=Decimal("15"),
liquidity_requirement="within_30_days",
)
assert result.status == "ready"
assert result.observation_count > 0
assert result.dynamic_rebalance_count > 0
assert result.dynamic_total_return_pct is not None
assert result.static_total_return_pct is not None
def test_backtest_refuses_to_compare_when_a_quality_approved_asset_class_is_missing() -> None:
result = AllocationBacktestService.run(
{"bond_etf": series("2026-01-01", "0.03", None)},
{"cash_management_etf": 15, "bond_etf": 45, "equity_etf": 40},
return_target_lower_pct=Decimal("6"),
max_drawdown_pct=Decimal("15"),
liquidity_requirement="within_30_days",
)
assert result.status == "data_quality_required"
assert result.limitations == ("missing_quality_approved_asset_class",)