一、事件驱动交易系统概述
大家好,我是你们的讲师。今天咱们聊聊事件驱动交易系统。说实话,这个架构是我在量化领域摸爬滚打多年后,觉得最实用的一套体系。
先问个问题:你平时写策略,是不是经常遇到这种情况——行情数据来了,你得轮询检查有没有信号?或者,多个策略同时跑,互相抢资源?
嗯,这就是典型的「轮询模式」的痛点。而事件驱动,说白了就是「有事才干活,没事就歇着」。系统不会傻傻地一直问「有没有新数据?」,而是等数据来了,系统自动被「唤醒」。
核心思想:系统对外部事件做出响应,而不是主动轮询。这就像你坐在工位上,不是每秒钟都去翻邮件,而是等邮件通知响了再处理。
什么是事件驱动架构
事件驱动架构,本质上是一种「发布-订阅」模式。我习惯把它拆成三个角色:
- 事件生产者——谁产生了这个事件?比如行情网关、订单回报、定时器
- 事件通道——事件怎么传递?通常是消息队列或事件总线
- 事件消费者——谁处理这个事件?比如策略模块、风控模块、记录模块
举个例子。你在写一个简单的均线策略。传统做法是:
while True:
data = fetch_latest_price()
if data is not None:
check_signal(data)
time.sleep(0.1) # 轮询间隔
事件驱动下,代码会变成这样:
def on_tick(tick_data):
# 行情来了才执行
check_signal(tick_data)
# 注册监听
event_bus.subscribe('tick', on_tick)
看出来区别了吗?前者是「主动问」,后者是「被动等通知」。我在项目中遇到过,用轮询方式跑10个策略,CPU直接飙到80%。换成事件驱动后,降到15%左右。你想想看,这差距有多大。
交易系统中的事件类型
交易系统里的事件,我大致分成这么几类。嗯,这里我列个表,方便你对照:
| 事件类型 | 说明 | 典型来源 |
|---|---|---|
| 行情事件 | Tick、K线、深度数据 | 交易所行情推送 |
| 订单事件 | 委托回报、成交回报、撤单回报 | 交易网关 |
| 账户事件 | 资金变动、持仓变动、冻结/解冻 | 风控模块/结算模块 |
| 定时事件 | 开盘、收盘、定时任务、定时检查 | 系统定时器 |
| 系统事件 | 启动、停止、异常、重连 | 系统管理器 |
| 策略事件 | 信号产生、策略启动/停止、参数变更 | 策略引擎 |
我个人习惯把事件按「优先级」再分一层。比如行情事件,如果系统处理不过来,可以丢弃一些低优先级的Tick,但订单事件绝对不能丢。我曾经因为没区分优先级,导致一次撤单事件被延迟处理,结果多亏了5000块。从那以后,我就在事件总线上加了优先级队列。
小技巧:事件类型建议用枚举或常量定义,不要用字符串硬编码。我见过有人用"tick"、"Tick"、"TICK"三种写法,排查问题排查到崩溃。
系统核心组件概览
一个完整的事件驱动交易系统,核心组件大概有这些。我画了张图,你先看看整体结构:
从上图你能看到,整个系统是「松耦合」的。每个组件只关心自己订阅的事件,不关心事件是谁发的。我简单介绍下每个组件:
- 行情网关:负责对接交易所,把原始行情数据转成内部事件。我建议这里做一层「标准化」,因为不同交易所的Tick格式差异很大。
- 交易网关:负责下单、撤单、查询持仓。它会监听来自策略引擎的「下单事件」,然后转成交易所协议发出去。
- 事件总线:整个系统的「血管」。所有事件都经过它分发。我个人喜欢用内存队列实现,延迟低。如果要做分布式,可以换成Kafka或RabbitMQ。
- 策略引擎:你的策略代码跑在这里。它订阅行情事件,计算信号,然后发出「下单事件」。
- 风控模块:这是安全阀。所有下单事件必须先经过风控检查。我曾经因为没加风控,策略在极端行情下连续开仓,差点爆仓。
- 日志记录:所有事件都应该落盘。别小看这个,出问题排查全靠它。
- 监控告警:系统异常时通知你。比如断线、风控触发、策略异常等。
避坑指南:事件总线的设计一定要考虑「背压」问题。如果行情来得太快,消费者处理不过来,事件会堆积。我曾经因为没做背压控制,内存直接爆了。建议用有界队列,满了就丢弃旧事件或阻塞生产者。
好了,这一章就到这里。事件驱动架构的核心思想其实很简单:让系统「等通知」,而不是「主动问」。后面的章节,我会带你一步步实现这些组件。