🚀 AI 一键生成 joinquant 策略代码

JoinQuant 技术分析:构建 DMA 平均线差交易系统

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

问题描述

请编写一个 DMA 平均线差指标的交易策略

解决方案

这是一个基于 DMA (Different of Moving Average) 平均线差指标 的聚宽(JoinQuant)交易策略。

策略原理

DMA 指标利用两条不同期间的移动平均线(通常为短期和长期)的差值来判断趋势。

  1. DIF (差值线) = 短期均线 (MA1) - 长期均线 (MA2)
  2. DIFMA (平均值线) = DIF 的 M 日移动平均
  3. 买入信号 (金叉):DIF 向上突破 DIFMA。
  4. 卖出信号 (死叉):DIF 向下跌破 DIFMA。

策略代码

# -*- coding: utf-8 -*-
import jqdata
import pandas as pd
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'
    
    # DMA指标参数
    g.N1 = 10  # 短期均线周期
    g.N2 = 50  # 长期均线周期
    g.M  = 10  # DIF的移动平均周期 (AMA)
    
    # 设定交易费用:买入万分之三,卖出万分之三加千分之一印花税
    set_order_cost(OrderCost(close_tax=0.001, open_commission=0.0003, close_commission=0.0003, min_commission=5), type='stock')
    
    # 每天开盘时运行
    run_daily(market_open, time='09:30')

def market_open(context):
    """
    每日交易逻辑
    """
    security = g.security
    
    # 1. 获取历史数据
    # 我们需要计算 N2 日均线,且需要计算 DIF 的 M 日均线,
    # 还需要前一日的数据来判断交叉,所以获取的数据长度要足够长
    # 长度至少为 N2 + M + 2
    history_count = g.N2 + g.M + 10 
    
    # 获取收盘价数据
    h_data = attribute_history(security, history_count, '1d', ['close'])
    
    # 如果数据不足,直接返回
    if len(h_data) < history_count:
        return
    
    close_prices = h_data['close']
    
    # 2. 计算 DMA 指标
    # 计算短期均线 MA(N1)
    ma_short = close_prices.rolling(window=g.N1).mean()
    # 计算长期均线 MA(N2)
    ma_long = close_prices.rolling(window=g.N2).mean()
    
    # 计算 DIF = MA_short - MA_long
    dif = ma_short - ma_long
    
    # 计算 DIFMA = DIF 的 M 日简单移动平均
    difma = dif.rolling(window=g.M).mean()
    
    # 3. 获取当前和前一日的指标值用于判断交叉
    # 当前值 (-1)
    curr_dif = dif.iloc[-1]
    curr_difma = difma.iloc[-1]
    
    # 前一日值 (-2)
    prev_dif = dif.iloc[-2]
    prev_difma = difma.iloc[-2]
    
    # 检查是否包含 NaN 值(数据计算初期可能产生NaN),若有则跳过
    if np.isnan(curr_dif) or np.isnan(curr_difma) or np.isnan(prev_dif) or np.isnan(prev_difma):
        return

    # 4. 交易逻辑
    # 获取当前仓位
    curr_position = context.portfolio.positions[security].closeable_amount
    # 获取可用资金
    cash = context.portfolio.available_cash
    
    # 信号判断
    # 金叉:前一日 DIF < DIFMA,且 当前 DIF > DIFMA
    golden_cross = (prev_dif < prev_difma) and (curr_dif > curr_difma)
    
    # 死叉:前一日 DIF > DIFMA,且 当前 DIF < DIFMA
    death_cross = (prev_dif > prev_difma) and (curr_dif < curr_difma)
    
    # 执行交易
    if golden_cross and cash > 0:
        # 全仓买入
        order_value(security, cash)
        log.info(f"DMA金叉买入: {security}, DIF: {curr_dif:.3f}, DIFMA: {curr_difma:.3f}")
        
    elif death_cross and curr_position > 0:
        # 清仓卖出
        order_target(security, 0)
        log.info(f"DMA死叉卖出: {security}, DIF: {curr_dif:.3f}, DIFMA: {curr_difma:.3f}")

    # 绘制图表(仅在回测中有效)
    record(DIF=curr_dif, DIFMA=curr_difma)

代码说明

  1. 参数设置
    • g.N1 = 10:短期均线周期。
    • g.N2 = 50:长期均线周期。
    • g.M = 10:DIFMA 的平滑周期。
    • 这些参数可以根据需要在 initialize 中修改。
  2. 数据获取
    • 使用 attribute_history 获取收盘价。
    • 获取的数据长度必须大于 N2 + M,否则无法计算出有效的 DIFMA 值。
  3. 指标计算
    • 利用 Pandas 的 rolling().mean() 方法快速计算移动平均线。
    • DIF 是两条均线的差。
    • DIFMADIF 的移动平均。
  4. 信号判断
    • 通过比较 iloc[-1] (当前) 和 iloc[-2] (前一天) 的数值关系来判断是否发生了“金叉”或“死叉”。
  5. 交易执行
    • 金叉时使用 order_value 全仓买入。
    • 死叉时使用 order_target 将仓位调整为 0(清仓)。

Q&A

Q: DMA指标和MACD指标有什么区别?
A: 两者逻辑相似,都是利用快慢均线的差离。区别在于:DMA通常使用简单移动平均(SMA),且参数设置较大(如10, 50),对趋势反应较稳健但滞后;MACD使用指数移动平均(EMA),且经过了更复杂的平滑处理,对近期价格变化更敏感。

Q: 如何优化这个策略?
A: 1. 参数优化:可以通过聚宽的研究模块遍历不同的 N1, N2, M 组合寻找最优参数。2. 增加过滤条件:仅靠 DMA 交叉容易在震荡市中频繁止损,可以结合成交量(如放量金叉)或大盘趋势(如沪深300在20日均线上方)作为过滤条件。3. 止损止盈:加入固定的百分比止损或移动止损逻辑。

Q: 为什么代码中要获取 g.N2 + g.M + 10 这么长的数据?
A: 计算长期均线需要前 N2 天的数据,计算 DIFMA 又需要前 M 个 DIF 值(即依赖更早的数据)。为了确保计算出的最新指标不是 NaN(空值),必须获取足够长的历史窗口。