4、数据获取:获取期货行情数据(Tushare/JoinQuant)、获取现货指数数据、数据对齐与清洗。

数据获取,说白了就是给策略找「食材」。

你策略再牛,模型再漂亮,拿到的数据是脏的、错的、缺的,那结果就是垃圾进垃圾出。我见过太多人花90%时间调参数,却不愿意花10%时间检查数据源。嗯,这里要敲黑板——数据质量决定了你的策略天花板。

4.1 期货行情数据怎么拿?

我个人习惯用Tushare和JoinQuant两个平台。为什么?因为稳定,而且接口设计得比较人性化。

Tushare获取期货日线数据

先注册账号,拿到token。然后装库、调接口。代码长这样:

import tushare as ts

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

# 获取螺纹钢主力连续合约日线
df_futures = pro.fut_daily(
    ts_code='RB9999.XSGE',  # 螺纹钢主力连续
    start_date='20240101',
    end_date='20241231',
    fields='trade_date,open,high,low,close,vol,amount'
)

print(df_futures.head())

这里有个坑——主力连续合约不是真实合约。它是把不同月份的主力合约拼接起来的。你直接拿它算基差,会出问题。我曾经在回测里用主力连续算套利收益,结果发现换月那天基差跳了2%,差点把策略搞崩。

避坑指南: 做期现套利,建议用具体合约(比如RB2405、RB2410),而不是主力连续。因为现货对应的是具体月份的期货,不是虚拟的连续合约。

JoinQuant获取数据

如果你在JoinQuant平台上跑策略,可以直接用它的API:

from jqdata import *

# 获取螺纹钢2010合约日线
df_jq = get_price(
    'RB2010.XSGE', 
    start_date='2024-01-01', 
    end_date='2024-12-31',
    frequency='daily',
    fields=['open', 'close', 'high', 'low', 'volume', 'money']
)

JoinQuant的好处是数据已经做过复权处理,但坏处是——它默认后复权。做套利时,后复权会把分红、配股等因素算进去,反而会扭曲基差。我个人建议用不复权数据,自己手动处理。

4.2 现货指数数据怎么拿?

现货数据比期货难拿。为什么?因为现货没有统一的交易所,数据分散在各个平台。

常见的现货数据源:

  • Wind/Choice:最全,但收费。如果你在机构,直接拉API就行。
  • Tushare指数数据:免费,但更新有延迟。
  • 中证指数公司官网:可以手动下载CSV,但没法自动化。
  • 聚宽/米筐:平台自带指数数据,方便回测。

拿沪深300指数举个例子:

# Tushare获取沪深300指数
df_index = pro.index_daily(
    ts_code='000300.SH',
    start_date='20240101',
    end_date='20241231',
    fields='trade_date,open,high,low,close,vol'
)

这里要注意——指数点位和期货价格的单位不一样。指数是点数,期货是元/吨(商品)或指数点(金融)。你直接拿它们相减算基差,数值会很大。我一般会做归一化处理,或者直接用百分比基差。

小技巧: 基差 = (期货价格 - 现货价格) / 现货价格 × 100%。这样算出来的基差是百分比,不同品种之间可以横向比较。

4.3 数据对齐与清洗——最磨人的一步

数据对齐,说白了就是让期货和现货的日期对上。

期货有夜盘,现货没有。所以期货的交易日和现货的交易日不完全重合。比如周五晚上期货还在交易,但现货已经收盘了。你拿周五夜盘的期货价格去对周五白天的现货价格,时间戳就不对。

对齐规则:

  • 期货日线数据,取15:00收盘价(日盘收盘),不要用夜盘收盘。
  • 如果某天现货休市(比如节假日),期货即使有交易,也要剔除。
  • 如果期货某天没有交易(比如涨停跌停),用前一天的收盘价填充?不,我建议直接剔除。

代码实现:

import pandas as pd

# 假设df_futures和df_index都已经有了
# 合并两个DataFrame,以现货日期为准
df_merged = pd.merge(
    df_futures[['trade_date', 'close']].rename(columns={'close': 'futures_close'}),
    df_index[['trade_date', 'close']].rename(columns={'close': 'index_close'}),
    on='trade_date',
    how='inner'  # 只保留两边都有的日期
)

# 计算基差
df_merged['basis'] = df_merged['futures_close'] - df_merged['index_close']
df_merged['basis_pct'] = df_merged['basis'] / df_merged['index_close'] * 100

数据清洗的常见问题:

问题类型 表现 处理方法
缺失值 某天期货或现货数据为空 直接删除该行,不要填充
异常值 价格突然跳涨/跳跌超过5% 检查是否除权除息,否则剔除
重复值 同一天出现多条记录 保留第一条,删除重复
时间戳错位 期货日期和现货日期不匹配 用merge的inner模式强制对齐
核心原则: 宁可数据少,不要数据脏。少一条数据只是样本量减少,脏数据会直接扭曲你的统计结果。

4.4 本章知识体系

下面这张图,是我自己画的数据获取流程。你看一遍,基本就知道每一步该做什么了。

数据获取与处理流程 期货行情数据 Tushare / JoinQuant 现货指数数据 Wind / Tushare / 官网 其他辅助数据 库存 / 持仓 / 基差 数据对齐 日期对齐(inner merge)| 时间戳统一 | 剔除不匹配数据 数据清洗 缺失值处理 | 异常值检测 | 重复值删除 | 单位统一 清洗后的干净数据集 可直接用于基差计算和策略回测

你看,整个流程其实不复杂。但每一步都有细节。我个人建议你先把数据拿到手,然后花一天时间做清洗和对齐。别急着跑策略——数据干净了,策略才有意义。

我的习惯: 每次拿到新数据,先画个分布图看看。如果价格序列里有明显的跳空或异常点,大概率是数据问题。先排查,再往下走。

嗯,数据获取这块就讲这么多。记住一句话:数据质量决定策略上限。你花在清洗上的每一分钟,都会在回测结果里回报你。

专注资料整理