03 — Marginal and Component Risk¶
Learning objectives¶
Compute marginal risk contribution (MCR) and component risk contribution
(CR) by hand on a 2-asset example, then on a realistic 4-asset
portfolio; confirm the identity sum(CR) = sigma_p; explain why capital
weight and risk weight can differ sharply.
Free learning pack¶
- MIT OCW Portfolio Theory I–III https://ocw.mit.edu/courses/15-401-finance-theory-i-fall-2008/pages/video-lectures-and-slides/portfolio-theory/
- CFA Portfolio Risk and Return Part I https://www.cfainstitute.org/insights/professional-learning/refresher-readings/2026/portfolio-risk-return-part-1
resources/portfolio_foundations.mdreference/concepts/risk_contribution.md
Do not search for more material until these are insufficient.
PREDICT¶
All capital weights in a 4-asset portfolio are 25%. Must all risk shares also be 25%? If one asset has much higher volatility and is positively correlated with everything else, will its risk share be above or below 25%?
HAND CALCULATION¶
Two uncorrelated assets, equal 50/50 weights, Sigma = [[0.04, 0], [0, 0.01]]
(asset 1 variance 0.04, asset 2 variance 0.01, zero covariance).
sigma_p = sqrt(w' Sigma w), MCR = (Sigma @ w) / sigma_p,
CR = w * MCR. Work this out by hand before the next cell — with zero
correlation, Sigma @ w is just [w1*var1, w2*var2].
import numpy as np
w2 = np.array([0.5, 0.5])
cov2 = np.array([[0.04, 0.0], [0.0, 0.01]])
# MANUAL FIRST:
sigma_p_2 = None
mcr_2 = None
cr_2 = None
print(sigma_p_2, mcr_2, cr_2)
# CHECK (uncomment after your attempt):
# assert np.isclose(sigma_p_2, 0.1118, atol=1e-3)
# assert np.allclose(mcr_2, [0.1789, 0.0447], atol=1e-3)
# assert np.allclose(cr_2, [0.0894, 0.0224], atol=1e-3)
# assert np.isclose(cr_2.sum(), sigma_p_2)
Now the realistic case¶
Four assets, unequal capital weights all at 25%, unequal volatilities, and real correlation (including one negative pair).
weights = np.array([0.25, 0.25, 0.25, 0.25])
vols = np.array([0.18, 0.06, 0.10, 0.15])
corr = np.array([
[1.0, -0.2, 0.6, 0.2],
[-0.2, 1.0, 0.1, 0.0],
[0.6, 0.1, 1.0, 0.2],
[0.2, 0.0, 0.2, 1.0],
])
cov = np.outer(vols, vols) * corr
# MANUAL FIRST:
# calculate sigma_p, MCR, CR for this 4-asset portfolio.
sigma_p = None
mcr = None
cr = None
risk_share = None # cr / sigma_p, for comparing against the 25% capital weights
print("sigma_p:", sigma_p)
print("risk share:", risk_share)
# CHECK (uncomment after your attempt):
# assert np.isclose(sigma_p, 0.0805, atol=1e-3)
# assert np.isclose(cr.sum(), sigma_p)
# assert risk_share[0] > 0.25, "asset 0 (highest vol, positively correlated with most others) should carry MORE than its 25% capital weight in risk"
# assert risk_share[1] < 0.25, "asset 1 (lowest vol, negatively correlated with asset 0) should carry LESS"
Reference¶
reference/concepts/risk_contribution.md
Promote¶
Use src/pm/risk.py (portfolio_volatility, marginal_risk_contribution,
component_risk_contribution) only after your own implementation.
Test¶
pytest tests/test_risk.py
ORAL CHECK¶
Explain capital weight vs. risk weight to a PM without using matrix notation — use the actual risk shares you just computed as your example.
Try /tutor risk contribution for an adaptive walkthrough.