4. 事件类型与分类:市场数据事件、订单事件、风控事件、系统事件、自定义事件

事件驱动架构的核心,说白了就是「万物皆事件」。你想想看,交易系统里发生的每一件事——行情来了、订单成交了、风控触发了——都可以抽象成一个事件对象。我做了这么多年量化系统,发现很多新手最容易犯的错误,就是把事件处理写得乱七八糟,最后系统一跑就崩。

今天我们就来聊聊,事件到底该怎么分类。我个人习惯把事件分成五大类,每一类都有它自己的脾气和用法。

4.1 市场数据事件

这类事件是最频繁的,也是系统压力的主要来源。市场数据事件包括:

  • Tick事件:每笔成交的细节,价格、数量、时间戳
  • Bar事件:K线数据,Open/High/Low/Close/Volume
  • 深度事件:买卖盘口的变动,Level2数据
  • 行情快照:某个时间点的全市场状态

我在项目中遇到过一个问题:Tick事件来得太快,每秒几千笔,结果事件队列直接撑爆了内存。后来怎么解决的?嗯,加了个采样器,把高频Tick聚合成低频事件再处理。

核心要点:市场数据事件是系统的「燃料」,但也是「毒药」。处理不好,系统会先于市场崩溃。

# 一个简单的Tick事件定义
@dataclass
class TickEvent:
    symbol: str
    price: float
    volume: int
    timestamp: int
    event_type: str = "TICK"

4.2 订单事件

订单事件是交易系统的「肌肉」,负责执行你的交易决策。这类事件包括:

  • 订单创建:新订单提交到交易所
  • 订单确认:交易所返回的确认消息
  • 订单成交:部分或全部成交
  • 订单拒绝:交易所拒绝了你的订单
  • 订单撤销:主动取消订单

这里有个坑,我曾经踩过:订单确认和订单成交是两个不同的事件,但很多人把它们混为一谈。结果呢?系统以为订单没成交,又发了一个重复订单,导致双倍仓位。嗯,血的教训。

避坑指南:我曾经因为订单状态机设计不严谨,导致生产环境出现「幽灵订单」。后来我强制要求每个订单事件都必须携带一个唯一的OrderID,并且状态转换必须严格遵循有限状态机模型。

# 订单事件的状态机
class OrderStatus(Enum):
    PENDING = "pending"
    CONFIRMED = "confirmed"
    PARTIAL_FILLED = "partial_filled"
    FILLED = "filled"
    CANCELLED = "cancelled"
    REJECTED = "rejected"

4.3 风控事件

风控事件是系统的「刹车片」。没有它,你的交易策略可能会把账户亏光。风控事件包括:

  • 仓位超限:某个品种的持仓超过了预设上限
  • 资金不足:账户余额不足以开新仓
  • 频率异常:交易频率超过了风控阈值
  • 策略异常:策略产生了不合逻辑的信号
  • 连接断开:与交易所的连接中断

我个人习惯把风控事件分成两个等级:警告熔断。警告事件只记录日志,熔断事件会直接停止交易。你想想看,如果每次风控都直接熔断,那系统就没法正常运作了。

小技巧:风控事件一定要有「降级」机制。比如连接断开时,先尝试重连3次,如果还不行,再触发熔断。我见过太多系统因为一次网络抖动就全线停摆的案例了。

4.4 系统事件

系统事件是交易系统的「骨架」,负责管理系统的生命周期。这类事件包括:

  • 系统启动:引擎初始化完成
  • 系统关闭:引擎正在优雅关闭
  • 心跳事件:定时触发的健康检查
  • 配置变更:运行时修改了配置参数
  • 日志轮转:日志文件需要切换

系统事件虽然不直接参与交易,但它们是系统稳定运行的保障。我记得有一次,系统因为日志文件写满了磁盘,导致所有交易都停了。从那以后,我就在系统事件里加了一个「磁盘空间预警」事件。

4.5 自定义事件

这是最灵活的一类事件。每个交易系统都有自己的特殊需求,自定义事件就是用来满足这些需求的。比如:

  • 策略信号事件:策略生成的买卖信号
  • 统计指标事件:计算出的技术指标值
  • 人工干预事件:交易员手动触发的操作
  • 回测结束事件:回测引擎完成了一次回测

自定义事件的设计原则是:不要过度设计。我见过有人把每个变量变化都定义成一个事件,结果事件类型多达几百种,维护起来简直噩梦。我的建议是:只定义那些需要被多个模块消费的事件。

# 自定义事件示例
@dataclass
class StrategySignalEvent:
    strategy_id: str
    symbol: str
    action: str  # "BUY" or "SELL"
    quantity: int
    price: float
    timestamp: int
    event_type: str = "STRATEGY_SIGNAL"

4.6 事件分类的核心逻辑

说了这么多,我们来总结一下事件分类的核心逻辑。我画了一张图,帮你理清思路:

事件驱动交易引擎 - 事件分类架构 事件总线 市场数据事件 Tick事件 Bar事件 深度事件 行情快照 订单事件 订单创建 订单确认 订单成交 订单拒绝/撤销 风控事件 仓位超限 资金不足 频率异常 连接断开 系统事件 系统启动/关闭 心跳事件 配置变更 日志轮转 自定义事件(策略信号、统计指标、人工干预等) 事件分类的核心原则:高内聚、低耦合、可扩展 每一类事件都有独立的处理管道,互不干扰

这张图展示了事件分类的核心逻辑。你看,所有事件都通过事件总线进行分发,但每一类事件都有自己独立的处理管道。这样做的好处是:高内聚、低耦合。市场数据事件处理得再快,也不会影响到订单事件的处理。

总结一下:事件分类不是死板的教条,而是为了让你更好地组织代码。我的建议是:先按这五大类分好,等系统跑起来之后,再根据实际需求调整。记住,好的架构是「长」出来的,不是「设计」出来的。


专注资料整理