一、Order Book 深度解析:订单簿到底长什么样?
做美股量化交易,你第一个要面对的数据结构就是 Order Book。说白了,它就是交易所维护的一个实时账本——记录着当前所有未成交的买单和卖单。
我个人习惯把 Order Book 想象成一个「排队系统」。买方排一队,卖方排一队。谁的价格好谁排前面。嗯,就这么简单。
1.1 订单簿的核心结构
一个标准的 Level 2 订单簿,包含两个核心部分:
- Bid(买方队列):按价格从高到低排列
- Ask(卖方队列):按价格从低到高排列
每个价格档位上,都挂着对应的挂单量(Size)。
关键点:Bid 的最高价叫 Best Bid,Ask 的最低价叫 Best Ask。两者之间的差距就是 Spread(价差)。
我在项目中遇到过一个问题:有些新手会把 Bid 和 Ask 搞反。记住一个口诀——「买方想便宜,所以出价低;卖方想赚钱,所以要价高」。但 Bid 是买方愿意出的最高价,Ask 是卖方愿意接受的最低价。别弄混了。
1.2 用 Python 模拟一个订单簿
光说理论没意思,咱们直接上代码。下面这个例子,我用 Python 模拟了一个简化版的订单簿:
# 模拟一个简单的订单簿
order_book = {
'bids': [ # 买方队列,价格从高到低
{'price': 150.25, 'size': 1200},
{'price': 150.20, 'size': 800},
{'price': 150.15, 'size': 2500},
{'price': 150.10, 'size': 1500},
],
'asks': [ # 卖方队列,价格从低到高
{'price': 150.30, 'size': 900},
{'price': 150.35, 'size': 1800},
{'price': 150.40, 'size': 1100},
{'price': 150.45, 'size': 2200},
]
}
# 获取最佳买卖价
best_bid = order_book['bids'][0]['price']
best_ask = order_book['asks'][0]['price']
spread = best_ask - best_bid
print(f"Best Bid: {best_bid}, Best Ask: {best_ask}")
print(f"Spread: {spread:.2f}")
你想想看,这个结构其实很直观。每个价格档位就是一个「水位线」,挂单量就是水位线上的「水量」。水量越大,说明这个价格位置的支撑或阻力越强。
二、Price Level 与 Size:价格档位和挂单量的数据结构
接下来我们深入看看 Price Level 和 Size 的具体结构。这部分我建议你重点理解,因为它是所有高级策略的基础。
2.1 价格档位的组织方式
交易所不会把每个价格都列出来,而是只展示有挂单的价格档位。每个档位包含:
- Price:价格,精确到小数点后两位(美股通常如此)
- Size:该价格上的总挂单量(单位:股)
- Order Count:该价格上有多少个独立的订单(有些数据源提供)
举个例子,苹果股票在 150.25 这个价位上有 1200 股挂单,这 1200 股可能是 3 个订单组成的(比如 500+400+300)。
我的经验:只看 Size 不够,还要看 Order Count。如果 Size 很大但 Order Count 很小,说明是大户在挂单,这个支撑/阻力往往更可靠。反之,如果 Size 大但 Order Count 也大,可能是散户聚集,容易被击穿。
2.2 数据更新的增量模式
Level 2 数据是实时推送的,每次更新只发送变化的部分。常见的更新类型有:
| 操作类型 | 说明 | 示例 |
|---|---|---|
| Add | 新增一个订单 | 在 150.25 增加 500 股买单 |
| Cancel | 撤销一个订单 | 在 150.25 减少 300 股买单 |
| Execute | 成交(部分或全部) | 在 150.25 成交 200 股买单 |
| Replace | 修改订单(价格或数量) | 把 150.25 的买单改为 150.30 |
我曾经踩过一个坑:以为每次推送都是完整的订单簿快照。结果用增量数据直接覆盖本地状态,导致数据错乱。后来才明白,必须维护一个本地订单簿,每次收到增量更新就做对应的增删改操作。
2.3 用 Python 处理增量更新
# 维护本地订单簿
local_book = {'bids': {}, 'asks': {}}
def apply_update(update):
"""处理增量更新"""
side = update['side'] # 'bid' or 'ask'
price = update['price']
size = update['size']
action = update['action'] # 'add', 'cancel', 'execute'
if action == 'add':
local_book[side][price] = local_book[side].get(price, 0) + size
elif action in ('cancel', 'execute'):
local_book[side][price] = local_book[side].get(price, 0) - size
# 如果该价格档位挂单量为0,删除该档位
if local_book[side][price] <= 0:
del local_book[side][price]
# 按价格排序
local_book[side] = dict(sorted(
local_book[side].items(),
reverse=(side == 'bids')
))
三、Time & Sales:逐笔成交数据结构
Time & Sales,也叫 Tape(磁带数据),记录的是每一笔实际发生的交易。和订单簿不同,它记录的是「已经发生的事」,而不是「正在等待的事」。
3.1 逐笔成交的核心字段
每一笔成交记录通常包含以下信息:
- Timestamp:成交时间,精确到毫秒甚至微秒
- Price:成交价格
- Size:成交数量
- Exchange:成交的交易所代码(如 NYSE、NASDAQ)
- Sale Condition:成交条件(如普通交易、大宗交易等)
注意:不同数据源的时间精度不一样。有的只到秒级,有的到毫秒级。做高频策略时,时间精度不够会导致信号延迟,直接影响收益。
3.2 逐笔成交与订单簿的关系
这两者其实是同一枚硬币的两面。订单簿展示的是「流动性池子」,逐笔成交展示的是「从池子里舀水的过程」。
举个例子:
- 订单簿显示 150.30 有 1000 股卖单
- 突然来了一笔 150.30 成交 800 股的记录
- 订单簿上 150.30 的挂单量减少 800 股
你看,逐笔成交就是订单簿变化的「原因」。我习惯把两者结合起来看——如果某价格档位频繁出现大额成交,说明这个位置有「真金白银」在博弈。
3.3 用 Python 解析逐笔成交数据
# 模拟逐笔成交数据
trades = [
{'time': '09:30:00.123', 'price': 150.30, 'size': 800, 'exchange': 'N'},
{'time': '09:30:00.456', 'price': 150.31, 'size': 200, 'exchange': 'Q'},
{'time': '09:30:00.789', 'price': 150.29, 'size': 1500, 'exchange': 'N'},
]
# 计算累计成交量
total_volume = sum(t['size'] for t in trades)
vwap = sum(t['price'] * t['size'] for t in trades) / total_volume
print(f"总成交量: {total_volume} 股")
print(f"成交量加权均价(VWAP): {vwap:.2f}")
四、知识体系总览
下面这张图,我把本章的核心知识点串起来了。你可以把它当作一个「地图」,随时回来对照:
嗯,以上就是本章的全部内容。数据结构是量化交易的基石,把这些搞明白了,后面讲策略的时候你才能跟得上。记住,数据本身不会说谎,但解读数据的方式决定了你的交易水平。
一句话总结:Order Book 是「将要发生」的,Time & Sales 是「已经发生」的,Price Level 是组织这些数据的「格子」。三者结合,才能看清市场的真实面貌。