Processos Gaussianos para Modelagem Não Paramétrica de Preços
Parte da série "Linhas de base do Classic ML".
Duas coisas fazem com que um processo gaussiano valha um artigo separado neste blog, e nenhuma delas "dá incerteza".
O primeiro é o design do kernel. Todo o viés indutivo de um GP reside em uma função , e essa função é algo que você escreve deliberadamente: quão difícil é o caminho, se ele se repete, se a repetição decai. Nada mais no kit de ferramentas padrão permite formular uma hipótese estrutural sobre a dinâmica do mercado que seja explicitamente adequada a ela. A segunda é a probabilidade marginal — um objetivo de treinamento com uma penalidade de complexidade derivada do próprio modelo, não de um conjunto retido. Todos os outros artigos no arco de sobreajuste neste blog (análise de platô, PBO, Sharpe deflacionado) existe porque a regularização do conjunto de validação é frágil na pesquisa. Um clínico geral afirma não precisar de um. Essa afirmação é testável, e testá-la é mais interessante do que outro tutorial sobre dimensionamento de incerteza.
Sobre a própria incerteza: a variância posterior do GP é estrutural — ela sai da mesma inferência que produz a média, em vez de ser posteriormente envolvida em um modelo ajustado. Esse é o verdadeiro contraste com a previsão conforme, que já aborda neste blog por que a incerteza é a entrada correta para o dimensionamento da posição e o que fazer com um intervalo quando você tiver um. Este artigo não argumenta novamente esse caso; vai atrás do modelo.
O que se segue é o kernel e o mecanismo de inferência, uma implementação GPyTorch e – declarado claramente no final – as medidas que este artigo ainda não possui.
O que é um processo gaussiano?
GPs já aparecem neste blog como um substituto da otimização Bayesiana em Optuna vs. descida coordenada, com a mesma notação e a mesma ressalva de baixa dimensionalidade. Aqui, o GP é o próprio modelo, ajustado aos dados de mercado e não a uma superfície de pesquisa de hiperparâmetros, portanto o tratamento é mais profundo.
Um processo gaussiano é uma coleção de variáveis aleatórias, qualquer número finito das quais tem uma distribuição gaussiana conjunta. É uma distribuição sobre funções, não uma distribuição sobre parâmetros.
Formalmente, uma função é extraído de um GP se para qualquer conjunto finito de entradas :
onde é a função média e é a função de covariância (kernel). Escrevemos isso de forma compacta como:
A função média codifica a crença anterior sobre o comportamento médio de . Na negociação, normalmente definimos , codificando a suposição de que não temos nenhum viés direcional anterior nos retornos. Toda a estrutura vai para o kernel.
Por que não paramétrico?
Um modelo de regressão linear com 5 características possui 6 parâmetros. Uma rede neural com duas camadas ocultas de 64 unidades possui milhares. Um GP não possui um número fixo de parâmetros — a complexidade do modelo aumenta com os dados. Com 10 observações, o GP define uma Gaussiana de 10 dimensões. Com 10.000 observações, define uma Gaussiana de 10.000 dimensões.
Isso não significa que os GPs não tenham hiperparâmetros. A função kernel possui hiperparâmetros (escalas de comprimento, amplitudes, periodicidades) que controlam as propriedades das funções extraídas do anterior. Mas a forma funcional em si nunca é fixa. O GP pode representar qualquer função contínua, desde que haja dados suficientes e o kernel correto. Isto é o que significa “não paramétrico” – o modelo não está restrito a uma família paramétrica como funções lineares ou polinômios.
Para modelagem financeira, isso é valioso. Os mercados mudam. A relação entre características e retornos é não linear, não estacionária e dependente do regime. Os modelos paramétricos impõem uma estrutura que pode não corresponder à realidade. Os GPs deixam os dados falarem.
Funções do Kernel: Codificação da Estrutura do Mercado
A função do núcleo é a alma de um processo gaussiano. Ele define quais funções são prováveis a priori, especificando a covariância entre os valores da função em quaisquer dois pontos de entrada. Diferentes kernels codificam diferentes suposições sobre suavidade, periodicidade e comportamento de longo alcance.
Função de Base Radial (RBF) / Exponencial Quadrado
O kernel RBF é o ponto de partida mais comum:
onde é a variação do sinal (escala de saída) e é a escala de comprimento. Funções extraídas de um GP com um kernel RBF são infinitamente diferenciáveis – muito suaves.
Interpretação de negociação: A escala de comprimento controla a distância entre dois pontos de dados e ainda assim ser correlacionados. Uma escala curta significa que o modelo reage a padrões locais; uma escala longa significa que ela captura tendências amplas. A variação do sinal controla a amplitude da função – quão grandes podem ser os retornos previstos.
Problema financeiro: Suavidade infinita não é realista. Os retornos financeiros apresentam saltos, mudanças de regime e descontinuidades. O kernel RBF pode suavizar esses recursos, produzindo previsões muito conservadoras perto de quebras estruturais.
Kernel Materno
A classe Matern generaliza o RBF introduzindo um parâmetro de suavidade :
onde é a função de Bessel modificada do segundo tipo. Como , o kernel Matern converge para o RBF. Escolhas comuns:
- : Equivalente ao processo Ornstein-Uhlenbeck. As funções são contínuas, mas não diferenciáveis — ásperas, como o movimento browniano.
- : As funções já são diferenciáveis. Um bom equilíbrio entre suavidade e flexibilidade.
- : As funções são duas vezes diferenciáveis. Mais suave que mas menos rígido que o RBF.
Interpretação de negociação: O Matern- kernel é indiscutivelmente o melhor padrão para séries temporais financeiras. Ele permite o tipo de rugosidade que as trajetórias reais dos preços exibem, sem ser tão irregular quanto . Isso se alinha com a literatura "a volatilidade é grosseira" (Gatheral, Jaisson, & Rosenbaum, 2018), que mostra empiricamente que os caminhos da volatilidade têm expoentes de Hurst em torno de , muito mais áspero que o movimento browniano.
A principal evidência publicada de que os kernels Matern superam os modelos clássicos de volatilidade é Rizvi et al. (2017), que relatam MSE cerca de 20% melhor do que um passeio aleatório e 50% melhor do que GARCH — em dados diários de pares de moedas de 2017, não criptográficos, e não reproduzidos aqui. Trate isso como motivação para experimentar o kernel, não como uma referência. O ajuste GARCH(1,1) deste blog em dados diários reais do BTC, com diagnósticos Ljung-Box e ARCH-LM, está em Previsão de volatilidade GARCH para criptografia; um confronto direto contra um Matern GP na mesma amostra seria uma comparação honesta, e não foi realizada.
Kernel Periódico
Os mercados financeiros têm padrões cíclicos: curvas de volume intradiárias, efeitos diários da semana, fluxos de reequilíbrio mensais, épocas de ganhos trimestrais. O kernel periódico captura estes:
onde é o período. Funções extraídas deste kernel se repetem com ponto final , modulado pela escala de comprimento que controla a rapidez com que a correlação decai dentro de um período.
Interpretação de negociação: Conjunto (horas) para capturar padrões intradiários, ou (dias de negociação) para sazonalidade semanal. Ao contrário dos recursos de Fourier, o kernel periódico não assume um número fixo de harmônicos – o GP aprende a forma do ciclo a partir dos dados.
Combinando Kernels: Composição Aditiva e Multiplicativa
O verdadeiro poder dos kernels GP está na composição. Se e são kernels válidos, então são:
- Soma: — a função é a soma dos componentes independentes (decomposição aditiva)
- Produto: — interações entre componentes (por exemplo, comportamento localmente periódico)
Um kernel composto útil para retornos financeiros:
Isso decompõe o sinal em:
- Um componente de tendência aperiódica e não suave (Matern-3/2)
- Um componente periódico cuja amplitude decai com o tempo (Periódico RBF)
O produto cria um kernel localmente periódico: o padrão se repete, mas as repetições distantes têm menos influência do que as próximas. Isto é exactamente correcto para a sazonalidade financeira, que varia ao longo do tempo à medida que a microestrutura do mercado evolui.
Núcleos de Mistura Espectral
Para máxima flexibilidade, o kernel de mistura espectral (SM) (Wilson & Adams, 2013) parametriza a densidade espectral do kernel como uma mistura de Gaussianas:
onde são pesos de mistura, são variações espectrais, e são médias espectrais (frequências). Pelo teorema de Bochner, qualquer núcleo estacionário pode ser representado desta forma. O kernel SM pode descobrir componentes periódicos, tendências de longo alcance e correlações de curto alcance simultaneamente – tudo a partir de dados.
Interpretação de negociação: O kernel SM é útil quando você não sabe quais padrões existem nos dados. Ele pode identificar periodicidades ocultas em séries de retorno (por exemplo, ciclos sutis de 4 horas em mercados criptográficos impulsionados por reequilíbrio automatizado). A desvantagem são mais hiperparâmetros e o risco de overfitting com pequenos conjuntos de dados.
Inferência posterior: de antes da previsão
Dados de treinamento dados onde e , o GP posterior nos pontos de teste tem uma solução de forma fechada. Esta é a principal vantagem computacional dos GPs sobre a maioria dos modelos Bayesianos.
As equações posteriores
Deixe seja o matriz de covariância de treinamento, seja o matriz de covariância cruzada, e seja o testar matriz de covariância. A posterior é:
onde:
A média posterior é uma combinação linear dos alvos de treinamento, ponderada pela similaridade do kernel entre os pontos de teste e de treinamento. A covariância posterior começa a partir da covariância anterior e subtrai as informações obtidas dos dados de treinamento. Onde os dados de treinamento são densos, a variância posterior é pequena. Onde os dados de treinamento são esparsos, a variância posterior reverte para a anterior.
A probabilidade marginal e a afirmação que vale a pena testar
Os hiperparâmetros do kernel (escalas de comprimento, variâncias, nível de ruído) são aprendidas maximizando a probabilidade marginal logarítmica:
O primeiro termo é um termo de ajuste de dados (penaliza previsões distantes das observações). O segundo termo é uma penalidade de complexidade (penaliza modelos que são muito flexíveis - ou seja, onde a matriz kernel tem um grande determinante). O terceiro termo é uma constante de normalização.
Esta é a navalha de Occam automática e é a coisa mais interessante que um GP traz para um pipeline de negociação. A versão forte da afirmação é que nenhum conjunto de validação separado é necessário para a regularização — a penalidade de complexidade está dentro do objetivo, portanto o modelo não pode comprar ajuste com flexibilidade gratuitamente.
Essa afirmação merece ceticismo especificamente neste blog. Análise de platô mostra que uma pontuação de validação de ponto único é um critério de seleção ruim e que a robustez vive na forma da vizinhança; PBO quantifica com que frequência o vencedor dentro da amostra perde fora da amostra; deflated Sharpe preços no número de testes. Uma probabilidade marginal ainda é um objetivo dentro da amostra sendo maximizado - o fator Occam penaliza a capacidade do modelo, não a seleção sobre muitos kernels ajustados. Se você ajustar doze kernels candidatos e escolher aquele com a melhor probabilidade marginal, estará de volta ao território de testes múltiplos e a lógica da deflação se aplicará inalterada. A versão falsificável: a seleção de probabilidade marginal produz uma lacuna dentro/fora da amostra menor do que a seleção do conjunto de validação nos mesmos dados e na mesma família de kernel? Isso é mensurável e não é medido abaixo.
A superfície de probabilidade marginal também possui ótimos locais. Múltiplas reinicializações aleatórias ou inicialização cuidadosa são importantes - inicializar a escala de comprimento para a distância mediana entre pares das entradas de treinamento e a variação de ruído para a variação de amostra dos alvos é um ponto de partida razoável.
Custo computacional e escalabilidade
O gargalo está se invertendo , que custa no tempo e na memória. A parede cúbica já é discutida neste blog por outra direção: método de pesquisa vs. custo de avaliação desqualifica totalmente a otimização bayesiana baseada em GP quando o objetivo é barato, porque o substituto custa mais do que as avaliações que ele economiza. A aritmética é a mesma aqui; a aplicação é diferente. Sendo um modelo ajustado aos dados de mercado, o termo cúbico não é uma desqualificação, mas um orçamento: estabelece um limite máximo para a janela de formação.
Estratégias de escalabilidade para aplicações comerciais:
-
GPs esparsos (pontos de indução). Substitua o matriz com um matriz onde . Os pontos indutores são pseudoentradas que resumem os dados de treinamento. A formulação SVGP (Stochastic Variational GP) de Hensman et al. (2013) permite treinamento em minilote com custo por iteração. GPyTorch suporta isso nativamente.
-
Interpolação estruturada do kernel (SKI/KISS-GP). Explora a estrutura Kronecker e Toeplitz na matriz do kernel quando as entradas estão em uma grade. Reduz custo para onde é o tamanho da grade. Ideal para séries temporais amostradas regularmente (por exemplo, barras de 1 minuto).
-
GPs locais em janelas deslizantes. Treine GPs separados apenas com dados recentes. É aqui que entra a restrição específica do GP: os kernels padrão são estacionários ( depende apenas de ) e os mercados não, então a resposta usual é uma janela contínua – mas o comprimento da janela é limitado acima por , não apenas por estatísticas. Otimização walk-forward abrange janelas ancoradas versus janelas rolantes, comprimentos de trens/testes e frequência de reotimização em geral; para um GP, o dimensionamento da janela é uma decisão computacional tanto quanto estatística, e uma janela ancorada (em constante crescimento) simplesmente não está disponível após alguns milhares de pontos sem aproximação. Essa reformulação é a consequência prática: com os GPs você não pode escolher “usar todo o histórico”.
GP para previsão de retorno: uma estrutura prática
Design de recursos: por que 5 a 20 recursos, não 500
A taxonomia de características gerais para ML de dados de mercado - desequilíbrio da carteira de pedidos, pressão da carteira, VPIN, lambda de Kyle, recursos de volatilidade realizada, codificação de tempo cíclico, sinais de ativos cruzados e taxas de financiamento - já está definida em modelagem de spread com aprendizado de máquina; use essa lista.
O que é específico do GP é o tamanho da lista. Os métodos de kernel degradam-se em grandes dimensões: as distâncias se concentram e um kernel estacionário perde a discriminação. O intervalo prático para um GP financeiro é de 5 a 20 insumos, e não as centenas que uma árvore aumentada por gradiente come alegremente. O mecanismo que torna isso viável é ARD (Determinação Automática de Relevância): dê a cada dimensão de entrada sua própria escala de comprimento , e o treinamento de probabilidade marginal empurra para dimensões que não transportam sinal, uma vez que uma escala de comprimento infinita significa que o kernel ignora essa coordenada. A seleção de recursos torna-se um subproduto do ajuste, e o aprendizado são diretamente legíveis como uma classificação de relevância - o que também o torna falsificável: ajuste o kernel composto em barras reais, imprima as escalas de comprimento e veja se os recursos em que você acredita são aqueles que o modelo mantém.
Dimensionamento de posição e abstenção
O GP posterior dá diretamente, então ele cai direto no dimensionamento da proporção de borda e no filtro sem negociação desenvolvido em Previsão Conformal para Dimensionamento de Posição Consciente de Risco, com a largura do intervalo . A única diferença é a proveniência: o GP produz a largura do próprio modelo, e não de um conjunto de calibração, portanto, varia de acordo com a localização do ponto de teste em relação aos dados de treinamento, e não com um quantil global de resíduos passados.
Implementação com GPyTorch
GPyTorch é uma biblioteca baseada em PyTorch para inferência GP escalonável. Ele aproveita a aceleração da GPU, a diferenciação automática e técnicas modernas de álgebra linear (gradientes conjugados, decomposição de Lanczos) para dimensionar GPs além do ingênuo limite.
GP exato básico para previsão de retorno
import torch
import gpytorch
import numpy as np
from torch.utils.data import TensorDataset, DataLoader
class ExactGPModel(gpytorch.models.ExactGP):
"""Exact GP with a composite kernel for financial returns."""
def __init__(self, train_x, train_y, likelihood):
super().__init__(train_x, train_y, likelihood)
self.mean_module = gpytorch.means.ZeroMean()
self.covar_matern = gpytorch.kernels.ScaleKernel(
gpytorch.kernels.MaternKernel(nu=1.5, ard_num_dims=train_x.shape[1])
)
self.covar_periodic = gpytorch.kernels.ScaleKernel(
gpytorch.kernels.PeriodicKernel()
)
self.covar_rbf_decay = gpytorch.kernels.ScaleKernel(
gpytorch.kernels.RBFKernel()
)
def forward(self, x):
mean = self.mean_module(x)
covar = self.covar_matern(x) + self.covar_periodic(x) * self.covar_rbf_decay(x)
return gpytorch.distributions.MultivariateNormal(mean, covar)
ard_num_dims é o que permite as escalas de comprimento por dimensão discutidas acima. Após o treinamento, model.covar_matern.base_kernel.lengthscale é o vetor a ser lido.
Ciclo de treinamento
def train_gp(train_x, train_y, n_epochs=200, lr=0.05, device=None):
"""Train the GP by maximizing the marginal log-likelihood.
Returns the device alongside the model so that callers move test
tensors to the same place -- otherwise prediction crashes on GPU.
"""
if device is None:
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
train_x = train_x.to(device)
train_y = train_y.to(device)
likelihood = gpytorch.likelihoods.GaussianLikelihood().to(device)
model = ExactGPModel(train_x, train_y, likelihood).to(device)
model.train()
likelihood.train()
optimizer = torch.optim.Adam(model.parameters(), lr=lr)
mll = gpytorch.mlls.ExactMarginalLogLikelihood(likelihood, model)
losses = []
for epoch in range(n_epochs):
optimizer.zero_grad()
output = model(train_x)
loss = -mll(output, train_y)
loss.backward()
optimizer.step()
losses.append(loss.item())
if (epoch + 1) % 50 == 0:
noise = likelihood.noise.item()
print(
f"Epoch {epoch+1}/{n_epochs} | "
f"Loss: {loss.item():.4f} | "
f"Noise: {noise:.6f}"
)
return model, likelihood, device, losses
Previsão com incerteza
def predict_with_uncertainty(model, likelihood, test_x, device):
"""Generate predictions with uncertainty estimates."""
model.eval()
likelihood.eval()
test_x = test_x.to(device)
with torch.no_grad(), gpytorch.settings.fast_pred_var():
posterior = likelihood(model(test_x))
mean = posterior.mean
variance = posterior.variance
lower, upper = posterior.confidence_region() # 2-sigma bounds
return {
"mean": mean.cpu().numpy(),
"std": variance.sqrt().cpu().numpy(),
"lower_2sigma": lower.cpu().numpy(),
"upper_2sigma": upper.cpu().numpy(),
}
O fast_pred_var() O gerenciador de contexto usa o algoritmo LOVE (Lanczos Variance Estimates) para calcular variações preditivas em tempo em vez de .
Pipeline de negociação de ponta a ponta
Observação sobre o criador de recursos abaixo: cada estatística contínua deve ser estritamente retrospectiva e a padronização de entrada deve ser ajustada apenas à fatia de treinamento. Esse segundo ponto não é higiene genérica – é precisamente o canal de vazamento de normalização dissecado e medido na taxonomia de viés de antecipação, que relata a inflação de Sharpe que cada tipo de vazamento produz. Um GP padroniza seus insumos por construção, então esse é o vazamento ao qual ele está mais exposto.
import pandas as pd
def build_features(df: pd.DataFrame, lookback: int = 10) -> pd.DataFrame:
"""Build features for GP-based return prediction."""
features = pd.DataFrame(index=df.index)
for lag in range(1, lookback + 1):
features[f"ret_lag_{lag}"] = df["close"].pct_change().shift(lag)
ret = df["close"].pct_change()
features["vol_ratio"] = (
ret.rolling(10).std() / ret.rolling(50).std()
)
features["vol_zscore"] = (
(df["volume"] - df["volume"].rolling(50).mean())
/ df["volume"].rolling(50).std()
)
if "bid_vol" in df.columns and "ask_vol" in df.columns:
features["obi"] = (
(df["bid_vol"] - df["ask_vol"])
/ (df["bid_vol"] + df["ask_vol"])
)
if hasattr(df.index, "hour"):
hours = df.index.hour + df.index.minute / 60.0
features["time_sin"] = np.sin(2 * np.pi * hours / 24)
features["time_cos"] = np.cos(2 * np.pi * hours / 24)
features.dropna(inplace=True)
return features
def run_gp_strategy(
df: pd.DataFrame,
train_window: int = 500,
retrain_every: int = 50,
confidence_threshold: float = 1.0,
risk_fraction: float = 0.02,
max_leverage: float = 1.0,
):
"""Walk-forward GP trading strategy with uncertainty-based sizing."""
features = build_features(df)
returns = df["close"].pct_change().reindex(features.index)
target = returns.shift(-1) # predict next-bar return
mask = features.notna().all(axis=1) & target.notna()
features = features[mask]
target = target[mask]
positions = pd.Series(0.0, index=features.index)
predictions = pd.DataFrame(
index=features.index, columns=["mean", "std"], dtype=float
)
model = likelihood = device = None
x_mean = x_std = y_mean = y_std = None
for i in range(train_window, len(features)):
if model is None or (i - train_window) % retrain_every == 0:
train_x = torch.tensor(
features.iloc[i - train_window : i].values,
dtype=torch.float32,
)
train_y = torch.tensor(
target.iloc[i - train_window : i].values,
dtype=torch.float32,
)
x_mean, x_std = train_x.mean(0), train_x.std(0) + 1e-8
y_mean, y_std = train_y.mean(), train_y.std() + 1e-8
train_x_norm = (train_x - x_mean) / x_std
train_y_norm = (train_y - y_mean) / y_std
model, likelihood, device, _ = train_gp(
train_x_norm, train_y_norm, n_epochs=100
)
test_x = torch.tensor(
features.iloc[i : i + 1].values, dtype=torch.float32
)
test_x_norm = (test_x - x_mean) / x_std
pred = predict_with_uncertainty(model, likelihood, test_x_norm, device)
pred_mean = pred["mean"][0] * y_std.item() + y_mean.item()
pred_std = pred["std"][0] * y_std.item()
predictions.iloc[i] = [pred_mean, pred_std]
z_score = abs(pred_mean) / (pred_std + 1e-8)
if z_score > confidence_threshold:
size = min(z_score * risk_fraction, max_leverage)
positions.iloc[i] = np.sign(pred_mean) * size
strategy_returns = positions.shift(1) * returns
return strategy_returns, predictions, positions
Escalando para conjuntos de dados maiores com GPs esparsos
Quando a janela de treinamento excede alguns milhares de pontos, a inferência exata do GP torna-se lenta. Mude para um GP variacional esparso:
class SparseGPModel(gpytorch.models.ApproximateGP):
"""Sparse variational GP for large-scale return prediction."""
def __init__(self, inducing_points):
variational_distribution = (
gpytorch.variational.CholeskyVariationalDistribution(
inducing_points.size(0)
)
)
variational_strategy = (
gpytorch.variational.VariationalStrategy(
self,
inducing_points,
variational_distribution,
learn_inducing_locations=True,
)
)
super().__init__(variational_strategy)
self.mean_module = gpytorch.means.ZeroMean()
self.covar_module = gpytorch.kernels.ScaleKernel(
gpytorch.kernels.MaternKernel(nu=1.5)
)
def forward(self, x):
mean = self.mean_module(x)
covar = self.covar_module(x)
return gpytorch.distributions.MultivariateNormal(mean, covar)
def train_sparse_gp(train_x, train_y, n_inducing=128, n_epochs=50, batch_size=256):
"""Train sparse GP with mini-batch stochastic variational inference."""
indices = torch.randperm(train_x.size(0))[:n_inducing]
inducing_points = train_x[indices]
model = SparseGPModel(inducing_points)
likelihood = gpytorch.likelihoods.GaussianLikelihood()
model.train()
likelihood.train()
optimizer = torch.optim.Adam(
[{"params": model.parameters()}, {"params": likelihood.parameters()}],
lr=0.01,
)
mll = gpytorch.mlls.VariationalELBO(
likelihood, model, num_data=train_y.size(0)
)
dataset = TensorDataset(train_x, train_y)
loader = DataLoader(dataset, batch_size=batch_size, shuffle=True)
for epoch in range(n_epochs):
for x_batch, y_batch in loader:
optimizer.zero_grad()
output = model(x_batch)
loss = -mll(output, y_batch)
loss.backward()
optimizer.step()
return model, likelihood
Com 128 pontos indutores, o custo por lote é — cerca de 4 milhões de operações por lote. Isso lida facilmente com conjuntos de dados de mais de 100.000 observações em uma única GPU.
Deep Kernel Learning: GP encontra redes neurais
Quando o espaço de entrada é altamente dimensional ou a relação entre recursos e retornos é altamente não linear, um kernel simples pode ter dificuldades. O aprendizado profundo do kernel (DKL) passa as entradas por uma rede neural antes de aplicar o kernel GP:
onde é uma rede neural com parâmetros e é um kernel padrão (por exemplo, Matern-3/2). A rede aprende uma representação de recurso onde o kernel GP é mais eficaz. Todo o modelo – parâmetros de rede e hiperparâmetros de kernel – é treinado de ponta a ponta, maximizando a probabilidade marginal.
class DeepKernelGP(gpytorch.models.ExactGP):
"""GP with a neural network feature extractor."""
def __init__(self, train_x, train_y, likelihood, input_dim):
super().__init__(train_x, train_y, likelihood)
self.mean_module = gpytorch.means.ZeroMean()
self.feature_extractor = torch.nn.Sequential(
torch.nn.Linear(input_dim, 8),
torch.nn.ReLU(),
torch.nn.Linear(8, 4),
torch.nn.ReLU(),
torch.nn.Linear(4, 2),
)
self.covar_module = gpytorch.kernels.ScaleKernel(
gpytorch.kernels.MaternKernel(nu=1.5, ard_num_dims=2)
)
def forward(self, x):
features = self.feature_extractor(x)
mean = self.mean_module(features)
covar = self.covar_module(features)
return gpytorch.distributions.MultivariateNormal(mean, covar)
DKL combina o aprendizado de representação de redes neurais com a quantificação de incerteza de GPs. A camada GP garante que as previsões distantes dos dados de treinamento tenham alta incerteza – algo que as redes neurais padrão notoriamente falham em fornecer. Observe também que o DKL reintroduz exatamente a flexibilidade que a probabilidade marginal deveria policiar: o fator Occam penaliza o kernel, mas a rede à sua frente tem milhares de parâmetros livres e nenhuma penalidade desse tipo.
Ludkovski e Risk (2025), Gaussian Process Models for Quantitative Finance, pesquisa DKL em um contexto mais amplo de finanças quantitativas, incluindo precificação de opções e otimização de portfólio; esses resultados são deles, sobre seus problemas, e não são evidências sobre a previsão de retorno criptográfico.
Diagnósticos e armadilhas
Calibração
Um GP bem calibrado possui intervalos preditivos que correspondem à cobertura empírica. A verificação é a mesma usada em previsão conforme — que também cobre a teoria de por que a cobertura marginal não é uma cobertura condicional, uma limitação que se aplica igualmente aos intervalos GP:
from scipy.stats import norm
def calibration_report(predictions, actuals):
"""Check if GP uncertainty is well-calibrated."""
z_scores = (actuals - predictions["mean"]) / (predictions["std"] + 1e-8)
for sigma in [1, 2, 3]:
expected_outside = 2 * (1 - norm.cdf(sigma))
actual_outside = (np.abs(z_scores) > sigma).mean()
print(
f"{sigma}-sigma | Expected outside: {expected_outside:.3f} | "
f"Actual outside: {actual_outside:.3f}"
)
A superação esperada é 0,317/0,046/0,003 em 1/2/3 sigma. O número que importa é o que isso imprime em uma verdadeira corrida pelas barras criptográficas, e essa tabela ainda não está neste artigo. A expectativa anterior é que o GP esteja excessivamente confiante – uma probabilidade gaussiana em retornos não estacionários de cauda gorda deveria ser mal disfarçada em 3 sigma – mas “deveria” não é uma medida.
Armadilhas restantes
-
Escala de entrada. As escalas de comprimento são relativas à escala de entrada, portanto, um recurso varia e um variado não posso compartilhar um significado ; ARD é o que resgata intervalos heterogêneos, e a padronização é o que torna a inicialização do ARD sensata.
-
Ajuste excessivo da probabilidade marginal. Com muitos hiperparâmetros de kernel (especialmente núcleos compostos ou de mistura espectral), a probabilidade marginal ainda pode ajustar-se excessivamente. Use anteriores: um anterior log-normal em escalas de comprimento centradas na distância mediana aos pares, um anterior meio normal em variâncias.
-
Condicionamento da matriz de covariância. pode se tornar numericamente singular quando o ruído é muito pequeno ou quando os pontos de treinamento estão quase duplicados. GPyTorch adiciona jitter para a diagonal; dados financeiros mal condicionados muitas vezes precisam de mais.
-
Viés de antecipação. Abordado acima e, em detalhes medidos, na taxonomia de viés de antecipação.
Quando usar GPs versus outros modelos
| Critério | GP | XGBoost | Rede Neural |
|---|---|---|---|
| Incerteza embutida | Sim (estrutural) | Não (precisa de conformidade/bootstrap) | Não (precisa de abandono/conjunto de MC) |
| Eficiência de dados | Excelente (<1000 amostras) | * | * |
| Escalabilidade | Fraco exato, bom esparso | * | * |
| Não linearidade | Dependente do kernel | * | * |
| Interpretabilidade | Decomposição do kernel + escalas de comprimento ARD | * | * |
| Não estacionariedade | Requer janela deslizante ou DKL | * | * |
* Para as colunas XGBoost e de rede neural, siga as comparações publicadas em vez de uma nova afirmação aqui: modelagem de propagação com aprendizado de máquina tem a tabela de aumento de gradiente versus aprendizado profundo para dados tabulares financeiros (limiares de tamanho de dados, interpretabilidade, adaptação de regime, latência) e fusão temporal transformers tem TFT vs. LSTM vs. vanilla Transformer.
Use GPs quando:
- Você tem conjuntos de dados pequenos a médios (menos de aproximadamente 10.000 observações por janela de treinamento)
- Você quer uma hipótese estrutural explícita e inspecionável sobre o sinal (tendência + sazonalidade + ruído)
- A incerteza deve variar com a distância dos dados de treinamento, e não apenas com um quantil residual global
Não use GPs quando:
- Você precisa de inferências abaixo de milissegundos sobre milhões de observações
- Dimensionalidade de entrada excede ~50
- O sinal vive em interações complexas de recursos de alta ordem que os kernels estacionários não podem representar (o DKL ajuda, ao custo da propriedade Occam)
O que este artigo ainda não mede
Tudo acima são máquinas modelo. Nada disso é evidência de que um GP ganha dinheiro com criptografia, e o padrão deste blog é que o artigo contenha seus próprios números. As partidas em aberto, na ordem em que devem ser executadas:
- Escalas de comprimento ARD em barras BTCUSDT reais de 1m. Ajuste o kernel composto, imprima por recurso. Isso testa diretamente a afirmação "ARD é uma seleção de recursos integrada" e produz uma classificação falsificável de relevância de recursos.
- A tabela de calibração de uma corrida walk-forward. Excedência esperada vs. empírica em 1/2/3 sigma, declarada claramente, incluindo o caso em que o GP se revela excessivamente confiante.
- A estratégia baseada na confiança, walk-forward, nas mesmas cinco especialidades que o negativo honesto, deflacionada para contagem de testes. Se falhar, ela se encaixa nessa série como outro resultado negativo.
- O relógio de parede curva para , exato versus SVGP, portanto, a seção de escalabilidade fica em um gráfico em vez de em uma afirmação.
Conclusão
O caso dos processos gaussianos na negociação não é “eles fornecem barras de erro” – a previsão conforme fornece barras de erro com menos suposições de distribuição e uma garantia de cobertura que o GP não possui. O caso é que um GP faz você escrever sua hipótese estrutural como um kernel, encaixá-la em um objetivo que avalia sua própria complexidade e, em seguida, informa quais de seus recursos ele realmente usou por meio das escalas de comprimento ARD. Esse é um modelo incomumente legível.
Os custos são igualmente concretos: escala cúbica que limita sua janela de treinamento, suposições de estacionariedade que uma janela móvel repara apenas parcialmente, uma probabilidade gaussiana de que os retornos de cauda gorda serão violados e - uma vez que você alcance o aprendizado profundo do kernel - a perda silenciosa da própria propriedade de Occam que motivou a probabilidade marginal em primeiro lugar. Se o que resta é negociável é uma questão empírica, e as quatro medidas listadas acima são as que a responderiam.
Referências
- Rasmussen, CE, & Williams, CKI (2006). Processos Gaussianos para Aprendizado de Máquina. Imprensa do MIT.
- Wilson, A. e Adams, R. (2013). Kernels de Processo Gaussiano para Descoberta e Extrapolação de Padrões. ICML.
- Hensman, J., Fusi, N., & Lawrence, ND (2013). Processos Gaussianos para Big Data. UAI.
- Gatheral, J., Jaisson, T., & Rosenbaum, M. (2018). A volatilidade é difícil. Finanças Quantitativas, 18(6).
- Rizvi, SAA, Roberts, SJ, Osborne, MA e Nyikosa, F. (2017). Uma nova abordagem para prever a volatilidade financeira com envelopes de processo gaussianos. arXiv:1705.00891.
- Ludkovski, M., & Risco, J. (2025). Modelos de Processo Gaussianos para Finanças Quantitativas. Springer.
- Gardner, JR, Pleiss, G., Bindel, D., Weinberger, KQ, & Wilson, AG (2018). GPyTorch: Inferência de processo gaussiano de matriz-matriz Blackbox com aceleração de GPU. NeurIPS.
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.