🚀 AI 一键生成 joinquant 策略代码

JoinQuant 平台 DeMarker (DeM) 震荡指标策略编写

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

问题描述

请帮我实现一个 DeMarker 指标的均值回归策略

解决方案

这是一个基于 DeMarker (DeM) 指标的均值回归策略实现。

策略原理

DeMarker 指标 是一种震荡指标,用于评估价格变动的需求。它的取值范围在 0 到 1 之间。

  • 超卖区(Oversold):通常低于 0.3。意味着价格可能被低估,存在反弹(均值回归)的需求。
  • 超买区(Overbought):通常高于 0.7。意味着价格可能被高估,存在回调的需求。

交易逻辑:

  1. 买入信号:当 DeMarker 指标值 小于 0.3(超卖)时,认为价格偏低,全仓买入。
  2. 卖出信号:当 DeMarker 指标值 大于 0.7(超买)时,认为价格偏高,清仓卖出。
  3. 标的:本示例使用沪深300 ETF(510300.XSHG)作为标的,ETF通常比个股更适合均值回归策略。

策略代码

# -*- coding: utf-8 -*-
import numpy as np
import pandas as pd

def initialize(context):
    """
    初始化函数,设定基准、手续费、全局变量等
    """
    # 设定沪深300作为基准
    set_benchmark('000300.XSHG')
    # 开启动态复权模式(真实价格)
    set_option('use_real_price', True)
    # 过滤掉order系列API产生的比error级别低的log
    log.set_level('order', 'error')
    
    # 设定股票类每笔交易时的手续费
    set_order_cost(OrderCost(close_tax=0.001, open_commission=0.0003, close_commission=0.0003, min_commission=5), type='stock')
    
    # --- 策略参数设置 ---
    # 操作标的:这里选择沪深300ETF,适合均值回归
    g.security = '510300.XSHG' 
    # DeMarker 计算周期
    g.n_period = 13
    # 超买阈值 (卖出)
    g.upper_bound = 0.7
    # 超卖阈值 (买入)
    g.lower_bound = 0.3
    
    # 每天开盘时运行
    run_daily(market_open, time='09:30')

def calculate_demarker(security, n_days):
    """
    计算 DeMarker 指标
    """
    # 获取历史数据:我们需要 N+1 天的数据来计算 N 天的差值
    # 为了保证 rolling 计算有足够数据,多取一些缓冲数据
    fetch_count = n_days + 5
    
    # 获取最高价和最低价
    h_data = attribute_history(security, fetch_count, '1d', ['high', 'low'])
    
    if len(h_data) < 2:
        return None
        
    highs = h_data['high']
    lows = h_data['low']
    
    # 1. 计算 DeMax
    # DeMax = High(t) - High(t-1), 如果 > 0, 否则 0
    de_max = highs - highs.shift(1)
    de_max[de_max < 0] = 0
    
    # 2. 计算 DeMin
    # DeMin = Low(t-1) - Low(t), 如果 > 0, 否则 0
    de_min = lows.shift(1) - lows
    de_min[de_min < 0] = 0
    
    # 3. 计算移动平均 (SMA)
    # 这里的 mean() 对应 SMA
    sma_de_max = de_max.rolling(window=n_days).mean()
    sma_de_min = de_min.rolling(window=n_days).mean()
    
    # 4. 计算 DeMarker
    # DeM = SMA(DeMax) / (SMA(DeMax) + SMA(DeMin))
    dem = sma_de_max / (sma_de_max + sma_de_min)
    
    # 返回最新的 DeM 值
    return dem.iloc[-1]

def market_open(context):
    """
    每日交易逻辑
    """
    security = g.security
    
    # 计算当前的 DeMarker 值
    current_dem = calculate_demarker(security, g.n_period)
    
    if current_dem is None or np.isnan(current_dem):
        log.info("数据不足,跳过计算")
        return

    # 获取当前持仓
    position = context.portfolio.positions[security]
    # 获取可用现金
    cash = context.portfolio.available_cash
    
    # --- 交易信号判断 ---
    
    # 信号1:超卖 (DeM < 0.3) -> 买入
    if current_dem < g.lower_bound:
        if cash > 0:
            # 全仓买入
            order_value(security, cash)
            log.info("DeMarker值: %.3f (超卖), 买入 %s" % (current_dem, security))
            
    # 信号2:超买 (DeM > 0.7) -> 卖出
    elif current_dem > g.upper_bound:
        if position.closeable_amount > 0:
            # 清仓卖出
            order_target(security, 0)
            log.info("DeMarker值: %.3f (超买), 卖出 %s" % (current_dem, security))
            
    # 记录指标值以便在回测图中查看
    record(DeMarker=current_dem)

常见问题 (Q&A)

Q1: 为什么选择 ETF 而不是个股进行均值回归?
A: 均值回归策略假设价格会围绕价值中枢波动。个股容易受到黑天鹅事件(如财务造假、重大利空)影响出现单边下跌不回头的现象。而指数 ETF 代表一篮子股票,波动相对平稳,更符合均值回归的统计特性。

Q2: 代码中的 attribute_history 获取的是什么时间的数据?
A:run_daily(market_open, time='09:30') 中调用 attribute_history,默认获取的是截止到前一个交易日收盘的数据。这意味着我们是利用昨天的收盘指标来决定今天开盘的操作,避免了未来函数。

Q3: 如何调整策略的灵敏度?
A:

  • 调整周期 g.n_period:减小周期(如改为 9)会使指标更敏感,交易更频繁;增大周期(如改为 20)会使指标更平滑,信号更少但可能更可靠。
  • 调整阈值:将买入阈值调低(如 0.2)或卖出阈值调高(如 0.8)会减少交易次数,试图捕捉更极端的反转机会。

Q4: 策略可以做空吗?
A: 上述代码是针对 A 股股票/ETF 市场的,默认只能做多(Long Only)。如果需要做空,需要开通融资融券账户或交易股指期货,并修改 order 函数为 order_target(security, 0) (平仓) 或开空单逻辑。