Course contents
Building the portfolio
Turning signals into positions is portfolio construction, and it rests on a risk model, an estimate of how assets move together. This chapter covers the covariance idea, the common weighting schemes from equal-weight to risk parity to mean-variance, and the honest warning that mean-variance optimisation is dangerously sensitive to its inputs.
- Explain a covariance-based risk model in plain terms
- Compare equal-weight, risk-parity, and mean-variance construction
- Recognise why mean-variance optimisation is fragile and why simpler schemes often win
You have several edges you want to hold together. How much of each? That is portfolio construction, and it rests on a risk model: an estimate of how the pieces move together. This chapter covers the risk model, the main ways to weight a portfolio, and a hard-won warning that the most sophisticated method is often the worst, because it fits the past too precisely.
The risk model: covariance
A risk model estimates how the parts of a portfolio co-move, captured in the covariance matrix: each holding's variance on the diagonal, and the covariance between every pair off it. It is what lets you compute the risk of the whole from the parts, and it is the input to every weighting scheme that follows. But it is estimated from limited, noisy data, and the covariances between assets are especially hard to pin down. That estimation noise is the villain of this chapter, because a method that leans hard on the covariance matrix is really leaning on its errors.
Three ways to weight
Here are the three most common schemes, fitted on one half of the data and judged on the other.
# Turning signals into positions is portfolio construction, and it rests on a risk
# model: how the pieces move together (the covariance). We compare three weighting
# schemes fitted on the in-sample half, then judge them out-of-sample. The fancy
# one (mean-variance) takes extreme bets and is fragile. Synthetic data.
import numpy as np
import pandas as pd
from factor_data import make_market, long_short, annual_sharpe
returns, static = make_market()
T = len(returns)
def broadcast(col):
return pd.DataFrame([static[col].values] * T, columns=returns.columns, index=returns.index)
factors = pd.DataFrame({
"value": long_short(broadcast("earnings_yield"), returns),
"quality": long_short(broadcast("roe"), returns),
"momentum": long_short((1 + returns).rolling(12).apply(np.prod, raw=True).shift(1) - 1, returns),
"lowvol": long_short(-returns.rolling(12).std().shift(1), returns),
}).dropna()
half = len(factors) // 2
ins, oos = factors.iloc[:half], factors.iloc[half:]
n = factors.shape[1]
w_equal = np.repeat(1 / n, n) # equal weight
inv_vol = 1 / ins.std()
w_riskparity = (inv_vol / inv_vol.sum()).values # risk parity (inverse vol)
w_mv = np.linalg.solve(ins.cov().values, ins.mean().values) # mean-variance (max Sharpe)
w_mv = w_mv / np.abs(w_mv).sum()
print("Weights fitted on the in-sample half:")
for name, w in [("equal", w_equal), ("risk-parity", w_riskparity), ("mean-variance", w_mv)]:
parts = " ".join(f"{col}={x:+.2f}" for col, x in zip(factors.columns, w))
print(f" {name:13s} {parts}")
print("\nOut-of-sample Sharpe of each scheme:")
for name, w in [("equal", w_equal), ("risk-parity", w_riskparity), ("mean-variance", w_mv)]:
oos_ret = (oos * w).sum(axis=1)
print(f" {name:13s} {annual_sharpe(oos_ret):.2f}")
print("\nMean-variance takes extreme, concentrated bets fitted to the past's noise,")
print("and it is fragile out-of-sample. The simple schemes are usually sturdier.")
print("A risk model helps, but do not trust an optimiser to size your bets precisely.")Weights fitted on the in-sample half: equal value=+0.25 quality=+0.25 momentum=+0.25 lowvol=+0.25 risk-parity value=+0.25 quality=+0.27 momentum=+0.20 lowvol=+0.28 mean-variance value=+0.59 quality=+0.26 momentum=+0.11 lowvol=-0.03 Out-of-sample Sharpe of each scheme: equal 2.72 risk-parity 2.74 mean-variance 2.22 Mean-variance takes extreme, concentrated bets fitted to the past's noise, and it is fragile out-of-sample. The simple schemes are usually sturdier. A risk model helps, but do not trust an optimiser to size your bets precisely.
Equal weight gives each edge the same share: crude, stable, and hard to beat. Risk parity weights by inverse volatility so each contributes roughly equal risk: a little smarter, still stable. Mean-variance optimisation, the famous method, chooses the weights that would have maximised the Sharpe on the in-sample data: mathematically optimal, in theory. Now read the result. The mean-variance weights are concentrated and extreme, piling into value at 0.59 and even shorting low volatility, fitted to the quirks of the past. And out-of-sample, it is the worst of the three, a Sharpe of 2.22 against 2.72 for equal weight and 2.74 for risk parity. The optimal method lost to the naive ones.
Simpler is sturdier
This is not a fluke; it is famous. Mean-variance optimisation is exquisitely sensitive to its inputs: a tiny change in an estimated return or covariance swings the weights wildly, and because those estimates are mostly noise, the optimiser cheerfully optimises the noise, producing concentrated bets that fall apart on new data. Equal weight and risk parity barely use the fragile covariances, so they have little noise to overfit, and they win out-of-sample far more often than theory would suggest. This is the portfolio version of the whole course's theme. The past is mostly noise, and a method that fits it too precisely fits the noise. Use the risk model to understand your risk, not to chase a false precision, and prefer simple, stable weights.
What to carry forward
Portfolio construction weights your edges, and it rests on a risk model, the covariance matrix, estimated from noisy data. The three main schemes are equal weight, risk parity (inverse volatility), and mean-variance optimisation, and the last, though mathematically optimal in-sample, is fragile: it overfits its noisy inputs, takes concentrated bets, and lost to both simple schemes out-of-sample here. The lesson is the course's theme again: fitting the noisy past too precisely fails, so prefer simple, stable weights. With the portfolio weighted, one decision remains and it is as important as the edge: how much total capital to put at risk, which is position sizing and the Kelly criterion.