Session candles & reversals / quantifytools
// This source code is subject to the terms of the Mozilla Public License 2.0 at https://mozilla.org/MPL/2.0/
// © quantifytools

//@version=5
indicator("Session candles & session reversals (+ Backtest)", max_boxes_count=500, max_lines_count=500, max_labels_count=500, overlay=true)

//                      Inputs

//Session time, session candle offset and show/hide session candle
groupSession = "Sessions"

//Session 1 (London)
show_london = input(title='Show session #1', defval=true, inline="london", group=groupSession)
i_londonOffset = input.int(0, "", inline="london", minval=0, group=groupSession, tooltip="Moves candle forward by specified amount of bars.")
london = input.session(title="Session #1 period", defval='0800-1700', tooltip="London by default (UTC)", group=groupSession)

//Session 2 (New York)
show_ny = input(title='Show session #2', defval=true, inline="ny", group=groupSession)
i_nyOffset = input.int(0, "", inline="ny", minval=0, group=groupSession)
ny = input.session(title="Session #2 period", defval='1300-2200', tooltip="New York by default (UTC)", group=groupSession)

//Session 3 (Sydney)
show_sydney = input(title='Show session #3', defval=true, inline="sydney", group=groupSession)
i_sydneyOffset = input.int(0, "", inline="sydney", minval=0, group=groupSession)
sydney = input.session(title="Session #3 period", defval='2100-0600', tooltip="Sydney by default (UTC)", group=groupSession)

//Session 4 (Tokyo)
show_tokyo = input(title='Show session #4', defval=true, inline="tokyo", group=groupSession)
i_tokyoOffset = input.int(0, "", inline="tokyo", minval=0, group=groupSession)
tokyo = input.session(title="Session #4 period", defval='0000-0900', tooltip="Tokyo by default (UTC)", group=groupSession)

//Session moving average type, length and selected sessions for MA
groupMa = "Session moving average"

i_smoothingType = input.string("SMA", "", options=["SMA", "EMA", "HMA", "RMA", "WMA"], group=groupMa, inline="ma")
i_sessionMaLength = input.int(20, "", group=groupMa, inline="ma")

i_useSession1Ma = input.bool(true, "Include session #1", group=groupMa)
i_useSession2Ma = input.bool(true, "Include session #2", group=groupMa)
i_useSession3Ma = input.bool(true, "Include session #3", group=groupMa)
i_useSession4Ma = input.bool(true, "Include session #4", group=groupMa)

//Hide chart candles/bars, highlight sessions for selected session candles, hide timeframe notification, numerize sessions
groupChart = "Chart"

i_highlightBt = input.string("None", "Highlight backtesting visuals", options=["None", "Session #1", "Session #2", "Session #3", "Session #4"], group=groupChart)
i_hideChart = input.bool(true, "Hide chart", tooltip="", group=groupChart)
i_highlightSession = input.bool(false, "Highlight session periods", group=groupChart, tooltip="Highlights session periods on chart background.")
i_numberedSessions = input.bool(false, "Numerize sessions", group=groupChart, tooltip="Replaces L, N, S, T with 1, 2, 3, 4. Helps with custom sessions.")
i_hideNotification = input.bool(false, "Hide timeframe notification", group=groupChart, tooltip="Hides timeframe notification that pops up when timeframe is > 1 hour.")

//Colors
groupColor= "Colors"

i_maColUp = input.color(color.green, "MA ▲", group=groupColor, inline="ma")
i_maColDown = input.color(color.red, "MA ▼", group=groupColor, inline="ma")

i_londonColUp = input.color(#c8e6c9, "Session #1 ▲", group=groupColor, inline="session1")
i_londonColDown = input.color(#faa1a4, "Session #1 ▼", group=groupColor, inline="session1")
i_londonColSession = input.color(color.gray, "Session #1 ON", group=groupColor, inline="session1", tooltip="Session background color. Visible only if enabled.")

i_nyColUp = input.color(#81c784, "Session #2 ▲", group=groupColor, inline="session2")
i_nyColDown = input.color(#f46166, "Session #2 ▼", group=groupColor, inline="session2")
i_nyColSession = input.color(color.black, "Session #2 ON", group=groupColor, inline="session2")

i_sydneyColUp = input.color(#05900b, "Session #3 ▲", group=groupColor, inline="session3")
i_sydneyColDown = input.color(#f23645, "Session #3 ▼", group=groupColor, inline="session3")
i_sydneyColSession = input.color(color.blue, "Session #3 ON", group=groupColor, inline="session3")

i_tokyoColUp = input.color(#004505, "Session #4 ▲", group=groupColor, inline="session4")
i_tokyoColDown = input.color(#880e4f, "Session #4 ▼", group=groupColor, inline="session4")
i_tokyoColSession = input.color(color.navy, "Session #4 ON", group=groupColor, inline="session4")


//                      Session start, on and end functions

sessionStart(session) =>
    na(time(timeframe.period, session, "GMT"))[1] and not na(time(timeframe.period, session, "GMT"))

sessionOn(session) =>
    not na(time(timeframe.period, session, "GMT"))

sessionEnd(session) =>
    na(time(timeframe.period, session, "GMT")) and not na(time(timeframe.period, session, "GMT"))[1]


//                      Session OHLC arrays

//London
var londonOpen = array.new_float(0, 0)
var londonHigh = array.new_float(0, 0)
var londonLow = array.new_float(0, 0)
var londonClose = array.new_float(0, 0)

recentLondonOpen = array.size(londonOpen) > 0 ? array.get(londonOpen, 0) : na
recentLondonHigh = array.size(londonHigh) > 0 ? array.get(londonHigh, 0) : na
recentLondonLow = array.size(londonLow) > 0 ? array.get(londonLow, 0) : na
recentLondonClose = array.size(londonClose) > 0 ? array.get(londonClose, 0) : na

//New York
var nyOpen = array.new_float(0, 0)
var nyHigh = array.new_float(0, 0)
var nyLow = array.new_float(0, 0)
var nyClose = array.new_float(0, 0)

recentNyOpen = array.size(nyOpen) > 0 ? array.get(nyOpen, 0) : na
recentNyHigh = array.size(nyHigh) > 0 ? array.get(nyHigh, 0) : na
recentNyLow = array.size(nyLow) > 0 ? array.get(nyLow, 0) : na
recentNyClose = array.size(nyClose) > 0 ? array.get(nyClose, 0) : na

//Sydney
var sydneyOpen = array.new_float(0, 0)
var sydneyHigh = array.new_float(0, 0)
var sydneyLow = array.new_float(0, 0)
var sydneyClose = array.new_float(0, 0)

recentSydneyOpen = array.size(sydneyOpen) > 0 ? array.get(sydneyOpen, 0) : na
recentSydneyHigh = array.size(sydneyHigh) > 0 ? array.get(sydneyHigh, 0) : na
recentSydneyLow = array.size(sydneyLow) > 0 ? array.get(sydneyLow, 0) : na
recentSydneyClose = array.size(sydneyClose) > 0 ? array.get(sydneyClose, 0) : na

//Tokyo
var tokyoOpen = array.new_float(0, 0)
var tokyoHigh = array.new_float(0, 0)
var tokyoLow = array.new_float(0, 0)
var tokyoClose = array.new_float(0, 0)

recentTokyoOpen = array.size(tokyoOpen) > 0 ? array.get(tokyoOpen, 0) : na
recentTokyoHigh = array.size(tokyoHigh) > 0 ? array.get(tokyoHigh, 0) : na
recentTokyoLow = array.size(tokyoLow) > 0 ? array.get(tokyoLow, 0) : na
recentTokyoClose = array.size(tokyoClose) > 0 ? array.get(tokyoClose, 0) : na


//          Session calculations

//Initializing session highest high/lowest low trackers
var float londonHh = 0
var float londonLl = 0

var float nyHh = 0
var float nyLl = 0

var float sydneyHh = 0
var float sydneyLl = 0

var float tokyoHh = 0
var float tokyoLl = 0
        
//London calculations

//If session starts, add open to array, high to HH tracker, low to LL tracker
if sessionStart(london)
    array.unshift(londonOpen, open)
    londonHh := high
    londonLl := low

//If session is on and high > HH tracker value, replace HH tracker value with high
if sessionOn(london) and high > londonHh[1]
    londonHh := high

//If session is on and low < LL tracker value, replace LL tracker value with low
if sessionOn(london) and low < londonLl[1]
    londonLl := low

//If session has ended, add session close (candle open, because session ended 1 time period ago) and add highest high/lowest low to session arrays
if sessionEnd(london)
    array.unshift(londonClose, open)
    array.unshift(londonHigh, londonHh)
    array.unshift(londonLow, londonLl)

//Repeating above steps for all other sessions.

//New York calculations
if sessionStart(ny)
    array.unshift(nyOpen, open)
    nyHh := high
    nyLl := low

if sessionOn(ny) and high > nyHh[1]
    nyHh := high

if sessionOn(ny) and low < nyLl[1]
    nyLl := low

if sessionEnd(ny)
    array.unshift(nyClose, open)
    array.unshift(nyHigh, nyHh)
    array.unshift(nyLow, nyLl)

//Sydney calculations
if sessionStart(sydney)
    array.unshift(sydneyOpen, open)
    sydneyHh := high
    sydneyLl := low

if sessionOn(sydney) and high > sydneyHh[1]
    sydneyHh := high

if sessionOn(sydney) and low < sydneyLl[1]
    sydneyLl := low

if sessionEnd(sydney)
    array.unshift(sydneyClose, open)
    array.unshift(sydneyHigh, sydneyHh)
    array.unshift(sydneyLow, sydneyLl)

//Tokyo calculations
if sessionStart(tokyo)
    array.unshift(tokyoOpen, open)
    tokyoHh := high
    tokyoLl := low

if sessionOn(tokyo) and high > tokyoHh[1]
    tokyoHh := high

if sessionOn(tokyo) and low < tokyoLl[1]
    tokyoLl := low

if sessionEnd(tokyo)
    array.unshift(tokyoClose, open)
    array.unshift(tokyoHigh, tokyoHh)
    array.unshift(tokyoLow, tokyoLl)

//Function to keep array sizes fixed
arraySize(sessionOpen, sessionHigh, sessionLow, sessionClose) =>

    sizeLimit = 2

    if array.size(sessionOpen) >sizeLimit
        array.remove(sessionOpen, sizeLimit)
        array.remove(sessionHigh, sizeLimit)
        array.remove(sessionLow, sizeLimit)
        array.remove(sessionClose, sizeLimit)
        
//Clearing arrays 
arraySize(londonOpen, londonHigh, londonLow, londonClose)
arraySize(nyOpen, nyHigh, nyLow, nyClose)
arraySize(sydneyOpen, sydneyHigh, sydneyLow, sydneyClose)
arraySize(tokyoOpen, tokyoHigh, tokyoLow, tokyoClose)


//                      Session moving average

//Function for user defined smoothing method
smoothedValue(source, length) =>
    i_smoothingType == "SMA" ? ta.sma(source, length) :
     i_smoothingType == "EMA" ? ta.ema(source, length) :
      i_smoothingType == "HMA" ? ta.hma(source, length) :
       i_smoothingType == "RMA" ? ta.rma(source, length) : ta.wma(source, length)

//Session MA's, if not included use 0
londonSma = i_useSession1Ma ? smoothedValue(recentLondonClose, i_sessionMaLength) : 0
nySma = i_useSession2Ma ? smoothedValue(recentNyClose, i_sessionMaLength) : 0
sydneySma = i_useSession3Ma ? smoothedValue(recentSydneyClose, i_sessionMaLength) : 0
tokyoSma = i_useSession4Ma ? smoothedValue(recentTokyoClose, i_sessionMaLength) : 0

//Calculate amount of sessions included in MA calculation
int maDivider = 0

if i_useSession1Ma 
    maDivider += 1

if i_useSession2Ma 
    maDivider += 1

if i_useSession3Ma 
    maDivider += 1

if i_useSession4Ma 
    maDivider += 1

//Selected session MA's divided by amount of selected sessions
hybridSma = (nySma + tokyoSma + londonSma + sydneySma) / maDivider


//                      Session reversals

//Session reversal function with session OHLC variables
sessionReversal(sessionOpen, sessionHigh, sessionLow, sessionClose) =>

    //Position of session close relative to range (high - low)
    sessionPos = (sessionClose - sessionLow) / (sessionHigh - sessionLow)

    //Position of session open relative to range (high - low)
    sessionOpenPos = (sessionOpen - sessionLow) / (sessionHigh - sessionLow)

    //Session high lower than previous high, session low lower than previous low, close >= 65% position (1% being low, 100% being high) and session open at >= 40% position relative to range. A wick up.
    wickUp = sessionHigh < sessionHigh[1] and sessionLow < sessionLow[1] and sessionPos >= 0.65 and sessionOpenPos >= 0.40

    //Session high higher than previous high, session low higher than previous low, close <= 35% position and session open <= 60% position relative to range. A wick down.
    wickDown = sessionHigh > sessionHigh[1] and sessionLow > sessionLow[1] and sessionPos <= 0.35 and sessionOpenPos <= 0.60

    //Session high higher than previous high, session low lower than previous low and close position at same 65%/35% values. Engulfing candle up/down.
    engulfUp = sessionHigh > sessionHigh[1] and sessionLow < sessionLow[1] and sessionPos >= 0.65
    engulfDown = sessionHigh > sessionHigh[1] and sessionLow < sessionLow[1] and sessionPos <= 0.35


    [wickUp, wickDown, engulfUp, engulfDown]

//Fetching session reversals for each session
[wickUpLondon, wickDownLondon, engulfUpLondon, engulfDownLondon] = sessionReversal(recentLondonOpen, recentLondonHigh, recentLondonLow, recentLondonClose) 
[wickUpNy, wickDownNy, engulfUpNy, engulfDownNy] = sessionReversal(recentNyOpen, recentNyHigh, recentNyLow, recentNyClose) 
[wickUpSydney, wickDownSydney, engulfUpSydney, engulfDownSydney] = sessionReversal(recentSydneyOpen, recentSydneyHigh, recentSydneyLow, recentSydneyClose) 
[wickUpTokyo, wickDownTokyo, engulfUpTokyo, engulfDownTokyo] = sessionReversal(recentTokyoOpen, recentTokyoHigh, recentTokyoLow, recentTokyoClose) 


//                      Session reversal backtest

//Sessions are backtested by calculating odds of next matching session closing in supporting direction, known as win rate.
//Magnitude is measured by calculating percentage increase/decrease between session reversal close and next session high/low. This calculation is done only when a reversal is succesful (closes higher/lower compared previous matching session).

//Session reversal backtest function
sessionReversalBt(wickUp, wickDown, engulfUp, engulfDown, sessionClose, sessionHigh, sessionLow, session) =>

    //Get bar_index space between sessions
    btPeriod = ta.valuewhen(sessionEnd(session), bar_index, 0) - ta.valuewhen(sessionEnd(session), bar_index, 1)

    //Initializing counters
    var int sessionRevUpCount = 0
    var int sessionRevDownCount = 0

    var int succesfulRevUpCount = 0
    var int succesfulRevDownCount = 0

    var float revUpDepth = 0
    var float revDownDepth = 0

    //Any reversal up/down, either wick or engulfing.
    anyRevUp = wickUp or engulfUp
    anyRevDown = wickDown or engulfDown

    //Add 1 to reversal counter when session reversal occurs
    if anyRevUp
        sessionRevUpCount += 1
    else
        if anyRevDown
            sessionRevDownCount += 1

    //If session closes higher than previous session and previous session was a reversal, add 1 to succesful reversal counter. Add percentage increase (from previous session close to current session high) to magnitude counter.
    if sessionClose > sessionClose[1] and anyRevUp[btPeriod]
        succesfulRevUpCount += 1
        revUpDepth += (sessionHigh / sessionClose[1]) - 1

    //Same, but for reversals down
    if sessionClose < sessionClose[1] and anyRevDown[btPeriod]
        succesfulRevDownCount += 1
        revDownDepth += (sessionLow / sessionClose[1]) - 1

    //Form win rates by dividing succesful reversal count with total reversal count
    revUpWr = math.round((succesfulRevUpCount / sessionRevUpCount) * 100, 0)
    revDownWr = math.round((succesfulRevDownCount / sessionRevDownCount) * 100, 0)

    //Form average magnitude by dividing percentage increase/decrease with total reversal count
    revUpMagnitude = math.round((revUpDepth / sessionRevUpCount) * 100, 2)
    revDownMagnitude = math.round((revDownDepth / sessionRevDownCount) * 100, 2)

    [sessionRevUpCount, sessionRevDownCount, succesfulRevDownCount, succesfulRevUpCount, revUpDepth, revDownDepth,
     revUpWr, revDownWr, revUpMagnitude, revDownMagnitude]

//Fetching backtest results for each session reversal
[revUpsLondon, revDownsLondon, sRevDownsLondon, sRevUpsLondon, LondonUpDepth, LondonDownDepth,
 LondonRevUpWr, LondonRevDownWr, LondonRevUpMagnitude, LondonRevDownMagnitude] 
  = sessionReversalBt(wickUpLondon, wickDownLondon, engulfUpLondon, engulfDownLondon, recentLondonClose, recentLondonHigh, recentLondonLow, london) 

[revUpsNy, revDownsNy, sRevDownsNy, sRevUpsNy, NyUpDepth, NyDownDepth,
 NyRevUpWr, NyRevDownWr, NyRevUpMagnitude, NyRevDownMagnitude] 
  = sessionReversalBt(wickUpNy, wickDownNy, engulfUpNy, engulfDownNy, recentNyClose, recentNyHigh, recentNyLow, ny) 

[revUpsSydney, revDownsSydney, sRevDownsSydney, sRevUpsSydney, SydneyUpDepth, SydneyDownDepth,
 SydneyRevUpWr, SydneyRevDownWr, SydneyRevUpMagnitude, SydneyRevDownMagnitude] 
  = sessionReversalBt(wickUpSydney, wickDownSydney, engulfUpSydney, engulfDownSydney, recentSydneyClose, recentSydneyHigh, recentSydneyLow, sydney) 

[revUpsTokyo, revDownsTokyo, sRevDownsTokyo, sRevUpsTokyo, TokyoUpDepth, TokyoDownDepth,
 TokyoRevUpWr, TokyoRevDownWr, TokyoRevUpMagnitude, TokyoRevDownMagnitude] 
  = sessionReversalBt(wickUpTokyo, wickDownTokyo, engulfUpTokyo, engulfDownTokyo, recentTokyoClose, recentTokyoHigh, recentTokyoLow, tokyo) 


//                      Plot related calculations                         

//Current timeframe
currentTf = timeframe.in_seconds(timeframe.period) / 60

//Note users when timeframe is above 1H and possibly not matching with selected session time periods
tfNotification = currentTf > 60 and i_hideNotification == false ? "(!) Make sure timeframe supports selected session time periods. By default 1H is recommended." : ""

//Session candle colors
londonColor = recentLondonClose > recentLondonClose[1] ? i_londonColUp : i_londonColDown
nyColor = recentNyClose > recentNyOpen ? i_nyColUp : i_nyColDown
sydneyColor = recentSydneyClose > recentSydneyClose[1] ? i_sydneyColUp : i_sydneyColDown
tokyoColor = recentTokyoClose > recentTokyoClose[1] ? i_tokyoColUp : i_tokyoColDown

//MA color, using "switch" method to avoid incorrect colors when MA value is equal to last MA value (happens on lower timeframes)
var color maCol = color.gray

if hybridSma > hybridSma[1]
    maCol := i_maColUp 

if hybridSma == hybridSma[1]
    maCol := maCol[1]

if hybridSma < hybridSma[1]
    maCol := i_maColDown

//Session candle visuals
borderWidth = 0
linewidth = 2

//Session reversal colors

//Any session reversal
anyWickUp = wickUpNy or wickUpLondon or wickUpSydney or wickUpTokyo
anyWickDown = wickDownNy or wickDownLondon or wickDownSydney or wickDownTokyo
anyEngulfUp = engulfUpNy or engulfUpLondon or engulfUpSydney or engulfUpTokyo
anyEngulfDown = engulfDownNy or engulfDownLondon or engulfDownSydney or engulfDownTokyo

//Session reversal color
revCol = anyWickUp ? color.green : anyWickDown ? color.red : anyEngulfUp ? color.teal : anyEngulfDown ? color.maroon : na

//Reversal number/text. Use LNST by default, 1-4 if numerized sessions is on.
session1Text = i_numberedSessions ? "1" : "L"
session2Text = i_numberedSessions ? "2" : "N"
session3Text = i_numberedSessions ? "3" : "S"
session4Text = i_numberedSessions ? "4" : "T"

//Reversal label texts
revUpL = "▲" + "\n" + session1Text
revDownL = session1Text + "\n" + "▼" 

revUpN = "▲" + "\n" + session2Text
revDownN = session2Text + "\n" + "▼" 

revUpS = "▲" + "\n" + session3Text
revDownS = session3Text + "\n" + "▼" 

revUpT = "▲" + "\n" + session4Text
revDownT = session4Text + "\n" + "▼" 

//Highest session reversal up win rate
highestWrUp = LondonRevUpWr >= NyRevUpWr and LondonRevUpWr >= SydneyRevUpWr and LondonRevUpWr >= TokyoRevUpWr ? 1 : 
 NyRevUpWr >= LondonRevUpWr and NyRevUpWr >= SydneyRevUpWr and NyRevUpWr >= TokyoRevUpWr ? 2 : 
  SydneyRevUpWr >= LondonRevUpWr and SydneyRevUpWr >= NyRevUpWr and SydneyRevUpWr >= TokyoRevUpWr ? 3 : 4

//Highest session reversal down win rate
highestWrDown = LondonRevDownWr >= NyRevDownWr and LondonRevDownWr >= SydneyRevDownWr and LondonRevDownWr >= TokyoRevDownWr ? 1 : 
 NyRevDownWr >= LondonRevDownWr and NyRevDownWr >= SydneyRevDownWr and NyRevDownWr >= TokyoRevDownWr ? 2 : 
  SydneyRevDownWr >= LondonRevDownWr and SydneyRevDownWr >= NyRevDownWr and SydneyRevDownWr >= TokyoRevDownWr ? 3 : 4

//Highest session magnitude up
highestMagnitudeUp = LondonRevUpMagnitude >= NyRevUpMagnitude and LondonRevUpMagnitude >= SydneyRevUpMagnitude and LondonRevUpMagnitude >= TokyoRevUpMagnitude ? 1 : 
 NyRevUpMagnitude >= LondonRevUpMagnitude and NyRevUpMagnitude >= SydneyRevUpMagnitude and NyRevUpMagnitude >= TokyoRevUpMagnitude ? 2 : 
  SydneyRevUpMagnitude >= LondonRevUpMagnitude and SydneyRevUpMagnitude >= NyRevUpMagnitude and SydneyRevUpMagnitude >= TokyoRevUpMagnitude ? 3 : 4

//Highest session magnitude down
highestMagnitudeDown = LondonRevDownMagnitude <= NyRevDownMagnitude and LondonRevDownMagnitude <= SydneyRevDownMagnitude and LondonRevDownMagnitude <= TokyoRevDownMagnitude ? 1 : 
 NyRevDownMagnitude <= LondonRevDownMagnitude and NyRevDownMagnitude <= SydneyRevDownMagnitude and NyRevDownMagnitude <= TokyoRevDownMagnitude ? 2 : 
  SydneyRevDownMagnitude <= LondonRevDownMagnitude and SydneyRevDownMagnitude <= NyRevDownMagnitude and SydneyRevDownMagnitude <= TokyoRevDownMagnitude ? 3 : 4

//Reversal backtest table texts. Add 🔸 if given session reversal backtest metric is best perofrming
revUpLText = session1Text + " ▲ : " + str.tostring(revUpsLondon) + " | " + "WR: " + str.tostring(LondonRevUpWr) + "%" + (highestWrUp == 1 ? " 🔸" : "") + " | " + "M: " + str.tostring(LondonRevUpMagnitude) + "%" + (highestMagnitudeUp == 1 ? " 🔸" : "")
revDownLText = session1Text + " ▼ : " + str.tostring(revDownsLondon)  + " | " +  "WR: " + str.tostring(LondonRevDownWr) + "%" + (highestWrDown == 1 ? " 🔸" : "") +  " | " + " M: " + str.tostring(LondonRevDownMagnitude) + "%" + (highestMagnitudeDown == 1 ? " 🔸" : "")

revUpNText = session2Text + " ▲ : " + str.tostring(revUpsNy) + " | " + "WR: " + str.tostring(NyRevUpWr) + "%" + (highestWrUp == 2 ? " 🔸" : "") + " | " + "M: " + str.tostring(NyRevUpMagnitude) + "%" + (highestMagnitudeUp == 2 ? " 🔸" : "")
revDownNText = session2Text + " ▼ : " + str.tostring(revDownsNy)  + " | " +  "WR: " + str.tostring(NyRevDownWr) + "%" + (highestWrDown == 2 ? " 🔸" : "") +  " | " + " M: " + str.tostring(NyRevDownMagnitude) + "%" + (highestMagnitudeDown == 2 ? " 🔸" : "")

revUpSText = session3Text + " ▲ : " + str.tostring(revUpsSydney) + " | " + "WR: " + str.tostring(SydneyRevUpWr) + "%" + (highestWrUp == 3 ? " 🔸" : "") + " | " + "M: " + str.tostring(SydneyRevUpMagnitude) + "%" + (highestMagnitudeUp == 3 ? " 🔸" : "")
revDownSText = session3Text + " ▼ : " + str.tostring(revDownsSydney)  + " | " +  "WR: " + str.tostring(SydneyRevDownWr) + "%" + (highestWrDown == 3 ? " 🔸" : "") +  " | " + " M: " + str.tostring(SydneyRevDownMagnitude) + "%" + (highestMagnitudeDown == 3 ? " 🔸" : "")

revUpTText = session4Text + " ▲ : " + str.tostring(revUpsTokyo) + " | " + "WR: " + str.tostring(TokyoRevUpWr) + "%" + (highestWrUp == 4 ? " 🔸" : "") + " | " + "M: " + str.tostring(TokyoRevUpMagnitude) + "%" + (highestMagnitudeUp == 4 ? " 🔸" : "")
revDownTText = session4Text + " ▼ : " + str.tostring(revDownsTokyo)  + " | " +  "WR: " + str.tostring(TokyoRevDownWr) + "%" + (highestWrDown == 4 ? " 🔸" : "") +  " | " + " M: " + str.tostring(TokyoRevDownMagnitude) + "%" + (highestMagnitudeDown == 4 ? " 🔸" : "")

//Function for forming session candles using line and box, available for first 500 bars. Clear look. Forming session reversal labels also.
sessionCandles(session, sessionOffset, sessionOpen, sessionHigh, sessionLow, sessionClose, sessionWickUp, sessionEngUp, sessionWickDown, sessionEngDown, sessionCol, revUpText, revDownText) =>

    //Session candles
    if sessionEnd(session)[1]
        line.new(bar_index + sessionOffset, sessionHigh, bar_index + sessionOffset, sessionLow, xloc=xloc.bar_index, color=sessionCol, width=linewidth, style=line.style_solid)
        box.new(left=bar_index - 1 + sessionOffset, top=sessionClose, right=bar_index + 1 + sessionOffset, bottom=sessionOpen, bgcolor=sessionCol, border_color=color.new(color.white, 100), border_width=borderWidth)

    //Session reversals
    if (sessionWickUp or sessionEngUp) and sessionEnd(session)[1] and barstate.isconfirmed
        label.new(x=bar_index + sessionOffset, y=sessionLow, xloc=xloc.bar_index, yloc=yloc.price, text=revUpText, style=label.style_label_up, textcolor=color.new(revCol, 1), color=color.new(color.green, 100), size=size.normal)

    if (sessionWickDown or sessionEngDown) and sessionEnd(session)[1] and barstate.isconfirmed
        label.new(x=bar_index + sessionOffset, y=sessionHigh, xloc=xloc.bar_index, yloc=yloc.price, text=revDownText, style=label.style_label_down, textcolor=color.new(revCol, 1), color=color.new(color.red, 100), size=size.normal)

//Function for forming session candles using plotcandle() function, available for all bars. Not so clear, but goes back indefinitely.
sessionCandles2(session, sessionOffset, showSession, sessionOpen, sessionHigh, sessionLow, sessionClose) =>

    //Session OHLC values with offset for plotcandle() function
    sessionEndO = ta.barssince(sessionEnd(session)) == 1 + sessionOffset and showSession ? sessionOpen : na
    sessionEndH = ta.barssince(sessionEnd(session)) == 1 + sessionOffset and showSession ? sessionHigh : na
    sessionEndL = ta.barssince(sessionEnd(session)) == 1 + sessionOffset and showSession ? sessionLow : na
    sessionEndC = ta.barssince(sessionEnd(session)) == 1 + sessionOffset and showSession ? sessionClose : na

    [sessionEndO, sessionEndH, sessionEndL, sessionEndC]

//Function for forming backtest visuals.
sessionReversalBtVisuals(session, sessionHigh, sessionLow, sessionClose, succesfulRevsUp, succesfulRevsDown, revsUp, revsDown, revUpWr, revUpMagnitude, revDownWr, revDownMagnitude, sessionOffset) =>

    //Get bar_index space between session reversals
    btPeriod = ta.valuewhen(sessionEnd(session), bar_index, 0) - ta.valuewhen(sessionEnd(session), bar_index, 1)

    //Reversal up texts
    sRevUpMagnitudeText = "      Gain: +" + str.tostring(math.round((sessionHigh / sessionClose[1]) - 1, 4) * 100) + "%" + "\n" + "◣ Avg: +" + str.tostring(revUpMagnitude) + "%"
    sRevUpWrText = "\n" + "\n" + "\n" + "Succesful: " + str.tostring(succesfulRevsUp) + "\n" + "Win rate: " + str.tostring(revUpWr) + "%"
    revUpCountText = "\n" + "\n" + "Count: " + str.tostring(revsUp)

    //Reversal down texts
    sRevDownMagnitudeText = "◤ Gain: " + str.tostring(math.round((sessionLow / sessionClose[1]) - 1, 4) * 100) + "%" + "\n" + "     Avg: " + str.tostring(revDownMagnitude) + "%"
    sRevDownWrText = "Succesful: " + str.tostring(succesfulRevsDown) + "\n" + "Win rate: " + str.tostring(revDownWr) + "%" + "\n" + "\n" + "\n"
    revDownCountText = "Count: " + str.tostring(revsDown) + "\n" + "\n"

    //If reversal is succesful and session ended 1 period ago, plot line and labels
    if succesfulRevsUp > succesfulRevsUp[1] and sessionEnd(session)[1]
        line.new(bar_index[btPeriod] + sessionOffset, sessionClose[1], bar_index + sessionOffset, sessionHigh, xloc=xloc.bar_index, color=color.white, width=1, style=line.style_solid)
        label.new(x=bar_index + sessionOffset, y=sessionHigh, xloc=xloc.bar_index, yloc=yloc.price, text=sRevUpMagnitudeText, style=label.style_label_lower_left, textcolor=color.new(color.white, 1), color=color.new(color.green, 100), size=size.normal)
        label.new(x=bar_index[btPeriod] + sessionOffset, y=sessionLow[btPeriod], xloc=xloc.bar_index, yloc=yloc.price, text=sRevUpWrText, style=label.style_label_up, textcolor=color.new(color.white, 1), color=color.new(color.green, 100), size=size.normal)

    //If reversal occurs, plot label
    if revsUp > revsUp[1] 
        label.new(x=bar_index + sessionOffset, y=sessionLow, xloc=xloc.bar_index, yloc=yloc.price, text=revUpCountText, style=label.style_label_up, textcolor=color.new(color.white, 1), color=color.new(color.green, 100), size=size.normal)

    //If reversal is succesful and session ended 1 period ago, plot line and labels
    if succesfulRevsDown > succesfulRevsDown[1] and sessionEnd(session)[1]
        line.new(bar_index[btPeriod] + sessionOffset, sessionClose[1], bar_index + sessionOffset, sessionLow, xloc=xloc.bar_index, color=color.white, width=1, style=line.style_solid)
        label.new(x=bar_index + sessionOffset, y=sessionLow, xloc=xloc.bar_index, yloc=yloc.price, text=sRevDownMagnitudeText, style=label.style_label_upper_left, textcolor=color.new(color.white, 1), color=color.new(color.red, 100), size=size.normal)
        label.new(x=bar_index[btPeriod] + sessionOffset, y=sessionHigh[btPeriod], xloc=xloc.bar_index, yloc=yloc.price, text=sRevDownWrText, style=label.style_label_down, textcolor=color.new(color.white, 1), color=color.new(color.green, 100), size=size.normal)
   
    //If reversal occurs, plot label
    if revsDown > revsDown[1] 
        label.new(x=bar_index + sessionOffset, y=sessionHigh, xloc=xloc.bar_index, yloc=yloc.price, text=revDownCountText, style=label.style_label_down, textcolor=color.new(color.white, 1), color=color.new(color.green, 100), size=size.normal)

//Session candles using line and box
londonCandles = show_london ? sessionCandles(london, i_londonOffset, recentLondonOpen, recentLondonHigh, recentLondonLow, recentLondonClose, wickUpLondon, engulfUpLondon, wickDownLondon, engulfDownLondon, londonColor, revUpL, revDownL) : na
nyCandles = show_ny ? sessionCandles(ny, i_nyOffset, recentNyOpen, recentNyHigh, recentNyLow, recentNyClose, wickUpNy, engulfUpNy, wickDownNy, engulfDownNy, nyColor, revUpN, revDownN) : na
sydneyCandles = show_sydney ? sessionCandles(sydney, i_sydneyOffset, recentSydneyOpen, recentSydneyHigh, recentSydneyLow, recentSydneyClose, wickUpSydney, engulfUpSydney, wickDownSydney, engulfDownSydney, sydneyColor, revUpS, revDownS) : na
tokyoCandles = show_tokyo ? sessionCandles(tokyo, i_tokyoOffset, recentTokyoOpen, recentTokyoHigh, recentTokyoLow, recentTokyoClose, wickUpTokyo, engulfUpTokyo, wickDownTokyo, engulfDownTokyo, tokyoColor, revUpT, revDownT) : na

//Session candles using plotcandle()
[sessionEndLondonO, sessionEndLondonH, sessionEndLondonL, sessionEndLondonC] = sessionCandles2(london, i_londonOffset, show_london, recentLondonOpen, recentLondonHigh, recentLondonLow, recentLondonClose) 
[sessionEndNyO, sessionEndNyH, sessionEndNyL, sessionEndNyC] = sessionCandles2(ny, i_nyOffset, show_ny, recentNyOpen, recentNyHigh, recentNyLow, recentNyClose) 
[sessionEndSydneyO, sessionEndSydneyH, sessionEndSydneyL, sessionEndSydneyC] = sessionCandles2(sydney, i_sydneyOffset, show_sydney, recentSydneyOpen, recentSydneyHigh, recentSydneyLow, recentSydneyClose) 
[sessionEndTokyoO, sessionEndTokyoH, sessionEndTokyoL, sessionEndTokyoC] = sessionCandles2(tokyo, i_tokyoOffset, show_tokyo, recentTokyoOpen, recentTokyoHigh, recentTokyoLow, recentTokyoClose) 

//Session reversal backtest visuals
londonBtVisuals = i_highlightBt == "Session #1" and show_london ? 
 sessionReversalBtVisuals(london, recentLondonHigh, recentLondonLow, recentLondonClose, sRevUpsLondon, sRevDownsLondon, revUpsLondon, revDownsLondon, LondonRevUpWr, LondonRevUpMagnitude, LondonRevDownWr, LondonRevDownMagnitude, i_londonOffset) : na

nyBtVisuals = i_highlightBt == "Session #2" and show_ny ? 
 sessionReversalBtVisuals(ny, recentNyHigh, recentNyLow, recentNyClose, sRevUpsNy, sRevDownsNy, revUpsNy, revDownsNy, NyRevUpWr, NyRevUpMagnitude, NyRevDownWr, NyRevDownMagnitude, i_nyOffset) : na

sydneyBtVisuals = i_highlightBt == "Session #3" and show_sydney ? 
 sessionReversalBtVisuals(sydney, recentSydneyHigh, recentSydneyLow, recentSydneyClose, sRevUpsSydney, sRevDownsSydney, revUpsSydney, revDownsSydney, SydneyRevUpWr, SydneyRevUpMagnitude, SydneyRevDownWr, SydneyRevDownMagnitude, i_sydneyOffset) : na

tokyoBtVisuals = i_highlightBt == "Session #4" and show_tokyo ? 
 sessionReversalBtVisuals(tokyo, recentTokyoHigh, recentTokyoLow, recentTokyoClose, sRevUpsTokyo, sRevDownsTokyo, revUpsTokyo, revDownsTokyo, TokyoRevUpWr, TokyoRevUpMagnitude, TokyoRevDownWr, TokyoRevDownMagnitude, i_tokyoOffset) : na


//                      Plots 

//MA
plot(hybridSma, color=maCol, title="Session moving average")

//Regular session candle using plotcandle() function.
plotcandle(sessionEndLondonO, sessionEndLondonH, sessionEndLondonL, sessionEndLondonC, color=londonColor)
plotcandle(sessionEndNyO, sessionEndNyH, sessionEndNyL, sessionEndNyC, color=nyColor)
plotcandle(sessionEndSydneyO, sessionEndSydneyH, sessionEndSydneyL, sessionEndSydneyC, color=sydneyColor)
plotcandle(sessionEndTokyoO, sessionEndTokyoH, sessionEndTokyoL, sessionEndTokyoC, color=tokyoColor)

//Session period background highlights
bgcolor(i_highlightSession == true and show_london == true and sessionOn(london) ? color.new(i_londonColSession, 90) : na)
bgcolor(i_highlightSession == true and show_london == true and sessionStart(london) ? color.new(i_londonColSession, 50) : na)
bgcolor(i_highlightSession == true and show_london == true and sessionEnd(london) ? color.new(i_londonColSession, 50) : na)

bgcolor(i_highlightSession == true and show_ny == true and sessionOn(ny) ? color.new(i_nyColSession, 90) : na)
bgcolor(i_highlightSession == true and show_ny == true and sessionStart(ny) ? color.new(i_nyColSession, 50) : na)
bgcolor(i_highlightSession == true and show_ny == true and sessionEnd(ny) ? color.new(i_nyColSession, 50) : na)

bgcolor(i_highlightSession == true and show_sydney == true and sessionOn(sydney) ? color.new(i_sydneyColSession, 90) : na)
bgcolor(i_highlightSession == true and show_sydney == true and sessionStart(sydney) ? color.new(i_sydneyColSession, 50) : na)
bgcolor(i_highlightSession == true and show_sydney == true and sessionEnd(sydney) ? color.new(i_sydneyColSession, 50) : na)

bgcolor(i_highlightSession == true and show_tokyo == true and sessionOn(tokyo) ? color.new(i_tokyoColSession, 90) : na)
bgcolor(i_highlightSession == true and show_tokyo == true and sessionStart(tokyo) ? color.new(i_tokyoColSession, 50) : na)
bgcolor(i_highlightSession == true and show_tokyo == true and sessionEnd(tokyo) ? color.new(i_tokyoColSession, 50) : na)

//Hide chart
barcolor(i_hideChart == true ? color.new(color.white, 100) : na)


//                      Alerts

//Confirmed session candle closes
alertcondition(sessionEnd(london)[1], "Confirmed session #1 candle close", "Confirmed session #1 candle close detected")
alertcondition(sessionEnd(ny)[1], "Confirmed session #2 candle close", "Confirmed session #2 candle close detected")
alertcondition(sessionEnd(sydney)[1], "Confirmed session #3 candle close", "Confirmed session #3 candle close detected")
alertcondition(sessionEnd(tokyo)[1], "Confirmed session #4 candle close", "Confirmed session #4 candle close detected")

//Confirmed any session candle close
anySessionClose = sessionEnd(london)[1] or sessionEnd(ny)[1] or sessionEnd(sydney)[1] or sessionEnd(tokyo)[1]
alertcondition(anySessionClose, "Confirmed session (any) candle close", "Confirmed session (any) candle close detected")

//Confirmed session reversals up
alertcondition(revUpsLondon > revUpsLondon[1], "Confirmed session #1 reversal up", "Confirmed session #1 reversal up detected")
alertcondition(revUpsNy > revUpsNy[1], "Confirmed session #2 reversal up", "Confirmed session #2 reversal up detected")
alertcondition(revUpsSydney > revUpsSydney[1], "Confirmed session #3 reversal up", "Confirmed session #3 reversal up detected")
alertcondition(revUpsTokyo > revUpsTokyo[1], "Confirmed session #4 reversal up", "Confirmed session #4 reversal up detected")

//Confirmed any session reversal up
anySessionRevUp = revUpsLondon > revUpsLondon[1] or revUpsNy > revUpsNy[1] or revUpsSydney > revUpsSydney[1] or revUpsTokyo > revUpsTokyo[1]
alertcondition(anySessionRevUp, "Confirmed session (any) reversal up", "Confirmed session (any) reversal up detected")

//Confirmed session reversals down
alertcondition(revDownsLondon > revDownsLondon[1], "Confirmed session #1 reversal down", "Confirmed session #1 reversal down detected")
alertcondition(revDownsNy > revDownsNy[1], "Confirmed session #2 reversal down", "Confirmed session #2 reversal down detected")
alertcondition(revDownsSydney > revDownsSydney[1], "Confirmed session #3 reversal down", "Confirmed session #3 reversal down detected")
alertcondition(revDownsTokyo > revDownsTokyo[1], "Confirmed session #4 reversal down", "Confirmed session #4 reversal down detected")

//Confirmed any session reversal down
anySessionRevDown = revDownsLondon > revDownsLondon[1] or revDownsNy > revDownsNy[1] or revDownsSydney > revDownsSydney[1] or revDownsTokyo > revDownsTokyo[1]
alertcondition(anySessionRevDown, "Confirmed session (any) candle reversal down", "Confirmed session (any) reversal down detected")

//Confirmed any session reversal up/down
alertcondition(anySessionRevDown or anySessionRevUp, "Confirmed session (any) reversal up/down", "Confirmed session (any) reversal up/down detected")


//                      Backtest table

//Initialize backtest table and timeframe notification table
var activeTable = table.new(position = position.top_right, columns = 50, rows = 50, bgcolor = color.rgb(29, 29, 29), border_width = 3, border_color = color.new(color.white, 100))
var notifyTable = table.new(position = position.bottom_right, columns = 50, rows = 50, bgcolor = color.new(color.white, 100), border_width = 3)

//Populate backtest table cells
table.cell(table_id = activeTable, column = 0, row = 0, text = revUpLText + "\n" + "⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯" + "\n" + revDownLText, bgcolor=color.rgb(48, 48, 48), text_color =  color.white)
table.cell(table_id = activeTable, column = 1, row = 0, text = revUpNText + "\n" + "⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯" + "\n" + revDownNText, bgcolor=color.rgb(48, 48, 48), text_color =  color.white)
table.cell(table_id = activeTable, column = 0, row = 2, text = revUpSText + "\n" + "⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯" + "\n" + revDownSText, bgcolor=color.rgb(48, 48, 48), text_color = color.white)
table.cell(table_id = activeTable, column = 1, row = 2, text = revUpTText + "\n" + "⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯" + "\n" + revDownTText, bgcolor=color.rgb(48, 48, 48), text_color = color.white)

//Populate timeframe notification table if applicable
table.cell(table_id = notifyTable, column = 0, row = 0, text = "" + tfNotification, text_color=color.yellow, bgcolor = color.new(color.white, 100))