halo 的技术博客

返回

你打开一张 12 个资产(行业指数 + 宽基)的日收益协方差矩阵,它是 12×12、78 个独立数字。直觉告诉你这些数字里有大量冗余——毕竟沪深300涨的时候,中证500、上证50、金融消费大多一起动。

PCA(主成分分析) 把这直觉量化了:它告诉我们,这 78 个数字里,真正「独立」的信息维度其实很少。本文要论证的是——

资产收益的截面(cross-section),是由少数几个宏观因子驱动的。只要抓住前几个主成分,你就能用极低的维度还原绝大部分协方差结构。这件事在协方差估计、风险模型、组合优化里都有硬核用途。

1. 用因子结构合成「有真相」的数据#

为了能验证 PCA 真的抓到了因子,我们先用一个有明确答案的合成数据:12 个资产的收益,由 3 个潜在宏观因子线性驱动 + 特质噪声。

rk=βk1F1(市场)+βk2F2(价值-成长)+βk3F3(商品-通胀)+εkr_k = \beta_{k1} F_1(\text{市场}) + \beta_{k2} F_2(\text{价值-成长}) + \beta_{k3} F_3(\text{商品-通胀}) + \varepsilon_k

  • F1F_1(市场):所有资产正载荷,普涨普跌。
  • F2F_2(价值-成长):金融/上证50 正、科技/新能源 负,典型劈叉。
  • F3F_3(商品-通胀):煤炭/黄金 正、利率敏感资产 负。
import numpy as np
K = 12
names = ["沪深300","中证500","中证1000","上证50","金融","消费","医药",
         "科技","新能源","军工","煤炭","黄金"]
rng = np.random.default_rng(20260719)
B = np.zeros((K, 3))
B[:, 0] = 0.6 + 0.2 * rng.standard_normal(K)          # 市场: 全正
vg = np.array([1,0.6,-0.4,1.1,1.2,0.3,-0.3,-0.9,-0.7,-0.2,0.5,-0.5])
B[:, 1] = 0.5 * vg / vg.std()                          # 价值-成长劈叉
ci = np.array([-0.2,-0.3,-0.4,-0.1,-0.5,-0.1,0.0,0.2,0.6,0.4,1.0,0.9])
B[:, 2] = 0.5 * ci / ci.std()                          # 商品-通胀
T = 1008
F = rng.standard_normal((T, 3)) * np.array([0.016, 0.011, 0.013])
idio = rng.standard_normal((T, K)) * 0.004             # 特质噪声(远小于因子)
R = F @ B.T + idio                                      # T × K 资产收益
python

注意特质噪声设得远小于因子波动——这是为了让「3 因子主导」这个真相成立。如果噪声很大,PCA 就抓不到清晰结构(后文陷阱三会详谈)。

2. 碎石图:截面波动是低维的#

在训练期(前一半样本)估计样本协方差,做特征分解,看特征值怎么分布。

def pca_cov(Rtr):
    Xc = Rtr - Rtr.mean(axis=0)
    cov = Xc.T @ Xc / (len(Xc) - 1)
    ev, evec = np.linalg.eigh(cov)
    order = np.argsort(ev)[::-1]
    return ev[order], evec[:, order], cov

ev_tr, evec_tr, cov_tr = pca_cov(R[:504])
var_exp = ev_tr / ev_tr.sum()
cum_var = np.cumsum(var_exp)
print("前3主成分累计解释方差 = %.1f%%" % (cum_var[2] * 100))
python

碎石图:前 3 主成分解释 92.8% 的 12 资产协方差

结果:前 3 个主成分累计解释 92.8% 的截面方差(PC1=47.4%、PC2=32.9%、PC3=12.6%),剩余 9 个主成分加起来才 7.2%。这就是「宏观因子定价」的实证版本——资产收益的联合运动,被 3 个因子装下了。

验证一下 PC1 是不是市场因子:把 PC1 的得分(主成分投影)和等权市场收益算相关,得到 −0.992(符号反了只是方向约定,强度几乎完美)。PC1 就是市场。

3. 载荷:主成分的物理含义#

PCA 不给你因子名字,但载荷(eigenvector 在各资产上的投影)会暴露它的结构。

前 3 主成分载荷:PC1 普遍为正,PC2/PC3 劈叉

  • PC1:几乎全资产正载荷 → 市场Beta,普涨普跌。
  • PC2:金融/上证50 正、科技/新能源 负 → 价值-成长风格分化。
  • PC3:煤炭/黄金 正、利率敏感资产 负 → 商品-通胀链条。

这三张载荷图,就是一份「宏观因子暴露说明书」。你不需要预先知道因子叫什么,PCA 自己把结构浮出来了——这正是它比「我主观指定几个因子」更客观的地方。

需要提醒的是,载荷只是「相关方向的几何投影」,它的数值大小不能直接读成「因子收益的贡献度」。载荷是协方差结构里的权重,而收益归因还要结合因子自身的时间序列波动与风险溢价。一个载荷很大但本身波动很小的主成分,对组合风险的贡献可能远小于载荷中等但波动剧烈的主成分。PCA 给你的是「结构地图」,不是「收益账本」——把两者混淆,就容易高估某个主成分的实际影响力。

4. 核心应用:PCA 协方差收缩#

PCA 不只是「看一眼因子」,它最实用的工程价值在协方差估计的去噪

样本协方差 Σ^\hat\Sigma 在高维下严重过拟合——它把噪声也当成了信号。PCA 收缩的做法是:只保留前 k 个主成分,把其余(噪声)特征值压到地板,重构出一个更稳健的协方差:

Σ^k=jkλjvjvj+δI\hat\Sigma_k = \sum_{j\le k} \lambda_j v_j v_j^\top + \delta \cdot I

我们用它对最小方差组合做协方差输入,对比三种估计在短估计窗口(90 天滚动、月度再平衡)下的真实表现。

短窗口重估:raw 权重剧烈摆动,PCA-3 收缩更稳健

回测结果(90 天窗口、月度再平衡,12 资产):

协方差估计实现年化波动月度换手率
朴素全样本 Σ^\hat\Sigma8.52%0.85
PCA-3 收缩8.26%0.50

PCA-3 收缩把月度换手率直降 42%,实现波动率也略低。为什么?因为短窗口下朴素协方差被噪声晃得厉害,最小方差解会追逐样本里那些「假的小方差资产」,权重每个月大幅跳变;PCA 收缩把噪声维度砍掉,权重更稳,交易成本更低。

三种协方差估计下最小方差组合权重

全样本协方差给出的权重(图3左)比 PCA-3(右)更「尖」、更极端——尖就意味着过拟合了样本里的偶然结构。PCA 收缩把它抚平,这正是稳健性的来源。

注意:在「训练期足够长(504 天)、K=12」的基准设定下,朴素与 PCA-3 的波动率几乎相等(8.02% vs 8.03%)。PCA 收缩的优势只在估计样本相对维度偏短时显现——这恰恰是最真实、最不粉饰的结论。

5. 五类真实陷阱(必读)#

陷阱一:PCA 是「方差」分解,不是「收益」分解。 它告诉你「波动从哪来」,不告诉你「收益从哪来」。PC1 解释最多方差 ≠ PC1 有最高预期收益。用 PCA 做配置时,别把「主成分」误当「alpha 来源」。

陷阱二:主成分的方向随样本变。 换一段训练期,PC1/PC2/PC3 的载荷会漂移——今天的 PC3 是商品,下一期可能变成别的东西。所以载荷的解释要每次重新读,不能写死「PC3 永远是商品因子」。

陷阱三:噪声一高,结构就糊。 我们刻意把特质噪声设小(0.004 vs 因子 0.01+)才得到干净的 92.8%。真实股票日收益里,特质噪声占比往往高得多,前 3 主成分可能只解释 40%~60%。那时 PCA 收缩的「砍掉后 9 个」就过头了——k 要按碎石图选,不要无脑砍

陷阱四:保留几个主成分(k)是超参数。 k 太小(如只留 PC1)会欠拟合,组合退化成类市值;k 太大就回到朴素协方差。常见的「保留累计解释度 80%~95%」是经验起点,但要在样本外验证,而非样本内看着好看就定。

陷阱五:PCA 收缩 ≠ 万能去噪。 它假设「前 k 个主成分都可信、其余全是噪声」,这个二阶假设在因子结构清晰时成立,在结构模糊或存在多个弱因子时未必。更讲究的做法是用 Ledoit-Wolf 收缩(整体往单位阵拉),或因子模型(先指定因子、再估计特质方差)。PCA 是其中最直观的一种,不是唯一解。

6. 结语#

资产收益的截面,是被少数宏观因子撑起来的。PCA 用一份碎石图就能告诉你「维度有多低」,用一组载荷图就能把因子结构浮出来,再用一次协方差收缩就能让短窗口下的组合更稳、更便宜。

但它始终是降维与去噪工具,不是印钞机:它优化的是「你对协方差的认知质量」,不替你决定「该配多少风险」。把因子看清楚、把噪声压下去,剩下的——该用什么目标权重、该承受多少风险——还是要回到你的投资框架里。

最后给一个落地清单,方便你今天就能用起来:第一,先算碎石图,确认你的资产池前几个主成分到底解释了多少——如果不到 60%,说明要么噪声太大、要么资产池冗余严重,别急着砍维度;第二,用碎石图的「拐点」或累计 80%~95% 经验线定 k,但一定要在样本外验证组合表现,而不是样本内好看就定;第三,把 PCA 收缩和 Ledoit-Wolf shrinkage 各跑一遍,选样本外更稳的那个,别只信一种;第四,载荷图每次重训都要重新读,别把「PC3=商品」写死进生产代码。做好这四步,PCA 才会从「画图好看」变成「真的帮你少付摩擦、少踩过拟合」的工程组件。

数据说明:本文所有图表与关键数字(前 3 主成分 92.8%、PC1 与市场相关 −0.992、短窗口 PCA-3 换手率降 42%)均由文中 Python 代码真实计算生成;收益由 3 因子+特质噪声的合成数据自洽生成,仅用于演示方法论,不构成任何投资建议。

宏观PCA定价:用少数主成分解释资产收益的截面
https://blog.halo26812.eu.org/blog/macro-pca-pricing
Author halo
Published at 2026年7月19日
版权声明 CC BY-NC-SA 4.0
Comment seems to stuck. Try to refresh?✨