🚀 AI 一键生成 joinquant 策略代码

如何在 JoinQuant 中实现移动止损(Trailing Stop-Loss)策略?

joinquant | 发布时间: 2025/12/9
以下内容由 EasyQuant 生成。

问题描述

请帮我编写一个移动止损策略,当价格从最高点回撤一定比例时卖出。

解决方案

这是一个基于聚宽(JoinQuant)API编写的移动止损(Trailing Stop)策略。

策略逻辑说明

  1. 买入条件:为了演示止损逻辑,这里设置了一个简单的入场条件:当收盘价高于20日均线且当前无持仓时全仓买入。
  2. 移动止损逻辑
    • 买入后,记录持仓期间的最高价格
    • 如果当前价格创出新高,则更新最高价格。
    • 计算回撤幅度:(最高价 - 当前价) / 最高价
    • 如果回撤幅度超过设定的阈值(例如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: 移动止损是针对每一次独立的持仓周期的。一旦卖出(止损或止盈),之前的最高价记录就失效了。下一次买入时,需要以新的买入价作为初始的“最高价”重新开始跟踪。