4、数据采集与接入:API对接、WebSocket实时流、文件批量导入、数据校验与清洗

做市商系统里,数据就是命根子。这话一点都不夸张。

我见过太多团队,策略模型跑得飞起,结果数据源一断,整个系统直接瘫痪。所以今天咱们聊聊数据采集与接入——说白了,就是怎么把外面乱七八糟的数据,变成系统能吃的干净粮食。

4.1 数据接入的三种主流方式

做市商的数据源,无非就三类:交易所的REST API、WebSocket实时流、还有历史数据的文件导入。每种方式都有自己的脾气,咱们一个一个说。

4.1.1 REST API 对接

REST API 是最常见的接入方式。交易所给你一个HTTP接口,你发请求,它返回JSON。简单吧?但坑不少。

我个人的习惯是:

  • 限频控制:交易所都有请求频率限制。比如每秒最多5次。你超了,它直接封你IP。我在项目中遇到过,一个同事没做限频,结果整个机房的IP都被封了,那叫一个惨。
  • 重试机制:网络波动是常态。请求失败后,等几秒再试。一般重试3次,间隔指数增长。
  • 数据缓存:别每次都去拉全量数据。增量更新才是王道。
# 伪代码示例:带限频和重试的API请求
import time
import requests

def fetch_with_retry(url, max_retries=3):
    for i in range(max_retries):
        try:
            resp = requests.get(url, timeout=5)
            if resp.status_code == 200:
                return resp.json()
        except Exception as e:
            print(f"第{i+1}次请求失败: {e}")
            time.sleep(2 ** i)  # 指数退避
    raise Exception("请求彻底失败")
⚠️ 注意:千万别把API Key硬编码在代码里。用环境变量或者配置中心管理。我曾经见过有人把密钥提交到GitHub,结果被机器人扫到,账户直接被盗刷。

4.1.2 WebSocket 实时流

做市商对实时性要求极高。REST API 延迟太高,动不动几百毫秒。这时候就得用 WebSocket。

WebSocket 是长连接,交易所主动往你这边推数据。你想想看,行情一变,数据瞬间就到,这才是做市商该有的节奏。

接入WebSocket的几个要点:

  • 心跳维持:连接久了容易断。交易所一般要求你每隔30秒发一个ping包。你不发,它就当你死了,直接断开。
  • 断线重连:网络波动导致断开是家常便饭。我建议你写个自动重连逻辑,断开后立即重连,别让数据断流超过1秒。
  • 数据去重:有时候交易所会重复推送同一条数据。你收到后得做个去重,不然策略会重复下单。
# 伪代码示例:WebSocket心跳与重连
import websocket

def on_message(ws, message):
    # 处理行情数据
    process_tick(message)

def on_close(ws, close_status_code, close_msg):
    print("连接断开,准备重连...")
    time.sleep(1)
    ws.run_forever()  # 自动重连

ws = websocket.WebSocketApp(
    "wss://exchange.com/ws",
    on_message=on_message,
    on_close=on_close
)
ws.run_forever(ping_interval=30)  # 每30秒发一次心跳
💡 小技巧:WebSocket 数据量大的时候,别在主线程里处理。用队列把数据丢给后台线程,主线程只管收。不然处理慢了,数据会堆积,延迟越来越高。

4.1.3 文件批量导入

历史数据怎么办?比如你要回测一个策略,需要过去一年的订单簿数据。这时候就得靠文件批量导入。

常见的文件格式有CSV、Parquet、甚至交易所自己的二进制格式。我个人偏爱Parquet,因为它压缩率高,读取快,还支持列式存储。

导入流程:

  1. 文件校验:先检查文件完整性。比如MD5校验,防止文件传输过程中损坏。
  2. 格式转换:把原始数据转成系统内部统一的Schema。
  3. 分批写入:别一次性加载几十GB的文件到内存。分批次读,每批10万条,写入数据库。
# 伪代码示例:分批导入CSV文件
import pandas as pd

chunk_size = 100000
for chunk in pd.read_csv("history.csv", chunksize=chunk_size):
    # 数据清洗
    chunk = chunk.dropna()
    chunk = chunk[chunk['price'] > 0]  # 过滤无效价格
    # 写入数据库
    db.insert(chunk)

4.2 数据校验与清洗

数据进来了,但能用吗?不一定。交易所的数据也可能有错。比如价格字段出现负数,时间戳格式不对,甚至数据重复。

我把它总结成三步:

4.2.1 格式校验

检查每个字段的类型和范围。比如:

  • 价格必须是正数
  • 时间戳必须是毫秒级整数
  • 交易量不能为0

4.2.2 逻辑校验

有些数据格式对,但逻辑不对。比如:

  • 卖一价低于买一价(这明显不合理)
  • 时间戳比当前时间还晚(数据穿越了?)
  • 同一笔交易出现两次(重复数据)

4.2.3 异常处理

发现异常数据怎么办?我建议:

  • 丢弃:明显错误的数据直接扔掉
  • 标记:可疑数据打个标签,后续人工审核
  • 告警:异常率超过阈值,立刻通知运维
📌 核心原则:脏数据进,脏数据出。数据清洗不是可选项,是必选项。我见过一个团队,因为没做数据校验,策略根据错误数据下了几十笔废单,亏了几十万。

4.3 数据接入的整体架构

说了这么多,咱们画张图看看整体流程。

交易所API WebSocket实时流 历史文件导入 数据接入层 限频控制 心跳维持 断线重连 分批读取 格式转换 数据缓存 数据校验层 格式校验 逻辑校验 去重处理 异常标记 告警通知 数据清洗 干净数据

这张图展示了数据从源头到清洗完成的完整链路。你看,数据源有三种,经过接入层处理,再到校验层清洗,最后输出干净数据给下游策略使用。

4.4 实战中的避坑指南

最后,分享几个我踩过的坑:

  • 别信交易所的文档:文档写的和实际返回的数据经常对不上。我建议你写个测试脚本,先跑一遍看看真实数据长什么样。
  • 数据延迟要监控:WebSocket 偶尔会卡住,数据延迟从几毫秒变成几秒。你得有个监控,延迟超过阈值就告警。
  • 文件导入要幂等:同一个文件导入两次,结果应该一样。不然回测结果对不上,你都不知道该信哪个。
💡 我的经验:数据接入这块,花80%的时间做校验和容错,只花20%的时间写接入代码。因为数据不出错,后面的策略才能跑得稳。

好了,数据采集与接入就聊到这儿。记住一句话:数据质量决定策略上限。把数据管好了,你的做市商系统就成功了一半。

专注资料整理