问题描述
解决方案
利用 QMT 的 Level 2 数据计算 买卖压力失衡(Order Imbalance) 是高频交易和短线策略中常用的因子。买卖压力失衡主要衡量订单簿中买盘和卖盘力量的差异,用于预测短期价格走势。
在 QMT 中,我们可以通过两种主要方式利用 Level 2 数据来计算失衡:
- 基于盘口挂单(Order Book)的失衡:利用
get_full_tick获取买一到买十(或买五)的挂单量,计算加权或简单的量比。 - 基于全市场总委托(Total Queue)的失衡:利用
l2quoteaux(Level 2 行情快照指标)获取全市场的委买总量和委卖总量。
下面我将为您提供详细的计算逻辑和一个完整的 Python 策略代码示例。
核心计算逻辑
1. 盘口深度失衡 (Depth Imbalance)
这是最常用的微观结构因子。公式如下:
$$ Imbalance = \frac{\sum(BidVol_i) - \sum(AskVol_i)}{\sum(BidVol_i) + \sum(AskVol_i)} $$
- BidVol_i: 第 i 档买单量
- AskVol_i: 第 i 档卖单量
- 取值范围: [-1, 1]。接近 1 表示买压极大,接近 -1 表示卖压极大。
2. 总委托失衡 (Total Queue Imbalance)
利用 Level 2 特有的“总委买量”和“总委卖量”数据:
$$ TotalImbalance = \frac{TotalBidQty - TotalOffQty}{TotalBidQty + TotalOffQty} $$
- TotalBidQty: 委买总量(所有价位)
- TotalOffQty: 委卖总量(所有价位)
QMT 策略代码实现
以下代码展示了如何在 QMT 中同时计算这两种失衡指标。
# -*- coding: gbk -*-
import pandas as pd
import numpy as np
def init(ContextInfo):
# 1. 设置股票池 (示例使用 浦发银行 和 平安银行)
# 注意:Level 2 数据通常需要开通相应权限
ContextInfo.stock_list = ['600000.SH', '000001.SZ']
ContextInfo.set_universe(ContextInfo.stock_list)
# 2. 设置账号 (实盘需要,回测可忽略或设置虚拟账号)
# ContextInfo.set_account('您的资金账号')
# 3. 订阅行情
# 'tick': 用于获取盘口挂单 (买一到买五/买十)
# 'l2quoteaux': Level 2 特色指标,包含总委买/委卖量、撤单量等
for stock in ContextInfo.stock_list:
ContextInfo.subscribe_quote(stock, period='tick', dividend_type='none')
ContextInfo.subscribe_quote(stock, period='l2quoteaux', dividend_type='none')
print("策略初始化完成,已订阅 Tick 和 Level 2 快照数据")
def handlebar(ContextInfo):
# 获取当前 K 线位置,避免在历史数据回放时重复计算(视需求而定)
if not ContextInfo.is_last_bar():
return
# 遍历股票池计算压力失衡
for stock in ContextInfo.stock_list:
calculate_pressure_imbalance(ContextInfo, stock)
def calculate_pressure_imbalance(ContextInfo, stock_code):
"""
计算买卖压力失衡的核心函数
"""
# ------------------------------------------------------------------
# 方法一:基于盘口挂单 (Order Book) 的失衡计算
# ------------------------------------------------------------------
full_tick = ContextInfo.get_full_tick([stock_code])
if not full_tick:
return
tick_data = full_tick.get(stock_code)
if tick_data:
# 获取买盘和卖盘的挂单量列表 (通常为5档或10档,取决于行情权限)
# bidVol: [买1量, 买2量, ...]
# askVol: [卖1量, 卖2量, ...]
bid_vols = tick_data.get('bidVol', [])
ask_vols = tick_data.get('askVol', [])
# 简单求和计算 (也可以根据档位加权,例如给买1更高的权重)
sum_bid_vol = sum(bid_vols)
sum_ask_vol = sum(ask_vols)
# 防止分母为0
if (sum_bid_vol + sum_ask_vol) > 0:
# 计算盘口失衡度 [-1, 1]
lob_imbalance = (sum_bid_vol - sum_ask_vol) / (sum_bid_vol + sum_ask_vol)
else:
lob_imbalance = 0.0
print(f"[{stock_code}] 盘口失衡度(LOB): {lob_imbalance:.4f} (买量:{sum_bid_vol}, 卖量:{sum_ask_vol})")
# ------------------------------------------------------------------
# 方法二:基于 Level 2 总委托量 (Total Queue) 的失衡计算
# ------------------------------------------------------------------
# 使用 get_market_data_ex 获取 l2quoteaux 数据
# 注意:count=-1 表示取最新一笔
l2_data = ContextInfo.get_market_data_ex(
fields=[], # 空列表代表获取所有字段
stock_code=[stock_code],
period='l2quoteaux',
count=1,
subscribe=True
)
if stock_code in l2_data and not l2_data[stock_code].empty:
df_l2 = l2_data[stock_code]
# 获取最新的一行数据
latest_l2 = df_l2.iloc[-1]
# 字段说明 (参考 QMT 文档):
# totalBidQuantity: 委买总量
# totalOffQuantity: 委卖总量
total_bid_qty = latest_l2.get('totalBidQuantity', 0)
total_off_qty = latest_l2.get('totalOffQuantity', 0)
# 计算总委托失衡度
if (total_bid_qty + total_off_qty) > 0:
total_imbalance = (total_bid_qty - total_off_qty) / (total_bid_qty + total_off_qty)
else:
total_imbalance = 0.0
print(f"[{stock_code}] 总委托失衡度(L2): {total_imbalance:.4f} (总委买:{total_bid_qty}, 总委卖:{total_off_qty})")
# --------------------------------------------------------------
# 简单的交易信号示例
# --------------------------------------------------------------
# 逻辑:如果盘口买压大 且 总委买量也大,视为强买入信号
if lob_imbalance > 0.6 and total_imbalance > 0.3:
print(f"!!! 发现买入信号: {stock_code} 买压显著 !!!")
# order_shares(stock_code, 100, 'fix', tick_data['lastPrice'], ContextInfo, 'account_id')
print("-" * 30)
代码关键点解析
-
数据订阅 (
subscribe_quote):- 必须显式订阅
period='l2quoteaux'才能获取 Level 2 的快照指标(如总委买/委卖量)。 period='tick'用于获取高频的盘口挂单数据。
- 必须显式订阅
-
获取盘口数据 (
get_full_tick):- 返回的字典中
bidVol和askVol是列表。 - 在标准版中通常是 5 档数据,在开通 Level 2 权限的环境下,部分接口可能返回 10 档数据。
- 进阶技巧:您可以对
bid_vols进行加权。例如,买一的权重设为 1.0,买五的权重设为 0.2,因为越靠近成交价的挂单对价格影响越大。
- 返回的字典中
-
获取 Level 2 统计数据 (
get_market_data_ex+l2quoteaux):- 这是计算宏观买卖压力的关键。
totalBidQuantity(委买总量) 和totalOffQuantity(委卖总量) 反映了市场上所有的意愿,不仅仅是前几档。- 该接口返回的是
pandas.DataFrame,方便进行时间序列分析。
常见问题
-
为什么
l2quoteaux数据为空?- 请确认您的 QMT 账号是否已开通 Level 2 行情权限。
- 请确认是否在
init中正确调用了subscribe_quote。 - 该数据仅在实盘或模拟盘的交易时间段内推送,非交易时间可能无数据。
-
如何利用撤单数据?
l2quoteaux中还包含withdrawBidQuantity(买入撤单总量) 和withdrawOffQuantity(卖出撤单总量)。- 撤单压力逻辑:如果价格上涨,但卖单撤单量激增,可能意味着压盘撤退,利好上涨。您可以将撤单量加入到失衡公式中进行修正。
Q&A
Q: get_full_tick 返回的是 5 档还是 10 档数据?
A: 这取决于您的券商服务器配置和账号权限。通常普通账号是 5 档,Level 2 账号可能是 10 档。您可以通过 len(tick_data['bidVol']) 来打印查看实际长度。
Q: 这个策略可以直接用于回测吗?
A: l2quoteaux (Level 2 快照) 的历史数据量非常大,并非所有券商的 QMT 服务器都提供完整的 Level 2 历史数据回测支持。通常 Tick 级回测支持较好,但 Level 2 统计字段可能需要您先下载补充数据或仅在实盘中使用。
Q: 如何对盘口进行加权计算?
A: 您可以定义一个权重数组,例如 weights = [1.0, 0.8, 0.6, 0.4, 0.2],然后使用 np.average(bid_vols, weights=weights) 来计算加权买量,这样能更准确地反映即时压力。