VaRが言わないこと
信頼水準99%のVaRが3%なら、100日のうち99日は損失が3%未満です。100日目については何も言わず、3.1%かもしれず30%かもしれません。期待ショートフォール(ES、CVaR)がこの穴を埋めます。VaRを超えた日の平均損失なので、裾の形を表します。平均を取るため滑らかに変化し、VaRと違って常に分散を評価します。バーゼルが市場リスクの指標をVaRからESへ移したのはそのためです。
import numpy as np
rng = np.random.default_rng(5)
returns = rng.standard_t(4, size=1000) * 0.01
cutoff = np.quantile(returns, 0.05) # the 95% VaR is minus this
tail = np.sort(returns[returns <= cutoff]) # the worst 5% of the days, lowest first
print(len(tail), round(float(-cutoff), 5)) # 50 days; the VaR
print(np.round(tail[:4], 4)) # the four worst days裾を平均する
ヒストリカル法では、ESは単純です。VaRの閾値以下のリターンを選んで平均し、符号を反転します。裾のどのリターンも閾値と同じかそれ以下なので、結果がVaRより小さくなることはありません。比率 ES / VaR は裾の重さを示します。正規分布では95%でおよそ1.2で、ファットテールのデータでは通常もっと大きくなります。
import numpy as np
def tail_returns(returns, confidence):
cutoff = np.quantile(returns, 1 - confidence)
return np.sort(returns[returns <= cutoff])
def historical_es(returns, confidence):
return float(-tail_returns(returns, confidence).mean())
rng = np.random.default_rng(5)
returns = rng.standard_t(4, size=1000) * 0.01
for confidence in (0.95, 0.99):
var = -np.quantile(returns, 1 - confidence)
es = historical_es(returns, confidence)
print(confidence, round(float(var), 5), round(es, 5), round(es / float(var), 3))正規分布の場合の公式
正規分布のリターンでは、裾の平均に閉じた式があります:ES = sigma * pdf(z) / (1 - confidence) - mu。z はVaRに使う分位点、pdf は z での標準正規密度です。計算は2行の関数になりますが、正確なのはデータが正規のときだけなので、ヒストリカルESとの比較は、データが正規性からどれだけ外れているかの手早い検査になります。
from statistics import NormalDist
def normal_es(mu, sigma, confidence):
z = NormalDist().inv_cdf(confidence)
return sigma * NormalDist().pdf(z) / (1 - confidence) - mu
for confidence in (0.95, 0.975, 0.99):
var = NormalDist().inv_cdf(confidence) # in standard deviations, mu = 0
es = normal_es(0.0, 1.0, confidence)
print(confidence, round(var, 3), round(es, 3), round(es / var, 3)) # ES/VaR: 1.254, 1.193, 1.146# The formula against a simulated tail
import numpy as np
from statistics import NormalDist
rng = np.random.default_rng(0)
sample = rng.normal(0.0, 0.01, size=1_000_000)
tail = sample[sample <= np.quantile(sample, 0.01)]
simulated = -tail.mean()
z = NormalDist().inv_cdf(0.99)
formula = 0.01 * NormalDist().pdf(z) / 0.01
print(round(float(simulated), 5), round(formula, 5)) # both close to 0.02665ESはバックテストも難しいVaRは超過日を数えて検証できます。ESは裾にあるわずかな観測に依存し、99%・250日なら2〜3個のリターンです。推定のノイズが大きく、より多くのデータかモデルが必要です。実務では両方を併せて報告し、VaRは閾値、ESはそれを超えた先の深刻さを示します。
- ESはVaRを超えた裾の平均損失。
(1 - confidence)分位点以下のリターンの平均にマイナスを付けたもの - VaRを下回ることはない。
ES / VaRは裾の重さを測り、正規分布の公式はsigma * pdf(z) / (1 - confidence) - mu - VaRは閾値、ESは深刻さとして報告する。ESは裾の観測が少ないためノイズが大きい
次はストレステストです。特定の極端なシナリオでポートフォリオがいくら失うかを問います。
ラボ:採点される内容
tail_returnsで最悪の日々を取り出す25点historical_esで裾の平均を取る25点normal_esで公式から求める25点es_var_ratioで裾と閾値を比べる25点