3 分钟快速生成代码
输入想法,AI 即刻生成可运行代码
在PTrade量化交易平台中,虽然系统提供了MACD、KDJ等常见指标的计算函数,但并未直接提供计算ATR(平均真实波幅)的内置函数。我们需要利用numpy和pandas库基于get_history获取的行情数据自行编写计算逻辑。
ATR(Average True Range)主要用于衡量市场的波动性。在策略中,它常被用来构建动态追踪止损(Trailing Stop),即随着价格向有利方向变动,止损线也随之移动,从而锁定利润;当价格反向波动超过一定幅度的ATR时,触发止损。
以下是完整的策略代码,包含自定义的ATR计算函数以及基于ATR的动态止损逻辑。
import numpy as np
import pandas as pd
def initialize(context):
"""
初始化函数
"""
# 设置我们要操作的股票,这里以恒生电子为例
g.security = '600570.SS'
set_universe(g.security)
# 策略参数设置
g.atr_period = 14 # ATR计算周期
g.atr_multiplier = 2.0 # 止损倍数(止损距离 = ATR * 倍数)
# 记录动态止损价
g.stop_loss_price = 0.0
# 设置手续费等(回测用)
set_commission(commission_ratio=0.0003, min_commission=5.0)
def calculate_atr(security, period):
"""
自定义计算ATR指标的函数
参数:
security: 标的代码
period: 计算周期
返回:
ATR数值 (float)
"""
# 获取历史数据
# 我们需要获取 period + 1 天的数据,因为计算TR需要前一日收盘价
# include=False 表示不包含当前正在进行的这一根K线,使用历史收盘数据更稳定
count = period + 2
h_data = get_history(count, '1d', ['high', 'low', 'close'], security, fq='pre', include=False)
# 数据长度不足时返回None
if len(h_data) < count:
return None
# 将数据转换为DataFrame以便处理
df = h_data
# 计算TR (真实波幅)
# TR = Max(High - Low, abs(High - PreClose), abs(Low - PreClose))
# 1. 当前最高价 - 当前最低价
hl = df['high'] - df['low']
# 2. abs(当前最高价 - 前一日收盘价)
# shift(1) 是将数据向下移动一行,即获取前一日数据
hc = (df['high'] - df['close'].shift(1)).abs()
# 3. abs(当前最低价 - 前一日收盘价)
lc = (df['low'] - df['close'].shift(1)).abs()
# 组合成一个DataFrame取最大值
tr_df = pd.concat([hl, hc, lc], axis=1)
tr = tr_df.max(axis=1)
# 移除因为shift产生的NaN值
tr = tr.dropna()
# 计算ATR:通常使用简单移动平均(SMA)或者Wilder平滑
# 这里使用简单移动平均作为示例,取最后period天的均值
if len(tr) < period:
return None
atr = tr.tail(period).mean()
return atr
def handle_data(context, data):
"""
盘中运行函数
"""
security = g.security
# 1. 获取当前价格
current_price = data[security]['close']
# 2. 计算ATR
atr_value = calculate_atr(security, g.atr_period)
if atr_value is None:
log.info("数据不足,无法计算ATR,跳过本次循环")
return
# 获取当前持仓
position = get_position(security)
# ---------------------------------------------------
# 交易逻辑部分
# ---------------------------------------------------
# 情况A:当前无持仓,进行买入判断(这里仅作演示,使用简单的买入逻辑)
if position.amount == 0:
# 演示逻辑:直接买入
order(security, 1000)
# 【关键步骤】开仓时初始化止损价
# 初始止损价 = 开仓价 - (ATR * 倍数)
g.stop_loss_price = current_price - (atr_value * g.atr_multiplier)
log.info("买入 %s, 价格: %.2f, 初始止损价: %.2f, 当前ATR: %.3f" % (
security, current_price, g.stop_loss_price, atr_value))
# 情况B:当前有持仓,进行动态止损维护
elif position.amount > 0:
# 1. 计算基于当前价格的潜在新止损价
# 吊灯止损逻辑:随着价格上涨,止损线跟着上涨;价格下跌,止损线不变
potential_new_stop = current_price - (atr_value * g.atr_multiplier)
# 2. 更新止损价:只有当新的止损价高于旧的止损价时才更新(只升不降)
if potential_new_stop > g.stop_loss_price:
old_stop = g.stop_loss_price
g.stop_loss_price = potential_new_stop
log.info("价格上涨,上移止损价。旧止损: %.2f -> 新止损: %.2f" % (old_stop, g.stop_loss_price))
# 3. 检查是否触发止损
if current_price < g.stop_loss_price:
log.info("触发ATR动态止损!当前价: %.2f < 止损价: %.2f. 卖出平仓。" % (current_price, g.stop_loss_price))
order_target(security, 0)
# 重置止损价
g.stop_loss_price = 0.0
calculate_atr 函数:
get_history 获取 high, low, close 数据。注意 count 设置为 period + 2,因为计算 TR(真实波幅)涉及到前一日收盘价(PreClose),且计算移动平均需要足够的数据点。N 天的简单移动平均(Mean)。动态止损(Trailing Stop)逻辑:
当前价格 - (ATR * 倍数)。倍数通常设为 2 到 3,意味着价格需要反向波动 2-3 倍的平均波动幅度才会触发止损,这能有效过滤市场噪音。handle_data)中,根据当前价格重新计算一个“潜在止损价”。潜在止损价 > 当前记录的止损价,说明价格上涨了,我们将止损线上移,锁定利润。g.stop_loss_price,立即执行清仓操作。calculate_atr 函数复制到你的策略代码中。initialize 中设置 g.atr_period(通常为14)和 g.atr_multiplier(通常为2.0或3.0)。handle_data 或 run_daily 调用的函数中,按照示例代码的逻辑,在开仓时初始化止损价,在持仓期间不断尝试抬高止损价并检查是否跌破。Q1: 为什么计算ATR时要使用 include=False?
A1: include=False 表示获取的数据不包含当前正在交易的这一天(或这一分钟)。在计算止损基准时,使用已经收盘确认的历史数据(昨日ATR)通常比使用正在变动的当日ATR更稳定,能避免盘中剧烈波动导致止损线频繁跳变。
Q2: ATR倍数(Multiplier)应该设置多少?
A2: 这取决于你的交易风格和标的波动性。
Q3: 这个函数支持分钟级别回测吗?
A3: 支持。get_history 的第二个参数 frequency 如果设置为 '1m' 或 '5m',该函数计算的就是分钟级别的ATR。但在分钟级别策略中,建议适当增大 period 或者倍数,因为分钟数据的噪音通常比日线更大。