ICT Killzone Session
// This source code is subject to the terms of the Mozilla Public License 2.0 at https://mozilla.org/MPL/2.0/
// © rayshen0411

//@version=5
indicator("ICT Killzone Session[BlueGeek]", "ICT Killzones Session[BlueGeek]", overlay = true, max_boxes_count = 500, max_labels_count = 500)

labels(session, css, txt)=>
    var label lbl = na
    var float max = na 
    var int anchor = na
    var get_css = color.rgb(color.r(css), color.g(css), color.b(css))

    if session and not session[1]
        max := high
        anchor := time
        
        lbl := label.new(anchor, max
          , txt
          , xloc.bar_time
          , color = #ffffff00
          , style = label.style_label_down
          , textcolor = get_css
          , size = size.small)
    
    if session
        max := math.max(high, max)

        label.set_x(lbl, int(math.avg(time, anchor)))
        label.set_y(lbl, max)

//--------------------------------------------------------------------
//                              ICT Killzone Inputs
//--------------------------------------------------------------------
lno_time = input.session(title="London Open Killzone", defval="0200-0500")
nyo_time = input.session(title="New York Open Killzone", defval="0700-0900")
asb_time = input.session(title="Asian Range", defval="2000-0000")
lnc_time = input.session(title="London Close Killzone", defval="1000-1200")

lno_ = input.bool(true, "London Open Killzone", "02:00-05:00 London timezone", "", "Session")
nyo_ = input.bool(true, "New York Open Killzone", "07:00-09:00 London timezone", "", "Session")
lnc_ = input.bool(true, "London Close Killzone", "10:00-12:00 London timezone", "", "Session")
asb_ = input.bool(true, "Asian Range", "20:00-00:00 London timezone", "", "Session")
lno_b = input.bool(true, "London Open box", "", "", "session in box")
nyo_b = input.bool(true, "New York Open box", "", "", "session in box")
lnc_b = input.bool(true, "London Close box", "", "", "session in box")
asb_b = input.bool(true, "Asian Range box", "", "", "session in box")
info = input.bool(false, "info over box", "this will show the range of the session in pips (make sense only on forex)", "", "session in box")
spec = input.int(90, "Transparency", 0, 100, 1, "from 0 to 100, how much you want the color transparent?", "", "Visual Features")
lno_c = input.color(color.green, "London Open Killzone", "", "", "Visual Features")
nyo_c = input.color(color.blue, "New York Open Killzone", "", "", "Visual Features")
lnc_c = input.color(color.orange, "London Close Killzone", "", "", "Visual Features")
asb_c = input.color(color.yellow, "Asian Range", "", "", "Visual Features")

iMDisplay = input.bool    (false, "Display", group="New York Midnight Day Divider")
iMDColor   = input.color   (#58A2B0, "Color", group="New York Midnight Day Divider")
iMDStyle   = input.string  ("Solid", "Line Style", options=["Solid", "Dotted", "Dashed"], group="New York Midnight Day Divider")

iMTime    = input.session ('0000-0001:1234567', "Session", group="New York Midnight Open")
iMStyle   = input.string  ("Dashed", "Line Style", options=["Solid", "Dotted", "Dashed"], group="New York Midnight Open")
iMColor   = input.color   (#58A2B0, "Color", group="New York Midnight Open")
iMHistory = input.bool    (false, "History", group="New York Midnight Open")
iMLabel   = input.bool    (false, "Show Label", group="New York Midnight Open")

i8Display = input.bool    (true, "Display", group="New York 8:30 Open")
i8Time    = input.session ('0830-0831:1234567', "Session", group="New York 8:30 Open")
i8Style   = input.string  ("Dotted", "Line Style", options=["Solid", "Dotted", "Dashed"], group="New York 8:30 Open")
i8Color   = input.color   (#58A2B0, "Color", group="New York 8:30 Open")
i8History = input.bool    (true, "History", group="New York 8:30 Open")
i8Label   = input.bool    (true, "Show Label", group="New York 8:30 Open")


//--------------------------------------------------------------------
//                              Silver Bullet Inputs
//--------------------------------------------------------------------
iShowSB     = input.bool (true, 'Show SB session', inline='SB', group='Silver Bullet session')
col_SB      = input.color(#735e5ee8,       '     '       , inline='SB'    , group='Silver Bullet session' )

//--------------------------------------------------------------------
//                              NWOG Inputs
//--------------------------------------------------------------------
iNWOG      = input.bool  (false, "Display", inline='NWOG', group = 'NWOG/NDOG')
cNWOG1     = input.color (color.new(#ff5252, 28), title = 'NWOG    ', inline='NWOG', group = 'NWOG/NDOG')
cNWOG2     = input.color (color.new(#b2b5be, 50), title = ''        , inline='NWOG', group = 'NWOG/NDOG')
maxNWOG    = input.int   (3, title = 'Show max', inline='NWOG',  group = 'NWOG/NDOG', minval = 0, maxval = 50)

//--------------------------------------------------------------------
//                              NDOG Inputs
//--------------------------------------------------------------------
iNDOG      = input.bool  (false, "Display", inline='NDOG', group = 'NWOG/NDOG')
cNDOG1     = input.color (color.new(#ff9800, 20), title = 'NDOG    ', inline='NDOG', group = 'NWOG/NDOG')
cNDOG2     = input.color (color.new(#4dd0e1, 65), title =    ''     , inline='NDOG', group = 'NWOG/NDOG')
maxNDOG    = input.int   (1, title = 'Show max', inline='NDOG', group = 'NWOG/NDOG', minval = 0, maxval = 50)

//--------------------------------------------------------------------
//                              Daily/Weekly/Yearly Open Inputs
//--------------------------------------------------------------------

t_tails                 = "Extends last opens on historical bars."
t_heads                 = "Extends previous opens in the future."
t_discoverPrices        = "Discovers the opening prices from the intraday chart.\n\nYou may use this setting when there are discrepancies between the data from intraday and high time frames."
t_extendedHours         = "Discovers the opening prices from the extended trading hours if available."

var i_isDailyEnabled    = input     (true,          "Daily",                                inline="Daily",     group="Opens")
var i_dailyColor        = input     (color.green,   "",                                     inline="Daily",     group="Opens")
var i_dailyLookback     = input.int (1,             "", 1,                                  inline="Daily",     group="Opens")
var i_isWeeklyEnabled   = input     (true,          "Weekly",                               inline="Weekly",    group="Opens")
var i_weeklyColor       = input     (color.orange,  "",                                     inline="Weekly",    group="Opens")
var i_weeklyLookback    = input.int (1,             "", 1,                                  inline="Weekly",    group="Opens")
var i_isMonthlyEnabled  = input     (true,          "Monthly",                              inline="Monthly",   group="Opens")
var i_monthlyColor      = input     (color.red,     "",                                     inline="Monthly",   group="Opens")
var i_monthlyLookback   = input.int (1,             "", 1,                                  inline="Monthly",   group="Opens")
var i_isYearlyEnabled   = input     (true,          "Yearly",                               inline="Yearly",    group="Opens")
var i_yearlyColor       = input     (color.blue,    "",                                     inline="Yearly",    group="Opens")
var i_yearlyLookback    = input.int (1,             "", 1,                                  inline="Yearly",    group="Opens")
var i_rightOffset       = input.int (20,            "Offset", 1,                                                group="Style")
var i_areTailsEnabled   = input     (false,         "Show Tails",       t_tails,                                group="Style")
var i_areHeadsEnabled   = input     (false,         "Show Projections", t_heads,                                group="Style")
var i_discoverPrices    = input     (false,         "Discover Prices",  t_discoverPrices,                       group="Settings")
var i_extendedHours     = input     (false,         "Extended Hours",   t_extendedHours,                        group="Settings")

//--------------------------------------------------------------------
//                             Constants
//--------------------------------------------------------------------
var HEAD_PADDING        = -2
var HEAD_TRANSP         = 60
var LABEL_SIZE          = size.small
var LABEL_STYLE         = label.style_none
var LINE_STYLE          = line.style_solid
var LINE_WIDTH          = 1
var OFFSET_PADDING      = 4
var TAIL_STYLE          = line.style_dotted

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

var a_lastOpens             = array.new_float(4)
var canShowDaily            = i_isDailyEnabled and timeframe.isintraday
var canShowWeekly           = i_isWeeklyEnabled and (timeframe.isintraday or timeframe.isdaily)
var canShowMonthly          = i_isMonthlyEnabled and not timeframe.ismonthly
var canShowYearly           = i_isYearlyEnabled and not (timeframe.ismonthly and timeframe.multiplier >= 12)
var hasExtendedHours        = i_extendedHours and syminfo.session == session.extended
var discoverPrices          = i_discoverPrices or hasExtendedHours

[dailyTime, dailyOpen]      = request.security(syminfo.tickerid, 'D',   [time, open], lookahead=barmerge.lookahead_on)
[weeklyTime, weeklyOpen]    = request.security(syminfo.tickerid, 'W',   [time, open], lookahead=barmerge.lookahead_on)
[monthlyTime, monthlyOpen]  = request.security(syminfo.tickerid, 'M',   [time, open], lookahead=barmerge.lookahead_on)
[yearlyTime, yearlyOpen]    = request.security(syminfo.tickerid, '12M', [time, open], lookahead=barmerge.lookahead_on)

hasDailyTimeChanged         = hasExtendedHours ? time_tradingday != time_tradingday[1] : dailyTime != dailyTime[1]
hasWeekklyTimeChanged       = hasExtendedHours ? weekofyear != weekofyear[1] : weeklyTime != weeklyTime[1]
hasMonthlyTimeChanged       = hasExtendedHours ? month != month[1] : monthlyTime != monthlyTime[1]
hasYearlyTimeChanged        = hasExtendedHours ? year != year[1] : yearlyTime != yearlyTime[1]

b_color = color.new(color.gray,95)
n = bar_index

lno = time('', lno_time, 'UTC-5') and lno_ and timeframe.isintraday and timeframe.multiplier <= 60
nyo = time('', nyo_time, 'UTC-5') and nyo_ and timeframe.isintraday and timeframe.multiplier <= 60
lnc = time('', lnc_time, 'UTC-5') and lnc_ and timeframe.isintraday and timeframe.multiplier <= 60
asb = time('', asb_time, 'UTC-5') and asb_ and timeframe.isintraday and timeframe.multiplier <= 60

labels(nyo, nyo_c, 'New York')
labels(lno, lno_c, 'London Open')
labels(lnc, lnc_c, 'London Close')
labels(asb, asb_c, 'Asian')

bgcolor(lno and not lno_b and lno_? color.new(lno_c, spec) : na)
bgcolor(nyo and not nyo_b and nyo_? color.new(nyo_c, spec) : na)
bgcolor(lnc and not lnc_b and lnc_? color.new(lnc_c, spec) : na)
bgcolor(asb and not asb_b and asb_? color.new(asb_c, spec) : na)

type bx_ln
    box   b 
    line  l

var box lno_box = na
var box nyo_box = na
var box lnc_box = na
var box asb_box = na

var label lno_l = na
var label nyo_l = na
var label lnc_l = na
var label asb_l = na

var float friCp = na, var int friCi = na // Friday Close price/index
var float monOp = na, var int monOi = na // Monday Open  price/index

var float prDCp = na, var int prDCi = na // Previous Day Open price/index
var float cuDOp = na, var int cuDOi = na // Current  Day Open price/index

var bx_ln[] bl_NWOG = array.new<bx_ln>()
var bx_ln[] bl_NDOG = array.new<bx_ln>()

minT = syminfo.mintick
var l_SB = array.new<line>() 

method timeSess(string timezone, string session) => time(timeframe.period, session, timezone)

//Silver Bullet Periods
SB_LN_per = "America/New_York".timeSess("0300-0400") // period/session ~ The London Open Silver Bullet ( 3 AM —  4 AM New York local time)  03:00 - 04:00
SB_AM_per = "America/New_York".timeSess("1000-1100") // period/session ~ The AM  Session Silver Bullet (10 AM — 11 AM New York local time)  10:00 - 11:00
SB_PM_per = "America/New_York".timeSess("1400-1500") // period/session ~ The PM  Session Silver Bullet ( 2 PM —  3 PM New York local time)  14:00 - 15:00


is_in_SB  = SB_LN_per or SB_AM_per or SB_PM_per
strSB     = is_in_SB  and not is_in_SB [1]
strLN     = SB_LN_per and not SB_LN_per[1]
strAM     = SB_AM_per and not SB_AM_per[1]
strPM     = SB_PM_per and not SB_PM_per[1]
endSB     = not is_in_SB  and is_in_SB [1]
endLN     = not SB_LN_per and SB_LN_per[1]
endAM     = not SB_AM_per and SB_AM_per[1]
endPM     = not SB_PM_per and SB_PM_per[1]

if lno_ and lno_b and timeframe.multiplier<=60 and timeframe.isintraday
    if lno and not lno[1]
        lno_box := box.new(bar_index,high,bar_index+1,low,b_color,bgcolor = color.new(lno_c,spec))
        if info
            lno_l := label.new(bar_index,high,str.tostring((high-low)/10/syminfo.mintick)+" pip",textcolor = #ffffff, size = size.small,style = label.style_none)
    if lno and lno[1]
        if high > box.get_top(lno_box)
            box.set_top(lno_box,high)
        if low < box.get_bottom(lno_box)
            box.set_bottom(lno_box,low)
        box.set_right(lno_box,bar_index+1)
        if info
            label.set_text(lno_l,str.tostring((box.get_top(lno_box)-box.get_bottom(lno_box))/10/syminfo.mintick)+" pip")
            label.set_x(lno_l,(box.get_left(lno_box)+box.get_right(lno_box))/2)
            label.set_y(lno_l,box.get_top(lno_box))
    if not lno and lno[1]
        box.set_right(lno_box,bar_index-1)

if nyo_ and nyo_b and timeframe.multiplier<=60 and timeframe.isintraday
    if nyo and not nyo[1]
        nyo_box := box.new(bar_index,high,bar_index+1,low,b_color,bgcolor = color.new(nyo_c,spec))
        if info
            nyo_l := label.new(bar_index,high,str.tostring((high-low)/10/syminfo.mintick)+" pip",textcolor = #ffffff, size = size.small,style = label.style_none)
    if nyo and nyo[1]
        if high > box.get_top(nyo_box)
            box.set_top(nyo_box,high)
        if low < box.get_bottom(nyo_box)
            box.set_bottom(nyo_box,low)
        box.set_right(nyo_box,bar_index+1)
        if info
            label.set_text(nyo_l,str.tostring((box.get_top(nyo_box)-box.get_bottom(nyo_box))/10/syminfo.mintick)+" pip")
            label.set_x(nyo_l,(box.get_left(nyo_box)+box.get_right(nyo_box))/2)
            label.set_y(nyo_l,box.get_top(nyo_box))
    if not nyo and nyo[1]
        box.set_right(nyo_box,bar_index-1)

if lnc_ and lnc_b and timeframe.multiplier<=60 and timeframe.isintraday
    if lnc and not lnc[1]
        lnc_box := box.new(bar_index,high,bar_index+1,low,b_color,bgcolor = color.new(lnc_c,spec))
        if info
            lnc_l := label.new(bar_index,high,str.tostring((high-low)/10/syminfo.mintick)+" pip",textcolor = #ffffff, size = size.small,style = label.style_none)
    if lnc and lnc[1]
        if high > box.get_top(lnc_box)
            box.set_top(lnc_box,high)
        if low < box.get_bottom(lnc_box)
            box.set_bottom(lnc_box,low)
        box.set_right(lnc_box,bar_index+1)
        if info
            label.set_text(lnc_l,str.tostring((box.get_top(lnc_box)-box.get_bottom(lnc_box))/10/syminfo.mintick)+" pip")
            label.set_x(lnc_l,(box.get_left(lnc_box)+box.get_right(lnc_box))/2)
            label.set_y(lnc_l,box.get_top(lnc_box))
    if not lnc and lnc[1]
        box.set_right(lnc_box,bar_index-1)

if asb_ and asb_b and timeframe.multiplier<=60 and timeframe.isintraday
    if asb and not asb[1]
        asb_box := box.new(bar_index,high,bar_index+1,low,b_color,bgcolor = color.new(asb_c,spec))
        if info
            asb_l := label.new(bar_index,high,str.tostring((high-low)/10/syminfo.mintick)+" pip",textcolor = #ffffff, size = size.small,style = label.style_none)
    if asb and asb[1]
        if high > box.get_top(asb_box)
            box.set_top(asb_box,high)
        if low < box.get_bottom(asb_box)
            box.set_bottom(asb_box,low)
        box.set_right(asb_box,bar_index+1)
        if info
            label.set_text(asb_l,str.tostring((box.get_top(asb_box)-box.get_bottom(asb_box))/10/syminfo.mintick)+" pip")
            label.set_x(asb_l,(box.get_left(asb_box)+box.get_right(asb_box))/2)
            label.set_y(asb_l,box.get_top(asb_box))
    if not asb and asb[1]
        box.set_right(asb_box,bar_index-1)

tMidnight = time          ("1", iMTime)
t830      = time          ("1", i8Time)

_MStyle   = iMStyle ==    "Solid" ? line.style_solid : iMStyle == "Dotted" ? line.style_dotted : line.style_dashed
_8Style   = i8Style ==    "Solid" ? line.style_solid : i8Style == "Dotted" ? line.style_dotted : line.style_dashed
_DStyle   = iMDStyle ==   "Solid" ? line.style_solid : iMDStyle == "Dotted" ? line.style_dotted : line.style_dashed


//==== Midnight Open ====
if iMDisplay
    var line dayLine = na
    dayLine :=line.new(tMidnight, high, tMidnight, low, xloc.bar_time, extend.both, iMDColor, _DStyle, 1)
    //if not iMDHistory
       //line.delete(dayLine[1])

if iMDisplay
    var openMidnight  = 0.0
    if tMidnight
        if not tMidnight[1]
            openMidnight  := open
        else
            openMidnight := math.max(open, openMidnight)

    var label lb = na
    var line lne = na
    if openMidnight != openMidnight[1]
        if barstate.isconfirmed
            line.set_x2(lne, tMidnight)
            lne := line.new(tMidnight, openMidnight, last_bar_time + 14400000/2, openMidnight, xloc.bar_time, extend.none, iMColor, _MStyle, 1)
            if iMLabel
                lb  := label.new(last_bar_time + 14400000/2, openMidnight, "Midnight", xloc.bar_time, yloc.price, na, label.style_none, iMColor, size.normal, text.align_right)
            label.delete(lb[1])
            if not iMHistory
                line.delete(lne[1])
//===========================


//==== 8:30 Open ====
if i8Display
    var open830  = 0.0
    if t830
        if not t830[1]
            open830  := open
        else
            open830 := math.max(open, open830)

    var label lb2 = na
    var line lne2 = na
    if open830 != open830[1]
        if barstate.isconfirmed
            line.set_x2(lne2, t830 - 30600000)
            lne2 := line.new(t830, open830, last_bar_time + 14400000/2, open830, xloc.bar_time, extend.none, i8Color, _8Style, 1)
            if i8Label
                lb2  := label.new(last_bar_time + 14400000/2, open830, "8:30", xloc.bar_time, yloc.price, na, label.style_none, i8Color, size.normal, text.align_right)
            label.delete(lb2[1])
            if not i8History
                line.delete(lne2[1])

//NWOG/NDOG 
if barstate.isfirst 
    for i = 0 to maxNWOG -1
        bl_NWOG.unshift(bx_ln.new(box(na), line(na)))
    for i = 0 to maxNDOG -1
        bl_NDOG.unshift(bx_ln.new(box(na), line(na)))

if dayofweek == dayofweek.friday
    friCp := close, friCi := n

if ta.change(dayofweek) 
    if  dayofweek == dayofweek.monday and iNWOG
        monOp := open , monOi := n
        bl_NWOG.unshift(bx_ln.new(   
         box.new(
           friCi        ,   math.max (friCp  , monOp  )
         , monOi        ,   math.min (friCp  , monOp  )
         , bgcolor      =   color    ( na             )
         , border_color =             cNWOG2
         , extend       =             extend.right    )
         ,
         line.new(
           monOi        ,   math.avg (friCp  , monOp  ) 
         , monOi +1     ,   math.avg (friCp  , monOp  )
         , color        =             cNWOG1
         , style        =             line.style_dotted
         , extend       =             extend.right    )         
         ))
        bl = bl_NWOG.pop(), bl.b.delete(), bl.l.delete()
    if iNDOG
        cuDOp := open    , cuDOi := n
        prDCp := close[1], prDCi := n -1
        //
        bl_NDOG.unshift(bx_ln.new(   
         box.new(
           prDCi        ,   math.max (prDCp  , cuDOp  )
         , cuDOi        ,   math.min (prDCp  , cuDOp  )
         , bgcolor      =   color    ( na             )
         , border_color =             cNDOG2
         , extend       =             extend.right    )
         ,
         line.new(
           cuDOi        ,   math.avg (prDCp  , cuDOp  ) 
         , cuDOi +1     ,   math.avg (prDCp  , cuDOp  )
         , color        =             cNDOG1
         , style        =             line.style_dotted
         , extend       =             extend.right    )     
         ))
        bl = bl_NDOG.pop(), bl.b.delete(), bl.l.delete()


// SB session vLines & 'lock' previous FVG boxes
if strSB
    if iShowSB
        l_SB.unshift(line.new(n, close, n, close + minT
                   , color= col_SB, extend=extend.both))


if endSB
    if iShowSB 
        l_SB.unshift(line.new(n, close, n, close + minT
                   , color= col_SB, extend=extend.both))

//-----------------------------------------------------------------------------}
//Methods/Functions
//-----------------------------------------------------------------------------{
method type(string str) => 
    ( syminfo.type == 'stock'   and str == 'stock'  ) or 
     (syminfo.type == 'futures' and str == 'futures') or 
     (syminfo.type == 'index'   and str == 'index'  ) or 
     (syminfo.type == 'forex'   and str == 'forex'  ) or 
     (syminfo.type == 'crypto'  and str == 'crypto' ) or 
     (syminfo.type == 'fund'    and str == 'fund'   ) 

//-------------------------------------------}
//Plotchar/table
//-------------------------------------------}
tfs = (60 / (timeframe.in_seconds(timeframe.period) / 60)) / 2

plotchar(not na(SB_LN_per) and na(SB_LN_per[1]) and iShowSB
 , title= '3-4 AM' , location=location.top, text= '3-4 AM\nNY', color=color(na)
 , textcolor=col_SB, offset= +tfs)

plotchar(not na(SB_AM_per) and na(SB_AM_per[1]) and iShowSB
 , title='10-11 AM', location=location.top, text='10-11 AM\nNY', color=color(na)
 , textcolor=col_SB, offset= +tfs)

plotchar(not na(SB_PM_per) and na(SB_PM_per[1]) and iShowSB
 , title= '2-3 PM' , location=location.top, text= '2-3 PM\nNY', color=color(na)
 , textcolor=col_SB, offset= +tfs)

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

// If different opens share the same opening prices, their labels will overlap
// Instead, we pad the higher time frame ones for achieving a "separator sheet"
f_getPadding(int _index) =>
    _padding = 0
    // Weekly, monthly, or yearly overlaps the daily open
    if _index > 0 and canShowDaily and array.get(a_lastOpens, 0) == array.get(a_lastOpens, _index)
        _padding += 1
    // Monthly or yearly overlaps the weekly open
    if _index > 1 and canShowWeekly and array.get(a_lastOpens, 1) == array.get(a_lastOpens, _index)
        _padding += 1
    // Yearly overlaps the monthly open
    if _index > 2 and canShowMonthly and array.get(a_lastOpens, 2) == array.get(a_lastOpens, _index)
        _padding += 1
    _padding

f_getRightBarIndex(int _padding) => bar_index + i_rightOffset + _padding * OFFSET_PADDING

// On the weekly time frame, the monthly/yearly open is drawn from the first candle of the month/year
// The first weekly candle is not necessarily containing the first day of the month
// In such case, we visually anchor the open of the previous weekly candle
f_isContainedOnPrevWeekly(int _time) => timeframe.isweekly and dayofmonth(time) > dayofmonth(_time)

// Create and update an open level. An open is composed of a label, a "body" line, and optional "tail" and "head" lines extensions
f_draw(bool _newOpen, float _y, int _lookback, int _padding, color _color, string _text, bool _prevTime=false) =>
    var line _body  = na 
    var _label      = label.new(na, na, _text, style=LABEL_STYLE, textcolor=_color, size=LABEL_SIZE)
    var _tail       = line.new(na, na, na, na, color=_color, style=TAIL_STYLE, width=LINE_WIDTH, extend=extend.left)
    var _bodies     = array.new_line()
    var _heads      = array.new_line()
    _start          = _prevTime ? bar_index - 1 : bar_index
    _end            = f_getRightBarIndex(_padding)

    if _newOpen
        // Label
        label.set_xy(_label, _end, _y)
        label.set_tooltip(_label, str.tostring(_y, format.mintick))

        // Body
        line.set_x2(_body, bar_index)
        _body := line.new(_start, _y, _end, _y, color=_color, style=LINE_STYLE, width=LINE_WIDTH)
        array.push(_bodies, _body)

        // Head
        if i_areHeadsEnabled
            array.push(_heads, line.new(bar_index, _y, bar_index, _y, color=color.new(_color, HEAD_TRANSP), style=LINE_STYLE, width=LINE_WIDTH))

        // Tail
        if i_areTailsEnabled
            line.set_xy1(_tail, _start - 1, _y)
            line.set_xy2(_tail, _start, _y)

        if array.size(_bodies) > _lookback
            line.delete(array.shift(_bodies))
        if array.size(_heads) > _lookback
            line.delete(array.shift(_heads))

    if barstate.islast
        line.set_x2(_body, _end)
        label.set_x(_label, _end)

        if i_areHeadsEnabled and array.size(_heads) > 1
            // Not updating the last open's projection
            for i = 0 to array.size(_heads) - 2
                // Avoid projecting on last opens bodies
                if not array.includes(a_lastOpens, line.get_y1(array.get(_heads, i)))
                    line.set_x2(array.get(_heads, i), f_getRightBarIndex(HEAD_PADDING))

//--------------------------------------------------------------------
//                                Logic
//--------------------------------------------------------------------

if canShowDaily and hasDailyTimeChanged 
    array.set(a_lastOpens, 0, discoverPrices ? open : dailyOpen)

if canShowWeekly and hasWeekklyTimeChanged
    array.set(a_lastOpens, 1, discoverPrices ? open : weeklyOpen)

if canShowMonthly and hasMonthlyTimeChanged
    array.set(a_lastOpens, 2, discoverPrices ? open : monthlyOpen)

if canShowYearly and hasYearlyTimeChanged
    array.set(a_lastOpens, 3, discoverPrices ? open : yearlyOpen)

//--------------------------------------------------------------------
//                          Plotting & styling
//--------------------------------------------------------------------

if canShowYearly
    f_draw(hasYearlyTimeChanged, array.get(a_lastOpens, 3), i_yearlyLookback, f_getPadding(3), i_yearlyColor, "Y   ", f_isContainedOnPrevWeekly(yearlyTime))

if canShowMonthly
    f_draw(hasMonthlyTimeChanged, array.get(a_lastOpens, 2), i_monthlyLookback, f_getPadding(2), i_monthlyColor, "M   ", f_isContainedOnPrevWeekly(monthlyTime))

if canShowWeekly
    f_draw(hasWeekklyTimeChanged, array.get(a_lastOpens, 1), i_weeklyLookback, f_getPadding(1), i_weeklyColor, "W   ")

if canShowDaily
    f_draw(hasDailyTimeChanged, array.get(a_lastOpens, 0), i_dailyLookback, f_getPadding(0), i_dailyColor, "D   ")

// Plot invisible opens for displaying last values in `status line`, `scale`, `data window` as well for providing defaults alert conditions

plot(array.get(a_lastOpens, 0), "D",    color.new(i_dailyColor, 100),   editable=false)
plot(array.get(a_lastOpens, 1), "W",    color.new(i_weeklyColor, 100),  editable=false)
plot(array.get(a_lastOpens, 2), "M",    color.new(i_monthlyColor, 100), editable=false)
plot(array.get(a_lastOpens, 3), "Y",    color.new(i_yearlyColor, 100),  editable=false)