2. 回测系统架构设计:核心模块、数据流与事件驱动
做市策略的回测,说白了就是一场「模拟交易游戏」。
你想想看,我们不可能拿真金白银去测试一个还没验证过的策略。所以,回测系统就是我们的「虚拟战场」。我个人习惯把回测系统拆成三个核心部分:数据层、逻辑层、执行层。今天我们就来聊聊,怎么搭这个架子。
2.1 核心模块:回测系统的「三驾马车」
一个完整的回测系统,至少需要这三个模块协同工作。我在项目中遇到过不少同学,只关注策略逻辑,忽略了数据管理和执行引擎,结果回测结果跟实盘差得十万八千里。
核心模块一览:
- 数据管理模块:负责历史数据的加载、清洗、对齐。包括行情数据(Tick、1分钟、日线)和订单簿数据。
- 策略引擎模块:你的做市策略逻辑就写在这里。比如报价生成、库存管理、风险控制。
- 执行与撮合模块:模拟订单簿,处理你的挂单、撤单、成交。这是回测最核心也最容易出错的地方。
这三个模块的关系,我画了一张图,你可以直观感受一下数据是怎么流动的:
2.2 数据流设计:别让数据「卡住」
数据流设计,是回测系统的「血管」。数据流不通,策略跑得再漂亮也是白搭。
我一般把数据流分为三个阶段:
- 数据预处理阶段:原始数据进来,先做清洗。比如去掉停牌期间的数据、填充缺失值、对齐时间戳。我曾经因为没对齐时间戳,导致策略在模拟中「预知未来」,回测收益高得离谱,实盘直接亏哭。
- 策略计算阶段:清洗后的数据按时间顺序喂给策略引擎。策略引擎根据当前市场状态(价差、深度、库存)决定要不要报价、报什么价。
- 结果回写阶段:成交记录、订单状态、账户资金变化,全部写回数据库或内存中,供后续分析。
我的小技巧:数据流设计时,尽量保持「单向流动」。数据从数据源到策略,再到执行,最后到结果存储。不要搞成循环依赖,否则调试起来你会想砸电脑。
2.3 事件驱动架构:让系统「活」起来
做市策略的回测,跟普通策略不太一样。普通策略可能是「每天收盘后计算信号,第二天开盘执行」。但做市策略是高频的,每一笔Tick进来,都可能触发报价更新。
所以,我们需要事件驱动架构。说白了,就是系统不主动干活,而是等「事件」来了再响应。
常见的回测事件类型:
| 事件类型 | 触发时机 | 处理内容 |
|---|---|---|
| MarketDataEvent | 新的Tick或K线到达 | 更新市场状态,触发策略报价计算 |
| OrderEvent | 策略发出挂单/撤单指令 | 将订单提交给撮合引擎 |
| FillEvent | 订单被成交 | 更新持仓、资金、统计信息 |
| TimerEvent | 定时器触发(如每秒) | 执行定期任务(如风险检查) |
事件驱动架构的核心,是一个事件循环。我习惯用Python的queue.Queue来实现:
import queue
from enum import Enum
class EventType(Enum):
MARKET_DATA = 1
ORDER = 2
FILL = 3
TIMER = 4
class Event:
def __init__(self, event_type, data):
self.type = event_type
self.data = data
class EventEngine:
def __init__(self):
self.queue = queue.Queue()
self.handlers = {}
def register_handler(self, event_type, handler):
if event_type not in self.handlers:
self.handlers[event_type] = []
self.handlers[event_type].append(handler)
def put(self, event):
self.queue.put(event)
def run(self):
while True:
event = self.queue.get()
if event.type == EventType.MARKET_DATA:
# 处理行情事件
for handler in self.handlers.get(EventType.MARKET_DATA, []):
handler(event.data)
elif event.type == EventType.ORDER:
# 处理订单事件
for handler in self.handlers.get(EventType.ORDER, []):
handler(event.data)
# ... 其他事件类型
注意:事件驱动架构虽然灵活,但要注意事件处理的「顺序性」。做市策略对时序极其敏感,如果事件处理顺序乱了,回测结果就废了。我曾经因为多线程处理事件,导致成交顺序错乱,查了三天bug才发现。
2.4 架构设计的「避坑指南」
最后,分享几个我踩过的坑:
- 不要用真实时间做回测:回测必须用历史数据的时间戳驱动,不能用
time.sleep()。否则回测速度慢得离谱,而且无法保证时序一致性。 - 订单簿模拟要够「真」:简单的「看到买一卖一就成交」是不够的。要考虑滑点、部分成交、撤单延迟。我见过有人回测时每笔都按最优价成交,实盘时发现根本抢不到单。
- 资金管理要严谨:做市策略涉及频繁挂单,保证金占用、手续费、库存成本都要算清楚。否则回测收益曲线漂亮,实盘却因为保证金不足爆仓。
嗯,架构设计就聊到这里。记住一句话:回测系统的架构,决定了你能走多远。一个设计良好的架构,能让你快速迭代策略;一个糟糕的架构,会让你在调试中耗尽耐心。