数据源接入:交易所API协议、WebSocket实时流、CSV/Parquet历史数据加载

做高频交易,第一件事不是写策略,而是搞定数据。我见过太多人花几个月写了个漂亮的策略回测,结果一上实盘就崩——为什么?数据源没接对。今天咱们就把这块地基打牢。

交易所API协议:REST vs WebSocket

交易所给咱们提供了两种主要的数据获取方式。说白了,一个像你去图书馆借书(REST),一个像订报纸每天送到家(WebSocket)。

特性 REST API WebSocket
请求方式 HTTP请求-响应 长连接推送
延迟 50-200ms 1-10ms
适用场景 历史数据、下单 实时行情、订单簿
带宽消耗 高(持续连接)

核心原则:REST拿历史,WebSocket接实时。别搞反了。

我个人习惯是这样:启动时先用REST拉一波快照,然后切到WebSocket收增量更新。这样既保证了初始数据的完整性,又不会错过实时变化。

WebSocket实时流:心跳、重连、序列号

WebSocket看着简单,坑可不少。我曾经在实盘中被坑过一次——交易所WebSocket断开了,我这边没发现,结果订单簿数据停了整整30秒。嗯,那30秒的行情波动,够我喝一壶的。

这里有几个关键点:

  • 心跳机制:每隔15-30秒发一个ping,收不到pong就准备重连
  • 序列号校验:每条消息带一个递增的seq,发现跳号说明丢数据了
  • 快照+增量:每N条增量后,主动请求一次全量快照做校验

我的小技巧:WebSocket重连时不要立刻请求全量快照,等1-2秒。因为交易所刚断连时负载高,你挤进去反而容易再次断开。

代码示例(Python伪代码):

import websocket
import json

class OrderBookStream:
    def __init__(self, exchange_url):
        self.ws = None
        self.seq = 0
        self.snapshot = None
        
    def on_message(self, ws, message):
        data = json.loads(message)
        # 校验序列号
        if data['seq'] != self.seq + 1:
            self.request_snapshot()  # 发现跳号,重新拉快照
        self.seq = data['seq']
        # 更新订单簿
        self.update_orderbook(data)
        
    def on_error(self, ws, error):
        print(f"连接异常: {error}")
        self.reconnect_with_delay()  # 延迟1-2秒重连

CSV/Parquet历史数据加载

回测时咱们需要历史数据。CSV是最常见的格式,但Parquet才是高频交易的首选。为什么?

  • CSV:文本格式,体积大,加载慢。100GB的CSV,光解析就要半小时
  • Parquet:列式存储,压缩率高,加载快。同样数据,Parquet只有CSV的1/5大小

你想想看,回测一次要加载100GB数据,CSV得等半小时,Parquet只要5分钟。这差距,在迭代策略时就是天壤之别。

注意:Parquet虽然快,但有个坑——它不支持流式写入。如果你要实时存储数据,建议先用CSV或二进制格式缓冲,定时转存为Parquet。

加载代码示例:

import pandas as pd
import pyarrow.parquet as pq

# CSV加载(慢)
df_csv = pd.read_csv('orderbook_2024.csv', parse_dates=['timestamp'])

# Parquet加载(快)
df_pq = pq.read_table('orderbook_2024.parquet').to_pandas()

# 我建议:数据清洗后统一存Parquet
# 原始数据保留CSV作为备份

数据源接入的整体流程

咱们把上面这些串起来,看看一个完整的数据接入流程长什么样:

数据源接入流程 交易所API WebSocket实时流 CSV/Parquet文件 数据清洗模块(去重、校验、对齐) 数据存储(内存队列 + Parquet归档) REST拉快照 → WebSocket收增量 → 清洗 → 存储

这个流程我用了好几年,基本没出过大问题。核心思路就一条:分层解耦。数据源只管收数据,清洗模块只管处理,存储模块只管存。各干各的,出了问题也好排查。

避坑指南:我曾经把数据清洗和存储写在一个线程里,结果清洗慢了,存储堵住了,WebSocket缓冲区爆了。后来改成生产者-消费者模式,用两个独立线程,问题就解决了。

好了,数据源接入这块就聊到这儿。记住:REST拿历史,WebSocket接实时,Parquet存归档。这三板斧用好了,后面的策略开发就顺了。

专注资料整理