3. 事件类型定义与分类

在事件驱动回测系统中,事件是系统运转的“燃料”。所有逻辑——从数据接收、信号生成、订单执行到账户更新——都通过事件来触发和传递。一个清晰、完备的事件类型体系,决定了回测框架的扩展性与可维护性。本章将定义五大核心事件类型:市场事件、订单事件、信号事件、账户事件和定时事件,并给出标准化的数据结构。

3.1 事件分类总览

事件类型按照其产生来源与消费用途进行划分。下表列出了五种事件的核心特征与典型用途:

事件类型 产生源 主要消费者 触发时机 典型用途
市场事件 数据馈送模块 策略模块、风控模块 新行情数据到达 驱动策略计算、更新市场状态
信号事件 策略模块 订单管理模块、风控模块 策略逻辑产生买卖意向 传递交易信号,触发订单生成
订单事件 订单管理模块 执行模拟器、风控模块 订单状态变更(创建、成交、撤销) 跟踪订单生命周期,更新持仓
账户事件 执行模拟器/风控 绩效模块、账户管理器 持仓、资金、保证金变动 更新账户状态,计算盈亏
定时事件 定时器模块 策略模块、调度器 预设时间点或间隔到达 执行定期任务(如再平衡、收盘处理)
设计原则: 事件类型应保持正交性,即一个事件只描述一类事实。例如,市场事件只携带行情数据,不携带交易决策;订单事件只描述订单状态变化,不包含策略逻辑。这种分离使得每个模块可以独立演进。

3.2 事件基类与通用结构

所有事件都继承自一个抽象基类 Event,包含两个通用字段:

from dataclasses import dataclass, field
from datetime import datetime
from enum import Enum, auto
from typing import Any, Dict

class EventType(Enum):
    MARKET = auto()
    SIGNAL = auto()
    ORDER = auto()
    ACCOUNT = auto()
    TIMER = auto()

@dataclass
class Event:
    """事件基类"""
    event_type: EventType          # 事件类型枚举
    timestamp: datetime            # 事件发生时间(交易所时间)
    source: str = ""               # 事件来源模块标识
    metadata: Dict[str, Any] = field(default_factory=dict)  # 扩展元数据

timestamp 字段是回测时间轴的核心锚点,所有事件处理必须严格按时间顺序进行。metadata 字典用于携带调试信息或模块间传递的临时数据,不参与核心业务逻辑。

3.3 市场事件 (MarketEvent)

市场事件是回测系统中最基础、最频繁的事件。它代表一个新的市场数据点(如 tick、1分钟K线、5分钟K线)到达系统。市场事件驱动策略模块进行判断,是“事件驱动”一词的直接体现。

@dataclass
class MarketEvent(Event):
    """市场事件:携带新的行情数据"""
    symbol: str                    # 交易标的代码
    price: float                   # 最新成交价(或K线收盘价)
    volume: float                  # 成交量
    bid: float = 0.0               # 买一价(tick级别)
    ask: float = 0.0               # 卖一价(tick级别)
    open_interest: float = 0.0     # 持仓量(期货/期权)
    # 扩展字段:可携带K线OHLCV结构
    ohlcv: Dict[str, float] = field(default_factory=dict)

    def __post_init__(self):
        self.event_type = EventType.MARKET
关键设计点:
  • 粒度无关: 无论是 tick 还是日线,都使用同一个 MarketEvent 结构,通过 ohlcv 字段承载不同粒度的数据。
  • 买卖价差: bid 和 ask 字段在 tick 级别数据中填充,用于模拟滑点和限价单成交逻辑。
  • 时间戳精度: 回测中必须使用交易所时间戳,而非系统时间,以保证回放顺序的准确性。

3.4 信号事件 (SignalEvent)

当策略模块接收到市场事件并完成计算后,如果产生交易意向,就会生成一个信号事件。信号事件是策略逻辑的输出,它不包含具体的订单指令,而是表达“我想做什么”。

@dataclass
class SignalEvent(Event):
    """信号事件:策略产生的交易意向"""
    symbol: str                    # 交易标的
    direction: str                 # 'BUY' 或 'SELL'
    strength: float = 1.0          # 信号强度(0~1),用于仓位管理
    signal_id: str = ""            # 信号唯一标识,用于追踪
    strategy_name: str = ""        # 产生信号的策略名称(多策略场景)

    def __post_init__(self):
        self.event_type = EventType.SIGNAL
常见误区: 不要在信号事件中携带目标价格或数量。信号只表达方向与强度,具体的订单参数(价格、数量、订单类型)应由订单管理模块根据风控规则和仓位管理策略决定。这实现了“策略逻辑”与“执行逻辑”的解耦。

3.5 订单事件 (OrderEvent)

订单事件是回测系统中状态最复杂的事件。它描述了订单从创建到最终状态(成交、撤销、拒绝)的完整生命周期。订单事件由订单管理模块生成,并被执行模拟器消费。

class OrderStatus(Enum):
    CREATED = auto()       # 已创建
    SUBMITTED = auto()     # 已提交到交易所
    PARTIALLY_FILLED = auto()  # 部分成交
    FILLED = auto()        # 全部成交
    CANCELLED = auto()     # 已撤销
    REJECTED = auto()      # 被拒绝

class OrderType(Enum):
    MARKET = auto()        # 市价单
    LIMIT = auto()         # 限价单
    STOP = auto()          # 止损单
    STOP_LIMIT = auto()    # 止损限价单

@dataclass
class OrderEvent(Event):
    """订单事件:描述订单状态变化"""
    order_id: str                  # 订单唯一编号
    symbol: str                    # 交易标的
    order_type: OrderType          # 订单类型
    direction: str                 # 'BUY' 或 'SELL'
    quantity: float                # 委托数量
    price: float = 0.0             # 限价/触发价(市价单为0)
    status: OrderStatus = OrderStatus.CREATED  # 当前状态
    filled_quantity: float = 0.0   # 已成交数量
    avg_fill_price: float = 0.0    # 平均成交价格
    commission: float = 0.0        # 手续费
    reason: str = ""               # 拒绝/撤销原因

    def __post_init__(self):
        self.event_type = EventType.ORDER

订单事件的生命周期流转如下图所示:

订单事件生命周期状态机 CREATED SUBMITTED PARTIALLY_FILLED FILLED CANCELLED REJECTED 继续成交 撤销 拒绝 说明:每个状态变更都会生成一个新的 OrderEvent,事件 ID 相同但 status 字段不同

3.6 账户事件 (AccountEvent)

账户事件用于通知系统账户状态的变化。当订单成交、资金划转、分红或保证金调整发生时,系统生成账户事件,绩效模块和风控模块据此更新账户快照。

@dataclass
class AccountEvent(Event):
    """账户事件:账户资金/持仓变动"""
    account_id: str                # 账户编号
    cash_balance: float            # 当前现金余额
    total_equity: float            # 总权益(现金 + 持仓市值)
    unrealized_pnl: float = 0.0    # 未实现盈亏
    realized_pnl: float = 0.0      # 已实现盈亏
    margin_used: float = 0.0       # 已占用保证金
    positions: Dict[str, float] = field(default_factory=dict)  # 持仓快照 {symbol: quantity}
    change_reason: str = ""        # 变动原因(如 'FILL', 'DIVIDEND', 'FEE')

    def __post_init__(self):
        self.event_type = EventType.ACCOUNT
性能优化: 账户事件不需要在每个订单事件后都生成。通常只在订单完全成交(FILLED)或部分成交(PARTIALLY_FILLED)时生成一次,避免高频更新带来的性能开销。对于日内高频回测,建议将账户快照的更新频率控制在每笔成交一次。

3.7 定时事件 (TimerEvent)

定时事件是回测系统中唯一不直接由市场数据驱动的事件类型。它由定时器模块在预设的时间点触发,用于执行与行情无关的定期任务。

@dataclass
class TimerEvent(Event):
    """定时事件:按预设时间触发"""
    timer_id: str                  # 定时器标识
    interval_seconds: float        # 触发间隔(秒)
    repeat_count: int = -1         # 重复次数(-1表示无限重复)
    current_count: int = 0         # 当前已触发次数
    user_data: Any = None          # 用户自定义数据

    def __post_init__(self):
        self.event_type = EventType.TIMER

定时事件的典型应用场景包括:

  • 收盘处理: 在交易日收盘前5分钟触发,执行平仓或调整操作。
  • 定期再平衡: 每月第一个交易日触发,执行投资组合再平衡。
  • 风控检查: 每隔30分钟触发一次,检查持仓集中度或杠杆率。
  • 数据快照: 每日收盘后触发,保存账户状态快照用于绩效分析。

3.8 事件类型关系图

下图展示了五种事件类型在回测系统中的流转关系与模块边界:

事件驱动回测系统——事件流转架构 数据馈送模块 (市场数据源) 策略模块 (信号生成) 订单管理模块 (订单生命周期) 执行模拟器 (撮合/滑点) 账户/绩效模块 (状态更新) 风控模块 (检查/拦截) 市场事件 信号事件 订单事件 订单事件 账户事件 定时事件 → 所有模块

3.9 事件处理优先级与顺序

在事件驱动回测中,同一时间戳可能同时到达多个事件。为了保证回测的确定性,必须定义事件处理优先级:

  1. 定时事件(最高优先级):确保定期任务在行情处理前执行。
  2. 市场事件:驱动策略计算,产生信号。
  3. 信号事件:由策略产生后立即处理,生成订单。
  4. 订单事件:订单状态变更,触发执行模拟。
  5. 账户事件(最低优先级):在订单处理完成后更新账户状态。
实现要点: 事件队列应支持优先级排序。在 Python 中,可以使用 heapq 或 PriorityQueue 实现,以 (timestamp, priority, sequence_id) 作为排序键,确保相同时间戳下的事件按预定顺序处理。

3.10 扩展性设计

上述事件类型定义并非一成不变。在实际项目中,你可能需要扩展:

  • 自定义事件: 继承 Event 基类,添加业务特定字段(如新闻事件、宏观经济数据事件)。
  • 事件过滤器: 在事件总线上添加过滤器,允许模块只订阅感兴趣的事件类型。
  • 事件序列化: 为事件添加 to_dict() / from_dict() 方法,支持回测结果的序列化存储与复盘。
@dataclass
class NewsEvent(Event):
    """扩展示例:新闻事件"""
    headline: str
    sentiment_score: float  # -1.0 ~ 1.0
    source: str

    def __post_init__(self):
        self.event_type = EventType.MARKET  # 复用市场事件类型,或新增枚举值

通过这种清晰的事件分类与数据结构设计,回测系统的各个模块可以像乐高积木一样组合与替换,而事件总线则成为连接所有模块的“神经系统”。