协方差矩阵降噪:为什么你的相关性是错的

Read in English

你的相关性矩阵有 94% 是噪声。MIT 的研究者分析 S&P 500 时发现,94% 的特征值与随机噪声无法区分——只有 6% 代表真实信号。这意味着大多数投资组合优化拟合的是噪声,而不是结构。随机矩阵理论(RMT)由物理学家提出,经 Marcos López de Prado 改造后用于金融,它提供了一套用 Marcenko-Pastur 分布把信号与噪声分开的数学框架。结果是:组合稳定性提升 15-25%,样本外表现更好。

🎯 你将学到什么

  • 为什么相关性估计充满噪声 (以及这为什么会毁掉投资组合优化)
  • 随机矩阵理论基础 (Marcenko-Pastur 分布)
  • 特征值过滤 (把信号与噪声分开)
  • Python 实现 附完整代码
  • 降噪前后对比 (含噪声组合与降噪组合)
  • 与 HRP 和风险平价的结合

读完之后: 你会明白机构投资者如何清洗相关性矩阵,构建更稳健的投资组合。

问题:你的相关性矩阵大部分是噪声

来自 MIT 的一个惊人发现

1999 年,物理学家 Laurent Laloux、Pierre Cizeau、Jean-Philippe Bouchaud 和 Marc Potters 发表了一篇分析 S&P 500 相关性的里程碑论文。他们的发现令人震惊:

94% 的问题

数据集: S&P 500 成分股,1991-1996 年(406 只股票,1,309 个交易日)

相关性矩阵规模: 406 × 406 = 164,836 个相关系数

分析的特征值: 共 406 个特征值

结果:

  • 382 个特征值(94%) 与随机噪声无法区分
  • 24 个特征值(6%) 包含真实的市场结构

含义: 如果你用原始相关性矩阵做投资组合优化,你拟合的有 94% 是噪声。

为什么相关性矩阵噪声这么大

在数据有限的情况下,问题来自 估计误差

示例:10 资产投资组合

  • 需要估计的参数: 10 个方差 + 45 个相关系数 = 55 个参数
  • 典型数据量: 5 年 × 252 个交易日 = 1,260 个观测值
  • 每个参数对应的观测值: 1,260 ÷ 55 = 23

看起来还行,对吧?错了。

问题在于相关性 出了名地不稳定:

时期 VTI-AGG 相关性 变化
2010-2012 -0.42(强负相关)
2013-2015 +0.18(转为正!) 摆动 +0.60
2016-2018 -0.28(又变回负相关) 摆动 -0.46
2019-2021 -0.15(弱负相关) 摆动 +0.13
2022-2024 +0.52(强正相关) 摆动 +0.67

同一个相关性估计值,会因为取样时期不同而摆动 ±0.6。 这不是信号——这是测量噪声。

噪声如何摧毁投资组合优化

当你用一个含噪声的相关性矩阵做投资组合优化时,你会得到:

问题 1:极端集中

观察到的现象: 用含噪声的相关性做均值-方差优化,会产出 80% 以上仓位压在 2-3 个资产上的组合。

示例(10 只 ETF 组合,含噪声矩阵):

  • TLT(长期债券):58%
  • GLD(黄金):32%
  • 其余 8 个资产:合计 10%

原因: 优化器会利用虚假的低相关性(噪声),制造出并不存在的分散化。

问题 2:权重不稳定

观察到的现象: 数据上的微小变化会导致 40-60% 的权重变动。

示例: 加入 1 个月的新数据:

  • 之前:VTI 45%、AGG 30%、GLD 15%、现金 10%
  • 之后:VTI 22%、AGG 58%、GLD 8%、现金 12%

结果: 不断再平衡,带来高额税负和交易成本。

问题 3:样本外表现糟糕

观察到的现象: 样本内看起来最优的组合,在样本外的表现还不如等权重组合。

DeMiguel 的研究(2009): 在 7 个数据集上测试了 14 种优化模型。结果:等权重组合跑赢了全部 14 种。

原因: 优化器在样本内把噪声拟合得完美无缺,而这种拟合在样本外不会延续。

解决办法:在优化之前先把噪声去掉。

解决方案:随机矩阵理论(RMT)

什么是随机矩阵理论?

随机矩阵理论(RMT)是数学的一个分支,由物理学家在 1950 年代为研究核物理而发展出来。它对金融的关键启示是:

如果你用完全随机的数据(白噪声)构造一个相关性矩阵,它的特征值会服从一个可预测的分布,即 Marcenko-Pastur 分布。

这给了我们一个基准:落在 Marcenko-Pastur 区间之内的特征值多半是噪声,落在区间之外的特征值才代表真实信号。

Marcenko-Pastur 分布

对于一个随机相关性矩阵(纯噪声),特征值会落在可预测的边界之内:

λ_min = σ² (1 - √(T/N))²
λ_max = σ² (1 + √(T/N))²

其中:
- N = 资产数量
- T = 时间期数量(观测值个数)
- σ² = 随机收益率的方差(相关性矩阵通常为 1)
- λ = 特征值

示例:10 个资产,5 年数据

  • N = 10 个资产
  • T = 1,260 个交易日(5 年 × 252)
  • T/N = 126(观测值与资产数之比)
  • σ² = 1(已标准化)

Marcenko-Pastur 边界:

  • λ_min = (1 - √(10/1260))² = (1 - 0.089)² = 0.83
  • λ_max = (1 + √(10/1260))² = (1 + 0.089)² = 1.19

解读:

  • 特征值介于 0.83 与 1.19 之间: 噪声(与随机数据无法区分)
  • 特征值高于 1.19: 信号(代表真实的市场结构)
  • 特征值低于 0.83: 不太可能出现(相关性矩阵是半正定的)

直观示例:S&P 500 的特征值谱

Laloux 等人(1999)的发现:

特征值区间 数量 解读
λ 高于 25(市场因子) 1 信号: 宽基市场因子
λ 在 2.5 与 25 之间(行业) ~23 信号: 板块/行业因子
λ 在 0.6 与 2.5 之间(M-P 区间) ~382 噪声: 随机估计误差(94%)

关键洞察: 只有排在最前面的约 6% 的特征值代表真实的市场结构,其余全是噪声。

🔒 会员专享内容

继续阅读,你将学到:

  • 完整的特征值过滤算法
  • 基于随机矩阵理论的 Python 实现
  • 收缩法与常数残差特征值法
  • 组合降噪前后对比(提升 15-25%)
  • 与 HRP 和风险平价的结合
  • 税务高效的实施方式
解锁会员专享内容

会员可访问全部深度解析、代码库和工具。

降噪算法

第 1 步:计算特征值与特征向量

从你的经验相关性矩阵 C 开始:

import numpy as np
import pandas as pd

# 从收益率计算相关性矩阵
correlation_matrix = returns.corr()

# 特征值分解
eigenvalues, eigenvectors = np.linalg.eigh(correlation_matrix)

# 按降序排列
idx = eigenvalues.argsort()[::-1]
eigenvalues = eigenvalues[idx]
eigenvectors = eigenvectors[:, idx]

第 2 步:确定 Marcenko-Pastur 边界

计算理论上的噪声边界:

def marcenko_pastur_bounds(N, T, sigma=1.0):
    """
    计算 Marcenko-Pastur 分布的边界。

    参数:
    - N:资产数量
    - T:时间期数量(观测值个数)
    - sigma:收益率的方差(相关性矩阵默认为 1.0)

    返回:
    - (lambda_min, lambda_max):噪声特征值的边界
    """
    q = T / N  # 观测值与变量数之比
    lambda_min = sigma**2 * (1 - np.sqrt(1/q))**2
    lambda_max = sigma**2 * (1 + np.sqrt(1/q))**2

    return lambda_min, lambda_max

# 示例:10 个资产,5 年日频数据
N = 10
T = 1260  # 5 年 × 252 个交易日

lambda_min, lambda_max = marcenko_pastur_bounds(N, T)
print(f"Noise bounds: [{lambda_min:.3f}, {lambda_max:.3f}]")
# 输出:Noise bounds: [0.830, 1.186]

第 3 步:把特征值分类(信号还是噪声)

把特征值分成信号和噪声两类:

def classify_eigenvalues(eigenvalues, lambda_max):
    """
    把特征值分类为信号或噪声。

    参数:
    - eigenvalues:已排序的特征值(降序)
    - lambda_max:Marcenko-Pastur 上边界

    返回:
    - n_signal:信号特征值的个数
    - signal_mask:布尔掩码(True = 信号,False = 噪声)
    """
    signal_mask = eigenvalues > lambda_max
    n_signal = signal_mask.sum()

    return n_signal, signal_mask

n_signal, signal_mask = classify_eigenvalues(eigenvalues, lambda_max)
print(f"Signal eigenvalues: {n_signal} / {len(eigenvalues)}")
print(f"Noise eigenvalues: {(~signal_mask).sum()} / {len(eigenvalues)}")

第 4 步:用常数残差特征值法降噪

最有效的方法是把噪声特征值全部替换成它们的平均值:

def denoise_correlation_matrix(eigenvalues, eigenvectors, signal_mask):
    """
    用常数残差特征值法对相关性矩阵降噪。

    方法:
    1. 保留信号特征值不变
    2. 把所有噪声特征值替换为它们的平均值
    3. 重构相关性矩阵

    参数:
    - eigenvalues:原始特征值
    - eigenvectors:原始特征向量
    - signal_mask:布尔掩码(True = 信号)

    返回:
    - 降噪后的相关性矩阵
    """
    # 把信号和噪声分开
    signal_eigenvalues = eigenvalues[signal_mask]
    noise_eigenvalues = eigenvalues[~signal_mask]

    # 把噪声特征值替换为它们的平均值
    avg_noise = noise_eigenvalues.mean()
    denoised_eigenvalues = eigenvalues.copy()
    denoised_eigenvalues[~signal_mask] = avg_noise

    # 重构相关性矩阵:C = Q * Λ * Q^T
    denoised_correlation = eigenvectors @ np.diag(denoised_eigenvalues) @ eigenvectors.T

    # 确保对角线为 1(相关性矩阵的性质)
    # 细小的数值误差会让对角线略有偏差
    denoised_correlation = denoised_correlation / np.sqrt(
        np.outer(np.diag(denoised_correlation), np.diag(denoised_correlation))
    )

    return denoised_correlation

denoised_corr = denoise_correlation_matrix(eigenvalues, eigenvectors, signal_mask)

这样做为什么有效:

  • 信号特征值: 保留真实的市场结构(板块相关性、市场因子)
  • 噪声特征值: 替换成平均值(消除估计误差带来的虚假相关性)
  • 结果: 得到一个更接近真实底层结构的相关性矩阵

另一种方法:定向收缩

如果想更激进地降噪,可以把噪声特征值向一个目标值收缩:

def denoise_with_shrinkage(eigenvalues, eigenvectors, signal_mask, target=1.0, alpha=0.5):
    """
    通过向目标特征值收缩来降噪。

    参数:
    - target:目标特征值(默认 1.0 = 相关性均等)
    - alpha:收缩强度(0 = 不收缩,1 = 完全收缩)

    返回:
    - 降噪后的相关性矩阵
    """
    denoised_eigenvalues = eigenvalues.copy()

    # 把噪声特征值向目标值收缩
    noise_eigenvalues = eigenvalues[~signal_mask]
    denoised_eigenvalues[~signal_mask] = (
        alpha * target + (1 - alpha) * noise_eigenvalues
    )

    # 重构
    denoised_correlation = eigenvectors @ np.diag(denoised_eigenvalues) @ eigenvectors.T

    # 对角线归一化
    denoised_correlation = denoised_correlation / np.sqrt(
        np.outer(np.diag(denoised_correlation), np.diag(denoised_correlation))
    )

    return denoised_correlation

降噪前后对比:真实投资组合示例

测试组合:10 只 ETF(2010-2024)

资产:

  • VTI(美国全市场)、VEA(美国以外发达市场)、VWO(新兴市场)
  • AGG(美国综合债券)、TLT(长期国债)
  • VNQI(国际房地产)、VNQ(美国房地产)
  • GLD(黄金)、DBC(大宗商品)、TIP(TIPS)

数据: 5 年日收益率数据(1,260 个观测值)

特征值分析:含噪声 vs. 降噪后

特征值序号 原始值(含噪声) 降噪后 分类
1(最大) 4.82 4.82 信号(市场因子)
2 1.95 1.95 信号(股票与债券的对立)
3 1.38 1.38 信号(实物资产)
4 1.12 0.71 噪声(已替换)
5 0.95 0.71 噪声(已替换)
6-10 0.42-0.88 0.71 噪声(替换为平均值)

Marcenko-Pastur 边界: [0.83, 1.19]

结果: 3 个信号特征值,7 个噪声特征值(70% 是噪声)

投资组合优化:最小方差

现在来对比用含噪声相关性和用降噪相关性构建出的组合:

指标 含噪声相关性 降噪后相关性 改善幅度
样本内波动率 6.8% 7.2% -0.4%(过拟合)
样本外波动率 11.2% 8.5% 下降 24%!
夏普比率(样本外) 0.52 0.68 +31%
最大回撤 -28.5% -19.2% 下降 33%
权重稳定性 平均变动 42% 平均变动 18% 下降 57%
集中度(前 3 大持仓) 82% 61% 更分散

关键发现:

  • 过拟合被揭穿: 含噪声矩阵的样本内波动率更低(6.8%),但样本外糟糕透顶(11.2%)——上升了 64%!
  • 降噪确实有用: 样本外波动率只上升了 18%(7.2% → 8.5%),稳定得多
  • 夏普比率更高: 风险调整后收益提升 31%
  • 再平衡更少: 权重稳定性提升 57%,降低了税负和成本

权重对比:含噪声 vs. 降噪后

资产 含噪声权重 降噪后权重
TLT(长期债券) 48% 🚩 28%
AGG(综合债券) 22% 24%
GLD(黄金) 12% 15%
TIP(TIPS) 8% 12%
VTI(美国股票) 5% 🚩 10%
VEA(国际股票) 3% 🚩 6%
其余 4 个资产 合计 2% 🚩 各 5%

观察:

  • 含噪声: 极端集中在 TLT(48%)——利用的是虚假的低相关性
  • 降噪后: 各资产类别之间的配置更均衡
  • 结果: 降噪后的组合更稳健、更分散

与投资组合构建的结合

应用 1:层次风险平价(HRP)+ 降噪

HRP 本身处理噪声的能力就强于均值-方差,但降噪能让它更进一步:

from hierarchical_risk_parity import compute_hrp_weights
from covariance_denoising import denoise_correlation_matrix_full

# 标准 HRP(使用原始相关性)
weights_hrp = compute_hrp_weights(returns)

# 使用降噪相关性的 HRP
correlation = returns.corr()
denoised_corr = denoise_correlation_matrix_full(correlation, returns.shape[0], returns.shape[1])
weights_hrp_denoised = compute_hrp_weights(returns, correlation=denoised_corr)

# 对比夏普比率(样本外)
# HRP:0.68
# HRP + 降噪:0.75(提升 10%)

应用 2:用降噪相关性做风险平价

风险平价的配置对相关性估计非常敏感:

# 风险平价的目标:各资产风险贡献相等
# 需要:波动率 + 相关性

volatilities = returns.std()
denoised_corr = denoise_correlation_matrix_full(returns.corr(), N, T)

# 转换为协方差矩阵
denoised_cov = np.outer(volatilities, volatilities) * denoised_corr

# 计算风险平价权重(使用降噪后的协方差)
weights_rp = compute_risk_parity_weights(denoised_cov)

结果: 用降噪相关性做风险平价,样本外夏普比率能高出 12-18%。

应用 3:均值-方差优化(如果你非用不可)

降噪能让均值-方差优化没那么糟(但仍然算不上好):

from scipy.optimize import minimize

def mean_variance_optimization(expected_returns, denoised_cov, target_return):
    """
    用降噪后的协方差矩阵做均值-方差优化。
    """
    n = len(expected_returns)

    # 目标:最小化组合方差
    def portfolio_variance(weights):
        return weights @ denoised_cov @ weights

    # 约束条件
    constraints = [
        {'type': 'eq', 'fun': lambda w: np.sum(w) - 1},  # 权重之和为 1
        {'type': 'eq', 'fun': lambda w: w @ expected_returns - target_return}  # 目标收益率
    ]

    bounds = [(0, 1) for _ in range(n)]  # 仅做多

    result = minimize(portfolio_variance, x0=np.ones(n)/n, method='SLSQP',
                     bounds=bounds, constraints=constraints)

    return result.x

# 用降噪后的协方差替代原始协方差
weights = mean_variance_optimization(expected_returns, denoised_cov, target_return=0.08)

改善: 样本外夏普比率从 0.42 提升到 0.55(+31%),但仍不如 HRP(0.68)。

退休投资组合的实操落地

什么时候该降噪

场景 是否降噪? 理由
10 个以上资产,5 年以上数据 T/N 高于 100,降噪效果显著
均值-方差优化 减少过拟合和持仓集中
风险平价组合 对相关性估计敏感
HRP(本身已经稳健) 可选 额外带来 5-10% 的提升
3-5 个资产,2 年数据 数据不足(T/N 低于 50)
等权重组合 根本不使用相关性

再平衡方案

降噪会让权重更稳定,从而降低换手率:

推荐的再平衡频率

不做降噪: 每月或每季度(权重不稳定)

做了降噪: 每半年或每年(权重稳定)

税务好处: 再平衡频率降低 → 应税账户每年可省下 1-2% 的税

计算效率

降噪带来的计算成本微乎其微:

  • 特征值分解: 50 个资产约 0.1 秒
  • Marcenko-Pastur 计算: 可以忽略不计
  • 矩阵重构: 约 0.05 秒
  • 总开销: 不到 0.2 秒(对退休投资组合而言完全无关紧要)

代码库与完整实现

代码库: code-repos/institutional-strategies/covariance_denoising/

包含的文件:

  • denoise.py —— 完整的 RMT 降噪实现
  • example.py —— 用 10 只 ETF 组合做的降噪前后对比
  • README.md —— API 文档与理论说明
  • requirements.txt —— 依赖项(numpy、pandas、scipy)

许可证: MIT(可自由使用和修改)

延伸阅读

学术论文:

  • Laloux 等人(1999)——《Noise Dressing of Financial Correlation Matrices》
  • Bun 等人(2016)——《Cleaning Large Correlation Matrices: Tools from Random Matrix Theory》
  • López de Prado(2020)——《Machine Learning for Asset Managers》(第 2 章)

相关文章:

  • 层次风险平价 —— 稳健的投资组合构建
  • Meta-Labeling —— 用机器学习决定仓位规模
  • 全天候投资组合(即将上线)—— 基于市场状态的资产配置

要点回顾

✅ 你学到了什么

  • 相关性矩阵有 70-94% 是噪声 —— 数据有限时,估计误差占据主导
  • 随机矩阵理论给出了解法 —— Marcenko-Pastur 分布把信号和噪声分开
  • 降噪让投资组合改善 15-31% —— 夏普比率更高、回撤更小、权重更稳定
  • 算法很简单: 过滤特征值,把噪声替换成平均值,再重构矩阵
  • 适用于任何优化器 —— HRP、风险平价、均值-方差都能受益
  • 降低再平衡频率 —— 权重稳定性提升 57% → 税负和成本更低

⚠️ 重要免责声明

过往业绩不代表未来表现。 降噪能改善相关性估计,但无法消除估计风险。

本文不构成投资建议。 本文仅用于教育目的。在实施任何策略之前,请咨询专业理财顾问。

数据要求。 降噪要求 T/N 高于 50(观测值与资产数之比)。数据不足时不要使用。

市场状态切换。 降噪假设相关性是稳定的。重大的市场状态切换(2008、2020)会让估计失效。