3、因子构建与数据清洗:如何从原始数据中构建有效因子,处理缺失值与极端值

好,咱们进入正题。前面聊了因子模型的理论框架,现在该动手了。说实话,因子构建和数据清洗这块,是量化研究里最「脏」的活。我见过太多人,模型理论讲得头头是道,一到处理数据就翻车。嗯,今天咱们就把这块硬骨头啃下来。

3.1 从原始数据到候选因子:第一步怎么走?

原始数据长什么样?说白了就是一张大宽表:股票代码、时间、开盘价、收盘价、成交量、财务指标……一堆字段堆在那。你要做的,是从里面提炼出有预测能力的信息。

我个人习惯把因子分成三类:

  • 价量因子:基于价格和成交量计算,比如动量、反转、波动率
  • 基本面因子:基于财务报表,比如市盈率、市净率、ROE
  • 另类因子:比如舆情、供应链数据、卫星图像(这个门槛高一些)

举个例子,你想构建一个「过去20日动量因子」。代码怎么写?

import pandas as pd
import numpy as np

# 假设df是日度数据,包含'stock_id', 'date', 'close'
def calc_momentum(df, window=20):
    df = df.sort_values(['stock_id', 'date'])
    df['momentum'] = df.groupby('stock_id')['close'].pct_change(window)
    return df

# 调用
df = calc_momentum(df, 20)

你看,核心逻辑就一行:用过去20天的收益率作为因子值。但这里有个坑——未来信息泄露。我在项目中遇到过,有人用当天的收盘价和当天的因子值做回归,结果回测曲线漂亮得不像话,实盘直接崩。为什么?因为因子值里包含了未来信息。所以一定要用shift(1)把数据对齐到前一天。

⚠️ 避坑指南:计算因子时,务必确保因子值在时间上是「可知的」。比如用T日收盘价算因子,这个因子只能用于预测T+1日的收益。我曾经因为没注意这个细节,多花了整整两周排查问题。

3.2 缺失值处理:不是简单删掉就完事

原始数据里缺失值太常见了。停牌、新股上市、财务数据未更新……都会导致缺失。怎么处理?

我的经验是分情况讨论:

缺失类型 推荐处理方式 适用场景
随机缺失 用截面均值/中位数填充 因子值分布较稳定时
连续缺失 向前填充(ffill) 停牌导致的价格缺失
大量缺失(>50%) 直接剔除该因子 因子本身数据质量差
新股/退市 设置最小上市天数门槛 避免次新股噪音

你想想看,如果一只股票停牌了5天,它的价格数据是缺失的。这时候用均值填充合理吗?不合理。因为停牌期间价格理论上不变,应该用停牌前的价格填充。所以向前填充(ffill)才是正解。

# 向前填充缺失值
df['close'] = df.groupby('stock_id')['close'].ffill()

# 如果还是缺失,用截面中位数填充
df['factor'] = df.groupby('date')['factor'].transform(
    lambda x: x.fillna(x.median())
)
💡 小技巧:我习惯先看缺失比例。如果某个因子在某一天的缺失率超过30%,我会直接把这天的因子值全部设为NaN,后面再统一处理。这样可以避免用少量数据填充大量缺失带来的偏差。

3.3 极端值处理:别让一颗老鼠屎坏了一锅粥

极端值,说白了就是那些离谱的数据。比如某只股票一天涨了100倍(别笑,A股历史上真有过),或者市盈率突然变成负数。这些值会严重扭曲你的因子模型。

处理极端值,我常用的方法有三种:

  1. MAD法(中位数绝对偏差):稳健性最好,适合大多数场景
  2. 百分位截断法:简单粗暴,直接砍掉上下1%或5%
  3. Winsorize(缩尾处理):把极端值拉到边界值,不删除样本

我个人最推荐MAD法。为什么?因为它不受极端值本身的影响。你看均值,一个极端值就能把均值拉偏,但中位数不会。

def winsorize_mad(series, n=5):
    """
    基于MAD的缩尾处理
    n: 中位数上下n倍MAD
    """
    median = series.median()
    mad = (series - median).abs().median()
    upper = median + n * mad
    lower = median - n * mad
    return series.clip(lower, upper)

# 应用
df['factor_winsor'] = df.groupby('date')['factor'].transform(winsorize_mad)
🔑 核心要点:极端值处理一定要在截面(同一天)上进行,而不是在整个时间序列上。因为不同时间点的因子分布可能完全不同。比如2015年牛市和2018年熊市,因子值的分布天差地别。

3.4 知识体系总览

说了这么多,咱们用一张图把整个流程串起来。从原始数据到最终可用的因子,中间经历了什么?

因子构建与数据清洗流程 原始数据 价量/基本面/另类 因子计算 动量/反转/估值 缺失值处理 ffill/中位数填充 极端值处理 MAD/缩尾 处理完成后,进入因子检验环节 各环节关键点: • 原始数据:注意数据频率对齐,日度/周度/月度不要混用 • 因子计算:警惕未来信息泄露,用shift(1)对齐时间 • 缺失值处理:先看缺失比例,再选方法,不要无脑删除 • 极端值处理:截面处理,推荐MAD法,稳健性最好 数据清洗占量化研究60%以上的工作量,别嫌麻烦

你看,从原始数据到最终可用的因子,中间要过好几道关卡。每一步处理不当,都会影响最终模型的表现。我刚开始做量化的时候,总觉得数据清洗是浪费时间,恨不得直接拿原始数据跑模型。结果呢?回测看起来不错,实盘一塌糊涂。后来才明白,数据质量决定了模型的上限

📌 总结一下:因子构建不是简单的公式套用,而是一个系统工程。从原始数据到候选因子,再到缺失值和极端值处理,每一步都需要你仔细斟酌。记住三个原则:
1. 时间对齐,避免未来信息
2. 截面处理,保持分布一致性
3. 稳健优先,别让极端值牵着鼻子走

嗯,这一章的内容就到这。数据清洗这块,说白了就是「慢工出细活」。你花在数据上的时间,最终都会在模型表现上体现出来。

专注资料整理