σμimplied vol · KKmax(S−K,0)udβρᵢⱼfat tailsPnL · drawdownyield · TdSₜ = μSₜ dt + σSₜ dWₜC = S·N(d₁) − K·e⁻ʳᵀ·N(d₂)σₚ² = wᵀΣwd₁ = [ln(S/K) + (r + σ²⁄2)T] ⁄ σ√T∂V/∂t + ½σ²S²·∂²V/∂S² + rS·∂V/∂S − rV = 0Sharpe = (Rₚ − R_f) ⁄ σₚVaR₉₉ = μ − 2.33σρ = Cov(X,Y) ⁄ σₓσᵧE[Rₚ] = Σ wᵢ·E[Rᵢ]φ(x) = e^(−x²/2) ⁄ √2πE[Rᵢ] = R_f + βᵢ(E[Rₘ] − R_f)dXₜ = θ(μ − Xₜ)dt + σ dWₜσ²ₜ = ω + α·ε²ₜ₋₁ + β·σ²ₜ₋₁C − P = S − K·e⁻ʳᵀΔ = ∂V/∂S Γ = ∂²V/∂S²p = (e^(rΔt) − d) ⁄ (u − d)rₜ = ln(Pₜ ⁄ Pₜ₋₁)E[Xₜ₊₁ | ℱₜ] = Xₜf* = (bp − q) ⁄ bz = (x − μ) ⁄ σP(N=k) = λᵏe^(−λ) ⁄ k!Θ = −∂V/∂Tν = S·φ(d₁)·√Tmin wᵀΣw s.t. wᵀμ = μ*∫Σ∂∞λ
σμimplied vol · KKmax(S−K,0)udβρᵢⱼfat tailsPnL · drawdownyield · TdSₜ = μSₜ dt + σSₜ dWₜC = S·N(d₁) − K·e⁻ʳᵀ·N(d₂)σₚ² = wᵀΣwd₁ = [ln(S/K) + (r + σ²⁄2)T] ⁄ σ√T∂V/∂t + ½σ²S²·∂²V/∂S² + rS·∂V/∂S − rV = 0Sharpe = (Rₚ − R_f) ⁄ σₚVaR₉₉ = μ − 2.33σρ = Cov(X,Y) ⁄ σₓσᵧE[Rₚ] = Σ wᵢ·E[Rᵢ]φ(x) = e^(−x²/2) ⁄ √2πE[Rᵢ] = R_f + βᵢ(E[Rₘ] − R_f)dXₜ = θ(μ − Xₜ)dt + σ dWₜσ²ₜ = ω + α·ε²ₜ₋₁ + β·σ²ₜ₋₁C − P = S − K·e⁻ʳᵀΔ = ∂V/∂S Γ = ∂²V/∂S²p = (e^(rΔt) − d) ⁄ (u − d)rₜ = ln(Pₜ ⁄ Pₜ₋₁)E[Xₜ₊₁ | ℱₜ] = Xₜf* = (bp − q) ⁄ bz = (x − μ) ⁄ σP(N=k) = λᵏe^(−λ) ⁄ k!Θ = −∂V/∂Tν = S·φ(d₁)·√Tmin wᵀΣw s.t. wᵀμ = μ*∫Σ∂∞λ
uantCraft Academyロードマップ
Ctrl K
AO
+1.2k 人の学習者
ログイン参加する ↗
ロードマップ
AO
+1.2k 人の学習者
ログイン
  • English
  • Français
  • Español
  • العربية
  • Português
  • Deutsch
  • 日本語
  • 中文
Python ロードマップ›モジュール 8 · 確率モデルと機械学習›トピック 8.9·~1 時間上級

金融データの特徴量エンジニアリング

ロードマップ上の位置モジュール 8/11 · トピック 9/10
8.9 金融データの特徴量エンジニアリング
—ラボスコア— / 100合計XP—連続日数—

モデルに比較可能な入力を与える

250という価格はモデルにとって意味がありません。銘柄によって異なり、時間とともに上昇するので、2020年の値は2010年の値とまったく似ません。特徴量エンジニアリングは、生データを時間や資産をまたいで比較できる入力に変えます。価格の代わりにリターン、値幅の代わりにボラティリティ、水準の代わりに標準化したスコアです。良い特徴量はモデルの選択以上に結果を左右し、それぞれはその日に分かっていたことだけを表さなければなりません。

import numpy as np

prices = np.array([100.0, 101.0, 99.0, 102.0, 104.0, 103.0, 106.0])

window = 3
momentum = np.full(len(prices), np.nan)
momentum[window:] = prices[window:] / prices[:-window] - 1          # the return over the last 3 days
print(np.round(momentum, 4))                                        # nan for the first 3 days: no price 3 days back

過去を見る窓

ほとんどの特徴量は後ろ向きの窓、つまり今日を含む直近 n 個の値の統計量です。モメンタムは今日と n 日前を比べ、ローリング・ボラティリティは直近 n 個のリターンの標準偏差で、ローリング・Zスコアは今日が窓の平均から窓の標準偏差いくつ分離れているかを表します。sliding_window_view を使えば、すべての窓を行列の行として得て、軸に沿って統計量を計算でき、ループは要りません。最初の n - 1 日は完全な窓がなく nan のままです。

import numpy as np

prices = np.array([100.0, 101.0, 99.0, 102.0, 104.0, 103.0, 106.0])
returns = np.diff(prices) / prices[:-1]
window = 3

windows = np.lib.stride_tricks.sliding_window_view(returns, window)        # every 3-day window as a row
vol = np.full(len(returns), np.nan)
vol[window - 1:] = windows.std(axis=1, ddof=1)
print(np.round(vol, 4))                                                    # nan nan then one value per complete window

p_windows = np.lib.stride_tricks.sliding_window_view(prices, window)
z = np.full(len(prices), np.nan)
z[window - 1:] = (prices[window - 1:] - p_windows.mean(axis=1)) / p_windows.std(axis=1, ddof=1)
print(np.round(z, 3))                                                      # how unusual each price is for its last 3 days
# Features put prices of different stocks on the same footing
import numpy as np

cheap = np.array([5.0, 5.2, 5.1, 5.4, 5.5])
dear = np.array([500.0, 520.0, 510.0, 540.0, 550.0])           # ten times the price, the same movements
print(cheap[-1] / cheap[-3] - 1, dear[-1] / dear[-3] - 1)        # identical momentum, about 0.0784 for both
print(dear[-1] - dear[-3], cheap[-1] - cheap[-3])                # price changes (40.0 and 0.4) would not be comparable

目的変数は未来を見る

モデルが予測するのは目的変数で、通常は先の h 日間のリターン prices[t + h] / prices[t] - 1 です。先を見てよいのはこの列だけで、最後の h 日は未来がまだ起きていないため nan になるので、学習前にその行を除きます。よくある間違いは、中心化した窓、全データで計算した統計量、入力に再利用した将来リターンなど、目的変数を誤って含んでしまう特徴量です。検査は簡単で、t 日の特徴量は後の日を追加しても変わってはいけません。

import numpy as np

def forward_return(prices, horizon):
    out = np.full(len(prices), np.nan)
    out[:-horizon] = prices[horizon:] / prices[:-horizon] - 1
    return out

def momentum(prices, window):
    out = np.full(len(prices), np.nan)
    out[window:] = prices[window:] / prices[:-window] - 1
    return out

prices = np.array([100.0, 101.0, 99.0, 102.0, 104.0, 103.0, 106.0])
print(np.round(momentum(prices, 2), 4))                 # looks back: known on each day
print(np.round(forward_return(prices, 2), 4))           # looks forward: the target, nan for the last 2 days

# The prefix test: a feature computed on the first 5 days equals the first 5 values of the full computation
print(np.allclose(momentum(prices[:5], 2), momentum(prices, 2)[:5], equal_nan=True))      # True
実は目的変数になっている特徴量に注意予測が良すぎる特徴量は危険信号です。中心化した窓のモメンタム、明日のリターンを含むボラティリティ、標本全体で標準化したZスコアは、どれもバックテストを輝かせ、実運用で失敗します。すべての特徴量は、t 日の朝に本番で計算できるように作り、上のプレフィックス検査で確かめます。
  • 価格を比較可能な特徴量に変える:モメンタム prices[t] / prices[t - n] - 1、ローリング・ボラティリティ、ローリング・Zスコア。いずれも今日を含む後ろ向きの窓
  • sliding_window_view が各窓を行として与えるので統計量にループは不要。最初の n - 1 日は nan のまま
  • 将来リターン prices[t + h] / prices[t] - 1 は目的変数であって特徴量ではない。各特徴量をプレフィックス検査で確かめる

次はウォークフォワード検証です。時間が実際に流れる順序でモデルを検証します。

ラボ:採点される内容

  • momentum でトレンドを測る25点
  • rolling_vol で直近のリスクを追う25点
  • rolling_zscore で今日がどれだけ異常かを示す25点
  • forward_return で予測対象を定義する25点
エクスプローラー · lab-110-1
▾ lab-110-1
…
bash · ブラウザ内
learner@quantcraft:~/lab-110-1$
Pythonは未読み込みPythonLn 1, Col 1Spaces: 4UTF-8

このトピックをリセットしますか?

lab-110-1 の内容はすべてスターターファイルに戻り、ターミナルも消去されます。現在の解答は失われ、元に戻せません。

リセットされるもの
  • main.py → スターターコード
  • ターミナル履歴とテスト出力
  • 未提出の変更
保持されるもの
  • ラボの最高スコア
  • XPと連続日数
  • 講義の進捗とメモ

コードを実行するにはログインしてください。

レクチャーはすべて無料で読めます。コードの実行、テスト、ラボの提出にはアカウントの作成かログインが必要です。

  • ブラウザ内IDEとbashターミナル。インストール不要
  • 即時採点、XP、ロードマップでの現在地
アカウントを作成ログイン
または
GitHubで続ける