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

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.

技术建议

一、止损失效问题修复

  1. 核心错误点:

    • 原代码中longSL/shortSL的赋值逻辑混乱,ta.barssince(longClose) < ta.barssince(longCondition)的时间差判断无实际意义,导致止损价格无法正确初始化
    • 空头仓位的订单判断条件错误:strategy.position_size > 0只会匹配多头仓位,空头仓位应使用strategy.position_size < 0
    • 重复调用strategy.exit且参数完全一致,会导致订单逻辑冲突
  2. 修复方案:

    • 移除复杂的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交叉信号不触发问题解决

  1. 原因分析:

    • ta.crossover/ta.crossunder默认基于bar收盘价判断交叉,若交叉发生在bar内部(非收盘价),且calc_on_every_tick=false,则信号会丢失
    • 原代码中rsi_ob/rsi_os要求K、D线同时处于超买/超卖区间,若交叉发生时其中一条线刚好离开区间,会导致信号不触发
  2. 解决方案:

    • 若需要捕捉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的交叉场景

三、可复用框架优化

  • 指标模块化:将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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.30 00:27:07