3. 数据获取与清洗:Level2行情数据、Tick级数据获取、数据清洗与对齐

做报价驱动市场的价差预测,说白了就是跟数据打交道。我个人习惯把数据获取比作「做饭前的买菜」——菜买得不好,厨艺再高也白搭。这一章咱们就聊聊怎么把「菜」买对、洗干净。

3.1 Level2行情数据:到底比Level1多了什么?

很多新手问我:「Level2和Level1有啥区别?」我通常会反问一句:「你想看冰山全貌,还是只看露出水面的那一角?」

Level1行情只给你五档买卖盘口、最新价、成交量这些基础信息。而Level2数据,说白了就是「深度透视」——它把整个订单簿的细节都摆在你面前。

对比项 Level1 Level2
买卖盘口 5档 10档(甚至更多)
逐笔成交 有(每笔交易的详细记录)
委托队列 有(前50笔挂单明细)
更新频率 3秒快照 实时推送(毫秒级)

我在项目中遇到过最典型的场景:用Level1数据做价差预测,模型准确率死活上不去。后来换成Level2的逐笔成交数据,准确率直接提升了15%。为什么?因为Level2能看到「大单拆单」的痕迹,这是价差变化的前兆。

3.2 Tick级数据获取:别被「实时」两个字骗了

Tick级数据,就是每一笔行情变动的记录。听起来简单,但实际获取时坑不少。

我个人建议用WebSocket连接交易所的行情推送接口。以国内某主流交易所为例,伪代码大概是这样的:

import websocket
import json

def on_message(ws, message):
    data = json.loads(message)
    # 这里拿到的是逐笔成交或盘口变动
    process_tick(data)

def on_error(ws, error):
    print(f"连接异常: {error}")
    # 我曾经在这里吃过亏——忘记重连逻辑
    reconnect()

ws = websocket.WebSocketApp(
    "wss://api.exchange.com/market/tick",
    on_message=on_message,
    on_error=on_error
)
ws.run_forever()
⚠️ 避坑指南:我曾经以为WebSocket连上就万事大吉,结果某次凌晨行情剧烈波动,连接断了整整5分钟没发现。后来我加了两层保障:一是心跳检测(每10秒发一次ping),二是本地缓存+断线重连后的数据补拉机制。

你想想看,如果模型训练时缺了那5分钟的数据,预测出来的价差能准吗?

3.3 数据清洗:脏数据比没数据更可怕

拿到原始Tick数据后,千万别急着喂给模型。我见过太多人直接拿原始数据跑回测,结果收益曲线漂亮得不像话——后来发现是数据里有「未来函数」。

常见的脏数据问题:

  • 时间戳错乱:交易所的时钟和你的服务器时钟不同步
  • 重复数据:同一笔成交被推送了两次
  • 缺失数据:某几秒的盘口数据完全空白
  • 异常价格:比如卖一价突然比买一价还低(这明显不合理)

我的清洗流程一般分三步:

  1. 去重:按「时间戳+成交编号」做唯一性校验
  2. 排序:按时间戳升序排列,检查是否有乱序
  3. 插值:对于缺失的秒级数据,用前后Tick做线性插值
def clean_ticks(raw_ticks):
    # 去重
    unique_ticks = raw_ticks.drop_duplicates(subset=['timestamp', 'trade_id'])
    # 排序
    sorted_ticks = unique_ticks.sort_values('timestamp')
    # 检查时间间隔
    time_gaps = sorted_ticks['timestamp'].diff()
    abnormal_gaps = time_gaps[time_gaps > pd.Timedelta(seconds=5)]
    if len(abnormal_gaps) > 0:
        print(f"发现{len(abnormal_gaps)}处异常时间间隔")
    return sorted_ticks
💡 小技巧:清洗时别只盯着价格字段。我习惯把「买卖盘口差价的波动率」也作为一个清洗指标——如果某段时间差价波动率突然为0,大概率是数据源出了问题。

3.4 数据对齐:让不同频率的数据「对上话」

做价差预测时,你通常需要同时用到多个数据源:Tick级的逐笔成交、秒级的盘口快照、分钟级的K线。这些数据的时间频率不一样,怎么对齐?

嗯,这里要注意一个核心原则:以最细粒度的时间戳为基准

举个例子:

  • 逐笔成交数据:时间戳精确到毫秒,但时间间隔不固定
  • 盘口快照数据:每500毫秒推送一次
  • K线数据:每分钟一根

我的做法是:先把所有数据都重采样到同一个时间轴上。通常我会选100毫秒作为基准间隔——太细了数据量太大,太粗了会丢失Tick级的信息。

import pandas as pd

# 假设tick_data是逐笔成交,snapshot_data是盘口快照
tick_resampled = tick_data.resample('100ms').agg({
    'price': 'last',
    'volume': 'sum'
})
snapshot_resampled = snapshot_data.resample('100ms').last()

# 对齐合并
aligned_data = pd.concat([tick_resampled, snapshot_resampled], axis=1)
aligned_data = aligned_data.dropna()  # 去掉对齐后仍有缺失的行

🔑 关键点:对齐时最容易犯的错误是「向前看」——用未来的数据填充当前的空缺。这在回测里是致命的。一定要用「向后填充」或「插值」,千万别用未来的信息。

3.5 知识体系总览

下面这张图是我自己总结的数据处理流程,你可以把它当作一个检查清单:

数据获取与清洗流程 Level2行情获取 Tick级数据订阅 多数据源接入 数据清洗 去重 → 排序 → 异常检测 → 缺失值处理 数据对齐(100ms基准时间轴) ✅ 对齐后的训练数据

这张图把整个流程串起来了。从数据获取到清洗再到对齐,每一步都马虎不得。我刚开始做的时候,总觉得清洗这一步浪费时间,后来被坑过几次才明白——数据质量决定了模型的天花板。

📌 我的经验:每次拿到新数据源,我都会先花一天时间做「数据质量报告」——统计缺失率、异常值比例、时间戳连续性。这个报告能帮你快速判断数据能不能用,省得白费力气建模。

好了,数据获取和清洗就聊到这儿。记住一句话:好数据是好模型的一半。下一章咱们会基于这些干净的数据,开始构建真正的价差预测特征。


专注资料整理