4. 因子数据获取与清洗:数据源选择、缺失值处理、异常值处理、标准化与中性化

各位同学,咱们今天聊点实在的。

因子数据,说白了就是量化模型的「原材料」。原材料不行,再牛的模型也是白搭。我在早期做策略的时候,就吃过这个亏——数据没洗干净,回测曲线漂亮得不行,一上实盘就崩。嗯,从那以后,我对数据清洗这件事,再也不敢马虎。

4.1 数据源选择:你的「米」从哪来?

我个人习惯,把数据源分成三类:

  • 免费公开数据:比如Tushare、AKShare。适合个人研究、快速验证想法。但要注意,免费数据偶尔会有延迟或缺失。
  • 商业金融终端:Wind、Choice、Bloomberg。机构标配,数据质量高,字段全。缺点是贵,一年几十万起步。
  • 自建数据库:从交易所原始数据开始,自己清洗入库。精度最高,但维护成本也最高。

我的建议:初期用免费数据跑通流程,策略稳定后再切换到商业数据。别一上来就砸钱买Wind,万一策略不行,钱就白花了。

你可能会问:「那我该选哪个?」

其实没有标准答案。我见过有人用AKShare跑出年化30%的策略,也见过有人用Wind数据照样亏钱。关键不是数据源多「高级」,而是你是否了解它的局限性

4.2 缺失值处理:那些「消失」的数据

现实中的数据,很少是完整的。停牌、退市、节假日、数据推送失败……都会造成缺失。

我在项目中遇到过最头疼的情况:某只小盘股因为重大资产重组停牌了半年,复牌后直接一字板涨停。如果你简单地把缺失值填充为前一天的收盘价,那你的因子值会完全失真。

常见的处理方法有这几种:

方法 适用场景 注意事项
删除缺失行 缺失比例很小(<5%) 会损失样本量,小心幸存者偏差
前向填充 时间序列数据,如价格 停牌期间用此方法,复牌后要修正
均值/中位数填充 截面数据,如财务指标 会压缩方差,影响因子区分度
行业均值填充 有行业属性的因子 比全局均值更合理,我常用这个
模型预测填充 缺失模式复杂时 计算量大,容易过拟合

我的小技巧:对于财务类因子(如PE、ROE),我习惯用「行业+市值」分组后的中位数填充。这样既保留了行业特性,又考虑了公司规模的影响。效果比单纯用行业均值好不少。

4.3 异常值处理:那些「离谱」的数字

什么叫异常值?举个例子:某只股票的市盈率突然变成10000倍,或者日收益率出现±50%的极端值。这些数据,十有八九是错的。

我曾经在回测中发现一个「神奇」的因子——「前一日收盘价与当日开盘价的差值」。回测夏普比率高达5.0,我当时兴奋得不行。后来一查,原来是某天数据推送错误,把开盘价写成了前收盘价的10倍。嗯,白高兴一场。

处理异常值,我一般分三步走:

  1. 识别:用3σ原则或IQR(四分位距)法。我个人更偏好IQR,因为它不受极端值本身的影响。
  2. 处理:要么截断(winsorize),要么删除。我通常用1%和99%分位数做截断。
  3. 验证:处理完后,看一眼分布图。如果还是「奇形怪状」,说明数据本身可能有问题。

注意:千万不要在不知道数据含义的情况下,机械地删除异常值。比如「涨停板」的股票,收益率就是10%,这不是异常,这是市场规则。我曾经见过有人把涨停数据当异常删了,结果因子信号全乱了。

4.4 标准化:让因子「站在同一起跑线」

不同因子的量纲差异很大。比如市值因子,数值动辄几十亿;而换手率因子,可能只有个位数。如果不做标准化,模型会天然「偏爱」数值大的因子。

常用的标准化方法:

  • Z-score标准化:(x - μ) / σ。假设数据服从正态分布。我最常用这个。
  • Min-Max标准化:(x - min) / (max - min)。把数据映射到[0,1]区间。适合有明确上下界的因子。
  • 排序标准化:把数值转为排名,再映射到[-1,1]或[0,1]。对异常值不敏感,但会丢失数值间的距离信息。

我个人习惯:对于截面因子(比如同一时刻所有股票的PE),用Z-score;对于时间序列因子(比如某只股票过去60天的波动率),用排序标准化。

4.5 中性化:剔除「噪音」干扰

中性化,说白了就是「把因子中你不想要的部分去掉」。最常见的是市值中性化和行业中性化。

举个例子:你发现「小市值因子」能赚钱。但问题是,小市值股票往往也是「高波动」的。你怎么知道赚钱的是「小市值」还是「高波动」?这时候就需要中性化——把市值因子中与波动率相关的部分剔除掉。

具体做法:

  1. 用因子值对市值(或行业虚拟变量)做回归。
  2. 取回归的残差作为「中性化后的因子」。

代码实现很简单:

import statsmodels.api as sm

# 假设 factor 是原始因子,market_cap 是市值
X = sm.add_constant(market_cap)
model = sm.OLS(factor, X).fit()
residual = model.resid  # 这就是中性化后的因子

我的经验:中性化不是「做得越多越好」。过度中性化会抹掉因子的有效信息。我一般只做行业中性化+市值中性化,最多再加一个波动率中性化。再多,因子就「死」了。

4.6 本章知识体系总览

下面这张图,是我自己梳理的因子数据清洗流程。你可以把它当作一个「检查清单」:

因子数据清洗流程 数据源选择 缺失值处理 异常值处理 标准化 中性化 免费/商业/自建 删除/填充/预测 识别/截断/删除 Z-score/Min-Max/排序 行业/市值/波动率 了解局限性 避免幸存者偏差 理解数据含义 保留区分度 不要过度中性化

这张图,你每次做因子清洗的时候都可以拿出来对照一下。我自己就把它贴在工位上,省得漏掉哪一步。

好了,关于因子数据获取与清洗,今天就聊到这儿。记住一句话:数据清洗花的时间,永远值得。你省掉的每一步,都会在实盘的时候加倍还给你。


公众号:蓝海资料掘金营,微信deep3321