如果你只用一个市场因子(CAPM)去解释股票收益,会发现一堆”解释不了”的组合——小盘股长期跑赢、低估值(价值)股跑赢、高盈利股跑赢。这些CAPM 解释不了的超额收益,被叫做 alpha(异象)。
Fama 和 French 的思路是:与其把 alpha 当成”免费午餐”,不如把它拆进更多定价因子里。于是有了三因子模型(市场 + 规模 SMB + 价值 HML),后来又加上盈利(RMW)和投资(CMA),变成五因子模型。本文要回答一个实战问题:这五个因子,在 A 股到底能不能”跑赢”、能解释多少异象?
为了把机制讲透且可复现,我用一套受控模拟(因子溢价对齐到文献常见量级、组合真实 alpha=0),干净地隔离出”模型误设造成的伪 alpha”这一现象。文末再给 A 股真实数据的落地路径。
一、五因子模型在说什么#
Fama–French (2015) 的五因子模型:
五个因子含义:
| 因子 | 含义 | 多头/空头 |
|---|---|---|
| Mkt−RF | 市场超额收益 | 市场组合 − 无风险 |
| SMB | 规模(Size) | 小盘 − 大盘 |
| HML | 价值(Value/BE-ME) | 高 BM(价值)− 低 BM(成长) |
| RMW | 盈利(Profitability/OP) | 高营业利润 − 低营业利润 |
| CMA | 投资(Investment/Inv) | 保守投资(低资产增长)− 激进投资 |
直觉:好股票(小盘、便宜、赚钱、不瞎扩张)长期应该跑赢。五因子模型认为,一个组合的超额收益,如果能被这五个因子的暴露(beta)完全解释,那它就没有”真 alpha”——只是承担了某种因子风险。
二、A 股实证:文献共识(先泼盆冷水)#
在动手之前,必须诚实说清 A 股的”特色”。大量 A 股五因子实证研究(综述级结论,非本文新发现)普遍指向几点:
- 价值因子 HML 长期疲软、甚至阶段性为负。和美股”价值长期溢价”不同,A 股在 2017 年后常出现”价值陷阱”——低估值股持续跑输。原因包括:壳价值消失、散户主导导致成长/题材溢价、以及价值股多为传统周期行业的结构性偏差。
- 规模因子 SMB 不稳定:小盘溢价在 2015 前后剧烈分化,且小盘流动性差、换手成本高,名义溢价常被摩擦吃掉。
- 盈利因子 RMW 相对更稳:高盈利、低杠杆的股票在 A 股确实表现出一定溢价,是五因子里”最能打”的一个。
- 投资因子 CMA 解释力中等且样本期敏感:结论高度依赖你用哪段历史。
所以”五因子能否跑赢 A 股”的诚实答案是:能解释一部分异象,但不是万能钥匙,且因子有效性随体制漂移。下面用模拟把”怎么判断它解释了多少”讲清楚。
三、可复现实验:构造因子与检验组合#
我们生成 600 个月度因子收益(50 年),并把因子溢价对齐到上面的目标量级(Mkt-RF 8.4%/年、SMB 3.6%、HML 1.8%、RMW 4.8%、CMA 2.4%)。
为什么”对齐”?真实数据每期均值会波动,反而把”模型误设→伪 alpha”这个机制淹没在噪声里。受控模拟去掉这层噪声,让你看清:即使组合真实没有任何 alpha,CAPM 依然会给你算出显著的 alpha——只因为它漏掉了别的因子。 这是 FF 框架最核心、也最容易被误读的一点。
构造 6 个检验组合,真实载荷如下(真实 alpha 全为 0):
| 组合 | Mkt | SMB | HML | RMW | CMA |
|---|---|---|---|---|---|
| 小盘价值 | 1 | 1 | 1 | 0 | 0 |
| 小盘中性 | 1 | 1 | 0 | 0 | 0 |
| 小盘成长 | 1 | 1 | −1 | 0 | 0 |
| 大盘高盈利 | 1 | −0.5 | 0 | 1 | 0 |
| 大盘低盈利 | 1 | −0.5 | 0 | −1 | 0 |
| 大盘保守投资 | 1 | 0 | 0 | 0 | 1 |
完整代码(和生成配图的代码一致,可复现):
import numpy as np
T = 600 # 50 年月度
rng = np.random.default_rng(20260711)
mu = np.array([0.0070, 0.0030, 0.0015, 0.0040, 0.0020]) # 月度超额收益
sd = np.array([0.0450, 0.0280, 0.0300, 0.0250, 0.0240])
corr = np.eye(5)
corr[0, 3] = corr[3, 0] = 0.25 # Mkt-RMW 弱相关
corr[1, 2] = corr[2, 1] = -0.15 # SMB-HML 弱负相关
cov = (sd[:, None]) * corr * (sd[None, :])
L = np.linalg.cholesky(cov)
F = mu + (L @ rng.standard_normal((5, T))).T # T x 5 因子超额收益
F = F - F.mean(0) + mu # 受控:对齐到目标溢价
# 组合真实载荷(alpha=0)与特异性噪声
load_true = np.array([
[1.0, 1.0, 1.0, 0.0, 0.0],
[1.0, 1.0, 0.0, 0.0, 0.0],
[1.0, 1.0, -1.0, 0.0, 0.0],
[1.0, -0.5, 0.0, 1.0, 0.0],
[1.0, -0.5, 0.0, -1.0, 0.0],
[1.0, 0.0, 0.0, 0.0, 1.0],
])
eps = rng.normal(0, 0.010, (6, T))
R_port = load_true @ F.T + eps # 组合收益
R_port_ex = R_port # F 已是超额,组合超额 = 载荷·因子 + 特异性
def ols(y, X):
X = np.column_stack([np.ones(len(y)), X])
b, *_ = np.linalg.lstsq(X, y, rcond=None)
return b[0], b[1:] # alpha, betas
def run_model(cols):
alphas, betas = [], []
for p in range(6):
a, b = ols(R_port_ex[p], F[:, cols])
alphas.append(a); betas.append(b)
return np.array(alphas), np.array(betas)
a_capm, _ = run_model([0]) # 仅 Mkt
a_ff3, b_ff3 = run_model([0, 1, 2]) # Mkt+SMB+HML
a_ff5, b_ff5 = run_model([0, 1, 2, 3, 4]) # 全五因子python四、关键结果:alpha 随模型扩张而收敛#
先看一下因子本身的”成色”——它们的累计净值和年化溢价/夏普:


然后是本文的核心图——同一个组合,在 CAPM / FF3 / FF5 三种模型下算出的 alpha 对比:

把数字摊开(alpha,单位 %/月):
| 组合 | CAPM alpha | FF3 alpha | FF5 alpha |
|---|---|---|---|
| 小盘价值 | +0.436 | −0.034 | −0.029 |
| 小盘中性 | +0.358 | +0.028 | +0.026 |
| 小盘成长 | +0.133 | −0.068 | −0.060 |
| 大盘高盈利 | +0.119 | +0.278 | −0.011 |
| 大盘低盈利 | −0.462 | −0.292 | +0.001 |
| 大盘保守投资 | +0.222 | +0.231 | +0.017 |
三组平均绝对 alpha:CAPM 0.288% → FF3 0.155% → FF5 0.024%。这是一个教科书级别的演示:
- 规模/价值组合(前 3 个):CAPM 给你算出 +0.44%、+0.36% 的”显著 alpha”,看起来像白捡的钱。但一旦加上 SMB 和 HML(FF3),alpha 立刻塌到 ±0.03%——所谓 alpha 不过是”小盘+价值”的因子风险补偿,被 CAPM 漏掉了。
- 盈利/投资组合(后 3 个):CAPM 留有 alpha 不说,连 FF3 也留着(+0.28%、−0.29%)——因为 FF3 没有盈利(RMW)和投资(CMA)因子。只有上齐 FF5,这些 alpha 才被吸收到近 0。
- 一句话总结:FF3 解释了规模/价值异象,FF5 再多解释一层盈利/投资异象;CAPM 在所有组合上都留下”伪 alpha”。
载荷热力图进一步印证——它展示每个组合在 FF5 下估计出的真实 beta:

五、怎么判断”因子到底有没有用”:GRS 检验#
单看某个组合的 alpha 不够。学术上常用 GRS 统计量(Gibbons–Ross–Shanken)来检验”一组组合的 alpha 是否联合为零”:
直观上,GRS 越大,说明这一组组合的 alpha 联合显著不为 0,即当前模型还漏掉了定价因子。我们的模拟里,CAPM 的 GRS 远大于 FF5——和”平均 |alpha| 从 0.29% 降到 0.024%“的结论一致。实践中你可以:
- 跑完回归,先看平均 |alpha| 和 GRS;
- 若 FF5 下平均 |alpha| 仍大、GRS 显著,说明 A 股还有 FF5 没覆盖的异象(比如动量、流动性、换手);
- 别只看 p 值——用 Newey–West 调整残差自相关后的 t 值,否则会高估显著性。
六、A 股真实数据落地路径#
模拟讲清了机制,落地要用真数据。以 A 股月度为例(用 westock-tool 取截面、westock-data 取复权价,或 tushare):
# 伪代码:在真实 A 股上构造五因子
# 1) 每月末取全体 A 股的:总市值(size)、账面/市值(BM)、
# 营业利润/权益(OP, 盈利)、资产增长率(Inv, 投资)
# 2) 按 size 分 2 组(小/大),按 BM 分 3 组(低/中/高)等 → 做 2x3 组合
# 3) SMB = 小盘三组均值 − 大盘三组均值
# HML = 高 BM 两组均值 − 低 BM 两组均值
# RMW = 高 OP 两组均值 − 低 OP 两组均值
# CMA = 低 Inv 两组均值 − 高 Inv 两组均值
# 4) 用复权月收益,对检验组合做上面的 run_model([0])/( [0,1,2] )/( 全因子 )
# 5) 关键:用相邻月(t-1 月末排序,t 月持有)构造组合,严格隔离前视python三个必须注意的工程细节:
- 排序用 t−1 信息、收益用 t 月:这是隔离前视的硬规则,否则因子溢价会被未来数据污染。
- 必须用复权收益:分红、送转会在不复权价里人造”下跌”,扭曲 SMB/HML。
- 剔除非流动性:小盘真实溢价的一部分是流动性补偿,回测不算冲击成本会虚高。
七、五大真实陷阱#
- 前视偏差(最致命):用当月同时知道”下月收益和因子值”去构造组合,等于作弊。严格 t−1 排序、t 持有。
- 价值因子在 A 股的”失灵”被误读:2017 年后 HML 常为负,新手会因此说”五因子没用”。正确结论是——因子有效性随时变,要做滚动样本、分体制检验,而不是一棍子打死。
- 幸存者偏差:用”现在还在上市”的股票做历史回测,会自动剔除已退市(常是绩差)公司,高估所有因子溢价。需用
point-in-time全样本。 - 多重检验:你对 20 个因子组合各跑一次 alpha 检验,总有 1 个”显著”——要像模型风险文章里那样做多重检验校正。
- 样本期依赖与过拟合:A 股 2015 股灾、2017 价值拐点都会让结论翻转。模型在 2010–2016 成立,不代表 2017–2024 成立;务必样本外 / 滚动窗口验证,别在整段历史上挑最好看的结果报告。
结语#
Fama–French 五因子不是”跑赢市场”的圣杯,而是一个诊断框架:它逼问你——你的超额收益,到底是承担了某种因子风险,还是真有模型没覆盖的 alpha?
本文的受控模拟给了一个干净的答案:当 CAPM 给你算出亮眼的 alpha 时,先别激动——它很可能只是模型漏掉了规模、价值、盈利或投资。把因子补齐到 FF5,大部分”伪 alpha”会塌掉。 而在 A 股,由于价值因子长期疲软、因子随时变,这个”补齐因子”的动作更要配上前视隔离、复权、幸存者修正和滚动验证,结论才站得住。