3. 数据获取与处理:打好量化交易的地基

做CTA趋势跟踪,说白了就是跟数据打交道。我见过太多人一上来就写策略、跑回测,结果数据都没整明白——那能赚钱才怪呢。今天咱们就聊聊数据这块儿,怎么拿、怎么洗、怎么存。

核心观点:数据质量决定了策略的天花板。数据脏,策略再牛也白搭。

3.1 期货历史数据从哪来?

我个人习惯用以下几个渠道,各有优劣,你根据情况选:

数据源 优点 缺点
Wind/聚宽/米筐 数据全、接口规范 要付费,个人用户门槛高
Tushare/AkShare 免费、社区活跃 稳定性一般,有调用限制
交易所官网 最权威、免费 需要自己爬,格式不统一
期货公司API 实时数据、延迟低 历史数据有限,接口复杂

我在项目中遇到过用免费接口跑回测,结果某天数据突然断档,策略信号全乱了。嗯,从那以后我都是至少两个数据源交叉验证。

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

拿到原始数据后,别急着用。先看看有没有这些问题:

  • 缺失值:某天没交易?或者数据没更新?
  • 异常值:价格突然跳空几百点?多半是数据错误。
  • 时间戳不对齐:不同合约的交易日历不一样,得统一。

我的小技巧:用pandas的isna()describe()先扫一遍,心里有个底。我曾经因为没检查,用了一组有10%缺失值的数据跑了三个月回测,结果策略表现好得离谱——后来发现是数据填充方式搞错了。

数据对齐这块,说白了就是让不同合约的数据在同一个时间轴上。举个例子:

import pandas as pd

# 假设你有两个合约的数据
df_1 = pd.read_csv('rb2401.csv', index_col='date', parse_dates=True)
df_5 = pd.read_csv('rb2405.csv', index_col='date', parse_dates=True)

# 对齐到共同的交易日
common_dates = df_1.index.intersection(df_5.index)
df_1_aligned = df_1.loc[common_dates]
df_5_aligned = df_5.loc[common_dates]

# 或者用reindex,自动填充缺失值
all_dates = pd.date_range(start='2023-01-01', end='2023-12-31', freq='B')
df_1_aligned = df_1.reindex(all_dates, method='ffill')

为什么会这样?因为不同合约的上市日期、退市日期都不一样,直接拼在一起肯定出问题。

3.3 复权处理——别让分红影响你的判断

期货虽然没有股票那种现金分红,但有换月交割的问题。你想想看,主力合约每个月都在变,价格本身就不连续。如果不做复权,回测结果会失真。

常用的复权方法有两种:

  • 前复权:调整历史价格,让它们和当前价格可比。适合看趋势。
  • 后复权:调整当前价格,让它们和历史价格可比。适合算收益。

注意:期货复权不是简单的加减,要考虑持仓成本、移仓损耗。我见过有人直接用股票复权方法做期货,结果回测收益虚高20%。

一个简单的复权示例:

def adjust_price(df, method='forward'):
    """
    简单的期货复权处理
    method: 'forward' 前复权, 'backward' 后复权
    """
    df = df.copy()
    if method == 'forward':
        # 以最新合约为基准,调整历史价格
        ratio = df['close'].iloc[-1] / df['close'].iloc[0]
        df['adj_close'] = df['close'] * ratio
    else:
        # 以最早合约为基准
        ratio = df['close'].iloc[0] / df['close'].iloc[-1]
        df['adj_close'] = df['close'] * ratio
    return df

当然,实际项目中复权逻辑要复杂得多。我个人习惯用连续合约,比如用持仓量最大的合约拼接成一条连续价格曲线,再配合复权处理。

3.4 数据存储——选CSV还是HDF5?

这个问题我纠结过很久。直接说结论:

存储方式 适用场景 我的建议
CSV 小数据量、临时分析、跨平台分享 适合做原型验证,简单粗暴
HDF5 大数据量、频繁读写、需要压缩 适合生产环境,性能好
Parquet 列式存储、大数据分析 如果你用Spark或Dask,可以考虑

举个例子,我一般这样存:

# CSV方式——简单直接
df.to_csv('data/rb_continuous.csv', index=False)

# HDF5方式——高效压缩
df.to_hdf('data/futures.h5', key='rb', mode='a', complevel=9)

# 读取也很方便
df_from_h5 = pd.read_hdf('data/futures.h5', key='rb')

避坑指南:我曾经用CSV存了十年的分钟线数据,结果一个文件就几个G,读取慢得要命。后来换成HDF5,读取速度快了10倍,文件大小还缩小了60%。所以,数据量大的话,别犹豫,上HDF5。

3.5 本章知识体系总览

下面这张图,是我自己梳理的数据处理流程。你照着这个走,基本不会出大问题:

数据获取与处理流程 数据获取 Wind/免费接口/交易所 数据清洗 缺失值/异常值处理 数据对齐 时间轴统一 复权处理 前复权/后复权/连续合约 数据存储 CSV / HDF5 / Parquet ✅ 可用数据集 每个环节都直接影响策略回测的准确性,别偷懒

嗯,数据这块儿就聊到这儿。记住一句话:数据干净了,策略才能跑得稳。下一章咱们开始真正写策略代码,到时候你就知道今天这些功夫没白花。

专注资料整理