3 分钟快速生成代码
输入想法,AI 即刻生成可运行代码
这是一个基于 QMT 平台的 Python 策略,实现了 MACD 指标金叉买入、死叉卖出 的逻辑。
talib 库计算 MACD 指标(快线 DIF、慢线 DEA、柱状图 MACD)。参数默认为 12, 26, 9。# -*- 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
init 函数中的 ContextInfo.accid = '6000000000',将 '6000000000' 修改为您实际的资金账号。get_market_data_ex:这是 QMT 获取行情的推荐接口。我们使用了 dividend_type='front'(前复权),因为 MACD 等技术指标在除权除息日如果不复权会出现断层,导致信号失真。talib.MACD:QMT 内置了 talib 库,计算指标非常快。注意 talib 返回的 macd 序列通常是 diff - dea 的值,而国内看盘软件(如通达信、同花顺)显示的 MACD 柱状图通常是 (diff - dea) * 2,所以在画图时我做了 * 2 处理。passorder:这是 QMT 最核心的下单函数。
23: 买入, 24: 卖出。1101: 代表单股、单账号、按股数下单。5: 代表使用最新价下单(在回测中通常以收盘价成交,实盘中为最新tick价格)。Q: 为什么回测没有交易?
A: 1. 请检查是否下载了对应的历史数据。2. 请检查 ContextInfo.accid 是否设置正确(回测模式下虽然是虚拟账号,但代码逻辑中获取持仓依赖于账号ID)。
Q: 如何调整 MACD 参数?
A: 修改 init 函数中的 ContextInfo.fast_period 等变量即可。
Q: 实盘中如何避免信号闪烁?
A: 上述代码在 handlebar 中运行。在实盘中,handlebar 会随最新的 Tick 跳动。为了避免 K 线没走完就下单(信号闪烁),可以使用 ContextInfo.is_last_bar() 结合时间判断,或者在代码中加入逻辑只在 K 线即将结束时(例如 14:59:50)下单。