量化交易必看!QMT逐K线驱动handlebar附带回测实盘示例

QMT 量化系统了两大类运行机制,也就是事件驱动和定时任务,具体拆成三种玩法,逐K线运行,订阅推送、定时任务。今儿重点唠唠 逐 K 线驱动里的 handlebar,后面会依次给大家讲后面两者。

量化交易必看!QMT逐K线驱动handlebar附带回测实盘示例

简单说,就是让策略跟着每一根 K 线 “走一步,算一步” 。不管是回测历史 K 线,还是实盘盯当下的 K 线,策略都会像 “看电影一帧一帧看” 一样,按 K 线顺序,一根一根处理数据、判断信号。

比如你做个均线策略,逐 K 线驱动会让策略:

回测时,从历史第一根 K 线开始,算当时的均线、判断买卖;

实盘时,新出一根 K 线(比如每天收盘出日线,每分钟出分钟线),策略立刻重新算均线、看要不要买卖。

量化交易必看!QMT逐K线驱动handlebar附带回测实盘示例

逐K线驱动和其它运行机制的差别?

QMT与另外两种机制(事件驱动、定时任务)简单对比:

机制类型

咋触发策略?

适合啥策略?

逐 K 线驱动(handlebar)

每根 K 线走完自动触发

均线、趋势类策略(需要跟 K 线节奏)

事件驱动

行情变化触发(比如股价跳变、下单成交)

高频策略、异动捕捉(比如突破瞬间买)

定时任务

到固定时间触发(比如每天 3 点)

尾盘选股、定时调仓策略

handlebar是啥?——逐K线驱动的核心工具

在 QMT 里,handlebar 就是实现逐 K 线驱动的函数 。你写策略时,把逻辑放进 handlebar 里,系统就会自动按 K 线顺序跑策略,不用自己操心 “啥时候算、咋算”。

举个极简例子(Python 代码):

def handlebar(C):
# 每根 K 线触发时,先取当前收盘价
    close = C.get_price('close')
# 简单逻辑:收盘价 > 100 就买,< 100 就卖(实盘/回测都这么跑)
if close >100:
        C.buy()
else:
        C.sell()

不管是回测 2023 年的历史 K 线,还是实盘看今天的新 K 线,只要触发逐 K 线驱动,handlebar 就会被调用,按最新 K 线算买卖。

基于handlebar回测示例

示例说明:通过计算快慢双均线,在金叉时买入,死叉时做卖出 。

#coding:gbk
#导入常用库
import pandas as pd
import numpy as np
import 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 0
if 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, '平')

基于handlebar实盘示例

示例说明:双均线实盘策略,通过计算快慢双均线,在金叉时买入,死叉时做卖出

#coding:gbk
# 导入包
import pandas as pd
import numpy as np
import datetime
"""
示例说明:双均线实盘策略,通过计算快慢双均线,在金叉时买入,死叉时做卖出
"""
class a():
pass
A = a() #创建空的类的实例 用来保存委托状态 
def init(C):
	A.stock= C.stockcode + '.' + C.market #品种为模型交易界面选择品种
	A.acct= account #账号为模型交易界面选择账号
	A.acct_type= accountType #账号类型为模型交易界面选择账号
	A.amount = 10000 #单笔买入金额 触发买入信号后买入指定金额
	A.line1=17   #快线周期
	A.line2=27   #慢线周期
	A.waiting_list = [] #未查到委托列表 存在未查到委托情况暂停后续报单 防止超单
	A.buy_code = 23 if A.acct_type == 'STOCK' else 33 #买卖代码 区分股票 与 两融账号
	A.sell_code = 24 if A.acct_type == 'STOCK' else 34
print(f'双均线实盘示例{A.stock} {A.acct} {A.acct_type} 单笔买入金额{A.amount}')
def handlebar(C):
#跳过历史k线
if not C.is_last_bar():
return
	now = datetime.datetime.now()
	now_time = now.strftime('%H%M%S')
# 跳过非交易时间
if now_time < '093000' or now_time > "150000":
return
	account = get_trade_detail_data(A.acct, A.acct_type, 'account')
if len(account)==0:
print(f'账号{A.acct} 未登录 请检查')
return
	account = account[0]
	available_cash = int(account.m_dAvailable)
#如果有未查到成交 查询成交
if A.waiting_list:
		found_list = []
		deals = get_trade_detail_data(A.acct, A.acct_type, 'deal')
for deal in deals:
if deal.m_strRemark in A.waiting_list:
				found_list.append(deal.m_strRemark)
		A.waiting_list = [i for i in A.waiting_list if i not in found_list]
if A.waiting_list:
print(f"当前有未查到委托 {A.waiting_list} 暂停后续报单")
return
	holdings = get_trade_detail_data(A.acct, A.acct_type, 'position')
	holdings = {i.m_strInstrumentID + '.' + i.m_strExchangeID : i.m_nCanUseVolume for i in holdings}
#获取行情数据
	data = C.get_market_data_ex(["close"],[A.stock],period = '1d',count = max(A.line1, A.line2)+1)
	close_list = data[A.stock].values
if len(close_list) < max(A.line1, A.line2)+1:
print('行情长度不足(新上市或最近有停牌) 跳过运行')
return
	pre_line1 = np.mean(close_list[-A.line1-1: -1])
	pre_line2 = np.mean(close_list[-A.line2-1: -1])
	current_line1 = np.mean(close_list[-A.line1:])
	current_line2 = np.mean(close_list[-A.line2:])
#如果快线穿过慢线,则买入委托 当前无持仓 买入
	vol = int(A.amount / close_list[-1] / 100) * 100 #买入数量 向下取整到100的整数倍
if A.amount < available_cash and vol >= 100 and A.stock not in holdings and pre_line1 < pre_line2 and current_line1 > current_line2:
#下单开仓 ,参数说明可搜索PY交易函数 passorder
		msg = f"双均线实盘 {A.stock} 上穿均线 买入 {vol}股"
		passorder(A.buy_code, 1101, A.acct, A.stock, 14, -1, vol, '双均线实盘', 2 , msg, C)
print(msg)
		A.waiting_list.append(msg)
#如果快线下穿慢线,则卖出委托
if A.stock in holdings and holdings[A.stock] > 0 and pre_line1 > pre_line2 and current_line1 < current_line2:
		msg = f"双均线实盘 {A.stock} 下穿均线 卖出 {holdings[A.stock]}股"
		passorder(A.sell_code, 1101, A.acct, A.stock, 14, -1, holdings[A.stock], '双均线实盘', 2 , msg, C)
print(msg)
		A.waiting_list.append(msg)

发布者:股市刺客,转载请注明出处:https://www.95sca.cn/archives/1507643
站内所有文章皆来自网络转载或读者投稿,请勿用于商业用途。如有侵权、不妥之处,请联系站长并出示版权证明以便删除。敬请谅解!

(0)
股市刺客的头像股市刺客
上一篇 2025 年 7 月 24 日 下午2:44
下一篇 2024 年 8 月 8 日

相关推荐

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注