SigmaField
Mathematical Foundations for Quant Finance
0 of 256 lessons
      1. 1.2.1 Lines: slope and interceptConcept · 25 min
      2. 1.2.2 Quadratics and completing the squareConcept · 25 min
      3. 1.2.3 Practice set: lines and quadraticsPractice set · 20 min
      4. 1.2.4 Case study: the minimum-variance two-asset portfolioCase study · 30 min

Level 1, Precalculus 1.2 Lines and quadratics Lesson 4 of 4

Case studyLesson 1.2.4 in Level 1, Precalculus. About 30 minutes.

Case study: the minimum-variance two-asset portfolio

The set-up

Put a fraction ww of your money in A and the rest, 1−w1 - w, in B. So w=0.75w = 0.75 means 75% in A and 25% in B, and w=1w = 1 means everything in A.

Risk is measured by volatility σ\sigma (sigma): how widely a year's return typically swings. Desks write it as a decimal, so 20% is σA=0.20\sigma_A = 0.20, and σB=0.30\sigma_B = 0.30. Its square, σ2\sigma^2, is the variance: σA2=0.04\sigma_A^2 = 0.04 and σB2=0.09\sigma_B^2 = 0.09.

How the two move together is measured by their correlation ρ\rho (rho), a number from −1-1 to 1. At 1 they move in perfect step, at −1-1 in exact opposition, and at 0 they are unrelated. Here ρ=0.2\rho = 0.2: they tend to move the same way, but loosely.

Module 5.5 derives the variance of the portfolio, σp2\sigma_p^2. Here, take it as given:

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

Check the ends first. At w=1w = 1 only the first term is left, σA2=0.04\sigma_A^2 = 0.04: all in A. At w=0w = 0 only the second is left, σB2=0.09\sigma_B^2 = 0.09: all in B.

Variance is a quadratic in the weight

Multiply out (1−w)2=1−2w+w2(1 - w)^2 = 1 - 2w + w^2 and 2w(1−w)=2w−2w22w(1 - w) = 2w - 2w^2, then collect the powers of ww. The result is a quadratic aw2+bw+caw^2 + bw + c (lesson 1.2.2), with

a=σA2+σB2−2ρ σAσBc=σB2b=−2σB2+2ρ σAσB\begin{gathered} a = \sigma_A^2 + \sigma_B^2 - 2\rho\,\sigma_A\sigma_B \qquad c = \sigma_B^2 \\[4pt] b = -2\sigma_B^2 + 2\rho\,\sigma_A\sigma_B \end{gathered}

With the numbers above, a=0.106a = 0.106, b=−0.156b = -0.156 and c=0.09c = 0.09. A table of the variance, and of the volatility (its square root), shows the shape:

ww00.250.50.751
variance0.090.05760.03850.03260.04
volatility0.300.24010.19620.18060.20

The variance falls and then rises again, so the lowest point lies somewhere near w=0.75w = 0.75. The table brackets it; the vertex will pin it down.

The parabola opens upwards whenever a>0a > 0. Rewritten as a=(σA−σB)2+2(1−ρ)σAσBa = (\sigma_A - \sigma_B)^2 + 2(1 - \rho)\sigma_A\sigma_B, it is a sum of two terms that cannot be negative while ρ≤1\rho \le 1. It is 0 only if ρ=1\rho = 1 and the two volatilities are equal.

The minimum-variance weight

The vertex rule from lesson 1.2.2, w∗=−b2aw^* = -\frac{b}{2a}, gives the weight with the least variance:

Rule: Minimum-variance weight

w∗=σB2−ρ σAσBσA2+σB2−2ρ σAσBw^* = \frac{\sigma_B^2 - \rho\,\sigma_A\sigma_B}{\sigma_A^2 + \sigma_B^2 - 2\rho\,\sigma_A\sigma_B}

The share of the money in A that makes the portfolio's variance as small as possible. The rest, 1−w∗1 - w^*, goes in B.

Here w∗=0.1560.212≈0.7358w^* = \frac{0.156}{0.212} \approx 0.7358: about 74% in A and 26% in B. The lowest variance is c−b24a≈0.0326c - \frac{b^2}{4a} \approx 0.0326, a volatility of about 0.1806. That is lower than A's 0.20 on its own.

The same weight gives the least volatility. A square root keeps the order of positive numbers, so the smallest variance has the smallest square root. Work with the variance, which is the quadratic, and take the square root at the end.

Portfolio variance against the weight in A, with σA=0.20\sigma_A = 0.20, σB=0.30\sigma_B = 0.30 and ρ=0.2\rho = 0.2. The vertex, at w∗=0.736w^* = 0.736, sits below the dotted level of A on its own.

Try it

The cell computes aa, bb and cc, then the vertex and the volatility there.

The plot shows volatility rather than variance. It is a different curve, not a parabola, but its lowest point is at the same weight.

⌘/Ctrl + Enter runs the cell

Try it

Always check a formula a second way. A brute-force search over 100,001 weights finds the same minimum to five decimal places.

⌘/Ctrl + Enter runs the cell

How correlation changes the answer

The lower the correlation, the more the two assets offset each other, and the lower the minimum risk.

ρ\rhow∗w^*lowest volatility
−0.5-0.50.63160.1192
00.69230.1664
0.20.73580.1806
0.50.85710.1964
0.81.23530.1952

The same two assets at three correlations. Every curve passes through 0.09 at w=0w = 0 and 0.04 at w=1w = 1; the correlation only bends the middle. At ρ=0.8\rho = 0.8 the vertex lies in the shaded band past w=1w = 1.

At ρ=0.8\rho = 0.8 the formula says to hold 123.5% in A. The extra 23.5% is paid for by a short sale of B: borrowing B's shares and selling them. The weight in B is −0.2353-0.2353.

With σA\sigma_A below σB\sigma_B, this happens exactly when ρ\rho is above σAσB\frac{\sigma_A}{\sigma_B}, here 0.200.30≈0.667\frac{0.20}{0.30} \approx 0.667. Above that, mixing in B adds risk, and only a short sale of B reduces it.

Many funds may not sell short, so their weight must stay between 0 and 1. With the vertex past 1, the variance falls all the way from w=0w = 0 to w=1w = 1. So the best allowed weight is the end nearest the vertex: w=1w = 1, all in A, with volatility 0.20.

Try it with the sliders. Raise ρ\rho past 0.667 and watch the lowest point leave [0,1][0, 1], while the best long-only weight sticks at w=1w = 1. Then lower σB\sigma_B and see the threshold σAσB\frac{\sigma_A}{\sigma_B} move.

Explore

Move the correlation and the two volatilities. The curve is the variance of the portfolio for each weight in A; the shaded bands need a short sale.

−0.500.511.500.050.10.150.2short Ashort B
Minimum-variance weight in A
0.7358. 73.6% in A and 26.4% in B, both held long.
Lowest risk
variance 0.0326, volatility 0.1806
Best without short sales
w = 0.7358, volatility 0.1806
Check yourself
Two assets have the same volatility, σA=σB=0.25\sigma_A = \sigma_B = 0.25, and correlation 0. What is the minimum-variance weight in A?

What goes wrong on a desk

The formula is one line, but its inputs come from data and from other people's code. These are the cases that cause trouble.

  • Perfect correlation. At ρ=−1\rho = -1 the formula gives w∗=0.6w^* = 0.6 and a variance of 0: the two assets cancel exactly. At ρ=1\rho = 1 it gives w∗=3w^* = 3, also with variance 0, by holding 300% in A and −200%-200\% in B. Real estimates never reach ±1\pm 1, so a zero-risk answer means the inputs need checking.
  • No single answer. If ρ=1\rho = 1 and σA=σB\sigma_A = \sigma_B, then a=0a = 0 and b=0b = 0. Every weight gives the same variance, and the formula reads 00\frac{0}{0}. Code must check a>0a > 0 before dividing.
  • Impossible inputs. A correlation of 1.2 cannot occur, but a typo can produce one. Then a=−0.014a = -0.014, the parabola opens downwards, and the vertex is a maximum. The "variance" at w=2w = 2 even comes out negative, −0.038-0.038.
  • Units. Volatilities typed in percent, 20 and 30, give the same w∗w^*, because only their ratio matters. But the variance then comes out in squared percent, 326.04. Mixing conventions is worse: A's monthly volatility, 0.20/12≈0.05770.20 / \sqrt{12} \approx 0.0577, with B's annual 0.30 gives w∗=1.0015w^* = 1.0015 instead of 0.7358.
  • Estimates move. Volatilities and correlations are estimated from past returns, and they change. The table shows w∗w^* moving from 0.63 to 1.24 as ρ\rho goes from −0.5-0.5 to 0.8. Treat w∗w^* as a guide, not a precise answer.

Try it

min_var returns the vertex and the variance there, rounded. The four lines are the two perfect correlations, percent inputs and mixed monthly and annual inputs.

At ρ=1\rho = 1 the computed variance is about 8×10−178 \times 10^{-17}, a rounding error, so after rounding it prints as 0.0.

⌘/Ctrl + Enter runs the cell

With many assets, the weights become a vector and the parabola becomes a bowl in many dimensions. Module 4.5 finds its lowest point.

A line in finance: the security market line

Lines from lesson 1.2.1 appear too. The capital asset pricing model (module 4.6) says a stock's expected return is a straight line in its beta β\beta. Beta measures how strongly the stock moves with the whole market: 1 means in step with it, 2 twice as strongly.

E[R]=rf+β (E[Rm]−rf)\mathbb{E}[R] = r_f + \beta\,\big(\mathbb{E}[R_m] - r_f\big)

Here E\mathbb{E} means "expected value", so E[R]\mathbb{E}[R] is the stock's expected yearly return and E[Rm]\mathbb{E}[R_m] the market's. rfr_f is the risk-free rate, the return on a safe government bill.

The intercept is rfr_f: the expected return at β=0\beta = 0. The slope is the market's extra return over rfr_f, in return per unit of beta. With rf=3%r_f = 3\% and E[Rm]=8%\mathbb{E}[R_m] = 8\%, the line is 0.03+0.05β0.03 + 0.05\beta.

  • A stock with β=1.4\beta = 1.4 should return 10%, one with β=0.8\beta = 0.8 should return 7%, and one with β=1\beta = 1 returns the market's 8%.
  • A negative beta is allowed. A stock with β=−0.5\beta = -0.5 tends to rise when the market falls, which works like insurance. The line asks only 0.5% of it, below the risk-free rate.

A stock's distance above or below the line is its alpha. A stock with β=0.8\beta = 0.8 expected to return 6% sits 1 point below the 7% the line asks: an alpha of −1%-1\%. Going the other way, 0.03+0.05β=0.090.03 + 0.05\beta = 0.09 gives β=1.2\beta = 1.2, the beta that a 9% expected return implies.

Try it

sml is the line 0.03+0.05β0.03 + 0.05\beta. The loop prints the expected return for five betas.

The last two lines are the alpha of the 6% stock and the beta implied by 9%, solved as in lesson 1.2.1.

⌘/Ctrl + Enter runs the cell
Check yourself
On that line, a stock with β=1.4\beta = 1.4 is expected to return 12%. What does the model suggest?
Exercise

The minimum-variance portfolio

Write three functions.

  1. portfolio_vol(w, sA, sB, rho) returns the volatility (the square root of the variance formula above) with weight w in A. It should work when w is a NumPy array.
  2. min_variance_weight(sA, sB, rho) returns w∗w^*. It raises ValueError when rho is outside [−1,1][-1, 1], and when a=0a = 0, because then every weight gives the same variance.
  3. long_only_weight(sA, sB, rho) returns the best weight when neither asset may be sold short: w∗w^* moved to 0 or 1 if it falls outside that range.

The tests compare your weight with a brute-force search, as in the cell above, and try the bad inputs from the desk section.

See the tests
import numpy as np


def test_all_in_one_asset():
    """With all the money in A, the volatility is A's own, 0.20"""
    expect_close(portfolio_vol(1.0, 0.20, 0.30, 0.2), 0.20, what="portfolio_vol(1, 0.20, 0.30, 0.2)")


def test_vol_array():
    """portfolio_vol works on an array of weights"""
    w = np.array([0.0, 0.5, 1.0])
    expected = np.sqrt(w**2 * 0.04 + (1 - w)**2 * 0.09 + 2 * w * (1 - w) * 0.2 * 0.06)
    expect_close(portfolio_vol(w, 0.20, 0.30, 0.2), expected, what="portfolio_vol at w = 0, 0.5, 1")


def test_equal_assets():
    """Equal volatilities and zero correlation: split evenly"""
    expect_close(min_variance_weight(0.25, 0.25, 0.0), 0.5, what="min_variance_weight(0.25, 0.25, 0)")


def test_matches_brute_force():
    """The weight matches a brute-force search over 100,001 weights"""
    w = np.linspace(0, 1, 100_001)
    best = w[np.argmin(w**2 * 0.04 + (1 - w)**2 * 0.09 + 2 * w * (1 - w) * 0.2 * 0.06)]
    expect_close(min_variance_weight(0.20, 0.30, 0.2), best, what="min_variance_weight(0.20, 0.30, 0.2)", rel=1e-4)


def test_negative_correlation():
    """Negative correlation: the minimum risk is far below either asset's"""
    w = min_variance_weight(0.20, 0.30, -0.5)
    vol = portfolio_vol(w, 0.20, 0.30, -0.5)
    assert vol < 0.15, f"expected a minimum volatility below 0.15, got {vol}"


def _raises_value_error(fn, *args):
    try:
        fn(*args)
    except ValueError:
        return True
    return False


def test_impossible_correlation():
    """A correlation above 1 raises ValueError"""
    assert _raises_value_error(min_variance_weight, 0.20, 0.30, 1.2), "min_variance_weight(0.20, 0.30, 1.2) should raise ValueError"


def test_no_single_best_weight():
    """Equal volatilities with correlation 1 raise ValueError: every weight gives the same variance"""
    assert _raises_value_error(min_variance_weight, 0.25, 0.25, 1.0), "min_variance_weight(0.25, 0.25, 1.0) should raise ValueError"


def test_long_only_inside():
    """Long-only, ρ = 0.2: the vertex is already between 0 and 1, so it is kept"""
    expect_close(long_only_weight(0.20, 0.30, 0.2), 0.156 / 0.212, what="long_only_weight(0.20, 0.30, 0.2)")


def test_long_only_clips_high():
    """Long-only, ρ = 0.8: the vertex is past 1, so the answer is 1 (all in A)"""
    expect_close(long_only_weight(0.20, 0.30, 0.8), 1.0, what="long_only_weight(0.20, 0.30, 0.8)")


def test_long_only_clips_low():
    """Long-only, with A the riskier asset and ρ = 0.8: the vertex is below 0, so the answer is 0"""
    expect_close(long_only_weight(0.30, 0.20, 0.8), 0.0, what="long_only_weight(0.30, 0.20, 0.8)")

Summary card

  • With weight ww in A, portfolio variance is a quadratic in ww. It opens upwards, because a=(σA−σB)2+2(1−ρ)σAσBa = (\sigma_A - \sigma_B)^2 + 2(1 - \rho)\sigma_A\sigma_B is positive unless ρ=1\rho = 1 and σA=σB\sigma_A = \sigma_B.
  • Its vertex is the minimum-variance weight w∗=σB2−ρσAσBσA2+σB2−2ρσAσBw^* = \frac{\sigma_B^2 - \rho\sigma_A\sigma_B}{\sigma_A^2 + \sigma_B^2 - 2\rho\sigma_A\sigma_B}. The same weight minimises volatility.
  • Mixing in a riskier asset can lower risk; the lower the correlation, the larger the benefit.
  • w∗>1w^* > 1 (a short sale of B) when ρ>σAσB\rho > \frac{\sigma_A}{\sigma_B}. Without short sales, take the end of [0,1][0, 1] nearest the vertex.
  • Check inputs: −1≤ρ≤1-1 \le \rho \le 1, a>0a > 0, and both volatilities in the same units.
  • The security market line E[R]=rf+β(E[Rm]−rf)\mathbb{E}[R] = r_f + \beta(\mathbb{E}[R_m] - r_f) has intercept rfr_f and slope the market's extra return. Alpha is the distance above the line.

Free further reading

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