3、市场数据事件:行情数据订阅、Tick数据解析、K线数据合成、实时数据推送架构
做量化交易,说白了就是跟数据打交道。而市场数据,是所有策略的起点。
我个人习惯把市场数据比作「原油」——它很原始,很粗糙,但经过提炼之后,就能变成驱动你策略引擎的燃料。这一章,我们就来聊聊这个「提炼」的过程。
3.1 行情数据订阅:你得先连上「水龙头」
要拿到行情数据,第一步就是订阅。你想想看,交易所每分钟产生海量的数据,你不可能全拉下来,也没那个必要。你需要的是「订阅」——告诉数据源:我只关心这几只股票,或者这几个合约。
在实际项目中,我遇到过不少新手直接去拉全市场快照,结果带宽打满,策略延迟高得离谱。嗯,这里要注意:订阅一定要精准。
核心要点:
- 订阅粒度:按合约代码订阅,不要按板块订阅
- 订阅频率:区分高频和低频数据流
- 重连机制:网络抖动是常态,必须自动重连
举个例子,如果你做股指期货,你只需要订阅 IF、IC、IH 这几个主力合约。别把中证500的所有股票都拉进来,那是给自己找麻烦。
# 伪代码示例:订阅行情
market_data_client.subscribe(
symbols=['IF2409', 'IC2409'],
data_type=['tick', 'depth'],
callback=on_market_data
)
3.2 Tick数据解析:最原始的「心跳」
Tick 数据是什么?就是交易所每笔成交的「快照」。它包含了价格、成交量、买卖盘口等信息。每一笔 Tick,都是市场的一次心跳。
我曾经在解析 Tick 时踩过一个坑:某家数据商的 Tick 时间戳是本地时间,不是交易所时间。结果我拿来做回测,策略信号全偏了。所以,拿到 Tick 后第一件事,检查时间戳的时区。
避坑指南:
我曾经因为 Tick 数据中的「成交量」是累计值还是单笔值没搞清楚,导致策略开仓逻辑完全错误。一定要确认数据字段的定义。
一个标准的 Tick 数据结构通常包含以下字段:
| 字段名 | 含义 | 示例值 |
|---|---|---|
| symbol | 合约代码 | IF2409 |
| timestamp | 时间戳(毫秒) | 1693123456789 |
| last_price | 最新成交价 | 3720.5 |
| volume | 成交量(累计) | 123456 |
| bid_price | 买一价 | 3720.2 |
| ask_price | 卖一价 | 3720.8 |
小技巧:解析 Tick 时,建议用结构体或命名元组,别用字典。字典的哈希查找在高频场景下太慢了。
3.3 K线数据合成:从「心跳」到「心电图」
Tick 数据太细碎了,就像心跳的每一次搏动。而 K 线,就像是心电图——它把一段时间内的搏动归纳成几个关键指标:开盘、收盘、最高、最低。
K 线合成的逻辑其实不复杂,但细节决定成败。我见过有人直接用 Tick 的最后一笔价格当收盘价,结果遇到最后一笔是异常单,整个 K 线都废了。
标准的合成逻辑是这样的:
- 收到第一个 Tick,记录开盘价
- 每个 Tick 更新最高价和最低价
- 最后一个 Tick 的价格作为收盘价
- 成交量累加
class KLineBuilder:
def __init__(self, period=60):
self.period = period # 60秒K线
self.reset()
def reset(self):
self.open = 0
self.high = 0
self.low = float('inf')
self.close = 0
self.volume = 0
self.count = 0
def update(self, tick):
if self.count == 0:
self.open = tick.last_price
self.high = max(self.high, tick.last_price)
self.low = min(self.low, tick.last_price)
self.close = tick.last_price
self.volume += tick.volume
self.count += 1
注意:合成 K 线时,一定要处理「跨周期」的情况。比如 60 秒 K 线,第 59 秒的 Tick 和第 61 秒的 Tick 不能混在一起。
3.4 实时数据推送架构:让数据「流」起来
数据拿到了,解析好了,K 线也合成了。接下来最关键的一步:怎么把这些数据高效地推送给策略引擎?
我个人比较推崇「发布-订阅」模式。说白了,就是有一个数据总线,谁需要什么数据,自己来订阅。策略模块不需要关心数据从哪里来,只需要关心数据怎么用。
这里我画了一张架构图,帮你理解整个流程:
这张图展示了从数据源到策略引擎的完整链路。你想想看,如果没有这个架构,每个策略都自己去连交易所,那得多乱?
我的经验:数据总线建议用内存队列实现,比如 ZeroMQ 或 Redis Stream。别用数据库,IO 延迟太高了。
3.5 避坑总结
做市场数据这一块,我踩过的坑不少。这里列几个最常见的,你遇到了可以少走弯路:
- 时间戳问题:不同数据源的时间戳格式可能不同,一定要统一成 UTC 毫秒
- 数据对齐:Tick 数据和 K 线数据的时间窗口要对齐,否则策略信号会错位
- 内存管理:Tick 数据量很大,不要全部缓存,用环形缓冲区或滑动窗口
- 异常处理:网络断开、数据缺失、字段异常,都要有兜底逻辑
嗯,市场数据这块就聊到这儿。数据是策略的血液,把这一层做好了,后面的策略开发才会顺畅。