回测系统

回测系统设计

生产级量化回测系统:事件驱动引擎、高保真撮合仿真、滑点与手续费建模、Walk-Forward 滚动优化、过拟合检测与实盘一致性验证。

1. 回测系统总览

什么是回测?一句话解释

回测 = 用过去的历史数据,模拟你的交易策略会赚还是亏。就像开车前先在驾校模拟器上练习一样——先用假的环境试错,别一上来就拿真金白银冒险。

一个具体例子

假设你有一个交易想法:「BTC 的 5 日均线上穿 20 日均线时买入,下穿时卖出」。你怎么知道这个想法靠不靠谱?

❌ 不靠谱的做法

直接拿真钱交易。如果亏了才发现策略有问题,已经来不及了。而且你不知道亏是因为策略差,还是因为运气不好。

✅ 靠谱的做法

用过去 2 年的 BTC 价格数据,模拟这个策略的表现。如果在过去 2 年里这个策略能赚钱,那它在未来大概率也有一定效果。

回测的核心流程

让我们用一个简化的例子走一遍完整流程。假设初始资金 10,000 USDT,策略是 5/20 日均线交叉:

日期BTC 价格5日均线20日均线信号操作资金
Day 20$100$98$95金叉买入 99 BTC$10,000
Day 45$120$118$110持有中$11,880
Day 60$105$107$112死叉卖出 99 BTC$10,395
结果盈利+$395 (+3.95%)

这只是一笔交易。真正的回测会用几百甚至几千笔历史交易来检验策略。交易次数越多,结果越可靠。

回测系统的整体架构

💡 核心设计原则:「回测即实盘」—— 策略代码在回测和实盘中完全相同,只有数据源(历史 vs 实时)和执行层(模拟 vs 交易所)不同。这样回测结果才有参考价值。

2. 事件驱动引擎架构

回测引擎的核心思路是「事件驱动」—— 就像一条流水线:数据进来 → 策略判断 → 下单 → 成交。每一步都是一个独立的事件,按顺序处理。

用一笔交易走完整个流程

为什么要用事件驱动?

松耦合

策略只管发信号,不管怎么成交。撮合只管成交,不管策略逻辑。各模块独立工作,互不干扰。想换一个策略?只需替换策略模块。

回测/实盘统一

回测时数据源是历史文件,实盘时数据源是 WebSocket。但策略代码完全不变!只需要切换数据源和执行层。

严格的时间顺序

事件按时间顺序逐个处理。策略只能看到「当前时间之前」的数据,绝对不能偷看未来的价格。这是回测可信的基础。

100% 可复现

相同的数据 + 相同的参数 = 完全相同的结果。每次运行结果一致,方便调试和验证。

引擎核心伪代码(极简版)

// 回测引擎核心循环 — 极简版
while 还有历史数据:
    K线 = 获取下一根K线()          // ① 数据推送

    信号 = 策略.判断(K线)           // ② 策略看到K线,决定买/卖/观望
    if 信号 == "买入":
        订单 = 组合.生成买单(信号)   // ③ 风控检查后生成订单

    成交 = 撮合.模拟成交(订单)      // ④ 模拟真实成交(含滑点、手续费)
    组合.更新(成交)                 // ⑤ 更新仓位和资金

输出 绩效报告()                    // 统计收益、回撤、胜率等

3. 历史数据管理

回测的质量取决于数据的质量。垃圾数据进去,垃圾结果出来。让我们看看回测需要什么样的数据。

数据长什么样?

最常用的是 K 线数据(也叫蜡烛图数据),每根 K 线记录了一段时间内的开盘价、最高价、最低价、收盘价和成交量:

// 一根 BTC/USDT 1小时 K线的数据
{
  时间: "2024-01-15 14:00",
  开盘价(Open):  42,850.00,   // 这1小时的第一笔成交价
  最高价(High):  43,120.50,   // 这1小时内的最高成交价
  最低价(Low):   42,780.00,   // 这1小时内的最低成交价
  收盘价(Close): 43,050.25,   // 这1小时的最后一笔成交价
  成交量(Volume): 1,234.56    // 这1小时总共成交了多少 BTC
}

三个层级的数据

💡 新手建议:从 Level 1 的日线或小时线开始。数据量小、下载方便,足够验证大多数策略想法。等策略成熟后再考虑更精细的数据。

数据质量检查——别被脏数据坑了

历史数据经常有问题:缺失几根K线、价格异常跳动、成交量为零等。如果不检查,回测结果会不准确。常见的检查项:

检查项什么意思举例
时间连续性相邻K线之间不能有缺失1小时K线之间应该间隔恰好1小时
OHLC 一致性最高价必须≥开盘和收盘如果 High < Close,这根K线有问题
价格合理性单根K线涨跌不应超过 50%BTC 从 40000 突然变成 100?数据错误
成交量异常价格变了但成交量为 0不可能的——有成交才有价格变化

4. 撮合仿真与滑点模型

⚠️ 这是回测中最容易犯错的地方!80% 的「回测很好、实盘亏钱」都是因为撮合仿真太理想化了。

理想 vs 现实:为什么回测收益会虚高?

具体数字对比

假设你想买入价值 10,000 USDT 的 BTC,当前价格 $100:

项目❌ 理想化回测✅ 现实回测🔥 实盘
成交价$100.00$100.05$100.03 ~ $100.08
滑点05 bps ($0.05)3~8 bps
手续费0$10 (0.1%)$10 (0.1%)
成交延迟立即下一根K线开盘50ms ~ 500ms
实际花费$10,000$10,015$10,011 ~ $10,018

一笔交易差 $15 好像不多?但如果一年交易 500 次,就是 $7,500!对 $10,000 本金来说就是 75% 的差距。所以撮合仿真的精度直接决定回测结果是否可信。

三种滑点模型对比

模型 1:固定滑点

每笔交易固定加 5 个基点(0.05%)。最简单,适合初步估算。缺点:大单和小单滑点一样,不够真实。

滑点 = 价格 × 5 / 10000
例: $100 × 5 / 10000 = $0.05

模型 2:成交量自适应

你的订单占当根K线成交量的比例越大,滑点越大。更真实——大单确实比小单滑点大。

参与率 = 你的量 / K线成交量
滑点 = 基础滑点 × (1 + 参与率 × 50)
例: 占成交量10% → 滑点放大6倍
💡 建议:对于中低频策略(每天 1-5 笔交易),固定滑点 5-10 bps 就够了。对于高频策略(每小时几十笔),必须用成交量自适应或订单簿模型。

5. 策略接口设计

策略代码应该尽可能简单——只关注「什么时候买、什么时候卖」,不需要关心数据怎么来的、订单怎么撮合的。下面是一个最简单的均线交叉策略:

// 均线交叉策略 — 最简实现
class 均线交叉策略:
    初始化():
        快线周期 = 5
        慢线周期 = 20

    每根K线(K线):
        收盘价列表 = 获取最近(慢线周期 + 1)根收盘价

        快线 = 最近5根的平均值
        慢线 = 最近20根的平均值
        上一根的快线 = ...
        上一根的慢线 = ...

        // 金叉:快线从下方穿越慢线 → 买入
        if 上一根快线 ≤ 上一根慢线 and 快线 > 慢线:
            买入(数量 = 95%资金 / 当前价格)

        // 死叉:快线从上方穿越慢线 → 卖出
        if 上一根快线 ≥ 上一根慢线 and 快线 < 慢线:
            卖出(全部持仓)

策略的生命周期

回调函数什么时候调用你要做什么
onInit()回测开始前设置参数(均线周期等)
onBar(candle)每根新K线到来时核心逻辑——判断买/卖/持有
onFill(fill)订单成交后记录日志、更新状态
onStop()回测结束时平掉所有持仓
💡 关键原则:策略代码中只能用 getClose()、getPosition() 等 API 获取「当前时间之前」的数据。绝对不能偷看未来数据!这是回测最大的忌讳,叫做 Look-Ahead Bias(前视偏差)。

6. 回测引擎模拟器

下面是一个可以实际运行的回测模拟器。选择不同的策略和市场参数,观察资金曲线和核心指标的变化。试试调高波动率看看回撤会怎样!

🎮 使用提示:1) 先用默认参数运行一次,看看基准表现 → 2) 改变波动率(0.01 = 低波动,0.05 = 高波动)观察策略在不同市场环境下的表现 → 3) 对比不同策略在同样市场条件下的差异
回测引擎模拟器 — 选择策略和参数后运行回测
快慢均线金叉做多、死叉做空

7. 绩效评估体系

回测跑完了,怎么判断策略好不好?不能只看「赚了多少钱」——还要看风险、稳定性、胜率等多个维度。下面用大白话解释最重要的几个指标:

Sharpe Ratio (夏普比率)

每承担 1 份风险,能赚多少超额收益。就像考试成绩除以复习时间——不仅看分数,还看效率。

Sharpe = 年化收益 / 年化波动率
< 0.5
0.5 ~ 1.5可接受
> 2.0优秀

最大回撤 (Max Drawdown)

从最高点到最低点的最大跌幅。比收益率更重要!回测赚 100% 但中间回撤 60% —— 你能扛住看着账户腰斩吗?

回撤 = (峰值 - 谷底) / 峰值
< 10%优秀
10% ~ 30%可接受
> 30%危险

胜率 (Win Rate)

赚钱的交易占总交易的比例。但胜率高不一定赚钱!如果赢 9 次每次赚 $10,输 1 次亏 $100,胜率 90% 照样亏。要配合盈亏比看。

趋势策略胜率 30-40%, 盈亏比 3:1
均值回归胜率 55-65%, 盈亏比 1:1
网格交易胜率 70-80%, 盈亏比 0.5:1

Profit Factor (利润因子)

总盈利 / 总亏损。大于 1 说明赚的比亏的多。最直观的指标之一。

利润因子 = 总盈利 / 总亏损
< 1.0亏钱
1.0 ~ 1.5勉强
> 2.0优秀

一张表看完所有指标的参考值

指标可接受优秀
Sharpe Ratio< 0.50.5 ~ 1.5> 2.0
Sortino Ratio< 0.50.5 ~ 2.0> 3.0
最大回撤> 30%10% ~ 30%< 10%
胜率< 40%40% ~ 55%> 55%
盈亏比< 1.01.0 ~ 2.0> 2.0
Profit Factor< 1.01.0 ~ 1.5> 2.0
SQN< 1.61.6 ~ 2.5> 3.0

8. Walk-Forward 优化

为什么需要 Walk-Forward?一个故事

想象你是一个学生,在做模拟考试。你在「模拟题 A」上反复练习,找到了最优的答题技巧,模拟分数 95 分。但真正考试时只考了 60 分。为什么?因为你的「技巧」只是记住了模拟题 A 的答案,并没有真正理解知识。Walk-Forward 就是解决这个问题的方法——它让你在多套不同的「模拟题」上验证你的「技巧」是否真的有效。

Walk-Forward 怎么做?图解

把历史数据分成多个窗口。每个窗口分两段:训练段(找最优参数)和验证段(用训练段的最优参数测试)。如果验证段也赚钱,说明策略有真实的预测能力。

具体步骤

步骤做什么举例
1把数据分成 N 个滚动窗口12个月数据 → 5个窗口
2每个窗口:70% 训练,30% 验证窗口1: 1-7月训练, 8-9月验证
3在训练段扫描所有参数组合均线周期从 3 到 50 全部试一遍
4选出训练段 Sharpe 最高的参数快线=5, 慢线=20 最好
5用这个参数在验证段上测试8-9月用快5慢20跑一遍
6拼接所有窗口的验证结果5个窗口的OOS收益平均 = 6.3%

如何判断结果好不好?

衰减率 < 50% ✅

训练段收益 20%,验证段收益 12%,衰减率 = (20-12)/20 = 40%。低于 50% 说明过拟合可控。如果衰减率 > 70%,说明大部分收益来自过拟合。

参数稳定性 ✅

如果 5 个窗口找到的最优参数分别是 5、6、5、7、5,说明策略稳健。如果是 3、20、8、50、12,说明策略对参数太敏感,不可靠。

Walk-Forward 模拟器

自己动手试试!调整信号强度和噪声水平,观察训练集和验证集收益的差距。如果差距很大,说明策略可能过拟合了:

Walk-Forward 优化 & 过拟合检测

9. 过拟合防范

什么是过拟合?

过拟合 = 你的策略只是「记住」了历史数据的噪声,而没有找到真正的规律。就像一个学生背了所有考试的原题,但碰到新题就不会做了。回测收益很高,实盘一塌糊涂。

过拟合的四大元凶

参数太多

策略有 10 个参数?每增加一个参数,过拟合风险指数级增长。好的策略参数 ≤ 5 个。如果需要 > 10 个参数才能盈利,大概率是在拟合噪声。

数据窥探

你在同一份数据上反复调参、反复回测。每次回测都「偷看」了答案,所以最终结果必然很好。但你其实只是在拟合这份特定的数据。

选择偏差

你测试了 100 个策略变种,只展示最好的那个。按概率,100 个随机策略中总有几个恰好在这段时间表现不错——但这是运气,不是能力。

无法解释

好的策略应该有清晰的逻辑(趋势跟踪、均值回归、套利)。如果你无法解释为什么你的策略能赚钱,它大概率是数据挖掘的产物。

如何防范过拟合?检查清单

检查项达标标准你的策略?
参数数量≤ 5 个越少越好
交易次数> 100太少不可信
Walk-Forward 衰减< 50%> 70% = 过拟合
多市场验证≥ 3 个市场盈利BTC/ETH/SOL 都测
逻辑可解释能用一句话解释「趋势跟踪」✅ 「周三满月」❌
衰减预算预留 30-50%回测 Sharpe 3.0 → 实盘约 1.5-2.0

10. 回测 vs 实盘对比

策略通过了回测验证,接下来怎么上线?不是直接拿真钱冲,而是分阶段验证:

三个阶段详细对比

对比项🔬 回测📝 模拟盘💰 实盘
数据历史数据实时行情实时行情
成交模拟撮合虚拟撮合真实成交
滑点模型估算更接近真实真实滑点
心理因素高(恐惧贪婪)
可信度参考较高最终标准
建议时长2-4 周先小仓 2 周

上线流程 — 四个阶段

Phase 1: 研究

回测达标:Sharpe > 2.0,最大回撤 < 15%,交易次数 > 100。Walk-Forward 衰减 < 50%。多市场验证通过。

Phase 2: 模拟盘 (2-4周)

用真实行情 + 虚拟撮合。对比回测:PnL 偏差 < 20%。验证信号一致性和系统稳定性。通过标准:模拟盘 Sharpe / 回测 Sharpe > 0.6。

Phase 3: 小资金 (2-4周)

用总资金的 5-10%。对比模拟盘:成交价差 < 5bps。实时监控 PnL 和滑点。通过标准:实盘与模拟盘方向一致。

Phase 4: 全量上线

逐步加仓到目标仓位。每日对比回测预期。自动熔断:日亏 > 2% 或回撤 > 10% 暂停。月度复盘:检查策略衰减。

⚠️ 最重要的一句话:实盘表现通常比回测差 30-50%。如果你的回测 Sharpe 是 3.0,实盘大概只有 1.5-2.0。在评估策略时一定要预留这个衰减空间。不要被漂亮的回测曲线欺骗!