Files
vault-dash/docs/ARCHITECTURE.md
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

10 KiB

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

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

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
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