feat: add Nailong Fund advisor capabilities
This commit is contained in:
@@ -0,0 +1,58 @@
|
||||
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
|
||||
Reference in New Issue
Block a user