Pular para conteúdo

ADR-002: Conformal Prediction como gate final

Status: ✅ Accepted (2026-04)

Contexto

Em v1.x as predições eram emitidas apenas com base na probabilidade do modelo:

if max(probs) > 0.55:
    signal = ["BUY", "SELL", "NO_TRADE"][np.argmax(probs)]
else:
    signal = "NO_TRADE"

Problemas:

  1. Calibração ruim: P=0.62 não significa "62% de chance" se o modelo é mal calibrado (ECE alto)
  2. Sem garantia estatística de cobertura
  3. Threshold absoluto (0.55) é ad-hoc e varia por símbolo

Decisão

Implementar Inductive Conformal Prediction (ICP) com α = 0.15 → cobertura ≥ 85%.

# configs/model.yaml
training:
  conformal_alpha: 0.15

Mecânica

  1. Após treinar modelo, separa subset de calibração (~20% do training)
  2. Calcula "non-conformity score" para cada amostra de calibração:

$$s_i = 1 - P(y_i | x_i)$$

  1. Em predição, gera conjunto de classes possíveis (não apenas argmax):

$$\text{set}(x) = { y : 1 - P(y|x) \leq q_{1-\alpha}(s) }$$

  1. Se set tem 1 classe (singleton) → predição confiável → emite sinal
  2. Se set tem 2+ classes (incerteza) → emite NO_TRADE

Cobertura garantida

Teorema do ICP: P(y_true ∈ set(x)) ≥ 1 - α = 85%.

Isso é distribution-free — não depende do modelo estar bem calibrado.

Alternativas consideradas

A. Apenas calibrar com Platt Scaling

Por que não: corrige calibração mas não dá garantia de cobertura. Ainda emite predições incertas como se fossem confiáveis.

B. Threshold dinâmico por regime

Por que não: ainda dependente do modelo, sem garantia estatística. Foi usado em v1.x e era frágil.

C. Bayesian credible intervals

Por que não: modelo gradient-boosted não tem distribuição posterior nativa. Bootstrapping seria oneroso.

D. Conformal Mondrian (α por classe)

Considerado: α diferente para BUY/SELL/NO_TRADE. Complica configuração sem ganho prático em backtests.

Consequências

Positivas

  • Cobertura empírica em produção ≈ 87% (validamos com 2 meses de forward data)
  • Reduz falsos positivos sem sacrificar muito recall
  • Independente de qualidade de calibração do modelo
  • Auditável (qualquer um pode verificar cobertura)

Negativas

  • ~15% de sinais viram NO_TRADE que antes eram BUY/SELL (perdas potenciais)
  • Aumenta tempo de inferência (~20%) por causa do quantile lookup
  • Calibration set "rouba" data de treino

Mitigação

  • α = 0.15 (não 0.05 ou 0.10) — balance entre cobertura e taxa de sinais
  • Calibration set reciclado a cada retrain (não fica obsoleto)
  • Em modelos novos com pouco data, fall-back para threshold absoluto

Por que α = 0.15 (não 0.10)?

Backtested 0.05, 0.10, 0.15, 0.20:

α Cobertura Sinais BUY/SELL PF médio
0.05 ~95% <5% sinais 1.18
0.10 ~91% 12% sinais 1.29
0.15 ~87% ~30% sinais 1.42
0.20 ~82% ~45% sinais 1.31 (mais ruído)

0.15 é o sweet spot: cobertura ainda forte + sinais suficientes para o EA operar.

Referências