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

59 lines
2.0 KiB
Python

from decimal import Decimal
from app.service.dynamic_allocation_optimizer import (
AssetClassMarketMetric,
DynamicAllocationOptimizer,
)
def metric(
asset_class: str, trailing_return: str, drawdown: str, turnover: str
) -> AssetClassMarketMetric:
return AssetClassMarketMetric(
asset_class=asset_class,
trailing_120d_return_pct=Decimal(trailing_return),
max_drawdown_pct=Decimal(drawdown),
average_daily_turnover_amount=Decimal(turnover),
product_count=2,
)
def test_optimizer_tilts_strategic_weights_using_return_drawdown_and_liquidity() -> None:
result = DynamicAllocationOptimizer.optimize(
{"cash_management_etf": 15, "bond_etf": 45, "equity_etf": 40},
[
metric("cash_management_etf", "1", "0", "50000000"),
metric("bond_etf", "5", "-3", "8000000"),
metric("equity_etf", "15", "-20", "10000000"),
],
return_target_lower_pct=Decimal("6"),
max_drawdown_pct=Decimal("15"),
liquidity_requirement="within_30_days",
)
assert result.dynamic is True
assert result.metric_coverage_pct == Decimal("100")
assert result.weights["bond_etf"] > 45
assert result.weights["cash_management_etf"] < 15
assert sum(result.weights.values()) == 100
assert {item["asset_class"] for item in result.factors} == {
"cash_management_etf", "bond_etf", "equity_etf"
}
def test_optimizer_applies_drawdown_cap_even_when_equity_metrics_are_strong() -> None:
result = DynamicAllocationOptimizer.optimize(
{"cash_management_etf": 5, "bond_etf": 15, "equity_etf": 80},
[
metric("cash_management_etf", "1", "0", "50000000"),
metric("bond_etf", "2", "-1", "5000000"),
metric("equity_etf", "30", "-5", "100000000"),
],
return_target_lower_pct=Decimal("8"),
max_drawdown_pct=Decimal("10"),
liquidity_requirement="within_30_days",
)
assert result.weights["equity_etf"] <= 20
assert sum(result.weights.values()) == 100