Overview

Accurate covariance estimation is critical for portfolio optimization and risk management. The challenge is balancing responsiveness with stability.

Exponential Weighting

Most common approach: weight recent observations more heavily.

\[\Sigma_t = (1-\lambda) \sum_{i=0}^{\infty} \lambda^i r_{t-i} r_{t-i}^T\]

Typical half-life: 21-63 days ($\lambda = 1 - \exp(-\log(2)/HL)$)

Advantages:

Disadvantages:

Shrinkage

Combine sample covariance with structured estimator:

\[\Sigma_{shrunk} = \delta \Sigma_{target} + (1-\delta) \Sigma_{sample}\]

Common targets:

Ledoit-Wolf optimal shrinkage:

\[\delta^* = \frac{\text{Var}(\Sigma_{sample})}{\text{MSE}(\Sigma_{sample}, \Sigma_{target})}\]

Factor Models

For large universes, use factor structure:

\[\Sigma = B F B^T + D\]

where $B$ are factor loadings, $F$ is factor covariance, and $D$ is idiosyncratic risk.

Advantages:

Disadvantages:

Practical Implementation

import numpy as np
import pandas as pd

def exp_weighted_cov(returns, halflife=21):
    """
    Calculate exponentially weighted covariance matrix.
    
    Parameters
    ----------
    returns : pd.DataFrame
        Asset returns (dates x assets)
    halflife : float
        Half-life for exponential weighting
        
    Returns
    -------
    pd.DataFrame
        Covariance matrix
    """
    # Decay factor
    alpha = 1 - np.exp(-np.log(2) / halflife)
    
    # Exponentially weighted covariance
    cov = returns.ewm(alpha=alpha, min_periods=252).cov()
    
    # Return most recent estimate
    return cov.loc[returns.index[-1]]

Choosing Half-Life

Shorter half-life (10-21 days):

Longer half-life (42-126 days):

Recommendation: Use 21-42 days for futures, with adaptive scaling for regime changes.