σμimplied vol · KKmax(S−K,0)udβρᵢⱼfat tailsPnL · drawdownyield · TdSₜ = μSₜ dt + σSₜ dWₜC = S·N(d₁) − K·e⁻ʳᵀ·N(d₂)σₚ² = wᵀΣwd₁ = [ln(S/K) + (r + σ²⁄2)T] ⁄ σ√T∂V/∂t + ½σ²S²·∂²V/∂S² + rS·∂V/∂S − rV = 0Sharpe = (Rₚ − R_f) ⁄ σₚVaR₉₉ = μ − 2.33σρ = Cov(X,Y) ⁄ σₓσᵧE[Rₚ] = Σ wᵢ·E[Rᵢ]φ(x) = e^(−x²/2) ⁄ √2πE[Rᵢ] = R_f + βᵢ(E[Rₘ] − R_f)dXₜ = θ(μ − Xₜ)dt + σ dWₜσ²ₜ = ω + α·ε²ₜ₋₁ + β·σ²ₜ₋₁C − P = S − K·e⁻ʳᵀΔ = ∂V/∂S Γ = ∂²V/∂S²p = (e^(rΔt) − d) ⁄ (u − d)rₜ = ln(Pₜ ⁄ Pₜ₋₁)E[Xₜ₊₁ | ℱₜ] = Xₜf* = (bp − q) ⁄ bz = (x − μ) ⁄ σP(N=k) = λᵏe^(−λ) ⁄ k!Θ = −∂V/∂Tν = S·φ(d₁)·√Tmin wᵀΣw s.t. wᵀμ = μ*∫Σ∂∞λ
σμimplied vol · KKmax(S−K,0)udβρᵢⱼfat tailsPnL · drawdownyield · TdSₜ = μSₜ dt + σSₜ dWₜC = S·N(d₁) − K·e⁻ʳᵀ·N(d₂)σₚ² = wᵀΣwd₁ = [ln(S/K) + (r + σ²⁄2)T] ⁄ σ√T∂V/∂t + ½σ²S²·∂²V/∂S² + rS·∂V/∂S − rV = 0Sharpe = (Rₚ − R_f) ⁄ σₚVaR₉₉ = μ − 2.33σρ = Cov(X,Y) ⁄ σₓσᵧE[Rₚ] = Σ wᵢ·E[Rᵢ]φ(x) = e^(−x²/2) ⁄ √2πE[Rᵢ] = R_f + βᵢ(E[Rₘ] − R_f)dXₜ = θ(μ − Xₜ)dt + σ dWₜσ²ₜ = ω + α·ε²ₜ₋₁ + β·σ²ₜ₋₁C − P = S − K·e⁻ʳᵀΔ = ∂V/∂S Γ = ∂²V/∂S²p = (e^(rΔt) − d) ⁄ (u − d)rₜ = ln(Pₜ ⁄ Pₜ₋₁)E[Xₜ₊₁ | ℱₜ] = Xₜf* = (bp − q) ⁄ bz = (x − μ) ⁄ σP(N=k) = λᵏe^(−λ) ⁄ k!Θ = −∂V/∂Tν = S·φ(d₁)·√Tmin wᵀΣw s.t. wᵀμ = μ*∫Σ∂∞λ
uantCraft AcademySkills
Ctrl K
AO
+1.2k Learners
LoginJoin ↗
Skills
AO
+1.2k Learners
Login
  • English
  • Français
  • Español
  • العربية
  • Português
  • Deutsch
  • 日本語
  • 中文
Python skill›Module 7 · Alpha research & risk›Topic 7.5·~50 minAdvanced

Parametric VaR

Skill positionModule 7 of 11 · Topic 5 of 9
7.5 Parametric VaR
—Lab score— / 100Total XP—Streak—

A formula instead of a history

Historical VaR reads the loss from past returns. Parametric VaR assumes returns follow a normal distribution and needs only two numbers, the mean and the standard deviation. The loss exceeded with probability 1 - confidence is then z * sigma - mu, where z is the standard normal quantile (1.645 at 95%, 2.326 at 99%). It is fast, smooth, and extends easily to a whole portfolio, which is why it was the industry standard for decades.

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.09

From one asset to a portfolio, and to ten days

For a portfolio, the mean is weights @ mu and the standard deviation is sqrt(weights @ cov @ weights), the quantities from the portfolio-variance topic. Diversification shows up directly: low correlations shrink the standard deviation and the VaR. For a longer horizon, the mean grows with the number of days and the standard deviation with its square root, so a 10-day VaR is 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

Where the normal assumption fails

Real returns have fat tails: extreme days are much more frequent than a normal distribution allows. The parametric VaR then looks too small, most of all at high confidence. Comparing it with the historical VaR on the same data shows the gap: positive means the normal model understates the risk. At 95% the two are often close, at 99% they can differ a lot.

# 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))
Choose the method for the questionParametric VaR is fine for a quick, smooth number and for portfolios of linear positions. It is a poor guide for options, where returns are not linear in the risk factors, and for tails. Many desks compute several VaRs, parametric, historical and Monte Carlo, and treat a large disagreement as a warning about the model.
  • Parametric VaR = z * sigma - mu with z = NormalDist().inv_cdf(confidence): only the mean and the standard deviation are needed.
  • A portfolio uses weights @ mu and sqrt(weights @ cov @ weights); a horizon scales the mean by days and the volatility by sqrt(days).
  • Real returns have fat tails, so compare with the historical VaR: a positive gap means the normal model understates the risk.

Next up: expected shortfall, the average loss on the days when the VaR is exceeded.

Lab · what's graded

  • Turn two numbers into a limit with normal_var25 pts
  • Cover a whole portfolio with portfolio_normal_var25 pts
  • Look further ahead with horizon_var25 pts
  • Measure the fat tails with normality_gap25 pts
Explorer · lab-97-1
▾ lab-97-1
…
bash · in-browser
learner@quantcraft:~/lab-97-1$
Python not loaded yetPythonLn 1, Col 1Spaces: 4UTF-8

Reset this topic?

Everything in lab-97-1 goes back to the starter files and the terminal is cleared. Your current solution will be lost — this can't be undone.

Will be reset
  • main.py → starter code
  • Terminal history and test output
  • Unsubmitted changes
Will be kept
  • Best lab score
  • Your XP and streak
  • Lecture progress and notes

Log in to run your code.

Every lecture is free to read. Create an account or log in to run code, run tests and submit this lab.

  • In-browser IDE with a bash terminal — nothing to install
  • Instant grading, XP and your place on the skill
Create an accountLog in
OR
Continue with GitHub