2. 回测系统架构设计:核心模块、数据流与事件驱动

做市策略的回测,说白了就是一场「模拟交易游戏」。

你想想看,我们不可能拿真金白银去测试一个还没验证过的策略。所以,回测系统就是我们的「虚拟战场」。我个人习惯把回测系统拆成三个核心部分:数据层、逻辑层、执行层。今天我们就来聊聊,怎么搭这个架子。

2.1 核心模块:回测系统的「三驾马车」

一个完整的回测系统,至少需要这三个模块协同工作。我在项目中遇到过不少同学,只关注策略逻辑,忽略了数据管理和执行引擎,结果回测结果跟实盘差得十万八千里。

核心模块一览:

  • 数据管理模块:负责历史数据的加载、清洗、对齐。包括行情数据(Tick、1分钟、日线)和订单簿数据。
  • 策略引擎模块:你的做市策略逻辑就写在这里。比如报价生成、库存管理、风险控制。
  • 执行与撮合模块:模拟订单簿,处理你的挂单、撤单、成交。这是回测最核心也最容易出错的地方。

这三个模块的关系,我画了一张图,你可以直观感受一下数据是怎么流动的:

回测系统核心数据流 数据管理模块 行情数据 · 订单簿 数据清洗 · 时间对齐 策略引擎模块 报价生成 · 库存管理 风险控制 · 信号计算 执行与撮合模块 订单簿模拟 · 撮合 滑点模拟 · 费用计算 行情推送 订单指令 成交回报 / 订单状态 数据流方向:历史数据 → 策略计算 → 订单执行 → 结果反馈 数据层 逻辑层 执行层

2.2 数据流设计:别让数据「卡住」

数据流设计,是回测系统的「血管」。数据流不通,策略跑得再漂亮也是白搭。

我一般把数据流分为三个阶段:

  1. 数据预处理阶段:原始数据进来,先做清洗。比如去掉停牌期间的数据、填充缺失值、对齐时间戳。我曾经因为没对齐时间戳,导致策略在模拟中「预知未来」,回测收益高得离谱,实盘直接亏哭。
  2. 策略计算阶段:清洗后的数据按时间顺序喂给策略引擎。策略引擎根据当前市场状态(价差、深度、库存)决定要不要报价、报什么价。
  3. 结果回写阶段:成交记录、订单状态、账户资金变化,全部写回数据库或内存中,供后续分析。

我的小技巧:数据流设计时,尽量保持「单向流动」。数据从数据源到策略,再到执行,最后到结果存储。不要搞成循环依赖,否则调试起来你会想砸电脑。

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()。否则回测速度慢得离谱,而且无法保证时序一致性。
  • 订单簿模拟要够「真」:简单的「看到买一卖一就成交」是不够的。要考虑滑点、部分成交、撤单延迟。我见过有人回测时每笔都按最优价成交,实盘时发现根本抢不到单。
  • 资金管理要严谨:做市策略涉及频繁挂单,保证金占用、手续费、库存成本都要算清楚。否则回测收益曲线漂亮,实盘却因为保证金不足爆仓。

嗯,架构设计就聊到这里。记住一句话:回测系统的架构,决定了你能走多远。一个设计良好的架构,能让你快速迭代策略;一个糟糕的架构,会让你在调试中耗尽耐心。


专注资料整理