15、策略回测框架:回测引擎设计、订单流数据回放、滑点与手续费模型、回测结果评估指标
回测,说白了就是让策略在历史数据上跑一遍。我见过太多人,策略回测曲线漂亮得像艺术品,一上实盘就崩。为什么?因为回测框架没搭好,忽略了太多细节。今天我就把这块硬骨头啃下来。
15.1 回测引擎的核心设计
回测引擎是整个框架的心脏。我个人习惯把它拆成三个模块:数据层、策略层、执行层。
- 数据层:负责喂数据,包括订单流数据、Tick级数据、K线数据。要支持时间戳对齐,不然回测结果就是错的。
- 策略层:接收数据,生成买卖信号。这里要保证信号生成逻辑和实盘完全一致。
- 执行层:模拟撮合、计算滑点、扣手续费。这是最容易出bug的地方。
我曾经犯过一个低级错误——回测时用了未来数据。策略在T时刻看到了T+1的成交量,结果回测收益翻倍,实盘亏成狗。嗯,从那以后我强制要求所有数据必须按时间顺序逐笔推送。
15.2 订单流数据回放
订单流数据回放,说白了就是模拟交易所的撮合过程。每一笔挂单、撤单、成交都要按时间戳重演。
你想想看,如果只用K线回测,你根本不知道价格是怎么从A点走到B点的。中间可能经历了大单砸盘、流动性枯竭,这些细节K线全丢了。订单流回放能让你看到真实的买卖博弈。
回放的核心逻辑:
- 按时间戳排序,逐笔推送
- 维护一个虚拟的订单簿(Order Book)
- 策略的挂单要插入到订单簿中,等待被吃
- 成交后更新持仓和资金
// 伪代码:订单流回放核心
while (dataStream.hasNext()) {
Event event = dataStream.next();
if (event.type == TRADE) {
// 外部成交,更新订单簿
orderBook.applyTrade(event.price, event.volume);
// 检查策略挂单是否被成交
checkPendingOrders(event.price);
} else if (event.type == ORDER) {
// 外部挂单/撤单
orderBook.applyOrder(event);
}
// 推送数据给策略
strategy.onEvent(event);
}
这里有个坑:回放速度。如果逐笔回放,一天的数据可能要跑几分钟。我建议加一个「加速模式」,跳过无成交的闲时阶段,只保留关键事件。但要注意,加速不能改变事件顺序。
15.3 滑点与手续费模型
滑点和手续费是回测和实盘差距的最大来源。我见过有人回测时设0滑点、0手续费,结果实盘手续费吃掉一半利润。
滑点模型
滑点说白了就是你下单的价格和实际成交价格之间的差价。原因很多:流动性不足、网络延迟、大单冲击。
我个人常用的三种滑点模型:
- 固定滑点:比如每笔交易固定加1个tick。简单粗暴,适合快速验证。
- 比例滑点:按成交金额的千分之几计算。更接近真实情况。
- 基于订单簿的滑点:根据当前盘口的挂单量,模拟你的订单吃掉了多少档位。这个最准,但计算量大。
我的经验:对于高频策略,一定要用基于订单簿的滑点模型。对于中低频,固定滑点加一个安全边际就够了。我曾经用固定滑点回测一个高频策略,结果实盘滑点比回测大了3倍,直接导致策略失效。
手续费模型
手续费各家交易所不一样。有的按成交金额收,有的按成交量收,还有的给VIP折扣。回测时一定要精确模拟。
| 交易所 | Taker费率 | Maker费率 | 备注 |
|---|---|---|---|
| Binance | 0.1% | 0.075% | VIP等级有折扣 |
| OKX | 0.08% | 0.05% | 挂单返佣 |
| Bybit | 0.075% | 0.025% | 合约费率不同 |
注意:有些交易所对Maker单是负费率(倒贴你钱)。如果你策略是挂单型,这部分收入不能忽略。
15.4 回测结果评估指标
回测跑完了,一堆数字摆在你面前。怎么判断策略好不好?光看收益率是远远不够的。
夏普比率
夏普比率衡量的是「每承担一单位风险,能获得多少超额收益」。公式很简单:
夏普比率 = (策略收益率 - 无风险利率) / 收益率标准差
一般来说,夏普比率大于1算及格,大于2算优秀,大于3就是顶级策略了。但我得提醒你,夏普比率对高频策略不太适用——高频策略的收益率分布往往不是正态的,用标准差衡量风险会失真。
最大回撤
最大回撤就是你从最高点跌到最低点的幅度。这个指标直接反映了策略的「抗揍能力」。
举个例子:你策略从100万涨到200万,然后跌到120万,再涨到300万。最大回撤就是 (200-120)/200 = 40%。
注意:最大回撤只看历史,不代表未来。但如果你回测中的最大回撤超过20%,实盘时心理压力会非常大。我见过有人回测最大回撤30%,实盘跌到15%就扛不住平仓了——结果后面涨了3倍。
其他重要指标
- 胜率:盈利交易次数 / 总交易次数。胜率高不代表赚钱,还要看盈亏比。
- 盈亏比:平均盈利 / 平均亏损。一般盈亏比大于2才算健康。
- 卡玛比率:年化收益率 / 最大回撤。比夏普更直观,我比较喜欢用这个。
- 索提诺比率:类似夏普,但只考虑下行风险。适合非正态分布的策略。
15.5 回测框架的整体结构
说了这么多,我画一张图帮你理清思路。
这张图把回测框架的四个核心模块串起来了。数据层喂数据给回放引擎,回放引擎驱动策略层,策略层生成信号交给执行层,最后所有结果汇总到评估层。
15.6 避坑指南
最后,我总结几个回测中容易踩的坑:
- 未来函数:回测时用了未来数据。比如用当天的收盘价计算当天的信号。检查方法:把回测日志打印出来,看信号时间戳是否在数据时间戳之后。
- 幸存者偏差:只回测了现在还活着的股票/币种。退市的、归零的都被忽略了。结果回测曲线好看,实盘一买就跌。
- 过拟合:参数调得太精细,完美拟合了历史数据。换个时间段就失效。解决办法:做样本外测试,或者用交叉验证。
- 忽略交易成本:滑点和手续费设得太低。我建议回测时把滑点设成实盘的2倍,给自己留点安全边际。
一个小技巧:回测完成后,把交易记录逐笔打印出来,手动检查前100笔交易。看成交价格是否合理,滑点是否在预期范围内。这一步虽然费时间,但能发现很多隐藏的bug。
回测框架搭建好了,你的策略才算有了「试金石」。别急着上实盘,先在历史数据里把策略「揍」一遍。扛得住回测的,才有资格面对真实市场。