问题描述
解决方案
这是一个基于聚宽(JoinQuant)API 实现的**肯特纳通道(Keltner Channel)**交易策略。
策略原理
肯特纳通道是一个趋势跟踪指标,由三条线组成:
- 中轨:通常是收盘价的指数移动平均线(EMA),一般取 20 日。
- 上轨:中轨 + $k \times ATR$(平均真实波幅)。
- 下轨:中轨 - $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)
代码说明
- 数据获取:使用了
attribute_history获取过去 60 天(g.n * 3)的最高价、最低价和收盘价。获取较长的数据是为了让talib的递归算法(如 EMA 和 ATR)收敛,使计算结果更准确。 - 指标计算:利用
talib库高效计算 ATR 和 EMA。talib.ATR: 计算市场波动率。talib.EMA: 计算趋势中枢。
- 通道构建:
- 上轨 = EMA + 2.0 * ATR
- 下轨 = EMA - 2.0 * ATR
- 交易执行:
- 使用
order_value进行全仓买入。 - 使用
order_target将仓位调整为 0 进行卖出。
- 使用
- 可视化:使用
record函数将股价和三条轨道绘制在回测图表中,便于观察策略表现。
Q&A
Q: 肯特纳通道和布林带(Bollinger Bands)有什么区别?
A: 两者都是通道类指标。布林带使用标准差来计算通道宽度,对价格的短期波动非常敏感,通道会频繁收缩和扩张。肯特纳通道使用 ATR(平均真实波幅) 来计算宽度,ATR 更加平滑,因此肯特纳通道发出的假突破信号通常比布林带少,更适合捕捉长期趋势。
Q: 如何调整策略的灵敏度?
A: 可以调整代码中的 g.n 和 g.k:
- 减小
g.n(例如改为 10)或减小g.k(例如改为 1.5),策略会更敏感,交易频率增加,但假信号也会增多。 - 增大
g.n(例如改为 30)或增大g.k(例如改为 2.5),策略会更迟钝,过滤震荡的效果更好,但可能错过趋势初期的利润。
Q: 这个策略适合什么类型的市场?
A: 肯特纳通道是典型的趋势跟踪策略。它在单边上涨或下跌的趋势行情中表现最好。在横盘震荡市中,价格容易频繁穿梭于上下轨之间,导致策略反复止损(来回打脸),造成本金磨损。建议结合其他过滤器(如 ADX 指标判断趋势强度)一起使用。