Koopman Operators and DMD: Do Market Modes Survive Out-of-Sample?
Dynamic Mode Decomposition fits a linear operator to nonlinear market dynamics. The only question that matters: do the fitted modes persist from one window to the next, and does the rolling spectral radius lead realised volatility? Here is the measurement protocol and the code to run it.

動的モード分解により、スペクトルが提供されます。スペクトルとは、成長率と周波数を備えたいくつかの複雑な固有値であり、それぞれが資産断面上の空間モードに関連付けられています。構造っぽいですね。問題全体は、それが構造体であるのか、それとも 1 つのノイズ ウィンドウを忠実に記憶する線形演算子であるのかということです。
この質問にはテスト可能な 2 つの部分があり、この記事はそれらを中心に構成されています。
- モードの永続性。 DMD をウィンドウ とウィンドウ にフィットさせます。支配的なモードは同じ部分空間にまたがるのか、それとも再調整されるたびに再シャッフルされるのか?再シャッフルする場合、DMD はサンプル内の分解に過ぎず、それ以上のことはありません。そして、それをはっきりと言うことは、別のチュートリアルよりも有益です。
- 先行指標としてのスペクトル半径。 最大固有値係数 は、適合されたダイナミクスがどれほど爆発的であるかを要約する単一のスカラーです。それは現実のボラティリティを「先導」しているのでしょうか、遅らせているのでしょうか、あるいは単にそれを言い直しているだけなのでしょうか?どちらの回答も公開可能です。最初のものだけが取引可能です。
市場が非定常非線形システムであることは、ここでは仮定されており、議論されていません。このブログでは、algotrading のアトラクターの位相空間幾何学と、HMMs による体制検出] の体制ごとの測定された BTC 統計を使用して、すでにそれを主張しています。以下に続くのは、非定常性が 適合されたクープマン演算子 に与える影響と、それをどのように測定するかというより狭い問題です。
1. 核となるアイデア: 非線形ダイナミクスの線形化

状態空間 上の離散時間動的システムを考えてみましょう。
ここで、 は非線形マップである可能性があります。市場の場合、 は、タイム ステップ における資産リターン、ボラティリティ、または注文帳の不均衡のベクトルです。
Koopman 演算子 は、状態 に直接作用するのではなく、スカラー値の観測可能な関数 に作用します。
重要なプロパティ: は、 がそうでない場合でも線形です。代償は次元です。 は無限次元の関数空間に作用します。優れた有限次元近似により、線形代数の扱いやすさを備えた非線形ダイナミクスの表現力が得られます。予測は行列のべき乗となり、すべてのモードがネットワークの重みに埋もれるのではなく検査可能になります。
2. Koopman作用素のスペクトル分解

に固有値 と固有関数 がある場合、、およびこれらの固有関数の範囲内の観測可能な は次のように分解されます。
ここで、 は クープマン モード、つまり各固有関数が完全な観測可能なベクトルにどのように寄与するかを記述するベクトル値の係数です。
各固有値 は、成長率または減衰率 () と発振周波数 () をエンコードします。
| コンポーネント | 固有値プロパティ | 財務解釈 |
|---|---|---|
| トレンド | 、 | ゆっくりとしたドリフト、勢い |
| サイクル | 、 | 振動、季節性 |
| トランジェント | 減衰するショック、短命な動き | |
| 不安定なモード | 成長する爆発的なダイナミクス |
このテーブルはお約束です。セクション 4 では、Promise がデータに対してチェックされます。
3. 動的モード分解 (DMD)

DMD は、データから を近似するための主力アルゴリズムです。行列に配置されたスナップショットを指定すると、次のようになります。
DMD は、 を使用して最適な線形演算子 を求めます。
- SVD を計算します: 2.プロジェクト:
- 固有分解:
- フルスペースモードを回復します:
の列は DMD モード です。 の対角には DMD 固有値が保持されます。
import numpy as np
from numpy.linalg import svd, eig, lstsq
def dmd(X: np.ndarray, rank: int | None = None) -> tuple:
"""
Dynamic Mode Decomposition.
Parameters
----------
X : np.ndarray, shape (n_features, n_snapshots)
Data matrix where each column is a state snapshot.
rank : int or None
Truncation rank for the SVD. None = no truncation.
Returns
-------
eigenvalues : np.ndarray, shape (r,)
DMD eigenvalues (approximating Koopman eigenvalues).
modes : np.ndarray, shape (n_features, r)
DMD modes (columns), L2-normalised.
amplitudes : np.ndarray, shape (r,)
Mode amplitudes fitted to the FINAL snapshot, so that a one-step
forecast is simply modes @ (eigenvalues * amplitudes).
"""
X0 = X[:, :-1]
X1 = X[:, 1:]
U, S, Vh = svd(X0, full_matrices=False)
if rank is not None:
U = U[:, :rank]
S = S[:rank]
Vh = Vh[:rank, :]
S_inv = np.diag(1.0 / S)
# Reduced operator and its spectrum
A_tilde = U.conj().T @ X1 @ Vh.conj().T @ S_inv
eigenvalues, W = eig(A_tilde)
# Full-space (exact) modes
modes = X1 @ Vh.conj().T @ S_inv @ W
# Normalise modes so that mode comparison across windows is scale-free
norms = np.linalg.norm(modes, axis=0)
norms[norms == 0] = 1.0
modes = modes / norms
# Amplitudes anchored at the last observed snapshot
amplitudes = lstsq(modes, X[:, -1].astype(complex), rcond=None)[0]
return eigenvalues, modes, amplitudes
ここで意図的な選択が 2 つあります。セクション 4.2 ではウィンドウ全体でモード部分空間を比較し、正規化されていない振幅が比較に影響を与えるため、モードは L2 正規化されています。振幅は最初のスナップショットではなく最後のスナップショットに適合します。これは、予測が固有値を大きな累乗にすることは決してないことを意味します。これは、浮動小数点オーバーフローのときに単純な DMD 信号がダイナミクスのように見える数値爆発の原因です。
4. 測定

この記事の教科書ではない部分です。上記はすべてフィッティング手順です。以下は、適合に意味があるかどうかを確認するためのプロトコルです。
データ。 株式 ETF の毎日のダウンロードではなく、プロジェクト独自の為替データを使用します。これは、一貫した分単位またはティック グリッド上の BTC、ETH、流動性アルトの断面です。このブログは暗号優先であり、マイクロ構造の議論は移転されません。行にアセット、列に時間を表示し、ログリターンに基づいて、各ウィンドウ内のアセットごとに評価された を構築します。
4.1 固有値スペクトル
代表的なウィンドウのレポート: 単位円の許容範囲内に収まる 固有値の数、それぞれの振動周期が 時間単位 (期間 バー、換算) に対応する振動周期、および上位の モードが再構築するリターン分散の何分の 1 か。
def spectrum_report(eigenvalues: np.ndarray, bar_minutes: float,
tol: float = 0.05) -> list[dict]:
"""
Turn a DMD spectrum into human-readable rows: modulus, period in hours,
and whether the eigenvalue sits on the unit circle within `tol`.
"""
rows = []
for lam in eigenvalues:
modulus = float(np.abs(lam))
omega = float(np.angle(lam))
period_hours = (2 * np.pi / abs(omega)) * bar_minutes / 60 if omega else np.inf
rows.append({
"modulus": modulus,
"period_hours": period_hours,
"on_unit_circle": abs(modulus - 1.0) < tol,
"regime": "unstable" if modulus > 1 + tol
else "persistent" if abs(modulus - 1.0) <= tol
else "decaying",
})
return sorted(rows, key=lambda r: -r["modulus"])
def reconstruction_r2(X: np.ndarray, modes: np.ndarray,
eigenvalues: np.ndarray, amplitudes: np.ndarray) -> float:
"""Fraction of in-window return variance captured by the truncated modes."""
n_steps = X.shape[1]
# amplitudes are anchored at the last snapshot -> walk backwards
powers = eigenvalues[:, None] ** np.arange(-(n_steps - 1), 1)
X_hat = (modes @ (amplitudes[:, None] * powers)).real
resid = np.var(X - X_hat)
return 1.0 - resid / np.var(X)
正直なスペクトルレポートはすでに記事の価値があります。単位円の近くに何も存在しない場合、持続的な取引サイクルは存在せず、株式関連の文献にある「毎年の季節性」の話は、24 時間年中無休の暗号通貨には単純に引き継がれません。
4.2 隣接するウィンドウ間でのモードの安定性
決定的なテスト。 DMD をウィンドウ にフィットさせ、次にウィンドウ にフィットさせ、ドミナント モード部分空間がどれだけ残っているかを測定します。正しい統計は、モード ベクトルの単純な相関ではなく、モードの順序と複素位相は任意です。2 つの部分空間間の主角です。
def subspace_stability(modes_a: np.ndarray, modes_b: np.ndarray,
k: int = 3) -> float:
"""
Overlap between the leading-k DMD mode subspaces of two adjacent windows.
Returns the mean cosine of the principal angles: 1.0 = identical subspace,
0.0 = orthogonal. Immune to mode reordering and complex phase, both of
which are arbitrary in a DMD fit.
"""
Qa, _ = np.linalg.qr(modes_a[:, :k])
Qb, _ = np.linalg.qr(modes_b[:, :k])
sing = np.linalg.svd(Qa.conj().T @ Qb, compute_uv=False)
return float(np.mean(np.clip(sing, 0.0, 1.0)))
def stability_curve(returns: np.ndarray, window: int, step: int,
rank: int, k: int = 3) -> np.ndarray:
"""Subspace overlap between every pair of adjacent windows."""
fits = []
for t_end in range(window, returns.shape[1], step):
evals, modes, _ = dmd(returns[:, t_end - window:t_end], rank=rank)
order = np.argsort(-np.abs(evals))
fits.append(modes[:, order])
return np.array([subspace_stability(fits[i], fits[i + 1], k=k)
for i in range(len(fits) - 1)])
このオーバーラップの分布をレポートし、ヌルに対してレポートします。同じ統計は、同じリターンの位相ランダム化サロゲートで計算されます。オーバーラップが高いものの、サロゲート null より大きくない場合は、モードがダイナミクスではなく共分散構造を追跡していることを意味します。
4.3 現実のボラティリティに対するローリングスペクトル半径
テスト対象の主張: ローリング ウィンドウで再計算された は、ボラティリティとともにではなく、ボラティリティが実現する「前」に移動します。スカラーは機械的に新しいものです。このブログでは以前に幾何学的スカラー、特に アルゴリズム取引のための複雑多様体] の小林曲率をリアルタイムで監視しましたが、コープマン固有値係数は異なる故障モードを持つ異なる量であり、継承されたピッチではなく独自のリードラグ テストに値します。
def rolling_spectral_radius(returns: np.ndarray, window: int = 1440,
step: int = 60, rank: int = 5) -> dict:
"""
Rolling DMD spectrum for regime monitoring.
returns : np.ndarray, shape (n_assets, n_timesteps)
window : rolling window length in bars
step : bars between refits
"""
idx, radii, dom_freq = [], [], []
for t_end in range(window, returns.shape[1], step):
X_win = returns[:, t_end - window:t_end]
try:
evals, _, _ = dmd(X_win, rank=rank)
except np.linalg.LinAlgError:
continue
idx.append(t_end)
radii.append(float(np.max(np.abs(evals))))
on_circle = np.abs(np.abs(evals) - 1.0) < 0.1
if on_circle.any():
sel = evals[on_circle]
dom_freq.append(float(np.abs(np.angle(sel[np.argmax(np.abs(sel))])) / (2 * np.pi)))
else:
dom_freq.append(0.0)
return {"index": np.array(idx),
"spectral_radius": np.array(radii),
"dominant_frequency": np.array(dom_freq)}
def lead_lag(signal: np.ndarray, target: np.ndarray, max_lag: int = 24) -> dict:
"""
Cross-correlation of `signal` against `target` over +/- max_lag steps.
A peak at negative lag means the signal LEADS the target.
"""
s = (signal - signal.mean()) / (signal.std() + 1e-12)
y = (target - target.mean()) / (target.std() + 1e-12)
lags = np.arange(-max_lag, max_lag + 1)
corrs = []
for L in lags:
if L < 0:
corrs.append(float(np.corrcoef(s[:L], y[-L:])[0, 1]))
elif L > 0:
corrs.append(float(np.corrcoef(s[L:], y[:-L])[0, 1]))
else:
corrs.append(float(np.corrcoef(s, y)[0, 1]))
corrs = np.array(corrs)
return {"lags": lags, "corr": corrs, "peak_lag": int(lags[np.argmax(np.abs(corrs))])}
spectral_radius を同じグリッド上で計算された実現ボラティリティに合わせて、peak_lag を読み取ります。ラグ 0 のピークは、 が追加のステップを含むボラティリティの再計算であることを意味します。負のラグにおけるピークは、サンプル全体およびランク全体にわたって安定しており、この記事の中で取引可能な主張が含まれる唯一のバージョンです。
肯定的な結果の後に起こることについてのメモ
がリードする場合、明らかな次の動きはクロスセクショナルな構築です。DMD が予測する次のステップのリターンによって資産をランク付けし、予測された勝者をロングし、予測された敗者をショートします。この構造はここでは新しいものではありません。これは、暗号化における統計的裁定取引とペア取引] および ベクトルと行列を使用した複雑な裁定取引;唯一真に Koopman 特有の工夫は、固有ポートフォリオが静的な PCA 負荷ではなく時間変化する固有値を運ぶことです。
ここでは手数料やスリッページに関するバックテストが行われていないため、この記事では戦略セクションを意図的に省略しています。そうである場合、ブログ自体のハードルをクリアする必要があります。デフレートされたシャープレシオと複数のテスト、正直なネガティブ]の常套例に対する有意性テストです。 DMD スペクトル プロットは結果ではありません。
ワンステップ予測自体については、正しい実装では、固有値をウィンドウの長さの乗で伝播するのではなく、最後のスナップショットから予測します。
def dmd_one_step(returns: np.ndarray, rank: int = 4) -> np.ndarray:
"""
One-step-ahead prediction from the final snapshot of the window.
Never raise eigenvalues to the window length: any |lambda| != 1 then
overflows or underflows and the "signal" becomes numerical garbage.
"""
evals, modes, amplitudes = dmd(returns, rank=rank)
return (modes @ (evals * amplitudes)).real
5. 拡張 DMD (EDMD): 非線形観測可能物

標準 DMD は生の状態ベクトルを操作します。 EDMD は、まず非線形基底関数の辞書を通じてデータをリフトします。
スカラー関数 の辞書 を指定して、リフトされた状態を定義します。
EDMD は、 を使用して をシークします。以下のコードで使用される規則では、columns として記述され、 が左側で動作します。
| 辞書の種類 | 機能 | キャプチャ |
|---|---|---|
| 多項式 | 非線形の資産間相互作用 | |
| ラジアル基底 (RBF) | 局所的類似性、体制クラスタリング | |
| 時間遅延埋め込み | 記憶 / 自己回帰構造 | |
| フーリエ | 既知の周期性 (日中、毎週) | |
| ボラティリティの特徴 | 不均一分散性、ボリューム クラスタリング |
辞書はドメインの知識が入る場所であり、時間遅延の行は、そもそも遅延座標を気にするコープマン特有の理由です。これらは追加された別個の技術ではなく、リフティング マップのもう 1 つのブロックです。埋め込み自体 (遅延 、埋め込みディメンション 、およびその背後にある再構成定理の選択) は、すでに導入され、コーディングされています アルゴリズム取引のための複雑な多様体 ;そこから遅延ベクトルを取得し、追加の行として build_financial_dictionary に直接入力します。
import numpy as np
from itertools import combinations_with_replacement
def build_financial_dictionary(X: np.ndarray, max_poly_degree: int = 2,
include_volatility: bool = True,
delay_steps: int = 0) -> np.ndarray:
"""
Build a dictionary of nonlinear observables for EDMD.
X : np.ndarray, shape (n_features, n_snapshots)
Returns Z of shape (n_dict, n_snapshots - delay_steps).
"""
n_features, n_snapshots = X.shape
offset = max(delay_steps, 0)
X_eff = X[:, offset:]
n_eff = X_eff.shape[1]
lifted = [X_eff] # degree-1 terms (identity)
if max_poly_degree >= 2:
for deg in range(2, max_poly_degree + 1):
for combo in combinations_with_replacement(range(n_features), deg):
term = np.ones(n_eff)
for idx in combo:
term *= X_eff[idx]
lifted.append(term.reshape(1, -1))
if include_volatility:
lifted.append(np.abs(X_eff)) # absolute returns
lifted.append(X_eff ** 2) # squared returns
for d in range(1, delay_steps + 1):
lifted.append(X[:, offset - d : n_snapshots - d])
return np.vstack(lifted)
def edmd(X: np.ndarray, dictionary_fn=None, reg: float = 1e-8,
**dict_kwargs) -> tuple:
"""
Extended Dynamic Mode Decomposition.
Solves Z1 ~= K @ Z0 in the least-squares sense. Uses a least-squares
solve rather than an explicit Gram inverse: `inv` on a near-singular
dictionary Gram matrix is how EDMD spectra get silently corrupted.
"""
if dictionary_fn is None:
dictionary_fn = lambda x: build_financial_dictionary(x, **dict_kwargs)
Z = dictionary_fn(X)
Z0, Z1 = Z[:, :-1], Z[:, 1:]
p = Z0.shape[0]
G = Z0 @ Z0.T + reg * np.eye(p) # regularised Gram matrix
A = Z1 @ Z0.T
# K = A @ G^{-1}, obtained as (G^T \ A^T)^T. G is symmetric, so G^T = G.
K = np.linalg.solve(G, A.T).T
eigenvalues, eigenvectors = np.linalg.eig(K)
return K, eigenvalues, eigenvectors
6. Deep Koopmanネットワーク

EDMD 辞書は手作りされており、クープマン不変部分空間が不明な場合、これが実際の制限となります。ディープ Koopman ネットワークはリフティングとオペレーターを共同で学習します。
このアーキテクチャはオートエンコーダです。エンコーダ、潜在コード、デコーダ、再構築損失はすべて algotrading における異常検出] で紹介されているものです。さらに、このセクションの要点である 1 つの追加が追加されています。それは、単一の学習済み行列 を通じて潜在ダイナミクスを強制する 線形性損失 です。
x_k --> [Encoder φ] --> z_k --> [Linear K] --> z_{k+1} --> [Decoder ψ] --> x̂_{k+1}
| |
+--- Linearity loss: ‖z_{k+1} - K z_k‖ ---+
項がなければ、潜在空間の後にたまたま行列乗算が続く通常のオートエンコーダーになります。これにより、ネットワークは、その展開が「線形ではない」潜在表現に対してペナルティを課せられます。これにより、学習された がクープマン近似となり、その固有値がセクション 4 の DMD スペクトルに匹敵するようになります。
import torch
import torch.nn as nn
class DeepKoopman(nn.Module):
"""Deep Koopman autoencoder: learned lifting + linear latent dynamics."""
def __init__(self, input_dim: int, latent_dim: int, hidden_dim: int = 128):
super().__init__()
self.encoder = nn.Sequential(
nn.Linear(input_dim, hidden_dim), nn.ReLU(),
nn.Linear(hidden_dim, hidden_dim), nn.ReLU(),
nn.Linear(hidden_dim, latent_dim),
)
self.decoder = nn.Sequential(
nn.Linear(latent_dim, hidden_dim), nn.ReLU(),
nn.Linear(hidden_dim, hidden_dim), nn.ReLU(),
nn.Linear(hidden_dim, input_dim),
)
# Learnable Koopman operator: the ONLY dynamics in latent space
self.K = nn.Linear(latent_dim, latent_dim, bias=False)
def encode(self, x: torch.Tensor) -> torch.Tensor:
return self.encoder(x)
def decode(self, z: torch.Tensor) -> torch.Tensor:
return self.decoder(z)
def forward(self, x_k: torch.Tensor) -> dict:
z_k = self.encode(x_k)
z_k1_pred = self.K(z_k)
return {
"z_k": z_k,
"z_k1_pred": z_k1_pred,
"x_k1_pred": self.decode(z_k1_pred),
"x_k_recon": self.decode(z_k),
}
def multi_step_predict(self, x_0: torch.Tensor, n_steps: int) -> torch.Tensor:
"""Roll out by repeated application of the linear operator."""
z = self.encode(x_0)
preds = []
for _ in range(n_steps):
z = self.K(z)
preds.append(self.decode(z))
return torch.stack(preds, dim=1)
def latent_spectrum(self) -> np.ndarray:
"""Eigenvalues of the learned K — directly comparable to DMD's."""
return np.linalg.eigvals(self.K.weight.detach().cpu().numpy())
def koopman_loss(model: DeepKoopman, x_k: torch.Tensor, x_k1: torch.Tensor,
alpha: float = 1.0, beta: float = 0.5) -> torch.Tensor:
out = model(x_k)
z_k1_true = model.encode(x_k1)
prediction = nn.functional.mse_loss(out["x_k1_pred"], x_k1)
linearity = nn.functional.mse_loss(out["z_k1_pred"], z_k1_true)
reconstruction = nn.functional.mse_loss(out["x_k_recon"], x_k)
return prediction + alpha * linearity + beta * reconstruction
latent_spectrum は、シーケンス モデルに到達するのではなく、これをトレーニングする価値があるものにします。学習された演算子はまだ行列であるため、セクション 4.2 の安定性テストとセクション 4.3 のリードラグ テストが変更されずに深いモデルに適用されます。
7. 実際的な考慮事項と落とし穴

ランクの選択。 切り捨てランク はバイアス分散ダイヤルです。低すぎるとダイナミクスを失い、高すぎるとノイズに適合します。特異値エルボを注目しないでください。このブログでは、ベクトルと行列を使用した複雑なアービトラージ] のランダム行列理論からのマルチェンコ-パスツール拘束を使用して、「ノイズをフィッティングする前にコンポーネントの数」についてすでに適切に答えています。ウィンドウ形状のマルチェンコ-パストゥール エッジを超える特異値を持つコンポーネントを保持し、結果として得られる を、公開するすべてのスペクトルとともに明示的に記述します。
ウィンドウの長さ コープマン理論では、固定の を前提としています。市場はそれを供給しません。ローリング改修は必須であり、セクション 4.2 の安定曲線は、選択したウィンドウが推定するのに十分な長さであるか、1 つのレジーム内にとどまるのに十分短いかどうかを正確に診断します。
ノイズ感度 財務データの信号対ノイズ比は低く、標準 DMD は のノイズによってバイアスされます。モードが不安定であると結論付ける前に、試してみる価値のある解決策:
- 合計 DMD (TDMD) — 合計最小二乗法により と の両方をノイズがあるものとして扱います。
- 最適化された DMD — 残差フロベニウス ノルムに対して固有値モード分解を直接最適化します。
- カーネル EDMD — 辞書を構築せずに、高次元の特徴空間で暗黙的に動作します。
TDMD の下でモードの安定性が大幅に向上する場合、その不安定性は測定ノイズでした。そうでない場合は、それは市場です。
8. DMD が存在する場所

| 方法 | 直線性 | 解釈可能 | 複数段階の予測 |
|---|---|---|---|
| DMD | 状態空間の線形 | はい (モード + 固有値) | 安定(マトリックスパワー) |
| EDMD | 持ち上げられた空間での線形 | 写真はい、辞書を考慮すると | 安定(マトリックスパワー) |
| ディープ・コープマン | 学習空間における線形 | 中程度 (潜在的な K を検査) | 安定(マトリックスパワー) |
ボラティリティ固有の予測の場合、比較ポイントは GARCH ファミリーです。「crypto](/en/blog/post/garch-volatility-forecasting-crypto) の GARCH ボラティリティ予測」を参照してください。完全に非線形のシーケンス モデルと、それらがもたらす解釈可能性と誤差累積のトレードオフについては、trading の Temporal Fusion Transformer を参照してください。
DMD が占めるニッチな領域は狭いですが現実的です。つまり、自己回帰ロールアウトではなく単一の行列乗によって生成されるマルチステップ予測であり、すべてのモードが検査可能です。そのニッチにアルファが含まれるかどうかはセクション 4 の問題であり、この表の問題ではありません。
## 結論

コープマン理論は、市場のダイナミクスを観察するための真にエレガントな方法であり、エレガンスだからこそ、利用可能な最も厳しいテストが必要です。要点:
- DMD をフィットし、すぐにフィットをテストします。 位相ランダム化ヌルに対して測定された、隣接するウィンドウ間の部分空間の重なりにより、構造を見つけたかウィンドウを記憶したかが午後以内にわかります。
- ローリングスペクトル半径は監視する価値のある 1 つのスカラーです。その値は完全に進み遅れの結果に依存します。ラグ 0 では、ボラティリティ プロキシになります。負の遅れでは、それは政権の警告です。
- 最後のスナップショットで振幅を固定し、固有値をウィンドウの長さまで上げないでください。世の中の DMD 「信号」の大部分は浮動小数点アーティファクトです。
- 肘を目視するのではなく、マルチェンコ-パストゥールによってランクを選択し、あらゆるスペクトルでランクを公開します。
- スペクトル プロットは結果ではありません。 これに基づいて構築された戦略は、手数料、スリッページ、およびデフレート シャープ テストに耐えなければ意味がありません。
これらを超える実装については、PyDMD ライブラリが DMD バリアントを包括的にカバーしており、Mallen らのリファレンス コードが Koopman 側の詳細をカバーしています。
市場は混乱し、非定常で、部分的に観測される状態が続くだろう。コープマン理論は、その混乱から構造を抽出するための原則に基づいたレンズを提供します。ただし、来週もその構造がまだ存在していることを確認できます。
参考文献と詳細情報:
- B. O. Koopman、「ヒルベルト空間におけるハミルトニアン システムと変換」米国科学アカデミー紀要、1931 年。
- J. H. Tu 他、「動的モード分解について: 理論と応用」、Journal of Computational Dynamics、2014 年。
- M. O. Williams、I. G. Kevrekidis、C. W. Rowley、「クープマン演算子のデータ駆動近似: 動的モード分解の拡張」Journal of Nonlinear Science、2015 年。
- B. Lusch、J. N. Kutz、S. L. Brunton、「非線形ダイナミクスの普遍的な線形埋め込みのための深層学習」Nature Communications、2018 年。
- J. Mann および J. N. Kutz、「金融取引戦略のための動的モード分解」Quantitative Finance、2016 年。
- A. Mallen 他、「時間分布シフトを伴う時系列用の Koopman Neural Forecaster」、ICML、2023 年。
- E. Gonzalez および M. Generelo、「コープマン演算子、EDMD、テインズの定理、および機械学習によるカオス経済モデルの分析」金融と経済におけるデータ サイエンス、2022 年。