feat(CORE-001B): migrate overview and hedge math to unit types
This commit is contained in:
@@ -6,6 +6,7 @@ from typing import Any
|
||||
|
||||
from nicegui import ui
|
||||
|
||||
from app.domain.portfolio_math import portfolio_snapshot_from_config, strategy_metrics_from_snapshot
|
||||
from app.models.portfolio import PortfolioConfig
|
||||
from app.services.strategy_templates import StrategyTemplateService
|
||||
|
||||
@@ -37,33 +38,7 @@ def demo_spot_price() -> float:
|
||||
|
||||
|
||||
def portfolio_snapshot(config: PortfolioConfig | None = None) -> dict[str, float]:
|
||||
if config is None:
|
||||
gold_units = 1_000.0
|
||||
spot = demo_spot_price()
|
||||
gold_value = gold_units * spot
|
||||
loan_amount = 145_000.0
|
||||
margin_call_ltv = 0.75
|
||||
hedge_budget = 8_000.0
|
||||
else:
|
||||
gold_units = float(config.gold_ounces or 0.0)
|
||||
spot = float(config.entry_price or 0.0)
|
||||
gold_value = float(config.gold_value or gold_units * spot)
|
||||
loan_amount = float(config.loan_amount)
|
||||
margin_call_ltv = float(config.margin_threshold)
|
||||
hedge_budget = float(config.monthly_budget)
|
||||
|
||||
return {
|
||||
"gold_value": gold_value,
|
||||
"loan_amount": loan_amount,
|
||||
"ltv_ratio": loan_amount / gold_value,
|
||||
"net_equity": gold_value - loan_amount,
|
||||
"spot_price": spot,
|
||||
"gold_units": gold_units,
|
||||
"margin_call_ltv": margin_call_ltv,
|
||||
"margin_call_price": loan_amount / (margin_call_ltv * gold_units),
|
||||
"cash_buffer": 18_500.0,
|
||||
"hedge_budget": hedge_budget,
|
||||
}
|
||||
return portfolio_snapshot_from_config(config)
|
||||
|
||||
|
||||
def strategy_catalog() -> list[dict[str, Any]]:
|
||||
@@ -145,54 +120,7 @@ def strategy_metrics(
|
||||
strategy_catalog()[0],
|
||||
)
|
||||
portfolio = portfolio or portfolio_snapshot()
|
||||
spot = float(portfolio["spot_price"])
|
||||
underlying_units = portfolio["gold_value"] / spot
|
||||
loan_amount = float(portfolio["loan_amount"])
|
||||
base_equity = float(portfolio["net_equity"])
|
||||
floor = float(strategy.get("max_drawdown_floor", spot * 0.95))
|
||||
cap = strategy.get("upside_cap")
|
||||
cost = float(strategy["estimated_cost"])
|
||||
|
||||
scenario_prices = [round(spot * (1 + pct / 100), 2) for pct in range(-25, 30, 5)]
|
||||
benefits: list[float] = []
|
||||
for price in scenario_prices:
|
||||
payoff = max(floor - price, 0.0)
|
||||
if isinstance(cap, (int, float)) and price > float(cap):
|
||||
payoff -= price - float(cap)
|
||||
benefits.append(round(payoff - cost, 2))
|
||||
|
||||
scenario_price = round(spot * (1 + scenario_pct / 100), 2)
|
||||
unhedged_equity = scenario_price * underlying_units - loan_amount
|
||||
scenario_payoff_per_unit = max(floor - scenario_price, 0.0)
|
||||
capped_upside_per_unit = 0.0
|
||||
if isinstance(cap, (int, float)) and scenario_price > float(cap):
|
||||
capped_upside_per_unit = -(scenario_price - float(cap))
|
||||
|
||||
option_payoff_cash = scenario_payoff_per_unit * underlying_units
|
||||
capped_upside_cash = capped_upside_per_unit * underlying_units
|
||||
hedge_cost_cash = cost * underlying_units
|
||||
hedged_equity = unhedged_equity + option_payoff_cash + capped_upside_cash - hedge_cost_cash
|
||||
|
||||
waterfall_steps = [
|
||||
("Base equity", round(base_equity, 2)),
|
||||
("Spot move", round((scenario_price - spot) * underlying_units, 2)),
|
||||
("Option payoff", round(option_payoff_cash, 2)),
|
||||
("Call cap", round(capped_upside_cash, 2)),
|
||||
("Hedge cost", round(-hedge_cost_cash, 2)),
|
||||
("Net equity", round(hedged_equity, 2)),
|
||||
]
|
||||
|
||||
return {
|
||||
"strategy": strategy,
|
||||
"scenario_pct": scenario_pct,
|
||||
"scenario_price": scenario_price,
|
||||
"scenario_series": [
|
||||
{"price": price, "benefit": benefit} for price, benefit in zip(scenario_prices, benefits, strict=True)
|
||||
],
|
||||
"waterfall_steps": waterfall_steps,
|
||||
"unhedged_equity": round(unhedged_equity, 2),
|
||||
"hedged_equity": round(hedged_equity, 2),
|
||||
}
|
||||
return strategy_metrics_from_snapshot(strategy, scenario_pct, portfolio)
|
||||
|
||||
|
||||
@contextmanager
|
||||
|
||||
Reference in New Issue
Block a user