Linear Algebra for Quants: Vectors, Matrices, and Why They Matter¶
Behind every portfolio optimization model, factor model, risk decomposition, PCA analysis, and machine learning algorithm in finance sits the same mathematical foundation: linear algebra. It is the language of multi-asset finance, the grammar of quantitative strategies, and the operating system of every modern quant library from NumPy to SciPy to TensorFlow. You cannot do serious quantitative work without it.
Most finance professionals know linear algebra the way they know French: enough to read a menu, not enough to discuss philosophy. That is usually fine until the day you need to explain why your risk model diagonalizes to identify factor exposures, or why the Capital Asset Pricing Model is really just a projection onto a line, or why PCA on stock returns reveals market and sector structure. At that moment, vague memories of matrix multiplication from college are not going to save you.
This article is not a full linear algebra textbook. It is a targeted tour of the concepts that actually come up in quantitative finance: vectors, matrices, multiplication, inversion, eigenvalues, eigenvectors, and their applications to portfolio theory, risk management, and factor analysis. Every concept is illustrated with concrete numerical examples you can run in Python.
Linear algebra is the mathematics of parallel thinking. Once you see portfolios as vectors and risk as matrices, every quantitative problem starts to look the same.
Why Linear Algebra Matters in Finance¶
Consider a simple question: what is the variance of a portfolio of 100 stocks?
Without linear algebra, you write a triple-nested sum. You track weights, pairwise correlations, individual variances, and sum terms by hand. For 100 assets, that is 10,000 pairwise terms. Write the code, verify it is correct, cross your fingers.
With linear algebra, the answer is one line:
portfolio_variance = w.T @ Sigma @ w
One matrix multiplication, executed in compiled code, correct by construction.
The same pattern appears everywhere in finance:
- Portfolio optimization is a quadratic form
- Factor models are matrix decompositions
- PCA is an eigenvalue problem
- OLS regression is a projection
- Risk budgeting is matrix arithmetic
- Stress testing is vector transformation
Every time you write messy loops over assets, dates, or factors, there is probably a cleaner linear algebra formulation sitting just underneath.
Key Insight: Linear algebra does not make calculations easier. It makes them thinkable. Once a problem is expressed as “solve Ax = b” or “find the eigenvectors of Sigma,” you have access to 200 years of mathematical tools and centuries of numerical methods. Trying to do the same thing with loops is working without a map.
Excel is a linear algebra engine with terrible ergonomics. Once you stop fighting it and start using matrices, everything gets shorter and clearer.
Vectors: The Building Block¶
A vector is an ordered list of numbers. In finance, a vector typically represents something like:
- Portfolio weights:
w = [0.25, 0.30, 0.15, 0.20, 0.10](5 assets) - Returns on a single day:
r = [0.012, -0.005, 0.008, 0.001, -0.003] - Factor exposures:
beta = [1.2, 0.8, 1.5](beta to market, size, value)
Vector Operations¶
import numpy as np
# Weights and returns for a 5-asset portfolio
w = np.array([0.25, 0.30, 0.15, 0.20, 0.10])
r = np.array([0.012, -0.005, 0.008, 0.001, -0.003])
# Element-wise operations
sum_w = w.sum() # 1.00
doubled = 2 * w # Scalar multiplication
# Dot product (inner product): portfolio return
portfolio_return = np.dot(w, r)
# Equivalent: (w * r).sum()
# Equivalent: w @ r
The Dot Product: The Single Most Important Operation¶
The dot product of two vectors is:
a . b = a[0]*b[0] + a[1]*b[1] + ... + a[n-1]*b[n-1]
Geometrically, it measures how “aligned” two vectors are. When the dot product is:
- Positive: the vectors point in similar directions
- Zero: they are perpendicular (orthogonal)
- Negative: they point in opposite directions
In finance, the dot product answers questions like:
- Portfolio return: weights . returns
- Portfolio beta: weights . betas
- Correlation-weighted sum: any aggregation where some things matter more than others
Vector Norms¶
The norm measures the length of a vector:
||v||_2 = sqrt(v . v) = sqrt(v[0]^2 + v[1]^2 + ... + v[n-1]^2)
np.linalg.norm(w) # L2 norm (Euclidean length)
np.linalg.norm(w, 1) # L1 norm (sum of absolute values)
In portfolio theory, the L1 norm of weights (sum of absolute values) is the gross exposure. The sum of weights is the net exposure. They differ when you have short positions.
Matrices: Vectors on Steroids¶
A matrix is a two-dimensional array of numbers. In finance, matrices represent things like:
- Returns history: rows = dates, columns = assets
- Covariance matrix: element (i, j) = covariance of asset i with asset j
- Correlation matrix: normalized covariance matrix
- Factor loading matrix: rows = assets, columns = factor exposures
Creating Matrices¶
# 3 days, 4 assets
returns = np.array([
[0.012, -0.005, 0.008, 0.001],
[-0.003, 0.007, -0.002, 0.004],
[0.009, 0.001, 0.006, -0.001]
])
print(returns.shape) # (3, 4)
Matrix Dimensions¶
For any calculation, dimensions must be compatible. For a matrix A with shape (m, n):
- m rows
- n columns
- To multiply by vector v: v must have length n, result has length m
- To multiply by matrix B: B must have shape (n, k), result has shape (m, k)
Dimension errors are the most common NumPy bug. When in doubt, print shapes.
Matrix Multiplication¶
Matrix multiplication is not element-wise. It combines rows of the first with columns of the second via dot products.
A = np.array([[1, 2], [3, 4]]) # 2x2
B = np.array([[5, 6], [7, 8]]) # 2x2
# Element-wise (Hadamard product)
A * B
# [[ 5, 12],
# [21, 32]]
# Matrix multiplication
A @ B
# [[19, 22],
# [43, 50]]
The @ operator (Python 3.5+) is the standard for matrix multiplication. Alternatively, use np.matmul() or np.dot().
Example: Portfolio Return Time Series¶
# Weights for 4 assets
w = np.array([0.25, 0.30, 0.25, 0.20])
# Daily returns matrix (3 days, 4 assets)
returns = np.array([
[0.012, -0.005, 0.008, 0.001],
[-0.003, 0.007, -0.002, 0.004],
[0.009, 0.001, 0.006, -0.001]
])
# Portfolio returns for each day
portfolio_returns = returns @ w
print(portfolio_returns)
# [0.00335, 0.001, 0.00545]
One matrix-vector multiplication gives you the full portfolio return history. No loops needed.
The Transpose¶
The transpose flips a matrix across its diagonal: rows become columns.
A = np.array([[1, 2, 3], [4, 5, 6]]) # 2x3
A.T
# [[1, 4],
# [2, 5],
# [3, 6]] # 3x2
For a vector, transpose conceptually switches between row and column form. In NumPy, 1D arrays do not distinguish; shape remains the same. For rigorous linear algebra, use 2D arrays.
Use in Finance: Quadratic Forms¶
x.T @ A @ x = scalar
This is how you get portfolio variance:
# Covariance matrix (4 assets)
Sigma = np.array([
[0.04, 0.02, 0.01, 0.00],
[0.02, 0.05, 0.02, 0.01],
[0.01, 0.02, 0.03, 0.01],
[0.00, 0.01, 0.01, 0.02]
])
w = np.array([0.25, 0.30, 0.25, 0.20])
portfolio_variance = w.T @ Sigma @ w
portfolio_std = np.sqrt(portfolio_variance)
print(f"Portfolio std: {portfolio_std:.4f}")
Key Insight: The expression
w.T @ Sigma @ wis the quadratic form that shows up everywhere in finance. Portfolio variance. Risk factor contributions. Mahalanobis distance. Minimum variance optimization. It is worth burning this pattern into muscle memory.
The Identity Matrix and Inverse¶
The identity matrix I has 1s on the diagonal and 0s elsewhere. It acts like “1” for matrices: A @ I = A.
I = np.eye(4) # 4x4 identity
The inverse of matrix A, denoted A^(-1), is the matrix such that A @ A^(-1) = I.
A_inv = np.linalg.inv(A)
Not every matrix is invertible. A matrix is invertible if and only if its determinant is non-zero and its columns are linearly independent.
Solving Linear Systems¶
Instead of computing the inverse (slow, numerically unstable), use np.linalg.solve():
# Solve Ax = b
x = np.linalg.solve(A, b)
# Equivalent to: x = A_inv @ b, but faster and more accurate
This is how you solve for portfolio weights in minimum variance optimization.
Covariance and Correlation Matrices¶
The covariance matrix is arguably the single most important matrix in quantitative finance.
Definition¶
For assets with returns r_1, r_2, ..., r_n:
Sigma[i, j] = Cov(r_i, r_j)
Properties:
- Symmetric: Sigma[i, j] = Sigma[j, i]
- Positive semi-definite: x.T @ Sigma @ x >= 0 for any vector x
- Diagonal elements: Sigma[i, i] = Var(r_i) = individual variances
Computing from Returns Data¶
import pandas as pd
import yfinance as yf
tickers = ["AAPL", "MSFT", "GOOGL", "AMZN", "NVDA"]
data = yf.download(tickers, start="2024-01-01", end="2026-01-01")["Close"]
returns = data.pct_change().dropna()
# Covariance matrix (daily)
Sigma = returns.cov()
print(Sigma)
# Annualized (multiply by 252)
Sigma_annual = Sigma * 252
# Correlation matrix
corr = returns.corr()
print(corr)
From Covariance to Correlation¶
std = np.sqrt(np.diag(Sigma))
corr_from_cov = Sigma / np.outer(std, std)
# Same as returns.corr()
Portfolio Optimization: The Mathematical Heart of Modern Finance¶
Harry Markowitz won the Nobel Prize for formalizing this as a matrix problem.
Minimum Variance Portfolio¶
Find the weights w that minimize variance subject to weights summing to 1:
minimize: w.T @ Sigma @ w
subject to: sum(w) = 1
Analytical solution using Lagrangian:
w_minvar = (Sigma^-1 @ 1) / (1.T @ Sigma^-1 @ 1)
Python Implementation¶
import numpy as np
# Example: 5-asset covariance matrix
Sigma = np.array([
[0.04, 0.01, 0.02, 0.00, 0.01],
[0.01, 0.05, 0.02, 0.01, 0.01],
[0.02, 0.02, 0.03, 0.01, 0.01],
[0.00, 0.01, 0.01, 0.02, 0.01],
[0.01, 0.01, 0.01, 0.01, 0.03]
])
ones = np.ones(5)
inv_Sigma = np.linalg.inv(Sigma)
# Minimum variance weights
w_minvar = inv_Sigma @ ones / (ones @ inv_Sigma @ ones)
print(f"Min variance weights: {w_minvar.round(4)}")
print(f"Sum: {w_minvar.sum():.4f}")
# Minimum portfolio variance
min_var = w_minvar @ Sigma @ w_minvar
print(f"Min variance: {min_var:.6f}")
print(f"Min std: {np.sqrt(min_var):.4f}")
Mean-Variance Optimization (Markowitz)¶
With expected returns mu and target return mu_target:
mu = np.array([0.10, 0.12, 0.08, 0.06, 0.09])
mu_target = 0.09
# Mean-variance optimal weights (analytical)
ones = np.ones(5)
A = ones @ inv_Sigma @ ones
B = ones @ inv_Sigma @ mu
C = mu @ inv_Sigma @ mu
D = A * C - B ** 2
# Lagrange multipliers
lambda_1 = (C - B * mu_target) / D
lambda_2 = (A * mu_target - B) / D
w_optimal = lambda_1 * (inv_Sigma @ ones) + lambda_2 * (inv_Sigma @ mu)
print(f"Optimal weights: {w_optimal.round(4)}")
Harry Markowitz took a branch of applied math that already existed and said “you can use this to invest.” He won a Nobel Prize. Sometimes the hard part is noticing the connection.
Eigenvalues and Eigenvectors¶
An eigenvector of a matrix A is a non-zero vector v such that:
A @ v = lambda * v
Where lambda is a scalar called the eigenvalue.
Geometrically, eigenvectors are the “special directions” where the matrix only stretches or shrinks, not rotates. Eigenvalues tell you by how much.
Computing Eigenvalues and Eigenvectors¶
# For a covariance matrix
eigenvalues, eigenvectors = np.linalg.eigh(Sigma)
# np.linalg.eigh is faster and more accurate for symmetric matrices
# Sort by magnitude (largest first)
idx = np.argsort(eigenvalues)[::-1]
eigenvalues = eigenvalues[idx]
eigenvectors = eigenvectors[:, idx]
print("Eigenvalues:", eigenvalues)
print("Sum:", eigenvalues.sum()) # Equals trace of Sigma (sum of variances)
Interpretation: Principal Components¶
The eigenvectors of a covariance matrix are the principal components. The largest eigenvalue corresponds to the direction of maximum variance in the data.
For stock returns, the first principal component usually represents the market factor: the direction that explains the most variance across all stocks. Subsequent components often represent sector factors, style factors, and idiosyncratic noise.
PCA in Practice¶
# Standardize returns (zero mean, unit variance)
returns_std = (returns - returns.mean()) / returns.std()
# Covariance matrix of standardized returns = correlation matrix
corr = returns_std.cov()
# Eigendecomposition
evals, evecs = np.linalg.eigh(corr)
# Sort descending
idx = np.argsort(evals)[::-1]
evals = evals[idx]
evecs = evecs[:, idx]
# Variance explained by each component
variance_explained = evals / evals.sum()
print("Variance explained by first 3 PCs:")
for i in range(3):
print(f" PC{i+1}: {variance_explained[i]:.1%}")
# Project returns onto principal components
pc_scores = returns_std @ evecs
Typical results on a diversified stock portfolio:
- PC1 explains 40-70% of variance (the market)
- PC2 and PC3 explain 5-15% each (sector or style factors)
- Remaining components are mostly noise
Key Insight: PCA reveals hidden structure in financial data. The first few principal components often correspond to interpretable economic factors (market, growth vs value, cyclicality). This is the mathematical foundation of factor models, risk parity, and many systematic trading strategies.
Singular Value Decomposition (SVD)¶
SVD is the Swiss Army knife of linear algebra. For any matrix A:
A = U @ S @ V.T
Where:
- U: orthogonal matrix (left singular vectors)
- S: diagonal matrix of singular values (non-negative)
- V: orthogonal matrix (right singular vectors)
SVD works for any matrix (not just square), is numerically stable, and generalizes eigendecomposition.
Uses in Finance¶
- Dimension reduction: keep only the largest singular values (PCA)
- Solving ill-conditioned systems: use pseudo-inverse via SVD
- Matrix approximation: find the best low-rank approximation
- Cleaning covariance matrices: filter noise by truncating small singular values
# SVD of returns matrix
U, S, Vt = np.linalg.svd(returns.values, full_matrices=False)
# Low-rank approximation (keep top k components)
k = 3
returns_approx = U[:, :k] @ np.diag(S[:k]) @ Vt[:k, :]
Linear Regression via Matrix Algebra¶
Ordinary Least Squares regression has a closed-form matrix solution.
Setup¶
Given:
- X: feature matrix (n observations x k features)
- y: target vector (n observations)
We want to find coefficients beta such that X @ beta ≈ y.
The Normal Equation¶
beta = (X.T @ X)^(-1) @ X.T @ y
Or more numerically:
beta = np.linalg.solve(X.T @ X, X.T @ y)
Example: Single-Factor Model (CAPM)¶
# Market returns (SPY) and stock returns (AAPL)
market = returns["SPY"].values
stock = returns["AAPL"].values
# Design matrix: [1, market] for intercept + slope
X = np.column_stack([np.ones(len(market)), market])
# OLS
beta = np.linalg.solve(X.T @ X, X.T @ stock)
alpha_estimate, beta_market = beta
print(f"Alpha: {alpha_estimate:.4f}")
print(f"Beta: {beta_market:.4f}")
Multi-Factor Regression¶
For a 3-factor model (Fama-French):
# X has columns: intercept, market, SMB, HML
X = np.column_stack([
np.ones(n),
market_returns,
smb_returns,
hml_returns
])
# Factor loadings
loadings = np.linalg.solve(X.T @ X, X.T @ stock_returns)
Matrix Decompositions: The Quant Toolbox¶
Beyond eigendecomposition and SVD, a few other decompositions come up constantly.
Cholesky Decomposition¶
For a positive-definite symmetric matrix A:
A = L @ L.T
Where L is lower triangular.
Use case: simulating correlated random variables:
# 3-asset covariance matrix
cov = np.array([
[0.04, 0.02, 0.01],
[0.02, 0.05, 0.02],
[0.01, 0.02, 0.03]
])
L = np.linalg.cholesky(cov)
# Simulate correlated returns
n_sim = 10000
uncorrelated = np.random.randn(n_sim, 3)
correlated = uncorrelated @ L.T
# Verify
print("Simulated cov:")
print(np.cov(correlated.T).round(4))
print("Original cov:")
print(cov.round(4))
QR Decomposition¶
For any matrix A:
A = Q @ R
Where Q is orthogonal and R is upper triangular.
Use case: numerically stable OLS regression without computing X.T @ X.
LU Decomposition¶
A = L @ U
Where L is lower triangular and U is upper triangular.
Use case: solving multiple linear systems efficiently.
Common Pitfalls¶
Shape mismatches. The #1 cause of linear algebra bugs. Print shapes. Always print shapes.
Computing inverses unnecessarily.
np.linalg.inv(A) @ bis slower and less accurate thannp.linalg.solve(A, b). Usesolvewhenever possible.Ill-conditioned matrices. A nearly-singular covariance matrix produces wildly unstable portfolio weights. Check the condition number:
np.linalg.cond(Sigma). Values above 10^10 suggest numerical trouble.Using
eiginstead ofeighfor symmetric matrices.eighis faster, more accurate, and guarantees real eigenvalues for symmetric matrices. For covariance matrices, always useeigh.Forgetting transposes. In quant finance,
w.T @ Sigma @ wvsw @ Sigma @ wgives the same number for 1D arrays (NumPy handles orientation automatically), but fails with matrices. Think carefully about dimensions.Confusing row vs column vectors. Mathematically, a column vector has shape (n, 1) and a row vector has shape (1, n). NumPy often collapses these to shape (n,). When precision matters, use explicit 2D shapes.
Element-wise vs matrix multiplication.
A * Bis element-wise.A @ Bis matrix multiplication. Using*by mistake gives silent, wrong answers.
Wrapping Up¶
Linear algebra is the universal language of quantitative finance. Every portfolio, risk model, factor strategy, and machine learning algorithm eventually boils down to vectors, matrices, and the operations you can do on them. The concepts are not difficult. The payoff is enormous.
The essentials: vectors represent portfolios or returns. Matrices represent covariances, correlations, or factor loadings. Dot products aggregate across assets or time. Matrix multiplication composes transformations. Eigendecomposition reveals hidden factor structure. SVD generalizes it all.
Once you internalize these patterns, quantitative code becomes dramatically shorter. A portfolio variance calculation is one line. A minimum variance optimization is two. A PCA factor model is three. The code matches the math, and the math is the same math that Markowitz, Sharpe, and every serious quant since has used.
Linear algebra is the math that finance people pretend to know in interviews and actually use in production. Learn it once, use it everywhere.
Cheat Sheet¶
Key Questions & Answers¶
What is the difference between element-wise and matrix multiplication?¶
Element-wise multiplication (
A * B) multiplies corresponding elements; shapes must match. Matrix multiplication (A @ B) combines rows of A with columns of B via dot products; inner dimensions must match. In NumPy, use*for element-wise and@for matrix multiplication.
How do I compute portfolio variance?¶
Given weights
wand covariance matrixSigma:portfolio_variance = w.T @ Sigma @ w. This is a quadratic form that aggregates individual variances and pairwise covariances. It works for any number of assets in one line.
What are eigenvalues and eigenvectors used for in finance?¶
The eigenvectors of a covariance matrix are the principal components: directions of maximum variance. The first PC usually represents the market factor, subsequent PCs capture sector or style factors. This underpins PCA, factor models, risk parity, and many systematic strategies.
Why use Cholesky decomposition in Monte Carlo simulations?¶
To generate correlated random variables with a specified covariance structure. If
cov = L @ L.Tis the Cholesky decomposition, then multiplying uncorrelated standard normals byL.Tproduces samples with covariancecov. Essential for multi-asset simulation.
Key Concepts at a Glance¶
| Concept | Summary |
|---|---|
| Vector | Ordered list of numbers (portfolio weights, returns) |
| Matrix | 2D array (covariance, factor loadings) |
| Dot product | a . b = sum(a[i]*b[i]); aggregates vectors |
| Matrix multiplication | A @ B; combines linear transformations |
| Transpose | A.T; swap rows and columns |
| Quadratic form | x.T @ A @ x; portfolio variance formula |
| Identity | np.eye(n); the “1” of matrices |
| Inverse | A^(-1); use np.linalg.solve instead |
| Covariance matrix | Symmetric, positive semi-definite, central in finance |
| Eigenvalue | Scaling factor for eigenvector direction |
| Eigenvector | Special direction: A @ v = lambda * v |
| Principal Component | Eigenvector of covariance matrix |
| SVD | A = U @ S @ V.T; universal decomposition |
| Cholesky | A = L @ L.T; for correlated random simulation |
| OLS | beta = (X.T @ X)^(-1) @ X.T @ y |
| Markowitz min var | w = Sigma^(-1) @ 1 / (1.T @ Sigma^(-1) @ 1) |
| Condition number | Measure of numerical stability |
Sources & Further Reading¶
- Strang, G., Introduction to Linear Algebra, Wellesley-Cambridge Press
- Markowitz, H.M. (1952), Portfolio Selection, Journal of Finance
- Fama, E.F. & French, K.R. (1993), Common Risk Factors in the Returns on Stocks and Bonds, Journal of Financial Economics
- Laloux, L., Cizeau, P., Bouchaud, J.P., & Potters, M. (1999), Noise Dressing of Financial Correlation Matrices, Physical Review Letters
- Meucci, A., Risk and Asset Allocation, Springer
- NumPy Documentation, Linear Algebra
- SciPy Documentation, Linear Algebra Tutorial
- Hayes, B., Writing Math with LaTeX for Quants, Cambridge University Press
- MIT OpenCourseWare, Linear Algebra (18.06)
- Ledoit, O. & Wolf, M. (2003), Honey, I Shrunk the Sample Covariance Matrix, Journal of Portfolio Management