问题描述
目前使用的是中金的L2 当之前已经订阅过L2数据如果重复订阅会发生什么?
如果重复订阅了 想反订阅 是使用后面订阅返回的订阅号还是?
解决方案
在 QMT 平台中订阅 Level2(L2)行情数据时,使用的是 ContextInfo.subscribe_quote() 接口。针对您提到的重复订阅和反订阅问题,以下是详细的机制解释和处理建议:
1. 重复订阅 L2 数据会发生什么?
在 QMT 中,每一次调用 subscribe_quote 函数,系统都会生成一个新的订阅任务,并返回一个全新的、唯一的订阅号(subId)。即使您订阅的是同一个标的、同一个周期(如 L2 数据),系统也不会自动覆盖之前的订阅。
重复订阅的后果:
- 回调函数被多次触发:如果您为同一个标的重复订阅了 N 次,那么每当该标的有一笔新的 L2 行情推送时,您绑定的回调函数(
callback)就会被触发 N 次。 - 性能损耗与逻辑错误:重复触发会导致您的策略进行大量冗余计算,严重时可能引起系统卡顿、延迟,甚至导致策略内部的下单逻辑被重复执行(例如重复开仓)。
2. 重复订阅后,如何进行反订阅?
反订阅使用的是 ContextInfo.unsubscribe_quote(subId) 接口,它严格依赖于订阅时返回的 subId。
- 使用哪个订阅号?:如果您重复订阅了多次,您会得到多个不同的
subId。如果您只使用最后一次返回的subId进行反订阅,那么仅仅只会取消最后一次的订阅任务,之前订阅的任务依然在后台运行,回调函数依然会被触发。 - 正确的做法:要完全取消该标的的订阅,您必须将每一次订阅返回的
subId都记录下来,并对每一个subId都调用一次unsubscribe_quote(subId)。
3. 最佳实践:如何避免重复订阅?
为了避免重复订阅带来的麻烦,建议在策略中维护一个订阅状态字典,或者利用 QMT 提供的 ContextInfo.get_all_subscription() 接口来检查当前已有的订阅。
示例代码:使用字典管理订阅状态
# 在 init 中初始化一个字典用于保存订阅号
def init(ContextInfo):
ContextInfo.my_subs = {}
def on_quote(datas):
print("收到L2数据:", datas)
def subscribe_l2(ContextInfo, stock_code):
# 检查是否已经订阅过
if stock_code in ContextInfo.my_subs:
print(f"{stock_code} 已经订阅过,请勿重复订阅!")
return
# 发起订阅并记录 subId
# 注意:订阅 L2 快照使用 'l2quoteaux',L2 逐笔使用 'tick' 等,具体视需求而定
sub_id = ContextInfo.subscribe_quote(stock_code, 'l2quoteaux', 'none', on_quote)
ContextInfo.my_subs[stock_code] = sub_id
print(f"订阅成功,标的: {stock_code}, 订阅号: {sub_id}")
def unsubscribe_l2(ContextInfo, stock_code):
# 检查是否存在该订阅
if stock_code in ContextInfo.my_subs:
sub_id = ContextInfo.my_subs[stock_code]
ContextInfo.unsubscribe_quote(sub_id)
# 反订阅后从字典中移除
del ContextInfo.my_subs[stock_code]
print(f"反订阅成功,标的: {stock_code}, 订阅号: {sub_id}")
else:
print(f"{stock_code} 未被订阅,无需反订阅。")
总结:
千万不要在 handlebar 等高频触发的函数中无条件地调用 subscribe_quote。始终保持“按需订阅、记录 subId、精准反订阅”的良好习惯,可以有效保障量化策略的稳定运行。