Sessions with High/Low Diff
// This source code provided free and open-source as defined by the terms of the Mozilla Public License 2.0 (https://mozilla.org/MPL/2.0/)
// with the following additional requirements:
//
// 1. Citations for sources and references must be maintained and updated when appropriate
// 2. Links to strategy references included in indicator tooptips and/or alerts should be retained to give credit to the original source
//             while also providing a freely available source of information on proper use and interpretation
//
// Author:  SamAccountX
//
// The main purpose of this indicator is to facilitate backtesting, but it may also be useful for traders to easily identify the current
// active/open trading sessions on lower-timeframe charts.  
//
// This indicator also tracks the session high/low difference and plots it as a label on the last candle of the session once the last
// bar of that session has finished printing and a new candle openend.  The position and direction of the label is based on the 
// session open and close - if the session open is greater than the session close (which would equate to the equivilent of a red candle), 
// the label will be printed UNDER the last candle, and vice versa if the session close is above the session open.  
//
// The number printed inside the label is the difference between the session high and the session low, scaled to the minimum tick value of the chart.
//
// Note:  There is a Pinescript maximum of 500 lables allowed on any chart.  While I could have gotten fancy and done some wizardry with label arrays,
// I didn't really see a point to it.  If labels are enabled for all 4 sessions at the same time, that would still have them available for the past 125 
// sessions, which would be about 6 months (approx 252 trading days per year, and this would cover 125 of them).  If you limit to 2 sessions, you double
// your potential look-back to almost a year (250 days ot of the 252 average trading days each year), and for a single session, you double it yet again
// to just under 2 years.
//
// While it would be easy to add alerts on sessions opening/closing, I didn't see a purpose or value in that as it would be little more than a
// glorified alarm clock.  If I get enough demand to add them, I will gladly consider it.
//
//
//@version=5
indicator(title='Sessions with High/Low Diff', shorttitle='Sess w/ H/L', overlay=true, max_labels_count=500)

// Inputs
// 
// session windows
g_Sessions = 'Session Settings'
showLondonSession = input.bool(title='', defval=true, group=g_Sessions, inline='showLondonSession')
londonSession = input.session(title='London Session', defval='0300-1200', group=g_Sessions, tooltip='Default values are US Eastern (UTC -5), please adjust as needed!', inline='showLondonSession')
showNYSession = input.bool(title='', defval=true, group=g_Sessions, inline='showNYSession')
nySession = input.session(title='New York Session', defval='0800-1700', group=g_Sessions, tooltip='Default values are US Eastern (UTC -5), please adjust as needed!', inline='showNYSession')
showTokyoSession = input.bool(title='', defval=true, group=g_Sessions, inline='showTokyoSession')
tokyoSession = input.session(title='Tokyo Session', defval='2000-0400', group=g_Sessions, tooltip='Default values are US Eastern (UTC -5), please adjust as needed!', inline='showTokyoSession')
showSydneySession = input.bool(title='', defval=true, group=g_Sessions, inline='showSydneySession')
sydneySession = input.session(title='Sydney Session', defval='1700-0200', group=g_Sessions, tooltip='Default values are US Eastern (UTC -5), please adjust as needed!', inline='showSydneySession')
//
// Low/High Resolution
// res = input.timeframe(title='Session High/Low Timeframe', defval='D', options=['D', 'W', 'M'], group='Session High/Low Resolution', tooltip='Select the time interval to use for identifying ' +
//				 'the session-high and session-low data points.  This affects what is shown on the chart as the session high/low.  For timeframes less than 1 week, the daily value is recommended.')
//
// Color inputs
g_Colors = 'Color inputs'
londonBGColor = input.color(title='London Session - Background:', group=g_Colors, defval=color.rgb(0, 255, 0, 90), inline='lc', tooltip='Colors to use for identifying London/Europe session info')
londonTextColor = input.color(title='Label Text:', group=g_Colors, defval=color.rgb(0, 0, 0, 0), inline='lc', tooltip='Colors to use for identifying London/Europe session info')
nyBGColor = input.color(title='New York Session  - Background:', group=g_Colors, inline="nc", defval=color.rgb(255, 0, 0, 90), tooltip='Colors to use for identifying New York/US session info')
nyTextColor = input.color(title='Label Text:', group=g_Colors, inline="nc", defval=color.rgb(255, 255, 255, 0), tooltip='Colors to use for identifying New York/US session info')
asiaBGColor = input.color(title='Tokyo Session  - Background:', group=g_Colors, inline="tc", defval=color.rgb(255, 255, 0, 90), tooltip='Colors to use for identifying Tokyo/Asia session info')
asiaTextColor = input.color(title='Label Text:', group=g_Colors, inline="tc", defval=color.rgb(0, 0, 0, 0), tooltip='Colors to use for identifying Tokyo/Asia session info')
ausBGColor = input.color(title='Sydney Session  - Background:', group=g_Colors, inline="ss", defval=color.rgb(0, 255, 255, 90), tooltip='Colors to use for identifying Sydney/Australia session info')
ausTextColor = input.color(title='Label Text:', group=g_Colors, inline="ss", defval=color.rgb(0, 0, 0, 0), tooltip='Colors to use for identifying Sydney/Australia session info')
//
// High/Low control inputs
doHighLowDiffLabels = input.bool(title='Label session high/low difference', defval=false, group='hlControls', tooltip='Show labels indicating difference between session high and session low')

// Functions
//
// Function to determine if current bar falls into the input session
isInSession(session) =>
    barSessionTime = time(timeframe.period, session)
    inSession = not na(barSessionTime)
    inSession
    
// Force-pull price via security function to help ensure consistency on non-standard chart types...
//
// Explicitly define our ticker to help ensure that we're always getting ACTUAL price instead of relying on the input
// ticker info and input vars (as they tend to inherit the type from what's displayed on the current chart)
realPriceTicker = ticker.new(prefix=syminfo.prefix, ticker=syminfo.ticker)
//
// This MAY be unnecessary, but in testing I've found some oddities when trying to use this on varying chart types
// like on the HA chart, where the source referece for the price skews to the values for the current chart type
// instead of the expected pure-price values.  For example, the 'source=close' reference - 'close' would be actual price
// close on a normal candlestick chart, but would be the HA close on the HA chart.
[o, h, l, c] = request.security(symbol=realPriceTicker, timeframe='', expression=[open, high, low, close], gaps=barmerge.gaps_off, lookahead=barmerge.lookahead_off)

// Session - London
//
// Full-height fill
bgcolor(color=isInSession(londonSession) and showLondonSession ? londonBGColor : na, title='London Session', editable=false)
//
// Track session high/low
var float londonSessionHigh = na
var float londonSessionLow = na
var float londonSessionOpen = na
var float londonSessionClose = na
var bool isLondonSessionStart = false
isInLondonSession = isInSession(londonSession)
if (isInLondonSession)
    // In session, check for session start...
    if (not isInLondonSession[1])
        // Start of session, reset high and low tracking vars
        londonSessionHigh := h
        londonSessionLow := l
        isLondonSessionStart := true
		londonSessionOpen := o
    else
        londonSessionHigh := math.max(h, londonSessionHigh[1])
        londonSessionLow := math.min(l, londonSessionLow[1])
		// Being a bit lazy here and updating close every candle instead of explicitly
		// waiting for the last candle close to grab the session close value, but the result
		// will still be the same...
		londonSessionClose := c
		
// Calculate session high/low diff
// plot(title="LondonHigh", series=isInLondonSession ? londonSessionHigh : na, color=londonBGColor, style=plot.style_circles)
// plot(title="LondonLow", series=isInLondonSession ? londonSessionLow : na, color=londonBGColor, style=plot.style_circles)
londonSessDiff = londonSessionHigh[1] - londonSessionLow[1]
// Plot session high/low diff as label if enables
if (isInLondonSession[1] and not isInLondonSession and doHighLowDiffLabels and showLondonSession)
    r = color.r(londonBGColor)
    g = color.g(londonBGColor)
    b = color.b(londonBGColor)
	isCloseBelowOpen = londonSessionClose < londonSessionOpen ? true : false
    label.new(bar_index[1], isCloseBelowOpen ? londonSessionLow : londonSessionHigh, str.tostring(londonSessDiff, format.mintick), style=isCloseBelowOpen ? label.style_label_up : label.style_label_down, color=color.rgb(r, g, b, 0), textcolor=londonTextColor)

// Session - New York
bgcolor(color=isInSession(nySession) and showNYSession ? nyBGColor : na, title='New York Session', editable=false)
//
// Track session high/low
var float nySessionHigh = na
var float nySessionLow = na
var float nySessionOpen = na
var float nySessionClose = na
var bool isNYSessionStart = false
isInNYSession = isInSession(nySession)
if (isInNYSession)
    // In session, check for session start...
    if (not isInNYSession[1])
        // Start of session, reset high and low tracking vars
        nySessionHigh := h
        nySessionLow := l
        isNYSessionStart := true
		nySessionOpen := o
    else
        nySessionHigh := math.max(h, nySessionHigh[1])
        nySessionLow := math.min(l, nySessionLow[1])
		// Being a bit lazy here and updating close every candle instead of explicitly
		// waiting for the last candle close to grab the session close value, but the result
		// will still be the same...
		nySessionClose := c
// Calculate session high/low diff
nySessDiff = nySessionHigh[1] - nySessionLow[1]
// Plot session high/low diff as label if enables
if (isInNYSession[1] and not isInNYSession and doHighLowDiffLabels and showNYSession)
    r = color.r(nyBGColor)
    g = color.g(nyBGColor)
    b = color.b(nyBGColor)
	isCloseBelowOpen = nySessionClose < nySessionOpen ? true : false
    label.new(bar_index[1], isCloseBelowOpen ? nySessionLow : nySessionHigh, str.tostring(nySessDiff, format.mintick), style=isCloseBelowOpen ? label.style_label_up : label.style_label_down, color=color.rgb(r, g, b, 0), textcolor=nyTextColor)

// Session - Tokyo
bgcolor(color=isInSession(tokyoSession) and showTokyoSession ? asiaBGColor : na, title='Tokyo Session', editable=false)
//
// Track session high/low
var float tokyoSessionHigh = na
var float tokyoSessionLow = na
var float tokyoSessionOpen = na
var float tokyoSessionClose = na
var bool isTokyoSessionStart = false
isInTokyoSession = isInSession(tokyoSession)
if (isInTokyoSession)
    // In session, check for session start...
    if (not isInTokyoSession[1])
        // Start of session, reset high and low tracking vars
        tokyoSessionHigh := h
        tokyoSessionLow := l
        isTokyoSessionStart := true
		tokyoSessionOpen := o
    else
        tokyoSessionHigh := math.max(h, tokyoSessionHigh[1])
        tokyoSessionLow := math.min(l, tokyoSessionLow[1])
		// Being a bit lazy here and updating close every candle instead of explicitly
		// waiting for the last candle close to grab the session close value, but the result
		// will still be the same...
		tokyoSessionClose := c
// Calculate session high/low diff
tokyoSessDiff = tokyoSessionHigh[1] - tokyoSessionLow[1]
// Plot session high/low diff as label if enables
if (isInTokyoSession[1] and not isInTokyoSession and doHighLowDiffLabels and showTokyoSession)
    r = color.r(asiaBGColor)
    g = color.g(asiaBGColor)
    b = color.b(asiaBGColor)
	isCloseBelowOpen = tokyoSessionClose < tokyoSessionOpen ? true : false
    label.new(bar_index[1], isCloseBelowOpen ? tokyoSessionLow : tokyoSessionHigh, str.tostring(tokyoSessDiff, format.mintick), style=isCloseBelowOpen ? label.style_label_up : label.style_label_down, color=color.rgb(r, g, b, 0), textcolor=asiaTextColor)


// Session - Sydney
bgcolor(color=isInSession(sydneySession) and showSydneySession ? ausBGColor : na, title='Sydney Session', editable=false)
//
// Track session high/low
var float sydneySessionHigh = na
var float sydneySessionLow = na
var float sydneySessionOpen = na
var float sydneySessionClose = na
var bool isSydneySessionStart = false
isInSydneySession = isInSession(sydneySession)
if (isInSydneySession)
    // In session, check for session start...
    if (not isInSydneySession[1])
        // Start of session, reset high and low tracking vars
        sydneySessionHigh := h
        sydneySessionLow := l
        isSydneySessionStart := true
		sydneySessionOpen := o
    else
        sydneySessionHigh := math.max(h, sydneySessionHigh[1])
        sydneySessionLow := math.min(l, sydneySessionLow[1])
		// Being a bit lazy here and updating close every candle instead of explicitly
		// waiting for the last candle close to grab the session close value, but the result
		// will still be the same...
		sydneySessionClose := c
// Calculate session high/low diff
sydneySessDiff = sydneySessionHigh[1] - sydneySessionLow[1]
// Plot session high/low diff as label if enables
if (isInSydneySession[1] and not isInSydneySession and doHighLowDiffLabels and showSydneySession)
    r = color.r(ausBGColor)
    g = color.g(ausBGColor)
    b = color.b(ausBGColor)
	isCloseBelowOpen = sydneySessionClose < sydneySessionOpen ? true : false
    label.new(bar_index[1], isCloseBelowOpen ? sydneySessionLow : sydneySessionHigh, str.tostring(sydneySessDiff, format.mintick), style=isCloseBelowOpen ? label.style_label_up : label.style_label_down, color=color.rgb(r, g, b, 0), textcolor=ausTextColor)
