第三章:数据获取与清洗——金融数据源介绍、Pandas基础操作、数据清洗实战

做量化投资,有一句老话叫「垃圾进,垃圾出」。你模型再牛,策略再花哨,数据源出了问题,一切都是白搭。我刚开始做多因子模型那会儿,就吃过这个亏——从某数据商拿到的日频行情,居然有3%的股票代码是错的,回测曲线漂亮得不行,实盘一跑直接崩。从那以后,我养成了一个习惯:花70%的时间在数据清洗上,只留30%给建模。

这一章,咱们就聊聊数据获取与清洗。说白了,就是教你如何把「脏数据」变成「干净数据」,为后续的Alpha信号挖掘打好地基。

3.1 金融数据源介绍

金融数据源五花八门,我按自己的经验把它们分成三类:免费公开的、半商业的、以及专业付费的。

类型 代表数据源 适用场景 我的评价
免费公开 Tushare、AKShare、Yahoo Finance 学习、回测、小规模研究 够用,但数据质量参差不齐
半商业 Wind(万得)、Choice(东方财富) 机构研究、中大型策略 数据较全,但接口限制多
专业付费 Bloomberg、Quandl、FactSet 高频交易、全球市场 贵,但数据质量极高

我个人习惯用Tushare做快速原型验证。为什么呢?因为它免费、文档全、社区活跃。但注意,免费数据有个坑——后复权价格经常出现跳变。我曾经用Tushare的复权数据跑了一个动量因子,结果发现因子收益异常高,一查才发现是复权算法在除权日产生了「伪信号」。嗯,这里要提醒你:永远不要完全信任任何数据源,一定要做交叉验证。

我的小技巧: 同时从两个数据源拉取同一只股票的数据,对比差异。如果差异超过0.1%,就要警惕了。

3.2 Pandas基础操作——你每天都会用的工具

Pandas是量化分析的瑞士军刀。你想想看,从数据读取、清洗、到因子计算,几乎每一步都离不开它。我见过不少新手,一上来就学各种花哨的模型,结果连DataFrame的合并都搞不明白——这不行。

咱们快速过一遍最核心的操作:

3.2.1 数据读取

import pandas as pd
import tushare as ts

# 获取股票日线数据
df = ts.get_k_data('000001.SZ', start='2023-01-01', end='2023-12-31')
print(df.head())

这里有个细节:ts.get_k_data返回的是普通DataFrame,但金融数据通常需要设置时间索引。我建议你养成习惯,一拿到数据就做索引转换:

df['date'] = pd.to_datetime(df['date'])
df.set_index('date', inplace=True)

3.2.2 数据对齐——多因子模型的核心

做多因子模型时,你经常需要把不同频率的数据对齐。比如,日频的收益率和月频的财务数据。Pandas的reindexalign方法就是干这个的。

# 假设我们有日频价格数据和月频的市盈率数据
price_df = pd.DataFrame(...)  # 日频
pe_df = pd.DataFrame(...)     # 月频

# 将月频数据向前填充到日频
pe_daily = pe_df.reindex(price_df.index, method='ffill')

为什么要用ffill(向前填充)?因为财务数据发布有滞后性。比如3月份的财报,可能4月底才公布。如果你用3月底的市盈率去预测4月初的收益,那就犯了未来函数的错误。我早期做因子时,就因为没注意这个对齐问题,回测收益虚高了20%。

避坑指南: 数据对齐时,一定要考虑「信息发布时间」。不要用未来数据去预测过去。我曾经用「后视镜」数据做因子,回测曲线漂亮得像假的一样——后来发现,它确实是假的。

3.3 数据清洗实战——去重、填充、对齐

数据清洗,说白了就是三件事:去重、填充、对齐。咱们一个一个来。

3.3.1 去重

金融数据经常出现重复行。比如,某天交易所数据推送了两次,或者你从不同源拉到了同一时间段的数据。

# 检查重复行
print(df.duplicated().sum())

# 删除重复行
df.drop_duplicates(inplace=True)

但注意,不是所有重复都是错的。比如,某只股票在一天内发生了多次交易,那每一笔交易都是合法的。这时候,你需要根据业务逻辑来判断:是保留最后一条?还是取平均值?我个人的经验是:先按「股票+日期」分组,再决定去重策略

3.3.2 填充缺失值

缺失值在金融数据里太常见了。停牌、节假日、数据源故障,都会导致NaN。常见的填充方法有:

  • 向前填充(ffill):用上一个有效值填充。适用于价格数据,因为停牌期间价格不变。
  • 向后填充(bfill):用下一个有效值填充。适用于某些财务指标,比如「最新公告日期」。
  • 插值法:用前后值的线性插值。适用于连续变化的指标,比如成交量。
  • 填充固定值:比如用0填充缺失的成交量,用行业均值填充缺失的市盈率。
# 向前填充
df['close'].fillna(method='ffill', inplace=True)

# 用行业均值填充市盈率
df['pe'].fillna(df.groupby('industry')['pe'].transform('mean'), inplace=True)

这里有个坑:不要盲目填充。如果某只股票连续停牌超过20天,它的价格数据可能已经失真了。我建议你设置一个阈值:如果缺失比例超过30%,直接删除该股票,而不是强行填充。

3.3.3 对齐——多因子模型的命脉

对齐不仅仅是时间对齐,还包括股票代码对齐频率对齐

举个例子:你有一个因子数据框,包含1000只股票的日频因子值;另一个是收益率数据框,包含500只股票的日频收益率。你需要把它们对齐到同一个股票池和时间轴上。

# 取两个数据框的交集
common_stocks = set(factor_df.columns) & set(return_df.columns)
common_dates = factor_df.index.intersection(return_df.index)

# 对齐
factor_aligned = factor_df.loc[common_dates, common_stocks]
return_aligned = return_df.loc[common_dates, common_stocks]

为什么要取交集?因为如果某只股票在因子数据里有,但在收益率数据里没有,那它就无法参与回测。强行保留只会引入偏差。

核心原则: 数据清洗的每一步,都要问自己三个问题——「这个操作会引入未来信息吗?」、「这个操作会改变数据分布吗?」、「这个操作是可逆的吗?」。如果答案有任何不确定,停下来,想清楚再动手。

3.4 本章知识体系

下面这张图,是我自己总结的数据清洗流程。你可以把它当作一个检查清单,每次做数据清洗时,按这个流程走一遍,基本不会漏掉关键步骤。

数据清洗核心流程 1. 数据获取 多源对比验证 2. 去重 按业务逻辑判断 3. 缺失值填充 ffill/bfill/插值 4. 数据对齐 时间/股票/频率 5. 异常值处理 MAD/分位数截断 6. 质量检查 交叉验证/统计检验 不合格则返回第一步

这张图里,我特意加了一个「循环箭头」。为什么?因为数据清洗很少能一次搞定。你做完质量检查,发现异常值太多,就得回去重新调整填充策略或去重规则。这是一个迭代的过程,别指望一步到位。

好了,这一章的内容就到这里。数据清洗虽然枯燥,但它是整个多因子模型的基石。你花在清洗上的每一分钟,都会在后续的建模和回测中得到回报。


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