Pivot Points (Channel | Fib | Support/Resistances)
// This source code is subject to the terms of the Mozilla Public License 2.0 at https://mozilla.org/MPL/2.0/
// © atolelole

//@version=5
indicator("Pivot Channel", overlay = true, max_bars_back = 500, max_lines_count = 500, max_labels_count = 500)


i_pivothigh_len = input.int(21, "Pivot high length(left, right)", group="Pivot points", inline="phb")
i_pivothigh_n   = input.int(5 , "Max pivot points"              , group="Pivot points", inline="phb")
i_pivotlow_len  = input.int(21, "Pivot low  length(left, right)", group="Pivot points", inline="plb")
i_pivotlow_n    = input.int(5 , "Max pivot points"              , group="Pivot points", inline="plb")
i_drawpivots    = input.bool(true, "Draw pivot points"          , group="Pivot points")
i_hsource_test	= input.source(close, "Pivot high test source"  , group="Pivot points", tooltip = "Series that is tested for a high pivot point, if yes takes price from Pivot high source")
i_hsource     	= input.source(high, "Pivot high source"        , group="Pivot points")
i_lsource_test	= input.source(close, "Pivot low test source"   , group="Pivot points", tooltip = "Series that is tested for a low pivot point, if yes takes price from Pivot low source")
i_lsource       = input.source(low, "Pivot low source"          , group="Pivot points")

i_trend_old_method = input.bool(false, "Use old method to find channel lines", group="Trends")
i_htrend_style = input.string(line.style_dashed, "High trend line style", options=[line.style_dotted, line.style_dashed, line.style_solid, line.style_arrow_both, line.style_arrow_left, line.style_arrow_right], group="Trends", inline="htr")
i_htrend_width = input.int(2, "High trend line width", group="Trends", inline="htr")
i_ltrend_style = input.string(line.style_dashed, "Low trend line style" , options=[line.style_dotted, line.style_dashed, line.style_solid, line.style_arrow_both, line.style_arrow_left, line.style_arrow_right], group="Trends", inline="ltr")
i_ltrend_width = input.int(2, "Low trend line width" , group="Trends", inline="ltr")
i_trend_extlen = input.int(5, "Right channel extension length", group="Trends")

i_hcolor = input.color(color.new(#99d31b, 0), "High color", group="Colors", inline="clr")
i_lcolor = input.color(color.new(#FA5032, 0), "Low color" , group="Colors", inline="clr")

i_drawheatmap = input.bool(false, "Enable"                        , group = "Heatmap")
i_minrsi_len  = input.int(2 , "Min RSI length"                    , group = "Heatmap", tooltip = "for step = 0 to 10 : rsi = step * (max - min) / 10")
i_maxrsi_len  = input.int(22, "Max RSI length"                    , group = "Heatmap", tooltip = "for step = 0 to 10 : rsi = step * (max - min) / 10")
i_grid_x      = input.int(100, "Number of horizontal grid segments", group = "Heatmap", tooltip = "X axis resolution, if > 45 first cells start to get deleted")

i_drawfibs          = input.bool(true , "Enable"                , group = "Fibs", inline="fiblines")
i_drawfibs_extended = input.bool(false, "Draw fib lines > 1.618", group = "Fibs", inline="fiblines")
i_fibline_widths    = input.int(1, "Fib line widths"            , group="Fibs")
i_fibline_styles    = input.string(line.style_dotted, "Fib lines style", options=[line.style_dotted, line.style_dashed, line.style_solid, line.style_arrow_both, line.style_arrow_left, line.style_arrow_right], group="Fibs")

i_alerts_enabled                = input.bool(false, "Enable", group="Alerts", inline="alrt", tooltip = "WIP, alarms dont trigger so just a label for now")
i_alerts_high_trend_trigger_pct = input.float(0.15, "High trend % trigger", group="Alerts", step=0.1, minval = 0.0, maxval = 1.0)
i_alerts_low_trend_trigger_pct  = input.float(0.15, "Low trend % trigger" , group="Alerts", step=0.1, minval = 0.0, maxval = 1.0)
i_alerts_draw_alert_zones       = input.bool(false, "Draw alert zones", group="Alerts", inline="alrt")
i_alerts_fill_alert_zones       = input.bool(false, "Fill alert zones", group="Alerts", inline="alrt")

get_color(rsi) =>
    clr = color.white
    if rsi >= 0 and rsi <= 25
        clr := color.from_gradient(rsi, 0 , 25 , color.rgb(69, 13,   85, 40), color.rgb(64, 70, 137 , 40))
    if rsi > 25 and rsi <= 50
        clr := color.from_gradient(rsi, 25, 50 , color.rgb(57, 87,  141, 40), color.rgb(35, 139, 140, 40))
    if rsi > 50 and rsi <= 75
        clr := color.from_gradient(rsi, 50, 75 , color.rgb(30, 150, 138, 40), color.rgb(85, 199, 103, 40))
    if rsi > 75 and rsi <= 100
        clr := color.from_gradient(rsi, 75, 100, color.rgb(115, 208, 85, 40), color.rgb(253, 230, 36, 40))
    clr

get_avg_rsi(source, start_index, len) =>
    avg = 0.0
    for i = start_index to start_index + len
        avg += source[i]
    avg / len

interp(l, h, s) => l + (h - l) * s

//PIVOT POINTS
type PivotPoint
    float price
    int index

var high_pivots = array.new<PivotPoint>()
var low_pivots  = array.new<PivotPoint>()

ph = ta.pivothigh(i_hsource_test, i_pivothigh_len, i_pivothigh_len)
if ph
    if array.size(high_pivots) >= i_pivothigh_n
        array.shift(high_pivots)
    array.push(high_pivots, PivotPoint.new(i_hsource[i_pivothigh_len], bar_index[i_pivothigh_len]))

pl = ta.pivotlow(i_lsource_test, i_pivotlow_len, i_pivotlow_len)
if pl
    if array.size(low_pivots) >= i_pivotlow_n
        array.shift(low_pivots)
    array.push(low_pivots, PivotPoint.new(i_lsource[i_pivotlow_len], bar_index[i_pivotlow_len]))

//FIND HIGH AND LOW TREND LINE
var low_trend  = line(na)
var high_trend = line(na)
var labels = array.new_label()

while array.size(labels) > 0
    label.delete(array.shift(labels))

if array.size(high_pivots) > 1
    if i_drawpivots
        for pivot in high_pivots
            array.push(labels, label.new(pivot.index, pivot.price, "", style=label.style_label_down, size=size.tiny, color=color.new(#99d31b, 70)))

    tmp = array.new_line()
    for i = 0 to array.size(high_pivots) - 1
        for j = i to array.size(high_pivots) - 1
            if i != j
                PivotPoint pp0 = array.get(high_pivots, i)
                PivotPoint pp1 = array.get(high_pivots, j)
                array.push(tmp, line.new(pp0.index, pp0.price, pp1.index, pp1.price, color=i_hcolor, width = 1, style = line.style_dashed))

    best_ind = int(na)
    if i_trend_old_method
        min_val = 10000000.0
        for i = 0 to array.size(tmp) - 1
            lp = line.get_price(array.get(tmp, i), bar_index)
            if lp > high
                if min_val > math.abs(lp - close)
                    min_val := math.abs(lp - close)
                    best_ind := i
    else
        best_cnt = 0
        for i = 0 to array.size(tmp) - 1
            trend = array.get(tmp, i)
            cnt = 0

            for pivot in high_pivots
                if line.get_price(trend, pivot.index) >= pivot.price
                    cnt += 1

            if cnt > best_cnt
                best_cnt := cnt
                best_ind := i

            if cnt == best_cnt
                if line.get_price(array.get(tmp, best_ind), bar_index + 1) > line.get_price(trend, bar_index + 1) and line.get_price(trend, bar_index + 1) > i_hsource
                    best_cnt := cnt
                    best_ind := i

    if not na(best_ind)
        line.delete(high_trend)
        high_trend := array.get(tmp, best_ind)
        array.remove(tmp, best_ind)

    while array.size(tmp) > 0
        line.delete(array.shift(tmp)) 

if array.size(low_pivots) > 1
    if i_drawpivots
        for pivot in low_pivots
            array.push(labels, label.new(pivot.index, pivot.price, "", style=label.style_label_up, size=size.tiny, color=color.new(#FA5032, 70)))

    tmp = array.new_line()
    for i = 0 to array.size(low_pivots) - 1
        for j = i to array.size(low_pivots) - 1
            if i != j
                PivotPoint pp0 = array.get(low_pivots, i)
                PivotPoint pp1 = array.get(low_pivots, j)
                array.push(tmp, line.new(pp0.index, pp0.price, pp1.index, pp1.price, color=i_lcolor, width = 1, style = line.style_dashed))

    best_ind = int(na)
    if i_trend_old_method
        min_val = 100000.0
        for i = 0 to array.size(tmp) - 1
            lp = line.get_price(array.get(tmp, i), bar_index)
            if lp < low
                if min_val > math.abs(lp - close)
                    min_val := math.abs(lp - close)
                    best_ind := i
    else
        best_cnt = 0
        for i = 0 to array.size(tmp) - 1
            trend = array.get(tmp, i)
            cnt = 0
            
            for pivot in low_pivots
                if line.get_price(trend, pivot.index) <= pivot.price
                    cnt += 1

            if cnt > best_cnt
                best_cnt := cnt
                best_ind := i

            if cnt == best_cnt
                if line.get_price(array.get(tmp, best_ind), bar_index + 1) < line.get_price(trend, bar_index + 1) and line.get_price(trend, bar_index + 1) < i_lsource
                    best_cnt := cnt
                    best_ind := i

    if not na(best_ind)
        line.delete(low_trend)
        low_trend := array.get(tmp, best_ind)
        array.remove(tmp, best_ind)

    while array.size(tmp) > 0
        line.delete(array.shift(tmp))

if not na(low_trend) and not na(high_trend)
    for l in labels
        if label.get_x(l) == line.get_x1(low_trend) or label.get_x(l) == line.get_x2(low_trend)
            label.set_color(l, color.new(#FA5032, 0))
    line.set_y2(low_trend, line.get_price(low_trend, bar_index + i_trend_extlen))
    line.set_x2(low_trend, bar_index + i_trend_extlen)
    line.set_width(low_trend, i_ltrend_width)
    line.set_style(low_trend, i_ltrend_style)
    if line.get_x1(high_trend) > line.get_x1(low_trend)
        line.set_y1(high_trend, line.get_price(high_trend, line.get_x1(low_trend)))
        line.set_x1(high_trend, line.get_x1(low_trend))

    for l in labels
        if label.get_x(l) == line.get_x1(high_trend) or label.get_x(l) == line.get_x2(high_trend)
            label.set_color(l, color.new(#99d31b, 0))
    line.set_y2(high_trend, line.get_price(high_trend, bar_index + i_trend_extlen))
    line.set_x2(high_trend, bar_index + i_trend_extlen)
    line.set_width(high_trend, i_htrend_width)
    line.set_style(high_trend, i_htrend_style)
    if line.get_x1(low_trend) > line.get_x1(high_trend)
        line.set_y1(low_trend, line.get_price(low_trend, line.get_x1(high_trend)))
        line.set_x1(low_trend, line.get_x1(high_trend))


//you can now use high and low trend line
//if not na(high_trend)
//  ...code...


//HEATMAP  
var fills = array.new_linefill()
var lines = array.new_line()

while array.size(fills) > 0
    linefill.delete(array.shift(fills))
while array.size(lines) > 0
    line.delete(array.shift(lines))

rsi0  = ta.rsi(close, i_minrsi_len +  0 * (i_maxrsi_len - i_minrsi_len) / 10)
rsi1  = ta.rsi(close, i_minrsi_len +  1 * (i_maxrsi_len - i_minrsi_len) / 10)
rsi2  = ta.rsi(close, i_minrsi_len +  2 * (i_maxrsi_len - i_minrsi_len) / 10)
rsi3  = ta.rsi(close, i_minrsi_len +  3 * (i_maxrsi_len - i_minrsi_len) / 10)
rsi4  = ta.rsi(close, i_minrsi_len +  4 * (i_maxrsi_len - i_minrsi_len) / 10)
rsi5  = ta.rsi(close, i_minrsi_len +  5 * (i_maxrsi_len - i_minrsi_len) / 10)
rsi6  = ta.rsi(close, i_minrsi_len +  6 * (i_maxrsi_len - i_minrsi_len) / 10)
rsi7  = ta.rsi(close, i_minrsi_len +  7 * (i_maxrsi_len - i_minrsi_len) / 10)
rsi8  = ta.rsi(close, i_minrsi_len +  8 * (i_maxrsi_len - i_minrsi_len) / 10)
rsi9  = ta.rsi(close, i_minrsi_len +  9 * (i_maxrsi_len - i_minrsi_len) / 10)
rsi10 = ta.rsi(close, i_minrsi_len + 10 * (i_maxrsi_len - i_minrsi_len) / 10)

if not na(high_trend) and not na(low_trend) and barstate.islast and i_drawheatmap
    X = i_grid_x //horizontal grid segments OK to change (limited by max_line_count? or something) (max 45 at 500)
    Y = 10 //vertical grid segments do NOT change or add rsi11 and so on with other relevant code
    for x = 0 to X - 1 by 1
        for y = 0 to Y
            x0 = int(line.get_x1(low_trend) + x * (bar_index - line.get_x1(low_trend)) / X)
            y0 = line.get_price(low_trend, x0) + y * (line.get_price(high_trend, x0) - line.get_price(low_trend, x0)) / Y
            x1 = int(line.get_x1(high_trend) + (x + 1) * (bar_index - line.get_x1(high_trend)) / X)
            y1 = line.get_price(low_trend, x1) + y * (line.get_price(high_trend, x1) - line.get_price(low_trend, x1)) / Y

            array.push(lines, line.new(x0, y0, x1, y1, color=na))

            if array.size(lines) > 1 and y != 0
                l0 = array.get(lines, array.size(lines) - 2)
                l1 = array.get(lines, array.size(lines) - 1)
                if y == 1
                    array.push(fills, linefill.new(l0, l1, get_color(rsi0[bar_index - x1 + int((x1 - x0) / 2)]))) //get_color(get_avg_rsi(rsi0, bar_index - x1, x1 - x0)) //not working great so lets just take the middle
                if y == 2
                    array.push(fills, linefill.new(l0, l1, get_color(rsi1[bar_index - x1 + int((x1 - x0) / 2)])))
                if y == 3
                    array.push(fills, linefill.new(l0, l1, get_color(rsi2[bar_index - x1 + int((x1 - x0) / 2)])))
                if y == 4
                    array.push(fills, linefill.new(l0, l1, get_color(rsi3[bar_index - x1 + int((x1 - x0) / 2)])))
                if y == 5
                    array.push(fills, linefill.new(l0, l1, get_color(rsi4[bar_index - x1 + int((x1 - x0) / 2)])))
                if y == 6
                    array.push(fills, linefill.new(l0, l1, get_color(rsi5[bar_index - x1 + int((x1 - x0) / 2)])))
                if y == 7
                    array.push(fills, linefill.new(l0, l1, get_color(rsi6[bar_index - x1 + int((x1 - x0) / 2)])))
                if y == 8
                    array.push(fills, linefill.new(l0, l1, get_color(rsi7[bar_index - x1 + int((x1 - x0) / 2)])))
                if y == 9
                    array.push(fills, linefill.new(l0, l1, get_color(rsi8[bar_index - x1 + int((x1 - x0) / 2)])))
                if y == 10
                    array.push(fills, linefill.new(l0, l1, get_color(rsi9[bar_index - x1 + int((x1 - x0) / 2)])))


//FIBONACI
var fibs = array.new_line()

while array.size(fibs) > 0
    line.delete(array.shift(fibs))

if not na(high_trend) and not na(low_trend) and barstate.islast and i_drawfibs
    left  = line.get_x1(low_trend)
    right = bar_index + i_trend_extlen
    left_val   = interp(line.get_price(low_trend, left) , line.get_price(high_trend, left) , -0.618)
    right_val  = interp(line.get_price(low_trend, right), line.get_price(high_trend, right), -0.618)
    array.push(fibs, line.new(left, left_val, right, right_val, style=i_fibline_styles, width=i_fibline_widths, color=color.from_gradient(right_val, line.get_price(low_trend, right), line.get_price(high_trend, right), i_lcolor , i_hcolor)))
    left_val  := interp(line.get_price(low_trend, left) , line.get_price(high_trend, left) , 0.236)
    right_val := interp(line.get_price(low_trend, right), line.get_price(high_trend, right), 0.236)
    array.push(fibs, line.new(left, left_val, right, right_val, style=i_fibline_styles, width=i_fibline_widths, color=color.from_gradient(right_val, line.get_price(low_trend, right), line.get_price(high_trend, right), i_lcolor , i_hcolor)))
    left_val  := interp(line.get_price(low_trend, left) , line.get_price(high_trend, left) , 0.382)
    right_val := interp(line.get_price(low_trend, right), line.get_price(high_trend, right), 0.382)
    array.push(fibs, line.new(left, left_val, right, right_val, style=i_fibline_styles, width=i_fibline_widths, color=color.from_gradient(right_val, line.get_price(low_trend, right), line.get_price(high_trend, right), i_lcolor , i_hcolor)))
    left_val  := interp(line.get_price(low_trend, left) , line.get_price(high_trend, left) , 0.5)
    right_val := interp(line.get_price(low_trend, right), line.get_price(high_trend, right), 0.5)
    array.push(fibs, line.new(left, left_val, right, right_val, style=i_fibline_styles, width=i_fibline_widths, color=color.from_gradient(right_val, line.get_price(low_trend, right), line.get_price(high_trend, right), i_lcolor , i_hcolor)))
    left_val  := interp(line.get_price(low_trend, left) , line.get_price(high_trend, left) , 0.618)
    right_val := interp(line.get_price(low_trend, right), line.get_price(high_trend, right), 0.618)
    array.push(fibs, line.new(left, left_val, right, right_val, style=i_fibline_styles, width=i_fibline_widths, color=color.from_gradient(right_val, line.get_price(low_trend, right), line.get_price(high_trend, right), i_lcolor , i_hcolor)))
    left_val  := interp(line.get_price(low_trend, left) , line.get_price(high_trend, left) , 0.75)
    right_val := interp(line.get_price(low_trend, right), line.get_price(high_trend, right), 0.75)
    array.push(fibs, line.new(left, left_val, right, right_val, style=i_fibline_styles, width=i_fibline_widths, color=color.from_gradient(right_val, line.get_price(low_trend, right), line.get_price(high_trend, right), i_lcolor , i_hcolor)))
    left_val  := interp(line.get_price(low_trend, left) , line.get_price(high_trend, left) , 1.618)
    right_val := interp(line.get_price(low_trend, right), line.get_price(high_trend, right), 1.618)
    array.push(fibs, line.new(left, left_val, right, right_val, style=i_fibline_styles, width=i_fibline_widths, color=color.from_gradient(right_val, line.get_price(low_trend, right), line.get_price(high_trend, right), i_lcolor , i_hcolor)))

    if i_drawfibs_extended
        left_val  := interp(line.get_price(low_trend, left) , line.get_price(high_trend, left) , 2.618)
        right_val := interp(line.get_price(low_trend, right), line.get_price(high_trend, right), 2.618)
        array.push(fibs, line.new(left, left_val, right, right_val, style=i_fibline_styles, width=i_fibline_widths, color=color.from_gradient(right_val, line.get_price(low_trend, right), line.get_price(high_trend, right), i_lcolor , i_hcolor)))
        left_val  := interp(line.get_price(low_trend, left) , line.get_price(high_trend, left) , 3.618)
        right_val := interp(line.get_price(low_trend, right), line.get_price(high_trend, right), 3.618)
        array.push(fibs, line.new(left, left_val, right, right_val, style=i_fibline_styles, width=i_fibline_widths, color=color.from_gradient(right_val, line.get_price(low_trend, right), line.get_price(high_trend, right), i_lcolor , i_hcolor)))
        left_val  := interp(line.get_price(low_trend, left) , line.get_price(high_trend, left) , 4.236)
        right_val := interp(line.get_price(low_trend, right), line.get_price(high_trend, right), 4.236)
        array.push(fibs, line.new(left, left_val, right, right_val, style=i_fibline_styles, width=i_fibline_widths, color=color.from_gradient(right_val, line.get_price(low_trend, right), line.get_price(high_trend, right), i_lcolor , i_hcolor)))




//ALERTS
var line alert_zone_low  = line(na)
var line alert_zone_high = line(na)
var linefill alert_zone_low_linefill  = linefill(na)
var linefill alert_zone_high_linefill = linefill(na)
var label alert_label = label(na)

if not na(low_trend) and not na(high_trend) and barstate.islast and i_alerts_enabled
    clp = line.get_price(low_trend, bar_index)
    chp = line.get_price(high_trend, bar_index)

    ldiff = (close - clp) / (chp - clp)
    hdiff = (chp - close) / (chp - clp)

    label.delete(alert_label)

    if ldiff <= i_alerts_low_trend_trigger_pct and ldiff > 0.0
        alert_label := label.new(bar_index + 3, close, str.tostring(ldiff, "buy #.##%"), style=label.style_label_left)
        alert("Possible bounce incoming " + syminfo.ticker, alert.freq_once_per_bar)      
    else if hdiff <= i_alerts_high_trend_trigger_pct and hdiff > 0.0
        alert_label := label.new(bar_index + 3, close, str.tostring(hdiff, "sell #.##%"), style=label.style_label_left)
        alert("Possible drop incoming " + syminfo.ticker, alert.freq_once_per_bar)

    if i_alerts_draw_alert_zones
        line.delete(alert_zone_low)
        line.delete(alert_zone_high)

        x0 = bar_index
        y0 = clp + (chp - clp) * i_alerts_low_trend_trigger_pct
        x1 = bar_index + i_trend_extlen
        y1 = line.get_price(low_trend, x1) + (line.get_price(high_trend, x1) - line.get_price(low_trend, x1)) * i_alerts_low_trend_trigger_pct
        alert_zone_low := line.new(x0, y0, x1, y1, color=i_lcolor)
        if i_alerts_fill_alert_zones
            linefill.delete(alert_zone_low_linefill)
            alert_zone_low_linefill := linefill.new(low_trend, alert_zone_low, color.new(i_lcolor, 70))

        x0 := bar_index
        y0 := clp + (chp - clp) * (1.0 - i_alerts_high_trend_trigger_pct)
        x1 := bar_index + i_trend_extlen
        y1 := line.get_price(low_trend, x1) + (line.get_price(high_trend, x1) - line.get_price(low_trend, x1)) * (1.0 - i_alerts_high_trend_trigger_pct)
        alert_zone_high := line.new(x0, y0, x1, y1, color=i_hcolor)
        if i_alerts_fill_alert_zones
            linefill.delete(alert_zone_high_linefill)
            alert_zone_high_linefill := linefill.new(high_trend, alert_zone_high, color.new(i_hcolor, 70))
