Utility and mean–variance preferences
Utility and mean–variance preferences (lesson 4.6.1)
0 of 8 correct
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
How to type answers
- Powers with
^:x^7,2^(1/3),a^-2. - Multiply with
*or by writing side by side:3x^2,2(x+1). ln(x)is the natural log,log(x, 2)is log base 2,eis Euler's number,sqrt(x)is a square root.sin(x),cos(x)andtan(x)take radians; writepifor π andsin(x)^2for sin²x.- Your first answer to each question is saved: it counts towards your score and brings the question back for review.
- Exact answers like
ln(20)/ln(3)are best. Decimals need about 4 significant figures.
The tangency portfolio and the Sharpe ratio
The tangency portfolio and the Sharpe ratio (lesson 4.6.2)
0 of 8 correct
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
How to type answers
- Powers with
^:x^7,2^(1/3),a^-2. - Multiply with
*or by writing side by side:3x^2,2(x+1). ln(x)is the natural log,log(x, 2)is log base 2,eis Euler's number,sqrt(x)is a square root.sin(x),cos(x)andtan(x)take radians; writepifor π andsin(x)^2for sin²x.- Your first answer to each question is saved: it counts towards your score and brings the question back for review.
- Exact answers like
ln(20)/ln(3)are best. Decimals need about 4 significant figures.
The capital asset pricing model
The capital asset pricing model (lesson 4.6.3)
0 of 8 correct
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
How to type answers
- Powers with
^:x^7,2^(1/3),a^-2. - Multiply with
*or by writing side by side:3x^2,2(x+1). ln(x)is the natural log,log(x, 2)is log base 2,eis Euler's number,sqrt(x)is a square root.sin(x),cos(x)andtan(x)take radians; writepifor π andsin(x)^2for sin²x.- Your first answer to each question is saved: it counts towards your score and brings the question back for review.
- Exact answers like
ln(20)/ln(3)are best. Decimals need about 4 significant figures.
Coding exercise
The tangency portfolio and betas
Write two functions, with NumPy.
tangency(mu, cov, rf)returns(w, mean, vol, sharpe).wholds the tangency weights of lesson 4.6.2, then come the portfolio's expected return, volatility and Sharpe ratio. Usenp.linalg.solve, not an inverse.betas(cov, w)returns the array of betas against the portfoliow, as in lesson 4.6.3.
Both accept lists as well as arrays. Raise ValueError when the sizes do not match, or cov is not symmetric or not positive definite (lesson 3.7.1: check its smallest eigenvalue with np.linalg.eigvalsh). tangency also raises it when , and betas when the portfolio has no variance.
The tests use the three assets, the pair A and C, and the 50/30/20 benchmark. They check that no random portfolio beats your Sharpe ratio and that your betas rebuild the security market line. They also try a risk-free rate of 7%, an asymmetric matrix and the impossible correlations of case study 3.2.5.
See the tests
import numpy as np
_MU = np.array([0.06, 0.08, 0.11])
_SIGMA = np.array([0.12, 0.18, 0.25])
_R = np.array([[1.0, 0.3, 0.1], [0.3, 1.0, 0.5], [0.1, 0.5, 1.0]])
_COV = np.diag(_SIGMA) @ _R @ np.diag(_SIGMA)
def _raises(f, *args):
try:
f(*args)
except ValueError:
return True
return False
def test_three_assets():
"""The three assets with rf = 3%: weights (0.5185, 0.1601, 0.3214)"""
w, mean, vol, sharpe = tangency(_MU, _COV, 0.03)
expect_close(w, [0.5184600126166481, 0.1601145467890288, 0.32142544059432315], what="weights", rel=1e-9)
def test_three_assets_numbers():
"""Its expected return is 7.93%, volatility 12.47% and Sharpe ratio 0.3951"""
w, mean, vol, sharpe = tangency(_MU, _COV, 0.03)
expect_close(mean, 0.07927356296549673, what="expected return", rel=1e-9)
expect_close(vol, 0.12470023950760678, what="volatility", rel=1e-9)
expect_close(sharpe, 0.3951360731948796, what="Sharpe ratio", rel=1e-9)
def test_two_assets_lists():
"""A and C alone, given as lists: 60.62% in A, Sharpe ratio 0.3878"""
w, mean, vol, sharpe = tangency([0.06, 0.11], [[0.0144, 0.003], [0.003, 0.0625]], 0.03)
expect_close(w, [0.6062291434927698, 0.39377085650723026], what="weights", rel=1e-9)
expect_close(sharpe, 0.3878195977565348, what="Sharpe ratio", rel=1e-9)
def test_highest_sharpe():
"""No random fully invested portfolio has a higher Sharpe ratio"""
w, mean, vol, sharpe = tangency(_MU, _COV, 0.03)
W = np.random.default_rng(4).normal(size=(20_000, 3))
W = W / W.sum(axis=1, keepdims=True)
best = np.max((W @ _MU - 0.03) / np.sqrt(np.sum((W @ _COV) * W, axis=1)))
assert best <= sharpe + 1e-12, f"a random portfolio has Sharpe ratio {best:.6f}, above yours, {sharpe:.6f}"
def test_rf_too_high():
"""rf = 7%, above the minimum-variance return of 6.81%: ValueError"""
assert _raises(tangency, _MU, _COV, 0.07), "expected ValueError: k < 0, so there is no tangency portfolio"
def test_not_symmetric():
"""An asymmetric covariance matrix raises ValueError"""
bad = _COV.copy()
bad[0, 1] = 0.0648
assert _raises(tangency, _MU, bad, 0.03), "expected ValueError for an asymmetric matrix"
def test_not_positive_definite():
"""Impossible correlations (0.9, 0.9, -0.9) raise ValueError"""
R = np.array([[1.0, 0.9, 0.9], [0.9, 1.0, -0.9], [0.9, -0.9, 1.0]])
assert _raises(tangency, _MU, np.diag(_SIGMA) @ R @ np.diag(_SIGMA), 0.03), "expected ValueError: this matrix has a negative eigenvalue"
def test_sizes():
"""Two expected returns with a 3 x 3 matrix raise ValueError"""
assert _raises(tangency, [0.06, 0.08], _COV, 0.03), "expected ValueError when the sizes do not match"
def test_betas_tangency():
"""Betas against the tangency portfolio are (0.6088, 1.0147, 1.6236)"""
w = tangency(_MU, _COV, 0.03)[0]
expect_close(betas(_COV, w), [0.6088457621992378, 1.01474293699873, 1.6235886991979676], what="betas", rel=1e-9)
def test_security_market_line():
"""Against the tangency portfolio, rf + beta (mu_T - rf) gives back each expected return"""
w, mean, vol, sharpe = tangency(_MU, _COV, 0.03)
expect_close(0.03 + betas(_COV, w) * (mean - 0.03), _MU, what="rf + beta (mu_T - rf)", rel=1e-9)
def test_betas_benchmark():
"""Against the 50/30/20 mix, betas are (0.6833, 1.2244, 1.4551) and average to 1"""
w = np.array([0.5, 0.3, 0.2])
b = betas(_COV, w)
expect_close(b, [0.6833099579242636, 1.2244039270687237, 1.4551192145862553], what="betas", rel=1e-9)
expect_close(float(w @ b), 1.0, what="weighted average beta")
def test_betas_zero_portfolio():
"""Betas against a portfolio with no variance raise ValueError"""
assert _raises(betas, _COV, [0.0, 0.0, 0.0]), "expected ValueError: the portfolio has no variance"