🚀 AI 一键生成 qmt 策略代码

如何编写基于肯特纳通道的趋势突破策略?(Python)

qmt | 发布时间: 2025/12/21
以下内容由 EasyQuant 生成。

问题描述

帮我写一个肯特纳通道交易策略

解决方案

这是一个基于 QMT (Quantitative Trading Platform)肯特纳通道 (Keltner Channel) 趋势突破交易策略。

策略逻辑说明

肯特纳通道(Keltner Channel)是一种趋势跟随指标,类似于布林带,但它使用 ATR (平均真实波幅) 来设定通道宽度,而不是标准差。

  1. 中轨 (Middle Line): 收盘价的指数移动平均线 (EMA),通常周期为 20。
  2. 上轨 (Upper Band): 中轨 + (ATR * 乘数)。
  3. 下轨 (Lower Band): 中轨 - (ATR * 乘数)。
  4. 交易信号:
    • 买入: 当收盘价突破上轨时,视为上升趋势确立,全仓买入。
    • 卖出: 当收盘价跌破下轨时,视为趋势反转,清仓卖出。

QMT 策略代码

# -*- coding: gbk -*-
import pandas as pd
import numpy as np

def init(ContextInfo):
    """
    初始化函数,设定策略参数和变量
    """
    # 1. 设置策略参数
    ContextInfo.period = 20       # EMA和ATR的计算周期
    ContextInfo.multiplier = 2.0  # 通道宽度的ATR倍数
    ContextInfo.stock_code = '600000.SH'  # 示例标的:浦发银行
    
    # 2. 设置股票池
    ContextInfo.set_universe([ContextInfo.stock_code])
    
    # 3. 设置资金账号 (请替换为您自己的实盘或模拟账号)
    ContextInfo.account_id = 'YOUR_ACCOUNT_ID' 
    ContextInfo.set_account(ContextInfo.account_id)
    
    # 4. 设定回测相关参数 (仅回测有效)
    ContextInfo.set_commission(0, [0.0001, 0.0001, 0.0003, 0.0003, 0, 5]) # 设置费率
    ContextInfo.capital = 1000000 # 初始资金

def handlebar(ContextInfo):
    """
    K线周期运行函数
    """
    # 获取当前正在运行的K线索引
    index = ContextInfo.barpos
    
    # 获取当前图表的周期 (例如 '1d')
    period = ContextInfo.period
    
    # 获取当前主图代码
    stock = ContextInfo.stock_code
    
    # 1. 获取历史行情数据
    # 我们需要足够的数据来计算EMA和ATR,所以取 period + 10 根K线
    # 使用 get_market_data_ex 获取数据 (推荐方式)
    data_len = ContextInfo.period + 20
    market_data = ContextInfo.get_market_data_ex(
        ['open', 'high', 'low', 'close'], 
        [stock], 
        period=period, 
        count=data_len,
        dividend_type='front' # 前复权
    )
    
    if stock not in market_data:
        return
        
    df = market_data[stock]
    
    # 数据长度不足时不计算
    if len(df) < ContextInfo.period + 1:
        return

    # 2. 计算肯特纳通道指标
    # 2.1 计算中轨: EMA(Close, 20)
    df['ema'] = df['close'].ewm(span=ContextInfo.period, adjust=False).mean()
    
    # 2.2 计算 ATR (平均真实波幅)
    # TR = Max(High-Low, Abs(High-PrevClose), Abs(Low-PrevClose))
    df['prev_close'] = df['close'].shift(1)
    df['h_l'] = df['high'] - df['low']
    df['h_pc'] = abs(df['high'] - df['prev_close'])
    df['l_pc'] = abs(df['low'] - df['prev_close'])
    df['tr'] = df[['h_l', 'h_pc', 'l_pc']].max(axis=1)
    
    # ATR通常使用简单移动平均(SMA)或EMA,这里使用SMA
    df['atr'] = df['tr'].rolling(window=ContextInfo.period).mean()
    
    # 2.3 计算上轨和下轨
    df['upper'] = df['ema'] + (df['atr'] * ContextInfo.multiplier)
    df['lower'] = df['ema'] - (df['atr'] * ContextInfo.multiplier)
    
    # 获取最新一根K线的数据(用于判断信号)
    current_close = df['close'].iloc[-1]
    current_upper = df['upper'].iloc[-1]
    current_lower = df['lower'].iloc[-1]
    
    # 打印调试信息 (可选)
    # print(f"时间: {df.index[-1]}, 收盘: {current_close:.2f}, 上轨: {current_upper:.2f}, 下轨: {current_lower:.2f}")

    # 3. 获取当前持仓
    # 注意:get_trade_detail_data 返回的是列表,需要遍历查找
    positions = get_trade_detail_data(ContextInfo.account_id, 'stock', 'position')
    current_holding = 0
    for pos in positions:
        if pos.m_strInstrumentID + '.' + pos.m_strExchangeID == stock:
            current_holding = pos.m_nVolume
            break

    # 4. 交易逻辑
    # 信号生成:价格突破上轨买入,跌破下轨卖出
    
    # 买入条件:收盘价 > 上轨 且 当前无持仓
    if current_close > current_upper and current_holding == 0:
        print(f"买入信号触发: 价格 {current_close} > 上轨 {current_upper}")
        # 全仓买入 (目标仓位 100%)
        order_target_percent(stock, 1.0, ContextInfo, ContextInfo.account_id)
        
    # 卖出条件:收盘价 < 下轨 且 当前有持仓
    elif current_close < current_lower and current_holding > 0:
        print(f"卖出信号触发: 价格 {current_close} < 下轨 {current_lower}")
        # 清仓卖出 (目标仓位 0%)
        order_target_percent(stock, 0.0, ContextInfo, ContextInfo.account_id)

代码关键点解析

  1. 编码格式: 第一行 # -*- coding: gbk -*- 是必须的,因为 QMT 的 Python 环境默认使用 GBK 编码,防止中文注释乱码。
  2. 数据获取 (get_market_data_ex):
    • 这是 QMT 推荐的新版数据接口。
    • 我们获取了 high, low, close 用于计算 ATR 和 EMA。
    • dividend_type='front' 设置为前复权,这对于技术指标计算非常重要,避免分红除权导致的价格跳空影响指标。
  3. 指标计算 (Pandas):
    • EMA: 使用 df['close'].ewm(span=n).mean() 计算指数移动平均。
    • ATR: 手动计算 TR (True Range),然后使用 rolling(window=n).mean() 计算 ATR。这样避免了依赖第三方库(如 TA-Lib),使代码兼容性更好。
  4. 交易函数 (order_target_percent):
    • 这是一个非常方便的高级函数。
    • 1.0 代表调整仓位至总资产的 100%(全仓买入)。
    • 0.0 代表调整仓位至 0%(清仓卖出)。
    • 它会自动计算需要买入或卖出的股数,并处理下单逻辑。

如何使用

  1. 打开 QMT 客户端,进入 “模型研究” -> “策略编辑器”
  2. 新建一个 Python 策略
  3. 将上述代码复制粘贴到编辑器中。
  4. 修改账号: 将 ContextInfo.account_id = 'YOUR_ACCOUNT_ID' 中的 'YOUR_ACCOUNT_ID' 替换为你实际的资金账号。
  5. 回测: 点击编辑器上方的“回测”按钮,选择回测区间和周期(建议日线),即可查看策略表现。
  6. 实盘: 如果要在实盘运行,请确保在“模型交易”界面加载该策略,并将运行模式设置为“实盘”。

常见问题 (Q&A)

Q: 为什么使用 get_market_data_ex 而不是 get_market_data?
A: get_market_data_ex 是 QMT 较新的接口,返回的数据结构更统一(Pandas DataFrame),且支持更丰富的参数(如复权方式、订阅开关),处理起来更方便。

Q: 如何修改通道的灵敏度?
A: 修改 init 函数中的 ContextInfo.period (周期) 和 ContextInfo.multiplier (倍数)。

  • 增大 periodmultiplier 会使通道变宽,交易频率降低,过滤震荡,但可能滞后。
  • 减小 periodmultiplier 会使通道变窄,交易频率增加,更敏感,但假信号可能增多。

Q: 这个策略可以用于期货吗?
A: 可以,但需要修改以下几点:

  1. stock_code 改为期货合约代码 (如 IF00.IF)。
  2. order_target_percent 在期货中可能不如 buy_open/sell_close 等函数精确,建议针对期货改写下单逻辑(涉及开平仓方向)。
  3. 期货通常需要做空逻辑(价格跌破下轨开空),上述代码仅实现了股票的多头逻辑(跌破下轨平仓)。