Gaußsche Prozesse zur nichtparametrischen Preismodellierung
Teil der „Classic ML Baselines“-Reihe.
Zwei Dinge machen einen Gaußschen Prozess einen separaten Artikel in diesem Blog wert, und keines davon ist „es gibt Ihnen Unsicherheit“.
Das erste ist Kernel-Design. Die gesamte induktive Vorspannung eines Hausarztes lebt in einer Funktion , und diese Funktion ist etwas, das Sie bewusst aufschreiben: wie rau der Weg ist, ob er sich wiederholt, ob die Wiederholung abklingt. Nichts anderes im Standard-Toolkit ermöglicht es Ihnen, eine Strukturhypothese über die Marktdynamik explizit aufzustellen und dann darauf zu passen. Die zweite ist die Grenzwahrscheinlichkeit – ein Trainingsziel mit einem Komplexitätsnachteil, der vom Modell selbst und nicht von einem zurückgehaltenen Satz abgeleitet wird. Jeder andere Artikel im Überanpassungsbogen in diesem Blog (Plateau-Analyse, PBO, deflated Sharpe) existiert, weil die Regularisierung des Validierungssatzes bei der Suche fragil ist. Ein Hausarzt behauptet, dass er keinen braucht. Diese Behauptung ist überprüfbar, und es ist interessanter, sie zu testen als ein weiteres Tutorial zur Bestimmung der Unsicherheitsgröße.
Zur Unsicherheit selbst: Die GP-Posteriori-Varianz ist strukturell – sie ergibt sich aus derselben Schlussfolgerung, die den Mittelwert erzeugt, und wird nicht nachträglich um ein angepasstes Modell gewickelt. Das ist der eigentliche Kontrast zur konformen Vorhersage, die in diesem Blog bereits erläutert, warum Unsicherheit die richtige Eingabe für die Positionsgrößenbestimmung ist und was mit einem Intervall zu tun ist, sobald man eines hat. In diesem Artikel wird dieser Fall nicht erneut diskutiert; es geht nach dem Modell.
Was folgt, ist der Kernel und die Inferenzmaschinerie, eine GPyTorch-Implementierung und – am Ende klar ausgedrückt – die Messungen, über die dieser Artikel noch nicht verfügt.
Was ist ein Gaußscher Prozess?
GPs erscheinen in diesem Blog bereits als Ersatz für Bayes'sche Optimierung in Optuna vs. Koordinatenabstieg, mit der gleichen Notation und dem gleichen Vorbehalt hinsichtlich der niedrigen Dimensionalität. Hier ist der GP das Modell selbst, das an Marktdaten und nicht an eine Hyperparameter-Suchoberfläche angepasst wird, sodass die Behandlung tiefer geht.
Ein Gauß-Prozess ist eine Sammlung von Zufallsvariablen, von denen jede endliche Anzahl eine gemeinsame Gauß-Verteilung aufweist. Es handelt sich um eine Verteilung über Funktionen, nicht um eine Verteilung über Parameter.
Formal eine Funktion wird aus einem GP für jede endliche Menge von Eingaben gezogen :
Wo ist die mittlere Funktion und ist die Kovarianzfunktion (Kernelfunktion). Wir schreiben dies kompakt als:
Die Mittelwertfunktion kodiert die vorherige Annahme über das durchschnittliche Verhalten von . Im Handel legen wir normalerweise fest , was die Annahme kodiert, dass wir keine vorherige Richtungsverzerrung bei den Renditen haben. Die gesamte Struktur geht in den Kernel.
Warum nichtparametrisch?
Ein lineares Regressionsmodell mit 5 Merkmalen hat 6 Parameter. Ein neuronales Netzwerk mit zwei verborgenen Schichten von 64 Einheiten hat Tausende. Ein GP hat keine feste Anzahl von Parametern – die Komplexität des Modells wächst mit den Daten. Mit 10 Beobachtungen definiert der GP eine 10-dimensionale Gaußsche Funktion. Mit 10.000 Beobachtungen definiert es eine 10.000-dimensionale Gaußsche Funktion.
Dies bedeutet nicht, dass Hausärzte keine Hyperparameter haben. Die Kernelfunktion verfügt über Hyperparameter (Längenskalen, Amplituden, Periodizitäten), die die Eigenschaften von Funktionen steuern, die aus dem Prior abgeleitet wurden. Aber die funktionale Form selbst ist nie festgelegt. Der GP kann jede kontinuierliche Funktion darstellen, sofern genügend Daten und der richtige Kernel vorhanden sind. Das bedeutet „nicht parametrisch“ – das Modell ist nicht auf eine parametrische Familie wie lineare Funktionen oder Polynome beschränkt.
Für die Finanzmodellierung ist dies wertvoll. Märkte verändern sich. Die Beziehung zwischen Merkmalen und Erträgen ist nichtlinear, nichtstationär und vom Regime abhängig. Parametrische Modelle legen eine Struktur fest, die möglicherweise nicht der Realität entspricht. Allgemeinmediziner lassen die Daten sprechen.
Kernelfunktionen: Kodierung der Marktstruktur
Die Kernelfunktion ist die Seele eines Gaußschen Prozesses. Es definiert, welche Funktionen a priori wahrscheinlich sind, indem es die Kovarianz zwischen Funktionswerten an zwei beliebigen Eingabepunkten angibt. Verschiedene Kernel kodieren unterschiedliche Annahmen über Glätte, Periodizität und Langstreckenverhalten.
Radiale Basisfunktion (RBF) / Quadratische Exponentialfunktion
Der RBF-Kernel ist der häufigste Ausgangspunkt:
Wo ist die Signalvarianz (Ausgabeskala) und ist die Längenskala. Von einem GP mit einem RBF-Kernel gezeichnete Funktionen sind unendlich differenzierbar – sehr glatt.
Handelsinterpretation: Die Längenskala steuert, wie weit zwei Datenpunkte voneinander entfernt sein und dennoch korreliert werden können. Eine kurze Längenskala bedeutet, dass das Modell auf lokale Muster reagiert; Eine lange Skala bedeutet, dass breite Trends erfasst werden. Die Signalvarianz steuert die Amplitude der Funktion – wie groß die vorhergesagten Renditen sein können.
Problem für die Finanzen: Unendliche Glätte ist unrealistisch. Finanzielle Erträge weisen Sprünge, Regimewechsel und Diskontinuitäten auf. Der RBF-Kernel kann diese Merkmale überglätten und so Vorhersagen erzeugen, die in der Nähe von Strukturbrüchen zu konservativ sind.
Matern-Kernel
Die Matern-Klasse verallgemeinert die RBF durch Einführung eines Glättungsparameters :
Wo ist die modifizierte Bessel-Funktion zweiter Art. Als , konvergiert der Matern-Kernel zum RBF. Häufige Optionen:
- : Entspricht dem Ornstein-Uhlenbeck-Prozess. Funktionen sind stetig, aber nicht differenzierbar – grob, wie die Brownsche Bewegung.
- : Funktionen sind einmal differenzierbar. Eine gute Balance zwischen Geschmeidigkeit und Flexibilität.
- : Funktionen sind zweifach differenzierbar. Glatter als aber weniger steif als RBF.
Handelsinterpretation: Die Matern- Der Kernel ist wohl der beste Standard für Finanzzeitreihen. Es lässt die Art von Rauheit zu, die reale Preispfade aufweisen, ohne so gezackt zu sein . Dies steht im Einklang mit der Literatur zum Thema „Volatilität ist rau“ (Gatheral, Jaisson & Rosenbaum, 2018), die empirisch zeigt, dass es bei Volatilitätspfaden Hurst-Exponenten gibt , weitaus rauer als die Brownsche Bewegung.
Der wichtigste veröffentlichte Beweis dafür, dass Matern-Kernel klassische Volatilitätsmodelle übertrifft, ist Rizvi et al. (2017), die etwa 20 % bessere MSE als ein Random Walk und 50 % bessere als GARCH berichten – auf täglichen Währungspaardaten von 2017, nicht auf Krypto, und hier nicht reproduziert. Betrachten Sie es als Motivation, den Kernel auszuprobieren, nicht als Benchmark. Die GARCH(1,1)-Anpassung dieses Blogs an reale BTC-Tagesdaten mit Ljung-Box- und ARCH-LM-Diagnose finden Sie in GARCH-Volatilitätsprognose für Krypto; Ein direktes Duell gegen einen GP von Matern auf derselben Stichprobe wäre der ehrliche Vergleich und wurde noch nicht durchgeführt.
Periodischer Kernel
Finanzmärkte weisen zyklische Muster auf: Intraday-Volumenkurven, Wochentagseffekte, monatliche Rebalancing-Ströme, vierteljährliche Gewinnsaisons. Der periodische Kernel erfasst Folgendes:
Wo ist der Zeitraum. Von diesem Kernel abgeleitete Funktionen wiederholen sich mit einem Punkt , moduliert durch die Längenskala Dies steuert, wie schnell die Korrelation innerhalb eines Zeitraums abnimmt.
Handelsinterpretation: Festgelegt (Stunden), um Intraday-Muster zu erfassen, oder (Handelstage) für wöchentliche Saisonalität. Im Gegensatz zu Fourier-Merkmalen geht der periodische Kern nicht von einer festen Anzahl von Harmonischen aus – der GP lernt die Form des Zyklus aus Daten.
Kernel kombinieren: Additive und multiplikative Zusammensetzung
Die wahre Stärke von GP-Kerneln liegt in der Zusammensetzung. Wenn Und sind gültige Kernel, also:
- Summe: — Die Funktion ist die Summe unabhängiger Komponenten (additive Zerlegung)
- Produkt: — Wechselwirkungen zwischen Komponenten (z. B. lokal periodisches Verhalten)
Ein nützlicher zusammengesetzter Kernel für finanzielle Erträge:
Dadurch wird das Signal zerlegt in:
- Eine nicht glatte, aperiodische Trendkomponente (Matern-3/2)
- Eine periodische Komponente, deren Amplitude mit der Zeit abnimmt (Periodisch RBF)
Das Produkt erstellt einen lokal periodischen Kernel: Das Muster wiederholt sich, aber entfernte Wiederholungen haben weniger Einfluss als nahegelegene. Dies ist genau das Richtige für die finanzielle Saisonalität, die sich mit der Zeit verändert, wenn sich die Marktmikrostruktur weiterentwickelt.
Spektralmischungskerne
Für maximale Flexibilität parametrisiert der Spektralmischungskern (SM-Kernel) (Wilson & Adams, 2013) die Spektraldichte des Kernels als eine Mischung von Gaußschen Gleichungen:
Wo sind Mischungsgewichte, sind spektrale Varianzen und sind spektrale Mittel (Frequenzen). Nach dem Satz von Bochner kann jeder stationäre Kern auf diese Weise dargestellt werden. Der SM-Kernel kann periodische Komponenten, langfristige Trends und kurzfristige Korrelationen gleichzeitig erkennen – alles aus Daten.
Handelsinterpretation: Der SM-Kernel ist nützlich, wenn Sie nicht wissen, welche Muster in den Daten vorhanden sind. Es kann versteckte Periodizitäten in Renditereihen identifizieren (z. B. subtile 4-Stunden-Zyklen in Kryptomärkten, die durch automatisierte Neuausrichtung gesteuert werden). Der Nachteil sind mehr Hyperparameter und das Risiko einer Überanpassung bei kleinen Datensätzen.
Hintere Schlussfolgerung: Von der Zeit vor der Vorhersage
Angegebene Trainingsdaten Wo Und , der GP posterior an Testpunkten hat eine geschlossene Lösung. Dies ist der entscheidende Rechenvorteil von GPs gegenüber den meisten Bayes'schen Modellen.
Die hinteren Gleichungen
Lass sei das Trainingskovarianzmatrix, sei das Kreuzkovarianzmatrix und sei das Kovarianzmatrix testen. Der Hinterteil ist:
Wo:
Der hintere Mittelwert ist eine lineare Kombination der Trainingsziele, gewichtet durch die Kernähnlichkeit zwischen Test- und Trainingspunkten. Die hintere Kovarianz geht von der vorherigen Kovarianz aus und subtrahiert die gewonnenen Informationen von den Trainingsdaten. Wenn die Trainingsdaten dicht sind, ist die hintere Varianz gering. Wenn die Trainingsdaten spärlich sind, kehrt die hintere Varianz zur vorherigen zurück.
Die Grenzwahrscheinlichkeit und die Behauptung, die es wert ist, getestet zu werden
Die Kernel-Hyperparameter (Längenskalen, Varianzen, Rauschpegel) werden durch Maximieren der logarithmischen Randwahrscheinlichkeit gelernt:
Der erste Term ist ein Data-Fit-Term (benachteiligt Vorhersagen, die weit von Beobachtungen entfernt sind). Der zweite Term ist ein Komplexitätsnachteil (bestraft Modelle, die zu flexibel sind – d. h. bei denen die Kernelmatrix eine große Determinante hat). Der dritte Term ist eine Normalisierungskonstante.
Dies ist das automatische Rasiermesser von Occam und das Interessanteste, was ein GP in eine Handelspipeline einbringt. Die starke Version der Behauptung besteht darin, dass für die Regularisierung kein separater Validierungssatz erforderlich ist – der Komplexitätsnachteil liegt innerhalb des Ziels, sodass das Modell nicht umsonst Anpassungsfähigkeit und Flexibilität kaufen kann.
Diese Behauptung verdient insbesondere in diesem Blog Skepsis. Plateau-Analyse zeigt, dass ein einzelner Validierungsscore ein schlechtes Auswahlkriterium ist und dass Robustheit von der Form der Nachbarschaft abhängt; PBO quantifiziert, wie oft der Gewinner innerhalb der Stichprobe außerhalb der Stichprobe verliert; deflated Sharpe Preise in der Anzahl der Versuche. Eine marginale Wahrscheinlichkeit ist immer noch ein In-Sample-Ziel, das maximiert wird – Der Occam-Faktor bestraft die Kapazität des Modells, nicht die Auswahl über viele angepasste Kernel. Wenn Sie zwölf Kandidatenkerne anpassen und den Kernel mit der höchsten Grenzwahrscheinlichkeit auswählen, befinden Sie sich wieder im Bereich der Mehrfachtests und die Deflationslogik gilt unverändert. Die falsifizierbare Version: Erzeugt die Randwahrscheinlichkeitsauswahl eine kleinere In-Sample-/Out-of-Sample-Lücke als die Validierungssatzauswahl für dieselben Daten und dieselbe Kernelfamilie? Das ist messbar und wird im Folgenden nicht gemessen.
Die Grenzwahrscheinlichkeitsoberfläche weist auch lokale Optima auf. Mehrere zufällige Neustarts oder eine sorgfältige Initialisierung sind wichtig – die Initialisierung der Längenskala auf den mittleren paarweisen Abstand der Trainingseingaben und der Rauschvarianz auf die Stichprobenvarianz der Ziele ist ein sinnvoller Ausgangspunkt.
Rechenkosten und Skalierbarkeit
Der Engpass kehrt sich um , was kostet rechtzeitig und in Erinnerung. Die kubische Wand wird in diesem Blog bereits aus der anderen Richtung argumentiert: Suchmethode vs. Bewertungskosten disqualifiziert die GP-basierte Bayes'sche Optimierung völlig, wenn das Ziel billig ist, weil der Ersatz mehr kostet als die Bewertungen, die er spart. Die Arithmetik ist hier dieselbe; Die Anwendung ist anders. Als an Marktdaten angepasstes Modell stellt der kubische Term keine Disqualifikation, sondern ein Budget dar: Er legt eine feste Obergrenze für das Trainingsfenster fest.
Skalierbarkeitsstrategien für Handelsanwendungen:
-
Spärliche GPs (induzierende Punkte). Ersetzen Sie die Matrix mit einem Matrix wo . Die auslösenden Punkte sind Pseudo-Eingaben, die die Trainingsdaten zusammenfassen. Die SVGP-Formulierung (Stochastic Variational GP) von Hensman et al. (2013) ermöglicht kostenpflichtiges Mini-Batch-Training pro Iteration. GPyTorch unterstützt dies nativ.
-
Strukturierte Kernel-Interpolation (SKI/KISS-GP). Nutzt die Kronecker- und Toeplitz-Struktur in der Kernel-Matrix, wenn Eingaben auf einem Gitter liegen. Reduziert die Kosten auf Wo ist die Rastergröße. Ideal für regelmäßig abgetastete Zeitreihen (z. B. 1-Minuten-Balken).
-
Lokale Hausärzte auf Schiebefenstern. Trainieren Sie separate Hausärzte nur anhand aktueller Daten. Hier greift die GP-spezifische Einschränkung: Standardkerne sind stationär ( hängt nur davon ab ) und Märkte nicht, daher ist die übliche Antwort ein rollierendes Fenster – aber die Fensterlänge ist oben begrenzt durch , nicht nur durch Statistiken. Walk-Forward-Optimierung behandelt verankerte vs. rollierende Fenster, Zug-/Testlängen und die Häufigkeit der Neuoptimierung im Allgemeinen; Für einen Allgemeinmediziner ist die Fenstergröße sowohl eine rechnerische als auch eine statistische Entscheidung, und ein verankertes (ständig wachsendes) Fenster ist über ein paar tausend Punkte hinaus ohne Näherung einfach nicht verfügbar. Diese Umformulierung ist die praktische Konsequenz: Bei Hausärzten kann man sich nicht für die „Verwendung der gesamten Historie“ entscheiden.
GP für Renditevorhersage: Ein praktischer Rahmen
Feature-Design: Warum 5-20 Features, nicht 500
Die allgemeine Funktionstaxonomie für Marktdaten-ML – Orderbuchungleichgewicht, Buchdruck, VPIN, Kyles Lambda, Merkmale der realisierten Volatilität, zyklische Zeitkodierung, Cross-Asset- und Finanzierungsratensignale – ist bereits in Spread-Modellierung mit maschinellem Lernen dargelegt; Verwenden Sie diese Liste.
GP-spezifisch ist die Größe der Liste. Kernel-Methoden verschlechtern sich in hohen Dimensionen: Abstände konzentrieren sich und ein stationärer Kernel verliert die Unterscheidungsfähigkeit. Der praktische Bereich für einen Finanz-GP liegt bei 5–20 Eingaben, nicht bei den Hunderten, die ein Gradienten-verstärkter Baum gerne frisst. Der Mechanismus, der dies überlebensfähig macht, ist ARD (Automatische Relevanzbestimmung): Geben Sie jeder Eingabedimension eine eigene Längenskala und Marginal-Likelihood-Trainingsschübe für Dimensionen, die kein Signal übertragen, da eine unendliche Längenskala bedeutet, dass der Kernel diese Koordinate ignoriert. Die Auswahl von Merkmalen wird zu einem Nebenprodukt der Anpassung und des Gelernten sind direkt als Relevanzranking lesbar – was es auch falsifizierbar macht: Passen Sie den zusammengesetzten Kernel an echte Balken an, drucken Sie die Längenskalen aus und prüfen Sie, ob die Features, an die Sie glauben, diejenigen sind, die das Modell behält.
Positionsbestimmung und Enthaltung
Der GP posterior gibt direkt, sodass es direkt in den Edge-Ratio-Sizing- und No-Trade-Filter fällt, der in Conformal Prediction for Risk-Aware Position Sizing mit der Intervallbreite entwickelt wurde . Der einzige Unterschied besteht in der Herkunft: Der GP erzeugt die Breite aus dem Modell selbst und nicht aus einem Kalibrierungssatz, sodass sie davon abhängt, wo sich der Testpunkt relativ zu den Trainingsdaten befindet, und nicht von einem globalen Quantil vergangener Residuen.
Implementierung mit GPyTorch
GPyTorch ist eine PyTorch-basierte Bibliothek für skalierbare GP-Inferenz. Es nutzt GPU-Beschleunigung, automatische Differenzierung und moderne Techniken der linearen Algebra (konjugierte Gradienten, Lanczos-Zerlegung), um GPs über das Naive hinaus zu skalieren Limit.
Grundlegender exakter GP für die Renditevorhersage
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 ist es, was die oben diskutierten Längenskalen pro Dimension ermöglicht. Nach dem Training, model.covar_matern.base_kernel.lengthscale ist der auszulesende Vektor.
Trainingsschleife
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
Vorhersage mit Unsicherheit
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(),
}
Der fast_pred_var() Der Kontextmanager verwendet den LOVE-Algorithmus (Lanczos Variance Estimates), um prädiktive Varianzen zu berechnen Zeit statt .
End-to-End Trading Pipeline
Hinweis zum Feature-Builder unten: Jede rollierende Statistik muss streng rückwärtsgerichtet sein und die Eingabestandardisierung darf nur auf dem Trainings-Slice angepasst werden. Bei diesem zweiten Punkt handelt es sich nicht um allgemeine Hygiene – es handelt sich genau um den Normalisierungsleckage-Kanal, der in der Look-Ahead-Bias-Taxonomie seziert und gemessen wird und die Sharpe-Inflation angibt, die jeder Leak-Typ erzeugt. Ein Hausarzt standardisiert seine Eingaben konstruktionsbedingt, daher ist dies das Leck, dem er am stärksten ausgesetzt ist.
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
Scaling to Larger Datasets with Sparse GPs
Wenn das Trainingsfenster einige tausend Punkte überschreitet, wird die genaue GP-Schlussfolgerung langsam. Switch to a variational sparse GP:
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
Bei 128 auslösenden Punkten betragen die Kosten pro Charge — roughly 4 million operations per batch. Dadurch können Datensätze mit mehr als 100.000 Beobachtungen problemlos auf einer einzigen GPU verarbeitet werden.
Deep Kernel Learning: GP trifft auf neuronale Netze
Wenn der Eingaberaum hochdimensional ist oder die Beziehung zwischen Merkmalen und Rückgaben stark nichtlinear ist, kann ein einfacher Kernel Probleme haben. Deep Kernel Learning (DKL) leitet die Eingaben durch ein neuronales Netzwerk, bevor der GP-Kernel angewendet wird:
Wo ist ein neuronales Netzwerk mit Parametern Und is a standard kernel (e.g., Matern-3/2). Das Netzwerk lernt eine Feature-Darstellung, bei der der GP-Kernel am effektivsten ist. Das gesamte Modell – Netzwerkparameter und Kernel-Hyperparameter – wird durchgängig durch Maximierung der Grenzwahrscheinlichkeit trainiert.
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 kombiniert das Repräsentationslernen neuronaler Netze mit der Unsicherheitsquantifizierung von GPs. Die GP-Schicht stellt sicher, dass Vorhersagen, die weit von den Trainingsdaten entfernt sind, eine hohe Unsicherheit aufweisen – etwas, das herkömmliche neuronale Netze bekanntermaßen nicht bieten können. Beachten Sie auch, dass DKL genau die Flexibilität wieder einführt, die die Grenzwahrscheinlichkeit überwachen sollte: Der Occam-Faktor bestraft den Kernel, aber das Netzwerk davor verfügt über Tausende von freien Parametern und keine solche Strafe.
Ludkovski und Risk (2025), Gaussian Process Models for Quantitative Finance, untersuchen DKL in einem breiteren quantitativen Finanzkontext, einschließlich Optionspreisgestaltung und Portfoliooptimierung; Diese Ergebnisse stammen von ihnen, beziehen sich auf ihre Probleme und sind kein Beweis für die Vorhersage der Kryptorendite.
Diagnose und Fallstricke
Kalibrierung
Ein gut kalibrierter GP verfügt über Vorhersageintervalle, die der empirischen Abdeckung entsprechen. Die Prüfung ist die gleiche, die in konforme Vorhersage verwendet wird – die auch die Theorie abdeckt, warum marginale Abdeckung keine bedingte Abdeckung ist, eine Einschränkung, die genauso für GP-Intervalle gilt:
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}"
)
Die erwartete Überschreitung beträgt 0,317 / 0,046 / 0,003 bei 1/2/3 Sigma. Die Zahl, die zählt, ist die, die sich bei einem echten Walk-Forward-Lauf über Kryptobarren ergibt, und diese Tabelle ist noch nicht in diesem Artikel enthalten. Die vorherige Erwartung ist, dass der Allgemeinmediziner zu selbstsicher ist – eine Gaußsche Wahrscheinlichkeit für nichtstationäre Renditen mit fettem Schwanz sollte bei 3 Sigma schlecht abgedeckt sein –, aber „sollte“ ist kein Maß.
Verbleibende Fallstricke
-
Eingabeskalierung. Längenskalen beziehen sich auf die Eingabeskalierung, sodass ein Feature einen Bereich aufweist und einer rang kann keine sinnvolle Aussage machen ; ARD rettet heterogene Bereiche, und Standardisierung macht die Initialisierung von ARD sinnvoll.
-
Überanpassung der Grenzwahrscheinlichkeit. Bei vielen Kernel-Hyperparametern (insbesondere Verbund- oder Spektralmischungskernen) kann die Grenzwahrscheinlichkeit immer noch überangepasst sein. Verwenden Sie Priors: einen logarithmisch-normalen Prior auf Längenskalen, der auf dem mittleren paarweisen Abstand zentriert ist, einen halbnormalen Prior auf Varianzen.
-
Konditionierung der Kovarianzmatrix. kann numerisch singulär werden, wenn das Rauschen zu klein ist oder wenn die Trainingspunkte fast doppelt vorhanden sind. GPyTorch fügt hinzu Jitter zur Diagonale; Schlecht aufbereitete Finanzdaten erfordern oft mehr.
-
Look-Ahead-Bias. Wird oben und, in angemessener Ausführlichkeit, in der Look-Ahead-Bias-Taxonomie behandelt.
Wann sollten GPs im Vergleich zu anderen Modellen verwendet werden?
| Kriterium | Hausarzt | XGBoost | Neuronales Netzwerk |
|---|---|---|---|
| Eingebaute Unsicherheit | Ja (strukturell) | Nein (benötigt konform/Bootstrap) | Nein (benötigt MC-Aussteiger/Ensemble) |
| Dateneffizienz | Ausgezeichnet (<1000 Proben) | * | * |
| Skalierbarkeit | Schlecht genau, gut spärlich | * | * |
| Nichtlinearität | Kernelabhängig | * | * |
| Interpretierbarkeit | Kernelzerlegung + ARD-Längenskalen | * | * |
| Nichtstationarität | Erfordert Schiebefenster oder DKL | * | * |
* Für die Spalten „XGBoost“ und „Neural-Network“ verweisen wir auf die veröffentlichten Vergleiche und nicht auf eine neue Aussage hier: Spread-Modellierung mit maschinellem Lernen verfügt über die Gradient-Boosting-vs-Deep-Learning-Tabelle für finanzielle Tabellendaten (Datengrößenschwellenwerte, Interpretierbarkeit, Regimeanpassung, Latenz) und zeitliche Fusion Transformers hat TFT vs. LSTM vs. Vanilla Transformer.
Verwenden Sie Hausärzte, wenn:
- Sie haben kleine bis mittlere Datensätze (unter ~10.000 Beobachtungen pro Trainingsfenster)
- Sie möchten eine explizite, überprüfbare Strukturhypothese über das Signal (Trend + Saisonalität + Rauschen)
- Die Unsicherheit muss mit der Entfernung von den Trainingsdaten variieren, nicht nur mit einem globalen Restquantil
Verwenden Sie keinen Hausarzt, wenn:
- Sie benötigen Rückschlüsse im Submillisekundenbereich über Millionen von Beobachtungen
- Die Eingabedimensionalität überschreitet ~50
- Das Signal lebt in komplexen Merkmalsinteraktionen höherer Ordnung, die stationäre Kernel nicht darstellen können (DKL hilft, auf Kosten der Occam-Eigenschaft)
Was dieser Artikel noch nicht misst
Bei allem oben genannten handelt es sich um Modellmaschinen. Nichts davon ist ein Beweis dafür, dass ein Hausarzt mit Krypto Geld verdient, und der Standard dieses Blogs besteht darin, dass der Artikel seine eigenen Zahlen trägt. Die offenen Posten in der Reihenfolge, in der sie ausgeführt werden sollen:
- ARD-Längenskalen auf echten BTCUSDT-1-m-Balken. Zusammengesetzten Kernel anpassen, drucken pro Funktion. Dadurch wird die Behauptung „ARD ist integrierte Funktionsauswahl“ direkt getestet und ein fälschbares Feature-Relevanz-Ranking erstellt.
- Die Kalibrierungstabelle aus einem Walk-Forward-Lauf. Erwartete vs. empirische Überschreitung bei 1/2/3 Sigma, klar ausgedrückt, einschließlich des Falles, in dem sich der Hausarzt als zu selbstsicher erweist.
- Die konfidenzgesteuerte Strategie „Walk-Forward“ auf denselben fünf Hauptmärkten wie das ehrliche Negativ, deflationiert für die Testauswertung. Wenn sie fehlschlägt, wird sie als weiteres negatives Ergebnis in diese Serie aufgenommen.
- Die Wanduhr Kurve für , exakt vs. SVGP, daher basiert der Skalierbarkeitsabschnitt auf einem Diagramm und nicht auf einer Behauptung.
Fazit
Das Argument für Gaußsche Prozesse im Handel lautet nicht: „Sie liefern Fehlerbalken“ – die konforme Vorhersage liefert Fehlerbalken mit weniger Verteilungsannahmen und einer Abdeckungsgarantie, die der GP nicht hat. Der Fall ist, dass ein Allgemeinmediziner Sie dazu bringt, Ihre Strukturhypothese als Kernel aufzuschreiben, sie mit einem Ziel zu vergleichen, das ihre eigene Komplexität einpreist, und Ihnen dann über die ARD-Längenskalen mitteilt, welche Ihrer Merkmale sie tatsächlich verwendet hat. That is an unusually legible model.
Die Kosten sind ebenso konkret: kubische Skalierung, die Ihr Trainingsfenster begrenzt, Stationaritätsannahmen, dass ein rollierendes Fenster nur teilweise repariert, eine Gaußsche Wahrscheinlichkeit, dass Fat-Tailed-Returns verletzen, und – sobald Sie zum Deep Kernel Learning greifen – der stille Verlust genau der Occam-Eigenschaft, die die Grenzwahrscheinlichkeit überhaupt motiviert hat. Ob das, was übrig bleibt, handelbar ist, ist eine empirische Frage, und die vier oben aufgeführten Messungen würden sie beantworten.
Referenzen
- Rasmussen, C. E., & Williams, C. K. I. (2006). Gaussian Processes for Machine Learning. MIT Press.
- Wilson, A., & Adams, R. (2013). Gaußsche Prozesskerne zur Mustererkennung und -extrapolation. ICML.
- Hensman, J., Fusi, N. und Lawrence, N. D. (2013). Gaussian Processes for Big Data. UAI. – Gatheral, J., Jaisson, T. & Rosenbaum, M. (2018). Die Volatilität ist hart. Quantitative Finance, 18(6).
- Rizvi, S. A. A., Roberts, S. J., Osborne, M. A. und Nyikosa, F. (2017). Ein neuartiger Ansatz zur Vorhersage finanzieller Volatilität mit Gaußschen Prozessumschlägen. arXiv:1705.00891.
- Ludkovski, M., & Risk, J. (2025). Gaußsche Prozessmodelle für quantitative Finanzen. Springer.
- Gardner, J. R., Pleiss, G., Bindel, D., Weinberger, K. Q., & Wilson, A. G. (2018). GPyTorch: Blackbox-Matrix-Matrix-Gauß-Prozessinferenz mit GPU-Beschleunigung. 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.