Cutlers RSI
// This source code is subject to the terms of the Mozilla Public License 2.0 at https://mozilla.org/MPL/2.0/
// © TanHef

//Cutlers' RSI is a variation of the original RSI Developed by Welles Wilder.
//This variation uses a simple moving average instead of an exponetial average.
//Since a simple moving average is used by this variation, a longer length tends to give better results.

//CALCULATION
//Step1: Calculating the Gains and Losses within the chosen period.
//Step2: Calculating the simple moving averages of gains and losses.
//Step3: Calculating Cutler’s Relative Strength (RS). Calculated using the following:
//   Cutler’s RS = SMA(gains,length) / SMA(losses,length)
//Step 4: Calculating the Cutler’s Relative Strength Index (RSI). Calculated used the following:
//   RSI = 100 — [100/(1 + RS)]

//@version=5
indicator("Cutlers RSI")

//RSI
length = input(14,title="Length",inline="LenSRC",group='RSI Settings',tooltip="It is suggested to use higher lengths rather than lower lengths due to the Simple Moving Average being used in the Cutler's RSI calculation and not the Exponential")
src = input.source(close,title="Source",inline="LenSRC",group='RSI Settings')

//MA
ma(source, length, type) =>
    switch type
        "SMA" => ta.sma(source, length)
        "Bollinger Bands" => ta.sma(source, length)
        "EMA" => ta.ema(source, length)
        "SMMA (RMA)" => ta.rma(source, length)
        "WMA" => ta.wma(source, length)
        "VWMA" => ta.vwma(source, length)
displayMA = input(false,title="Display MA",inline="MADisplay",group="MA Settings")
displayMACrossFill = input(false,title="Fill",inline="MADisplay",group="MA Settings")
maTypeInput = input.string("SMA", title="MA Type", options=["SMA", "Bollinger Bands", "EMA", "SMMA (RMA)", "WMA", "VWMA"], group="MA Settings",inline="MA")
maLengthInput = input.int(14, title="MA Length", group="MA Settings",inline="MA")
bbMultInput = input.float(2.0, minval=0.001, maxval=50, title="BB StdDev", group="MA Settings")

//Signals
tt_TRENDobos = "Trend OB/OS: Uptrend after above Overbought Level. Downtrend after below Oversold Level\n(For the traditional RSI OB=60 and OS=40 is used)"
tt_OBOS = "OB/OS: When above Overbought, or below oversold"
tt_50Cross = '50-Cross: Above 50 line is uptrend, below is downtrend.'
tt_Direction = "Direction: moving up or down"
tt_RSIvsMA = "RSI vs MA: RSI above is uptrend, RSI below is downtrend"
tt_signals = tt_TRENDobos +'\n\n' + tt_OBOS + '\n\n' + tt_50Cross + "\n\n" + tt_Direction +"\n\n" + tt_RSIvsMA
displaySignal = input(false,title="Display Signal",group="Signals",inline="Signal")
displaySignalVal = input.string("OB/OS      (RSI)",title="",options=["Trend OB/OS(RSI)","OB/OS      (RSI)","50-Cross     (RSI)","Direction    (RSI)","Trend OB/OS(MA)","OB/OS      (MA)","50-Cross     (MA)","Direction    (MA)","RSI vs MA"],group="Signals",inline="Signal",tooltip=tt_signals)

//Colors
upCol = input(color.new(color.lime,70),title='', inline="Signal", group='Signals')
downCol = input(color.new(color.red,70),title='', inline="Signal", group='Signals')

//Oversold / Overbought
osLevel = input.float(30.0, minval=1, maxval=99, title='Oversold   ', inline="os", group='Oversold/Overbought')
obLevel = input.float(70.0, minval=1, maxval=99, title='Overbought', inline="ob", group='Oversold/Overbought')

//Calculate Cuttlers RSI
float gains = 0.0
float losses = 0.0
for k = 0 to length - 1
    float difference = nz(src[k]) - nz(src[k+1]) 
    gains := difference > 0.0 ? gains + difference : gains
    losses := difference < 0.0 ? losses - difference : losses
rsi = losses > 0.0 ? 100. - 100. / (1.0 + gains / losses) : 50.0

//Calculate MA
rsiMA = ma(rsi, maLengthInput, maTypeInput)
isBB = maTypeInput == "Bollinger Bands"

//Calculate Signals
sigCol = color(na)
if displaySignalVal == "Trend OB/OS(RSI)"
    sigCol := rsi < osLevel ? downCol : (rsi > obLevel ? upCol : sigCol[1])
else if displaySignalVal == "OB/OS      (RSI)"
    sigCol := rsi < osLevel ? upCol : (rsi > obLevel ? downCol : na)
else if displaySignalVal == "50-Cross     (RSI)"
    sigCol := rsi >= 50 ? upCol : downCol
else if displaySignalVal == "Direction    (RSI)"
    sigCol := rsi == rsi[1] ? sigCol[1] : (rsi > rsi[1] ? upCol : downCol) 
else if displaySignalVal == "Trend OB/OS(MA)"
    sigCol := rsiMA < osLevel ? downCol : (rsiMA > obLevel ? upCol : sigCol[1])
else if displaySignalVal == "OB/OS      (MA)"
    sigCol := rsiMA < osLevel ? upCol : (rsiMA > obLevel ? downCol : na)
else if displaySignalVal == "50-Cross     (MA)"
    sigCol := rsiMA >= 50 ? upCol : downCol
else if displaySignalVal == "Direction    (MA)"
    sigCol := rsiMA == rsiMA[1] ? sigCol[1] : (rsiMA > rsiMA[1] ? upCol : downCol)
else if displaySignalVal == "RSI vs MA"
    sigCol := rsi == rsiMA ? sigCol[1] : (rsi > rsiMA ? upCol : downCol) 


//Plot Overbought/Oversold
rsiUpperBand = hline(obLevel, "Upper Band", color=#787B86, linewidth=1)
hline(50, "Middle Band", color=color.new(#787B86, 50))
rsiLowerBand = hline(osLevel, "Lower Band", color=#787B86, linewidth=1)
fill(rsiUpperBand, rsiLowerBand, color=color.rgb(126, 87, 194, 90), title="RSI Background Fill")

//Plot Signal
bgcolor(displaySignal ? sigCol : na)

//Plot MA
rsiMAPlot = plot(displayMA ? rsiMA : na, title="RSI-based MA", color=displayMACrossFill == false ? color.yellow : (rsi > rsiMA ? color.lime : color.red))
rsiForMAPlot = plot(displayMA ? rsi : na, title="RSI for MA (invisible placeholder)",color=color.new(color.blue,100))
//BB
bbUpperBand = plot(isBB ? rsiMA + ta.stdev(rsi, maLengthInput) * bbMultInput : na, title = "Upper Bollinger Band", color=color.green)
bbLowerBand = plot(isBB ? rsiMA - ta.stdev(rsi, maLengthInput) * bbMultInput : na, title = "Lower Bollinger Band", color=color.green)
fill(bbUpperBand, bbLowerBand, color= isBB ? color.new(color.green, 90) : na, title="Bollinger Bands Background Fill")
//Plot MA Fill
fill(rsiMAPlot,rsiForMAPlot, color = rsi > rsiMA ? color.lime : color.red, display = displayMACrossFill and isBB == false ? display.all : display.none) //Normal MA Fill
fill(bbUpperBand,rsiForMAPlot, color = rsi > (rsiMA + ta.stdev(rsi, maLengthInput) * bbMultInput) ? color.red : na, display = displayMACrossFill and isBB ? display.all : display.none) //Above BB Fill
fill(bbLowerBand,rsiForMAPlot, color = rsi < rsiMA - ta.stdev(rsi, maLengthInput) * bbMultInput ? color.lime : na, display = displayMACrossFill and isBB ? display.all : display.none) //Below BB Fill
//Plot RSI
plot(rsi,title="Harris RSI")


//Alerts
tt_alert = "Check off each piece of criteria you want for the alerts, then select Okay. Then go to 'Create Alert' and set the condition to 'Harris RSI', select create."

alertBuySignal = input(false,title="🟢Buy Signal Alert",group="Alerts",tooltip=tt_alert)
alertSellSignal = input(false,title="🔴Sell Signal Alert",group="Alerts")

if alertBuySignal and sigCol[1] == color.lime and sigCol[2] != color.lime 
    alert(timeframe.period + "🟢Harris RSI Alert - RSI: " + str.tostring(rsi,'#.#') + "\n" + displaySignalVal, alert.freq_once_per_bar)
if alertSellSignal and sigCol[1] == color.red and sigCol[2] != color.red 
    alert(timeframe.period + "🔴Harris RSI Alert - RSI: " + str.tostring(rsi,'#.#') + "\n" + displaySignalVal, alert.freq_once_per_bar)
