
简介金融计量学课程中Fama-Macbeth小组第一次汇报的完整演示PDF文档面向学习实证资产定价与面板数据分析的高年级本科生和研究生。内容从CAPM模型推导切入系统讲解Fama-Macbeth回归的模型设定与两阶段估计流程先通过时间序列回归获得个股的因子暴露β再逐期做截面回归估计风险溢价λ并给出了λ及β标准误的计算公式。文档还深入剖析了该方法的核心优势——排除随机扰动截面相关性对标准误的影响、得到异方差稳健估计——同时指出变量误差EIV问题、残差时序相关不一致等缺陷及改进思路。此外文末Stata实证部分总结了β标准误的具体计算与解读要点对模型回归操作有直接参考价值。资源为单个PDF文件压缩包约551KB目前已有1188人学习下载适合作为金融计量课程汇报模板或Fama-Macbeth方法自学入门材料。1. Fama-MacBeth 回归一篇讲透两步法原理、代码实现与汇报前必查的坑第一次做 Fama-MacBeth 小组汇报最容易被问倒的不是“回归怎么跑”而是“为什么非要跑两步”。这个方法是资产定价里检验因子是否被定价的经典工具——第一步用时间序列回归估计个股对因子的暴露beta第二步用横截面回归估计因子溢价risk premium。它的价值在于直接告诉你“承担这个风险市场到底给多少回报”而不是像普通多元回归那样只给一个平均系数。适合做因子研究、写实证论文、或者验证某个多头策略的读者。今天这篇不绕弯子从原理到可复现的代码再到汇报前必须自查的四个坑一次讲完。2. 两步法的本质为什么不能用一把梭的混合回归2.1 从 CAPM 到多因子Fama-MacBeth 要回答的问题资本资产定价模型CAPM说股票的期望收益只跟它对市场组合的敏感度beta有关。但现实中没有人只用一个因子解释收益于是有了多因子模型期望收益等于无风险利率加上每个因子的暴露乘以对应的因子溢价。问题来了——因子溢价是多少这不能直接对面板数据做一次混合 OLS 回归因为那样隐含假设所有个股的截距项相同、残差独立同分布而实际上截面个体效应和时序异方差都很严重。Fama-MacBeth 的核心贡献是把“估计暴露”和“估计溢价”两个任务拆开先用时间序列把每个股票的 beta 估准再用横截面回归把 beta 对收益的定价关系估出来。这样得到的标准误对截面相关性更稳健也更符合经济学直觉。2.2 第一步回归时间序列里估计因子暴露第一步是在每只股票的时间序列上做回归import pandas as pd import numpy as np import statsmodels.api as sm # 假设 returns 是股票收益率宽表行日期列股票代码 # factors 是因子收益率 DataFrame包含市场因子 mkt 和规模因子 smb # 以第一只股票为例 stock returns.iloc[:, 0] X factors[[mkt, smb]].copy() X sm.add_constant(X) # 按完整样本回归汇报时常用滚动版本见后文 ols sm.OLS(stock, X).fit() beta ols.params[[mkt, smb]] resid_std ols.resid.std()这段代码的关键在于回归的因变量是个股的日收益率或月收益率取决于你的频率设定自变量是因子组合的收益率。常数项代表股票在因子模型之外的超额收益alpha。beta 就是个股对每个因子的暴露。如果样本期间内公司的经营结构发生重大变化静态 beta 会失真一个常见修正是用 36 个月滚动窗口估计——每个月只使用过去 36 个月的数据重新回归一次得到当期的 beta。这会导致第二步回归里的 beta 不是观测值而是估计值产生误差项序列相关的问题也正是 Fama-MacBeth 标准误需要调整的原因。实操上我一般先看残差标准差。如果某只股票的 resid_std 是其他股票的数倍说明存在异常波动或数据错误拆股、停牌、极端值。汇报前把残差标准差大于 3 倍中位数的标的单独拉出来核对能省掉很多答辩时的尴尬。2.3 第二步回归横截面里估计因子溢价第二步每个月做一次横截面回归——用第一步估计出的 beta 作自变量当月的个股收益作因变量# 假设 beta_df 是第一步得到的暴露矩阵行股票列因子 # month_returns 是某月的个股收益 Series X_cross sm.add_constant(beta_df) cross sm.OLS(month_returns, X_cross).fit() gammas.append(cross.params) # 收集每个月的回归系数为什么不能把全样本放在一起做一次横截面回归因为不同月份的收益截面可能存在系统性差异——牛市的因子溢价和熊市的因子溢价可能完全相反。逐月回归的好处是每个月得到一个独立的 gamma因子溢价估计值最后把全部月份的 gamma 取平均得到平均溢价同时 gamma 的标准差用来构造 t 统计量。这就等于把“由于某个月的特殊行情导致因子溢价波动”的问题通过月度回归的分布暴露出来——如果只有少数月份因子显著平均下来 t 值可能不显著这本身就是重要结论。2.4 Shanken 标准误beta 是估出来的不是天上掉下来的第二步回归直接对逐月 gamma 取均值再除以标准误得到的 t 值会偏大——因为 beta 本身是第一步估计出来的存在测量误差第一步的估计误差会传导到第二步。Shanken1992修正的核心是给标准误乘上一个膨胀因子用公式写出来是# Shanken 修正系数 # sigma_f 是因子收益率的协方差矩阵 # gamma_est 是第二步回归系数的均值向量 # cov_gamma 是第二步回归系数的协方差矩阵 factor_cov factors.cov() shanken_factor 1 gamma_est.dot(factor_cov).dot(gamma_est) / (T - N - 1) adj_se np.sqrt(np.diag(cov_gamma) * shanken_factor) adj_t gamma_mean / adj_se注意这个修正幅度取决于因子收益的波动率和 gamma 的估值。因子月波动率越大、gamma 越高修正幅度越大。如果你发现某个因子 Shanken 修正前后的 t 值从 2.1 掉到 1.6不要慌这是常见情况——说明你的 beta 估计噪声较大或者因子之间的多重共线性比较强。汇报时主动把修正前和修正后的结果并列展示反而会让听众觉得你对方法有掌控力。3. 数据准备与分组Fama-MacBeth 最容易翻车的隐蔽环节3.1 数据频率与对齐别让收益和因子差一天Fama-MacBeth 对数据对齐极其敏感。最常见的错误是股票的月收益用的是月末收盘价计算而因子收益率用的是月初到月末的整月收益但数据源里因子更新有滞后。这会导致 beta 估计出现严重的“前瞻偏差”——用未来信息解释过去收益。我一般会在合并数据前检查日期索引是否完全一致# 检查对齐 assert returns.index.equals(factors.index), 日期索引不一致 # 或者用 merge 后检查 NaN merged returns.join(factors, howleft) nan_ratio merged.isna().mean().mean()对齐检查通过后再看 NaN 比例。如果某只股票因停牌导致月收益缺失不要直接删除整只股票——Fama-MacBeth 允许不平衡面板只需要在使用该股票参与回归的那个月跳过即可。但要注意如果某只股票数据缺失超过一半它的 beta 估计会失真参与第二步回归时可能引入噪声。我常用的过滤标准是保留至少 24 个月观测值的个股且最近 6 个月不能有连续缺失。这个阈值没有绝对标准你可以汇报中说明自己用的规则但一定要避免“全样本随意丢弃”。3.2 分组做组合 vs 个股横截面两种经典口径的取舍原始 Fama-MacBeth 论文用的是“先分组构建组合再做组合横截面回归”的方式目的是分散个股特质波动让 beta 估计更稳定。但实际做报告时很多同学直接对个股做第二步回归——这样上报的 gamma 的 t 值往往偏高因为个股层面的特质波动把标准误压低这是一个经典陷阱标准误小不一定是好事可能只是样本被过度分散。两条路线的选择没有对错取决于你的目的是检验某个因子是否定价用组合更稳健还是预测个股收益用个股更贴近市场。我的习惯是先用个股做全样本回归得到直觉再用 Fama-French 的 10x10 分组做稳健性检验。分组代码不复杂关键是分位数切分后的收益等权重加权# 按因子暴露分 5 组 bins pd.qcut(beta_df[mkt], 5, labelsFalse) portfolio_returns {} for group in range(5): selected returns.columns[bins group] portfolio_returns[group] returns[selected].mean(axis1) portfolio_df pd.DataFrame(portfolio_returns)分组口径下第一步回归的 beta 是组合的 beta组合收益对因子时间序列回归而不是组内个股 beta 的平均值。这是很多人搞混的地方——组合 beta 必须是重新回归得到的因为组合的收益特征不同于个股的简单平均。3.3 因子构造的口径统一溢价因子是零投资组合还是原始指标因子收益率必须是“可投资组合的收益率差”比如市值因子的 SMB小市值减大市值而不是原始的账面市值比数值。如果直接把公司的 book-to-market 值当作自变量放进横截面回归得到的是“每单位 B/P 对应的溢价”这跟 Fama-MacBeth 框架下的因子溢价含义完全不同。汇报时如果中途换了因子的定义方式前后结果不具备可比性——这是组内分工时最常见的矛盾来源。建议在第一次小组会议就定死因子的构造方法并写进代码注释里。数据构造完成后画一条因子累计净值曲线肉眼确认没有明显的趋势异常再做两步回归。4. 完整复现从数据到汇报图的 Fama-MacBeth 流水线4.1 项目目录与数据准备脚本实操时我习惯按“数据输入 → 因子构造 → 第一步回归 → 第二步回归 → 输出图表”拆成五个模块。目录结构如下project/ ├── data/raw/ # 原始行情数据 ├── data/processed/ # 清洗后的收益和因子序列 ├── src/ │ ├── make_factors.py │ ├── run_timeseries.py │ ├── run_crosssection.py │ └── output_tables.py └── output/ ├── tables/ └── figures/这样分组合作时每个人只负责一个模块最终由一个人统一跑全流程。第一次汇报时只需要提交 run_timeseries.py 和 run_crosssection.py 的输出不需要把全部数据放进附录但代码和随机种子必须固定。4.2 第一步回归的完整脚本含滚动窗口与参数说明def estimate_betas(returns, factors, window36, min_obs24): betas {} for stock in returns.columns: sr returns[stock].dropna() if len(sr) min_obs: continue # 滚动窗口每个时点只用过去 window 个月 for t in range(window, len(sr)): y sr.iloc[t - window:t] X factors.loc[y.index] X sm.add_constant(X) model sm.OLS(y, X).fit() betas.setdefault(stock, []).append({ date: sr.index[t], beta_mkt: model.params[mkt], beta_smb: model.params[smb] }) return pd.DataFrame(betas).T这个函数有两个关键参数。window 设为 36 个月是经典选择——太长会平滑掉公司基本面的变化太短则 beta 估计噪声大。min_obs 控制最小样本量防止早期上市或数据不全的股票产出不可靠的 beta。这里需要注意滚动窗口会导致前 36 个月没有 beta 估计因此第二步回归的起始时间会整体后移——如果样本只有 5 年实际有效区间只有 2 年样本量过低。一个常用的替代方案是“扩展窗口”expanding window从 24 个月开始逐步递增能多保留一些样本但 beta 的可变性会降低。两种方案的结果差异本身就可以作为稳健性检验的一项。4.3 第二步回归的完整脚本与 t 统计量汇总def run_fm_crosssection(beta_df, returns, factors): gammas [] dates [] for date in returns.index: # 取该月有效的 beta 截面 betas_at_date beta_df.apply(lambda row: row[beta_mkt] if row[date] date else np.nan, axis1) # 实际中 beta_df 结构不同这里简化为按日期切片 X_cross sm.add_constant(beta_df.loc[date].T) y_cross returns.loc[date] valid y_cross.notna() X_cross.notna().all(axis1) if valid.sum() 10: # 至少 10 只股票 continue model sm.OLS(y_cross[valid], X_cross[valid]).fit() gammas.append(model.params) dates.append(date) gamma_df pd.DataFrame(gammas, indexdates) mean_gamma gamma_df.mean() se_gamma gamma_df.std() / np.sqrt(len(gamma_df)) t_stats mean_gamma / se_gamma # 再加上 Shanken 修正略见 2.4 节 return mean_gamma, se_gamma, t_stats逐月回归的样本量差异很大——早期市场股票数量少回归的精度低gamma 的波动会大。如果发现 gamma 序列存在明显的季节性比如每年 1 月特别大这是学界早已发现的一月效应不影响方法本身但汇报时主动提一句会让别人觉得你有市场感觉。另外如果某个月的横截面回归用了不到 10 只股票直接剔除该月这个阈值写在汇报里作为数据筛选规则。4.4 汇报图的标配累计 gamma、分年 t 值和因子收益曲线听众最关心的三张图第一张是每个因子逐月 gamma 的累计值看方向是否稳定第二张是分年度的 t 值热力图看显著性是否来自某一年第三张是因子组合的累计收益曲线直观展示这个因子在市场里到底赚不赚钱。画图时注意——gamma 累计图的纵轴单位是“百分比月收益”不是“对数收益”如果画成对数收益听众会误解数值大小。用 matplotlib 加一行自动保存图片到 output/figures 即可。5. 汇报前必查避坑Fama-MacBeth 的五个经典翻车现场5.1 完整个股的混合 OLS 回归冒充 Fama-MacBeth现象直接对整个面板跑一次固定效应回归系数和 Fama-MacBeth 差不多于是有人觉得可以偷懒。原因混合回归的系数是“平均处理效应”而 Fama-MacBeth 的系数是“逐月横截面回归系数的时序平均”——两者的标准误结构完全不同混合回归会显著低估标准误得出虚假的显著结论。解决一旦要下“因子被定价”这个结论必须以逐月回归的 t 值为准混合回归只能当作初步探索。5.2 beta 用全样本估计导致第二步回归用未来信息现象第一步回归用了 2005-2020 全部数据估计 beta第二步回归却从 2015 年开始做横截面回归。原因这样的 beta 包含了未来 5 年的信息违反了“时点 t 只能用 t 之前的信息”的因果逻辑beta 的测量误差会被系统性低估gamma 的 t 值偏大。解决第一步必须用滚动或扩展窗口保证任何时点的 beta 仅依赖该时点之前的数据。这是答辩时最容易暴露的方法论漏洞。5.3 不同股票的 beta 横截面相关性被忽略现象t 统计量算出来 3.5以为稳了但换一组股票就掉到 1.8。原因Fama-MacBeth 假设逐月 gamma 独立同分布但跨股票截面的相关性没有被显式建模当某些股票同属一个行业或高度关联时beta 的横截面相关性会让负相关股票产生共线性。解决在第二步回归中加入行业哑变量作为控制但注意这会改变 gamma 的含义或者做 Newey-West 标准误调整以修正 gamma 序列的自相关。Shanken 修正能处理第一步误差但不能处理截面相关这是一个经常被混为一谈的细节。5.4 因子收益率与股票收益率的滞后期不匹配现象因子数据来自月度初股票收益用月末且没有做日期对齐导致同一个月的收益和因子之间发生了错位。原因数据源不同日期格式不一致合并时直接按位置拼接。解决统一使用月末交易日作为索引先用 merge 检查 NaN再画一张交叉相关图因子 t 期收益与股票 t1 期收益来验证没有明显“未来信息”泄漏。这一步可视化在汇报时也很有说服力。5.5 分组数太多导致组合 beta 估计噪声爆炸现象把个股分成 50 组每组只有十几只股票组合收益被少数个股主导beta 估计极不稳定第二步回归的 gamma 也大幅波动。原因分组不是越多越好组合的分散效应要求每组至少有几十只股票。解决经典 Fama-French 用 10x10 分组已算密集建议起步用 5x5共 25 组等样本量大了再增加分组数。如果样本总共只有 300 只股票25 组已经是上限。这个原则在汇报时作为参数选择依据写进脚注比“参考主流文献”更有说服力。6. 汇报加分技巧从“跑完结果”到“讲出故事”的最后一公里6.1 把 gamma 序列画成时间轴回答“这个因子什么时候有效”逐月 gamma 的时序图能直接揭示因子的有效期。比如市场因子 gamma 在危机期间显著变大在牛市后期转负——这种时变性比一个平均 t 值更有信息量。汇报时不要只给一个平均 gamma 就结束至少展示一张累计 gamma 曲线标出几个关键经济事件点政策调整、利率变动等说明因子溢价的均值回归特征。这一张图能区分“真正理解方法的人”和“只会跑回归的人”。我一般会在图注里写明“阴影区域为模型设定的样本扩展期”而不是随便标一下。6.2 用因子收益分解解释 gamma 的经济含义gamma 的经济含义不仅取决于统计显著性。假设规模因子的 gamma 平均为每月 0.3%看起来不大但换算成年度就是 3.6%——这到底是什么量级对比同期市场因子的年化溢价如果只有 5%规模因子就解释了其中很大一部分。推荐的做法是做一个“截面收益分解”表选两只典型股票分别列出来自市场因子和规模因子的收益贡献。这样你能很具体地说“这只股票在样本期内的超额收益有 60% 来自市场风险暴露30% 来自规模效应剩下 10% 是 alpha”。这种拆解让听众的注意力从统计检验转向经济解释。6.3 稳健性检验的标配组合时间区间、分组数量、因子替代指标三个维度各做一次变体写在附录里。比如市场因子用等权平均和市值加权各做一轮看 gamma 的方向和显著性是否改变。如果某个因子的显著性完全依赖市值加权方式这说明结论不稳健——你在汇报时主动展示这种不稳健比答辩被追问到时再承认要好得多。记住一个惯例Fama-MacBeth 的结论不是“这个因子有效”而是“在什么条件下、什么样本内、什么因子定义下这个因子的溢价显著”。最后提一个我自己的坚持Fama-MacBeth 跑完先用 Python 把全部中间结果 dump 成 CSV 存档再决定画哪些图。因为汇报前一到两天总是会发现需要重新切样本、调整分组或者临时加一个控制变量。没有一个稳定的中间结果缓存每次改动都要从头跑全流程既容易出错又浪费时间。这也算是我多次汇报前熬夜改代码换来的血泪经验。希望帮到你。本文还有配套的精品资源点击获取