🚀 AI 一键生成 joinquant 策略代码

如何在 JoinQuant 平台利用 ADX 指标构建趋势交易策略?

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

问题描述

请编写一个使用 ADX 指标判断趋势并进行交易的策略

解决方案

这是一个基于 ADX (Average Directional Index)DMI (Directional Movement Index) 指标的趋势跟踪策略。

策略逻辑说明

ADX 指标主要用于判断趋势的强度,而不是方向。通常结合 DMI 指标中的 +DI (PDI)-DI (MDI) 来判断方向。

  1. 指标定义

    • ADX:平均趋向指标,数值越大代表趋势越强(无论涨跌)。一般认为 ADX > 25 代表有趋势。
    • +DI (PDI):上升方向线,代表买方力量。
    • -DI (MDI):下降方向线,代表卖方力量。
  2. 交易信号

    • 买入条件
      • ADX > 25(趋势确立)。
      • +DI > -DI(多头占优)。
      • 当前无持仓。
    • 卖出条件
      • ADX < 25(趋势减弱/盘整)。
      • 或者 +DI < -DI(空头反转)。
      • 当前有持仓。
  3. 参数设置

    • 计算周期:14(标准设置)。
    • ADX 阈值:25。

策略代码

# -*- coding: utf-8 -*-
import jqdata
import talib
import numpy as np

def initialize(context):
    """
    初始化函数,设定基准、股票池、参数等
    """
    # 设定沪深300作为基准
    set_benchmark('000300.XSHG')
    # 开启动态复权模式(真实价格)
    set_option('use_real_price', True)
    # 过滤掉order系列API产生的比error级别低的log
    log.set_level('order', 'error')
    
    # 设定操作的股票,这里以平安银行为例
    g.security = '000001.XSHE'
    
    # 策略参数
    g.N = 14          # ADX计算周期
    g.adx_threshold = 25  # ADX趋势强度阈值
    
    # 每天开盘时运行
    run_daily(market_open, time='09:30')

def market_open(context):
    """
    每日交易逻辑
    """
    security = g.security
    
    # 1. 获取历史数据
    # talib计算ADX通常需要比周期更多的数据以保证平滑后的准确性,这里取 N * 3 天
    lookback = g.N * 3
    # 获取最高价、最低价、收盘价
    data = attribute_history(security, lookback, '1d', ['high', 'low', 'close'])
    
    # 如果数据不足,直接返回
    if len(data) < lookback:
        return
        
    # 转换数据格式为 numpy array,talib 需要 float 类型
    high_prices = data['high'].values.astype(float)
    low_prices = data['low'].values.astype(float)
    close_prices = data['close'].values.astype(float)
    
    # 2. 计算指标
    # 计算 ADX
    adx = talib.ADX(high_prices, low_prices, close_prices, timeperiod=g.N)
    # 计算 +DI (PDI)
    pdi = talib.PLUS_DI(high_prices, low_prices, close_prices, timeperiod=g.N)
    # 计算 -DI (MDI)
    mdi = talib.MINUS_DI(high_prices, low_prices, close_prices, timeperiod=g.N)
    
    # 获取最新的指标值(数组的最后一个元素)
    current_adx = adx[-1]
    current_pdi = pdi[-1]
    current_mdi = mdi[-1]
    
    # 获取当前持仓
    position = context.portfolio.positions[security]
    
    # 3. 交易逻辑
    # 买入逻辑:ADX大于阈值(趋势强) 且 PDI大于MDI(多头趋势)
    if current_adx > g.adx_threshold and current_pdi > current_mdi:
        if position.total_amount == 0:
            # 全仓买入
            order_value(security, context.portfolio.available_cash)
            log.info("买入信号触发: ADX={:.2f}, +DI={:.2f}, -DI={:.2f}".format(current_adx, current_pdi, current_mdi))
            
    # 卖出逻辑:ADX小于阈值(趋势减弱) 或者 PDI小于MDI(空头反转)
    elif current_adx < g.adx_threshold or current_pdi < current_mdi:
        if position.closeable_amount > 0:
            # 清仓卖出
            order_target(security, 0)
            log.info("卖出信号触发: ADX={:.2f}, +DI={:.2f}, -DI={:.2f}".format(current_adx, current_pdi, current_mdi))

    # 4. 记录数据用于画图
    record(ADX=current_adx, PDI=current_pdi, MDI=current_mdi)

代码解析

  1. 库导入
    • 导入 talib 库,这是金融量化中计算技术指标最常用的库,计算效率高且准确。
  2. 数据获取 (attribute_history)
    • ADX 计算依赖于最高价 (high)、最低价 (low) 和收盘价 (close)。
    • 我们获取了 g.N * 3 天的数据。虽然参数是 14 天,但 ADX 算法内部包含平滑移动平均,获取更多历史数据可以减少计算初期的误差,使指标值更稳定。
  3. 指标计算
    • talib.ADX(...): 计算趋势强度。
    • talib.PLUS_DI(...): 计算正向指标(多头力量)。
    • talib.MINUS_DI(...): 计算负向指标(空头力量)。
  4. 交易执行
    • 使用 order_value 进行全仓买入。
    • 使用 order_target 调整仓位至 0 进行平仓。
    • 加入了 position.closeable_amount > 0 判断,确保有可卖出的持仓(T+1 规则)。

常见问题 (Q&A)

Q1: 为什么 ADX 指标有时候反应比较滞后?
A: ADX 是对价格波动的平滑处理,本质上是均线的一种衍生。任何基于均线的指标都会有滞后性。为了减少滞后,可以尝试减小计算周期 g.N(例如改为 10 或 7),但这可能会增加假信号(噪音)。

Q2: 如何在多只股票上运行这个策略?
A: 可以将 g.security 改为一个列表(例如 get_index_stocks('000300.XSHG')),然后在 market_open 函数中使用 for 循环遍历每只股票,分别计算指标并独立下单。同时需要注意资金分配(例如每只股票分配总资金的 1/10)。

Q3: 为什么代码中要获取 g.N * 3 天的数据?
A: talib 计算 ADX 时使用了 Wilder's Smoothing(威尔德平滑),这种算法对起始数据敏感。如果只获取 14 天数据,计算出的第一个 ADX 值可能不准确。获取更多历史数据可以让指标“预热”,使最终的计算结果与看盘软件一致。

Q4: 这个策略适合震荡市吗?
A: 不适合。ADX 本身就是为了区分趋势和震荡而设计的。当 ADX < 25 时,策略会选择空仓观望,从而避免在震荡市中反复止损。这是该策略的核心优势之一。