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

基于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")

问题根源

  1. 重复退出指令互相覆盖:对同一个持仓(如sell 1)分别调用了strategy.exit("sell SL")和strategy.exit("sell TP"),Pine Script中后执行的strategy.exit会覆盖之前的设置,导致同一时间只有止损或止盈其中一个生效。
  2. 退出逻辑顺序混乱:先处理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")

关键修改点说明

  1. 合并止损止盈配置:对每个持仓用同一个strategy.exit调用,通过三元运算符判断是否启用止损/止盈,避免重复设置导致覆盖。
  2. 调整逻辑执行顺序:先设置止损止盈,再处理DMI/收盘平仓,确保止损止盈指令在持仓存续期间始终有效。
  3. 唯一退出ID:为每个持仓的退出指令设置独立ID,避免不同持仓的退出逻辑互相干扰。

内容的提问来源于stack exchange,提问作者Nicholas Wirth

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.27 15:04:52