market sessions by sellstreet
// This source code is subject to the terms of the Mozilla Public License 2.0 at https://mozilla.org/MPL/2.0/
// © sellstreet

//@version=5
indicator("market sessions by sellstreet", overlay=true, precision=0,
  max_bars_back=1500, max_lines_count=500, max_labels_count=500, max_boxes_count=500)
  // format=format.price, precision=0, scale=scale.none)

gr_1="HISTORY"
Show_Opens = input.bool(defval=true, title = "Opens", inline="0_1", group=gr_1)
Only_Last_Opens = input.bool(defval=true, title = "Only Last Opens", inline="0_1", group=gr_1)

Show_Sessions = input.bool(defval=true, title = "Sessions", inline="0_2", group=gr_1)
Only_Last_Sessions = input.bool(defval=false, title = "Only Last Sessions", inline="0_2", group=gr_1)

Show_Days_VLines = input.bool(defval=false, title = "Days Lines", inline="0_3", group=gr_1)
// Show_Days_Bg = input.bool(defval=false, title = "Days Background", inline="0_3", group=gr_1)


gr_2="OPENS"
ULine_Width = input.int(defval=1, title = "Line Width", minval=0, maxval=7, step=1, group=gr_2)
Show_Daily_Open = input.bool(defval=true, title = "Daily Open", inline = "2_1", group=gr_2)
Col_Daily_Open = input.color(color.rgb(0, 0, 0, 0), "", inline = "2_1", group=gr_2)
Show_Weekly_Open = input.bool(defval=true, title = "Weekly Open", inline = "2_1", group=gr_2)
Col_Weekly_Open = input.color(color.rgb(0, 0, 0, 0), "", inline = "2_1", group=gr_2)

Show_Monthly_Open = input.bool(defval=true, title = "Monthly Open", inline = "2_2", group=gr_2)
Col_Monthly_Open = input.color(color.rgb(0, 0, 0, 0), "", inline = "2_2", group=gr_2)
Show_NewYork_Midnight = input.bool(defval=true, title = "New York Midnight", inline = "2_2", group=gr_2)
Col_NewYork_Midnight = input.color(color.rgb(0, 0, 0, 0), "", inline = "2_2", group=gr_2)

Show_Previous_Daily_High = input.bool(defval=true, title = "Previous Daily High", inline = "2_3", group=gr_2)
Col_Previous_Daily_High = input.color(color.rgb(0, 0, 0, 0), "", inline = "2_3", group=gr_2)
Show_Previous_Daily_Low = input.bool(defval=true, title = "Previous Daily Low", inline = "2_3", group=gr_2)
Col_Previous_Daily_Low = input.color(color.rgb(0, 0, 0, 0), "", inline = "2_3", group=gr_2)

SLine_Width = input.int(defval=0, title = "Session Line Width", minval=0, maxval=7, step=1)

// gr_4="SYDNEY"
// Show_Sydney = input.bool(defval=false, title = "SYDNEY", inline="4_1", group=gr_4)
// Sydney_Display = input.string(defval="Border", title = "", options=["Border", "Filled Box"], inline="4_1", group=gr_4)
// Title_Col_Sydney = input.color(color.rgb(88, 43, 143, 20), "Title", inline="4_2", group=gr_4)
// Background_Col_Sydney = input.color(color.rgb(88, 43, 143, 65), "Background", inline="4_2", group=gr_4)

gr_3="ASIA"
Show_Asia = input.bool(defval=true, title = "ASIA", inline="3_1", group=gr_3)
Asia_Display = input.string(defval="Filled Box", title = "", options=["Border", "Filled Box"], inline="3_1", group=gr_3)
Title_Col_Asia = input.color(color.rgb(0, 0, 0, 0), "Title", inline="3_2", group=gr_3)
Background_Col_Asia = input.color(color.rgb(230, 184, 0, 90), "Background", inline="3_2", group=gr_3)

gr_8="Frankfurt"
Show_Frankfurt = input.bool(defval=true, title = "Frankfurt", inline="8_1", group=gr_8)
Frankfurt_Display = input.string(defval="Filled Box", title = "", options=["Border", "Filled Box"], inline="8_1", group=gr_8)
Title_Col_Frankfurt = input.color(color.rgb(0, 0, 0, 0), "Title", inline="8_2", group=gr_8)
Background_Col_Frankfurt = input.color(color.rgb(204, 51, 0, 80), "Background", inline="8_2", group=gr_8)

gr_5="LONDON"
Show_London = input.bool(defval=true, title = "London", inline="5_1", group=gr_5)
London_Display = input.string(defval="Filled Box", title = "", options=["Border", "Filled Box"], inline="5_1", group=gr_5)
Title_Col_London = input.color(color.rgb(0, 0, 0, 0), "Title", inline="5_2", group=gr_5)
Background_Col_London = input.color(color.rgb(128, 170, 255, 90), "Background", inline="5_2", group=gr_5)

gr_6="NEW YORK"
Show_NewYork = input.bool(defval=true, title = "NEW YORK", inline="6_1", group=gr_6)
NewYork_Display = input.string(defval="Filled Box", title = "", options=["Border", "Filled Box"], inline="6_1", group=gr_6)
Title_Col_NewYork = input.color(color.rgb(0, 0, 0, 0), "Title", inline="6_2", group=gr_6)
Background_Col_NewYork = input.color(color.rgb(179,102,255, 90), "Background", inline="6_2", group=gr_6)

gr_7="CBDR"
Show_CBDR = input.bool(defval=true, title = "CBDR", inline="7_1", group=gr_7)
CBDR_Display = input.string(defval="Filled Box", title = "", options=["Border", "Filled Box"], inline="7_1", group=gr_7)
Title_Col_CBDR = input.color(color.rgb(0, 0, 0, 0), "Title", inline="7_2", group=gr_7)
Background_Col_CBDR = input.color(color.rgb(255,102,102, 90), "Background", inline="7_2", group=gr_7)



// ----- Utils -----
// https://en.wikipedia.org/wiki/List_of_tz_database_time_zones
// https://www.forexmarkethours.com/

__Get_Price_At_Midnight(Timezone="America/New_York") =>
    var float res_open = na
    session_str = "0000-0000:1234567"    // Sun to Sat
    if time("D", session_str, Timezone) != time("D", session_str, Timezone)[1]
        res_open := open
    res_open

Get_Price_At_Midnight(Timezone="America/New_York") =>
    float Midnight_Price = request.security(syminfo.tickerid, "60", __Get_Price_At_Midnight(Timezone),
      gaps=barmerge.gaps_off, lookahead=barmerge.lookahead_on)
    Midnight_Price

Session_Range_Info(Range="0800-1600", Timezone="America/New_York") =>
    time_val = time("1", Range, Timezone)
    in_range = not na(time_val) ? true : false
    is_begin = in_range and na(time("1", Range, Timezone)[1])
    [in_range, is_begin, time_val]

Draw_Label(XLoc, YPrice, Text, TColor) =>
    nlabel = label.new(x=XLoc, y=YPrice, text=Text, textcolor=TColor, size=size.normal,
      xloc=xloc.bar_time, yloc=yloc.price, text_font_family=font.family_monospace, style=label.style_none, textalign=text.align_center)
    nlabel



// ----- Calc and Show Opens -----
float Daily_Open = Show_Opens and Show_Daily_Open ? request.security(syminfo.tickerid, "D", open, lookahead=barmerge.lookahead_on) : na
float Weekly_Open = Show_Opens and Show_Weekly_Open ? request.security(syminfo.tickerid, "W", open, lookahead=barmerge.lookahead_on) : na
float Monthly_Open = Show_Opens and Show_Monthly_Open ? request.security(syminfo.tickerid, "M", open, lookahead=barmerge.lookahead_on) : na
float NewYork_Midnight = Show_Opens and Show_NewYork_Midnight ? Get_Price_At_Midnight("America/New_York") : na
float Previous_Daily_High = Show_Opens and Show_Previous_Daily_High ? request.security(syminfo.tickerid, "D", high[1], lookahead=barmerge.lookahead_on) : na
float Previous_Daily_Low = Show_Opens and Show_Previous_Daily_Low ? request.security(syminfo.tickerid, "D", low[1], lookahead=barmerge.lookahead_on) : na

// var line[] DP_Lines_Arr = array.new_line(6)
Draw_Part_Line(XStart, XEnd, YPrice, Text, LColor, LWidth) =>
    line.new(x1=XStart, y1=YPrice, x2=XEnd, y2=YPrice, xloc=xloc.bar_time, color=LColor, width=LWidth, style=line.style_solid)
    Draw_Label(XEnd, YPrice, Text, LColor)

// -- Daily_Open --
if ta.change(Daily_Open, 1) != 0
    XStart = time(timeframe.period, "0000-0000:1234567")
    XEnd = XStart + 24 * 3600 * 1000 //  24 hours

    is_last_line = last_bar_time - XEnd < 3600 * 1000 // less than 1 hour
    // Draw_Label(XStart, low, str.format("LBT: {0}", last_bar_time - XEnd), color.maroon)
    is_draw = Only_Last_Opens ? is_last_line : true
    if is_draw
        Draw_Part_Line(XStart, XEnd, Daily_Open, "DO", Col_Daily_Open, ULine_Width)
    // Draw_Part_Line(XStart, XEnd, Daily_Open, "DO", Col_Daily_Open, ULine_Width)

// -- Weekly_Open --
if ta.change(Weekly_Open, 1) != 0
    XStart = time(timeframe.period, "0000-0000:1234567")
    XEnd = XStart + 7 * 24 * 3600 * 1000 //  7 days

    is_last_line = last_bar_time - XEnd < 3600 * 1000 // less than 1 hour
    is_draw = Only_Last_Opens ? is_last_line : true
    if is_draw
        Draw_Part_Line(XStart, XEnd, Weekly_Open, "WO", Col_Weekly_Open, ULine_Width)
    // Draw_Part_Line(XStart, XEnd, Weekly_Open, "WO", Col_Weekly_Open, ULine_Width)

// -- Monthly_Open --
if ta.change(Monthly_Open, 1) != 0
    XStart = time(timeframe.period, "0000-0000:1234567")
    XEnd = XStart + 30 * 24 * 3600 * 1000 //  30 days

    is_last_line = last_bar_time - XEnd < 3600 * 1000 // less than 1 hour
    is_draw = Only_Last_Opens ? is_last_line : true
    if is_draw
        Draw_Part_Line(XStart, XEnd, Monthly_Open, "MO", Col_Monthly_Open, ULine_Width)
    // Draw_Part_Line(XStart, XEnd, Monthly_Open, "MO", Col_Monthly_Open, ULine_Width)

// ----- Calc and Show New York Midnight -----
// var bool CharSS = false
// CharSS := false
if ta.change(NewYork_Midnight, 1) != 0
    XStart = time(timeframe.period, "0000-0000:1234567")
    XEnd = XStart + 24 * 3600 * 1000 //  24 hours

    // hval = hour(XStart, "America/New_York")
    if not na(time("1", "1700-1800:1", "America/New_York"))
        XEnd := XStart + 7 * 3600 * 1000 //  7 hour
        // CharSS := true
    // else
    //     CharSS := false

    is_last_line = last_bar_time - XEnd < 3600 * 1000 // less than 1 hour
    is_draw = Only_Last_Opens ? is_last_line : true
    if is_draw
        Draw_Part_Line(XStart, XEnd, NewYork_Midnight, "NYM", Col_NewYork_Midnight, ULine_Width)
    // Draw_Part_Line(XStart, XEnd, NewYork_Midnight, "NYM", Col_NewYork_Midnight, ULine_Width)

// triangle char: ▲ ▼ ◄ ►
// plotchar(CharSS, char='▼', location=location.top, color=color.teal, size=size.normal)

// ----- Previous Daily High -----
if ta.change(Previous_Daily_High, 1) != 0
    XStart = time(timeframe.period, "0000-0000:1234567")
    XEnd = XStart + 24 * 3600 * 1000 //  24 hours

    is_last_line = last_bar_time - XEnd < 3600 * 1000 // less than 1 hour
    is_draw = Only_Last_Opens ? is_last_line : true
    if is_draw
        Draw_Part_Line(XStart, XEnd, Previous_Daily_High, "PDH", Col_Previous_Daily_High, ULine_Width)
    // Draw_Part_Line(XStart, XEnd, Previous_Daily_High, "PDH", Col_Previous_Daily_High, ULine_Width)

// ----- Previous Daily Low -----
if ta.change(Previous_Daily_Low, 1) != 0
    XStart = time(timeframe.period, "0000-0000:1234567")
    XEnd = XStart + 24 * 3600 * 1000 //  24 hours

    is_last_line = last_bar_time - XEnd < 3600 * 1000 // less than 1 hour
    is_draw = Only_Last_Opens ? is_last_line : true
    if is_draw
        Draw_Part_Line(XStart, XEnd, Previous_Daily_Low, "PDL", Col_Previous_Daily_Low, ULine_Width)
    // Draw_Part_Line(XStart, XEnd, Previous_Daily_Low, "PDL", Col_Previous_Daily_Low, ULine_Width)



// plot(Daily_Open, color=Col_Daily_Open, linewidth=ULine_Width, title="Daily Open", editable=false, style=plot.style_steplinebr)
// plot(Weekly_Open, color=Col_Weekly_Open, linewidth=ULine_Width, title="Weekly Open", editable=false, style=plot.style_steplinebr)
// plot(Monthly_Open, color=Col_Monthly_Open, linewidth=ULine_Width, title="Monthly Open", editable=false, style=plot.style_steplinebr)
// plot(NewYork_Midnight, color=Col_NewYork_Midnight, linewidth=ULine_Width, title="New York Midnight", editable=false, style=plot.style_steplinebr)
// plot(Previous_Daily_High, color=Col_Previous_Daily_High, linewidth=ULine_Width, title="Previous Daily High", editable=false, style=plot.style_steplinebr)
// plot(Previous_Daily_Low, color=Col_Previous_Daily_Low, linewidth=ULine_Width, title="Previous Daily Low", editable=false, style=plot.style_steplinebr)



// ----- Calc and Show Sessions -----
arr_count = 8
var float[] CHigh_Arr = array.new_float(arr_count)
var float[] CLow_Arr = array.new_float(arr_count)
var box[] Box_Arr = array.new_box(arr_count)
var label[] Label_Arr = array.new_label(arr_count)
var bool[] InRange_Arr = array.new_bool(arr_count)

Session_Draw(SLabel="London", Session_Range="0700-1600", HoursDur=9, Timezone="Greenwich", CI=0,
  Display_Type="Border", SLine_Width=2, Title_Col=color.maroon, Background_Col=color.aqua) =>
    [in_range, is_begin, time_val] = Session_Range_Info(Session_Range, Timezone)
    array.set(InRange_Arr, CI, in_range)

    if is_begin
        end_time = time_val + (HoursDur - 1) * 3600 * 1000    // add Hours Duration
        is_last_box = last_bar_time - time_val < 24 * 3600 * 1000 // less than 24 hour
        is_draw = Only_Last_Sessions ? is_last_box : true

        if is_draw
            array.set(CHigh_Arr, CI, high)
            array.set(CLow_Arr, CI, low)

            NLabel = Draw_Label(time_val + (HoursDur / 2) * 3600 * 1000, high * 1.005, SLabel, Title_Col)
            array.set(Label_Arr, CI, NLabel)

            bg_color = Background_Col    // Display_Type == "Filled Box"
            if Display_Type == "Border"
                bg_color := color.new(Background_Col, 100)

            NBox = box.new(time_val, high * 1.001, end_time, low * 0.999, xloc=xloc.bar_time,
              border_color=Title_Col, border_width=SLine_Width, border_style=line.style_solid, bgcolor=bg_color)
            array.set(Box_Arr, CI, NBox)

        else
            array.set(CHigh_Arr, CI, na)
            array.set(CLow_Arr, CI, na)
            array.set(Label_Arr, CI, na)
            array.set(Box_Arr, CI, na)

    if in_range and not is_begin
        Cur_High = array.get(CHigh_Arr, CI)
        ncHigh = math.max(high, Cur_High)
        array.set(CHigh_Arr, CI, ncHigh)

        Cur_Low = array.get(CLow_Arr, CI)
        ncLow = math.min(low, Cur_Low)
        array.set(CLow_Arr, CI, ncLow)

        Cur_Label = array.get(Label_Arr, CI)
        label.set_y(Cur_Label, ncHigh)

        Cur_Box = array.get(Box_Arr, CI)
        box.set_top(Cur_Box, ncHigh)
        box.set_bottom(Cur_Box, ncLow)

// ----- Sydney, AEDT, UTC +11, with DST -----
// if Show_Sessions and Show_Sydney
//     Session_Draw("Sydney", "0700-1600", 9, "Australia/Sydney", 0, Sydney_Display, SLine_Width, Title_Col_Sydney, Background_Col_Sydney)

// ----- Asia, JST, UTC +9, not DST -----
if Show_Sessions and Show_Asia
    Session_Draw("Asia", "0800-1700", 9, "Asia/Tokyo", 1, Asia_Display, SLine_Width, Title_Col_Asia, Background_Col_Asia)

// ----- London, GMT, UTC +0, not DST -----
if Show_Sessions and Show_London
    Session_Draw("London", "0700-1600", 9, "Greenwich", 2, London_Display, SLine_Width, Title_Col_London, Background_Col_London)
// plotchar(array.get(InRange_Arr, 0), "in_range_London ", "▲", location.bottom, size=size.tiny, color=color.maroon, editable=false)

// ----- New York, EDT, UTC -4, with DST -----
if Show_Sessions and Show_NewYork
    Session_Draw("New York", "0800-1700", 9, "America/New_York", 3, NewYork_Display, SLine_Width, Title_Col_NewYork, Background_Col_NewYork)
// plotchar(array.get(InRange_Arr, 1), "in_range_NewYork ", "▲", location.bottom, size=size.tiny, color=color.maroon, editable=false)

// ----- Central Bank Dealers Range -----
if Show_Sessions and Show_CBDR
    Session_Draw("CBDR", "2000-0000", 4, "Greenwich", 4, CBDR_Display, SLine_Width, Title_Col_CBDR, Background_Col_CBDR)

// // ----- Frankfurt Modify-----
if Show_Sessions and Show_Frankfurt
    Session_Draw("Frankfurt", "0200-0300", 2, "America/New_York", 5, Frankfurt_Display, SLine_Width, Title_Col_Frankfurt, Background_Col_Frankfurt)

// // ----- Hong Kong -----
// if Show_Sessions and Show_HongKong
//     Session_Draw("Hong Kong", "0100-1000", 9, "Asia/Hong_Kong", 5, HongKong_Display, SLine_Width, Title_Col_HongKong, Background_Col_HongKong)
//
// // ----- Singapore -----
// if Show_Sessions and Show_Singapore
//     Session_Draw("Singapore", "0100-1000", 9, "Asia/Singapore", 6, Singapore_Display, SLine_Width, Title_Col_Singapore, Background_Col_Singapore)



// ----- Show Days -----
// Days Utils
dd_ses_str = "0000-0000:1234567"
dd_tz = syminfo.timezone
newDay = ta.change(time("D", dd_ses_str, syminfo.timezone))
LineLengthMult = 100
LineLength = ta.atr(100) * LineLengthMult
bgPlot2 = time(timeframe.period)

isMon() => dayofweek(time("D", dd_ses_str, dd_tz), dd_tz) == dayofweek.monday ? 1 : 0
isTue() => dayofweek(time("D", dd_ses_str, dd_tz), dd_tz) == dayofweek.tuesday ? 1 : 0
isWed() => dayofweek(time("D", dd_ses_str, dd_tz), dd_tz) == dayofweek.wednesday ? 1 : 0
isThu() => dayofweek(time("D", dd_ses_str, dd_tz), dd_tz) == dayofweek.thursday ? 1 : 0
isFri() => dayofweek(time("D", dd_ses_str, dd_tz), dd_tz) == dayofweek.friday ? 1 : 0
isSat() => dayofweek(time("D", dd_ses_str, dd_tz), dd_tz) == dayofweek.saturday ? 1 : 0
isSun() => dayofweek(time("D", dd_ses_str, dd_tz), dd_tz) == dayofweek.sunday ? 1 : 0

txtMon = "M\no\nn\nd\na\ny"
txtTue = "T\nu\ne\ns\nd\na\ny"
txtWed = "W\ne\nd\nn\ne\ns\nd\na\ny"
txtThu = "T\nh\nu\nr\ns\nd\na\ny"
txtFri = "F\nr\ni\nd\na\ny"
txtSat = "S\na\nt\nu\nr\nd\na\ny"
txtSun = "S\nu\nn\nd\na\ny"

drawVerticalLine(offset, cond) =>
    if cond
        line.new(bar_index[offset], low-LineLength, bar_index[offset], high+LineLength,
          color=color.new(color.black, 40), width=1)

// Plot Char and Lines
plotchar(Show_Days_VLines and newDay and isMon(), "Mon", "", location.bottom, color.black, size = size.tiny, text = txtMon, offset=2, editable=false, display=display.all - display.status_line)
plotchar(Show_Days_VLines and newDay and isTue(), "Tue", "", location.bottom, color.black, size = size.tiny, text = txtTue, offset=2, editable=false, display=display.all - display.status_line)
plotchar(Show_Days_VLines and newDay and isWed(), "Wed", "", location.bottom, color.black, size = size.tiny, text = txtWed, offset=2, editable=false, display=display.all - display.status_line)
plotchar(Show_Days_VLines and newDay and isThu(), "Thu", "", location.bottom, color.black, size = size.tiny, text = txtThu, offset=2, editable=false, display=display.all - display.status_line)
plotchar(Show_Days_VLines and newDay and isFri(), "Fri", "", location.bottom, color.black, size = size.tiny, text = txtFri, offset=2, editable=false, display=display.all - display.status_line)
plotchar(Show_Days_VLines and newDay and isSat(), "Sat", "", location.bottom, color.black, size = size.tiny, text = txtSat, offset=2, editable=false, display=display.all - display.status_line)
plotchar(Show_Days_VLines and newDay and isSun(), "Sun", "", location.bottom, color.black, size = size.tiny, text = txtSun, offset=2, editable=false, display=display.all - display.status_line)

drawVerticalLine(0, Show_Days_VLines and newDay and isMon())
drawVerticalLine(0, Show_Days_VLines and newDay and isTue())
drawVerticalLine(0, Show_Days_VLines and newDay and isWed())
drawVerticalLine(0, Show_Days_VLines and newDay and isThu())
drawVerticalLine(0, Show_Days_VLines and newDay and isFri())
drawVerticalLine(0, Show_Days_VLines and newDay and isSat())
drawVerticalLine(0, Show_Days_VLines and newDay and isSun())

// Background Color
// trpv = 90
// bgcolor(Show_Days_Bg and bgPlot2 and isMon() ? color.new(color.red, trpv) : na, title="Time", editable=false)
// bgcolor(Show_Days_Bg and bgPlot2 and isTue() ? color.new(color.orange, trpv): na, title="Time", editable=false)
// bgcolor(Show_Days_Bg and bgPlot2 and isWed() ? color.new(color.yellow, trpv) : na, title="Time", editable=false)
// bgcolor(Show_Days_Bg and bgPlot2 and isThu() ? color.new(color.green, trpv) : na, title="Time", editable=false)
// bgcolor(Show_Days_Bg and bgPlot2 and isFri() ? color.new(color.blue, trpv) : na, title="Time", editable=false)
// bgcolor(Show_Days_Bg and bgPlot2 and isSat() ? color.new(color.purple, trpv) : na, title="Time", editable=false)
// bgcolor(Show_Days_Bg and bgPlot2 and isSun() ? color.new(color.black, trpv) : na, title="Time", editable=false)
