一、订单簿基础:从零开始理解市场微观结构
做量化交易这些年,我见过不少新手一上来就盯着K线图猛看,却忽略了最底层的东西——订单簿。说实话,订单簿才是市场的「源代码」,K线只是它的编译结果。今天我们就从最基础的东西聊起。
1.1 什么是订单簿?
订单簿,说白了就是一个「等待成交的订单列表」。它记录了所有交易者挂出的、尚未成交的买卖意愿。
你可以把它想象成一个菜市场的大黑板:
- 左边写着「我想买,出价多少,要多少斤」
- 右边写着「我想卖,要价多少,有多少斤」
- 中间是成交价——买卖双方达成一致的地方
我个人习惯把订单簿看作市场的「心跳」。每一笔挂单、每一次撤单,都是市场参与者在用真金白银投票。我在项目中遇到过不少次,明明K线看起来风平浪静,订单簿却暗流涌动——这种时候往往有大行情要来了。
1.2 限价单 vs 市价单
这两种订单类型,是交易系统最基础的「积木」。搞懂它们,后面的策略才能搭得稳。
限价单(Limit Order)
你指定一个价格,只有市场达到这个价格时才会成交。说白了就是「不到这个价我不干」。
- 买单限价:「我只愿意在100元买入,高于100我不买」
- 卖单限价:「我只愿意在110元卖出,低于110我不卖」
嗯,这里要注意:限价单不保证立即成交,但能保证成交价格。我曾经在写策略时犯过一个低级错误——限价单价格设得太死,结果行情一路狂奔,单子一直没成交,白白错过一波行情。
市价单(Market Order)
你不指定价格,系统以当前最优价格立即成交。说白了就是「不管多少钱,我现在就要」。
- 买入市价单:吃掉当前卖一价的所有挂单
- 卖出市价单:吃掉当前买一价的所有挂单
市价单的优点是「快」,缺点是「贵」。你想想看,当你用市价单买入时,实际上是在为「立即成交」这个特权付费——这个费用就是买卖价差。
1.3 买卖盘口与价差
盘口,就是订单簿上「当前最优的买卖报价」。
| 术语 | 含义 | 例子 |
|---|---|---|
| 买一(Bid 1) | 当前最高的买入报价 | 100.00元,1000股 |
| 卖一(Ask 1) | 当前最低的卖出报价 | 100.02元,800股 |
| 价差(Spread) | 卖一价 - 买一价 | 0.02元 |
价差这个东西,我刚开始做交易时根本没当回事。直到有一次,我写了一个高频策略,回测时收益曲线漂亮得不行,结果实盘一跑就亏钱。查了半天才发现——回测时我用了成交价的中间价,但实盘里每次交易都要付出半个价差的成本。高频交易里,价差就是你的「隐形杀手」。
1.4 订单簿的数据结构
搞量化的人,光懂概念不够,还得知道数据长什么样。下面是我常用的订单簿数据结构:
# 一个典型的订单簿数据结构
order_book = {
'bids': [ # 买单列表,按价格从高到低排序
{'price': 100.00, 'size': 1000, 'order_id': 'bid_001'},
{'price': 99.98, 'size': 2000, 'order_id': 'bid_002'},
{'price': 99.96, 'size': 1500, 'order_id': 'bid_003'},
],
'asks': [ # 卖单列表,按价格从低到高排序
{'price': 100.02, 'size': 800, 'order_id': 'ask_001'},
{'price': 100.04, 'size': 1200, 'order_id': 'ask_002'},
{'price': 100.06, 'size': 900, 'order_id': 'ask_003'},
],
'timestamp': 1700000000.123, # 精确到毫秒的时间戳
'symbol': 'BTC/USDT'
}
为什么买单要按价格从高到低排?因为出价高的人优先成交。卖单则相反,要价低的人优先。这个排序规则,是撮合引擎的核心逻辑。
我建议你在本地搭建一个模拟订单簿,用Python跑一跑。代码很简单:
class OrderBook:
def __init__(self):
self.bids = [] # 买单
self.asks = [] # 卖单
def add_order(self, side, price, size):
"""添加订单到订单簿"""
order = {'price': price, 'size': size}
if side == 'buy':
self.bids.append(order)
# 按价格降序排列
self.bids.sort(key=lambda x: x['price'], reverse=True)
else:
self.asks.append(order)
# 按价格升序排列
self.asks.sort(key=lambda x: x['price'])
def get_spread(self):
"""计算当前价差"""
if not self.bids or not self.asks:
return None
best_bid = self.bids[0]['price']
best_ask = self.asks[0]['price']
return best_ask - best_bid
def match_order(self):
"""尝试撮合订单"""
while self.bids and self.asks:
best_bid = self.bids[0]
best_ask = self.asks[0]
if best_bid['price'] >= best_ask['price']:
# 成交!
trade_price = best_ask['price']
trade_size = min(best_bid['size'], best_ask['size'])
print(f"成交: {trade_size} @ {trade_price}")
# 更新剩余数量
best_bid['size'] -= trade_size
best_ask['size'] -= trade_size
# 移除已成交的订单
if best_bid['size'] == 0:
self.bids.pop(0)
if best_ask['size'] == 0:
self.asks.pop(0)
else:
break
这段代码虽然简单,但包含了订单簿的核心逻辑。我在做低延迟系统时,用的也是类似的思路——只不过把Python换成了C++,把列表换成了红黑树。
知识体系总览
下面这张图,是我自己总结的订单簿知识体系。建议你保存下来,学完本章后对照着复习:
这张图把本章的知识点串起来了。你想想看,从定义到订单类型,再到盘口价差,最后落到数据结构——这是一个完整的知识闭环。搞懂了这些,后面讲高频策略时你才能跟得上。
- 订单簿是市场的「源代码」,记录了所有未成交的买卖意愿
- 限价单保价格不保成交,市价单保成交不保价格
- 价差是高频交易最大的隐性成本,回测时一定要算进去
- 订单簿数据结构:bids降序排列,asks升序排列
公众号:蓝海资料掘金营,微信deep3321