一、订单簿基础:限价单与市价单、买卖盘口、订单簿数据结构
做量化交易这些年,我见过不少新手一上来就盯着K线图猛看,却忽略了最底层的东西——订单簿。说白了,订单簿就是市场的「实时账本」,每一笔成交、每一次价格跳动,都源于这里。今天我们就从最基础的东西聊起。
1.1 两种最基础的订单类型
先问个问题:你想买比特币,是直接按市场价买,还是挂个低价等着?这两种操作,对应了两种最核心的订单类型。
市价单(Market Order)
市价单,就是「我现在就要成交,不管价格多少」。你下单的那一刻,系统会立刻帮你吃掉对手盘上最便宜的卖单(如果你是买入)。
- 优点:成交速度快,几乎瞬间完成
- 缺点:价格不可控,遇到流动性差的时候,滑点会让你怀疑人生
限价单(Limit Order)
限价单,就是「我只愿意在这个价格买/卖,不到这个价我就不动」。你的订单会挂在订单簿上,等着别人来吃。
- 优点:价格可控,还能赚到买卖价差(如果你是做市商)
- 缺点:可能永远成交不了,或者只成交一部分
1.2 买卖盘口:Bid与Ask
打开任何交易软件的深度图,你会看到左右两边的柱状图。左边是买盘(Bid),右边是卖盘(Ask)。
- Bid(买方):所有人愿意出的最高买入价。比如Bid价格是100,意味着有人愿意花100块买。
- Ask(卖方):所有人愿意卖的最低卖出价。比如Ask价格是101,意味着有人愿意101块卖。
Bid和Ask之间的差距,叫「价差」(Spread)。价差越小,说明市场流动性越好。我一般看一个币种是否值得做,先看价差——如果价差超过0.1%,我基本就放弃了。
1.3 订单簿的数据结构
好了,现在我们来点硬核的。订单簿在计算机里是怎么存的?
说白了,就是一个「价格-数量」的映射。每个价格点,对应一个挂单总量。我习惯用两个数据结构:
- 买盘(Bids):按价格从高到低排序。最高价在最前面。
- 卖盘(Asks):按价格从低到高排序。最低价在最前面。
为什么这么排?因为成交逻辑是「价格优先,时间优先」。买单出价高的先成交,卖单出价低的先成交。
下面是我用Python模拟的一个简单订单簿结构:
class OrderBook:
def __init__(self):
self.bids = {} # 买盘:{价格: 数量}
self.asks = {} # 卖盘:{价格: 数量}
def update(self, side, price, quantity):
if quantity == 0:
# 数量为0,删除该价格档位
if side == 'bid':
self.bids.pop(price, None)
else:
self.asks.pop(price, None)
else:
if side == 'bid':
self.bids[price] = quantity
else:
self.asks[price] = quantity
def get_top_bid(self):
# 获取最高买入价
return max(self.bids.keys()) if self.bids else None
def get_top_ask(self):
# 获取最低卖出价
return min(self.asks.keys()) if self.asks else None
嗯,这里要注意:实际交易所的订单簿更新频率极高,每秒可能有几百次变化。所以一般不会用Python的dict直接存,而是用跳表(Skip List)或者红黑树来维护有序性。不过原理是一样的。
1.4 订单簿的增量更新
你有没有想过,交易所是怎么把订单簿数据推送给你的?
如果每次变化都发全量数据,那带宽根本扛不住。所以交易所一般用「增量更新」——只告诉你哪些价格变了。
举个例子:
- 原来买盘在100元有10个BTC
- 有人撤单了5个,交易所推送一条消息:「买盘100元,数量变为5」
- 你的本地程序收到后,更新本地订单簿
我刚开始做的时候,没处理好增量更新的时序问题,导致本地订单簿和交易所对不上。后来加了个序列号校验,才解决。
1.5 知识体系结构图
下面这张图,是我自己梳理的订单簿知识体系。你可以把它当作本章的「地图」:
这张图把本章的核心内容串起来了。从左到右,分别是订单类型、买卖盘口、数据结构。最下面那条虚线,指向的是我们下一节要讲的内容——订单簿深度与市场冲击成本的关系。
1.6 小结
好了,这一章我们聊了:
- 市价单和限价单的区别,以及各自的坑
- Bid和Ask的含义,价差怎么看
- 订单簿的数据结构,以及增量更新的原理
这些东西看着简单,但我在实际项目中见过太多人栽跟头。尤其是增量更新的时序问题,几乎每个新手都会踩一次。嗯,希望你能少走点弯路。