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 事件分类的核心逻辑
说了这么多,我们来总结一下事件分类的核心逻辑。我画了一张图,帮你理清思路:
这张图展示了事件分类的核心逻辑。你看,所有事件都通过事件总线进行分发,但每一类事件都有自己独立的处理管道。这样做的好处是:高内聚、低耦合。市场数据事件处理得再快,也不会影响到订单事件的处理。
总结一下:事件分类不是死板的教条,而是为了让你更好地组织代码。我的建议是:先按这五大类分好,等系统跑起来之后,再根据实际需求调整。记住,好的架构是「长」出来的,不是「设计」出来的。