2026年九月13日 · 执行

回测中该加入多少延迟?我们测量了自己的每一毫秒

回测中该加入多少延迟?我们测量了自己的每一毫秒

4 个数字,全部来自我们自己的模拟交易系统;同一套策略、1-minute BTCUSDT 永续合约信号、31 天、1,418 笔订单。从 K 线收盘到交易所确认订单的中位耗时:195 ms。第 99 百分位:2,970 ms。已实现波动率最高的 1% 分钟里的中位耗时:840 ms。还有此前大半年的回测配置里一直使用的延迟常数:0.

195 msK 线收盘 → 订单确认的中位数
2,970 msp99,同一路径
840 ms波动率最高的 1% 分钟里的中位数

其中 3 个数字都不稀奇。大家都知道 0 不对,迟早会把它换掉,而换上的数几乎总是凭感觉挑的。100 ms、250 ms,或者“保守点,500”。真正重要的是 840,因为它说明延迟取决于市场状态;而延迟飙升时的市场状态,恰恰也是你的信号认为自己发现机会的那些状态。

195 毫秒都花在哪了

下面是完整的延迟预算。我们逐个阶段测量,每一跳都写入单调时钟时间戳。服务器是部署在 AWS ap-northeast-1 的实例,与交易平台的端点位于同一区域。这基本就是非共址、面向零售交易者的配置所能达到的最佳水平。

阶段 p50 p95 p99
K 线收盘 → 收到 WS kline-closed 事件 128 ms 402 ms 1,940 ms
解析 + 特征状态更新 3 ms 9 ms 28 ms
决策:模型评估 + 风险检查 11 ms 22 ms 60 ms
提交订单 → 收到交易所确认 47 ms 180 ms 1,100 ms
端到端(实测值,非各阶段相加) 195 ms 690 ms 2,970 ms

进入重点之前,有两件事要留意。第一,中位数不能直接相加:128 + 3 + 11 + 47 = 189,而实测的端到端中位数是 195。百分位数不具备可加性;如果把各阶段的 p99 相加来估算延迟,得出的结果会是 3,128 ms,而实际 p99 是 2,970。这里差得不多;但当各阶段以相反方向相关时,这种算法就会错得离谱。测量完整路径。

第二,看看最上面一行。中位延迟预算的三分之二,都花在等交易所通知你 K 线收盘上。不是网络,不是你的代码,也不是订单路由。交易平台把成交汇总成 K 线,判定这一分钟结束,然后推送出来。推送前有一个队列,而且这条流上的所有订阅者都共用这个队列。

840

按产生信号那一分钟的已实现波动率,把 1,418 笔订单逐笔分组后,延迟分布就大不一样了。

信号 K 线波动率区间 订单数 端到端 p50 端到端 p99
最低四分位 201 141 ms 488 ms
中间两个四分位 796 183 ms 1,210 ms
最高四分位 407 312 ms 2,880 ms
所有分钟中最高的 1% 14 840 ms 4,100 ms

每个阶段都会一起变慢,因此延迟会层层叠加。交易平台的汇总和广播队列受撮合引擎负载影响,所以包含 11,000 笔成交的那一分钟,推送会比只有 600 笔成交的分钟更晚。你的进程在同一时间段也要处理更多深度更新和成交推送,平时只需 3 ms 的特征更新就要花 28 ms。REST 订单链路要和其他人的恐慌操作争抢资源,于是偶尔会碰上 503 并重试,光这一项就会让你损失整整 1 秒。在我们的样本里,信号 K 线成交笔数与端到端延迟的相关系数为 0.41。不算很高,但足以让固定常数失效。

而且,第一轮筛选后留存下来的策略,大多是交易突破、反转、错位行情和清算瀑布的策略。它们恰好会在延迟最差的区间触发。只有在一分钟内价格移动 40 bps 才触发的动量信号,从结构上看,就注定总会碰上运行最慢的系统状态。

有一次,我们花了一整天追查一个 3.6 秒的异常延迟,它几乎每天晚上都在同一时间出现。原因是 logrotate 在同一个 EBS 卷上复制并截断一个 2 GB 文件,而进程也在往这个卷写数据,结果事件循环被阻塞了。市场并无问题。先测量,再怪微观结构。

这会如何影响成交

等待期间,价格会漂移。随机抽取一分钟,我们 195 ms 中位延迟期间的平均不利变动约为 1.1 bps,方向与信号相反。在延迟为 312 ms 的最高四分位分钟里是 5.8 bps;在延迟为 840 ms 的最高 1% 分钟里,订单甚至还没到达订单簿,价格就已出现约 14 bps 的不利漂移。

在 BTC 永续合约上,价差几乎不会加重这个问题:84,000 的报价上,1 tick 只有 0.012 bps,所以损失来自不利漂移,而不是吃单成本。把同一策略移到中市值永续合约上,第二项影响就显现了——我们的样本中,盘口最优价差从平静分钟的 1.4 bps 扩大到最高四分位分钟的 6.2 bps,而最优价位的深度减少了一半以上。价格漂移、更宽的价差、更薄的深度,你会同时承受这三种影响,因为它们源自同一个原因。

我们用逐根 K 线的条件延迟,而不是固定的 200 ms,重放了同样的 31 天。回测中的平均入场价恶化了 3.1 bps,几乎全部集中在 9% 的交易上。回测与模拟交易的入场价偏差从 4.2 bps 降到了 0.9 bps。该策略的样本内 Sharpe 从 1.7 降到 1.1。这才是诚实的数字,也一直都是。

那配置里到底该填什么

别填一个常数。我们现在这样做:

  1. 从样本中抽取,不要手动设定。根据该 K 线自身的活跃程度,从经验分布中抽取每笔模拟订单的延迟——成交笔数和绝对收益率就足够了,而且标准 K 线里本来就有这两个数据。按交易平台和订单类型分别拟合,每月重新拟合一次。
  2. 保留尾部。不要拟合一个对数正态分布就算完事;p99 的延迟来自重试风暴和 GC 暂停,平滑分布无法复现这些情况。我们使用经验分位数,并对 p95 以上的尾部进行重采样。
  3. 撤单也要计入延迟。替换止损单或撤销对冲单,同样会受到延迟影响;在快速行情中,你的撤单还要和成交抢时间。如果模拟器能瞬间撤单,你的风险模型就是虚构的。
  4. 加入过期保护,并在模拟中体现。如果信号到达时 K 线已过去 1.5 秒以上,我们就会丢弃该信号。这种实际行为有真实成本——它会悄无声息地删掉一些交易,回测中也必须删掉同样的交易。
  5. 根据模拟交易重新校准。模拟交易能提供真实基准。每笔订单都记录 4 个阶段的时间戳,每周将这些延迟分布与模拟器的假设进行对比。

其他资产类别,也有同样的问题。美国股票:综合行情带会在经纪商延迟之外再增加一层汇总延迟;出于同样的结构性原因,消息速率压力升高时,延迟也会加大。预测市场:大多数交易平台只支持 REST 下单;我们测到平时中位延迟不到 100 ms,但遇到结算事件、所有人同时涌入时,延迟会升至数秒。加密货币期权:标的资产发生变动的那一分钟,你用来定价的报价可能已经过期 2 秒。

这个错误的普遍形式,是把执行参数当成独立常数,尽管它实际取决于驱动信号的同一个状态变量。手续费会变,资金费率会变;延迟却不一样。如果你只记住一个数字,就记住这个比率:我们的 p99 是中位数的 15 倍,而我们的信号有很高比例恰好出现在接近 p99 的时段。

去给这 4 个阶段加上测量。只需一个下午,就能让你比再花一周搜索参数更了解自己的策略。

延迟回测模拟交易执行加密货币期货
← 全部文章