from __future__ import annotations from app.domain.portfolio_math import ( build_alert_context, portfolio_snapshot_from_config, strategy_metrics_from_snapshot, ) from app.models.portfolio import PortfolioConfig from app.pages.common import strategy_catalog def test_portfolio_snapshot_from_config_preserves_weight_price_and_margin_values() -> None: config = PortfolioConfig(entry_price=4400.0, gold_ounces=220.0, entry_basis_mode="weight", loan_amount=145000.0) snapshot = portfolio_snapshot_from_config(config) assert snapshot["spot_price"] == 4400.0 assert snapshot["gold_units"] == 220.0 assert snapshot["gold_value"] == 968000.0 assert snapshot["net_equity"] == 823000.0 assert round(snapshot["margin_call_price"], 2) == 878.79 def test_build_alert_context_uses_unit_safe_gold_value_calculation() -> None: config = PortfolioConfig(entry_price=4400.0, gold_ounces=220.0, entry_basis_mode="weight", loan_amount=145000.0) context = build_alert_context( config, spot_price=4400.0, source="configured_entry_price", updated_at="", ) assert context["gold_units"] == 220.0 assert context["gold_value"] == 968000.0 assert context["net_equity"] == 823000.0 assert round(float(context["margin_call_price"]), 2) == 878.79 def test_strategy_metrics_from_snapshot_preserves_minus_20pct_protective_put_example() -> None: strategy = next(item for item in strategy_catalog() if item["name"] == "protective_put_atm") snapshot = { "gold_value": 215000.0, "loan_amount": 145000.0, "net_equity": 70000.0, "spot_price": 215.0, "gold_units": 1000.0, "margin_call_ltv": 0.75, "margin_call_price": 193.33, "cash_buffer": 18500.0, "hedge_budget": 8000.0, "ltv_ratio": 145000.0 / 215000.0, } metrics = strategy_metrics_from_snapshot(strategy, -20, snapshot) assert metrics["scenario_price"] == 172.0 assert metrics["unhedged_equity"] == 27000.0 assert metrics["hedged_equity"] == 58750.0 assert metrics["waterfall_steps"] == [ ("Base equity", 70000.0), ("Spot move", -43000.0), ("Option payoff", 38000.0), ("Call cap", 0.0), ("Hedge cost", -6250.0), ("Net equity", 58750.0), ]