一、事件驱动回测概述

1.1 事件驱动回测的定义

事件驱动回测(Event-Driven Backtesting)是一种以离散事件为推进核心的回测引擎架构。回测引擎维护一个按时间排序的事件队列,每次从队列中取出最早发生的事件(如行情Tick到达、订单成交、定时器触发),调用对应的回调函数处理该事件,并可能产生新的事件插入队列。整个过程模拟了真实交易环境中“等待-响应”的异步循环。

核心特征:事件循环 + 回调函数 + 时间戳排序。引擎不主动“跳转”时间,而是由事件本身驱动时间向前流动。

1.2 与向量化回测的区别

向量化回测(Vectorized Backtesting)一次性对整个历史价格序列进行矩阵运算,计算所有信号和持仓损益;事件驱动回测则逐笔处理市场数据,模拟订单簿、滑点、成交延迟等微观细节。两者在多个维度存在本质差异:

对比维度 向量化回测 事件驱动回测
推进方式 按时间步长(Bar)批量计算 按事件时间戳逐个处理
订单模拟 假设以收盘价/开盘价成交,无滑点 支持限价单、市价单、部分成交、滑点模型
数据粒度 通常使用日线或分钟线 可处理Tick级逐笔数据
执行速度 极快(矩阵运算) 较慢(逐事件回调)
策略复杂度 适合简单规则策略 适合高频、多资产、条件单策略
标签口径校准 难以精确对齐信号与成交时间 天然支持时间戳级对齐
实战建议:在标签口径校准场景中,向量化回测容易产生“未来信息泄露”——信号在Bar内生成却用Bar收盘价成交。事件驱动回测通过逐笔事件时间戳,能从根本上避免此类偏差。

1.3 适用场景

事件驱动回测并非万能,但在以下场景中具有不可替代的优势:

  • 高频策略回测:需要模拟订单簿深度、逐笔成交、微秒级延迟。
  • 多资产联动策略:不同品种数据频率不一致,需按事件时间戳统一对齐。
  • 条件单/止损单/冰山单:订单状态变化本身即为事件,需实时触发后续逻辑。
  • 标签口径校准:需要精确记录“信号生成时间戳”与“实际成交时间戳”的偏差,用于标签修正。
  • 仿真交易对接:回测引擎与实盘交易接口共用同一事件循环,降低代码迁移成本。

1.4 事件驱动回测核心架构

下图展示了一个典型的事件驱动回测引擎的组件关系与数据流:

事件驱动回测引擎核心架构 事件驱动回测引擎核心架构 历史数据源 事件队列 策略模块 行情事件 分发事件 订单管理 下单指令 成交引擎 订单路由 成交回报事件 标签校准模块 成交明细 说明:事件队列是整个引擎的“心脏”,所有模块通过事件解耦,支持异步回放与时间戳级标签对齐

1.5 事件类型与生命周期

在事件驱动回测中,事件是引擎运转的基本单元。常见的事件类型包括:

  • 市场数据事件(MarketDataEvent):Tick、Bar、订单簿快照到达。
  • 信号事件(SignalEvent):策略模块计算出的买卖信号。
  • 订单事件(OrderEvent):向成交引擎提交的限价单/市价单。
  • 成交事件(FillEvent):订单部分或全部成交的回报。
  • 定时事件(TimerEvent):用于定期执行的风控检查、仓位再平衡。
常见陷阱:事件处理顺序必须严格按时间戳排序。若同一时间戳存在多个事件,需定义优先级(如先处理成交回报,再处理新行情),否则会导致标签时间错乱。

1.6 事件驱动回测的代码骨架

以下是一个简化的事件循环核心代码,展示了事件队列的推进逻辑:

class EventDrivenBacktest:
    def __init__(self):
        self.events = []          # 事件队列(按时间戳排序)
        self.strategy = None
        self.portfolio = None
    
    def run(self):
        while self.events:
            # 取出最早事件
            event = heapq.heappop(self.events)
            # 更新时间指针
            self.current_time = event.timestamp
            # 分发事件
            if event.type == 'MARKET':
                self.strategy.on_market_data(event)
            elif event.type == 'SIGNAL':
                self.portfolio.on_signal(event)
            elif event.type == 'FILL':
                self.portfolio.on_fill(event)
            # 新事件可能被插入队列
            # 循环继续
标签校准关键点:在 on_fill() 回调中,记录 signal_timestamp(信号生成时间)与 fill_timestamp(成交时间)的差值,该差值即为标签口径偏差的核心来源。