Predição Conformal para Dimensionamento de Posição Sensível ao Risco
Toda fórmula de dimensionamento de posição precisa de uma estimativa de incerteza. O critério de Kelly precisa de uma probabilidade de vitória e uma razão de payoff (veja O critério de Kelly para estratégias). A otimização de média-variância precisa de uma matriz de covariância. O VaR precisa de uma distribuição de retornos. Todas essas abordagens exigem suposições sobre o processo gerador de dados — suposições que os mercados financeiros violam rotineiramente.
A predição conformal oferece algo diferente: intervalos de predição com garantias de cobertura em amostra finita, sem qualquer suposição distributiva paramétrica. Se você pedir 90% de cobertura, obtém pelo menos 90% de cobertura — independentemente de os retornos serem gaussianos, de cauda pesada, assimétricos ou heterocedásticos. O único requisito é a permutabilidade (exchangeability), ou condições mais fracas, como veremos.
Este artigo cobre a teoria, as principais variantes e uma implementação prática para dimensionamento de posição em Python.
A Ideia Central: Scores de Não Conformidade

A predição conformal funciona medindo o quão "estranha" é uma nova observação em relação aos dados passados. A estranheza é quantificada por um score de não conformidade — qualquer função que meça o quão mal um ponto de dado se conforma ao padrão visto no restante dos dados.
Para regressão (predizer um valor contínuo como retornos), o score de não conformidade mais simples é o resíduo absoluto:
onde é qualquer preditor pontual (regressão linear, random forest, rede neural — não importa) e é um ponto de dado.
O insight central: se os pontos de dado são permutáveis, então o ranking de entre é uniformemente distribuído sobre . Este é um fato puramente combinatório — não exige suposições sobre a distribuição de ou .
A partir dessa uniformidade de ranking, podemos construir intervalos de predição com cobertura em amostra finita.
Predição Conformal por Divisão (Split Conformal)

A predição conformal por divisão (Papadopoulos et al., 2002; Lei et al., 2018) é a variante mais prática. O algoritmo é simples:
Passo 1. Divida os dados em um conjunto de treino e um conjunto de calibração .
Passo 2. Ajuste qualquer modelo em .
Passo 3. Calcule os scores de não conformidade no conjunto de calibração:
Passo 4. Para um nível de não cobertura desejado , tome como o quantil empírico de . Concretamente, este é o -ésimo menor resíduo (e sempre que , ou seja, para muito pequeno).
Passo 5. O intervalo de predição para um novo ponto é:
A Garantia de Cobertura
Sob permutabilidade dos dados de calibração e do novo ponto de teste:
Esta é uma garantia em amostra finita — não uma aproximação assintótica. Vale para qualquer modelo , qualquer distribuição dos dados e qualquer tamanho de amostra . Se é um preditor péssimo, os intervalos simplesmente serão mais largos. A garantia de cobertura ainda vale.
Também existe um limite superior quando os scores não têm empates: , então a cobertura não é desnecessariamente conservadora.
Por Que Isso Importa Para o Trading
Intervalos de predição tradicionais de, digamos, uma regressão linear assumem erros gaussianos. Um intervalo gaussiano calibrado na maior parte dos dados pode julgar muito mal as caudas quando os resíduos reais têm cauda pesada (por exemplo, Student- com poucos graus de liberdade): a massa central é mais fina que a gaussiana, então um intervalo gaussiano ajustado pela variância cobre em excesso perto do centro mas cobre de menos nas caudas, e um ajustado pela cauda faz o oposto. O ponto não é um único número mágico — é que a cobertura realizada de um intervalo paramétrico depende de uma suposição distributiva que você não verificou.
Os intervalos de predição conformal contornam isso. Eles se ampliam automaticamente quando o modelo está incerto, e mantêm a cobertura marginal independentemente da distribuição real do erro. Para um trader, isso significa:
- Se você dimensiona posições inversamente proporcionais à largura do intervalo, você reduz automaticamente a exposição quando o modelo está incerto.
- A garantia de cobertura significa que suas estimativas de risco são honestas — se você diz "90% dos retornos realizados cairão dentro deste intervalo", essa afirmação é estatisticamente válida (marginalmente, sob permutabilidade).
Predição Conformal Completa e Jackknife+
O split conformal é simples, mas desperdiça dados: o conjunto de calibração não pode ser usado para treino. Duas alternativas resolvem isso.
Predição Conformal Completa (Full Conformal)
A predição conformal completa (Vovk et al., 2005) usa todos os dados tanto para treino quanto para calibração. Para cada valor candidato de :
- Aumente o conjunto de dados com .
- Reajuste o modelo no conjunto de dados aumentado.
- Calcule todos os scores de não conformidade.
- Inclua no conjunto de predição se o score de não for muito extremo.
O conjunto de predição é:
onde são os scores de não conformidade calculados com o conjunto de dados aumentado.
O conformal completo fornece os intervalos mais estreitos, mas é computacionalmente proibitivo para a maioria dos modelos — é preciso reajustar o modelo para cada candidato em uma grade. Para uma predição de retorno, isso pode significar milhares de reajustes por predição.
Jackknife+ (Barber et al., 2021)
O jackknife+ encontra um equilíbrio. Ele usa resíduos leave-one-out (LOO), mas leva em conta a variabilidade do modelo ajustado entre os folds LOO.
Seja o modelo treinado em todos os dados exceto o ponto . Defina o score de não conformidade LOO com o resíduo absoluto único:
O intervalo de predição do jackknife+ é então construído a partir das predições LOO no ponto de teste, ampliado por esses resíduos:
Aqui denota o -ésimo menor valor do conjunto , e o -ésimo menor valor. O limite inferior subtrai o resíduo de cada predição LOO; o limite superior soma. Essa assimetria é todo o ponto — colapsar ambos os limites para colocaria o limite inferior acima da predição, o que está errado.
O jackknife+ fornece uma garantia de cobertura de pelo menos (ligeiramente mais fraca que o do split conformal), mas usa todos os dados tanto para treino quanto para calibração. Na prática, a cobertura tipicamente fica próxima de .
Para modelos de trading treinados com dados limitados (por exemplo, modelos específicos de regime com apenas algumas centenas de observações), o jackknife+ é frequentemente a melhor escolha — não sacrifica dados escassos para calibração. O custo é reajustes do modelo.
O Problema Com Séries Temporais Financeiras: Não Permutabilidade

A garantia conformal padrão exige permutabilidade: a distribuição conjunta de é invariante sob permutações. Para dados i.i.d., isso vale trivialmente.
Séries temporais financeiras não são permutáveis. Os retornos exibem:
- Agrupamento de volatilidade: Períodos de alta volatilidade seguem períodos de alta volatilidade (efeitos GARCH).
- Momentum e reversão à média: Autocorrelação nos retornos ou nos retornos ao quadrado.
- Mudanças de regime: A distribuição muda ao longo do tempo (mercados em alta vs. em baixa).
Se você aplicar ingenuamente o split conformal a uma série temporal — usando uma divisão de calibração aleatória — você viola a estrutura temporal. Os scores de calibração de um 2017 calmo não refletirão a incerteza de um 2020 volátil. Sua garantia de cobertura se quebra.
Inferência Conformal Adaptativa (ACI)

Gibbs e Candes (2021, NeurIPS) introduziram a Inferência Conformal Adaptativa (ACI) para lidar com deslocamento de distribuição e dados não permutáveis. A ideia é elegante: em vez de usar um nível de cobertura fixo, adapte o nível-alvo de não cobertura online com base em se os intervalos recentes cobriram o resultado real, e reobtenha o quantil da distribuição de scores em cada passo.
O Algoritmo ACI
O ACI não ajusta a largura do intervalo diretamente. Ele mantém um parâmetro adaptativo de não cobertura e recalcula o quantil conformal a partir dele. Em cada instante :
- Calcule o limiar conformal como o quantil empírico do conjunto atual de resíduos (os scores de calibração, mais quaisquer scores realizados até agora): .
- Observe as características , produza o intervalo .
- Observe o valor real e calcule o indicador de erro .
- Atualize o nível (não a largura):
onde é um tamanho de passo e é a não cobertura alvo. Se um intervalo falhou (), diminui, o que empurra o próximo quantil para cima e amplia o intervalo; se cobriu, aumenta e os intervalos se estreitam. Crucialmente, aqui está em unidades de probabilidade — ele ajusta um nível em , não o limiar em unidades brutas de retorno — então o mesmo se comporta de forma sensata quer os resíduos sejam da ordem de ou não.
Garantia de Cobertura Para ACI
O ACI fornece uma garantia de cobertura de longo prazo que não depende de um modelo distributivo:
Como é limitado a , o numerador é limitado por vezes uma constante, então o lado direito é e a frequência empírica de não cobertura converge para . A afirmação precisa: o ACI garante que a frequência empírica de não cobertura de longo prazo converge para para sequências arbitrárias (incluindo adversariais), desde que os níveis adaptados permaneçam limitados — o que o clipe garante. É uma garantia sobre a frequência de cobertura, não sobre a informatividade do intervalo: sob uma sequência verdadeiramente adversarial, os intervalos podem crescer de forma não informativa e ainda assim atingir a meta de cobertura.
ACI Dinamicamente Ajustado (DtACI)
Gibbs e Candes (2024, JMLR) refinaram o ACI com ajuste dinâmico do tamanho de passo . Em vez de fixar , eles mantêm um conjunto de candidatos e os combinam via uma regra de agregação de especialistas, favorecendo o cuja cobertura recente está mais próxima do alvo.
Isso resolve um problema prático: um grande se adapta rapidamente a mudanças de regime mas produz larguras de intervalo voláteis; um pequeno é estável mas lento para se adaptar. O DtACI equilibra isso automaticamente.
Por Que Isso Importa Para o Trading
Considere uma estratégia de market-making que usa um modelo de previsão de retorno. Durante mercados calmos, os intervalos conformais são estreitos — o modelo está confiante, e você pode assumir posições maiores. Quando a volatilidade dispara (temporada de resultados, anúncios do FOMC, choques geopolíticos), o nível ACI se adapta e os intervalos se ampliam em poucos passos de tempo. Seu dimensionamento de posição encolhe em resposta, sem qualquer modelo explícito de volatilidade ou lógica de detecção de regime.
Isso é quantificação de incerteza como sinal de primeira classe, não como uma reflexão tardia.
Dimensionamento de Posição Com Intervalos Conformais

Agora vamos conectar a predição conformal ao dimensionamento concreto de posição. A variável-chave é a meia-largura do intervalo de predição em relação ao caso simétrico do resíduo absoluto. Com o intervalo simétrico , a largura total é . Para manter as fórmulas e o código consistentes, medimos tudo em relação à largura total ao longo de todo o texto.
Dimensionamento pelo Inverso da Largura
A abordagem mais simples: dimensione inversamente proporcional à largura do intervalo.
onde é uma constante de escala calibrada para seu orçamento de risco. Quando o modelo está confiante (intervalo estreito), você assume uma posição maior. Quando incerto (intervalo largo), assume uma menor.
Isso é análogo ao volatility targeting (), mas com uma diferença crucial: a largura do intervalo conformal é uma medida de incerteza livre de distribuição, não uma estimativa paramétrica de volatilidade. Ela captura a incerteza preditiva sob a garantia de cobertura, não apenas a variância dos retornos.
Dimensionamento por Razão de Edge e o Filtro de Não Negociação
O dimensionamento puro pelo inverso da largura ignora a força do próprio sinal. Um refinamento natural escala pela razão de edge — a predição pontual em relação à largura do intervalo:
Este é um análogo conformal de uma razão sinal-ruído: retorno esperado dividido por uma medida de incerteza livre de distribuição. Nós o usamos tanto para dimensionamento quanto para um filtro de não negociação.
O filtro é fundamentado. Se o intervalo abrange o zero,
então o intervalo de predição inclui tanto retornos positivos quanto negativos — o retorno realizado pode plausivelmente ter o sinal oposto ao de sua predição. Defina um limiar mínimo de edge e negocie apenas quando . Note que o conteúdo geométrico de " grande o suficiente para que o intervalo não abranja mais o zero" é exatamente (já que o intervalo limpa o zero quando ). Escolha na escala real de via backtesting; para resíduos de retornos diários, geralmente está bem abaixo de , então um minúsculo pode admitir quase todas as negociações e um grande pode não admitir nenhuma. Calibre-o para seus dados.
Sobre o "Kelly Conformal"
É tentador acoplar intervalos conformais à fração de Kelly . Mas já é uma fração completa e limitada derivada de uma probabilidade de vitória e uma razão de payoff ; multiplicá-la por uma razão ilimitada como não tem justificativa na teoria da decisão — pode exceder 1 ou inverter o sinal independentemente de , e conta a edge duplamente, algo que já codifica. Portanto, não apresentamos um multiplicador de "Kelly conformal".
Se você quiser conduzir o Kelly a partir do intervalo, deve efetivamente derivar e dele, o que exige uma suposição explícita sobre a distribuição dentro do intervalo (os intervalos conformais deliberadamente não dizem nada sobre isso — veja Limitações). Por exemplo, sob uma forma assumida dentro do intervalo, você pode aproximar e uma razão de payoff a partir da geometria do intervalo — mas sinalize essa suposição em alto e bom som, porque ela reintroduz exatamente o compromisso paramétrico que a predição conformal pretendia evitar.
A alternativa honesta e com poucas suposições é usar a razão de edge como uma redução fracionária de Kelly: aumente o tamanho quando o retorno esperado for grande em relação ao intervalo, reduza quando for pequeno, e aplique isso sobre um limite máximo rígido — explicitamente como uma heurística, não como "a fração de Kelly".
Implementação em Python
Aqui está uma implementação prática. Mostramos tanto o caminho split/prefit quanto o caminho temporal (EnbPI), já que todo o ponto deste artigo é que os dados financeiros não são permutáveis.
Configuração e Preparação dos Dados
import numpy as np
import pandas as pd
from sklearn.ensemble import GradientBoostingRegressor
from sklearn.model_selection import KFold
from mapie.regression import MapieRegressor, MapieTimeSeriesRegressor
from mapie.subsample import BlockBootstrap
def prepare_features(prices: pd.Series, lookback: int = 20) -> pd.DataFrame:
"""Create features from a price series."""
df = pd.DataFrame()
returns = prices.pct_change()
for lag in range(1, lookback + 1):
df[f"ret_lag_{lag}"] = returns.shift(lag)
for window in [5, 10, 20]:
df[f"ret_mean_{window}"] = returns.rolling(window).mean().shift(1)
df[f"ret_std_{window}"] = returns.rolling(window).std().shift(1)
df[f"ret_skew_{window}"] = returns.rolling(window).skew().shift(1)
df["target"] = returns.shift(-1)
return df.dropna()
Split Conformal Com MAPIE (Prefit)
Para o conformal split/prefit, cv="prefit" exige method="base" (o estimador conformal por divisão ingênuo). A opção method="plus" é o estimador CV+/jackknife+ e é incompatível com cv="prefit" — ela precisa de um objeto de validação cruzada em vez disso. Usamos a combinação correta aqui, e padronizamos o dimensionamento na largura total.
def split_conformal_sizing(
prices: pd.Series,
alpha: float = 0.1,
k: float = 1e-3, # scaling constant, in width units
max_position: float = 1.0,
min_edge: float = 0.05, # threshold on |pred| / width
) -> pd.DataFrame:
"""
Position sizing using split (prefit) conformal prediction intervals.
Sizing rule (consistent with the prose):
edge_t = |pred_t| / width_t
size_t = clip(k / width_t, 0, max_position) # inverse-width
size_t = 0 if edge_t < min_edge # no-trade filter
"""
df = prepare_features(prices)
X = df.drop(columns=["target"])
y = df["target"]
n_train = int(len(X) * 0.6)
n_cal = int(len(X) * 0.2)
X_train, y_train = X.iloc[:n_train], y.iloc[:n_train]
X_cal, y_cal = X.iloc[n_train:n_train + n_cal], y.iloc[n_train:n_train + n_cal]
X_test, y_test = X.iloc[n_train + n_cal:], y.iloc[n_train + n_cal:]
base_model = GradientBoostingRegressor(
n_estimators=200, max_depth=3, learning_rate=0.05, random_state=42,
)
base_model.fit(X_train, y_train)
mapie = MapieRegressor(estimator=base_model, cv="prefit", method="base")
mapie.fit(X_cal, y_cal)
y_pred, y_intervals = mapie.predict(X_test, alpha=alpha)
lower = y_intervals[:, 0, 0]
upper = y_intervals[:, 1, 0]
width = upper - lower
raw_size = k / np.where(width > 0, width, np.inf)
position_size = np.clip(raw_size, 0.0, max_position)
edge_ratio = np.abs(y_pred) / np.where(width > 0, width, np.inf)
position_size = np.where(edge_ratio < min_edge, 0.0, position_size)
position_size = position_size * np.sign(y_pred)
return pd.DataFrame({
"prediction": y_pred,
"lower": lower,
"upper": upper,
"width": width,
"edge_ratio": edge_ratio,
"position_size": position_size,
"actual": y_test.values,
}, index=X_test.index)
Conformal de Série Temporal Com EnbPI
Como os retornos não são permutáveis, a divisão aleatória/prefit acima é apenas uma linha de base. O MapieTimeSeriesRegressor do MAPIE com method="enbpi" (Xu & Xie, 2021) usa block bootstrap e atualização de resíduos projetados para dependência temporal. Esta é a ferramenta que corresponde ao próprio argumento deste artigo.
def enbpi_sizing(
prices: pd.Series,
alpha: float = 0.1,
k: float = 1e-3,
max_position: float = 1.0,
min_edge: float = 0.05,
) -> pd.DataFrame:
"""Position sizing with EnbPI (block-bootstrap, time-series conformal)."""
df = prepare_features(prices)
X = df.drop(columns=["target"])
y = df["target"]
n_train = int(len(X) * 0.7)
X_train, y_train = X.iloc[:n_train], y.iloc[:n_train]
X_test, y_test = X.iloc[n_train:], y.iloc[n_train:]
base_model = GradientBoostingRegressor(
n_estimators=200, max_depth=3, learning_rate=0.05, random_state=42,
)
cv = BlockBootstrap(n_resamplings=30, length=20, overlapping=False, random_state=42)
mapie_ts = MapieTimeSeriesRegressor(base_model, method="enbpi", cv=cv, agg_function="mean")
mapie_ts.fit(X_train, y_train)
y_pred, y_intervals = mapie_ts.predict(X_test, alpha=alpha, ensemble=True)
lower = y_intervals[:, 0, 0]
upper = y_intervals[:, 1, 0]
width = upper - lower
raw_size = k / np.where(width > 0, width, np.inf)
position_size = np.clip(raw_size, 0.0, max_position)
edge_ratio = np.abs(y_pred) / np.where(width > 0, width, np.inf)
position_size = np.where(edge_ratio < min_edge, 0.0, position_size)
position_size = position_size * np.sign(y_pred)
return pd.DataFrame({
"prediction": y_pred, "lower": lower, "upper": upper,
"width": width, "edge_ratio": edge_ratio,
"position_size": position_size, "actual": y_test.values,
}, index=X_test.index)
Inferência Conformal Adaptativa (Online)
Para negociação ao vivo, implementamos o ACI verdadeiro do zero: mantemos o nível de não cobertura , o atualizamos aditivamente, e reobtemos o quantil do conjunto de resíduos a cada passo. Dois detalhes de amostra finita importam:
- Use a estatística de ordem, não um quantil interpolado.
np.quantileinterpola por padrão, o que pode cair um pouco abaixo do ranking exigido e subcobrir; passemethod="higher"(equivalente a"inverted_cdf"). - Quando o ranking exigido excede ( pequeno, alta cobertura alvo), o limiar correto é (intervalo = a reta inteira), não um limite fixado no maior resíduo. Fixar o limite quebra silenciosamente a garantia .
class AdaptiveConformalSizer:
"""
Online position sizing with Adaptive Conformal Inference (Gibbs & Candes,
2021). Updates the miscoverage LEVEL alpha_t and re-derives the quantile
from the residual set each step -- gamma is in probability units.
"""
def __init__(self, base_model, alpha=0.1, gamma=0.02,
max_position=1.0, min_edge=0.05, k=1e-3):
self.base_model = base_model
self.alpha_target = alpha # target miscoverage
self.alpha_t = alpha # adaptive miscoverage level
self.gamma = gamma # step size, in [0, 1] units
self.max_position = max_position
self.min_edge = min_edge
self.k = k
self.residuals = []
self.q_hat = np.inf
self.coverage_history = []
@staticmethod
def _conformal_quantile(residuals, alpha_t):
"""(1 - alpha_t) conformal quantile via the order statistic."""
n = len(residuals)
if n == 0:
return np.inf
rank = int(np.ceil((1.0 - alpha_t) * (n + 1)))
if rank > n: # required order statistic does not exist
return np.inf # -> interval is the whole line
level = rank / n
return float(np.quantile(residuals, min(level, 1.0), method="higher"))
def calibrate(self, X_cal, y_cal):
preds = self.base_model.predict(X_cal)
self.residuals = list(np.abs(np.asarray(y_cal) - preds))
self.q_hat = self._conformal_quantile(self.residuals, self.alpha_t)
def predict_and_size(self, X_t) -> dict:
mu_hat = self.base_model.predict(np.asarray(X_t).reshape(1, -1))[0]
lower, upper = mu_hat - self.q_hat, mu_hat + self.q_hat
width = upper - lower # = 2 * q_hat
edge_ratio = abs(mu_hat) / width if np.isfinite(width) and width > 0 else 0.0
if edge_ratio < self.min_edge:
size = 0.0
else:
size = min(self.k / width, self.max_position) if width > 0 else 0.0
return {
"prediction": mu_hat, "lower": lower, "upper": upper,
"width": width, "edge_ratio": edge_ratio,
"position_size": size * np.sign(mu_hat),
"alpha_t": self.alpha_t, "q_hat": self.q_hat,
}
def update(self, X_t, y_t: float):
"""ACI update: adapt the LEVEL, then re-derive the quantile."""
mu_hat = self.base_model.predict(np.asarray(X_t).reshape(1, -1))[0]
residual = abs(y_t - mu_hat)
covered = int(residual <= self.q_hat)
err_t = 1 - covered
self.alpha_t = float(np.clip(
self.alpha_t + self.gamma * (self.alpha_target - err_t), 0.0, 1.0
))
self.residuals.append(residual)
self.q_hat = self._conformal_quantile(self.residuals, self.alpha_t)
self.coverage_history.append(covered)
@property
def running_coverage(self) -> float:
if not self.coverage_history:
return float("nan")
return float(np.mean(self.coverage_history))
Juntando Tudo: Loop de Backtest
def backtest_aci_sizing(prices: pd.Series, alpha=0.1, gamma=0.02) -> pd.DataFrame:
"""Backtest position sizing with Adaptive Conformal Inference."""
df = prepare_features(prices)
X = df.drop(columns=["target"]).values
y = df["target"].values
index = df.index
n_train = int(len(X) * 0.5)
n_cal = int(len(X) * 0.2)
X_train, y_train = X[:n_train], y[:n_train]
X_cal, y_cal = X[n_train:n_train + n_cal], y[n_train:n_train + n_cal]
X_test, y_test = X[n_train + n_cal:], y[n_train + n_cal:]
test_index = index[n_train + n_cal:]
model = GradientBoostingRegressor(
n_estimators=200, max_depth=3, learning_rate=0.05, random_state=42,
)
model.fit(X_train, y_train)
sizer = AdaptiveConformalSizer(base_model=model, alpha=alpha, gamma=gamma)
sizer.calibrate(X_cal, y_cal)
records = []
for i in range(len(X_test)):
result = sizer.predict_and_size(X_test[i])
result["actual"] = y_test[i]
result["pnl"] = result["position_size"] * y_test[i]
records.append(result)
sizer.update(X_test[i], y_test[i]) # online residual + level update
results = pd.DataFrame(records, index=test_index)
results["cumulative_pnl"] = results["pnl"].cumsum()
results["running_coverage"] = (
((results["lower"] <= results["actual"]) &
(results["actual"] <= results["upper"])).expanding().mean()
)
return results
Avaliando os Resultados
def evaluate(results: pd.DataFrame, alpha: float = 0.1):
"""Print evaluation metrics for conformal position sizing."""
covered = ((results["actual"] >= results["lower"]) &
(results["actual"] <= results["upper"]))
print(f"Target coverage: {1 - alpha:.1%}")
print(f"Empirical coverage: {covered.mean():.1%}")
print(f"Mean interval width: {results['width'].mean():.6f}")
print(f"Median position size: {results['position_size'].abs().median():.4f}")
print(f"Fraction no-trade: {(results['position_size'] == 0).mean():.1%}")
print(f"Total PnL (bps): {results['pnl'].sum() * 10000:.1f}")
sd = results['pnl'].std()
sharpe = results['pnl'].mean() / sd * np.sqrt(252) if sd > 0 else float('nan')
print(f"Sharpe (annualized): {sharpe:.2f}")
Considerações Práticas
Escolhendo o Score de Não Conformidade
O resíduo absoluto é o padrão, mas assume que o intervalo de predição deve ser simétrico em torno da predição pontual. Para retornos financeiros, intervalos assimétricos frequentemente fazem mais sentido:
- Regressão Quantílica Conformalizada (CQR): Ajuste regressores quantílicos nos níveis e , e depois conformalize (Romano et al., 2019). Os intervalos adaptam sua forma à distribuição local — mais largos no lado negativo durante drawdowns, mais largos no lado positivo durante rallies. (Com CQR, o intervalo deixa de ser simétrico, então é a genuína largura superior-menos-inferior — continue usando como denominador em todo lugar.)
- Scores normalizados: , onde é uma estimativa de volatilidade local. Isso produz intervalos condicionalmente adaptativos — mais estreitos em regimes de baixa volatilidade, mais largos em regimes de alta volatilidade — mantendo a cobertura marginal.
Tamanho do Conjunto de Calibração
A garantia de cobertura do split conformal vale para qualquer tamanho de conjunto de calibração , mas a largura do intervalo diminui à medida que aumenta. Para muito pequeno, a estatística de ordem exigida pode não existir, caso em que o limiar honesto é (um intervalo não informativo, mas válido) — fixar o limite no maior resíduo anula silenciosamente a garantia. Para negociação prática:
- pontos de calibração fornece intervalos razoavelmente estreitos.
- é preferível para estimação estável de quantis.
- Com o ACI, o conjunto de calibração serve apenas para inicialização; as atualizações do nível online cuidam do resto.
Frequência de Retreinamento
O modelo base pode ficar desatualizado. Duas abordagens:
- Retreinar periodicamente (por exemplo, mensalmente) e recalibrar o quantil conformal.
- Usar ACI e deixar o nível adaptativo compensar a defasagem do modelo — os intervalos se ampliam automaticamente à medida que os resíduos do modelo crescem.
A opção 2 é mais simples e surpreendentemente eficaz. A camada conformal atua como uma rede de segurança: mesmo que o modelo se deteriore, a frequência de cobertura ACI de longo prazo ainda converge para o alvo.
Custos de Transação
Os intervalos conformais interagem com os custos de transação de forma útil. Quando os intervalos são largos (alta incerteza), as posições são pequenas, então o turnover é baixo. Quando os intervalos se estreitam (o modelo está confiante), as posições crescem — mas o modelo tem mais chance de estar certo, então o turnover vale a pena.
Você também pode incorporar custos de transação diretamente no filtro de não negociação:
Isso garante que você só negocie quando a edge líquida esperada exceder um limiar escalado pela largura conformal — usando o mesmo denominador de sempre.
Comparação Com Métodos Tradicionais
| Propriedade | IC Gaussiano | IC Bootstrap | IP Conformal |
|---|---|---|---|
| Suposição de distribuição | Erros normais | i.i.d. + assintótico | Nenhuma (permutabilidade) |
| Garantia em amostra finita | Não | Não | Sim |
| Funciona com qualquer modelo | Não | Sim | Sim |
| Adapta-se à heterocedasticidade | Não | Parcialmente | Com CQR / scores normalizados |
| Lida com deslocamento de distribuição | Não | Não | Variante ACI / EnbPI |
| Custo computacional | Baixo | Médio | Split: baixo; jackknife+: reajustes; completo: proibitivo |
O bootstrap é "assintótico" apenas em sua garantia; ele ainda assume dados i.i.d./permutáveis e suavidade, então sua célula de suposição de distribuição não é "livre de suposições". E a única coluna "conformal" esconde custos muito diferentes entre variantes, o que a linha de custo agora deixa explícito.
Limitações
A predição conformal não é mágica. Principais limitações para negociação:
-
Cobertura marginal, não condicional. A garantia é que marginalmente — em média sobre a aleatoriedade tanto em quanto em . Para um valor específico de condicionamento , a cobertura local pode estar acima ou abaixo de . A regressão quantílica conformalizada aborda isso parcialmente.
-
A permutabilidade é uma exigência real. Para o split conformal, os dados de calibração e o ponto de teste devem ser permutáveis. Os dados financeiros não são. ACI e EnbPI relaxam isso para uma garantia de longo prazo, mas a cobertura de curto prazo pode se desviar.
-
A largura do intervalo não é uma densidade de probabilidade. Um intervalo conformal informa onde cairá com probabilidade , mas não diz nada sobre a distribuição dentro do intervalo. Não é um substituto para uma distribuição preditiva completa — exatamente por isso derivar um e um de Kelly a partir dele exige uma suposição adicional e explícita.
-
Lixo entra, mais largo sai. Um modelo base ruim produz intervalos largos. A predição conformal garante cobertura, mas não utilidade. Se seu modelo não tem poder preditivo, os intervalos serão tão largos que o dimensionador de posição nunca negociará.
Resumo
A predição conformal fornece um arcabouço rigoroso e livre de distribuição para quantificação de incerteza que se encaixa naturalmente no dimensionamento de posição:
- Split conformal para calibração estática e offline com cobertura em amostra finita.
- Jackknife+ quando os dados de calibração são escassos e você quer usar todas as observações (ao custo de reajustes).
- Inferência conformal adaptativa / EnbPI para negociação online com mercados não estacionários.
- Dimensionamento de posição via inverso da largura do intervalo e um filtro de não negociação por razão de edge — e, se você for para o Kelly, apenas após derivar e honestamente, não via um multiplicador injustificado.
A vantagem chave sobre as alternativas paramétricas: você nunca precisa especificar ou validar uma suposição distributiva paramétrica. Os intervalos são honestos por construção (marginalmente, sob permutabilidade). Para um trader sistemático, isso significa uma fonte a menos de risco de modelo — e em um negócio onde o risco de modelo é existencial, isso importa.
Referências:
- Papadopoulos, H., Proedrou, K., Vovk, V., Gammerman, A. (2002). Inductive confidence machines for regression. ECML.
- Vovk, V., Gammerman, A., Shafer, G. (2005). Algorithmic Learning in a Random World. Springer.
- Lei, J., G'Sell, M., Rinaldo, A., Tibshirani, R.J., Wasserman, L. (2018). Distribution-free predictive inference for regression. JASA.
- Xu, C., Xie, Y. (2021). Conformal prediction interval for dynamic time-series (EnbPI). ICML.
- Barber, R.F., Candes, E.J., Ramdas, A., Tibshirani, R.J. (2021). Predictive inference with the jackknife+. Annals of Statistics.
- Gibbs, I., Candes, E.J. (2021). Adaptive conformal inference under distribution shift. NeurIPS.
- Gibbs, I., Candes, E.J. (2024). Conformal inference for online prediction with arbitrary distribution shifts. JMLR.
- Romano, Y., Patterson, E., Candes, E.J. (2019). Conformalized quantile regression. NeurIPS.
- Cordier, T. et al. (2022). MAPIE: an open-source library for distribution-free uncertainty quantification. arXiv:2207.12274.
Authors
Trading-systems engineer
Trading-systems engineer building bots since 2017: cross-exchange arbitrage (connected up to 30 venues), cointegration-based pairs arbitrage across spot and futures, scalping, news and sentiment-driven strategies, trend algorithms, and portfolio management and balancing algorithms. Also builds sub-millisecond order execution, big-data warehouses, backtesting engines, AI agents, and trading interfaces (incl. open-source profitmaker.cc). Stack: JS/TS, Python, Rust/Zig/Go, DevOps, backend, frontend, architecture.