一、事件驱动架构基础

什么是事件驱动架构

先问大家一个问题:你平时写交易程序,是怎么处理行情数据的?

很多人第一反应是写个while循环,每隔几毫秒去查一下最新价格。嗯,这就是典型的轮询模式。但今天我要讲的,是另一种思路——事件驱动。

事件驱动架构,说白了就是:系统不主动去"问"数据来了没有,而是等数据来了"通知"你。就像你等外卖,不用每分钟去门口看一眼,外卖小哥到了会按门铃。

在交易系统里,这个"门铃"就是事件。一个行情更新、一笔成交回报、一条风控告警,都是事件。系统里各个模块都注册好自己关心的事件,事件来了,对应的处理函数自动触发。

核心思想:控制流反转。不是你的代码去调度数据,而是数据来调度你的代码。

我个人习惯把事件驱动架构拆成三个角色:

  • 事件源——谁产生事件?比如交易所推送的行情、订单系统返回的成交
  • 事件通道——事件怎么传递?内存队列、消息中间件、或者共享内存
  • 事件处理器——谁来消费事件?策略模块、风控模块、记录模块

这三个角色解耦之后,系统的扩展性会好很多。我在项目中遇到过,早期用轮询方式写的一个回测框架,想加一个新策略,得改主循环代码。后来改成事件驱动,新策略只需要注册自己关心的事件就行,主流程完全不用动。

事件驱动 vs 轮询

咱们来做个对比。我画了一张图,把两种模式的差异讲清楚。

事件驱动 vs 轮询 对比图 轮询模式 while True: 检查行情是否有更新 如果有 → 处理 如果没有 → sleep(10ms) CPU空转,延迟取决于轮询间隔 事件驱动模式 行情推送 → 产生事件 事件进入队列 事件循环取出事件 调用对应的处理器 有事件才处理,无事件就休眠 vs

从图上能看出来,轮询模式最大的问题是——你永远在"猜"数据什么时候来。猜快了,CPU空转浪费资源;猜慢了,延迟就上去了。

我曾经在一个高频回测项目里做过对比测试。同样的策略逻辑,轮询版本跑完需要47秒,事件驱动版本只用了12秒。为什么差这么多?因为轮询版本大部分时间都在做"检查有没有新数据"这个无用功。

来,看个代码对比,更直观:

# 轮询模式
def polling_loop():
    while True:
        price = get_latest_price()
        if price != last_price:
            on_price_update(price)
            last_price = price
        time.sleep(0.01)  # 10ms轮询间隔

# 事件驱动模式
def event_loop():
    while True:
        event = event_queue.get()  # 阻塞等待
        if event.type == 'PRICE_UPDATE':
            on_price_update(event.data)

我的建议:如果你的系统延迟要求超过100ms,轮询勉强能用。但一旦要求低于10ms,必须上事件驱动。这不是选择题,是生存题。

事件驱动在交易系统中的应用场景

讲完概念,咱们聊聊实战。事件驱动在交易系统里到底怎么用?我归纳了三个最常见的场景。

场景一:行情处理

这是最基础的应用。交易所通过TCP/UDP推送行情,你的系统收到后包装成事件,分发给各个策略模块。

你想想看,如果同时跑10个策略,每个策略都去轮询行情,那得重复查10次。事件驱动模式下,行情只处理一次,然后广播给所有订阅者。

class MarketDataHandler:
    def __init__(self):
        self.subscribers = []
    
    def on_tick(self, tick):
        # 收到行情tick,包装成事件
        event = TickEvent(tick)
        # 广播给所有订阅者
        for sub in self.subscribers:
            sub.on_event(event)

场景二:订单管理

发单之后,成交回报什么时候来?没人知道。可能是1毫秒,也可能是1秒。用轮询去查订单状态?太傻了。

事件驱动模式下,订单系统只管发单,然后等着。交易所返回成交了,触发一个FillEvent,风控模块更新持仓,策略模块调整仓位,一气呵成。

避坑提醒:我曾经在订单状态处理上吃过亏。当时用了一个全局变量记录订单状态,结果并发场景下状态被覆盖了。后来改成事件驱动,每个订单独立的事件流,再也没出过问题。

场景三:风控检查

风控模块需要监控各种指标:持仓上限、撤单频率、资金使用率。这些检查如果放在主循环里做,会拖慢整个系统。

事件驱动模式下,风控模块独立运行,只处理它关心的事件。比如每来一个OrderRequestEvent,风控模块检查一下是否超限,没问题才放行。

事件类型 触发时机 处理模块
TickEvent 行情更新 策略模块、记录模块
OrderEvent 发单请求 风控模块、路由模块
FillEvent 成交回报 持仓模块、策略模块
ErrorEvent 系统异常 告警模块、日志模块

嗯,这里要注意一点:事件驱动不是银弹。如果你的系统逻辑极其简单,比如就一个策略跑一个品种,轮询反而更直接。但一旦涉及多策略、多品种、多模块协作,事件驱动的优势就体现出来了。

一句话总结:事件驱动让系统从"主动问"变成"被动等",从"猜数据"变成"等通知"。延迟更低,资源利用率更高,模块之间更解耦。

我个人做了这么多年交易系统,见过太多团队在架构选型上走弯路。有的上来就上事件驱动,结果业务逻辑还没搞清楚,先被异步回调搞晕了。有的死守轮询,系统延迟高得离谱还不自知。

我的建议是:先理解业务场景,再选架构模式。事件驱动是个好工具,但得用在合适的地方。


专注资料整理