4. 因子暴露计算:因子载荷矩阵、标准化处理、暴露度计算
好,咱们进入第四章。因子暴露计算,这名字听着挺唬人,对吧?
说白了,就是搞清楚你的组合里,到底装了多少「动量」、多少「价值」、多少「质量」这些因子。我当年刚入行时,觉得这步就是个数学游戏。直到有一次,我辛辛苦苦跑出来的回测曲线漂亮得不像话,结果一算暴露,好家伙,全押在市值因子上了。那叫一个惨。
所以,这一章咱们把这事彻底掰扯清楚。
4.1 因子载荷矩阵:你的组合「基因图谱」
因子载荷矩阵,你可以把它想象成一张「基因图谱」。
每一行是一只股票,每一列是一个因子。矩阵里的数值,就是这只股票在这个因子上的「基因表达量」。比如,某只股票在「动量因子」上的载荷是1.5,说明它过去涨得比平均水平猛多了。
我个人习惯,把因子载荷矩阵记作 B。它的维度是 (N × K),N是股票数量,K是因子数量。
核心公式:
r_i = α_i + β_i1 * f_1 + β_i2 * f_2 + ... + β_iK * f_K + ε_i
其中,β_ij 就是股票 i 在因子 j 上的载荷。
嗯,这里要注意:载荷不是凭空算出来的。它通常来自两种方式:
- 回归法:用历史收益率对因子收益率做回归,系数就是载荷。这是最正统的做法。
- 特征法:直接用股票的特征值,比如PB的倒数、过去12个月的收益率。简单粗暴,但有效。
我在项目中遇到过一个问题:用回归法算载荷,时间窗口选多长?选60天,噪声太大;选252天,又太滞后。后来我折中了一下,用120天滚动窗口,效果还行。你想想看,这其实是个典型的偏差-方差权衡问题。
4.2 标准化处理:让因子「站在同一起跑线」
算完载荷,你是不是想直接拿过来用?别急。
不同因子的量纲差太多了。比如「波动率因子」的数值可能是0.3,而「市值因子」的数值可能是100亿。你直接拿它们去算暴露,市值因子会直接把其他因子「吃掉」。
所以,标准化是必须的。
我常用的标准化方法有三种:
| 方法 | 公式 | 适用场景 |
|---|---|---|
| Z-score | (x - μ) / σ | 数据近似正态分布时 |
| 分位数标准化 | 映射到[0,1]或[-1,1] | 数据有极端值时 |
| 市值加权标准化 | 以市值为权重做调整 | 需要消除市值偏差时 |
我个人最常用的是Z-score。为什么?因为它保留了数据的相对大小关系,而且处理起来简单。
小技巧:标准化时,一定要用「截面」数据做。也就是说,在每个时间点上,对所有股票的某个因子做标准化。千万别用时间序列上的均值和标准差,那会引入未来信息。
我曾经犯过一个低级错误:用全历史数据算均值和标准差,然后做标准化。结果呢?回测时看起来完美,实盘时一塌糊涂。因为历史均值里包含了未来数据,这叫「前视偏差」。嗯,从那以后,我每次做标准化都会反复检查:是不是只用了截止到当前时刻的数据。
4.3 暴露度计算:组合层面的「因子体检报告」
好了,现在你有了标准化的因子载荷矩阵 B,也有了组合的持仓权重向量 w。
组合在因子 j 上的暴露度,其实就是持仓权重的加权平均:
组合暴露度公式:
Exposure_j = Σ (w_i * B_ij)
其中,w_i 是股票 i 在组合中的权重,B_ij 是标准化后的因子载荷。
说白了,就是把你组合里每只股票的因子载荷,按权重加起来。结果是一个 K 维向量,每个分量代表组合在该因子上的暴露。
举个例子:
- 组合在「价值因子」上的暴露是 0.8,说明你偏重价值股。
- 在「动量因子」上的暴露是 -0.3,说明你偏重反转股。
- 在「市值因子」上的暴露是 0.1,说明你基本中性。
我一般会把这个暴露度向量画成一张「雷达图」或「条形图」,一眼就能看出组合的因子风格。你想想看,这比看一堆数字直观多了。
避坑指南:计算暴露度时,权重 w_i 必须是「当前」的持仓权重,而不是期初的。因为组合会调仓,权重会变。我曾经用期初权重算暴露,结果发现组合的因子暴露一直在漂移,还以为模型出了问题。后来才发现,是权重没更新。
4.4 核心逻辑:一张图说清楚
为了让你更直观地理解整个流程,我画了一张流程图。它展示了从原始数据到最终暴露度的完整链路。
这张图里,我特意加了一个反馈回路。为什么?因为因子暴露计算不是一次性的。你算完暴露,发现组合在某个因子上偏得太多,就得调整权重,然后重新算。这是一个迭代优化的过程。
4.5 代码示例:Python 实现
光说不练假把式。我给你一段 Python 代码,直接跑就能用。
import numpy as np
import pandas as pd
# 假设我们有 5 只股票,3 个因子
# 因子载荷矩阵 B (5x3)
B = np.array([
[1.2, 0.8, -0.3],
[0.9, 1.1, 0.2],
[1.5, -0.2, 0.7],
[0.6, 0.4, 1.3],
[1.1, 0.9, -0.1]
])
# 组合权重 w (5x1)
w = np.array([0.2, 0.3, 0.1, 0.25, 0.15])
# 1. 标准化处理(Z-score)
B_mean = B.mean(axis=0)
B_std = B.std(axis=0)
B_standardized = (B - B_mean) / B_std
# 2. 计算组合暴露度
exposure = w @ B_standardized
# 输出结果
print("标准化后的因子载荷矩阵:")
print(pd.DataFrame(B_standardized, columns=['因子1', '因子2', '因子3']))
print("\n组合因子暴露度:")
print(pd.Series(exposure, index=['因子1', '因子2', '因子3']))
这段代码里,我用了矩阵乘法 @,简洁高效。你想想看,如果用手写循环,不仅慢,还容易出错。在量化里,能用矩阵运算就别用循环,这是铁律。
扩展阅读:如果你用的是 Barra 模型,因子载荷矩阵通常由「行业因子」和「风格因子」两部分组成。行业因子是 0/1 变量,风格因子是标准化后的特征值。计算暴露时,需要分别处理。
好了,这一章的内容就这些。因子暴露计算,说白了就是三步:算载荷、标准化、加权平均。每一步都有坑,但只要你按我上面说的做,基本不会出大问题。
记住:暴露度不是目的,而是手段。它的真正价值,是帮你理解组合的风险来源,然后做出调整。
公众号:蓝海资料掘金营,微信deep3321