Session Volatility Indicator

What Is This Script?

This script calculates the average volatility of trading sessions.

You specify a start date, an end date, and a session time (eg. market open, Asian session etc)

The script then scans through all the price action on your chart and calculates the average price movement during that specified period.

What Is It For?

I created this script for my own purposes when developing certain strategies and testing certain ideas.

The purpose of this script is to give you an idea of how much price tends to move during certain times of day for certain markets.

You could think of it as a “session’s average true range”.

In crypto and forex this might be how much price tends to move on certain pairs during the Australian/Asian session, or the European session, or the overnight U.S. session etc.

In stocks this might be how much a symbol tends to move during the first hour of the day or the last hour of the day.

The point of calculating this information is for better understanding how markets move during certain times of day.

It’s not a perfect science obviously since some days can be wilder than others depending on what fundamental events are developing, but it’s useful information to have for times when there are no expected volatility-inducing events.

This info can help with optimizing targets and stop loss placement for certain day-trading strategies, and just generally getting an idea of what kinds of moves you might reasonably expect out of overnight positions or certain times of day etc – or at least that’s what I use it for.

How It Works

Once you specify a start date, an end date and a time session, the script will scan through all the bars on your chart and calculate the price range within that time session and calculate the average max price move for that time of day.

It saves the highest high of the range and the lowest low of the range, and then determines the difference between the two prices.

It then compares that number to the average to give you a % of the current average range.

Indicator Values

The indicator outputs a handful of values onto your chart. This is what the colors correspond to:

Top-Right Box: The average price range during the given time of day over historical price action.

Green Number: The recent session’s highest price.
Red Number: The recent session’s lowest price.
Purple Number: The recent session’s price range (high – low).
Orange Number: The recent session’s range ÷ the average (outputs a relative % of the average).


Hover your mouse over the “i” symbol to get more information on the script’s settings, but here’s a brief description:

Start Date:
The date to begin calculating from (set to 1000 by default so it scans the entire chart).

End Date:
The date to stop calculating (set to 2099 by default so it scans the entire chart)

Time Session:
This is the time session during the day that you want to analyze.

Color Background:
If turned on, this setting changes the background color to highlight the session.

Pine Script Basics Course Pine Script Mastery Course

Source Code

// This source code is subject to the terms of the Mozilla Public License 2.0 at
// © ZenAndTheArtOfTrading /
// Last Updated: 3rd June, 2021
// @version=4
study("[2021] Session Volatility", overlay=true)

// Get user input
startDate = input(title="Start Date", defval=timestamp("01 Jan 1000 13:30 +0000"), type=input.time, tooltip="Date & time to begin analysis")
endDate = input(title="End Date", defval=timestamp("1 Jan 2099 19:30 +0000"), type=input.time, tooltip="Date & time to stop analysis")
timeSession = input(title="Time Session To Analyze", type=input.session, defval="0600-0915", tooltip="Time session to analyze volatility")
colorBG = input(title="Color Background?", type=input.bool, defval=true, tooltip="Change the background color based on whether the current bar falls within the given session?")

// Custom function to convert forex pips into whole numbers
atr = atr(14)
toWhole(number) =>
    if syminfo.type == "forex" // This method only works on forex pairs
        return = atr < 1.0 ? (number / syminfo.mintick) / 10 : number
        return := atr >= 1.0 and atr < 100.0 and syminfo.currency == "JPY" ? return * 100 : return

// This function returns true if the current bar falls within the given time session (:1234567 is to include all weekdays)
inSession(sess) => na(time(timeframe.period, sess + ":1234567")) == false and time >= startDate and time <= endDate
bgcolor(inSession(timeSession) and colorBG ? color.rgb(0, 0, 255, 90) : na)

// Check if a new session has begun
var withinSession = false
if inSession(timeSession) and not inSession(timeSession)[1]
    withinSession := true
// Declare required variables for analysis
var recentMovement = 0.0
var totalMovement = 0.0
var sessionsCounted = 0
var MAXINT = 2147483647.0
var highestHigh = -1.0
var lowestLow = MAXINT

// Check if a session has ended
if not inSession(timeSession) and inSession(timeSession)[1]
    withinSession := false
    // Analyze movement
    recentMovement := highestHigh - lowestLow
    totalMovement := totalMovement + recentMovement
    sessionsCounted := sessionsCounted + 1
    // Reset stored high/low
    highestHigh := -1.0
    lowestLow := MAXINT
// Get highest high and lowest low of current session
if withinSession
    if high > highestHigh
        highestHigh := high
    if low < lowestLow
        lowestLow := low

// Calculate average pip movement over all analyzed sessions
averageMovement = totalMovement / sessionsCounted

// Plot volatility info
plot(withinSession ? highestHigh : na, color=color.rgb(0,255,0,100), style=plot.style_linebr, title="Highest High")
plot(withinSession ? lowestLow : na, color=color.rgb(255,0,0,100), style=plot.style_linebr, title="Lowest Low")
plot(not withinSession ? recentMovement : na, color=color.rgb(128,0,128,100), title="Recent Change")
plot(not withinSession ? recentMovement / averageMovement : na, color=color.rgb(255,165,0,100), title="Recent Change ÷ Average Change")

// Create an output table to display average pip change
var table resultTable =, 3, 2, border_width = 3)
f_fillCell(_table, _column, _row, _value, _text) =>
    _c_color =
    _transp = 80
    _cellText = tostring(_value, "#.#####") + "\n" + _text
    table.cell(_table, _column, _row, _cellText, bgcolor =, _transp), text_color = _c_color)

// Only fill the cell data on the last bar on the chart (for optimization)
if barstate.islast
    f_fillCell(resultTable, 0, 0, toWhole(averageMovement), syminfo.type == "forex" ? "Pip Change" : "Price Change")

Last Updated: 1st October, 2020

Free Premium Charts!

0 0 vote
Article Rating
Notify of
Inline Feedbacks
View all comments