Was der VaR nicht sagt
Ein 99-%-VaR von 3 % sagt, dass Sie an 99 von 100 Tagen weniger als 3 % verlieren. Über den 100. Tag sagt er nichts: Es können 3,1 % oder 30 % sein. Der Expected Shortfall (ES, CVaR) schließt die Lücke. Er ist der durchschnittliche Verlust an den Tagen, an denen der VaR überschritten wird, und beschreibt so die Form des Tails. Als Mittelwert ändert er sich glatt und belohnt im Gegensatz zum VaR immer Diversifikation; deshalb ging Basel beim Marktrisiko vom VaR zum ES über.
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 daysDen Tail mitteln
Historisch ist der ES einfach: Man wählt die Renditen auf oder unter der VaR-Schwelle, mittelt sie und kehrt das Vorzeichen um. Das Ergebnis ist nie kleiner als der VaR, weil jede Tail-Rendite mindestens so schlecht ist wie die Schwelle. Das Verhältnis ES / VaR zeigt, wie schwer der Tail ist. Bei einer Normalverteilung beträgt es bei 95 % etwa 1,2, Daten mit Fat Tails zeigen meist ein größeres Verhältnis.
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))Eine Formel für den Normalfall
Für eine normalverteilte Rendite hat der Mittelwert des Tails eine geschlossene Form: ES = sigma * pdf(z) / (1 - confidence) - mu, mit dem für den VaR verwendeten Quantil z und der Standardnormaldichte pdf bei z. Die Rechnung wird zu einer Zweizeilenfunktion und ist nur bei normalen Daten exakt; der Vergleich mit dem historischen ES ist daher ein schneller Test, wie weit die Daten von der Normalität abweichen.
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.02665- ES ist der durchschnittliche Verlust im Tail jenseits des VaR: minus der Mittelwert der Renditen auf oder unter dem
(1 - confidence)-Quantil. - Er liegt nie unter dem VaR;
ES / VaRmisst das Gewicht des Tails, und die Normalformel istsigma * pdf(z) / (1 - confidence) - mu. - Berichten Sie den VaR als Schwelle und den ES als Schwere; der ES ist verrauschter, weil er auf wenigen Tail-Beobachtungen beruht.
Als Nächstes: Stresstests, fragen, was ein Portfolio in einem bestimmten extremen Szenario verlieren würde.
Lab: Was bewertet wird
- Mit
tail_returnsdie schlimmsten Tage isolieren25 Pkt. - Mit
historical_esden Tail mitteln25 Pkt. - Mit
normal_esper Formel bestimmen25 Pkt. - Mit
es_var_ratioTail und Schwelle vergleichen25 Pkt.