edgekit

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.

✦Intuition — the shape of the problem

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:

✦Derivation — the closed-form min-variance weights

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.

min_var.py
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)
✦Scenario — the min-variance split of equities and gold by hand

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 .

→Unconstrained means shorts are allowed
The closed form places no sign restriction on , so a weight can come out negative — a short position — whenever assets are strongly correlated. edgekit’s 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 efficient frontier: a curve of portfolio return against volatility, with individual assets inside it, the minimum-variance point at the far left, and the tangency portfolio where a line from the risk-free rate touches the curve
The efficient frontier. Every individual asset sits inside the curve; combining them pushes the achievable set up and to the left — that leftward shift is diversification. The nose of the curve is the global minimum-variance portfolio; the point where a line from the risk-free rate is tangent to the curve is the max-Sharpe (tangency) portfolio.

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.

frontier.py
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 frontier
!max_sharpe is dangerously sensitive to μ
The tangency weights depend on , and — expected returns — is the single hardest thing to estimate in finance. Small errors in get amplified by into wildly concentrated, often heavily-short weights. The min-variance portfolio avoids this by dropping entirely; that is why it generalises better.
!Scenario — how a 0.5% estimate wobble flips the tangency book

Take 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.

shrinkage.py
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 weights

Risk 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):

erc.py
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 variance

Why 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.

MethodUses μ?Uses Σ⁻¹?Estimation riskedgekit
1/N equal weightNoNoNone(w = 1/N)
Risk parity (ERC)NoNo (fixed point)Lowequal_risk_contribution
Min-varianceNoYesMediummin_variance
Min-variance + shrinkageNoYes (conditioned)Lowerledoit_wolf → min_variance
Max-Sharpe (tangency)YesYesHighmax_sharpe
✦The practical hierarchy
The more an allocator relies on estimated inputs, the more it over-fits. So the robust ordering runsup the table: prefer 1/N or risk parity, reach for min-variance only with shrinkage, and treat max-Sharpe as a fragile view that needs strong priors on . This is precisely why edgekit’s deployment-facing portfolio construction layer combines validated R-streams with inverse-vol and hierarchical risk parity by default, keeping the fragile matrix inverse out of the live sizing path.

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.