RedK Relative Strength Ribbon: RS Ribbon and RS Charts
// This source code is subject to the terms of the Mozilla Public License 2.0 at https://mozilla.org/MPL/2.0/
// © RedKTrader

//@version=5
indicator(title="RedK Relative Strength Ribbon v1.0", shorttitle="RedK RS_Ribbon v1.0", precision = 2, 
  timeframe = "", timeframe_gaps = false)

// to-do:s
// add alerts for ribbon swining green/red - or a generic swing
// add other MA types (done)
// Support MTF (done)
//==============================================================================
// updated this version of LazyLine() to reflect a regular WMA for length < 5 
// newest version, avoids warning about wma() inside the if block, code cleaned-up
f_LazyLine(float _data, int _length) =>
  
    LL1 = 0.0,   LL2 = 0.0,   _LL = 0.0
    var float w = _length / 3
    var int w2 = math.round(w)
    var int w1 = math.round((_length - w2) / 2)
    var int w3 = int((_length - w2) / 2)   
    
    L1 = ta.wma(_data, w1)
    L2 = ta.wma(L1, w2)
    L3 = ta.wma(L2, w3)
    L4 = ta.wma(_data, _length)    

    _LL := _length > 4 ? L3 : L4
    
//==============================================================================

// =============================================================================    
f_getMA(source, length, mtype) =>
    mtype == "SMA" ? ta.sma(source, length) : 
      mtype == "EMA" ? ta.ema(source, length) :
      mtype == "WMA" ? ta.wma(source, length) :
      mtype == "HMA" ? ta.hma(source, length) :
      mtype == "RMA" ? ta.rma(source, length) :
      f_LazyLine(source, length)
// =============================================================================    

MA1 = "MA1"
MA2 = "MA2"
grp_1 = "RS Ribbon"
grp_2 = "New x-Weeks Hi/Lo RS (only 1D & 1W TFs)"

base        = input.symbol("SPY", "Base Symbol")
show_RSBars = input.bool(true, "Show RS Candles?      ",  inline = 'RScandles')
use_HA      = input.bool(false, 'Use Heikin Ashi?',       inline = 'RScandles')

show_Ribbon = input.bool(true, "Show RS Ribbon?",                   group = grp_1)
MA1_length  = input.int(5, "MA1 Length", minval = 1, inline = MA1,  group = grp_1)
MA1_type    = input.string('WMA', title = 'Type',    inline = MA1,  group = grp_1,
  options = ['RSS_WMA', 'WMA', 'EMA', 'SMA', 'HMA', 'RMA'] )

MA2_length  = input.int(15, "MA2 Length", minval = 1, inline = MA2, group = grp_1)
MA2_type    = input.string('RSS_WMA', title = 'Type', inline = MA2, group = grp_1,
  options = ['RSS_WMA', 'WMA', 'EMA', 'SMA', 'HMA', 'RMA'])


var string Rel_sym = ticker.standard(syminfo.tickerid) + ' / ' + ticker.standard(base)
var string Current_TF = timeframe.period
// note: Current_TF is not the chart's TF, it's the TF selected in the TF dropdown in indicator settings

[o, h, l , rs]  = request.security(Rel_sym, Current_TF, [100 * open, 100 * high, 100 * low, 100 * close])

// ====================================================================================================================
// Adding the option to show RS Candles - can be useful to do some TA on and check for patterns like VCP
// good tip hee is to use log scale too for these "RS charts"
// also added the ability to show the RS candles as HA Candles for any HA fans out there
// note that RS will continue to calc using normal close values
// ******
// HA bar construction from normal candles
// Open = (open of previous bar + close of previous bar) divided by 2
// Close = (open + close + high + low of current bar) divided by 4
// High = the maximum value from the high, open, or close of the current period
// Low = the minimum value from the low, open, or close of the current period


float ca_o = o
float ca_c = rs
float ca_h = h
float ca_l = l

var float HA_o = o
var float HA_c = rs
var float HA_h = h
var float HA_l = l

HA_o := math.avg(o[1], rs[1])
HA_c := math.avg(o, rs, h, l)
HA_h := math.max(h, o ,rs)
HA_l := math.min(l, o, rs)

if use_HA
    ca_o := HA_o, ca_c := HA_c
    ca_h := HA_h, ca_l := HA_l

up_bar = ca_c > ca_o  

c_barup     = color.new(#26a69a, 60)
c_bardn     = color.new(#ef5350, 60)

c_barupb    = color.new(#26a69a, 15)
c_bardnb    = color.new(#ef5350, 15)


barcolor    = up_bar ? c_barup  : c_bardn
borcolor    = up_bar ? c_barupb : c_bardnb

plotcandle(ca_o, ca_h, ca_l, ca_c, 'RS Candles', barcolor, borcolor, bordercolor = borcolor,
  display = show_RSBars ? display.pane : display.none)

// ====================================================================================================================

RS_avg1     = f_getMA(rs, MA1_length, MA1_type)
RS_avg2     = f_getMA(rs, MA2_length, MA2_type)

green       = #08440d80
red         = #80192280

l_green     = #1b5e20
l_red       = #981919
c_avg1      = color.new(color.gray,50)
c_avg2      = RS_avg1 >= RS_avg2 ? l_green : l_red 
c_ribbon    = show_Ribbon ? (RS_avg1 > RS_avg2 ? green :  red) : na

A1 = plot(RS_avg1, color = c_avg1, title="RS Avg1", linewidth = 2, 
  display = show_Ribbon ? display.pane + display.data_window : display.none)

A2 = plot(RS_avg2, color = c_avg2, title="RS Avg2", linewidth = 3, 
  display = show_Ribbon ? display.pane + display.data_window : display.none)

fill(A1, A2, color = c_ribbon)

// ====================================================================================================================
// New x Week Hi/Lo
// ====================================================================================================================

show_xwksHL = input.bool(true, 'Show New x_weeks Hi/Lo RS Markers?',        group = grp_2)
num_wks     = input.int (13, "Num Weeks", minval = 1,                       group = grp_2)
num_days    = input.int (5, "Adjust days per week (if needed)", minval = 1, group = grp_2)


// ================================================================
// Get all-time Hi/Lo RS, will be used if there's less than x-weeks of data
athi = ta.max(rs) 
atlo = ta.min(rs) 

// =============================================================================
CalcRS_HiLo (string _TF, int _Length) =>
    _hi_tf = request.security(Rel_sym, _TF, ta.highest(close, _Length)) * 100
    _lo_tf = request.security(Rel_sym, _TF,  ta.lowest(close, _Length)) * 100
    [_hi_tf, _lo_tf]
// =============================================================================


var string RS_TF = "D"
var int RS_Length = 1

if  Current_TF == "D"
    RS_TF       := "1D"
    RS_Length   := num_days * num_wks

else if Current_TF == "W" 
    RS_TF       := "1W"
    RS_Length   := num_wks

// this call will still calculate Hi/Lo on the current chart timeframe - but will do a daily calc
// then we'll ignore the outcome later in the code - to avoid the issue with lower TF's missing points 
// which is actually a "visual inconsistency" rather than a bug, due to the use of "closing price" in RS calculation vs high or low
[hi_tf, lo_tf] = CalcRS_HiLo(RS_TF, RS_Length)

// if the security call returns NA, use all-time  hi/lo values
v_hi52WK = nz(hi_tf, athi) 
v_lo52WK = nz(lo_tf, atlo) 

plot(rs, title = "RS", color=#ffb74d)
plotchar(athi, "All-time Hi", char = "", color = color.aqua, editable = false, display = display.data_window)
plotchar(atlo, "All-time Lo", char = "", color = color.yellow, editable = false, display = display.data_window)

plotchar(hi_tf, "Num Weeks' Hi", char = "", color = color.aqua, editable = false, display = display.data_window)
plotchar(lo_tf, "Num Weeks' Lo", char = "", color = color.yellow, editable = false, display = display.data_window)


float MaxHi = na
float MaxLo = na

if (Current_TF == "D" or Current_TF == "W") and show_xwksHL
    MaxHi := v_hi52WK
    MaxLo := v_lo52WK

// if other TF's, we maintain na value for these variables. so do nothing here ....

plotchar(0.0, "**********", char = "",      color = color.aqua, editable = false, display = display.data_window)
plotchar(MaxHi, "Final x_Wk Hi", char = "", color = color.aqua, editable = false, display = display.data_window)
plotchar(MaxLo, "Final x_Wk Lo", char = "", color = color.yellow, editable = false, display = display.data_window)

// set a flag if a new 52Wk Hi/Lo occurs
N52HL   = rs == MaxHi ? 1 : rs == MaxLo ? -1 : 0

// plot
NewHL   = N52HL == 0 ? na : rs
c_NewHL = N52HL == 1 ? color.aqua : color.orange

plot(NewHL, title = "New x_Wk Hi/Lo", color = c_NewHL, linewidth = 5, style = plot.style_circles, 
  display = display.pane + display.data_window)
