一、事件驱动回测引擎:让策略自己跑起来
做量化交易这些年,我见过太多人把回测想简单了。
很多人以为回测就是「把历史数据扔进去,看赚了多少」。其实不然。真正的回测引擎,核心是事件驱动。说白了,就是模拟真实市场的运行方式——市场发生什么,你的策略就响应什么。
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