一、交易链路概述:从订单生成到交易所撮合的全路径拆解
做量化交易这些年,我见过太多人把精力全花在策略模型上,却忽略了订单从生成到成交这一路上经历的「九九八十一难」。说白了,你策略再牛,如果订单在路上多走了几微秒,可能就吃不到那口肉了。
今天我们就来拆解这条链路。我个人习惯把交易链路分成三段:客户端内部处理、网络传输、交易所撮合。每一段都有它的坑。
1.1 全路径拆解:订单的「一生」
一个订单从诞生到成交,大致经历以下步骤:
- 策略信号生成 — 你的模型算出了买卖信号
- 订单组装 — 把信号转成交易所能识别的协议格式
- 编码与序列化 — 把订单结构体变成二进制字节流
- 内核态网络发送 — 经过协议栈、网卡驱动,把数据发出去
- 物理链路传输 — 光纤、交换机、微波塔(如果你够壕)
- 交易所前置机接收 — 网卡收包、内核处理、应用层解析
- 撮合引擎处理 — 进入订单簿,匹配成交
- 回报回传 — 成交确认原路返回
嗯,这里要注意:每一步都有延迟,而且不是简单的加法。我曾经在项目中遇到过,某家券商的订单系统在序列化这一步就耗了 20 微秒,比整个网络传输还长。
核心观点:交易链路的延迟不是均匀分布的。80% 的延迟往往集中在 20% 的环节上。找到那 20%,你就找到了优化的突破口。
1.2 延迟的构成:到底慢在哪?
我把延迟拆成三类,你想想看是不是这个理:
| 延迟类型 | 典型来源 | 量级参考 |
|---|---|---|
| 处理延迟 | CPU 计算、内存拷贝、序列化/反序列化 | 1-50 μs |
| 排队延迟 | 内核缓冲区、网卡队列、应用层消息队列 | 1-100 μs(波动大) |
| 传输延迟 | 光纤传播、交换机转发、微波传输 | 10 μs - 1 ms(取决于距离) |
为什么排队延迟波动大?因为它是「软」的。你想想看,如果系统突然来了个 GC(垃圾回收),或者网卡中断合并策略没调好,排队延迟可能瞬间翻几倍。我建议你在做延迟测量时,别只看平均值,P99 和 P99.9 才是真正要命的。
避坑指南:我曾经被 P50 延迟骗过一次。系统平均延迟 5 微秒,看起来很美。结果上线后发现,每 1000 笔订单就有一笔延迟飙到 200 微秒。后来查出来是内核的 netpoll 机制在特定条件下触发了慢路径。从那以后,我所有延迟测试都强制看尾延迟。
1.3 延迟测量:怎么知道到底慢在哪?
测量延迟,说白了就是给每个环节打上时间戳。但这里有个坑——时间戳本身也有开销。你每打一个时间戳,可能就多花几十纳秒。在微秒级的链路里,这可不是小数目。
我个人习惯的做法是:
- 使用 TSC 寄存器(x86 的 RDTSC 指令)获取高精度时间戳,开销约 20-30 纳秒
- 在关键路径上埋点,但不要每个函数都埋,只埋「可能出问题」的地方
- 用环形缓冲区存时间戳,避免打印日志带来的 IO 延迟
下面是一个简单的测量代码示例,我在项目中经常用这种模式:
// 伪代码:在关键路径上打时间戳
uint64_t t1 = rdtsc();
// ... 执行订单组装 ...
uint64_t t2 = rdtsc();
// ... 执行网络发送 ...
uint64_t t3 = rdtsc();
// 计算各阶段耗时(纳秒)
uint64_t assemble_latency = (t2 - t1) * ns_per_cycle;
uint64_t send_latency = (t3 - t2) * ns_per_cycle;
// 写入环形缓冲区,异步落盘
ring_buffer_push(assemble_latency, send_latency);
注意:RDTSC 在多核 CPU 上可能不准,因为不同核心的 TSC 可能不同步。我建议在程序启动时先校准一下,或者绑定核心运行。否则你测出来的延迟可能是负数——别问我怎么知道的。
1.4 知识体系总览
下面这张图是我自己画的,把交易链路的全貌和延迟构成串在了一起。你看完应该能有个整体印象:
1.5 小结
交易链路这件事,说白了就是跟时间赛跑。你不需要把每个环节都优化到极致,但一定要知道瓶颈在哪。我个人习惯是:先测量,再优化。没有数据支撑的优化,都是瞎忙活。
嗯,这一章就到这里。记住三个关键词:全路径拆解、延迟构成、尾延迟测量。后面我们会一步步深入每个环节,看看怎么把微秒级的延迟一点点抠出来。
一句话总结:交易链路的延迟优化,不是把每个环节都做快,而是把最慢的那个环节找出来,干掉它。
公众号:蓝海资料掘金营,微信deep3321