
在实证金融和资产定价研究中Fama-French五因子模型是分析股票收益横截面差异的基石工具。然而从原始数据清洗、因子构建到最终的回归检验整个过程涉及大量繁琐的代码编写和数据处理对于许多研究者尤其是刚接触Stata的同学来说是一个不小的挑战。本文将提供一套完整的、可复现的解决方案涵盖从2000年至2025年数据更新至最新可用的中国A股市场Fama-French五因子构建流程、数据说明以及全套Stata代码。无论你是需要完成课程作业、撰写学术论文还是进行量化策略研究都可以直接复用本文的代码框架和数据逻辑快速上手。1. Fama-French五因子模型核心概念与背景在深入代码之前我们有必要清晰地理解模型本身这有助于我们在后续处理数据时明白每一步的目的。1.1 模型是什么解决了什么问题传统的资本资产定价模型认为资产的预期收益只与市场风险Beta有关。但现实中市值小、账面市值比高的股票往往能获得更高的收益这被称为“市场异象”。Fama和French教授在1993年提出了三因子模型在市场因子的基础上加入了市值因子和账面市值比因子显著提升了模型对股票收益的解释力。2015年他们进一步扩展模型提出了五因子模型。该模型在原有三因子的基础上新增了两个因子盈利因子高盈利能力的公司股票组合收益与低盈利能力公司股票组合收益之差。投资因子低投资水平即保守投资的公司股票组合收益与高投资水平即激进投资的公司股票组合收益之差。这个模型解决了什么问题它试图更全面地捕捉股票横截面收益的驱动力量。盈利能力和投资风格被认为是影响公司未来现金流和风险的重要特征五因子模型通过将它们因子化旨在更准确地为资产定价并检验市场有效性。1.2 五因子具体定义与计算方法理解每个因子的具体构建方法是编写代码的前提。以下是基于中国A股市场特性的通用定义市场因子所有股票的投资组合收益率减去无风险利率。(Rm - Rf)市值因子用小市值股票组合收益率减去大市值股票组合收益率。(SMB)账面市值比因子用高账面市值比股票组合收益率减去低账面市值比股票组合收益率。(HML)盈利因子用高盈利能力股票组合收益率减去低盈利能力股票组合收益率。(RMW)投资因子用保守投资股票组合收益率减去激进投资股票组合收益率。(CMA)其中SMB、HML、RMW、CMA这四个因子的构建是核心难点需要根据市值、账面市值比、盈利能力和投资水平对股票进行独立双重排序分组。1.3 本文数据与代码范围说明本文提供的方案旨在构建适用于中国A股研究的五因子。需要明确以下几点数据源通常使用CSMAR、Wind或Tushare等数据库的原始数据。本文代码逻辑通用你只需替换数据源接口即可。样本范围A股所有上市公司通常剔除金融行业、ST/*ST股票、上市不足一定期限的股票。时间范围示例代码框架覆盖2000年至2025年。因子收益率通常计算为月度数据。关键指标收益率考虑现金红利再投资的月度回报率。市值流通市值或总市值。账面市值比股东权益合计 / 总市值。盈利能力营业利润 / 总资产。投资水平总资产增长率。2. 研究环境与数据准备2.1 Stata环境配置本文所有操作均在Stata 17中完成但代码兼容Stata 15及以上版本。确保你的Stata已安装常用命令如asdoc,esttab等它们能美化回归结果输出。如果没有安装可以使用ssc install命令安装。* 安装可能需要的外部命令非必需但推荐 ssc install asdoc, replace // 用于将结果导出到Word/Excel ssc install estout, replace // 用于制作回归结果表格2.2 原始数据结构与清洗假设你已经从数据库导出了原始数据并保存为Stata数据文件raw_data.dta。你的数据至少应包含以下变量变量名可自定义代码中对应修改即可变量名含义格式Stkcd股票代码stringDate日期月度numeric (e.g., ym格式: 202501)Ret个股月度收益率numericMktCap市值亿元numericBM账面市值比numericOP盈利能力如ROAnumericInv投资水平资产增长率numericRiskFree无风险利率月度numericMarketRet市场组合月度收益率numeric数据清洗关键步骤在构建因子前必须对原始数据进行严格清洗否则结果会产生严重偏差。* 示例数据清洗核心代码片段 use “raw_data.dta”, clear * 1. 剔除金融行业股票行业代码以J开头的 drop if substr(Indcd,1,1) “J” * 2. 剔除ST、*ST、PT等特殊处理股票假设有变量Status标识 drop if Status “ST” | Status “*ST” * 3. 剔除上市不足6个月的股票假设有变量ListDate gen list_ym ym(year(ListDate), month(ListDate)) gen months_since_list (Date - list_ym) drop if months_since_list 6 months_since_list ! . * 4. 处理极端值对关键连续变量进行缩尾处理Winsorize foreach var in Ret MktCap BM OP Inv { winsor2 var, cuts(1 99) replace // 在1%和99%分位数处缩尾 } * 5. 确保数据为面板格式并按股票和时间排序 sort Stkcd Date3. 因子构建SMB, HML, RMW, CMA 计算详解这是整个流程最核心的部分。我们将按照Fama-French的经典方法在每年6月末利用上一财年的财务数据和6月末的市值数据进行2x3独立双重排序。3.1 确定排序时点与财务数据匹配财务数据存在滞后性。我们通常使用t-1年的年报数据来计算t年7月到t1年6月的因子。* 生成财务数据生效的起始月份 gen fiscal_year_end ym(Year, 12) // 假设年报在12月31日 gen portfolio_formation_date ym(Year1, 7) // 次年7月开始使用该财务数据3.2 独立双重排序分组以市值和账面市值比为例构建SMB和HML因子按市值中位数分组形成大市值和小市值组合。按账面市值比30%/70%分位数分组形成低、中、高BM组合。交叉形成6个组合SL, SM, SH, BL, BM, BH。* 假设当前处理的数据集已经对齐到portfolio_formation_date并包含MktCap和BM * 对每个形成期每年6月进行操作 levelsof portfolio_formation_date, local(formation_dates) foreach date in formation_dates { preserve keep if portfolio_formation_date date * 计算市值中位数 sum MktCap, detail local size_median r(p50) * 计算BM的30%和70%分位数 _pctile BM, percentiles(30 70) local bm_30 r(r1) local bm_70 r(r2) * 生成市值分组虚拟变量 gen size_group “B” if MktCap size_median // Big replace size_group “S” if MktCap size_median // Small * 生成BM分组虚拟变量 gen bm_group “L” if BM bm_30 // Low replace bm_group “M” if BM bm_30 BM bm_70 // Medium replace bm_group “H” if BM bm_70 // High * 生成2x3组合标识 gen portfolio size_group bm_group // 如 “SH”, “BL” * 保存这个时点的分组信息后续与收益率数据合并 tempfile groups_date save groups_date restore }3.3 计算组合收益率及因子收益率将上述分组信息与未来12个月的月度收益率数据合并计算每个组合的市值加权平均月度收益率。* 合并分组信息与收益率数据略过详细合并代码 * 计算每个组合在每个月的市值加权收益率 egen total_mktcap_portfolio total(MktCap), by(Date portfolio) gen weight MktCap / total_mktcap_portfolio gen weighted_ret Ret * weight collapse (sum) port_ret weighted_ret, by(Date portfolio) * 将数据从长格式转换为宽格式每个组合一列 reshape wide port_ret, i(Date) j(portfolio) string * 计算SMB和HML因子收益率 * SMB (SLSMSH)/3 - (BLBMBH)/3 gen SMB (port_retSL port_retSM port_retSH)/3 - (port_retBL port_retBM port_retBH)/3 * HML (SHBH)/2 - (SLBL)/2 gen HML (port_retSH port_retBH)/2 - (port_retSL port_retBL)/2对于RMW和CMA因子流程完全类似只是排序变量换成了盈利能力OP和投资水平Inv。同样进行2x3独立双重排序按市值和OP/Inv然后计算RMW (SR BR)/2 - (SW BW)/2(Robust - Weak)CMA (SC BC)/2 - (SA BA)/2(Conservative - Aggressive)3.4 整合五因子收益率序列最终你需要将市场因子、无风险利率与计算出的SMB, HML, RMW, CMA序列合并形成一个包含以下变量的月度数据集FF5_Factors.dtaDateMkt_RF(市场超额收益)SMBHMLRMWCMARF(无风险利率)4. 完整实战Stata代码框架与回归检验4.1 主程序框架下面是一个高度整合的、模块化的主程序框架。在实际使用时你需要根据数据源调整数据导入和清洗部分。* * 主程序构建中国A股Fama-French五因子 * 时间范围2000-2025 * clear all set more off set mem 1g cap log close log using “FF5_Construction.log”, text replace * 1. 设置路径和参数 global ROOT “D:\Research\FF5_Factors” // 修改为你的项目路径 global DATA “$ROOT\data” global OUTPUT “$ROOT\output” global CODE “$ROOT\code” cd “$ROOT” * 2. 执行数据清洗模块 do “$CODE\1_data_cleaning.do” * 3. 执行因子构建模块 (此模块内部分为SMB/HML, RMW, CMA等子模块) do “$CODE\2_factor_building.do” * 4. 整合最终因子数据 do “$CODE\3_factor_merging.do” * 5. 因子描述性统计与相关性分析 use “$OUTPUT\FF5_Factors.dta”, clear sum Mkt_RF SMB HML RMW CMA RF corr Mkt_RF SMB HML RMW CMA asdoc sum Mkt_RF SMB HML RMW CMA RF, replace save(summary_stats.doc) asdoc corr Mkt_RF SMB HML RMW CMA, replace save(correlation_matrix.doc) * 6. 使用构建的因子进行资产定价测试示例 * 6.1 准备测试资产收益率例如25个按市值和BM排序的组合 do “$CODE\4_form_test_portfolios.do” // 生成测试组合收益率 test_portfolios.dta * 6.2 合并因子与测试资产收益率 use “$OUTPUT\FF5_Factors.dta”, clear merge 1:m Date using “$OUTPUT\test_portfolios.dta”, nogen * 6.3 运行时间序列回归 * 对每个测试组合i回归: R_i - Rf a b1*Mkt_RF b2*SMB b3*HML b4*RMW b5*CMA e local portfolios “port1 port2 port3 port4 port5” // 示例组合名 foreach port of local portfolios { gen port_excess port’ - RF // 计算超额收益 reg port_excess Mkt_RF SMB HML RMW CMA estimates store reg_port’ } * 6.4 输出回归结果表格 esttab reg_*, /// b(%9.4f) se(%9.4f) /// star(* 0.1 ** 0.05 *** 0.01) /// stats(N r2_a, fmt(%9.0g %9.3f)) /// title(“Fama-French 5-Factor Model Time-Series Regressions”) /// mtitle(“Port1” “Port2” “Port3” “Port4” “Port5”) /// compress replace log close4.2 关键子模块示例因子构建 (2_factor_building.do)* * 子程序构建SMB和HML因子 * use “$DATA\cleaned_data.dta”, clear * 确保有形成期变量formation_ym每年6月 gen formation_ym ym(year(Date), 6) if month(Date) 6 bysort Stkcd: carryforward formation_ym, replace * 循环每年6月进行排序 levelsof formation_ym, local(years) foreach y of local years { di “Processing formation year: y” * 获取排序截面数据 preserve keep if formation_ym y’ keep Stkcd MktCap BM duplicates drop // 确保每个股票在形成期只有一条记录 * 分组逻辑 _pctile MktCap, percentiles(50) gen size_dummy (MktCap r(r1)) _pctile BM, percentiles(30 70) gen bm_group 1 if BM r(r1) replace bm_group 2 if BM r(r1) BM r(r2) replace bm_group 3 if BM r(r2) BM ! . * 保存分组映射文件 keep Stkcd size_dummy bm_group tempfile groups_y’ save groups_y’’ restore } * 合并分组信息并计算组合收益率此处省略详细合并与加权计算步骤 * … … * 最终生成包含SMB和HML的临时文件 tempfile smb_hml save smb_hml’5. 常见问题与排查思路在构建因子过程中你几乎一定会遇到以下问题。这里提供排查思路。问题现象可能原因解决思路SMB因子收益率波动异常小或为01. 市值数据单位错误元 vs 亿元。2. 分组时所有股票都被分到了同一组中位数计算错误。3. 收益率数据与分组信息未正确合并。1. 检查sum MktCap的输出确认单位合理。2. 在分组后使用tab size_dummy查看分布。3. 仔细检查合并所用的关键变量Stkcd, Date是否匹配。回归截距项显著不为零1. 因子构建有误未能完全解释测试资产收益。2. 测试资产本身构造有问题。3. 样本期存在结构性变化。1. 重新检查因子计算每一步特别是加权平均部分。2. 用市场因子单独回归看截距是否显著。3. 分样本期如牛熊市进行回归检验。Stata报错 “weights not allowed”在collapse或reg命令中错误使用了权重语法。计算市值加权收益率时应先生成权重变量再计算weighted_ret ret * weight最后对weighted_ret求和。而不是在collapse中直接使用aweight。因子数据存在大量缺失值1. 财务数据缺失导致某些股票无法分组。2. 在非6月月份也执行了分组操作。3. 合并数据时使用了不恰当的连接方式。1. 在分组前使用drop if missing(BM, OP, Inv)。2. 确保只在formation_ym指定的时点进行排序分组。3. 使用merge 1:m或joinby时检查匹配情况tab _merge。结果与经典文献数值量级差异大1. 收益率使用的是百分比还是小数0.01代表1%2. 因子收益率是月度的还是年化的1. 统一所有收益率变量为小数形式。2. 明确输出的是月度因子溢价。年化通常需要乘以12但波动性也会变化。6. 最佳实践与工程化建议将学术研究代码工程化能极大提升复现性和效率。模块化编程如主程序所示将数据清洗、因子构建、回归检验拆分成独立的.do文件。通过主程序调用逻辑清晰易于调试。路径管理使用全局宏定义路径避免代码中充斥硬编码的路径。更换电脑或分享代码时只需修改一处。日志记录始终使用log using ...命令。它记录了所有执行步骤和结果是复现和排查错误的生命线。使用临时文件在循环或复杂数据处理中多用tempfile保存中间结果避免生成大量冗余的磁盘文件同时提升速度。版本控制使用Git管理你的代码和文档。特别是因子构建方法可能调整清晰的提交历史能让你随时回退到可用的版本。敏感性分析不要只做一套参数。尝试使用总市值 vs 流通市值。改变分组分位数如用50%分位数代替30%/70%。使用不同的盈利和投资指标如ROE代替ROA营收增长率代替资产增长率。在结果部分报告这些敏感性测试能使你的研究更稳健。代码注释与文档在关键步骤特别是容易出错的排序、合并、加权计算处详细注释你的意图和逻辑。可以单独编写一个README.md说明数据需求、运行顺序和输出文件。掌握Fama-French五因子模型的构建不仅是完成一项具体的计算任务更是深入理解多因子资产定价模型思想的过程。从数据清洗的严谨性到分组排序的精确性再到回归检验的完整性每一步都考验着研究者的数据处理能力和经济直觉。本文提供的Stata代码框架是一个强大的起点你可以在此基础上针对不同的市场、不同的样本期、甚至不同的因子定义进行探索和拓展。