订单簿基础:限价单与市价单,买卖盘口,数据结构,价格优先与时间优先

做市商这行,说白了就是跟订单簿打交道。你每天盯着屏幕,看到的那些跳动的数字、堆积的挂单,背后就是一套严谨的撮合逻辑。今天咱们就把订单簿的底裤扒干净,看看它到底怎么运作的。

一、两种最基础的单子:限价单与市价单

我刚入行时,带我的老大哥说过一句话:「你下的每一笔单,都是在跟市场对话。」限价单和市价单,就是两种完全不同的对话方式。

限价单(Limit Order),说白了就是你指定一个价格,告诉交易所:「不到这个价,我不买/不卖。」比如BTC当前价格30000 USDT,你觉得会跌到29500,就挂个29500的买单。价格没到,单子就躺在订单簿里等着。价格到了,就成交。

我个人习惯,做市商策略里90%都是限价单。为什么?因为你能控制成交价格,不会滑点。但代价是——可能永远成交不了。我见过有人挂单差0.1个tick,结果行情一路狂奔,单子晾在那儿一整天。

市价单(Market Order)就简单粗暴了。你不管价格,直接说:「我要买1个BTC,立刻马上!」交易所会从最优的卖一价开始吃,吃完了吃卖二,直到买够为止。

嗯,这里要注意:市价单的成交价格是不确定的。行情剧烈波动时,你可能吃到意想不到的价位。我曾经在ETH闪崩时下了一笔市价卖单,结果成交价比我看到的价格低了2%。那感觉,就像你急着卖房,结果中介给你找了个出价最低的买家。

核心区别一句话:限价单控制价格,不保证成交;市价单保证成交,不控制价格。

二、买卖盘口:Bid 与 Ask

打开任何一个交易界面,你都会看到左右两边的挂单列表。左边是买单,右边是卖单。专业术语里,买单叫Bid,卖单叫Ask

为什么叫Bid和Ask?Bid是「出价」,Ask是「要价」。买方在出价,卖方在要价。两者之间的差距,就是价差(Spread)

举个例子,当前盘口是这样的:

Bid(买单) 价格 Ask(卖单)
10.5 BTC @ 29950 29950 8.2 BTC @ 30000
15.3 BTC @ 29940 29940 12.1 BTC @ 30010
20.1 BTC @ 29930 29930 9.8 BTC @ 30020

这里最优Bid是29950,最优Ask是30000,价差50 USDT。做市商赚的就是这个价差——你在29950买入,在30000卖出,一进一出赚50块差价。

你想想看,如果价差太小,做市商没利润;价差太大,交易者觉得贵,流动性就差了。所以平衡价差是做市商的核心手艺之一。

三、订单簿的数据结构

聊到数据结构,很多人的第一反应是「这不就是个排序列表吗?」嗯,对了一半。但真正在生产环境里,订单簿的数据结构设计直接决定了撮合引擎的性能。

常见的实现方式有两种:

  • 红黑树(Red-Black Tree):价格作为key,按价格排序。插入、删除、查找都是O(log n)。适合价格档位多、挂单分散的场景。
  • 跳表(Skip List):概率性数据结构,实现简单,并发友好。我在做高频做市商系统时,就用的跳表。为什么?因为红黑树在并发场景下锁竞争太严重了。

下面这张图是我手绘的订单簿核心逻辑,你看一眼就明白了:

订单簿核心数据结构 买单(Bid) - 价格降序 价格: 29950 | 数量: 10.5 BTC 价格: 29940 | 数量: 15.3 BTC 价格: 29930 | 数量: 20.1 BTC 价格: 29920 | 数量: 8.7 BTC 卖单(Ask) - 价格升序 价格: 30000 | 数量: 8.2 BTC 价格: 30010 | 数量: 12.1 BTC 价格: 30020 | 数量: 9.8 BTC 价格: 30030 | 数量: 6.5 BTC 价差 撮合引擎:价格优先 → 时间优先 买单按价格从高到低排序,卖单按价格从低到高排序 最优Bid(最高买价)与最优Ask(最低卖价)之间的差距即为价差

代码层面,一个简单的订单簿节点大概长这样:

// 订单节点结构
struct Order {
    uint64_t order_id;      // 订单ID
    uint64_t user_id;       // 用户ID
    double price;           // 价格
    double quantity;        // 数量
    uint64_t timestamp;     // 时间戳(纳秒级)
    OrderSide side;         // BUY 或 SELL
    OrderType type;         // LIMIT 或 MARKET
};

注意那个timestamp字段。为什么需要纳秒级?因为在高频场景下,同一价格可能有多个订单,时间戳决定了谁先成交。我曾经遇到过系统时间精度不够,导致两个订单时间戳相同,撮合引擎直接报错。那一次排查了整整一个通宵。

四、价格优先与时间优先原则

这是订单簿撮合的黄金法则,没有之一。

价格优先:买单出价高的优先成交,卖单要价低的优先成交。说白了,谁出的价更「狠」,谁先成交。

时间优先:价格相同的情况下,先挂单的先成交。这就好比排队买奶茶,先来的先得。

举个例子你就明白了:

  • 小明挂买单,价格30000,时间09:00:00.001
  • 小红挂买单,价格30000,时间09:00:00.002
  • 小刚挂买单,价格30001,时间09:00:00.003

这时候来了一个卖单,价格30000。谁会成交?

答案是:小刚。因为他的出价更高(30001 > 30000),价格优先。虽然他是最后一个挂单的,但价格优势碾压一切。

如果小刚的买单也是30000,那才是小明先成交,因为时间优先。

避坑指南:我曾经在写撮合引擎时,把价格优先和时间优先的顺序搞反了。结果测试时发现,高价的买单反而被低价的买单「插队」了。那感觉就像你排了半天队,结果有人加塞还理直气壮。记住:先比价格,再比时间。

嗯,这里还有一个细节:市价单怎么处理?市价单没有价格,它默认以最优对手价成交。所以市价单在撮合时,相当于「价格无限好」,永远排在价格优先的第一位。但代价就是——你吃到的价格可能不是你想的。

做市商系统里,订单簿就是一切。你理解了限价单和市价单的区别,看懂了Bid和Ask的盘口,掌握了价格优先和时间优先的规则,基本上就拿到了做市商的入门钥匙。剩下的,就是在实战中不断打磨你的策略了。