订单簿结构:限价单与市价单、订单簿的构建、买卖盘口深度
做市商的核心战场在哪里?
就在订单簿上。你想想看,我们每天跟价格打交道,本质上就是在跟订单簿里的每一笔挂单博弈。我个人习惯把订单簿比作一个「战场沙盘」——谁在哪个价位埋伏了多少兵力,一目了然。
限价单与市价单:两种最基本的武器
先聊聊这两种订单类型。说实话,很多新手搞不清它们的本质区别。
限价单(Limit Order),说白了就是你指定一个价格,挂在那里等对手来吃。比如我挂一个「买入 BTC,价格 30000 USDT」,那只有市场价格跌到 30000 或以下时才会成交。它的特点是:价格确定,成交不确定。
我在项目中遇到过一个问题:某次做市策略里,我挂了大量限价买单,结果市场快速下跌,我的单子全被吃掉,但价格还在往下砸。嗯,这就是限价单的风险——你成了别人的「接盘侠」。
市价单(Market Order)就简单粗暴多了。你不管价格,直接按当前最优价成交。比如「买入 1 BTC,市价」,系统会立刻吃掉卖一、卖二、卖三……直到买够为止。它的特点是:成交确定,价格不确定。
核心区别一句话:
- 限价单 = 我要这个价,爱卖不卖
- 市价单 = 我要马上成交,贵点也行
做市商主要用限价单来提供流动性,用市价单来调整仓位。我建议你记住这个原则:做市靠限价,对冲靠市价。
订单簿的构建:从零开始搭一个
订单簿长什么样?其实就是两个列表:
- 买单队列(Bids):从高到低排列,价格高的优先
- 卖单队列(Asks):从低到高排列,价格低的优先
每个价格点上,都挂着一堆订单。价格相同的话,先到先得。
我曾经踩过一个坑:刚开始写订单簿时,我用 Python 的 dict 来存每个价位的订单列表。结果发现,当价格精度很高时(比如 0.01 的 tick size),dict 的 key 会膨胀得特别快,内存直接炸了。后来我改用 sortedcontainers 库里的 SortedDict,才解决了这个问题。
下面是一个简化版的订单簿构建代码:
from sortedcontainers import SortedDict
from collections import deque
import time
class OrderBook:
def __init__(self):
# 买单:价格降序排列
self.bids = SortedDict(lambda x: -x)
# 卖单:价格升序排列
self.asks = SortedDict()
# 每个价格点的订单队列
self.orders = {}
def add_order(self, order):
"""添加一个限价单"""
price = order['price']
side = order['side']
# 选择对应的订单簿
book = self.bids if side == 'buy' else self.asks
# 如果这个价格还没有队列,新建一个
if price not in book:
book[price] = deque()
self.orders[price] = []
# 记录订单信息
order_id = f"{side}_{price}_{time.time_ns()}"
order['id'] = order_id
book[price].append(order)
self.orders[price].append(order)
return order_id
def get_top_of_book(self):
"""获取最优买卖价"""
best_bid = next(iter(self.bids.items())) if self.bids else None
best_ask = next(iter(self.asks.items())) if self.asks else None
return best_bid, best_ask
def get_depth(self, levels=5):
"""获取指定深度的盘口"""
bid_depth = []
ask_depth = []
for price, queue in self.bids.items():
if len(bid_depth) >= levels:
break
total_qty = sum(o['qty'] for o in queue)
bid_depth.append((price, total_qty))
for price, queue in self.asks.items():
if len(ask_depth) >= levels:
break
total_qty = sum(o['qty'] for o in queue)
ask_depth.append((price, total_qty))
return bid_depth, ask_depth
小技巧:实际生产环境中,订单簿的更新频率极高(微秒级)。用 Python 的 deque 做队列,插入和删除都是 O(1) 的,比 list 快很多。我一般还会加一个 order_id 到订单对象的映射,方便快速撤单。
买卖盘口深度:看懂市场的「厚度」
盘口深度,说白了就是每个价位上到底有多少单子。它告诉你两件事:
- 流动性好不好——深度越厚,大单对价格的冲击越小
- 支撑和阻力在哪——买单密集区是支撑,卖单密集区是阻力
我习惯用「深度图」来直观感受市场情绪。举个例子:
| 价格 (USDT) | 买单数量 (BTC) | 卖单数量 (BTC) |
|---|---|---|
| 30050 | — | 12.5 |
| 30040 | — | 8.3 |
| 30030 | — | 5.1 |
| 30020 | 3.2 | — |
| 30010 | 7.8 | — |
| 30000 | 15.6 | — |
看到没?30000 这个价位有 15.6 BTC 的买单撑着,而 30030 以上卖单开始变多。这说明什么?市场在 30000 附近有强支撑,但往上突破 30030 需要吃掉不少卖单。
做市商怎么利用这个信息?
- 在深度厚的地方挂单——不容易被吃掉,还能赚点手续费返还
- 在深度薄的地方小心——一个大单就能把价格打穿,你的止损可能来不及
- 观察深度变化——如果买单深度突然减少,说明有人在撤单,可能要跌
注意:盘口深度是会骗人的。有些大单是「冰山订单」,只显示一部分。还有些是「虚假挂单」,挂上去又撤掉,就是为了引诱你进场。我吃过这个亏——看到卖一有 100 BTC 压着,以为涨不上去,结果人家一撤单,价格直接飞了。
最后说一句,订单簿是动态的。每一毫秒都在变化。你看到的深度,只是那一瞬间的快照。真正的高手,看的是深度变化的趋势,而不是绝对值。
嗯,这一章就到这里。下一章我们聊聊「价差与库存管理」,那是做市商真正赚钱的核心。