3.3.1 A portfolio is a vector
Engineers already think in vectors and matrices. The only new thing here is what the numbers mean: each coordinate is a bet, and the matrix that turns bets into risk is built from how assets move together.
A portfolio of assets is a list of numbers: the fraction of your capital in each one. Stack them in a column and you have the weight vector . Long positions are positive and shorts negative; a fully invested long-only book has weights that sum to 1, a dollar-neutral long-short book has weights that sum to 0. Stack the assets' simple returns for one period into a vector as well, and the portfolio's return is a dot product, . That is the "simple returns add across assets" rule of Session 2.3 — Returns, P&L, Compounding & Leverage, written as linear algebra.
Risk needs one more object. In Session 3.1 — Probability & Expected Value you met the variance of a sum of two bets: is plus a cross term, . With assets there are variances and distinct covariances, and the natural place to keep them is an table: the covariance matrix , whose entry in row , column is , with the variances on the diagonal. It is symmetric, because . The portfolio variance — the variance of the book's return — is then
An expression of the form is called a quadratic form: every term multiplies two weights. Fig 3.3.1 draws it as a grid. Each cell is visited by one pair of weights; the diagonal cells hold each position's own risk, the off-diagonal cells its co-movement with every other position. Add up all cells and you have the variance of the book.
A worked example with illustrative inputs: the classic 60/40 portfolio, 60% in equities with 16% annual volatility and 40% in bonds with 6%, correlated 0.2. The four cells add up to a variance of 0.0107 a year, a volatility of 10.35%. That is less than the weighted average of the two volatilities, 12.00%, because the two co-movement cells are small. The gap between the average of the parts and the whole is diversification, and you now have a formula for it.
Double every weight and the expected return doubles, while the variance quadruples and the volatility doubles. Return is linear in the weights, ; risk is quadratic, — and almost all of it lives off the diagonal. With positions there are own-risk cells and co-movement cells, so in any real book the covariances, not the individual volatilities, decide how risky you are.
3.3.2 From returns to risk: the pipeline
Everything in this session is one pipeline: a table of returns goes in, a covariance matrix comes out, you check it, you take it apart into eigenvectors, and you use the pieces. Each stage has one classic bug.
Step 1 is a matrix of returns: periods in the rows, assets in the columns, on aligned dates (Session 4.4 — Time, Calendars, As-of Joins & Bars builds the joins that make this true). Step 2 estimates . The sample covariance matrix, with the vector of average returns, is an average of outer products, one per day:
Each outer product gives any portfolio a squared, hence non-negative, contribution, so a matrix built this way from one complete table can never assign a negative variance to anything. Estimates built any other way can (§3.3.6), which is why step 3 exists. Steps 4 and 5 are §3.3.4 and §3.3.5.
The correlation matrix holds the correlations , with ones on the diagonal. It carries the same information as once you know the volatilities: with ,
Practitioners often estimate the two parts separately and reassemble them: volatilities move fast and can be forecast (Session 5.5 — Modeling Volatility), while correlations are often estimated over longer windows. The correlation matrix is also the better input to PCA when assets have very different volatilities (§3.3.5).
| Object | Shape | What it holds | In this session's lab |
|---|---|---|---|
| Returns matrix | one row per day, one column per asset | 2,520 days × 50 assets | |
| Weight vector | fraction of capital in each asset | 60/40 book: (0.6, 0.4) | |
| Covariance matrix | , symmetric | variances on the diagonal, covariances off it | 1,275 distinct entries to estimate |
| Correlation matrix | , symmetric | correlations, ones on the diagonal | holds exactly |
| Portfolio variance | the variance of the whole book | equal-weight book: volatility 16.9% true, 16.8% estimated | |
| Eigenvectors , eigenvalues | and | uncorrelated portfolios and their variances | the first carries 28.6% of all variance |
How good is a sample covariance matrix? With 10 years of daily data on 50 assets, the lab's estimate is off by 5.8% of 's size (the relative error in the Frobenius norm, the square root of the sum of squared entry errors); for normal returns the root-mean-square error can be computed exactly, and it is 6.4%. That sounds fine, and for the big structure it is. §3.3.7 shows why it is not fine for everything.
Estimating well is a whole session: Session 9.2 — Covariance Estimation covers the failure modes of the sample covariance and the shrinkage and denoising fixes. Session 9.4 — Factor Risk Models replaces the free entries with a factor model that has far fewer. In this session the sample covariance matrix is the default, and §3.3.6–§3.3.7 show where it breaks.
3.3.3 Risk as a quadratic form, and the diversification floor
Write the variance as a quadratic form and the limit of diversification falls out in one line: adding assets shrinks the diagonal of fig 3.3.1, but the off-diagonal cells never go away.
Take the simplest possible market: assets, all with volatility , every pair correlated , held in equal weights . Count the cells of fig 3.3.1. The diagonal cells are each worth , a total of . The off-diagonal cells are each worth , a total of . Add them:
Session 3.1 — Probability & Expected Value previewed this limit; the matrix form makes it a one-line count. The first term dies away as you add assets; the second never does. Fig 3.3.3 draws the anatomy.
With the variance falls like and the volatility like — the √n rule of Session 3.1. With the variance has a floor at . For single stocks with 30% volatility and a pairwise correlation of 0.3, no number of stocks takes an equal-weight portfolio below = 16.4%. Fig 3.3.4 plots the curves, computed in the lab both from the closed form and as with the full matrix (they agree to machine precision for every ).
Two summary numbers make the curve portable. The diversification ratio of Choueifaty & Coignard (2008) divides the weighted-average volatility of the holdings by the portfolio's volatility (the 60/40 book scores 1.16). Its square, in this equal-correlation market , is the effective number of independent bets: the number of uncorrelated assets that would give the same risk reduction. The ten correlated positions of Session 3.1 are worth only 2.70 independent bets, which is why they carried 1.92 times the risk of ten independent ones. Carver-style position sizing (Session 9.5 — Sizing, Vol Targeting & Allocating Across Strategies) applies the same idea to volatility-scaled positions and calls it the diversification multiplier.
| Common correlation | Volatility left, | Diversification ratio, | Effective bets, | ||
|---|---|---|---|---|---|
| 0 | 31.6% | 10.0% | 0 | 10.0 | unlimited |
| 0.3 | 60.8% | 55.4% | 54.8% | 1.80 | 3.33 |
| 0.6 | 80.0% | 77.7% | 77.5% | 1.29 | 1.67 |
Past a few dozen assets, adding names barely helps; only lower correlation lowers the floor. The floor is the risk every asset shares — which is why the first principal component in §3.3.5 turns out to be the market. A book of 200 stocks at an average correlation of 0.3 is worth fewer than 3.3 independent bets on "stocks", not 200.
Who is really driving the risk?
Because variance is a quadratic form, it splits exactly into one piece per position. Scale every weight by and the variance scales by ; Euler's theorem for such functions says that the weights times the gradient add back up to twice the function. Halve both sides:
Each term is the position's risk contribution: its weight times its covariance with the whole portfolio. For the 60/40 book the split is lopsided:
| Asset | Share of capital | Volatility | Share of portfolio variance |
|---|---|---|---|
| Equities | 60% | 16% | 90.3% |
| Bonds | 40% | 6% | 9.7% |
A "balanced" portfolio by capital is an equity portfolio by risk. Session 9.3 — Risk Parity & HRP turns this decomposition into portfolios that balance risk instead of capital.
3.3.4 Eigenvectors: the portfolios of risk
The covariance matrix mixes everything with everything. Its eigenvectors un-mix it into a set of portfolios that are uncorrelated with each other and whose variances add up to the total.
An eigenvector of is a direction that only stretches, ; the stretch factor is its eigenvalue. (The course reserves for multipliers, and an eigenvalue is exactly that: the number multiplies by.) A symmetric matrix has mutually orthogonal unit eigenvectors and real eigenvalues — the spectral theorem — so it factors as
with the eigenvectors as the columns of and the eigenvalues, sorted from largest to smallest, on the diagonal of .
Now read each eigenvector as a portfolio, with weights . Its variance is , and the covariance between two different ones is . So the eigen-decomposition re-expresses the market as uncorrelated eigen-portfolios, the -th with variance . Every portfolio is a mix of them, and its variance splits cleanly, with no cross terms:
The lab checks each of these identities to machine precision on its 50-asset market. It also finds that 99.0% of the equal-weight portfolio's variance sits in the first eigen-portfolio alone: an equal-weight book is, for risk purposes, a bet on one thing.
Fig 3.3.5 shows the geometry for two assets with equal volatility and correlation . The cloud of daily returns is an ellipse. Its long axis, both assets up together, is the first eigenvector, and the variance along it is . Its short axis, one up and the other down, is the second, with . The higher the correlation, the longer and thinner the ellipse — and the less variance is left in the spread. Rotating the coordinates to these axes turns two correlated return series into two uncorrelated ones without losing any variance.
Because each eigenvalue is the variance of a portfolio, none can be negative. A symmetric matrix is positive semi-definite (PSD) when for every ; equivalently, when all its eigenvalues are . Every true covariance matrix is PSD, and so is every sample covariance matrix computed from one complete table (it is an average of outer products). A zero (or near-zero) eigenvalue means some portfolio has (almost) no variance: a perfect hedge, or a redundant asset such as an ETF held alongside all its constituents. When all eigenvalues are strictly positive the matrix is positive definite and can be inverted. A negative eigenvalue means the "matrix" is not the covariance matrix of anything (§3.3.6).
Diagonalising re-expresses the market as uncorrelated portfolios whose variances add up to the total. The big eigenvalues are the risks everything shares; the small ones are the fine print — and, as §3.3.7 shows, the fine print is where estimation error hides.
3.3.5 PCA: the hidden structure of a market
Principal component analysis is the eigen-decomposition of a covariance matrix estimated from data. On a stock market the first component is almost always the market itself. On synthetic data you can check what PCA gets right, and what it cannot know.
Principal component analysis (PCA) estimates the covariance (or correlation) matrix of returns, eigen-decomposes it, and calls the eigenvectors principal components. PC1 is the unit-length weight vector with the largest variance; PC2 is the largest-variance direction uncorrelated with PC1; and so on down. Keep the first few and you have compressed correlated return series into a handful of uncorrelated factors plus what is left over.
The lab tests PCA where the answer is known. It simulates 10 years (2,520 trading days) of returns on 50 assets driven by 3 factors: a market factor that every asset loads on (loadings scattered around 1) and two style factors with loadings scattered around 0. Each factor has 15% annual volatility, and every asset adds 25% of its own noise. That is a factor model with known factor loadings — the structure Session 7.2 — CAPM, APT & Factor Regressions estimates by regression — so the true covariance matrix is known too:
It has 3 large eigenvalues and 47 identical small ones, each worth 1.1% of the total. Fig 3.3.6 compares the estimate with the truth.
PC1 carries 28.6% of the total variance (truth: 29.1%), and the first three together 46.4% (truth: 46.9%). After PC3 the spectrum drops to a floor; sampling noise spreads that floor out, so PC4 is estimated at 1.5% although its true share is 1.1%. The elbow is how you would guess the number of factors on real data, where nobody tells you.
PC1 is the market mode: 100% of its weights are positive, and they line up with the planted market loadings with a correlation of 0.958 (fig 3.3.7). Even the true PC1 correlates only 0.961 with the market loadings, because the style loadings are not exactly orthogonal to them, so the top eigenvector mixes a little of each in.
How close is close? Sampling theory for PCA (Anderson, 1963) gives the expected squared angle between the sample PC1 and the true one for normal returns and long samples:
In words: an eigenvector is pinned down well when you have many observations and its eigenvalue stands well clear of the others. Here the formula predicts a typical angle of 2.1°, and the lab's sample PC1 sits 1.8° from the truth. Across 300 simulated decades from the same market, the average is 0.00137 ± 0.00003 against a prediction of 0.00130, inside the four standard errors the lab asserts. The same theory predicts what share of the market loading vector falls outside the estimated three-factor subspace: 0.081%, against 0.081% simulated.
What PCA cannot know is the labels. PC2 and PC3 together span the same plane as the two style factors — at least 99.7% of each loading vector lies in the space of the top three components — but PC2 correlates only 0.79 and 0.70 with the two style loadings. It is a blend, and any rotation inside that plane explains the data equally well. PCA recovers the subspace, not the factors' names. Naming them ("value", "momentum") takes information PCA never sees, such as company characteristics, which is how the risk models of Session 9.4 — Factor Risk Models are built. The lab's code, with the eigenvector signs fixed (an eigenvector's sign is arbitrary, and numerical libraries return either):
def eig_desc(m: np.ndarray) -> tuple[np.ndarray, np.ndarray]: """Eigenvalues (largest first) and eigenvectors of a symmetric matrix, signs fixed so sum(v) ≥ 0.""" lam, vec = np.linalg.eigh(m) # eigh: for symmetric matrices; ascending order lam, vec = lam[::-1], vec[:, ::-1] vec = vec * np.where(vec.sum(axis=0) < 0, -1.0, 1.0) return lam, vec S = R.cov().to_numpy() # sample covariance (divides by T − 1) lam_s, V_s = eig_desc(S) assert np.allclose(V_s @ np.diag(lam_s) @ V_s.T, S) # S = V Λ V' pcs = (R - R.mean()).to_numpy() @ V_s # returns of the eigen-portfolios
Real markets look the same at the top. In Laloux et al.'s (1999) study of 406 S&P 500 stocks over 1991–96, the largest eigenvalue of the correlation matrix sat far above anything pure noise could produce, and its eigenvector had "roughly equal components on all the N stocks" — the market — while 94% of the eigenvalues were indistinguishable from noise. Session 9.2 — Covariance Estimation makes that "indistinguishable from noise" precise.
PCA on weights each asset by its variance, so in a mixed book the most volatile assets dominate PC1: a crypto coin swamps a bond fund. Run PCA on the correlation matrix (standardised returns) when volatilities differ a lot and you want the co-movement structure; run it on when the units are comparable and you care about risk in money.
PCA comes back three times: Session 8.3 — Pairs Trading & Statistical Arbitrage trades the residuals left after removing the top components (PCA-residual statistical arbitrage), Session 9.4 — Factor Risk Models builds factor risk models in which a few factors explain the common risk, and Session 11.2 — Duration, Convexity & Curve PCA finds that three components — level, slope and curvature — drive almost every yield curve.
3.3.6 When the matrix lies: positive semi-definiteness
A covariance matrix must be positive semi-definite, because variances cannot be negative. A matrix estimated one entry at a time can break that rule without any error message — and an optimizer will find the break before you do.
Correlations cannot be chosen freely. If asset A is 0.9-correlated with both B and C, then B and C are forced to be at least 0.62 correlated: the smallest value that keeps the matrix PSD is . Type in −0.9 instead and the "correlation matrix" has an eigenvalue of −0.80. Its eigenvector — short A, long B and long C in equal parts — is a portfolio with negative variance. No market can produce it, but a minimum-variance optimizer handed this matrix will pile into that portfolio without limit, because every extra unit "reduces" the risk.
Nobody types an impossible correlation on purpose. The common way to get a non-PSD matrix is missing data. The lab takes one year (252 days) of returns on the same 50 assets and blanks out each asset on a random 40% of days, as happens with illiquid assets, different exchange holidays or a flaky feed. Listwise deletion — keep only the days on which every asset has a price — leaves 0 days. pandas' DataFrame.cov() instead uses pairwise deletion by default: each entry is estimated from the days on which that particular pair overlaps, 92 days on average. Every entry is a sensible estimate; the documentation itself warns that the matrix as a whole "is not guaranteed to be positive semi-definite".
S_pw = X_obs.cov().to_numpy() # pandas default: pairwise-complete lam_pw, V_pw = eig_desc(S_pw) n_neg = int((lam_pw < 0).sum()) assert n_neg >= 1, "pairwise estimate should not be PSD in this design" ... S_clip = V_pw @ np.diag(np.clip(lam_pw, 0, None)) @ V_pw.T
Here 9 of the 50 eigenvalues are negative. Scale the worst eigen-portfolio to 100% gross exposure and the matrix assigns it an annual variance of −0.0007 — while its true volatility is 4.4%.
The standard repair is eigenvalue clipping, the last line of the snippet: set the negative eigenvalues to zero and rebuild. The result is the nearest PSD matrix in the Frobenius norm, and because that is a projection onto a convex set that contains the true , it can only move the estimate closer to the truth (the lab asserts this). But read the small print:
- Valid is not accurate. The error against the true goes from 34.6% to 33.9%. The matrix was noisy because each entry saw only about 92 days, and clipping does not fix that.
- Clipped directions now claim zero risk. The worst portfolio's estimated variance is now exactly 0, and its true volatility is still 4.4%. An optimizer will treat those directions as free hedges. Floor the eigenvalues at a small positive value instead, or better, use a shrinkage estimator (Session 9.2) or a factor model (Session 9.4).
- It moves the variances too. Here by up to 5.3%. For a correlation matrix you also need the unit diagonal back: that is the nearest correlation matrix problem, which Higham (2002) solves by alternating projections.
- It treats the symptom. The cause is the data: align it (Session 4.4) and check it (Session 4.6) before you estimate anything.
| Situation | What breaks | What to do |
|---|---|---|
| Missing or misaligned data, pairwise deletion | negative eigenvalues; "negative-variance" portfolios | align the data; a model-based estimate; clip only as a last resort |
| Hand-edited or stress-scenario correlations | impossible combinations, as in the 3 × 3 example above | check the smallest eigenvalue after every edit; nearest correlation matrix |
| Fewer periods than assets () | PSD but singular: zero eigenvalues, no inverse | shrinkage or a factor model; more data per asset |
| A redundant asset (an ETF plus all its constituents) | a zero or near-zero eigenvalue | drop the redundant asset |
The call succeeded, the diagonal is positive, every pairwise correlation looks plausible — and the matrix can still be invalid. Every time you build from anything other than one complete, aligned table, check the smallest eigenvalue: np.linalg.eigvalsh(S)[0] >= -tol. It is a one-line unit test, and it catches the bug before an optimizer does.
3.3.7 The condition number: why inverting Σ amplifies noise
Every tool that uses divides by the small eigenvalues — the directions your data measured worst. The condition number tells you how much that division can magnify your estimation error.
The condition number of a positive definite matrix is the ratio of its largest to its smallest eigenvalue:
(Numerical analysts write ; this course reserves for the borrow fee of Session 2.2.) It bounds how errors are amplified: if you solve and carries a relative error , the solution can carry a relative error up to (Golub & Van Loan, 2013, §2.6). The reason is visible in the eigen-decomposition: , so inverting flips the spectrum and the smallest eigenvalue becomes the largest multiplier. The minimum-variance portfolio , mean-variance weights and regression coefficients all lean hardest on the directions with the least variance.
In a sample, those are exactly the directions estimated worst, and wrong in a predictable way: the smallest sample eigenvalues are biased down and the largest up, more so as the number of observations per asset, , falls. Random-matrix theory — the Marchenko–Pastur law, which Session 9.2 puts to work — predicts by how much. So a sample is worse conditioned than the truth: the lab's true matrix has = 25.7, and even 10 years of daily data give an estimate at 33.1.
The lab measures the damage. For each ratio it draws 500 samples from the same 50-asset market, estimates , and builds the sample global minimum-variance portfolio . It then compares the variance the portfolio promises (measured with ) and the variance it delivers (measured with the true ) with the true minimum variance . For normal returns both have exact expectations, derived in the lab from the Wishart distribution of (Kan & Smith, 2008, develop the full finite-sample theory):
| Days | Mean | Promised variance ÷ true minimum | Delivered variance ÷ true minimum (exact) | Weight error | ||
|---|---|---|---|---|---|---|
| 1.2 | 60 | 2,864 | 111 | 0.17 | 6.37 (6.44) | 250% |
| 2 | 100 | 265 | 10.3 | 0.51 | 2.01 (2.00) | 111% |
| 5 | 250 | 78 | 3.0 | 0.81 | 1.25 (1.25) | 55% |
| 10 | 500 | 52 | 2.0 | 0.90 | 1.11 (1.11) | 37% |
| 50 | 2,500 | 34 | 1.3 | 0.98 | 1.02 (1.02) | 16% |
The weight error is the total absolute difference between the estimated and the true minimum-variance weights, as a share of the true weights' gross size. The simulated ratios match the exact formulas within four standard errors at every , which the lab asserts.
Read the row as a story: 100 days of data on 50 assets. The optimizer promises about half the true minimum variance and delivers about twice it, so the realised volatility is 1.99 times what was promised, and the weights miss by 111% of their own gross size. Nothing about the code is buggy. It is the arithmetic of dividing by eigenvalues that the data could not pin down.
Inverting a sample covariance matrix points an amplifier at its smallest, noisiest eigenvalues. The defences are more observations per asset, a better-conditioned estimate (shrinkage, factor models) and constraints on the weights. Session 9.1 — Mean-Variance & Why It Breaks shows why unconstrained mean-variance optimisation is called an error maximiser, and Session 3.4 — Convex Optimization shows how constraints tame the optimizer.
The standard fix pulls toward a well-conditioned target: Ledoit–Wolf shrinkage (Session 9.2), a form of the shrinkage of Session 5.3. It raises the small eigenvalues and lowers the large ones — the exact opposite of the sample bias.
3.3.8 Try it
Open the companion lab, predict before you run, and change one thing at a time.
1. Predict, then check. With = 50, for what number of days does the formula say the minimum-variance portfolio delivers 1.25 times the true minimum variance? Solve it by hand, then compare with the row of the table in §3.3.7.
2. Less noise, stronger market. In section 3 of the lab, change IDIO_VOL from 0.25 to 0.10 and rerun. Before you look, predict three things: does PC1's share of variance go up or down, does the predicted PC1 angle (pc1_angle_deg_theory) grow or shrink, and do you get more or fewer negative eigenvalues in section 7? (Hint: is .)
3. Less missing data. Set P_MISS to 0.30 in section 7. How many negative eigenvalues are left, and does clipping help more or less?
Success looks like this: every assert still passes, and you can explain each change from a formula on this page.
3.3.9 Glossary · Check yourself · Go deeper
Check yourself
1. Two assets, each with 20% volatility and correlation 0.5, held 50/50. What is the portfolio's volatility?
2. You hold 200 stocks in equal weights; average volatility 30%, average pairwise correlation 0.25. Roughly how volatile is the book, and what does doubling it to 400 stocks buy you?
3. A colleague's risk model has eigenvalues 12.1, 3.4, 0.9, …, −0.02. What does the last one mean, and what do you do?
4. PCA on 500 stocks: PC1 has all-positive weights and explains 35% of the variance. Your manager says "PC2 is obviously the value factor." What is right and what is overreach?
5. You have 60 days of data on 100 stocks and want the minimum-variance portfolio. What goes wrong?
6. A 60/40 portfolio holds 40% of its capital in bonds. Why is "40% bonds" a poor description of its risk?
1. Write portfolio_vol(w, vols, corr) that builds and returns . Unit-test it against the two-asset formula (agreement to ) and against the sample volatility of simulated portfolio returns (agreement within four standard errors).
2. Add a cell to the lab that runs eig_desc on the correlation matrix C instead of S. Compare PC1's share of variance and PC1's weights with the covariance version, and explain the difference in one sentence.
3. Prepare for Session 3.4: write down the problem "minimise subject to " and check numerically, with the lab's true , that no random fully invested portfolio beats . Session 3.4 derives why.
4. Read Laloux et al. (1999), four pages, below.
📚 Go deeper
- Strang, G. (2016). Introduction to Linear Algebra, 5th ed., §6.4 "Symmetric Matrices", §6.5 "Positive Definite Matrices", §7.3 "Principal Component Analysis (PCA by the SVD)" and §12.2 "Covariance Matrices and Joint Probabilities". Wellesley-Cambridge Press. ISBN 978-0-9802327-7-6
- Jolliffe, I. T. & Cadima, J. (2016). "Principal component analysis: a review and recent developments." Philosophical Transactions of the Royal Society A 374(2065), 20150202. doi:10.1098/rsta.2015.0202
- Meucci, A. (2009). "Managing Diversification." Risk 22(5), 74–79. Effective number of bets from principal components. SSRN 1358533
- Higham, N. J. (2002). "Computing the nearest correlation matrix — a problem from finance." IMA Journal of Numerical Analysis 22(3), 329–343. doi:10.1093/imanum/22.3.329
Sources for the numbers
- Anderson, T. W. (1963). "Asymptotic Theory for Principal Component Analysis." Annals of Mathematical Statistics 34(1), 122–148. doi:10.1214/aoms/1177704248
- Kan, R. & Smith, D. R. (2008). "The Distribution of the Sample Minimum-Variance Frontier." Management Science 54(7), 1364–1380. doi:10.1287/mnsc.1070.0852
- Laloux, L., Cizeau, P., Bouchaud, J.-P. & Potters, M. (1999). "Noise Dressing of Financial Correlation Matrices." Physical Review Letters 83(7), 1467–1470. doi:10.1103/PhysRevLett.83.1467
- Choueifaty, Y. & Coignard, Y. (2008). "Toward Maximum Diversification." Journal of Portfolio Management 35(1), 40–51. doi:10.3905/JPM.2008.35.1.40
- Golub, G. H. & Van Loan, C. F. (2013). Matrix Computations, 4th ed., §2.6 "The Sensitivity of Square Systems". Johns Hopkins University Press. ISBN 978-1-4214-0794-4
- pandas development team (2026). "pandas.DataFrame.cov", pandas 3.0 API reference (notes on missing data and positive semi-definiteness). https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.cov.html
Educational material only — not investment, legal or tax advice. Backtests and simulations are hypothetical. Disclaimer · Text CC BY 4.0 · Code MIT.