一、事件驱动回测引擎:让策略自己跑起来

做量化交易这些年,我见过太多人把回测想简单了。

很多人以为回测就是「把历史数据扔进去,看赚了多少」。其实不然。真正的回测引擎,核心是事件驱动。说白了,就是模拟真实市场的运行方式——市场发生什么,你的策略就响应什么。

1.1 事件驱动 vs 向量化回测

先说说两种主流回测方式:

  • 向量化回测:一次性计算所有信号。快,但太理想化。不考虑交易成本、滑点、订单排队。
  • 事件驱动回测:逐笔模拟。市场来了一个 tick,引擎就触发一次事件。你的策略收到事件,决定要不要下单。

我个人习惯用事件驱动。为什么?因为它更接近实盘。你想想看,实盘里你能提前知道明天的收盘价吗?不能。事件驱动回测也不会让你知道。

核心原则:回测时不知道未来,实盘时才能不翻车。

1.2 事件驱动引擎的骨架

一个最小可用的回测引擎,至少包含这几个组件:

  • 事件队列:先进先出,按时间顺序处理
  • 数据源:喂给引擎历史行情
  • 策略模块:收到事件后产生信号
  • 订单管理:处理下单、撤单、成交
  • 持仓管理:记录当前持仓
  • 资金管理:计算可用资金、保证金

我刚开始写回测引擎时,犯过一个低级错误——把订单管理和资金管理混在一起。结果算资金时总出错。后来我学乖了:各模块职责要清晰,别搞大杂烩

二、订单管理:别让订单「飞」了

订单管理是回测引擎里最容易出bug的地方。我在项目中遇到过,订单状态机没写好,导致同一个订单被成交了两次。嗯,那天的回测结果直接翻倍了,我还以为自己发现了圣杯。

2.1 订单的生命周期

一个订单从出生到死亡,大概经历这些状态:

创建 → 提交 → 部分成交 → 完全成交
                → 撤单 → 已撤销
                → 拒单 → 已拒绝

每个状态转换都要记录时间戳。为什么?因为回测结束后你要复盘,得知道订单在哪个环节卡住了。

2.2 订单类型支持

我个人建议至少支持这三种:

订单类型 说明 回测注意事项
市价单 立即成交,不挑价格 滑点模型要合理,别用最优价成交
限价单 指定价格,不到不成交 要考虑部分成交的情况
止损单 触发后变市价单 触发价和成交价可能不同

小技巧:回测时把订单日志打全。每个订单的创建、修改、成交、撤销,都记下来。实盘出问题时,这些日志就是你的救命稻草。

三、持仓管理与资金管理:算清楚每一分钱

持仓管理和资金管理,其实是同一枚硬币的两面。持仓变了,资金一定变。资金不够了,持仓就得平。

3.1 持仓管理要记录什么

  • 持仓方向:多还是空
  • 持仓数量:多少手/股
  • 持仓均价:加权平均成本
  • 浮动盈亏:按最新价算
  • 占用保证金:期货/杠杆产品需要

我曾经在持仓均价的计算上栽过跟头。当时用的简单平均,没考虑不同时间买入的权重。结果平仓时算出来的盈亏全是错的。后来改成加权平均,问题才解决。

3.2 资金管理的核心公式

总资产 = 可用资金 + 持仓市值 + 冻结资金
净值 = 总资产 - 总负债
收益率 = (期末净值 - 期初净值) / 期初净值

这个公式看着简单,但实现时要注意:冻结资金很多人会漏掉。比如你提交了一个买单,资金被冻结了,但还没成交。这时候可用资金要扣掉这部分。

避坑指南:我曾经在回测中忘记处理「撤单后资金解冻」的逻辑。结果策略越跑资金越少,我还以为是亏的,其实是资金被「锁死」了。这个bug查了我整整两天。

四、回测绩效指标:别只看收益率

很多新手回测完只看一个数:总收益率。这是大忌。收益率高不代表策略好,可能是运气好,也可能是风险极高。

4.1 三个核心指标

指标 公式 我的理解
年化收益率 (1+总收益率)^(1/年数)-1 把收益拉到同一时间尺度比较
最大回撤 从峰值跌到谷底的最大幅度 衡量你能承受的最大心理压力
夏普比率 (策略收益率-无风险利率)/波动率 每承担一单位风险,换来多少超额收益

我个人最看重最大回撤。为什么?因为实盘时,回撤超过30%,大部分人就拿不住了。策略再好,拿不住也是白搭。

4.2 夏普比率的坑

夏普比率有个隐含假设:收益率是正态分布的。但金融市场哪有那么乖?我见过一个策略,夏普比率高达3.0,但仔细一看,90%的收益来自一次极端行情。这种策略你敢实盘吗?

我的建议:夏普比率要看,但别迷信。配合最大回撤、胜率、盈亏比一起看,才能全面评估策略。

五、回测中的常见陷阱:你踩过几个?

做回测这么多年,我踩过的坑比吃过的盐还多。下面这两个陷阱,几乎每个量化新手都会遇到。

5.1 前视偏差:最隐蔽的杀手

前视偏差,说白了就是「用未来的数据做今天的决策」。比如:

  • 用当天的收盘价计算当天的信号(收盘价还没出来呢)
  • 用未来数据做归一化(比如用全量数据的均值和标准差)
  • 用复权数据计算买入信号(复权本身包含了未来信息)

我曾经犯过一个经典错误:用当天的最高价和最低价计算当天的波动率指标。回测结果漂亮得不行,实盘直接亏成狗。后来才发现,最高价和最低价是收盘后才能知道的,我提前「看到」了。

避坑指南:写回测代码时,时刻问自己一句:「这个数据在实盘时能拿到吗?」如果答案是否定的,那就是前视偏差。

5.2 幸存者偏差:只看到活下来的

幸存者偏差在选股策略里特别常见。比如你回测「买入过去一年涨幅最大的10只股票」,用的数据是当前还在交易的股票。但那些退市的、暴跌的股票呢?它们已经被剔除了。

结果就是:你的回测只看到了「活下来」的股票,那些「死掉」的股票被自动过滤了。回测收益率自然虚高。

怎么避免?用全量数据,包括已经退市的。虽然数据难找,但这是必须付出的代价。

六、一张图看懂回测引擎

下面这张图,是我自己画的事件驱动回测引擎架构。每次带新人时,我都会先让他们看这张图。

事件驱动回测引擎架构图 数据源 历史行情数据 事件队列 先进先出 策略模块 产生交易信号 订单管理 下单/撤单/成交 成交引擎 模拟撮合 持仓管理 更新持仓 资金管理 行情数据 推送事件 成交回报 更新持仓

这张图的核心逻辑是:数据源产生事件 → 事件队列排队 → 策略处理事件 → 产生订单 → 成交引擎模拟撮合 → 更新持仓和资金。循环往复,直到回测结束。

七、写在最后

回测引擎是量化交易的基石。没有靠谱的回测,实盘就是赌博。我见过太多人,回测做得马马虎虎,实盘亏了才回头找原因。何必呢?

记住三件事:事件驱动模拟真实市场,绩效指标要全面看,陷阱要提前避开。做到这三点,你的回测结果才有参考价值。

嗯,这一章就到这里。下一章我们聊聊策略怎么写,但那是后话了。


公众号:蓝海资料掘金营,微信deep3321