edgekit

Linear algebra for quants

A portfolio is a weighted sum of assets, and its risk is a quadratic form in those weights. Once you see that, the whole machinery of portfolio theory — variance, diversification, risk factors — collapses into three objects from linear algebra: a weight vector, a covariance matrix, and its eigenvectors. This chapter builds that vocabulary from the ground up, derives the one formula every optimizer uses, and shows why the eigenvectors of the covariance matrix arethe market’s hidden risk factors.

Intuition — before any symbols

Forget matrices for a second. You hold three things: some Bitcoin, some Ether, a little gold. Two questions run your book. What did I make today?— add up each holding’s move, weighted by how much of it you own. How risky am I? — that one is subtler, because BTC and ETH tend to crash together, so holding both is not two bets but nearly one, while gold usually zigs when crypto zags and quietly cancels risk. Linear algebra is just the bookkeeping that turns those two questions into a vector (the first) and a matrix sandwiched between two vectors (the second). Everything hard in this chapter is a way of reading that second object — the covariance matrix — correctly.

Vectors: returns and weights#

Take assets. On a given day their returns are just a list of numbers, which we stack into a column vector . How you split your capital across them is a second vector, the weights , where is the fraction of the book in asset . “Fully invested” is the linear constraint that the weights sum to one:

The portfolio’s return for the day is the dot productof the two — each asset’s return scaled by how much you held of it, summed:

The superscript is transpose: it turns the column on its side so the multiplication lines up term-by-term. Everything that follows is built by stacking these vectors into matrices and asking what happens to the dot products.

The covariance matrix Σ#

Return is linear in the weights, so its expectation is trivial: where is the vector of expected returns. Risk is where the structure lives, because it depends on how assets move together. That co-movement is packed into the covariance matrix , an table whose entries are

The diagonal is each asset’s own variance; the off-diagonal is how asset and asset co-vary. Because , the matrix is symmetric: . From a matrix of historical returns (rows = periods, columns = assets), the sample estimate is a single matrix product on the demeaned data:

That is exactly what ek.optimize.sample_cov computes — a demeaned Gram matrix normalised by . It is the maximum-likelihood covariance and it is the raw material for every risk calculation in the rest of Part VI.

cov.py
import edgekit as ek

# returns: T x N DataFrame (rows = days, columns = assets)
Sigma = ek.optimize.sample_cov(returns)   # N x N covariance, ddof=1
Sigma.shape          # (N, N)
Sigma.diagonal()     # per-asset variances (sigma_i^2)

Portfolio variance as a quadratic form#

Here is the formula that motivates the whole chapter. The variance of the portfolio return is the quadratic form

Derivation — expand the double sum, then collect it into a matrix product

Portfolio return is a weighted sum, . Variance of a sum expands into variances plus every pairwise covariance — this is the bilinearity of covariance, :

The right-hand double sum is, by the definition of matrix multiplication, precisely : multiplying by on the right forms the inner sum over , and the outer forms the sum over . Hence . Why it matters: risk is not the weighted average of individual risks — the cross terms are the whole story. Two volatile assets with negative covariance can combine into a low-variance book; that cancellation, buried in the off-diagonals, is what diversification means mathematically.

Split the double sum into its diagonal and off-diagonal parts to read the diversification directly:

If the assets were uncorrelated the second term vanishes and risk is just the weighted sum of variances. Real assets are correlated, and the sign and size of that second term is what an optimizer is really trading against return. When correlations are positive it inflates risk; when some are negative it deflates it — the free lunch of diversification.

Scenario — a 3-asset book of BTC, ETH, and gold

Put real-ish annualised numbers on the desk. Volatilities: , , . Correlations: (crypto moves together), , . Each covariance is , so the matrix is

Now evaluate the quadratic form. The diagonal (“own risk”) contributes ; the off-diagonal (“interaction”) contributes . Add them:

Read the numbers. A trader who ignoredcorrelation would guess the book’s risk was the weighted average of vols, . If the assets were truly uncorrelated the diagonal alone would give . Reality — — sits between: the BTC/ETH correlation is almost the entire interaction term (it alone adds of variance), while gold’s near-zero correlations barely move it. That single off-diagonal cell is why “I hold BTC and ETH” is closer to one bet than two — and why the calm, uncorrelated gold sleeve is doing the real diversification work despite its tiny weight-share of the risk.

Positive semi-definiteness#

A variance can never be negative. Since is a variance for any weights whatsoever, the covariance matrix must satisfy

A symmetric matrix with this property is called positive semi-definite (PSD). It is not a technicality — it is a hard constraint that every valid covariance must obey, and it has three consequences you will keep meeting:

  • All eigenvalues are . A negative eigenvalue would let you build a portfolio with negative variance — impossible. (Strictly positive eigenvalues make positive definite and therefore invertible.)
  • The inverse can be unstable. A near-zero eigenvalue means is nearly singular, so (which the optimizers of the next chapter need) explodes. This is the numerical face of over-fitting a covariance from too little data.
  • Sample estimates can break PSD. When you have fewer observations than assets (), the sample covariance is rank-deficient — it has exact zero eigenvalues and is only semi-definite. That is one of several reasons shrinkage estimators exist.
Why edgekit ridges the inverse
Because a noisy sample can have eigenvalues so small that the inverse is nonsense, ek.optimize adds a tiny multiple of the identity () when the condition number blows up. That nudges every eigenvalue up by , restoring a stable, still-PSD matrix without moving a well-behaved one. The principled version of the same idea — Ledoit-Wolf shrinkage — is the subject of the next chapter.

The correlation matrix#

Covariance mixes co-movement with scale — a large could just mean two volatile assets. To see the pure relationship, standardise each asset by its own volatility. The result is the correlation matrix :

In matrix form, with the diagonal matrix of volatilities, — a unit-diagonal, symmetric, still-PSD matrix. It is the right object to look at, because every entry is on the same scale. In edgekit, ek.portfolio.correlation takes a dict of strategy R-streams and returns exactly this matrix — the diagnostic you use to check that the sleeves of a book are actually diversifying rather than secretly the same bet.

corr.py
import edgekit as ek

# books: {"orb": r_series, "sma": r_series, ...} of daily-R Series
rho = ek.portfolio.correlation(books)   # unit-diagonal correlation DataFrame
rho.loc["orb", "sma"]                   # pairwise correlation of two sleeves
A heatmap of a correlation matrix, with a unit diagonal and colour-coded off-diagonal correlations
A correlation matrix as a heatmap. The unit diagonal is trivial; the off-diagonal block is what you read. Warm clusters are groups of assets that move together — a hint that the effective number of independent bets is smaller than the number of assets.
!A correlation heatmap hides the effective bet count
Eyeballing pairwise correlations tells you which assets look related, but not how many independent risks you truly hold. Ten assets that all correlate at 0.8 are close to one bet, not ten. Answering “how many bets do I really have?” needs the eigenvalues of the matrix — which is exactly what PCA delivers.

Eigenvalues, eigenvectors, and PCA#

A symmetric PSD matrix has a special structure: it can be written entirely in terms of its eigenvectors and eigenvalues. An eigenvector is a direction that merely stretches without rotating, and its eigenvalue is the stretch factor:

The spectral theorem says any symmetric decomposes into an orthonormal set of such eigenvectors, collected as columns of an orthogonal matrix , with eigenvalues on a diagonal matrix :

This is Principal Component Analysis. Each eigenvector — a principal component — is a specific combination of the assets (a synthetic portfolio), and its eigenvalue is the variance of returns along that direction.

A 2D cloud of return points with two arrows from its centre along the principal-component directions, the longer arrow along the direction of greatest spread
Eigenvectors of the covariance, drawn on the data. The arrows point along the principal components — directions the matrix only stretches, not rotates — and each arrow's length is its eigenvalue, the variance along it. The long arrow is the highest-variance combination of the assets; the short one is what is left over and uncorrelated with it.
Derivation — the variance captured by a principal component is its eigenvalue

Consider the portfolio whose weights are a unit-length eigenvector, with . Its variance is the quadratic form from above; substitute the eigenvector equation :

So the variance of the return along principal component is exactly its eigenvalue. The largest eigenvalue belongs to the highest-variance direction in the whole asset space — no unit-weight portfolio has more variance, which is a one-line consequence of the Rayleigh quotient . Because the eigenvectors are orthogonal, the components are uncorrelated synthetic assets, and the trace identity says the eigenvalues simply repartition the total variance of the market into independent buckets.

The fraction of total variance explained by the first components is the cumulative eigenvalue ratio, and it is the honest answer to “how many independent risks am I holding?”:

A scree plot: variance explained by each principal component, with a steep drop after the first few and a cumulative curve
A PCA scree plot. The first component alone often captures most of the variance of a basket of correlated assets, and a handful more capture nearly all of it — the long tail of tiny eigenvalues is noise. The number of components needed to reach ~90% is the effective number of bets.
Scenario — how many bets are in the BTC/ETH/gold book?

Diagonalise the from the scenario above and its three eigenvalues come out roughly , , (they sum to , as the trace identity demands). The top eigenvector is almost entirelya long-BTC/long-ETH combination — that is “crypto beta,” and it alone is of the total variance. The second eigenvector is dominated by gold. The upshot: three tickers, but of your risk lives on one axis — you are running closer to one and a bitindependent bets, not three. That is the number a correlation heatmap cannot tell you but the eigenvalues can, and it is exactly what a risk manager means by “effective breadth.”

Why PCA finds risk factors#

The eigenvectors are not just a numerical convenience — for financial returns they line up with economically meaningful risk factors. When you run PCA on a basket of correlated assets you almost always see the same pattern:

  • The first component is “the market.” Its eigenvector has the same sign in every asset — a roughly equal-weight long that goes up when everything goes up. For equities it typically explains the large majority of the variance; it is the systematic beta that no amount of stock-picking diversifies away. This is the linear-algebra shadow of the market factor.
  • Later components are style/sector spreads. The second and third components have mixed signs — long one group, short another — and correspond to interpretable tilts (sector, size, value, duration). They are the market-neutral spreads a relative-value trader lives on.
  • The tail is noise. The many tiny eigenvalues correspond to idiosyncratic wiggle. A near-zero is precisely the near-singular direction that destabilises — over-fitting risk, made visible.

This is why factor models and PCA are two views of the same object: a factor model says returns are driven by a few common factors , and PCA discovers those factors as the top eigenvectors of without being told what they are. The next chapter takes the complementary route: given a factor you name in advance (the market), regress against it to measure exposure.

ObjectSymbolLinear-algebra roleedgekit
WeightsVector, sums to 1portfolio weights
CovarianceSymmetric PSD matrixek.optimize.sample_cov
CorrelationStandardised Σ, unit diagonalek.portfolio.correlation
Portfolio varianceQuadratic formek.optimize.portfolio_vol
Risk factorsEigenvectors / eigenvalues of ΣPCA (numpy.linalg.eigh)
The one idea to keep
Risk is a quadratic form ; diversification is the cancellation in its off-diagonal terms; and the eigenvectors of are the independent directions of that risk. Optimization (next chapter but one) is nothing more than minimising this quadratic form subject to linear constraints — which is where Lagrange multipliers enter.

Next: we have the covariance matrix and its factors — now we name a factor in advance and measure how much of a return it explains. Regression & factor models.