🚀 AI 一键生成 qmt 策略代码

QMT 平台如何用 Python 编写 DMI 趋向指标策略?

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

问题描述

如何编写 DMI 指标策略?

解决方案

编写 DMI(Directional Movement Index,动向指标)策略通常涉及计算 +DI(上升方向线)、-DI(下降方向线)和 ADX(平均趋向指标)。

DMI 策略的核心逻辑通常是:

  1. 买入信号:+DI 上穿 -DI(金叉),且 ADX 处于一定阈值(如 20)以上,表示趋势走强。
  2. 卖出信号:+DI 下穿 -DI(死叉),或者 -DI 上穿 +DI。

以下是一个完整的 QMT Python DMI 策略示例代码。

DMI 指标策略代码

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

def init(ContextInfo):
    """
    策略初始化函数
    """
    # 设置要交易的股票列表
    ContextInfo.stock_list = ['600000.SH', '000001.SZ']
    
    # 设置交易账号(请替换为您自己的资金账号)
    ContextInfo.account_id = '您的资金账号'
    ContextInfo.account_type = 'STOCK' # 股票账号
    
    # 设置策略参数
    ContextInfo.N = 14  # DMI计算周期
    ContextInfo.M = 6   # ADX平滑周期
    ContextInfo.ADX_THRESHOLD = 20 # ADX 阈值,用于确认趋势强度
    
    # 设置周期
    ContextInfo.period = '1d'
    
    # 设置基准和费率(回测用)
    ContextInfo.set_universe(ContextInfo.stock_list)
    ContextInfo.set_account(ContextInfo.account_id)

def handlebar(ContextInfo):
    """
    K线周期运行函数
    """
    # 获取当前K线位置
    index = ContextInfo.barpos
    
    # 获取当前时间
    realtime = ContextInfo.get_bar_timetag(index)
    
    # 遍历股票池
    for stock in ContextInfo.stock_list:
        # 获取历史行情数据 (多取一些数据以保证指标计算准确)
        # 注意:talib计算通常需要一定长度的前置数据才能稳定
        count = 100 
        data = ContextInfo.get_market_data_ex(
            ['high', 'low', 'close'], 
            [stock], 
            period=ContextInfo.period, 
            count=count, 
            dividend_type='front' # 前复权
        )
        
        if stock not in data or data[stock].empty:
            continue
            
        df = data[stock]
        
        # 确保数据量足够计算指标
        if len(df) < ContextInfo.N + ContextInfo.M + 5:
            continue
            
        # 提取 numpy 数组供 talib 使用
        high_prices = df['high'].values
        low_prices = df['low'].values
        close_prices = df['close'].values
        
        # ----------------------------------------------------------------
        # 计算 DMI 指标
        # ----------------------------------------------------------------
        # 计算 +DI (PDI)
        pdi = talib.PLUS_DI(high_prices, low_prices, close_prices, timeperiod=ContextInfo.N)
        # 计算 -DI (MDI)
        mdi = talib.MINUS_DI(high_prices, low_prices, close_prices, timeperiod=ContextInfo.N)
        # 计算 ADX
        adx = talib.ADX(high_prices, low_prices, close_prices, timeperiod=ContextInfo.N)
        
        # 获取最新和上一个周期的指标值
        # -1 代表当前K线,-2 代表上一根K线
        current_pdi = pdi[-1]
        current_mdi = mdi[-1]
        current_adx = adx[-1]
        
        last_pdi = pdi[-2]
        last_mdi = mdi[-2]
        
        # ----------------------------------------------------------------
        # 交易逻辑
        # ----------------------------------------------------------------
        
        # 获取当前持仓
        positions = ContextInfo.get_position(stock)
        current_holding = 0
        if positions:
            # 注意:get_position 返回的是对象列表,通常取第一个
            # 实盘中建议使用 get_trade_detail_data 获取更准确持仓
            pass 
            # 这里为了简化回测逻辑,我们使用 order_target_percent 来控制仓位
        
        # 买入条件:
        # 1. +DI 上穿 -DI (金叉)
        # 2. ADX 大于阈值 (趋势确认)
        buy_condition = (last_pdi <= last_mdi) and (current_pdi > current_mdi) and (current_adx > ContextInfo.ADX_THRESHOLD)
        
        # 卖出条件:
        # 1. +DI 下穿 -DI (死叉)
        sell_condition = (last_pdi >= last_mdi) and (current_pdi < current_mdi)
        
        # 执行交易
        if buy_condition:
            # 满仓买入 (目标仓位调整为 100%)
            # 注意:实盘请做好资金管理,不要轻易满仓
            print(f"{stock} 触发买入信号: PDI={current_pdi:.2f}, MDI={current_mdi:.2f}, ADX={current_adx:.2f}")
            order_target_percent(stock, 1.0, ContextInfo, ContextInfo.account_id)
            
        elif sell_condition:
            # 清仓卖出 (目标仓位调整为 0%)
            print(f"{stock} 触发卖出信号: PDI={current_pdi:.2f}, MDI={current_mdi:.2f}")
            order_target_percent(stock, 0.0, ContextInfo, ContextInfo.account_id)

代码详解

  1. 初始化 (init):

    • 定义了股票池 stock_list 和交易账号。
    • 设置了 DMI 指标的参数:N=14 是标准周期,ADX_THRESHOLD=20 用于过滤震荡行情。
    • 使用 ContextInfo.set_universe 设定股票池。
  2. 数据获取 (handlebar):

    • 使用 ContextInfo.get_market_data_ex 获取历史的高、低、收盘价。
    • 注意:获取的数据长度 count 必须大于指标计算所需的周期(例如 100),否则 talib 计算出的结果可能是 NaN
  3. 指标计算:

    • 利用 QMT 内置的 talib 库进行高效计算。
    • talib.PLUS_DI: 计算 +DI。
    • talib.MINUS_DI: 计算 -DI。
    • talib.ADX: 计算 ADX。
  4. 交易逻辑:

    • 金叉判断last_pdi <= last_mdicurrent_pdi > current_mdi。这意味着上一根 K 线 -DI 在上方或相等,当前 K 线 +DI 跑到了上方。
    • 趋势过滤:加入 current_adx > ContextInfo.ADX_THRESHOLD 条件,避免在无趋势的震荡市中频繁开仓。
    • 死叉判断last_pdi >= last_mdicurrent_pdi < current_mdi
  5. 下单执行:

    • 使用 order_target_percent 函数。这是一个非常方便的函数,它会自动计算需要买卖的数量,将该股票的持仓调整到总资产的指定比例(例如 1.0 代表满仓,0.0 代表清仓)。

注意事项

  • Talib 依赖:QMT 自带 Python 环境通常已包含 talib 库。如果您的环境报错找不到模块,可能需要联系管理员或手动安装。
  • 数据长度:DMI 指标对数据长度比较敏感,如果数据太短,ADX 的值可能不准确。建议 count 设置为 N 的 3-5 倍以上。
  • 实盘账号:代码中的 ContextInfo.account_id 必须修改为您实际登录的资金账号,否则无法下单。
  • 频率:此策略默认在日线 (1d) 级别运行。如果需要在分钟级别运行,请修改 ContextInfo.period 并确保下载了对应的分钟数据。

Q&A

Q: 如何调整 DMI 的灵敏度?
A: 修改 init 函数中的 ContextInfo.N。减小 N 值(如改为 7)会使指标更灵敏,但假信号增多;增大 N 值(如改为 20)会使指标更平滑,但信号会有滞后。

Q: 为什么要用 ADX 过滤?
A: DMI 是一个趋势指标。在盘整(横盘)行情中,+DI 和 -DI 会频繁交叉,导致频繁止损。ADX 代表趋势的强度,只有当 ADX 大于一定数值(如 20 或 25)时,才认为当前有足够的趋势去进行交易。

Q: order_target_percent 在回测和实盘都能用吗?
A: 是的,这个函数在回测和实盘模式下都支持。在实盘中,它会根据账户的总资产计算目标市值,然后下单调整持仓。