4 个数字,全部来自我们自己的模拟交易系统;同一套策略、1-minute BTCUSDT 永续合约信号、31 天、1,418 笔订单。从 K 线收盘到交易所确认订单的中位耗时:195 ms。第 99 百分位:2,970 ms。已实现波动率最高的 1% 分钟里的中位耗时:840 ms。还有此前大半年的回测配置里一直使用的延迟常数:0.
其中 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。这才是诚实的数字,也一直都是。
那配置里到底该填什么
别填一个常数。我们现在这样做:
- 从样本中抽取,不要手动设定。根据该 K 线自身的活跃程度,从经验分布中抽取每笔模拟订单的延迟——成交笔数和绝对收益率就足够了,而且标准 K 线里本来就有这两个数据。按交易平台和订单类型分别拟合,每月重新拟合一次。
- 保留尾部。不要拟合一个对数正态分布就算完事;p99 的延迟来自重试风暴和 GC 暂停,平滑分布无法复现这些情况。我们使用经验分位数,并对 p95 以上的尾部进行重采样。
- 撤单也要计入延迟。替换止损单或撤销对冲单,同样会受到延迟影响;在快速行情中,你的撤单还要和成交抢时间。如果模拟器能瞬间撤单,你的风险模型就是虚构的。
- 加入过期保护,并在模拟中体现。如果信号到达时 K 线已过去 1.5 秒以上,我们就会丢弃该信号。这种实际行为有真实成本——它会悄无声息地删掉一些交易,回测中也必须删掉同样的交易。
- 根据模拟交易重新校准。模拟交易能提供真实基准。每笔订单都记录 4 个阶段的时间戳,每周将这些延迟分布与模拟器的假设进行对比。
其他资产类别,也有同样的问题。美国股票:综合行情带会在经纪商延迟之外再增加一层汇总延迟;出于同样的结构性原因,消息速率压力升高时,延迟也会加大。预测市场:大多数交易平台只支持 REST 下单;我们测到平时中位延迟不到 100 ms,但遇到结算事件、所有人同时涌入时,延迟会升至数秒。加密货币期权:标的资产发生变动的那一分钟,你用来定价的报价可能已经过期 2 秒。
这个错误的普遍形式,是把执行参数当成独立常数,尽管它实际取决于驱动信号的同一个状态变量。手续费会变,资金费率会变;延迟却不一样。如果你只记住一个数字,就记住这个比率:我们的 p99 是中位数的 15 倍,而我们的信号有很高比例恰好出现在接近 p99 的时段。
去给这 4 个阶段加上测量。只需一个下午,就能让你比再花一周搜索参数更了解自己的策略。
← 全部文章


