第一章:数据获取与清洗——基差交易的基石

做量化交易,尤其是基差套利,有个铁律:数据质量决定策略上限。我见过太多人花几个月写策略,结果数据一塌糊涂,回测曲线漂亮得像假的一样。说白了,数据没洗干净,后面全是白费功夫。

这一章,咱们就聊聊怎么用 Python 把期货数据搞到手,再把它收拾得服服帖帖。我个人习惯用 yfinance,虽然它主要是股票数据,但期货数据也能拿,关键是免费、方便。

核心要点:数据获取只是开始,清洗才是重头戏。对齐、重采样、去异常、补缺失——每一步都可能让你踩坑。

1.1 用 yfinance 获取期货数据

先装库:

pip install yfinance pandas numpy matplotlib

然后写个函数,把主力连续合约的数据拉下来。我一般用 yf.download(),指定代码和周期:

import yfinance as yf
import pandas as pd

def get_futures_data(ticker, start='2020-01-01', end='2024-12-31'):
    data = yf.download(ticker, start=start, end=end, auto_adjust=False)
    return data

# 以沪深300股指期货为例
if_data = get_futures_data('IF=F')
print(if_data.head())

嗯,这里要注意:yfinance 的期货代码后面要加 =F。比如 IF=F 是沪深300期货,CL=F 是原油。我刚开始用的时候老忘,结果拉了一堆空数据回来,气得我差点砸键盘。

小技巧:如果数据量太大,可以分段拉取,避免网络超时。我一般一次拉一年,然后合并。

1.2 数据对齐与重采样

做跨品种套利,最烦的就是两个品种的交易时间不一样。比如股指期货和商品期货,一个9:30开盘,一个9:00开盘。你直接拿收盘价算基差?那肯定对不上。

我的做法是:先对齐时间戳,再重采样到统一频率。比如都按日线来:

# 假设有两个品种的数据
df1 = get_futures_data('IF=F')
df2 = get_futures_data('IC=F')

# 只保留收盘价
df1_close = df1['Close'].resample('D').last()
df2_close = df2['Close'].resample('D').last()

# 对齐到相同日期
aligned = pd.concat([df1_close, df2_close], axis=1, join='inner')
aligned.columns = ['IF', 'IC']
print(aligned.head())

这里用了 resample('D').last(),意思是按天重采样,取最后一个值。如果你做日内交易,可以改成 resample('1H') 按小时对齐。

避坑指南:我曾经在重采样时用了 ffill() 填充缺失值,结果把非交易日的数据也填上了,导致基差曲线出现奇怪的平台。后来我改用 dropna() 直接扔掉非交易日,反而更干净。

1.3 异常值处理

期货数据里经常有「鬼数据」——比如某天收盘价突然跳涨10%,第二天又跌回来。这可能是数据源错误,也可能是极端行情。但做套利时,这种异常值会严重干扰统计模型。

我常用的方法是 Z-score 法

import numpy as np

def detect_outliers_zscore(series, threshold=3):
    z_scores = np.abs((series - series.mean()) / series.std())
    return z_scores > threshold

# 找出异常点
outliers = detect_outliers_zscore(aligned['IF'])
print(f"发现 {outliers.sum()} 个异常值")

如果发现异常值,我一般直接替换成前一天的收盘价,或者用中位数填充。千万别用均值填充——均值会被异常值本身拉偏,越填越歪。

个人经验:有一次我处理螺纹钢和热卷的套利数据,发现一个异常点导致基差突然扩大。后来查出来是数据源把主力合约切换那天的价格搞错了。这种错误,光靠统计方法很难发现,得结合合约换月规则手动检查。

1.4 缺失值填充策略

缺失值在期货数据里太常见了。节假日、合约到期、数据源中断……都会导致缺失。我的策略分三种情况:

缺失类型 推荐方法 说明
连续缺失 ≤ 2 天 线性插值 用前后两个有效值线性填充
连续缺失 3-5 天 前向填充 用最近的有效值填充
连续缺失 > 5 天 直接删除 保留反而引入噪声

代码实现:

# 线性插值
aligned['IF'] = aligned['IF'].interpolate(method='linear', limit=2)

# 前向填充
aligned['IF'] = aligned['IF'].fillna(method='ffill', limit=5)

# 删除大段缺失
aligned = aligned.dropna(subset=['IF'])

你想想看,如果缺失值太多,说明这个品种流动性有问题,做套利时得格外小心。我一般会设个阈值:如果某个月缺失超过10%,直接放弃这个品种。

避坑指南:我曾经用 bfill() 后向填充,结果把未来的数据填到了过去,回测时看起来完美,实盘一塌糊涂。记住:永远不要用未来数据填充过去

1.5 本章知识体系

下面这张图,是我自己总结的数据处理流程。每次做新策略,我都会按这个步骤走一遍:

数据获取与清洗流程 1. 数据获取 yfinance 拉取 2. 对齐重采样 统一时间频率 3. 异常值处理 Z-score 检测 4. 缺失值填充 插值/前向/删除 5. 数据验证 统计检验/可视化 6. 存储导出 HDF5/CSV/数据库 迭代优化 每个步骤都可能需要回溯,数据清洗不是线性的

这张图看着简单,但每一步都有坑。比如数据验证,我一般会画个基差曲线图,肉眼扫一遍。有时候统计方法没发现的问题,眼睛一看就发现了——比如某天基差突然变成负数,但实际不可能发生。

好了,数据获取和清洗就聊到这儿。记住一句话:数据干净了,策略就成功了一半。下一章咱们聊聊基差的计算和可视化,到时候你会感谢今天认真洗数据的自己。


专注资料整理