🚀 AI 一键生成 joinquant 策略代码

如何用 Python 在 JoinQuant 实现肯特纳通道交易策略?

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

问题描述

请帮我用 Python 实现一个肯特纳通道(Keltner Channel)交易策略

解决方案

这是一个基于聚宽(JoinQuant)API 实现的**肯特纳通道(Keltner Channel)**交易策略。

策略原理

肯特纳通道是一个趋势跟踪指标,由三条线组成:

  1. 中轨:通常是收盘价的指数移动平均线(EMA),一般取 20 日。
  2. 上轨:中轨 + $k \times ATR$(平均真实波幅)。
  3. 下轨:中轨 - $k \times ATR$。

交易逻辑:

  • 买入信号:当收盘价突破上轨时,视为趋势向上,全仓买入。
  • 卖出信号:当收盘价跌破下轨时,视为趋势反转,清仓卖出。

策略代码

# -*- 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')
    
    # 设定股票交易手续费:买入万三,卖出万三加千一印花税,最低5元
    set_order_cost(OrderCost(close_tax=0.001, open_commission=0.0003, close_commission=0.0003, min_commission=5), type='stock')
    
    # 设定要操作的标的,这里以平安银行(000001.XSHE)为例
    g.security = '000001.XSHE'
    
    # 策略参数设置
    g.n = 20      # EMA 和 ATR 的计算周期
    g.k = 2.0     # 通道宽度的倍数
    
    # 每天开盘时运行
    run_daily(market_open, time='open')

def market_open(context):
    """
    每日交易逻辑
    """
    security = g.security
    
    # 获取过去的数据,为了保证ATR计算准确,获取长度建议大于计算周期的2倍
    # 需要 high, low, close 来计算 ATR
    data_count = g.n * 3
    h = attribute_history(security, data_count, '1d', ['high', 'low', 'close'])
    
    # 如果数据不足,直接返回
    if len(h) < data_count:
        return
        
    # 将数据转换为 numpy 数组,供 talib 使用
    high = h['high'].values
    low = h['low'].values
    close = h['close'].values
    
    # 1. 计算 ATR (平均真实波幅)
    atr = talib.ATR(high, low, close, timeperiod=g.n)
    
    # 2. 计算 中轨 (EMA 指数移动平均线)
    ema = talib.EMA(close, timeperiod=g.n)
    
    # 获取最新的指标值 (数组的最后一个元素)
    current_atr = atr[-1]
    current_ema = ema[-1]
    current_close = close[-1]
    
    # 3. 计算 上轨 和 下轨
    upper_band = current_ema + g.k * current_atr
    lower_band = current_ema - g.k * current_atr
    
    # 获取当前持仓情况
    position = context.portfolio.positions[security]
    
    # --- 交易信号判断 ---
    
    # 信号1:价格突破上轨 -> 买入
    if current_close > upper_band:
        # 如果没有持仓,则全仓买入
        if position.closeable_amount == 0:
            # 获取当前可用资金
            cash = context.portfolio.available_cash
            # 按价值下单
            order_value(security, cash)
            log.info("价格突破上轨,买入 %s,价格:%.2f,上轨:%.2f" % (security, current_close, upper_band))
            
    # 信号2:价格跌破下轨 -> 卖出
    elif current_close < lower_band:
        # 如果有持仓,则清仓
        if position.closeable_amount > 0:
            order_target(security, 0)
            log.info("价格跌破下轨,卖出 %s,价格:%.2f,下轨:%.2f" % (security, current_close, lower_band))
            
    # 记录曲线,方便在回测界面查看
    record(price=current_close, upper=upper_band, lower=lower_band, middle=current_ema)

代码说明

  1. 数据获取:使用了 attribute_history 获取过去 60 天(g.n * 3)的最高价、最低价和收盘价。获取较长的数据是为了让 talib 的递归算法(如 EMA 和 ATR)收敛,使计算结果更准确。
  2. 指标计算:利用 talib 库高效计算 ATR 和 EMA。
    • talib.ATR: 计算市场波动率。
    • talib.EMA: 计算趋势中枢。
  3. 通道构建
    • 上轨 = EMA + 2.0 * ATR
    • 下轨 = EMA - 2.0 * ATR
  4. 交易执行
    • 使用 order_value 进行全仓买入。
    • 使用 order_target 将仓位调整为 0 进行卖出。
  5. 可视化:使用 record 函数将股价和三条轨道绘制在回测图表中,便于观察策略表现。

Q&A

Q: 肯特纳通道和布林带(Bollinger Bands)有什么区别?
A: 两者都是通道类指标。布林带使用标准差来计算通道宽度,对价格的短期波动非常敏感,通道会频繁收缩和扩张。肯特纳通道使用 ATR(平均真实波幅) 来计算宽度,ATR 更加平滑,因此肯特纳通道发出的假突破信号通常比布林带少,更适合捕捉长期趋势。

Q: 如何调整策略的灵敏度?
A: 可以调整代码中的 g.ng.k

  • 减小 g.n(例如改为 10)或减小 g.k(例如改为 1.5),策略会更敏感,交易频率增加,但假信号也会增多。
  • 增大 g.n(例如改为 30)或增大 g.k(例如改为 2.5),策略会更迟钝,过滤震荡的效果更好,但可能错过趋势初期的利润。

Q: 这个策略适合什么类型的市场?
A: 肯特纳通道是典型的趋势跟踪策略。它在单边上涨或下跌的趋势行情中表现最好。在横盘震荡市中,价格容易频繁穿梭于上下轨之间,导致策略反复止损(来回打脸),造成本金磨损。建议结合其他过滤器(如 ADX 指标判断趋势强度)一起使用。