🚀 AI 一键生成 ptrade 策略代码

PTrade 编程指南:计算 +DI、-DI 和 ADX 实现 DMI 策略

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

问题描述

请提供一个 DMI 趋向指标的量化策略源码

解决方案

这是一个基于 DMI (Directional Movement Index, 趋向指标) 的量化交易策略源码。

策略原理

DMI 指标主要由四条线组成:

  1. PDI (+DI):上升方向线,代表上涨趋势的强度。
  2. MDI (-DI):下降方向线,代表下跌趋势的强度。
  3. ADX:趋向平均值,代表趋势的强度(不分上涨或下跌)。
  4. ADXR:ADX 的评估数值(本策略主要使用前三者)。

交易逻辑:

  • 买入信号(多头)
    1. PDI (+DI) 上穿 MDI (-DI):形成金叉,表明多头力量开始强于空头。
    2. ADX > 阈值 (如 20):确保当前处于有趋势的状态,避免在盘整震荡期频繁开仓。
  • 卖出信号(空头/平仓)
    1. PDI (+DI) 下穿 MDI (-DI):形成死叉,表明空头力量开始强于多头。
    2. 或者持仓股票发生亏损/止盈(本策略演示基础的死叉平仓)。

PTrade 策略源码

import talib
import numpy as np
import pandas as pd

def initialize(context):
    """
    策略初始化函数,只在开始时运行一次
    """
    # 设置我们要操作的股票,这里以恒生电子为例
    g.security = '600570.SS'
    set_universe(g.security)
    
    # DMI 指标参数设置
    g.N = 14  # 计算 DI 的周期,通常为 14
    g.M = 6   # 计算 ADX 的周期,通常为 6
    
    # ADX 趋势强度阈值,低于此值认为震荡,不交易
    g.adx_threshold = 20
    
    # 设置滑点和佣金(回测用,实盘可忽略)
    set_slippage(slippage=0.002)
    set_commission(commission_ratio=0.0003, min_commission=5.0)

def handle_data(context, data):
    """
    策略主要逻辑,按频率(分钟或日线)执行
    """
    security = g.security
    
    # 1. 获取历史数据
    # DMI计算需要 High, Low, Close 数据
    # 获取过去 60 根 K 线,保证 talib 计算有足够的数据预热
    # 注意:count 必须大于 g.N + g.M 很多,否则计算出的 ADX 前期可能是 NaN
    h = get_history(60, frequency='1d', field=['high', 'low', 'close'], security_list=security)
    
    # 如果数据不足,直接返回
    if len(h['close']) < 60:
        return

    # 将数据转换为 numpy 数组,供 talib 使用
    high_prices = h['high'].values
    low_prices = h['low'].values
    close_prices = h['close'].values
    
    # 2. 计算 DMI 指标
    # PLUS_DI (+DI)
    p_di = talib.PLUS_DI(high_prices, low_prices, close_prices, timeperiod=g.N)
    # MINUS_DI (-DI)
    m_di = talib.MINUS_DI(high_prices, low_prices, close_prices, timeperiod=g.N)
    # ADX
    adx = talib.ADX(high_prices, low_prices, close_prices, timeperiod=g.N)
    
    # 获取当前和前一时刻的指标值(用于判断交叉)
    # -1 代表当前最新值,-2 代表上一周期值
    current_p_di = p_di[-1]
    last_p_di = p_di[-2]
    
    current_m_di = m_di[-1]
    last_m_di = m_di[-2]
    
    current_adx = adx[-1]
    
    # 检查计算结果是否包含 NaN (数据不足时可能发生)
    if np.isnan(current_p_di) or np.isnan(current_adx):
        return

    # 3. 获取当前持仓和资金
    position = get_position(security)
    cash = context.portfolio.cash
    
    # 4. 交易逻辑判断
    
    # 买入条件:
    # 1. +DI 上穿 -DI (金叉)
    # 2. ADX 大于阈值 (趋势够强)
    # 3. 当前无持仓
    cross_over = (last_p_di <= last_m_di) and (current_p_di > current_m_di)
    trend_strong = current_adx > g.adx_threshold
    
    if cross_over and trend_strong and position.amount == 0:
        # 全仓买入
        order_value(security, cash)
        log.info("买入信号触发: +DI金叉-DI且ADX强于阈值。当前ADX: %.2f" % current_adx)
        
    # 卖出条件:
    # 1. +DI 下穿 -DI (死叉)
    # 2. 当前有持仓
    cross_under = (last_p_di >= last_m_di) and (current_p_di < current_m_di)
    
    if cross_under and position.amount > 0:
        # 清仓卖出
        order_target(security, 0)
        log.info("卖出信号触发: +DI死叉-DI。")

def before_trading_start(context, data):
    """
    盘前处理,可选
    """
    pass

def after_trading_end(context, data):
    """
    盘后处理,可选
    """
    pass

代码关键点解析

  1. 库的引用

    • 使用了 talib 库。PTrade 环境内置了 TA-Lib,这是计算技术指标最高效、最标准的方法。
    • talib.PLUS_DI 对应 +DI。
    • talib.MINUS_DI 对应 -DI。
    • talib.ADX 对应 ADX。
  2. 数据获取 (get_history)

    • DMI 计算依赖最高价 (high)、最低价 (low) 和收盘价 (close)。
    • count=60:虽然周期是 14,但 ADX 的计算涉及平滑移动平均,需要较长的历史数据来消除计算初期的不稳定(预热),因此获取 60 天数据比较稳妥。
  3. 交叉判断

    • 通过比较 [-1] (当前值) 和 [-2] (上一周期值) 来判断是否发生了“上穿”或“下穿”。
    • last_p_di <= last_m_dicurrent_p_di > current_m_di 意味着刚刚发生了金叉。
  4. ADX 过滤

    • DMI 策略的一个常见弱点是在震荡市中频繁发出假信号。代码中加入了 if current_adx > g.adx_threshold 判断,只有当趋势强度大于 20 时才允许开仓,这能有效过滤掉一部分无效波动。

如何在 PTrade 中使用

  1. 打开 PTrade 客户端。
  2. 进入 “量化” -> “研究”“回测” 界面。
  3. 新建一个策略文件。
  4. 将上述代码完整复制粘贴进去。
  5. 点击 “回测”,设置回测时间段(例如最近一年),即可查看策略表现。

Q&A

Q: 为什么不直接用 PTrade 自带的指标函数?
A: PTrade 的 API 文档中提供了 get_MACD, get_KDJ 等函数,但并没有直接提供 get_DMI。因此,使用 Python 标准金融库 talib 是最通用且准确的实现方式。

Q: 如何调整策略的灵敏度?
A: 修改 initialize 函数中的 g.N

  • 减小 g.N (如改为 7):指标更敏感,信号更多,但假信号也更多。
  • 增大 g.N (如改为 20):指标更迟钝,信号更稳,但可能错过行情前半段。

Q: 这个策略可以用于分钟线吗?
A: 可以。只需在回测配置时将频率选为“分钟”,或者在 get_history 中将 frequency='1d' 改为 frequency='1m' (或其他分钟周期) 即可。