4. 快照数据详解(一):五档/十档买卖盘口、最新价、涨跌幅、成交量、成交额、换手率
Level2 快照数据是逐笔成交与委托队列在某一时刻的“截面”状态。与 Level1 相比,Level2 快照提供了更深的委托队列(十档)以及更精确的逐笔成交统计。本章聚焦于快照中最核心的六个字段:买卖盘口、最新价、涨跌幅、成交量、成交额与换手率,并讲解其标签构造逻辑。
4.1 五档与十档买卖盘口
买卖盘口(Order Book)是市场微观结构的核心。Level1 仅提供买一至买五、卖一至卖五共 10 个价位;Level2 则扩展为买一至买十、卖一至卖十共 20 个价位,每个价位同时包含价格与委托数量。
关键差异:Level2 的十档盘口并非简单的“多显示五档”,其数据源来自交易所的实时逐笔委托流,能更早、更完整地反映市场深度与挂单意愿。
4.1.1 盘口字段结构
| 字段名 | 类型 | 说明 |
|---|---|---|
| bid_price[1..10] | float64 | 买一至买十价格 |
| bid_volume[1..10] | int64 | 买一至买十委托量(手) |
| ask_price[1..10] | float64 | 卖一至卖十价格 |
| ask_volume[1..10] | int64 | 卖一至卖十委托量(手) |
4.1.2 盘口衍生标签
- 买卖价差(Spread):卖一价 - 买一价,衡量市场流动性成本。
- 加权平均深度:按价格加权计算前五档或前十档的平均委托成本。
- 委比:(委买总量 - 委卖总量) / (委买总量 + 委卖总量) × 100%,反映多空挂单力量对比。
- 盘口斜率:对买/卖档位的价格与数量进行线性回归,斜率绝对值越大,说明价格越远离当前价时挂单越稀疏。
标签构造示例:计算“买一至买五加权均价” = Σ(买i价 × 买i量) / Σ(买i量),用于衡量买方短期成本中枢。
4.2 最新价与涨跌幅
最新价(Last Price)是当前时刻最后一笔成交的价格,是快照中最直观的价格信号。涨跌幅则基于前一交易日收盘价计算:
涨跌幅 = (最新价 - 前收盘价) / 前收盘价 × 100%
注意:在盘中临时停牌或集合竞价阶段,最新价可能不更新或为特殊值(如 0)。构造标签时需做缺失值与异常值处理。
4.2.1 衍生标签
- 日内价格区间:当日最高价 - 当日最低价,衡量日内波动幅度。
- 价格位置:(最新价 - 当日最低价) / (当日最高价 - 当日最低价),反映当前价格在日内区间的相对位置。
- 涨跌停距离:最新价距涨停价或跌停价的百分比,用于判断封板强度。
4.3 成交量、成交额与换手率
这三个指标共同刻画了市场的交易活跃度与资金参与程度。
| 指标 | 公式 / 说明 | 单位 |
|---|---|---|
| 成交量(Volume) | 当日开盘至当前时刻的累计成交股数(或手数) | 手(1手=100股) |
| 成交额(Turnover) | 当日累计成交金额 = Σ(每笔成交价 × 每笔成交量) | 元 |
| 换手率(Turnover Rate) | 成交量 / 流通股本 × 100% | 百分比 |
4.3.1 换手率的标签意义
换手率是判断筹码交换频率的核心指标。不同换手率区间对应不同的市场状态:
- < 1%:极度冷清,流动性枯竭。
- 1% ~ 3%:正常交投。
- 3% ~ 7%:活跃,主力资金参与明显。
- 7% ~ 15%:非常活跃,可能处于主升浪或分歧阶段。
- > 15%:极度活跃,换手充分,短期方向选择在即。
实战标签:构造“量比”指标 = 当前分钟成交量 / 过去5分钟平均成交量,用于捕捉盘中放量异动。
4.4 快照数据标签构造流程
从原始快照数据到可用标签,通常需要经过以下步骤:
- 数据清洗:剔除停牌、未开盘、数据异常的快照记录。
- 字段对齐:将买卖十档价格与数量展开为扁平化特征列。
- 衍生计算:计算价差、委比、加权均价、换手率等。
- 标准化:对成交额、成交量等绝对值字段做对数或 Z-score 变换。
- 时间对齐:将快照时间戳对齐到固定频率(如 3 秒、1 分钟),便于后续建模。
4.5 代码示例:快照字段提取与标签计算
以下 Python 代码演示如何从 Level2 快照字典中提取核心字段并计算常用标签:
import numpy as np
def compute_snapshot_labels(snapshot: dict, total_shares: float):
"""
从单条 Level2 快照数据计算衍生标签
:param snapshot: 包含 bid/ask 十档价格与数量、最新价、成交量等字段的字典
:param total_shares: 流通股本(股)
:return: 标签字典
"""
labels = {}
# 1. 提取最新价与涨跌幅
last_price = snapshot.get('last_price', 0.0)
pre_close = snapshot.get('pre_close', 0.0)
if pre_close > 0:
labels['pct_change'] = (last_price - pre_close) / pre_close * 100
else:
labels['pct_change'] = 0.0
# 2. 买卖盘口价差
bid1_price = snapshot.get('bid_price_1', 0.0)
ask1_price = snapshot.get('ask_price_1', 0.0)
if bid1_price > 0 and ask1_price > 0:
labels['spread'] = ask1_price - bid1_price
else:
labels['spread'] = np.nan
# 3. 委比
total_bid_vol = sum(snapshot.get(f'bid_volume_{i}', 0) for i in range(1, 11))
total_ask_vol = sum(snapshot.get(f'ask_volume_{i}', 0) for i in range(1, 11))
if total_bid_vol + total_ask_vol > 0:
labels['order_ratio'] = (total_bid_vol - total_ask_vol) / (total_bid_vol + total_ask_vol) * 100
else:
labels['order_ratio'] = 0.0
# 4. 换手率
volume = snapshot.get('volume', 0) # 单位:股
if total_shares > 0:
labels['turnover_rate'] = volume / total_shares * 100
else:
labels['turnover_rate'] = 0.0
# 5. 成交额(元)
turnover = snapshot.get('turnover', 0.0)
labels['turnover'] = turnover
# 6. 买一至买五加权均价(买方成本中枢)
bid_prices = [snapshot.get(f'bid_price_{i}', 0) for i in range(1, 6)]
bid_volumes = [snapshot.get(f'bid_volume_{i}', 0) for i in range(1, 6)]
total_bid5_vol = sum(bid_volumes)
if total_bid5_vol > 0:
labels['bid5_weighted_avg'] = sum(p * v for p, v in zip(bid_prices, bid_volumes)) / total_bid5_vol
else:
labels['bid5_weighted_avg'] = 0.0
return labels
工程建议:在生产环境中,快照数据通常以 Parquet 格式存储,建议使用 Pandas 或 Polars 进行向量化计算,避免逐行循环。上述函数适用于单条调试或流式计算场景。