4、期货数据获取:数据是量化交易的血液

做量化交易,尤其是商品CTA趋势跟踪,有一句话我特别认同:「数据质量决定了策略的天花板」。你策略再牛,如果数据是脏的、缺的、错的,那结果就是垃圾进垃圾出。我自己早年就吃过这个亏,回测曲线漂亮得不行,实盘一跑直接崩——后来发现是数据源有个字段搞错了。

所以这一章,咱们就踏踏实实地把数据获取这件事讲透。我会带你用两个主流工具——TushareAkshare,把期货数据拿到手,然后做清洗、预处理,最后存成CSV或HDF5格式。嗯,都是实战中反复验证过的套路。

核心要点:数据获取不是简单的「调个API就完事」。你需要考虑:数据源是否稳定?字段是否完整?时间戳是否对齐?复权怎么处理?这些坑,我一个一个帮你填上。

4.1 使用Tushare获取期货数据

Tushare是国内比较老牌的金融数据接口,数据质量高,但需要注册获取token。我个人习惯用它来做主力合约的日线数据获取,因为它的复权处理做得比较规范。

先安装库:

pip install tushare

然后注册获取token(免费版够用,但会有频率限制)。我建议你申请一个pro版,虽然要积分,但数据稳定性好很多。

下面是一个获取螺纹钢主力合约日线数据的例子:

import tushare as ts

# 设置token
ts.set_token('你的token')
pro = ts.pro_api()

# 获取螺纹钢主力连续合约
df = pro.fut_daily(
    ts_code='RB9999.XSGE',  # 9999代表主力连续
    start_date='20200101',
    end_date='20231231',
    fields='trade_date,open,high,low,close,vol,amount'
)

print(df.head())

这里有个细节:RB9999.XSGE 中的 9999 表示主力连续合约,它会自动进行换月处理。但要注意,Tushare的换月规则是「持仓量最大」,这和我们实际交易中「流动性最好」的合约有时会有偏差。我在做回测时,通常会再手动校验一下换月点。

我的小技巧:获取数据后,先检查一下 trade_date 字段是否连续。如果某天缺失,可能是节假日,也可能是数据源漏了。我会用 df['trade_date'].diff().dt.days 快速定位间隔超过1天的地方。

4.2 使用Akshare获取期货数据

Akshare是另一个我非常喜欢的库,开源免费,数据源来自新浪、东方财富等。它的优点是接口丰富,而且不需要token,上手快。缺点是数据偶尔会有延迟或格式变化,需要自己多留个心眼。

安装:

pip install akshare

获取期货实时行情或历史数据都很方便。比如拿沪铜的日线数据:

import akshare as ak

# 获取沪铜主力合约日线
df = ak.futures_main_sina(
    symbol='CU0',  # 0代表主力
    start_date='20200101',
    end_date='20231231'
)

print(df.head())

注意Akshare的字段命名和Tushare不太一样,比如日期字段可能是 date 而不是 trade_date。我建议你统一做一个字段映射,后面处理起来会省很多事。

我曾经遇到过一个坑:Akshare在获取某些品种的分钟线时,时间戳是东八区,但有些接口返回的是UTC时间。如果你不转换,回测时K线时间就对不上了。所以拿到数据后,第一件事就是检查时间字段的时区。

注意:Akshare的接口有时会因网站改版而失效。我建议你定期(比如每月)跑一次测试脚本,确保所有接口都还能正常返回数据。别等到要跑回测了才发现数据拉不下来。

4.3 数据清洗与预处理

数据拿到手,别急着用。清洗这一步,我花的时间往往比写策略还多。你想想看,如果数据里有空值、异常值、或者复权没处理好,策略再漂亮也是白搭。

常见的清洗步骤:

  • 缺失值处理:期货数据偶尔会有某天缺失(比如交易所故障)。我一般用前向填充(ffill)或直接删除。如果连续缺失超过3天,我会标记出来,回测时跳过这段。
  • 异常值检测:比如某天收盘价突然涨了20%,明显不合理。我会用滚动窗口的均值±3倍标准差来识别,然后手动核实。
  • 复权处理:期货的复权比股票复杂,因为涉及换月。我习惯用「后复权」方式,把历史价格调整到当前合约的价位。Tushare的 fut_daily 接口已经做了复权,但Akshare需要自己处理。
  • 时间对齐:不同品种的交易时间不同(比如夜盘品种),如果你要做多品种组合,必须把时间戳对齐到同一个基准。我一般用 pandas.resample 按日线重采样。

下面是一个清洗的代码示例:

import pandas as pd
import numpy as np

def clean_futures_data(df):
    # 1. 删除全空的行
    df = df.dropna(how='all')
    
    # 2. 填充缺失值(前向填充,最多填充2天)
    df = df.fillna(method='ffill', limit=2)
    
    # 3. 异常值检测(以收盘价为例)
    mean = df['close'].rolling(20).mean()
    std = df['close'].rolling(20).std()
    df['is_outlier'] = (np.abs(df['close'] - mean) > 3 * std)
    
    # 4. 删除异常值(或者标记,看你的策略需求)
    df = df[~df['is_outlier']]
    
    # 5. 确保日期排序
    df = df.sort_values('trade_date')
    
    return df

# 使用
df_clean = clean_futures_data(df)

避坑指南:我曾经在清洗时犯过一个低级错误——把缺失值全部删除了,结果导致时间序列不连续,策略在回测时「跳过了」很多交易机会。后来我改成「先填充,再标记」,效果好了很多。记住:删除数据要谨慎,填充数据要合理

4.4 数据存储:CSV vs HDF5

数据清洗完了,得存起来。我一般分两种情况:

  • CSV:适合小规模数据(比如单个品种的日线),方便查看和分享。但读取速度慢,而且不支持多表。
  • HDF5:适合大规模数据(比如多品种的分钟线),读取速度快,支持压缩,还能按索引快速查询。我自己的策略回测系统,底层存储就是HDF5。

存成CSV很简单:

df_clean.to_csv('RB9999_daily.csv', index=False)

存成HDF5稍微复杂一点,但性能好很多:

# 存储
df_clean.to_hdf(
    'futures_data.h5',
    key='RB9999',
    mode='a',  # 追加模式,可以存多个品种
    complevel=9,  # 压缩级别,0-9
    complib='blosc'  # 压缩算法,推荐blosc
)

# 读取
df_read = pd.read_hdf('futures_data.h5', key='RB9999')

我个人建议:日常分析用CSV,生产环境用HDF5。如果你做的是多品种、多周期的回测,HDF5几乎是必须的。我见过有人用CSV存了100个品种的分钟线,回测一次要加载好几分钟,换成HDF5后,加载时间缩短到几秒。

存储策略总结:

存储格式 适用场景 优点 缺点
CSV 单品种、小数据量、临时分析 可读性强、通用性好 读取慢、不支持多表
HDF5 多品种、大数据量、生产环境 读取快、支持压缩、支持多表 可读性差、依赖库

4.5 本章知识体系总览

下面这张图,是我自己整理的数据获取与处理流程。你可以把它当作一个检查清单,每次做数据准备时对照着走一遍,基本不会漏掉什么。

期货数据获取与处理流程 数据源 数据源 Tushare(需token) Akshare(免费开源) 数据清洗与预处理 缺失值处理 → 异常值检测 → 复权处理 → 时间对齐 CSV存储 HDF5存储 小数据量、临时分析 大数据量、生产环境 可用于回测的干净数据

这张图把整个流程串起来了:从两个数据源出发,经过清洗预处理,最后落到两种存储格式。你每次做数据准备时,都可以对照着这个流程走一遍,确保没有遗漏。

最后说一句:数据获取和处理这件事,看起来枯燥,但它是整个量化交易体系的基石。我见过太多人在这上面偷懒,结果后面花了几倍的时间来排查问题。所以,慢就是快,把这一步做扎实了,后面的策略开发才会顺风顺水。


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