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

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)

错误原因

  1. 参数有效性检查更严格:Pine Script v5要求strategy.exit必须有至少一个有效参数,若stop参数为na,即使设置了trail_points也会触发错误。原代码中longSL/shortSL在无仓位时赋值,首次开仓时longSL/shortSL仍为na,导致stop参数无效。
  2. trail_points参数用法错误:trail_points是固定价格点数,原代码用trailStop * close(百分比计算价格)赋值不符合v5要求,应使用trail_offset实现百分比追踪止损。

解决方法

  1. 开仓时赋值止损价:将longSL/shortSL的赋值逻辑移至开仓条件内,确保开仓时止损价已被正确初始化。
  2. 替换trail_points为trail_offset:使用trail_offset参数实现百分比追踪止损,符合v5的参数规范。
  3. 适配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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.27 12:47:06