🚀 AI 一键生成 qmt 策略代码

QMT 量化教程:唐奇安通道 (Donchian Channel) 突破策略 Python 实现

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

问题描述

怎么写唐奇安通道突破策略?

解决方案

这是一个非常经典的趋势跟随策略。唐奇安通道(Donchian Channel)策略的核心逻辑是:当价格突破过去 N 天的最高价时买入,当价格跌破过去 N 天的最低价时卖出

以下是基于 QMT Python API 实现的唐奇安通道突破策略。该代码适用于股票交易(做多逻辑),使用了 get_market_data_ex 获取数据,并使用 order_target_percent 进行仓位管理。

策略逻辑说明

  1. 上轨(Upper Band):过去 N 根 K 线的最高价的最大值(不包含当前 K 线)。
  2. 下轨(Lower Band):过去 N 根 K 线的最低价的最小值(不包含当前 K 线)。
  3. 买入信号:当前收盘价 > 上轨,且当前无持仓 -> 全仓买入。
  4. 卖出信号:当前收盘价 < 下轨,且当前有持仓 -> 清仓卖出。

QMT 策略代码

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

def init(ContextInfo):
    """
    初始化函数,设定策略参数和变量
    """
    # 策略参数:唐奇安通道周期
    ContextInfo.N = 20
    
    # 设置交易账号(请替换为您真实的资金账号)
    ContextInfo.account_id = 'YOUR_ACCOUNT_ID'
    ContextInfo.account_type = 'STOCK' # 股票账户
    
    # 设置基准和回测参数(仅回测有效)
    ContextInfo.set_account(ContextInfo.account_id)
    
    # 设定股票池,这里默认取当前主图的品种
    # 如果需要固定股票池,可以使用 ContextInfo.set_universe(['600000.SH'])
    ContextInfo.stock_code = ContextInfo.stockcode + '.' + ContextInfo.market
    
    print("策略初始化完成,交易品种: {}, 通道周期: {}".format(ContextInfo.stock_code, ContextInfo.N))

def handlebar(ContextInfo):
    """
    K线逐根运行函数
    """
    # 获取当前 K 线索引
    index = ContextInfo.barpos
    
    # 获取当前图表的周期
    period = ContextInfo.period
    
    # 确保有足够的数据计算通道(至少需要 N+1 根 K 线)
    if index < ContextInfo.N:
        return

    # 获取历史行情数据
    # 我们需要获取过去 N+1 根 K 线的数据,以计算前 N 天的高低点和当前的收盘价
    # count = N + 1
    data_df = ContextInfo.get_market_data_ex(
        fields=['high', 'low', 'close'],
        stock_code=[ContextInfo.stock_code],
        period=period,
        end_time='', 
        count=ContextInfo.N + 2 # 多取一点防止边界问题
    )[ContextInfo.stock_code]

    # 如果数据获取失败或长度不足,直接返回
    if data_df is None or len(data_df) < ContextInfo.N + 1:
        return

    # --- 计算唐奇安通道 ---
    # 注意:为了避免未来函数,计算通道时必须使用"过去"的数据
    # data_df.iloc[-1] 是当前 K 线(正在走的或刚走完的)
    # data_df.iloc[-2] 是上一根 K 线
    
    # 获取用于计算通道的历史数据片段:从倒数第 N+1 根到倒数第 2 根
    # 例如 N=20,我们取当前 K 线之前的 20 根 K 线
    history_highs = data_df['high'].iloc[-(ContextInfo.N + 1):-1]
    history_lows = data_df['low'].iloc[-(ContextInfo.N + 1):-1]
    
    # 计算上轨:过去 N 根 K 线的最高价的最大值
    donchian_upper = history_highs.max()
    
    # 计算下轨:过去 N 根 K 线的最低价的最小值
    donchian_lower = history_lows.min()
    
    # 获取当前最新价格(收盘价)
    current_price = data_df['close'].iloc[-1]
    
    # --- 交易逻辑 ---
    
    # 获取当前持仓信息
    positions = ContextInfo.get_position()
    holding_vol = 0
    for pos in positions:
        if pos.m_strInstrumentID + '.' + pos.m_strExchangeID == ContextInfo.stock_code:
            holding_vol = pos.m_nVolume
            break

    # 1. 买入逻辑:价格突破上轨,且当前空仓
    if current_price > donchian_upper and holding_vol == 0:
        print("时间: {} - 价格突破上轨 ({:.2f} > {:.2f}),触发买入".format(
            timetag_to_datetime(ContextInfo.get_bar_timetag(index), '%Y-%m-%d %H:%M:%S'),
            current_price, donchian_upper
        ))
        # 全仓买入 (目标仓位 100%)
        order_target_percent(ContextInfo.stock_code, 1.0, ContextInfo, ContextInfo.account_id)

    # 2. 卖出逻辑:价格跌破下轨,且当前持仓
    elif current_price < donchian_lower and holding_vol > 0:
        print("时间: {} - 价格跌破下轨 ({:.2f} < {:.2f}),触发卖出".format(
            timetag_to_datetime(ContextInfo.get_bar_timetag(index), '%Y-%m-%d %H:%M:%S'),
            current_price, donchian_lower
        ))
        # 清仓卖出 (目标仓位 0%)
        order_target_percent(ContextInfo.stock_code, 0.0, ContextInfo, ContextInfo.account_id)

    # --- (可选) 在图表上画线方便观察 ---
    # 注意:paint 函数在回测和实盘图表上绘制
    ContextInfo.paint('Upper_Band', donchian_upper, -1, 0, 'red')
    ContextInfo.paint('Lower_Band', donchian_lower, -1, 0, 'green')

代码关键点解析

  1. 数据获取 (get_market_data_ex):

    • 我们使用 count=ContextInfo.N + 2 来获取最近的一小段数据。
    • 防未来函数:在计算 donchian_upperdonchian_lower 时,使用了切片 .iloc[-(ContextInfo.N + 1):-1]。这表示我们只看当前 K 线之前的 N 根 K 线。如果包含了当前 K 线,那么当天的最高价创新高时,上轨也会同步升高,导致永远无法触发突破信号。
  2. 交易函数 (order_target_percent):

    • 这是一个非常方便的高级交易函数。
    • 1.0 代表调整仓位至总资产的 100%(全仓买入)。
    • 0.0 代表调整仓位至 0%(清仓卖出)。
    • 它会自动计算需要买卖的股数,并处理买卖方向。
  3. 绘图 (paint):

    • 代码最后添加了 paint 函数,这样你在 QMT 界面上运行策略(或回测)时,可以直接在主图上看到红色的上轨和绿色的下轨,方便验证逻辑是否正确。
  4. 编码格式:

    • 第一行 # -*- coding: gbk -*- 是必须的,因为 QMT 的 Python 环境默认使用 GBK 编码,否则中文字符会乱码或报错。

如何在 QMT 中运行

  1. 打开 QMT 客户端,进入【模型研究】或【策略编辑器】。
  2. 新建一个 Python 策略。
  3. 将上述代码完整复制粘贴进去。
  4. 修改 init 函数中的 ContextInfo.account_id 为你的实际账号(如果是回测则不需要修改,系统会自动分配虚拟账号)。
  5. 点击【编译】检查是否有语法错误。
  6. 在左侧选择一个股票(如 600000.SH),选择周期(如日线),点击【运行】或【回测】。

Q&A

Q: 这个策略可以用于期货吗?
A: 可以,但需要修改交易逻辑。目前的逻辑是股票的“做多/平仓”。如果是期货,需要增加“开空/平空”的逻辑(即价格跌破下轨开空,突破上轨平空)。同时需要将 order_target_percent 替换为 buy_open, sell_close, sell_open, buy_close 等期货专用指令。

Q: 为什么计算通道时要排除当前 K 线?
A: 唐奇安通道的定义是基于“过去 N 天”的极值。如果在计算上轨时包含了今天的最高价,那么今天的价格永远不可能“突破”上轨(最多只能等于上轨),这样就永远不会产生买入信号。

Q: 如何修改通道的周期?
A: 修改 init 函数中的 ContextInfo.N = 20 即可。例如改为 ContextInfo.N = 55 就变成了著名的“海龟交易法则”中的长周期突破系统。