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)**
```json
{
"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)**
```json
{
"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](#response-envelope)).
**`data` payload (illustrative):**
```json
{
"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:**
```json
{
"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:**
```json
{
"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:**
```json
{
"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:**
```json
{
"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:**
```json
{
"results": [
{ "status": "ok", "result": { ... } },
{ "status": "ok", "result": { ... } }
]
}
```
---
### Backtest
#### `POST /api/portfolio/backtest`
Run historical backtest.
**Request:**
```json
{
"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:**
```json
{
"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:**
```json
{
"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:**
```json
{
"tickers": ["AAPL", "MSFT", "GOOGL"],
"start_date": "2020-01-01",
"end_date": "2024-12-31",
"n_points": 20
}
```
**Response:**
```json
{
"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:**
```json
{
"tickers": [
{ "symbol": "AAPL", "name": "Apple Inc.", "sector": "Technology" }
]
}
```
---
### Async Jobs
#### `POST /api/jobs/optimize`
Submit async optimization. Returns `job_id`.
**Request:**
```json
{
"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:**
```json
{
"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):**
```json
{ "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:**
```json
{
"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:
```json
{
"error": {
"code": "ERROR",
"message": "Human-readable message"
}
}
```
HTTP status codes: 400 (validation), 401 (unauthorized), 429 (rate limit), 500 (server error).
---
*Last updated: 2026-02*