PineScript策略止损失效与RSI信号异常技术求助
TradingView策略开发问题与代码求助
核心问题
- 止损机制完全失效,多次调整仍无效果
- RSI的K、D线在超买/超卖区间交叉时,信号偶尔不触发(疑似发生在K线收盘价交叉场景)
- 开发目标:搭建可复用的策略框架,而非追求完美策略,接触TradingView技术不足一周
当前代码
//@version=5 //optimized for BTCUSDT 30min TF strategy("Chriscross_v5", overlay=true, precision=2, pyramiding=0, calc_on_every_tick=false, calc_on_order_fills=true, default_qty_type=strategy.percent_of_equity, default_qty_value=100, initial_capital=10000, commission_type=strategy.commission.percent, commission_value=0.015 ) // RSI smoothK = input.int(3, "K", minval=1, group = "RSI Parameters") smoothD = input.int(3, "D", minval=1) lengthRSI = input.int(14, "RSI Length", minval=1) lengthStoch = input.int(14, "Stochastic Length", minval=1) src = input(close, title="RSI Source") rsi1 = ta.rsi(src, lengthRSI) k = ta.sma(ta.stoch(rsi1, rsi1, rsi1, lengthStoch), smoothK) d = ta.sma(k, smoothD) rsi_index = input.int(21, 'RSI Index', 0, 100) rsi_ob = k > 100 - rsi_index and d > 100 - rsi_index rsi_os = k < rsi_index and d < rsi_index rsi_crossdown = ta.crossunder(k, d) rsi_crossup = ta.crossover(k, d) // Parameters tp1 = input.float(title=' Take profit1 %', defval=1.9, minval=0.01) tp2 = input.float(title=' Take profit2 %', defval=3, minval=0.01) q1 = input.int(title=' TP1 Quantity %', defval=100, minval=1) q2 = input.int(title=' TP2 Quantity %', defval=50, minval=1) sl = input.float(title=' stop loss %', defval=3, minval=0.01) ep = strategy.opentrades.entry_price(0) // Functions per(pcnt) => strategy.position_size != 0 ? math.round(pcnt / 100.0 * strategy.position_avg_price / syminfo.mintick) : float(na) //percent as points //perc(pcnt) => // strategy.position_size != 0 ? (pcnt / 100.0 + 1.0) * strategy.position_avg_price : float(na) //percent as price // Colors colorRed = #FF2052 colorGreen = #66FF00 // Date Range Filter useDateFilter = input.bool(true, title="Filter Date Range of Backtest", group="Backtest Time Period") backtestStartDate = input.time(timestamp("1 Jan 2022"), title="Start Date", group="Backtest Time Period") backtestEndDate = input.time(timestamp("1 Jan 2025"), title="End Date", group="Backtest Time Period") inTradeWindow = not useDateFilter or (time >= backtestStartDate and time < backtestEndDate) // ORDERS // Check if strategy has open positions inLong = strategy.position_size > 0 inShort = strategy.position_size < 0 // Check if strategy reduced position size in last bar longClose = strategy.position_size < strategy.position_size[1] shortClose = strategy.position_size > strategy.position_size[1] // Entry Conditions longCondition = rsi_os and rsi_crossup shortCondition = rsi_ob and rsi_crossdown // Exit Conditions current_position_size = math.abs(strategy.position_size) initial_position_size = math.abs(ta.valuewhen(strategy.position_size[1] == 0.0, strategy.position_size, 0)) longTP1 = strategy.position_avg_price + per(tp1) * syminfo.mintick * strategy.position_size / math.abs(strategy.position_size) longTP2 = strategy.position_avg_price + per(tp2) * syminfo.mintick * strategy.position_size / math.abs(strategy.position_size) shortTP1 = strategy.position_avg_price - per(tp1) * syminfo.mintick * strategy.position_size / math.abs(strategy.position_size) shortTP2 = strategy.position_avg_price - per(tp2) * syminfo.mintick * strategy.position_size / math.abs(strategy.position_size) // Calculate Stop Loss // Initialise variables var float longSL = 0.0 var float shortSL = 0.0 // When in a position, check to see if the position was reduced on the last bar // If it was, set stop loss to position entry price. Otherwise, maintain last stop loss value // When not in position, set stop loss using close price? longSL := if inLong and ta.barssince(longClose) < ta.barssince(longCondition) strategy.position_avg_price - per(sl) * syminfo.mintick * strategy.position_size / math.abs(strategy.position_size) else if inLong longSL[1] //else // close - longSL shortSL := if inShort and ta.barssince(shortClose) < ta.barssince(shortCondition) strategy.position_avg_price + per(sl) * syminfo.mintick * strategy.position_size / math.abs(strategy.position_size) else if inShort shortSL[1] //else // close - shortSL // Manage positions if longCondition and inTradeWindow strategy.close("Short", comment="Exit Short") strategy.close("Long", comment="Exit Long") strategy.entry("Long", strategy.long, comment="Enter Long") if strategy.position_size > 0 strategy.exit('TP1', from_entry='Long', qty_percent=q1, limit=longTP1, stop=longSL) strategy.exit('TP2', from_entry='Long', qty_percent=q1, limit=longTP1, stop=longSL) if shortCondition and inTradeWindow strategy.close("Long", comment="Exit Long") strategy.close("Short", comment="Exit Short") strategy.entry("Short", strategy.short, comment="Enter Short") if strategy.position_size > 0 strategy.exit('TP1', from_entry='Short', qty_percent=q1, limit=shortTP1, stop=shortSL) strategy.exit('TP2', from_entry='Short', qty_percent=q1, limit=shortTP1, stop=shortSL) //draw plot(strategy.position_size >= 0 ? na : ep, color=color.new(#ffffff, 0), style=plot.style_linebr) plot(strategy.position_size <= 0 ? na : ep, color=color.new(#ffffff, 0), style=plot.style_linebr) //indicator("Chriscross_B", overlay=false, precision=2) // ////Levels //bandno0 = (100 - rsi_index) //bandno2 = (50) //bandno1 = (rsi_index) // //h0 = hline(bandno0, 'Upper Band', color=#606060) //h2 = hline(bandno2, 'Middle Band', color=#606060) //h1 = hline(bandno1, 'Lower Band', color=#606060) //fill(h0, h1, color=color.new(#9915FF, 80), title='Background') // //hline(100, "Max", color.red, hline.style_solid) //hline(0, "Min", color.red, hline.style_solid) // // Stochastic Chart //plot(k, 'K', color=color.new(#0094FF, 0), linewidth=2) //plot(d, 'D', color=color.new(#FF6A00, 0), linewidth=1) // // Circles //stOBOS = input.bool(true) //plot(stOBOS ? rsi_crossdown and k >= bandno0 ? d : na : rsi_crossdown ? d : na, color=colorRed, style=plot.style_circles, linewidth=3) //plot(stOBOS ? rsi_crossup and k <= bandno1 ? d : na : rsi_crossup ? k : na, color=colorGreen, style=plot.style_circles, linewidth=3)
之前可正常运行的简化版本代码
// Entry and Exit buy = stoch_rsi_os and ta.crossover(k, d) sell = stoch_rsi_ob and ta.crossunder(k, d) if buy and inTradeWindow strategy.close("Sell", comment="Exit Short") strategy.entry("Buy", strategy.long, comment="Enter Long") if sell and inTradeWindow strategy.close("Buy", comment="Exit Long") strategy.entry("Sell", strategy.short, comment="Enter Short") strategy.exit('x1', qty_percent=q1, profit=per(tp1), loss=per(los)) strategy.exit('x2', qty_percent=q2, profit=per(tp2), loss=0) // Moves SL to entry after TP1? Not sure if this is right.
技术建议
一、止损失效问题修复
核心错误点:
- 原代码中
longSL/shortSL的赋值逻辑混乱,ta.barssince(longClose) < ta.barssince(longCondition)的时间差判断无实际意义,导致止损价格无法正确初始化 - 空头仓位的订单判断条件错误:
strategy.position_size > 0只会匹配多头仓位,空头仓位应使用strategy.position_size < 0 - 重复调用
strategy.exit且参数完全一致,会导致订单逻辑冲突
- 原代码中
修复方案:
- 移除复杂的
longSL/shortSL变量,直接在strategy.exit中动态计算止损价格 - 修正空头仓位的判断条件,确保止盈止损订单正确绑定到对应仓位
- 简化止损计算逻辑,基于开仓均价直接计算:
- 移除复杂的
// 替换原止损计算与订单逻辑 // 多头订单管理 if longCondition and inTradeWindow strategy.close("Short", comment="Exit Short") strategy.entry("Long", strategy.long, comment="Enter Long") if inLong long_stop = strategy.position_avg_price - (sl / 100) * strategy.position_avg_price long_tp1 = strategy.position_avg_price + (tp1 / 100) * strategy.position_avg_price long_tp2 = strategy.position_avg_price + (tp2 / 100) * strategy.position_avg_price strategy.exit('Long_TP1', from_entry='Long', qty_percent=q1, limit=long_tp1, stop=long_stop) strategy.exit('Long_TP2', from_entry='Long', qty_percent=q2, limit=long_tp2, stop=long_stop) // 空头订单管理 if shortCondition and inTradeWindow strategy.close("Long", comment="Exit Long") strategy.entry("Short", strategy.short, comment="Enter Short") if inShort short_stop = strategy.position_avg_price + (sl / 100) * strategy.position_avg_price short_tp1 = strategy.position_avg_price - (tp1 / 100) * strategy.position_avg_price short_tp2 = strategy.position_avg_price - (tp2 / 100) * strategy.position_avg_price strategy.exit('Short_TP1', from_entry='Short', qty_percent=q1, limit=short_tp1, stop=short_stop) strategy.exit('Short_TP2', from_entry='Short', qty_percent=q2, limit=short_tp2, stop=short_stop)
二、RSI交叉信号不触发问题解决
原因分析:
ta.crossover/ta.crossunder默认基于bar收盘价判断交叉,若交叉发生在bar内部(非收盘价),且calc_on_every_tick=false,则信号会丢失- 原代码中
rsi_ob/rsi_os要求K、D线同时处于超买/超卖区间,若交叉发生时其中一条线刚好离开区间,会导致信号不触发
解决方案:
- 若需要捕捉bar内交叉信号,将策略参数
calc_on_every_tick设为true(注意会增加计算量) - 调整交叉信号判断逻辑,允许交叉发生在超买/超卖区间边缘:
// 修正后的信号条件 longCondition = (rsi_os or k[1] < rsi_index) and rsi_crossup shortCondition = (rsi_ob or k[1] > 100 - rsi_index) and rsi_crossdown - 或使用
ta.crossover(k, d) or ta.crossover(k[1], d[1])覆盖相邻bar的交叉场景
- 若需要捕捉bar内交叉信号,将策略参数
三、可复用框架优化
- 指标模块化:将StochRSI计算封装为函数,便于后续策略复用:
getStochRSI(lengthRSI, lengthStoch, smoothK, smoothD, src) => rsiVal = ta.rsi(src, lengthRSI) kVal = ta.sma(ta.stoch(rsiVal, rsiVal, rsiVal, lengthStoch), smoothK) dVal = ta.sma(kVal, smoothD) [kVal, dVal] - 订单管理模块化:将开仓、止盈止损逻辑拆分为独立函数,降低代码耦合度
- 参数统一管理:将所有策略参数集中到一个输入组,便于维护
内容的提问来源于stack exchange,提问作者Crazyhorse
相关产品推荐
相关产品推荐

