一、事件驱动回测概述
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(成交时间)的差值,该差值即为标签口径偏差的核心来源。