Initial commit: Vault Dashboard for options hedging

- FastAPI + NiceGUI web application
- QuantLib-based Black-Scholes pricing with Greeks
- Protective put, laddered, and LEAPS strategies
- Real-time WebSocket updates
- TradingView-style charts via Lightweight-Charts
- Docker containerization
- GitLab CI/CD pipeline for VPS deployment
- VPN-only access configuration
This commit is contained in:
Bu5hm4nn
2026-03-21 19:21:40 +01:00
commit 00a68bc767
63 changed files with 6239 additions and 0 deletions

View File

@@ -0,0 +1,68 @@
from __future__ import annotations
from typing import Any
from nicegui import ui
class PortfolioOverview:
"""Portfolio summary card with LTV risk coloring and margin warning."""
def __init__(self, *, margin_call_ltv: float = 0.75) -> None:
self.margin_call_ltv = margin_call_ltv
with ui.card().classes("w-full rounded-2xl border border-slate-200 bg-white shadow-sm dark:border-slate-800 dark:bg-slate-900"):
with ui.row().classes("w-full items-center justify-between"):
ui.label("Portfolio Overview").classes("text-lg font-semibold text-slate-900 dark:text-slate-100")
self.warning_badge = ui.label("Monitoring").classes(
"rounded-full bg-amber-100 px-3 py-1 text-xs font-semibold text-amber-700 dark:bg-amber-500/15 dark:text-amber-300"
)
with ui.grid(columns=2).classes("w-full gap-4 max-sm:grid-cols-1"):
self.gold_value = self._metric_card("Current Gold Value")
self.loan_amount = self._metric_card("Loan Amount")
self.ltv = self._metric_card("Current LTV")
self.net_equity = self._metric_card("Net Equity")
def _metric_card(self, label: str) -> ui.label:
with ui.card().classes("rounded-xl border border-slate-200 bg-slate-50 p-4 shadow-none dark:border-slate-800 dark:bg-slate-950"):
ui.label(label).classes("text-sm font-medium text-slate-500 dark:text-slate-400")
return ui.label("--").classes("text-2xl font-bold text-slate-900 dark:text-slate-50")
def update(self, portfolio: dict[str, Any], *, margin_call_ltv: float | None = None) -> None:
threshold = margin_call_ltv if margin_call_ltv is not None else portfolio.get("margin_call_ltv", self.margin_call_ltv)
gold_value = float(portfolio.get("gold_value", portfolio.get("portfolio_value", 0.0)))
loan_amount = float(portfolio.get("loan_amount", 0.0))
current_ltv = float(portfolio.get("ltv_ratio", portfolio.get("current_ltv", 0.0)))
net_equity = float(portfolio.get("net_equity", gold_value - loan_amount))
self.gold_value.set_text(self._money(gold_value))
self.loan_amount.set_text(self._money(loan_amount))
self.net_equity.set_text(self._money(net_equity))
self.ltv.set_text(f"{current_ltv * 100:.1f}%")
self.ltv.style(f"color: {self._ltv_color(current_ltv, threshold)}")
badge_text, badge_style = self._warning_state(current_ltv, threshold)
self.warning_badge.set_text(badge_text)
self.warning_badge.style(badge_style)
@staticmethod
def _money(value: float) -> str:
return f"${value:,.2f}"
@staticmethod
def _ltv_color(ltv: float, threshold: float) -> str:
if ltv >= threshold:
return "#f43f5e"
if ltv >= threshold * 0.9:
return "#f59e0b"
return "#22c55e"
@staticmethod
def _warning_state(ltv: float, threshold: float) -> tuple[str, str]:
base = "border-radius: 9999px; padding: 0.25rem 0.75rem; font-size: 0.75rem; font-weight: 600;"
if ltv >= threshold:
return ("Margin call risk", base + " background: rgba(244, 63, 94, 0.14); color: #f43f5e;")
if ltv >= threshold * 0.9:
return ("Approaching threshold", base + " background: rgba(245, 158, 11, 0.14); color: #f59e0b;")
return ("Healthy collateral", base + " background: rgba(34, 197, 94, 0.14); color: #22c55e;")