第二章:数据准备与预处理——从原始数据到可用特征

大家好,欢迎来到《Auto Alpha因子挖掘全流程实战》的第二章。

上一章我们聊了因子挖掘的整体框架。今天,咱们来干点实在的——数据准备与预处理。

说实话,我在这个行业摸爬滚打这么多年,见过太多人一上来就急着跑模型、挖因子。结果呢?数据质量不过关,后面全白干。我自己的血泪教训:有一次花了整整两周挖出一个看起来完美的因子,回测夏普比高达3.5,结果发现是分钟线数据里有个字段填充错了。嗯,从那以后,我再也不敢轻视数据预处理这一步了。

今天的内容,说白了就三件事:选对数据源、洗好数据、标准化处理。咱们一个一个来。


一、数据源选择:你的因子质量,取决于数据颗粒度

做量化的人都知道,数据是燃料。但燃料也有等级之分。

1. 日线数据

这是最基础的数据源。包含开盘价、收盘价、最高价、最低价、成交量、成交额等字段。

  • 优点:获取容易,存储成本低,适合做中长周期因子(如动量、反转、估值类)
  • 缺点:信息量有限,日内波动完全丢失
  • 我个人的习惯:做周频或月频调仓的策略,日线足够了。但如果你做高频,日线就是杯水车薪。

2. 分钟线数据

通常指1分钟、5分钟、15分钟、30分钟、60分钟K线。

  • 优点:能捕捉日内交易行为,适合构建高频因子(如成交量分布、价格冲击、微观结构类)
  • 缺点:数据量大,存储和计算成本高,且容易引入噪音
  • 避坑指南:我曾经用5分钟线数据做因子,结果发现回测表现极好,实盘却一塌糊涂。后来排查发现,分钟线数据存在严重的未来函数问题——某些数据源会把当根K线的收盘价提前暴露。所以,选分钟线数据源时,一定要确认数据是否经过前向修正

3. Tick级数据

这是最细颗粒度的数据,记录每一笔成交的详细信息。

  • 优点:信息最丰富,能构建最精细的微观结构因子(如买卖价差、订单簿不平衡、成交笔数等)
  • 缺点:数据量巨大(一天的数据可能几个GB),处理难度高,且对硬件要求极高
  • 我的建议:除非你做的是高频做市或套利策略,否则不建议一开始就上Tick级数据。从分钟线开始,性价比更高。

核心结论:数据颗粒度越高,因子挖掘的潜力越大,但噪音和成本也越高。我的经验是——先做日线,再逐步降级到分钟线,最后才考虑Tick级。别一上来就追求极致颗粒度,容易翻车。


二、数据清洗:去极值与填充缺失值

数据拿到手,第一件事不是跑模型,而是洗数据。你想想看,如果数据里有几个异常值,算出来的因子可能完全失真。

1. 去极值(Winsorization)

极值,说白了就是那些离谱的数据点。比如某只股票某天涨了1000%,或者成交量突然变成平时的100倍。这些数据要么是数据录入错误,要么是极端事件(如除权除息未复权)。

常用的去极值方法有两种:

  • MAD法(中位数绝对偏差):稳健性更好,适合数据分布不对称的情况
  • 百分位法:直接截断上下1%或5%的数据

我个人更推荐MAD法。为什么?因为百分位法会直接砍掉数据,而MAD法只是把极端值拉回到边界值,保留了样本量。

import numpy as np

def mad_winsorize(series, n=5):
    """
    MAD法去极值
    series: pandas Series
    n: 阈值倍数,默认5倍MAD
    """
    median = series.median()
    mad = np.median(np.abs(series - median))
    upper = median + n * mad
    lower = median - n * mad
    return series.clip(lower, upper)

# 示例:对收益率序列去极值
returns = df['pct_chg']
df['pct_chg_clean'] = mad_winsorize(returns, n=5)

小技巧:MAD法的阈值n一般取3-5。我习惯先取5,如果发现还有明显异常,再降到3。但别低于3,否则会把正常数据也砍掉。

2. 填充缺失值

缺失值在金融数据里太常见了。停牌、节假日、数据源缺失……原因五花八门。

填充方法主要有以下几种:

方法 适用场景 注意事项
前向填充(ffill) 停牌数据、非交易时段 停牌超过5天建议直接删除
后向填充(bfill) 数据开头缺失 慎用,容易引入未来信息
线性插值 连续型指标(如价格、成交量) 适合缺失值较少的情况
行业均值填充 财务指标(如PE、PB) 按行业分组填充,避免跨行业偏差

避坑指南:我曾经在填充停牌数据时,直接用前向填充把停牌期间的价格全部填成停牌前最后一天的价格。结果呢?因子计算时把停牌期间的“假价格”当成了真实交易价格,回测表现异常好——因为停牌期间价格不变,因子值也不变,相当于变相引入了未来信息。后来我改成:停牌超过3天的数据,直接删除该时间段

# 前向填充示例
df['close'] = df.groupby('stock_code')['close'].ffill()

# 行业均值填充示例
df['pe'] = df.groupby('industry')['pe'].transform(
    lambda x: x.fillna(x.median())
)

三、数据标准化与中性化处理

数据洗完了,接下来就是让数据“站在同一起跑线上”。

1. 标准化(Standardization)

为什么要标准化?你想想看,有的因子值在0到1之间,有的因子值在-100到100之间。如果不做标准化,模型会天然偏向数值大的因子。

常用的标准化方法:

  • Z-score标准化:(x - mean) / std,适合数据近似正态分布
  • Min-Max标准化:(x - min) / (max - min),将数据映射到[0,1]区间
  • Rank标准化:将数据转换为排名,再映射到[-1,1]或[0,1]

我个人在因子挖掘中,最常用的是Rank标准化。为什么?因为金融数据往往有厚尾分布,Z-score对极端值敏感,而Rank标准化天然对异常值鲁棒。

from scipy.stats import rankdata

def rank_standardize(series):
    """
    Rank标准化:将数据映射到[-1, 1]区间
    """
    ranks = rankdata(series)
    n = len(series)
    return (ranks / (n + 1) - 0.5) * 2

# 示例
df['factor_rank'] = df.groupby('date')['raw_factor'].transform(rank_standardize)

注意:标准化一定要按时间截面做,即每个交易日单独做标准化。如果跨时间做,会把不同时间点的市场状态混在一起,导致因子失效。

2. 中性化处理(Neutralization)

中性化,说白了就是剔除因子中与某些已知风险因子相关的部分。最常见的两种中性化:

  • 市值中性化:剔除因子中与市值相关的部分
  • 行业中性化:剔除因子中与行业相关的部分

为什么要做中性化?举个例子:你发现小市值股票的某个因子表现特别好。但问题是,小市值本身就是一个强因子。如果你不做市值中性化,你无法判断这个因子是独立有效,还是仅仅因为“小市值效应”。

实现方法很简单——线性回归

import statsmodels.api as sm

def neutralize_factor(factor, market_cap, industry_dummies):
    """
    对因子做市值和行业中性化
    factor: 原始因子值
    market_cap: 对数市值
    industry_dummies: 行业哑变量矩阵
    """
    X = pd.concat([market_cap, industry_dummies], axis=1)
    X = sm.add_constant(X)
    model = sm.OLS(factor, X).fit()
    residual = factor - model.predict(X)
    return residual

# 示例:按日期分组做中性化
df['factor_neutral'] = df.groupby('date').apply(
    lambda g: neutralize_factor(
        g['factor_rank'], 
        g['log_market_cap'], 
        pd.get_dummies(g['industry'])
    )
).values

我的经验:中性化不是越多越好。我见过有人把市值、行业、波动率、换手率……十几个变量全做中性化。结果呢?因子被“洗”得干干净净,什么信号都没了。我的建议是:只对已知的、稳定的风险因子做中性化。市值和行业是必做的,其他的看情况。


本章知识体系总览

说了这么多,咱们用一张图来总结一下本章的核心逻辑:

数据准备与预处理核心流程 数据源选择 日线数据 分钟线数据 Tick级数据 数据清洗 去极值(MAD法 / 百分位法) 填充缺失值(ffill / 插值 / 行业均值) 标准化与中性化 标准化(Z-score / Rank标准化) 中性化(市值 / 行业中性化) 可用因子数据

这张图清晰地展示了从原始数据到可用因子数据的完整流程。每一步都环环相扣,缺一不可。


写在最后

数据准备与预处理,听起来枯燥,但它是整个因子挖掘的地基。地基不稳,楼盖得再高也得塌。

我个人做了这么多年量化,最大的感悟就是:花在数据预处理上的时间,永远不会白费。你省掉的每一步,都会在后面的回测和实盘中加倍还给你。

好了,今天的内容就到这里。数据准备好了,下一章咱们就可以正式进入因子挖掘的核心环节了。


专注资料整理