订单簿数据结构:价格优先与时间优先原则
做量化交易这些年,我见过不少新手一上来就急着写策略,结果连订单簿都搞不清楚。说白了,订单簿就是市场的「记账本」——谁想买、谁想卖、什么价格、多少量,全记在上面。今天咱们就把这个记账本的结构彻底讲透。
价格优先与时间优先原则
交易所撮合订单,靠的就是这两条铁律。我刚开始接触时觉得很简单,后来在实盘中发现,很多坑都出在对这个原则的理解不够深。
价格优先:买方出价高的优先成交,卖方出价低的优先成交。这很好理解——你出100块买,别人出99块,那肯定先满足你。
时间优先:价格相同时,先挂单的先成交。嗯,这里要注意:时间优先只在同一价格层级内有效。
举个实际例子:假设当前买一价是10.00元,挂单量1000股。你挂10.01元买入500股,另一个交易员也挂10.01元买入300股,但比你晚0.1秒。那么你的500股会先成交,他的300股排在你后面。
我曾经在回测中忽略了这个细节,结果策略在模拟中表现完美,实盘却频频滑点。后来一查,原来是订单簿的排队逻辑没写对。
订单簿的增删改查操作
订单簿本质上就是一个动态数据结构。我们需要支持四种基本操作:
- 增加订单:新订单进入队列
- 删除订单:撤单或成交后移除
- 修改订单:改价格或数量(实际是撤单+重新挂单)
- 查询订单:查看当前最优买卖价、深度等
这里有个坑:修改订单在交易所层面通常不是直接改,而是先撤单再重新挂单。这意味着你的时间优先级会丢失。我见过有人写策略时直接修改订单,结果排队位置变了,成交率大幅下降。
使用Python字典与列表模拟订单簿
好了,理论说完了,咱们直接上代码。我个人习惯用字典来存储价格层级,用列表来管理同一价格下的订单队列。
class OrderBook:
def __init__(self):
# 买盘:价格从高到低排序
self.bids = {} # 价格 -> 订单队列
self.bid_prices = [] # 维护价格顺序
# 卖盘:价格从低到高排序
self.asks = {}
self.ask_prices = []
def add_order(self, side, price, quantity, order_id):
"""增加订单"""
book = self.bids if side == 'buy' else self.asks
prices = self.bid_prices if side == 'buy' else self.ask_prices
if price not in book:
book[price] = []
prices.append(price)
# 买盘降序,卖盘升序
prices.sort(reverse=(side == 'buy'))
book[price].append({
'id': order_id,
'price': price,
'quantity': quantity,
'timestamp': time.time()
})
def remove_order(self, side, price, order_id):
"""删除订单"""
book = self.bids if side == 'buy' else self.asks
if price in book:
book[price] = [o for o in book[price] if o['id'] != order_id]
if not book[price]:
del book[price]
prices = self.bid_prices if side == 'buy' else self.ask_prices
prices.remove(price)
def get_best_price(self, side):
"""查询最优价格"""
prices = self.bid_prices if side == 'buy' else self.ask_prices
if not prices:
return None
return prices[0]
def get_depth(self, levels=5):
"""获取深度数据"""
bids_depth = []
for price in self.bid_prices[:levels]:
total_qty = sum(o['quantity'] for o in self.bids[price])
bids_depth.append((price, total_qty))
asks_depth = []
for price in self.ask_prices[:levels]:
total_qty = sum(o['quantity'] for o in self.asks[price])
asks_depth.append((price, total_qty))
return {'bids': bids_depth, 'asks': asks_depth}
这段代码虽然简单,但已经能跑通基本的订单簿逻辑了。我在实际项目中,还会加上成交撮合的逻辑,不过那是后面章节的内容。
小技巧:生产环境中,建议用双向链表或跳表来维护价格层级,而不是用列表排序。列表排序在订单量大的时候性能会急剧下降。我曾在一次压力测试中,用列表排序处理10万笔订单,结果跑了3分钟还没跑完。
订单簿的核心结构图
下面这张图展示了订单簿的完整结构。你可以看到买盘和卖盘是如何组织的,以及价格优先和时间优先是如何体现在数据结构中的。
注意:上面的代码示例是教学用的简化版本。在实际的高频交易场景中,订单簿的更新速度可能达到每秒数万次,这时候Python的字典和列表就不够用了。我建议你至少用collections.deque来管理订单队列,或者直接上C++扩展。
说实话,订单簿的数据结构看起来简单,但真正用好它需要理解市场微观结构。比如,为什么有些交易员喜欢在买一价挂大单?为什么撤单率高的股票往往流动性差?这些问题的答案,都藏在订单簿的增删改查里。
好了,这一章的内容就到这里。记住:订单簿是市场的脉搏,读懂它,你就能听懂市场在说什么。