第三章 行为金融因子构建方法论:数据清洗与预处理、因子构建原则、因子标准化与中性化处理
各位同学,欢迎来到第三章。说实话,这一章是整个课程里最「脏」的活,但也是最重要的基础。我见过太多人,策略逻辑写得天花乱坠,结果一上实盘就崩——为什么?数据没洗干净,因子没做中性化。今天咱们就把这些「地基」打牢。
3.1 数据清洗与预处理:别让垃圾数据毁了你的因子
我刚开始做量化那会儿,犯过一个特别蠢的错误。拿到的日频数据里,某只股票某天突然涨了300%,我以为是重大利好,赶紧写了个因子去捕捉这种「异动」。结果呢?那是除权除息日,数据没做复权处理。嗯,从那以后,我每次拿到数据第一件事就是做清洗。
数据清洗,说白了就是三件事:
- 处理缺失值:停牌、退市、新股上市前几天的数据都是空的。我个人习惯,对于停牌超过20天的股票,直接剔除该时间段的数据。别用插值法去补,金融数据不是连续函数,你补出来的全是噪声。
- 处理异常值:比如涨跌幅超过20%的(A股涨停板才10%),或者成交量突然放大100倍的。这些要么是数据错误,要么是极端事件。我建议用MAD(中位数绝对偏差)法,比3σ更稳健。
- 复权处理:前复权还是后复权?我个人偏好前复权,因为这样历史价格是连续的,计算收益率时不会出现跳空。
核心原则:清洗后的数据,要能通过「常识检验」。比如贵州茅台的日收益率,如果某天出现-15%,那一定是数据有问题——茅台历史上从没跌停过。
3.2 因子构建三大原则:经济直觉、统计显著性、稳健性
很多新手喜欢做一件事:把几百个技术指标全算一遍,然后跑回测,哪个夏普高就用哪个。这叫「数据挖掘」,不叫因子构建。真正的因子,必须满足三个条件。
3.2.1 经济直觉:你的因子得有「故事」
你想想看,为什么这个因子能预测收益?背后一定有行为金融学的解释。比如「换手率异动因子」——当一只股票突然放量,说明散户情绪被点燃了,这时候往往意味着短期见顶。这个逻辑说得通吧?
我在项目中遇到过有人用「月亮星座因子」,说满月时投资者情绪更极端。回测结果居然还不错!但我直接否掉了——因为没法解释经济逻辑。你总不能跟基金经理说「今晚满月,我们减仓吧」?
3.2.2 统计显著性:别被过拟合骗了
统计显著性不是看p值小于0.05就完事了。我见过最离谱的案例:有人用100个因子做多因子模型,其中95个p值都显著。结果一查,他用了10年的数据,样本量太大,任何微小的相关性都会显著。
我的建议是:
- 用Newey-West调整后的t统计量,处理异方差和自相关
- 至少做5年的滚动回测,看显著性是否稳定
- 如果某个因子只在某一年显著,其他年份都不行——那大概率是巧合
一个小技巧:把样本随机分成10份,做10次交叉验证。如果每次因子收益率的符号都一致,那这个因子大概率是靠谱的。
3.2.3 稳健性:换个参数就不行了?那趁早放弃
因子构建里最怕的就是「参数敏感」。比如你算「过去20日动量」,结果换成21日就不行了。这种因子你敢用吗?
我曾经开发过一个「日内反转因子」,参数从5分钟到60分钟都试了一遍,结果只有15分钟的参数有效。后来我仔细一想,这哪是反转因子,分明是高频交易的噪声。最后我放弃了,因为不稳健的东西,实盘就是送钱。
稳健性检验的方法:
- 参数敏感性分析:核心参数±20%范围内,因子表现不能剧烈波动
- 样本外检验:用前80%的数据做训练,后20%做验证
- 不同市场环境:牛市、熊市、震荡市,因子都得有效
3.3 因子标准化与中性化:让因子「公平竞争」
假设你构建了两个因子:一个是「市盈率」,取值范围从5到500;另一个是「换手率」,取值范围从0.1%到10%。如果不做标准化,市盈率这个因子会天然主导模型——因为它的数值大。这不公平。
3.3.1 标准化:把因子拉到同一个量级
常用的方法有三种:
| 方法 | 公式 | 适用场景 |
|---|---|---|
| Z-score | (x - μ) / σ | 因子近似正态分布时 |
| 排序标准化 | rank(x) / N | 因子有极端值时 |
| 分位数映射 | 映射到[0,1]均匀分布 | 需要因子分布均匀时 |
我个人习惯用排序标准化。为什么?因为金融数据很少有正态分布的,Z-score会被极端值拉偏。排序标准化把因子变成0到1之间的均匀分布,稳健得多。
# 排序标准化的Python示例
import pandas as pd
import numpy as np
def rank_standardize(factor_series):
"""
对因子序列做排序标准化
返回0-1之间的值
"""
ranks = factor_series.rank()
return (ranks - 1) / (len(ranks) - 1)
# 使用示例
factor = pd.Series([1.2, 3.4, 0.5, 2.1, 5.6])
standardized = rank_standardize(factor)
print(standardized)
# 输出: [0.25, 0.75, 0.0, 0.5, 1.0]
3.3.2 中性化:剔除「脏东西」的影响
标准化只是第一步。真正让因子「干净」的,是中性化处理。什么意思呢?比如你发现「小市值因子」很赚钱——但小市值股票往往也是高波动的。你到底是赚了小市值的钱,还是赚了高波动的钱?
中性化就是要把这些「脏东西」剔除掉。常见的需要中性化的变量:
- 市值中性化:剔除大小盘风格的影响
- 行业中性化:剔除行业轮动的影响
- 波动率中性化:剔除风险偏好变化的影响
具体做法很简单:用因子值对需要中性化的变量做回归,取残差作为新的因子。
# 市值中性化的Python示例
import statsmodels.api as sm
def market_cap_neutralize(factor, market_cap):
"""
对因子做市值中性化
返回残差作为新因子
"""
X = sm.add_constant(np.log(market_cap)) # 市值取对数
model = sm.OLS(factor, X).fit()
residual = model.resid
return residual
# 使用示例
factor = np.array([1.2, 3.4, 0.5, 2.1, 5.6])
market_cap = np.array([100, 500, 50, 200, 1000]) # 单位:亿
neutralized = market_cap_neutralize(factor, market_cap)
print(neutralized)
注意:中性化不是万能的。如果你把因子对「所有」变量都做中性化,最后得到的残差可能什么信息都没有了。我建议只对3-5个最核心的变量做中性化,比如市值、行业、波动率。
3.4 本章知识体系总览
说了这么多,咱们用一张图把整个流程串起来。下面是我手绘的因子构建流程图,你仔细看看,每一步都环环相扣。
这张图你看懂了吗?从原始数据开始,经过清洗、构建、标准化、中性化,最后得到一个「干净」的因子。每一步都不能跳过,否则后面全是坑。
我的个人经验:因子构建最花时间的不是写代码,而是「想清楚」。我经常花一周时间想一个因子的经济逻辑,然后花一天写代码验证。如果逻辑不通,代码写得再漂亮也没用。
好了,这一章的内容就到这里。记住:数据清洗是良心活,因子构建是技术活,标准化和中性化是细节活。三者缺一不可。下一章我们会讲如何用这些因子构建多因子模型,到时候你就知道今天打的基础有多重要了。