问题描述
解决方案
在 QMT (Quantitative Trading Platform) 中编写多股票交易策略时,默认的 handlebar(ContextInfo) 函数是由主图行情(即策略界面上选择的那个标的)驱动的。这意味着如果主图标的没有成交或更新,handlebar 就不会运行,或者当它运行时,其他非主图股票的行情可能不是毫秒级最新的。
为了确保在多股票策略中获取到的所有股票行情都是最新的,通常有以下两种最有效的方案:
- 使用
subscribe_quote进行独立行情订阅(推荐):这是最实时的方式。通过订阅指定股票列表的 Tick 数据,每当任何一只股票有行情更新时,都会触发独立的回调函数,而不是依赖主图驱动。 - 使用
get_full_tick获取全推快照:如果在定时任务或主图驱动的逻辑中需要下单,可以使用此函数一次性拉取股票池中所有股票当前时刻的最新 Tick 快照(盘口、最新价等)。
下面我将提供一个完整的策略代码示例,展示如何结合这两种方法来确保数据的实时性。
策略代码示例
该策略演示了如何订阅多只股票的实时行情,并在数据更新时立即获取最新价格,同时展示了如何在主逻辑中获取一篮子股票的最新快照。
# -*- coding: gbk -*-
import pandas as pd
def init(ContextInfo):
"""
初始化函数
"""
# 1. 设定需要交易或监控的股票池
ContextInfo.stock_list = ['600000.SH', '000001.SZ', '600519.SH']
# 2. 设置账号(实盘必须设置)
# ContextInfo.set_account('您的资金账号')
print("策略初始化启动,开始订阅行情...")
# 3. 【核心方法1】使用 subscribe_quote 订阅独立行情
# 这种方式不依赖主图,任何一只股票有Tick更新都会触发 on_quote_callback
for stock in ContextInfo.stock_list:
# 参数说明:代码, 周期('tick'为最实时), 复权方式, 回调函数
ContextInfo.subscribe_quote(stock, 'tick', 'follow', on_quote_callback)
def on_quote_callback(datas):
"""
【核心方法1的回调】行情推送回调函数
当 subscribe_quote 订阅的股票有新行情时,系统自动调用此函数
datas: {code: dataframe} 结构
"""
for stock_code, df in datas.items():
# 获取最新的一行数据
last_row = df.iloc[-1]
current_price = last_row['lastPrice'] # 最新价
timestamp = last_row.name # 时间戳
# 在这里编写针对该股票的实时交易逻辑
# print(f"【实时推送】{stock_code} 最新价: {current_price}, 时间: {timestamp}")
# 示例:如果价格触发条件则下单(此处仅为演示,未实际开启交易)
# if current_price > 100:
# passorder(23, 1101, ContextInfo.accid, stock_code, 5, -1, 100, ContextInfo)
def handlebar(ContextInfo):
"""
K线驱动函数(由主图行情驱动)
"""
# 如果是回测模式或需要基于K线走完才交易,逻辑写在这里
if not ContextInfo.is_last_bar():
return
# 4. 【核心方法2】使用 get_full_tick 获取全推快照
# 这种方式适合在某个时间点(如定时任务或主图更新时)一次性查看所有股票的最新状态
# 它返回的是调用该函数那一刻的最新盘口数据,确保数据是最新的
print("=" * 30)
print("主图驱动触发,正在拉取股票池最新快照...")
# 获取股票池中所有股票的最新 Tick 数据
tick_data_map = ContextInfo.get_full_tick(ContextInfo.stock_list)
for stock_code in ContextInfo.stock_list:
if stock_code in tick_data_map:
tick_data = tick_data_map[stock_code]
# 解析 Tick 数据
last_price = tick_data.get('lastPrice')
ask_price_1 = tick_data.get('askPrice')[0] # 卖一价
bid_price_1 = tick_data.get('bidPrice')[0] # 买一价
print(f"【快照检查】{stock_code} -> 最新价: {last_price}, 卖一: {ask_price_1}, 买一: {bid_price_1}")
# 在这里可以进行篮子交易逻辑判断
# ...
print("=" * 30)
# 注意:
# 1. subscribe_quote 适合高频、事件驱动的策略,响应速度最快。
# 2. get_full_tick 适合轮询、定时任务或低频策略,确保获取那一刻的最新切片。
# 3. 请勿使用 get_market_data 来获取"最新"的实时价格,因为它通常返回的是K线数据,
# 在实时性上不如上述两个接口。
核心原理解析
1. subscribe_quote (事件驱动模式)
这是确保多股票行情最新的最佳方案。
- 机制:它告诉 QMT 系统:“不要只管主图,请盯着这几只股票,一旦它们有动静,立刻通知我。”
- 优势:解耦了主图行情。即使主图(如上证指数)1分钟没动,只要你订阅的股票(如某只活跃的小盘股)有成交,
on_quote_callback就会被触发。 - 数据格式:回调返回的是
pandas.DataFrame,包含最新的 Tick 数据。
2. get_full_tick (快照轮询模式)
这是在特定时间点同步数据的方案。
- 机制:当你决定要进行一次逻辑判断(例如每隔3秒,或者主图K线走完时),你调用此函数,QMT 会直接从内存中读取这些股票当前这一毫秒的最新状态。
- 优势:数据结构是字典(Dict),包含详细的五档盘口(
askPrice,bidPrice)、持仓量等,非常适合做截面分析(Cross-sectional analysis)。
常见误区提醒
- 不要依赖
get_market_data获取最新价:get_market_data主要用于获取历史序列数据(如过去100天的收盘价)。虽然它也能获取最新数据,但在实盘的高频场景下,它的更新机制不如get_full_tick直接和高效。 - 主图依赖:如果你的策略只写在
handlebar里且没有使用上述两种方法,那么如果主图停牌或交易不活跃,你的整个策略逻辑都会停止运行,导致错过其他股票的行情。
Q&A
Q: subscribe_quote 订阅的股票数量有限制吗?
A: QMT 对订阅数量通常没有硬性限制,但订阅过多(如几千只)会消耗大量内存和CPU资源,导致回调处理变慢。建议根据硬件配置合理控制,一般几百只以内问题不大。
Q: get_full_tick 和 get_market_data_ex 有什么区别?
A: get_full_tick 专门用于获取当前时刻的 Tick 快照(盘口、最新价),返回字典格式,速度极快。get_market_data_ex 更通用,可以获取历史K线序列,返回 DataFrame。如果只是为了看最新价下单,get_full_tick 更合适。
Q: 回测模式下 subscribe_quote 有效吗?
A: 在回测模式下,subscribe_quote 的行为会模拟实盘,但受限于回测数据的精度(通常是分钟线或日线,除非有Tick级回测数据)。在回测中,通常主要依赖 handlebar 配合 ContextInfo.get_market_data 进行逻辑验证。上述提到的实时性问题主要针对实盘交易。