Skip to content

Three Ways to Calculate Portfolio Value at Risk

Original question

How can I calculate historical, parametric, and Monte Carlo Value at Risk for a portfolio of multiple assets?
Set the holding period, confidence level and current exposures, then estimate the corresponding quantile of portfolio losses. Historical VaR replays observed joint moves. Parametric VaR uses a formula from a fitted distribution. Monte Carlo VaR simulates joint moves and revalues the portfolio under each scenario. The three calculations can share the same holdings and still produce different estimates because they make different assumptions about the distribution of returns.

Same exposure and horizon, three ways to generate losses

Let VV be current portfolio value, ww current weights and RR the vector of one-day simple returns. For linear exposures the one-day loss is L=−Vw⊤RL=-Vw^\top R. Historical simulation applies this formula to each aligned historical return row and takes the empirical confidence-α\alpha quantile. Keep the portfolio fixed at today's weights when the question concerns today's portfolio.

If R∼N(μ,Σ)R\sim N(\mu,\Sigma), then the loss is normal with mean −Vw⊤μ-Vw^\top\mu and variance V2w⊤ΣwV^2w^\top\Sigma w. Thus
VaRα=V[−w⊤μ+Φ−1(α)w⊤Σw].\mathrm{VaR}_\alpha=V\bigl[-w^\top\mu+\Phi^{-1}(\alpha)\sqrt{w^\top\Sigma w}\bigr].
For simulation, draw rows from a joint return or risk-factor model, compute a loss for each row and take the same empirical quantile. For an options portfolio, update spot, implied volatility and other required inputs jointly, then reprice the options under each scenario.
Consider a portfolio worth 1 million dollars invested 60% in equities and 40% in bonds. Suppose daily volatilities are 1.2% and 0.6%, their correlation is 0.25, and daily drift is zero. Portfolio volatility is about 0.814% a day. Multiplying by the standard normal 99th percentile of about 2.326 gives a one-day VaR of roughly 18,934 dollars. Compare that result with the quantile of historical portfolio losses over a chosen window. The historical estimate can be lower or higher, depending on whether that sample contains severe joint moves.
An apples-to-apples comparison uses today's weights in all three calculations. Historical simulation applies each old vector of market moves to those weights. It does not use the returns earned by whatever portfolio you happened to hold on those old dates. A normal Monte Carlo draw with the same mean and covariance should agree with the normal formula within simulation error. A persistent difference then points to units, quantile conventions or the valuation code. A materially different scenario model is a separate comparison.

Estimate all three on the same inputs

This example uses synthetic history and compares fitted normal and Student-tt scenarios. The Student-tt draws preserve the fitted covariance in expectation using the scale factor shown.
import numpy as np from scipy.stats import norm rng = np.random.default_rng(1729) vol = np.array([0.012, 0.006]) cov = np.outer(vol, vol) * np.array([[1.0, 0.25], [0.25, 1.0]]) history = rng.multivariate_normal([0, 0], cov, size=1_500) value, alpha = 1_000_000, 0.99 weights = np.array([0.6, 0.4]) mu_hat, cov_hat = history.mean(axis=0), np.cov(history, rowvar=False) historical = np.quantile(-value * (history @ weights), alpha) parametric = value * (-weights @ mu_hat + norm.ppf(alpha) * np.sqrt(weights @ cov_hat @ weights)) normal = rng.multivariate_normal([0, 0], cov_hat, size=100_000) df = 5 tail_scale = np.sqrt((df - 2) / rng.chisquare(df, size=len(normal))) student = mu_hat + normal * tail_scale[:, None] for name, draws in [("normal", normal + mu_hat), ("Student t", student)]: print(name, round(np.quantile(-value * (draws @ weights), alpha))) print("historical", round(historical), "parametric", round(parametric))

Interactive example

Compare a loss threshold with losses beyond it

A synthetic set contains 100 equally likely losses, including one large loss. Expected shortfall averages the worst observations with fractional weight at a boundary.

95% VaR 4.18% · ES 7.75% · worst 20%
Sorted synthetic losses and the value-at-risk threshold01020125.850.575.3100Sorted observationLoss (%)
  • Sorted losses
  • VaR threshold

References and next steps