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

//@version=6
indicator(
    "BiasForge - Session Range Lite",
    shorttitle="BF Session Lite",
    overlay=true,
    max_lines_count=12,
    max_labels_count=6
)

string sessionGroup = "Sessions"
string displayGroup = "Display"

string sessionTimezone = input.string(
    "Etc/UTC",
    "Session timezone",
    options=["Etc/UTC", "Europe/London", "America/New_York"],
    group=sessionGroup
)
string asiaSession = input.session("0000-0800", "Asia session", group=sessionGroup)
string londonSession = input.session("0700-1000", "London session", group=sessionGroup)
string newYorkSession = input.session("1230-1600", "New York session", group=sessionGroup)

color asiaColor = input.color(color.rgb(56, 189, 248), "Asia color", group=displayGroup)
color londonColor = input.color(color.rgb(168, 85, 247), "London color", group=displayGroup)
color newYorkColor = input.color(color.rgb(118, 229, 71), "New York color", group=displayGroup)
bool showMidpoints = input.bool(true, "Show midpoints", group=displayGroup)
bool showLabels = input.bool(true, "Show session labels", group=displayGroup)
bool shadeActiveSession = input.bool(true, "Shade active session", group=displayGroup)

bool intradayChart = timeframe.isintraday
bool inAsia = intradayChart and not na(time(timeframe.period, asiaSession, sessionTimezone))
bool inLondon = intradayChart and not na(time(timeframe.period, londonSession, sessionTimezone))
bool inNewYork = intradayChart and not na(time(timeframe.period, newYorkSession, sessionTimezone))
bool asiaStarted = inAsia and not inAsia[1] and barstate.isnew
bool londonStarted = inLondon and not inLondon[1] and barstate.isnew
bool newYorkStarted = inNewYork and not inNewYork[1] and barstate.isnew

var float asiaHigh = na
var float asiaLow = na
var int asiaStartIndex = na
var line asiaHighLine = na
var line asiaMidLine = na
var line asiaLowLine = na
var label asiaLabel = na

var float londonHigh = na
var float londonLow = na
var int londonStartIndex = na
var line londonHighLine = na
var line londonMidLine = na
var line londonLowLine = na
var label londonLabel = na

var float newYorkHigh = na
var float newYorkLow = na
var int newYorkStartIndex = na
var line newYorkHighLine = na
var line newYorkMidLine = na
var line newYorkLowLine = na
var label newYorkLabel = na

if asiaStarted
    if not na(asiaHighLine)
        line.delete(asiaHighLine)
    if not na(asiaMidLine)
        line.delete(asiaMidLine)
    if not na(asiaLowLine)
        line.delete(asiaLowLine)
    if not na(asiaLabel)
        label.delete(asiaLabel)

    asiaHigh := high
    asiaLow := low
    asiaStartIndex := bar_index
    asiaHighLine := line.new(
        bar_index,
        asiaHigh,
        bar_index + 1,
        asiaHigh,
        extend=extend.right,
        color=asiaColor,
        width=1
    )
    asiaLowLine := line.new(
        bar_index,
        asiaLow,
        bar_index + 1,
        asiaLow,
        extend=extend.right,
        color=asiaColor,
        width=1
    )
    asiaMidLine := showMidpoints
        ? line.new(
            bar_index,
            math.avg(asiaHigh, asiaLow),
            bar_index + 1,
            math.avg(asiaHigh, asiaLow),
            extend=extend.right,
            color=color.new(asiaColor, 35),
            style=line.style_dashed
        )
        : na
    asiaLabel := showLabels
        ? label.new(
            bar_index,
            asiaHigh,
            "Asia",
            color=color.new(asiaColor, 82),
            textcolor=asiaColor,
            style=label.style_label_down,
            size=size.tiny
        )
        : na

if inAsia
    asiaHigh := na(asiaHigh) ? high : math.max(asiaHigh, high)
    asiaLow := na(asiaLow) ? low : math.min(asiaLow, low)
    float asiaMidpoint = math.avg(asiaHigh, asiaLow)

    if not na(asiaHighLine)
        line.set_xy1(asiaHighLine, asiaStartIndex, asiaHigh)
        line.set_xy2(asiaHighLine, bar_index + 1, asiaHigh)
    if not na(asiaLowLine)
        line.set_xy1(asiaLowLine, asiaStartIndex, asiaLow)
        line.set_xy2(asiaLowLine, bar_index + 1, asiaLow)
    if not na(asiaMidLine)
        line.set_xy1(asiaMidLine, asiaStartIndex, asiaMidpoint)
        line.set_xy2(asiaMidLine, bar_index + 1, asiaMidpoint)
    if not na(asiaLabel)
        label.set_xy(asiaLabel, asiaStartIndex, asiaHigh)

if londonStarted
    if not na(londonHighLine)
        line.delete(londonHighLine)
    if not na(londonMidLine)
        line.delete(londonMidLine)
    if not na(londonLowLine)
        line.delete(londonLowLine)
    if not na(londonLabel)
        label.delete(londonLabel)

    londonHigh := high
    londonLow := low
    londonStartIndex := bar_index
    londonHighLine := line.new(
        bar_index,
        londonHigh,
        bar_index + 1,
        londonHigh,
        extend=extend.right,
        color=londonColor,
        width=1
    )
    londonLowLine := line.new(
        bar_index,
        londonLow,
        bar_index + 1,
        londonLow,
        extend=extend.right,
        color=londonColor,
        width=1
    )
    londonMidLine := showMidpoints
        ? line.new(
            bar_index,
            math.avg(londonHigh, londonLow),
            bar_index + 1,
            math.avg(londonHigh, londonLow),
            extend=extend.right,
            color=color.new(londonColor, 35),
            style=line.style_dashed
        )
        : na
    londonLabel := showLabels
        ? label.new(
            bar_index,
            londonHigh,
            "London",
            color=color.new(londonColor, 82),
            textcolor=londonColor,
            style=label.style_label_down,
            size=size.tiny
        )
        : na

if inLondon
    londonHigh := na(londonHigh) ? high : math.max(londonHigh, high)
    londonLow := na(londonLow) ? low : math.min(londonLow, low)
    float londonMidpoint = math.avg(londonHigh, londonLow)

    if not na(londonHighLine)
        line.set_xy1(londonHighLine, londonStartIndex, londonHigh)
        line.set_xy2(londonHighLine, bar_index + 1, londonHigh)
    if not na(londonLowLine)
        line.set_xy1(londonLowLine, londonStartIndex, londonLow)
        line.set_xy2(londonLowLine, bar_index + 1, londonLow)
    if not na(londonMidLine)
        line.set_xy1(londonMidLine, londonStartIndex, londonMidpoint)
        line.set_xy2(londonMidLine, bar_index + 1, londonMidpoint)
    if not na(londonLabel)
        label.set_xy(londonLabel, londonStartIndex, londonHigh)

if newYorkStarted
    if not na(newYorkHighLine)
        line.delete(newYorkHighLine)
    if not na(newYorkMidLine)
        line.delete(newYorkMidLine)
    if not na(newYorkLowLine)
        line.delete(newYorkLowLine)
    if not na(newYorkLabel)
        label.delete(newYorkLabel)

    newYorkHigh := high
    newYorkLow := low
    newYorkStartIndex := bar_index
    newYorkHighLine := line.new(
        bar_index,
        newYorkHigh,
        bar_index + 1,
        newYorkHigh,
        extend=extend.right,
        color=newYorkColor,
        width=1
    )
    newYorkLowLine := line.new(
        bar_index,
        newYorkLow,
        bar_index + 1,
        newYorkLow,
        extend=extend.right,
        color=newYorkColor,
        width=1
    )
    newYorkMidLine := showMidpoints
        ? line.new(
            bar_index,
            math.avg(newYorkHigh, newYorkLow),
            bar_index + 1,
            math.avg(newYorkHigh, newYorkLow),
            extend=extend.right,
            color=color.new(newYorkColor, 35),
            style=line.style_dashed
        )
        : na
    newYorkLabel := showLabels
        ? label.new(
            bar_index,
            newYorkHigh,
            "New York",
            color=color.new(newYorkColor, 82),
            textcolor=newYorkColor,
            style=label.style_label_down,
            size=size.tiny
        )
        : na

if inNewYork
    newYorkHigh := na(newYorkHigh) ? high : math.max(newYorkHigh, high)
    newYorkLow := na(newYorkLow) ? low : math.min(newYorkLow, low)
    float newYorkMidpoint = math.avg(newYorkHigh, newYorkLow)

    if not na(newYorkHighLine)
        line.set_xy1(newYorkHighLine, newYorkStartIndex, newYorkHigh)
        line.set_xy2(newYorkHighLine, bar_index + 1, newYorkHigh)
    if not na(newYorkLowLine)
        line.set_xy1(newYorkLowLine, newYorkStartIndex, newYorkLow)
        line.set_xy2(newYorkLowLine, bar_index + 1, newYorkLow)
    if not na(newYorkMidLine)
        line.set_xy1(newYorkMidLine, newYorkStartIndex, newYorkMidpoint)
        line.set_xy2(newYorkMidLine, bar_index + 1, newYorkMidpoint)
    if not na(newYorkLabel)
        label.set_xy(newYorkLabel, newYorkStartIndex, newYorkHigh)

color activeBackground = inNewYork
    ? color.new(newYorkColor, 91)
    : inLondon
        ? color.new(londonColor, 91)
        : inAsia
            ? color.new(asiaColor, 91)
            : na
bgcolor(
    shadeActiveSession and intradayChart ? activeBackground : na,
    title="Active session"
)

bool asiaHighBreach = (
    barstate.isconfirmed
    and intradayChart
    and not inAsia
    and not na(asiaHigh)
    and high > asiaHigh
    and high[1] <= asiaHigh
)
bool asiaLowBreach = (
    barstate.isconfirmed
    and intradayChart
    and not inAsia
    and not na(asiaLow)
    and low < asiaLow
    and low[1] >= asiaLow
)
bool londonHighBreach = (
    barstate.isconfirmed
    and intradayChart
    and not inLondon
    and not na(londonHigh)
    and high > londonHigh
    and high[1] <= londonHigh
)
bool londonLowBreach = (
    barstate.isconfirmed
    and intradayChart
    and not inLondon
    and not na(londonLow)
    and low < londonLow
    and low[1] >= londonLow
)
bool newYorkHighBreach = (
    barstate.isconfirmed
    and intradayChart
    and not inNewYork
    and not na(newYorkHigh)
    and high > newYorkHigh
    and high[1] <= newYorkHigh
)
bool newYorkLowBreach = (
    barstate.isconfirmed
    and intradayChart
    and not inNewYork
    and not na(newYorkLow)
    and low < newYorkLow
    and low[1] >= newYorkLow
)

alertcondition(
    asiaHighBreach,
    "BiasForge Session Lite Asia High",
    "Price traded above the latest Asia session high."
)
alertcondition(
    asiaLowBreach,
    "BiasForge Session Lite Asia Low",
    "Price traded below the latest Asia session low."
)
alertcondition(
    londonHighBreach,
    "BiasForge Session Lite London High",
    "Price traded above the latest London session high."
)
alertcondition(
    londonLowBreach,
    "BiasForge Session Lite London Low",
    "Price traded below the latest London session low."
)
alertcondition(
    newYorkHighBreach,
    "BiasForge Session Lite New York High",
    "Price traded above the latest New York session high."
)
alertcondition(
    newYorkLowBreach,
    "BiasForge Session Lite New York Low",
    "Price traded below the latest New York session low."
)

var table statusTable = table.new(
    position.top_right,
    1,
    1,
    bgcolor=color.new(color.black, 18),
    border_color=color.new(color.white, 82),
    border_width=1
)

if barstate.islast
    string statusText = intradayChart
        ? "Session Lite · " + sessionTimezone
        : "Session Lite · use an intraday chart"
    table.cell(
        statusTable,
        0,
        0,
        statusText,
        text_color=color.new(color.white, 12),
        text_size=size.tiny
    )
