feat(CORE-002B): roll out hedge quote unit conversion
This commit is contained in:
@@ -1,17 +1,22 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import logging
|
||||
|
||||
from fastapi.responses import RedirectResponse
|
||||
from nicegui import ui
|
||||
|
||||
from app.domain.portfolio_math import resolve_portfolio_spot_from_quote
|
||||
from app.models.workspace import get_workspace_repository
|
||||
from app.pages.common import (
|
||||
dashboard_page,
|
||||
demo_spot_price,
|
||||
portfolio_snapshot,
|
||||
render_workspace_recovery,
|
||||
strategy_catalog,
|
||||
strategy_metrics,
|
||||
)
|
||||
from app.services.runtime import get_data_service
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def _cost_benefit_options(metrics: dict) -> dict:
|
||||
@@ -35,7 +40,6 @@ def _cost_benefit_options(metrics: dict) -> dict:
|
||||
}
|
||||
|
||||
|
||||
|
||||
def _waterfall_options(metrics: dict) -> dict:
|
||||
steps = metrics["waterfall_steps"]
|
||||
values: list[dict[str, object]] = []
|
||||
@@ -57,22 +61,49 @@ def _waterfall_options(metrics: dict) -> dict:
|
||||
|
||||
|
||||
@ui.page("/{workspace_id}/hedge")
|
||||
def workspace_hedge_page(workspace_id: str) -> None:
|
||||
async def workspace_hedge_page(workspace_id: str) -> None:
|
||||
repo = get_workspace_repository()
|
||||
if not repo.workspace_exists(workspace_id):
|
||||
return RedirectResponse(url="/", status_code=307)
|
||||
_render_hedge_page(workspace_id=workspace_id)
|
||||
await _render_hedge_page(workspace_id=workspace_id)
|
||||
|
||||
|
||||
|
||||
def _render_hedge_page(workspace_id: str | None = None) -> None:
|
||||
async def _resolve_hedge_spot(workspace_id: str | None = None) -> tuple[dict[str, float], str, str]:
|
||||
"""Resolve hedge page spot price using the same quote-unit seam as overview."""
|
||||
repo = get_workspace_repository()
|
||||
config = repo.load_portfolio_config(workspace_id) if workspace_id else None
|
||||
portfolio = portfolio_snapshot(config)
|
||||
if config is None:
|
||||
return {"spot_price": demo_spot_price()}, "demo", ""
|
||||
|
||||
try:
|
||||
data_service = get_data_service()
|
||||
quote = await data_service.get_quote(data_service.default_symbol)
|
||||
spot, source, updated_at = resolve_portfolio_spot_from_quote(
|
||||
config, quote, fallback_symbol=data_service.default_symbol
|
||||
)
|
||||
portfolio = portfolio_snapshot(config, runtime_spot_price=spot)
|
||||
return portfolio, source, updated_at
|
||||
except Exception as exc:
|
||||
logger.warning("Falling back to configured hedge spot for workspace %s: %s", workspace_id, exc)
|
||||
portfolio = portfolio_snapshot(config)
|
||||
return portfolio, "configured_entry_price", ""
|
||||
|
||||
|
||||
async def _render_hedge_page(workspace_id: str | None = None) -> None:
|
||||
repo = get_workspace_repository()
|
||||
config = repo.load_portfolio_config(workspace_id) if workspace_id else None
|
||||
portfolio, quote_source, quote_updated_at = await _resolve_hedge_spot(workspace_id)
|
||||
strategies = strategy_catalog()
|
||||
strategy_map = {strategy["label"]: strategy["name"] for strategy in strategies}
|
||||
selected = {"strategy": strategies[0]["name"], "label": strategies[0]["label"], "scenario_pct": 0}
|
||||
|
||||
if quote_source == "configured_entry_price":
|
||||
spot_label = f"Current spot reference: ${portfolio['spot_price']:,.2f}/oz (configured entry price)"
|
||||
else:
|
||||
spot_label = (
|
||||
f"Current spot reference: ${portfolio['spot_price']:,.2f}/oz (converted collateral spot via {quote_source})"
|
||||
)
|
||||
|
||||
with dashboard_page(
|
||||
"Hedge Analysis",
|
||||
"Compare hedge structures across scenarios, visualize cost-benefit tradeoffs, and inspect net equity impacts.",
|
||||
@@ -87,9 +118,7 @@ def _render_hedge_page(workspace_id: str | None = None) -> None:
|
||||
selector = ui.select(strategy_map, value=selected["label"], label="Strategy selector").classes("w-full")
|
||||
slider_value = ui.label("Scenario move: +0%").classes("text-sm text-slate-500 dark:text-slate-400")
|
||||
slider = ui.slider(min=-25, max=25, value=0, step=5).classes("w-full")
|
||||
ui.label(f"Current spot reference: ${portfolio['spot_price']:,.2f}").classes(
|
||||
"text-sm text-slate-500 dark:text-slate-400"
|
||||
)
|
||||
ui.label(spot_label).classes("text-sm text-slate-500 dark:text-slate-400")
|
||||
if workspace_id:
|
||||
ui.label(f"Workspace route: /{workspace_id}/hedge").classes(
|
||||
"text-xs text-slate-500 dark:text-slate-400"
|
||||
|
||||
Reference in New Issue
Block a user