Higher High / Lower Low based on Heikin Ashi
// This source code is subject to the terms of the Mozilla Public License 2.0 at https://mozilla.org/MPL/2.0/
// © zathruswriter

//@version=5
indicator( "Higher High / Lower Low based on Heikin Ashi", overlay = true, max_labels_count = 500 )

candles_to_confirm_trend = input.int( 5, "HA Candles to Confirm Trend" )
candletype      = input.string  ("Candles", "Candle Type", options=["Hollow", "Bars", "Candles"], tooltip="Candle Type to be dispalyed. User will have to 'Mute' the main series bar using the 👁 symbol next to ticker id.")
ha_show         = input.bool    (true, "Show Heikin Ashi Candles Overlay")
hide_candlesticks = input.bool( true, "Hollow candlesticks" )
BodyBull        = input.color   ( #26a69a, "", inline="a")
BodyBear        = input.color   ( #ef5350, "", inline="a")
BorderBull      = input.color   ( color.new( #26a69a, 50 ), "", inline="b")
BorderBear      = input.color   ( color.new( #ef5350, 50 ), "", inline="b")
WickBull        = input.color   ( color.new( #26a69a, 50 ), "", inline="c")
WickBear        = input.color   ( color.new( #ef5350, 50 ), "", inline="c")

// Optional Mathmatical Calcualtion to avoid the security function 
// hkClose         = (open + high + low + close) / 4
// hkOpen          = float(na)
// hkOpen          := na(hkOpen[1]) ? (open + close) / 2 : (nz(hkOpen[1]) + nz(hkClose[1])) / 2
// hkHigh          = math.max(high, math.max(hkOpen, hkClose))
// hkLow           = math.min(low,  math.min(hkOpen, hkClose))
[hkOpen, hkHigh, hkLow, hkClose] = request.security(ticker.heikinashi(syminfo.tickerid), timeframe.period, [open, high, low, close])
hollow          = candletype == "Hollow" 
bars            = candletype == "Bars"
candle          = candletype == "Candles"

var prev_high = 0.0
var prev_high_index = 0
var prev_low = 0.0
var prev_low_index = 0
var ha_candle_counter = 0
var trend_candles_counter = 0
var current_trend = ""
var long_term_trend = ""

plotcandle(
  ha_show ? hkOpen : na, ha_show ? hkHigh : na, ha_show ? hkLow : na, ha_show ? hkClose : na, 
  "Hollow Candles",
  hollow ?      hkClose <   hkOpen  ?   BodyBear :  na :        candle ?        hkClose < hkOpen ?  BodyBear : BodyBull : na,
  hollow or     candle ?    hkClose <    hkOpen  ?  WickBear :  WickBull :      na,
  bordercolor = hollow or   candle ?    hkClose  <  hkOpen   ?  BorderBear :    BorderBull : na)

plotbar(
  ha_show ? hkOpen : na, ha_show ? hkHigh : na, ha_show ? hkLow : na, ha_show ? hkClose : na,
  "Bars",
  bars ? hkClose < hkOpen ? BodyBear : BodyBull : na)

barcolor( hide_candlesticks ? color.new( color.white, 100 ) : na )

s_( txt ) =>
    str.tostring( txt )

f_debug_label( txt ) =>
    label.new(bar_index, high, str.tostring( txt ), color = color.lime, textcolor = color.black)

f_debug_label_down( txt ) =>
    label.new(bar_index, low, str.tostring( txt ), color = color.lime, textcolor = color.black, style = label.style_label_up)

f_find_last_low() =>
    start_at = barstate.isconfirmed ? 0 : 1
    opposite_trend_candle_counter = 0
    low_ = 0.0
    idx = 0
    for i = start_at to 100
        // red Heikin Ashi
        if hkClose[ i ] < hkOpen[ i ] and opposite_trend_candle_counter >= 2
            break
        else if hkClose[ i ] < hkOpen[ i ] and opposite_trend_candle_counter < 2
            opposite_trend_candle_counter := opposite_trend_candle_counter + 1

        if low_ == 0 or low[ i ] < low_
            low_ := low[ i ]
            idx := i

    [ low_, idx ]

f_find_last_high() =>
    start_at = barstate.isconfirmed ? 0 : 1
    opposite_trend_candle_counter = 0
    high_ = 0.0
    idx = 0
    for i = start_at to 100
        // green Heikin Ashi
        if hkClose[ i ] > hkOpen[ i ] and opposite_trend_candle_counter >= 2
            break
        else if hkClose[ i ] > hkOpen[ i ] and opposite_trend_candle_counter < 2
            opposite_trend_candle_counter := opposite_trend_candle_counter + 1

        if high[ i ] > high_
            high_ := high[ i ]
            idx := i

    [ high_, idx ]

// if we just started, find the last high or low
plot_hl = false
plot_hh = false
plot_eqh = false
plot_ll = false
plot_lh = false
plot_eql = false
first_run = false

if prev_high == 0 or prev_low == 0
    first_run := true
    if ( barstate.isconfirmed and hkClose > hkOpen ) or hkClose[ 1 ] > hkOpen[ 1 ]
        // previous Heikin Ashi was green, find the low
        [ l, li ] = f_find_last_low()
        prev_low := l
        prev_low_index := bar_index - li
        plot_ll := true
        current_trend := "bull"
        long_term_trend := "bull"
    else if ( barstate.isconfirmed and hkClose < hkOpen ) or hkClose[ 1 ] < hkOpen[ 1 ]
        // previous Heikin Ashi was red, find the high
        [ h, hi ] = f_find_last_high()
        prev_high := h
        prev_high_index := bar_index - hi
        plot_hh := true
        current_trend := "bear"
        long_term_trend := "bear"
else
    // check if our trend has changed, in which case reset the counter
    trend = hkClose > hkOpen ? "bull" : "bear"
    if current_trend != trend
        ha_candle_counter := 1
        current_trend := trend
    else
        ha_candle_counter := ha_candle_counter + 1

    // check if our long-term trend has changed, i.e. if we have X HA candles confirmation of an opposite trend
    change_confirmed = false
    if long_term_trend != trend and ha_candle_counter >= candles_to_confirm_trend
        trend_candles_counter := 1
        long_term_trend := trend
        change_confirmed := true
    else
        trend_candles_counter := trend_candles_counter + 1

    // if we have same X heikin ashi candles in a row opposite to the long-term trend, the last highest high / lowest low is confirmed
    if change_confirmed
        if trend == "bear"
            // find the highest high from now until the previous high
            higher_high = 0.0
            hi = 0
            for i = bar_index to prev_low_index + 1
                ix = bar_index - i
                if high[ ix ] > higher_high
                    higher_high := high[ ix ]
                    hi := i

            if higher_high > prev_high
                plot_hh := true
            else if higher_high < prev_high
                plot_lh := true
            else
                plot_eqh := true

            prev_high := higher_high
            prev_high_index := hi
        else if trend == "bull"
            // find the lowest low from now until the previous low
            lower_low = 0.0
            li = 0
            for i = bar_index to prev_high_index + 1
                ix = bar_index - i
                if lower_low == 0 or low[ ix ] < lower_low
                    lower_low := low[ ix ]
                    li := i

            if lower_low < prev_low
                plot_ll := true
            else if lower_low > prev_low
                plot_hl := true
            else
                plot_eql := true

            prev_low := lower_low
            prev_low_index := li

if plot_hh or plot_lh or plot_eqh
    label.new( prev_high_index, prev_high, ( plot_hh ? "HH" : plot_lh ? "LH" : "EQH" ), color = color.lime, textcolor = color.black, style = label.style_label_down )

if plot_ll or plot_hl or plot_eql
    label.new( prev_low_index, prev_low, ( plot_ll ? "LL" : plot_hl ? "HL" : "EQL" ), color = color.red, textcolor = color.white, style = label.style_label_up )