第三节:因子数据获取——数据源与预处理实战

做因子投资,第一步就是搞数据。这步要是没做好,后面再牛的模型也是白搭。我见过太多人花大把时间调参数,结果问题出在数据上——数据没对齐、有缺失、频率不对,全白干。

今天咱们就聊聊,因子数据到底从哪来,拿到手之后怎么处理。说白了,就是让你少踩坑。

3.1 主流数据源:Wind、Tushare、JoinQuant

国内做量化,绕不开这三个数据源。我个人的习惯是:生产环境用Wind,研究阶段用Tushare或JoinQuant。为什么?往下看。

3.1.1 Wind(万得)

Wind是机构标配。数据全、质量高、更新快。但贵,一年几万块。个人用户基本用不起。

  • 优点:覆盖A股、港股、期货、债券、基金,还有宏观经济数据。因子数据可以直接拉,比如PE、PB、ROE这些。
  • 缺点:贵,接口是COM组件,Python调用有点麻烦。我早期用Wind的时候,经常被COM报错搞到崩溃。
  • 适用场景:机构、私募、有预算的个人。
小技巧:Wind的Python接口叫WindPy,安装后记得先启动Wind客户端。我踩过这个坑——没开客户端直接调接口,报错半天才发现。

3.1.2 Tushare

Tushare是个人量化玩家的福音。免费版就能拿到不少数据,付费版也不贵。我刚开始做因子研究时,用的就是Tushare。

  • 优点:免费、接口简单、文档清晰。支持股票、基金、期货、指数等。
  • 缺点:免费版有调用频率限制,数据更新有延迟。因子数据需要自己算,比如你要算动量因子,得自己拿行情数据算。
  • 适用场景:个人研究、小团队、学生。
# Tushare获取股票日线数据示例
import tushare as ts

# 设置token(需要注册)
ts.set_token('your_token_here')
pro = ts.pro_api()

# 获取平安银行2023年日线数据
df = pro.daily(ts_code='000001.SZ', start_date='20230101', end_date='20231231')
print(df.head())

3.1.3 JoinQuant(聚宽)

JoinQuant是量化平台,也提供数据API。它的优势在于:数据已经做了清洗和对齐,直接拿来用就行。

  • 优点:数据质量高、频率转换方便、有因子库可以直接调用。比如你想要的动量因子、价值因子,平台已经算好了。
  • 缺点:本地使用需要付费,免费版只能在平台内跑策略。
  • 适用场景:快速验证策略、不想自己处理数据的用户。
我的建议:如果你刚开始学因子投资,先用Tushare或JoinQuant。等策略跑通了,再考虑上Wind。别一上来就花大钱买数据,万一策略不行呢?

3.2 数据清洗与预处理

数据拿到手,第一件事不是跑模型,而是清洗。你想想看,原始数据里什么妖魔鬼怪都有:缺失值、异常值、重复数据、ST股票……不清洗直接跑,结果就是垃圾进垃圾出。

3.2.1 缺失值处理

因子数据经常有缺失。比如某只股票停牌了,那天的行情数据就是空的。处理方法有三种:

  • 删除:直接删掉缺失的行。简单粗暴,但会损失信息。
  • 填充:用前值、后值、均值填充。我习惯用前值填充,因为因子数据通常是缓慢变化的。
  • 插值:用线性插值或时间插值。适合时间序列数据。
# 缺失值处理示例
import pandas as pd

# 假设df是因子数据,index是日期,columns是股票代码
# 前值填充
df.fillna(method='ffill', inplace=True)

# 或者删除缺失超过20%的股票
df.dropna(thresh=len(df) * 0.8, axis=1, inplace=True)
注意:千万不要用0填充缺失值!因子数据为0和缺失是两码事。我曾经见过有人把缺失的PE填成0,结果回测结果异常好——因为PE=0的股票被当成低估股,实际上那是停牌股。

3.2.2 异常值处理

因子数据里经常有极端值。比如某只股票一天涨了100倍(新股),或者PE突然变成负数。这些异常值会严重影响因子效果。

我常用的方法是MAD(中位数绝对偏差)法:

# MAD法处理异常值
import numpy as np

def winsorize_series(s, n=5):
    median = s.median()
    mad = np.abs(s - median).median()
    upper = median + n * mad
    lower = median - n * mad
    return s.clip(lower, upper)

# 对因子列做缩尾处理
df['factor'] = winsorize_series(df['factor'])

3.2.3 特殊处理:ST股、新股、停牌股

这些股票在因子投资中通常要剔除。为什么?因为它们的交易行为不正常,因子信号不可靠。

  • ST股:涨跌幅限制5%,流动性差,直接剔除。
  • 新股:上市前60天,波动大,因子不稳定。我一般剔除上市不满60天的股票。
  • 停牌股:复牌后容易补涨补跌,因子数据有断层。建议剔除停牌超过5天的股票。

3.3 数据对齐与频率转换

因子数据来自不同数据源,频率可能不一样。比如行情数据是日频,财报数据是季频,宏观数据是月频。怎么对齐?

3.3.1 数据对齐

对齐的核心原则是:用低频率数据填充高频率数据。比如你要做日频因子,那季频的财报数据就要填充到每一天。

具体做法:

  • 以日期为索引,把不同频率的数据合并到一个DataFrame里。
  • 低频率数据用前值填充(ffill),保证未来数据不会泄露。
  • 对齐后检查一下,有没有缺失值。
# 数据对齐示例
# 假设daily_data是日频行情,quarterly_data是季频财报
# 将季频数据填充到日频
quarterly_daily = quarterly_data.reindex(daily_data.index, method='ffill')

# 合并
merged = pd.concat([daily_data, quarterly_daily], axis=1)
避坑指南:我曾经犯过一个错误——用后值填充(bfill)季频数据。结果把下个季度的财报数据用到了本季度,造成了未来函数。回测结果漂亮得不行,实盘一塌糊涂。记住:永远用前值填充

3.3.2 频率转换

有时候你需要把日频数据转成周频或月频。比如做月度调仓的策略,因子数据就要用月频。

转换方法:

  • 日频转周频:取每周最后一个交易日的值。
  • 日频转月频:取每月最后一个交易日的值。
  • 日频转季频:取每季最后一个交易日的值。
# 日频转月频示例
# 取每月最后一个交易日的数据
monthly = df.resample('M').last()

# 或者取每月均值
monthly_mean = df.resample('M').mean()

3.4 知识体系总览

下面这张图,把因子数据获取的整个流程串起来了。你可以把它当成一个检查清单,做数据的时候对照着来。

因子数据获取与预处理流程 数据源 数据清洗与预处理 数据对齐与频率转换 Wind(机构首选) Tushare(个人免费) JoinQuant(平台数据) 缺失值处理(填充/删除) 异常值处理(MAD缩尾) 特殊股票剔除(ST/新股) 多频率数据合并 前值填充(防未来函数) 日频/周频/月频转换 输出:干净、对齐的因子数据 核心原则:数据质量决定因子效果,清洗对齐不可跳过 关键点:前值填充防未来函数 | MAD缩尾处理异常值 | 剔除ST/新股/停牌股

嗯,数据这块就聊到这儿。记住一句话:数据质量决定因子效果。你花80%的时间在数据处理上,一点都不夸张。我见过太多人因子选得不错,结果数据没处理好,回测和实盘差距巨大。

下一节咱们聊聊因子计算的具体方法。不过在那之前,先把数据搞干净——这是基本功,偷懒不得。

专注资料整理