履歴の代わりに公式を
ヒストリカルVaRは過去のリターンから損失を読み取ります。パラメトリックVaRはリターンが正規分布に従うと仮定し、平均と標準偏差の2つの数だけを使います。確率 1 - confidence で超える損失は z * sigma - mu で、z は標準正規分布の分位点(95%で1.645、99%で2.326)です。高速で滑らかで、ポートフォリオ全体にも簡単に拡張できるため、何十年も業界標準でした。
from statistics import NormalDist
def normal_var(mu, sigma, confidence):
z = NormalDist().inv_cdf(confidence)
return z * sigma - mu
print(round(NormalDist().inv_cdf(0.95), 4), round(NormalDist().inv_cdf(0.99), 4))
print(round(normal_var(0.0005, 0.012, 0.95), 5)) # a 95% one-day loss limit
print(round(normal_var(0.0005, 0.012, 0.99), 5)) # a higher confidence, a larger limit# The multiplier z for common confidence levels
from statistics import NormalDist
for confidence in (0.90, 0.95, 0.975, 0.99, 0.999):
print(confidence, round(NormalDist().inv_cdf(confidence), 3)) # 1.282, 1.645, 1.96, 2.326, 3.091資産からポートフォリオへ、10日へ
ポートフォリオでは平均が weights @ mu、標準偏差が sqrt(weights @ cov @ weights) で、ポートフォリオの分散のトピックで扱った量です。分散効果がそのまま現れ、相関が低いほど標準偏差とVaRが小さくなります。より長い期間では、平均は日数に比例し、標準偏差はその平方根で増えるので、10日VaRは z * sigma * sqrt(10) - mu * 10 です。
import numpy as np
from statistics import NormalDist
def normal_var(mu, sigma, confidence):
return NormalDist().inv_cdf(confidence) * sigma - mu
weights = np.array([0.6, 0.4])
mu = np.array([0.0005, 0.0003])
cov = np.array([[0.00016, 0.00004], [0.00004, 0.00009]]) # volatilities 1.26% and 0.95%, correlation 0.33
mean, std = float(weights @ mu), float(np.sqrt(weights @ cov @ weights))
print(round(mean, 5), round(std, 5))
print(round(normal_var(mean, std, 0.99), 5))
print(round(normal_var(mean * 10, std * np.sqrt(10), 0.99), 5)) # the 10-day figure正規性の仮定が破れるところ
実際のリターンにはファットテールがあり、極端な日は正規分布が許すよりずっと頻繁に起きます。そのためパラメトリックVaRは、特に高い信頼水準で小さすぎるように見えます。同じデータでヒストリカルVaRと比べるとその差が分かり、正なら正規モデルがリスクを過小評価しています。95%では近いことが多く、99%では大きく違うことがあります。
# Fat-tailed returns: the normal model understates the 99% loss
import numpy as np
from statistics import NormalDist
rng = np.random.default_rng(2)
returns = rng.standard_t(3, size=5000) * 0.008 # a fat-tailed distribution (3 degrees of freedom)
for confidence in (0.95, 0.99):
historical = -np.quantile(returns, 1 - confidence)
parametric = NormalDist().inv_cdf(confidence) * returns.std(ddof=1) - returns.mean()
print(confidence, round(float(historical), 5), round(float(parametric), 5), round(float(historical - parametric), 5))問いに合わせて手法を選ぶパラメトリックVaRは、手早く滑らかな数字を得たいときや、線形なポジションのポートフォリオには十分です。リターンがリスクファクターに対して線形でないオプションや、分布の裾に関しては当てになりません。多くのデスクはパラメトリック、ヒストリカル、モンテカルロの複数のVaRを計算し、大きな乖離をモデルへの警告と受け止めます。
- パラメトリックVaR=
z * sigma - mu、z = NormalDist().inv_cdf(confidence)。必要なのは平均と標準偏差だけ - ポートフォリオは
weights @ muとsqrt(weights @ cov @ weights)を使う。期間は平均をdays倍、ボラティリティをsqrt(days)倍する - 実際のリターンにはファットテールがある。ヒストリカルVaRと比べ、差が正なら正規モデルがリスクを過小評価している
次は期待ショートフォールです。VaRを超えた日の平均損失です。
ラボ:採点される内容
normal_varで2つの数から限度額を求める25点portfolio_normal_varでポートフォリオ全体を扱う25点horizon_varでより先の期間を見通す25点normality_gapでファットテールの大きさを測る25点