🎓 レベル:標準 | 重要度:A(必須)
📎 前提:VaR(バリュー・アット・リスク)・CVaR(期待ショートフォール) | 数理:カイ二乗検定(適合度・独立性)(統計)
要点(BLUF)
- バックテストは、VaR モデルが妥当かを「実際の損失が VaR を超えた回数(違反)」で検証します。99% VaR なら、違反は全期間の約1%に収まるはず。
- **Kupiec 検定(POF)**は、観測された違反率が目標 と統計的に整合するかを尤度比で判定します(カイ二乗分布・カイ二乗検定(適合度・独立性)(統計))。
- ストレステストは確率に頼らず、過去の危機や仮想の極端シナリオで「もし起きたら」の損失を直接評価します。VaR が苦手なテールイベントを補完します。
1. バックテストの考え方
VaR は「99%の日は損失が VaR 以内」と主張します。この主張が正しいなら、長期間観察したとき**損失が VaR を超える日(違反)は全体の約1%**のはず。違反が多すぎれば VaR が小さすぎ(リスク過小評価)、少なすぎれば大きすぎ(資本の無駄)。違反を数えてモデルを検証するのがバックテストです。
2. 違反回数とKupiec検定
2000日ぶんのリターンに対し 99% VaR を当て、違反を数えます。さらに、観測違反率が目標1%と整合するかを Kupiec の POF(Proportion of Failures)検定で判定します。尤度比統計量
は、VaR が妥当なら自由度1のカイ二乗分布に従います( は違反回数)。
import numpy as np
from scipy import stats
rng = np.random.default_rng(2)
n = 2000
rets = rng.normal(0.0005, 0.015, n)
alpha = 0.99
VaR = -(rets.mean() + rets.std()*stats.norm.ppf(1-alpha))
x = int(np.sum(-rets > VaR)) # 違反回数(損失がVaRを超えた日)
expected = n*(1-alpha)
print(f"VaR99 = {VaR:.4f}")
print(f"違反回数 x = {x}(期待 {expected:.0f}), 違反率 {x/n:.2%}(目標 {1-alpha:.0%})")
# Kupiec POF 検定
p = 1 - alpha
pi = x/n
LR = -2*(np.log((1-p)**(n-x) * p**x) - np.log((1-pi)**(n-x) * pi**x))
pval = 1 - stats.chi2.cdf(LR, df=1)
print(f"Kupiec LR統計量 = {LR:.4f}, p値 = {pval:.4f}")
出力:
VaR99 = 0.0352
違反回数 x = 16(期待 20), 違反率 0.80%(目標 1%)
Kupiec LR統計量 = 0.8675, p値 = 0.3517
出力の意味:2000日で違反は16回、期待20回より少なめ(違反率 0.80% vs 目標 1%)。Kupiec 検定の p値は 0.3517 と大きく、「違反率は目標1%と整合する」という帰無仮説を棄却できません——この VaR モデルは妥当と判断できます。もし違反が40回(2%)もあれば p値は小さくなり、モデルが危険を過小評価していると分かります。バックテストは、リスクモデルが「絵に描いた餅」でないかを実データで突き合わせる手続きです。
3. 違反を可視化する
どの日に違反が起きたかを時系列で見ると、違反が偶発的に散らばっているか、危機時に固まっているかが分かります。
import numpy as np
import matplotlib.pyplot as plt
import japanize_matplotlib
from scipy import stats
rng = np.random.default_rng(2)
n = 2000
rets = rng.normal(0.0005, 0.015, n)
alpha = 0.99
VaR = -(rets.mean() + rets.std()*stats.norm.ppf(1-alpha))
viol = -rets > VaR
plt.figure(figsize=(9, 4))
plt.plot(rets, lw=0.5, color="gray", label="日次リターン")
plt.axhline(-VaR, color="crimson", ls="--", label=f"-VaR99 = {-VaR:.3f}")
plt.scatter(np.where(viol)[0], rets[viol], color="red", s=25, zorder=5,
label=f"違反({viol.sum()}回)")
plt.xlabel("日"); plt.ylabel("日次リターン")
plt.title("VaRバックテスト:違反の発生")
plt.legend(); plt.tight_layout(); plt.show()
出力の意味:赤破線(VaR)を下回った日が違反(赤点)。今回は正規分布から生成したので違反はランダムに散らばります。現実のデータでは**違反が特定の時期に固まる(クラスタリング)**ことが多く、これはボラティリティが時間変動する(CVaR(期待ショートフォール) のテールリスク)証拠。Kupiec 検定は回数だけを見ますが、違反の「独立性」まで見る検定(Christoffersen)もあり、固まりを検出します。
4. ストレステスト
バックテストと VaR は「過去の分布」に依存します。しかし真に怖いのは、過去に無かった・確率では捉えられない極端な事象。ストレステストは確率を介さず、特定のシナリオを直接ポートフォリオに当てて損失を評価します。
- ヒストリカル・シナリオ:2008年リーマン・ショック、2020年コロナ・ショックなど過去の危機を再現する。
- 仮想シナリオ:「株価 ・金利 bp・ボラ2倍が同時に起きたら」を仮定して評価する。
- 逆ストレステスト:「自社が破綻する損失額」を先に決め、それを生むシナリオを逆算する。
VaR が「確率99%の世界」を見るのに対し、ストレステストは「残り1%の中身」を具体的に問います。両者は補完関係で、規制(バーゼル)も両方を要求します(要最新確認)。
⚠️ よくある誤解
- 「違反ゼロが良いモデル」ではない:99% VaR なら違反は約1%出るのが正常。違反が少なすぎる VaR は過大で、資本を無駄に拘束しています。違反回数は「ちょうど良い」かを見るもの。
- バックテストは過去にしか効かない:過去データで妥当でも、未来の構造変化(レジーム転換)は捉えられません。だからストレステストで「過去に無いシナリオ」を補います。
- ストレステストのシナリオ選びが肝:適当なシナリオでは意味がありません。歴史的整合性・経済的妥当性・十分な厳しさを備えたシナリオ設計そのものが専門技能です(要最新確認)。
関連ノート
- 第8章 リスク管理 目次
- VaR(バリュー・アット・リスク)・CVaR(期待ショートフォール) — 前提:検証するリスク指標
- カイ二乗検定(適合度・独立性)(統計)— Kupiec 検定の土台
- 金融工学テキスト 全体目次 — 第9章 発展トピックへ続く