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小盘股的神秘溢价SMBSmall Minus Big代表小市值公司组合收益率减去大市值公司组合收益率。这个因子捕捉的是规模效应长期来看小盘股的平均收益高于大盘股。为什么会有这个溢价学术界的解释有好几种。一种说法是流动性风险——小盘股交易不活跃买卖价差大投资者需要额外收益来补偿这个流动性风险。另一种说法是信息不对称——小盘股关注度低信息传播慢存在错误定价的机会。还有一种行为金融学的解释投资者对小盘股过度乐观或过度悲观导致价格偏离基本面。不管原因是什么这个因子在实证中确实存在。我在回测中观察到A股市场的小盘股效应在某些年份非常显著但在另一些年份比如2017年的大盘蓝筹行情会完全反转。所以这个因子不是永远有效而是有周期性。2.3 价值因子HML便宜货的逆袭HMLHigh 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还是5x5Fama和French原始论文用的是2x3分组先按市值中位数分成小盘和大盘两组再按账面市值比的30%、70%分位数分成低、中、高三组交叉后得到6个组合。SMB (小盘价值 小盘中值 小盘成长)/3 - (大盘价值 大盘中值 大盘成长)/3HML (小盘价值 大盘价值)/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(pctTrue) 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年提出了四因素模型在三因素的基础上加入了动量因子UMDUp Minus Down即过去12个月收益率最高的30%股票组合减去最低的30%股票组合。我在A股测试过动量因子发现A股的动量效应比美股弱很多而且反转效应更强。具体来说A股在1个月以内的短期反转效应非常显著但3-12个月的中期动量效应不稳定。所以如果你要在A股用四因素模型动量因子的构建方法需要调整——可能用1个月反转因子比用12个月动量因子更有效。6.2 五因素模型盈利和投资的加入Fama和French在2015年又提出了五因素模型增加了盈利因子RMWRobust Minus Weak高盈利公司减低盈利公司和投资因子CMAConservative 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值alpha0.0021.2mkt_excess0.8512.5SMB0.354.8HML0.627.3R-squared 0.91解读alpha的t值只有1.2不显著说明策略没有真正的选股能力。收益主要来自三个方面市场暴露beta0.85、小盘股暴露SMB0.35、价值股暴露HML0.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股的反转效应。这些调整没有标准答案需要你在实操中不断测试和优化。最后分享一个小技巧在做因子回归之前先画一下因子收益的累积曲线。如果累积曲线在某段时间突然跳升或跳水说明有极端值或结构性变化需要进一步排查。这个简单的可视化步骤能帮你避免很多数据问题导致的错误结论。