RUNBOOK — QuantFX (ML-Forex)¶
Objetivo: ter procedimentos passo-a-passo para os 8 cenários de falha mais prováveis. Se você ou alguém de confiança seguir o que está aqui, qualquer um deve voltar em ≤1 hora sem precisar improvisar.
Princípio: primeiro estanca o sangramento (recuperar serviço), depois investiga (logs/causa-raiz), por último corrige permanente.
🗺️ Mapa rápido — onde está o quê¶
| Coisa | Lugar |
|---|---|
| API (FastAPI) | VPS 177.7.58.99 — container forex_api em /opt/quantfx/ |
| Worker (predict/retrain) | PC local H: — container forex_worker em C:/Users/henri/Documents/PROJETOS/ML-Forex/ |
| Database (Postgres 16) | VPS — container forex_db, db forex, user forex |
| Cache (Redis) | VPS — container forex_redis |
| Backups | Backblaze B2 bucket quantfx-backups-prod (cluster us-east-005) |
| Domínio | quantfx.com.br + api.quantfx.com.br (Cloudflare) |
| Monitoração | UptimeRobot — https://dashboard.uptimerobot.com/monitors |
| Frontend | Servido pela própria API (rotas /, /dashboard, /planos, etc.) |
SSH VPS: ssh root@177.7.58.99 (chave em C:/Users/henri/.ssh/id_ed25519)
🚑 Cenário 1 — API caiu (UptimeRobot vermelho, EAs não recebem sinal)¶
Sintomas: Email/SMS do UptimeRobot. curl https://api.quantfx.com.br/api/health retorna 502, timeout ou 5xx.
1.1 Diagnóstico (30 segundos)¶
Decisão:
- forex_api não aparece ou Exited → vai pra 1.2 (restart)
- forex_api está Up mas health falha → vai pra 1.3 (deep check)
- Tudo Up mas não acessível externamente → vai pra 1.4 (rede/CF)
1.2 Restart simples¶
ssh root@177.7.58.99 "cd /opt/quantfx && docker compose -f docker-compose.vps.yml up -d api"
sleep 10
curl -s -w "\n%{http_code}\n" https://api.quantfx.com.br/api/health
200 + status:ok → resolvido. Investigue logs depois com:
1.3 Deep check (container Up mas /health falha)¶
ssh root@177.7.58.99 "docker logs forex_api --tail 100" | grep -iE "error|traceback|fatal|critical"
ssh root@177.7.58.99 "docker exec forex_api curl -s http://localhost:8000/api/health"
Causas comuns:
- DB indisponível: docker logs forex_db | tail -50 — se DB caiu, restart: docker start forex_db && sleep 10 && docker restart forex_api
- Bug em deploy recente: rollback do container — docker compose -f docker-compose.vps.yml pull api (se imagem versionada) ou restore do código via scp da última versão conhecida boa
- OOM (memória estourada): dmesg | tail -30 mostra OOM kill — aumentar swap ou matar grafana temporariamente
1.4 Container Up mas inacessível externamente¶
- Verifica Cloudflare: https://www.cloudflarestatus.com
- Verifica DNS:
dig api.quantfx.com.br - Verifica firewall:
ssh root@177.7.58.99 "ufw status"— porta 80/443 abertas?
🤖 Cenário 2 — Worker travou (sem predição há > 15 min)¶
Sintomas: UptimeRobot marca /api/health como degraded (503 com status:"degraded"). pred_lag_minutes > 30. Alerta Telegram de cobertura.
2.1 Diagnóstico¶
No PC local (worker roda aqui, não na VPS):
docker ps --filter name=forex_worker --format "table {{.Names}}\t{{.Status}}"
docker logs forex_worker --tail 50
Verifica heartbeat (atualizado a cada ciclo):
Se oModify: for > 5 min atrás, worker travou.
2.2 Restart¶
⚠️ Se mudou código Python, restart não basta — precisa rebuild:
2.3 Worker repetidamente cai¶
Provável bug em alguma feature/modelo. Plano:
Se for crash em treinamento, desabilita temporariamente o retrain comentandomaybe_retrain() em scripts/run_worker.py linha ~1898, rebuild, e investiga depois com cabeça fria.
🔇 Cenário 3 — Sinais zerados (alerta "0 sinais em 24h")¶
Sintomas: Mensagem Telegram 🔇 Silêncio anômalo. Sistema rodando mas todas as predições são NO_TRADE.
3.1 Diagnóstico¶
ssh root@177.7.58.99 "docker exec forex_db psql -U forex -d forex -c \"
SELECT signal, COUNT(*) FROM predictions
WHERE created_at >= NOW() - INTERVAL '24 hours'
GROUP BY signal;\""
3.2 Checar feature integrity (foi o que pegou o bug FDA)¶
ssh root@177.7.58.99 "docker exec forex_db psql -U forex -d forex -c \"
SELECT s.symbol, t.tf_code, f.payload->'fda_close' as fda_close, f.ts
FROM features f
JOIN symbols s USING(symbol_id)
JOIN timeframes t USING(tf_id)
WHERE f.ts >= NOW() - INTERVAL '1 hour'
ORDER BY f.ts DESC LIMIT 10;\""
fda_close é NULL, é o mesmo bug que tivemos → feature não está sendo computada. Investigue app/features/build_features.py.
3.3 Causas possíveis (em ordem de probabilidade)¶
- Conformal gate travado (calibradores desatualizados após mudança de features) → trigger retrain
- Threshold muito alto → checar
configs/model.yamldecision.taue validar limiar relativo emapp/models/predict.py - Auto-rollback ativou em todos os slots → checar
ls -la models/.bias_rollback_state.json - Feature ausente em produção → vide 3.2
3.4 Recurso de emergência — forçar threshold mais baixo¶
Não recomendado, mas se precisar gerar sinais rapidamente (ex: demonstração ao vivo):
- Editar configs/model.yaml → decision.tau: 0.55 → 0.50
- Rebuild worker
- Reverte no dia seguinte — esses sinais não passaram pelos gates de qualidade
🗄️ Cenário 4 — DB inacessível¶
Sintomas: /api/health retorna status:"down","db":"error". Logs API cheios de connection refused.
4.1 Diagnóstico¶
ssh root@177.7.58.99 "docker ps -a --filter name=forex_db"
ssh root@177.7.58.99 "docker logs forex_db --tail 50"
4.2 Restart simples¶
4.3 DB não sobe (corruption)¶
Se virdatabase system is shut down, PANIC, ou corruption, fazer restore:
# No PC local
cd C:\Users\henri\Documents\PROJETOS\ML-Forex
.venv\Scripts\python.exe scripts\restore_from_b2.py --list
# Pegar a data do backup mais recente, ex: 20260522
.venv\Scripts\python.exe scripts\restore_from_b2.py --restore-db 20260522
# Inspeciona no DB temporário forex_restore antes de comitar
docker exec forex_db psql -U forex -d forex_restore -c "SELECT COUNT(*) FROM predictions;"
# Se OK, restaura sobre o de produção (CUIDADO):
.venv\Scripts\python.exe scripts\restore_from_b2.py --restore-db-prod 20260522 --confirm
💾 Cenário 5 — Disco cheio na VPS¶
Sintomas: API começa a falhar, logs com No space left on device.
5.1 Diagnóstico¶
5.2 Limpeza imediata (libera 5-20 GB)¶
ssh root@177.7.58.99 "docker system prune -af --volumes" # CUIDADO: remove volumes não-usados
ssh root@177.7.58.99 "find /opt/quantfx -name '*.log' -size +100M -delete"
5.3 Reduzir crescimento futuro¶
- DB tem retention de 90 dias em
predictions(já configurado emmaybe_cleanup_db) - Logs do Docker: configurar
max-size: 10mem/etc/docker/daemon.json(não está): Reinicia Docker:systemctl restart docker(CUIDA: derruba tudo).
🔐 Cenário 6 — Key suspeita (compartilhada/vazada)¶
Sintomas: Alerta de rate limit por IP (mesma key em > 3 IPs simultâneos). Comportamento anômalo.
6.1 Diagnóstico¶
# Quais keys e seus heartbeats
ssh root@177.7.58.99 "docker exec forex_db psql -U forex -d forex -c \"
SELECT user_name, plan_tier, last_seen_at,
EXTRACT(EPOCH FROM (NOW() - last_seen_at))/60 as min_since
FROM api_keys WHERE active=TRUE
ORDER BY last_seen_at DESC;\""
6.2 Revogar key suspeita¶
# Via endpoint admin
curl -X DELETE "https://api.quantfx.com.br/admin/keys/<key_id>" \
-H "X-Admin-Secret: $ADMIN_SECRET"
# Ou direto no DB
ssh root@177.7.58.99 "docker exec forex_db psql -U forex -d forex -c \"
UPDATE api_keys SET active=FALSE WHERE key_id=123;\""
📉 Cenário 7 — Modelo gerando sinais ruins (Sharpe/PF despencando)¶
Sintomas: Alerta Telegram 🟡 Degradação detectada (CUSUM). Performance < threshold no /api/performance.
7.1 Auto-rollback já tentou?¶
ssh root@177.7.58.99 "docker exec forex_db psql -U forex -d forex -c \"
SELECT * FROM model_versions
WHERE rolled_back_at IS NOT NULL
AND rolled_back_at > NOW() - INTERVAL '7 days';\""
7.2 Rollback manual (se auto-rollback não disparou)¶
# PC local
cd C:\Users\henri\Documents\PROJETOS\ML-Forex
# Lista modelos disponíveis no histórico do B2
.venv\Scripts\python.exe scripts\restore_from_b2.py --list-models EURUSD H1
# Restaurar versão específica
.venv\Scripts\python.exe scripts\restore_from_b2.py --restore-model EURUSD H1 20260501
docker compose up -d --build worker
7.3 Desativar slot problemático temporariamente¶
Marca o slot como inativo no DB para parar de gerar predições:
Comunica nos canais (Telegram/Discord) queXXXX está temporariamente off para investigação.
☁️ Cenário 8 — Cloudflare bloqueando ou DNS quebrado¶
Sintomas: Site fora externamente mas containers Up. curl direto IP funciona, curl domain não.
8.1 Diagnóstico¶
dig api.quantfx.com.br +short # Deve mostrar IP CF
curl --resolve api.quantfx.com.br:443:<CF_IP> ... # Bypass DNS
ssh root@177.7.58.99 "curl -s http://localhost:8000/api/health" # Direto sem CF
8.2 Bypass Cloudflare emergencial¶
- Loga no Cloudflare dashboard → DNS → mudar o registro de Proxied (laranja) para DNS only (cinza)
- DNS propaga em ~1 min. UptimeRobot e EAs voltam a alcançar direto.
- Lembre de religar o proxy depois (Cloudflare oferece DDoS, cache, certificado).
8.3 SSL/cert expirado¶
Cloudflare gerencia. Verificar em Cloudflare dashboard → SSL/TLS → Edge Certificates → status.
🆘 Cenário 9 — "Não sei o que está errado" (último recurso)¶
Sintomas confusos ou múltiplas coisas pifando. Restart completo da pilha:
# Worker
docker compose restart worker
# API + DB + Redis (na VPS)
ssh root@177.7.58.99 "cd /opt/quantfx && docker compose -f docker-compose.vps.yml restart"
sleep 30
curl https://api.quantfx.com.br/api/health
Se ainda quebrado: leia logs na ordem e procure o primeiro ERROR/Traceback cronológico:
ssh root@177.7.58.99 "docker logs forex_db --since 1h --tail 50"
ssh root@177.7.58.99 "docker logs forex_api --since 1h --tail 100"
docker logs forex_worker --since 1h --tail 100
📞 Quem chamar (escalonamento)¶
| Camada | Quem | Quando |
|---|---|---|
| 1. Você | Henrique | Sempre primeiro |
| 2. Hosting | Hostinger suporte | Se VPS não pingar (problema do datacenter) |
| 3. Cloudflare | https://www.cloudflarestatus.com | Se DNS/proxy estiver fora globalmente |
| 4. Backblaze | https://status.backblaze.com | Se restore B2 falhar |
✅ Pós-incident (sempre depois)¶
- Documenta no Telegram/notes: o quê + quando + o quê causou + o que resolveu
- Atualiza este runbook com aprendizado novo
- Avisa os assinantes se eles foram impactados (transparência > drama)
- Cria task de causa-raiz para que isso não volte