🚀 AI 一键生成 joinquant 策略代码
立即体验

聚宽平台如何优化“头七战法”(跌停后第五日买入)的止盈止损以提升期望收益?

joinquant | 发布时间: 2026/8/10
以下内容由 EasyQuant 生成。

问题描述

JoinQuant 头七战法:跌停后第五个交易日买入如何优化止盈止损?
在聚宽中实现尾盘恐慌跌停股票的交易日计数和第五日买入后,如何处理胜率较高但大亏损导致期望收益为负的问题?

解决方案

在量化交易中,“头七战法”(即标的跌停后,在第五个交易日寻找超跌反弹机会买入)是一种典型的逆势均值回归策略。这类策略经常面临**“高胜率、低盈亏比”**的窘境:虽然多数交易能微利止盈,但极少数持续暴跌的极端案例(如连续一字跌停、退市整理等)会产生巨大亏损,直接将策略的整体期望收益拉低至负数。

要在聚宽(JoinQuant)平台中解决这一痛点,必须从数据防真、动态止损、仓位控制三个维度进行深度优化。以下是具体的优化方案及聚宽实现代码:

一、 核心优化策略

  1. 开启动态复权(真实价格)模式
    在逆势策略中,必须使用真实价格进行撮合。如果不开启 set_option('use_real_price', True),回测可能会使用未来的复权因子,导致历史跌停价格不准确,产生回测幻觉。
  2. 引入 ATR(真实波幅)动态止损
    固定百分比止损(如-5%)无法适应不同股票的波动率。引入 ATR 指标,将止损线设为 买入价 - N * ATR。对于高波动股票给予更宽的容忍度,对于低波动股票则快速止损。
  3. 时间硬止损(Time Exit)
    超跌反弹策略的核心是“快进快出”。如果买入后 3-5 个交易日内没有产生预期的反弹,说明均值回归失效,无论盈亏必须强行平仓,避免陷入阴跌深渊。
  4. 过滤 ST 及退市风险股
    跌停股票极易踩雷。必须利用 get_current_data() 动态过滤 ST、*ST 以及退市整理期的股票,从源头上规避连续无量一字跌停无法卖出的风险。

二、 聚宽(JoinQuant)优化版策略源码

以下代码展示了如何在聚宽中实现跌停计数、第五日买入,并结合 ATR 动态止损 + 时间硬止损 的完整逻辑:

import jqdata
import numpy as np
import talib

def initialize(context):
    # 1. 开启真实价格模式,防范未来函数
    set_option('use_real_price', True)
    set_benchmark('000300.XSHG')
    
    # 交易税费设置
    set_order_cost(OrderCost(close_tax=0.001, open_commission=0.0003, close_commission=0.0003, min_commission=5), type='stock')
    
    # 策略参数
    g.target_hold_days = 5  # 跌停后第5个交易日买入
    g.max_hold_days = 3     # 买入后最大持有天数(时间止损)
    g.atr_period = 14       # ATR计算周期
    g.atr_multiplier = 1.5  # ATR止损倍数
    g.profit_target = 0.08  # 8%止盈线
    
    # 记录持仓信息 {stock: {'buy_price': price, 'hold_days': 0, 'stop_loss': price}}
    g.tracker = {}
    
    # 每日运行
    run_daily(market_open, time='every_bar')

def before_trading_start(context):
    # 过滤ST和退市股
    current_data = get_current_data()
    g.all_stocks = [s for s in get_all_securities(['stock'], context.current_dt).index 
                    if not current_data[s].is_st and not current_data[s].paused]

def market_open(context):
    # 1. 处理已有持仓的止盈止损
    handle_exits(context)
    
    # 2. 寻找符合“头七战法”的股票并买入
    check_and_buy(context)

def handle_exits(context):
    current_data = get_current_data()
    positions = context.portfolio.positions
    
    for stock in list(positions.keys()):
        if stock not in g.tracker:
            continue
            
        current_price = current_data[stock].last_price
        buy_price = g.tracker[stock]['buy_price']
        stop_loss = g.tracker[stock]['stop_loss']
        g.tracker[stock]['hold_days'] += 1
        
        # 止损触发:跌破 ATR 动态止损线
        if current_price <= stop_loss:
            order_target(stock, 0)
            log.info(f"[止损卖出] {stock},当前价: {current_price},止损价: {stop_loss}")
            g.tracker.pop(stock, None)
            continue
            
        # 止盈触发:达到目标收益
        if current_price >= buy_price * (1 + g.profit_target):
            order_target(stock, 0)
            log.info(f"[止盈卖出] {stock},当前价: {current_price},买入价: {buy_price}")
            g.tracker.pop(stock, None)
            continue
            
        # 时间止损触发:持有超过最大天数
        if g.tracker[stock]['hold_days'] >= g.max_hold_days:
            order_target(stock, 0)
            log.info(f"[时间止损] {stock} 持有达 {g.max_hold_days} 天,强行平仓")
            g.tracker.pop(stock, None)

def check_and_buy(context):
    # 避免重复买入
    available_cash = context.portfolio.available_cash
    if available_cash < 20000: # 剩余资金不足
        return
        
    for stock in g.all_stocks:
        if stock in context.portfolio.positions:
            continue
            
        # 获取过去6天的数据(判断5天前是否跌停)
        hist = attribute_history(stock, 6, '1d', ['close', 'high_limit', 'low_limit'])
        if len(hist) < 6:
            continue
            
        # 5天前的K线索引为 hist.iloc[0]
        was_limit_down = (hist['close'].iloc[0] <= hist['low_limit'].iloc[0] * 1.005) # 允许0.5%的误差
        
        # 确保中间4天没有大幅反弹(维持超跌状态)
        no_rebound = all(hist['close'].iloc[1:5] < hist['close'].iloc[0] * 1.05)
        
        if was_limit_down and no_rebound:
            # 计算 ATR 用于设置止损线
            prices = attribute_history(stock, g.atr_period + 5, '1d', ['high', 'low', 'close'])
            atr = talib.ATR(prices['high'].values, prices['low'].values, prices['close'].values, timeperiod=g.atr_period)[-1]
            
            if np.isnan(atr):
                continue
                
            # 执行买入
            current_price = get_current_data()[stock].last_price
            order_value(stock, 20000) # 均分仓位,每只买入2万
            
            # 记录买入信息与动态 ATR 止损线
            g.tracker[stock] = {
                'buy_price': current_price,
                'hold_days': 0,
                'stop_loss': current_price - (g.atr_multiplier * atr)
            }
            log.info(f"[头七买入] {stock},买入价: {current_price},动态止损价: {g.tracker[stock]['stop_loss']}")