← 返回文章列表
August 6, 2026
5 分鐘閱讀

非參數價格建模的高斯過程

非參數價格建模的高斯過程
#bayesian
#gaussian-process
#kernel
#uncertainty
#non-parametric

*「經典 ML 基準」系列的一部分。 *

Two things make a Gaussian process worth a separate article on this blog, and neither of them is "it gives you uncertainty."

首先是核心設計。 A GP's entire inductive bias lives in one function k(x,x)k(x, x'), and that function is something you write down deliberately: how rough the path is, whether it repeats, whether the repetition decays. Nothing else in the standard toolkit lets you state a structural hypothesis about market dynamics that explicitly and then fit it.第二個是邊際可能性-一個訓練目標,其複雜性懲罰源自於模型本身,而不是來自保留的集合。本博客上过拟合弧中的所有其他文章(plateauanalysisPBOdeflated Sharpe)都存在,因为验证集正则化在搜索下很脆弱。一位全科醫生聲稱不需要。 That claim is testable, and testing it is more interesting than another uncertainty-sizing tutorial.

就不确定性本身而言:GP 后验方差是“结构性的”——它脱离了产生均值的相同推论,而不是事后包裹在拟合模型中。这与共形预测形成了真正的对比,该预测已经在本博客中介绍了为什么不确定性是头寸调整的正确输入,以及一旦有了间隔就如何处理间隔。本文並沒有重新論證這個案例;它跟隨模型。

接下來是內核和推理機制、GPyTorch 實現,以及(在最後明確說明的)本文尚未提供的測量結果。

什麼是高斯過程?

GP 已经作为贝叶斯优化代理出现在本博客中的 Optuna 与坐标下降 中,具有相同的符号和相同的低维警告。 Here the GP is the model itself, fitted to market data rather than to a hyperparameter search surface, so the treatment goes deeper.

A Gaussian process is a collection of random variables, any finite number of which have a joint Gaussian distribution. It is a distribution over functions, not a distribution over parameters.

形式上,一個函數 f:XRf: \mathcal{X} \to \mathbb{R} 如果對於任何有限的輸入集,則從 GP 中得出 {x1,x2,,xn}X\{x_1, x_2, \ldots, x_n\} \subset \mathcal{X}:

(f(x1)f(x2)f(xn))N((m(x1)m(x2)m(xn)),(k(x1,x1)k(x1,xn)k(xn,x1)k(xn,xn)))\begin{pmatrix} f(x_1) \\ f(x_2) \\ \vdots \\ f(x_n) \end{pmatrix} \sim \mathcal{N}\left(\begin{pmatrix} m(x_1) \\ m(x_2) \\ \vdots \\ m(x_n) \end{pmatrix}, \begin{pmatrix} k(x_1, x_1) & \cdots & k(x_1, x_n) \\ \vdots & \ddots & \vdots \\ k(x_n, x_1) & \cdots & k(x_n, x_n) \end{pmatrix}\right)

在哪裡 m(x)=E[f(x)]m(x) = \mathbb{E}[f(x)] 是均值函數且 k(x,x)=Cov(f(x),f(x))k(x, x') = \text{Cov}(f(x), f(x')) 是協方差(核)函數。我們將其簡潔地寫成:

fGP(m(),k(,))f \sim \mathcal{GP}(m(\cdot), k(\cdot, \cdot))

平均函數編碼了關於平均行為的先驗信念 ff。在交易中,我們通常會設定 m(x)=0m(x) = 0,編碼我們對回報沒有先驗方向偏差的假設。所有結構都進入內核。

為什麼是非參數?

具有 5 個特徵的線性迴歸模型有 6 個參數。具有兩個 64 個單元的隱藏層的神經網路有數千個。 GP 沒有固定數量的參數——模型的複雜性隨著數據的增加而增長。透過 10 個觀測值,GP 定義了 10 維高斯。透過 10,000 個觀測值,它定義了 10,000 維高斯。

這並不意味著 GP 沒有超參數。核函數具有超參數(長度尺度、振幅、週期性),用於控制從先驗中得出的函數的屬性。但函數形式本身從來就不是固定的。只要有足夠的資料和正確的核,GP 就可以表示任何連續函數。這就是「非參數」的意義——模型不受線性函數或多項式等參數族的限制。

對於財務建模來說,這是有價值的。市場發生變化。特徵和回報之間的關係是非線性、非平穩且依賴於狀態的。參數模型強加的結構可能與現實不符。全科醫生讓數據說話。

核心功能:編碼市場結構

核函數 k(x,x)k(x, x') 是高斯過程的靈魂。它透過指定任意兩個輸入點的函數值之間的協方差來先驗地定義哪些函數可能是先驗的。不同的內核對平滑性、週期性和遠端行為的不同假設進行編碼。

徑向基底函數 (RBF) / 平方指數

RBF 內核是最常見的起點:

kRBF(x,x)=σ2exp(xx222)k_{\text{RBF}}(x, x') = \sigma^2 \exp\left(-\frac{\|x - x'\|^2}{2\ell^2}\right)

在哪裡 σ2\sigma^2 是訊號方差(輸出尺度)且 \ell 是長度尺度。從帶有 RBF 核的 GP 中得出的函數是無限可微的——非常平滑。

交易解讀: 長度尺度 \ell 控制兩個數據點可以相距多遠並且仍然相關。較短的長度尺度意味著模型會對局部模式做出反應;長尺度意味著它可以捕捉廣泛的趨勢。訊號方差 σ2\sigma^2 控制函數的幅度-預測收益有多大。

金融問題: 無限平滑是不切實際的。財務回報有跳躍、政權更迭和間斷。 RBF 內核可以過度平滑這些特徵,從而在結構斷裂附近產生過於保守的預測。

母親內核

Matern 類別透過引入平滑參數來推廣 RBF ν\nu:

kMatern(x,x)=σ221νΓ(ν)(2νxx)νKν(2νxx)k_{\text{Matern}}(x, x') = \sigma^2 \frac{2^{1-\nu}}{\Gamma(\nu)} \left(\frac{\sqrt{2\nu}\|x - x'\|}{\ell}\right)^{\nu} K_{\nu}\left(\frac{\sqrt{2\nu}\|x - x'\|}{\ell}\right)

在哪裡 KνK_{\nu} 是第二類修正貝塞爾函數。作為 ν\nu \to \infty,Matern 核收斂到 RBF。常見選擇:

  • ν=1/2\nu = 1/2:相當於 Ornstein-Uhlenbeck 過程。函數是連續的,但不可微分——粗糙,就像布朗運動一樣。
  • ν=3/2\nu = 3/2:函數一旦可微。平滑度和靈活性之間的良好平衡。
  • ν=5/2\nu = 5/2:函數是二次可微的。比平滑 3/23/2 但剛性不如 RBF。

交易解讀: The Matern-3/23/2 內核可以說是金融時間序列的最佳預設值。它允許真實價格路徑表現出的粗糙度,而不會像 ν=1/2\nu = 1/2。這與「波動性很粗糙」文獻(Gatheral、Jaisson 和 Rosenbaum,2018 年)一致,該文獻根據經驗表明,波動性路徑的赫斯特指數約為 H0.1H \approx 0.1,比布朗運動粗糙得多。

Rizvi 等人已發表的證明 Matern 內核擊敗經典波動率模型的主要證據。 (2017),他們報告 MSE 比隨機遊走好大約 20%,比 GARCH 好 50% — **基於 2017 年每日貨幣對數據,而不是加密貨幣,並且不在此處複製。 ** 將其視為嘗試核心的動機,而不是基準。該部落格自己的 GARCH(1,1) 適合真實的 BTC 每日數據,具有 Ljung-Box 和 ARCH-LM 診斷,位於 GARCH 加密貨幣波動率預測;在同一樣本上與 Matern GP 進行的比較是沒有誠實的比較。

週期性內核

金融市場有週期性模式:日內交易量曲線、週內效應、每月再平衡流量、季度收益季。週期性內核捕獲這些:

kPeriodic(x,x)=σ2exp(2sin2(πxx/p)2)k_{\text{Periodic}}(x, x') = \sigma^2 \exp\left(-\frac{2\sin^2\left(\pi|x - x'|/p\right)}{\ell^2}\right)

在哪裡 pp 是時期。從該內核中提取的函數以句點重複 pp,由長度尺度調製 \ell 它控制一段時間內相關性衰減的速度。

**交易解讀:**設定 p=24p = 24 (小時)捕捉日內模式,或 p=5p = 5 (交易日)每週季節性。與傅立葉特徵不同,週期核不假設固定數量的諧波-GP 從資料中學習週期的形狀。

組合核心:加法和乘法組合

GP 核心的真正威力在於組合。如果 k1k_1k2k_2 是有效的內核,因此:

  • 總和: k1+k2k_1 + k_2 — 函數是獨立分量的總和(加法分解)
  • 產品: k1×k2k_1 \times k_2 - 組件之間的相互作用(例如,局部週期性行為)

一個有用的財務回報複合內核:

k(x,x)=kMatern-3/2(x,x)+kPeriodic(x,x)kRBF(x,x)k(x, x') = k_{\text{Matern-3/2}}(x, x') + k_{\text{Periodic}}(x, x') \cdot k_{\text{RBF}}(x, x')

這將信號分解為:

  1. 非平滑、非週期性趨勢成分 (Matern-3/2)
  2. 振幅隨時間衰減的週期性成分(Periodic ×\times 徑向基底函數)

產品 kPeriodickRBFk_{\text{Periodic}} \cdot k_{\text{RBF}} 建立一個局部週期性內核:模式重複,但遠處重複的影響小於附近重複的影響。這對金融季節性來說是完全正確的,隨著市場微觀結構的發展,金融季節性會隨著時間的推移而改變。

光譜混合內核

為了獲得最大的靈活性,光譜混合 (SM) 內核(Wilson & Adams,2013)將內核的頻譜密度參數化為高斯混合:

kSM(x,x)=q=1Qwqexp(2π2xx2vq)cos(2πxxμq)k_{\text{SM}}(x, x') = \sum_{q=1}^{Q} w_q \exp\left(-2\pi^2 \|x - x'\|^2 v_q\right) \cos\left(2\pi \|x - x'\| \mu_q\right)

在哪裡 wqw_q 是混合物重量, vqv_q 是光譜方差,並且 μq\mu_q 是頻譜平均值(頻率)。根據博赫納定理,任何平穩核都可以用這種方式表示。 SM 核心可以同時從資料中發現週期性成分、長期趨勢和短期相關性。

交易解讀: 當您不知道資料中存在什麼模式時,SM 核心非常有用。它可以識別回報系列中隱藏的週期性(例如,由自動重新平衡驅動的加密貨幣市場中微妙的 4 小時週期)。缺點是超參數較多,並且存在小資料集過度擬合的風險。

後驗推理:從先驗到預測

給定訓練數據 D={(xi,yi)}i=1n\mathcal{D} = \{(x_i, y_i)\}_{i=1}^n 在哪裡 yi=f(xi)+ϵiy_i = f(x_i) + \epsilon_iϵiN(0,σn2)\epsilon_i \sim \mathcal{N}(0, \sigma_n^2), 測試點處的 GP 後驗 XX_* 有一個封閉式解。這是 GP 相對於大多數貝葉斯模型的關鍵計算優勢。

後驗方程

K=k(X,X)K = k(X, X) 成為 n×nn \times n 訓練協方差矩陣, K=k(X,X)K_* = k(X_*, X) 成為 m×nm \times n 互協方差矩陣,以及 K=k(X,X)K_{**} = k(X_*, X_*) 成為 m×mm \times m 檢定協方差矩陣。後驗是:

fX,y,XN(fˉ,Cov(f))f_* | X, y, X_* \sim \mathcal{N}(\bar{f}_*, \text{Cov}(f_*))

在哪裡:

fˉ=K(K+σn2I)1y\bar{f}_* = K_* (K + \sigma_n^2 I)^{-1} y

Cov(f)=KK(K+σn2I)1KT\text{Cov}(f_*) = K_{**} - K_* (K + \sigma_n^2 I)^{-1} K_*^T

後驗均值 fˉ\bar{f}_* 是訓練目標的線性組合,由測試點和訓練點之間的核相似度加權。事後協方差 Cov(f)\text{Cov}(f_*) 從先前的協方差開始 KK_{**} 並減去從訓練資料中獲得的資訊。當訓練資料密集時,後驗方差很小。當訓練資料稀疏時,後驗方差會恢復為先驗方差。

邊際可能性和值得測試的聲明

核心超參數 θ\theta (長度尺度、變異數、雜訊水準)是透過最大化對數邊際似然來學習的:

logp(yX,θ)=12yT(K+σn2I)1y12logK+σn2In2log2π\log p(y | X, \theta) = -\frac{1}{2} y^T (K + \sigma_n^2 I)^{-1} y - \frac{1}{2} \log |K + \sigma_n^2 I| - \frac{n}{2} \log 2\pi

第一項是資料擬合項(懲罰遠離觀察值的預測)。第二項是複雜性懲罰(懲罰過於彈性的模型 - 即核心矩陣具有較大行列式的模型)。第三項是歸一化常數。

這就是自動奧卡姆剃刀,也是 GP 為交易管道帶來的最有趣的東西。 該主張的強有力版本是正則化不需要單獨的驗證集 - 複雜性懲罰在目標之內,因此模型不能免費購買靈活性。

這種說法在這個部落格上特別值得懷疑。 高原分析顯示單點驗證分數是一個糟糕的選擇標準,且穩健性取決於鄰域的形狀;PBO 量化樣本內獲勝者輸掉樣本外的頻率;经调整夏普比率按试验次数折减。邊際可能性仍然是樣本內目標被最大化 θ\theta — 奧卡姆因子懲罰模型容量,而不是對許多擬合核的選擇。如果您適合 12 個候選核心並選擇具有最佳邊際可能性的一個,那麼您將回到多重測試領域,並且通貨緊縮邏輯將保持不變。可證偽的版本:在相同資料和相同核心系列上,邊際似然選擇是否比驗證集選擇產生較小的樣本內/樣本外差距?這是可以測量的,下面不測量。

邊際似然面也具有局部最優。多次隨機重新啟動或仔細的初始化很重要——將長度尺度初始化為訓練輸入的中位數成對距離,將雜訊方差初始化為目標的樣本變異數是一個合理的起點。

計算成本和可擴充性

瓶頸在於反轉 (K+σn2I)(K + \sigma_n^2 I),這需要花費 O(n3)O(n^3) 及時和 O(n2)O(n^2) 記憶中。立方牆已經在這個部落格上從另一個方向進行了爭論:搜尋方法與評估成本當目標很便宜時,徹底取消基於 GP 的貝葉斯優化的資格,因為替代成本超過了它節省的評估成本。這裡的算術是相同的;應用程式不同。作為適合市場數據的“模型”,三次項並不是取消資格,而是預算:它為訓練窗口設定了硬性上限。

交易應用程式的可擴展性策略:

  1. **稀疏 GP(誘導點)。 ** 替換 n×nn \times n 矩陣有一個 m×mm \times m 矩陣其中 mnm \ll n。誘發點 Z={z1,,zm}Z = \{z_1, \ldots, z_m\} 是總結訓練資料的偽輸入。 Hensman 等人提出的 SVGP(隨機變分 GP)公式。 (2013) 允許有成本的小批量訓練 O(nm2)O(nm^2) 每次迭代。 GPyTorch 本身就支持這一點。

  2. **結構化核內插 (SKI/KISS-GP)。 ** 當輸入位於網格上時,請利用核矩陣中的 Kronecker 和 Toeplitz 結構。降低成本 O(n+glogg)O(n + g \log g) 在哪裡 gg 是網格大小。非常適合定期採樣的時間序列(例如 1 分鐘柱)。

  3. **滑動視窗上的本地 GP。 ** 僅根據最近的資料訓練單獨的 GP。這就是 GP 特定約束的作用所在:標準內核是靜止的(k(x,x)k(x,x') 僅取決於 xxx - x')而市場則不然,所以通常的答案是滾動窗口——但窗口長度的上限為 O(n3)O(n^3),而不僅僅是統計數據。 前向最佳化 一般涵蓋錨定視窗與滾動視窗、訓練/測試長度以及重新最佳化頻率;對於 GP 來說,視窗大小調整既是一種計算決策,也是一種統計決策,並且錨定(不斷增長)的視窗在沒有近似的情況下根本無法超過幾千個點。這種重構是實際的結果:對於全科醫生來說,你不能選擇「使用所有歷史記錄」。

報酬預測 GP:實用框架

功能設計:為什麼是 5-20 個功能,而不是 500 個

市場資料機器學習的一般特徵分類——訂單不平衡、帳面壓力、VPIN、Kyle lambda、已實現波動率特徵、循環時間編碼、跨資產和融資率信號——已經在機器學習的傳播建模中列出;使用該清單。

GP 特定的是列表的大小。核方法在高維度上會退化:距離集中,固定核失去辨別能力。金融 GP 的實際輸入範圍是 5-20 個輸入,而不是梯度提升樹愉快地吃掉的數百個輸入。使其生存的機制是ARD(自動相關性確定):為每個輸入維度提供自己的長度尺度 d\ell_d和邊際似然訓練推動 d\ell_d \to \infty 對於不攜帶訊號的維度,因為無限的長度尺度意味著內核忽略該座標。特徵選擇成為擬合的副產品,並且學到的 d\ell_d 作為相關性排名可以直接讀取——這也是它可證偽的原因:將複合內核擬合到真實的條上,列印長度尺度,並查看您相信的特徵是否是模型保留的特徵。

頭寸規模與棄權

GP 後驗給出 σ(x)\sigma_*(x) 直接,因此它直接進入風險感知頭寸調整的保形預測中開發的邊緣比調整和無交易過濾器,間隔寬度 wt=2κσ(x)w_t = 2\kappa\sigma_*(x)。唯一的區別是來源:GP 從模型本身而不是從校準集產生寬度,因此它隨著測試點相對於訓練資料的位置而變化,而不是隨著過去殘差的全域分位數而變化。

使用 GPyTorch 實現

GPyTorch 是一個基於 PyTorch 的函式庫,用於可擴展的 GP 推理。它利用 GPU 加速、自動微分和現代線性代數技術(共軛梯度、Lanczos 分解)來擴展 GP 超越簡單的範圍 O(n3)O(n^3) 限制。

用於回報預測的基本精確 GP

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 是實現上面討論的每維長度尺度的原因。訓練結束後, model.covar_matern.base_kernel.lengthscale 是要讀出的向量。

訓練循環

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

具有不確定性的預測

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(),
    }

fast_pred_var() 上下文管理器使用 LOVE(Lanczos 方差估計)演算法來計算預測方差 O(n)O(n) 時間而不是 O(n2)O(n^2)

端對端交易管道

請注意下面的特徵建構器:每個滾動統計資料都必須嚴格向後看,並且輸入標準化必須僅適合訓練切片。第二點不是一般衛生-它正是在前瞻偏差分類法中剖析和測量的「標準化洩漏」通道,它報告了每種洩漏類型產生的夏普膨脹。 GP 透過建構標準化其輸入,因此這是它最容易遭受的洩漏。

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

使用稀疏 GP 擴展到更大的資料集

當訓練窗口超過幾千點時,精確的 GP 推理會變得很慢。切換到變分稀疏 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

有 128 個誘導點,每批成本為 O(1282×batch_size)O(128^2 \times \text{batch\_size}) — 每批約 400 萬次操作。這可以在單一 GPU 上輕鬆處理超過 100,000 個觀測值的資料集。

深度核心學習:GP 遇上神經網絡

當輸入空間是高維的或特徵和回報之間的關係是高度非線性的時,普通的內核可能會遇到困難。深度內核學習 (DKL) 在應用 GP 核心之前透過神經網路傳遞輸入:

kDKL(x,x)=kbase(gϕ(x),gϕ(x))k_{\text{DKL}}(x, x') = k_{\text{base}}(g_\phi(x), g_\phi(x'))

在哪裡 gϕg_\phi 是一個帶有參數的神經網絡 ϕ\phikbasek_{\text{base}} 是一個標準內核(例如,Matern-3/2)。網路學習 GP 內核最有效的特徵表示。整個模型(網路參數和內核超參數)透過最大化邊際似然進行端到端訓練。

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 將神經網路的表示學習與 GP 的不確定性量化相結合。 GP 層確保遠離訓練資料的預測具有高度不確定性——眾所周知,標準神經網路無法提供這一點。還要注意,DKL 準確地重新引入了邊際似然應該監管的靈活性:奧卡姆因子懲罰內核,但它前面的網路有數千個自由參數,並且沒有這樣的懲罰。

Ludkovski 和 Risk (2025),定量金融的高斯過程模型,在更廣泛的定量金融背景下對 DKL 進行了調查,包括期權定價和投資組合優化;這些結果是他們的,針對他們的問題,並不是加密貨幣回報預測的證據。

診斷與陷阱

校準

經過良好校準的全科醫生具有與經驗覆蓋範圍相符的預測區間。此檢查與共形預測中使用的檢查相同-它也涵蓋了為什麼邊際覆蓋率不是條件覆蓋率的理論,這一限制同樣適用於 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}"
        )

1/2/3 西格瑪時的預期超出值為 0.317 / 0.046 / 0.003。重要的數字是在加密貨幣條上真正向前運行時列印的數字,而該表尚未出現在本文中。先前的預期是 GP 過於自信——肥尾、非平穩回報的高斯似然值應該在 3 sigma 下表現得很差——但「應該」並不是一個衡量標準。

剩餘的陷阱

  1. **輸入縮放。 ** 長度比例與輸入比例相關,因此特徵範圍 [0,10000][0, 10000] 和一個遠端 [0,1][0, 1] 無法分享有意義的 \ell; ARD 可以拯救異構範圍,而標準化則可以使 ARD 的初始化更加理智。

  2. **過度擬合邊際似然。 ** 對於許多核超參數(尤其是複合或頻譜混合核),邊際似然仍然可能過度擬合。使用先驗:以中位數成對距離為中心的長度尺度上的對數常態先驗,變異數上的半常態先驗。

  3. **協方差矩陣調整。 ** (K+σn2I)(K + \sigma_n^2 I) 當噪音可以在數值上變得奇異 σn2\sigma_n^2 太小或訓練點幾乎重複時。 GPyTorch 添加 10610^{-6} 對角線抖動;條件較差的財務資料往往需要更多。

  4. **前瞻偏差。 ** 上文已介紹,並在前瞻偏差分類法 中詳細介紹。

何時使用 GP 與其他模型

標準 全科醫生 XGBoost 神經網路
內在的不確定性 是(結構性) 否(需要保形/引導) 否(需要 MC 退出/合奏)
資料效率 優(<1000 個樣本) * *
可擴充性 差精確,好稀疏 * *
非線性 依賴內核 * *
可解釋性 核分解 + ARD 長度尺度 * *
非平穩性 需推拉窗或DKL * *

* 對於 XGBoost 和神經網路專欄,請遵循已發布的比較,而不是此處的新斷言:使用機器學習進行傳播建模 具有金融表格資料的梯度提升與深度學習表(資料大小閾值、可解釋性、機制適應、延遲)和時間融合 Transformer包含 TFT、LSTM 和普通 Transformer。

在以下情況使用全科醫生:

  • 您擁有中小型資料集(每個訓練視窗約 10,000 個觀察值)
  • 您需要關於訊號的明確的、可檢查的結構假設(趨勢+季節性+噪音)
  • 不確定性必須隨與訓練資料的距離而變化,而不僅僅是隨全域殘差分位數而變化

在以下情況下不要使用全科醫生:

  • 您需要對數百萬個觀察結果進行亞毫秒推理
  • 輸入維度超過~50
  • 訊號存在於固定核無法表示的複雜高階特徵交互作用(DKL 有幫助,但以奧卡姆性質為代價)

本文尚未測量的內容

以上都是模型機械。這些都不是 GP 透過加密貨幣賺錢的證據,而該部落格的標準是文章帶有自己的數字。未清項目依其運行順序排列:

  1. **真實 BTCUSDT 1m 條上的 ARD 長度刻度。 ** 適合複合內核,列印 d\ell_d 每個功能。這直接測試了「ARD 是內建特徵選擇」的說法,並產生了可偽造的特徵相關性排名。
  2. **前向運行的校準表。 ** 1/2/3 sigma 處的預期超出與經驗超出,明確說明,包括 GP 證明過於自信的情況。
  3. **信心門控策略,向前邁進,在與誠實的負面相同的五個專業上,對試驗計數進行縮減。 **如果失敗,它將作為另一個負面結果插入該系列。
  4. 掛鐘 O(n3)O(n^3) 曲線n=250/500/1000/2000/5000n = 250 / 500 / 1000 / 2000 / 5000,精確與 SVGP,因此可擴展性部分依賴圖表而不是斷言。

結論

高斯過程在交易中的情況並不是「它們給你誤差線」——保形預測給你誤差線,具有較少的分佈假設和 GP 沒有的覆蓋保證。情況是,GP 讓你將結構假設寫成內核,將其與以其自身複雜性定價的目標進行擬合,然後透過 ARD 長度尺度告訴你它實際使用了哪些特徵。這是一個異常清晰的模型。

成本同樣具體:三次縮放限制了你的訓練窗口,滾動窗口只能部分修復的平穩性假設,肥尾回報將違反的高斯可能性,以及——一旦你進行深度核學習——最初激發邊際可能性的奧卡姆性質的悄然損失。剩下的東西是否可以交易是一個經驗問題,上面列出的四個衡量標準可以回答這個問題。

參考文獻

  • Rasmussen, C. E. 與 Williams, C. K. I. (2006)。 機器學習的高斯過程。麻省理工學院出版社。
  • 威爾遜,A. 和亞當斯,R. (2013)。用於模式發現和外推的高斯過程內核。 ICML
  • Hensman, J.、Fusi, N. 與 Lawrence, N.D. (2013)。大數據的高斯過程。 UAI
  • Gatheral, J.、Jaisson, T. 與 Rosenbaum, M. (2018)。波動性很大。 定量金融,18(6)。
  • Rizvi, S.A.A.、Roberts, S.J.、Osborne, M.A. 與 Nyikosa, F. (2017)。用高斯過程包絡線預測金融波動的新方法。 arXiv:1705.00891
  • Ludkovski, M. 與 Risk, J. (2025)。 定量金融的高斯過程模型。施普林格。
  • Gardner, J. R.、Pleiss, G.、Bindel, D.、Weinberger, K. Q. 與 Wilson, A. G. (2018)。 GPyTorch:具有 GPU 加速的黑盒矩陣-矩陣高斯過程推理。 NeurIPS
免責宣告:本文提供的資訊僅用於教育和參考目的,不構成財務、投資或交易建議。加密貨幣交易涉及重大損失風險。

Authors

Eugen Soloviov
Eugen Soloviov

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.

Newsletter

緊跟市場步伐

訂閱我們的時事通訊,獲取獨家 AI 交易見解、市場分析和平台更新。

我們尊重您的隱私。您可以隨時退訂。