SessionInBoxesPro
// This source code is subject to the terms of the Mozilla Public License 2.0 at https://mozilla.org/MPL/2.0/
// © theATR

//@version=5
library("SessionInBoxesPro")

///////////////
// Functions
///////////////
export get_time_by_bar(int bar_count) => timeframe.multiplier * bar_count * 60 * 1000

export get_positions_func (int sessiontime_, int duration_) =>
    int max = math.round(duration_ / timeframe.multiplier) // minutes to bar length
    int begin_index = 0
    
    for i = 1 to max
        if (not na(sessiontime_[i]))
            begin_index := begin_index + 1

    int to_index = math.max(begin_index, 1)
    int pR = bar_index
    int pL = bar_index - begin_index
    float pT = math.max(ta.highest(high[1], to_index), 0)
    float pB = math.min(ta.lowest(low[1],   to_index), high)
    
    [pT, pB]

export get_period (int _session, int _start, int _lookback) =>
    result = math.max(_start, 1)
    for i = result to _lookback
        if na(_session[i+1]) and _session[i]
            result := i
            break
    result

export is_start (int _session) => na(_session[1]) and _session

export is_end (int _session) => na(_session) and _session[1]

f_get_day (n) =>
    switch n
        1 => 'Sun'
        2 => 'Mon'
        3 => 'Tue'
        4 => 'Wed'
        5 => 'Thu'
        6 => 'Fri'
        7 => 'Sat'

// ------------------------
// Drawing progress
// ------------------------
// [├, ┤, ∅, •]
export draw_progress (bool _show, int _session, bool _is_started, bool _is_ended, color _color, float _bottom, bool _delete_history) =>
    var label my_label = na
    // Variables
    icon_closed = '⚀'
    
    if _show
        if _is_started
            my_label := label.new(time, _bottom, '', textcolor=color.new(_color, 10), style=label.style_label_lower_right, color=color.new(color.black, 100), xloc=xloc.bar_time, size=size.small)
            
            if _delete_history
                label.delete(my_label[1])

        if _session
            label.set_y(my_label, _bottom)
            label.set_x(my_label, time)
        
        if _is_ended
            label.set_text(my_label, icon_closed)
            label.set_x(my_label, time)

// ------------------------
// Drawing labels
// ------------------------
export draw_label (bool _show, int _session, bool _is_started, color _color, float _top, float _bottom, string _text, bool _delete_history, string i_label_chg, string i_label_size, string i_label_position, string i_tz, bool i_label_format_day) =>
    var label my_label = na
    label_text = array.new_string(1, _text)
    f_price = '{0,number,#.#####}'
    f_pip = '{0,number,#.#}'    
    float pips = syminfo.mintick * 10
    icon_separator = ' • '
    
    var int start_time = na
    if _is_started
        start_time := time
    
    
    chg = _top - _bottom
    chg_text = i_label_chg == "YES" 
      ? str.format(f_price, chg)
      : str.format(f_pip, chg / pips)

    if i_label_chg != "NO"
        array.push(label_text, chg_text)
    
    if i_label_format_day
        array.push(label_text, f_get_day(dayofweek(start_time, i_tz)))
    
    if _show
        if _is_started
            my_label := label.new(time, _top, array.join(label_text, icon_separator), textcolor=_color, color=color.new(color.black, 100), size=i_label_size, style=i_label_position, xloc=xloc.bar_time)
            
            if _delete_history
                label.delete(my_label[1])

        if _session
            label.set_y(my_label, _top)
            label.set_text(my_label, array.join(label_text, icon_separator))

// ------------------------
// Drawing fibonacci lines
// ------------------------
export draw_fib (bool _show, int _session, bool _is_started, color _color, float _top, float _bottom, float _level, int _width, string _style, bool _is_extend, bool _delete_history) =>
    var line my_line = na
    y = (_top - _bottom) * _level + _bottom

    if _show
        if _is_started
            my_line := line.new(time, y, time, y, width=_width, color=color.new(_color, 30), style=_style, xloc=xloc.bar_time)

            if _is_extend
                line.set_extend(my_line, extend.right)

            if _delete_history
                line.delete(my_line[1])

        if _session
            line.set_x2(my_line, time + get_time_by_bar(1))
            line.set_y1(my_line, y)
            line.set_y2(my_line, y)


// ------------------------
// Drawing Opening range
// ------------------------
export get_op_stricts(int _session, bool _is_started, float top, float bottom, int i_o_minutes)=>
    var is_closeover = false
    var is_closeunder = false
    var int x1 = na
    is_closeover := false
    is_closeunder := false
    if _is_started
        x1 := time
    if _session
        is_op = x1 + (i_o_minutes * 60 * 1000)
        
        if time >= is_op and time < is_op + get_time_by_bar(1)
            is_closeover := false
            is_closeunder := false

        else
            is_closeover := close>top
            is_closeunder := close<bottom
    [is_closeover,is_closeunder]

export draw_op (bool _show, int _session, bool _is_started, color _color, float top, float bottom, bool _is_extend, bool _delete_history, int i_o_minutes, int i_o_opacity) =>
    var int x1 = na
    var box my_box = na
    // top = ta.highest(high, _max)
    // bottom = ta.lowest(low, _max)
    is_crossover = ta.crossover(close, box.get_top(my_box))
    is_crossunder = ta.crossunder(close, box.get_bottom(my_box))
    
    if _show
        if _is_started
            x1 := time
            my_box := na

            if _delete_history
                box.delete(my_box[1])
                my_box
                

        if _session
            is_op = x1 + (i_o_minutes * 60 * 1000)
            
            if time >= is_op and time < is_op + get_time_by_bar(1)
                my_box := box.new(x1, top, time, bottom, xloc=xloc.bar_time, border_width=0, bgcolor=color.new(_color, i_o_opacity))

                if _is_extend
                    box.set_extend(my_box, extend.right)
                    my_box

            else
                box.set_right(my_box, time + get_time_by_bar(1))

                if is_crossover
                    alert('Price crossed over the opening range', alert.freq_once_per_bar)

                if is_crossunder
                    alert('Price crossed under the opening range', alert.freq_once_per_bar)

                my_box

// ------------------------
// Drawing market
// ------------------------

export get_pm_stricts(bool _show, bool _show_pm, string tf, string ctf, bool _is_started)=>
    
    var line pm_top = na 
    var line pm_bott = na
    var label pm_top_lbl = na 
    var label pm_bott_lbl = na
    
    var label err_lbl = na
    
    a=0
    top=0.0
    bottom=10000000000000000.0
    if _is_started and _show and _show_pm
        if tf=="15m"
            if ctf == "1" or ctf == "3" or ctf == "5" 
                lb = ctf == "1"?15:ctf == "3"?5:3
                
                
                for i = 0 to lb
                    top:=high[i]>top?high[i]:top
                
                
                for i = 0 to lb
                    bottom:=low[i]<bottom?low[i]:bottom
                
                
                a:=1
            
        else if tf=="30m"
            if ctf == "1" or ctf == "3" or ctf == "5" or ctf == "10"  
                lb = ctf == "1"?30:ctf == "3"?10:ctf == "5"?6:3
                
                for i = 0 to lb
                    top:=high[i]>top?high[i]:top
                
                for i = 0 to lb
                    bottom:=low[i]<bottom?low[i]:bottom
                
                a:=1
        else if tf=="45m"
            if ctf == "1" or ctf == "3" or ctf == "5" or ctf == "10" or ctf == "15" 
                lb = ctf == "1"?45:ctf == "3"?15:ctf == "5"?9:ctf == "10"?3:2
                
                for i = 0 to lb
                    top:=high[i]>top?high[i]:top
                
                for i = 0 to lb
                    bottom:=low[i]<bottom?low[i]:bottom
                
                a:=1   
        else if tf=="1h"
            if ctf == "1" or ctf == "3" or ctf == "5" or ctf == "10" or ctf == "15" or ctf == "30" 
                lb = ctf == "1"?60:ctf == "3"?20:ctf == "5"?12:ctf == "10"?6:ctf == "15"?4:2
                
                for i = 0 to lb
                    top:=high[i]>top?high[i]:top
                
                for i = 0 to lb
                    bottom:=low[i]<bottom?low[i]:bottom
                
                a:=1
        else if tf=="2h"
            if ctf == "1" or ctf == "3" or ctf == "5" or ctf == "10" or ctf == "15" or ctf == "30" or ctf == "60" 
                lb = ctf == "1"?120:ctf == "3"?40:ctf == "5"?24:ctf == "10"?12:ctf == "15"?8:ctf == "30"?4:2
                
                for i = 0 to lb
                    top:=high[i]>top?high[i]:top
                
                for i = 0 to lb
                    bottom:=low[i]<bottom?low[i]:bottom
                
                a:=1
            
        else if tf=="4h"
            if ctf == "1" or ctf == "3" or ctf == "5" or ctf == "10" or ctf == "15" or ctf == "30" or ctf == "60" or ctf == "120" 
                lb = ctf == "1"?240:ctf == "3"?80:ctf == "5"?48:ctf == "10"?24:ctf == "15"?16:ctf == "30"?8:ctf == "60"?4:2
                
                for i = 0 to lb
                    top:=high[i]>top?high[i]:top
                
                for i = 0 to lb
                    bottom:=low[i]<bottom?low[i]:bottom
                
                a:=1
            
          
    [top,bottom]


export draw_pm(bool _show, bool _show_pm, string tf, string ctf, bool _is_started, bool _is_ended, bool _delete_history, color _color)=>
    
    var line pm_top = na 
    var line pm_bott = na
    var label pm_top_lbl = na 
    var label pm_bott_lbl = na
    
    var label err_lbl = na
    
    var pm_start=false
    pm_start:=_is_started
    pm_start:=pm_start!=pm_start[1]?true:false
    
    var pm_ends=false
    pm_ends:=_is_ended
    pm_ends:=pm_ends!=pm_ends[1]?true:false
    
    a=0
    top=0.0
    bottom=10000000000000000.0
    
    if _is_started and _show and _show_pm
        if tf=="15m"
            if ctf == "1" or ctf == "3" or ctf == "5" 
                lb = ctf == "1"?15:ctf == "3"?5:3
                
                
                for i = 0 to lb
                    top:=high[i]>top?high[i]:top
                
                for i = 0 to lb
                    bottom:=low[i]<bottom?low[i]:bottom
                
                
                pm_top:=line.new(time[lb],top,time,top,xloc.bar_time,color=_color)
                pm_top_lbl:=label.new(time[lb],top,"Pre Mkt H",xloc.bar_time, color=#00000000, textcolor=_color, style=label.style_label_down )
                pm_bott:=line.new(time[lb],bottom,time,bottom,xloc.bar_time,color=_color)
                pm_bott_lbl:=label.new(time[lb],bottom,"Pre Mkt L",xloc.bar_time, color=#00000000, textcolor=_color, style=label.style_label_up )
                a:=1
            else
                err_lbl :=label.new(time,close,"Error:TF not matching.",xloc.bar_time,color=_color, textcolor=color.white)
                a:=1
        else if tf=="30m"
            if ctf == "1" or ctf == "3" or ctf == "5" or ctf == "10"  
                lb = ctf == "1"?30:ctf == "3"?10:ctf == "5"?6:3
                
                for i = 0 to lb
                    top:=high[i]>top?high[i]:top
                
                for i = 0 to lb
                    bottom:=low[i]<bottom?low[i]:bottom
                
                pm_top:=line.new(time[lb],top,time,top,xloc.bar_time,color=_color)
                pm_top_lbl:=label.new(time[lb],top,"Pre Mkt H",xloc.bar_time, color=#00000000, textcolor=_color, style=label.style_label_down )
                pm_bott:=line.new(time[lb],bottom,time,bottom,xloc.bar_time,color=_color)
                pm_bott_lbl:=label.new(time[lb],bottom,"Pre Mkt L",xloc.bar_time, color=#00000000, textcolor=_color, style=label.style_label_up )
                a:=1
            else
                err_lbl :=label.new(time,close,"Error:TF not matching.",xloc.bar_time,color=_color, textcolor=color.white)
                a:=1
        else if tf=="45m"
            if ctf == "1" or ctf == "3" or ctf == "5" or ctf == "10" or ctf == "15" 
                lb = ctf == "1"?45:ctf == "3"?15:ctf == "5"?9:ctf == "10"?3:2
                
                for i = 0 to lb
                    top:=high[i]>top?high[i]:top
                
                for i = 0 to lb
                    bottom:=low[i]<bottom?low[i]:bottom
                
                pm_top:=line.new(time[lb],top,time,top,xloc.bar_time,color=_color)
                pm_top_lbl:=label.new(time[lb],top,"Pre Mkt H",xloc.bar_time, color=#00000000, textcolor=_color, style=label.style_label_down )
                pm_bott:=line.new(time[lb],bottom,time,bottom,xloc.bar_time,color=_color)
                pm_bott_lbl:=label.new(time[lb],bottom,"Pre Mkt L",xloc.bar_time, color=#00000000, textcolor=_color, style=label.style_label_up )
                a:=1
            else
                err_lbl :=label.new(time,close,"Error:TF not matching.",xloc.bar_time,color=_color, textcolor=color.white)
                a:=1    
        else if tf=="1h"
            if ctf == "1" or ctf == "3" or ctf == "5" or ctf == "10" or ctf == "15" or ctf == "30" 
                lb = ctf == "1"?60:ctf == "3"?20:ctf == "5"?12:ctf == "10"?6:ctf == "15"?4:2
                
                for i = 0 to lb
                    top:=high[i]>top?high[i]:top
                
                for i = 0 to lb
                    bottom:=low[i]<bottom?low[i]:bottom
                
                pm_top:=line.new(time[lb],top,time,top,xloc.bar_time,color=_color)
                pm_top_lbl:=label.new(time[lb],top,"Pre Mkt H",xloc.bar_time, color=#00000000, textcolor=_color, style=label.style_label_down )
                pm_bott:=line.new(time[lb],bottom,time,bottom,xloc.bar_time,color=_color)
                pm_bott_lbl:=label.new(time[lb],bottom,"Pre Mkt L",xloc.bar_time, color=#00000000, textcolor=_color, style=label.style_label_up )
                a:=1
            else
                err_lbl :=label.new(time,close,"Error:TF not matching.",xloc.bar_time,color=_color, textcolor=color.white)
                a:=1
        else if tf=="2h"
            if ctf == "1" or ctf == "3" or ctf == "5" or ctf == "10" or ctf == "15" or ctf == "30" or ctf == "60" 
                lb = ctf == "1"?120:ctf == "3"?40:ctf == "5"?24:ctf == "10"?12:ctf == "15"?8:ctf == "30"?4:2
                
                for i = 0 to lb
                    top:=high[i]>top?high[i]:top
                
                for i = 0 to lb
                    bottom:=low[i]<bottom?low[i]:bottom
                
                pm_top:=line.new(time[lb],top,time,top,xloc.bar_time,color=_color)
                pm_top_lbl:=label.new(time[lb],top,"Pre Mkt H",xloc.bar_time, color=#00000000, textcolor=_color, style=label.style_label_down )
                pm_bott:=line.new(time[lb],bottom,time,bottom,xloc.bar_time,color=_color)
                pm_bott_lbl:=label.new(time[lb],bottom,"Pre Mkt L",xloc.bar_time, color=#00000000, textcolor=_color, style=label.style_label_up )
                a:=1
            else
                err_lbl :=label.new(time,close,"Error:TF not matching.",xloc.bar_time,color=_color, textcolor=color.white)
                a:=1
        else if tf=="4h"
            if ctf == "1" or ctf == "3" or ctf == "5" or ctf == "10" or ctf == "15" or ctf == "30" or ctf == "60" or ctf == "120" 
                lb = ctf == "1"?240:ctf == "3"?80:ctf == "5"?48:ctf == "10"?24:ctf == "15"?16:ctf == "30"?8:ctf == "60"?4:2
                
                for i = 0 to lb
                    top:=high[i]>top?high[i]:top
                
                for i = 0 to lb
                    bottom:=low[i]<bottom?low[i]:bottom
                
                pm_top:=line.new(time[lb],top,time,top,xloc.bar_time,color=_color)
                pm_top_lbl:=label.new(time[lb],top,"Pre Mkt H",xloc.bar_time, color=#00000000, textcolor=_color, style=label.style_label_down )
                pm_bott:=line.new(time[lb],bottom,time,bottom,xloc.bar_time,color=_color)
                pm_bott_lbl:=label.new(time[lb],bottom,"Pre Mkt L",xloc.bar_time, color=#00000000, textcolor=_color, style=label.style_label_up )
                a:=1
            else
                err_lbl :=label.new(time,close,"Error:TF not matching.",xloc.bar_time,color=_color, textcolor=color.white)                       
                a:=1
          
        
    else if _is_ended and _delete_history and _show and _show_pm
        line.delete(pm_top[1])
        line.delete(pm_bott[1])
        label.delete(pm_top_lbl[1])
        label.delete(pm_bott_lbl[1])
        label.delete(err_lbl[1])
        a:=1
export draw_market (bool _show, int _session, bool _is_started, color _color, int btr, float _top, float _bottom, string _extend, bool _is_extend, bool _delete_history, string i_sess_border_style, int i_sess_border_width, int i_sess_bgopacity) =>
    var box my_box = na
    should_draw_endline = (not _is_extend) or (_extend == 'Extend + End line')
    var box_x = 0 
    
    if _show
        if _is_started
            my_box := box.new(time, _top, time, _bottom, xloc=xloc.bar_time)
            box.set_border_style(my_box, i_sess_border_style)
            box.set_border_width(my_box, i_sess_border_width)
            box.set_border_color(my_box, color.new(_color, btr))
            box.set_bgcolor(my_box, color.new(_color, i_sess_bgopacity))

            if _is_extend
                box.set_extend(my_box, extend.right)

            if _delete_history
                box.delete(my_box[1])
    
        // Proccesing
        if _session
            box.set_top(my_box, _top)
            box.set_bottom(my_box, _bottom)

            if should_draw_endline
                box.set_right(my_box, time + get_time_by_bar(1))

// ------------------------
// Drawing
// ------------------------
export draw (bool _show, bool _show_pm, string pm_tf, string ctf, int _session, color _color, int btr, string _label, string _extend, bool _show_fib, bool _show_op, string i_label_chg, string i_label_size, string i_label_position, int i_o_minutes, int i_o_opacity, string i_sess_border_style, int i_sess_border_width, int i_sess_bgopacity, bool i_show_history, bool i_show_closed, bool i_label_show, int i_f_linewidth, string i_f_linestyle, float top, float bottom, string i_tz, bool i_label_format_day) =>
    // max_bars_back(_session, 750)
    // max = get_period(_session, 1, _lookback)
    // top = ta.highest(high, max)
    // bottom = ta.lowest(low, max)
    
    is_started = is_start(_session)
    is_ended = is_end(_session)
    is_extend = _extend != "NO"

    delete_history = (not i_show_history) or is_extend

    draw_market(_show, _session, is_started, _color, btr, top, bottom, _extend, is_extend, delete_history, i_sess_border_style, i_sess_border_width, i_sess_bgopacity)
    
    if _show_pm
        draw_pm(_show,_show_pm, pm_tf, ctf, is_started, is_ended, delete_history, _color)
    
    if i_show_closed
        draw_progress(_show, _session, is_started, is_ended, _color, bottom, delete_history)
    
    if i_label_show
        draw_label(_show, _session, is_started, _color, top, bottom, _label, delete_history, i_label_chg, i_label_size, i_label_position, i_tz, i_label_format_day)

    if _show_op
        draw_op(_show, _session, is_started, _color, top, bottom, is_extend, delete_history, i_o_minutes, i_o_opacity)

    if _show_fib
        draw_fib(_show, _session, is_started, _color, top, bottom, 0.500, 2, line.style_solid, is_extend, delete_history)
        draw_fib(_show, _session, is_started, _color, top, bottom, 0.628, i_f_linewidth, i_f_linestyle, is_extend, delete_history)
        draw_fib(_show, _session, is_started, _color, top, bottom, 0.382, i_f_linewidth, i_f_linestyle, is_extend, delete_history)
    
    _session
