[_ParkF]Fractal
// This source code is subject to the terms of the Mozilla Public License 2.0 at https://mozilla.org/MPL/2.0/
// © ParkF

//@version=5
indicator(title='[_ParkF]Fractal', overlay=true)

// input
ShowFractals = true
ShowFractalLevels = input(true, title='Show Fractal Levels')
ShowHHLL = input(true, title='Show HH/LL')
filterBW = input(true, title='Show Ideal Fractals Only')
uRenko = false
maxLvlLen = input(0, title='Maximum Fractal Level Extension Length (0=No limit)')

// uRenko calc
close_ = close
open_ = open
high_ = uRenko ? math.max(close_, open_) : high
low_ = uRenko ? math.min(close_, open_) : low

// fractal function
isIdealFractal(mode) =>
    ret = mode == 1 ? high_[5] < high_[4] and high_[4] < high_[3] and high_[3] > high_[2] and high_[2] > high_[1] : mode == -1 ? low_[5] > low_[4] and low_[4] > low_[3] and low_[3] < low_[2] and low_[2] < low_[1] : false
    ret

isRegularFractal(mode) =>
    ret = mode == 1 ? high_[5] < high_[3] and high_[4] < high_[3] and high_[3] > high_[2] and high_[3] > high_[1] : mode == -1 ? low_[5] > low_[3] and low_[4] > low_[3] and low_[3] < low_[2] and low_[3] < low_[1] : false
    ret

// fractals
isIdealFractal__1 = isIdealFractal(1)
isRegularFractal__1 = isRegularFractal(1)
filteredtopf = filterBW ? isIdealFractal__1 : isRegularFractal__1
isIdealFractal__2 = isIdealFractal(-1)
isRegularFractal__2 = isRegularFractal(-1)
filteredbotf = filterBW ? isIdealFractal__2 : isRegularFractal__2

plotshape(ShowFractals or filterBW ? filteredtopf : na, title='Top Fractal', style=shape.circle, location=location.abovebar, color=color.new(color.black, 0), offset=-3)
plotshape(ShowFractals or filterBW ? filteredbotf : na, title='Bottom Fractal', style=shape.circle, location=location.belowbar, color=color.new(color.black, 0), offset=-3)

topfractals = 0.0
botfractals = 0.0
topfractals := filteredtopf ? high_[3] : topfractals[1]
botfractals := filteredbotf ? low_[3] : botfractals[1]

topfcolor = topfractals != topfractals[1] ? na : color.black
botfcolor = botfractals != botfractals[1] ? na : color.black

// HH, LH, HL, LL calc
valuewhen_1 = ta.valuewhen(filteredtopf == true, high_[3], 1)
valuewhen_2 = ta.valuewhen(filteredtopf == true, high_[3], 0)
valuewhen_3 = ta.valuewhen(filteredtopf == true, high_[3], 2)
valuewhen_4 = ta.valuewhen(filteredtopf == true, high_[3], 0)
higherhigh = filteredtopf == false ? false : valuewhen_1 < valuewhen_2 and (filterBW or valuewhen_3 < valuewhen_4)
valuewhen_5 = ta.valuewhen(filteredtopf == true, high_[3], 1)
valuewhen_6 = ta.valuewhen(filteredtopf == true, high_[3], 0)
valuewhen_7 = ta.valuewhen(filteredtopf == true, high_[3], 2)
valuewhen_8 = ta.valuewhen(filteredtopf == true, high_[3], 0)
lowerhigh = filteredtopf == false ? false : valuewhen_5 > valuewhen_6 and (filterBW or valuewhen_7 > valuewhen_8)
valuewhen_9 = ta.valuewhen(filteredbotf == true, low_[3], 1)
valuewhen_10 = ta.valuewhen(filteredbotf == true, low_[3], 0)
valuewhen_11 = ta.valuewhen(filteredbotf == true, low_[3], 2)
valuewhen_12 = ta.valuewhen(filteredbotf == true, low_[3], 0)
higherlow = filteredbotf == false ? false : valuewhen_9 < valuewhen_10 and (filterBW or valuewhen_11 < valuewhen_12)
valuewhen_13 = ta.valuewhen(filteredbotf == true, low_[3], 1)
valuewhen_14 = ta.valuewhen(filteredbotf == true, low_[3], 0)
valuewhen_15 = ta.valuewhen(filteredbotf == true, low_[3], 2)
valuewhen_16 = ta.valuewhen(filteredbotf == true, low_[3], 0)
lowerlow = filteredbotf == false ? false : valuewhen_13 > valuewhen_14 and (filterBW or valuewhen_15 > valuewhen_16)

// HH, LH, HL, LL plot
plotshape(ShowHHLL ? higherhigh : na, title='HH', style=shape.square, location=location.abovebar, color=color.new(color.black, 0), text='HH', offset=-3)
plotshape(ShowHHLL ? lowerhigh : na, title='LH', style=shape.square, location=location.abovebar, color=color.new(color.black, 0), text='LH', offset=-3)
plotshape(ShowHHLL ? higherlow : na, title='HL', style=shape.square, location=location.belowbar, color=color.new(color.black, 0), text='HL', offset=-3)
plotshape(ShowHHLL ? lowerlow : na, title='LL', style=shape.square, location=location.belowbar, color=color.new(color.black, 0), text='LL', offset=-3)

// pivot lv counting and new reset
topcnt = 0
botcnt = 0
topcnt := filteredtopf ? 0 : nz(topcnt[1]) + 1
botcnt := filteredbotf ? 0 : nz(botcnt[1]) + 1

topfs = 0.0
botfs = 0.0
topfs := filteredtopf ? high_[3] : topfs[1]
botfs := filteredbotf ? low_[3] : botfs[1]

topfc = topfs != topfs[1] ? na : color.new(#787b86, 25)
botfc = botfs != botfs[1] ? na : color.new(#787b86, 25)

// fractal lv line plot
plot(ShowFractalLevels and topcnt <= 3 ? topfs : na, color=topfc, linewidth=4, offset=-3, title='Top LV -3')
plot(ShowFractalLevels and botcnt <= 3 ? botfs : na, color=botfc, linewidth=4, offset=-3, title='Bottom LV -3')
plot(ShowFractalLevels and (maxLvlLen == 0 or topcnt < maxLvlLen) ? topfs : na, color=topfc, linewidth=4, offset=0, title='Top Lv 1')
plot(ShowFractalLevels and (maxLvlLen == 0 or botcnt < maxLvlLen) ? botfs : na, color=botfc, linewidth=4, offset=0, title='Bottom Lv 1')
plot(math.avg(topfs, botfs), color=color.new(#FF0000, 0), linewidth=2, title='AVG Fractal', offset=-1)

