quantum-hybrid-portfolio / docs /API_REFERENCE.md
rocRevyAreGoals15's picture
Initial HF Space deployment
9e89154
|
Raw
History Blame Contribute Delete
11 kB

API Reference

The Quantum Hybrid Portfolio API is a Flask REST API running on port 5000 by default.

Base URL

  • Local: http://localhost:5000
  • Health: GET /api/health

Authentication

Optional API key via header:

X-API-Key: <your-api-key>

Set API_KEY environment variable on the server for static key validation.

When API_KEY_REQUIRED=true, clients must send X-API-Key on protected routes. GET /api/health and GET /metrics remain usable for probes without a key (see route decorators in api.py).


Response envelope

Most JSON routes wrap payloads in a standard envelope (success_response / error_response in api.py):

Success (2xx)

{
  "data": {},
  "meta": {
    "request_id": "<uuid>",
    "duration_ms": 12.34
  }
}

The Next.js client (web/src/lib/api.ts) and CRA client (frontend/src/services/api.js) unwrap data in a response interceptor, so application code typically sees the inner payload only.

Error (4xx / 5xx)

{
  "error": { "code": "ERROR_CODE", "message": "Human-readable message" },
  "meta": { "request_id": "<uuid>" }
}

Exceptions: GET /metrics returns Prometheus text (no JSON envelope). Machine-readable contract: GET /api/docs/openapi.


Web client contract (migration Phase 1)

Must-not-break for the Next.js app (web/) and CRA dashboard (frontend/): changing method, path, auth requirement, or the shape of the unwrapped data payload for these routes requires coordination (version bump, dual support, or client updates). Automated checks: scripts/test_api_integration.py, tests/test_api_integration.py.

Endpoint Method Auth Notes
/api/health GET No key required Rate limit exempt.
/api/config/objectives GET If API_KEY_REQUIRED Optimization objective list.
/api/config/presets GET If required Preset configurations.
/api/config/constraints GET If required Constraint schema.
/api/config/ibm-quantum POST If required Body: { "token": "..." }.
/api/config/ibm-quantum DELETE If required Clears stored token.
/api/config/ibm-quantum/status GET If required Connection status.
/api/config/ibm-quantum/workloads GET Yes Query limit (default 20, max 100). Lists IBM Runtime jobs for the tenant token.
/api/market-data POST If required Historical returns / covariance inputs.
/api/portfolio/optimize POST If required Single optimization run.
/api/portfolio/optimize/batch POST If required Batch optimize.
/api/portfolio/backtest POST If required Backtest.
/api/portfolio/backtest/batch POST If required Batch backtest.
/api/portfolio/efficient-frontier POST If required Efficient frontier points.
/api/runs POST If required Create a durable lab run (async optimization). Returns run_id.
/api/runs GET If required List recent lab runs for the tenant. Query limit (default 20).
/api/runs/<id> GET If required Fetch run status, spec, and result (tenant-scoped).

Environment variables (Flask server vs Next.js)

Variable Where Purpose
PORT Flask (server) API listen port (default 5000).
API_KEY, API_KEY_REQUIRED Flask (server) Static API key validation.
DATABASE_URL, REDIS_*, etc. Flask (server) Backend only; never expose to the browser bundle.
NEXT_PUBLIC_API_URL Next.js (build-time / browser) Base URL for the Flask API (e.g. http://127.0.0.1:5000). Empty string = same origin (use when a dev proxy forwards /api to Flask).
NEXT_PUBLIC_API_KEY Next.js (browser) Optional; sent as X-API-Key if set. Treat as sensitive if the app is public; prefer same-origin proxy and server-side secrets for production.

Copy .env.example to .env for Flask. For the Next app, use web/.env.local (see Next.js docs); only NEXT_PUBLIC_* variables are embedded in client bundles.


Endpoints

Health

GET /api/health

Returns API health and dependency checks. Uses the standard envelope; below is the shape of data after unwrapping (see Response envelope).

data payload (illustrative):

{
  "status": "healthy",
  "checks": { "api": "ok", "market_data": "available" },
  "details": { "version": "1.0.0", "timestamp": "2026-02-15T12:00:00Z" },
  "cache_entries": 0,
  "message": "Quantum Portfolio Backend is running"
}

Market Data

POST /api/market-data

Fetch historical market data for given tickers.

Request:

{
  "tickers": ["AAPL", "MSFT", "GOOGL"],
  "start_date": "2020-01-01",
  "end_date": "2024-12-31"
}

Response: Returns returns matrix, covariance, asset names, dates.


Portfolio Optimization

POST /api/portfolio/optimize

Run QSW portfolio optimization.

Request:

{
  "tickers": ["AAPL", "MSFT", "GOOGL"],
  "start_date": "2020-01-01",
  "end_date": "2024-12-31",
  "objective": "max_sharpe",
  "omega": 0.3,
  "evolution_time": 10,
  "max_weight": 0.1,
  "turnover_limit": 0.2,
  "regime": "normal",
  "strategy_preset": "balanced",
  "constraints": {
    "max_weight": 0.1,
    "turnover_limit": 0.2,
    "min_weight": 0.001
  },
  "target_return": 0.08
}

Parameters:

Field Type Description
tickers string[] Asset symbols
start_date string YYYY-MM-DD
end_date string YYYY-MM-DD
objective string max_sharpe, min_variance, risk_parity, hrp, target_return
omega number Quantum mixing (0.05–0.6)
evolution_time number Evolution steps
max_weight number Max position (e.g. 0.1 = 10%)
turnover_limit number Max turnover per rebalance
regime string normal, bull, bear, volatile
strategy_preset string balanced, conservative, etc.
target_return number Required when objective=target_return

Response:

{
  "qsw_result": {
    "weights": [0.05, 0.12, ...],
    "sharpe_ratio": 1.45,
    "expected_return": 0.12,
    "volatility": 0.15,
    "n_active": 18
  },
  "holdings": [
    { "name": "AAPL", "weight": 0.05, "sector": "Technology" }
  ],
  "benchmarks": { ... },
  "assets": [ ... ]
}

Batch Optimization

POST /api/portfolio/optimize/batch

Run multiple optimizations in one request.

Request:

{
  "requests": [
    { "tickers": ["AAPL", "MSFT"], "start_date": "2020-01-01", "end_date": "2024-12-31", "omega": 0.2 },
    { "tickers": ["AAPL", "MSFT"], "start_date": "2020-01-01", "end_date": "2024-12-31", "omega": 0.4 }
  ],
  "stop_on_error": false
}

Response:

{
  "results": [
    { "status": "ok", "result": { ... } },
    { "status": "ok", "result": { ... } }
  ]
}

Backtest

POST /api/portfolio/backtest

Run historical backtest.

Request:

{
  "tickers": ["AAPL", "MSFT", "GOOGL"],
  "start_date": "2020-01-01",
  "end_date": "2024-12-31",
  "objective": "max_sharpe",
  "strategy_preset": "balanced",
  "rebalance_frequency": "monthly",
  "constraints": { "max_weight": 0.1 }
}

Response:

{
  "total_return": 0.45,
  "annual_return": 0.12,
  "sharpe_ratio": 1.2,
  "max_drawdown": 0.15,
  "volatility": 0.18,
  "equity_curve": [
    { "date": "2020-01-02", "portfolio_value": 100000 },
    ...
  ]
}

Batch Backtest

POST /api/portfolio/backtest/batch

Run multiple backtests (e.g. for Scenario Tester).

Request:

{
  "requests": [
    { "tickers": ["SPY"], "start_date": "2020-01-01", "end_date": "2024-12-31" },
    { "tickers": ["QQQ"], "start_date": "2020-01-01", "end_date": "2024-12-31" }
  ],
  "stop_on_error": false
}

Efficient Frontier

POST /api/portfolio/efficient-frontier

Compute efficient frontier points.

Request:

{
  "tickers": ["AAPL", "MSFT", "GOOGL"],
  "start_date": "2020-01-01",
  "end_date": "2024-12-31",
  "n_points": 20
}

Response:

{
  "frontier_points": [
    { "volatility": 0.12, "target_return": 0.08 },
    ...
  ]
}

Configuration

GET /api/config/objectives

Returns available optimization objectives.

GET /api/config/constraints

Returns constraint schema (max_weight, turnover_limit, etc.).

GET /api/config/presets

Returns strategy presets (balanced, conservative, etc.).


Ticker Search

GET /api/tickers/search?q=AAP

Search ticker catalog (autocomplete).

Response:

{
  "tickers": [
    { "symbol": "AAPL", "name": "Apple Inc.", "sector": "Technology" }
  ]
}

Async Jobs

POST /api/jobs/optimize

Submit async optimization. Returns job_id.

Request:

{
  "payload": { /* optimize request */ },
  "webhook_url": "https://your-server/callback"
}

POST /api/jobs/backtest

Submit async backtest. Returns job_id.

GET /api/jobs/<job_id>

Poll job status. Returns status (pending/running/completed/failed) and result when completed.


Lab Runs (durable experiment registry)

Lab runs persist experiment specs and results to SQLite so users can leave the Portfolio Lab and return later to view a report.

POST /api/runs

Create a durable lab run. Enqueues async optimization and returns immediately.

Request:

{
  "payload": {
    "returns": [...],
    "covariance": [[...]],
    "asset_names": ["AAPL", "MSFT"],
    "objective": "hybrid",
    "weight_min": 0.005,
    "weight_max": 0.30,
    "seed": 42,
    "data_mode": "synthetic",
    "regime": "normal"
  }
}

Response (202):

{ "run_id": "<uuid>", "status": "queued" }

GET /api/runs

List recent lab runs for the authenticated tenant.

Query param Default Notes
limit 20 Max 100.

Response: { "runs": [...], "count": N }

GET /api/runs/<id>

Fetch a single run. Returns spec, status, result (when completed), and error (when failed). 403 if the run belongs to a different tenant.

Response:

{
  "id": "<uuid>",
  "tenant_id": "default",
  "status": "completed",
  "execution_kind": "async_optimize",
  "spec": { "objective": "hybrid", "weight_min": 0.005, "..." : "..." },
  "result": { "sharpe_ratio": 1.23, "weights": [...], "holdings": [...] },
  "error": null,
  "created_at": "2026-03-24T...",
  "started_at": "...",
  "finished_at": "..."
}

Metrics

GET /metrics

Prometheus metrics (request counts, latencies, optimization duration).


Error Responses

Errors return:

{
  "error": {
    "code": "ERROR",
    "message": "Human-readable message"
  }
}

HTTP status codes: 400 (validation), 401 (unauthorized), 429 (rate limit), 500 (server error).


Last updated: 2026-02