一、订单簿系统概述
什么是订单簿
订单簿,说白了就是一张「买卖挂单表」。
我刚开始接触金融系统时,觉得这名字挺唬人。后来发现,它本质上就是一个实时更新的价格-数量映射表。买方报出想买的价格和数量,卖方报出想卖的价格和数量,系统把这些订单按价格排序,就形成了订单簿。
举个例子。假设现在有几个人想买苹果:
- A 说:我出 5 元,买 10 个
- B 说:我出 4.9 元,买 20 个
- C 说:我出 4.8 元,买 15 个
另外几个人想卖:
- X 说:我 5.1 元卖 10 个
- Y 说:我 5.2 元卖 25 个
- Z 说:我 5.3 元卖 30 个
把这些信息按价格排好,就是订单簿。买方这边叫「买盘」(Bid Side),卖方那边叫「卖盘」(Ask Side)。中间那个价格差,叫「价差」(Spread)。
订单簿在金融交易中的核心作用
订单簿到底有多重要?我这么跟你说吧——没有订单簿,交易所就没办法运转。
它的核心作用有三个:
- 价格发现——买卖双方通过订单簿博弈,最终形成市场公认的价格。我在做期货系统时,经常盯着订单簿看,能明显感觉到大资金在某个价位「筑墙」。
- 流动性展示——订单簿告诉你,在什么价位能买到多少货。深度好的市场,大单砸进去价格波动很小;深度差的市场,几万块就能把价格打飞。
- 交易撮合——系统根据订单簿的排队规则,把买单和卖单配对。谁先来、谁价优,谁就先成交。
嗯,这里要注意一点:订单簿展示的是「限价单」,不是所有订单。市价单不会出现在订单簿里,它直接跟订单簿上的对手单成交。
常见订单类型
做交易系统这么多年,我接触过的订单类型少说也有几十种。但最核心的,其实就两种。
限价单(Limit Order)
限价单就是「指定价格下单」。你说 5 元买,那就只能 5 元或更低的价格成交。卖单同理。
限价单会进入订单簿排队。价格优先,时间其次。什么意思?
- 买单:出价高的排前面
- 卖单:出价低的排前面
- 价格相同:先来的排前面
我曾经遇到过一个坑:某个客户下了大量限价单,价格设得跟市场价差 1 分钱。结果行情剧烈波动,这些单子全部成交,客户亏损严重。所以限价单不是「安全单」,它只是「指定价格单」。
市价单(Market Order)
市价单不指定价格,只指定数量。系统会以当前最优价格帮你成交。
比如你想买 100 股,当前卖一价是 5.1 元,卖二价是 5.2 元。市价买单会先吃掉卖一的 50 股,再吃掉卖二的 50 股。成交均价是 (5.1×50 + 5.2×50) / 100 = 5.15 元。
订单簿数据结构
好了,理论说完了,咱们看看实际怎么存。
订单簿的数据结构,核心就是两个队列:
- 买盘队列(Bid Queue):按价格降序排列(出价高的在前)
- 卖盘队列(Ask Queue):按价格升序排列(出价低的在前)
每个队列里的元素,通常包含:价格、总数量、订单列表。
我习惯用这样的结构来表示:
// 订单簿核心数据结构(伪代码)
struct OrderBook {
PriceLevel[] bids; // 买盘,按价格降序
PriceLevel[] asks; // 卖盘,按价格升序
}
struct PriceLevel {
double price; // 价格
long totalQuantity; // 该价格总数量
Order[] orders; // 该价格下的订单列表(按时间排序)
}
struct Order {
string orderId; // 订单ID
string side; // 方向:buy/sell
double price; // 价格(市价单为0)
long quantity; // 数量
long filledQuantity; // 已成交数量
long timestamp; // 下单时间戳
}
你想想看,为什么买盘要降序、卖盘要升序?
因为撮合时,系统要快速找到「最优价格」。买单的最高价、卖单的最低价,就是最优价。所以买盘队列头是最高价,卖盘队列头是最低价。这样取第一个元素就能拿到最优价,时间复杂度 O(1)。
下面这张图,是我画的一个简化版订单簿结构:
这张图里,买盘最高价是 5.00,卖盘最低价是 5.01。中间差了 0.01 元,这就是价差。任何一笔新的买单,如果价格 ≥ 5.01,就会立即跟卖盘成交;反之,如果卖单价格 ≤ 5.00,就会跟买盘成交。
我记得有一次做性能优化,发现订单簿的插入操作成了瓶颈。后来把价格队列从数组换成了跳表,插入性能提升了 3 倍。所以数据结构选型,真的会影响整个系统的吞吐量。
公众号:蓝海资料掘金营,微信deep3321