Cyclic RSI High Low With Noise Filter
// This source code is subject to the terms of the Mozilla Public License 2.0 at https://mozilla.org/MPL/2.0/
// © RozaniGhani-RG

//@version=5
indicator('Cyclic RSI High Low With Noise Filter', 'cRSI')

// 0. Inputs
// 1. Variables and calculations
// 2. Custom functions
// 3. Construct
// 4. Plot, fill and hline

// ————————————————————————————————————————————————————————————————————————————— 0. Inputs {
i_s_switch = input.string('Cyclic RSI', 'RSI or Cyclic RSI', options = ['RSI', 'Cyclic RSI'])
i_src      = input.source(close,                   'Source', inline = '0')
i_i_len    = input.int(13,                         'Length', inline = '0', minval = 1)
i_b_line   = input.bool(false,                  'Show line', tooltip = 'Best used plot  is disabled')
i_b_plot   = input.bool(true,                   'Show plot', tooltip = 'Best used line is disabled')
i_b_fill   = input.bool(true,                   'Show fill', tooltip = 'Only for Cyclic RSI')
i_s_noise  = input.string('none',            'Remove Noise', options = ['none', '35 < value < 65', '50 < value < 65', '35 < value < 50'])
i_s_label  = input.string('arrow',            'Noise label', options = ['both', 'OB/OS', 'arrow'], tooltip = 'Beginner use both')
i_s_alert  = input.string('Once Per Bar Close', 'Frequency', options = ['Once Per Bar Close', 'Once Per Bar', 'All'], group ='Alert')
_freq      = i_s_alert == 'Once Per Bar Close' ? alert.freq_once_per_bar_close : i_s_alert == 'Once Per Bar' ? alert.freq_once_per_bar : alert.freq_all
// }
// 1. Variables and calculations
// ————————————————————————————————————————————————————————————————————————————— 1.1 Cyclic RSI formula {
// Credits to WhenToTrade
crsi       = 0.0
vibration  = 10
torque     = 0.618 / (vibration + 1)
phasingLag = (vibration - 1) / 0.618

rsi        = ta.rsi(i_src, i_i_len)
crsi      := torque * (2 * rsi - rsi[phasingLag]) + (1 - torque) * nz(crsi[1])
// }
// ————————————————————————————————————————————————————————————————————————————— 1.2 Cyclic RSI formula {
float osc = switch i_s_switch
    'Cyclic RSI' => crsi
    'RSI'        => rsi
// }
// ————————————————————————————————————————————————————————————————————————————— 1.3 Zigzag formula {
// Credits to LonesomeTheBlue
float ph = ta.highestbars(high, i_i_len) == 0 ? osc : na
float pl = ta.lowestbars(low,   i_i_len) == 0 ? osc : na

var int dir = 0
dir := ph and na(pl) ? 1 : pl and na(ph) ? -1 : dir

var max_array_size = 10
var arr_zz         = array.new_float(0)
older_zz           = array.copy(arr_zz)
dirchanged         = ta.change(dir)
// }
// 2. Custom functions
// ————————————————————————————————————————————————————————————————————————————— 2.1 Zigzag functions {
// Credits to LonesomeTheBlue
add_to_zigzag(_id, float value, int bindex) =>
    array.unshift(_id, bindex)
    array.unshift(_id, value)
    if array.size(_id) > max_array_size
        array.pop(_id)
        array.pop(_id)

update_zigzag(_id, float value, int bindex, int dir) =>
    if array.size(_id) == 0
        add_to_zigzag(_id, value, bindex)
    else
        if dir == 1 and value > array.get(_id, 0) or dir == -1 and value < array.get(_id, 0)
            array.set(_id, 0, value)
            array.set(_id, 1, bindex)
        0.

if ph or pl
    if dirchanged
        add_to_zigzag(arr_zz, dir == 1 ? ph : pl, bar_index)
    else
        update_zigzag(arr_zz, dir == 1 ? ph : pl, bar_index, dir)
// }
// ————————————————————————————————————————————————————————————————————————————— 2.2 Remove noise {
f_noise() =>
    filter_noise = switch i_s_noise
        '35 < value < 65' => array.get(arr_zz, 2) <= 65 and array.get(arr_zz, 2) >= 35
        '50 < value < 65' => array.get(arr_zz, 2) >= 35
        '30 < value < 50' => array.get(arr_zz, 2) <= 65

// 2.3 Noise label
[str_OB, str_OS] = switch i_s_label
    'both'  => ['OB\n▼', '▲\nOS']
    'OB/OS' => [   'OB', 'OS']
    'arrow' => [    '▼', '▲']
// }
// ————————————————————————————————————————————————————————————————————————————— 3. Construct {
if array.size(arr_zz) >= 6
    // Variables
    var line line_zz   = na
    var label label_zz = na
    
    // Bools for or
    bool bool_or_1 = array.get(arr_zz, 0) != array.get(older_zz, 0)
    bool bool_or_2 = array.get(arr_zz, 1) != array.get(older_zz, 1)
    
    // Bools for and
    bool bool_n_1 = array.get(arr_zz, 2) == array.get(older_zz, 2)
    bool bool_n_2 = array.get(arr_zz, 3) == array.get(older_zz, 3)

    // Bools for more than and less than
    bool bool_0_mt_4 = array.get(arr_zz, 0) > array.get(arr_zz, 4)
    bool bool_0_lt_4 = array.get(arr_zz, 0) < array.get(arr_zz, 4)
    
    if bool_or_1 or bool_or_2
        if bool_n_1 and bool_n_2
            line.delete(line_zz)
            label.delete(label_zz)
        if f_noise()
            label.delete(label_zz)       
        if i_b_line
            line_zz := line.new( x1 = math.round(array.get(arr_zz, 1)), y1 = array.get(arr_zz, 0),
                                 x2 = math.round(array.get(arr_zz, 3)), y2 = array.get(arr_zz, 2),
                                 color = color.gray,
                                 width = 2)
        str_label = dir == 1 ?   bool_0_mt_4 ? str_OB     : '◍' :
                                 bool_0_lt_4 ? str_OS     : '◍'
        col_label = dir == 1 ?   bool_0_mt_4 ? color.red  : color.teal :
                                 bool_0_lt_4 ? color.teal : color.red
        label_zz := label.new(x = math.round(array.get(arr_zz, 1)), y = array.get(arr_zz, 0), text = str_label,
                                 color = color.new(color.blue, 100), textcolor = col_label,
                                 style=dir == 1 ? label.style_label_down : label.style_label_up)
        label_zz
        if bool_0_mt_4
            alert(syminfo.ticker + ' ' + i_s_switch + ' ' + str.tostring(osc, ' (#.00)') + ' ' + str_OB, _freq)
        if bool_0_lt_4
            alert(syminfo.ticker + ' ' + i_s_switch + ' ' + str.tostring(osc, ' (#.00)') + ' ' + str_OB, _freq)
// }
// ————————————————————————————————————————————————————————————————————————————— 4. Plot, fill and hline {
p_rsi  = plot(i_s_switch ==        'RSI' ? rsi : na,   'RSI', color.new(color.yellow, i_b_plot ? 0 : 100), linewidth = 2)
p_crsi = plot(i_s_switch == 'Cyclic RSI' ? crsi : na, 'CRSI', color.new(color.yellow, i_b_plot ? 0 : 100), linewidth = 2)
p_os   = plot(30,                   'OS',                     color.new(color.teal,   i_b_plot ? 0 : 50))
p_ob   = plot(70,                   'OB',                     color.new(color.red,    i_b_plot ? 0 : 50))

fill(p_os, p_crsi, color.new(color.teal, i_b_fill ? 65 : 100))
fill(p_ob, p_crsi, color.new(color.red,  i_b_fill ? 65 : 100))
hline(50)
// }
