3. 数据获取与清洗:常见数据源、字段解析与处理技巧

做量化交易,尤其是商品期货,有一句老话:「数据决定下限,策略决定上限」。我个人做了这么多年风控,见过太多策略回测漂亮、实盘却崩盘的案例,十有八九是数据源头就出了问题。

这一章,咱们就聊聊数据获取与清洗那些事儿。说白了,就是怎么把「原材料」搞干净,让后续的策略能放心下锅。

3.1 常见数据源:你该选哪个?

国内做期货量化,主流的数据源就那么几个。我挨个说说我的使用感受。

3.1.1 Wind(万得)

金融圈的标配。数据全、质量高、更新快。但价格也贵,个人用户基本用不起。我建议机构团队直接上Wind,省心。

  • 优点:覆盖全(期货、期权、现货、宏观),历史数据长,API稳定。
  • 缺点:贵,接口是COM组件,Python调用略麻烦。
  • 适用场景:机构实盘、深度研究。

3.1.2 Tushare

国内开源数据里做得最早的。免费版够用,但积分限制多。我早期做回测时常用它,后来数据量大了就转付费了。

  • 优点:免费、社区活跃、文档清晰。
  • 缺点:免费版有调用频率限制,历史数据偶尔有缺失。
  • 适用场景:个人研究、小规模回测。

3.1.3 聚宽(JoinQuant)

量化平台自带数据,本地化做得不错。如果你用聚宽平台写策略,数据直接取就行,不用操心清洗。

  • 优点:与平台无缝集成,数据预处理过,方便。
  • 缺点:脱离平台后数据获取受限,自定义程度低。
  • 适用场景:聚宽平台用户、快速原型验证。
我的建议:个人学习用Tushare或聚宽就够了。实盘交易,尤其是高频或套利策略,建议上Wind或直接对接交易所数据。

3.2 期货数据字段解析

拿到数据后,先别急着跑策略。你得搞清楚每个字段到底代表什么。我见过有人把「结算价」当「收盘价」用,回测结果直接翻车。

以下是商品期货最核心的字段,我列个表:

字段名 含义 注意事项
open 开盘价 注意集合竞价时段,有些数据源会包含夜盘开盘
high 最高价 日内最高,注意涨跌停板时的价格
low 最低价 同上
close 收盘价 15:00的收盘价,夜盘品种注意区分
settle 结算价 关键字段:用于计算盈亏和保证金,不是收盘价!
volume 成交量 注意是单边还是双边统计,国内期货是双边
open_interest 持仓量 反映市场参与度,换月时会有剧烈变化
pre_settle 前结算价 用于计算涨跌停板幅度
避坑指南:我曾经在回测一个跨期套利策略时,直接用收盘价计算价差,结果发现实盘时盈亏对不上。后来排查才发现,期货的保证金和盈亏都是按结算价算的,不是收盘价。记住:回测用结算价,实盘也用结算价

3.3 缺失值与异常值处理

数据拿到手,第一件事就是检查缺失值和异常值。我习惯用pandas的isnull()describe()快速扫一眼。

3.3.1 缺失值处理

期货数据缺失常见原因:节假日、数据源漏采、合约刚上市或临近退市。

  • 直接删除:如果缺失比例很小(<1%),直接dropna()。
  • 向前填充:比如用前一天的结算价填充当天的缺失值。适合非活跃合约。
  • 插值:线性插值或时间加权插值。我一般只在分钟数据上用。
import pandas as pd

# 示例:向前填充缺失的结算价
df['settle'].fillna(method='ffill', inplace=True)

# 或者用前一天的数据填充
df['settle'].fillna(df['pre_settle'], inplace=True)

3.3.2 异常值处理

异常值比缺失值更隐蔽。我常用的方法:

  • 涨跌停板检查:如果价格超过当日涨跌停板,直接标记为异常。
  • 3-sigma原则:计算收益率的标准差,超过3倍标准差的视为异常。
  • 业务逻辑检查:比如成交量不能为负,持仓量不能小于0。
实战经验:我在处理螺纹钢数据时,发现某天成交量突然放大10倍,但价格没怎么动。查了一下,原来是数据源把「双边成交量」和「单边成交量」搞混了。这种异常靠统计方法很难发现,必须结合业务知识。

3.4 复权与连续合约构建

这是期货数据清洗里最头疼的一环。股票有复权,期货有换月。你想想看,一个合约只有几个月生命周期,你要做长周期回测,必须把不同合约连起来。

3.4.1 为什么要构建连续合约?

因为单个合约有到期日。你不能用主力合约直接做回测,否则换月那天会出现价格跳空,策略信号会乱掉。

3.4.2 常见的连续合约构建方法

  • 主力合约连续:每天取持仓量最大的合约。简单,但换月时会有跳空。
  • 加权连续:按持仓量加权多个合约。平滑,但计算复杂。
  • 复权连续:在换月时对历史价格进行调整,消除跳空。我最推荐这种方法。

3.4.3 复权连续合约的构建逻辑

说白了,就是找到每次换月的价差,然后把这个价差累加到历史价格上。这样回测时就不会有跳空。

# 伪代码示例:复权连续合约构建
def build_adjusted_series(df_old, df_new, roll_date):
    # 计算换月当天的价差
    spread = df_new.loc[roll_date, 'close'] - df_old.loc[roll_date, 'close']
    # 将价差累加到旧合约的历史价格上
    df_old['close_adjusted'] = df_old['close'] + spread
    # 拼接
    return pd.concat([df_old, df_new])
我的习惯:我一般用「复权连续合约」做回测,用「主力合约连续」做实盘监控。因为实盘时你只能交易当前主力,但回测需要平滑的价格序列。

3.5 本章知识体系

下面这张图,是我自己总结的数据获取与清洗的完整流程。你可以把它当作一个检查清单。

数据获取与清洗流程 数据源 Wind / Tushare / 聚宽 字段解析 open/high/low/close/settle 缺失值处理 删除 / 填充 / 插值 异常值处理 涨跌停 / 3-sigma / 业务逻辑 连续合约构建 主力连续 / 加权连续 / 复权连续 清洗后的数据

嗯,这张图基本把本章的核心逻辑串起来了。从数据源到最终可用的清洗数据,每一步都不能跳过。我见过太多人直接拿原始数据跑策略,结果换月那天信号乱跳,还以为是策略问题,查了半天才发现是数据没处理干净。

最后提醒一句:数据清洗没有一劳永逸的方法。不同品种、不同频率的数据,处理方式都不一样。比如股指期货和商品期货的换月规则就完全不同。我的建议是:每次拿到新数据,先花20%的时间做清洗和验证,这比花80%的时间调参数划算得多。

专注资料整理