问题描述
解决方案
在 QMT 中,订阅多个股票的实时行情并触发回调主要通过 ContextInfo.subscribe_quote 接口实现。
实现逻辑
- 定义回调函数:首先定义一个回调函数(例如
on_quote_callback),该函数接收一个参数(通常命名为datas)。当订阅的股票有行情更新时,QMT 会自动调用此函数。datas是一个字典,Key 是股票代码,Value 是包含最新行情的 Pandas DataFrame。 - 遍历订阅:在
init初始化函数中,定义需要订阅的股票列表。通过循环遍历该列表,对每一个股票代码调用ContextInfo.subscribe_quote。 - 设置参数:调用订阅函数时,指定周期(如
'tick'为分笔数据)、复权方式以及刚才定义的回调函数。
策略代码实现
以下是完整的策略代码示例:
# -*- coding: gbk -*-
def on_quote_callback(datas):
"""
行情推送回调函数
datas: {stock_code: data_dataframe}
"""
# 遍历推送过来的数据字典
for stock_code, df in datas.items():
# df 是一个 pandas DataFrame,包含该股票的行情数据
# 通常取最后一行作为最新数据
if not df.empty:
last_row = df.iloc[-1]
# 打印接收到的行情信息
print("=" * 30)
print(f"收到行情推送 - 代码: {stock_code}")
print(f"时间: {last_row.name}") # index通常是时间字符串
print(f"最新价: {last_row.get('close', 0)}")
print(f"成交量: {last_row.get('volume', 0)}")
print(f"买一价: {last_row.get('bid1', 0)}") # 注意:字段名取决于周期,tick数据包含盘口
print("=" * 30)
def init(ContextInfo):
"""
初始化函数
"""
print("策略初始化...")
# 1. 定义需要订阅的股票列表
stock_list = ['600000.SH', '000001.SZ', '600519.SH']
# 2. 遍历列表进行订阅
for code in stock_list:
# subscribe_quote 参数说明:
# stock_code: 股票代码
# period: 周期,'tick'表示分笔数据,'1m'表示1分钟线等
# dividend_type: 复权方式,'follow'跟随主图,'none'不复权
# callback: 数据推送时的回调函数
sub_id = ContextInfo.subscribe_quote(
stock_code=code,
period='tick',
dividend_type='none',
callback=on_quote_callback
)
print(f"已订阅 {code}, 订阅ID: {sub_id}")
def handlebar(ContextInfo):
"""
K线运行函数
"""
# 必须存在,但在纯行情订阅模式下,逻辑主要在回调函数中处理
pass
关键点说明
-
ContextInfo.subscribe_quote:- 这是核心接口。虽然它一次只接受一个股票代码字符串,但通过
for循环可以轻松实现批量订阅。 period='tick':如果需要最高频率的实时行情(分笔成交、盘口挂单变化),请务必使用'tick'周期。如果设置为'1m',则只会在每分钟结束生成新的 K 线时触发回调。- 返回值:该函数返回一个
int类型的订阅号(subId),如果后续需要取消特定订阅,可以使用ContextInfo.unsubscribe_quote(subId)。
- 这是核心接口。虽然它一次只接受一个股票代码字符串,但通过
-
回调数据结构 (
datas):- 回调函数的入参
datas是一个字典{code: DataFrame}。 - 即使你订阅了多个股票,每次回调触发时,
datas中通常只包含当前发生变动的那只股票的数据(或者同一时刻变动的几只)。因此,必须在回调内部遍历datas.items()来处理。
- 回调函数的入参
-
运行模式:
- 此功能仅在实盘运行(或全推行情的模拟盘)模式下有效。在回测模式下,
subscribe_quote不会触发实时推送回调。
- 此功能仅在实盘运行(或全推行情的模拟盘)模式下有效。在回测模式下,
Q&A
Q: subscribe_quote 订阅的数量有限制吗?
A: QMT 客户端本身对订阅数量没有硬性限制,但订阅过多的 Tick 数据(例如几千只股票)会导致回调函数频繁触发,可能造成 Python 线程阻塞或界面卡顿。建议根据硬件性能合理控制订阅数量。
Q: 回调函数中可以直接下单吗?
A: 可以。在 on_quote_callback 中根据行情逻辑判断后,可以直接调用 passorder 或 algo_passorder 进行交易。这是实现高频或盘口策略的常用方式。
Q: 如何取消订阅?
A: ContextInfo.subscribe_quote 会返回一个订阅 ID。你可以将这些 ID 保存在一个列表中,需要取消时调用 ContextInfo.unsubscribe_quote(sub_id)。另外,策略停止运行时,系统通常会自动清理订阅。
Q: 为什么回调函数里的 DataFrame 字段和文档不一样?
A: DataFrame 的列(Columns)取决于订阅的 period。
period='tick': 包含lastPrice,volume,amount,bid1...bid5,ask1...ask5等详细盘口数据。period='1d'/'1m': 包含open,high,low,close,volume,amount等标准 OHLCV 数据。