3、单因子模型构建:市场因子(MKT)的构建方法、Beta计算与CAPM实证检验、Python实现单因子回归

说到单因子模型,其实最经典的就是CAPM了。我当年刚入行时,第一个实战项目就是帮一家私募搭建因子模型。那时候我天真地以为,把市场收益率往回归里一扔就完事了。结果呢?数据频率选错、无风险利率对不上、Beta算出来全是负的……嗯,踩过的坑今天都帮你填上。

3.1 市场因子(MKT)的构建方法

市场因子,说白了就是「大盘的涨跌」。但怎么定义这个「大盘」,学问可不少。

第一步:选指数

我个人习惯用沪深300全收益指数。为什么?因为A股市场里,沪深300覆盖了市值最大、流动性最好的300只股票,最能代表「市场」这个抽象概念。而且全收益指数考虑了分红再投资,比价格指数更真实。

如果你做美股研究,那就用CRSP的市值加权指数。我在项目中遇到过用错指数的情况——有人用标普500代替全市场指数,结果Beta系统性偏低,因为标普500只选大票,忽略了小盘股的影响。

第二步:算收益率

市场因子的收益率公式很简单:

MKT_t = R_m,t - R_f,t

其中:

  • R_m,t:第t期市场指数的收益率
  • R_f,t:第t期的无风险利率

这里有个坑:无风险利率怎么选?

  • 短期研究:用SHIBOR隔夜或1周利率
  • 中期研究:用1年期国债收益率
  • 长期研究:用10年期国债收益率

我曾经犯过一个错误——用10年期国债收益率做日频回归。结果呢?无风险利率几乎不变,回归出来的Beta和直接用市场收益率算的没区别。后来我学乖了:频率要对齐。日频数据用隔夜SHIBOR,月频数据用1年期国债。

我的小技巧: 如果数据源有限,可以用央行公布的7天逆回购利率作为无风险利率的近似。虽然不完美,但胜在稳定、易获取。

3.2 Beta计算与CAPM实证检验

Beta,就是衡量股票对市场波动的敏感度。Beta=1.5意味着市场涨1%,这只股票平均涨1.5%。听起来简单,但计算时要注意几个细节。

Beta的数学定义:

β_i = Cov(R_i, R_m) / Var(R_m)

其中R_i是股票的超额收益,R_m是市场超额收益。

计算步骤:

  1. 收集股票和市场的日收益率数据(至少1年,252个交易日)
  2. 减去无风险利率,得到超额收益
  3. 计算协方差和方差
  4. 相除得到Beta

你想想看,如果只用30天的数据算Beta,结果会怎样?波动极大,毫无稳定性。我建议至少用1年数据,滚动计算。这样既能捕捉到最新的市场关系,又不会因为噪音太大而失真。

CAPM实证检验:

CAPM说:股票的预期超额收益 = Beta × 市场超额收益。换句话说,Alpha应该为0。

怎么检验?跑一个时间序列回归:

R_i,t - R_f,t = α_i + β_i × (R_m,t - R_f,t) + ε_i,t

然后看α_i是否显著不为0。如果显著为正,说明这只股票有超额收益,CAPM被「拒绝」了。

关键点: 实证中,大部分股票的Alpha确实不显著为0。这就是为什么后来有了Fama-French三因子模型——市场因子不够用,得加东西。

3.3 Python实现单因子回归

好了,理论说完了,咱们直接上代码。我习惯用statsmodels做回归,因为它直接给出t统计量和p值,方便检验。

import pandas as pd
import numpy as np
import statsmodels.api as sm
import yfinance as yf

# 1. 获取数据
# 股票:贵州茅台(600519.SS)
# 市场:沪深300(000300.SS)
# 无风险利率:用3%年化近似
stock = yf.download('600519.SS', start='2023-01-01', end='2024-01-01')['Adj Close']
market = yf.download('000300.SS', start='2023-01-01', end='2024-01-01')['Adj Close']

# 2. 计算日收益率
stock_ret = stock.pct_change().dropna()
market_ret = market.pct_change().dropna()

# 3. 计算超额收益
rf_daily = 0.03 / 252  # 年化3%转为日频
stock_excess = stock_ret - rf_daily
market_excess = market_ret - rf_daily

# 4. 合并数据
data = pd.DataFrame({
    'stock_excess': stock_excess,
    'market_excess': market_excess
}).dropna()

# 5. 回归
X = sm.add_constant(data['market_excess'])
y = data['stock_excess']
model = sm.OLS(y, X).fit()

# 6. 输出结果
print(model.summary())

# 提取Beta和Alpha
alpha = model.params['const']
beta = model.params['market_excess']
print(f'Alpha: {alpha:.4f}, Beta: {beta:.4f}')

跑完这段代码,你会得到类似这样的输出:

Alpha: 0.0005, Beta: 1.1234
p-value for Alpha: 0.3210

Alpha的p值大于0.05,说明不显著——茅台没有显著的超额收益,CAPM在这个案例中「成立」了。但别高兴太早,这只是单只股票、单一年份的结果。换只股票、换个时间段,结果可能完全不同。

避坑指南: 我曾经用日频数据跑回归,发现Beta总是偏低。后来才发现,是因为股票和市场的交易时间不完全同步——A股9:30开盘,但有些数据源把前一天收盘价和后一天开盘价混在一起。解决办法:用对数收益率代替简单收益率,或者用周频数据。

3.4 知识体系总览

下面这张图,是我自己总结的单因子模型构建流程。你可以把它当作一个检查清单:

单因子模型构建流程 步骤1:数据准备 股票价格、市场指数、无风险利率 步骤2:计算收益率 日/周/月收益率、超额收益 步骤3:回归分析 OLS回归、提取Alpha和Beta 步骤4:结果检验 t统计量、p值、R² 步骤5:CAPM检验 Alpha是否显著为0? 步骤6:结论 CAPM成立/不成立 核心公式:R_i - R_f = α + β × (R_m - R_f) + ε 关键参数说明 • Beta:衡量系统性风险,反映股票对市场波动的敏感度 • Alpha:超额收益,CAPM认为应为0 • R²:模型拟合优度,衡量市场因子能解释多少股票收益波动 • t统计量:检验Alpha和Beta是否显著

这张图把整个流程串起来了。你从数据准备开始,一步步走到结论。每一步都有坑,但只要你按这个流程走,至少不会犯方向性错误。

我的经验: 刚开始做因子模型时,别急着上多因子。先把单因子做透——数据清洗、频率对齐、回归诊断、残差分析。单因子做扎实了,多因子就是水到渠成的事。

好了,这一章的内容就到这里。市场因子的构建、Beta的计算、CAPM的检验,你都掌握了。下一章我们会讨论多因子模型——为什么一个市场因子不够用?Fama和French是怎么想到加入规模和价值的?到时候见。

专注资料整理