Support and Resistance
// This source code is subject to the terms of the Mozilla Public License 2.0 at https://mozilla.org/MPL/2.0/
// © Rain5369

//@version=5
indicator('Support and Resistance', shorttitle = 'S/R', overlay=true, max_bars_back=1000)

display_reversal_price = false
display_cumulative_volume = false
display_reversal_price_change = false

length = input.int(25, 'Length : PC',inline = 'length', group='Basic Settings', tooltip='Pivot length. Use higher values for having lines connected to more significant pivots')
lookback = input.int(3, minval=1, title='Lookback : PC', inline = 'lookback', group='Basic Settings', tooltip='Number of lines connecting a pivot high/low to display')
Slope = input.float(1, minval=-2, maxval=2, step=0.1,title = 'Deviation : PC' ,inline = 'Slope', group='Basic Settings', tooltip='Allows to multiply the linear regression slope by a number within -2 and 2')

extend_to_last_bar = input(title='Extend to Last Bar', group='Line Settings', defval=false)
i_extendLines = input(false, 'Extend to current bar', group='Line Settings')
extendL2 = input.bool(false, 'Parallel lines Left?', group='Line Settings')
var extend2 = extend.right
if extendL2
    extend2 := extend.both
    extend2

showpar = input.bool(false, 'Show Parallel Lines',group='Line Settings')
//Style
ph_col = input.color(color.red, 'High Lines Color', group='Line Colors', inline='col1')
pl_col = input.color(color.green, 'Low Lines Color', group='Line Colors', inline='col1')
line_color = input(title='zigzag Colour', defval=color.new(color.white,95), group='Line Colors')

devTooltip = 'Deviation is a multiplier that affects how much the price should deviate from the previous pivot in order for the bar to become a new pivot.'
depthTooltip = 'The minimum number of bars that will be taken into account when calculating the indicator.'
// pivots threshold
threshold_multiplier = input.float(title=': ZZ', defval=3, minval=0, tooltip=devTooltip,inline = 'Slope', group='Basic Settings')
dev_threshold = ta.atr(10) / close * 100 * threshold_multiplier
depth = input.int(title=':ZZ', defval=25, minval=1, tooltip=depthTooltip,inline = 'length', group='Basic Settings')


pivots(src, length, isHigh) =>
    p = nz(src[length])
    if length == 0
        [bar_index, p]
    else
        isFound = true
        for i = 0 to length - 1 by 1
            if isHigh and src[i] > p
                isFound := false
                isFound
            if not isHigh and src[i] < p
                isFound := false
                isFound
        for i = length + 1 to 2 * length by 1
            if isHigh and src[i] >= p
                isFound := false
                isFound
            if not isHigh and src[i] <= p
                isFound := false
                isFound
        if isFound and length * 2 <= bar_index
            [bar_index[length], p]
        else
            [int(na), float(na)]
[iH, pH] = pivots(high, math.floor(depth / 2), true)
[iL, pL] = pivots(low, math.floor(depth / 2), false)

calc_dev(base_price, price) =>
    100 * (price - base_price) / base_price

price_rotation_aggregate(price_rotation, pLast, cum_volume) =>
    str = ''
    if display_reversal_price
        str := str + str.tostring(pLast, format.mintick) + ' '
        str
    if display_reversal_price_change
        str := str + price_rotation + ' '
        str
    if display_cumulative_volume
        str := str + '\n' + cum_volume
        str
    str

caption(isHigh, iLast, pLast, price_rotation, cum_volume) =>
    price_rotation_str = price_rotation_aggregate(price_rotation, pLast, cum_volume)
    if display_reversal_price or display_reversal_price_change or display_cumulative_volume
        if not isHigh
            label.new(iLast, pLast, text=price_rotation_str, style=label.style_none, yloc=yloc.belowbar, textcolor=color.red)
        else
            label.new(iLast, pLast, text=price_rotation_str, style=label.style_none, yloc=yloc.abovebar, textcolor=color.green)

price_rotation_diff(pLast, price) =>
    if display_reversal_price_change
        tmp_calc = price - pLast
        str = (math.sign(tmp_calc) > 0 ? '+' : '-') + str.tostring(math.abs(tmp_calc) * 100 / pLast, format.percent)
        str := '(' + str + ')'
        str
    else
        ''
volume_sum(index1, index2) =>
    float CVI = 0
    for i = index1 + 1 to index2 by 1
        CVI := CVI + volume[bar_index - i]
        CVI
    str.tostring(CVI, format.volume)

var line lineLast = na
var label labelLast = na
var int iLast = 0
var float pLast = 0
var bool isHighLast = true  // otherwise the last pivot is a low pivot
var int linesCount = 0

pivotFound(dev, isHigh, index, price) =>
    if isHighLast == isHigh and not na(lineLast)
        // same direction
        if isHighLast ? price > pLast : price < pLast
            if linesCount <= 1
                line.set_xy1(lineLast, index, price)
            line.set_xy2(lineLast, index, price)
            label.set_xy(labelLast, index, price)
            label.set_text(labelLast, price_rotation_aggregate(price_rotation_diff(line.get_y1(lineLast), price), price, volume_sum(line.get_x1(lineLast), index)))
            [lineLast, labelLast, isHighLast, false]
        else
            [line(na), label(na), bool(na), false]
    else
        // reverse the direction (or create the very first line)
        if na(lineLast)
            id = line.new(index, price, index, price, color=line_color, width=1)
            lb = caption(isHigh, index, price, price_rotation_diff(pLast, price), volume_sum(index, index))
            [id, lb, isHigh, true]
        else
            // price move is significant
            if math.abs(dev) >= dev_threshold
                id = line.new(iLast, pLast, index, price, color=line_color, width=1)
                lb = caption(isHigh, index, price, price_rotation_diff(pLast, price), volume_sum(iLast, index))
                [id, lb, isHigh, true]
            else
                [line(na), label(na), bool(na), false]


if not na(iH) and not na(iL) and iH == iL
    dev1 = calc_dev(pLast, pH)
    [id2, lb2, isHigh2, isNew2] = pivotFound(dev1, true, iH, pH)
    if isNew2
        linesCount := linesCount + 1
        linesCount
    if not na(id2)
        lineLast := id2
        labelLast := lb2
        isHighLast := isHigh2
        iLast := iH
        pLast := pH
        pLast
    dev2 = calc_dev(pLast, pL)
    [id1, lb1, isHigh1, isNew1] = pivotFound(dev2, false, iL, pL)
    if isNew1
        linesCount := linesCount + 1
        linesCount
    if not na(id1)
        lineLast := id1
        labelLast := lb1
        isHighLast := isHigh1
        iLast := iL
        pLast := pL
        pLast
else
    if not na(iH)
        dev1 = calc_dev(pLast, pH)
        [id, lb, isHigh, isNew] = pivotFound(dev1, true, iH, pH)
        if isNew
            linesCount := linesCount + 1
            linesCount
        if not na(id)
            lineLast := id
            labelLast := lb
            isHighLast := isHigh
            iLast := iH
            pLast := pH
            pLast
    else
        if not na(iL)
            dev2 = calc_dev(pLast, pL)
            [id, lb, isHigh, isNew] = pivotFound(dev2, false, iL, pL)
            if isNew
                linesCount := linesCount + 1
                linesCount
            if not na(id)
                lineLast := id
                labelLast := lb
                isHighLast := isHigh
                iLast := iL
                pLast := pL
                pLast

var line extend_line = na
var label extend_label = na
if extend_to_last_bar == true and barstate.islast == true
    isHighLastPoint = not isHighLast
    curSeries = isHighLastPoint ? high : low
    if na(extend_line) and na(extend_label)
        extend_line := line.new(line.get_x2(lineLast), line.get_y2(lineLast), bar_index, curSeries, color=line_color, width=1)
        extend_label := caption(not isHighLast, bar_index, curSeries, price_rotation_diff(line.get_y2(lineLast), curSeries), volume_sum(line.get_x2(lineLast), bar_index))
        extend_label

    line.set_xy1(extend_line, line.get_x2(lineLast), line.get_y2(lineLast))
    line.set_xy2(extend_line, bar_index, curSeries)
    label.set_xy(extend_label, bar_index, curSeries)
    price_rotation = price_rotation_diff(line.get_y1(extend_line), curSeries)
    volume_cum = volume_sum(line.get_x1(extend_line), bar_index)
    label.set_text(extend_label, price_rotation_aggregate(price_rotation, curSeries, volume_cum))
    label.set_textcolor(extend_label, isHighLastPoint ? color.green : color.red)
    label.set_yloc(extend_label, yloc=isHighLastPoint ? yloc.abovebar : yloc.belowbar)
/////////


ShowSupResZones = input(false, title='Factal Support/Resistance',group='Line Settings')

InvertColors = true

TF1 = timeframe.period
TF1_Menu = 'S/R Zones'
TF1_VolMA1Input = 6


// S/R - Current Time Frame = Time Frame 1 = TF1

TF1_Vol = request.security(syminfo.tickerid, TF1, volume)
TF1_VolMA = ta.sma(TF1_Vol, TF1_VolMA1Input)
TF1_High = request.security(syminfo.tickerid, TF1, high)
TF1_Low = request.security(syminfo.tickerid, TF1, low)
TF1_Open = request.security(syminfo.tickerid, TF1, open)
TF1_Close = request.security(syminfo.tickerid, TF1, close)

TF1_Up = TF1_High[3] > TF1_High[4] and TF1_High[4] > TF1_High[5] and TF1_High[2] < TF1_High[3] and TF1_High[1] < TF1_High[2] and TF1_Vol[3] > TF1_VolMA[3]  // or volume[3] > VolMA2Current[3])
TF1_Down = TF1_Low[3] < TF1_Low[4] and TF1_Low[4] < TF1_Low[5] and TF1_Low[2] > TF1_Low[3] and TF1_Low[1] > TF1_Low[2] and TF1_Vol[3] > TF1_VolMA[3]  // or volume[3] > VolMA2Current[3])


TF1_CalcFractalUp() =>
    TF1_FractalUp = 0.0
    TF1_FractalUp := TF1_Up ? TF1_High[3] : TF1_FractalUp[1]
    TF1_FractalUp

TF1_CalcFractalDown() =>
    TF1_FractalDown = 0.0
    TF1_FractalDown := TF1_Down ? TF1_Low[3] : TF1_FractalDown[1]
    TF1_FractalDown


TF1_FractalUp = request.security(syminfo.tickerid, TF1, TF1_CalcFractalUp())
TF1_FractalDown = request.security(syminfo.tickerid, TF1, TF1_CalcFractalDown())


// Zones - Current Time Frame = Time Frame 1 = TF1

// Fractal Up Zones

TF1_CalcFractalUpLowerZone() =>
    TF1_FractalUpLowerZone = 0.0
    TF1_FractalUpLowerZone := TF1_Up and TF1_Close[3] > TF1_Open[3] ? TF1_Close[3] : TF1_Up and TF1_Close[3] < TF1_Open[3] ? TF1_Open[3] : TF1_FractalUpLowerZone[1]
    TF1_FractalUpLowerZone

TF1_CalcFractalUpUpperZone() =>
    TF1_FractalUpUpperZone = 0.0
    TF1_FractalUpUpperZone := TF1_Up and TF1_Close[3] > TF1_Open[3] ? TF1_High[3] - TF1_Close[3] + TF1_High[3] : TF1_Up and TF1_Close[3] < TF1_Open[3] ? TF1_High[3] - TF1_Open[3] + TF1_High[3] : TF1_FractalUpUpperZone[1]
    TF1_FractalUpUpperZone


TF1_FractalUpLowerZone = request.security(syminfo.tickerid, TF1, TF1_CalcFractalUpLowerZone())
TF1_FractalUpUpperZone = request.security(syminfo.tickerid, TF1, TF1_CalcFractalUpUpperZone())

TF1_ResistanceUpperZone = TF1_FractalUpUpperZone
TF1_ResistanceLowerZone = TF1_FractalUpLowerZone


// Fractal Down Zones


TF1_CalcFractalDownUpperZone() =>
    TF1_FractalDownUpperZone = 0.0
    TF1_FractalDownUpperZone := TF1_Down and TF1_Close[3] > TF1_Open[3] ? TF1_Open[3] : TF1_Down and TF1_Close[3] < TF1_Open[3] ? TF1_Close[3] : TF1_FractalDownUpperZone[1]
    TF1_FractalDownUpperZone


TF1_CalcFractalDownLowerZone() =>
    TF1_FractalDownLowerZone = 0.0
    TF1_FractalDownLowerZone := TF1_Down and TF1_Close[3] > TF1_Open[3] ? TF1_Low[3] + TF1_Low[3] - TF1_Open[3] : TF1_Down and TF1_Close[3] < TF1_Open[3] ? TF1_Low[3] + TF1_Low[3] - TF1_Close[3] : TF1_FractalDownLowerZone[1]
    TF1_FractalDownLowerZone


TF1_FractalDownLowerZone = request.security(syminfo.tickerid, TF1, TF1_CalcFractalDownLowerZone())
TF1_FractalDownUpperZone = request.security(syminfo.tickerid, TF1, TF1_CalcFractalDownUpperZone())


TF1_SupportUpperZone = TF1_FractalDownUpperZone
TF1_SupportLowerZone = TF1_FractalDownLowerZone

// Colors - Current Time Frame = Time Frame 1 = TF1


TF1_ResistanceColor = not InvertColors ? #FF00008C : #00FF00E6  // red  : lime
TF1_SupportColor = not InvertColors ? #00FF00E6 : #FF00008C  // lime : red


TF1_ResZoneColor = TF1_FractalUp != TF1_FractalUp[1] ? na : color.red
TF1_ResZoneColorInverted = TF1_FractalUp != TF1_FractalUp[1] ? na : color.lime


TF1_SupZoneColor = TF1_FractalDown != TF1_FractalDown[1] ? na : color.lime
TF1_SupZoneColorInverted = TF1_FractalDown != TF1_FractalDown[1] ? na : color.red


TF1_ResistanceZonesColor = not InvertColors and TF1_Menu == 'S/R Zones' ? TF1_ResZoneColor : InvertColors and TF1_Menu == 'S/R Zones' ? TF1_ResZoneColorInverted : na  // red  : lime
TF1_SupportZonesColor = not InvertColors and TF1_Menu == 'S/R Zones' ? TF1_SupZoneColor : InvertColors and TF1_Menu == 'S/R Zones' ? TF1_SupZoneColorInverted : na  // lime : red


// S/R & S/R Zone Plots - Current Time Frame = Time Frame 1 = TF1

plot(ShowSupResZones ? TF1_FractalUp : na, 'Current Timeframe - Resistance', color=TF1_ResistanceColor, linewidth=1, offset=-3, style=plot.style_circles, join=false)
plot(ShowSupResZones ? TF1_FractalDown : na, 'Current Timeframe - Support', color=TF1_SupportColor, linewidth=1, offset=-3, style=plot.style_circles, join=false)

i_drawLines = input(true, 'Draw lines or just dots',group='Line Settings')
i_lineCount = input(10, ': ZZ',inline = 'lookback', group='Basic Settings')

i_showFirst = true
i_firstDepth = depth
i_firstDeviation = dev_threshold
i_firstColor = input(color.white, 'Extented line Colour', group='Line Colors')



var zigZagArray = array.new_line()
var zigZagLabelArray = array.new_label()

f_zigzag(_depth, _deviation) =>
    var lw = 1
    var hg = 1
    lw := lw + 1
    hg := hg + 1
    lowestValue = -ta.lowestbars(_depth)
    highestValue = -ta.highestbars(_depth)
    lowing = lw == lowestValue or low - low[lowestValue] > _deviation * syminfo.mintick
    highing = hg == highestValue or high[highestValue] - high > _deviation * syminfo.mintick
    lh = ta.barssince(not highing)
    ll = ta.barssince(not lowing)
    down = lh > ll
    lower = low[lw] > low[lowestValue]
    higher = high[hg] < high[highestValue]
    if lw != lowestValue and (not down[1] or lower)
        lw := lowestValue < hg ? lowestValue : 0
        lw
    if hg != highestValue and (down[1] or higher)
        hg := highestValue < lw ? highestValue : 0
        hg
    x1 = down ? lw : hg
    y1 = down ? low[lw] : high[hg]
    lb = down ? label.style_label_up : label.style_label_down
    [down != down[1], x1, y1, lw, hg, down]


f_drawLine_p(_x1, _x2, _yValue, _lineColor) =>
    line.new(x1=_x1, y1=_yValue, x2=_x2, y2=_yValue, color=_lineColor, style=line.style_solid, width=1)

f_drawLabel(_x, _y, _textColor) =>
    label.new(_x, _y, '         ' + str.tostring(_y), xloc.bar_index, yloc.price, #00000000, label.style_none, _textColor)

f_extendArray(_lineArray, _extendLines) =>
    if array.size(_lineArray) > 0
        for _i = array.size(_lineArray) - 1 to 0 by 1

            x2 = line.get_x2(array.get(_lineArray, _i))
            yValue = line.get_y1(array.get(_lineArray, _i))

            if _extendLines or bar_index - 1 == x2 and not(high > yValue and low < yValue)
                line.set_x2(array.get(_lineArray, _i), bar_index)


f_drawLabelSemafor(_switch, _x1, _y1, _lw, _hg, _down, _colorLine, _size) =>
    label point = na
    if _down != _down[1]
        nx = _down ? _hg : _lw
        point := label.new(bar_index - nx, _down ? high[nx] : low[nx], color=_colorLine, style=label.style_circle, size=size.tiny)
        point

f_addLine(_switch, _x1, _y1, _lw, _hg, _down, _colorLine) =>
    if _switch
        line l = na
        label lab = na

        nx = _down ? _hg : _lw
        yValue = _down ? high[nx] : low[nx]
        x1 = bar_index - nx
        x2 = bar_index

        if _down != _down[1]
            l := f_drawLine_p(x1, x2, yValue, _colorLine)
            lab := f_drawLabel(x1, yValue, _colorLine)
            lab

        if array.size(zigZagArray) == i_lineCount
            line.delete(array.shift(zigZagArray))
            label.delete(array.shift(zigZagLabelArray))
        array.push(zigZagArray, l)
        array.push(zigZagLabelArray, lab)

[switch1, x11, y11, lw1, hg1, down1] = f_zigzag(i_firstDepth, i_firstDeviation)


switch_1 = switch1


if not i_drawLines
    if i_showFirst
        f_drawLabelSemafor(switch1, x11, y11, lw1, hg1, down1, i_firstColor, size.tiny)

if i_drawLines
    if i_showFirst
        f_addLine(switch1, x11, y11, lw1, hg1, down1, i_firstColor)

    f_extendArray(zigZagArray, i_extendLines)



//──────────────────────────────────────────────────────────────────────────────
Sma(src, p) =>
    a = ta.cum(src)
    (a - a[math.max(p, 0)]) / math.max(p, 0)
Variance(src, p) =>
    p == 1 ? 0 : Sma(src * src, p) - math.pow(Sma(src, p), 2)
Covariance(x, y, p) =>
    Sma(x * y, p) - Sma(x, p) * Sma(y, p)
//──────────────────────────────────────────────────────────────────────────────
n = bar_index
ph = ta.pivothigh(length, length)
pl = ta.pivotlow(length, length)

//──────────────────────────────────────────────────────────────────────────────
varip ph_array = array.new_float(0)
varip pl_array = array.new_float(0)
varip ph_n_array = array.new_int(0)
varip pl_n_array = array.new_int(0)
if ph
    array.insert(ph_array, 0, ph)
    array.insert(ph_n_array, 0, n)
if pl
    array.insert(pl_array, 0, pl)
    array.insert(pl_n_array, 0, n)
//──────────────────────────────────────────────────────────────────────────────
val_ph = ta.valuewhen(ph, n - length, lookback - 1)
val_pl = ta.valuewhen(pl, n - length, lookback - 1)
val = math.min(val_ph, val_pl)
k = n - val > 0 ? n - val : 2
slope = Covariance(close, n, k) / Variance(n, k) * Slope
var line ph_l = na
var line pl_l = na
var line ph_l2 = na
var line pl_l2 = na

if barstate.islast and showpar
    for i = 0 to lookback - 1 by 1
        ph_y2 = array.get(ph_array, i)
        ph_x1 = array.get(ph_n_array, i) - length
        pl_y2 = array.get(pl_array, i)
        pl_x1 = array.get(pl_n_array, i) - length
        ph_l2 := line.new(ph_x1, ph_y2, ph_x1 + 1, ph_y2 + slope, extend=extend2, color=color.new(ph_col, 0))
        pl_l2 := line.new(pl_x1, pl_y2, pl_x1 + 1, pl_y2 + slope, extend=extend2, color=color.new(pl_col, 0))
        pl_l2


_draw_label(price, txt, txtColor) =>
    x = bar_index
    labelStyle = label.style_label_right
    align = text.align_right 
    labelsAlignStrLeft = txt + '\n ‏  ‏  ‏  ‏  ‏  ‏  ‏  ‏  ‏  ‏  ‏  ‏  ‏  ‏  ‏  ‏  ‏  ‏  ‏  ‏  ‏  ‏  ‏  ‏  ‏  ‏  ‏  ‏  ‏  ‏  ‏  ‏  ‏  ‏  ‏ \n'
    labelsAlignStrRight = '       ' + txt + '\n ‏  ‏  ‏  ‏  ‏  ‏  ‏  ‏  ‏  ‏  ‏  ‏  ‏  ‏  ‏  ‏  ‏  ‏  ‏  ‏  ‏  ‏  ‏  ‏  ‏  ‏  ‏  ‏  ‏  ‏  ‏  ‏  ‏  ‏  ‏ \n'
    labelsAlignStr = labelsAlignStrRight
    var id = label.new(x=x, y=price, text=labelsAlignStr, textcolor=txtColor, style=labelStyle, textalign=align, color=#00000000)
    label.set_xy(id, x, price)
    label.set_text(id, labelsAlignStr)
    label.set_textcolor(id, txtColor)


// Input options
sensitivity = input.int(title='OB', defval=25, minval=5, maxval=100, inline = 'length', group='Basic Settings')

bearishLLColour = input.color(title='OB Colour: Bearish', defval=color.rgb(255, 0, 0, 80), inline = 'bearishLLColour', group='Line Colors')
bullishLLColour = input.color(title=': Bullish', defval=color.rgb(0, 255, 0, 80),inline = 'bearishLLColour', group='Line Colors')


// tracking for entries
var int lastDownIndex = 0
var float lastDown = 0
var float lastDownClose = 0
var float lastDownLow = 0
var float lastDownOpen = 0

var int lastUpIndex = 0
var float lastUp = 0
var float lastUpLow = 0
var float lastUpHigh = 0
var float lastUpClose = 0
var float lastUpOpen = 0

// structure
var int structureLowIndex = 0
float structureLow = 1000000

// drawings
var longBoxes = array.new_box()
var shortBoxes = array.new_box()
var bullBosLines = array.new_line()
var bearBosLines = array.new_line()

// load levels
var int lastLongIndex = 0
var int lastShortIndex = 0
var bool lastShortIndex_EL = 0

// stop run detection
var box last_bull = na
var box last_bear = na

// get the lowest point in the range
structureLow := ta.lowest(low, sensitivity)[1]

// bearish break of structure
if ta.crossunder(low, structureLow)
    if bar_index - lastUpIndex < 1000
        // update structure high
        for p = 1 to bar_index - lastUpIndex by 1
            if high[p] > lastUpHigh
                lastUpHigh := high[p]
                lastUpHigh

        last_bear := box.new(left=lastUpIndex, top=(lastUpHigh + lastUpLow) / 2, bottom=lastUpLow, right=lastUpIndex, bgcolor=lastShortIndex == lastUpIndex ? color.rgb(0, 0, 0, 100) : bearishLLColour, border_color=bearishLLColour, extend=extend.right)
        array.push(shortBoxes, last_bear)


// bullish break of structure?
if array.size(shortBoxes) > 0
    for i = array.size(shortBoxes) - 1 to 0 by 1
        box = array.get(shortBoxes, i)
        top = box.get_top(box)
        bottom = box.get_bottom(box)
        left = box.get_left(box)
        if high > top and close > open and bar_index != lastLongIndex
            // remove the short box 
            box.delete(box)
            array.remove(shortBoxes, i)
            // calculate LL bottom
            LLBottom = close < lastDownLow ? close : lastDownLow
            // ok to draw
            if bar_index - lastDownIndex < 1000

                // update structure low
                for p = 1 to bar_index - lastDownIndex by 1
                    if low[p] < LLBottom
                        LLBottom := low[p]
                        LLBottom

                //
                last_bull := box.new(left=lastDownIndex, top=lastDown, bottom=(lastDown + LLBottom) / 2, right=lastDownIndex, bgcolor=lastLongIndex == lastDownIndex ? color.rgb(0, 0, 0, 100) : lastShortIndex_EL == true ? color.rgb(0, 255, 0, 80) : bullishLLColour, border_color=lastLongIndex == lastDownIndex ? color.rgb(0, 0, 0, 100) : bullishLLColour, extend=extend.right)
                array.push(longBoxes, last_bull)



        lastLongIndex := bar_index
        lastLongIndex


// remove LL if close below
if array.size(longBoxes) > 0
    for i = array.size(longBoxes) - 1 to 0 by 1
        lbox = array.get(longBoxes, i)
        bottom = box.get_bottom(lbox)
        top = box.get_top(lbox)
        if close < bottom
            array.remove(longBoxes, i)
            box.delete(lbox)


// record last up and down candles
if close < open
    lastDown := high
    lastDownClose := close
    lastDownOpen := open
    lastDownLow := low
    lastDownIndex := bar_index
    lastDownIndex

if close > open
    lastUp := close
    lastUpIndex := bar_index
    lastUpOpen := open
    lastUpLow := low
    lastUpClose := close
    lastUpHigh := high
    lastUpHigh

