σμ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 ロードマップ›モジュール 7 · アルファ研究とリスク›トピック 7.4·~50 分上級

ヒストリカルVaR

ロードマップ上の位置モジュール 7/11 · トピック 4/9
7.4 ヒストリカルVaR
—ラボスコア— / 100合計XP—連続日数—

信頼水準付きの損失限度

バリュー・アット・リスクは、最悪ではない悪い日にいくら失うかに答えます。1日・信頼水準95%のVaRが2%なら、100日のうち95日は損失が2%を超えないはずです。ヒストリカル法はモデルを必要としません。過去のリターンを並べ替えて5パーセンタイルの損失を読み取り、VaRはその分位点にマイナスを付けたものです。運用者が限度額と比べられる1つの数なので、規制やリスク限度で広く使われます。

import numpy as np

rng = np.random.default_rng(3)
returns = rng.normal(0.0004, 0.012, size=500)           # two years of daily returns

var_95 = -np.quantile(returns, 0.05)                    # minus the 5th percentile
var_99 = -np.quantile(returns, 0.01)
print(round(float(var_95), 5), round(float(var_99), 5))  # 0.01928 0.027  the 99% loss limit is larger

VaRの検証:超過日

VaRは予測なので検証できます。超過日とは、損失がVaRを上回った日、つまり return < -VaR となる日です。95%のVaRなら超過日は全体の約5%になるはずで、それよりずっと多ければモデルがリスクを過小評価し、ずっと少なければ慎重すぎます。規制当局が250日の超過日数を数えるのはこのためです。不等号が厳密である点に注意してください。VaRと等しい損失は超過ではありません。

import numpy as np

rng = np.random.default_rng(3)
returns = rng.normal(0.0004, 0.012, size=500)
var_95 = -np.quantile(returns, 0.05)

exceptions = int(np.sum(returns < -var_95))
print(exceptions, "exceptions in", len(returns), "days; expected about", int(0.05 * len(returns)))     # 25 25

より長い期間と、当時の再現

10日VaRが求められることもよくあります。日次リターンが独立なら、ボラティリティは時間の平方根で増えるので、平方根ルールで換算します:VaR_T = VaR_1 * sqrt(T)。これは近似で、ファットテールや自己相関のあるリターンでは精度が落ちます。誠実なバックテストは時間も守ります。t 日に使うVaRは t 日より前のリターンだけから作らねばならず、先読みを避ける規律と同じです。ローリングVaRは直近 n 日の窓でそれを行います。

# The square-root-of-time rule for several horizons
import math

one_day = 0.0186
for days in (1, 5, 10, 20):
    print(days, round(one_day * math.sqrt(days), 4))        # 1: 0.0186, 5: 0.0416, 10: 0.0588, 20: 0.0832
import numpy as np

def historical_var(returns, confidence):
    return float(-np.quantile(returns, 1 - confidence))

def rolling_var(returns, window, confidence):
    out = np.full(len(returns), np.nan)
    for t in range(window, len(returns)):
        out[t] = historical_var(returns[t - window:t], confidence)      # the days BEFORE day t
    return out

rng = np.random.default_rng(3)
returns = rng.normal(0.0004, 0.012, size=500)
rolling = rolling_var(returns, 250, 0.99)
print(np.isnan(rolling[:250]).all(), round(float(rolling[250]), 5), round(float(rolling[-1]), 5))
exceptions = int(np.sum(returns[250:] < -rolling[250:]))
print(exceptions, "exceptions in", len(returns) - 250, "days (about 2 expected at 99%)")
print(round(historical_var(returns, 0.99) * np.sqrt(10), 5))                 # a 10-day figure by the square-root rule
VaRが教えてくれないことVaRは最悪のケースではなく閾値です。それを超える損失がどれほど悪いかは何も語らないため、次のトピックで期待ショートフォールとストレステストを扱います。ヒストリカル法は窓の質で決まり、平穏な2年間は危機の直前に小さなVaRを出し、99%の数字はわずかな観測にしか基づきません。
  • ヒストリカルVaRは過去リターンの (1 - confidence) 分位点にマイナスを付けたもの(np.quantile)。モデル不要の正の損失率
  • 超過日(return < -VaR)を数えて検証する。全体の約 1 - confidence が目安で、それより多ければVaRが低すぎる
  • var * sqrt(days) で期間を換算する。ローリング窓は過去の日だけを使う。VaRは閾値であり、それを超える損失の大きさは示さない

次はパラメトリックVaRです。生の履歴ではなく正規分布から同じ閾値を計算します。

ラボ:採点される内容

  • historical_var で過去データから損失額を読む25点
  • var_exceptions で超過日数を数える25点
  • scale_var で保有期間を延ばす25点
  • rolling_var で当時の推定値を再現する25点
エクスプローラー · lab-96-1
▾ lab-96-1
…
bash · ブラウザ内
learner@quantcraft:~/lab-96-1$
Pythonは未読み込みPythonLn 1, Col 1Spaces: 4UTF-8

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

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

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

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

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

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