订单簿数据获取:交易所API对接、WebSocket实时流、数据清洗与存储

做量化交易,尤其是做市商策略,数据就是你的血液。没有干净、实时的订单簿数据,再牛的模型也是空中楼阁。今天我们就来聊聊,怎么把这口「活水」引进来。

我个人习惯把数据获取分成三个层次:接口对接、实时流处理、清洗存储。每一层都有坑,踩过才知道疼。

一、交易所API对接:选对协议,少走弯路

大部分交易所都提供REST API和WebSocket两种接口。REST适合拉取历史快照,WebSocket适合订阅实时增量。

REST API 要点

  • 限频控制:每个交易所的限频规则不同。币安是每分钟1200次权重,OKX是每秒60次。我建议你在代码里做一个本地计数器,超过阈值就sleep一下。
  • 签名认证:私钥千万别硬编码在代码里。我用环境变量加载,或者用密钥管理服务。
  • 错误重试:网络抖动是常态。我一般用指数退避策略,第一次等1秒,第二次等2秒,最多重试3次。

避坑指南:我曾经在对接某二线交易所时,发现它的REST接口返回的订单簿深度只有20档。后来仔细看文档,才发现需要传参limit=100。嗯,文档还是要读的。

WebSocket 要点

  • 心跳维持:大部分交易所要求客户端每隔30秒发送ping帧。我习惯用一个定时器,每25秒发一次。
  • 断线重连:WebSocket连接说断就断。我写了一个自动重连模块,检测到close事件后,等待5秒再重新订阅。
  • 数据去重:有时候交易所会重复推送同一条消息。我根据sequenceIdtimestamp做去重,避免重复更新订单簿。

二、WebSocket实时流:增量更新才是王道

全量拉取订单簿太慢了,而且浪费带宽。真正做市商用的都是增量更新——只推送变化的部分。

举个例子,币安的深度流格式是这样的:

{
  "e": "depthUpdate",   // 事件类型
  "E": 123456789,       // 事件时间
  "s": "BTCUSDT",       // 交易对
  "U": 157,             // 第一更新ID
  "u": 160,             // 最后更新ID
  "b": [                // 买单变化
    ["10000.00", "1.500"]
  ],
  "a": [                // 卖单变化
    ["10001.00", "2.000"]
  ]
}

收到增量后,怎么合并到本地订单簿?我一般维护两个字典:bidsasks。key是价格,value是数量。如果数量为0,就删除这个档位。

小技巧:增量更新时,记得检查Uu的连续性。如果发现跳号,说明丢包了,需要重新拉一次全量快照。

为什么会丢包?说白了,网络拥堵或者交易所负载高。我遇到过几次,后来加了一个定时器,每5分钟强制拉一次全量快照做校准。

三、数据清洗:脏数据会要了你的命

你以为交易所推送的数据都是干净的?太天真了。我见过的情况包括:

  • 价格异常:突然出现一个0.0001的价格,明显是撮合错误。
  • 时间戳乱跳:某条消息的时间戳比上一条早了10秒。
  • 重复推送:同一个更新ID出现两次。

我的清洗流程是这样的:

  1. 格式校验:检查字段类型是否正确,价格和数量是否为正数。
  2. 逻辑校验:买单价格必须小于卖单价格?不一定,但至少不能为负。
  3. 时间戳排序:按事件时间排序,丢弃明显乱序的数据。
  4. 去重:用(交易对, 更新ID)作为唯一键。

注意:清洗后的数据一定要打上received_at时间戳,记录你本地收到的时间。这个字段在回测时非常有用,能帮你判断数据延迟。

四、数据存储:选对工具,事半功倍

订单簿数据量很大。一个热门交易对,每秒可能推送几十次更新。一天下来就是几百万条记录。

存储方案对比

方案 适用场景 优点 缺点
CSV文件 小规模回测 简单、易读 查询慢、占用空间大
SQLite 单机分析 轻量、支持SQL 并发写入差
InfluxDB 时序数据 写入快、压缩率高 学习成本高
Parquet 大规模分析 列式存储、查询快 不适合实时写入

我个人习惯用InfluxDB存实时数据,每天凌晨跑一个任务,把昨天的数据转成Parquet格式归档。这样既保证了实时查询的速度,又节省了存储成本。

五、整体架构图

下面这张图展示了从数据获取到存储的完整流程。你想想看,每一步都环环相扣,任何一个环节出问题,下游的策略都会受影响。

交易所 REST API WebSocket 数据清洗(校验、去重、排序) InfluxDB Parquet 订单簿数据流架构 全量快照 + 增量更新 → 清洗 → 存储

六、实战中的几个坑

最后分享几个我踩过的坑,希望能帮你省点时间。

  • 时间戳陷阱:有些交易所返回的是毫秒级时间戳,有些是微秒级。我建议统一转成纳秒级存储,避免精度丢失。
  • 内存泄漏:WebSocket客户端如果长时间运行,回调函数里如果持有大对象的引用,内存会慢慢涨。我习惯用弱引用或者手动清理。
  • 磁盘写满:数据量大的时候,一天能写几十GB。我写了一个监控脚本,磁盘使用率超过80%就自动清理最早的数据。

核心总结:数据获取不是简单的「调个接口就完事」。从对接、清洗到存储,每一步都需要精心设计。尤其是做市商策略,对数据的实时性和准确性要求极高。记住一句话:垃圾数据进,垃圾策略出

蓝海数据掘金营,专注资料整理