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

//@version=5


indicator('autofibandGanFanPK', 'Auto Fibo & Gann Fan/Rets.', true, max_lines_count=500, max_labels_count=500)

// inputs & definitions {

showExtrema = input(true, 'Mark Extremum Points on the Chart                                                          ')

fiboGroup = '===============[ Fibonacci ]==============='
ffCount = input.int(1, 'Show', minval=0, inline='FiboFan', group=fiboGroup)
ffStyle = input.string('Solid', 'Fans', options=['Dashed', 'Dotted', 'Solid'], inline='FiboFan', group=fiboGroup)
showFFLabels = input.bool(true, 'Show Labels', inline='FiboFan', group=fiboGroup)
frCount = input.int(1, 'Show', minval=0, inline='FiboRet', group=fiboGroup)
frStyle = input.string('Dashed', 'Retracements', options=['Dashed', 'Dotted', 'Solid'], inline='FiboRet', group=fiboGroup)
showFRLabels = input.bool(true, 'Show Labels', inline='FiboRet', group=fiboGroup)
extendFR = input.bool(true, 'Extend the Most Recent Retracement Lines', group=fiboGroup)

gannGroup = '===============[ Gann ]==============='
gfCount = input.int(1, 'Show', minval=0, inline='GannFan', group=gannGroup)
gfStyle = input.string('Solid', 'Fans', options=['Dashed', 'Dotted', 'Solid'], inline='GannFan', group=gannGroup)
showGFLabels = input.bool(true, 'Show Labels', inline='GannFan', group=gannGroup)
grCount = input.int(0, 'Show', minval=0, inline='GannRet', group=gannGroup)
grStyle = input.string('Dotted', 'Retracements', options=['Dashed', 'Dotted', 'Solid'], inline='GannRet', group=gannGroup)
showGRLabels = input.bool(true, 'Show Labels', inline='GannRet', group=gannGroup)
extendGR = input.bool(true, 'Extend the Most Recent Retracement Lines', group=gannGroup)

colorsGroup = '===============[ Line Colors ]==============='
var fiboFanBearColors = array.from(input.color(color.new(color.red, 60), 'FiboFan ↘   23.6:', inline='FiboFan1', group=colorsGroup), input.color(color.new(color.red, 40), '38.2:', inline='FiboFan1', group=colorsGroup), input.color(color.new(color.red, 0), '50.0:', inline='FiboFan1', group=colorsGroup), input.color(color.new(color.red, 40), '61.8:', inline='FiboFan1', group=colorsGroup), input.color(color.new(color.red, 60), '78.6:', inline='FiboFan1', group=colorsGroup))
var fiboFanBullColors = array.from(input.color(color.new(color.green, 60), 'FiboFan ↗   23.6:', inline='FiboFan2', group=colorsGroup), input.color(color.new(color.green, 40), '38.2:', inline='FiboFan2', group=colorsGroup), input.color(color.new(color.green, 0), '50.0:', inline='FiboFan2', group=colorsGroup), input.color(color.new(color.green, 40), '61.8:', inline='FiboFan2', group=colorsGroup), input.color(color.new(color.green, 60), '78.6:', inline='FiboFan2', group=colorsGroup))
var fiboRetBearColors = array.from(input.color(color.new(color.red, 60), 'FiboRet ↘   23.6:', inline='FiboRet1', group=colorsGroup), input.color(color.new(color.red, 40), '38.2:', inline='FiboRet1', group=colorsGroup), input.color(color.new(color.red, 0), '50.0:', inline='FiboRet1', group=colorsGroup), input.color(color.new(color.red, 40), '61.8:', inline='FiboRet1', group=colorsGroup), input.color(color.new(color.red, 60), '78.6:', inline='FiboRet1', group=colorsGroup))
var fiboRetBullColors = array.from(input.color(color.new(color.green, 60), 'FiboRet ↗   23.6:', inline='FiboRet2', group=colorsGroup), input.color(color.new(color.green, 40), '38.2:', inline='FiboRet2', group=colorsGroup), input.color(color.new(color.green, 0), '50.0:', inline='FiboRet2', group=colorsGroup), input.color(color.new(color.green, 40), '61.8:', inline='FiboRet2', group=colorsGroup), input.color(color.new(color.green, 60), '78.6:', inline='FiboRet2', group=colorsGroup))
var gannFanBearColors = array.from(input.color(color.new(color.maroon, 60), 'GannFan ↘     4/1:', inline='GannFan1', group=colorsGroup), input.color(color.new(color.maroon, 40), '2/1:', inline='GannFan1', group=colorsGroup), input.color(color.new(color.maroon, 0), '1/1:', inline='GannFan1', group=colorsGroup), input.color(color.new(color.maroon, 40), '1/2:', inline='GannFan1', group=colorsGroup), input.color(color.new(color.maroon, 60), '1/4:', inline='GannFan1', group=colorsGroup))
var gannFanBullColors = array.from(input.color(color.new(color.teal, 60), 'GannFan ↗     4/1:', inline='GannFan2', group=colorsGroup), input.color(color.new(color.teal, 40), '2/1:', inline='GannFan2', group=colorsGroup), input.color(color.new(color.teal, 0), '1/1:', inline='GannFan2', group=colorsGroup), input.color(color.new(color.teal, 40), '1/2:', inline='GannFan2', group=colorsGroup), input.color(color.new(color.teal, 60), '1/4:', inline='GannFan2', group=colorsGroup))
var gannRetBearColors = array.from(input.color(color.new(color.maroon, 60), 'GannRet ↘   12.5:', inline='GannRet1', group=colorsGroup), input.color(color.new(color.maroon, 40), '25.0:', inline='GannRet1', group=colorsGroup), input.color(color.new(color.maroon, 0), '50.0:', inline='GannRet1', group=colorsGroup), input.color(color.new(color.maroon, 40), '75.0:', inline='GannRet1', group=colorsGroup), input.color(color.new(color.maroon, 60), '87.5:', inline='GannRet1', group=colorsGroup))
var gannRetBullColors = array.from(input.color(color.new(color.teal, 60), 'GannRet ↗   12.5:', inline='GannRet2', group=colorsGroup), input.color(color.new(color.teal, 40), '25.0:', inline='GannRet2', group=colorsGroup), input.color(color.new(color.teal, 0), '50.0:', inline='GannRet2', group=colorsGroup), input.color(color.new(color.teal, 40), '75.0:', inline='GannRet2', group=colorsGroup), input.color(color.new(color.teal, 60), '87.5:', inline='GannRet2', group=colorsGroup))

fiboRetLineStyle = frStyle == 'Dashed' ? line.style_dashed : frStyle == 'Dotted' ? line.style_dotted : line.style_solid
fiboFanLineStyle = ffStyle == 'Dashed' ? line.style_dashed : ffStyle == 'Dotted' ? line.style_dotted : line.style_solid
gannRetLineStyle = grStyle == 'Dashed' ? line.style_dashed : grStyle == 'Dotted' ? line.style_dotted : line.style_solid
gannFanLineStyle = gfStyle == 'Dashed' ? line.style_dashed : gfStyle == 'Dotted' ? line.style_dotted : line.style_solid

var float highPrice = na
var float highestPrice = na
var float lowPrice = na
var float lowestPrice = na
var highBar = 0
var highestBar = 0
var lowBar = 0
var lowestBar = 0
var waitingFor = ''

var chartScale = 1.
var scales = array.new_float()

fiboRatios = array.from(.236, .382, .5, .618, .786)
fiboRatioLabels = array.from('23.6', '38.2', '50', '61.8', '78.6')
gannFanRatios = array.from(15. / 90, 26.25 / 90, 45. / 90, 63.75 / 90, 75. / 90)
gannFanRatioLabels = array.from('4:1', '2:1', '1:1', '1:2', '1:4')
gannRetRatios = array.from(.125, .25, .5, .75, .875)
gannRetRatioLabels = array.from('12.5', '25', '50', '75', '87.5')

var fiboRet1 = array.new_line()
var fiboRet2 = array.new_line()
var fiboRet3 = array.new_line()
var fiboRet4 = array.new_line()
var fiboRet5 = array.new_line()
var fiboRet6 = array.new_line()
var fiboRet7 = array.new_line()
var gannRet1 = array.new_line()
var gannRet2 = array.new_line()
var gannRet3 = array.new_line()
var gannRet4 = array.new_line()
var gannRet5 = array.new_line()
var gannRet6 = array.new_line()
var gannRet7 = array.new_line()
var fiboFan1 = array.new_line()
var fiboFan2 = array.new_line()
var fiboFan3 = array.new_line()
var fiboFan4 = array.new_line()
var fiboFan5 = array.new_line()
var fiboFan6 = array.new_line()
var fiboFan7 = array.new_line()
var gannFan1 = array.new_line()
var gannFan2 = array.new_line()
var gannFan3 = array.new_line()
var gannFan4 = array.new_line()
var gannFan5 = array.new_line()
var gannFan6 = array.new_line()
var gannFan7 = array.new_line()

var fiboRetLabel1 = array.new_label()
var fiboRetLabel2 = array.new_label()
var fiboRetLabel3 = array.new_label()
var fiboRetLabel4 = array.new_label()
var fiboRetLabel5 = array.new_label()
var fiboFanLabel1 = array.new_label()
var fiboFanLabel2 = array.new_label()
var fiboFanLabel3 = array.new_label()
var fiboFanLabel4 = array.new_label()
var fiboFanLabel5 = array.new_label()
var gannRetLabel1 = array.new_label()
var gannRetLabel2 = array.new_label()
var gannRetLabel3 = array.new_label()
var gannRetLabel4 = array.new_label()
var gannRetLabel5 = array.new_label()
var gannFanLabel1 = array.new_label()
var gannFanLabel2 = array.new_label()
var gannFanLabel3 = array.new_label()
var gannFanLabel4 = array.new_label()
var gannFanLabel5 = array.new_label()

var fiboRetLabels = array.new_label(5, na)
var fiboFanLabels = array.new_label(5, na)
var gannRetLabels = array.new_label(5, na)
var gannFanLabels = array.new_label(5, na)

var fiboFanSlopes = array.new_float(5, na)
var gannFanSlopes = array.new_float(5, na)

//}
// find proper chart scale, highest and lowest pivots and draw line sets {

// functions
calcScale(bar1, price1, bar2, price2) =>
    if array.size(scales) == 100000
        array.shift(scales)
    array.push(scales, math.abs(price2 - price1) / (bar2 - bar1))
    array.median(scales)

isHigh() =>
    isHigh = false
    bar = bar_index
    price = high
    if close < open
        for i = 1 to 10 by 1
            if close[i] < open[i]
                break
            else
                bar := price < high[i] ? bar_index - i : bar
                price := math.max(price, high[i])
                if close[i] == open[i]
                    continue
                if close[i] > open[i]
                    isHigh := true
                    break
    [isHigh, bar, price]
isLow() =>
    isLow = false
    bar = bar_index
    price = low
    if close > open
        for i = 1 to 10 by 1
            if close[i] > open[i]
                break
            else
                bar := price > low[i] ? bar_index - i : bar
                price := math.min(price, low[i])
                if close[i] == open[i]
                    continue
                if close[i] < open[i]
                    isLow := true
                    break
    [isLow, bar, price]

showLabel(setType, labelArray, arrayIndex, x, y, text_1, colour) =>
    if showFRLabels and setType == 'fret' or showGRLabels and setType == 'gret' or showFFLabels and setType == 'ffan' or showGFLabels and setType == 'gfan'
        label.delete(array.get(labelArray, arrayIndex))
        array.set(labelArray, arrayIndex, label.new(x, y, text_1, color=na, textcolor=colour))

saveLastLabels(setType, labelArray) =>
    if setType == 'fret'
        array.push(fiboRetLabel1, array.get(labelArray, 0))
        array.push(fiboRetLabel2, array.get(labelArray, 1))
        array.push(fiboRetLabel3, array.get(labelArray, 2))
        array.push(fiboRetLabel4, array.get(labelArray, 3))
        array.push(fiboRetLabel5, array.get(labelArray, 4))
    else if setType == 'gret'
        array.push(gannRetLabel1, array.get(labelArray, 0))
        array.push(gannRetLabel2, array.get(labelArray, 1))
        array.push(gannRetLabel3, array.get(labelArray, 2))
        array.push(gannRetLabel4, array.get(labelArray, 3))
        array.push(gannRetLabel5, array.get(labelArray, 4))
    else if setType == 'ffan'
        array.push(fiboFanLabel1, array.get(labelArray, 0))
        array.push(fiboFanLabel2, array.get(labelArray, 1))
        array.push(fiboFanLabel3, array.get(labelArray, 2))
        array.push(fiboFanLabel4, array.get(labelArray, 3))
        array.push(fiboFanLabel5, array.get(labelArray, 4))
    else if setType == 'gfan'
        array.push(gannFanLabel1, array.get(labelArray, 0))
        array.push(gannFanLabel2, array.get(labelArray, 1))
        array.push(gannFanLabel3, array.get(labelArray, 2))
        array.push(gannFanLabel4, array.get(labelArray, 3))
        array.push(gannFanLabel5, array.get(labelArray, 4))
    for i = 0 to 4 by 1
        array.set(labelArray, i, na)

drawLineSet(setType, ratioArray, ratioLabelArray, labelArray, lineArray1, lineArray2, lineArray3, lineArray4, lineArray5, lineArray6, lineArray7, bar1, price1, bar2, price2, extend, bullColors, bearColors, style) =>
    colors = array.copy(price1 < price2 ? bullColors : bearColors)
    barRange = price1 < price2 ? bar1 - bar2 : bar2 - bar1
    priceRange = price1 - price2
    levelPrice = 0.
    array.push(lineArray1, line.new(bar1, price1, bar2, price1, extend=extend or setType == 'ffan' or setType == 'gfan' ? extend.right : extend.none, color=array.get(colors, 2), style=style))
    levelPrice := price1 - (setType != 'gfan' ? priceRange * (1 - array.get(ratioArray, 0)) : barRange * math.tan(array.get(ratioArray, 0) * math.pi / 2) * chartScale)
    array.push(lineArray2, line.new(bar1, setType == 'ffan' or setType == 'gfan' ? price1 : levelPrice, bar2, levelPrice, extend=extend or setType == 'ffan' or setType == 'gfan' ? extend.right : extend.none, color=array.get(colors, 0), style=style))
    showLabel(setType, labelArray, 0, bar2, levelPrice, array.get(ratioLabelArray, 0), array.get(colors, 0))
    if setType == 'ffan'
        array.set(fiboFanSlopes, 0, (levelPrice - price1) / (bar2 - bar1))
    if setType == 'gfan'
        array.set(gannFanSlopes, 0, (levelPrice - price1) / (bar2 - bar1))
    levelPrice := price1 - (setType != 'gfan' ? priceRange * (1 - array.get(ratioArray, 1)) : barRange * math.tan(array.get(ratioArray, 1) * math.pi / 2) * chartScale)
    array.push(lineArray3, line.new(bar1, setType == 'ffan' or setType == 'gfan' ? price1 : levelPrice, bar2, levelPrice, extend=extend or setType == 'ffan' or setType == 'gfan' ? extend.right : extend.none, color=array.get(colors, 1), style=style))
    showLabel(setType, labelArray, 1, bar2, levelPrice, array.get(ratioLabelArray, 1), array.get(colors, 1))
    if setType == 'ffan'
        array.set(fiboFanSlopes, 1, (levelPrice - price1) / (bar2 - bar1))
    if setType == 'gfan'
        array.set(gannFanSlopes, 1, (levelPrice - price1) / (bar2 - bar1))
    levelPrice := price1 - (setType != 'gfan' ? priceRange * (1 - array.get(ratioArray, 2)) : barRange * math.tan(array.get(ratioArray, 2) * math.pi / 2) * chartScale)
    array.push(lineArray4, line.new(bar1, setType == 'ffan' or setType == 'gfan' ? price1 : levelPrice, bar2, levelPrice, extend=extend or setType == 'ffan' or setType == 'gfan' ? extend.right : extend.none, color=array.get(colors, 2), style=style))
    showLabel(setType, labelArray, 2, bar2, levelPrice, array.get(ratioLabelArray, 2), array.get(colors, 2))
    if setType == 'ffan'
        array.set(fiboFanSlopes, 2, (levelPrice - price1) / (bar2 - bar1))
    if setType == 'gfan'
        array.set(gannFanSlopes, 2, (levelPrice - price1) / (bar2 - bar1))
    levelPrice := price1 - (setType != 'gfan' ? priceRange * (1 - array.get(ratioArray, 3)) : barRange * math.tan(array.get(ratioArray, 3) * math.pi / 2) * chartScale)
    array.push(lineArray5, line.new(bar1, setType == 'ffan' or setType == 'gfan' ? price1 : levelPrice, bar2, levelPrice, extend=extend or setType == 'ffan' or setType == 'gfan' ? extend.right : extend.none, color=array.get(colors, 3), style=style))
    showLabel(setType, labelArray, 3, bar2, levelPrice, array.get(ratioLabelArray, 3), array.get(colors, 3))
    if setType == 'ffan'
        array.set(fiboFanSlopes, 3, (levelPrice - price1) / (bar2 - bar1))
    if setType == 'gfan'
        array.set(gannFanSlopes, 3, (levelPrice - price1) / (bar2 - bar1))
    levelPrice := price1 - (setType != 'gfan' ? priceRange * (1 - array.get(ratioArray, 4)) : barRange * math.tan(array.get(ratioArray, 4) * math.pi / 2) * chartScale)
    array.push(lineArray6, line.new(bar1, setType == 'ffan' or setType == 'gfan' ? price1 : levelPrice, bar2, levelPrice, extend=extend or setType == 'ffan' or setType == 'gfan' ? extend.right : extend.none, color=array.get(colors, 4), style=style))
    showLabel(setType, labelArray, 4, bar2, levelPrice, array.get(ratioLabelArray, 4), array.get(colors, 4))
    if setType == 'ffan'
        array.set(fiboFanSlopes, 4, (levelPrice - price1) / (bar2 - bar1))
    if setType == 'gfan'
        array.set(gannFanSlopes, 4, (levelPrice - price1) / (bar2 - bar1))
    array.push(lineArray7, line.new(bar1, setType == 'ffan' or setType == 'gfan' ? price1 : price2, setType == 'gfan' ? bar1 : bar2, price2, extend=extend or setType == 'ffan' or setType == 'gfan' ? extend.right : extend.none, color=array.get(colors, 2), style=style))

cutLastRets() =>
    index = array.size(fiboRet1) - 1
    if index > -1
        line.set_extend(array.get(fiboRet1, index), extend.none)
        line.set_extend(array.get(fiboRet2, index), extend.none)
        line.set_extend(array.get(fiboRet3, index), extend.none)
        line.set_extend(array.get(fiboRet4, index), extend.none)
        line.set_extend(array.get(fiboRet5, index), extend.none)
        line.set_extend(array.get(fiboRet6, index), extend.none)
        line.set_extend(array.get(fiboRet7, index), extend.none)
    index := array.size(gannRet1) - 1
    if index > -1
        line.set_extend(array.get(gannRet1, index), extend.none)
        line.set_extend(array.get(gannRet2, index), extend.none)
        line.set_extend(array.get(gannRet3, index), extend.none)
        line.set_extend(array.get(gannRet4, index), extend.none)
        line.set_extend(array.get(gannRet5, index), extend.none)
        line.set_extend(array.get(gannRet6, index), extend.none)
        line.set_extend(array.get(gannRet7, index), extend.none)

delUnneededLines() =>
    if array.size(fiboRet1) > frCount
        line.delete(array.shift(fiboRet1))
        line.delete(array.shift(fiboRet2))
        line.delete(array.shift(fiboRet3))
        line.delete(array.shift(fiboRet4))
        line.delete(array.shift(fiboRet5))
        line.delete(array.shift(fiboRet6))
        line.delete(array.shift(fiboRet7))
    if array.size(fiboRetLabel1) > frCount - 1 and frCount > 0
        label.delete(array.shift(fiboRetLabel1))
        label.delete(array.shift(fiboRetLabel2))
        label.delete(array.shift(fiboRetLabel3))
        label.delete(array.shift(fiboRetLabel4))
        label.delete(array.shift(fiboRetLabel5))
    if array.size(gannRet1) > grCount
        line.delete(array.shift(gannRet1))
        line.delete(array.shift(gannRet2))
        line.delete(array.shift(gannRet3))
        line.delete(array.shift(gannRet4))
        line.delete(array.shift(gannRet5))
        line.delete(array.shift(gannRet6))
        line.delete(array.shift(gannRet7))
    if array.size(gannRetLabel1) > grCount - 1 and grCount > 0
        label.delete(array.shift(gannRetLabel1))
        label.delete(array.shift(gannRetLabel2))
        label.delete(array.shift(gannRetLabel3))
        label.delete(array.shift(gannRetLabel4))
        label.delete(array.shift(gannRetLabel5))
    if array.size(fiboFan1) > ffCount
        line.delete(array.shift(fiboFan1))
        line.delete(array.shift(fiboFan2))
        line.delete(array.shift(fiboFan3))
        line.delete(array.shift(fiboFan4))
        line.delete(array.shift(fiboFan5))
        line.delete(array.shift(fiboFan6))
        line.delete(array.shift(fiboFan7))
    if array.size(fiboFanLabel1) > ffCount - 1 and ffCount > 0
        label.delete(array.shift(fiboFanLabel1))
        label.delete(array.shift(fiboFanLabel2))
        label.delete(array.shift(fiboFanLabel3))
        label.delete(array.shift(fiboFanLabel4))
        label.delete(array.shift(fiboFanLabel5))
    if array.size(gannFan1) > gfCount
        line.delete(array.shift(gannFan1))
        line.delete(array.shift(gannFan2))
        line.delete(array.shift(gannFan3))
        line.delete(array.shift(gannFan4))
        line.delete(array.shift(gannFan5))
        line.delete(array.shift(gannFan6))
        line.delete(array.shift(gannFan7))
    if array.size(gannFanLabel1) > gfCount - 1 and gfCount > 0
        label.delete(array.shift(gannFanLabel1))
        label.delete(array.shift(gannFanLabel2))
        label.delete(array.shift(gannFanLabel3))
        label.delete(array.shift(gannFanLabel4))
        label.delete(array.shift(gannFanLabel5))

// find highests and lowests and draw retracements and fans
[isH, hBar, hPrice] = isHigh()
if isH
    chartScale := calcScale(highBar, highPrice, hBar, hPrice)
    if waitingFor != 'l' and hPrice < highPrice
        highestBar := highBar
        highestPrice := highPrice
        cutLastRets()
        if frCount > 0
            saveLastLabels('fret', fiboRetLabels)
            drawLineSet('fret', fiboRatios, fiboRatioLabels, fiboRetLabels, fiboRet1, fiboRet2, fiboRet3, fiboRet4, fiboRet5, fiboRet6, fiboRet7, lowestBar, lowestPrice, highestBar, highestPrice, extendFR, fiboRetBullColors, fiboRetBearColors, fiboRetLineStyle)
        if ffCount > 0
            saveLastLabels('ffan', fiboFanLabels)
            drawLineSet('ffan', fiboRatios, fiboRatioLabels, fiboFanLabels, fiboFan1, fiboFan2, fiboFan3, fiboFan4, fiboFan5, fiboFan6, fiboFan7, lowestBar, lowestPrice, highestBar, highestPrice, extendFR, fiboFanBullColors, fiboFanBearColors, fiboFanLineStyle)
        if grCount > 0
            saveLastLabels('gret', gannRetLabels)
            drawLineSet('gret', gannRetRatios, gannRetRatioLabels, gannRetLabels, gannRet1, gannRet2, gannRet3, gannRet4, gannRet5, gannRet6, gannRet7, lowestBar, lowestPrice, highestBar, highestPrice, extendGR, gannRetBullColors, gannRetBearColors, gannRetLineStyle)
        if gfCount > 0
            saveLastLabels('gfan', gannFanLabels)
            drawLineSet('gfan', gannFanRatios, gannFanRatioLabels, gannFanLabels, gannFan1, gannFan2, gannFan3, gannFan4, gannFan5, gannFan6, gannFan7, highestBar, highestPrice, bar_index, low, extendGR, gannFanBullColors, gannFanBearColors, gannFanLineStyle)
        delUnneededLines()
        waitingFor := 'l'
        if showExtrema
            label.new(highestBar, highestPrice, '')
    highBar := hBar
    highPrice := hPrice
    highPrice
[isL, lBar, lPrice] = isLow()
if isL
    chartScale := calcScale(lowBar, lowPrice, lBar, lPrice)
    if waitingFor != 'h' and lPrice > lowPrice
        lowestBar := lowBar
        lowestPrice := lowPrice
        cutLastRets()
        if frCount > 0
            saveLastLabels('fret', fiboRetLabels)
            drawLineSet('fret', fiboRatios, fiboRatioLabels, fiboRetLabels, fiboRet1, fiboRet2, fiboRet3, fiboRet4, fiboRet5, fiboRet6, fiboRet7, highestBar, highestPrice, lowestBar, lowestPrice, extendFR, fiboRetBullColors, fiboRetBearColors, fiboRetLineStyle)
        if ffCount > 0
            saveLastLabels('ffan', fiboFanLabels)
            drawLineSet('ffan', fiboRatios, fiboRatioLabels, fiboFanLabels, fiboFan1, fiboFan2, fiboFan3, fiboFan4, fiboFan5, fiboFan6, fiboFan7, highestBar, highestPrice, lowestBar, lowestPrice, extendFR, fiboFanBullColors, fiboFanBearColors, fiboFanLineStyle)
        if grCount > 0
            saveLastLabels('gret', gannRetLabels)
            drawLineSet('gret', gannRetRatios, gannRetRatioLabels, gannRetLabels, gannRet1, gannRet2, gannRet3, gannRet4, gannRet5, gannRet6, gannRet7, highestBar, highestPrice, lowestBar, lowestPrice, extendGR, gannRetBullColors, gannRetBearColors, gannRetLineStyle)
        if gfCount > 0
            saveLastLabels('gfan', gannFanLabels)
            drawLineSet('gfan', gannFanRatios, gannFanRatioLabels, gannFanLabels, gannFan1, gannFan2, gannFan3, gannFan4, gannFan5, gannFan6, gannFan7, lowestBar, lowestPrice, bar_index, high, extendGR, gannFanBullColors, gannFanBearColors, gannFanLineStyle)
        delUnneededLines()
        waitingFor := 'h'
        if showExtrema
            label.new(lowestBar, lowestPrice, '', style=label.style_label_up)
    lowBar := lBar
    lowPrice := lPrice
    lowPrice

// extend latest retracements
index = array.size(fiboRet1) - 1
if index > -1
    line.set_x2(array.get(fiboRet1, index), bar_index)
    line.set_x2(array.get(fiboRet2, index), bar_index)
    line.set_x2(array.get(fiboRet3, index), bar_index)
    line.set_x2(array.get(fiboRet4, index), bar_index)
    line.set_x2(array.get(fiboRet5, index), bar_index)
    line.set_x2(array.get(fiboRet6, index), bar_index)
    line.set_x2(array.get(fiboRet7, index), bar_index)
index := array.size(gannRet1) - 1
if index > -1
    line.set_x2(array.get(gannRet1, index), bar_index)
    line.set_x2(array.get(gannRet2, index), bar_index)
    line.set_x2(array.get(gannRet3, index), bar_index)
    line.set_x2(array.get(gannRet4, index), bar_index)
    line.set_x2(array.get(gannRet5, index), bar_index)
    line.set_x2(array.get(gannRet6, index), bar_index)
    line.set_x2(array.get(gannRet7, index), bar_index)

// relocate labels
for i = 0 to 4 by 1
    if showFRLabels
        label.set_x(array.get(fiboRetLabels, i), bar_index + 2)
    if showGRLabels
        label.set_x(array.get(gannRetLabels, i), bar_index + 2)
    if showFFLabels
        fiboFanLabel = array.get(fiboFanLabels, i)
        label.set_x(fiboFanLabel, bar_index + 2 * (1 + i))
        label.set_y(fiboFanLabel, (waitingFor == 'l' ? lowestPrice : highestPrice) + array.get(fiboFanSlopes, i) * (bar_index + 2 * (1 + i) - (waitingFor == 'l' ? lowestBar : highestBar)))
    if showGFLabels
        gannFanLabel = array.get(gannFanLabels, i)
        label.set_x(gannFanLabel, bar_index + 2 * (5 - i))
        label.set_y(gannFanLabel, (waitingFor == 'h' ? lowestPrice : highestPrice) + array.get(gannFanSlopes, i) * (bar_index + 2 * (5 - i) - (waitingFor == 'h' ? lowestBar : highestBar)))
//label.set_y(gannFanLabel, label.get_y(gannFanLabel) + tan(array.get(gannFanRatios, i) * math.pi / 2) * chartScale * (waitingFor == "h" ? 1 : waitingFor == "l" ? -1 : 0))

//}


depthTooltip = "The minimum number of bars that will be taken into account when calculating the indicator."
depth = input.int(10, "Depth", minval = 1, tooltip = depthTooltip)
typeP = input.string("Original", "Type", options = ["Original", "Schiff", "Modified Schiff", "Inside"])
backgroundTransparency = input.int(85, "Background Transparency", minval = 0, maxval = 100)

isExtended = input.bool(false, "Extend left")
extendLine = isExtended ? extend.both : extend.right 

medianColor = input(#f44336, "Median line                 ", inline = "ML")
medianWidth = input.int(1, "", minval = 1, inline = "ML")
medianStyle = input.string("Solid", "", options = ["Dashed", "Dotted", "Solid"], inline = "ML")

var line lineLast = na
var int iLast = 0
var int iPrev = 0
var float pLast = 0
var isHighLast = false // otherwise the last pivot is a low pivot

var iPrevPvt = 0
var pPrevPvt = 0.
var iLastPvt = 0
var pLastPvt = 0.

pivots(src, length, isHigh) =>
    l2 = length * 2
    p  = nz(src[length])
    isFound = true
    for i = 0 to l2
        if isHigh and src[i] > p
            isFound := false
        if not isHigh and src[i] < p
            isFound := false
    if isFound
        [bar_index[length], p]
    else
        [int(na), float(na)]

[iH, pH] = pivots(high, depth / 2, true )
[iL, pL] = pivots(low , depth / 2, false)

pivotFound(isHigh, index, price) =>
    if isHighLast == isHigh and not na(lineLast) // same direction
        if isHighLast ? price > pLast : price < pLast
            line.set_xy2(lineLast, index, price)
            [lineLast, isHighLast]
        else
            [line(na), bool(na)]
    else // reverse the direction (or create the very first line)
        id = line.new(iLast, pLast, index, price, color=na)
        [id, isHigh]

if not na(iH)
    [id, isHigh] = pivotFound(true, iH, pH)
    if not na(id)
        if id != lineLast
            iPrevPvt := line.get_x1(lineLast)
            pPrevPvt := line.get_y1(lineLast)
            iLastPvt := line.get_x2(lineLast)
            pLastPvt := line.get_y2(lineLast)
            line.delete(lineLast)
        lineLast := id
        isHighLast := isHigh
        iPrev := iLast
        iLast := iH
        pLast := pH
else
    if not na(iL)
        [id, isHigh] = pivotFound(false, iL, pL)
        if not na(id)
            if id != lineLast
                iPrevPvt := line.get_x1(lineLast)
                pPrevPvt := line.get_y1(lineLast)
                iLastPvt := line.get_x2(lineLast)
                pLastPvt := line.get_y2(lineLast)
                line.delete(lineLast)
            lineLast := id
            isHighLast := isHigh
            iPrev := iLast
            iLast := iL
            pLast := pL

iPrev2Pvt = ta.valuewhen(ta.change(iPrevPvt), iPrevPvt, 1)
pPrev2Pvt = ta.valuewhen(ta.change(pPrevPvt), pPrevPvt, 1)

getMedianData(type) =>
    var iStartMedian = 0
    var pStartMedian = 0.
    var iEndMedian = 0
    var pEndMedian = 0.
    
    iEndMedian := math.floor(math.avg(iPrevPvt, iLastPvt))
    pEndMedian := math.avg(pPrevPvt, pLastPvt)

    if type == "Original"
        iStartMedian := iPrev2Pvt
        pStartMedian := pPrev2Pvt
        offsetEnd = (iLastPvt - iPrevPvt) % 2 != 0 ? (pStartMedian - pEndMedian) * 0.5 / math.abs(iStartMedian - iEndMedian - 0.5) : 0 // add offset if iEndMedian was rounded (floor) to whole bar_index
        pEndMedian += offsetEnd

    else if type == "Schiff"
        iStartMedian := iPrev2Pvt
        pStartMedian := math.avg(pPrevPvt, pPrev2Pvt)
        offsetEnd = (iLastPvt - iPrevPvt) % 2 != 0 ? (pStartMedian - pEndMedian) * 0.5 / math.abs(iStartMedian - iEndMedian - 0.5) : 0
        pEndMedian += offsetEnd

    else if type == "Modified Schiff"
        iStartMedian := math.floor(math.avg(iPrevPvt, iPrev2Pvt))
        pStartMedian := math.avg(pPrevPvt, pPrev2Pvt)
        offset = (pStartMedian - pEndMedian) * 0.5 / math.abs(iStartMedian - iEndMedian - 0.5) 
        pStartMedian += (iPrev2Pvt - iPrevPvt) % 2 != 0 ? offset : 0
        pEndMedian += (iLastPvt - iPrevPvt) % 2 != 0 ? offset : 0

    else if type == "Inside"
        iStartMedian := line.get_x2(lineLast)
        slopeInside = (math.avg(pPrevPvt, pPrev2Pvt) - pLastPvt) / (math.avg(iPrevPvt, iPrev2Pvt) - iLastPvt)
        pStartMedian := slopeInside *(iStartMedian - math.avg(iPrevPvt, iLastPvt)) + pEndMedian
        offsetEnd = (iLastPvt - iPrevPvt) % 2 != 0 ? (pStartMedian - pEndMedian) * 0.5 / math.abs(iStartMedian - iEndMedian - 0.5) : 0
        pEndMedian -= offsetEnd
        
    [iStartMedian, pStartMedian, iEndMedian, pEndMedian]


drawPitchforkLine(iStart, pStart, iEnd, pEnd, color, width, style, extend) =>
    _style = style == "Solid" ? line.style_solid : style == "Dotted" ? line.style_dotted : line.style_dashed
    var id = line.new(iStart, pStart, iEnd, pEnd, xloc.bar_index, extend, color, _style, width)
    line.set_xy1(id, iStart, pStart)
    line.set_xy2(id, iEnd, pEnd)
    id


[iStartMedian, pStartMedian, iEndMedian, pEndMedian] = getMedianData(typeP)


processLevel(show, level, color, width, style, lineIdOther1, lineIdOther2) =>
    if show
        iEndMedianWithotRound = math.avg(iPrevPvt, iLastPvt)
        pEndMedianWithotOffset = math.avg(pPrevPvt, pLastPvt)
        iDiff = math.abs(iPrevPvt - iLastPvt) / 2
        pDiff = math.abs(pPrevPvt - pLastPvt) / 2
        slopeMedian = (pEndMedian - pStartMedian) / (iEndMedian - iStartMedian)
        
    
    	tempUp = pPrevPvt > pLastPvt ? iEndMedianWithotRound - iDiff * level : iEndMedianWithotRound + iDiff * level
    	isIntBarIndexUp =  math.ceil(tempUp) - tempUp

        iStartUp = math.ceil(tempUp)
        iEndUp = iStartUp + 1
    
        pStartUp = pEndMedianWithotOffset + pDiff * level
        pEndUp = slopeMedian * (iEndUp - tempUp) + pStartUp
        offsetUp = isIntBarIndexUp == 0 ? 0 : (pStartUp - pEndUp) * (isIntBarIndexUp)  / (tempUp - iEndUp)
        
        
        tempDown = pPrevPvt > pLastPvt ? iEndMedianWithotRound + iDiff * level : iEndMedianWithotRound - iDiff * level
        isIntBarIndexDown =  tempDown - math.ceil(tempDown)
        
        iStartDown = math.ceil(tempDown)
        iEndDown = iStartDown + 1
    
        pStartDown = pEndMedianWithotOffset - pDiff * level 
        pEndDown = slopeMedian * (iEndDown - tempDown) + pStartDown
        offsetDown = isIntBarIndexDown == 0 ? 0 : (pStartDown - pEndDown) * (isIntBarIndexDown) / (tempDown - iEndDown)


        lineId1 = drawPitchforkLine(iStartUp, pStartUp + offsetUp, iEndUp, pEndUp, color, width, style, extendLine)
        lineId2 = drawPitchforkLine(iStartDown, pStartDown - offsetDown, iEndDown, pEndDown, color, width, style, extendLine)
        
        if not na(lineIdOther1)
            linefill.new(lineId1, lineIdOther1, color = color.new(color, backgroundTransparency))
            linefill.new(lineId2, lineIdOther2, color = color.new(color, backgroundTransparency))
        else
            linefill.new(lineId1, lineId2, color = color.new(color, backgroundTransparency))
        [lineId1, lineId2]
    else
        [lineIdOther1, lineIdOther2]

show_0_25 = input(false, "", inline = "level0")
value_0_25 = input.float(0.25, "", minval = .0, step = .1, inline = "level0")
color_0_25 = input(#ffb74d, "", inline = "level0")
width_0_25 = input.int(1, "", minval = 1, inline = "level0")
style_0_25 = input.string("Solid", "", options = ["Dashed", "Dotted", "Solid"], inline = "level0")
    
show_0_382 = input(false, "", inline = "level1")
value_0_382 = input.float(0.382, "", minval = .0, step = .1, inline = "level1")
color_0_382 = input(#81c784, "", inline = "level1")
width_0_382 = input.int(1, "", minval = 1, inline = "level1")
style_0_382 = input.string("Solid", "", options = ["Dashed", "Dotted", "Solid"], inline = "level1")

show_0_5 = input(true, "", inline = "level2")
value_0_5 = input.float(0.5, "", minval = .0, step = .1, inline = "level2")
color_0_5 = input(#4caf50, "", inline = "level2")
width_0_5 = input.int(1, "", minval = 1, inline = "level2")
style_0_5 = input.string("Solid", "", options = ["Dashed", "Dotted", "Solid"], inline = "level2")

show_0_618 = input(false, "", inline = "level3")
value_0_618 = input.float(0.618, "", minval = .0, step = .1, inline = "level3")
color_0_618 = input(#009688, "", inline = "level3")
width_0_618 = input.int(1, "", minval = 1, inline = "level3")
style_0_618 = input.string("Solid", "", options = ["Dashed", "Dotted", "Solid"], inline = "level3")

show_0_75 = input(false, "", inline = "level4")
value_0_75 = input.float(0.75, "", minval = .0, step = .1, inline = "level4")
color_0_75 = input(#64b5f6, "", inline = "level4")
width_0_75 = input.int(1, "", minval = 1, inline = "level4")
style_0_75 = input.string("Solid", "", options = ["Dashed", "Dotted", "Solid"], inline = "level4")

show_1 = input(true, "", inline = "level5")
value_1 = input.float(1, "", minval = .0, step = .1, inline = "level5")
color_1 = input(#2962ff, "", inline = "level5")
width_1 = input.int(1, "", minval = 1, inline = "level5")
style_1 = input.string("Solid", "", options = ["Dashed", "Dotted", "Solid"], inline = "level5")

show_1_5 = input(false, "", inline = "level6")
value_1_5 = input.float(1.5, "", minval = .0, step = .1, inline = "level6")
color_1_5 = input(#9c27b0, "", inline = "level6")
width_1_5 = input.int(1, "", minval = 1, inline = "level6")
style_1_5 = input.string("Solid", "", options = ["Dashed", "Dotted", "Solid"], inline = "level6")

show_1_75 = input(false, "", inline = "level7")
value_1_75 = input.float(1.75, "", minval = .0, step = .1, inline = "level7")
color_1_75 = input(#e91e63, "", inline = "level7")
width_1_75 = input.int(1, "", minval = 1, inline = "level7")
style_1_75 = input.string("Solid", "", options = ["Dashed", "Dotted", "Solid"], inline = "level7")

show_2 = input(false, "", inline = "level8")
value_2 = input.float(2, "", minval = .0, step = .1, inline = "level8") 
color_2 = input(#e91e63, "", inline = "level8")
width_2 = input.int(1, "", minval = 1, inline = "level8")
style_2 = input.string("Solid", "", options = ["Dashed", "Dotted", "Solid"], inline = "level8")
    

if typeP == "Inside"
    drawPitchforkLine(iEndMedian, pEndMedian, iStartMedian, pStartMedian, medianColor, medianWidth, medianStyle, extendLine)
else
    drawPitchforkLine(iStartMedian, pStartMedian, iEndMedian, pEndMedian, medianColor, medianWidth, medianStyle, extendLine)
    
drawPitchforkLine(iPrevPvt, pPrevPvt, iLastPvt, pLastPvt, medianColor, medianWidth, medianStyle, extend.none)

if typeP != "Original"
    drawPitchforkLine(iPrev2Pvt, pPrev2Pvt, iPrevPvt, pPrevPvt, medianColor, medianWidth, medianStyle, extend.none)

if typeP == "Inside"
    drawPitchforkLine(math.round(math.avg(iPrevPvt, iPrev2Pvt)), math.avg(pPrevPvt, pPrev2Pvt), iLastPvt, pLastPvt, medianColor, medianWidth, medianStyle, extend.none)

[lineId0_1, lineId0_2] = processLevel(show_0_25, value_0_25, color_0_25, width_0_25, style_0_25, line(na), line(na))
[lineId1_1, lineId1_2] = processLevel(show_0_382, value_0_382, color_0_382, width_0_382, style_0_382, lineId0_1, lineId0_2)
[lineId2_1, lineId2_2] = processLevel(show_0_5, value_0_5, color_0_5, width_0_5, style_0_5, lineId1_1, lineId1_2)
[lineId3_1, lineId3_2] = processLevel(show_0_618, value_0_618, color_0_618, width_0_618, style_0_618, lineId2_1, lineId2_2)
[lineId4_1, lineId4_2] = processLevel(show_0_75, value_0_75, color_0_75, width_0_75, style_0_75, lineId3_1, lineId3_2)
[lineId5_1, lineId5_2] = processLevel(show_1, value_1, color_1, width_1, style_1, lineId4_1, lineId4_2)
[lineId6_1, lineId6_2] = processLevel(show_1_5, value_1_5, color_1_5, width_1_5, style_1_5, lineId5_1, lineId5_2)
[lineId7_1, lineId7_2] = processLevel(show_1_75, value_1_75, color_1_75, width_1_75, style_1_75, lineId6_1, lineId6_2)
[lineId8_1, lineId8_2] = processLevel(show_2, value_2, color_2, width_2, style_2, lineId7_1, lineId7_2)

len = input.int(20, minval=1, title="Length")
src = input(high, title="Source")
offset = input.int(title="Offset", defval=0, minval=-500, maxval=500)
out = ta.sma(src, len)
plot(out, color=color.blue, title="MA", offset=offset)

ma(source, length, type) =>
    switch type
        "SMA" => 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)

typeMA = input.string(title = "Method", defval = "SMA", options=["SMA", "EMA", "SMMA (RMA)", "WMA", "VWMA"], group="Smoothing")
smoothingLength = input.int(title = "Length", defval = 5, minval = 1, maxval = 100, group="Smoothing")

smoothingLine = ma(out, smoothingLength, typeMA)
plot(smoothingLine, title="Smoothing Line", color=#f37f20, offset=offset, display=display.none)




