Range Deviations @joshuuu
// This source code is subject to the terms of the Mozilla Public License 2.0 at https://mozilla.org/MPL/2.0/
// © joshuuu
//@version=5
indicator("Range Deviations @joshuuu", overlay = true, max_lines_count = 500, max_boxes_count = 500)

//#region[Inputs and Variables]
show_boxes      = input.bool(true, "Show Boxes", group = "general", inline = "bool")
vertical_lines  = input.bool(false, "Start/End Lines", group = "general", inline = "bool")
line_style_     = input.string("Solid", "Line Style", options = ["Solid", "Dotted", "Dashed"], group = "general")
wicks_bodies    = input.string("Wicks", "STDV Anchor Point", options = ["Wicks","Bodies"], group = "general")

line_style = switch line_style_
    'Dashed' => line.style_dashed
    'Dotted' => line.style_dotted
    'Solid'  => line.style_solid

show_asia   = input.bool(true, "Show asia", group = "asia", inline = "1")
asia_color  = input.color(color.new(color.red, 60), "", group = "asia", inline = "1")
asia_eq     = input.bool(false, "Show Equlibrium", group = "asia", inline = "1")
asia_count  = input.int(3, "", minval = 1, group = "asia", inline = "2")
asia_type   = input.string("Full Deviations", "",  options = ["Full Deviations", "Half Deviations"] ,group = "asia", inline = "2")

show_cbdr   = input.bool(true, "Show cbdr", group = "cbdr", inline = "1")
cbdr_color  = input.color(color.new(color.orange, 60), "", group = "cbdr", inline = "1")
cbdr_eq     = input.bool(false, "Show Equlibrium", group = "cbdr", inline = "1")
cbdr_count  = input.int(3, "", minval = 1, group = "cbdr", inline = "2")
cbdr_type   = input.string("Full Deviations", "",  options = ["Full Deviations", "Half Deviations"] ,group = "cbdr", inline = "2")

show_flout  = input.bool(false, "Show flout", group = "flout", inline = "1")
flout_color = input.color(color.new(color.purple, 60), "", group = "flout", inline = "1")
flout_eq    = input.bool(true, "Show Equlibrium", group = "flout", inline = "1")
flout_count = input.int(3, "", minval = 1, group = "flout", inline = "2")
flout_type  = input.string("Half Deviations", "",  options = ["Full Deviations", "Half Deviations"] ,group = "flout", inline = "2")

show_ons    = input.bool(false, "Show ons", group = "ons", inline = "1")
ons_color   = input.color(color.new(color.blue, 60), "", group = "ons", inline = "1")
ons_eq      = input.bool(true, "Show Equlibrium", group = "ons", inline = "1")
ons_count   = input.int(3, "", minval = 1, group = "ons", inline = "2")
ons_type    = input.string("Full Deviations", "",  options = ["Full Deviations", "Half Deviations"] ,group = "ons", inline = "2")

show_custom = input.bool(false, "Show custom", group = "custom", inline = "1")
custom_color= input.color(color.new(color.gray, 60), "", group = "custom", inline = "1")
custom_eq   = input.bool(true, "Show Equlibrium", group = "custom", inline = "1")
custom_count= input.int(3, "", minval = 1, group = "custom", inline = "2")
custom_type = input.string("Full Deviation", "",  options = ["Full Deviation", "Half Deviation"] ,group = "custom", inline = "2")
custom      = input.session("2000-0000", "Session")

asia        = "2000-0000"
cbdr        = "1400-2000"
flout       = "1500-0000"
ons         = "0500-0900"
na_color    = color.new(color.gray,100)

//#endregion

//#region[Functions]
is_newbar(string sess) =>
    t = time('D', sess, timezone = "America/New_York")
    na(t[1]) and not na(t) or t[1] < t

is_session(string sess) =>
    not na(time('D', sess, timezone = "America/New_York"))

is_over(string sess)   =>
    sess_index = 0
    sess_over = false
    if is_session(sess)
        sess_index := bar_index
    if bar_index[1] == sess_index[1] and bar_index > sess_index
        sess_over := true
    else
        sess_over := false

session_high_low(string session, bool previous = false, bool bodies = false) =>
    var float p_high = na
    var float p_low = na
    var float s_high = na
    var float s_low = na

    _high = bodies ? math.max(open, close) : high
    _low = bodies ? math.min(open,close) : low
    if is_newbar(session)
        p_high := s_high
        p_low := s_low
        s_high := _high
        s_low := _low
    else if is_session(session)
        if _high > s_high
            s_high := _high
        if _low < s_low
            s_low := _low
    var float x_high = na
    var float x_low = na
    if previous
        x_high := p_high
        x_low := p_low
    else
        x_high := s_high
        x_low := s_low
    [x_high, x_low]

range_box(string session = "2000-0000", color color = color.red, string bodies_wicks = "Wicks") =>
    _high = bodies_wicks == "Wicks" ? high : math.max(open,close)
    _low  = bodies_wicks == "Wicks" ? low  : math.min(open,close)

    var box s_box = na
    if is_newbar(session)
        s_box := box.new(bar_index, _high, bar_index, _low, border_color = na_color, bgcolor = color)
    else if is_session(session)
        if _high > box.get_top(s_box)
            box.set_top(s_box, _high)
        if _low < box.get_bottom(s_box)
            box.set_bottom(s_box, _low)
        box.set_right(s_box, bar_index)
    
    s_box

deviations(string session = "2000-0000", bool half_deviations = true, int deviation_number = 3, color color, bool show_eq ) =>
    s_start = fixnan(is_newbar(session) ? bar_index : na)
    [s_high, s_low] = session_high_low(session, bodies = wicks_bodies == "Bodies")
    s_range = s_high - s_low
    if is_over(session)
        for i = 0 to deviation_number * (half_deviations ? 2 : 1)
            line.new(s_start, s_high + i * (half_deviations? 0.5 : 1) * s_range, bar_index, s_high + i * (half_deviations? 0.5 : 1) * s_range, style = line_style, color = color)
            line.new(s_start, s_low - i * (half_deviations? 0.5 : 1) * s_range, bar_index, s_low - i * (half_deviations? 0.5 : 1) * s_range, style = line_style, color = color)
        if vertical_lines
            line.new(s_start, s_high + deviation_number * s_range, s_start, s_low - deviation_number * s_range, style = line_style, color = color, extend = extend.both)
            line.new(bar_index, s_high + deviation_number * s_range, bar_index, s_low - deviation_number * s_range, style = line_style, color = color, extend = extend.both)
        if show_eq
            line.new(s_start, math.avg(s_high, s_low), bar_index, math.avg(s_high, s_low), style = line_style, color = color)


//#endregion

//#region[Plot]
if show_asia
    if show_boxes
        range_box(asia, color.new(asia_color, 90), wicks_bodies)
    deviations(asia, asia_type == "Half Deviations", asia_count, color = asia_color, show_eq = asia_eq)
if show_cbdr
    if show_boxes
        range_box(cbdr, color.new(cbdr_color, 90), wicks_bodies)
    deviations(cbdr, cbdr_type == "Half Deviations", cbdr_count, color = cbdr_color, show_eq = cbdr_eq)
if show_flout
    if show_boxes
        range_box(flout, color.new(flout_color, 90), wicks_bodies)
    deviations(flout, flout_type == "Half Deviations", flout_count, color = flout_color, show_eq = flout_eq)
if show_ons
    if show_boxes
        range_box(ons, color.new(ons_color, 90), wicks_bodies)
    deviations(ons, ons_type == "Half Deviations", ons_count, color = ons_color, show_eq = ons_eq)
if show_custom
    if show_boxes
        range_box(custom, color.new(custom_color, 90), wicks_bodies)
    deviations(custom, custom_type == "Half Deviations", custom_count, color = custom_color, show_eq = custom_eq)
//#endregion