Fama-French五因子模型:从数据获取到Stata回归的完整实战指南 📅 发布时间:2026/9/3 12:05:45 👁 浏览次数: 大家好我是专注于金融计量与实证研究的博主。在学术研究和投资分析中Fama-French五因子模型是评估资产定价和投资组合表现的核心工具。然而从数据获取、因子构建到模型回归每一步都可能遇到数据格式、代码报错、结果解读等实际问题。网上资料虽多但往往零散代码片段不完整导致新手难以复现老手也常为细节调试耗费时间。本文旨在提供一套从数据到结果的完整、可复现的实战指南。我们将详细讲解如何获取2000-2025年数据截至最新可用的A股市场数据手动计算Fama-French五因子并利用Stata完成模型回归与结果分析。文章不仅包含可直接运行的Stata代码更会深入解释每一步背后的金融逻辑和计量原理帮助你真正理解模型而非仅仅“跑通代码”。无论你是金融专业的学生、从事量化研究的分析师还是对资产定价感兴趣的开发者都能从本文中获得一套可直接用于自己研究的工具箱。1. Fama-French五因子模型核心概念与应用场景在深入代码之前我们必须先理解模型本身。Fama-French五因子模型是在经典的三因子模型市场因子、市值因子、账面市值比因子基础上增加了盈利因子和投资因子旨在更全面地解释股票横截面收益率的差异。1.1 模型定义与因子解释该模型认为股票的预期收益率可以由其对五个风险因子的暴露程度来解释。模型表达式如下[ E(R_i) - R_f \beta_{iMKT}(R_m - R_f) \beta_{iSMB}SMB \beta_{iHML}HML \beta_{iRMW}RMW \beta_{iCMA}CMA ]其中(R_i - R_f)股票i的超额收益率。(R_m - R_f) (MKT)市场风险溢价。代表市场组合收益率与无风险利率之差是最基础的风险因子。SMB (Small Minus Big)市值因子。用小市值公司组合收益率减去大市值公司组合收益率。该因子捕捉了“规模效应”即历史上小公司往往能获得更高收益。HML (High Minus Low)账面市值比因子。用高账面市值比价值股组合收益率减去低账面市值比成长股组合收益率。捕捉“价值效应”。RMW (Robust Minus Weak)盈利因子。用高盈利公司组合收益率减去低盈利公司组合收益率。捕捉“盈利效应”。CMA (Conservative Minus Aggressive)投资因子。用低投资保守公司组合收益率减去高投资激进公司组合收益率。捕捉“投资效应”。1.2 为什么需要五因子模型三因子模型在很长一段时间内是资产定价的基准但后续研究发现它无法完全解释“盈利效应”和“投资效应”。即盈利能力强、投资保守的公司其股票收益率也倾向于更高。五因子模型的提出正是为了纳入这两个重要的公司特征从而提升模型的解释力。在当下的A股市场研究中应用五因子模型也更为普遍和严谨。1.3 主要应用场景学术研究检验中国市场是否存在规模、价值、盈利、投资效应作为基准模型检验新因子的增量解释能力。基金业绩评价分析基金经理的阿尔法α是否在控制了这五个风险因子后依然显著从而判断其选股能力是源于运气还是真正的技能。量化策略开发基于因子暴露β构建多空组合或寻找模型无法解释的“错误定价”股票进行套利。风险管理评估投资组合对各类风格因子的风险敞口。理解这些概念是正确构建因子和解读结果的前提。接下来我们将进入实战环节。2. 环境准备与数据说明工欲善其事必先利其器。本节的目的是搭建可复现的研究环境并明确数据来源和处理边界。2.1 Stata环境与必要命令本文所有操作基于Stata 17/18但代码在Stata 15及以上版本通常均可运行。确保你的Stata已安装以下常用命令如果未安装可以使用ssc install命令进行安装。* 检查并安装常用命令包 ssc install winsor2 // 用于缩尾处理 ssc install asdoc // 用于将结果导出到Word/Excel ssc install estout // 用于回归结果输出和格式化这些命令将极大方便后续的数据处理和结果汇报。2.2 数据来源与结构因子构建需要个股和市场的财务、交易数据。常见的数据源有CSMAR、Wind、Tushare等。为保持教程的通用性我们假设你已经从数据商处获得了以下结构的原始数据.dta或.csv格式股票月度收益率文件(monthly_return.dta)包含变量stkcd(股票代码),year(年份),month(月份),ret(考虑现金红利再投资的月度收益率),total_mv(月度总市值)。公司财务数据文件(financials.dta)包含变量stkcd,year(财报年份),bp(账面市值比即股东权益/总市值)roa(总资产收益率用于代理盈利能力)inv_growth(资产增长率用于代理投资水平)。注意财务数据通常是年报需要与后续月份的收益率进行匹配。市场无风险利率文件(risk_free.dta)包含变量year,month,rf(月度无风险利率通常使用三个月期国债收益率)。市场收益率文件(market_return.dta)包含变量year,month,rm(市场月度收益率如上证综指或沪深300全收益指数收益率)。数据时间范围我们的目标是构建2000年1月至2025年12月的因子。实际操作中因财务数据滞后构建2025年因子可能需要用到2024年的年报数据。请根据你的数据最新日期调整代码中的时间范围。2.3 项目工作目录与文件结构建议建立如下清晰的文件夹结构便于管理你的项目路径/ ├── raw_data/ # 存放原始数据文件 ├── processed_data/ # 存放处理中的中间数据 ├── results/ # 存放最终因子数据和回归结果 └── scripts/ # 存放Stata do文件 ├── 01_data_preparation.do ├── 02_factor_construction.do ├── 03_portfolio_sorting.do ├── 04_regression_analysis.do └── main.do # 主执行文件在Stata中使用cd “你的项目路径”命令切换到工作目录。3. 因子构建原理与Stata实现详解这是最核心的部分。我们将分步计算MKT、SMB、HML、RMW、CMA五个因子。因子构建的核心是2x3独立双重排序。3.1 数据预处理与合并首先我们需要将财务数据和收益率数据在时间上正确匹配。财务数据在t年报告通常从t年7月开始应用到t1年6月避免前瞻性偏差。这里我们采用一种简化但常用的方法将t年年报数据与t1年1月至12月的收益率进行匹配。* 脚本: 01_data_preparation.do * 目的: 数据清洗、匹配与预处理 use “raw_data/financials.dta”, clear * 生成财务变量假设已有bp, roa, inv_growth keep stkcd year bp roa inv_growth * 将财务年份标识为用于匹配的年份次年 gen fyear year 1 save “processed_data/financials_lagged.dta”, replace use “raw_data/monthly_return.dta”, clear merge m:1 stkcd fyear using “processed_data/financials_lagged.dta” keep if _merge 3 // 保留成功匹配的观测值 drop _merge * 合并市场收益率和无风险利率 merge m:1 year month using “raw_data/market_return.dta” keep if _merge 3 drop _merge merge m:1 year month using “raw_data/risk_free.dta” keep if _merge 3 drop _merge * 计算超额收益率 gen excess_ret ret - rf * 处理极端值对主要连续变量在1%和99%分位数上进行缩尾 winsor2 total_mv bp roa inv_growth, cuts(1 99) replace * 保存预处理后的主数据 save “processed_data/merged_monthly_data.dta”, replace3.2 2x3独立双重排序法构建因子以构建SMB和HML为例原理相同划分节点每年6月底利用总市值Size和账面市值比BP的中位数作为分界点。形成组合根据Size分为Big(B)和Small(S)根据BP分为High(H), Neutral(N), Low(L)。交叉形成6个组合(S/L, S/N, S/H, B/L, B/N, B/H)。计算组合收益率计算接下来12个月当年7月至次年6月每个组合的价值加权月度收益率。计算因子SMB (S/L S/N S/H)/3 - (B/L B/N B/H)/3 HML (S/H B/H)/2 - (S/L B/L)/2。* 脚本: 02_factor_construction.do (部分核心代码) * 目的: 构建SMB, HML, RMW, CMA因子 use “processed_data/merged_monthly_data.dta”, clear * 步骤1确定每年6月的截面数据用于分类 gen ym ym(year, month) bysort stkcd ym: keep if month 6 // 保留每年6月的数据点用于排序 keep stkcd year month total_mv bp roa inv_growth rename year sort_year // 排序年份 save “processed_data/june_sorting_data.dta”, replace * 步骤2对原始月度数据生成“排序年份”标识 * 规则当年7月到次年6月的数据使用当年6月的排序结果 use “processed_data/merged_monthly_data.dta”, clear gen sort_year year replace sort_year year - 1 if month 7 // 7-12月的数据使用上一年6月的指标排序 * 步骤3合并排序指标 merge m:1 stkcd sort_year using “processed_data/june_sorting_data.dta”, /// keepusing(total_mv bp roa inv_growth) /// rename(total_mv size_june, bp bp_june, roa roa_june, inv_growth inv_june) keep if _merge 3 drop _merge * 步骤4在每年6月的截面上进行独立双重排序 * 4.1 为SMB/HML排序 (Size and BP) gen size_median . gen bp_median . levelsof sort_year, local(years) foreach y of local years { sum size_june if sort_year y, detail replace size_median r(p50) if sort_year y’ sum bp_june if sort_year y’, detail replace bp_median r(p50) if sort_year y’ } * 生成规模分组 gen size_group “B” if size_june size_median !missing(size_june) replace size_group “S” if size_june size_median !missing(size_june) * 生成BP分组按30%, 40%, 30%分位数 gen bp_group “” foreach y of local years { xtile bp_pctile bp_june if sort_year y’, nq(10) replace bp_group “L” if bp_pctile 3 sort_year y’ // Low 30% replace bp_group “N” if bp_pctile 3 bp_pctile 7 sort_year y’ // Neutral 40% replace bp_group “H” if bp_pctile 7 sort_year y’ // High 30% drop bp_pctile } * 步骤5计算6个组合的月度价值加权收益率 gen port_sh size_group “” bp_group // 组合标识如 “SH”, “BN” * 计算市值权重 bysort ym port_sh: egen port_total_mv total(total_mv) gen weight total_mv / port_total_mv * 计算组合收益率 (价值加权) gen ret_weighted ret * weight bysort ym port_sh: egen port_ret total(ret_weighted) * 步骤6计算因子收益率 (以SMB和HML为例) preserve keep ym port_sh port_ret duplicates drop // 每个组合每月一个收益率 reshape wide port_ret, i(ym) j(port_sh) string * 计算SMB和HML gen SMB (port_retSH port_retSN port_retSL) / 3 - (port_retBH port_retBN port_retBL) / 3 gen HML (port_retSH port_retBH) / 2 - (port_retSL port_retBL) / 2 keep ym SMB HML save “processed_data/SMB_HML_factor.dta”, replace restore * 步骤7类似地构建RMW盈利和CMA投资因子 * 原理完全相同只是排序变量换成roa_june盈利和inv_june投资 * … (代码逻辑类似需根据ROA和INV_Growth进行分组) … * 假设已生成RMW和CMA因子保存 * merge m:1 ym using “processed_data/RMW_CMA_factor.dta” * 步骤8合并所有因子与市场因子 gen MKT rm - rf keep ym MKT excess_ret stkcd // 保留必要变量 merge m:1 ym using “processed_data/all_factors.dta” // 假设此文件已包含SMB,HML,RMW,CMA keep if _merge 3 drop _merge order ym stkcd excess_ret MKT SMB HML RMW CMA * 保存最终用于回归的数据集 save “results/regression_data.dta”, replace关键解释此段代码是因子构建的核心。winsor2用于处理异常值。xtile命令用于按分位数分组。reshape wide是将长数据转换为宽数据以便于计算因子差值。实际操作中RMW和CMA的构建需重复步骤4-6排序变量分别为roa_june和inv_june。4. 完整实战五因子模型回归与结果分析现在我们有了包含个股超额收益率和各因子的面板数据可以进行模型回归了。4.1 投资组合层面回归常用通常先构建不同特征的投资组合如按市值分10组然后对每个组合的收益率时间序列回归观察其因子暴露和阿尔法。* 脚本: 03_portfolio_sorting.do * 目的构建测试组合并运行时间序列回归 use “results/regression_data.dta”, clear * 1. 构建市值十分位组合 (示例) xtile size_decile total_mv, nq(10) by(year) // 每年按市值分10组 egen port_id group(year size_decile) // 生成组合标识 * 2. 计算组合的月度超额收益率 (等权) bysort ym port_id: egen port_excess_ret_ew mean(excess_ret) * 3. 对每个组合进行时间序列回归 levelsof port_id, local(ports) foreach p of local ports { qui reg port_excess_ret_ew MKT SMB HML RMW CMA if port_id p’ est store port_p’ } * 4. 使用esttab优雅地输出所有回归结果 esttab port_*, /// 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 Regressions for Size-Sorted Portfolios”) /// mtitle(“D1” “D2” … “D10”) // 手动调整标题为分位数组运行后你会得到一个表格显示10个组合各自的五个因子载荷β和截距项α即超额收益。一个运行良好的模型α应该不显著异于0。4.2 个股层面Fama-MacBeth回归另一种重要方法是Fama-MacBeth两步回归用于检验因子的风险溢价是否在横截面上显著。* 脚本: 04_regression_analysis.do (Fama-MacBeth部分) * 目的执行Fama-MacBeth回归 use “results/regression_data.dta”, clear xtset stkcd ym // 声明面板数据 * 第一步对每只股票用其全部时间序列数据回归得到因子暴露β gen beta_MKT . gen beta_SMB . gen beta_HML . gen beta_RMW . gen beta_CMA . levelsof stkcd, local(stocks) foreach s of local stocks { cap qui reg excess_ret MKT SMB HML RMW CMA if stkcd s’ if _rc 0 { // 如果回归成功 replace beta_MKT _b[MKT] if stkcd s’ replace beta_SMB _b[SMB] if stkcd s’ replace beta_HML _b[HML] if stkcd s’ replace beta_RMW _b[RMW] if stkcd s’ replace beta_CMA _b[CMA] if stkcd s’ } } * 第二步在每个月用所有股票的β对当月收益率做横截面回归得到该月的因子风险溢价λ gen lambda_MKT . gen lambda_SMB . gen lambda_HML . gen lambda_RMW . gen lambda_CMA . levelsof ym, local(months) foreach m of local months { cap qui reg excess_ret beta_MKT beta_SMB beta_HML beta_RMW beta_CMA if ym m’ !missing(beta_MKT, beta_SMB, beta_HML, beta_RMW, beta_CMA) if _rc 0 e(N) 10 { // 确保有足够样本 replace lambda_MKT _b[beta_MKT] if ym m’ replace lambda_SMB _b[beta_SMB] if ym m’ replace lambda_HML _b[beta_HML] if ym m’ replace lambda_RMW _b[beta_RMW] if ym m’ replace lambda_CMA _b[beta_CMA] if ym m’ } } * 第三步对得到的λ时间序列求均值、标准差并进行t检验 preserve keep ym lambda_* duplicates drop // 每月一个λ值 foreach var of varlist lambda_* { qui sum var’ di “Mean of var’: ” r(mean) di “Std Dev of var’: ” r(sd) qui ttest var’ 0 di “t-statistic for var’: ” r(t) di “p-value for var’: ” r(p) } restore结果解读Fama-MacBeth回归中λ的均值代表因子的平均风险溢价。如果λ_MKT显著为正说明市场风险溢价存在如果λ_SMB显著为正说明小市值股票获得了显著的正向风险补偿以此类推。4.3 结果输出与可视化使用asdoc或estout将关键结果导出到文档。* 将投资组合回归结果导出到Word asdoc esttab port_* using “results/portfolio_regressions.docx”, /// replace b(%9.4f) se(%9.4f) star(* 0.1 ** 0.05 *** 0.01) /// stats(N r2_a, fmt(%9.0g %9.3f)) title(“Table 1: Portfolio Regressions”) * 绘制因子收益率时间序列图 (以SMB为例) use “processed_data/all_factors.dta”, clear tsset ym tsline SMB, title(“SMB Factor Return (2000-2025)”) ytitle(“Return”) xtitle(“Date”) graph export “results/SMB_timeseries.png”, replace5. 常见问题与排查思路在实际操作中你几乎一定会遇到以下问题。这里提供排查指南。问题现象可能原因排查步骤与解决方案merge命令后观测值大量丢失1. 合并键如stkcd,year,month格式不一致。2. 数据存在缺失值或重复值。3. 时间或代码标识不匹配。1. 使用describe和codebook检查合并键的存储类型str, int, float和格式。用tostring或destring统一。2.duplicates report stkcd ym检查重复项并处理。3. 在merge前使用keep if !missing(合并键)。执行merge后用tab _merge查看匹配情况。因子收益率数值异常大如1或异常小1. 收益率数据未正确处理如用了百分比还是小数。2. 缩尾处理失败极端值未被剔除。3. 权重计算错误价值加权时分母为0。1. 确认ret,rf,rm都是小数形式如0.01代表1%。2. 检查winsor2命令是否成功执行用sum ret, detail对比处理前后。3. 在计算port_total_mv后添加replace port_total_mv . if port_total_mv 0避免除零错误。回归结果中因子不显著或符号与理论相反1. 因子构建逻辑错误如排序分组弄反。2. 样本期选择问题特定市场阶段。3. 数据频率或匹配规则不当。4. 模型本身在中国市场不适用。1.仔细核对分组逻辑确保SMB是“S减B”HML是“H减L”。打印几个典型年份的分类结果 (tab size_group bp_group if sort_year2020) 验证。2. 分样本期如牛熊市回归看结果是否稳定。3. 尝试不同的财务数据匹配窗口如t年5月匹配t年财报。4. 这是学术问题可参考相关中国市场的文献。Stata报错 “unknown command xxx”必要的用户命令未安装。使用ssc install xxx或findit xxx查找并安装对应命令。Fama-MacBeth第二步回归样本量不足第一步中很多股票时间序列太短导致β估计缺失。在第一步回归前要求股票至少有24个月或36个月的数据。bysort stkcd: gen nobs _N然后drop if nobs 24。结果无法复现随机种子、排序细节或数据更新导致。1. 在do文件开头使用set seed 12345固定随机数种子如果涉及随机抽样。2. 确保所有排序xtile,egen rank逻辑完全一致。3. 记录使用的数据版本和Stata版本。6. 最佳实践与工程化建议将学术代码转化为稳健、可维护的研究工程需要遵循以下实践模块化与注释如本文所示将不同步骤数据清洗、因子构建、回归分析写入独立的.do文件并通过一个main.do主文件调用。在每个文件开头和关键步骤添加详细注释。版本控制使用Git管理你的Stata代码、数据清洗逻辑和最终结果。results文件夹下的输出文件不应被提交应在.gitignore中忽略。参数化与可配置性将关键参数如数据起止年份、缩尾分位数、排序分组比例放在do文件开头的局部宏中便于修改和实验。local data_start 2000 local data_end 2025 local winsor_cut 1 // 缩尾1% local bp_cut_low 30 // BP低组30% local bp_cut_high 70 // BP高组70% (以上)健壮性检查在关键步骤后加入检查点。* 检查因子收益率的基本统计量 sum MKT SMB HML RMW CMA * 检查是否有缺失值 misstable summarize MKT SMB HML RMW CMA * 检查因子间的相关性 pwcorr MKT SMB HML RMW CMA, star(0.05)数据备份原始数据永远只读。所有处理步骤生成的新数据应另存为新文件。复杂的处理流程可以考虑使用Stata的tempfile来管理中间数据。结果可追溯在输出结果如图表、表格的文件名或内容中包含数据版本、代码版本和运行时间戳。理解经济意义不要只追求统计显著性。如果CMA因子在中国市场显著为负需要思考其背后的经济逻辑高投资增长的公司表现更差并与现有文献对话。掌握Fama-French五因子模型的完整构建与检验流程是进行严谨资产定价实证研究的基石。本文提供的代码框架和思路你可以直接应用于A股或其他市场数据。最重要的不是机械地运行代码而是在每一步都问“为什么”并能够根据自己具体的研究问题调整模型设定例如考虑流动性因子、换手率因子等。建议你尝试用不同的排序变量如用ROE代替ROA、不同的分组方法2x2, 2x3, 3x3来构建因子观察结果的稳健性。这将极大地深化你对多因子模型的理解和应用能力。如果在复现过程中遇到其他具体问题欢迎在评论区交流探讨。