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

437
docs/ARCHITECTURE.md Normal file
View File

@@ -0,0 +1,437 @@
# Architecture
## Overview
Vault Dashboard is a FastAPI application with NiceGUI pages for the frontend, a lightweight API layer for market and strategy data, a strategy engine for paper hedge comparisons, and an optional Redis cache.
At runtime the app exposes:
- HTML/UI pages via NiceGUI
- REST-style JSON endpoints under `/api`
- a health endpoint at `/health`
- a WebSocket feed at `/ws/updates`
The system is currently optimized for research, visualization, and paper analysis of Lombard-loan hedging strategies rather than live trade execution.
---
## System components
### 1. Application entry point
**File:** `app/main.py`
Responsibilities:
- create the FastAPI app
- load environment-driven settings
- configure CORS
- initialize cache and data services during lifespan startup
- start a background publisher task for WebSocket updates
- mount NiceGUI onto the app
- expose `/health` and `/ws/updates`
### 2. API layer
**File:** `app/api/routes.py`
Responsibilities:
- expose JSON endpoints under `/api`
- resolve the shared `DataService` from application state
- provide read-only portfolio, options, and strategy data
Current endpoints:
- `GET /api/portfolio`
- `GET /api/options`
- `GET /api/strategies`
### 3. UI layer
**Files:** `app/pages/*.py`, `app/components/*.py`
Responsibilities:
- render dashboard pages using NiceGUI
- present charts, tables, strategy views, and scenario widgets
- consume data generated within the app and, in production, align with API/WebSocket-backed state
Representative pages:
- `app/pages/overview.py`
- `app/pages/options.py`
- `app/pages/hedge.py`
- `app/pages/settings.py`
### 4. Data service
**File:** `app/services/data_service.py`
Responsibilities:
- fetch quote data
- build a synthetic options chain response
- build portfolio snapshots
- invoke the strategy engine and shape strategy comparison responses
- cache results when Redis is available
Data source behavior:
- primary live quote source: `yfinance` when installed and reachable
- fallback quote source: static fallback data if live fetch fails
### 5. Cache service
**File:** `app/services/cache.py`
Responsibilities:
- provide async JSON get/set operations
- wrap Redis without making the rest of the app depend directly on Redis primitives
- degrade gracefully when Redis is unavailable
The app remains functional without Redis; caching is optional.
### 6. Strategy engine
**Files:**
- `app/strategies/engine.py`
- `app/strategies/base.py`
- `app/strategies/protective_put.py`
- `app/strategies/laddered_put.py`
- `app/strategies/lease.py`
Responsibilities:
- construct standardized paper strategies for comparison
- calculate cost and protection metrics
- run scenario analysis across price shocks
- recommend a strategy by risk profile
- run simple sensitivity analysis
### 7. Domain models
**Files:**
- `app/models/portfolio.py`
- `app/models/option.py`
- `app/models/strategy.py`
Responsibilities:
- represent Lombard-backed portfolios
- represent option contracts and Greeks
- represent multi-leg hedging structures
- enforce validation rules at object boundaries
### 8. Pricing layer
**Files:** `app/core/pricing/*.py`
Responsibilities:
- compute option prices and Greeks
- support Black-Scholes-based valuation inputs used by the research strategies
---
## High-level data flow
```mermaid
flowchart TD
A[Browser / NiceGUI Client] -->|HTTP| B[FastAPI + NiceGUI app]
A -->|WebSocket /ws/updates| B
B --> C[API routes]
B --> D[ConnectionManager + background publisher]
C --> E[DataService]
D --> E
E --> F[CacheService]
F -->|optional| G[(Redis)]
E --> H[yfinance]
E --> I[StrategySelectionEngine]
I --> J[ProtectivePutStrategy]
I --> K[LadderedPutStrategy]
I --> L[LeaseStrategy]
J --> M[Pricing + models]
K --> M
L --> M
```
### Request/response flow
1. Client sends an HTTP request to an API endpoint or loads a NiceGUI page
2. FastAPI resolves shared app services from `app.state`
3. `DataService` checks Redis cache first when enabled
4. If cache misses, `DataService` fetches or builds the payload:
- quote via `yfinance` or fallback
- synthetic options chain
- strategy comparison via `StrategySelectionEngine`
5. Response is returned as JSON or used by the UI
6. Background task periodically broadcasts portfolio snapshots over WebSocket
---
## Runtime lifecycle
### Startup
When the app starts:
1. environment variables are loaded into `Settings`
2. `CacheService` is created and attempts Redis connection
3. `DataService` is initialized
4. `ConnectionManager` is initialized
5. background task `publish_updates()` starts
6. NiceGUI is mounted on the FastAPI app
### Steady state
During normal operation:
- API requests are served on demand
- WebSocket clients stay connected to `/ws/updates`
- every `WEBSOCKET_INTERVAL_SECONDS`, the app publishes a fresh portfolio payload
- Redis caches repeated quote/portfolio/options requests when configured
### Shutdown
On shutdown:
- publisher task is cancelled
- cache connection is closed
- FastAPI lifecycle exits cleanly
---
## Strategy engine design
## Core design goals
The strategy subsystem is built to compare paper hedging approaches for a Lombard loan secured by gold exposure. It emphasizes:
- deterministic calculations
- shared configuration across strategies
- comparable output shapes
- easy extension for new strategies
### Base contract
**File:** `app/strategies/base.py`
All strategies implement:
- `name`
- `calculate_cost()`
- `calculate_protection()`
- `get_scenarios()`
All strategies receive a shared `StrategyConfig` containing:
- `portfolio`
- `spot_price`
- `volatility`
- `risk_free_rate`
### Portfolio construction
**File:** `app/strategies/engine.py`
`StrategySelectionEngine` builds a canonical research portfolio using:
- portfolio value
- loan amount
- margin call threshold
- spot price
- volatility
- risk-free rate
The engine converts these into a validated `LombardPortfolio`, then instantiates a suite of candidate strategies.
### Candidate strategies
Current strategy set:
1. `protective_put_atm`
2. `protective_put_otm_95`
3. `protective_put_otm_90`
4. `laddered_put_50_50_atm_otm95`
5. `laddered_put_33_33_33_atm_otm95_otm90`
6. `lease_duration_analysis`
### Strategy outputs
Each strategy returns three complementary views:
#### Cost view
Examples:
- premium per share
- total hedge cost
- annualized cost
- cost as percentage of portfolio
- weighted leg costs for ladders
#### Protection view
Examples:
- threshold price where margin stress occurs
- payoff at threshold
- hedged LTV at threshold
- whether the strategy maintains a buffer below margin-call LTV
- floor value implied by option strikes
#### Scenario view
Examples:
- underlying price change percentage
- simulated spot price
- unhedged vs hedged LTV
- option payoff
- hedge cost
- net portfolio value
- margin call triggered or avoided
### Recommendation model
`StrategySelectionEngine.recommend()` scores strategies using a small heuristic.
Risk profiles:
- `conservative`: prioritize lower hedged LTV, then lower annual cost
- `cost_sensitive`: prioritize lower annual cost, then lower hedged LTV
- `balanced`: combine hedged LTV and normalized annual cost
This is a ranking heuristic, not an optimizer or live execution model.
### Sensitivity analysis
The engine also reruns recommendations across:
- multiple volatility assumptions
- multiple spot-price assumptions
This helps identify whether a recommendation is robust to input changes.
---
## Data flow diagram for strategy computation
```mermaid
sequenceDiagram
participant Client
participant API as /api/strategies
participant DS as DataService
participant SE as StrategySelectionEngine
participant S as Strategy implementations
Client->>API: GET /api/strategies?symbol=GLD
API->>DS: get_strategies(symbol)
DS->>DS: get_quote(symbol)
DS->>SE: create engine with spot and research parameters
SE->>S: compare_all_strategies()
S-->>SE: cost/protection/scenario payloads
SE->>SE: recommend() by risk profile
SE->>SE: sensitivity_analysis()
SE-->>DS: comparison + recommendations + sensitivity
DS-->>API: JSON response
API-->>Client: strategies payload
```
---
## API endpoints
### Health
- `GET /health`
Purpose:
- liveness/readiness-style check for deploy validation
Returns:
- application status
- current environment
- whether Redis is enabled
### Portfolio API
- `GET /api/portfolio?symbol=GLD`
Purpose:
- return a current portfolio snapshot derived from the latest quote
### Options API
- `GET /api/options?symbol=GLD`
Purpose:
- return a simplified options chain snapshot for the selected symbol
### Strategies API
- `GET /api/strategies?symbol=GLD`
Purpose:
- return strategy comparisons, recommendations, and sensitivity analysis
### WebSocket updates
- `WS /ws/updates`
Purpose:
- push periodic `portfolio_update` messages to connected clients
---
## Deployment architecture
Production deployment currently assumes:
- containerized app on a VPS
- image stored in GitLab Container Registry
- deployment initiated by GitLab CI/CD over SSH
- optional Redis, depending on runtime configuration
- VPN-restricted network access preferred
- reverse proxy/TLS termination recommended in front of the app
```mermaid
flowchart LR
A[GitLab CI/CD] -->|build + push| B[GitLab Container Registry]
A -->|SSH deploy| C[VPS]
B -->|docker pull| C
C --> D[vault-dash container]
E[VPN / Reverse Proxy] --> D
```
---
## Architectural constraints and assumptions
- Strategy calculations are currently research-oriented, not broker-executed trades
- Quote retrieval is best-effort and may fall back to static data
- Options chain payloads are synthetic examples, not a full market data feed
- Redis is optional and the app must work without it
- WebSocket updates currently publish portfolio snapshots only
- NiceGUI and API routes run in the same Python application process
## Near-term extension points
Likely future architecture additions:
- real broker integration for positions and option chains
- persistent storage for scenarios, settings, and user sessions
- reverse proxy configuration in deployment Compose files
- authenticated API access
- OAuth provider integration over HTTPS
- richer WebSocket event types