第三章:数据获取与清洗——数据源选择、清洗流程与复权处理

做量化投资,有一句话我特别认同:「垃圾进,垃圾出」。你策略再牛,模型再花哨,数据源选错了,或者数据没洗干净,结果就是白忙活。这一章,咱们就聊聊数据获取与清洗的那些事儿。

核心观点:数据质量决定了策略的天花板。80%的量化时间其实都花在数据清洗上,而不是写策略。

3.1 数据源选择:Wind、Tushare、聚宽,怎么选?

我个人习惯,先看需求再选数据源。别一上来就追求「全」,够用就行。

数据源 特点 适用场景 成本
Wind 机构级,数据全,接口稳定 公募、私募、券商自营 高(年费几万起)
Tushare 开源免费,社区活跃,数据较全 个人研究、小团队回测 低(积分制,部分免费)
聚宽 平台化,自带回测环境 策略开发、快速验证 免费/付费
其他(东方财富、Yahoo Finance等) 免费但质量参差不齐 快速原型、海外数据 免费

我在项目中遇到过一件事:用Tushare拉A股日线数据,结果某天突然发现某只股票的前复权价格是负数。嗯,这就是数据源的问题。后来我加了一层校验逻辑,才避免了这个坑。

我的建议:个人研究用Tushare或聚宽就够了。如果是实盘,至少用Wind。别省那点钱,数据出问题,亏的是真金白银。

3.2 数据清洗流程:从原始数据到可用数据

数据清洗,说白了就是「去伪存真」。我一般按这个流程走:

  1. 格式统一:日期格式、股票代码格式、字段命名统一
  2. 缺失值处理:空值、NaN、停牌数据
  3. 异常值处理:价格突变、成交量异常、财务数据错误
  4. 复权处理:前复权、后复权、不复权
  5. 去重与排序:按时间排序,去除重复行

你想想看,如果第一步格式都没统一,后面所有分析都是错的。我曾经见过一个团队,因为日期格式不统一(有的用YYYY-MM-DD,有的用YYYYMMDD),导致回测结果完全对不上,查了三天才发现问题。

3.3 缺失值处理:别直接drop,先想想

缺失值处理,很多人上来就dropna(),这其实很危险。为什么?因为缺失值可能包含信息。

  • 停牌数据:停牌期间没有交易数据,但复牌后会有补涨/补跌。直接删除会丢失信息。
  • 财务数据:季报发布有延迟,某些字段可能为空。这时候用前向填充(ffill)更合理。
  • 新股上市:上市前几天的数据可能缺失,需要特殊处理。
# 我的常用处理方式
import pandas as pd

# 前向填充:用上一个有效值填充
df['close'].fillna(method='ffill', inplace=True)

# 插值法:适用于连续数据
df['volume'].interpolate(method='linear', inplace=True)

# 标记缺失:保留缺失信息
df['is_missing'] = df['close'].isna().astype(int)

避坑指南:我曾经用ffill填充了连续5天的缺失值,结果发现那5天其实是停牌。复牌后价格跳空,我的策略完全没反应过来。后来我加了一个「连续缺失天数」的阈值,超过3天就标记为停牌,单独处理。

3.4 异常值处理:别一刀切,要分情况

异常值,说白了就是「看起来不对劲」的数据。比如某只股票突然涨了1000%,或者成交量突然放大100倍。这些可能是数据错误,也可能是真实事件(比如借壳上市)。

我一般用两种方法:

  • 3σ原则:超出均值±3倍标准差的数据,视为异常
  • IQR方法:超出Q1-1.5*IQR或Q3+1.5*IQR的数据,视为异常
# 3σ原则处理
def remove_outliers_3sigma(df, col):
    mean = df[col].mean()
    std = df[col].std()
    return df[(df[col] >= mean - 3*std) & (df[col] <= mean + 3*std)]

# IQR方法处理
def remove_outliers_iqr(df, col):
    Q1 = df[col].quantile(0.25)
    Q3 = df[col].quantile(0.75)
    IQR = Q3 - Q1
    return df[(df[col] >= Q1 - 1.5*IQR) & (df[col] <= Q3 + 1.5*IQR)]

我的经验:别直接删除异常值。先标记,然后人工核查。如果是数据错误,修正;如果是真实事件,保留但单独标注。我见过有人把茅台2015年股灾时的暴跌当成异常值删掉了,结果回测结果异常漂亮——但实盘完全不是那么回事。

3.5 复权处理:前复权 vs 后复权

复权处理,是数据清洗里最容易出错的地方。说白了,就是处理分红、送股、配股这些事件对价格的影响。

复权方式 原理 适用场景
前复权 调整历史价格,使当前价格不变 回测、技术分析
后复权 调整当前价格,使历史价格不变 长期趋势分析
不复权 保持原始价格 事件研究、分红套利

我个人习惯用前复权做回测。为什么?因为前复权保证了当前价格是真实的,回测时买入卖出价格更接近实际。但要注意:前复权可能导致历史价格变成负数(比如某只股票分红特别多)。

# 聚宽平台的前复权数据获取
from jqdatasdk import *

# 获取前复权数据
df = get_price('000001.XSHE', start_date='2020-01-01', end_date='2023-12-31', 
               frequency='daily', fields=['close', 'volume'], fq='pre')

# 手动复权计算(以Tushare为例)
import tushare as ts

# 获取复权因子
df_factor = ts.pro_bar(ts_code='000001.SZ', adj='qfq', 
                       start_date='20200101', end_date='20231231')

# 前复权价格 = 原始价格 * 复权因子
df_factor['adj_close'] = df_factor['close'] * df_factor['adj_factor']

避坑指南:我曾经用后复权数据做回测,结果发现策略收益高得离谱。后来一查,后复权价格包含了分红再投资的收益,而我的策略根本没考虑分红。从那以后,我回测只用前复权,分红单独处理。

3.6 数据清洗的完整流程(SVG流程图)

下面这张图,是我自己总结的数据清洗流程。每次做新项目,我都会先过一遍这个流程。

数据清洗完整流程 原始数据获取 格式统一 缺失值处理 异常值处理 复权处理 可用数据 关键检查点 ✓ 日期格式统一 ✓ 股票代码标准化 ✓ 字段命名规范 ✓ 停牌数据标记 ✓ 财务数据对齐 ✓ 3σ / IQR 检测 ✓ 人工核查异常 ✓ 前复权/后复权选择 ✓ 复权因子验证

3.7 总结:数据清洗的「黄金法则」

做了这么多年量化,我总结了几条数据清洗的「黄金法则」:

  • 不要信任任何数据源:即使是Wind,也可能有错误。一定要交叉验证。
  • 保留原始数据:永远不要修改原始数据。清洗后的数据另存一份。
  • 记录每一步操作:清洗流程要可复现。我习惯用Jupyter Notebook记录每一步。
  • 自动化清洗流程:写一个清洗函数,每次新数据直接调用。别手动操作。

最后说一句:数据清洗很枯燥,但它是量化投资的基石。你花在清洗上的每一分钟,都会在回测和实盘中得到回报。别嫌麻烦,认真做。

蓝海数据掘金营,专注资料整理