--- generated_by: repo-insight version: 1 created: 2026-03-03 last_updated: 2026-03-03 source_commit: 0d9dffa coverage: standard --- # 03 — API Contracts ## Purpose Documents all REST API endpoints, authentication requirements, request/response shapes, and error conventions. ## TL;DR - Base URL: `https://arb.zhouyangclaw.com` (prod) or `http://localhost:8000` (local). - Auth: Bearer JWT in `Authorization` header. Two public endpoints (`/api/health`, `/api/rates`) need no token. - Token lifecycle: access token 24 h, refresh token 7 d; use `POST /api/refresh` to renew. - Registration is invite-code gated: must supply a valid `invite_code` in register body. - All timestamps are Unix epoch (seconds or ms depending on field; see per-endpoint notes). - Funding rates are stored as decimals (e.g. `0.0001` = 0.01%). Frontend multiplies by 10000 for "万分之" display. - Error responses: standard FastAPI `{"detail": "..."}` with appropriate HTTP status codes. ## Canonical Facts ### Authentication #### `POST /api/register` ```json // Request { "email": "user@example.com", "password": "...", "invite_code": "XXXX" } // Response 200 { "access_token": "", "refresh_token": "", "token_type": "bearer", "user": { "id": 1, "email": "...", "role": "user" } } // Errors: 400 (invite invalid/expired), 409 (email taken) ``` #### `POST /api/login` ```json // Request { "email": "user@example.com", "password": "..." } // Response 200 { "access_token": "", "refresh_token": "", "token_type": "bearer", "user": { "id": 1, "email": "...", "role": "user" } } // Errors: 401 (invalid credentials), 403 (banned) ``` #### `POST /api/refresh` ```json // Request { "refresh_token": "" } // Response 200 { "access_token": "", "token_type": "bearer" } // Errors: 401 (expired/revoked) ``` #### `POST /api/logout` ```json // Request header: Authorization: Bearer // Request body: { "refresh_token": "" } // Response 200: { "ok": true } ``` #### `GET /api/me` ```json // Auth required // Response 200 { "id": 1, "email": "...", "role": "user", "created_at": "..." } ``` ### Public Endpoints (no auth) #### `GET /api/health` ```json { "status": "ok", "timestamp": "2026-03-03T12:00:00" } ``` #### `GET /api/rates` Returns live Binance premiumIndex for BTCUSDT, ETHUSDT, XRPUSDT, SOLUSDT. Cached 3 s. ```json { "BTC": { "symbol": "BTCUSDT", "markPrice": 65000.0, "indexPrice": 64990.0, "lastFundingRate": 0.0001, "nextFundingTime": 1234567890000, "timestamp": 1234567890000 }, "ETH": { ... }, "XRP": { ... }, "SOL": { ... } } ``` ### Protected Endpoints (Bearer JWT required) #### `GET /api/history` 7-day funding rate history from Binance. Cached 60 s. ```json { "BTC": [ { "fundingTime": 1234567890000, "fundingRate": 0.0001, "timestamp": "2026-03-01T08:00:00" } ], "ETH": [ ... ], "XRP": [ ... ], "SOL": [ ... ] } ``` #### `GET /api/stats` 7-day funding rate statistics. Cached 60 s. ```json { "BTC": { "mean7d": 0.01, "annualized": 10.95, "count": 21 }, "ETH": { ... }, "combo": { "mean7d": 0.009, "annualized": 9.85 } } // mean7d in %; annualized = mean * 3 * 365 * 100 ``` #### `GET /api/stats/ytd` Year-to-date annualized stats. Cached 3600 s. ```json { "BTC": { "annualized": 12.5, "count": 150 }, "ETH": { ... } } ``` #### `GET /api/snapshots?hours=24&limit=5000` Rate snapshots from local PG. ```json { "count": 43200, "hours": 24, "data": [ { "ts": 1709000000, "btc_rate": 0.0001, "eth_rate": 0.00008, "btc_price": 65000, "eth_price": 3200 } ] } ``` #### `GET /api/kline?symbol=BTC&interval=1h&limit=500` Candlestick bars derived from `rate_snapshots`. Rates scaled by ×10000. - `interval`: `1m`, `5m`, `30m`, `1h`, `4h`, `8h`, `1d`, `1w`, `1M` ```json { "symbol": "BTC", "interval": "1h", "count": 24, "data": [ { "time": 1709000000, "open": 1.0, "high": 1.2, "low": 0.8, "close": 1.1, "price_open": 65000, "price_high": 65500, "price_low": 64800, "price_close": 65200 } ] } ``` #### `GET /api/signals/history?limit=100` Legacy signal log from `signal_logs` table. ```json { "items": [ { "id": 1, "symbol": "BTCUSDT", "rate": 0.0001, "annualized": 10.95, "sent_at": "2026-03-01T08:00:00", "message": "..." } ] } ``` #### `GET /api/trades/meta` aggTrades collection status. ```json { "BTC": { "last_agg_id": 123456789, "last_time_ms": 1709000000000, "updated_at": "2026-03-03 12:00:00" } } ``` #### `GET /api/trades/summary?symbol=BTC&start_ms=0&end_ms=0&interval=1m` Aggregated OHLCV from `agg_trades` via PG native aggregation. ```json { "symbol": "BTC", "interval": "1m", "count": 60, "data": [ { "bar_ms": 1709000000000, "buy_vol": 10.5, "sell_vol": 9.3, "trade_count": 45, "vwap": 65000.0, "max_qty": 2.5 } ] } ``` #### Signal V52 Endpoints (inferred from frontend routes) - `GET /api/signals/v52` — signals for v52_8signals strategy - `GET /api/paper/trades` — paper trade history - `GET /api/paper/trades/v52` — v52 paper trade history - `GET /api/live/trades` — live trade history - `GET /api/live/config` — current live config - `GET /api/live/events` — live trading event log - `GET /api/server/stats` — server process stats (psutil) ### Auth Header Format ``` Authorization: Bearer ``` Frontend auto-injects via `authFetch()` in `lib/auth.tsx`. On 401, attempts token refresh before retry. ### Error Shape All errors follow FastAPI default: ```json { "detail": "Human-readable error message" } ``` Common HTTP status codes: 400 (bad request), 401 (unauthorized), 403 (forbidden/banned), 404 (not found), 422 (validation error), 502 (Binance upstream error). ## Interfaces / Dependencies - Binance USDC-M Futures REST: `https://fapi.binance.com/fapi/v1/premiumIndex`, `/fundingRate` - CORS allowed origins: `https://arb.zhouyangclaw.com`, `http://localhost:3000`, `http://localhost:3001` - `NEXT_PUBLIC_API_URL` env var controls the frontend base URL (empty = same-origin) ## Unknowns & Risks - [inference] Full endpoint list for signals-v52, paper-v52, live, server pages not confirmed by reading main.py lines 300+. The full saved output contains more routes. - [inference] `POST /api/register` exact field validation (password min length, etc.) not confirmed. - [risk] No rate limiting visible on public endpoints; `/api/rates` with 3 s cache could be bypassed by direct calls. ## Source Refs - `backend/main.py:101-298` — all confirmed REST endpoints - `backend/auth.py:23` — auth router prefix - `backend/main.py:16-21` — CORS config - `frontend/lib/api.ts:90-116` — client-side API wrappers - `frontend/lib/auth.tsx` — `authFetch` with auto-refresh (not fully read)