Eine Formel statt einer Historie
Der historische VaR liest den Verlust aus vergangenen Renditen ab. Der parametrische VaR nimmt eine Normalverteilung der Renditen an und braucht nur zwei Zahlen, Mittelwert und Standardabweichung. Der mit Wahrscheinlichkeit 1 - confidence überschrittene Verlust ist dann z * sigma - mu, mit dem Standardnormal-Quantil z (1,645 bei 95 %, 2,326 bei 99 %). Er ist schnell, glatt und lässt sich leicht auf ein ganzes Portfolio ausdehnen, weshalb er jahrzehntelang Industriestandard war.
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.09Vom Einzelwert zum Portfolio und auf zehn Tage
Für ein Portfolio ist der Mittelwert weights @ mu und die Standardabweichung sqrt(weights @ cov @ weights), die Größen aus dem Thema Portfoliovarianz. Diversifikation zeigt sich direkt: niedrige Korrelationen senken Standardabweichung und VaR. Für einen längeren Horizont wächst der Mittelwert mit der Tageszahl und die Standardabweichung mit ihrer Wurzel, ein 10-Tages-VaR ist also 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 figureWo die Normalverteilungsannahme versagt
Echte Renditen haben Fat Tails: Extremtage sind viel häufiger, als eine Normalverteilung erlaubt. Der parametrische VaR wirkt dann zu klein, am meisten bei hohem Konfidenzniveau. Der Vergleich mit dem historischen VaR auf denselben Daten zeigt die Lücke: positiv heißt, das Normalmodell unterschätzt das Risiko. Bei 95 % liegen beide oft nah beieinander, bei 99 % können sie stark abweichen.
# 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))- Parametrischer VaR =
z * sigma - mumitz = NormalDist().inv_cdf(confidence): nur Mittelwert und Standardabweichung sind nötig. - Ein Portfolio nutzt
weights @ muundsqrt(weights @ cov @ weights); ein Horizont skaliert den Mittelwert mitdaysund die Volatilität mitsqrt(days). - Echte Renditen haben Fat Tails: Vergleichen Sie mit dem historischen VaR; eine positive Lücke heißt, das Normalmodell unterschätzt das Risiko.
Als Nächstes: Expected Shortfall, der durchschnittliche Verlust an den Tagen, an denen der VaR überschritten wird.
Lab: Was bewertet wird
- Mit
normal_varaus zwei Zahlen ein Limit machen25 Pkt. - Mit
portfolio_normal_varein ganzes Portfolio abdecken25 Pkt. - Mit
horizon_varweiter vorausschauen25 Pkt. - Mit
normality_gapdie Fat Tails messen25 Pkt.