一、订单簿基础:什么是订单簿、限价单与市价单、买卖盘口与价差、订单簿数据结构
大家好,我是老张。今天咱们聊聊订单簿——这个量化交易里最基础、也最核心的东西。
说实话,我刚开始做量化那会儿,觉得订单簿不就是个买卖挂单列表嘛,有啥好研究的?后来在实盘里吃过亏,才明白这东西的水有多深。嗯,咱们一步步来。
1.1 什么是订单簿?
订单簿,英文叫 Order Book。说白了,就是交易所用来记录所有未成交订单的电子账本。
你想想看,你去菜市场买菜,有人喊价5块,有人还价4块。交易所就把这些喊价和还价都记下来,按价格排好队。这就是订单簿。
我个人习惯把订单簿理解成「市场的实时温度计」。它告诉你:
- 现在有多少人想买?出什么价?
- 有多少人想卖?要什么价?
- 买卖双方的力量对比如何?
核心要点:订单簿是市场微观结构的核心。所有价格发现、流动性提供,都围绕它展开。
1.2 限价单与市价单
这是订单簿里的两种基本订单类型。我经常跟团队新人说:搞懂这两个,你就入门了一半。
限价单(Limit Order)
限价单,就是你指定一个价格,只有市场达到这个价格时才会成交。
举个例子:BTC现价30000,你觉得跌到29500是机会,就挂一个买入限价单,价格29500,数量1个。市场跌到29500,单子就成交。没到?那就等着。
限价单的特点:
- 价格可控——你说了算
- 成交不确定——可能等很久,也可能永远不成交
- 提供流动性——你在订单簿里挂了单,别人可以吃你的单
我的经验:做高频做市策略时,限价单是我们的主力。我曾经在ETH上挂过一张限价单,挂了整整3天才成交。那3天里,价格上上下下,但我的单子始终在那里。这就是做市商的耐心。
市价单(Market Order)
市价单,就是「不管什么价格,我现在就要成交」。
还是那个例子:BTC现价30000,你急着买入,直接下市价单。系统会从卖一、卖二、卖三...一路吃上去,直到买够数量。
市价单的特点:
- 成交确定——100%成交
- 价格不确定——可能滑点,尤其大单时
- 消耗流动性——你把别人的挂单吃掉了
避坑指南:我曾经在流动性差的币种上用过市价单,结果滑点高达2%。那笔交易亏得我肉疼。后来我学乖了:流动性差的时候,宁可用限价单慢慢等,也别用市价单去冲。
1.3 买卖盘口与价差
订单簿里,买方的最高出价叫「买一」,卖方的最低要价叫「卖一」。买一和卖一之间的价格差,就是价差(Spread)。
举个例子:
| 盘口 | 价格 | 数量 |
|---|---|---|
| 卖五 | 30010 | 2.5 |
| 卖四 | 30008 | 1.2 |
| 卖三 | 30005 | 3.0 |
| 卖二 | 30003 | 0.8 |
| 卖一 | 30001 | 1.5 |
| 买一 | 29999 | 2.0 |
| 买二 | 29997 | 1.8 |
| 买三 | 29995 | 3.2 |
| 买四 | 29992 | 0.5 |
| 买五 | 29990 | 4.0 |
这个例子里,价差 = 30001 - 29999 = 2。也就是说,你如果想立即买卖,至少要付出2个点的成本。
价差越小,说明市场流动性越好。BTC/ETH这些主流币,价差通常只有0.01%。一些小币种,价差可能高达1%甚至更多。
关键认知:价差就是你的交易成本。做高频策略时,价差直接决定了你能不能赚钱。我见过太多策略,回测跑得飞起,一上实盘就亏,原因就是没把价差算进去。
1.4 订单簿数据结构
好了,理论说完了。咱们来看看代码里订单簿长什么样。
我个人习惯用Python的字典和列表来模拟订单簿。结构其实很简单:
class OrderBook:
def __init__(self):
self.bids = {} # 买单:价格 -> 数量
self.asks = {} # 卖单:价格 -> 数量
def update(self, side, price, quantity):
"""更新订单簿"""
if side == 'buy':
if quantity == 0:
self.bids.pop(price, None) # 数量为0,删除该价格
else:
self.bids[price] = quantity
else:
if quantity == 0:
self.asks.pop(price, None)
else:
self.asks[price] = quantity
def get_top_of_book(self):
"""获取最优买卖价"""
best_bid = max(self.bids.keys()) if self.bids else None
best_ask = min(self.asks.keys()) if self.asks else None
return best_bid, best_ask
def get_spread(self):
"""计算价差"""
best_bid, best_ask = self.get_top_of_book()
if best_bid and best_ask:
return best_ask - best_bid
return None
实际交易所的订单簿数据,通常通过WebSocket推送。数据格式一般是这样的:
{
"type": "snapshot", // 全量快照
"bids": [
[29999, 2.0], // [价格, 数量]
[29997, 1.8],
...
],
"asks": [
[30001, 1.5],
[30003, 0.8],
...
]
}
// 增量更新
{
"type": "update",
"bids": [
[29999, 0], // 数量为0,表示删除该价格
[29998, 3.5] // 新增或更新
],
"asks": [
[30001, 2.0] // 更新卖一的数量
]
}
我的建议:处理订单簿数据时,一定要处理好全量快照和增量更新的关系。我见过有人直接用增量更新覆盖本地数据,结果因为丢包导致数据错乱。正确的做法是:先拿快照,再用增量更新去修正。每次重连后,都要重新获取快照。
知识体系总览
下面这张图,是我自己画的订单簿知识体系。你看一眼,心里就有数了:
嗯,订单簿的基础就这些。记住一句话:订单簿是市场的骨架,价格是血肉,成交量是脉搏。搞懂它,你才算真正入了量化的门。
公众号:蓝海资料掘金营,微信deep3321