Debug de Modelos¶
Como investigar problemas comuns.
Modelo está retornando sempre NO_TRADE¶
Causa 1: confiança baixa¶
# Verificar manualmente
docker exec forex_worker python -c "
import joblib, json
from app.models.predict import load_champion, predict
m = load_champion('EURUSD', 'H1')
print(m.predict_proba(X)[-5:])
"
Se probs estão tipo [0.33, 0.32, 0.35] (próximas de 1/3) — modelo está incerto. Razões:
- Features ruidosas/quebradas
- Mercado em range (modelo treinado em trend)
- Dataset desbalanceado
Causa 2: conformal filtrando tudo¶
Se α muito baixo (ex: 0.05 = 95% cobertura) → muitos NO_TRADE.
Teste:
# Aumenta temporariamente e ver diferença
conformal_alpha: 0.25 # cobertura 75% — mais sinais, mas menos confiáveis
Causa 3: regime mismatch¶
Modelo foi treinado em regime A, mercado está em B.
# Ver distribution
docker exec forex_db psql -U forex -d forex -c "
SELECT regime, COUNT(*) FROM predictions
WHERE symbol_id = (SELECT symbol_id FROM symbols WHERE symbol='EURUSD')
AND created_at >= NOW() - INTERVAL '7 days'
GROUP BY regime;"
Modelo não está sendo carregado¶
Sintoma: log diz "modelo não encontrado" ou /api/scan ignora ativo.
# Listar champions ativos no DB
docker exec forex_db psql -U forex -d forex -c "
SELECT s.symbol, t.tf_code, mr.model_v, mr.is_active, mr.auc_wf
FROM model_registry mr
JOIN symbols s USING(symbol_id)
JOIN timeframes t USING(tf_id)
WHERE mr.is_active = TRUE
ORDER BY s.symbol, t.tf_code;"
Se não aparece o ativo X TF:
Se existir mas não está active → problema na promotion. Ver meta.json:
approved: false → rejeitado por gates. Lógica:
auc < 0.58ouauc_p5 < 0.55ouece > 0.25oupf < 0.95
Como promover manualmente (sem passar gates)¶
Use com cuidado
Bypass de gates pode colocar modelo perdedor em produção.
docker exec forex_db psql -U forex -d forex -c "
UPDATE model_registry
SET is_active = TRUE
WHERE model_v = 'lgbm_EURUSD_H1_v<hash>';"
Marcar o anterior como inactive:
UPDATE model_registry
SET is_active = FALSE
WHERE symbol_id = (SELECT symbol_id FROM symbols WHERE symbol='EURUSD')
AND tf_id = (SELECT tf_id FROM timeframes WHERE tf_code='H1')
AND model_v != 'lgbm_EURUSD_H1_v<hash>';
Sempre atualizar o .meta.json correspondente também (set is_active: true).
Predição está demorando (>500ms)¶
# Profile
docker exec forex_worker python -c "
import time
from app.models.predict import predict_for_slot
t0 = time.time()
res = predict_for_slot('EURUSD', 'H1')
print(f'Tempo: {(time.time()-t0)*1000:.1f}ms')
print(res)
"
Causas comuns:
- Modelo gigante (>50MB) —
feature_clusteringdesabilitado? - Features pesadas (FDA com janela enorme)
- DB lento (cache de candles desligado)
Como adicionar novo modelo (ex: LSTM)¶
- Implementar em
app/models/train_lstm.py - Adicionar ao ensemble em
train_lightgbm.py(apesar do nome):
ensemble_models = [
("lgbm", train_lgbm(...)),
("xgb", train_xgb(...)),
("cat", train_cat(...)),
("lstm", train_lstm(...)), # novo
]
- Atualizar
configs/model.yaml:
training:
ensemble_weight_lgbm: 0.40
ensemble_weight_xgb: 0.25
ensemble_weight_cat: 0.15
ensemble_weight_lstm: 0.20 # novo
- Validar com 1 símbolo:
- Atualizar ADR-003 explicando os novos pesos.
Drift detection — como ler¶
scripts/check_models.py:
Output:
Model lgbm_EURUSD_H1_v85b52764
PSI: 0.12 ✓ (threshold 2.0)
CUSUM: 1.4 ⚠️ (threshold 2.5)
recent_pf: 1.08 ✓
recent_auc: 0.594 ✓
status: OK
Model lgbm_GBPJPY_M15_v...
PSI: 2.3 ✗ FAIL
→ CUSUM rollback triggered
→ Promoting older version: lgbm_GBPJPY_M15_v...
Histórico de modelos (B2 sticky)¶
Cada modelo treinado fica no models-history/ do B2 para sempre:
Útil para auto-rollback CUSUM ou investigação histórica.