22、做市商数据基础设施:实时数据流、历史数据库、数据清洗与存储。
做市商这行,说白了就是跟数据打交道。你策略再牛,模型再漂亮,底层数据一塌糊涂,那全是白搭。我见过太多团队,策略回测曲线漂亮得像艺术品,一上实盘就崩,十有八九是数据基础设施没搭好。
今天我们就来拆解一下,做市商的数据基础设施到底该怎么搭。核心就三块:实时数据流、历史数据库、数据清洗与存储。这三者缺一不可,而且顺序不能乱。
实时数据流:做市商的“神经末梢”
实时数据流,就是你的行情源。做市商对行情的要求,跟普通交易者完全不是一个量级。普通交易者看个1秒快照可能就够了,但做市商需要的是逐笔成交、逐笔挂单的深度数据。
我个人习惯,至少接三路独立的行情源。为什么?因为单点故障太可怕了。我曾经遇到过一次,交易所的行情网关挂了,整整5分钟没推送数据。如果你只接了一路,那5分钟你的报价就是瞎报,风险敞口无限大。
实时数据流的核心指标,我列个表给你看:
| 指标 | 普通交易者 | 做市商要求 |
|---|---|---|
| 数据频率 | 1秒快照 | 逐笔(Tick级) |
| 延迟要求 | 100ms以内 | 1ms以内 |
| 冗余备份 | 单路 | 至少3路独立源 |
| 数据完整性 | 允许丢包 | 零容忍,需校验 |
嗯,这里要注意。实时数据流不仅仅是接收,还要做“对齐”。不同行情源的时间戳可能不同,甚至同一个交易所的不同产品,时间基准都可能不一致。我建议在数据入口处就做一次统一的时间戳校准,用NTP服务器同步,精度至少到微秒级。
核心原则:实时数据流是“活水”,必须保证它的纯净度和流速。任何一点污染或延迟,都会直接传导到你的报价模型里。
历史数据库:回测的“照妖镜”
历史数据库,说白了就是你的“记忆”。做市商策略的回测,对历史数据的要求极其苛刻。你不能拿日线数据去回测一个高频做市策略,那等于拿望远镜看蚂蚁打架。
我建议历史数据库至少存储以下三个层级的数据:
- 原始Tick数据:每一笔成交、每一次挂单变动,原封不动地存下来。这是最宝贵的资产。
- 切片数据:按固定时间间隔(比如100ms)对订单簿进行快照。方便快速回测,不用每次都从头扫描Tick数据。
- 衍生数据:比如计算好的买卖价差、订单簿不平衡度、成交量加权价格等。这些是特征工程的结果,存下来可以加速后续分析。
存储历史数据,我踩过一个坑。当时用MySQL存Tick数据,结果一张表几个月就几十亿条记录,查询慢得像蜗牛。后来我换成了时序数据库,比如InfluxDB或ClickHouse,专门为这种时间序列数据优化的,查询速度快了几个数量级。
避坑指南:我曾经以为历史数据存得越多越好,结果发现数据量太大,回测一次要跑好几天。后来我学会了“分层存储”——热数据用SSD,冷数据用HDD,归档数据用对象存储。这样既保证了速度,又控制了成本。
数据清洗与存储:脏活累活,但必须干
数据清洗,是数据基础设施里最不起眼、但最重要的一环。你想想看,交易所的数据也不是完美的。偶尔会有错误订单、异常价格、数据缺失、重复推送。如果你不把这些“脏数据”洗掉,你的模型就会被它们带偏。
我总结了一套数据清洗的流程,你可以参考:
- 去重:同一个事件被推送了两次,只保留第一条。
- 异常值过滤:价格超过3个标准差,或者买卖价差为负数,直接丢弃。
- 时间戳校验:检查时间戳是否单调递增,如果出现倒挂,说明数据有问题。
- 缺失值填充:如果某一段数据缺失,用前一个有效值填充,或者标记为“无效”。
- 数据对齐:不同数据源的时间戳统一到同一个基准上。
下面是一个简单的数据清洗代码示例,用Python写的,你可以看看:
import pandas as pd
import numpy as np
def clean_tick_data(df):
# 1. 去重
df = df.drop_duplicates(subset=['timestamp', 'price', 'volume'])
# 2. 异常值过滤
mean_price = df['price'].mean()
std_price = df['price'].std()
df = df[(df['price'] > mean_price - 3*std_price) &
(df['price'] < mean_price + 3*std_price)]
# 3. 时间戳校验
df = df.sort_values('timestamp')
df = df[df['timestamp'].diff() >= 0]
# 4. 缺失值填充(用前向填充)
df = df.fillna(method='ffill')
return df
存储方面,我建议采用“冷热分离”的策略。最近7天的数据放在高性能的SSD上,7天到3个月的数据放在普通HDD上,3个月以上的数据压缩后存到对象存储(比如S3)里。这样既能保证实时查询的速度,又能控制存储成本。
警告:千万不要把原始数据和清洗后的数据混在一起存。我见过有人图省事,直接在原表上做修改,结果回测时发现数据对不上,查了三天才发现是清洗逻辑变了,但历史数据已经被污染了。正确的做法是:原始数据只读不写,清洗后的数据另存一份,并记录清洗的版本号。
知识体系框架
为了让你更直观地理解这三者的关系,我画了一张图:
这张图清晰地展示了数据从源头到存储的完整链路。实时数据流进来后,先经过清洗,再分流到历史数据库和存储层。注意,清洗这一步不能省,它是整个数据基础设施的“守门员”。
最后说一句,数据基础设施的建设,没有一劳永逸的方案。随着你的交易品种增加、交易量上升,这套架构需要不断迭代。我每年都会做一次数据基础设施的复盘,看看哪些地方可以优化。毕竟,做市商拼到最后,拼的就是数据处理的效率。