策略逻辑概述
本教程旨在详解如何在 QMT 量化交易平台上,使用 Python 编写一个经典的 MACD 指标交易策略。该策略遵循以下核心逻辑:
- 指标计算:通过
talib 库计算 MACD 指标,包括快线 (DIF)、慢线 (DEA) 和柱状图 (MACD)。默认参数为 12, 26, 9。
- 交易信号:
- 金叉 (买入信号):当 DIF 线由下向上穿过 DEA 线时触发。
- 死叉 (卖出信号):当 DIF 线由上向下穿过 DEA 线时触发。
- 执行规则:
- 出现金叉信号且账户无该股票持仓时,执行买入操作。
- 出现死叉信号且账户持有该股票时,执行卖出清仓操作。
完整的 Python 策略源码
以下代码可直接复制到 QMT 策略编辑器中使用。通过编写此类策略,你可以深入理解 Python 在金融数据分析与自动化交易中的应用。
# -*- 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. 获取历史行情数据
# 获取足够多的数据以确保MACD计算准确,这里取过去100根K线
data_len = 100
# 使用 get_market_data_ex 获取数据 (推荐方式)
# dividend_type='front' 表示前复权,计算技术指标通常使用前复权
market_data = ContextInfo.get_market_data_ex(
['close'],
[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
# 提取收盘价序列
close_price = market_data[stock_code]['close']
# 转为numpy数组,方便talib计算
close_np = np.array(close_price)
# 2. 计算 MACD 指标
# diff: 快线, dea: 慢线, macd: 柱状图
diff, dea, macd = talib.MACD(
close_np,
fastperiod=ContextInfo.fast_period,
slowperiod=ContextInfo.slow_period,
signalperiod=ContextInfo.signal_period
)
# 获取当前K线和上一根K线的指标值
# -1 代表当前最新一根K线(如果是盘中,则是正在跳动的K线)
# -2 代表上一根K线
current_diff = diff[-1]
current_dea = dea[-1]
last_diff = diff[-2]
last_dea = dea[-2]
# 检查是否计算出有效值 (剔除NaN)
if np.isnan(current_diff) or np.isnan(last_diff):
return
# 3. 判断金叉和死叉
# 金叉:上一根K线 DIF < DEA,且当前K线 DIF > DEA
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)
# 4. 获取当前持仓情况
# 注意:回测模式下,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
# 5. 交易逻辑执行
# 获取最新价格用于下单
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)
# 6. 在副图画出MACD指标 (可选,方便回测观察)
ContextInfo.paint('DIFF', current_diff, -1, 0, 'white')
ContextInfo.paint('DEA', current_dea, -1, 0, 'yellow')
# 画柱状图
macd_color = 'red' if macd[-1] > 0 else 'green'
ContextInfo.paint('MACD', macd[-1] * 2, -1, 42, macd_color) # talib算出的macd通常是diff-dea,国内软件习惯显示(diff-dea)*2
策略部署与运行步骤
- 新建策略:在 QMT 客户端内打开策略编辑器,创建一个新的 Python 策略文件。
- 粘贴代码:将上述完整代码复制到新建的策略文件中。
- 配置账号:找到
init 函数里的 ContextInfo.accid = '6000000000',将其中的账号字符串替换成你自己的真实资金账号。
- 补充历史数据:
- 点击 QMT 菜单栏的【数据管理】->【补充数据】。
- 选择需要回测或交易的股票,下载相应的日线或分钟线历史数据。
- 执行策略:
- 回测:在编辑器点击“回测”按钮,设置好时间范围、基准和交易费率后即可开始。
- 实盘/模拟:在行情K线图上加载该策略,或通过“模型交易”界面进行运行。
核心代码解析
数据获取与处理
get_market_data_ex:这是 QMT 推荐的行情获取接口。参数 dividend_type='front' 指使用前复权数据,这对计算 MACD 等基于价格的技术指标至关重要,可以避免因除权除息导致的指标跳空失真。
- 数据清洗:代码中检查了数据是否存在及长度是否足够(大于35根,以确保MACD能计算出有效值),这是构建健壮策略的基础。
指标计算
talib.MACD:QMT 内置了强大的 talib 技术分析库。需要注意的是,talib 返回的 macd 序列值通常是 (DIF - DEA),而国内主流软件(如通达信)显示的 MACD 柱状图是 (DIF - DEA) * 2。因此,在最后的绘图部分对数值进行了 * 2 处理以符合观察习惯。
交易下单
passorder:这是 QMT 最核心的下单函数。
23 代表买入操作,24 代表卖出操作。
1101 表示单市场、单股票、按数量下单的模式。
5 表示以最新价 (PriceType.LATEST) 下单。在回测中,此价格通常对应K线的收盘价;在实盘中,则对应触发时的最新Tick价格。
常见问题与优化建议
Q: 回测时没有产生任何交易怎么办?
A: 首先,请确认已正确下载了标的股票的历史数据。其次,检查代码中 ContextInfo.accid 是否已修改或设置为有效的回测账号(部分回测上下文可能需要特定ID)。最后,检查MACD参数是否过于敏感或迟钝,导致在回测区间内未产生金叉/死叉信号。
Q: 如何调整 MACD 的计算参数?
A: 直接修改 init 函数中的 ContextInfo.fast_period, ContextInfo.slow_period, ContextInfo.signal_period 这三个变量的值即可。
Q: 实盘中如何防止因K线未走完导致的信号闪烁?
A: 当前代码在 handlebar 函数中运行,在实盘Tick级推送下可能每笔成交都会触发计算。为了避免在单根K线内因价格波动反复开平仓,可以加入时间过滤逻辑。例如,使用 ContextInfo.is_last_bar() 判断是否为K线最后一笔,或限定仅在交易日特定时间(如下午14:55之后)才允许发出交易指令。
策略逻辑示意图(如金叉死叉识别点)可帮助理解买卖时机,如下图所示:

提示:本策略为MACD基础应用示例,实际交易中需结合风险管理、仓位控制等多方面因素进行完善。对利用人工智能方法生成或优化量化策略模型感兴趣的读者,可以关注相关领域的最新进展。