API & Backend¶
app/api/main.py + 11 routers em app/api/routers/. FastAPI 100% async sobre asyncpg.
Estrutura¶
app/api/
├── main.py # FastAPI app, lifespan, auth, rate limit
├── routers/
│ ├── scan.py # /api/scan — sinais para o EA
│ ├── signals.py # /api/signals — histórico
│ ├── history.py # /api/history — agregados
│ ├── executions.py # POST /api/executions — EA reporta
│ ├── ea_status.py # /api/ea-status — diagnóstico EA
│ ├── account.py # /api/account/* — self-service assinante
│ ├── performance.py # /api/performance — track record
│ ├── calibration.py # /api/calibration — ECE/Brier
│ ├── models.py # /api/models — lista champions
│ ├── status.py # /api/status, /api/health
│ ├── public.py # /api/public/stats (sem auth)
│ ├── admin.py # /admin/keys, /admin/monitoring-data
│ └── webhooks.py # /webhooks/eduzz — HMAC + provisionamento
└── static/ # HTML+JS (frontend)
Camadas de auth¶
flowchart LR
Req[Request] --> IPCap{IP hard cap<br/>200/min?}
IPCap -->|excedeu| 429a[HTTP 429]
IPCap -->|ok| Owner{key == env<br/>API_KEY?}
Owner -->|sim| OwnerOK[KeyInfo owner]
Owner -->|não| DB{lookup<br/>api_keys table}
DB -->|404| 401[HTTP 401]
DB -->|ok| PlanRL{rate limit<br/>do plano?}
PlanRL -->|excedeu| 429b[HTTP 429]
PlanRL -->|ok| Shared{shared key<br/>3+ IPs/1h?}
Shared -->|sim| Alert[Telegram alert]
Shared --> Allow[KeyInfo assinante]
Rate limits¶
| Camada | Limite | Janela |
|---|---|---|
| IP hard cap (DDoS) | 200 req/min | 1 minuto sliding |
| Owner | 600 req/min | 1 minuto sliding |
| Pro | 300 req/min | 1 minuto sliding |
| Basic | 60 req/min | 1 minuto sliding |
| Invalid keys (anti brute-force) | 20 req/min | 1 minuto sliding |
Implementação: Redis ZSET com fallback in-memory. Veja app/api/main.py:71-105.
Anti-key-sharing¶
Tracking de IPs únicos por chave em 1h (Redis SET + EXPIRE). Se > 3 IPs distintos → alerta no Telegram (cooldown 6h).
Tolerante para uso legítimo: VPS + PC + celular = 3 IPs OK.
KeyInfo (v2.28)¶
class KeyInfo:
plan_tier: str # owner | pro | basic | telegram
symbols_allowed: list | None # None = todos
tfs_allowed: list | None
expires_at: datetime | None
key_id: int | None # PK em api_keys (None para owner-env)
user_name: str | None
email: str | None
last_seen_at: datetime | None
Anexado a request.state.key_info por require_api_key. Routers leem via request.state.key_info.symbols_allowed para filtrar.
CORS¶
_ALLOWED_ORIGINS = "https://quantfx.com.br,https://api.quantfx.com.br"
methods = ["GET", "POST", "DELETE"]
Restrito ao próprio domínio. Cloudflare Tunnel garante que requests externos passam por proxy CF, com cabeçalho cf-connecting-ip (usado pelo _client_ip()).
Endpoint públicos vs autenticados¶
| Tipo | Auth | Endpoints |
|---|---|---|
| Público | nenhuma | /api/public/stats, /api/health (no fail mode) |
| Assinante | X-API-Key |
/api/scan, /api/account/*, /api/signals, etc. |
| Admin | X-Admin-Secret |
/admin/keys, /admin/monitoring-data |
| Webhook | HMAC-SHA256 | /webhooks/eduzz |
OpenAPI¶
FastAPI gera spec automaticamente:
- Swagger UI:
https://api.quantfx.com.br/docs - ReDoc:
https://api.quantfx.com.br/redoc - JSON:
https://api.quantfx.com.br/openapi.json
Healthcheck¶
GET /api/health retorna 200/503 conforme:
{
"status": "ok", // ok | degraded | down
"api_version": "2.0.0",
"db": "ok",
"last_prediction": "2026-05-27T02:51:37+00:00",
"last_candle": "2026-05-27T05:45:00+00:00",
"pred_lag_minutes": 3.5,
"btc_lag_minutes": 4.1,
"stale_pred_count": 17,
"total_active_slots": 40
}
UptimeRobot consulta a cada 5 min. Se 503 → notifica Telegram.
Performance characteristics¶
| Métrica | Valor |
|---|---|
| Connection pool | min 2, max 10 (asyncpg) |
| Command timeout | 30s |
| DB latency média | ~3ms (local) |
Cache /api/models |
Pré-aquecido no lifespan (evita cold start 9s) |
| Memory | ~150MB por worker uvicorn |
| CPU idle | ~2% (eventloop dorme) |