📝

Draft article

This draft is visible to admins and superusers only. Sign in with an authorized account.

← 返回文章列表
July 22, 2026
5 分钟阅读

加密市场的智能订单路由:一笔订单,十二个场所,没有 NBBO

加密市场的智能订单路由:一笔订单,十二个场所,没有 NBBO
#执行
#智能订单路由
#市场微观结构
#流动性碎片化
#crypto
#quant
#python
#tca

你需要买入 400 BTC。Binance 的深度最好。OKX 和 Bybit 各自在宽一个 tick 的位置显示出不错的量。Coinbase 报出更好的表面价格,但计价是 USD,不是 USDT。Kraken 的盘口顶端看起来棒极了——而且已经陈旧了 400 毫秒。Upbit 更好,但你在韩国的合规批文并不存在,你的 KRW 也不存在。一个股票交易员面对这团乱局,会伸手去用智能订单路由器,然后就不用再想了,因为在股票市场,路由中最难的部分早在二十年前就被监管压进了基础管道里。在加密市场里,你自己就是那管道。没有合并行情,没有订单保护规则,没有费用上限,没有净额结算——而且最根本的是,你无法在你的资本尚未存放的地方进行交易。

本文讲的是——即便如此,依然要把路由器造出来:合并订单簿,以及为什么对着它做朴素的最优价路由会亏钱;把配置问题作为一个你确实能在子订单截止时限内求解的凸规划来处理;那条使加密 SOR 与资金库管理密不可分的资本约束;跨场所的 maker 感知路由;以及那条能告诉你这一切是否奏效的 TCA 反馈闭环。它是 用 Rust 实现复杂套利执行 的工程兄弟篇——那篇讲的是纳秒;这一篇讲的是决策。

股票 SOR 免费得到了什么

值得精确地说清楚,美国股票路由从监管那里继承了什么,因为清单上的每一项,都是你必须重建、或有意识地在缺少它的情况下过活的东西。

Regulation NMS(SEC,2005)做了三件在这里重要的事。Rule 611,即订单保护规则,禁止以劣于另一家交易所所显示的受保护报价的价格成交——即"穿价成交"(trade-through)——这迫使每个经纪商要么把订单路由到最优显示价,要么用跨市场扫单(intermarket sweep orders)把它扫掉。Rule 610 把任何场所对拿取受保护报价所能收取的准入费上限设为每股 $0.003,于是各场所的显示价可以在 30 mils 以内相互比较。而合并行情(那些 SIP)会发布全国最优买卖报价(National Best Bid and Offer),对"市场是什么"给出一个单一的官方答案。

学术界对这套架构的裁决是:它出人意料地有效。O'Hara and Ye(2011),"Is market fragmentation harming market quality?"(Journal of Financial Economics 100(3), 459–474),考察了不同碎片化程度下的美国股票,发现碎片化程度更高的股票反而有更低的交易成本和更快的成交,价格也更接近随机游走。他们的总结是那句关键的话:美国股票的行为像"一个带有多个入口的单一虚拟市场"。当智能路由器加上穿价保护把碎片重新缝合起来时,碎片化是无害的。Foucault and Menkveld(2008),"Competition for Order Flow and Smart Order Routing Systems"(Journal of Finance 63(1), 119–158),在荷兰市场揭示了这一机制:当第二个限价订单簿(LSE 的 EuroSETS)进场与 Euronext 竞争时,合并深度反而增加了,而一个场所的流动性供给会被其穿价率直接抑制——忽略某个场所的路由器,会扼杀它报价的动力。

即使在这个受监管的世界里,合并视图在短时间尺度上也是一个谎言。Ding, Hanna, and Hendershott(2014),"How Slow Is the NBBO? A Comparison with Direct Exchange Feeds"(Financial Review 49(2), 313–332),把 SIP 的 NBBO 与用同一数据中心内的交易所直连行情构建的 NBBO 做了对比,发现在活跃标的上,每秒会出现数次错位,通常持续一到两毫秒——纯粹是聚合与传输延迟造成的。记住这个数字:它是这样一个问题的股票版本,而这个问题在加密里要严重一到两个数量级。

现在把这一切全部删掉。加密没有 NBBO,因为没有 SIP。没有穿价规则:一个场所会乐呵呵地在穿过另一场所报价五个 tick 的价位上给你成交,而且没人会去申报任何东西。没有费用上限:taker 费从不到一个基点的协商分层到 10 bps 的零售费率不等,于是显示价排序与净价排序经常相互矛盾。也没有合并清算:每家交易所都是自己的孤岛,各自持有预注余额。你同时是 SIP、路由器和清算公司。

构建合并订单簿,以及为什么朴素的最优价路由会失败

工程基线并不光鲜:N 路 WebSocket L2 行情、分场所的序列号缺口处理、符号与 tick 大小归一化,以及计价货币归一化(一个 BTC-USD 订单簿和一个 BTC-USDT 订单簿相差一个 USDT/USD 汇率,而这个汇率并不恒等于 1.0,偶尔还很不等于 1.0)。把归一化后的订单簿合并成一个按价格排序的阶梯,给每个价位标注它的来源场所,以及——至关重要地——它所来自的快照的年龄。如果你合并后的订单簿没有把分场所报价年龄当作一等字段来携带,那你造出来的是屏保,不是路由器。

跨场所的合并订单簿,陈旧与幻影流动性被高亮标注

朴素路由器贪婪地在这个合并阶梯上行走:先取最优净价。它会因三个各不相同的原因而失败,值得把它们分开来看,因为修复方式各不相同。

陈旧报价与延迟偏斜。 你的各个场所并不以相同的延迟送达数据。一个同机房行情在你据其行动时可能只有 3 ms 的年龄;而一个来自另一大洲场所的公开 WebSocket 可能已经陈旧了 300 ms。因此合并后的盘口顶端是不同过去时刻的一个混合体。当 BTC 在 200 ms 内变动 10 bps——这是家常便饭——那个陈旧场所的报价,会恰好在错误的一侧,系统性地显得诱人。把订单路由到它们那里,买来的是一场你早已输掉的竞速:报价已经消失,你的 IOC 空手而归或只有部分成交,而等你重新路由时,新鲜的场所早已重新定价。这就是 Ding–Hanna–Hendershott 那个 1–2 ms 的 SIP 错位问题,只不过你的错位持续数百毫秒,而且没有任何人有义务兑现任何东西。

幻影流动性。 把各场所的显示量加总会高估,因为同一个做市商的存货被同时报在好几个地方。Van Kervel(2015),"Competition for Order Flow with Fast and Slow Traders"(Review of Financial Studies 28(7), 2094–2127),在碎片化的股票中记录了这一点:在一个场所上的一笔成交,会在数毫秒内伴随着竞争场所上大量限价单的撤单,这恰恰符合这样一个模型的预测——快速流动性提供者在各处报出重复的量,一旦某处被击中就把这些副本撤走。加密做市商在 Binance/OKX/Bybit 上跑的是同一套剧本,所以可触达的合并深度实质上小于显示的合并深度,而且你越是顺序地(而非同时地)去打这些场所,这个缺口就越大。如果你的路由器一次向一个场所发送子订单,等待每一笔成交确认,那你就是在割自己的韭菜:每一笔成交都在向街上其他人发信号去撤单。

费用会重排阶梯。 一个显示出最优原始价、却带 7.5 bps taker 费的场所,往往是订单簿中最差的净价。这听起来明显得都不值一提,可"最优显示价"路由,恰恰是大多数第一代加密路由器(以及若干厂商产品)所实现的东西。扣费后比较是最低门槛;兄弟篇 maker-taker 费用与返佣 讲了分场所的费用数学、VIP 分层动态,以及为什么应该进路由器的是你的边际费率——而不是标价。

路由优化

把子订单问题形式化。你必须现在、以可成交方式,跨越场所 v=1,,Vv = 1,\dots,V 买入数量 QQ。设 Av(x)A_v(x) 为在吃掉场所 vv 订单簿的 xx 个单位后其边际卖价(来自 L2 快照的一个非递减阶梯函数),fvf_v 为其 taker 费,λv\lambda_v 为对场所 vv 的陈旧性与逆向选择的每单位惩罚(在下文中,由你自己的 markout 校准)。配置 q=(q1,,qV)q = (q_1, \dots, q_V) 求解

minq0,  vqv=Q    v=1VCv(qv),Cv(qv)=(1+fv)0qvAv(x)dx  +  λvqv.\min_{q \ge 0,\; \sum_v q_v = Q} \;\; \sum_{v=1}^{V} C_v(q_v), \qquad C_v(q_v) = (1+f_v)\int_0^{q_v} A_v(x)\,dx \;+\; \lambda_v\, q_v .

每个 CvC_v 都是凸的(一个非递减函数的积分,加上一个线性项),所以该问题是凸的,而 KKT 条件道尽了整个故事:存在一个阈值 μ\mu,使得

Cv(qv)=(1+fv)Av(qv)+λv=μfor every venue with qv>0,C_v'(q_v^*) = (1+f_v)\,A_v(q_v^*) + \lambda_v = \mu \quad \text{for every venue with } q_v^* > 0,

而对于分不到任何量的场所,Cv(0)μC_v'(0) \ge \mu。用一句话说:像倒水一样把订单铺到各场所上,在你所交易的每一处把边际全包成本拉平。一个场所被排除,恰恰是在它的第一个单位——最优价,加费用,加陈旧性惩罚——都比别处的边际单位更差的时候。

水填充配置,在各场所成本曲线间拉平边际成本

对于阶梯函数订单簿,水填充解是通过在经费用与惩罚调整过的合并阶梯上做一次贪婪行走计算出来的——所以贪婪式深度行走本身并没有错;它正是精确解,前提是你贪婪行走的是净边际成本、用的是打折后的量,而不是原始显示价。这个区别,就是路由器与屏保之间的全部差异。

对这个一般问题的经典处理是 Cont and Kukanov,"Optimal order placement in limit order markets"(Quantitative Finance 17(1), 21–39, 2017; arXiv:1210.1625)。他们把跨场所的订单下达——包括限价单与市价单之间的拆分、费用与返佣,以及一个对执行风险的惩罚——形式化为一个凸优化,推导出单场所限价/市价拆分的显式闭式解,并为多场所情形给出一个随机逼近算法,能在不到 200 ms 内计算出跨越十二家交易所的配置。这个框架早于加密而出现,却几乎原封不动地迁移过来,因为它从一开始就不曾假设有 NBBO——它只假设了分场所的订单簿、分场所的费用,以及成交的不确定性,而这恰恰就是加密的处境。

可成交一侧(带陈旧性折价的凸水填充)的一个精简、诚实的示意:

import math
from dataclasses import dataclass

@dataclass
class Venue:
    name: str
    asks: list[tuple[float, float]]  # (price, displayed size), sorted
    taker_fee: float                 # fractional, e.g. 0.0002 = 2 bps
    quote_age_ms: float
    kappa: float                     # phantom-liquidity decay rate, 1/ms
    lam: float                       # staleness/toxicity penalty, $ per unit

def allocate(venues: list[Venue], Q: float):
    ladder = []  # (marginal all-in cost, accessible qty, venue)
    for v in venues:
        surv = math.exp(-v.kappa * v.quote_age_ms)  # P(level still there)
        for price, size in v.asks:
            cost = price * (1.0 + v.taker_fee) + v.lam
            ladder.append((cost, size * surv, v.name))
    ladder.sort()

    fills, remaining = {}, Q
    for cost, qty, name in ladder:
        take = min(qty, remaining)
        fills[name] = fills.get(name, 0.0) + take
        remaining -= take
        if remaining <= 1e-12:
            break
    return fills, remaining  # remaining > 0 => book too thin: slice parent

十六行逻辑;全部的智能都活在输入里。κ\kappa(显示量随报价年龄蒸发得有多快)是从你自己的 IOC 成交率、作为发单时报价年龄的函数校准出来的。λv\lambda_v 来自分场所的 markout(见最后一节)。两者都是测出来的,不是猜出来的。

一个算例。 跨三个场所买入 Q=10Q = 10 BTC:

场所 Taker 费 报价年龄 卖单(价格 × 量)
A(深、新鲜) 2.0 bps 10 ms 87,000 × 3.0; 87,010 × 4.0; 87,025 × 6.0
B(费便宜、陈旧) 1.0 bps 250 ms 86,995 × 1.5; 87,015 × 2.0
C(表面最优、费高) 7.5 bps 20 ms 86,990 × 2.0; 87,000 × 3.0

原始行情说 C 有最优卖价(86,990),其次是 B。做费用调整,阶梯就彻底重排:C 的顶层净价为 86,990×1.00075=87,05586{,}990 \times 1.00075 = 87{,}055,是屏幕上最差的流动性。B 的顶层净价 87,004,A 的净价 87,017。对 B 的显示量施加 30% 的陈旧性折价(eκ250ms0.7e^{-\kappa \cdot 250\text{ms}} \approx 0.7)再做水填充:从 B 的第一层取 1.05 BTC,从 A 的第一层取 3.0,从 B 的第二层取 1.4,从 A 的第二层取 4.0,从 A 的第三层取 0.55。全包均价:**每 BTC 87,022.2。朴素的最优显示价路由器——先C,再按面值量取B,再取C的第二层——付出87,022.2**。朴素的最优显示价路由器——先 C,再按面值量取 B,再取 C 的第二层——付出 87,038.8:差 1.9 bps,在一个 10-BTC 的子订单上约 $166,而这会在一整天里每一个母单的每一个子单上复利叠加。请注意这个包袱点:在行情上有着最优显示价的那个场所,从优化后的路由器那里得到了流量。在加密里,没有人强迫你在那里交易——路由穿过一个"受保护报价"根本不是一个概念——而正确的配置经常干脆完全忽略那个表面上的最优价。

那个凸规划仍然忽略的东西:同时性(要在同一毫秒里把所有场所的子单打出去,否则 van Kervel 的撤单会在执行中途重新给场所定价)、离散手数与最小名义额(把连续解取整,再贪婪地修补),以及根本不去跨价差的这个选项——那是第 5 节的主题。至于在这些场所逻辑运行之前,大额母单应当如何随时间被切片这个更深层的问题,见 Almgren–Chriss 最优执行;SOR 决定的是一个子单去哪里,不是子单何时发生。

资本约束:SOR 就是资金库管理

上面的一切都默默地假设了你能够在场所 vv 上交易 qvq_v。在股票市场,这个假设是免费的:一个主经纪商、净额结算,现在交易、稍后再挪钱。在加密里,它是整个系统的约束绑定项。交易所要求预注余额——你不能用停在 Binance 上的 USDT 去吃 Kraken 的卖价。所以真正的问题是

minq0,  qv=QvCv(qv)s.t.qvBv    v,\min_{q \ge 0,\; \sum q_v = Q} \sum_v C_v(q_v) \quad \text{s.t.} \quad q_v \le B_v \;\; \forall v,

其中 BvB_v 是你在场所 vv 上的可用余额(买入时用计价资产计,卖出时用基础资产计)。现在 KKT 条件写作 Cv(qv)=μηvC_v'(q_v^*) = \mu - \eta_v,其中 ηv0\eta_v \ge 0 是余额上限的乘子。在触及上限的场所上,ηv=μCv(Bv)>0\eta_v = \mu - C_v'(B_v) > 0:那里的边际一美元执行起来比全市场的水位更便宜,而你却被迫把流量推向更贵的场所。那个乘子不是抽象概念——ηv\eta_v 字面上就是如果场所 vv 此刻多存在一个单位的余额,你每单位能省下多少美元。在你预测的流量上求和,它就是你为一次再平衡转账所愿意付出的金额,而再平衡决策就变成任何资金库系统都能执行的一个比较:在如下情形挪动存货

E ⁣[future childrenηv]  >  transfer fee+expected adverse move during transfer latency.\mathbb{E}\!\left[\textstyle\sum_{\text{future children}} \eta_v \right] \;>\; \text{transfer fee} + \text{expected adverse move during transfer latency}.

右边既不小,也不恒定。链上 BTC 需要 2–6 个确认(20–60 分钟)交易所才会入账;ERC-20 转账要几分钟外加 gas,而 gas 恰恰在市场繁忙时飙升;TRC-20 和 Solana 通道更快更便宜,却并非处处都支持;而每家交易所又都加上自己的提现处理队列,它会从几分钟拉长到几小时——恰恰是在波动事件期间,而那正是你的路由器最想把存货挪走的时候。完整的转账成本算术——费用、延迟分布,以及你在途中承担的价格风险——在 资金费率跨交易所套利那篇文章 里已经算清楚了,而它可以逐字迁移过来:一次 SOR 再平衡与一次套利腿的转账是同一个对象,连成本一侧都一样。

这也是为什么在这里要内化的学术成果不是一篇执行论文,而是 Makarov and Schoar(2020),"Trading and Arbitrage in Cryptocurrency Markets"(Journal of Financial Economics 135(2), 293–319)。他们记录了持续数天到数周的跨交易所价格偏离——包括 2018 年初一度超过 40% 的韩国"泡菜溢价"——并证明交易成本无法解释它们;能解释的是缓慢移动、受资本管制的套利资本。加密场所不是 O'Hara–Ye 那个"带有多个入口的单一虚拟市场"。它们是由缓慢、昂贵的管道连接起来的部分分割的池子,而你的路由器就活在那种分割之中。一个没有资金库模型的加密 SOR,是一个 cosplay 的股票 SOR。

快速路由环在余额之内做配置,缓慢资金库环重新布置存货

在实践中,这会变成一个双时间尺度的控制器。快环(毫秒级)在当前余额之内,为每一个子订单求解那个受约束的水填充。慢环(分钟到小时级)盯着影子价格 ηv\eta_v 的时间序列和预测流量,并在 ηv\eta_v 的持续性分量清过转账成本门槛时安排转账——带有滞回,因为在噪声上把存货在场所之间来回乒乓,正是你把自己的优势捐给 Tron 网络的方式。机构交易台用场外结算(Copper ClearLoop、Ceffu MirrorX)来压缩这个问题:抵押品放在托管方那里,并被镜像到各场所,这对受支持的场所大幅缩短了转账延迟——它收窄了约束,但并不删除约束,而且它引入了自己的一条交易对手方费用项。

Maker 感知路由与跨场所排队博弈

一个只会跨价差的路由器,是把最便宜的流动性——你自己的——留着没买。Cont–Kukanov 框架早已包含答案——他们的单场所闭式解,基于费用、排队位置与执行风险厌恶,把一笔订单在挂单与拿单之间拆分——而多场所版本对它做了推广:在(maker 费、排队长度、子单截止时限内的成交概率)占优的场所上被动挂单,在即时性便宜的场所上拿单,并把未成交的被动残余,当作在截止时刻重新进入 taker 优化的流量来处理。

两个加密特有的皱褶,使这件事比股票版本更丰富。

追逐费用是一个被测量出来的陷阱。 Battalio, Corwin, and Jennings(2016),"Can Brokers Have It All? On the Relation between Make-Take Fees and Limit Order Execution Quality"(Journal of Finance 71(5)),证明把限价单路由到返佣最高场所的美国经纪商,交付的成交在可测量的意义上更差——更低的成交率、更差的实现质量——因为那个返佣场所正是每一个其他返佣追逐者的订单也待着的地方:最长的队伍、最逆向的成交。加密里的对应完全一致。付出最优 maker 返佣的场所,吸引来每一个做市商的被动报价;你的订单加入一条深长的队伍,而它主要在价格即将穿过你时成交。分场所的扣除 markout 后的 maker 经济学(见下一节),经常把各场所的排名排得与其费率表恰好相反。

延迟偏斜是一场双向博弈。 van Kervel 记录为防御性撤单的那同一股跨场所信息流,从另一侧看是一个进攻性信号:Binance 盘口顶端的一笔成交,预示着 OKX 对应价位在数毫秒内的成交与撤单。因此一个 maker 感知的路由器必须(a)在其他场所的事件上重新给自己挂着的订单定价——锚定到一个跨场所的微观价格,而不是本地中间价——否则它就会变成那个被跨场所套利者宰割的慢速交易对手;并且(b)它可以刻意地玩这场博弈:在滞后的场所上挂单,在成交的那一刻在领先的场所上对冲。那就是跨场所排队套利,它与 跨交易所套利执行 中被利用的延迟结构是同一个,只不过嵌在一个执行委托而非一个统计套利账本之内。运营要求是完全一致的:场所 A 上的成交事件与发往场所 B 的对冲单,必须活在同一条个位数毫秒的代码路径里,否则那份优势就归了别人。

度量路由质量:markout 与排行榜

股票路由器受 Rule 605/606 披露规范的约束。没有任何东西约束你的路由器,除了你自己的 TCA——来自 执行落差与 TCA 的框架就是记分牌;这里是它专属于路由器的那一片。

原子级的度量是分场所 markout:对每一笔成交,记录在 t+Δt + \Delta 时刻(对 Δ{100ms,1s,10s,1min}\Delta \in \{100\text{ms}, 1\text{s}, 10\text{s}, 1\text{min}\})的(合并的、延迟校正后的)中间价,并带符号,使得负值意味着市场朝着不利于你成交的方向变动。汇总成一张全包排行榜:

场所 Taker 成交占比 有效价差 (bps) 费用 (bps) 1s Markout (bps) 全包成本 (bps)
A 46% 1.4 2.0 −0.6 4.0
B 31% 1.9 1.0 −2.1 5.0
C 23% 1.2 7.5 −0.4 9.1

费用那一列说 B 是便宜的场所。全包那一列说 B 是的场所:它那些陈旧报价专挑市场已经在朝它们穿过时把你选择性成交,而 1 秒的 markout 把这笔账收了回来。这种反转——费用排名对有效成本排名——是各交易台第一次构建这张表时最一致的发现,而它恰恰就是应该回填进路由器的那个数字:凸规划里的 λv\lambda_v 惩罚项,就是那个持续性的分场所 markout 亏空,被测量、被平滑、被更新。路由器与 TCA 构成一个闭环,否则两者都不奏效。

一个方法论上的陷阱:从一个运行中的路由器构建出来的排行榜,会被选择偏差污染。如果路由器已经把困难的、有信息含量的流量送去深度场所,把容易的流量送去便宜场所,那么深度场所的 markout 就会显得不公平地糟。干净的修法是刻意的随机化——把百分之几的子单以均匀随机方式路由(一个 ε\varepsilon-greedy 路由器,量化交易者会认出这是一个带凸优化先验的 bandit)——这样一个反事实就存在了。它在随机化的那一小片上要花掉一些基点,而它是让其余 95% 的流量可被证明地路由良好的唯一途径。

于是,这套技术栈是:一个延迟诚实的合并订单簿;一次在净边际成本上、带测量出来的蒸发与毒性参数的凸水填充;一个受余额约束的快环,其影子价格驱动一个感知转账成本的慢环;尊重排队与跨场所信息流的被动挂单;以及随机化的、基于 markout 的 TCA,把每一个参数反馈回去。这些组件没有一个是单独看很深的。系统才是——因为在加密里,不像股票,没有任何监管者替你造好其中任何一层,而市场会对每一个子订单收取 2 bps,永远收下去,直到你自己动手为止。

免责声明:本文提供的信息仅用于教育和参考目的,不构成财务、投资或交易建议。加密货币交易涉及重大损失风险。

Authors

Eugen Soloviov
Eugen Soloviov

Trading-systems engineer

Trading-systems engineer building bots since 2017: cross-exchange arbitrage (connected up to 30 venues), cointegration-based pairs arbitrage across spot and futures, scalping, news and sentiment-driven strategies, trend algorithms, and portfolio management and balancing algorithms. Also builds sub-millisecond order execution, big-data warehouses, backtesting engines, AI agents, and trading interfaces (incl. open-source profitmaker.cc). Stack: JS/TS, Python, Rust/Zig/Go, DevOps, backend, frontend, architecture.

Newsletter

紧跟市场步伐

订阅我们的时事通讯,获取独家 AI 交易见解、市场分析和平台更新。

我们尊重您的隐私。您可以随时退订。