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

594 lines
12 KiB
Markdown

# 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