90 Minute Cycles
// This source code is subject to the terms of the Mozilla Public License 2.0 at https://mozilla.org/MPL/2.0/
// © sunwoo101

//@version=5
indicator("90 Minute Cycles, MOP, DTCC", overlay = true, max_boxes_count = 500, max_labels_count = 500, max_lines_count = 500)

markLast20Candles = input(true, "Last 20 Candles Box Outline", group = "Candle Range")
last20CandlesColor = input(color.yellow, "Last 20 Candles Box Outline Color", group = "Candle Range")
markLast40Candles = input(true, "Last 40 Candles Box Outline", group = "Candle Range")
last40CandlesColor = input(color.orange, "Last 40 Candles Box Outline Color", group = "Candle Range")
show90mCycles = input(true, "Show 90m Cycles", group = "Cycles")
miniCyclesColor = input(color.white, "Mini Cycles Color", group = "Cycles")
_6_00Cycle = input(true, "Q1 (6-7:30)", group = "Cycles")
_6_00CycleColor = input(color.purple, "Q1 (6-7:30) Color", group = "Cycles")
_7_30Cycle = input(true, "Q2 (7:30-9)", group = "Cycles")
_7_30CycleColor = input(color.yellow, "Q2 (7:30-9) Color", group = "Cycles")
_9_00Cycle = input(true, "Q3 (9-10:30)", group = "Cycles")
_9_00CycleColor = input(color.green, "Q3 (9-10:30) Color", group = "Cycles")
_10_30Cycle = input(true, "Q4 (10:30-12)", group = "Cycles")
_10_30CycleColor = input(color.blue, "Q4 (10:30-12) Color", group = "Cycles")
showFVG = input(true, "Show FVG", group = "FVG")
FVGOnlyInSession = input(true, "FVG Only In Session", group = "FVG")
_6_00FVG = input(true, "6-7:30", group = "FVG")
_7_30FVG = input(true, "7:30-9", group = "FVG")
_9_00FVG = input(true, "9-10:30", group = "FVG")
_10_30FVG = input(true, "10:30-12", group = "FVG")
FVGColor = input(color.new(color.white, 50), "FVG Color", group = "FVG")
showMidnightStart = input(true, "Show Midnight Start", group = "Midnight")
midnightStartColor = input(color.black, "Midnight Start Color", group = "Midnight")
showMidnightPrice = input(true, "Show Midnight Opening Price", group = "Midnight")
midnightPriceColor = input(color.black, "Midnight Opening Price Line Color", group = "Midnight")
showNYTrueOpenStart = input(true, "Show NY True Open Start", group = "NY True Open")
NYTrueOpenStartColor = input(color.blue, "NY True Open Start Color", group = "NY True Open")
showNYTrueOpenPrice = input(true, "Show NY True Open Price", group = "NY True Open")
NYTrueOpenPriceColor = input(color.blue, "NY True Open Price Color", group = "NY True Open")
showICTMacros = input(true, "Show ICT Macros", group = "ICT Macros")
ICTMacrosColor = input(color.new(color.black, 50), "ICT Macros Color", group = "ICT Macros")

IsTime(h, m) =>
    s = 0
    not na(time) and hour(time, "America/New_York") == h and minute(time, "America/New_York") == m and second(time, "America/New_York") == s

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

is6_00Session = IsSession("0600-0730")
is7_30Session = IsSession("0730-0900")
is9_00Session = IsSession("0900-1030")
is10_30Session = IsSession("1030-1200")

// Last 20 candles
highest20 = ta.highest(high, 20)
lowest20 = ta.lowest(low, 20)
var _20CandlesBox = box.new(na, na, na, na, bgcolor=na, border_color = last20CandlesColor)
box.set_left(_20CandlesBox, bar_index[19])
box.set_right(_20CandlesBox, bar_index)
box.set_top(_20CandlesBox, highest20)
box.set_bottom(_20CandlesBox, lowest20)
box.set_border_color(_20CandlesBox, markLast20Candles ? last20CandlesColor : na)

// Last 40 candles
highest40 = ta.highest(high, 40)
lowest40 = ta.lowest(low, 40)
var _40CandlesBox = box.new(na, na, na, na, bgcolor=na, border_color = last40CandlesColor)
box.set_left(_40CandlesBox, bar_index[39])
box.set_right(_40CandlesBox, bar_index)
box.set_top(_40CandlesBox, highest40)
box.set_bottom(_40CandlesBox, lowest40)
box.set_border_color(_40CandlesBox, markLast40Candles ? last40CandlesColor : na)

// Cycles
if show90mCycles
    if _6_00Cycle
        if IsTime(6, 0) // LQ run (Q1 start)
            line.new(x1 = bar_index, y1 = low, x2 = bar_index, y2 = high, color = _6_00CycleColor, width = 1, extend = extend.both, style = line.style_solid)

        if IsTime(6, 12) // Trade entry
            line.new(x1 = bar_index, y1 = low, x2 = bar_index, y2 = high, color = miniCyclesColor, width = 1, extend = extend.both, style = line.style_dashed)

        if IsTime(6, 30) // LQ run
            line.new(x1 = bar_index, y1 = low, x2 = bar_index, y2 = high, color = miniCyclesColor, width = 1, extend = extend.both, style = line.style_solid)

        if IsTime(6, 42) // Trade entry
            line.new(x1 = bar_index, y1 = low, x2 = bar_index, y2 = high, color = miniCyclesColor, width = 1, extend = extend.both, style = line.style_dashed)

        if IsTime(7, 0) // LQ run
            line.new(x1 = bar_index, y1 = low, x2 = bar_index, y2 = high, color = miniCyclesColor, width = 1, extend = extend.both, style = line.style_solid)

        if IsTime(7, 12) // Trade entry
            line.new(x1 = bar_index, y1 = low, x2 = bar_index, y2 = high, color = miniCyclesColor, width = 1, extend = extend.both, style = line.style_dashed)

        if IsTime(7, 30) // LQ run (Q3 start)
            line.new(x1 = bar_index, y1 = low, x2 = bar_index, y2 = high, color = _7_30CycleColor, width = 1, extend = extend.both, style = line.style_solid)

    if _7_30Cycle
        if IsTime(7, 30) // LQ run (Q2 start)
            line.new(x1 = bar_index, y1 = low, x2 = bar_index, y2 = high, color = _7_30CycleColor, width = 1, extend = extend.both, style = line.style_solid)

        if IsTime(7, 42) // Trade entry
            line.new(x1 = bar_index, y1 = low, x2 = bar_index, y2 = high, color = miniCyclesColor, width = 1, extend = extend.both, style = line.style_dashed)

        if IsTime(8, 0) // LQ run
            line.new(x1 = bar_index, y1 = low, x2 = bar_index, y2 = high, color = miniCyclesColor, width = 1, extend = extend.both, style = line.style_solid)

        if IsTime(8, 12) // Trade entry
            line.new(x1 = bar_index, y1 = low, x2 = bar_index, y2 = high, color = miniCyclesColor, width = 1, extend = extend.both, style = line.style_dashed)

        if IsTime(8, 30) // LQ run
            line.new(x1 = bar_index, y1 = low, x2 = bar_index, y2 = high, color = miniCyclesColor, width = 1, extend = extend.both, style = line.style_solid)

        if IsTime(8, 42) // Trade entry
            line.new(x1 = bar_index, y1 = low, x2 = bar_index, y2 = high, color = miniCyclesColor, width = 1, extend = extend.both, style = line.style_dashed)

        if IsTime(9, 0) // LQ run (Q3 start)
            line.new(x1 = bar_index, y1 = low, x2 = bar_index, y2 = high, color = _9_00CycleColor, width = 1, extend = extend.both, style = line.style_solid)

    if _9_00Cycle
        if IsTime(9, 0) // LQ run (Q3 start)
            line.new(x1 = bar_index, y1 = low, x2 = bar_index, y2 = high, color = _9_00CycleColor, width = 1, extend = extend.both, style = line.style_solid)

        if IsTime(9, 12) // Trade entry
            line.new(x1 = bar_index, y1 = low, x2 = bar_index, y2 = high, color = miniCyclesColor, width = 1, extend = extend.both, style = line.style_dashed)

        if IsTime(9, 30) // LQ run
            line.new(x1 = bar_index, y1 = low, x2 = bar_index, y2 = high, color = miniCyclesColor, width = 1, extend = extend.both, style = line.style_solid)

        if IsTime(9, 42) // Trade entry
            line.new(x1 = bar_index, y1 = low, x2 = bar_index, y2 = high, color = miniCyclesColor, width = 1, extend = extend.both, style = line.style_dashed)

        if IsTime(10, 0) // LQ run
            line.new(x1 = bar_index, y1 = low, x2 = bar_index, y2 = high, color = miniCyclesColor, width = 1, extend = extend.both, style = line.style_solid)

        if IsTime(10, 12) // Trade entry
            line.new(x1 = bar_index, y1 = low, x2 = bar_index, y2 = high, color = miniCyclesColor, width = 1, extend = extend.both, style = line.style_dashed)

        if IsTime(10, 30) // LQ run (Q4 start)
            line.new(x1 = bar_index, y1 = low, x2 = bar_index, y2 = high, color = _10_30CycleColor, width = 1, extend = extend.both, style = line.style_solid)

    if _10_30Cycle
        if IsTime(10, 30) // LQ run (Q4 start)
            line.new(x1 = bar_index, y1 = low, x2 = bar_index, y2 = high, color = _10_30CycleColor, width = 1, extend = extend.both, style = line.style_solid)
            
        if IsTime(10, 42) // Trade entry
            line.new(x1 = bar_index, y1 = low, x2 = bar_index, y2 = high, color = miniCyclesColor, width = 1, extend = extend.both, style = line.style_dashed)

        if IsTime(11, 0) // LQ run
            line.new(x1 = bar_index, y1 = low, x2 = bar_index, y2 = high, color = miniCyclesColor, width = 1, extend = extend.both, style = line.style_solid)

        if IsTime(11, 12) // Trade entry
            line.new(x1 = bar_index, y1 = low, x2 = bar_index, y2 = high, color = miniCyclesColor, width = 1, extend = extend.both, style = line.style_dashed)

        if IsTime(11, 30) // LQ run
            line.new(x1 = bar_index, y1 = low, x2 = bar_index, y2 = high, color = miniCyclesColor, width = 1, extend = extend.both, style = line.style_solid)

        if IsTime(11, 42) // Trade entry
            line.new(x1 = bar_index, y1 = low, x2 = bar_index, y2 = high, color = miniCyclesColor, width = 1, extend = extend.both, style = line.style_dashed)

        if IsTime(12, 0) // LQ run
            line.new(x1 = bar_index, y1 = low, x2 = bar_index, y2 = high, color = miniCyclesColor, width = 1, extend = extend.both, style = line.style_solid)

// FVG
if showFVG and (not FVGOnlyInSession or (_6_00FVG and is6_00Session or _7_30FVG and is7_30Session or _9_00FVG and is9_00Session or _10_30FVG and is10_30Session))
    // Bullish FVG
    if low > high[2] and close[1] > open[1]
        box.new(left=bar_index[2], top=low, right=bar_index, bottom=high[2], bgcolor=FVGColor, border_color = FVGColor)

    // Bearish FVG
    if high < low[2] and close[1] < open[1]
        box.new(left=bar_index[2], top=high, right=bar_index, bottom=low[2], bgcolor=FVGColor, border_color = FVGColor)

// MOP
var MOPLine = line.new(na, na, na, na, color = showMidnightPrice ? midnightPriceColor : na, width = 2, style = line.style_solid)

if IsTime(0, 0)
    line.new(x1 = bar_index, y1 = low, x2 = bar_index, y2 = high, color = showMidnightStart ? midnightStartColor : na, width = 2, extend = extend.both, style = line.style_solid)
    line.set_xy1(MOPLine, bar_index, open)
    line.set_xy2(MOPLine, bar_index, open)

if barstate.islast
    line.set_x2(MOPLine, bar_index)

// NY True Open
var NYTrueOpenLine = line.new(na, na, na, na, color = showNYTrueOpenPrice ? NYTrueOpenPriceColor : na, width = 2, style = line.style_solid)

if IsTime(7, 30)
    line.new(x1 = bar_index, y1 = low, x2 = bar_index, y2 = high, color = showNYTrueOpenStart ? NYTrueOpenStartColor : na, width = 2, extend = extend.both, style = line.style_solid)
    line.set_xy1(NYTrueOpenLine, bar_index, open)
    line.set_xy2(NYTrueOpenLine, bar_index, open)

if barstate.islast
    line.set_x2(NYTrueOpenLine, bar_index)

// ICT Macros
is8_50Macro = IsSession("0850-0910")
is9_50Macro = IsSession("0950-1010")
is10_50Macro = IsSession("1050-1110")
is13_50Macro = IsSession("1350-1410")
is14_50Macro = IsSession("1450-1510")
is15_00Macro = IsSession("1500-1600")

bgcolor(showICTMacros and (is8_50Macro or is9_50Macro or is10_50Macro or is13_50Macro or is14_50Macro or is15_00Macro) ? ICTMacrosColor : na)