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

//@version=5

indicator("Index Overlay", overlay=true, max_lines_count=500)

// --------------- INPUTS ---------------
var GRP0 = "CODED by @carlob777"
sessionInput = input.string("Set New York time", title='Timezone', options=['Set New York time'], group=GRP0, tooltip= 'please use this indicator only with the New York timezone on your chart')

hoursOffsetInput = '-5'
newDayHr = 0
res = "D"

var GRP2 = "Quick Settings"
showMidnightOpen = input.bool(true, title='Midnight Open', group=GRP2, tooltip = 'show midnight open line')
ShowMidnight = input.bool(true, "Show True Day Separator", group = GRP2, tooltip = 'show day separator bands')
showSundayOpen = input.bool(true, title='Sunday Open', group=GRP2, tooltip = 'show true weekly market open')
showFVG = input.bool(false, title = 'Fair Value Gap', group = GRP2, tooltip =  'show FVGs')
showVWAP = input(false, title="VWAP", group=GRP2, tooltip = 'show VWAP anchored to midnight')
showBreakout = input(false, title="Breakout Probability", group=GRP2, tooltip = 'show the breakout probability for the current candle')
showDailyDividers = input.bool(true, title='Day Labels', group=GRP2, tooltip = 'show day labels')
showTDHTLD = input.bool(false, title='Todays High & Low', group = GRP2, tooltip = 'show today high & low, calculated from midnight to 9:30 equity open')
//showPDHPDL = input.bool(false, title='Yesterdays High & Low', group = GRP2, tooltip = 'show yesterday high & low, calculated from midnight to 11:59pm NY time')
showTT = input.bool(true, title='Trading Time Window', group = GRP2, tooltip = 'show the time window to trade, from 9:30 to 12:00 NY time')
//i_isDailyEnabled = input(true, "Show BSL & SSL Liquitidy Lines", group=GRP2, tooltip = 'show intraday liqidity pools' ) ///// liquidity


var GRP3 = "midnight & sunday open settings"
//midnight
midnightColor = input.color(color.new(#673ab7, 0), title='Midnight Line & Label Color', group=GRP3)
midnightExtendHours = input.int(24, title='Line Extend hours', minval=1, maxval=24, group=GRP3, tooltip = '24 hours default means that the Midnight Open line will last all day, until next Midnight Open')
//sunday open 

sundayColor = input.color(color.new(#434651, 0), title='Sunday Line & Label Color', group=GRP3)
sundayExtendHours = input.int(120, title='Line Extend hours', minval=1, maxval=120, group=GRP3, tooltip = '120 hours default means that the Sunday Open line will last all week, until next Sunday Open')
//general settings lines for both minight and sunday open 
var GRP4 = "Lines General settings"

openLineStyle = input.string(title="Orizontal Line Style", defval="Dotted", options=["Solid", "Dashed", "Dotted"], group=GRP4)
openWidth = input.int(1, title='Orizontal Line Width', minval=1, maxval=4, group=GRP4)
openLabelSize = input.string("Small", title='Label Size', options=['Auto', 'Tiny', 'Small', 'Normal'], group=GRP4)
openMaxLines = 1
TTColor = input.color(color.new(#434651, 80), title='Trading Time Lines Color', group=GRP4)

var GRP5 = "day labels"

dividerLineTextColor = input(color.gray, 'Text Color', group=GRP5)
dayLabelOffset = input.int(10, title='Labels Offset', minval=1, maxval=48, group=GRP5)
midnightColor1 = color.new(#5d606b, 90)
midnight = input.session("0000-0001:1234567", "True Day Open", group = GRP5)

var GRP1 = "vwap settings"
vwapColor = input(color.blue, 'VWAP Color', group=GRP1)
srcVWAP = input(title = "Source", defval = hlc3, group=GRP1)
offsetVWAP = input(0, title="Offset", group=GRP1)

showBand_1 = input(true, title="", group=GRP1, inline="band_1")
stdevMult_1 = input(1.0, title="Bands Multiplier #1", group=GRP1, inline="band_1")
Band_1Color =  input(color.new(color.green, 70), "Color", inline ="band_1",group=GRP1) 

showBand_2 = input(true, title="", group=GRP1, inline="band_2")
stdevMult_2 = input(1.5, title="Bands Multiplier #2", group=GRP1, inline="band_2")
Band_2Color = input(color.new(color.olive, 70), "Color", inline="band_2", group=GRP1)

showBand_3 = input(true, title="", group=GRP1, inline="band_3")
stdevMult_3 = input(2.0, title="Bands Multiplier #3", group=GRP1, inline="band_3")
Band_3Color = input(color.new(color.teal, 70), "Color", inline="band_3", group=GRP1) 

var GRP6 = 'Todays High & Low Settings'
//showLabels = input.bool(defval=true, title="Show labels on Day Lines ", group=GRP6)
//tdhAlert = input(false, title = "Todays High Liquidation Alert", group=GRP6, inline='alert')
//tdlAlert = input(false, title = "Todays Low Liquidation Alert", group=GRP6, inline='alert')
// --------------- INPUTS ---------------

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
///////////////// ================== LOGIC =====================
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

is_newbar(sess) =>
    t = time(res, sess, "America/New_York")
    na(t[1]) and not na(t) or t[1] < t

is_session(sess) =>
    not na(time(timeframe.period, sess, "America/New_York"))
    
//highlight midnight

MidnightNewbar = is_newbar(midnight)
MidnightSession = is_session(midnight)

float midnightLow = na
midnightLow := if MidnightSession
    if MidnightNewbar
        low
    else
        math.min(midnightLow[1],low)
else
    midnightLow

float midnightHigh = na
midnightHigh := if MidnightSession
    if MidnightNewbar
        high
    else
        math.max(midnightHigh[1],high)
else
    midnightHigh

bgcolor(MidnightSession and ShowMidnight ? midnightColor1 : na)

// --------------- CONSTANTS ---------------
TIMEZONE = "GMT"+hoursOffsetInput
ISNEWDAY = dayofweek != dayofweek[1]
TRANSPARENT = color.new(color.white, 100)
exchangeOffset = hour(timenow, TIMEZONE) - hour(timenow, syminfo.timezone)
HOUR = hour + exchangeOffset <= 23 ? hour + exchangeOffset : (hour + exchangeOffset) - 24

// label.new(bar_index, high, str.tostring(HOUR))
// --------------- CONSTANTS ---------------


// --------------- FUNCTIONS ---------------
reso(exp, res) => request.security(syminfo.tickerid, res, exp, lookahead=barmerge.lookahead_on)

getLineStyle(_style) =>
    _linestyle = _style == "Dotted" ? line.style_dotted : _style == "Dashed" ? line.style_dashed : line.style_solid
    _linestyle
    
getlabelSize(_labelSize) =>
    _size = _labelSize == "auto" ? size.auto : _labelSize == "Tiny" ? size.tiny : _labelSize == "Small" ? size.small : _labelSize == "Normal" ? size.normal : size.huge
    _size

addToArray(_array, _val) =>
    array.push(_array, _val)

resolutionInMinutes() =>
    resInMinutes = timeframe.multiplier * (timeframe.isseconds ? 1. / 60 : timeframe.isminutes ? 1. : timeframe.isdaily ? 60. * 24 : timeframe.isweekly ? 60. * 24 * 7 : timeframe.ismonthly ? 60. * 24 * 30.4375 : na)
    int(resInMinutes)

tfInMinutes(simple string tf = "") =>
    float chartTf =
      timeframe.multiplier * (
      timeframe.isseconds ? 1. / 60             :
      timeframe.isminutes ? 1.                  :
      timeframe.isdaily   ? 60. * 24            :
      timeframe.isweekly  ? 60. * 24 * 7        :
      timeframe.ismonthly ? 60. * 24 * 30.4375  : na)
    float result = tf == "" ? chartTf : request.security(syminfo.tickerid, tf, chartTf)

inTimeframe(_t) => tfInMinutes(_t) > tfInMinutes(timeframe.period)

getOhlcData(_t, _var, _offset) =>
    o = reso(open[_offset], _t)
    h = reso(high[_offset], _t)
    l = reso(low[_offset], _t)
    c = reso(close[_offset], _t)
    show = _var != "Off" and inTimeframe(_t)
    _time = time(_t)
    newbar = na(_time[1]) or _time > _time[1]
    hl = _var == 'HL' or _var == 'OHLC'
    oc = _var == 'OC' or _var == 'OHLC'

    [o, h, l, c, show, newbar, hl, oc]



getLastElementInArray(_arr) =>
    _size = array.size(_arr)
    _el = _size > 0 ? array.get(_arr, _size-1) : na
    _el
// --------------- FUNCTIONS ---------------

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
///////////////// ================== LINES =====================
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

var line[] midnightLines = array.new_line(0)
var label[] midnightLabels = array.new_label(0)

var line[] sundayLines = array.new_line(0)
var label[] sundayLabels = array.new_label(0)

inMidnight = HOUR == 0 and minute == 0
inSunday = HOUR == 18 and minute == 0 and dayofweek == dayofweek.sunday

LABEL_SPACE = "                       "

//Midnight plot line
if inMidnight and not inMidnight[1] and showMidnightOpen
    _extend = midnightExtendHours * 3600000
    array.push(midnightLines, line.new(time, open, time + _extend, open, xloc=xloc.bar_time, color=midnightColor, style=getLineStyle(openLineStyle), width=openWidth))
    array.push(midnightLabels, label.new(time + _extend, open, "Midnight Open" + LABEL_SPACE, xloc=xloc.bar_time, style=label.style_none, size=getlabelSize(openLabelSize), textcolor=midnightColor))

// EXTEND

// DELETE OLD
    
if array.size(midnightLines) > openMaxLines
    line.delete(array.shift(midnightLines))
if array.size(midnightLabels) > openMaxLines
    label.delete(array.shift(midnightLabels))

//sunday open plot line
if inSunday and not inSunday[1] and showSundayOpen
    _extend1 = sundayExtendHours * 3600000
    array.push(sundayLines, line.new(time, open, time + _extend1, open, xloc=xloc.bar_time, color=sundayColor, style=getLineStyle(openLineStyle), width=openWidth))
    array.push(sundayLabels, label.new(time + _extend1, open, "Sunday Open" + LABEL_SPACE, xloc=xloc.bar_time, style=label.style_none, size=getlabelSize(openLabelSize), textcolor=sundayColor))

// EXTEND

// DELETE OLD
    
if array.size(sundayLines) > openMaxLines
    line.delete(array.shift(sundayLines))
if array.size(sundayLabels) > openMaxLines
    label.delete(array.shift(sundayLabels))

// --------------- TIME LINES ---------------

// Divider
tickerExchangeOffset = 5
int dayLabelStartTime = 1

_rin = resolutionInMinutes()

if syminfo.timezone == 'Etc/UTC'
    dayLabelStartTime += tickerExchangeOffset
    dayLabelStartTime

isStartTime = HOUR == newDayHr and HOUR[1] != newDayHr //and minute == 0 
_inTimeframe = _rin <= 180 and timeframe.isintraday

// if barstate.islastHOUR
//     label.new(bar_index, high, str.tostring(hour) + "\n" + str.tostring(exchangeOffset))

txtMonH = "Monday"
txtTueH = "Tuesday"
txtWedH = "Wednesday"
txtThuH = "Thursday"
txtFriH = "Friday"
txtSatH = "Saturday"
txtSunH = "Sunday"

txtMonV = "M\no\nn\nd\na\ny"
txtTueV = "T\nu\ne\ns\nd\na\ny"
txtWedV = "W\ne\nd\nn\ne\ns\nd\na\ny"
txtThuV = "T\nh\nu\nr\ns\nd\na\ny"
txtFriV = "F\nr\ni\nd\na\ny"
txtSatV = "S\na\nt\nu\nr\nd\na\ny"
txtSunV = "S\nu\nn\nd\na\ny"

isMon() => _inTimeframe and showDailyDividers ? HOUR == dayLabelStartTime and minute == 0 and dayofweek == dayofweek.monday : false
isTue() => _inTimeframe and showDailyDividers ? HOUR == dayLabelStartTime and minute == 0 and dayofweek == dayofweek.tuesday : false
isWed() => _inTimeframe and showDailyDividers ? HOUR == dayLabelStartTime and minute == 0 and dayofweek == dayofweek.wednesday : false
isThu() => _inTimeframe and showDailyDividers ? HOUR == dayLabelStartTime and minute == 0 and dayofweek == dayofweek.thursday : false
isFri() => _inTimeframe and showDailyDividers ? HOUR == dayLabelStartTime and minute == 0 and dayofweek == dayofweek.friday : false
isSat() => _inTimeframe and showDailyDividers ? HOUR == dayLabelStartTime and minute == 0 and dayofweek == dayofweek.saturday : false
isSun() => _inTimeframe and showDailyDividers ? HOUR == dayLabelStartTime and minute == 0 and dayofweek == dayofweek.sunday : false


_offset = _rin <= 7 ? math.round(dayLabelOffset * 12) : _rin <= 14 ? math.round(dayLabelOffset * 6) : _rin <= 20 ? math.round(dayLabelOffset * 4) : _rin <= 30 ? math.round(dayLabelOffset * 2) : _rin <= 60 ? math.round(dayLabelOffset * 1) : math.round(dayLabelOffset * 0.2)

plotshape(isMon(), text=txtMonH, color=TRANSPARENT, offset=_offset, style=shape.circle, location=location.bottom, textcolor=dividerLineTextColor, size=size.tiny, editable=false)
plotshape(isTue(), text=txtTueH, color=TRANSPARENT, offset=_offset, style=shape.circle, location=location.bottom, textcolor=dividerLineTextColor, size=size.tiny, editable=false)
plotshape(isWed(), text=txtWedH, color=TRANSPARENT, offset=_offset, style=shape.circle, location=location.bottom, textcolor=dividerLineTextColor, size=size.tiny, editable=false)
plotshape(isThu(), text=txtThuH, color=TRANSPARENT, offset=_offset, style=shape.circle, location=location.bottom, textcolor=dividerLineTextColor, size=size.tiny, editable=false)
plotshape(isFri(), text=txtFriH, color=TRANSPARENT, offset=_offset, style=shape.circle, location=location.bottom, textcolor=dividerLineTextColor, size=size.tiny, editable=false)
plotshape(isSat(), text=txtSatH, color=TRANSPARENT, offset=_offset, style=shape.circle, location=location.bottom, textcolor=dividerLineTextColor, size=size.tiny, editable=false)
plotshape(isSun(), text=txtSunH, color=TRANSPARENT, offset=_offset, style=shape.circle, location=location.bottom, textcolor=dividerLineTextColor, size=size.tiny, editable=false)
// --------------- SESSIONS ---------------

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
///////////////// ================== VWAP ANCHORED =====================
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

currentOnly = false
if year(timenow) == year(time) and month(timenow) == month(time) and dayofmonth(timenow) == dayofmonth(time) 
    currentOnly := true

if barstate.islast and ta.cum(volume) == 0
    runtime.error("No volume is provided by the data vendor.")

float vwapValue = na
float upperBandValue1 = na
float lowerBandValue1 = na
float upperBandValue2 = na
float lowerBandValue2 = na
float upperBandValue3 = na
float lowerBandValue3 = na

if not (timeframe.isdwm)
    [_vwap, _stdevUpper, _] = ta.vwap(srcVWAP, inMidnight, 1)
	vwapValue := _vwap
    stdevAbs = _stdevUpper - _vwap
	upperBandValue1 := _vwap + stdevAbs * stdevMult_1
	lowerBandValue1 := _vwap - stdevAbs * stdevMult_1
	upperBandValue2 := _vwap + stdevAbs * stdevMult_2
	lowerBandValue2 := _vwap - stdevAbs * stdevMult_2
	upperBandValue3 := _vwap + stdevAbs * stdevMult_3
	lowerBandValue3 := _vwap - stdevAbs * stdevMult_3

plot(showVWAP and currentOnly ? vwapValue : na, title='VWAP', color=vwapColor, display = display.all - display.price_scale)

plot(showVWAP and showBand_1 and currentOnly ? upperBandValue1 : na, title="Upper Band #1", color=Band_1Color, offset=offsetVWAP, display = display.all - display.price_scale)
plot(showVWAP and showBand_1 and currentOnly ? lowerBandValue1 : na, title="Lower Band #1", color=Band_1Color, offset=offsetVWAP, display = display.all - display.price_scale)
//fill(upperBand_1, lowerBand_1, title="Bands Fill #1", color= color.new(color.green, 99)    , display = showBand_1 ? display.all : display.none)

plot(showVWAP and showBand_2 and currentOnly ? upperBandValue2 : na, title="Upper Band #2", color=Band_2Color, offset=offsetVWAP, display = display.all - display.price_scale)
plot(showVWAP and showBand_2 and currentOnly ? lowerBandValue2 : na, title="Lower Band #2", color=Band_2Color, offset=offsetVWAP, display = display.all - display.price_scale)
//fill(upperBand_2, lowerBand_2, title="Bands Fill #2", color= color.new(color.olive, 99)    , display = showBand_2 ? display.all : display.none)

plot(showVWAP and showBand_3 and currentOnly ? upperBandValue3 : na, title="Upper Band #3", color=Band_3Color, offset=offsetVWAP, display = display.all - display.price_scale)
plot(showVWAP and showBand_3 and currentOnly ? lowerBandValue3 : na, title="Lower Band #3", color=Band_3Color, offset=offsetVWAP, display = display.all - display.price_scale)
//fill(upperBand_3, lowerBand_3, title="Bands Fill #3", color= color.new(color.teal, 99)    , display = showBand_3 ? display.all : display.none)

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
///////////////// ================== TDH & TDL liquidity + trading time window =====================
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

// Sessions and inputs
i_timezone          = "America/New_York"
i_showDaysDraw      = 1
var TodayStart      = time  // Store the timestamp when a new day starts
inputMaxInterval    = 30
showIndicator       = (timeframe.multiplier <= inputMaxInterval) and (timeframe.isintraday)                                                                                         // If current timeframe is lower or equal to input then showIndicator = true
i_linestyle         = input.string(title="Vertical Line Style", options=["solid", "dotted", "dashed"], defval="solid",group=GRP4, inline="Line")              // Default value is dotted to avoid drawing over wicks
i_lineWidth         = input.int(title="Line Width", defval=1, minval=1, maxval=5, group=GRP4, inline="Line", tooltip="Linestyle settings for the vertical dividers")          // Set the linewidth of the vertical lines
i_linestyleLevels   = "dashed"
i_lineWidthLevels   = 1

// Daily Session settings
group_daily         = "Daily Session"
i_DailySession      = "0000-0930:1234567"
DailySession        = time(timeframe.period, i_DailySession, i_timezone)                                                                                                           // Calculate time for the Globex/Overnight session. 
var float Daily_hi  = na        // Daily High
var float Daily_lo  = na        // Daily Low
showDailyHigh       = showTDHTLD
i_DailyHighCol      = input.color(title="Todays High Color", defval=color.new(#3f62ff, 50), group=GRP6, inline="High")
showDailyLow        = showTDHTLD
i_DailyLowCol       = input.color(title="Todays Low Color", defval=color.new(#b22833, 50), group=GRP6, inline="High")
var DailyHighLine   = line.new(x1=na, y1=na, x2=na, xloc=xloc.bar_time, y2=na, color=i_DailyHighCol, width=i_lineWidthLevels)                                                       // Today's High Price level line
var DailyHighLabel  = label.new(x=na, y=na, xloc=xloc.bar_time, color=color.new(color.gray, 90), textcolor=color.white, style=label.style_label_left, size=size.small)              // Today's High Price label
var DailyLowLine    = line.new(x1=na, y1=na, x2=na, xloc=xloc.bar_time, y2=na, color=i_DailyLowCol, width=i_lineWidthLevels)                                                        // Today's Low Price level line
var DailyLowLabel   = label.new(x=na, y=na, xloc=xloc.bar_time, color=color.new(color.gray, 90), textcolor=color.white, style=label.style_label_left, size=size.small)              // Today's Low Price label
var DailySessionStart = time                                                                                                                                                        // To register the timestamp when the Daily Session begins


// Globex (or Overnight) session settings and inputs
i_GlobexSession     = "1700-0830:1234567"
GlobexSession       = time(timeframe.period, i_GlobexSession, i_timezone)                                                                                                           // Calculate time for the Globex/Overnight session. 
var float Globex_open = na                                                                                                                                                          // Store the Globex Opening price
var float Globex_hi = na                                                                                                                                                            // High for the Globex Session
var float Globex_lo = na                                                                                                                                                            // Low for the Globex Session                                               // Globex Low line
var GlobexSessionStart = time                                                                                                                                                       // To register the timestamp when the Globex Overnight Session begins

// -------------------------------- KILLZONE SESSIONS ---------------------------------------------------------------------------
//ASIA Killzone Inputs and Settings
showLine_Asian         = false
i_AsianSession         = "2000-0000"
AsianSession           = time(timeframe.period, i_AsianSession, i_timezone)                                                                                                             // Calculate time for the Asian session. 
i_AsianSessionHour     = 20
i_AsianSessionMin      = 00
AsianSessionTime       = timestamp(i_timezone, year, month, dayofmonth, i_AsianSessionHour, i_AsianSessionMin, 00)-86400000                                                             // Coordinate for Asian Killzone Start time (minus 1 day)              
i_AsianSessionEndHour  = 23
i_AsianSessionEndMin   = 59
AsianSessionEndTime    = timestamp(i_timezone, year, month, dayofmonth, i_AsianSessionEndHour, i_AsianSessionEndMin, 00)-86400000                                                       // Coordinate for Asian Killzone End time  (minus 1 day)
showLevel_Asian        = false

// London Killzone session settings and inputs
showLine_London         = false
i_LondonSessionHour     = 02
i_LondonSessionMin      = 00
LondonSessionTime       = timestamp(i_timezone, year, month, dayofmonth, i_LondonSessionHour, i_LondonSessionMin, 00)                                                                   // Coordinate for London Killzone Start time              
i_LondonSessionEndHour  = 5
i_LondonSessionEndMin   = 0
LondonSessionEndTime    = timestamp(i_timezone, year, month, dayofmonth, i_LondonSessionEndHour, i_LondonSessionEndMin, 00)                                                             // Coordinate for London Killzone End time 
showLevel_London        = false

// NY Open Killzone session settings and inputs
showLine_NYKZ           = false
i_NYKZSessionHour       = 07 
i_NYKZSessionMin        = 00
NYKZSessionTime         = timestamp(i_timezone, year, month, dayofmonth, i_NYKZSessionHour, i_NYKZSessionMin, 00)                                                                       // Coordinate for NY Killzone Start time              
i_NYKZSessionEndHour    = 10
i_NYKZSessionEndMin     = 0
NYKZSessionEndTime      = timestamp(i_timezone, year, month, dayofmonth, i_NYKZSessionEndHour, i_NYKZSessionEndMin, 00)                                                                 // Coordinate for NY Killzone End time 
showLevel_NYKZ          = false

// New York Session settings and inputs
ny_group            = "New York Session Vertical Settings"
nyprice_group       = "New York Price Horizontal Level Settings"
nytime_group        = "New York Session Time Settings"
cmeprice_group      = "CME Price Horizontal Level Settings"

i_NYMNVertCol       = color.new(color.fuchsia,0)
i_AMSessionCol      = color.new(color.blue,0)


//////////////////////////////////////////// 930 linea verticaleeeee ///////////////////////////////////////////////////

showLine_CME        = showTT
i_CMESessionCol     = TTColor

///////////////////////////////////////////////// 12 vertical lineeee //////////////////////////////////////////////////

showLine_Lunch      = showTT
i_LunchSessionCol   = TTColor

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

i_LunchSessionFillCol= color.new(color.red,80)
i_PMSessionCol      = color.new(color.blue,0)
i_NYSessionEndCol   = color.new(color.blue,0)

NYMNOpenTime        = timestamp(i_timezone, year, month, dayofmonth, 00, 00, 00)                                                                                                // Coordinate for NY Midnight Opening Time coordinate (Every day 00:00)
AMSessionTime       = timestamp(i_timezone, year, month, dayofmonth, 08, 30, 00)                                                                       // Coordinate for NY AM Start session time              
PMSessionTime       = timestamp(i_timezone, year, month, dayofmonth, 13, 00, 00) 

CMESessionTime      = timestamp(i_timezone, year, month, dayofmonth, 09, 30, 00)                                                  // Coordinate for CME Start session time 
var CMESessionLine  = line.new(x1=na, y1=na, x2=na, xloc=xloc.bar_time, y2=close, color=i_CMESessionCol, width=i_lineWidth)                                  // CME Open Vertical Line
LunchSessionTime    = timestamp(i_timezone, year, month, dayofmonth, 12, 00, 00)                                              // Coordinate for Lunch Start time 
var LunchStartLine  = line.new(x1=na, y1=na, x2=na, xloc=xloc.bar_time, y2=close, color=i_LunchSessionCol, width=i_lineWidth)                                // Lunch Start Vertical Line

var NYSessionEndTime= timestamp(i_timezone, year, month, dayofmonth, 16, 30, 00)                                                             // Coordinate for NY End of session time (this is also the terminus for the Level lines)

// HTF Lines

// LOGGING FUNCTION
var logsize  = 50
var bar_arr  = array.new_int(logsize)
var time_arr = array.new_string(logsize)
var msg_arr  = array.new_string(logsize)
var type_arr = array.new_string(logsize)

log_msg(message, type) => 
    array.push(bar_arr,  bar_index)
    array.push(time_arr, str.tostring(year) + "-" + str.tostring(month) + "-" + str.tostring(dayofmonth) + " " + str.tostring(hour(time,i_timezone)) + ":" + str.tostring(minute(time,i_timezone)) + ":" + str.tostring(second(time,i_timezone)))
    array.push(msg_arr,  message)
    array.push(type_arr, type)
    // Housekeeping, delete the oldest log entries. This prevents the array from becoming too big.
    array.shift(bar_arr)
    array.shift(time_arr)
    array.shift(msg_arr)
    array.shift(type_arr)
// END LOGGING CODE

// *** End of inputs and variables

// Convert linestyle
linestyle = (i_linestyle == "solid") ? line.style_solid :
 (i_linestyle == "dashed") ? line.style_dashed :
 line.style_dotted    // Default value is dotted to avoid drawing over wicks

// Functions
// Function to draw Vertical Lines
vline(Start, Color, linestyle, LineWidth) =>
    line.new(x1=Start, y1=low - ta.tr, x2=Start, y2=high + ta.tr, xloc=xloc.bar_time, extend=extend.both, color=Color, style=linestyle, width=LineWidth)
// End function

// Function for determining the Start of a Session (taken from the Pinescript manual: https://www.tradingview.com/pine-script-docs/en/v5/concepts/Sessions.html )
sessionBegins(sess) =>
    t = time("", sess , i_timezone)
    showIndicator and (not barstate.isfirst) and na(t[1]) and not na(t)
// End session function

// Housekeeping - Remove old drawing objects
housekeeping(days) =>
    // Delete old drawing objects
    // One day is 86400000 milliseconds
    removal_timestamp = AsianSessionTime - (days * 86400000)    // Remove every drawing object older than the start of the Asian Session.
    a_allLines = line.all
    a_allLabels = label.all
    // Remove old lines
    if array.size(a_allLines) > 0
	    for i = 0 to array.size(a_allLines) - 1
		    line_x2 = line.get_x2(array.get(a_allLines, i))    
		    if line_x2 < removal_timestamp
		        line.delete(array.get(a_allLines, i))
    // Remove old labels
    if array.size(a_allLabels) > 0
	    for i = 0 to array.size(a_allLabels) - 1
		    label_x = label.get_x(array.get(a_allLabels, i))    
		    if label_x < removal_timestamp
		        label.delete(array.get(a_allLabels, i))

// End of housekeeping function

// *** End of all functions

// ************************** <<
// Start MAIN Logic

// Daily Session Start | this works for everything but stocks
if sessionBegins(i_DailySession)                // When a new Daily Session is started
    log_msg("Daily Session Start", 'medium')
    log_msg("You are trading: " + syminfo.ticker + " and its asset class is: " + syminfo.type, 'medium')
    DailySessionStart := time   // Store the Session Starting timestamp
    NYMNOpenTime := time
    // Calculate timestamps for the coming session
    // Killzone vertical line timestamps
    if AsianSessionTime < DailySessionStart  // If the AsianSessionTime is in the past, then add 1 day
        AsianSessionTime := AsianSessionTime+86400000 // Add 1 day, so level lines will be drawn to the right like they should
    
    if AsianSessionEndTime < DailySessionStart  // If the AsianSessionEndTime is in the past, then add 1 day
        AsianSessionEndTime := AsianSessionEndTime+86400000 // Add 1 day, so level lines will be drawn to the right like they should
        
    if LondonSessionTime < DailySessionStart  // If the LondonSessionTime is in the past, then add 1 day
        LondonSessionTime := LondonSessionTime+86400000 // Add 1 day, so level lines will be drawn to the right like they should
    
    if LondonSessionEndTime < DailySessionStart  // If the LondonSessionEndTime is in the past, then add 1 day
        LondonSessionEndTime := LondonSessionEndTime+86400000 // Add 1 day, so level lines will be drawn to the right like they should
    
    if NYKZSessionTime < DailySessionStart  // If the NYKZSessionTime is in the past, then add 1 day
        NYKZSessionTime := NYKZSessionTime+86400000 // Add 1 day, so level lines will be drawn to the right like they should
    
    if NYKZSessionEndTime < DailySessionStart  // If the NYKZSessionEndTime is in the past, then add 1 day
        NYKZSessionEndTime := NYKZSessionEndTime+86400000 // Add 1 day, so level lines will be drawn to the right like they should
    
    // Session vertical line timestamps
    if AMSessionTime < DailySessionStart  // If the AMSessionTime is in the past, then add 1 day
        AMSessionTime := AMSessionTime+86400000 // Add 1 day, so level lines will be drawn to the right like they should
    
    if AMSessionTime < DailySessionStart  // If the AMSessionTime is in the past, then add 1 day
        AMSessionTime := AMSessionTime+86400000 // Add 1 day, so level lines will be drawn to the right like they should
    
    if CMESessionTime < DailySessionStart  // If the NYSessionEndTime is in the past, then add 1 day
        CMESessionTime := CMESessionTime+86400000 // Add 1 day, so level lines will be drawn to the right like they should
    
    if LunchSessionTime < DailySessionStart  // If the LunchSessionTime is in the past, then add 1 day
        LunchSessionTime := LunchSessionTime+86400000 // Add 1 day, so level lines will be drawn to the right like they should
    
    if PMSessionTime < DailySessionStart  // If the PMSessionTime is in the past, then add 1 day
        PMSessionTime := PMSessionTime+86400000 // Add 1 day, so level lines will be drawn to the right like they should
    
    NYSessionEndTime  := timestamp(i_timezone, year, month, dayofmonth, 16, 30, 00)   // Coordinate for NY End of Cash session time (this is the terminus for the Level lines)
    if NYSessionEndTime < DailySessionStart  // If the NYSessionEndTime is in the past, then add 1 day
        NYSessionEndTime := NYSessionEndTime+86400000 // Add 1 day, so level lines will be drawn to the right like they should
    
    // We are entering Daily Session hours; reset hi/lo.
    Daily_hi       := high
    Daily_lo       := low
    // Initialize the drawings for today
    if showDailyHigh   // Draw the Daily Open Vertical Line
        DailyHighLine   := line.new(x1=NYMNOpenTime, y1=Daily_hi, x2=NYSessionEndTime, xloc=xloc.bar_time, y2=Daily_hi, color=i_DailyHighCol, width=i_lineWidthLevels)                                  // Today's High Price level line
    if showDailyLow    
        DailyLowLine   := line.new(x1=NYMNOpenTime, y1=Daily_lo, x2=NYSessionEndTime, xloc=xloc.bar_time, y2=Daily_lo, color=i_DailyLowCol, width=i_lineWidthLevels)                                      // Today's Low Price level line

    // When a new day start, draw all the vertical lines

    // Draw the CME Open Start Line
    if (showLine_CME and showIndicator)
        CMESessionLine := vline(CMESessionTime, i_CMESessionCol, linestyle, i_lineWidth)

  
    // Draw the NY Lunch Start Line    
    if (showLine_Lunch and showIndicator)
        LunchStartLine := vline(LunchSessionTime, i_LunchSessionCol, linestyle, i_lineWidth)

// *** End of Vertical Lines section

else if DailySession
    // We are in allowed DailySession hours; track hi/lo.
    Daily_hi := math.max(high, Daily_hi)
    Daily_lo := math.min(low, Daily_lo)
    if (Daily_hi > Daily_hi[1])
        if showDailyHigh
            line.set_y1(id=DailyHighLine, y=Daily_hi)
            line.set_y2(id=DailyHighLine, y=Daily_hi)
            
    if (Daily_lo < Daily_lo[1])
        if showDailyLow
            line.set_y1(id=DailyLowLine, y=Daily_lo)
            line.set_y2(id=DailyLowLine, y=Daily_lo)

// END Daily Session Code

//condizioni per alert === dopo 9:30, che liquida tdh o tdl

//TDHalert = (close, DailyHighLine)

// Do housekeeping of old objects - remove everything before the number of days set
housekeeping(i_showDaysDraw)


//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
///////////////// ================== FVG =====================
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////


_bullImb = showFVG
_bullCol = input.color(color.new(#4599e2, 60), "Bullish FVG Color", group="Fair Value Gap Settings", inline="1")
_bullColBorder = input.color(color.new(#254e85, 75), "     Border", group="Fair Value Gap Settings", inline="1")

_bearImb = showFVG
_bearCol = input.color(color.new(#452581, 60), "Bearish FVG Color", group="Fair Value Gap Settings", inline="2")
_bearColBorder = input.color(color.new(#411d5e, 81), "     Border", group="Fair Value Gap Settings", inline="2")

_removeOld = input.bool(true, " Delete Mitigated FVGs", group="Fair Value Gap Settings")
_width = input.int(0, "Extend FVG", minval=0, maxval=500, group="Fair Value Gap Settings")
_totalCount = input.int(10, "Max FVGs to show", group="Fair Value Gap Settings")

// Variables
var bearBox = array.new_box()
var bullBox = array.new_box()


// Top Imbalance
bearImb = low[2] <= open[1] and high >= close[1]
bearGap = low[1] > high
bearImbSize = low[2] - high
if _bearImb and bearImb and bearImbSize > 0 or bearGap
    n = bearGap ? 1 : 2
    array.push(bearBox, box.new(left=bar_index[1], top=low[n], right=bar_index+_width, bottom=high, bgcolor=_bearCol, border_color=_bearColBorder))

// Bottom Imbalance
bullImb = high[2] >= open[1] and low <= close[1]
bullImbSize = low - high[2]
bullGap = high[1] < low
if _bullImb and bullImb and bullImbSize > 0 or bullGap
    n = bullGap ? 1 : 2
    array.push(bullBox, box.new(left=bar_index[1], top=low, right=bar_index+_width, bottom=high[n], bgcolor=_bullCol, border_color=_bullColBorder))


// Remove Mitigated
remove_mitigate(arrayType, pos) =>
    if array.size(arrayType) > 0
        for i = array.size(arrayType) - 1 to 0 by 1
            sbox = array.get(arrayType, i)
            top = box.get_top(sbox)
            bot = box.get_bottom(sbox)
            pass = pos =="Bull" ? low < bot + ((top - bot)/2) : high > top - ((top - bot)/2)
            if pass
                txt = pos =="Bull" ? "Bullish" : "Bearish"
                alert("Price (" + str.tostring(close) + ") inside "+txt+" Imbalance", alert.freq_once_per_bar)
                if _removeOld
                    array.remove(arrayType, i)
                    box.delete(sbox)

// Run Functions
remove_mitigate(bullBox, "Bull")
remove_mitigate(bearBox, "Bear")


// Remove Older Imb
if array.size(bearBox) > _totalCount
    b = array.shift(bearBox)
    box.delete(b)

if array.size(bullBox) > _totalCount
    b = array.shift(bullBox)
    box.delete(b)

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////======= BREAKOUT PROBABILITY =======================///////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////


// ~~ Tooltips {
t1 = "The space between the levels can be adjusted with a percentage step. 1% means that each level is located 1% above/under the previous one."
t2 = "Set the number of levels you want to display."
t3 = "If a level got 0 % likelihood of being hit, the level is not displayed as default. Enable the option if you want to see all levels regardless of their values."
t4 = "Enable this option if you want to display the backtest statistics for that a new high or low is made."
string [] tbl_tips = array.from("Number of times price has reached the first highest percentage level",
 "Number of times price failed to reach the first highest percentage level",
 "Win/Loss ratio")
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}

// ~~ Inputs {
perc                    = input.float(1.0,title="Percentage Step",step=.1,minval=0,group="Breakout Probability Settings",tooltip=t1)
nbr                     = input.int(1, title="Number of Lines",maxval=5,minval=1,group="Breakout Probability Settings",tooltip=t2)
upCol                   = input.color(color.new(#333333, 0),title="",inline="col",group="Breakout Probability Settings"),dnCol=input.color(color.new(#333333, 0),title="",inline="col",group="Breakout Probability Settings")
var bool [] bools       = array.from(true,false)
var bool [] alert_bool  = array.from(
 input.bool(true,title="Ticker ID",group="Any alert() function call"),
 input.bool(true,title="High/Low Price",group="Any alert() function call"),
 input.bool(true,title="Bullish/Bearish Bias",group="Any alert() function call"),
 input.bool(true,title="Bullish/Bearish Percentage",group="Any alert() function call"))
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}

// ~~ Variables {
b = bar_index
o = open
h = high
l = low
c = close
step = c*(perc/100)
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}

// ~~ Save Values In Matrix {
var total  = matrix.new<int>(7,4,0)
var vals   = matrix.new<float>(5,4,0.0)
var lines  = matrix.new<line>(1,10,line(na))
var labels = matrix.new<label>(1,10,label(na))
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}

// ~~ Save Number Of Green & Red Candles {
green = c[1]>o[1]
red   = c[1]<o[1]
if green
    prev = matrix.get(total,5,0)
    matrix.set(total,5,0,prev+1)
if red
    prev = matrix.get(total,5,1)
    matrix.set(total,5,1,prev+1)
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}

// ~~ Functions {
//Lines
CreateLine(p,i,c)=>
    prevLine = matrix.get(lines,0,i)
    line.delete(prevLine)
    li = line.new(b[1],p,b,p,color=c,width=2)
    matrix.set(lines,0,i,li)
//Labels
CreateLabel(p,i,c,r,v)=>
    prevLabel = matrix.get(labels,0,i)
    label.delete(prevLabel)
    la = label.new(b+1,p,text=str.tostring(matrix.get(vals,r,v),format.percent),
     style=label.style_label_left,color=color.new(color.black,100),textcolor=c)
    matrix.set(labels,0,i,la)
//Score Calculation
Score(x,i)=>
    ghh = matrix.get(total,i,0)
    gll = matrix.get(total,i,1)
    rhh = matrix.get(total,i,2)
    rll = matrix.get(total,i,3)
    gtotal = matrix.get(total,5,0)
    rtotal = matrix.get(total,5,1)
    hh = h>=h[1] + x
    ll = l<=l[1] - x
    if green and hh
        matrix.set(total,i,0,ghh+1)
        matrix.set(vals,i,0,math.round(((ghh+1)/gtotal)*100,2))
    if green and ll
        matrix.set(total,i,1,gll+1)
        matrix.set(vals,i,1,math.round(((gll+1)/gtotal)*100,2))
    if red and hh
        matrix.set(total,i,2,rhh+1)
        matrix.set(vals,i,2,math.round(((rhh+1)/rtotal)*100,2))
    if red and ll
        matrix.set(total,i,3,rll+1)
        matrix.set(vals,i,3,math.round(((rll+1)/rtotal)*100,2))
//Backtest
Backtest(v)=>
    p1 = matrix.get(total,6,0)
    p2 = matrix.get(total,6,1)
    if v==h[1]
        if h>=v
            matrix.set(total,6,0,p1+1)
        else
            matrix.set(total,6,1,p2+1)
    else
        if l<=v
            matrix.set(total,6,0,p1+1)
        else 
            matrix.set(total,6,1,p2+1)
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}

// ~~ Code {
//Run Score Function
Score(0,0)
Score(step,1)
Score(step*2,2)
Score(step*3,3)
Score(step*4,4)

//Fetch Score Values
a1 = matrix.get(vals,0,0)
b1 = matrix.get(vals,0,1)
a2 = matrix.get(vals,0,2)
b2 = matrix.get(vals,0,3)

//Lines & Labels & Alerts
for i=0 to nbr-1
    hide = array.get(bools,0)
    if not hide or (hide and (green?math.min(matrix.get(vals,i,0),
     matrix.get(vals,i,1))>0:
     math.min(matrix.get(vals,i,2),
     matrix.get(vals,i,3))>0))
        hi = h[1]+(step*i)
        lo = l[1]-(step*i)
        //Plot Lines
        if showBreakout 
            CreateLine(hi,i,upCol)
            CreateLine(lo,5+i,dnCol)
        //Plot Labels
        if green and showBreakout
            CreateLabel(hi,i,upCol,i,0)
            CreateLabel(lo,5+i,dnCol,i,1)
        else if showBreakout
            CreateLabel(hi,i,upCol,i,2)
            CreateLabel(lo,5+i,dnCol,i,3)
        //Create Alert
        if array.includes(alert_bool, true)
            s1 = str.tostring(syminfo.ticker)
            s2 = "High Price: "+str.tostring(math.round_to_mintick(h[1]))+
             " | Low Price: "+str.tostring(math.round_to_mintick(l[1]))
            s3 = green?(math.max(a1,b1)==a1?"BULLISH":"BEARISH"):
             (math.max(a2,b2)==a2?"BULLISH":"BEARISH")
            s4 = green?(math.max(a1,b1)==a1?a1:b1):(math.min(a2,b2)==a2?a2:b2)
            s5 = red  ?(math.max(a2,b2)==a2?a2:b2):(math.min(a1,b1)==a1?a1:b1)
            string [] str_vals = array.from(s1,s2,"BIAS: "+s3,
             "Percentage: High: "+str.tostring(s4,format.percent)
             +" | Low: "+str.tostring(s5,format.percent))
            output = array.new_string()
            for x=0 to array.size(alert_bool)-1
                if array.get(alert_bool,x)
                    array.push(output,array.get(str_vals,x))
            //Alert Is Triggered On Every Bar Open With Bias And Percentage Ratio
            alert(array.join(output,'\n'),alert.freq_once_per_bar)
    else
        //Delete Old Lines & Labels
        line.delete(matrix.get(lines,0,i))
        line.delete(matrix.get(lines,0,5+i))
        label.delete(matrix.get(labels,0,i))
        label.delete(matrix.get(labels,0,5+i))

//Run Backtest Function
Backtest(green?(math.max(a1,b1)==a1?h[1]:l[1]):(math.max(a2,b2)==a2?h[1]:l[1]))
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}