第四章:数据获取与清洗——量化分析的“地基工程”

做量化投资,有个道理我越做越觉得深刻:数据质量决定策略上限。你模型再漂亮,因子再新颖,数据是脏的,结果就是垃圾进、垃圾出。这一章,咱们就聊聊数据获取与清洗那些事儿。

核心观点:数据清洗占量化研究60%以上的时间,别嫌烦,这是必修课。

4.1 数据源选择:三大主流平台怎么选?

国内做量化,数据源基本就三个选项:Tushare、Wind、聚宽。我三个都用过,说说我的真实感受。

数据源 优点 缺点 适合场景
Tushare 免费、数据全、社区活跃 有调用频率限制、需积分 个人研究、小规模回测
Wind 数据质量高、接口稳定 贵(年费几万起) 机构、专业团队
聚宽 一站式平台、数据+回测+交易 本地化不够灵活 快速原型、策略验证

我个人习惯是:研究阶段用Tushare,免费且够用;实盘阶段用Wind,数据可靠性高。聚宽呢?适合不想折腾环境的朋友,开箱即用。

小技巧:如果你刚开始学,建议从Tushare入手。注册个账号,拿token就能用。代码示例:

import tushare as ts

# 设置token(去官网注册获取)
ts.set_token('your_token_here')
pro = ts.pro_api()

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

嗯,这里要注意:Tushare的积分制度。新用户只有100分,很多接口需要200分以上。我当时为了攒积分,每天签到、发帖,折腾了半个月才够用。如果你不想等,可以直接买积分,也不贵。

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

数据拿到手,别急着跑模型。先走一遍清洗流程。我总结了一个四步法:

  1. 格式统一——日期格式、列名、数据类型
  2. 缺失值处理——该补的补,该删的删
  3. 异常值处理——识别并处理极端值
  4. 一致性检查——前后逻辑、跨表校验

为什么会这样排序?因为格式不统一,后面所有操作都会报错。我刚开始做的时候,就因为日期格式没统一,卡了整整一个下午。

下面这张图,是我自己画的数据清洗流程,你看一眼就明白了:

数据清洗四步流程图 原始数据 格式统一 缺失值处理 异常值处理 一致性检查 ✅ 可用数据 原始数据格式杂乱 统一日期、列名、类型 填充或删除缺失值 识别并处理极端值 跨表校验、逻辑检查

4.3 缺失值处理:别让“空”坑了你

缺失值,说白了就是数据里有些格子是空的。原因很多:停牌、数据源没更新、节假日等等。怎么处理?三种主流方法:

  • 删除法——直接删掉有缺失的行或列。简单粗暴,但会损失信息。
  • 填充法——用均值、中位数、前向/后向填充。我常用前向填充(ffill),因为金融数据有连续性。
  • 插值法——用线性插值、多项式插值。适合时间序列数据。

我曾经在做一个因子回测时,因为没处理停牌日的缺失值,结果策略夏普比率虚高。后来发现,停牌日的数据全是NaN,回测引擎自动跳过了,相当于“只选好日子交易”。你说这能真实吗?

避坑指南:千万不要对所有缺失值一删了之。先搞清楚缺失原因。如果是停牌,建议用前向填充;如果是数据源问题,建议重新获取。

import pandas as pd
import numpy as np

# 示例数据
df = pd.DataFrame({
    'date': pd.date_range('2023-01-01', periods=10, freq='D'),
    'close': [10, 11, np.nan, 12, 13, np.nan, 14, 15, 16, 17]
})

# 方法1:前向填充(推荐)
df['close_ffill'] = df['close'].ffill()

# 方法2:线性插值
df['close_interp'] = df['close'].interpolate(method='linear')

# 方法3:删除缺失值
df_drop = df.dropna(subset=['close'])

print(df)

4.4 异常值处理:识别“离谱”的数据点

异常值,就是那些明显偏离正常范围的数据。比如某只股票一天涨了1000%,或者成交量突然变成0。这些数据会严重干扰你的模型。

识别异常值,我常用两种方法:

  1. 3σ原则——超出均值±3倍标准差的数据,视为异常。适合正态分布的数据。
  2. IQR方法——超出Q1-1.5*IQR或Q3+1.5*IQR的数据,视为异常。适合偏态分布。

你想想看,金融数据大多不是正态分布,所以IQR方法更实用。我个人的经验是:先用IQR筛一遍,再结合业务逻辑人工判断。

实战技巧:处理异常值,不要直接删除。先标记出来,看看是不是数据错误。如果是数据错误,修正;如果是真实极端事件(比如股灾),保留但做缩尾处理。

def detect_outliers_iqr(df, column):
    """用IQR方法检测异常值"""
    Q1 = df[column].quantile(0.25)
    Q3 = df[column].quantile(0.75)
    IQR = Q3 - Q1
    
    lower_bound = Q1 - 1.5 * IQR
    upper_bound = Q3 + 1.5 * IQR
    
    outliers = df[(df[column] < lower_bound) | (df[column] > upper_bound)]
    return outliers, lower_bound, upper_bound

# 示例
df = pd.DataFrame({'return': [0.01, 0.02, -0.01, 0.03, 0.5, -0.02, 0.015, 0.025]})
outliers, lb, ub = detect_outliers_iqr(df, 'return')
print(f"异常值:{outliers['return'].values}")
print(f"正常范围:{lb:.3f} ~ {ub:.3f}")

嗯,这里要提醒一下:异常值处理没有银弹。同一个数据,在不同策略里可能一个算异常、一个不算。比如高频交易中,0.5%的收益率可能算异常;但在日频策略里,这很正常。所以,一定要结合你的策略逻辑来判断。

总结一句话:数据清洗不是一次性工作,而是贯穿整个量化研究过程的持续任务。每次新增数据、每次修改策略,都要重新检查数据质量。养成这个习惯,你的策略会少踩很多坑。


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