第二章:订单流数据采集

数据采集,是整个订单流分析体系的基石。

我见过太多人,策略写得花里胡哨,结果数据源一塌糊涂。说白了,数据不准,后面全是白搭。今天咱们就把这块硬骨头啃下来。

2.1 交易所API对接:选对路子

对接交易所API,是第一步。我个人习惯,先看文档,再看社区反馈。

主流交易所的API,大体分两类:

  • REST API:请求-响应模式。适合拉取历史数据、查询账户信息。延迟高,但稳定。
  • WebSocket API:全双工通信。适合实时行情、订单流推送。延迟低,但连接管理麻烦。

我在项目中遇到过,有人用REST API去轮询Tick数据,结果被交易所限流,直接封了IP。嗯,这里要注意:实时数据,必须用WebSocket

核心原则:

  • 历史数据用REST,批量拉取
  • 实时数据用WebSocket,持续监听
  • 下单接口用REST,确保幂等性

2.2 WebSocket实时流:心跳与重连

WebSocket连接,说白了就是一条长管道。管道断了,数据就断了。

你想想看,如果行情正剧烈波动,你的WebSocket突然断开,那几秒钟的Tick数据丢失,可能就错过了一波行情。

我曾经踩过一个坑:交易所的WebSocket服务器,每30秒会发一个ping帧。如果客户端没及时回复pong,连接就会被断开。后来我加了个心跳检测,每15秒主动发一次ping,问题就解决了。

避坑指南:

我曾经在对接某二线交易所时,发现它的WebSocket每隔一小时会自动断开。后来查文档才知道,这是它的「安全策略」。解决方案:实现自动重连机制,断线后立即重新订阅。

一个典型的WebSocket订阅流程:

// 伪代码示例
ws.connect('wss://api.exchange.com/ws')
ws.on('open', () => {
    // 订阅订单簿深度
    ws.send(JSON.stringify({
        event: 'subscribe',
        channel: 'depth',
        symbol: 'BTCUSDT'
    }))
})
ws.on('message', (data) => {
    // 解析Tick数据
    const tick = JSON.parse(data)
    processTick(tick)
})
ws.on('close', () => {
    // 自动重连
    setTimeout(() => reconnect(), 1000)
})

2.3 数据清洗与存储:脏数据是毒药

原始数据,永远是脏的。为什么?

  • 网络抖动导致数据重复
  • 交易所撮合引擎延迟导致时间戳错乱
  • 部分Tick数据缺失(比如盘口快照丢失)

我见过最离谱的一次,某交易所的API在高峰期返回了重复的成交记录,时间戳一模一样。如果不做去重,你的回测结果会直接翻倍。

警告:

千万不要把原始数据直接喂给策略!一定要经过清洗。我曾经因为偷懒,没做去重,结果策略在实盘时频繁开仓,亏了六位数。

数据清洗的标准流程:

  1. 去重:按交易ID或时间戳+价格+数量组合去重
  2. 排序:按时间戳升序排列,确保时序正确
  3. 补全:对于缺失的Tick,用前一个有效值填充(或者丢弃)
  4. 校验:检查价格是否在合理范围内(比如BTC价格不会突然从1万跳到100万)

存储方面,我个人推荐用时序数据库,比如InfluxDB或TimescaleDB。关系型数据库(MySQL)在存储高频Tick数据时,写入性能会成问题。

-- 创建Tick数据表(TimescaleDB示例)
CREATE TABLE ticks (
    time TIMESTAMPTZ NOT NULL,
    symbol TEXT NOT NULL,
    price DOUBLE PRECISION,
    volume DOUBLE PRECISION,
    side TEXT,
    trade_id BIGINT
);
-- 转换为超表
SELECT create_hypertable('ticks', 'time');

2.4 Tick级数据解析:毫秒定胜负

Tick级数据,就是每一笔成交的原始记录。它包含:

  • 时间戳(精确到毫秒甚至微秒)
  • 成交价格
  • 成交数量
  • 买卖方向(主动买还是主动卖)
  • 交易ID

为什么Tick数据这么重要?因为订单流分析,就是基于这些「微观」数据来推断「宏观」趋势的。

举个例子:如果连续10笔Tick都是主动买(即买方吃单),说明买方力量强,价格大概率要涨。这就是订单流的基本逻辑。

关键字段解析:

字段 含义 注意事项
time 成交时间 注意交易所时区,统一转成UTC
price 成交价格 浮点数精度问题,建议用整数存储(如乘以1e8)
volume 成交数量 注意合约的乘数(比如永续合约的volume是张数)
side 买卖方向 buy/sell,注意有些交易所用'B'/'S'

解析Tick数据时,有个小技巧:不要直接解析JSON字符串。我在项目中试过,用Python的json.loads()解析每秒上千条Tick,CPU直接飙到80%。后来改用Protocol Buffers或者MessagePack,性能提升了5倍。

性能优化建议:

如果数据量极大(比如每秒上万条Tick),可以考虑用C++或Rust编写解析模块,通过Python的C扩展调用。我曾在某家做市商公司见过,他们用Rust写的解析器,单机每秒处理50万条Tick。

2.5 知识体系总览

下面这张图,是我自己总结的订单流数据采集全流程。你照着这个框架搭,基本不会出大问题。

订单流数据采集全流程 交易所API 采集方式 REST API(历史数据) | WebSocket(实时数据) 数据清洗 去重 → 排序 → 补全 → 校验 时序数据库(InfluxDB / TimescaleDB) Tick解析 时间戳 价格/数量 买卖方向 输出:清洗后的Tick数据流

嗯,这套流程,我在多个项目中验证过。从数据源到最终存储,每一步都有坑。但只要按这个框架走,至少能保证数据质量过关。