Rolling HTF Liquidity Levels [CHE]
// This source code is subject to the terms of the Mozilla Public License 2.0 at https://mozilla.org/MPL/2.0/
// © chervolino
// the program code is inspired by sbtnc https://de.tradingview.com/script/PL0iPxy0-HTF-Liquidity-Levels/
// and sbtnc used fixed timeframes
// my version use a rolling time frame modul - so that the functionality is increased and the clarity
//@version=5
indicator("Rolling HTF Liquidity Levels [CHE]", "HTF Liquidity Rolling", overlay=true, max_lines_count=500)

//--------------------------------------------------------------------
//                             Constants
//--------------------------------------------------------------------

var START_OFFSET                = 0
var END_OFFSET                  = 25


//--------------------------------------------------------------------
//                               Inputs
//--------------------------------------------------------------------

var g_htf                       = 'HTF Liquidity :'

var g_purged                    = 'Liquidity :'
string  GRP2                    = 'Time Period :'
bool    showInfoBoxInput        = input.bool(true,          "Show time period",        group = GRP2)
string  infoBoxSizeInput        = input.string("small",     "Size ",    inline = "21", group = GRP2, options = ["tiny", "small", "normal", "large", "huge", "auto"])
string  infoBoxYPosInput        = input.string("bottom",    "↕",        inline = "21", group = GRP2, options = ["top", "middle", "bottom"])
string  infoBoxXPosInput        = input.string("left",      "↔",        inline = "21", group = GRP2, options = ["left", "center", "right"])
color   infoBoxColorInput       = input.color(color.gray,   "",         inline = "21", group = GRP2)
color   infoBoxTxtColorInput    = input.color(color.white,  "T",        inline = "21", group = GRP2)

i_isAutoEnabled                = input         (true,                          "Auto",    inline="Auto",                 group=g_htf)
i_AutoAboveLiquidityColor      = input         (#00ff08,    "",         inline="Auto",                 group=g_htf)
i_AutoBelowLiquidityColor      = input         (#ff061b,    "",         inline="Auto",                 group=g_htf)
i_AutoWidth                    = input         (2,                             "Width",    inline="Auto",                 group=g_htf)

i_purgedLevelColor              = input         (color.new(color.gray, 60),     "Color",                                    group=g_purged)
i_purgedLevelStyle              = input.string  ("Dashed",                      "Style",    ["Solid", "Dashed", "Dotted"],  group=g_purged)


//--------------------------------------------------------------------
//                        Variables declarations
//--------------------------------------------------------------------

var highsArray                  = array.new_float()
var lowsArray                   = array.new_float()
var highLinesArray              = array.new_line()
var lowLinesArray               = array.new_line()
var purgedLinesArray            = array.new_line()
var float dayHigh               = na
var float dayLow                = na

int MS_IN_MIN   = 60 * 1000
int MS_IN_HOUR  = MS_IN_MIN  * 60
int MS_IN_DAY   = MS_IN_HOUR * 24

timeStep_translate() =>
    int tfInMs = timeframe.in_seconds() * 1000
    string step =
      switch
        tfInMs <= MS_IN_MIN        => "60"
        tfInMs <= MS_IN_MIN  * 5   => "240"
        tfInMs <= MS_IN_HOUR       => "1D"
        tfInMs <= MS_IN_HOUR * 4   => "3D"
        tfInMs <= MS_IN_HOUR * 12  => "7D"
        tfInMs <= MS_IN_DAY        => "1M"
        tfInMs <= MS_IN_DAY  * 7   => "3M"
        => "12M"

[prevDayHigh, prevDayLow]       = request.security(syminfo.tickerid, timeStep_translate(), [high[1], low[1]], lookahead=barmerge.lookahead_on)

//--------------------------------------------------------------------
//                              Functions 
//--------------------------------------------------------------------

f_drawLine(float _y, color _c, int _w=1) => line.new(bar_index, _y, bar_index, _y, color=_c, width=_w)

f_create(float _high, float _low, color _upperColor, color _lowerColor, int _linewidth) =>
    array.push(highsArray, _high)
    array.push(lowsArray, _low)
    array.push(highLinesArray, f_drawLine(_high, _upperColor, _linewidth))
    array.push(lowLinesArray, f_drawLine(_low, _lowerColor, _linewidth))

f_updateStickyLevels(array<line> _levels) =>
    for _line in _levels
        line.set_x1(_line, bar_index + START_OFFSET)
        line.set_x2(_line, bar_index + END_OFFSET)

f_moveLevel(array<line> _from, array<line> _to, line _level, int _index) =>
    array.push(_to, _level)
    array.remove(_from, _index)

f_highlightPurgedLevel(line _level) =>
    _style = i_purgedLevelStyle == "Solid" ? line.style_solid : i_purgedLevelStyle == "Dashed" ? line.style_dashed : line.style_dotted
    line.set_color(_level, i_purgedLevelColor)
    line.set_style(_level, _style)

f_updateUpperLevels(float _high, array<float> _highs, array<line> _levels, array<line> _purgedLevels) =>
    while array.min(_highs) < _high
        for [_index, _value] in _highs
            if _high > _value
                _line = array.get(_levels, _index)
                f_highlightPurgedLevel(_line)
                f_moveLevel(_levels, _purgedLevels, _line, _index)
                array.remove(_highs, _index)

f_updateLowerLevels(float _low, array<float> _lows, array<line> _levels, array<line> _purgedLevels) =>
    while array.max(_lows) > _low
        for [_index, _value] in _lows
            if _low < _value
                _line = array.get(_levels, _index)
                f_highlightPurgedLevel(_line)
                f_moveLevel(_levels, _purgedLevels, _line, _index) 
                array.remove(_lows, _index)

f_clearLevels(array<line> _levels) =>
    while array.size(_levels) > 0
        for [_index, _line] in _levels
            line.delete(array.remove(_levels, _index))

f_isHigherTimeframe(string _timeframe) => timeframe.in_seconds() <= timeframe.in_seconds(_timeframe)


//--------------------------------------------------------------------
//                                Logic
//--------------------------------------------------------------------
if i_isAutoEnabled and f_isHigherTimeframe(timeStep_translate()) and ta.change(time(timeStep_translate()))
    f_create(prevDayHigh, prevDayLow, i_AutoAboveLiquidityColor, i_AutoBelowLiquidityColor, i_AutoWidth)
if barstate.islast
    f_updateStickyLevels(highLinesArray)
    f_updateStickyLevels(lowLinesArray)
    f_updateStickyLevels(purgedLinesArray)
f_updateUpperLevels(high, highsArray, highLinesArray, purgedLinesArray)
f_updateLowerLevels(low, lowsArray, lowLinesArray, purgedLinesArray)
if ta.change(time(timeStep_translate()))
    f_clearLevels(purgedLinesArray)

// Display of time period.
var table tfDisplay = table.new(infoBoxYPosInput + "_" + infoBoxXPosInput, 2, 2)
if showInfoBoxInput and barstate.islastconfirmedhistory
    table.cell(tfDisplay, 0, 0, "Timeframe: " + timeStep_translate(), bgcolor = infoBoxColorInput, text_color = infoBoxTxtColorInput, text_size = infoBoxSizeInput)
