Pine Script v4转v5时strategy.exit参数错误排查求助
Pine Script v4转v5策略时strategy.exit参数错误的原因及解决
strategy.exit must have at least one of the following parameters: 'profit', 'limit', 'loss', 'stop' or one of the following pairs: 'trail_offset' and 'trail_price' / 'trail_points'. To close the position at market price, use 'strategy.close'
将Pine Script v4策略转换为v5版本时触发上述错误,代码中已设置stop和trail_points参数,且v4版本可正常运行,经排查未解决,现明确错误原因及修复方案。
原v4代码
// This source code is subject to the terms of the Mozilla Public License 2.0 at https://mozilla.org/MPL/2.0/ // © IndyDave //@version=4 strategy("SMA-EMA+BB+OBV", overlay=true, initial_capital = 10000, currency = currency.USD, slippage = 12) // Input parameters smaLength = input(14, title="SMA Length", type=input.integer) emaLength = input(31, title="EMA Length", type=input.integer) bbLength = input(6, title="Bollinger Bands Length", type=input.integer) bbStdDev = input(2, title="Bollinger Bands Standard Deviation", type=input.float, step = .25) obvLength = input(9, title="OBV Length", type=input.integer) obvThreshold = input(0.1, title="OBV Threshold", type=input.float) bbwThreshold = input(.1, title="BBW Threshold", type=input.float, step = .05) trailStop = input(14, title="Trailing Stop Loss Percentage", type=input.float, step = .5) / 100 stopLoss = input(2.7, title="Stop Loss Percentage", type=input.float, step = .05) / 100 maLen = input(100,'MA Filter Length',type = input.integer, step = 25) // Custom Position Size Inputs positionSizeType = input("Percentage of Equity", title="Position Size Type", options=["Risk of Equity Percentage", "Percentage of Equity", "Set Dollar Size", "Set Contract Size"]) riskEquityPercentage = input(1, title="Risk of Equity Percentage", type=input.float, step = .05) / 100 percentageOfEquity = input(10, title="Percentage of Equity", type=input.float) / 100 flatDollarSize = input(1000, title="Set Dollar Size", type=input.float) flatContractSize = input(1, title="Set Contract Size", type=input.integer) leverageAmount = input(1, title="Leverage Amount", type=input.integer) limitPositionSize = input(false,'Limit Position Size', type = input.bool,tooltip = 'Limit position size to strategy equity * leverage amount') limitExchangeMax = input(false,'Limit Exchange Maximum',type = input.bool,tooltip = 'Limit position size to a certain amount set by the exchange') exchangeMaxSize = input(0,'Exchange Max Contract Size', type = input.integer) //Alerts longEntryAlert = input(title="Long Entry Alert Message", type=input.string, defval="Take Profit: {{longTP}}\nStop Loss: {{longSL}}\nQty: {{qty}}") longExitAlert = input(title="Long Exit Alert Message", type=input.string, defval="Long Exit") shortEntryAlert = input(title="Short Entry Alert Message", type=input.string, defval="Take Profit: {{shortTP}}\nStop Loss: {{shortSL}}\nQty: {{qty}}") shortExitAlert = input(title="Short Exit Alert Message", type=input.string, defval="Short Exit") // Position Size Calculation capital = strategy.equity * leverageAmount var float customPositionSize = na if positionSizeType == "Risk of Equity Percentage" customPositionSize := capital * riskEquityPercentage / (close * stopLoss) else if positionSizeType == "Percentage of Equity" customPositionSize := capital * percentageOfEquity / close else if positionSizeType == "Set Dollar Size" customPositionSize := flatDollarSize / close * leverageAmount else customPositionSize := flatContractSize if limitPositionSize and customPositionSize > (strategy.equity * leverageAmount) / close customPositionSize := (strategy.equity * leverageAmount) / close if limitExchangeMax and customPositionSize > exchangeMaxSize customPositionSize := exchangeMaxSize // Calculations smaLine = sma(close, smaLength) emaLine = ema(close, emaLength) [upperBand, middleBand, lowerBand] = bb(close, bbLength, bbStdDev) bandWidth = upperBand - lowerBand // On-Balance Volume (OBV) Calculation custom_obv = cum(change(close) > 0 ? volume : change(close) < 0 ? - volume : 0) //changeClose = change(close) //custom_obv = cum(changeClose > 0 ? volume : changeClose < 0 ? - volume : 0) obvSlope = (custom_obv - nz(custom_obv[obvLength])) / obvLength // Bollinger BandWidth (BBW) Calculation bbw = (upperBand - lowerBand) / middleBand * 100 // Entry conditions maFilter = sma(close,maLen) enterLong = crossover(smaLine, emaLine) and bbw > bbwThreshold and obvSlope > obvThreshold and close > maFilter enterShort = crossunder(smaLine, emaLine) and bbw > bbwThreshold and obvSlope < -obvThreshold and close < maFilter // Trailing stop loss and stop loss var float longSL = na var float shortSL = na var float longTP = na var float shortTP = na if strategy.position_size <= 0 longSL := close * (1 - stopLoss) if strategy.position_size >= 0 shortSL := close * (1 + stopLoss) // Define the alert messages alert_messageLongEntry = str.replace_all(longEntryAlert, " {{longTP}}", tostring(longTP)) alert_messageLongEntry := str.replace_all(alert_messageLongEntry, " {{longSL}}", tostring(longSL)) alert_messageLongEntry := str.replace_all(alert_messageLongEntry, " {{qty}}", tostring(customPositionSize)) alert_messageLongEntry := str.replace_all(alert_messageLongEntry, " {{leverage}}", tostring(leverageAmount)) alert_messageShortEntry = str.replace_all(shortEntryAlert, " {{shortTP}}", tostring(shortTP)) alert_messageShortEntry := str.replace_all(shortEntryAlert, " {{shortSL}}", tostring(shortSL)) alert_messageShortEntry := str.replace_all(shortEntryAlert, " {{qty}}", tostring(customPositionSize)) alert_messageShortEntry := str.replace_all(shortEntryAlert, " {{leverage}}", tostring(leverageAmount)) // Execute trades if enterLong strategy.entry("Long", strategy.long, qty=customPositionSize, alert_message = alert_messageLongEntry) if enterShort strategy.entry("Short", strategy.short, qty=customPositionSize, alert_message = alert_messageShortEntry) if strategy.position_size > 0 strategy.exit("Exit Long", "Long", stop = longSL, trail_points=trailStop * close) if strategy.position_size < 0 strategy.exit("Exit Short", "Short", stop = shortSL, trail_points=trailStop * close)
错误原因
- 参数有效性检查更严格:Pine Script v5要求
strategy.exit必须有至少一个有效参数,若stop参数为na,即使设置了trail_points也会触发错误。原代码中longSL/shortSL在无仓位时赋值,首次开仓时longSL/shortSL仍为na,导致stop参数无效。 - trail_points参数用法错误:
trail_points是固定价格点数,原代码用trailStop * close(百分比计算价格)赋值不符合v5要求,应使用trail_offset实现百分比追踪止损。
解决方法
- 开仓时赋值止损价:将
longSL/shortSL的赋值逻辑移至开仓条件内,确保开仓时止损价已被正确初始化。 - 替换trail_points为trail_offset:使用
trail_offset参数实现百分比追踪止损,符合v5的参数规范。 - 适配v5语法变更:调整strategy初始化参数(如
currency改为strategy.currency,slippage改为strategy.slippage)、变量作用域等细节。
修正后的Pine Script v5代码
// This source code is subject to the terms of the Mozilla Public License 2.0 at https://mozilla.org/MPL/2.0/ // © IndyDave //@version=5 strategy("SMA-EMA+BB+OBV", overlay=true, initial_capital = 10000, currency = strategy.currency.USD, slippage = strategy.slippage.fixed(12)) // Input parameters smaLength = input.int(14, title="SMA Length") emaLength = input.int(31, title="EMA Length") bbLength = input.int(6, title="Bollinger Bands Length") bbStdDev = input.float(2, title="Bollinger Bands Standard Deviation", step = .25) obvLength = input.int(9, title="OBV Length") obvThreshold = input.float(0.1, title="OBV Threshold") bbwThreshold = input.float(.1, title="BBW Threshold", step = .05) trailStop = input.float(14, title="Trailing Stop Loss Percentage", step = .5) / 100 stopLoss = input.float(2.7, title="Stop Loss Percentage", step = .05) / 100 maLen = input.int(100,'MA Filter Length', step = 25) // Custom Position Size Inputs positionSizeType = input.string("Percentage of Equity", title="Position Size Type", options=["Risk of Equity Percentage", "Percentage of Equity", "Set Dollar Size", "Set Contract Size"]) riskEquityPercentage = input.float(1, title="Risk of Equity Percentage", step = .05) / 100 percentageOfEquity = input.float(10, title="Percentage of Equity") / 100 flatDollarSize = input.float(1000, title="Set Dollar Size") flatContractSize = input.int(1, title="Set Contract Size") leverageAmount = input.int(1, title="Leverage Amount") limitPositionSize = input.bool(false,'Limit Position Size',tooltip = 'Limit position size to strategy equity * leverage amount') limitExchangeMax = input.bool(false,'Limit Exchange Maximum',tooltip = 'Limit position size to a certain amount set by the exchange') exchangeMaxSize = input.int(0,'Exchange Max Contract Size') //Alerts longEntryAlert = input.string("Take Profit: {{longTP}}\nStop Loss: {{longSL}}\nQty: {{qty}}", title="Long Entry Alert Message") longExitAlert = input.string("Long Exit", title="Long Exit Alert Message") shortEntryAlert = input.string("Take Profit: {{shortTP}}\nStop Loss: {{shortSL}}\nQty: {{qty}}", title="Short Entry Alert Message") shortExitAlert = input.string("Short Exit", title="Short Exit Alert Message") // Position Size Calculation capital = strategy.equity * leverageAmount var float customPositionSize = na if positionSizeType == "Risk of Equity Percentage" customPositionSize := capital * riskEquityPercentage / (close * stopLoss) else if positionSizeType == "Percentage of Equity" customPositionSize := capital * percentageOfEquity / close else if positionSizeType == "Set Dollar Size" customPositionSize := flatDollarSize / close * leverageAmount else customPositionSize := flatContractSize if limitPositionSize and customPositionSize > (strategy.equity * leverageAmount) / close customPositionSize := (strategy.equity * leverageAmount) / close if limitExchangeMax and customPositionSize > exchangeMaxSize customPositionSize := exchangeMaxSize // Calculations smaLine = ta.sma(close, smaLength) emaLine = ta.ema(close, emaLength) [upperBand, middleBand, lowerBand] = ta.bb(close, bbLength, bbStdDev) bandWidth = upperBand - lowerBand // On-Balance Volume (OBV) Calculation custom_obv = ta.cum(ta.change(close) > 0 ? volume : ta.change(close) < 0 ? - volume : 0) obvSlope = (custom_obv - nz(custom_obv[obvLength])) / obvLength // Bollinger BandWidth (BBW) Calculation bbw = (upperBand - lowerBand) / middleBand * 100 // Entry conditions maFilter = ta.sma(close,maLen) enterLong = ta.crossover(smaLine, emaLine) and bbw > bbwThreshold and obvSlope > obvThreshold and close > maFilter enterShort = ta.crossunder(smaLine, emaLine) and bbw > bbwThreshold and obvSlope < -obvThreshold and close < maFilter // Trailing stop loss and stop loss var float longSL = na var float shortSL = na var float longTP = na var float shortTP = na // Execute trades if enterLong longSL := close * (1 - stopLoss) alert_messageLongEntry = str.replace_all(longEntryAlert, "{{longTP}}", str.tostring(longTP)) alert_messageLongEntry := str.replace_all(alert_messageLongEntry, "{{longSL}}", str.tostring(longSL)) alert_messageLongEntry := str.replace_all(alert_messageLongEntry, "{{qty}}", str.tostring(customPositionSize)) alert_messageLongEntry := str.replace_all(alert_messageLongEntry, "{{leverage}}", str.tostring(leverageAmount)) strategy.entry("Long", strategy.long, qty=customPositionSize, alert_message = alert_messageLongEntry) if enterShort shortSL := close * (1 + stopLoss) alert_messageShortEntry = str.replace_all(shortEntryAlert, "{{shortTP}}", str.tostring(shortTP)) alert_messageShortEntry := str.replace_all(alert_messageShortEntry, "{{shortSL}}", str.tostring(shortSL)) alert_messageShortEntry := str.replace_all(alert_messageShortEntry, "{{qty}}", str.tostring(customPositionSize)) alert_messageShortEntry := str.replace_all(alert_messageShortEntry, "{{leverage}}", str.tostring(leverageAmount)) strategy.entry("Short", strategy.short, qty=customPositionSize, alert_message = alert_messageShortEntry) if strategy.position_size > 0 strategy.exit("Exit Long", "Long", stop = longSL, trail_offset = trailStop * 100) if strategy.position_size < 0 strategy.exit("Exit Short", "Short", stop = shortSL, trail_offset = trailStop * 100)
内容的提问来源于stack exchange,提问作者IndyDave
相关产品推荐
相关产品推荐

