问题描述
解决方案
实现日线和分钟线共振(Multi-timeframe Resonance)的交易策略,核心逻辑是**“大周期定方向,小周期找买点”**。
策略逻辑设计
- 大周期(日线):用于判断主要趋势。
- 指标:20日均线(MA20)。
- 逻辑:当日线收盘价 > 日线MA20时,判定为多头趋势,允许做多;否则禁止开仓或清仓。
- 小周期(5分钟线):用于寻找具体的进场和出场信号。
- 指标:5周期均线(MA5)和 10周期均线(MA10)。
- 买入信号:日线处于多头趋势 且 5分钟线出现金叉(MA5上穿MA10)。
- 卖出信号:5分钟线出现死叉(MA5下穿MA10)。
- 运行频率:策略应挂载在 5分钟 周期上运行,以便及时捕捉分钟级别的信号。
QMT 策略代码实现
以下是完整的 Python 策略代码。请在 QMT 的策略编辑器中新建一个 Python 策略,将代码复制进去。
注意:
- 请将
ContextInfo.account_id替换为您真实的资金账号。 - 运行时请选择 5分钟 周期作为主图周期。
# -*- coding: gbk -*-
import pandas as pd
import numpy as np
def init(ContextInfo):
"""
初始化函数,设定策略参数和变量
"""
# 1. 设置资金账号 (请修改为您自己的账号)
ContextInfo.account_id = '6000000000'
ContextInfo.account_type = 'STOCK' # 股票账号
ContextInfo.set_account(ContextInfo.account_id)
# 2. 设置股票池 (示例:平安银行)
ContextInfo.stock_code = '000001.SZ'
ContextInfo.set_universe([ContextInfo.stock_code])
# 3. 策略参数设置
# 日线参数
ContextInfo.day_period = '1d'
ContextInfo.day_ma_len = 20 # 日线趋势均线
# 分钟线参数 (主图运行周期建议设为 5m)
ContextInfo.min_period = '5m' # 策略运行周期
ContextInfo.min_ma_fast = 5 # 分钟快线
ContextInfo.min_ma_slow = 10 # 分钟慢线
# 交易数量
ContextInfo.trade_vol = 100 # 每次交易100股
def get_position(ContextInfo, stock_code):
"""
获取当前持仓数量的辅助函数
"""
try:
# 获取持仓对象列表
positions = get_trade_detail_data(ContextInfo.account_id, ContextInfo.account_type, 'POSITION')
for pos in positions:
if pos.m_strInstrumentID + '.' + pos.m_strExchangeID == stock_code:
return pos.m_nVolume
except Exception as e:
print("获取持仓失败: ", e)
return 0
def handlebar(ContextInfo):
"""
K线运行逻辑,每根K线(或Tick)调用一次
"""
# 获取当前正在处理的股票代码
stock = ContextInfo.stock_code
# ----------------------------------------------------------------
# 第一步:获取日线数据,判断大趋势 (大周期)
# ----------------------------------------------------------------
# 获取足够计算MA20的日线数据
# 注意:dividend_type='front' 为前复权,确保价格连续
day_data_map = ContextInfo.get_market_data_ex(
['close'],
[stock],
period=ContextInfo.day_period,
count=ContextInfo.day_ma_len + 5,
dividend_type='front',
fill_data=True,
subscribe=True
)
if stock not in day_data_map:
return # 数据未准备好
df_day = day_data_map[stock]
if len(df_day) < ContextInfo.day_ma_len:
return # 数据长度不足
# 计算日线 MA20
day_ma = df_day['close'].rolling(window=ContextInfo.day_ma_len).mean()
# 获取最新的日线收盘价和均线值
# 注意:盘中运行时,iloc[-1] 是包含当天实时数据的(动态复盘)
current_day_close = df_day['close'].iloc[-1]
current_day_ma = day_ma.iloc[-1]
# 判断日线趋势:收盘价 > MA20 为多头趋势
is_day_bullish = current_day_close > current_day_ma
# ----------------------------------------------------------------
# 第二步:获取分钟线数据,寻找买卖点 (小周期)
# ----------------------------------------------------------------
# 获取分钟线数据
min_data_map = ContextInfo.get_market_data_ex(
['close'],
[stock],
period=ContextInfo.min_period,
count=ContextInfo.min_ma_slow + 5,
dividend_type='front',
fill_data=True,
subscribe=True
)
if stock not in min_data_map:
return
df_min = min_data_map[stock]
if len(df_min) < ContextInfo.min_ma_slow:
return
# 计算分钟线均线
min_ma_fast = df_min['close'].rolling(window=ContextInfo.min_ma_fast).mean()
min_ma_slow = df_min['close'].rolling(window=ContextInfo.min_ma_slow).mean()
# 获取当前和上一根K线的均线值,用于判断交叉
curr_fast = min_ma_fast.iloc[-1]
curr_slow = min_ma_slow.iloc[-1]
prev_fast = min_ma_fast.iloc[-2]
prev_slow = min_ma_slow.iloc[-2]
# 判断金叉:快线上穿慢线
is_golden_cross = (prev_fast <= prev_slow) and (curr_fast > curr_slow)
# 判断死叉:快线下穿慢线
is_death_cross = (prev_fast >= prev_slow) and (curr_fast < curr_slow)
# ----------------------------------------------------------------
# 第三步:交易执行逻辑 (共振判断)
# ----------------------------------------------------------------
# 获取当前持仓
current_pos = get_position(ContextInfo, stock)
# 1. 买入逻辑:日线多头趋势 + 分钟线金叉 + 当前无持仓
if is_day_bullish and is_golden_cross and current_pos == 0:
print(f"【买入信号】{stock} 日线趋势向上,5分钟金叉。价格: {df_min['close'].iloc[-1]}")
# 23: 买入, 1101: 单股单账号, 5: 最新价
passorder(23, 1101, ContextInfo.account_id, stock, 5, -1, ContextInfo.trade_vol, ContextInfo)
# 2. 卖出逻辑:分钟线死叉 + 当前有持仓 (止盈/止损)
# 注:这里只看小周期死叉离场,也可以加上日线转空的条件
elif is_death_cross and current_pos > 0:
print(f"【卖出信号】{stock} 5分钟死叉。价格: {df_min['close'].iloc[-1]}")
# 24: 卖出, 1101: 单股单账号, 5: 最新价
passorder(24, 1101, ContextInfo.account_id, stock, 5, -1, current_pos, ContextInfo)
# 调试信息输出 (可选)
if ContextInfo.is_last_bar():
print(f"监控中: {stock} | 日线多头: {is_day_bullish} | 5分快线: {curr_fast:.2f} | 5分慢线: {curr_slow:.2f}")
代码关键点解析
-
跨周期数据获取 (
get_market_data_ex):- QMT 的
get_market_data_ex接口非常强大,允许你在同一个策略中请求不同周期的数据。 - 代码中分别请求了
period=ContextInfo.day_period('1d') 和period=ContextInfo.min_period('5m') 的数据。 - 注意:在分钟周期上运行策略时,获取到的日线数据最后一行通常是当日截至目前的动态数据。这符合“共振”的逻辑,即参考当天的实时趋势。
- QMT 的
-
共振逻辑实现:
- 变量
is_day_bullish锁定了大方向。只有当它为True时,代码才会进入买入判断逻辑。 - 变量
is_golden_cross提供了精确的进场时机。 - 两者结合
if is_day_bullish and is_golden_cross:实现了共振。
- 变量
-
交易执行 (
passorder):- 使用了标准的
passorder函数。 opType=23代表股票买入,24代表股票卖出。prType=5代表使用最新价下单(实盘中可能需要考虑滑点或使用对手价prType=14)。
- 使用了标准的
-
持仓检查:
- 为了防止重复开仓,代码中增加了一个
get_position辅助函数,通过get_trade_detail_data接口查询当前账户的实际持仓。
- 为了防止重复开仓,代码中增加了一个
如何回测与运行
- 回测设置:
- 在策略编辑器右侧的“回测参数”中,设置好开始和结束时间。
- 主图周期:必须选择 5分钟 (或 1分钟/15分钟,取决于你想捕捉的小周期级别)。如果选日线,分钟线的逻辑将无法正常触发。
- 基准:选择沪深300或其他。
- 数据下载:
- 确保在“数据管理”中下载了对应股票的 日线 和 5分钟线 历史数据。
- 实盘/模拟:
- 点击“运行”即可启动。确保账号已登录且
ContextInfo.account_id填写正确。
- 点击“运行”即可启动。确保账号已登录且
Q&A
Q: 为什么不在日线周期上运行这个策略?
A: 如果在日线周期上运行,handlebar 每天只会在收盘时调用一次。这样你就无法捕捉到盘中 5 分钟级别的金叉买点,失去了“共振”中利用小周期优化进场点的意义。
Q: 如何让日线趋势判断更稳定,不随盘中价格波动而变化?
A: 当前代码使用的是当日实时价格与 MA20 比较。如果你希望趋势判断更保守,可以使用昨日的收盘数据。修改代码如下:
# 使用倒数第二根K线(昨日)的数据
prev_day_close = df_day['close'].iloc[-2]
prev_day_ma = day_ma.iloc[-2]
is_day_bullish = prev_day_close > prev_day_ma
Q: get_market_data_ex 和 get_market_data 有什么区别?
A: get_market_data_ex 是 QMT 推荐的新版接口,返回的数据结构更清晰(字典包含 DataFrame),且支持更丰富的参数(如复权方式、停牌填充等),在处理多股、多周期数据时更稳定。