← 記事一覧に戻る
August 6, 2026
読了時間: 5分

ノンパラメトリック価格モデリングのためのガウス過程

ノンパラメトリック価格モデリングのためのガウス過程
#bayesian
#gaussian-process
#kernel
#uncertainty
#non-parametric

「クラシック ML ベースライン」シリーズの一部。

ガウス プロセスがこのブログで別の記事を書く価値がある理由は 2 つありますが、そのどちらも「不確実性をもたらす」ものではありません。

1 つ目は カーネル設計 です。 GP の誘導バイアス全体が 1 つの関数内に存在します k(x,x)k(x, x')そしてその関数は、道がどれほど荒いのか、繰り返すかどうか、繰り返しが衰退するかどうかなど、意図的に書き留めるものです。標準ツールキットには、市場ダイナミクスに関する構造的仮説を明示的に述べてそれに適合させるものは他にありません。 2 つ目は 限界尤度 です。これは、保持されたセットからではなく、モデル自体から導出される複雑さのペナルティを伴うトレーニング目標です。このブログの過学習アークの他のすべての記事 (プラトー分析PBOdeflate Sharpe) は、検証セットの正則化が検索下で脆弱であるため、存在します。一般医は必要ないと主張している。この主張は検証可能であり、それをテストすることは、不確実性を評価する別のチュートリアルよりも興味深いものです。

不確実性自体について: GP 事後分散は 構造的 であり、後で近似されたモデルに巻き付けられるのではなく、平均を生成するのと同じ推論から外れます。これは、コンフォーマル予測 とは真の対照的です。このブログでは、不確実性がポジションサイジングへの適切な入力である理由と、インターバルを取得した後にどうするかについてすでにこのブログで説明しています。この記事はその件について再反論するものではありません。それはモデルの後に続きます。

以下にカーネルと推論機構、GPyTorch の実装、そして最後にはっきりと述べられていますが、この記事ではまだ得られていない測定値を示します。

ガウス過程とは何ですか?

GP はすでにこのブログの Optuna vs. 座標降下法 でベイジアン最適化サロゲートとして登場しており、同じ表記と同じ低次元性の警告が付いています。ここで GP はモデル自体であり、ハイパーパラメーター検索面ではなく市場データに適合するため、扱いはさらに深くなります。

ガウス プロセスは確率変数の集合であり、有限数の確率変数は結合ガウス分布を持ちます。これは関数に関する分布であり、パラメータに関する分布ではありません。

正式には関数 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 ユニットからなる 2 つの隠れ層を持つニューラル ネットワークには数千のユニットがあります。 GP には固定数のパラメータがありません。モデルの複雑さはデータとともに増大します。 10 個の観測値を使用して、GP は 10 次元のガウス分布を定義します。 10,000 回の観測により、10,000 次元のガウス分布が定義されます。

これは、GP にハイパーパラメータがないという意味ではありません。カーネル関数には、事前関数から引き出された関数のプロパティを制御するハイパーパラメーター (長さスケール、振幅、周期性) があります。しかし、機能形式自体は決して固定されません。 GP は、十分なデータと適切なカーネルがあれば、任意の連続関数を表すことができます。これが「ノンパラメトリック」の意味です。モデルは線形関数や多項式のようなパラメトリック ファミリに制約されません。

財務モデリングにとって、これは貴重です。市場は変化します。特徴と収益の関係は非線形、非定常であり、レジームに依存します。パラメトリック モデルは、現実と一致しない可能性のある構造を課します。 GP はデータに語らせます。

カーネル関数: エンコーディング市場構造

カーネル関数 k(x,x)k(x, x') ガウス過程の魂です。任意の 2 つの入力点における関数値間の共分散を指定することにより、どの関数が確率的であるかを事前に定義します。カーネルが異なれば、滑らかさ、周期性、長距離動作に関する異なる仮定がエンコードされます。

放射基底関数 (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 つのデータ ポイントがどの程度離れていても相関関係を維持できるかを制御します。長さのスケールが短いということは、モデルが局所的なパターンに反応することを意味します。長いレングススケールは、幅広いトレンドを捉えていることを意味します。信号の分散 σ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} は第 2 種修正ベッセル関数です。として ν\nu \to \infty、Matern カーネルは RBF に収束します。一般的な選択肢:

  • ν=1/2\nu = 1/2: Ornstein-Uhlenbeck プロセスと同等。関数は連続的ですが微分可能ではありません。ブラウン運動のように 大まか です。
  • ν=3/2\nu = 3/2: 関数は一旦微分可能です。滑らかさと柔軟性のバランスが優れています。
  • ν=5/2\nu = 5/2: 関数は 2 回微分可能です。よりもスムーズ 3/23/2 ただし剛性はRBFより劣ります。

取引解釈: 母性 -3/23/2 カーネルはおそらく金融時系列の最適なデフォルトです。これにより、実際の価格パスが示すような粗さを、ギザギザになることなく実現できます。 ν=1/2\nu = 1/2。これは、ボラティリティの経路にはハースト指数が存在することを経験的に示している「ボラティリティは粗い」文献 (Gatheral、Jaisson、および Rosenbaum、2018 年) と一致しています。 H0.1H \approx 0.1、ブラウン運動よりもはるかに粗い。

Matern カーネルが古典的なボラティリティ モデルを上回る主な公開証拠は、Rizvi et al. (2017) は、ランダム ウォークよりも約 20% 優れた MSE、GARCH よりも 50% 優れていると報告しています。仮想通貨ではなく、2017 年の毎日の通貨ペア データに関するものであり、ここでは再現されていません。ベンチマークとしてではなく、カーネルを試す動機として扱ってください。このブログ独自の GARCH(1,1) は、Ljung-Box と ARCH-LM 診断を使用して、実際の BTC 日次データに当てはめられており、暗号通貨の GARCH ボラティリティ予測 にあります。同じサンプルでのマーターン 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_1 そして k2k_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. 時間の経過とともに振幅が減衰する周期成分 (周期的 ×\times RBF)

製品 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

最初の項はデータ フィット項です (観測値からかけ離れた予測にペナルティを与えます)。 2 番目の項は、複雑さのペナルティです (柔軟性が高すぎるモデル、つまり、カーネル行列の行列式が大きい場合にペナルティを課します)。第 3 項は正規化定数です。

これは自動オッカムの剃刀であり、GP が取引パイプラインにもたらす最も興味深いものです。 この主張の強力なバージョンは、正則化に別個の検証セットは必要ないということです。複雑さのペナルティは目的の範囲内にあるため、モデルは無料で柔軟性を備えた適合性を購入することはできません。

この主張は特にこのブログでは懐疑に値します。 プラトー分析 は、単一点検証スコアが不適切な選択基準であり、堅牢性が近傍の形状に存在することを示しています。 PBO は、サンプル内の勝者がサンプル外で負ける頻度を定量化します。 defrated Sharpe 試行回数の価格。限界尤度は依然としてサンプル内目標であり、最大化されています。 θ\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)。 入力がグリッド上にある場合に、カーネル行列のクロネッカー構造とテプリッツ構造を利用します。コストを削減します 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 では「すべての履歴を使用する」という選択はできません。

収益予測のための GP: 実践的なフレームワーク

機能設計: 500 の機能ではなく、5 ~ 20 の機能を使用する理由

マーケットデータ ML の一般的な特徴分類法 (オーダーブックの不均衡、ブックプレッシャー、VPIN、カイルのラムダ、実現ボラティリティ特徴、循環時間エンコーディング、クロスアセットおよび資金調達レートシグナル) は、すでに 機械学習によるスプレッドモデリング で説明されています。そのリストを使用します。

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 は、スケーラブルな GP 推論のための PyTorch ベースのライブラリです。 GPU アクセラレーション、自動微分、最新の線形代数手法 (共役勾配、ランチョス分解) を活用して、素朴なスケールを超えて 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 Variance Estimates) アルゴリズムを使用して予測分散を計算します。 O(n)O(n) 代わりに時間 O(n2)O(n^2)

エンドツーエンドの取引パイプライン

以下の機能ビルダーに関する注意: すべてのローリング統計は厳密に後方参照である必要があり、入力の標準化はトレーニング スライスにのみ適合する必要があります。その 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 万の操作。これにより、100,000 以上の観測値のデータセットを単一の GPU で簡単に処理できます。

ディープ カーネル ラーニング: 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 パラメータを備えたニューラルネットワークです ϕ\phi そして kbasek_{\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 and Risk (2025)、定量的金融のガウス プロセス モデル は、オプション価格設定やポートフォリオの最適化を含む、より広範な定量的金融の文脈で DKL を調査しています。それらの結果は彼らの問題に関する彼らのものであり、暗号通貨のリターン予測に関する証拠ではありません。

診断と落とし穴

校正

適切に調整された GP には、経験的範囲と一致する予測間隔があります。このチェックは、コンフォーマル予測 で使用されているものと同じです。このチェックでは、限界カバレッジが条件付きカバレッジではない理由の理論もカバーされており、この制限は 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 シグマでかなり下回るはずです。しかし、「すべき」は測定値ではありません。

残りの落とし穴

  1. 入力スケーリング。 長さスケールは入力スケールに相対的なため、フィーチャの範囲は異なります。 [0,10000][0, 10000] そして1つは遠距離攻撃 [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 と他のモデルを使用する場合

基準 GP XGブースト ニューラルネットワーク
内蔵の不確実性 はい (構造的) いいえ (コンフォーマル/ブートストラップが必要) いいえ (MC ドロップアウト/アンサンブルが必要)
データ効率 優れた (<1000 サンプル) * *
スケーラビリティ 正確性は低く、スパース性は良好 * *
非線形性 カーネル依存 * *
解釈可能性 カーネル分解 + ARD 長さスケール * *
非定常性 スライディング ウィンドウまたは DKL が必要です * *

* XGBoost 列とニューラル ネットワーク列については、ここでの新たな主張ではなく、公開されている比較を参照してください: 機械学習を使用したスプレッド モデリング には、財務表データ (データ サイズのしきい値、解釈可能性、レジーム適応、レイテンシー) および時間融合トランスフォーマーには、TFT、LSTM、バニラ Transformer の比較があります。 には、TFT 対 LSTM 対バニラ トランスフォーマーが掲載されています。

次の場合に GP を使用します。

  • 小規模から中規模のデータセットがある場合 (トレーニング ウィンドウあたりの観測数が約 10,000 未満)
  • シグナル (トレンド + 季節性 + ノイズ) に関する明示的で検査可能な構造仮説が必要です。
  • 不確実性は、グローバル残差分位数だけでなく、トレーニング データからの距離によっても変化する必要があります

次の場合には GP を使用しないでください

  • 数百万の観測結果に対するミリ秒未満の推論が必要
  • 入力次元が ~50 を超えています
  • 信号は、定常カーネルでは表現できない複雑な高次機能相互作用の中に存在します (Occam プロパティを犠牲にして DKL が役に立ちます)

この記事でまだ測定されていないこと

上記のものはすべて模型機械です。いずれもGPが仮想通貨で儲けているという証拠ではなく、記事には独自の数字を掲載するのがこのブログの基準だ。開いている項目を実行する順序で示します。

  1. ARD の長さは、実際の BTCUSDT 1m バーに基づいてスケールされます。 複合カーネルをフィットし、出力します。 d\ell_d 機能ごとに。これは、「ARD は機能選択機能が組み込まれている」という主張を直接テストし、改ざん可能な機能関連性ランキングを生成します。
  2. ウォークフォワード実行によるキャリブレーションテーブル 1/2/3 シグマでの予想超過と経験的超過を明確に示し、GP が自信過剰であることが判明した場合を含みます。
  3. 正直なネガティブ と同じ 5 つのメジャーに関する信頼ゲート戦略であるウォークフォワード。試行回数が減ります。 失敗した場合は、別のネガティブな結果としてそのシリーズに組み込まれます。
  4. 壁掛け時計 O(n3)O(n^3) カーブn=250/500/1000/2000/5000n = 250 / 500 / 1000 / 2000 / 5000、正確な対 SVGP であるため、スケーラビリティ セクションはアサーションではなくチャートに基づいています。

結論

トレーディングにおけるガウス プロセスの場合は、「誤差バーが得られる」ということではありません。コンフォーマル予測では、分布の仮定が少なく、GP にはないカバレッジ保証で誤差バーが得られます。その場合、GP は構造仮説をカーネルとして書き留めさせ、それを独自の複雑さの価格を設定する目的に適合させ、ARD の長さスケールを介してどの機能が実際に使用されたかを通知します。異常に読みやすいモデルです。

コストも同様に具体的です。トレーニング ウィンドウを制限する 3 次スケーリング、ローリング ウィンドウが部分的にのみ修復する定常性の仮定、ファットテール リターンが違反するガウス尤度、そして - ディープ カーネル学習に達すると、そもそも限界尤度を動機づけたまさにオッカムの性質が静かに失われます。残ったものが取引可能かどうかは経験的な問題であり、上に挙げた 4 つの測定値がその答えになります。

参考文献

  • Rasmussen, C.E. & Williams, C.K.I. (2006)。 機械学習のためのガウス プロセス。 MITプレス。
  • ウィルソン、A.、アダムス、R. (2013)。パターンの発見と外挿のためのガウス プロセス カーネル。 ICML
  • Hensman、J.、Fusi、N.、Lawrence、ND. (2013)。ビッグデータのガウス過程。 UAI
  • ギャザラル、J.、ジェイソン、T.、ローゼンバウム、M. (2018)。ボラティリティは荒い。 定量的金融、18(6)。
  • Rizvi, S.A.A.、Roberts, S.J.、Osborne, M.A.、Nyikosa, F. (2017)。ガウス過程エンベロープを使用して金融ボラティリティを予測する新しいアプローチ。 arXiv:1705.00891
  • ルドコフスキー、M.、リスク、J. (2025)。 定量的金融のためのガウス プロセス モデル。スプリンガー。
  • Gardner, J.R.、Pleiss, G.、Bindel, D.、Weinberger, K.Q.、および Wilson, A.G. (2018)。 GPyTorch: GPU アクセラレーションを使用したブラックボックス行列-行列ガウス プロセス推論。 NeurIPS
blog.disclaimer

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取引の洞察、市場分析、プラットフォームの更新情報を受け取りましょう。

プライバシーを尊重します。いつでも配信停止可能です。