第三节:因子计算与标准化
因子计算完了,原始数据拿到手了。然后呢?
直接扔进模型?别急。我见过太多人,因子算出来就直接往模型里塞,结果跑出来的结果一塌糊涂。为什么?因为原始因子数据里,有异常值、有量纲差异、有分布偏态。说白了,数据本身就不干净,模型怎么可能给你好结果?
3.1 因子计算逻辑:从原始数据到因子值
因子计算,本质上就是特征工程。把原始的交易数据、财务数据,加工成能刻画股票某个维度的指标。
举个例子,动量因子。原始数据是每天的收盘价,因子计算就是:
# 简单动量因子:过去20个交易日的累计收益率
def calc_momentum(close_prices, window=20):
return close_prices / close_prices.shift(window) - 1
看起来简单吧?但实际项目中,坑多着呢。
我踩过的坑:
- 复权问题:用前复权还是后复权?我习惯用后复权计算收益率,因为前复权会改变历史价格,导致因子值失真。
- 停牌处理:停牌期间的价格是无效的。我一般会填充NaN,后续标准化时自动忽略。
- 行业中性化:很多因子在不同行业里分布差异巨大。比如市盈率,银行股和科技股能比吗?不能。所以我会先做行业中性化,再去做标准化。
3.2 去极值:把那些「离谱」的数据拉回来
因子数据里,经常会出现一些极端值。比如某只股票因为乌龙指,一天涨了100倍。这种数据不处理,模型会被它带偏。
去极值的方法,主流的有两种:MAD法和百分位法。
3.2.1 MAD法(绝对中位差法)
MAD法的核心思想是:用中位数代替均值,用绝对中位差代替标准差。为什么?因为中位数对异常值不敏感。
def mad_winsorize(series, n=5):
median = series.median()
mad = (series - median).abs().median()
upper = median + n * mad
lower = median - n * mad
return series.clip(lower, upper)
这里n一般取3到5。我习惯取5,因为A股市场波动大,取3容易把正常值也给截掉。
我曾经犯过的错:有一次做波动率因子,n取了3,结果把很多正常的高波动股票给截断了,导致因子区分度大幅下降。后来改成5,效果就好了很多。
3.2.2 百分位法
百分位法更直观:设定上下限的百分位,超出部分直接截断或替换。
def percentile_winsorize(series, lower=0.01, upper=0.99):
low = series.quantile(lower)
high = series.quantile(upper)
return series.clip(low, high)
上下限一般取1%和99%,或者0.5%和99.5%。
3.3 标准化:让所有因子站在同一起跑线
因子之间的量纲差异太大了。市盈率可能是几十,换手率可能是百分之几,动量可能是0.1。如果不标准化,模型会天然偏向数值大的因子。
标准化的方法,主流有两种:Z-score和秩标准化。
3.3.1 Z-score标准化
Z-score的公式很简单:减去均值,除以标准差。结果就是均值为0,标准差为1的分布。
def zscore_normalize(series):
return (series - series.mean()) / series.std()
但这里有个坑:Z-score假设数据是正态分布。如果数据是偏态的,标准化后的效果并不好。
我的经验:Z-score适合那些分布比较对称的因子,比如市值、波动率。对于偏态严重的因子(比如市盈率),我会先做对数变换,再做Z-score。
3.3.2 秩标准化(Rank Normalization)
秩标准化,说白了就是把因子值转换成排名,然后再映射到标准正态分布上。
from scipy.stats import norm
def rank_normalize(series):
rank = series.rank()
uniform = rank / (len(series) + 1)
return norm.ppf(uniform)
这样做的好处是:完全消除了分布形态的影响。不管原始数据是什么分布,标准化后都是标准正态分布。
我个人更偏爱秩标准化。为什么?因为它在多因子模型里表现更稳定。Z-score对异常值敏感,而秩标准化天然免疫异常值。你想想看,一个极端值在Z-score里可能变成10个标准差,但在秩标准化里,它最多就是排名第一或最后一名。
3.4 本章知识体系
下面这张图,把因子计算与标准化的核心逻辑串起来了:
3.5 实战中的一些细节
讲完了理论,聊聊实战中我的一些习惯:
- 因子计算频率:日频因子每天算一次,月频因子每月算一次。别把频率搞混了,否则因子值会前后不一致。
- 数据对齐:因子计算时,一定要确保数据的时间戳对齐。我遇到过用前一天的收盘价算当天的因子,结果差了整整一天。
- 滚动窗口:很多因子需要滚动窗口计算(比如过去20天的均值)。滚动窗口的起始点要小心,别把未来数据泄露进去。
因子计算与标准化,看似基础,但细节决定成败。把这一步做扎实了,后面的模型才能跑得稳。