This matters especially for inverse covariance problems. A near-zero covariance eigenvalue describes a direction that appears almost riskless in the sample. Inverting the matrix turns that direction into a large numerical weight. Raising those small eigenvalues reduces the tendency to take extreme positions along noisy low-risk directions.
Why blending helps the inversion
For sample covariance , a positive-definite target , and shrinkage intensity , use
If with , the eigenvectors of are unchanged. Each eigenvalue becomes . Small eigenvalues rise and large ones fall. A positive makes the estimate positive definite even if is singular. Estimators in the Ledoit–Wolf family estimate the blend intensity from data. The choice of target differs across implementations and should be documented.
Suppose the annual variances of two sleeves are 0.04 and 0.01, and their covariance is 0.018. For a fully invested, unrestricted minimum-variance allocation, the first sleeve's weight is
Using the sample covariance gives a weight of about −57.1% in the first sleeve and 157.1% in the second. Shrink the matrix 25% toward the scaled identity target with diagonal 0.025. The variances become 0.03625 and 0.01375, and covariance becomes 0.01350. The first sleeve's weight becomes about 1.1%. This example illustrates how a change in the covariance estimate affects the same objective. Whether it improves future realised risk depends on the data and constraints.
Choose the shrinkage target to match the job. A diagonal target reduces all estimated cross-asset covariances towards zero. A constant-correlation target retains a common dependence level while reducing the number of distinct correlation estimates. These choices imply different stress exposures even if both matrices are well-conditioned. To compare them for portfolio construction, fix the rebalance clock and use the same later return observations. Inspect realised variance, transaction costs and the magnitude of weight changes. A smoother matrix is valuable if it improves the portfolio decision it feeds.
Compare weights before and after shrinking the estimate
import numpy as np
from sklearn.covariance import LedoitWolf
rng = np.random.default_rng(34)
factors = rng.normal(0, 0.01, size=(180, 4))
loadings = rng.normal(0, 0.5, size=(4, 60))
returns = factors @ loadings + rng.normal(0, 0.006, size=(180, 60))
train, test = returns[:90], returns[90:]
model = LedoitWolf().fit(train)
sample_cov = np.cov(train, rowvar=False, ddof=0)
ones = np.ones(train.shape[1])
for name, cov in [("sample", sample_cov), ("shrinkage", model.covariance_)]:
weights = np.linalg.solve(cov, ones)
weights /= weights.sum()
print(name, "condition number", np.linalg.cond(cov),
"test volatility", np.std(test @ weights, ddof=1))
print("shrinkage intensity", model.shrinkage_)