问题描述
解决方案
这是一个基于 PTrade 量化交易平台的 肯特纳通道(Keltner Channel)突破策略。
策略原理
肯特纳通道由三条线组成:
- 中轨:通常是收盘价的指数移动平均线(EMA),一般取20日。
- 上轨:中轨 + (ATR指标 $\times$ 倍数)。
- 下轨:中轨 - (ATR指标 $\times$ 倍数)。
交易逻辑:
- 买入信号:当收盘价突破上轨时,视为多头趋势增强,全仓买入。
- 卖出信号:当收盘价跌破下轨时,视为趋势反转,清仓卖出。
策略代码
import talib
import numpy as np
def initialize(context):
"""
策略初始化函数
"""
# 1. 设置标的股票,这里以恒生电子(600570.SS)为例
g.security = '600570.SS'
set_universe(g.security)
# 2. 设置策略参数
g.ema_period = 20 # EMA均线周期
g.atr_period = 20 # ATR波动率周期
g.multiplier = 2.0 # 通道宽度倍数
# 3. 设置滑点和手续费(可选,回测使用)
set_commission(commission_ratio=0.0003, min_commission=5.0, type="STOCK")
set_slippage(slippage=0.002)
def handle_data(context, data):
"""
盘中运行函数,每个周期(如每日)运行一次
"""
security = g.security
# 1. 检查标的是否停牌或未上市
if data[security].is_open == 0:
return
# 2. 获取历史数据
# 获取过去 100 个交易日的数据,确保有足够的数据计算 EMA 和 ATR
# 包含 high, low, close 字段
hist_data = get_history(100, frequency='1d', field=['high', 'low', 'close'], security_list=security, fq='pre')
if hist_data is None or len(hist_data) < 50:
log.info("历史数据不足,跳过计算")
return
# 提取 numpy 数组,用于 talib 计算
high_prices = hist_data['high'].values
low_prices = hist_data['low'].values
close_prices = hist_data['close'].values
# 3. 计算肯特纳通道指标
# 计算 ATR (平均真实波幅)
atr = talib.ATR(high_prices, low_prices, close_prices, timeperiod=g.atr_period)
# 计算 EMA (中轨)
ema = talib.EMA(close_prices, timeperiod=g.ema_period)
# 获取最新的指标值(数组的最后一个元素)
current_atr = atr[-1]
current_ema = ema[-1]
# 计算上轨和下轨
upper_band = current_ema + (g.multiplier * current_atr)
lower_band = current_ema - (g.multiplier * current_atr)
# 获取当前收盘价
current_price = close_prices[-1]
# 4. 获取当前持仓情况
position = context.portfolio.positions[security].amount
available_cash = context.portfolio.cash
# 5. 交易逻辑判断
# 信号1:价格突破上轨 -> 买入
if current_price > upper_band:
if position == 0:
# 检查是否涨停,涨停无法买入
if check_limit_up(security, current_price):
log.info("今日涨停,无法买入: %s" % security)
return
# 全仓买入
log.info("价格突破上轨,买入: 价格 %.2f > 上轨 %.2f" % (current_price, upper_band))
order_value(security, available_cash)
# 信号2:价格跌破下轨 -> 卖出
elif current_price < lower_band:
if position > 0:
# 检查是否跌停,跌停无法卖出
if check_limit_down(security, current_price):
log.info("今日跌停,无法卖出: %s" % security)
return
# 清仓卖出
log.info("价格跌破下轨,卖出: 价格 %.2f < 下轨 %.2f" % (current_price, lower_band))
order_target(security, 0)
def check_limit_up(security, price):
"""
辅助函数:判断是否涨停
"""
# 获取快照数据中的涨跌停价格
snapshot = get_snapshot(security)
if snapshot:
limit_up = snapshot[security].get('up_px')
if limit_up and price >= limit_up:
return True
return False
def check_limit_down(security, price):
"""
辅助函数:判断是否跌停
"""
snapshot = get_snapshot(security)
if snapshot:
limit_down = snapshot[security].get('down_px')
if limit_down and price <= limit_down:
return True
return False
代码要点解析
-
库的引用:
- 使用了
talib库来计算 EMA 和 ATR,这是 PTrade 环境支持的高效技术指标库。 - 使用了
numpy来处理价格数组。
- 使用了
-
数据获取 (
get_history):- 获取了
high,low,close三种价格,因为 ATR 指标的计算需要最高价和最低价,而不仅仅是收盘价。 count设置为 100,是为了确保有足够的数据让 EMA 和 ATR 指标“预热”并趋于稳定。
- 获取了
-
通道计算:
- 中轨:
talib.EMA(close, 20) - ATR:
talib.ATR(high, low, close, 20) - 上轨:中轨 + 2 * ATR
- 下轨:中轨 - 2 * ATR
- 中轨:
-
交易控制:
- 使用
order_value进行按金额买入(这里演示的是全仓买入)。 - 使用
order_target进行卖出(目标仓位设为0即为清仓)。 - 增加了简单的涨跌停判断逻辑,避免在无法成交的价格下单。
- 使用
-
Python 版本兼容性:
- 代码遵循 Python 3.5 语法规范(例如使用
%进行字符串格式化,而不是 f-string),确保在 PTrade 旧版环境中也能运行。
- 代码遵循 Python 3.5 语法规范(例如使用
如何在 PTrade 中使用
- 新建一个策略。
- 将上述代码完整复制到策略编辑区。
- 设置回测时间段(例如过去一年)。
- 设置回测频率为“日线”。
- 点击“运行回测”。
常见问题 Q&A
Q: 如何修改策略的灵敏度?
A: 可以调整 initialize 中的参数:
- 减小
g.multiplier(例如改为 1.5),通道变窄,交易会更频繁,但假信号可能增加。 - 增大
g.multiplier(例如改为 2.5),通道变宽,交易更稳健,但可能错过部分行情。 - 调整
g.ema_period可以改变对趋势反应的速度。
Q: 可以做空吗?
A: 上述代码是针对 A 股普通账户设计的(仅做多)。如果是期货或融券账户,可以在“跌破下轨”时使用 sell_open (期货) 或 marginsec_open (融券) 进行开空操作。
Q: 为什么使用 EMA 而不是 SMA?
A: 肯特纳通道的标准定义通常使用 EMA(指数移动平均),因为它对近期价格变动更敏感。如果想用布林带那种 SMA(简单移动平均),可以将 talib.EMA 改为 talib.SMA。