- Generate full AI-consumable docs (docs/ai/): system overview, architecture, module cheatsheet, API contracts, data model, build guide, decision log, glossary, and open questions (deep tier coverage) - Add PROBLEM_REPORT.md: categorized bug/risk summary - Add DETAILED_CODE_REVIEW.md: full line-by-line review of all 15 backend files, documenting 4 fatal issues, 5 critical deployment bugs, 4 security vulnerabilities, and 6 architecture defects with prioritized fix plan
243 lines
6.6 KiB
Markdown
243 lines
6.6 KiB
Markdown
---
|
||
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": "<jwt>",
|
||
"refresh_token": "<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": "<jwt>",
|
||
"refresh_token": "<token>",
|
||
"token_type": "bearer",
|
||
"user": { "id": 1, "email": "...", "role": "user" }
|
||
}
|
||
// Errors: 401 (invalid credentials), 403 (banned)
|
||
```
|
||
|
||
#### `POST /api/refresh`
|
||
```json
|
||
// Request
|
||
{ "refresh_token": "<token>" }
|
||
// Response 200
|
||
{ "access_token": "<new_jwt>", "token_type": "bearer" }
|
||
// Errors: 401 (expired/revoked)
|
||
```
|
||
|
||
#### `POST /api/logout`
|
||
```json
|
||
// Request header: Authorization: Bearer <access_token>
|
||
// Request body: { "refresh_token": "<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 <access_token>
|
||
```
|
||
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)
|