Optimization & portfolios
Given a covariance matrix and a set of return views, what weights should you hold? Markowitz turned that into a constrained optimization problem with a clean closed-form answer — and six decades of experience turned it into a cautionary tale about trusting your inputs. This chapter derives the minimum-variance and tangency portfolios with Lagrange multipliers, draws the efficient frontier, and then explains, honestly, why the elegant solution so often loses to naive 1/N out of sample.
You have a pile of cash and a handful of assets. Put too much in the high-return one and a bad month wrecks you; spread it evenly and you leave return on the table. Somewhere between those extremes is a “best” split — and because risk is that bowl-shaped quadratic from the last chapter, there is exactly onebottom of the bowl, so “best” is well-defined and reachable by formula rather than by trial and error. The catch, which the second half of the chapter is honest about: the formula’s answer is only as good as the return and covariance estimates you feed it, and those are noisy. The whole arc is “here is the beautiful closed form; here is why the pros often trust a cruder, sturdier version of it.”
Convexity — why a unique answer exists#
Portfolio variance is the quadratic form from Linear algebra for quants. Because is positive semi-definite, this function is convex: its Hessian is , so the surface is a bowl with no false bottoms. That is what makes portfolio optimization tractable — a convex objective over linear constraints has a single global minimum, reachable in closed form. There is no local-minimum trap and no need for iterative solvers; the entire problem is one matrix inverse away.
Lagrange multipliers#
To minimise a function subject to equality constraints, we use Lagrange multipliers. The idea: at a constrained optimum, the gradient of the objective must be a linear combination of the constraint gradients — otherwise you could slide along the constraint surface and improve. Encode that by forming the Lagrangian, a single function whose stationary point solves the constrained problem. For “minimise subject to ”:
The second condition just re-imposes the constraint; the first is the stationarity condition. This single tool cracks both portfolios below.
The global minimum-variance portfolio#
The simplest Markowitz problem ignores returns entirely and asks for the least-risky fully-invested portfolio:
Form the Lagrangian with one multiplier for the budget constraint:
Set the gradient in to zero, using (valid because is symmetric):
The weights are proportional to ; the multiplier is just an overall scale, pinned by the budget constraint. Impose :
Substitute back to get the celebrated closed form:
Why it matters: notice the answer depends only on , never on expected returns. That is why min-variance is the most robust MVO output — there is no to mis-estimate. On a diagonal it reduces to inverse-variance weighting, — put more in the calm assets. The catch is the : it is exactly the noisy, near-singular inverse that the previous chapter warned about.
import edgekit as ek
Sigma = ek.optimize.sample_cov(returns)
w = ek.optimize.min_variance(Sigma) # Sigma^{-1} 1 / (1' Sigma^{-1} 1)
w.sum() # 1.0 (fully invested)
ek.optimize.portfolio_vol(w, Sigma) # sqrt(w' Sigma w)Two assets make the closed form arithmetic you can do on paper. Take equities (, ) and gold (, ) with correlation , so . For two assets reduces to a one-liner:
So hold equities, gold. Plug back into the quadratic form:
Read that number: the least-risky asset alone had vol, yet the blend sits at — below either leg. That is the negative correlation cashing out as the diversification free lunch, and it is the leftmost nose of the efficient frontier drawn below. This min-variance mix earns ; if you wanted more return you would slide right along the frontier, accepting more vol for it. Notice the whole calculation never touched expected returns until the very last line — which is why the min-variance point is the one Markowitz output you can trust when you distrust .
min_variance/max_sharpe are deliberately unconstrained; add your own box or no-short constraints downstream if the venue forbids shorting.Adding a return target: the efficient frontier#
Now bring returns back. Minimise variance for a chosen target return , giving two constraints:
Two constraints means two multipliers. The stationarity condition gives , and solving the two constraints for makes the optimal weights affine in the target return — the two-fund theorem: . Sweeping traces a parabola in variance, whose upper branch is the efficient frontier: the set of portfolios with the highest return for each level of risk. edgekit computes the whole curve analytically (no per-point solve) with the constants , , :

The tangency (max-Sharpe) portfolio#
Among all frontier portfolios, one has the steepest reward-per-risk — the highest Sharpe ratio . It is where a line drawn from the risk-free rate is tangent to the frontier, hence the tangency portfolio. Maximising the Sharpe ratio and solving gives another clean closed form:
Under the two-fund theorem, every investor holds some mix of the risk-free asset and this one risky portfolio — risk preference only sets the split, not the composition. edgekit exposes it as ek.optimize.max_sharpe, and the full frontier (returns, vols, weights, Sharpe at each point) as ek.optimize.efficient_frontier.
import edgekit as ek
Sigma = ek.optimize.sample_cov(returns)
mu = returns.mean().to_numpy() # expected-return VIEW (treat skeptically!)
w_tan = ek.optimize.max_sharpe(mu, Sigma, rf=0.0) # Sigma^{-1}(mu - rf) normalised
front = ek.optimize.efficient_frontier(mu, Sigma, n=50)
front["vols"], front["returns"] # trace the curve
front["sharpe"].max() # best reward-per-risk on the frontierTake two near-twin assets: , correlation , and estimated returns , (). The high correlation makes nearly singular, so is huge, and comes out
A return gap became a leveraged long/short. Now nudge just one input — from to , well within any estimation error — and the weights swing to : the short flips to a fifth of the book long. Nothing about the world changed; a rounding error in did, and amplified it into a different portfolio. That is the error-maximiser in one example — and the concrete reason the desk reaches for shrinkage, risk parity, or plain 1/N below.
Why the optimizer is fragile#
Markowitz optimization is an error-maximiser. It seeks out assets that look high-return and low-correlation — and those are precisely the estimates most likely to be lucky sampling noise. The inverse makes it worse: near-zero eigenvalues (see the PCA discussion) blow up into enormous long/short bets on directions that are pure estimation error. The result is a portfolio that is beautifully optimal in-sample and unstable out of sample.
Ledoit-Wolf shrinkage#
The standard fix is to shrink the noisy sample covariance toward a stable, structured target — a scaled identity ( = average sample variance). Ledoit-Wolf picks the convex mix that minimises expected squared error, with the shrinkage intensity available in closed form — no cross-validation:
Shrinkage pulls the tiny, noise-dominated eigenvalues up and the inflated ones down — it conditions the matrix, so stops exploding. The sample cov is unbiased but high-variance; the identity target is biased but stable; the convex blend trades a little bias for a large variance reduction. Because it is a mix of two PSD matrices it is itself PSD, and strictly better-conditioned than . Feed to the optimizers whenever the raw inverse looks unstable.
import edgekit as ek
S = ek.optimize.sample_cov(returns) # unbiased but noisy
Sig = ek.optimize.ledoit_wolf(returns) # shrunk toward scaled identity — stable inverse
w_raw = ek.optimize.min_variance(S) # can be wild / heavily short
w_shrunk = ek.optimize.min_variance(Sig) # tamer, more diversified weightsRisk parity — sizing by risk, not dollars#
A robust middle ground drops expected returns and the fragile inverse. Equal-risk contribution (risk parity) asks that every asset supply the same share of total portfolio variance. Using the Euler decomposition of the variance, asset ’s risk contribution is , and these sum exactly to . Risk parity solves for the long-only where all are equal — no single asset can dominate the risk budget. edgekit finds it with a stable damped fixed point (no matrix inverse at all):
import edgekit as ek
Sigma = ek.optimize.sample_cov(returns)
w_erc = ek.optimize.equal_risk_contribution(Sigma) # every asset = 1/N of the variance
ek.optimize.risk_contributions(w_erc, Sigma) # ~equal entries, sum to portfolio varianceWhy 1/N often wins out of sample#
Here is the humbling result. In a well-known study, DeMiguel, Garlappi & Uppal (2009) found that across many datasets, sophisticated mean-variance optimizers failed to reliably beat the naive equal-weight (1/N) portfolio out of sample. The reason is exactly the fragility above: the estimation error in and costs more than the theoretical optimization gain. Equal weighting has zero estimation error — there are no parameters to get wrong.
| Method | Uses μ? | Uses Σ⁻¹? | Estimation risk | edgekit |
|---|---|---|---|---|
| 1/N equal weight | No | No | None | (w = 1/N) |
| Risk parity (ERC) | No | No (fixed point) | Low | equal_risk_contribution |
| Min-variance | No | Yes | Medium | min_variance |
| Min-variance + shrinkage | No | Yes (conditioned) | Lower | ledoit_wolf → min_variance |
| Max-Sharpe (tangency) | Yes | Yes | High | max_sharpe |
The lesson is not that optimization is useless — it is that its inputs must be defensible. Use it when you have few assets, a long history, and a shrunk covariance you trust; fall back to risk-based heuristics when you do not. The math gives you the optimum conditional on the inputs being right; the discipline is knowing how wrong the inputs are.
Next: we have modelled portfolios of returns — now we model how a single price moves through time, the random process the whole edifice rests on. Stochastic processes.
