一、订单簿基础:什么是订单簿、限价单与市价单、买卖盘口与价差、订单簿数据结构

大家好,我是老张。今天咱们聊聊订单簿——这个量化交易里最基础、也最核心的东西。

说实话,我刚开始做量化那会儿,觉得订单簿不就是个买卖挂单列表嘛,有啥好研究的?后来在实盘里吃过亏,才明白这东西的水有多深。嗯,咱们一步步来。

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