问题描述
解决方案
在 QMT (Quantitative Trading Platform) 中,获取实时盘口买卖价差 (Bid-Ask Spread) 的最直接方法是使用 ContextInfo.get_full_tick() 接口。
该接口可以获取指定标的的最新分笔数据(Tick Data),其中包含了买一价(Bid1)和卖一价(Ask1)。
核心逻辑
- 获取 Tick 数据:调用
ContextInfo.get_full_tick(code_list)获取最新的盘口快照。 - 提取价格:从返回的数据字典中提取
askPrice(卖价列表)和bidPrice(买价列表)。列表的第 0 位即为卖一价和买一价。 - 计算价差:
- 绝对价差 (Spread) = 卖一价 - 买一价
- 相对价差 (Spread Ratio) = (卖一价 - 买一价) / 中间价 * 100% (可选,用于衡量流动性成本比例)
QMT 策略代码示例
以下是一个完整的 Python 策略示例,展示如何在 handlebar 周期内实时计算并打印当前主图品种的买卖价差。
# -*- coding: gbk -*-
def init(ContextInfo):
# 设置示例股票池,这里以贵州茅台为例
# 实际运行时会根据界面选择的品种或此处设置的 universe 运行
ContextInfo.set_universe(['600519.SH'])
# 设置账号(如果需要交易)
# ContextInfo.set_account('您的资金账号')
def handlebar(ContextInfo):
# 获取当前主图的股票代码
current_stock = ContextInfo.stockcode + '.' + ContextInfo.market
# 1. 获取最新分笔数据 (Tick Data)
# get_full_tick 返回一个字典,key是股票代码,value是数据字典
tick_data_map = ContextInfo.get_full_tick([current_stock])
# 检查是否成功获取数据
if current_stock in tick_data_map:
tick_data = tick_data_map[current_stock]
# 2. 提取盘口价格
# askPrice 和 bidPrice 是列表,包含五档或十档行情
# 索引 0 对应 卖一 和 买一
ask_prices = tick_data.get('askPrice', [])
bid_prices = tick_data.get('bidPrice', [])
# 确保数据有效(防止列表为空,例如跌停或涨停时可能一边为空)
if len(ask_prices) > 0 and len(bid_prices) > 0:
ask_1 = ask_prices[0] # 卖一价
bid_1 = bid_prices[0] # 买一价
# 过滤无效价格(如0)
if ask_1 > 0 and bid_1 > 0:
# 3. 计算买卖价差
spread = ask_1 - bid_1
# 计算相对价差 (Spread %)
mid_price = (ask_1 + bid_1) / 2
spread_ratio = (spread / mid_price) * 100
# 打印结果
print(f"代码: {current_stock}")
print(f"时间: {tick_data.get('timetag')}") # 注意:这里通常不需要转换,仅作展示
print(f"卖一价: {ask_1}, 买一价: {bid_1}")
print(f"绝对价差 (Spread): {spread:.2f}")
print(f"相对价差 (Spread %): {spread_ratio:.4f}%")
print("-" * 30)
else:
print(f"{current_stock} 价格数据异常 (可能为0)")
else:
# 处理涨跌停板情况(可能只有买单或只有卖单)
if len(ask_prices) == 0 and len(bid_prices) > 0:
print(f"{current_stock} 当前可能涨停 (无卖单)")
elif len(bid_prices) == 0 and len(ask_prices) > 0:
print(f"{current_stock} 当前可能跌停 (无买单)")
else:
print(f"未获取到 {current_stock} 的 Tick 数据")
代码解析
-
ContextInfo.get_full_tick([code]):- 这是获取实时盘口最关键的函数。它不需要像
get_market_data那样指定复杂的参数,专门用于获取最新的 Level-1 或 Level-2 快照。 - 返回的数据结构中,
askPrice和bidPrice是列表。对于普通 Level-1 行情,列表长度通常为 5;对于 Level-2 行情,长度可能为 10。
- 这是获取实时盘口最关键的函数。它不需要像
-
异常处理:
- 在计算价差前,必须判断
ask_prices和bid_prices是否为空。 - 涨停板:此时可能没有卖单(
askPrice为空或价格为 0),无法计算常规价差。 - 跌停板:此时可能没有买单(
bidPrice为空或价格为 0)。
- 在计算价差前,必须判断
-
运行模式:
- 此代码需要在实盘模式(“运行”按钮)下才能看到实时的 Tick 变化。
- 如果在回测模式下,
get_full_tick可能会返回回测时间点对应的快照(取决于数据精度),但通常用于实盘高频监控。
常见问题 (Q&A)
Q: get_full_tick 和 get_market_data 有什么区别?
A: get_full_tick 专门用于获取当前时刻的最新盘口快照(Tick),返回的是字典结构,包含买卖五档/十档,速度快且使用方便。get_market_data 更通用,可以获取历史序列数据(如过去 100 个 tick 或 K 线),返回的是 Pandas DataFrame 或 Series,处理起来相对重一些。计算实时盘口价差推荐使用 get_full_tick。
Q: 为什么计算出的 Spread 有时是负数?
A: 在正常的连续竞价交易中,卖一价永远大于买一价,Spread 应为正数。如果出现负数,可能是数据源异常、使用了错误的市场代码(如套利合约),或者在极端的市场交叉盘瞬间(极少见)。最常见的情况是代码逻辑错误,例如将 bid - ask 算反了。
Q: 如何在盘口价差超过特定阈值时触发交易?
A: 您可以在 handlebar 中加入逻辑判断。例如:
if spread_ratio < 0.05: # 如果价差小于 0.05%,认为流动性好,适合交易
passorder(...) # 执行下单函数
Q: 这个接口支持期货和期权吗?
A: 支持。get_full_tick 适用于股票、期货、期权等所有 QMT 支持的品种。对于期货,Spread 通常是一个最小变动价位(Tick Size);对于期权,Spread 是衡量流动性的重要指标。