第二章:数据准备与清洗——量化分析的“地基工程”

说实话,很多做量化的人,一上来就急着跑模型、调参数。我见过太多这样的案例了。结果呢?数据没处理好,模型再花哨也是白搭。我个人习惯,花在数据清洗上的时间,至少占整个项目的40%。这一章,咱们就把这个地基打牢。

核心观点: 数据质量决定了策略收益的上限,模型只是逼近这个上限的工具。

2.1 数据源介绍:行情、财务与另类数据

做因子挖掘,首先得知道“料”从哪来。我把它分成三大类:

  • 行情数据: 最基础,也最干净。包括开盘价、收盘价、最高价、最低价、成交量、成交额。频率上,日线最常见,分钟线和Tick级数据用于高频策略。
  • 财务数据: 来自上市公司财报。比如净利润、营收、资产负债率。注意,财务数据有滞后性,而且经常被“粉饰”。
  • 另类数据: 这是近年来的热点。比如新闻舆情、卫星图像、电商销售数据、供应链数据。我曾在项目中用过招聘网站的岗位数量来预测公司扩张速度,效果还不错。

你想想看,这三种数据的时间粒度完全不同。行情数据是秒级甚至毫秒级,财务数据是季度级,另类数据则五花八门。怎么把它们对齐?这就是下一个问题。

2.2 数据对齐:别让时间错位坑了你

数据对齐,说白了就是让不同频率的数据在同一个时间点上“说话”。

常见的对齐方法:

数据类型 对齐方式 注意事项
日频行情 + 日频财务 财务数据使用最新报告期 避免未来函数,必须使用报告发布日之后的数据
高频行情 + 低频财务 财务数据向前填充(forward fill) 填充后要标记“未更新”区间
另类数据 + 行情数据 按时间戳精确匹配 另类数据常有缺失,需做插值或滞后处理

我的经验: 我曾经在回测中忘记处理财务数据的发布日,直接用财报截止日的数据。结果策略表现异常好,后来才发现是“未来函数”在作祟。嗯,这个坑我替你们踩过了。

2.3 去极值:别让异常值带偏了模型

极值,就是那些明显离谱的数据点。比如某只股票一天涨了1000倍,这明显是数据错误。去极值的方法有很多,我常用这两种:

  1. MAD法(中位数绝对偏差): 稳健性比标准差法好。公式是:剔除超出中位数 ± 3 * MAD 的数据点。
  2. 分位数法: 直接剔除上下1%或5%的数据。简单粗暴,但有效。

我个人更推荐MAD法。为什么?因为标准差法对极值本身就很敏感,你用它去识别极值,就像让小偷去抓小偷,不太靠谱。

# Python示例:MAD法去极值
import numpy as np

def mad_detection(data, threshold=3):
    median = np.median(data)
    mad = np.median(np.abs(data - median))
    modified_z_scores = 0.6745 * (data - median) / mad
    return np.abs(modified_z_scores) > threshold

注意: 去极值不是简单地删除数据。对于时间序列,你可以用前后值的均值替换,或者用行业均值填充。我曾经因为直接删除极值,导致时间序列出现断点,后续的滚动计算全错了。

2.4 缺失值处理:填还是不填?这是个问题

缺失值在金融数据里太常见了。停牌、新股上市、数据采集失败,都会导致缺失。处理方式取决于缺失的比例和模式。

  • 缺失比例 < 5%: 可以用前向填充(ffill)或后向填充(bfill)。我个人习惯用ffill,因为金融数据通常有“惯性”。
  • 缺失比例 5%-20%: 可以考虑插值法,比如线性插值或时间加权插值。
  • 缺失比例 > 20%: 建议直接剔除该特征或该股票。强行填充会引入大量噪声。

你可能会问:为什么不直接用均值填充?嗯,金融数据的分布往往不是正态的,均值填充会扭曲分布形态,尤其是对于尾部特征。

2.5 标准化与中性化:让因子站在同一起跑线

标准化,就是把不同量纲的因子拉到同一个尺度。最常用的是Z-score标准化:减去均值,除以标准差。

但光标准化还不够。很多因子会受到市场风格的影响。比如市值因子,小盘股在牛市中表现好,大盘股在熊市中抗跌。如果不做中性化处理,你挖掘出的因子可能只是市场风格的“影子”。

中性化的核心思想: 剔除因子中与已知风险因子(如市值、行业、波动率)相关的部分。做法很简单:用因子值对风险因子做线性回归,取残差作为新的因子值。

# Python示例:市值中性化
import statsmodels.api as sm

def neutralize(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()
    return model.resid  # 残差就是中性化后的因子

关键点: 中性化后的因子,与市值、行业的相关性理论上为0。这样你才能确信,因子的预测能力来自其自身,而不是来自风格暴露。

本章知识体系

下面这张图,是我自己梳理的数据准备流程。你可以把它当作一个检查清单。

数据准备与清洗核心流程 数据源 数据对齐 去极值 缺失值处理 标准化 中性化 干净、对齐、标准化的因子矩阵 注意:实际项目中,这些步骤可能需要迭代多次 前向填充 / 插值 / 剔除 Z-score / Min-Max 市值 / 行业 / 风格

数据准备这一步,看似枯燥,但却是整个因子挖掘流程中最关键的一环。我见过太多人在这上面栽跟头。记住一句话:垃圾进,垃圾出。把数据收拾干净了,后面的模型才能跑得稳。

避坑指南: 我曾经在标准化时忘了处理极端值,结果Z-score算出来,大部分数据都集中在-0.5到0.5之间,少数几个极值却跑到了±10。这样的因子,输入模型后直接导致梯度爆炸。所以,去极值一定要在标准化之前做。

专注资料整理