Analyzing Afternoon vs. Morning Ranges in Pine Script

This lesson explores a script I made to validate a hypothesis posed by a trader at SMB Capital regarding morning/afternoon ranges and trend-continuation.

Check out my other free lessons!

Source Code

// This Pine Script™ code is subject to the terms of the Mozilla Public License 2.0 at https://mozilla.org/MPL/2.0/
// © ZenAndTheArtOfTrading
// @version=5
indicator("RANGE CONTINUATION TEST", overlay=true)

// Get user input
Session1    = input.session(title="First Session", defval="0930-1200", display=display.none)
Session2    = input.session(title="Second Session", defval="1200-1600", display=display.none)
StartDate   = input.time(timestamp("1 January 2000"))
EndDate     = input.time(timestamp("1 January 2030"))

// InSession() determines if a price bar falls inside the specified session on the given weekdays
InSession(sess) => na(time(timeframe.period, sess + ":1234567")) == false

// Highlight sessions
bgcolor(InSession(Session1) ? color.new(color.green, 90) : na)
bgcolor(InSession(Session2) ? color.new(color.red,   90) : na)

// Get higher timeframe data
float yesterdayHigh  = request.security(syminfo.tickerid, "D", high [barstate.isrealtime ? 1 : 0])[barstate.isrealtime ? 0 : 1]
float yesterdayLow   = request.security(syminfo.tickerid, "D", low  [barstate.isrealtime ? 1 : 0])[barstate.isrealtime ? 0 : 1]

// Detect session start
bool startSession1 = not InSession(Session1)[1] and InSession(Session1) and time >= StartDate
bool startSession2 = not InSession(Session2)[1] and InSession(Session2) and time >= StartDate

// Analysis variables
var int bullishRangeExpansionCount  = 0
var int bearishRangeExpansionCount  = 0
var int bullishRangeContinuation    = 0
var int bearishRangeContinuation    = 0

// First session variables
var int   sessionIndex1         = na
var float dayOpenPrice          = na
var float sessionLow1           = na 
var float sessionHigh1          = na 

// Second session variables 
var int   sessionIndex2         = na
var float sessionLow2           = na 
var float sessionHigh2          = na 
var bool  rangeExpansion        = false
var bool  checkContinuation     = false

// Analysis variables
var bool  yesterdayWasBullish   = false
var bool  monitoring1stSession  = false 

// 1. Detect start of day's first session
// 2. Save previously monitored session's range 
// 3. Save yesterday's close and today's open 
// 4. Reset and start monitoring today's first session
// 5. Start of day's first session is also end of yesterday's second session, so analyze data
if startSession1
    bool bullishClose = close[1] > dayOpenPrice
    line.new(sessionIndex1, dayOpenPrice, bar_index - 1, close[1], color=bullishClose ? color.green : color.red)
    line.new(sessionIndex2, sessionHigh2 + (sessionHigh1 - sessionHigh2), sessionIndex2, sessionLow2 + (sessionHigh1 - sessionHigh2), color=color.red, width=2)
    if (sessionHigh2 - sessionLow2) >= (sessionHigh1 - sessionLow1)
        rangeExpansion := true
        label.new(bar_index - 1, high[1], (bullishClose ? "BULLISH" : "BEARISH") + " RANGE\nEXPANSION!", textcolor=color.white)
    else
        rangeExpansion := false

    sessionIndex1           := bar_index
    sessionLow1             := na 
    sessionHigh1            := na
    monitoring1stSession    := true
    dayOpenPrice            := open

    if checkContinuation // We had range expansion yesterday - check if we continued in trend
        if yesterdayWasBullish // Yesterday was a bullish day - check for bullish continuation
            bool bullishContinuation = close[1] > yesterdayHigh
            bullishRangeExpansionCount := bullishRangeExpansionCount + 1
            line.new(bar_index, yesterdayHigh, bar_index - 20, yesterdayHigh, color=bullishContinuation ? color.green : color.gray)
            if bullishContinuation
                bullishRangeContinuation := bullishRangeContinuation + 1
                label.new(bar_index - 1, high[1], "BULLISH CONTINUATION!", color=color.green, textcolor=color.white)
        else
            bool bearishContinuation = close[1] < yesterdayLow
            bearishRangeExpansionCount := bearishRangeExpansionCount + 1
            line.new(bar_index, yesterdayLow, bar_index - 20, yesterdayLow, color=bearishContinuation ? color.red : color.gray)
            if bearishContinuation
                bearishRangeContinuation := bearishRangeContinuation + 1
                label.new(bar_index - 1, high[1], "BEARISH CONTINUATION!", color=color.red, textcolor=color.white)

    checkContinuation       := false
    yesterdayWasBullish     := close[1] > dayOpenPrice[1]

// Track first session's range 
if monitoring1stSession
    if na(sessionLow1) or low < sessionLow1
        sessionLow1 := low 
    if na(sessionHigh1) or high > sessionHigh1
        sessionHigh1 := high

// 1. Detect start of day's second session
// 2. Reset and start monitoring today's second session
if startSession2
    line.new(bar_index - 1, sessionHigh1, bar_index - 1, sessionLow1, color=color.green, width=2)
    monitoring1stSession    := false
    sessionLow2             := na 
    sessionHigh2            := na
    sessionIndex2           := bar_index
    checkContinuation       := rangeExpansion

// Track second session's range 
if not monitoring1stSession
    if na(sessionLow2) or low < sessionLow2
        sessionLow2 := low 
    if na(sessionHigh2) or high > sessionHigh2
        sessionHigh2 := high

// Analysis of range expansion data
int totalRangeExpansionCount    = bullishRangeExpansionCount + bearishRangeExpansionCount
float totalExpansionChance      = ((bullishRangeContinuation + bearishRangeContinuation) / totalRangeExpansionCount) * 100
float bullishExpansionChance    = (bullishRangeContinuation / bullishRangeExpansionCount) * 100
float bearishExpansionChance    = (bearishRangeContinuation / bearishRangeExpansionCount) * 100

// Debug data window plots 
plot(yesterdayWasBullish ? 1 : 0, title="BULLISH DAY?", display=display.data_window)
plotshape(rangeExpansion ? 1 : na, "PREVIOUS EXPANSION?", shape.square, location.top, color=yesterdayWasBullish ? color.green : color.red)
plot(na, title="-----------", display=display.data_window)
plot(totalRangeExpansionCount, title="TOTAL RANGE EXPANSION", display=display.data_window)
plot(bullishRangeExpansionCount, title="TOTAL BULLISH RANGE EXP", display=display.data_window)
plot(bullishRangeContinuation, title="TOTAL BULLISH RANGE CNT", display=display.data_window)
plot(bearishRangeExpansionCount, title="TOTAL BEARISH RANGE EXP", display=display.data_window)
plot(bearishRangeContinuation, title="TOTAL BEARISH RANGE CNT", display=display.data_window)
plot(na, title="-----------", display=display.data_window)
plot(totalExpansionChance, title="Total Continuation Chance", display=display.data_window)
plot(bullishExpansionChance, title="Bullish Continuation Chance", display=display.data_window)
plot(bearishExpansionChance, title="Bearish Continuation Chance", display=display.data_window)

// Create stat display table
if barstate.islast 
    var table myTable = table.new(position.top_right, 5, 2, border_width=1)

    txt1 = "Total Range Expansion\n"        + str.tostring(totalRangeExpansionCount)
    txt2 = "Total Continuation Chance\n"    + str.tostring(totalExpansionChance, "#.##") + "%"
    txt3 = "Bullish Days w/ Expansion\n"    + str.tostring(bullishRangeExpansionCount)
    txt4 = "Bullish Continuation Chance\n"  + str.tostring(bullishExpansionChance, "#.##") + "%"
    txt5 = "Bearish Days w/ Expansion\n"    + str.tostring(bearishRangeExpansionCount)
    txt6 = "Bearish Continuation Chance\n"  + str.tostring(bearishExpansionChance, "#.##") + "%"

    table.cell(myTable, 0, 0, text=txt1, bgcolor=color.black, text_color=color.white)
    table.cell(myTable, 0, 1, text=txt2, bgcolor=color.black, text_color=color.white)
    table.cell(myTable, 1, 0, text=txt3, bgcolor=color.black, text_color=color.white)
    table.cell(myTable, 1, 1, text=txt4, bgcolor=color.black, text_color=color.white)
    table.cell(myTable, 2, 0, text=txt5, bgcolor=color.black, text_color=color.white)
    table.cell(myTable, 2, 1, text=txt6, bgcolor=color.black, text_color=color.white)