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
问题原因与修复方案
核心问题
- 止盈触发判断逻辑错误:仅用
high >= fixedUpline2判断第二层止盈是否触发,会在价格触碰止盈线的每一根K线重复发送止损指令,导致策略内部指令冲突,时而失效。 - 止损方向逻辑错误:多单止损误用了上方ATR线
fixedUpline3,空单止损误用了下方ATR线fixedBottomline3,完全违背了止损的方向逻辑。 - 无状态跟踪:未跟踪第二层止盈是否实际成交,仅靠价格触碰会出现误触发。
修复步骤
- 添加状态变量:定义布尔变量跟踪第一层、第二层止盈的成交状态,避免重复发送指令。
- 基于持仓变化判断成交:通过持仓量的变化确认止盈是否实际执行,而非仅依赖价格触碰。
- 修正止损方向:多单止损使用下方ATR线
fixedBottomline3,空单止损使用上方ATR线fixedUpline3。 - 优化指令发送逻辑:仅在第二层止盈成交后,为剩余仓位设置止损。
修改后的完整代码
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
相关产品推荐
相关产品推荐

