基于EMA的动量策略中止损与止盈执行异常问题求助
基于EMA的动量策略止损止盈异常排查
我编写的基于EMA的动量策略脚本整体运行正常,但止损(Stop Loss)与止盈(Take Profit)退出订单存在异常——开启止损功能后,止盈指令完全无法执行。我确定这和退出条件的评估顺序有关,但多次调整结构都没能解决,麻烦帮忙排查。
原脚本如下:
//@version=5 strategy("Momentum Wolf v3", overlay=true) // Input parameters length = input.int(14, title="Length") adxSmoothing = input.int(14, title="ADX Smoothing") threshold = input.float(25, title="Threshold") stopLossEnabled = input.bool(true, title="Enable Stop Loss") takeProfitEnabled = input.bool(true, title="Enable Take Profit") stopLossValue = input.float(50, title="Stop Loss Value", minval=0, step=0.1) takeProfitValue = input.float(100, title="Take Profit Value", minval=0, step=0.1) allowedSessionStartHour = input.int(9, title="Allowed Trading Session Start Hour", minval=0, maxval=23) allowedSessionStartMinute = input.int(30, title="Allowed Trading Session Start Minute", minval=0, maxval=59) allowedSessionEndHour = input.int(16, title="Allowed Trading Session End Hour", minval=0, maxval=23) allowedSessionEndMinute = input.int(0, title="Allowed Trading Session End Minute", minval=0, maxval=59) onlyPositiveDMI = input.bool(true, title="Take Longs only when DMI is Positive") onlyNegativeDMI = input.bool(true, title="Take Shorts only when DMI is Negative") // Custom function to check if the current time is within the allowed trading session isAllowedSession() => currentHour = hour(time) currentMinute = minute(time) sessionStartHour = allowedSessionStartHour sessionStartMinute = allowedSessionStartMinute sessionEndHour = allowedSessionEndHour sessionEndMinute = allowedSessionEndMinute // Check if the current time is within the allowed session (currentHour > sessionStartHour or (currentHour == sessionStartHour and currentMinute >= sessionStartMinute)) and (currentHour < sessionEndHour or (currentHour == sessionEndHour and currentMinute <= sessionEndMinute)) // Custom function to check if the current time is beyond the allowed session end time isSessionEnded() => currentHour = hour(time) currentMinute = minute(time) sessionEndHour = allowedSessionEndHour sessionEndMinute = allowedSessionEndMinute // Check if the current time is beyond the allowed session end time currentHour > sessionEndHour or (currentHour == sessionEndHour and currentMinute > sessionEndMinute) // Calculate moving averages baseMA = ta.ema(close, 3) fastMA = ta.ema(close, 20) mediumMA = ta.ema(close, 100) slowMA = ta.ema(close, 200) // Calculate DMI components [di_plus, di_minus, adx] = ta.dmi(length, adxSmoothing) // Detect crossover and crossunder crossover = ta.crossover(di_plus, di_minus) crossunder = ta.crossunder(di_plus, di_minus) // Enter long positions if ta.crossover(baseMA, fastMA) and fastMA > mediumMA and fastMA > slowMA and (onlyPositiveDMI ? di_plus > di_minus : true) and isAllowedSession() strategy.entry("buy 1", strategy.long) strategy.close("sell 1") strategy.close("sell 2") strategy.close("sell 3") if ta.crossover(baseMA, mediumMA) and mediumMA > fastMA and mediumMA > slowMA and (onlyPositiveDMI ? di_plus > di_minus : true) and isAllowedSession() strategy.entry("buy 2", strategy.long) strategy.close("sell 1") strategy.close("sell 2") strategy.close("sell 3") if ta.crossover(baseMA, slowMA) and slowMA > fastMA and slowMA > mediumMA and (onlyPositiveDMI ? di_plus > di_minus : true) and isAllowedSession() strategy.entry("buy 3", strategy.long) strategy.close("sell 1") strategy.close("sell 2") strategy.close("sell 3") // Enter short positions if ta.crossunder(baseMA, fastMA) and fastMA < mediumMA and fastMA < slowMA and (onlyNegativeDMI ? di_minus > di_plus : true) and isAllowedSession() strategy.entry("sell 1", strategy.short) strategy.close("buy 1") strategy.close("buy 2") strategy.close("buy 3") if ta.crossunder(baseMA, mediumMA) and mediumMA < fastMA and mediumMA < slowMA and (onlyNegativeDMI ? di_minus > di_plus : true) and isAllowedSession() strategy.entry("sell 2", strategy.short) strategy.close("buy 1") strategy.close("buy 2") strategy.close("buy 3") if ta.crossunder(baseMA, slowMA) and slowMA < fastMA and slowMA < mediumMA and (onlyNegativeDMI ? di_minus > di_plus : true) and isAllowedSession() strategy.entry("sell 3", strategy.short) strategy.close("buy 1") strategy.close("buy 2") strategy.close("buy 3") // Exit orders if ta.crossunder(di_plus, di_minus) or isSessionEnded() strategy.close("buy 1") strategy.close("buy 2") strategy.close("buy 3") if stopLossEnabled strategy.exit("sell SL", "sell 1", loss=stopLossValue) strategy.exit("sell SL", "sell 2", loss=stopLossValue) strategy.exit("sell SL", "sell 3", loss=stopLossValue) if takeProfitEnabled strategy.exit("sell TP", "sell 1", profit=takeProfitValue) strategy.exit("sell TP", "sell 2", profit=takeProfitValue) strategy.exit("sell TP", "sell 3", profit=takeProfitValue) if ta.crossover(di_plus, di_minus) or isSessionEnded() strategy.close("sell 1") strategy.close("sell 2") strategy.close("sell 3") if stopLossEnabled strategy.exit("buy SL", "buy 1", loss=stopLossValue) strategy.exit("buy SL", "buy 2", loss=stopLossValue) strategy.exit("buy SL", "buy 3", loss=stopLossValue) if takeProfitEnabled strategy.exit("buy TP", "buy 1", profit=takeProfitValue) strategy.exit("buy TP", "buy 2", profit=takeProfitValue) strategy.exit("buy TP", "buy 3", profit=takeProfitValue) // Plot moving averages plot(baseMA, "Base MA", color.white) plot(fastMA, "Fast MA", color.yellow) plot(mediumMA, "Medium MA", color.orange) plot(slowMA, "Slow MA", color.red) // Plot signals plotshape(strategy.opentrades != 0 ? crossover : na, style=shape.triangleup, location=location.belowbar, color=color.green, size=size.small, title="DMI Crossover") plotshape(strategy.opentrades != 0 ? crossunder : na, style=shape.triangledown, location=location.abovebar, color=color.red, size=size.small, title="DMI Crossunder")
问题根源
- 重复退出指令互相覆盖:对同一个持仓(如
sell 1)分别调用了strategy.exit("sell SL")和strategy.exit("sell TP"),Pine Script中后执行的strategy.exit会覆盖之前的设置,导致同一时间只有止损或止盈其中一个生效。 - 退出逻辑顺序混乱:先处理DMI/收盘平仓条件,再设置止损止盈,若DMI条件触发,持仓会被提前平仓,止损止盈指令根本没有生效的机会。
修复方案
- 对同一个持仓,将止损和止盈合并到同一个
strategy.exit调用中,通过profit和loss参数同时配置 - 调整逻辑顺序:先设置所有持仓的止损止盈,再处理DMI/收盘平仓条件,确保止损止盈在持仓存续期间始终有效
- 为每个持仓的退出指令设置唯一ID,避免不同持仓的退出逻辑互相干扰
修改后的完整脚本
//@version=5 strategy("Momentum Wolf v3 Fixed", overlay=true) // Input parameters length = input.int(14, title="Length") adxSmoothing = input.int(14, title="ADX Smoothing") threshold = input.float(25, title="Threshold") stopLossEnabled = input.bool(true, title="Enable Stop Loss") takeProfitEnabled = input.bool(true, title="Enable Take Profit") stopLossValue = input.float(50, title="Stop Loss Value", minval=0, step=0.1) takeProfitValue = input.float(100, title="Take Profit Value", minval=0, step=0.1) allowedSessionStartHour = input.int(9, title="Allowed Trading Session Start Hour", minval=0, maxval=23) allowedSessionStartMinute = input.int(30, title="Allowed Trading Session Start Minute", minval=0, maxval=59) allowedSessionEndHour = input.int(16, title="Allowed Trading Session End Hour", minval=0, maxval=23) allowedSessionEndMinute = input.int(0, title="Allowed Trading Session End Minute", minval=0, maxval=59) onlyPositiveDMI = input.bool(true, title="Take Longs only when DMI is Positive") onlyNegativeDMI = input.bool(true, title="Take Shorts only when DMI is Negative") // Custom function to check if the current time is within the allowed trading session isAllowedSession() => currentHour = hour(time) currentMinute = minute(time) sessionStartHour = allowedSessionStartHour sessionStartMinute = allowedSessionStartMinute sessionEndHour = allowedSessionEndHour sessionEndMinute = allowedSessionEndMinute // Check if the current time is within the allowed session (currentHour > sessionStartHour or (currentHour == sessionStartHour and currentMinute >= sessionStartMinute)) and (currentHour < sessionEndHour or (currentHour == sessionEndHour and currentMinute <= sessionEndMinute)) // Custom function to check if the current time is beyond the allowed session end time isSessionEnded() => currentHour = hour(time) currentMinute = minute(time) sessionEndHour = allowedSessionEndHour sessionEndMinute = allowedSessionEndMinute // Check if the current time is beyond the allowed session end time currentHour > sessionEndHour or (currentHour == sessionEndHour and currentMinute > sessionEndMinute) // Calculate moving averages baseMA = ta.ema(close, 3) fastMA = ta.ema(close, 20) mediumMA = ta.ema(close, 100) slowMA = ta.ema(close, 200) // Calculate DMI components [di_plus, di_minus, adx] = ta.dmi(length, adxSmoothing) // Detect crossover and crossunder crossover = ta.crossover(di_plus, di_minus) crossunder = ta.crossunder(di_plus, di_minus) // Enter long positions if ta.crossover(baseMA, fastMA) and fastMA > mediumMA and fastMA > slowMA and (onlyPositiveDMI ? di_plus > di_minus : true) and isAllowedSession() strategy.entry("buy 1", strategy.long) strategy.close("sell 1") strategy.close("sell 2") strategy.close("sell 3") if ta.crossover(baseMA, mediumMA) and mediumMA > fastMA and mediumMA > slowMA and (onlyPositiveDMI ? di_plus > di_minus : true) and isAllowedSession() strategy.entry("buy 2", strategy.long) strategy.close("sell 1") strategy.close("sell 2") strategy.close("sell 3") if ta.crossover(baseMA, slowMA) and slowMA > fastMA and slowMA > mediumMA and (onlyPositiveDMI ? di_plus > di_minus : true) and isAllowedSession() strategy.entry("buy 3", strategy.long) strategy.close("sell 1") strategy.close("sell 2") strategy.close("sell 3") // Enter short positions if ta.crossunder(baseMA, fastMA) and fastMA < mediumMA and fastMA < slowMA and (onlyNegativeDMI ? di_minus > di_plus : true) and isAllowedSession() strategy.entry("sell 1", strategy.short) strategy.close("buy 1") strategy.close("buy 2") strategy.close("buy 3") if ta.crossunder(baseMA, mediumMA) and mediumMA < fastMA and mediumMA < slowMA and (onlyNegativeDMI ? di_minus > di_plus : true) and isAllowedSession() strategy.entry("sell 2", strategy.short) strategy.close("buy 1") strategy.close("buy 2") strategy.close("buy 3") if ta.crossunder(baseMA, slowMA) and slowMA < fastMA and slowMA < mediumMA and (onlyNegativeDMI ? di_minus > di_plus : true) and isAllowedSession() strategy.entry("sell 3", strategy.short) strategy.close("buy 1") strategy.close("buy 2") strategy.close("buy 3") // --- 修复后的退出逻辑:先设置止损止盈,再处理其他平仓条件 --- // 处理多头持仓的止损止盈 if stopLossEnabled or takeProfitEnabled strategy.exit("buy Exit 1", "buy 1", loss=stopLossEnabled ? stopLossValue : na, profit=takeProfitEnabled ? takeProfitValue : na) strategy.exit("buy Exit 2", "buy 2", loss=stopLossEnabled ? stopLossValue : na, profit=takeProfitEnabled ? takeProfitValue : na) strategy.exit("buy Exit 3", "buy 3", loss=stopLossEnabled ? stopLossValue : na, profit=takeProfitEnabled ? takeProfitValue : na) // 处理空头持仓的止损止盈 if stopLossEnabled or takeProfitEnabled strategy.exit("sell Exit 1", "sell 1", loss=stopLossEnabled ? stopLossValue : na, profit=takeProfitEnabled ? takeProfitValue : na) strategy.exit("sell Exit 2", "sell 2", loss=stopLossEnabled ? stopLossValue : na, profit=takeProfitEnabled ? takeProfitValue : na) strategy.exit("sell Exit 3", "sell 3", loss=stopLossEnabled ? stopLossValue : na, profit=takeProfitEnabled ? takeProfitValue : na) // 处理DMI或收盘平仓条件 if ta.crossunder(di_plus, di_minus) or isSessionEnded() strategy.close("buy 1") strategy.close("buy 2") strategy.close("buy 3") if ta.crossover(di_plus, di_minus) or isSessionEnded() strategy.close("sell 1") strategy.close("sell 2") strategy.close("sell 3") // Plot moving averages plot(baseMA, "Base MA", color.white) plot(fastMA, "Fast MA", color.yellow) plot(mediumMA, "Medium MA", color.orange) plot(slowMA, "Slow MA", color.red) // Plot signals plotshape(strategy.opentrades != 0 ? crossover : na, style=shape.triangleup, location=location.belowbar, color=color.green, size=size.small, title="DMI Crossover") plotshape(strategy.opentrades != 0 ? crossunder : na, style=shape.triangledown, location=location.abovebar, color=color.red, size=size.small, title="DMI Crossunder")
关键修改点说明
- 合并止损止盈配置:对每个持仓用同一个
strategy.exit调用,通过三元运算符判断是否启用止损/止盈,避免重复设置导致覆盖。 - 调整逻辑执行顺序:先设置止损止盈,再处理DMI/收盘平仓,确保止损止盈指令在持仓存续期间始终有效。
- 唯一退出ID:为每个持仓的退出指令设置独立ID,避免不同持仓的退出逻辑互相干扰。
内容的提问来源于stack exchange,提问作者Nicholas Wirth
相关产品推荐
相关产品推荐

