1. 从"一只股票涨了"到"为什么涨":三因素模型的出场逻辑
很多人做投资分析时,习惯盯着单只股票的涨跌找原因——公司发了财报、行业出了政策、大盘情绪好。但如果你同时持有几十只甚至上百只股票,这种"一只一只找原因"的方法就彻底失效了。你需要一个能解释一组股票收益差异的框架。Fama-French三因素模型就是干这个的。
这个模型的核心价值在于:它把股票收益的来源拆解成三个维度——市场整体涨跌、小盘股相对大盘股的溢价、价值股相对成长股的溢价。换句话说,它告诉你,你赚的钱到底是来自"大盘涨了"这种系统性因素,还是来自你选到了小盘股或价值股这类有特定风格特征的股票。
我第一次接触这个模型是在做量化策略回测的时候。当时我构建了一个"低市盈率选股"策略,回测显示年化超额收益有8%,看起来很不错。但用三因素模型一分解,发现这8%里面大部分是价值因子带来的beta收益,真正的alpha只有不到2%。这个发现让我重新审视了整个策略——原来我赚的主要是风格暴露的钱,而不是选股能力。这就是三因素模型最直接的应用场景:业绩归因。
它适合谁?量化研究员、基金经理、做策略回测的开发者、甚至是想搞清楚自己投资收益来源的散户。你不需要是经济学博士,但需要理解基本的回归分析和统计概念。接下来的内容,我会从模型的经济学直觉讲起,然后手把手带你用Python实现因子计算和回归分析,最后分享几个我在实操中踩过的坑和总结的技巧。
2. 三因素模型的经济学直觉:为什么是这三个因子
2.1 市场因子:CAPM的遗产
三因素模型的第一个因子是市场超额收益,也就是市场组合收益率减去无风险利率。这个因子直接继承自CAPM(资本资产定价模型)。CAPM的核心思想很简单:你承担了市场风险,就应该获得相应的风险溢价。如果一只股票的beta是1.2,市场涨10%,你预期它涨12%(不考虑无风险利率),多出来的2%就是对你承担额外市场风险的补偿。
但CAPM在实证中表现并不好。大量研究发现,beta并不能完全解释股票收益的横截面差异。比如小盘股长期跑赢大盘股,价值股长期跑赢成长股,这些现象CAPM都解释不了。Fama和French在1992年的论文里系统性地展示了这些"异象",然后在1993年提出了三因素模型。
2.2 规模因子SMB:小盘股的"神秘溢价"
SMB(Small Minus Big)代表小市值公司组合收益率减去大市值公司组合收益率。这个因子捕捉的是规模效应:长期来看,小盘股的平均收益高于大盘股。
为什么会有这个溢价?学术界的解释有好几种。一种说法是流动性风险——小盘股交易不活跃,买卖价差大,投资者需要额外收益来补偿这个流动性风险。另一种说法是信息不对称——小盘股关注度低,信息传播慢,存在错误定价的机会。还有一种行为金融学的解释:投资者对小盘股过度乐观或过度悲观,导致价格偏离基本面。
不管原因是什么,这个因子在实证中确实存在。我在回测中观察到,A股市场的小盘股效应在某些年份非常显著,但在另一些年份(比如2017年的大盘蓝筹行情)会完全反转。所以这个因子不是"永远有效",而是有周期性。
2.3 价值因子HML:便宜货的"逆袭"
HML(High Minus Low)代表高账面市值比公司组合收益率减去低账面市值比公司组合收益率。账面市值比就是净资产除以市值,比值高说明公司"便宜"(价值股),比值低说明公司"贵"(成长股)。
价值溢价的经济学逻辑更直观:投资者对成长股过度乐观,愿意支付高价,导致预期收益降低;对价值股过度悲观,价格被压低,反而提供了更高的预期收益。这本质上是一种均值回归的力量——便宜的东西长期来看会回归合理价值。
我在实操中发现,HML因子在A股的表现和美股有显著差异。A股的"价值股"很多时候集中在银行、地产、钢铁这些传统行业,而这些行业又受到宏观经济政策和产业周期的强烈影响。所以直接用美股的HML构建方法套到A股,效果可能不理想。后面我会详细讲怎么根据市场特点调整因子构建方法。
3. 因子构建的实操细节:从数据到组合
3.1 数据准备:你需要哪些字段
构建三因素模型需要以下几类数据:
- 股票收益率:日度或月度收益率,建议用考虑现金分红的复权收益率
- 市值数据:流通市值或总市值,用于分组
- 账面价值数据:净资产,通常从财报中获取
- 无风险利率:一般用一年期定期存款利率或国债收益率
这里有个关键细节:账面价值数据的时滞问题。财报数据不是实时更新的,年报通常在次年4月底前披露。所以你在6月份构建组合时,应该用上一年年报的净资产数据,而不是一季报的。Fama和French在原始论文中用的是t-1年末的账面价值除以t年6月末的市值。这个细节看起来小,但如果不注意,会引入前视偏差(look-ahead bias),让你的回测结果虚高。
我在早期做回测时就犯过这个错误。当时直接用最新的财报数据计算账面市值比,回测显示价值因子年化收益超过15%。后来修正了时滞后,收益降到了8%左右。这个教训让我明白:回测中的每一个数据点,都必须确保在决策时点是可获得的。
3.2 分组方法:2x3还是5x5
Fama和French原始论文用的是2x3分组:先按市值中位数分成小盘和大盘两组,再按账面市值比的30%、70%分位数分成低、中、高三组,交叉后得到6个组合。SMB = (小盘价值 + 小盘中值 + 小盘成长)/3 - (大盘价值 + 大盘中值 + 大盘成长)/3,HML = (小盘价值 + 大盘价值)/2 - (小盘成长 + 大盘成长)/2。
后来Fama和French在2015年的五因素模型中改用了2x3分组的变体,但核心逻辑没变。也有研究者用5x5分组(按市值和账面市值比各分5组),得到25个组合。分组越细,因子收益的估计越精确,但对数据量的要求也越高。
我的建议是:A股市场用2x3分组就够了。因为A股股票数量虽然多,但很多小盘股的流动性很差,分得太细会导致某些组合里只有几只股票,因子收益的噪声会很大。而且A股的账面市值比分布和美股不同,30%和70%分位数可能不是最优的切分点。你可以先用中位数和30/70分位数试一下,看看因子收益的单调性如何,再决定要不要调整。
3.3 因子收益的计算:加权还是等权
计算组合收益率时,有两种加权方式:等权和市值加权。Fama和French原始论文用的是市值加权,理由是市值加权更接近市场组合的构造方式,能更好地反映投资者的实际收益。
但等权也有它的优势:等权组合对小盘股的暴露更大,因为小盘股在等权组合中的权重被放大了。如果你研究的是小盘股效应,等权可能更合适。我在实操中会同时计算两种加权方式的因子收益,然后对比它们的表现。如果差异很大,说明因子收益对加权方式敏感,需要谨慎解释。
还有一个细节:组合再平衡的频率。Fama和French用的是年度再平衡(每年6月重新分组),因为账面价值数据是年度更新的。但市值数据是实时变化的,所以组合权重在一年内会自然漂移。你也可以选择月度再平衡,但这样换手率会很高,交易成本会吃掉很多收益。我的经验是:年度再平衡 + 月度计算因子收益是一个比较平衡的方案。
4. Python实现:从数据到回归的完整流程
4.1 数据获取与清洗
假设你已经有了股票收益率、市值和账面价值的数据,存在一个DataFrame里。数据清洗的关键步骤包括:
import pandas as pd import numpy as np import statsmodels.api as sm # 假设df包含以下列:date, stock_id, ret, mkt_cap, book_value # 先处理缺失值 df = df.dropna(subset=['ret', 'mkt_cap', 'book_value']) # 计算账面市值比 df['bm'] = df['book_value'] / df['mkt_cap'] # 剔除账面市值比为负的样本(资不抵债的公司) df = df[df['bm'] > 0] # 剔除市值最小的30%股票(避免流动性极差的股票影响结果) df['mkt_cap_rank'] = df.groupby('date')['mkt_cap'].rank(pct=True) df = df[df['mkt_cap_rank'] > 0.3]这里有个重要的细节:为什么要剔除小市值股票?因为A股有很多"壳公司",市值极小,流动性极差,价格容易被操纵。如果把这些股票纳入组合,因子收益会被极端值主导,失去经济意义。Fama和French在原始论文中也剔除了市值最小的股票。具体剔除比例可以根据市场情况调整,我一般用30%作为默认值。
4.2 因子构建的核心代码
def construct_factors(df): """ 构建SMB和HML因子 df: 包含date, stock_id, ret, mkt_cap, bm的DataFrame """ factors = [] for date, group in df.groupby('date'): # 按市值中位数分组 mkt_median = group['mkt_cap'].median() group['size_group'] = np.where(group['mkt_cap'] <= mkt_median, 'S', 'B') # 按账面市值比的30%和70%分位数分组 bm_30 = group['bm'].quantile(0.3) bm_70 = group['bm'].quantile(0.7) group['bm_group'] = np.where(group['bm'] <= bm_30, 'L', np.where(group['bm'] >= bm_70, 'H', 'M')) # 计算6个组合的市值加权收益率 portfolios = {} for size in ['S', 'B']: for bm in ['L', 'M', 'H']: subset = group[(group['size_group'] == size) & (group['bm_group'] == bm)] if len(subset) > 0: # 市值加权 weights = subset['mkt_cap'] / subset['mkt_cap'].sum() portfolios[f'{size}{bm}'] = (subset['ret'] * weights).sum() else: portfolios[f'{size}{bm}'] = np.nan # 计算SMB和HML smb = (portfolios['SL'] + portfolios['SM'] + portfolios['SH'])/3 - \ (portfolios['BL'] + portfolios['BM'] + portfolios['BH'])/3 hml = (portfolios['SH'] + portfolios['BH'])/2 - \ (portfolios['SL'] + portfolios['BL'])/2 factors.append({ 'date': date, 'SMB': smb, 'HML': hml }) return pd.DataFrame(factors)这段代码的核心逻辑是:每个月末,按市值和账面市值比把股票分成6组,计算每组的市值加权收益率,然后构造SMB和HML。注意这里用的是月度再平衡,如果你想要年度再平衡,需要把分组逻辑改成只在每年6月执行一次,其他月份沿用上次的分组结果。
4.3 回归分析:如何解读结果
有了因子收益数据后,就可以做时间序列回归了。假设你要分析某个投资组合的超额收益:
# 假设portfolio_ret是投资组合的月度收益率 # rf是无风险利率 # mkt_ret是市场组合收益率 # 计算超额收益 portfolio_excess = portfolio_ret - rf mkt_excess = mkt_ret - rf # 合并数据 reg_data = pd.DataFrame({ 'portfolio_excess': portfolio_excess, 'mkt_excess': mkt_excess, 'SMB': factors['SMB'], 'HML': factors['HML'] }).dropna() # 添加常数项 X = sm.add_constant(reg_data[['mkt_excess', 'SMB', 'HML']]) y = reg_data['portfolio_excess'] # OLS回归 model = sm.OLS(y, X).fit() print(model.summary())回归结果中,你需要关注几个关键指标:
- alpha(常数项):这是因子模型无法解释的超额收益。如果alpha显著为正,说明你的策略有真正的选股能力;如果不显著,说明收益主要来自因子暴露。
- beta系数:分别对应市场、规模、价值因子的暴露。比如SMB的系数是0.5,说明你的组合偏向小盘股。
- R-squared:模型解释了多少收益变异。三因素模型通常能解释80%-95%的收益变异,如果R-squared很低,说明还有重要的因子没被纳入。
我在实操中会特别关注alpha的t统计量。如果t值小于2,我基本不会认为这个策略有真正的alpha。很多看起来不错的策略,回归之后alpha都不显著,收益主要来自因子暴露。这种情况下,你不如直接买对应的因子ETF,成本更低。
5. 三因素模型在A股的适用性:我踩过的坑
5.1 小盘股效应在A股的"反转"
前面提到,SMB因子在A股的表现和美股有显著差异。我在2015-2020年的数据上做过测试,发现SMB因子在某些年份收益很高(比如2015年小盘股牛市),但在2017年和2018年完全反转(大盘蓝筹行情)。更麻烦的是,A股的"小盘股"和"壳价值"高度相关。很多小市值公司的主营业务很差,但因为上市资格稀缺,股价被"壳价值"支撑。这种情况下,SMB因子捕捉的就不是纯粹的规模效应,而是"壳价值"的波动。
我的应对方法是:在构建SMB因子时,剔除那些市值极小且基本面极差的公司。具体来说,我会剔除市值最小的10%股票,同时剔除ROE为负且资产负债率超过80%的公司。这样构造出来的SMB因子更干净,更能反映真实的规模效应。
5.2 账面市值比的行业偏差
A股的账面市值比有明显的行业特征:银行、地产、钢铁、煤炭这些行业的账面市值比普遍偏高,而科技、医药、消费行业的账面市值比普遍偏低。如果你直接用全市场的30%和70%分位数分组,会发现"价值股"组合里全是银行地产,"成长股"组合里全是科技医药。这样构造的HML因子,本质上是在做行业轮动,而不是纯粹的價值溢价。
解决办法有两种:一是行业中性化,在每个行业内部分组,然后跨行业汇总;二是剔除金融行业,因为金融行业的会计规则和实体企业差异太大,账面价值的意义不同。我一般会同时计算"全市场HML"和"行业中性HML",对比两者的表现。如果差异很大,说明行业偏差严重,需要用行业中性化的版本。
5.3 数据频率的选择:月度还是日度
Fama和French原始论文用的是月度数据,因为账面价值数据是年度更新的,月度频率已经足够。但有些研究者会用日度数据做回归,认为样本量更大,统计显著性更高。
我的经验是:日度数据在A股要慎用。原因有两个:一是A股的日度收益率噪声很大,很多股票经常涨停跌停,价格不连续;二是日度数据的微观结构问题(比如买卖价差、非同步交易)会严重影响因子收益的估计。如果你一定要用日度数据,建议用周度或双周度作为折中,既能增加样本量,又能避免日度噪声。
6. 从三因素到多因素:模型的扩展思路
6.1 动量因子的加入:Carhart四因素模型
三因素模型最大的缺陷是无法解释动量效应——过去涨得好的股票,未来一段时间继续涨。Carhart在1997年提出了四因素模型,在三因素的基础上加入了动量因子UMD(Up Minus Down),即过去12个月收益率最高的30%股票组合减去最低的30%股票组合。
我在A股测试过动量因子,发现A股的动量效应比美股弱很多,而且反转效应更强。具体来说,A股在1个月以内的短期反转效应非常显著,但3-12个月的中期动量效应不稳定。所以如果你要在A股用四因素模型,动量因子的构建方法需要调整——可能用1个月反转因子比用12个月动量因子更有效。
6.2 五因素模型:盈利和投资的加入
Fama和French在2015年又提出了五因素模型,增加了盈利因子RMW(Robust Minus Weak,高盈利公司减低盈利公司)和投资因子CMA(Conservative Minus Aggressive,低投资公司减高投资公司)。这两个因子的经济学逻辑是:盈利能力强的公司应该获得更高收益,投资激进的公司应该获得更低收益(因为过度投资会毁灭价值)。
我在A股测试过这两个因子,发现RMW因子的表现比较稳定,但CMA因子的效果不稳定。可能的原因是A股的投资行为受政策和产业周期影响很大,很多高投资的公司其实是处于扩张期的优质企业,不应该被简单归类为"价值毁灭"。所以如果你要用五因素模型,建议先单独测试每个因子在A股的表现,不要直接套用美股的结论。
6.3 因子择时:什么时候该超配哪个因子
三因素模型不仅能用来归因,还能用来做因子择时。核心思路是:根据宏观经济状态和市场情绪,动态调整对各个因子的暴露。比如在经济复苏期,小盘股和价值股通常表现更好;在经济衰退期,大盘股和成长股更抗跌。
我做过一个简单的因子择时策略:用PMI(采购经理人指数)作为经济状态的代理变量,当PMI上行时超配SMB和HML,当PMI下行时低配。回测显示这个策略能带来2%-3%的年化超额收益,但换手率很高,扣除交易成本后收益有限。所以因子择时不是"圣杯",更适合作为辅助工具,而不是核心策略。
7. 实操中的几个关键注意事项
7.1 前视偏差:最容易犯的错误
前视偏差是指你在回测中使用了当时无法获得的信息。在三因素模型中,最常见的前视偏差是用最新的财报数据计算账面市值比。比如你在2023年6月做回测,用了2023年一季报的净资产数据,但一季报可能要到4月底才披露,而且很多公司会延迟披露。正确的做法是:用t-1年年报的净资产数据,除以t年6月末的市值。这样确保你在6月末做决策时,所有信息都是可获得的。
7.2 生存者偏差:退市股票的处理
如果你用的股票池只包含当前还在上市的股票,就会引入生存者偏差——那些退市的、表现极差的股票被剔除了,导致回测收益虚高。正确的做法是:使用包含退市股票的全样本数据,并且在股票退市时,假设以退市前的最后价格卖出。很多免费数据源不包含退市股票,这是你需要特别注意的。
7.3 交易成本:不可忽视的"隐形杀手"
三因素模型的因子组合通常换手率较高,尤其是小盘股组合。如果你不做交易成本的扣除,回测收益会严重高估。我的经验是:A股的单边交易成本大约在0.2%-0.3%(包括佣金、印花税、冲击成本)。对于月度再平衡的组合,年化换手率可能在200%-300%,这意味着每年要扣除0.4%-0.9%的收益。对于高频再平衡的策略,交易成本可能吃掉大部分超额收益。
7.4 因子收益的单调性检验
在构建因子之前,建议先做分组单调性检验:按账面市值比从低到高分成10组,计算每组的平均收益率。如果收益率从低到高单调递增,说明价值因子有效;如果中间有跳跃或反转,说明因子构建方法可能有问题。我在实操中会把单调性检验作为因子有效性的第一道门槛,不通过就不进入后续的回归分析。
8. 一个完整的案例:用三因素模型分析某量化策略
假设你有一个"低估值+高ROE"的选股策略,回测显示年化收益18%,夏普比率1.2。你想知道这个策略的收益来源。步骤是这样的:
第一步,获取策略的月度收益率序列,计算超额收益(减去无风险利率)。第二步,获取市场组合收益率、SMB和HML因子收益率。第三步,做时间序列回归。假设回归结果如下:
| 变量 | 系数 | t值 |
|---|---|---|
| alpha | 0.002 | 1.2 |
| mkt_excess | 0.85 | 12.5 |
| SMB | 0.35 | 4.8 |
| HML | 0.62 | 7.3 |
R-squared = 0.91
解读:alpha的t值只有1.2,不显著,说明策略没有真正的选股能力。收益主要来自三个方面:市场暴露(beta=0.85)、小盘股暴露(SMB=0.35)、价值股暴露(HML=0.62)。换句话说,这个策略本质上是一个"小盘价值"策略,你完全可以买小盘价值ETF来复制,成本更低。
这个案例告诉我们:三因素模型的最大价值不是预测收益,而是帮你理解收益的来源。很多看起来"很厉害"的策略,回归之后发现只是因子暴露的包装。真正有alpha的策略,alpha的t值应该大于2,而且R-squared不会太高(因为还有因子没被纳入)。
9. 常见问题与排查思路
9.1 因子收益不显著怎么办
如果你构建的SMB或HML因子收益不显著,可能的原因有:样本期太短(建议至少5年)、分组方法不合理(试试不同的分位数)、数据质量问题(检查是否有异常值)、市场特点不同(A股的小盘股效应可能确实比美股弱)。排查顺序是:先检查数据质量,再调整分组方法,最后考虑市场特点。
9.2 回归R-squared太低怎么办
如果R-squared低于70%,说明三因素模型解释力不足。可能的原因有:你的组合有强烈的行业暴露(试试加入行业因子)、有动量暴露(试试加入动量因子)、有特殊的交易模式(比如高频交易)。排查方法是:先做残差分析,看看残差是否有自相关或异方差,再考虑加入更多因子。
9.3 因子之间的相关性太高怎么办
SMB和HML之间通常有相关性,但不会太高(一般在0.1-0.3之间)。如果相关性超过0.5,说明你的因子构建方法可能有问题。比如如果你在分组时没有独立地按市值和账面市值比分组,而是先按市值分组再按账面市值比分组,会导致两个因子高度相关。正确的做法是独立分组:先按市值中位数分成两组,再独立地按账面市值比分位数分成三组,交叉后得到6个组合。
10. 最后的经验分享
做了这么多年的因子研究,我最大的体会是:三因素模型不是"真理",而是一个"工具"。它能帮你理解收益来源,但不能保证你赚钱。真正赚钱的策略,往往是在因子暴露的基础上,还有独特的选股逻辑或交易执行优势。
另外,A股的市场结构和美股差异很大,直接套用美股的因子构建方法往往会水土不服。我的建议是:先理解因子的经济学逻辑,再根据A股的特点调整构建方法。比如小盘股因子要剔除壳价值,价值因子要做行业中性化,动量因子要考虑A股的反转效应。这些调整没有标准答案,需要你在实操中不断测试和优化。
最后分享一个小技巧:在做因子回归之前,先画一下因子收益的累积曲线。如果累积曲线在某段时间突然跳升或跳水,说明有极端值或结构性变化,需要进一步排查。这个简单的可视化步骤,能帮你避免很多数据问题导致的错误结论。