限价订单簿动态:价格优先与时间优先,以及你的排队位置
订单簿不是一张静态列表,是一条队列。撮合引擎怎么排序、你排在第几,决定了谁先成交、谁付点差、谁只能干瞪眼。
市面上大部分讲交易的文章会把订单簿描述成一张静态列表:买价在一边,卖价在另一边,点差夹在中间。给入门看看可以。但真正重要的东西全被漏掉了。
订单簿本质上是个队列。每一笔挂着的限价单都排在一个价格上,你的单子能不能成交,取决于三件事:你排在哪儿;你排队的时候别人在干什么;撮合引擎认不认为有人跟你交叉。这三件事贯穿全文——最后你会看到,它们合起来把”挂单还是吃单”变成一道可以算期望值的题,而且算出来的答案经常让人不舒服。
交易所运行的两条铁律
每家现代交易所(NYSE、Nasdaq、Cboe、CME、Binance、Coinbase、EBS,甚至那些号称”没有订单簿”的暗池)都跑在同一套规则下。价格优先:最优价格先成交,100.02 美元的买单排在所有 100.01 美元的买单前面。时间优先:同一个价格上,先到的单子先成交,先进先出。(NYSE 历史上有过例外,当时由场内经纪人酌情分配,但合并行情看起来还是 FIFO。)
Harris 把这两条叫做交易所的交易规则(Harris,《Trading and Exchanges》,第 4 章)。听起来像废话,其实不是——因为确实有交易所打破其中一条。按比例成交(pro-rata)市场(部分期货交易所,历史上 CME 的某些品种)在同一价格上按挂单量比例分配成交,而不是按时间:你挂 10 手、别人挂 30 手,每笔进来的市价单你都只吃到 25%。还有少见的数量优先(同价偏好大单)和混合制(盘口按比例、盘口下方 FIFO)。
如果你交易的是一个 pro-rata 产品,整个思维框架都得换:你不该追求排到队列最前面,而应该在 touch 上挂一个大单。本文后面不展开这条支线,因为主流的股票和加密交易所都是纯价格-时间制。但请记住,“订单簿到处都这样”是一句谎话。
一笔挂单的五要素
一笔挂着没动的限价单有五个属性决定它的命运:方向(买或卖)、价格(持有人能接受的最差价格)、数量、到达时间(进系统的时刻,也就是 FIFO 时间戳)、订单 ID(交易所分配的标识符,用来处理冲突和追踪单子的一生)。
当一笔可成交订单(marketable order)到达——买价 ≥ 最优卖价,或卖价 ≤ 最优买价——撮合引擎就从对面那侧的最优价格开始往外逐层吃单,每个价格层内部按时间顺序吃。一句话讲完撮合算法;Harris 整本第 4 章就是把这句话展开。
排队位置的数学
假设你在 100.00 美元挂了一笔买单,加入了一个队列。100.00 这条上排在你前面一共有 股,你自己的单量是 。对面吃单事件以每秒 次的速率到达(每个事件是别人的市价卖单打到买价上,消掉队列里的一些量)。
你的期望成交时间是:
其中 是你等待期间 100.00 始终是最优买价的概率。这个概率是麻烦的部分。你干等的时候,价格可能跑掉:有人扫掉卖一,新的最优买价变成 100.01,这时候哪怕你到得早,也已经落在新价格层的后面了。
想给这个概率一个粗算,把中间价建模成波动率为 的随机游走。在水平期 内,中间价向任一方向移动超过半个点差的概率大致是:
其中 是以价格单位衡量的点差, 是标准正态 CDF(系数 2 就是”两个方向都算”)。为什么两个方向都算坏事?往上走,你永远成交不了、眼看行情跑掉;往下走,你倒是成交了——在下跌途中接了货,这叫逆向选择,第 4 篇细讲。这个信封背面的估算告诉你:点差宽且波动低,你等到天荒地老;点差窄且波动高,你要么撤单要么被行情碾过去。
实操要点:行情快的时候,排队位置是一种会衰减的资产。每多等一秒,条件成交概率都在下降。
订单交叉时会发生什么
看个具体的。一笔买单以 100.02 美元到达。订单簿如下:
ASK $100.02 | 500 shares
ASK $100.03 | 800 shares
BID $100.01 | 200 shares
BID $100.00 | 1200 shares
你这笔 100.02 的买单是可成交的——它等于或高于最优卖价。撮合引擎立即把它和挂在 100.02 的 500 股撮合,你用 100.02 美元买到 500 股(如果你下的量超过 500,剩下的按 100.03 继续吃)。
挂在 100.01 的 200 股买单不参与成交。它们站错了边。撮合引擎不关心 100.01 < 100.02 这个逻辑关系;它只关心买是买、卖是卖,必须买价够到卖价才能撮。
但如果有人下了一笔可成交的限价卖单,价格 100.01 呢?这单会和 100.01 的买价交叉(100.01 ≤ 100.01),在 100.01 成交 200 股。剩余量呢?它的限价是 100.01,够不到下一层 100.00 的买价,于是剩余量以 100.01 挂回簿上的卖侧,成为新的最优卖价。这个行为叫扫单(sweeping):可成交订单逐价格层扫过去,扫到自己限价够不到的那层为止,未成交的残余以限价挂回簿上。一笔可成交的限价单,机制上就是”市价单 + 残余限价单”,这也是 fill-or-kill 这类指令必须在进入时就指明全成或全撤的原因。(Harris,第 4 章,关于订单的生命周期。)
防自成交的边界情况
现代交易所会跟踪你自家的挂单,拒绝自己跟自己撮合。如果你在簿上挂着一笔 100.00 美元的买单,然后又下了一笔 100.01 美元的买单,撮合引擎要么撤销你老的 100.00 单、把新的挂上去(替换行为),要么拒绝跟自己的单成交、把新单排在后面,要么直接把新单拒掉。各家交易所做法不一样。
这是执行算法里很多诡异行为的根源。如果你见过 TWAP 的某个子单莫名”跳过”,多半是因为算法在下可成交买单的时候,同一父单的一笔限价卖单还挂在簿上。
排队位置本身是种资产
这一点散户基本不会去想。你在队列里的位置是一种资产。 不上你的资产负债表,但它有经济价值:这个价值等于你能拿到的价格与排在更后面的人会拿到的价格之差。如果买价上你排第 10 名、点差 0.01 美元,那比起排第 1000 名的人,你大概相当于握着一跳的”插队补偿”——当最优买价被打掉、你的单子成为新的最优买价时,你就拿到了一格价格改善。
这就是 maker-taker 收费结构存在的原因。交易所给挂单方(簿上的限价单)返佣,向吃单方(拿走流动性的市价单)收费;maker 返佣是对排队风险的部分补偿。股票市场上 maker 返佣很小(每股不到一分钱),加密市场大得多——Binance 历史上给过 0.01%–0.02% 的 maker 返佣,对高频策略是真金白银。
你做做市策略的话,应该清楚自己在每一 tick 的期望排队位置和每秒期望成交率。这条线的数学在 Harris 第 13 章和学术文献里都有(参见 Parlour,“Price Dynamics in Limit Order Markets”,1998;Handa、Schwartz、Tiwari,2003)。
实操中如何估算成交概率
把前面两个量(队列消耗速度、价格漂移风险)拼在一起,就是一个还算合理的成交概率估算器:
import numpy as np
from scipy.stats import norm
def fill_probability(queue_ahead, my_size, events_per_sec, vol_per_sec,
spread, horizon_sec):
"""
在点差收窄或价格反向之前,
挂在 touch 的限价单成交的概率。
"""
# 前方队列清空所需期望时间
eta = (queue_ahead + my_size) / max(events_per_sec, 1e-9)
# 中间价在 eta 秒内上移半 tick 的概率
# (假设点差不变、touch 失效)
drift_std = vol_per_sec * np.sqrt(eta)
prob_uptick = norm.cdf(spread / 2 / max(drift_std, 1e-9))
return float(np.clip(1.0 - prob_uptick, 0.0, 1.0))
# 玩具示例:前 500 股、自己挂 100、4 单/秒、
# vol = 0.005 $/sqrt(s)、点差 0.02、水平期 60 秒
print(fill_probability(500, 100, 4, 0.005, 0.02, 60))
跑一下你会看到,对典型的短水平期参数,成交概率大概落在 30–80% 之间。这不是研究级的模型——要做研究你得上 Hawkes 过程模拟事件到达、用跳跃扩散模拟中间价——但主要直觉它都覆盖了。
为什么排队位置决定策略选择
同一个量,在不同水平期上的重要性天差地别。你做长线投资,排队位置就是噪音:市价单成交、付几个基点的点差,你根本不在乎。你做高频做市,排队位置就是一切:共置、更快的撤改、抢占盘口,巨大的工程投入全是为了往队列前面挪,因为排队位置的每一微秒都是期望盈亏。
零售和机构的执行算法处在中间,必须做选择:被动挂单(吃限价、赚点差、承担不成交风险),还是主动吃单(市价单、付点差、保证成交)?答案取决于父单紧急程度、簿上深度、波动率、时间段。这就是执行算法存在的根本原因——后面讲 Almgren-Chriss、VWAP 和 implementation shortfall 的文章,全部可以读成这一句话的展开:每一笔执行决策,本质上都是排队位置决策。
撤单的隐形代价
你在成交之前撤销一笔限价单,没人收你费。但你付出了别的东西:信息。撤得很快,市场可能推断出你丢了优势(或者根本没等到成交)。高频做市商会把撤单率当作毒性(toxicity)信号盯着。Harris 第 13 章关于 dealer 行为那部分专门讨论这个——它也是纯粹”挂单祈祷”策略总被逆向选择的原因之一。
真账本里的真数字
来点真实量级的数字。你在买价上挂 200 股买单。买价 100.00,卖价 100.01。前方队列 800 股。接下来 60 秒里,你看到 12 笔市价卖单打在买价上(每笔消掉队列里一些量),另有 4 笔挂单撤掉。现实的队列消耗事件率 /秒。
不算价格漂移,期望等待时间:
差不多一个小时。一小时里价格大概率不会老实待着。如果日波动 0.02 美元、交易日 6.5 小时,每秒波动率约 美元;一小时的典型漂移是 美元,而半点差只有 0.005 美元。中间价在一小时内向任一方向移动超过半点差的概率:
而两个方向都不是好事:往上你白等,往下你在接刀。你大约只有 48% 的机会在价格还没跑之前干净成交。抛硬币。0.0002 美元的 maker 返佣(股票的常见水平)或 0.002%(加密的常见水平),值不值这个赔率?单笔看几乎不值。挂单的每股期望收益:
其中 adverse 是价格跑掉时你多亏的半点差甚至更多。这个期望值经常落在零附近或略负——裸做市流血就是这么流的。怎么把它做成正的(用报价偏移管理库存和逆向选择),是 Avellaneda-Stoikov 那篇的主题。
Arithmion SDK 对簿干了什么
如果你用 Arithmion 跑执行算法回测,模拟器必须建模排队位置才能给出真实的成交。arithmion.microstructure 里的 OrderBookSimulator 为每笔挂单维护按订单 ID 的时间戳,在每个价格层内按 FIFO 撮合可成交订单,这是基线。想更真实,可以启用撤改竞速(进来的撤单可能”晚了”,因为同价的市价单已经开始撮合)、部分扫层后的优先权保留(市价单扫过一层时,剩余挂单保留原时间优先权)、以及逐单延迟(每笔订单带自己的时间戳,按到达顺序撮合)。
这就是学术回测和现实回测的差距。多数散户回测假设你想成交就能成交——看完这篇你知道,那是幻想:订单簿是队列,排队位置就是资产,而大多数回测把这份资产当成了免费的。
参考文献
- Harris, L. Trading and Exchanges: Market Microstructure for Practitioners. Oxford University Press, 2003. 第 4 章(交易规则、订单类型与撮合),第 13 章(dealer 行为)。ISBN: 978-0-19-514470-3.
- Parlour, C. “Price Dynamics in Limit Order Markets.” Journal of Financial Economics, 1998.
- Handa, P., Schwartz, R., Tiwari, S. “Quote Setting and Price Formation in an Order Driven Market.” Journal of Financial Markets, 2003.