You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Pine Script策略:二层止盈后止损(SL)偶尔失效问题

问题描述

我基于ATR带开发了量化策略,设置了两层止盈(TP):第一层平仓40%仓位,第二层平仓20%仓位,剩余40%持仓至策略多空反转,这部分运行正常;但在第二层止盈触发后,通过第三个ATR值设置的止损(SL)功能时而有效时而失效。

策略止损失效截图

策略代码

strategy('Strategy', initial_capital=100, precision=4, default_qty_type=strategy.percent_of_equity, commission_type=strategy.commission.percent, commission_value=0.02, slippage=0, margin_long = 2, margin_short = 2,default_qty_value=100, overlay=false)

start = timestamp(input(2024, 'start year'), input(1, 'start month'), input(1, 'start day'), input(1, 'start hour'), 00)
end = timestamp(input(2026, 'end year'), input(1, 'end month'), input(1, 'end day'), 00, 00)


//---------
//EMA Slope
//---------

//define slope inputs
average = input.string(title='Source MA Type', defval='EMA', options=['EMA', 'SMA'], group = "Slope inputs")
len = input.int(130, minval=1, title='Source MA Length', group = "Slope inputs")
slopeFlen = input.int(6, title='Fast Slope MA Length', group = "Slope inputs")
slopeSlen = input.int(11, title='Slow Slope MA Length', group = "Slope inputs")

//Variables
out = if average == 'EMA'
    ta.ema(close, len)
else
    ta.sma(close, len)

slp = ta.change(out) / out

emaslopeF = ta.ema(slp, slopeFlen)
emaslopeS = ta.ema(slp, slopeSlen)


//plot
plot(slp*100, color=color.rgb(145, 35, 139), title = 'Slope', linewidth = 2)
plot(emaslopeF*100, color=color.rgb(59, 226, 255), title = 'EMA Slope fast',linewidth = 2)
plot(emaslopeS*100, color=color.rgb(88, 45, 187), title = 'EMA Slope slow', linewidth = 2)

percentTPATR= input.int(defval = 40, title = " Percent amount of 1st TP on ATR ", group = "Partial TP and SL quantity")
percent2TPATR= input.int(defval = 20, title = " Percent amount of 2nd TP on ATR ", group = "Partial TP and SL quantity")


//---
//ATR
//---

//Input settings for 1st TP
atrPeriod = input.int(title='ATR Period', defval=14, minval=1, group="ATR Bands Settings on First TP")
atrMultiplier = input.float(title='ATR Band Scale Factor', defval=1.4, step=0.1, minval=0.01, group="ATR Bands Settings on First TP")
atrSourceRef = "close"

//Calcs
atr = ta.atr(atrPeriod)
upline = close + atrMultiplier*atr[0]
bottomline = close - atrMultiplier*atr[0]


//Input settings for 2nd TP
atrPeriod2 = input.int(title='ATR Period', defval=14, minval=1, group="ATR Bands Settings on Second TP")
atrMultiplier2 = input.float(title='ATR Band Scale Factor', defval=4, step=0.1, minval=0.01, group="ATR Bands Settings on Second TP")
atrSourceRef2 = "close"

//Calcs
atr2 = ta.atr(atrPeriod2)
upline2 = close + atrMultiplier2*atr2[0]
bottomline2 = close - atrMultiplier2*atr2[0]


//Input settings for SL after 2nd TP
atrPeriod3 = input.int(title='ATR Period', defval=14, minval=1, group="ATR Bands Settings on SL after 2 TP")
atrMultiplier3 = input.float(title='ATR Band Scale Factor', defval=2, step=0.1, minval=0.01, group="ATR Bands Settings on SL after 2 TP")
atrSourceRef3 = "close"

//Calcs
atr3 = ta.atr(atrPeriod3)
upline3 = close + atrMultiplier3*atr3[0]
bottomline3 = close - atrMultiplier3*atr3[0]

//-------------------------
//Entry and exit conditions
//-------------------------

ConditionEntryL = emaslopeF > emaslopeS
ConditionEntryS = emaslopeF < emaslopeS

//Entry conditions
if time > start and time < end
    if ConditionEntryL
        strategy.entry('Long', direction=strategy.long)

if time > start and time < end
    if ConditionEntryS
        strategy.entry('Short', direction=strategy.short)


//------------------------------
//get ATR values on entry for TP
//------------------------------

//get ATR values on entry for 1st TP
var float fixedBottomline = 0
var float fixedUpline = 0

if nz(ConditionEntryL[1]) and strategy.position_size > 0 and strategy.position_size[1] <= 0
    fixedUpline := upline

if nz(ConditionEntryS[1]) and strategy.position_size < 0 and strategy.position_size[1] >= 0
    fixedBottomline := bottomline

//get ATR values on entry for 2nd TP
var float fixedBottomline2 = 0
var float fixedUpline2 = 0

if nz(ConditionEntryL[1]) and strategy.position_size > 0 and strategy.position_size[1] <= 0
    fixedUpline2 := upline2

if nz(ConditionEntryS[1]) and strategy.position_size < 0 and strategy.position_size[1] >= 0
    fixedBottomline2 := bottomline2

//get ATR values on entry for SL after 2 TP
var float fixedBottomline3 = 0
var float fixedUpline3 = 0

if nz(ConditionEntryL[1]) and strategy.position_size > 0 and strategy.position_size[1] <= 0
    fixedUpline3 := upline3

if nz(ConditionEntryS[1]) and strategy.position_size < 0 and strategy.position_size[1] >= 0
    fixedBottomline3 := bottomline3

//-------------
//Exit Strategy
//-------------


if time > start and time < end
    if strategy.position_size > 0

        strategy.exit("Long ATR 40% Take Profit", disable_alert=true, limit=fixedUpline, qty_percent=percentTPATR)
        strategy.exit("Long ATR 20% Take Profit", disable_alert=true, limit=fixedUpline2, qty_percent=percent2TPATR)

    if high >= fixedUpline2
        strategy.exit("Long SL", from_entry="Long", stop=fixedUpline3) //I'm not sure if this is correct

if time > start and time < end
    if strategy.position_size < 0
        strategy.exit("Short ATR 40% Take Profit", disable_alert=true, limit=fixedBottomline, qty_percent=percentTPATR)
        strategy.exit("Short ATR 20% Take Profit", disable_alert=true, limit=fixedBottomline2, qty_percent=percent2TPATR)

    if low <= fixedBottomline2
        strategy.exit("ShortSL", from_entry="Short", stop=fixedBottomline3) //I'm not sure if this is correct
问题原因与修复方案

核心问题

  1. 止盈触发判断逻辑错误:仅用high >= fixedUpline2判断第二层止盈是否触发,会在价格触碰止盈线的每一根K线重复发送止损指令,导致策略内部指令冲突,时而失效。
  2. 止损方向逻辑错误:多单止损误用了上方ATR线fixedUpline3,空单止损误用了下方ATR线fixedBottomline3,完全违背了止损的方向逻辑。
  3. 无状态跟踪:未跟踪第二层止盈是否实际成交,仅靠价格触碰会出现误触发。

修复步骤

  1. 添加状态变量:定义布尔变量跟踪第一层、第二层止盈的成交状态,避免重复发送指令。
  2. 基于持仓变化判断成交:通过持仓量的变化确认止盈是否实际执行,而非仅依赖价格触碰。
  3. 修正止损方向:多单止损使用下方ATR线fixedBottomline3,空单止损使用上方ATR线fixedUpline3。
  4. 优化指令发送逻辑:仅在第二层止盈成交后,为剩余仓位设置止损。

修改后的完整代码

strategy('Strategy', initial_capital=100, precision=4, default_qty_type=strategy.percent_of_equity, commission_type=strategy.commission.percent, commission_value=0.02, slippage=0, margin_long = 2, margin_short = 2,default_qty_value=100, overlay=false)

start = timestamp(input(2024, 'start year'), input(1, 'start month'), input(1, 'start day'), input(1, 'start hour'), 00)
end = timestamp(input(2026, 'end year'), input(1, 'end month'), input(1, 'end day'), 00, 00)


//---------
//EMA Slope
//---------

//define slope inputs
average = input.string(title='Source MA Type', defval='EMA', options=['EMA', 'SMA'], group = "Slope inputs")
len = input.int(130, minval=1, title='Source MA Length', group = "Slope inputs")
slopeFlen = input.int(6, title='Fast Slope MA Length', group = "Slope inputs")
slopeSlen = input.int(11, title='Slow Slope MA Length', group = "Slope inputs")

//Variables
out = if average == 'EMA'
    ta.ema(close, len)
else
    ta.sma(close, len)

slp = ta.change(out) / out

emaslopeF = ta.ema(slp, slopeFlen)
emaslopeS = ta.ema(slp, slopeSlen)


//plot
plot(slp*100, color=color.rgb(145, 35, 139), title = 'Slope', linewidth = 2)
plot(emaslopeF*100, color=color.rgb(59, 226, 255), title = 'EMA Slope fast',linewidth = 2)
plot(emaslopeS*100, color=color.rgb(88, 45, 187), title = 'EMA Slope slow', linewidth = 2)

percentTPATR= input.int(defval = 40, title = " Percent amount of 1st TP on ATR ", group = "Partial TP and SL quantity")
percent2TPATR= input.int(defval = 20, title = " Percent amount of 2nd TP on ATR ", group = "Partial TP and SL quantity")


//---
//ATR
//---

//Input settings for 1st TP
atrPeriod = input.int(title='ATR Period', defval=14, minval=1, group="ATR Bands Settings on First TP")
atrMultiplier = input.float(title='ATR Band Scale Factor', defval=1.4, step=0.1, minval=0.01, group="ATR Bands Settings on First TP")
atrSourceRef = "close"

//Calcs
atr = ta.atr(atrPeriod)
upline = close + atrMultiplier*atr[0]
bottomline = close - atrMultiplier*atr[0]


//Input settings for 2nd TP
atrPeriod2 = input.int(title='ATR Period', defval=14, minval=1, group="ATR Bands Settings on Second TP")
atrMultiplier2 = input.float(title='ATR Band Scale Factor', defval=4, step=0.1, minval=0.01, group="ATR Bands Settings on Second TP")
atrSourceRef2 = "close"

//Calcs
atr2 = ta.atr(atrPeriod2)
upline2 = close + atrMultiplier2*atr2[0]
bottomline2 = close - atrMultiplier2*atr2[0]


//Input settings for SL after 2nd TP
atrPeriod3 = input.int(title='ATR Period', defval=14, minval=1, group="ATR Bands Settings on SL after 2 TP")
atrMultiplier3 = input.float(title='ATR Band Scale Factor', defval=2, step=0.1, minval=0.01, group="ATR Bands Settings on SL after 2 TP")
atrSourceRef3 = "close"

//Calcs
atr3 = ta.atr(atrPeriod3)
upline3 = close + atrMultiplier3*atr3[0]
bottomline3 = close - atrMultiplier3*atr3[0]

//-------------------------
//Entry and exit conditions
//-------------------------

ConditionEntryL = emaslopeF > emaslopeS
ConditionEntryS = emaslopeF < emaslopeS

//Entry conditions
if time > start and time < end
    if ConditionEntryL
        strategy.entry('Long', direction=strategy.long)

if time > start and time < end
    if ConditionEntryS
        strategy.entry('Short', direction=strategy.short)


//------------------------------
//get ATR values on entry for TP
//------------------------------

//get ATR values on entry for 1st TP
var float fixedBottomline = 0
var float fixedUpline = 0

if nz(ConditionEntryL[1]) and strategy.position_size > 0 and strategy.position_size[1] <= 0
    fixedUpline := upline

if nz(ConditionEntryS[1]) and strategy.position_size < 0 and strategy.position_size[1] >= 0
    fixedBottomline := bottomline

//get ATR values on entry for 2nd TP
var float fixedBottomline2 = 0
var float fixedUpline2 = 0

if nz(ConditionEntryL[1]) and strategy.position_size > 0 and strategy.position_size[1] <= 0
    fixedUpline2 := upline2

if nz(ConditionEntryS[1]) and strategy.position_size < 0 and strategy.position_size[1] >= 0
    fixedBottomline2 := bottomline2

//get ATR values on entry for SL after 2 TP
var float fixedBottomline3 = 0
var float fixedUpline3 = 0

if nz(ConditionEntryL[1]) and strategy.position_size > 0 and strategy.position_size[1] <= 0
    fixedBottomline3 := bottomline3

if nz(ConditionEntryS[1]) and strategy.position_size < 0 and strategy.position_size[1] >= 0
    fixedUpline3 := upline3

//-------------
//状态变量:跟踪止盈执行情况
//-------------
var bool isFirstTPHitLong = false
var bool isSecondTPHitLong = false
var bool isFirstTPHitShort = false
var bool isSecondTPHitShort = false

// 重置状态变量(新仓位建立时)
if strategy.position_size > 0 and strategy.position_size[1] <= 0
    isFirstTPHitLong := false
    isSecondTPHitLong := false

if strategy.position_size < 0 and strategy.position_size[1] >= 0
    isFirstTPHitShort := false
    isSecondTPHitShort := false

// 检测第一层、第二层止盈是否成交
if strategy.position_size > 0
    // 第一层止盈成交:持仓量减少40%
    if strategy.position_size < strategy.position_size[1] * 0.6
        isFirstTPHitLong := true
    // 第二层止盈成交:持仓量减少至初始的40%
    if strategy.position_size <= strategy.position_size[1] * 0.4
        isSecondTPHitLong := true

if strategy.position_size < 0
    // 第一层止盈成交:持仓量绝对值减少40%
    if strategy.position_size > strategy.position_size[1] * 0.6
        isFirstTPHitShort := true
    // 第二层止盈成交:持仓量绝对值减少至初始的40%
    if strategy.position_size >= strategy.position_size[1] * 0.4
        isSecondTPHitShort := true

//-------------
//Exit Strategy
//-------------

if time > start and time < end
    if strategy.position_size > 0
        // 发送第一层、第二层止盈指令
        strategy.exit("Long ATR 40% Take Profit", disable_alert=true, limit=fixedUpline, qty_percent=percentTPATR)
        strategy.exit("Long ATR 20% Take Profit", disable_alert=true, limit=fixedUpline2, qty_percent=percent2TPATR)
        
        // 第二层止盈成交后,设置剩余仓位的止损
        if isSecondTPHitLong
            strategy.exit("Long SL", from_entry="Long", stop=fixedBottomline3, qty_percent=100)

    if strategy.position_size < 0
        // 发送第一层、第二层止盈指令
        strategy.exit("Short ATR 40% Take Profit", disable
相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.18 23:52:49