问题描述
解决方案
这是一个基于聚宽(JoinQuant)API编写的移动止损(Trailing Stop)策略。
策略逻辑说明
- 买入条件:为了演示止损逻辑,这里设置了一个简单的入场条件:当收盘价高于20日均线且当前无持仓时全仓买入。
- 移动止损逻辑:
- 买入后,记录持仓期间的最高价格。
- 如果当前价格创出新高,则更新最高价格。
- 计算回撤幅度:
(最高价 - 当前价) / 最高价。 - 如果回撤幅度超过设定的阈值(例如10%),则触发止损,卖出全部持仓。
策略代码
# -*- coding: utf-8 -*-
# 导入聚宽函数库
import jqdata
def initialize(context):
"""
初始化函数,设定基准、股票、参数等
"""
# 设定沪深300作为基准
set_benchmark('000300.XSHG')
# 开启动态复权模式(真实价格)
set_option('use_real_price', True)
# 过滤掉order系列API产生的比error级别低的log
log.set_level('order', 'error')
# 设定股票类每笔交易时的手续费
set_order_cost(OrderCost(close_tax=0.001, open_commission=0.0003, close_commission=0.0003, min_commission=5), type='stock')
# --- 策略参数设置 ---
# 要操作的股票:平安银行
g.security = '000001.XSHE'
# 移动止损的回撤阈值 (10%)
g.drawdown_limit = 0.10
# 记录持仓期间的最高价,初始化为0
g.highest_price = 0
# 每天开盘时运行
run_daily(market_open, time='every_bar')
def market_open(context):
"""
每日交易逻辑
"""
security = g.security
# 获取股票的收盘价
# 获取过去20天的数据用于计算均线
close_data = attribute_history(security, 20, '1d', ['close'])
# 如果数据不足20天,直接返回
if len(close_data) < 20:
return
# 取得当前价格
current_price = close_data['close'][-1]
# 取得20日均线价格
ma20 = close_data['close'].mean()
# 获取当前现金
cash = context.portfolio.available_cash
# --- 止损与卖出逻辑 ---
# 如果当前持有股票
if context.portfolio.positions[security].closeable_amount > 0:
# 1. 更新持仓期间的最高价
if current_price > g.highest_price:
g.highest_price = current_price
# log.info("创新高,更新最高价为: %.2f" % g.highest_price)
# 2. 计算当前回撤幅度
# 回撤幅度 = (最高价 - 当前价) / 最高价
drawdown = (g.highest_price - current_price) / g.highest_price
# 3. 判断是否触发移动止损
if drawdown > g.drawdown_limit:
log.info("触发移动止损!最高价: %.2f, 当前价: %.2f, 回撤: %.2f%%" % (g.highest_price, current_price, drawdown * 100))
# 卖出所有股票
order_target(security, 0)
# 重置最高价记录
g.highest_price = 0
# --- 买入逻辑 ---
# 如果当前没有持仓,且价格高于20日均线 (简单的趋势跟随入场)
elif context.portfolio.positions[security].closeable_amount == 0:
if current_price > ma20:
log.info("价格站上20日均线,买入 %s" % security)
# 用所有现金买入
order_value(security, cash)
# 买入后,初始化最高价为当前价格
g.highest_price = current_price
# 记录当前价格和最高价,方便在回测图表中查看
record(price=current_price, high_mark=g.highest_price)
常见问题 Q&A
Q: 如何修改止损的比例?
A: 修改 initialize 函数中的 g.drawdown_limit 变量。例如,改为 0.05 即为 5% 回撤止损。
Q: 这个策略可以用于多只股票吗?
A: 当前代码仅针对单只股票(g.security)。如果要操作多只股票,需要将 g.highest_price 改为一个字典(Dictionary),例如 g.highest_prices = {},以股票代码为 Key,最高价为 Value,并在循环中分别处理每只股票的逻辑。
Q: 为什么使用 attribute_history 而不是 data[security]?
A: attribute_history 可以获取历史窗口数据(如过去20天),适合计算均线等指标。而 data 对象通常用于获取当前时间点的切片数据。
Q: 为什么买入后要重置 g.highest_price?
A: 移动止损是针对每一次独立的持仓周期的。一旦卖出(止损或止盈),之前的最高价记录就失效了。下一次买入时,需要以新的买入价作为初始的“最高价”重新开始跟踪。