
QMT是迅投开发的量化交易平台,支持多种Python策略机制,包括逐K线驱动handlebar、定时任务run_time、订阅推送subscribe),可以满足不同策略需求,灵活度高。
但很多朋友在QMT实操中常会遇到行情触发异常、信号错乱、代码无报错但不下单等问题,多数情况并不是策略逻辑出错,而是对QMT底层运行机制不熟悉,今天我们详细介绍一下QMT三大运行机制。
一、量化软件QMT运行机制
QMT 系统提供两大类(事件驱动与定时任务),共三种运行机制。

1、逐 K 线驱动:handlebar
handlebar是主图历史 k 线+盘中订阅推送。
当你运行策略时,它会从主图的第一根K线开始,逐根调用handlebar,直到最新的一根K线。
在盘中,最新的一根K线会随着每一个新tick的到来而不断变化,因此handlebar会被多次调用(每个tick一次)。
只有在最后一根K线的最后一个tick(即K线确定时)触发的交易信号,才会被认为是有效信号,并在下一根K线的第一个tick发送下单指令。这是为了避免在K线形成过程中因价格频繁波动产生虚假信号。
逐K线驱动测试示例:
#coding:gbk#导入常用库import pandas as pdimport numpy as npimport talib#示例说明:本策略,通过计算快慢双均线,在金叉时买入,死叉时做卖出 点击回测运行 主图选择要交易的股票品种def init(C):#init handlebar函数的入参是ContextInfo对象 可以缩写为C#设置测试标的为主图品种 C.stock= C.stockcode + '.' +C.market#line1和line2分别为两条均线期数 C.line1=10 #快线参数 C.line2=20 #慢线参数#accountid为测试的ID 回测模式资金账号可以填任意字符串 C.accountid = "testS"def handlebar(C):#当前k线日期 bar_date = timetag_to_datetime(C.get_bar_timetag(C.barpos), '%Y%m%d%H%M%S')#回测不需要订阅最新行情使用本地数据速度更快 指定subscribe参数为否. 如果回测多个品种 需要先下载对应周期历史数据 local_data = C.get_market_data_ex(['close'], [C.stock], end_time = bar_date, period = C.period, count = max(C.line1, C.line2), subscribe = False) close_list = list(local_data[C.stock].iloc[:, 0])#将获取的历史数据转换为DataFrame格式方便计算#如果目前未持仓,同时快线穿过慢线,则买入8成仓位if len(close_list) <1:print(bar_date, '行情不足 跳过') line1_mean = round(np.mean(close_list[-C.line1:]), 2) line2_mean = round(np.mean(close_list[-C.line2:]), 2)print(f"{bar_date} 短均线{line1_mean} 长均线{line2_mean}") account = get_trade_detail_data('test', 'stock', 'account') account = account[0] available_cash = int(account.m_dAvailable) holdings = get_trade_detail_data('test', 'stock', 'position') holdings = {i.m_strInstrumentID + '.' + i.m_strExchangeID : i.m_nVolume for i in holdings} holding_vol = holdings[C.stock] if C.stock in holdings else 0if holding_vol == 0 and line1_mean > line2_mean: vol = int(available_cash / close_list[-1] / 100) * 100#下单开仓 passorder(23, 1101, C.accountid, C.stock, 5, -1, vol, C)print(f"{bar_date} 开仓") C.draw_text(1, 1, '开')#如果目前持仓中,同时快线下穿慢线,则全部平仓elif holding_vol > 0 and line1_mean < line2_mean:#状态变更为未持仓 C.holding=False#下单平仓 passorder(24, 1101, C.accountid, C.stock, 5, -1, holding_vol, C)print(f"{bar_date} 平仓") C.draw_text(1, 1, '平')2、订阅推送(subscribe)
QMT系统中的订阅推送 subscribe 是一种事件驱动的运行机制,主要用于盘中实时获取指定品种的分笔行情数据。
触发方式为盘中订阅指定品种的分笔数据,每当有新分笔到达时,会触发指定的回调函数,适用于盘中需要随分笔行情进行实时判断交易的需求。
与handlebar的区别: handlebar 虽然也是事件驱动,但其基于主图K线,默认采用逐K线生效模式(信号需等待K线确认后才发出);但subscribe 直接在分笔数据到达时触发回调,可实现更实时、精细的响应。
事件驱动(subscribe)示例:
#coding:gbkclass a():passA = a()A.bought_list = []account = 'testaccount'def init(C):#下单函数的参数需要 ContextInfo对象 在init中定义行情回调函数 可以用到init函数的入参 不用手动传入 def callback_func(data):#print(data)for stock in data: current_price = data[stock]['close'] pre_price = data[stock]['preClose'] ratio = current_price / pre_price - 1print(stock, C.get_stock_name(stock), '当前涨幅', ratio)if ratio > 0 and stock not in A.bought_list: msg = f"当前涨幅 {ratio} 大于0 买入100股"print(msg)#下单函数passorder 安全起见处于注释状态 需要实际测试下单交易时再放开#passorder(23, 1101, account, stock, 5, -1, 100, '订阅下单示例', 2, msg, C) A.bought_list.append(stock) stock_list = ['******.SH', '******.SZ']for stock in stock_list: C.subscribe_quote(stock, period = '1d', callback = callback_func)3、定时任务(run_time)
QMT系统中的定时任务 run_time 是一种基于固定时间间隔触发的运行机制,主要适用于盘中需要在固定时间间隔进行判断或交易的场景,比如每5分钟检查一次持仓状态、每分钟执行一次条件判断等。
定时任务(run_time)示例:
#coding:gbkimport time, datetimeclass a(): passA = a()def init(C): A.hsa = C.get_stock_list_in_sector('沪深A股') A.vol_dict = {}for stock in A.hsa: A.vol_dict[stock] = C.get_last_volume(stock) A.bought_list = [] C.run_time("f", "1nSecond", "2019-10-14 13:20:00")def f(C): t0 = time.time() now = datetime.datetime.now() full_tick = C.get_full_tick(A.hsa) total_market_value = 0 total_ratio = 0 count = 0for stock in A.hsa: ratio = full_tick[stock]['lastPrice'] / full_tick[stock]['lastClose'] - 1if ratio > 0.09 and stock not in A.bought_list: msg = f"{now} {stock} {C.get_stock_name(stock)} 当前涨幅 {ratio} 大于5% 买入100股"#下单示例 安全起见处于注释状态 需要实际测试下单时可以放开 #passorder(23, 1101, account, stock, 5, -1, 100, '示例策略', 2, msg, C) A.bought_list.append(stock) market_value = full_tick[stock]['lastPrice'] * A.vol_dict[stock] total_ratio += ratio * market_value total_market_value += market_value count += 1 total_ratio /= total_market_value total_ratio *= 100print(f'{now} 当前A股加权涨幅 {round(total_ratio, 2)}% 函数运行耗时{round(time.time()- t0, 5)}秒')三种机制对比
QMT不同机制匹配不同场景需求:

使用建议:如果策略逻辑不需要依赖每一笔行情的变化,而是在固定时间点执行(比如定时检查、定时调仓),使用 run_time 更为高效。
如果需要结合实时分笔行情做高频判断,建议使用 subscribe 或 handlebar 。
以上就是今天分享的全部内容,有任何疑问可以评论区交流!
风险提示!!!:
建议投资者务必确认自身风险承受能力及投资目标,国金证券不推荐投资目标不相符的投资者阅读本信息。智能交易可能因系统、通讯等原因无法正常使用或无法按照您的设置价格发出委托指令及完成成交,最终成交价格及数量以交易所、登记结算机构等记录为准。请密切关注交易回报情况及条件单设置情况。
以上信息仅供参考,不构成对委托指令成交的承诺,不构成投资建议,不构成收益或避免损失的承诺。请您务必仔细阅读相关风险提示及协议,了解各类智能交易功能的区别及不同风险,审慎决策是否使用相关功能。
投资有风险,入市需谨慎!
夜雨聆风