问题描述
解决方案
这是一个基于 PTrade 平台的 Aberration(非正常)波动率突破策略的实现。
策略原理
Aberration 策略由 Keith Fitschen 于 1986 年发明,是一个经典的趋势跟踪策略,通常用于期货市场(CTA策略),但也可以应用于股票。
它的核心逻辑基于布林带(Bollinger Bands):
- 中轨:N 周期的移动平均线(通常 N=35)。
- 上轨:中轨 + k * 标准差(通常 k=2)。
- 下轨:中轨 - k * 标准差。
- 开仓信号:
- 做多:价格突破上轨。
- 做空:价格跌破下轨。
- 平仓信号:
- 平多:价格回落至中轨。
- 平空:价格反弹至中轨。
策略代码实现
以下代码是为期货交易设计的(支持双向交易)。如果您用于股票,请忽略做空部分的逻辑,并将下单函数替换为 order_target 等股票专用接口。
import numpy as np
def initialize(context):
"""
策略初始化函数
"""
# 1. 设置标的:此处以股指期货主力合约为例
# 注意:实际回测或实盘请修改为您需要交易的具体合约代码,如 'IF2312.CCFX'
g.security = 'IF2312.CCFX'
# 2. 设置要操作的股票池/合约
set_universe(g.security)
# 3. 策略参数设置 (Aberration 经典参数)
g.N = 35 # 均线周期
g.k = 2.0 # 标准差倍数
g.unit = 1 # 每次开仓手数
# 4. 设置手续费(可选,根据实际情况调整)
# set_commission(...)
# 5. 开启日志输出
log.info("Aberration 策略初始化完成")
def handle_data(context, data):
"""
按周期运行的策略逻辑(日线级别)
"""
security = g.security
# 1. 获取历史数据
# 获取过去 N 天的收盘价,不包含当前周期(避免未来函数,用过去的数据计算通道)
# count 设置为 g.N,频率为 '1d'
hist = get_history(g.N, '1d', 'close', security, include=False)
# 如果数据长度不足 N 天,无法计算指标,直接返回
if len(hist) < g.N:
log.info("历史数据不足,跳过计算")
return
# 提取收盘价数组
closes = hist['close'].values
# 2. 计算指标 (均线和标准差)
# 计算均值 (中轨)
avg_price = np.mean(closes)
# 计算标准差 (ddof=1 表示样本标准差)
std_dev = np.std(closes, ddof=1)
# 计算上轨和下轨
up_band = avg_price + g.k * std_dev
lo_band = avg_price - g.k * std_dev
# 3. 获取当前最新价格
# data[security] 返回 SecurityUnitData 对象
current_price = data[security].close
# 4. 获取当前持仓信息
position = get_position(security)
long_pos = position.long_amount # 多头持仓
short_pos = position.short_amount # 空头持仓
# 5. 交易逻辑
# --- 平仓逻辑 ---
# 如果持有多单,且价格回落到中轨下方,平多
if long_pos > 0 and current_price < avg_price:
log.info("价格 %.2f 回落至中轨 %.2f 下方,平多仓" % (current_price, avg_price))
sell_close(security, long_pos)
# 如果持有空单,且价格反弹到中轨上方,平空
if short_pos > 0 and current_price > avg_price:
log.info("价格 %.2f 反弹至中轨 %.2f 上方,平空仓" % (current_price, avg_price))
buy_close(security, short_pos)
# --- 开仓逻辑 ---
# 如果没有多单,且价格突破上轨,开多
if long_pos == 0 and current_price > up_band:
log.info("价格 %.2f 突破上轨 %.2f,开多仓" % (current_price, up_band))
# 如果持有空单,先平空(反手逻辑,视情况可选)
if short_pos > 0:
buy_close(security, short_pos)
buy_open(security, g.unit)
# 如果没有空单,且价格跌破下轨,开空
if short_pos == 0 and current_price < lo_band:
log.info("价格 %.2f 跌破下轨 %.2f,开空仓" % (current_price, lo_band))
# 如果持有多单,先平多(反手逻辑,视情况可选)
if long_pos > 0:
sell_close(security, long_pos)
sell_open(security, g.unit)
代码要点解析
-
数据获取 (
get_history):- 使用了
include=False。这是为了确保我们使用的是昨天及之前的 N 天数据来构建今天的布林带通道。如果包含今天的数据,通道会随着今天的价格变动而变动,这在回测中可能引入未来函数或导致信号闪烁。 - 我们比较的是
current_price(当前价格)和基于历史数据计算出的up_band/lo_band。
- 使用了
-
指标计算:
- 使用了
numpy库的mean和std函数。这是计算移动平均和标准差的标准方法。 ddof=1用于计算样本标准差,这在金融统计中更为常用。
- 使用了
-
交易函数:
- 使用了期货专用的
buy_open(买入开仓),sell_close(卖出平仓),sell_open(卖出开仓),buy_close(买入平仓)。 - 如果您交易的是股票,请将开仓逻辑改为
order(security, amount),平仓逻辑改为order_target(security, 0),并删除所有关于空头(short)的判断和操作。
- 使用了期货专用的
-
持仓判断:
- 通过
get_position(security).long_amount和short_amount来精确判断当前的多空持仓状态。
- 通过
常见问题 (Q&A)
Q: 这个策略适合什么频率?
A: Aberration 策略最初是为日线级别设计的。它是一个捕捉中长线趋势的策略。如果在分钟级别使用,可能需要调整 N 和 k 的参数(例如增大 N),否则手续费和滑点会侵蚀利润。
Q: 如何在股票上使用此策略?
A: 股票不能直接做空(除非融券),因此只能使用“突破上轨买入”和“跌破中轨卖出”的逻辑。您需要删除代码中 short_pos 相关的部分,并将 buy_open 替换为 order,sell_close 替换为 order_target(..., 0)。
Q: 为什么回测时没有交易?
A:
- 检查
g.security是否设置了正确的代码(注意后缀,如.CCFX,.XSGE,.SS等)。 - 检查回测时间段内,价格是否真的产生了足以突破 2 倍标准差的剧烈波动。
- 检查
get_history是否获取到了数据(日志中是否有 "历史数据不足" 的提示)。
Q: 如何优化这个策略?
A:
- 参数优化:调整 N (周期) 和 k (标准差倍数)。
- 多品种组合:Aberration 的核心优势在于多品种组合投资,通过分散投资来捕捉不同品种的趋势,同时平滑资金曲线。您可以将
g.security改为一个列表,并在handle_data中遍历列表进行交易。