Weekly Opening Gap (cryptonnnite)
// This source code is subject to the terms of the Mozilla Public License 2.0 at https://mozilla.org/MPL/2.0/
// © cryptonnnite

//@version=5
indicator("NWOG/NDOG (cryptonnnite)", overlay = true, max_boxes_count = 500, max_labels_count = 500)

import cryptonnnite/helpers/1 as helpers

type gap
    float high
    float low
    int start
    int end

var string nwogGroup = "New Week Opening Gap"
var string ndogGroup = "New Day Opening Gap"
var string generalSettings = "General Settings"
var string openLineSettings = "Week & Day Open"

bool showNWOG = input.bool(true, "", inline = "1", group = nwogGroup)
string nwogLabel = input.string("NWOG", "", inline = "1", group = nwogGroup)
color nwogBg = input.color(color.rgb(164, 249, 73, 85), "", inline = "1", group = nwogGroup)
bool showNwogCE = input.bool(false, "NWOG C.E.", tooltip = "C.E. - Consequent Encroachment or 'middle point' of any Gap or Inefficiency.", group = nwogGroup, inline = "2")
string nwogCEStyle = input.string("dotted", "", options = ["dotted", "dashed", "solid"], group = nwogGroup, inline = "2")
color nwogCEColor = input.color(color.blue, "", group = nwogGroup, inline = "2")
string nwogLabelXPosition = input.string('right', title = "Label", options = ["left", "center", "right"], inline = "5", group = nwogGroup)
string nwogLabelYPosition = input.string('top', title = "", options = ["top", "center", "bottom"], inline = "5", group = nwogGroup)
string nwogLabelSize = input.string('small', title = "", options = ["tiny", "small", "normal", "large", "huge"], inline = "5", group = nwogGroup)
color nwogLabelColor = input.color(color.black, "", inline = "5", group = nwogGroup)
int nwogsAmount = input.int(4, "Previous NWOGs", minval = 1, maxval = 100, inline = "3", group = nwogGroup)
color previousNwogBg = input.color(color.rgb(0, 0, 0, 90), "", inline = "3", group = nwogGroup)
bool extendAllNwogs = input.bool(false, "Extend Previous NWOGs", group = nwogGroup)
bool extendToCurrentDayCloseTime = input.bool(true, "Extend to Current Day", tooltip = "Otherwise it will be extended to the end of the week", group = nwogGroup)

bool showNDOG = input.bool(true, "", inline = "2", group = ndogGroup)
string ndogLabel = input.string("NDOG", "", inline = "2", group = ndogGroup)
color ndogBg = input.color(color.rgb(255, 235, 59, 85), "", inline = "2", group = ndogGroup)
bool showNdogCE = input.bool(false, "NDOG C.E.", tooltip = "C.E. - Consequent Encroachment or 'middle point' of any Gap or Inefficiency.", group = ndogGroup, inline = "3")
string ndogCEStyle = input.string("dotted", "", options = ["dotted", "dashed", "solid"], group = ndogGroup, inline = "3")
color ndogCEColor = input.color(color.blue, "", group = ndogGroup, inline = "3")
string ndogLabelXPosition = input.string('right', title = "Label", options = ["left", "center", "right"], inline = "5", group = ndogGroup)
string ndogLabelYPosition = input.string('top', title = "", options = ["top", "center", "bottom"], inline = "5", group = ndogGroup)
string ndogLabelSize = input.string('small', title = "", options = ["tiny", "small", "normal", "large", "huge"], inline = "5", group = ndogGroup)
color ndogLabelColor = input.color(color.black, "", inline = "5", group = ndogGroup)
int ndogsAmount = input.int(4, "Previous NDOG", minval = 1, maxval = 100, inline = "4", group = ndogGroup)
color previousNdogBg = input.color(color.rgb(0, 0, 0, 90), "", inline = "4", group = ndogGroup)
bool extendAllNdogs = input.bool(false, "Extend Previous NDOGs", group = ndogGroup)

bool eventHorizon = input.bool(false, "Event Horizon", inline = "2", group = generalSettings)
string eventHorizonLineStyle = input.string("dashed", "", options = ["dotted", "dashed", "solid"], group = generalSettings, inline = "2")
color eventHorizonLineColor = input.color(color.red, "", inline = "2", group = generalSettings)

bool fiveToSixGap = input.bool(true, "5pm to 6pm GAP", tooltip = "This option will show only 5pm to 6pm gaps. If you will see at the daily timeframe there is a big difference between previous day close and current day open GAP and 5pm to 6pm GAP. This option allows you to choose which GAP to use.", group = generalSettings)
bool showPriceLabels = input.bool(false, "Price Labels", inline = "1", group = generalSettings)
string priceLabelSize = input.string('small', title = "", options = ["tiny", "small", "normal", "large", "huge"], inline = "1", group = generalSettings)
color priceLabelColor = input.color(color.black, "", inline = "1", group = generalSettings)
bool showDateLabel = input.bool(true, "Show date label", group = generalSettings)
string dateFormat = input.string("dd.MM", title = "Date Format", options = ["dd.MM", "MM.dd", "dd.MM.yy", "MM.dd.yy"], group = generalSettings)

getGapData() =>
    gap.new(math.max(close[1], open), math.min(close[1], open), time, time_close)

getDayOfWeekLabel(_dayofweek) =>
    switch _dayofweek
        dayofweek.sunday => "Sun"
        dayofweek.monday => "Mon"
        dayofweek.tuesday => "Tue"
        dayofweek.wednesday => "Wed"
        dayofweek.thursday => "Thu"
        dayofweek.friday => "Fri"
        dayofweek.saturday => "Sat"

gap weekOpeningGap = request.security(syminfo.tickerid, "1W", getGapData(), lookahead =  barmerge.lookahead_on)
[dayOpeningGap, currentDayTimeClose] = request.security(syminfo.tickerid, "1D", [getGapData(), time_close], lookahead =  barmerge.lookahead_on)
[isNewWeek, hourPreviousBarClose, hourCurrentBarOpen] = request.security(syminfo.tickerid, "240", [helpers.isNewbar("W"), close[1], open], lookahead =  barmerge.lookahead_on)

if timeframe.isintraday and helpers.isNewbar("D") and fiveToSixGap
    dayOpeningGap.high := math.max(close[1], open)
    dayOpeningGap.low := math.min(close[1], open)

if timeframe.multiplier < 60
    if isNewWeek and fiveToSixGap
        weekOpeningGap.high := math.max(hourPreviousBarClose, hourCurrentBarOpen)
        weekOpeningGap.low := math.min(hourPreviousBarClose, hourCurrentBarOpen)
else if (timeframe.isdaily or timeframe.isintraday) and helpers.isNewbar("W") and fiveToSixGap
    weekOpeningGap.high := math.max(close[1], open)
    weekOpeningGap.low := math.min(close[1], open)

var array<box> nwogArray = array.new<box>(0)
var array<line> nwogCEArray = array.new<line>(0)
var array<label> nwogHighLabel = array.new<label>(0)
var array<label> nwogCELabel = array.new<label>(0)
var array<label> nwogLowLabel = array.new<label>(0)

var array<box> ndogArray = array.new<box>(0)
var array<line> ndogCEArray = array.new<line>(0)
var array<label> ndogHighLabel = array.new<label>(0)
var array<label> ndogCELabel = array.new<label>(0)
var array<label> ndogLowLabel = array.new<label>(0)

var line eventHorizonLine = na

if showNWOG and (timeframe.isdaily or timeframe.isintraday) 
    if helpers.isNewbar("W") // if (hour(time, "America/New_York") == 9 and minute(time, America/New_York) == 30 and nineThirtyGap) or (not nineThirtyGap and helpers.isNewbar("W"))
        string weekLabel = showDateLabel ? str.format_time(weekOpeningGap.start, dateFormat, syminfo.timezone) + " - " + str.format_time(weekOpeningGap.end, dateFormat, syminfo.timezone) + " | " + nwogLabel : nwogLabel
        float nwogCePrice = (weekOpeningGap.high + weekOpeningGap.low) / 2
        
        array.push(nwogArray, box.new(weekOpeningGap.start, weekOpeningGap.high, weekOpeningGap.end, weekOpeningGap.low, xloc = xloc.bar_time, bgcolor = nwogBg, border_width = 0, text = weekLabel, text_size = helpers.getTextSize(nwogLabelSize), text_halign = helpers.getBoxTextPosition(nwogLabelXPosition), text_valign = helpers.getBoxTextPosition(nwogLabelYPosition), text_color = nwogLabelColor))
        
        if showNwogCE
            array.push(nwogCEArray, line.new(weekOpeningGap.start, nwogCePrice, weekOpeningGap.end, nwogCePrice, xloc = xloc.bar_time, style = helpers.getLineStyle(nwogCEStyle), color = nwogCEColor))
        
        if showPriceLabels
            array.push(nwogHighLabel, label.new(weekOpeningGap.end, weekOpeningGap.high, str.tostring(weekOpeningGap.high, format.mintick), style = label.style_label_lower_left, xloc = xloc.bar_time, color = color.rgb(0, 0, 0, 100), textcolor = priceLabelColor, size = helpers.getTextSize(priceLabelSize)))
            array.push(nwogCELabel, label.new(weekOpeningGap.end, nwogCePrice, str.tostring(nwogCePrice, format.mintick), style = label.style_label_left, xloc = xloc.bar_time, color = color.rgb(0, 0, 0, 100), textcolor = priceLabelColor, size = helpers.getTextSize(priceLabelSize)))
            array.push(nwogLowLabel, label.new(weekOpeningGap.end, weekOpeningGap.low, str.tostring(weekOpeningGap.low, format.mintick), style = label.style_label_upper_left, xloc = xloc.bar_time, color = color.rgb(0, 0, 0, 100), textcolor = priceLabelColor, size = helpers.getTextSize(priceLabelSize)))

        if array.size(nwogArray) > nwogsAmount
            box.delete(array.shift(nwogArray))
            
            if showNwogCE
                line.delete(array.shift(nwogCEArray))
            
            if showPriceLabels
                label.delete(array.shift(nwogHighLabel))
                label.delete(array.shift(nwogCELabel))
                label.delete(array.shift(nwogLowLabel))
        
        if eventHorizon and array.size(nwogArray) > 1
            box currentNwog = array.last(nwogArray)
            box previousNwog = array.get(nwogArray, array.size(nwogArray) - 2)

            float currentNwog_high = box.get_top(currentNwog)
            float currentNwog_low = box.get_bottom(currentNwog)
            
            float previousNwog_high = box.get_top(previousNwog)
            float previousNwog_low = box.get_bottom(previousNwog)
            
            float eventHorizonLevel = 0.0

            if currentNwog_high > previousNwog_high and currentNwog_low > previousNwog_high
                eventHorizonLevel := (currentNwog_low + previousNwog_high) / 2
            else if previousNwog_high > currentNwog_high and previousNwog_low > currentNwog_high
                eventHorizonLevel := (previousNwog_low + currentNwog_high) / 2

            eventHorizonLine := line.new(weekOpeningGap.start, eventHorizonLevel, weekOpeningGap.end, eventHorizonLevel, xloc = xloc.bar_time, style = helpers.getLineStyle(eventHorizonLineStyle), color = eventHorizonLineColor)
            line.delete(eventHorizonLine[1])

    if helpers.isNewbar("D") 
        if extendAllNwogs and array.size(nwogArray) > 0
            for i = array.size(nwogArray) - 1 to 0
                extendTo = extendToCurrentDayCloseTime ? currentDayTimeClose : weekOpeningGap.end
                box.set_right(array.get(nwogArray, i), extendTo)
                
                if i != array.size(nwogArray) - 1
                    box.set_bgcolor(array.get(nwogArray, i), previousNwogBg)

                if showNwogCE
                    line.set_x2(array.get(nwogCEArray, i), extendTo)
                
                if showPriceLabels
                    label.set_x(array.get(nwogHighLabel, i), extendTo)
                    label.set_x(array.get(nwogCELabel, i), extendTo)
                    label.set_x(array.get(nwogLowLabel, i), extendTo)

        if extendToCurrentDayCloseTime and array.size(nwogArray) > 0
            box.set_right(array.last(nwogArray), currentDayTimeClose)
            
            if showNwogCE
                line.set_x2(array.last(nwogCEArray), currentDayTimeClose)
                
            if showPriceLabels
                label.set_x(array.last(nwogHighLabel), currentDayTimeClose)
                label.set_x(array.last(nwogCELabel), currentDayTimeClose)
                label.set_x(array.last(nwogLowLabel), currentDayTimeClose)
            
            if eventHorizon
                line.set_x2(eventHorizonLine, currentDayTimeClose)

if showNDOG and timeframe.isintraday
    if helpers.isNewbar("D")// and dayOpeningGap.high != dayOpeningGap.low
        if (dayofweek != dayofweek.sunday and showNWOG) or not showNWOG
            string dayLabel = showDateLabel ? str.format_time(dayOpeningGap.start, dateFormat, syminfo.timezone) + " | " + ndogLabel : ndogLabel //showDayLabels ? getDayOfWeekLabel(dayofweek) + " | " + ndogLabel : ndogLabel
            float ndogCePrice = (dayOpeningGap.high + dayOpeningGap.low) / 2
            
            array.push(ndogArray, box.new(dayOpeningGap.start, dayOpeningGap.high, dayOpeningGap.end, dayOpeningGap.low, xloc = xloc.bar_time, bgcolor = ndogBg, border_width = 0, text = dayLabel, text_size = helpers.getTextSize(ndogLabelSize), text_halign = helpers.getBoxTextPosition(ndogLabelXPosition), text_valign = helpers.getBoxTextPosition(ndogLabelYPosition), text_color = ndogLabelColor))
        
            if showNdogCE
                array.push(ndogCEArray, line.new(dayOpeningGap.start, ndogCePrice, dayOpeningGap.end, ndogCePrice, xloc = xloc.bar_time, style = helpers.getLineStyle(ndogCEStyle), color = ndogCEColor))

            if showPriceLabels
                array.push(ndogHighLabel, label.new(dayOpeningGap.end, dayOpeningGap.high, str.tostring(dayOpeningGap.high, format.mintick), style = label.style_label_lower_left, xloc = xloc.bar_time, color = color.rgb(0, 0, 0, 100), textcolor = priceLabelColor, size = helpers.getTextSize(priceLabelSize)))
                array.push(ndogCELabel, label.new(dayOpeningGap.end, ndogCePrice, str.tostring(ndogCePrice, format.mintick), style = label.style_label_left, xloc = xloc.bar_time, color = color.rgb(0, 0, 0, 100), textcolor = priceLabelColor, size = helpers.getTextSize(priceLabelSize)))
                array.push(ndogLowLabel, label.new(dayOpeningGap.end, dayOpeningGap.low, str.tostring(dayOpeningGap.low, format.mintick), style = label.style_label_upper_left, xloc = xloc.bar_time, color = color.rgb(0, 0, 0, 100), textcolor = priceLabelColor, size = helpers.getTextSize(priceLabelSize)))

            if array.size(ndogArray) > ndogsAmount
                box.delete(array.shift(ndogArray))

                if showNdogCE
                    line.delete(array.shift(ndogCEArray))
                
                if showPriceLabels
                    label.delete(array.shift(ndogHighLabel))
                    label.delete(array.shift(ndogCELabel))
                    label.delete(array.shift(ndogLowLabel))

        
    if extendAllNdogs and array.size(ndogArray) > 0
        for i = 0 to array.size(ndogArray) - 1
            box.set_right(array.get(ndogArray, i), dayOpeningGap.end)
            
            if (showNWOG and dayofweek == dayofweek.sunday and i <= array.size(ndogArray) - 1) or i < array.size(ndogArray) - 1
                box.set_bgcolor(array.get(ndogArray, i), previousNdogBg)

            if showNdogCE
                line.set_x2(array.get(ndogCEArray, i), dayOpeningGap.end)
            
            if showPriceLabels
                label.set_x(array.get(ndogHighLabel, i), dayOpeningGap.end)
                label.set_x(array.get(ndogCELabel, i), dayOpeningGap.end)
                label.set_x(array.get(ndogLowLabel, i), dayOpeningGap.end)
