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

12 KiB

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:

http://localhost:8000
https://vault.example.com

Content type

Responses use:

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

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

curl -fsS http://localhost:8000/health

Example response

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

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

curl "http://localhost:8000/api/portfolio?symbol=GLD"

Example response

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

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

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

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

{
  "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():

{
  "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_*:

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

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

{
  "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_*:

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

{
  "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:

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

{
  "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:

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

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

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

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

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