Files
vault-dash/app/core/calculations.py
Bu5hm4nn 00a68bc767 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
2026-03-21 19:21:40 +01:00

99 lines
3.7 KiB
Python

from __future__ import annotations
from typing import Iterable
from app.models.option import OptionContract
from app.models.portfolio import LombardPortfolio
from app.models.strategy import HedgingStrategy
def margin_call_price(gold_ounces: float, loan_amount: float, margin_call_ltv: float) -> float:
"""Calculate the gold price per ounce that triggers a margin call."""
if gold_ounces <= 0:
raise ValueError("gold_ounces must be positive")
if loan_amount < 0:
raise ValueError("loan_amount must be non-negative")
if not 0 < margin_call_ltv < 1:
raise ValueError("margin_call_ltv must be between 0 and 1")
return loan_amount / (margin_call_ltv * gold_ounces)
def loan_to_value(loan_amount: float, collateral_value: float) -> float:
"""Calculate the loan-to-value ratio."""
if loan_amount < 0:
raise ValueError("loan_amount must be non-negative")
if collateral_value <= 0:
raise ValueError("collateral_value must be positive")
return loan_amount / collateral_value
def ltv_scenarios(portfolio: LombardPortfolio, gold_prices: Iterable[float]) -> dict[float, float]:
"""Return LTV values for a collection of gold-price scenarios."""
scenarios: dict[float, float] = {}
for price in gold_prices:
if price <= 0:
raise ValueError("scenario gold prices must be positive")
scenarios[price] = portfolio.ltv_at_price(price)
if not scenarios:
raise ValueError("gold_prices must contain at least one scenario")
return scenarios
def option_payoff(contracts: Iterable[OptionContract], underlying_price: float, *, short: bool = False) -> float:
"""Aggregate expiry payoff across option contracts."""
if underlying_price <= 0:
raise ValueError("underlying_price must be positive")
payoff = sum(contract.payoff(underlying_price) for contract in contracts)
return -payoff if short else payoff
def strategy_payoff(strategy: HedgingStrategy, underlying_price: float) -> float:
"""Net option payoff before premium cost for a hedging strategy."""
return strategy.gross_payoff(underlying_price)
def net_equity(
gold_ounces: float,
gold_price_per_ounce: float,
loan_amount: float,
hedge_cost: float = 0.0,
option_payoff_value: float = 0.0,
) -> float:
"""Calculate net equity after debt and hedging effects.
Formula:
``gold_value - loan_amount - hedge_cost + option_payoff``
"""
if gold_ounces <= 0:
raise ValueError("gold_ounces must be positive")
if gold_price_per_ounce <= 0:
raise ValueError("gold_price_per_ounce must be positive")
if loan_amount < 0:
raise ValueError("loan_amount must be non-negative")
if hedge_cost < 0:
raise ValueError("hedge_cost must be non-negative")
gold_value = gold_ounces * gold_price_per_ounce
return gold_value - loan_amount - hedge_cost + option_payoff_value
def portfolio_net_equity(
portfolio: LombardPortfolio,
gold_price_per_ounce: float | None = None,
strategy: HedgingStrategy | None = None,
) -> float:
"""Calculate scenario net equity for a portfolio with an optional hedge."""
scenario_price = portfolio.gold_price_per_ounce if gold_price_per_ounce is None else gold_price_per_ounce
if scenario_price <= 0:
raise ValueError("gold_price_per_ounce must be positive")
payoff_value = strategy.gross_payoff(scenario_price) if strategy is not None else 0.0
hedge_cost = strategy.hedge_cost if strategy is not None else 0.0
return net_equity(
gold_ounces=portfolio.gold_ounces,
gold_price_per_ounce=scenario_price,
loan_amount=portfolio.loan_amount,
hedge_cost=hedge_cost,
option_payoff_value=payoff_value,
)