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:
593
docs/API.md
Normal file
593
docs/API.md
Normal file
@@ -0,0 +1,593 @@
|
||||
# API Documentation
|
||||
|
||||
## Overview
|
||||
|
||||
Vault Dashboard exposes a small read-only HTTP API plus a WebSocket stream.
|
||||
|
||||
Base capabilities:
|
||||
|
||||
- health monitoring
|
||||
- portfolio snapshot retrieval
|
||||
- options chain retrieval
|
||||
- strategy analysis retrieval
|
||||
- periodic real-time portfolio updates over WebSocket
|
||||
|
||||
Unless noted otherwise, all responses are JSON.
|
||||
|
||||
---
|
||||
|
||||
## Conventions
|
||||
|
||||
### Base URL
|
||||
|
||||
Examples:
|
||||
|
||||
```text
|
||||
http://localhost:8000
|
||||
https://vault.example.com
|
||||
```
|
||||
|
||||
### Content type
|
||||
|
||||
Responses use:
|
||||
|
||||
```http
|
||||
Content-Type: application/json
|
||||
```
|
||||
|
||||
### Authentication
|
||||
|
||||
There is currently **no application-layer authentication** on these endpoints.
|
||||
|
||||
If the deployment requires restricted access, enforce it at the network or reverse-proxy layer.
|
||||
|
||||
### Errors
|
||||
|
||||
The app currently relies mostly on framework defaults. Typical failures may include:
|
||||
|
||||
- `422 Unprocessable Entity` for invalid query parameters
|
||||
- `500 Internal Server Error` for unexpected runtime issues
|
||||
|
||||
---
|
||||
|
||||
## HTTP endpoints
|
||||
|
||||
## 1. Health
|
||||
|
||||
### `GET /health`
|
||||
|
||||
Deployment and uptime check.
|
||||
|
||||
#### Query parameters
|
||||
|
||||
None.
|
||||
|
||||
#### Response schema
|
||||
|
||||
```json
|
||||
{
|
||||
"status": "ok",
|
||||
"environment": "production",
|
||||
"redis_enabled": false
|
||||
}
|
||||
```
|
||||
|
||||
#### Field definitions
|
||||
|
||||
- `status` (`string`): expected value is currently `"ok"`
|
||||
- `environment` (`string`): runtime environment from `APP_ENV` or `ENVIRONMENT`
|
||||
- `redis_enabled` (`boolean`): `true` when Redis is configured and connected
|
||||
|
||||
#### Example request
|
||||
|
||||
```bash
|
||||
curl -fsS http://localhost:8000/health
|
||||
```
|
||||
|
||||
#### Example response
|
||||
|
||||
```json
|
||||
{
|
||||
"status": "ok",
|
||||
"environment": "development",
|
||||
"redis_enabled": false
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 2. Portfolio
|
||||
|
||||
### `GET /api/portfolio`
|
||||
|
||||
Returns a portfolio snapshot derived from the current quote for a symbol.
|
||||
|
||||
#### Query parameters
|
||||
|
||||
| Name | Type | Required | Default | Description |
|
||||
|---|---|---:|---|---|
|
||||
| `symbol` | string | no | `GLD` | Ticker symbol to analyze |
|
||||
|
||||
#### Response schema
|
||||
|
||||
```json
|
||||
{
|
||||
"symbol": "GLD",
|
||||
"spot_price": 215.0,
|
||||
"portfolio_value": 215000.0,
|
||||
"loan_amount": 600000.0,
|
||||
"ltv_ratio": 2.7907,
|
||||
"updated_at": "2026-03-21T12:34:56.000000+00:00",
|
||||
"source": "fallback"
|
||||
}
|
||||
```
|
||||
|
||||
#### Field definitions
|
||||
|
||||
- `symbol` (`string`): requested ticker, uppercased
|
||||
- `spot_price` (`number`): latest spot/quote price
|
||||
- `portfolio_value` (`number`): current modeled collateral value, currently `spot_price * 1000`
|
||||
- `loan_amount` (`number`): modeled loan balance, currently fixed at `600000.0`
|
||||
- `ltv_ratio` (`number`): `loan_amount / portfolio_value`
|
||||
- `updated_at` (`string`, ISO 8601): response generation timestamp
|
||||
- `source` (`string`): quote source such as `yfinance` or `fallback`
|
||||
|
||||
#### Example request
|
||||
|
||||
```bash
|
||||
curl "http://localhost:8000/api/portfolio?symbol=GLD"
|
||||
```
|
||||
|
||||
#### Example response
|
||||
|
||||
```json
|
||||
{
|
||||
"symbol": "GLD",
|
||||
"spot_price": 215.0,
|
||||
"portfolio_value": 215000.0,
|
||||
"loan_amount": 600000.0,
|
||||
"ltv_ratio": 2.7907,
|
||||
"updated_at": "2026-03-21T12:34:56.000000+00:00",
|
||||
"source": "fallback"
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 3. Options chain
|
||||
|
||||
### `GET /api/options`
|
||||
|
||||
Returns a simplified options chain snapshot for the symbol.
|
||||
|
||||
#### Query parameters
|
||||
|
||||
| Name | Type | Required | Default | Description |
|
||||
|---|---|---:|---|---|
|
||||
| `symbol` | string | no | `GLD` | Ticker symbol to analyze |
|
||||
|
||||
#### Response schema
|
||||
|
||||
```json
|
||||
{
|
||||
"symbol": "GLD",
|
||||
"updated_at": "2026-03-21T12:34:56.000000+00:00",
|
||||
"calls": [
|
||||
{
|
||||
"strike": 225.75,
|
||||
"premium": 6.45,
|
||||
"expiry": "2026-06-19"
|
||||
}
|
||||
],
|
||||
"puts": [
|
||||
{
|
||||
"strike": 204.25,
|
||||
"premium": 6.02,
|
||||
"expiry": "2026-06-19"
|
||||
}
|
||||
],
|
||||
"source": "fallback"
|
||||
}
|
||||
```
|
||||
|
||||
#### Array item schema
|
||||
|
||||
Each option row in `calls` or `puts` has:
|
||||
|
||||
- `strike` (`number`)
|
||||
- `premium` (`number`)
|
||||
- `expiry` (`string`, `YYYY-MM-DD`)
|
||||
|
||||
#### Field definitions
|
||||
|
||||
- `symbol` (`string`): requested ticker, uppercased
|
||||
- `updated_at` (`string`, ISO 8601): response generation timestamp
|
||||
- `calls` (`array<object>`): example call rows derived from spot
|
||||
- `puts` (`array<object>`): example put rows derived from spot
|
||||
- `source` (`string`): upstream quote source used to derive the chain
|
||||
|
||||
#### Notes
|
||||
|
||||
The current options chain is synthetic. It is not yet a full broker-grade chain feed.
|
||||
|
||||
#### Example request
|
||||
|
||||
```bash
|
||||
curl "http://localhost:8000/api/options?symbol=GLD"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4. Strategies
|
||||
|
||||
### `GET /api/strategies`
|
||||
|
||||
Returns strategy comparison data, recommendations by risk profile, and sensitivity analysis.
|
||||
|
||||
#### Query parameters
|
||||
|
||||
| Name | Type | Required | Default | Description |
|
||||
|---|---|---:|---|---|
|
||||
| `symbol` | string | no | `GLD` | Ticker symbol to analyze |
|
||||
|
||||
#### Top-level response schema
|
||||
|
||||
```json
|
||||
{
|
||||
"symbol": "GLD",
|
||||
"updated_at": "2026-03-21T12:34:56.000000+00:00",
|
||||
"paper_parameters": {},
|
||||
"strategies": [],
|
||||
"recommendations": {},
|
||||
"sensitivity_analysis": {}
|
||||
}
|
||||
```
|
||||
|
||||
#### Top-level field definitions
|
||||
|
||||
- `symbol` (`string`): requested ticker, uppercased
|
||||
- `updated_at` (`string`, ISO 8601): response generation timestamp
|
||||
- `paper_parameters` (`object`): engine inputs used for the analysis
|
||||
- `strategies` (`array<object>`): detailed strategy comparison rows
|
||||
- `recommendations` (`object`): recommendation results keyed by risk profile
|
||||
- `sensitivity_analysis` (`object`): recommendation changes across parameter shifts
|
||||
|
||||
### 4.1 `paper_parameters` schema
|
||||
|
||||
```json
|
||||
{
|
||||
"portfolio_value": 1000000.0,
|
||||
"loan_amount": 600000.0,
|
||||
"margin_call_threshold": 0.75,
|
||||
"spot_price": 460.0,
|
||||
"volatility": 0.16,
|
||||
"risk_free_rate": 0.045
|
||||
}
|
||||
```
|
||||
|
||||
Fields:
|
||||
|
||||
- `portfolio_value` (`number`)
|
||||
- `loan_amount` (`number`)
|
||||
- `margin_call_threshold` (`number`)
|
||||
- `spot_price` (`number`)
|
||||
- `volatility` (`number`)
|
||||
- `risk_free_rate` (`number`)
|
||||
|
||||
### 4.2 `strategies[]` schema
|
||||
|
||||
Each element in `strategies` is produced by `StrategySelectionEngine.compare_all_strategies()`:
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "protective_put_atm",
|
||||
"cost": {},
|
||||
"protection": {},
|
||||
"scenarios": [],
|
||||
"score_inputs": {
|
||||
"annual_cost": 0.0,
|
||||
"hedged_ltv_at_threshold": 0.0
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Fields:
|
||||
|
||||
- `name` (`string`): internal strategy identifier
|
||||
- `cost` (`object`): strategy-specific cost payload
|
||||
- `protection` (`object`): strategy-specific protection payload
|
||||
- `scenarios` (`array<object>`): scenario analysis rows
|
||||
- `score_inputs` (`object`): normalized inputs used for recommendation scoring
|
||||
|
||||
#### Protective put cost schema
|
||||
|
||||
Typical `cost` object for `protective_put_*`:
|
||||
|
||||
```json
|
||||
{
|
||||
"strategy": "protective_put_atm",
|
||||
"label": "ATM",
|
||||
"strike": 460.0,
|
||||
"strike_pct": 1.0,
|
||||
"premium_per_share": 21.1234,
|
||||
"total_cost": 45920.43,
|
||||
"cost_pct_of_portfolio": 0.04592,
|
||||
"term_months": 12,
|
||||
"annualized_cost": 45920.43,
|
||||
"annualized_cost_pct": 0.04592
|
||||
}
|
||||
```
|
||||
|
||||
#### Protective put protection schema
|
||||
|
||||
```json
|
||||
{
|
||||
"strategy": "protective_put_atm",
|
||||
"threshold_price": 368.0,
|
||||
"strike": 460.0,
|
||||
"portfolio_floor_value": 1000000.0,
|
||||
"unhedged_ltv_at_threshold": 0.75,
|
||||
"hedged_ltv_at_threshold": 0.652174,
|
||||
"payoff_at_threshold": 200000.0,
|
||||
"maintains_margin_call_buffer": true
|
||||
}
|
||||
```
|
||||
|
||||
#### Protective put scenario row schema
|
||||
|
||||
```json
|
||||
{
|
||||
"price_change_pct": -0.2,
|
||||
"gld_price": 368.0,
|
||||
"gold_value": 800000.0,
|
||||
"option_payoff": 200000.0,
|
||||
"hedge_cost": 45920.43,
|
||||
"net_portfolio_value": 954079.57,
|
||||
"unhedged_ltv": 0.75,
|
||||
"hedged_ltv": 0.6,
|
||||
"margin_call_without_hedge": true,
|
||||
"margin_call_with_hedge": false
|
||||
}
|
||||
```
|
||||
|
||||
#### Laddered put cost schema
|
||||
|
||||
Typical `cost` object for `laddered_put_*`:
|
||||
|
||||
```json
|
||||
{
|
||||
"strategy": "laddered_put_50_50_atm_otm95",
|
||||
"label": "50_50_ATM_OTM95",
|
||||
"legs": [
|
||||
{
|
||||
"weight": 0.5,
|
||||
"strike": 460.0,
|
||||
"premium_per_share": 21.1234,
|
||||
"weighted_cost": 22960.22
|
||||
}
|
||||
],
|
||||
"blended_premium_per_share": 18.4567,
|
||||
"blended_cost": 40123.45,
|
||||
"cost_pct_of_portfolio": 0.040123,
|
||||
"annualized_cost": 40123.45,
|
||||
"annualized_cost_pct": 0.040123
|
||||
}
|
||||
```
|
||||
|
||||
#### Laddered put protection schema
|
||||
|
||||
```json
|
||||
{
|
||||
"strategy": "laddered_put_50_50_atm_otm95",
|
||||
"threshold_price": 368.0,
|
||||
"portfolio_floor_value": 975000.0,
|
||||
"payoff_at_threshold": 175000.0,
|
||||
"unhedged_ltv_at_threshold": 0.75,
|
||||
"hedged_ltv_at_threshold": 0.615385,
|
||||
"maintains_margin_call_buffer": true,
|
||||
"legs": [
|
||||
{
|
||||
"weight": 0.5,
|
||||
"strike": 460.0,
|
||||
"weighted_payoff_at_threshold": 100000.0
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
#### Lease duration analysis cost schema
|
||||
|
||||
Typical `cost` object for `lease_duration_analysis`:
|
||||
|
||||
```json
|
||||
{
|
||||
"strategy": "lease_duration_analysis",
|
||||
"comparison": [
|
||||
{
|
||||
"months": 3,
|
||||
"strike": 460.0,
|
||||
"premium_per_share": 9.1234,
|
||||
"total_cost": 19833.48,
|
||||
"annualized_cost": 79333.92,
|
||||
"annualized_cost_pct": 0.079334,
|
||||
"rolls_per_year": 4.0,
|
||||
"recommended_roll_month": 2
|
||||
}
|
||||
],
|
||||
"optimal_duration_months": 12,
|
||||
"lowest_annual_cost": 45920.43,
|
||||
"lowest_annual_cost_pct": 0.04592
|
||||
}
|
||||
```
|
||||
|
||||
#### Lease duration analysis protection schema
|
||||
|
||||
```json
|
||||
{
|
||||
"strategy": "lease_duration_analysis",
|
||||
"threshold_price": 368.0,
|
||||
"durations": [
|
||||
{
|
||||
"months": 12,
|
||||
"payoff_at_threshold": 200000.0,
|
||||
"hedged_ltv_at_threshold": 0.6,
|
||||
"maintains_margin_call_buffer": true
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### 4.3 `recommendations` schema
|
||||
|
||||
The object contains keys:
|
||||
|
||||
- `conservative`
|
||||
- `balanced`
|
||||
- `cost_sensitive`
|
||||
|
||||
Each recommendation object has this shape:
|
||||
|
||||
```json
|
||||
{
|
||||
"risk_profile": "balanced",
|
||||
"recommended_strategy": "laddered_put_50_50_atm_otm95",
|
||||
"rationale": {
|
||||
"portfolio_value": 1000000.0,
|
||||
"loan_amount": 600000.0,
|
||||
"margin_call_threshold": 0.75,
|
||||
"spot_price": 460.0,
|
||||
"volatility": 0.16,
|
||||
"risk_free_rate": 0.045
|
||||
},
|
||||
"comparison_summary": [
|
||||
{
|
||||
"name": "protective_put_atm",
|
||||
"annual_cost": 45920.43,
|
||||
"hedged_ltv_at_threshold": 0.6
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### 4.4 `sensitivity_analysis` schema
|
||||
|
||||
```json
|
||||
{
|
||||
"volatility": [
|
||||
{
|
||||
"volatility": 0.12,
|
||||
"recommended_strategy": "protective_put_otm_95"
|
||||
}
|
||||
],
|
||||
"spot_price": [
|
||||
{
|
||||
"spot_price": 414.0,
|
||||
"recommended_strategy": "protective_put_otm_95"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## WebSocket API
|
||||
|
||||
## Endpoint
|
||||
|
||||
### `WS /ws/updates`
|
||||
|
||||
Used for server-pushed real-time updates.
|
||||
|
||||
### Connection lifecycle
|
||||
|
||||
1. Client opens a WebSocket connection to `/ws/updates`
|
||||
2. Server accepts the connection
|
||||
3. Server immediately sends a `connected` event
|
||||
4. Server periodically broadcasts `portfolio_update` events
|
||||
5. Client may keep the connection alive by sending text frames
|
||||
6. Server removes the connection when disconnected or on send failure
|
||||
|
||||
### Event: `connected`
|
||||
|
||||
Sent once after successful connection.
|
||||
|
||||
#### Schema
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "connected",
|
||||
"message": "Real-time updates enabled"
|
||||
}
|
||||
```
|
||||
|
||||
Fields:
|
||||
|
||||
- `type` (`string`): event name
|
||||
- `message` (`string`): human-readable confirmation
|
||||
|
||||
### Event: `portfolio_update`
|
||||
|
||||
Broadcast on an interval controlled by `WEBSOCKET_INTERVAL_SECONDS`.
|
||||
|
||||
#### Schema
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "portfolio_update",
|
||||
"connections": 2,
|
||||
"portfolio": {
|
||||
"symbol": "GLD",
|
||||
"spot_price": 215.0,
|
||||
"portfolio_value": 215000.0,
|
||||
"loan_amount": 600000.0,
|
||||
"ltv_ratio": 2.7907,
|
||||
"updated_at": "2026-03-21T12:34:56.000000+00:00",
|
||||
"source": "fallback"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Fields:
|
||||
|
||||
- `type` (`string`): event name
|
||||
- `connections` (`integer`): current number of connected WebSocket clients
|
||||
- `portfolio` (`object`): same schema as `GET /api/portfolio`
|
||||
|
||||
### Example JavaScript client
|
||||
|
||||
```js
|
||||
const ws = new WebSocket('ws://localhost:8000/ws/updates');
|
||||
|
||||
ws.onmessage = (event) => {
|
||||
const payload = JSON.parse(event.data);
|
||||
console.log(payload.type, payload);
|
||||
};
|
||||
|
||||
ws.onopen = () => {
|
||||
// optional keepalive or ping surrogate
|
||||
setInterval(() => {
|
||||
if (ws.readyState === WebSocket.OPEN) {
|
||||
ws.send('ping');
|
||||
}
|
||||
}, 10000);
|
||||
};
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## OpenAPI
|
||||
|
||||
Because the app is built on FastAPI, interactive docs are typically available at:
|
||||
|
||||
- `/docs`
|
||||
- `/redoc`
|
||||
|
||||
This file is the human-oriented reference for payload semantics and current behavior.
|
||||
|
||||
## Notes and limitations
|
||||
|
||||
- Endpoints are read-only today
|
||||
- There are no POST/PUT/DELETE endpoints yet
|
||||
- The options chain is currently synthetic
|
||||
- Strategy outputs are paper-analysis results, not execution instructions
|
||||
- Symbol validation is minimal and currently delegated to downstream quote behavior
|
||||
437
docs/ARCHITECTURE.md
Normal file
437
docs/ARCHITECTURE.md
Normal 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
|
||||
424
docs/STRATEGIES.md
Normal file
424
docs/STRATEGIES.md
Normal file
@@ -0,0 +1,424 @@
|
||||
# Strategy Documentation
|
||||
|
||||
## Overview
|
||||
|
||||
Vault Dashboard currently documents and compares hedging approaches for a Lombard-style loan backed by gold exposure. The implementation focuses on paper analysis using option pricing, LTV protection metrics, and scenario analysis.
|
||||
|
||||
The strategy subsystem currently includes:
|
||||
|
||||
- protective puts
|
||||
- laddered puts
|
||||
- lease/LEAPS duration analysis
|
||||
|
||||
This document focuses on the two primary hedge structures requested here:
|
||||
|
||||
- protective put
|
||||
- laddered put
|
||||
|
||||
---
|
||||
|
||||
## Common portfolio assumptions
|
||||
|
||||
The default research engine in `app/strategies/engine.py` uses:
|
||||
|
||||
- portfolio value: `1,000,000`
|
||||
- loan amount: `600,000`
|
||||
- margin-call threshold: `0.75`
|
||||
- spot price: `460`
|
||||
- volatility: `0.16`
|
||||
- risk-free rate: `0.045`
|
||||
|
||||
From these values, the portfolio is modeled as a `LombardPortfolio`:
|
||||
|
||||
- gold ounces = `portfolio_value / spot_price`
|
||||
- initial LTV = `loan_amount / portfolio_value`
|
||||
- margin call price = `loan_amount / (margin_call_ltv * gold_ounces)`
|
||||
|
||||
These assumptions create the common basis used to compare all strategies.
|
||||
|
||||
---
|
||||
|
||||
## Protective put
|
||||
|
||||
## What it is
|
||||
|
||||
A protective put is the simplest downside hedge in the project.
|
||||
|
||||
Structure:
|
||||
|
||||
- long the underlying collateral exposure implicitly represented by the gold-backed portfolio
|
||||
- buy one put hedge sized to the portfolio's underlying units
|
||||
|
||||
In this codebase, `ProtectivePutStrategy` creates a single long put whose strike is defined as a percentage of spot.
|
||||
|
||||
Examples currently used by the engine:
|
||||
|
||||
- ATM protective put: strike = `100%` of spot
|
||||
- 95% OTM protective put: strike = `95%` of spot
|
||||
- 90% OTM protective put: strike = `90%` of spot
|
||||
|
||||
## Why use it
|
||||
|
||||
A protective put sets a floor on downside beyond the strike, helping reduce the chance that falling collateral value pushes the portfolio above the margin-call LTV.
|
||||
|
||||
## How it is implemented
|
||||
|
||||
**File:** `app/strategies/protective_put.py`
|
||||
|
||||
Main properties:
|
||||
|
||||
- `hedge_units`: `portfolio.gold_value / spot_price`
|
||||
- `strike`: `spot_price * strike_pct`
|
||||
- `term_years`: `months / 12`
|
||||
|
||||
A put contract is priced with Black-Scholes inputs:
|
||||
|
||||
- current spot
|
||||
- strike
|
||||
- time to expiry
|
||||
- risk-free rate
|
||||
- volatility
|
||||
- option type = `put`
|
||||
|
||||
The resulting `OptionContract` uses:
|
||||
|
||||
- `quantity = 1.0`
|
||||
- `contract_size = hedge_units`
|
||||
|
||||
That means one model contract covers the full portfolio exposure in underlying units.
|
||||
|
||||
## Protective put payoff intuition
|
||||
|
||||
At expiry:
|
||||
|
||||
- if spot is above strike, the put expires worthless
|
||||
- if spot is below strike, payoff rises linearly as `strike - spot`
|
||||
|
||||
Total gross payoff is:
|
||||
|
||||
```text
|
||||
max(strike - spot, 0) * hedge_units
|
||||
```
|
||||
|
||||
## Protective put trade-offs
|
||||
|
||||
Advantages:
|
||||
|
||||
- simple to explain
|
||||
- clear downside floor
|
||||
- strongest protection when strike is high
|
||||
|
||||
Costs:
|
||||
|
||||
- premium can be expensive, especially at-the-money and for longer tenor
|
||||
- full notional protection may overspend relative to a client's risk budget
|
||||
- upside is preserved, but cost drags returns
|
||||
|
||||
---
|
||||
|
||||
## Laddered put
|
||||
|
||||
## What it is
|
||||
|
||||
A laddered put splits the hedge across multiple put strikes instead of buying the full hedge at one strike.
|
||||
|
||||
Structure:
|
||||
|
||||
- multiple long put legs
|
||||
- each leg covers a weighted fraction of the total hedge
|
||||
- lower strikes usually reduce premium while preserving some tail protection
|
||||
|
||||
Examples currently used by the engine:
|
||||
|
||||
- `50/50` ATM + 95% OTM
|
||||
- `33/33/33` ATM + 95% OTM + 90% OTM
|
||||
|
||||
## Why use it
|
||||
|
||||
A ladder can reduce hedge cost versus a full ATM protective put, while still providing meaningful protection as the underlying falls.
|
||||
|
||||
This is useful when:
|
||||
|
||||
- full-cost protection is too expensive
|
||||
- some drawdown can be tolerated before the hedge fully engages
|
||||
- the client wants a better cost/protection balance
|
||||
|
||||
## How it is implemented
|
||||
|
||||
**File:** `app/strategies/laddered_put.py`
|
||||
|
||||
A `LadderSpec` defines:
|
||||
|
||||
- `weights`
|
||||
- `strike_pcts`
|
||||
- `months`
|
||||
|
||||
Validation rules:
|
||||
|
||||
- number of weights must equal number of strikes
|
||||
- weights must sum to `1.0`
|
||||
|
||||
Each leg is implemented by internally creating a `ProtectivePutStrategy`, then weighting its premium and payoff.
|
||||
|
||||
## Ladder payoff intuition
|
||||
|
||||
Each leg pays off independently:
|
||||
|
||||
```text
|
||||
max(leg_strike - spot, 0) * hedge_units * weight
|
||||
```
|
||||
|
||||
Total ladder payoff is the sum across legs.
|
||||
|
||||
Relative to a single-strike hedge:
|
||||
|
||||
- protection turns on in stages
|
||||
- blended premium is lower when some legs are farther OTM
|
||||
- downside support is smoother but less absolute near the first loss zone than a full ATM hedge
|
||||
|
||||
## Ladder trade-offs
|
||||
|
||||
Advantages:
|
||||
|
||||
- lower blended hedge cost
|
||||
- more flexible cost/protection shaping
|
||||
- better fit for cost-sensitive clients
|
||||
|
||||
Costs and limitations:
|
||||
|
||||
- weaker immediate protection than a fully ATM hedge
|
||||
- more complex to explain to users
|
||||
- floor value depends on weight distribution across strikes
|
||||
|
||||
---
|
||||
|
||||
## Cost calculations
|
||||
|
||||
## Protective put cost calculation
|
||||
|
||||
`ProtectivePutStrategy.calculate_cost()` returns:
|
||||
|
||||
- `premium_per_share`
|
||||
- `total_cost`
|
||||
- `cost_pct_of_portfolio`
|
||||
- `term_months`
|
||||
- `annualized_cost`
|
||||
- `annualized_cost_pct`
|
||||
|
||||
### Formula summary
|
||||
|
||||
Let:
|
||||
|
||||
- `P` = option premium per underlying unit
|
||||
- `U` = hedge units
|
||||
- `T` = term in years
|
||||
- `V` = portfolio value
|
||||
|
||||
Then:
|
||||
|
||||
```text
|
||||
total_cost = P * U
|
||||
cost_pct_of_portfolio = total_cost / V
|
||||
annualized_cost = total_cost / T
|
||||
annualized_cost_pct = annualized_cost / V
|
||||
```
|
||||
|
||||
Because the model contract size equals the full hedge units, the total premium directly represents the whole-portfolio hedge cost.
|
||||
|
||||
## Laddered put cost calculation
|
||||
|
||||
`LadderedPutStrategy.calculate_cost()` computes weighted leg costs.
|
||||
|
||||
For each leg `i`:
|
||||
|
||||
- `weight_i`
|
||||
- `premium_i`
|
||||
- `hedge_units`
|
||||
|
||||
Leg cost:
|
||||
|
||||
```text
|
||||
leg_cost_i = premium_i * hedge_units * weight_i
|
||||
```
|
||||
|
||||
Blended totals:
|
||||
|
||||
```text
|
||||
blended_cost = sum(leg_cost_i)
|
||||
blended_premium_per_share = sum(premium_i * weight_i)
|
||||
annualized_cost = blended_cost / term_years
|
||||
cost_pct_of_portfolio = blended_cost / portfolio_value
|
||||
annualized_cost_pct = annualized_cost / portfolio_value
|
||||
```
|
||||
|
||||
## Why annualized cost matters
|
||||
|
||||
The engine compares strategies with different durations, especially in `LeaseStrategy`. Annualizing allows the system to compare short-dated and long-dated hedges on a common yearly basis.
|
||||
|
||||
---
|
||||
|
||||
## Protection calculations
|
||||
|
||||
## Margin-call threshold price
|
||||
|
||||
The project defines the collateral price that would trigger a margin call as:
|
||||
|
||||
```text
|
||||
margin_call_price = loan_amount / (margin_call_ltv * gold_ounces)
|
||||
```
|
||||
|
||||
This is a key reference point for all protection calculations.
|
||||
|
||||
## Protective put protection calculation
|
||||
|
||||
At the threshold price:
|
||||
|
||||
1. compute the put payoff
|
||||
2. add that payoff to the stressed collateral value
|
||||
3. recompute LTV on the hedged collateral
|
||||
|
||||
Formulas:
|
||||
|
||||
```text
|
||||
payoff_at_threshold = max(strike - threshold_price, 0) * hedge_units
|
||||
hedged_value_at_threshold = gold_value_at_threshold + payoff_at_threshold
|
||||
hedged_ltv_at_threshold = loan_amount / hedged_value_at_threshold
|
||||
```
|
||||
|
||||
The strategy is flagged as maintaining a margin buffer when:
|
||||
|
||||
```text
|
||||
hedged_ltv_at_threshold < margin_call_ltv
|
||||
```
|
||||
|
||||
## Laddered put protection calculation
|
||||
|
||||
For a ladder, threshold payoff is the weighted sum of all leg payoffs:
|
||||
|
||||
```text
|
||||
weighted_payoff_i = max(strike_i - threshold_price, 0) * hedge_units * weight_i
|
||||
payoff_at_threshold = sum(weighted_payoff_i)
|
||||
hedged_value_at_threshold = gold_value_at_threshold + payoff_at_threshold
|
||||
hedged_ltv_at_threshold = loan_amount / hedged_value_at_threshold
|
||||
```
|
||||
|
||||
The ladder's implied floor value is approximated as the weighted strike coverage:
|
||||
|
||||
```text
|
||||
portfolio_floor_value = sum(strike_i * hedge_units * weight_i)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Scenario analysis methodology
|
||||
|
||||
## Scenario grid
|
||||
|
||||
The current scenario engine in `ProtectivePutStrategy` uses a fixed price-change grid:
|
||||
|
||||
```text
|
||||
-60%, -50%, -40%, -30%, -20%, -10%, 0%, +10%, +20%, +30%, +40%, +50%
|
||||
```
|
||||
|
||||
For each change:
|
||||
|
||||
```text
|
||||
scenario_price = spot_price * (1 + change)
|
||||
```
|
||||
|
||||
Negative or zero prices are ignored.
|
||||
|
||||
## Metrics produced per scenario
|
||||
|
||||
For each scenario, the strategy computes:
|
||||
|
||||
- scenario spot price
|
||||
- unhedged gold value
|
||||
- option payoff
|
||||
- hedge cost
|
||||
- net portfolio value after hedge cost
|
||||
- unhedged LTV
|
||||
- hedged LTV
|
||||
- whether a margin call occurs without the hedge
|
||||
- whether a margin call occurs with the hedge
|
||||
|
||||
### Protective put scenario formulas
|
||||
|
||||
Let `S` be scenario spot.
|
||||
|
||||
```text
|
||||
gold_value = gold_ounces * S
|
||||
option_payoff = max(strike - S, 0) * hedge_units
|
||||
hedged_collateral = gold_value + option_payoff
|
||||
net_portfolio_value = gold_value + option_payoff - hedge_cost
|
||||
unhedged_ltv = loan_amount / gold_value
|
||||
hedged_ltv = loan_amount / hedged_collateral
|
||||
```
|
||||
|
||||
Margin-call flags:
|
||||
|
||||
```text
|
||||
margin_call_without_hedge = unhedged_ltv >= margin_call_ltv
|
||||
margin_call_with_hedge = hedged_ltv >= margin_call_ltv
|
||||
```
|
||||
|
||||
### Laddered put scenario formulas
|
||||
|
||||
For ladders:
|
||||
|
||||
```text
|
||||
option_payoff = sum(max(strike_i - S, 0) * hedge_units * weight_i)
|
||||
hedged_collateral = gold_value + option_payoff
|
||||
net_portfolio_value = gold_value + option_payoff - blended_cost
|
||||
```
|
||||
|
||||
All other LTV and margin-call logic is the same.
|
||||
|
||||
## Interpretation methodology
|
||||
|
||||
Scenario analysis is used to answer four practical questions:
|
||||
|
||||
1. **Cost:** How much premium is paid upfront?
|
||||
2. **Activation:** At what downside level does protection meaningfully start?
|
||||
3. **Buffer:** Does the hedge keep LTV below the margin-call threshold under stress?
|
||||
4. **Efficiency:** How much protection is obtained per dollar of annualized hedge cost?
|
||||
|
||||
This is why each strategy exposes both:
|
||||
|
||||
- a `calculate_protection()` summary around the threshold price
|
||||
- a full `get_scenarios()` table across broad upside/downside moves
|
||||
|
||||
---
|
||||
|
||||
## Comparing protective puts vs laddered puts
|
||||
|
||||
| Dimension | Protective put | Laddered put |
|
||||
|---|---|---|
|
||||
| Structure | Single put strike | Multiple weighted put strikes |
|
||||
| Simplicity | Highest | Moderate |
|
||||
| Upfront cost | Usually higher | Usually lower |
|
||||
| Near-threshold protection | Stronger if ATM-heavy | Depends on ladder weights |
|
||||
| Tail downside protection | Strong | Strong, but blended |
|
||||
| Customization | Limited | High |
|
||||
| Best fit | conservative protection | balanced or cost-sensitive protection |
|
||||
|
||||
---
|
||||
|
||||
## Important limitations
|
||||
|
||||
- The strategy engine is currently research-oriented, not an execution engine
|
||||
- Black-Scholes assumptions simplify real-world market behavior
|
||||
- Transaction costs, slippage, taxes, liquidity, and early exercise effects are not modeled here
|
||||
- The API payloads should be treated as analytical outputs, not trade recommendations
|
||||
- For non-`GLD` symbols, the engine currently still uses research-style assumptions rather than a complete live instrument-specific calibration
|
||||
|
||||
## Future strategy extensions
|
||||
|
||||
Natural follow-ups for this subsystem:
|
||||
|
||||
- collars and financed hedges
|
||||
- partial notional hedging
|
||||
- dynamic re-hedging rules
|
||||
- volatility surface-based pricing
|
||||
- broker-native contract sizing and expiries
|
||||
- user-configurable scenario grids
|
||||
Reference in New Issue
Block a user