4. 单因子分析基础:因子定义、因子计算、因子暴露、因子收益率
做量化投资的朋友,几乎每天都要跟因子打交道。但说实话,很多人对因子的理解还停留在「拿个指标排序分组」的阶段。我刚开始做回测时也踩过这个坑——因子定义不清晰,计算逻辑有漏洞,最后出来的结果自己都不敢信。
这一章,咱们就把单因子分析的四个核心环节掰开揉碎讲清楚。你想想看,如果连因子是什么、怎么算、暴露度怎么衡量、收益率怎么归因都搞不明白,后面的多因子模型、组合优化就更别谈了。
4.1 因子定义:先把概念说清楚
因子,说白了就是能解释股票收益率差异的某种特征。比如市盈率、市净率、动量、波动率等等。但这里有个关键问题:同一个因子,不同人定义可能完全不同。
举个例子,「低波动因子」。有人用过去60日收益率的标准差,有人用过去252日的日收益率波动率,还有人用贝塔值来近似。我在项目中遇到过,同一个因子用不同口径算,IC值能差出一倍多。
所以,因子定义必须包含三个要素:
- 因子名称:清晰、无歧义,比如「BP因子」而不是「估值因子」
- 计算公式:明确每个变量的来源和计算方式
- 处理规则:缺失值怎么处理、极端值是否截尾、是否中性化
4.2 因子计算:从原始数据到因子值
因子计算是体力活,也是技术活。我见过太多人在这步翻车——数据没对齐、复权没处理好、行业分类用了不同版本……
标准的因子计算流程大致如下:
- 数据获取:行情数据、财务数据、另类数据
- 数据清洗:去重、对齐、处理缺失值
- 因子原始值计算:按公式算出每个股票每个时点的因子值
- 因子预处理:去极值、标准化、中性化
- 因子值存储:以面板数据格式存储,方便后续分析
这里我特别想强调一点:因子计算的时间点一定要跟交易时间对齐。比如你用财报数据算因子,必须考虑财报发布日期,而不是财报截止日。我曾经因为这个细节,回测结果跟实盘差了十万八千里。
# 一个简单的因子计算示例:BP因子(市净率倒数)
import pandas as pd
import numpy as np
def calc_bp_factor(market_cap, book_value):
"""
计算BP因子
market_cap: 总市值(万元)
book_value: 净资产(万元)
"""
# 避免除零
bp = np.where(market_cap > 0, book_value / market_cap, np.nan)
return bp
# 去极值处理(MAD方法)
def winsorize_mad(factor, n=5):
median = np.nanmedian(factor)
mad = np.nanmedian(np.abs(factor - median))
upper = median + n * mad
lower = median - n * mad
return np.clip(factor, lower, upper)
# 标准化
def standardize(factor):
return (factor - np.nanmean(factor)) / np.nanstd(factor)
4.3 因子暴露:你的组合到底暴露了多少因子?
因子暴露,简单说就是「某个股票在某个因子上的得分」。比如某只股票的BP因子暴露是0.8,意味着它的估值水平比市场平均低20%(标准化后的含义)。
但这里有个容易混淆的点:因子暴露可以是原始值,也可以是标准化后的值。我个人习惯用标准化后的Z-score,因为这样不同因子之间可以直接比较。
因子暴露的计算其实很简单:
# 计算因子暴露(标准化后的值)
def calc_factor_exposure(factor_raw):
# 去极值
factor_clean = winsorize_mad(factor_raw)
# 标准化
factor_exposure = standardize(factor_clean)
return factor_exposure
嗯,这里要注意:因子暴露不是一成不变的。随着时间推移,股票的因子暴露会发生变化。所以我们在做回测时,通常每个月(或每季度)重新计算一次因子暴露。
4.4 因子收益率:到底赚了多少钱?
因子收益率,是衡量因子策略赚钱能力的核心指标。计算方式有很多种,最常用的是分组收益法和回归法。
分组收益法:把股票按因子暴露从高到低分成N组(通常是5组或10组),然后计算每组下一期的收益率。最高组减最低组的收益差,就是因子收益率。
回归法:用截面回归,把股票收益率对因子暴露做回归,回归系数就是因子收益率。这种方法更精确,但计算量也更大。
# 分组收益法计算因子收益率
def calc_factor_return(factor_exposure, stock_returns, n_groups=5):
"""
factor_exposure: 当期因子暴露
stock_returns: 下期股票收益率
"""
# 按因子暴露分组
groups = pd.qcut(factor_exposure, n_groups, labels=False)
# 计算每组平均收益率
group_returns = []
for i in range(n_groups):
mask = (groups == i)
group_returns.append(stock_returns[mask].mean())
# 因子收益率 = 最高组 - 最低组
factor_return = group_returns[-1] - group_returns[0]
return factor_return, group_returns
4.5 知识体系总览
说了这么多,咱们用一张图把单因子分析的逻辑串起来:
4.6 几个容易踩的坑
最后,分享几个我亲身踩过的坑,希望能帮你省点时间:
- 幸存者偏差:回测时只用了现在还存活的股票,退市的股票没包含。结果因子收益率虚高。解决办法是用全样本,包括已退市的。
- 前视偏差:用到了未来数据。比如用季报数据时,没考虑财报发布日期。我建议所有因子计算都做严格的「时间戳对齐」。
- 多重比较偏差:同时测试几百个因子,总有几个看起来显著。这其实是统计上的假阳性。解决方法是用Bonferroni校正,或者做样本外测试。
好了,单因子分析的基础框架就讲到这里。记住这四个环节:定义、计算、暴露、收益率。每一步都扎实了,后面的多因子模型才能站得住脚。