在 QMT 平台使用 Python 实现 MACD 金叉死叉交易策略
本文将详细介绍如何基于 QMT 量化交易平台,利用 Python 编写一个经典的 MACD 技术指标交易策略。该策略以“金叉买入、死叉卖出”为核心逻辑,结合真实行情数据进行信号判断与交易执行,适合初学者快速掌握量化策略开发流程。
策略核心逻辑解析
本策略依赖于 MACD 指标的三要素:快线(DIF)、慢线(DEA)以及柱状图(MACD 值)。通过监测 DIF 与 DEA 的交叉关系来生成买卖信号。
- 金叉(买入信号):当 DIF 线由下向上穿越 DEA 线时触发,表示短期趋势强于长期趋势,视为看涨信号。
- 死叉(卖出信号):当 DIF 线由上向下穿越 DEA 线时触发,意味着上涨动能减弱,作为清仓依据。
talib
交易执行规则
为避免频繁交易和无效信号干扰,策略设置了持仓状态判断机制:
- 若出现金叉信号且当前未持有仓位,则执行全仓买入操作;
- 若发生死叉信号且账户中已有持仓,则立即全部卖出,实现止盈或止损。
代码实现结构说明
整个策略分为两个主要函数:init 用于初始化设置,handlebar 负责每根 K 线到来时的处理逻辑。
# -*- coding: gbk -*-
import pandas as pd
import numpy as np
import talib
def init(ContextInfo):
"""
策略启动时仅运行一次,完成参数设定与账户绑定
"""
# 设置资金账号(请替换为实际账号)
ContextInfo.accid = '6000000000'
# 指定账户类型:支持 'STOCK' 或 'FUTURE'
ContextInfo.accountType = 'STOCK'
# 绑定指定账户以便接收成交回报
ContextInfo.set_account(ContextInfo.accid)
# 定义 MACD 计算参数
ContextInfo.fast_period = 12 # 快速均线周期
ContextInfo.slow_period = 26 # 慢速均线周期
ContextInfo.signal_period = 9 # 信号线平滑周期
print("策略初始化完成,MACD参数: {}, {}, {}".format(
ContextInfo.fast_period, ContextInfo.slow_period, ContextInfo.signal_period))
def handlebar(ContextInfo):
"""
每根K线到达时自动调用一次
"""
# 获取当前主图显示的股票代码与K线周期
stock_code = ContextInfo.stockcode
period = ContextInfo.period
# 1. 获取历史行情数据
data_len = 100 # 取最近100根K线确保指标计算稳定
market_data = ContextInfo.get_market_data_ex(
fields=['close'],
codes=[stock_code],
period=period,
count=data_len,
dividend_type='front' # 前复权处理,利于技术分析
)
# 数据校验:若获取失败或长度不足则跳过本次处理
if stock_code not in market_data or len(market_data[stock_code]) < 35:
return
# 提取收盘价并转换为 NumPy 数组供 talib 使用
close_price = market_data[stock_code]['close']
close_np = np.array(close_price)
# 2. 计算 MACD 指标值
diff, dea, macd = talib.MACD(
close_np,
fastperiod=ContextInfo.fast_period,
slowperiod=ContextInfo.slow_period,
signalperiod=ContextInfo.signal_period
)
# 获取最新两根K线的 DIF 和 DEA 数值
current_diff = diff[-1] # 当前K线
current_dea = dea[-1]
last_diff = diff[-2] # 上一根K线
last_dea = dea[-2]
# 排除无效值(NaN)
if np.isnan(current_diff) or np.isnan(last_diff):
return
# 3. 判断交叉信号
golden_cross = (last_diff < last_dea) and (current_diff > current_dea) # 金叉
death_cross = (last_diff > last_dea) and (current_diff < current_dea) # 死叉
# 查询当前持仓情况
positions = ContextInfo.get_positions()
has_position = len(positions) > 0
# 执行交易指令
if golden_cross and not has_position:
# 全仓买入
order_value = ContextInfo.get_cash() * 0.99 # 预留部分资金防误差
ContextInfo.order_value(stock_code, price=0, value=order_value, side='buy', order_type='market')
print(f"【买入】在 {stock_code} 触发金叉,执行市价单买入")
elif death_cross and has_position:
# 清仓卖出
for pos in positions:
if pos['symbol'] == stock_code:
sell_amount = pos['volume']
ContextInfo.order_volume(stock_code, price=0, volume=sell_amount, side='sell', order_type='market')
print(f"【卖出】在 {stock_code} 触发死叉,执行市价单清仓")
总结
该策略完整实现了 MACD 指标驱动的自动化交易流程,涵盖数据获取、指标计算、信号识别与订单执行四大环节。用户可根据自身需求调整参数、优化入场条件或加入风控模块以提升实战表现。
golden_cross = (last_diff < last_dea) and (current_diff > current_dea)
# 死叉判断:上一K线满足 DIF > DEA,当前K线变为 DIF < DEA
death_cross = (last_diff > last_dea) and (current_diff < current_dea)
# 获取当前持仓信息
# 注意:在回测环境中,ContextInfo.get_position() 可能无法使用,建议采用 get_trade_detail_data 方法获取持仓数据
# 为兼容实盘与回测场景,此处通过交易明细进行持仓查询
positions = ContextInfo.get_trade_detail_data(ContextInfo.accid, ContextInfo.accountType, 'POSITION')
current_vol = 0
for pos in positions:
if pos.m_strInstrumentID == stock_code:
current_vol = pos.m_nVolume
break
# 执行交易逻辑
# 获取最新成交价格用于下单操作
last_price = close_price.iloc[-1]
# 仅在K线收盘后执行下单操作,防止盘中信号频繁波动(也可结合 ContextInfo.is_last_bar() 判断)
# 在回测中每根bar都会触发;实盘通常在bar结束或tick级别处理
# --- 买入条件触发 ---
if golden_cross:
if current_vol == 0:
print(f"[{ContextInfo.barpos}] {stock_code} 出现金叉信号,准备买入")
ContextInfo.draw_text(True, last_price, '金叉买入')
# 下单示例:买入1000股(实际应根据可用资金动态计算)
# opType=23 表示买入,orderType=1101 代表普通下单,priceType=5 使用最新价
passorder(23, 1101, ContextInfo.accid, stock_code, 5, -1, 1000, ContextInfo)
# --- 卖出条件触发 ---
elif death_cross:
if current_vol > 0:
print(f"[{ContextInfo.barpos}] {stock_code} 出现死叉信号,准备卖出")
ContextInfo.draw_text(True, last_price, '死叉卖出')
# 卖出全部当前持仓
# opType=24 表示卖出操作
passorder(24, 1101, ContextInfo.accid, stock_code, 5, -1, current_vol, ContextInfo)
# (可选)在副图区域绘制MACD相关指标,便于回测分析
ContextInfo.paint('DIFF', current_diff, -1, 0, 'white')
ContextInfo.paint('DEA', current_dea, -1, 0, 'yellow')
# 绘制柱状图(MACD柱)
macd_color = 'red' if macd[-1] > 0 else 'green'
ContextInfo.paint('MACD', macd[-1] * 2, -1, 42, macd_color) # 国内软件习惯将 (diff-dea) 的值乘以2显示
使用步骤说明
- 创建策略:打开 QMT 策略编辑器,新建一个 Python 类型的策略文件。
- 粘贴代码:将上述完整策略代码复制到编辑器中。
- 配置账号信息:
定位到相关函数中的参数设置部分
,找到占位符ContextInfo.accid = '6000000000'
,将其替换为您实际使用的资金账户号码。'6000000000'init - 补充历史数据:
进入 QMT 菜单栏,点击【数据管理】→【补充数据】,下载所需股票的历史日线或分钟线数据,以支持回测或实盘运行。
- 运行或回测策略:
- 回测模式:点击编辑器上方“回测”按钮,设定时间范围、比较基准和交易费率后开始测试。
- 实盘/模拟交易:可在行情图表中加载该策略,或通过“模型交易”模块启动运行。
核心代码要点解析
get_market_data_ex:推荐使用此接口获取行情数据。本例中采用前复权模式 dividend_type='front',原因是技术指标如 MACD 在未复权情况下会因除权除息产生数据断层,影响信号准确性。
talib.MACD:QMT 平台内置了高效的 talib 计算库,适用于快速生成各类技术指标。需要注意的是,talib 返回的 macd 序列一般为 diff - dea 格式,开发者需了解其输出结构以便正确引用。国内常用的看盘工具(例如同花顺、通达信)所展示的MACD柱状图一般为
(diff - dea) * 2
因此,在绘图过程中,我对其进行了相应的
* 2
处理,以保证显示效果的一致性。
passorder:该函数是QMT系统中最为核心的下单指令模块。
:用于执行买入操作;23
:用于执行卖出操作;24
:表示按股数进行交易,适用于单账户、单只股票的委托下单模式;1101
:意味着采用最新价格成交,在回测环境中通常使用K线的收盘价,在实盘中则依据最新的Tick数据进行撮合。5
常见问题解答(Q&A)
Q: 回测为何没有产生任何交易记录?
A: 可能原因如下:
1. 请确认是否已成功下载所需的历史行情数据。
2. 检查
ContextInfo.accid
参数是否配置正确。即使在回测模式下使用的是虚拟账户,程序中获取持仓信息仍依赖于指定的账号ID。
Q: 如何修改MACD的技术参数?
A: 只需调整
init
函数内部的
ContextInfo.fast_period
等相关变量即可实现参数自定义。
Q: 实盘运行时如何减少信号闪烁问题?
A: 上述策略代码会在
handlebar
环境中持续运行,而该值会随着最新Tick数据实时更新。为防止在K线尚未结束时就触发交易(即出现信号闪烁),建议结合
ContextInfo.is_last_bar()
与时间判断逻辑,或设定仅在K线即将收尾时刻(如14:59:50)才发出订单,从而提升信号稳定性。


雷达卡


京公网安备 11010802022788号







