SigmaField
Mathematical Foundations for Quant Finance
0 of 256 lessons
      1. 5.5.1 Joint, marginal and conditional distributionsConcept · 30 min
      2. 5.5.2 Covariance, correlation and independenceConcept · 35 min
      3. 5.5.3 The multivariate normal distributionConcept · 35 min
      4. 5.5.4 Practice set: joint distributionsPractice set · 25 min
      5. 5.5.5 Case study: portfolio risk and conditional expectationCase study · 35 min

Level 5, Probability and statistics 5.5 Joint distributions Lesson 5 of 5

Case studyLesson 5.5.5 in Level 5, Probability and statistics. About 35 minutes.

Case study: portfolio risk and conditional expectation

The fund's variance, from the rules

The data are case study 3.2.5's, invented but typical, in annual decimals. The expected returns are μ=(0.06,0.08,0.11)\boldsymbol{\mu} = (0.06, 0.08, 0.11) and the volatilities 0.12, 0.18 and 0.25. The correlations are ρAB=0.3\rho_{AB} = 0.3, ρAC=0.1\rho_{AC} = 0.1 and ρBC=0.5\rho_{BC} = 0.5, and the weights w=(0.5,0.3,0.2)\mathbf{w} = (0.5, 0.3, 0.2).

Before the year starts, its returns are random. Write them as a random vector R=(RA,RB,RC)\mathbf{R} = (R_A, R_B, R_C), in capitals; case study 3.1.4 wrote one year's values as r\mathbf{r}.

Its mean is μ\boldsymbol{\mu}, and its covariance matrix Σ\Sigma has entries Σij=Cov⁡(Ri,Rj)=ρijσiσj\Sigma_{ij} = \operatorname{Cov}(R_i, R_j) = \rho_{ij}\sigma_i\sigma_j (lesson 5.5.2). It is the matrix case study 3.2.5 built from the volatilities and correlations.

The fund's return is Rp=w⊤R=wARA+wBRB+wCRCR_p = \mathbf{w}^\top\mathbf{R} = w_AR_A + w_BR_B + w_CR_C (case study 3.1.4). Its mean follows from linearity (lesson 5.3.2): E[Rp]=w⊤μ=0.076\mathbb{E}[R_p] = \mathbf{w}^\top\boldsymbol{\mu} = 0.076. Its variance takes three steps.

  1. 1

    Var⁡(Rp)=Cov⁡(Rp, Rp)\operatorname{Var}(R_p) = \operatorname{Cov}(R_p,\ R_p)

  2. 2

    Cov⁡(∑iwiRi, ∑jwjRj)=∑i∑jwiwjCov⁡(Ri,Rj)\operatorname{Cov}\big(\sum_i w_iR_i,\ \sum_j w_jR_j\big) = \sum_i\sum_j w_iw_j\operatorname{Cov}(R_i, R_j)

  3. 3

    ∑i∑jwiwj Σij=w⊤Σ w\sum_i\sum_j w_iw_j\,\Sigma_{ij} = \mathbf{w}^\top\Sigma\,\mathbf{w}

Rule: Mean and variance of a portfolio

E[w⊤R]=w⊤μVar⁡(w⊤R)=w⊤Σ w\begin{gathered} \mathbb{E}[\mathbf{w}^\top\mathbf{R}] = \mathbf{w}^\top\boldsymbol{\mu} \\[4pt] \operatorname{Var}(\mathbf{w}^\top\mathbf{R}) = \mathbf{w}^\top\Sigma\,\mathbf{w} \end{gathered}

R\mathbf{R} is the random vector of the assets' returns over one period, with mean μ\boldsymbol{\mu} and covariance matrix Σ\Sigma. It is lesson 5.5.3's rule AΣA⊤A\Sigma A^\top with the single row A=w⊤A = \mathbf{w}^\top.

The double sum has nine terms. The three on the diagonal, each asset with itself, add up to 0.009016; the six off it, each pair twice, add 0.005244. The total is 0.01426, a volatility of 11.94%: case study 3.2.5's numbers, now derived.

Two assets: case study 1.2.4. With weight ww in A and 1−w1 - w in B, lesson 5.5.2's rule for Var⁡(aX+bY)\operatorname{Var}(aX + bY) gives

σp2=w2σA2+(1−w)2σB2+2w(1−w)ρ σAσB\begin{aligned} \sigma_p^2 = {} & w^2\sigma_A^2 + (1 - w)^2\sigma_B^2 \\ & + 2w(1 - w)\rho\,\sigma_A\sigma_B \end{aligned}

with Cov⁡(RA,RB)=ρσAσB\operatorname{Cov}(R_A, R_B) = \rho\sigma_A\sigma_B. That is the formula case study 1.2.4 took as given.

Its factor 2 counts Cov⁡(RA,RB)\operatorname{Cov}(R_A, R_B) and Cov⁡(RB,RA)\operatorname{Cov}(R_B, R_A). With that case study's σA=0.2\sigma_A = 0.2, σB=0.3\sigma_B = 0.3 and ρ=0.2\rho = 0.2, the minimum-variance weight 0.7358 still gives a variance of 0.0326.

What the formula needs, and what it does not

The steps used only expectations and covariances. The formula needs no normal distribution and no independence: any returns with this Σ\Sigma give a variance of 0.01426.

A simulation shows it. Draw 200,000 years as μ+LU\boldsymbol{\mu} + L\mathbf{U}, with LL the Cholesky factor of Σ\Sigma (case study 3.7.4) and U\mathbf{U} normal.

Then do it again with U\mathbf{U} uniform on [−3,3][-\sqrt{3}, \sqrt{3}], which also has mean 0 and variance 1. By lesson 5.5.3's rule both have covariance matrix LL⊤=ΣLL^\top = \Sigma.

In this seeded run both give a sample variance close to 0.01426. Their chances of a loss beyond 10% differ, though: 7.03% with normal draws and 7.55% with uniform ones. The variance comes from Σ\Sigma alone; a probability needs the whole distribution.

With jointly normal returns, Rp∼N(0.076, 0.01426)R_p \sim \mathcal{N}(0.076,\ 0.01426) (lesson 5.5.3). So P(Rp<−0.10)=N(−0.10−0.0760.119415)=N(−1.4738)=0.0703P(R_p < -0.10) = N\big(\frac{-0.10 - 0.076}{0.119415}\big) = N(-1.4738) = 0.0703, matching the normal draws.

Try it

The first lines print the nine terms wiwjΣijw_iw_j\Sigma_{ij}, their diagonal and off-diagonal totals, and w⊤Σw\mathbf{w}^\top\Sigma\mathbf{w}.

Then 200,000 seeded years, from normal and from uniform U\mathbf{U}, both mixed by U @ L.T. Each line prints the sample mean, variance and volatility of RpR_p, and the share of years below −10%-10\%. The last line is the exact normal chance.

⌘/Ctrl + Enter runs the cell

From months to a year: when √12 works

Log returns add across time: a year's log return is the sum of its twelve monthly ones (case study 1.4.7). Lesson 5.5.2's rule for a sum of many variables then gives

Var⁡(r1+⋯+r12)=∑tVar⁡(rt)+2∑s<tCov⁡(rs,rt)\begin{gathered} \operatorname{Var}(r_1 + \cdots + r_{12}) \\ = \textstyle\sum_t \operatorname{Var}(r_t) + 2\sum_{s < t}\operatorname{Cov}(r_s, r_t) \end{gathered}

with rtr_t the log return of month tt. If the months are uncorrelated, every covariance is 0 and only the twelve variances are left. Independence is enough for that, since independent variables are uncorrelated (lesson 5.5.2).

Rule: Scaling risk over time

Var⁡(r1+⋯+rn)=n σ2σtotal=n σ\begin{gathered} \operatorname{Var}(r_1 + \cdots + r_n) = n\,\sigma^2 \\[4pt] \sigma_{\text{total}} = \sqrt{n}\,\sigma \end{gathered}

For nn uncorrelated periods, each with variance σ2\sigma^2. The mean grows nn times and the volatility only n\sqrt{n} times. Without the zero covariances, the rule fails.

This is what case studies 3.2.5 and 3.6.5 assumed.

  • Months to a year. The fund's monthly volatility is 3.4472%, from Σ/12\Sigma/12; times 12\sqrt{12} it is the annual 11.94% (case study 3.2.5).
  • Days to a year. The level component of case study 3.6.5's yield changes has a daily volatility of 10.52 bp. Yield changes add up exactly over days, so with uncorrelated days it is 10.52252=167.010.52\sqrt{252} = 167.0 bp a year.

When neighbouring months are correlated

Suppose each month's return has correlation ρ1\rho_1 with the month before, and none with months further apart: a lag-one correlation. Twelve months have eleven neighbouring pairs, each counted twice, so the variance becomes σ2(12+22ρ1)\sigma^2(12 + 22\rho_1).

ρ1\rho_1−0.2-0.200.2
12+22ρ112 + 22\rho_17.61216.4
annual volatility9.50%11.94%13.96%

With ρ1=0.2\rho_1 = 0.2, the 12\sqrt{12} rule reports 11.94% when the risk is 13.96%: 14.5% too low. Positive lag-one correlation is typical of smoothed or stale prices, such as appraisal values of property and thinly traded assets. A negative one, as in a series that tends to reverse its moves, makes 12\sqrt{12} overstate the risk.

Volatility of the total over nn months. With uncorrelated months it grows like n\sqrt{n}; a positive lag-one correlation makes it grow faster, a negative one slower. The dots mark a year.

Try it

The first lines give the monthly volatility and the two scalings, the second from case study 3.6.5's own data and seed.

Then 20,000 seeded years of 12 months with a lag-one correlation of 0.2, made as c (et+θet−1)c\,(e_t + \theta e_{t-1}) with independent normal ete_t. In this run the sample lag-one correlation is 0.199 and the annual volatility 14.0%, against the formula's 13.96%.

⌘/Ctrl + Enter runs the cell
Check yourself
A fund's daily returns have volatility 1% and a lag-one correlation of −0.1-0.1. Its risk report multiplies by 252\sqrt{252}. What is wrong with the annual figure?

What B's return says about C's

Now take two assets, B and C, and treat their returns as jointly normal (lesson 5.5.3). Their means are 8% and 11%, their volatilities 18% and 25%, and ρ=0.5\rho = 0.5. Build them from independent standard normals Z1Z_1 and Z2Z_2 with the Cholesky factor, B first, as case study 4.7.5 built A and B:

RB=μB+σBZ1RC=μC+ρ σCZ1+σC1−ρ2 Z2\begin{gathered} R_B = \mu_B + \sigma_B Z_1 \\ R_C = \mu_C + \rho\,\sigma_C Z_1 + \sigma_C\sqrt{1 - \rho^2}\,Z_2 \end{gathered}

Learning that RB=bR_B = b fixes Z1=b−μBσBZ_1 = \frac{b - \mu_B}{\sigma_B}. It says nothing about Z2Z_2, which is independent of Z1Z_1, so Z2Z_2 is still standard normal (lesson 5.5.1). Given RB=bR_B = b, then, RCR_C is a known number plus σC1−ρ2 Z2\sigma_C\sqrt{1 - \rho^2}\,Z_2.

Rule: Conditional mean of a bivariate normal

E[RC∣RB=b]=μC+ρ σCσB (b−μB)standard deviation: σC1−ρ2\begin{gathered} \mathbb{E}[R_C \mid R_B = b] \\ = \mu_C + \rho\,\frac{\sigma_C}{\sigma_B}\,(b - \mu_B) \\[6pt] \text{standard deviation: } \sigma_C\sqrt{1 - \rho^2} \end{gathered}

Given RB=bR_B = b, RCR_C is normal with this mean and standard deviation. The mean is a straight line in bb through (μB,μC)(\mu_B, \mu_C), with slope ρσCσB=Cov⁡(RB,RC)Var⁡(RB)\rho\frac{\sigma_C}{\sigma_B} = \frac{\operatorname{Cov}(R_B, R_C)}{\operatorname{Var}(R_B)}. The spread does not depend on bb.

Here the slope is 0.5×0.250.18=0.69440.5 \times \frac{0.25}{0.18} = 0.6944, and the conditional volatility is 0.250.75=21.65%0.25\sqrt{0.75} = 21.65\%, against 25% before anything is known.

B's year, bb−30%-30\%−10%-10\%8%20%40%
E[RC∣RB=b]\mathbb{E}[R_C \mid R_B = b]−15.39%-15.39\%−1.5%-1.5\%11%19.33%33.22%
minus one conditional sd−37.04%-37.04\%−23.15%-23.15\%−10.65%-10.65\%−2.32%-2.32\%11.57%
plus one conditional sd6.26%20.15%32.65%40.98%54.87%

At b=8%b = 8\%, B's mean, the line gives C's mean, 11%. Each point that B's return lies above or below 8% moves the expected C by 0.6944 points the same way.

Probabilities shift too. Unconditionally, C loses money in N(−0.110.25)=33.0%N\big(\frac{-0.11}{0.25}\big) = 33.0\% of years. Given that B lost 10%, the chance is N(0.0150.2165)=52.76%N\big(\frac{0.015}{0.2165}\big) = 52.76\%.

The hedge ratio again

The slope Cov⁡(RB,RC)Var⁡(RB)=0.6944\frac{\operatorname{Cov}(R_B, R_C)}{\operatorname{Var}(R_B)} = 0.6944 is case study 3.5.5's hedge ratio for C hedged with B alone. That case study found it as the coefficient of a projection, with R2=ρ2=0.25R^2 = \rho^2 = 0.25.

Probability adds a reading. Selling 0.6944 of B per dollar of C leaves RC−0.6944RBR_C - 0.6944R_B, which is σC1−ρ2 Z2\sigma_C\sqrt{1 - \rho^2}\,Z_2 plus a constant.

That depends only on Z2Z_2, while RBR_B depends only on Z1Z_1, so the two are independent (lesson 5.5.1). What the hedge leaves, 21.65% a year, is a risk that B's moves cannot explain at all.

The same rule works for any pair. A's return given B's has slope ρABσAσB=0.3×0.120.18=0.2\rho_{AB}\frac{\sigma_A}{\sigma_B} = 0.3 \times \frac{0.12}{0.18} = 0.2.

The line does not reverse. Given C's return instead, E[RB∣RC=c]\mathbb{E}[R_B \mid R_C = c] has slope ρσBσC=0.36\rho\frac{\sigma_B}{\sigma_C} = 0.36, not 10.6944=1.44\frac{1}{0.6944} = 1.44. Both lines pass through the two means, but they differ unless ρ=±1\rho = \pm 1.

Estimated from simulated years

In the explorer, XX and YY stand for B's and C's returns, each measured from its mean in units of B's volatility, 18%. Set the standard deviation of YY to 1.4, close to σCσB=1.39\frac{\sigma_C}{\sigma_B} = 1.39. The dashed line of conditional means then has slope 0.7.

Turn on the fitted line and cut the number of draws to 20. Press "Draw again" a few times: a slope estimated from a short sample wanders.

Explore

Each dot is one draw of the pair (X, Y). Move ρ and watch the cloud stretch along a line. The dashed line is E[Y | X = x], the average of Y for each x; turn on the fitted line to see least squares estimate it from the dots.

−202−202
Correlation
true ρ = 0.5; sample r = 0.522
Slope of E[Y | X]
ρσ_Y/σ_X = 0.5 × 1 / 1 = 0.5
Explained
r² = 0.272: the share of the variance of Y that the line accounts for

With many draws the estimate is close. In 200,000 simulated years, the years with RBR_B within 2 points of each bb in the table have average RCR_C close to the line. The sample slope, sBCsB2\frac{s_{BC}}{s_B^2} (lesson 5.5.2), is 0.696.

Ten years of monthly returns are another matter. Case study 3.2.5's 120 months give a slope of 0.5109. Across 1,000 such ten-year samples, the middle 90% of the estimates runs from 0.5169 to 0.881.

Try it

The first lines compute the line's slope, the conditional volatility, the table and the two chances of a loss.

Then 200,000 seeded years of RBR_B and RCR_C. For each bb, the cell averages RCR_C over the years with RBR_B within 0.02 of bb. In this run every average is within 0.007 of the line.

The sample slope is 0.696 and the residuals have standard deviation 0.2164.

The last lines estimate the slope from case study 3.2.5's 120 months, then from 1,000 ten-year samples.

⌘/Ctrl + Enter runs the cell

When the line is not straight

The straight line is a property of the normal distribution. For other joint distributions, E[Y∣X=x]\mathbb{E}[Y \mid X = x] can bend: lesson 5.5.1's density x+yx + y had E[Y∣X=x]=x/2+1/3x+1/2\mathbb{E}[Y \mid X = x] = \frac{x/2 + 1/3}{x + 1/2}, a curve.

The slope Cov⁡Var⁡\frac{\operatorname{Cov}}{\operatorname{Var}} then still gives the best straight line through the cloud, the least-squares line of lesson 3.5.3. But it is only an approximation to the conditional mean. Module 5.10 studies such lines as regressions.

Check yourself
For B and C, the model gives E[RC∣RB=b]\mathbb{E}[R_C \mid R_B = b] with slope 0.6944. A colleague wants C's best guess for B's return and divides by 0.6944, giving a slope of 1.44. What is the right slope?

On the desk

Exercise

Conditional means and scaled volatility

Write two functions, with NumPy.

  1. conditional_normal(mu, cov, target, given, value) returns (mean, sd) for XtargetX_{\text{target}} given Xgiven=X_{\text{given}} = value. Here X\mathbf{X} is jointly normal with mean mu and covariance matrix cov. target and given are indices, such as 2 and 1 for C given B.
  2. annual_vol(period_vol, periods, lag1=0.0) returns the volatility of a sum of periods returns, each with volatility period_vol. Each has correlation lag1 with the one before and no other correlations, so the answer is σn+2(n−1)ρ1\sigma\sqrt{n + 2(n - 1)\rho_1}.

In conditional_normal, raise ValueError if cov is not square and symmetric or an index is out of range. Raise it too if target == given, or if a variance is not positive.

In annual_vol, raise it if periods is below 1 or period_vol is negative. Raise it too if lag1 is outside [−1,1][-1, 1], or if the variance would be negative.

The tests use the three assets, the hook's −1.5%-1.5\% and the reverse slope 0.36. They also use case study 3.2.5's monthly volatility, case study 3.6.5's 10.52 bp a day, and each bad input.

See the tests
import math

import numpy as np

MU = np.array([0.06, 0.08, 0.11])
VOLS = np.array([0.12, 0.18, 0.25])
CORR = np.array([[1.0, 0.3, 0.1], [0.3, 1.0, 0.5], [0.1, 0.5, 1.0]])
SIGMA = np.diag(VOLS) @ CORR @ np.diag(VOLS)


def _raises(f, *args, what=""):
    try:
        f(*args)
    except ValueError:
        return
    raise AssertionError(f"expected ValueError: {what}")


def test_hook():
    """If B returns −10%, C's conditional mean is −1.5% with standard deviation about 21.65%"""
    expect_close(conditional_normal(MU, SIGMA, 2, 1, -0.10), (-0.015, 0.25 * math.sqrt(0.75)), what="conditional_normal(C given B = -0.10)")


def test_at_the_mean():
    """Given B at its mean of 8%, C's conditional mean is its own mean, 11%"""
    expect_close(conditional_normal(MU, SIGMA, 2, 1, 0.08)[0], 0.11, what="mean of C given B = 0.08")


def test_slope():
    """The conditional mean of C moves 0.6944 points per point of B"""
    up = conditional_normal(MU, SIGMA, 2, 1, 0.20)[0]
    down = conditional_normal(MU, SIGMA, 2, 1, 0.10)[0]
    expect_close((up - down) / 0.10, 0.5 * 0.25 / 0.18, what="slope of C on B")


def test_reverse():
    """B given C has slope 0.36, not 1 / 0.6944, and standard deviation 18% × √0.75"""
    up = conditional_normal(MU, SIGMA, 1, 2, 0.21)
    down = conditional_normal(MU, SIGMA, 1, 2, 0.11)
    expect_close((up[0] - down[0]) / 0.10, 0.36, what="slope of B on C")
    expect_close(up[1], 0.18 * math.sqrt(0.75), what="standard deviation of B given C")


def test_a_given_b():
    """A given B = 20% has mean 8.4%: slope 0.2"""
    expect_close(conditional_normal(MU, SIGMA, 0, 1, 0.20)[0], 0.084, what="mean of A given B = 0.20")


def test_bad_cov():
    """A non-symmetric covariance matrix, a bad index or target equal to given raises ValueError"""
    bad = SIGMA.copy()
    bad[0, 1] = 0.0648
    _raises(conditional_normal, MU, bad, 0, 1, 0.1, what="a non-symmetric covariance matrix")
    _raises(conditional_normal, MU, SIGMA, 3, 1, 0.1, what="index 3 with three assets")
    _raises(conditional_normal, MU, SIGMA, 1, 1, 0.1, what="target equal to given")
    _raises(conditional_normal, MU, SIGMA[:2, :2], 0, 1, 0.1, what="a 2 x 2 matrix with three means")


def test_months():
    """The fund's monthly volatility 3.4472%, over 12 uncorrelated months, is 11.94% a year"""
    monthly = math.sqrt(np.array([0.5, 0.3, 0.2]) @ (SIGMA / 12) @ np.array([0.5, 0.3, 0.2]))
    expect_close(annual_vol(monthly, 12), 0.11941524190239804, what="annual_vol(monthly, 12)")


def test_days():
    """10.52 bp a day over 252 uncorrelated days is about 167.0 bp"""
    expect_close(annual_vol(10.52, 252), 10.52 * math.sqrt(252), what="annual_vol(10.52, 252)")


def test_lag_one():
    """A lag-one correlation of 0.2 raises 12 months of 3.4472% to √16.4 times as much; −0.2 lowers it to √7.6 times"""
    expect_close(annual_vol(0.034472, 12, 0.2), 0.034472 * math.sqrt(16.4), what="annual_vol(0.034472, 12, 0.2)")
    expect_close(annual_vol(0.034472, 12, -0.2), 0.034472 * math.sqrt(7.6), what="annual_vol(0.034472, 12, -0.2)")


def test_one_period():
    """One period is the period's own volatility, whatever lag1 is"""
    expect_close(annual_vol(0.02, 1, 0.5), 0.02, what="annual_vol(0.02, 1, 0.5)")


def test_bad_inputs():
    """No periods, a negative volatility, a correlation above 1 or a negative variance raises ValueError"""
    _raises(annual_vol, 0.01, 0, what="periods = 0")
    _raises(annual_vol, -0.01, 12, what="a negative volatility")
    _raises(annual_vol, 0.01, 12, 1.5, what="lag1 = 1.5")
    _raises(annual_vol, 0.01, 12, -0.6, what="lag1 = -0.6, which gives 12 - 13.2 < 0")

Summary card

  • A portfolio's return w⊤R\mathbf{w}^\top\mathbf{R} has mean w⊤μ\mathbf{w}^\top\boldsymbol{\mu} and variance w⊤Σw\mathbf{w}^\top\Sigma\mathbf{w}, derived from bilinearity: Var⁡(∑wiRi)=∑i∑jwiwjCov⁡(Ri,Rj)\operatorname{Var}(\sum w_iR_i) = \sum_i\sum_j w_iw_j\operatorname{Cov}(R_i, R_j). For the fund, a mean of 7.6% and a volatility of 11.94%; with two assets, case study 1.2.4's formula.
  • The variance needs only Σ\Sigma, with no normality or independence; probabilities need the distribution.
  • Over nn uncorrelated periods (independent is enough), variance scales by nn and volatility by n\sqrt{n}. So 3.4472% a month gives 11.94% a year, and 10.52 bp a day gives 167.0 bp. With a lag-one correlation ρ1\rho_1, the variance of twelve months is σ2(12+22ρ1)\sigma^2(12 + 22\rho_1).
  • For jointly normal returns, E[RC∣RB=b]=μC+ρσCσB(b−μB)\mathbb{E}[R_C \mid R_B = b] = \mu_C + \rho\frac{\sigma_C}{\sigma_B}(b - \mu_B), with spread σC1−ρ2\sigma_C\sqrt{1 - \rho^2}. The slope is case study 3.5.5's hedge ratio, 0.6944 for C on B; the reverse slope is 0.36.
  • Slopes estimated from ten years of data are rough. Outside the normal model, the conditional mean need not be a straight line.

Free further reading

Optional. This lesson covers what you need; these add more examples and practice. All are free to read.