Mímisbrunnr知恵の泉

← 金融工学 一覧

🎓 レベル:標準 | 重要度:A(必須)

📎 前提:VaR(バリュー・アット・リスク)CVaR(期待ショートフォール) | 数理:カイ二乗検定(適合度・独立性)(統計)

要点(BLUF)

1. バックテストの考え方

VaR は「99%の日は損失が VaR 以内」と主張します。この主張が正しいなら、長期間観察したとき**損失が VaR を超える日(違反)は全体の約1%**のはず。違反が多すぎれば VaR が小さすぎ(リスク過小評価)、少なすぎれば大きすぎ(資本の無駄)。違反を数えてモデルを検証するのがバックテストです。

2. 違反回数とKupiec検定

2000日ぶんのリターンに対し 99% VaR を当て、違反を数えます。さらに、観測違反率が目標1%と整合するかを Kupiec の POF(Proportion of Failures)検定で判定します。尤度比統計量

LR=2ln(1p)nxpx(1π^)nxπ^x(p=1α, π^=x/n)\mathrm{LR} = -2\ln\frac{(1-p)^{n-x}\,p^{x}}{(1-\hat\pi)^{n-x}\,\hat\pi^{x}} \quad(\,p=1-\alpha,\ \hat\pi=x/n\,)

は、VaR が妥当なら自由度1のカイ二乗分布に従います(xx は違反回数)。

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 は「過去の分布」に依存します。しかし真に怖いのは、過去に無かった・確率では捉えられない極端な事象。ストレステストは確率を介さず、特定のシナリオを直接ポートフォリオに当てて損失を評価します。

VaR が「確率99%の世界」を見るのに対し、ストレステストは「残り1%の中身」を具体的に問います。両者は補完関係で、規制(バーゼル)も両方を要求します(要最新確認)。

⚠️ よくある誤解

関連ノート