Skip to content

Risk budgeting: ERC, VaR, and ES contributions

Translated from Examples/rpb/test_erc1.m, which reproduces Tables 2.2, 2.3, and 2.4 (pages 81–82) of Roncalli, T. (2013), Introduction to Risk Parity and Budgeting.

Three assets with given volatilities and correlations; a fixed (non-equal-risk) weight vector x = [0.50, 0.20, 0.30]. The example computes the risk contribution decomposition under three different risk measures: plain volatility, 99% VaR, and 99% Expected Shortfall.

import numpy as np
from quanttoolbox.linalg.special_matrices import xpnd
from quanttoolbox.stats.moments import corr_to_cov
from quanttoolbox.portfolio.risk_budgeting import (
    risk_contribution,
    risk_contribution_var,
    risk_contribution_es,
)

sigma = np.array([0.30, 0.20, 0.15])
rho_vech = np.array([1.00, 0.80, 1.00, 0.50, 0.30, 1.00])
rho = xpnd(rho_vech, method=1)
cov_matrix = corr_to_cov(sigma, rho)

x = np.array([0.50, 0.20, 0.30])

# --- Volatility-based risk contribution ---
rc_vol = risk_contribution(x, cov_matrix)
print("portfolio volatility:", round(100 * rc_vol.risk, 2))
print("pct risk contribution:", np.round(100 * rc_vol.pct_risk_contribution, 2))

# --- VaR-based (99% confidence) ---
mu = np.zeros(3)
alpha = 0.99
rc_var = risk_contribution_var(x, cov_matrix, mu, alpha)
print("99% VaR:", round(100 * rc_var.risk, 2))

# --- ES-based (99% confidence) ---
rc_es = risk_contribution_es(x, cov_matrix, mu, alpha)
print("99% ES:", round(100 * rc_es.risk, 2))

Output:

portfolio volatility: 20.87
pct risk contribution: [70.43 15.93 13.64]
99% VaR: 48.55
99% ES: 55.62

The 20.87% portfolio volatility and the [70.43, 15.93, 13.64] percent risk contribution split match the published Table 2.2 values exactly.

Equal Risk Contribution portfolio

The same covariance matrix, but solving for the weights that make each asset's risk contribution equal (rather than decomposing a given, unequal weight vector):

from quanttoolbox.portfolio.risk_budgeting import erc_portfolio

erc = erc_portfolio(cov_matrix)
print("ERC weights:", np.round(erc.weights, 4))
print("pct risk contribution:", np.round(100 * erc.pct_risk_contribution, 2))

Output:

ERC weights: [0.1969 0.3244 0.4787]
pct risk contribution: [33.33 33.33 33.33]

By construction, all three assets now contribute exactly one-third of total portfolio risk.