问题描述
解决方案
这是一个非常经典的多周期共振策略。在量化交易中,通常的做法是在小周期(如5分钟)上运行策略,同时在代码中获取大周期(如日线)的数据来进行趋势过滤。
策略逻辑设计
- 运行周期:策略挂载在 5分钟 K线上运行。
- 大周期判断(日线):
- 获取日线数据。
- 判断逻辑:当前价格 > 日线20日均线(MA20),视为日线看涨趋势。
- 小周期信号(5分钟):
- 获取5分钟数据。
- 判断逻辑:5分钟 MA5 上穿 MA10(金叉),视为买入信号。
- 卖出逻辑(可选):5分钟 MA5 下穿 MA10(死叉)进行平仓。
- 交易执行:
- 当“日线看涨”且“5分钟金叉”且“当前无持仓”时,买入。
- 当“5分钟死叉”且“有持仓”时,卖出。
QMT 策略代码实现
# -*- coding: gbk -*-
import pandas as pd
def init(ContextInfo):
"""
初始化函数,设定账户、股票池和参数
"""
# 1. 设置资金账号 (请替换为您自己的资金账号)
ContextInfo.accID = '6000000000'
ContextInfo.set_account(ContextInfo.accID)
# 2. 设置要交易的股票 (示例:平安银行)
ContextInfo.stock_code = '000001.SZ'
ContextInfo.set_universe([ContextInfo.stock_code])
# 3. 策略参数设置
ContextInfo.trade_qty = 1000 # 每次买入数量
ContextInfo.day_ma_len = 20 # 日线趋势均线周期
ContextInfo.min_ma_short = 5 # 5分钟短均线
ContextInfo.min_ma_long = 10 # 5分钟长均线
# 4. 周期设置
ContextInfo.period_day = '1d' # 大周期:日线
ContextInfo.period_min = '5m' # 小周期:5分钟
def get_position(ContextInfo, stock_code):
"""
辅助函数:获取指定股票的持仓数量
"""
# 获取持仓列表
positions = get_trade_detail_data(ContextInfo.accID, 'stock', 'position')
for pos in positions:
# QMT返回的持仓对象中,证券代码通常不带后缀,需要注意匹配
# 这里使用 m_strInstrumentID + '.' + m_strExchangeID 拼接成完整代码
pos_code = pos.m_strInstrumentID + '.' + pos.m_strExchangeID
if pos_code == stock_code:
return pos.m_nVolume
return 0
def handlebar(ContextInfo):
"""
核心逻辑函数,每根K线(5分钟)执行一次
"""
# 获取当前正在处理的股票代码
stock = ContextInfo.stock_code
# ----------------------------------------------------------------
# 第一步:获取日线数据,判断大趋势
# ----------------------------------------------------------------
# 获取足够计算MA20的日线数据,count设大一点防止停牌等导致数据不足
day_data = ContextInfo.get_market_data_ex(
['close'],
[stock],
period=ContextInfo.period_day,
count=ContextInfo.day_ma_len + 5,
dividend_type='front',
subscribe=True
)
if stock not in day_data or len(day_data[stock]) < ContextInfo.day_ma_len:
# 数据不足,跳过
return
df_day = day_data[stock]
# 计算日线 MA20
df_day['ma20'] = df_day['close'].rolling(window=ContextInfo.day_ma_len).mean()
# 获取最新的日线收盘价和均线值
last_day_close = df_day['close'].iloc[-1]
last_day_ma20 = df_day['ma20'].iloc[-1]
# 判断日线趋势:价格在20日均线之上为看涨
is_day_bullish = last_day_close > last_day_ma20
# ----------------------------------------------------------------
# 第二步:获取5分钟数据,判断入场信号
# ----------------------------------------------------------------
min_data = ContextInfo.get_market_data_ex(
['close'],
[stock],
period=ContextInfo.period_min,
count=ContextInfo.min_ma_long + 5,
dividend_type='front',
subscribe=True
)
if stock not in min_data or len(min_data[stock]) < ContextInfo.min_ma_long:
return
df_min = min_data[stock]
# 计算5分钟均线
df_min['ma_short'] = df_min['close'].rolling(window=ContextInfo.min_ma_short).mean()
df_min['ma_long'] = df_min['close'].rolling(window=ContextInfo.min_ma_long).mean()
# 获取当前和上一根K线的均线值(用于判断交叉)
curr_short = df_min['ma_short'].iloc[-1]
curr_long = df_min['ma_long'].iloc[-1]
prev_short = df_min['ma_short'].iloc[-2]
prev_long = df_min['ma_long'].iloc[-2]
# 判断金叉:上一根短线<=长线,且当前短线>长线
is_golden_cross = (prev_short <= prev_long) and (curr_short > curr_long)
# 判断死叉:上一根短线>=长线,且当前短线<长线
is_death_cross = (prev_short >= prev_long) and (curr_short < curr_long)
# 获取最新价格用于下单
current_price = df_min['close'].iloc[-1]
# ----------------------------------------------------------------
# 第三步:交易执行
# ----------------------------------------------------------------
# 获取当前持仓
curr_pos = get_position(ContextInfo, stock)
# 买入逻辑:日线看涨 + 5分钟金叉 + 无持仓
if is_day_bullish and is_golden_cross and curr_pos == 0:
print(f"【买入信号】{stock} 日线看涨(价格:{last_day_close:.2f}>MA20:{last_day_ma20:.2f}), 5分钟金叉")
# 使用 order_shares 下单指定股数
order_shares(stock, ContextInfo.trade_qty, 'fix', current_price, ContextInfo, ContextInfo.accID)
# 卖出逻辑:5分钟死叉 + 有持仓 (这里只看小周期信号平仓,也可加入日线趋势反转平仓)
elif is_death_cross and curr_pos > 0:
print(f"【卖出信号】{stock} 5分钟死叉,执行平仓")
order_shares(stock, -curr_pos, 'fix', current_price, ContextInfo, ContextInfo.accID)
使用说明
- 设置运行周期:在 QMT 界面运行此策略时,请务必将主图周期设置为 5分钟(或者您希望捕捉信号的小周期)。
- 账号设置:请将代码中的
ContextInfo.accID = '6000000000'修改为您实际的资金账号。 - 数据补充:
- 在运行或回测前,请确保已下载了对应股票的 日线 和 5分钟线 的历史数据。
- QMT 菜单 -> 数据管理 -> 补充数据。
- API 选择:
- 代码使用了
get_market_data_ex,这是 QMT 推荐的高效数据接口,支持订阅实时行情。 - 使用了
order_shares进行下单,fix代表限价单(以当前最新价下单),如果需要市价单可改为market(注意回测和实盘的区别)。
- 代码使用了
常见问题 (Q&A)
Q: 为什么要在5分钟周期上运行,而不是日线周期?
A: 因为买入信号是基于5分钟均线金叉触发的。如果在日线周期上运行,handlebar 函数每天只会在收盘时调用一次,无法捕捉盘中的5分钟级别信号。
Q: get_market_data_ex 中的 subscribe=True 是什么意思?
A: 这表示订阅数据。在实盘模式下,当有新的行情推送过来时,QMT 会自动更新数据并触发策略计算,保证信号的实时性。
Q: 如何修改“日线看涨”的定义?
A: 您可以修改代码中 is_day_bullish 的计算逻辑。例如,改为“日线 MA5 > 日线 MA10”:
# 计算日线 MA5 和 MA10
df_day['ma5'] = df_day['close'].rolling(window=5).mean()
df_day['ma10'] = df_day['close'].rolling(window=10).mean()
is_day_bullish = df_day['ma5'].iloc[-1] > df_day['ma10'].iloc[-1]