问题描述
解决方案
这是一个为您编写的适用于 PTrade 平台的 追踪止损(Trailing Stop-Loss) 函数。
策略逻辑说明
追踪止损的核心逻辑是:记录持仓以来的最高价格(High Watermark),当当前价格从最高点回撤达到设定的百分比时,触发卖出。
- 初始化:在
initialize中定义一个全局字典g.high_prices用来存储每只股票的最高价,以及设定回撤阈值g.trailing_gap。 - 更新最高价:在每个交易周期(
handle_data),检查持仓股票的当前价格。如果当前价格高于记录的最高价,则更新最高价。 - 触发止损:计算
止损价 = 最高价 * (1 - 回撤阈值)。如果当前价格 <= 止损价,则清仓卖出。 - 清理数据:卖出后,从字典中移除该股票的记录,以便下次买入时重新计算。
PTrade 策略代码
def initialize(context):
"""
初始化函数
"""
# 设置股票池(示例:恒生电子)
g.security = '600570.SS'
set_universe(g.security)
# 设定追踪止损的回撤阈值 (例如 5%)
g.trailing_gap = 0.05
# 用于存储每只持仓股票的历史最高价(High Watermark)
# 格式: {'股票代码': 最高价格}
g.high_prices = {}
# 简单的买入标记,用于演示
g.bought = False
def handle_data(context, data):
"""
盘中运行函数
"""
# ---------------------------------------------------
# 1. 简单的买入逻辑(仅用于演示,以便有持仓可以测试止损)
# ---------------------------------------------------
if not g.bought:
# 全仓买入示例股票
order_target_value(g.security, context.portfolio.cash)
g.bought = True
log.info("演示买入: %s" % g.security)
# ---------------------------------------------------
# 2. 调用追踪止损函数
# ---------------------------------------------------
check_trailing_stop(context, data)
def check_trailing_stop(context, data):
"""
追踪止损核心函数
"""
# 获取当前账户所有持仓的股票代码列表
# context.portfolio.positions 包含所有持仓信息
holding_list = list(context.portfolio.positions.keys())
# 遍历每一只持仓股票
for stock in holding_list:
position = context.portfolio.positions[stock]
# 如果持仓数量为0,跳过(可能是已卖出但对象还在缓存中)
if position.amount == 0:
# 如果该股票还在最高价记录中,清理掉
if stock in g.high_prices:
del g.high_prices[stock]
continue
# 获取当前价格
# 注意:data[stock] 获取的是 SecurityUnitData 对象
current_price = data[stock]['close']
# ---------------------------------------------------
# 逻辑 A: 初始化或更新最高价 (High Watermark)
# ---------------------------------------------------
# 如果该股票不在记录中(刚买入),或者当前价格创新高
if stock not in g.high_prices:
# 刚买入时,将最高价初始化为当前价(或者可以使用持仓成本 position.cost_basis)
g.high_prices[stock] = current_price
log.info("初始化追踪止损记录: %s, 当前价: %.2f" % (stock, current_price))
elif current_price > g.high_prices[stock]:
# 创新高,更新最高价
old_high = g.high_prices[stock]
g.high_prices[stock] = current_price
log.info("更新最高价: %s, 从 %.2f -> %.2f" % (stock, old_high, current_price))
# ---------------------------------------------------
# 逻辑 B: 计算止损线并检查是否触发
# ---------------------------------------------------
# 计算触发止损的价格
high_price = g.high_prices[stock]
stop_price = high_price * (1 - g.trailing_gap)
# 检查是否跌破止损价
if current_price <= stop_price:
log.warning("触发追踪止损: %s" % stock)
log.warning("历史最高价: %.2f, 设定回撤: %.1f%%, 止损触发价: %.2f, 当前价: %.2f" % (
high_price,
g.trailing_gap * 100,
stop_price,
current_price
))
# 执行卖出操作:清仓
order_target(stock, 0)
# 卖出后,从记录字典中移除该股票,防止重复逻辑或干扰下次交易
del g.high_prices[stock]
代码关键点解析
-
g.high_prices字典:- 这是实现追踪止损的关键。因为
handle_data是循环运行的,我们需要一个持久的变量来记住“这只股票自从我买入以来,最高涨到了多少”。 - 使用字典结构
{'600570.SS': 50.5, '000001.SZ': 12.3}可以同时管理多只股票的止损逻辑。
- 这是实现追踪止损的关键。因为
-
context.portfolio.positions:- 我们遍历
context.portfolio.positions而不是g.security,这样可以确保函数对账户里所有实际持有的股票生效,无论它们是如何买入的。
- 我们遍历
-
初始化逻辑:
if stock not in g.high_prices:这一步处理刚买入的情况。通常我们将“当前价”设为初始最高价。如果您希望更保守,也可以使用position.cost_basis(持仓成本)作为初始基准。
-
Python 版本兼容性:
- 代码使用了
%格式化字符串(例如"当前价: %.2f" % current_price),这是为了确保在 PTrade 可能使用的旧版本 Python(如 Python 3.5)中也能完美运行,避免使用 f-string 导致语法错误。
- 代码使用了
如何在您的策略中使用
- 将
check_trailing_stop函数完整复制到您的策略代码底部。 - 在
initialize函数中添加g.high_prices = {}和g.trailing_gap = 0.05(您可以修改 0.05 为您想要的比例,如 0.10 代表 10%)。 - 在
handle_data函数的末尾调用check_trailing_stop(context, data)。
Q&A
Q1: 这个函数支持分钟级别回测吗?
A1: 支持。handle_data 在分钟回测模式下每分钟运行一次,该函数会实时更新分钟线收盘价作为最高价,并在盘中触发止损。
Q2: 如果我手动卖出了一部分股票,这个函数会出错吗?
A2: 不会。函数中使用 order_target(stock, 0) 是清仓指令。如果您手动卖出了部分股票,context.portfolio.positions 中的数量会更新,函数会继续对剩余持仓进行追踪止损。如果您手动全卖了,position.amount 变为 0,代码中的判断逻辑会清理掉对应的 g.high_prices 记录。
Q3: 如何修改为“固定价格止损”而不是“追踪止损”?
A3: 如果需要固定价格止损(例如亏损 5% 就卖,不管中间涨了多少),只需将 g.high_prices[stock] 的更新逻辑去掉,始终将其固定为 position.cost_basis(持仓成本价)即可。