数据获取与清洗:期货Tick/分钟/日线数据来源、数据格式解析、缺失值处理、异常值检测与修复、数据对齐与重采样

做量化交易,有一句话我特别认同:数据决定策略的天花板,而清洗决定你能不能摸到天花板

期货数据不像股票那样好拿,Tick 数据更是金贵。我刚开始做期货量化那会儿,花在数据上的时间比写策略还多。今天咱们就把这块硬骨头啃下来。

数据来源:你的弹药库在哪?

我个人习惯把数据来源分成三类:

  • 交易所直连:比如 CTP(综合交易平台)。延迟最低,但需要期货公司授权,而且你得自己存。适合高频策略。
  • 第三方数据商:像 Wind、聚宽、Tushare、RiceQuant。省心,但 Tick 数据通常要付费。适合回测和研究中低频策略。
  • 开源/社区数据:比如一些 GitHub 项目爬取的。嗯,这里要提醒一句:用之前一定检查数据完整性,我踩过坑,少了几天的数据,回测曲线漂亮得不像话,实盘直接崩。

数据格式解析:Tick、分钟、日线长什么样?

说白了,不同频率的数据,就是不同时间颗粒度的快照。

数据类型 核心字段 典型行数/天(单品种)
Tick 时间戳、最新价、成交量、持仓量、买卖盘口(五档或十档) 4000~8000 行(取决于品种活跃度)
1分钟线 时间戳、开高低收、成交量、持仓量 约 270 行(4小时夜盘+3小时日盘)
日线 日期、开高低收、成交量、持仓量 1 行

举个例子,一个典型的 Tick 数据 CSV 长这样:

datetime,last_price,volume,open_interest,bid_price1,bid_volume1,ask_price1,ask_volume1
2024-01-15 09:00:00.500,3850.0,1200,50000,3849.5,10,3850.5,15
2024-01-15 09:00:00.750,3850.5,1201,50000,3850.0,8,3851.0,12

注意那个时间戳,精确到毫秒甚至微秒。我见过有人把时间戳当字符串处理,排序时按字典序排,结果 09:00:00.500 排到了 09:00:00.1000 后面——这显然是错的。

缺失值处理:数据里的「黑洞」

为什么会缺失?原因很多:网络断连、交易所休市、合约换月、甚至数据商抽风。

我的处理原则是:先分类,再动手

  • 连续缺失(比如连续 5 分钟没数据):直接标记为 NaN,或者用前一个有效值填充(ffill)。但要注意,如果缺失发生在开盘或收盘附近,ffill 可能会引入偏差。
  • 单点缺失(比如某个 Tick 丢了):用前后 Tick 的均值插值。对于分钟线,可以用线性插值。
  • 合约换月导致的「缺失」:这不是真缺失,是数据源切换。我建议用主力合约连续数据,或者自己写个换月逻辑。
一个小技巧:处理缺失值时,先检查数据的时间间隔是否均匀。比如 1 分钟线,理想间隔是 60 秒。如果某两行之间差了 120 秒,那中间大概率丢了一根 K 线。

异常值检测与修复:揪出那些「离谱」的数据

异常值比缺失值更隐蔽,也更危险。它会让你的策略在回测里看起来完美,实盘却一塌糊涂。

我常用的方法有几种:

  • 价格突变检测:如果当前 Tick 价格相比前一个 Tick 变动超过 1%(或者 3 个标准差),就标记为异常。举个例子,螺纹钢正常波动 1~2 个点,突然跳了 50 个点,那大概率是数据错误。
  • 成交量异常:成交量突然放大到前 10 根 K 线均值的 10 倍以上,且没有对应的价格大幅波动,可能是数据商合并了多笔交易。
  • 买卖盘口倒挂:买一价高于卖一价。这在正常市场里几乎不可能发生。如果出现,直接丢弃该 Tick。
我曾经遇到过:一个品种的 Tick 数据里,某天的开盘价被记录成了 0。回测时没注意,策略在那天开仓信号全乱套。后来我加了一条规则:任何价格字段为 0 或负数的行,直接删除。

数据对齐与重采样:让不同频率的数据「对上话」

这是最考验耐心的一步。你的策略可能需要同时用到 Tick 数据和 1 分钟数据,但它们的索引(时间戳)往往对不齐。

对齐的核心思路是:以低频数据的时间戳为基准,将高频数据聚合到该时间点上

举个例子,把 Tick 数据重采样成 1 分钟线:

import pandas as pd

# 假设 df_tick 是 Tick 数据,索引是 datetime
df_1min = df_tick.resample('1min', label='right', closed='right').agg({
    'last_price': 'ohlc',  # 生成开高低收
    'volume': 'sum',
    'open_interest': 'last'
})

这里有个细节:labelclosed 参数决定了时间窗口的归属。我个人习惯用 label='right', closed='right',这样 09:01:00 这根 K 线包含的是 09:00:00 到 09:00:59.999 的数据。不同数据商可能用不同的约定,一定要先确认。

对齐不同品种的数据时,我建议用 pd.merge_asof。它比普通的 merge 更智能,能自动找到最近的时间戳进行匹配。

# 对齐螺纹钢和铁矿石的 1 分钟数据
df_merged = pd.merge_asof(df_rb_1min, df_i_1min, on='datetime', direction='nearest')

嗯,这里要注意:merge_asof 要求两个 DataFrame 都按时间排序。我吃过亏,没排序直接 merge,结果数据全乱了。

知识体系总览

下面这张图,是我自己梳理的数据处理流程。每次拿到新数据,我都会按这个步骤走一遍。

数据获取 格式解析 缺失值处理 异常值检测 数据对齐 重采样 数据处理流水线:从左到右,从上到下 每个环节都可能需要回溯到上一步

你看,整个流程是环环相扣的。数据没拿对,后面全白干。格式没解析好,缺失值处理就会出错。所以我的建议是:每做完一步,都停下来看一眼数据。画个图,打印几行,确认没问题再往下走。

核心总结:数据获取与清洗不是「一次性工作」,而是贯穿整个量化策略生命周期的持续任务。你花在数据上的每一分钟,都会在策略表现上得到回报。

专注资料整理