# 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