Indicator · Pine Script v6 · On the chart

Session Standard Deviations with Stats

by TamaraKGreen· code credited to notprofessorgreen

This indicator calculates and plots standard deviation projection levels based on historical session ranges.

View on GitHub

GitHub stars46on the repository
Pine versionv6current
Lines of code786
Settings35inputs
LicenceNone statedfile: MPL-2.0
Last update7 weeks ago4 Sep 2026

Add It to Your TradingView Chart

  1. 1Copy the scriptPress Copy script above, or the copy button on the code below.
  2. 2Open the Pine EditorOpen a TradingView chart and choose Pine Editor at the bottom of the screen.
  3. 3Paste over the templateCreate a new indicator, select all of the template text and paste. Press Save and give it a name.
  4. 4Add it to the chartPress Add to chart, then adjust the settings with the gear icon next to its name.

Source Code

786 lines of Pine Script v6 from Session standard deviations with stats.txt, published under MPL-2.0 (file header). Keep the author's credit and licence lines when you reuse it.

Session standard deviations with stats.txt
// This Pine Script™ code is subject to the terms of the Mozilla Public License 2.0 at https://mozilla.org/MPL/2.0/
// © notprofessorgreen

//@version=6
indicator("Session standard deviations with stats", overlay=true)

// === Input Groups ===
var g_session_settings = "Session Settings"
var g_projections = "Projection Settings"
var g_display = "Display Settings"

// === Inputs ===
session_time = input.string("0000-1600", "Session Time (HHMM-HHMM)", group=g_session_settings, tooltip="Specify the time range for the session (e.g., 0400-0800 for 4:00 AM to 8:00 AM)")
time_zone = input.string("America/New_York", "Time Zone", options=["America/New_York", "America/Chicago", "America/Los_Angeles", "Europe/London", "Asia/Tokyo"], group=g_session_settings, tooltip="Select the timezone for session timing")
hist_sessions = input.int(20, "Historical Sessions", minval=1, group=g_session_settings, tooltip="Number of past sessions to calculate standard deviation")
projection_offset = input.int(0, "Offset (Bars)", minval=0, group=g_display, tooltip="Number of bars to extend lines beyond the current bar")

// Open Line Settings
show_open = input.bool(true, "s0.00", inline="open", group=g_display, tooltip="Toggle visibility of the session open line")
open_line_style = input.string("Dotted", "Style", options=["Solid", "Dashed", "Dotted"], inline="open", group=g_display, tooltip="Line style for session open line")
open_line_color = input.color(color.gray, "Color", inline="open", group=g_display, tooltip="Color for session open line")
open_line_width = input.int(1, "Width", minval=1, maxval=4, inline="open", group=g_display, tooltip="Line width for session open line")

// Label Size
label_size = input.string("Small", "Label Size", options=["Tiny", "Small", "Normal", "Large"], group=g_display, tooltip="Size of labels for projections and open line")

// Projection Settings
show_05_sd = input.bool(true, "0.5 SD", inline="05", group=g_projections, tooltip="Toggle visibility of ±0.5 SD projection lines")
projection_05_style = input.string("Dotted", "Style", options=["Solid", "Dashed", "Dotted"], inline="05", group=g_projections)
projection_05_color = input.color(color.red, "Color", inline="05", group=g_projections)
projection_05_width = input.int(1, "Width", minval=1, maxval=4, inline="05", group=g_projections)

show_10_sd = input.bool(true, "1.0 SD", inline="10", group=g_projections, tooltip="Toggle visibility of ±1.0 SD projection lines")
projection_10_style = input.string("Dotted", "Style", options=["Solid", "Dashed", "Dotted"], inline="10", group=g_projections)
projection_10_color = input.color(color.blue, "Color", inline="10", group=g_projections)
projection_10_width = input.int(1, "Width", minval=1, maxval=4, inline="10", group=g_projections)

show_20_sd = input.bool(true, "2.0 SD", inline="20", group=g_projections, tooltip="Toggle visibility of ±2.0 SD projection lines")
projection_20_style = input.string("Dotted", "Style", options=["Solid", "Dashed", "Dotted"], inline="20", group=g_projections)
projection_20_color = input.color(color.gray, "Color", inline="20", group=g_projections)
projection_20_width = input.int(1, "Width", minval=1, maxval=4, inline="20", group=g_projections)

show_30_sd = input.bool(true, "3.0 SD", inline="30", group=g_projections, tooltip="Toggle visibility of ±3.0 SD projection lines")
projection_30_style = input.string("Dotted", "Style", options=["Solid", "Dashed", "Dotted"], inline="30", group=g_projections)
projection_30_color = input.color(color.gray, "Color", inline="30", group=g_projections)
projection_30_width = input.int(1, "Width", minval=1, maxval=4, inline="30", group=g_projections)

show_40_sd = input.bool(true, "4.0 SD", inline="40", group=g_projections, tooltip="Toggle visibility of ±4.0 SD projection lines")
projection_40_style = input.string("Dotted", "Style", options=["Solid", "Dashed", "Dotted"], inline="40", group=g_projections)
projection_40_color = input.color(color.gray, "Color", inline="40", group=g_projections)
projection_40_width = input.int(1, "Width", minval=1, maxval=4, inline="40", group=g_projections)

// === Helper Functions ===
get_line_style(style_string) =>
    style_string == "Solid" ? line.style_solid : style_string == "Dashed" ? line.style_dashed : line.style_dotted

get_label_size(size_string) =>
    size_string == "Tiny" ? size.tiny : size_string == "Small" ? size.small : size_string == "Normal" ? size.normal : size.large

get_label_style() =>
    label.style_label_left

is_newbar(sess) =>
    t = time('D', sess, timezone=time_zone)
    na(t[1]) and not na(t) or t[1] < t

is_session(sess) =>
    not na(time('D', sess, timezone=time_zone))

get_weekday() =>
    day = dayofweek(time, time_zone)
    day == dayofweek.monday ? "Monday" : day == dayofweek.tuesday ? "Tuesday" : day == dayofweek.wednesday ? "Wednesday" : day == dayofweek.thursday ? "Thursday" : day == dayofweek.friday ? "Friday" : "Other"

get_table_position(pos) =>
    switch pos
        "Top Left" => position.top_left
        "Top Center" => position.top_center
        "Top Right" => position.top_right
        "Middle Left" => position.middle_left
        "Middle Center" => position.middle_center
        "Middle Right" => position.middle_right
        "Bottom Left" => position.bottom_left
        "Bottom Center" => position.bottom_center
        "Bottom Right" => position.bottom_right

get_table_text_size(size) =>
    switch size
        "Tiny" => size.tiny
        "Small" => size.small
        "Normal" => size.normal
        "Large" => size.large



// === Persistent Variables ===
var float[] historical_data = array.new_float(0)
var int[] session_sizes = array.new_int(0)
var float current_open = na
var float[] current_data = array.new_float(0)
var int session_start_bar = na
var float sd = na

// Define Lines and Labels (Created Once)
var line open_line = na
var label open_label = na
var line upper_05_line = na
var label upper_05_label = na
var line lower_05_line = na
var label lower_05_label = na
var line upper_10_line = na
var label upper_10_label = na
var line lower_10_line = na
var label lower_10_label = na
var line upper_20_line = na
var label upper_20_label = na
var line lower_20_line = na
var label lower_20_label = na
var line upper_30_line = na
var label upper_30_label = na
var line lower_30_line = na
var label lower_30_label = na
var line upper_40_line = na
var label upper_40_label = na
var line lower_40_line = na
var label lower_40_label = na

// Initialize Lines and Labels at Script Start
if bar_index == 0
    open_line := line.new(bar_index, na, bar_index + 1, na, color=open_line_color, style=get_line_style(open_line_style), width=open_line_width)
    open_label := label.new(bar_index, na, "s0.00", style=get_label_style(), color=color.new(color.black, 100), textcolor=open_line_color, size=get_label_size(label_size))
    
    upper_05_line := line.new(bar_index, na, bar_index + 1, na, color=projection_05_color, style=get_line_style(projection_05_style), width=projection_05_width)
    upper_05_label := label.new(bar_index, na, "+0.5", style=get_label_style(), color=color.new(color.black, 100), textcolor=projection_05_color, size=get_label_size(label_size))
    lower_05_line := line.new(bar_index, na, bar_index + 1, na, color=projection_05_color, style=get_line_style(projection_05_style), width=projection_05_width)
    lower_05_label := label.new(bar_index, na, "-0.5", style=get_label_style(), color=color.new(color.black, 100), textcolor=projection_05_color, size=get_label_size(label_size))
    
    upper_10_line := line.new(bar_index, na, bar_index + 1, na, color=projection_10_color, style=get_line_style(projection_10_style), width=projection_10_width)
    upper_10_label := label.new(bar_index, na, "+1.0", style=get_label_style(), color=color.new(color.black, 100), textcolor=projection_10_color, size=get_label_size(label_size))
    lower_10_line := line.new(bar_index, na, bar_index + 1, na, color=projection_10_color, style=get_line_style(projection_10_style), width=projection_10_width)
    lower_10_label := label.new(bar_index, na, "-1.0", style=get_label_style(), color=color.new(color.black, 100), textcolor=projection_10_color, size=get_label_size(label_size))
    
    upper_20_line := line.new(bar_index, na, bar_index + 1, na, color=projection_20_color, style=get_line_style(projection_20_style), width=projection_20_width)
    upper_20_label := label.new(bar_index, na, "+2.0", style=get_label_style(), color=color.new(color.black, 100), textcolor=projection_20_color, size=get_label_size(label_size))
    lower_20_line := line.new(bar_index, na, bar_index + 1, na, color=projection_20_color, style=get_line_style(projection_20_style), width=projection_20_width)
    lower_20_label := label.new(bar_index, na, "-2.0", style=get_label_style(), color=color.new(color.black, 100), textcolor=projection_20_color, size=get_label_size(label_size))
    
    upper_30_line := line.new(bar_index, na, bar_index + 1, na, color=projection_30_color, style=get_line_style(projection_30_style), width=projection_30_width)
    upper_30_label := label.new(bar_index, na, "+3.0", style=get_label_style(), color=color.new(color.black, 100), textcolor=projection_30_color, size=get_label_size(label_size))
    lower_30_line := line.new(bar_index, na, bar_index + 1, na, color=projection_30_color, style=get_line_style(projection_30_style), width=projection_30_width)
    lower_30_label := label.new(bar_index, na, "-3.0", style=get_label_style(), color=color.new(color.black, 100), textcolor=projection_30_color, size=get_label_size(label_size))
    
    upper_40_line := line.new(bar_index, na, bar_index + 1, na, color=projection_40_color, style=get_line_style(projection_40_style), width=projection_40_width)
    upper_40_label := label.new(bar_index, na, "+4.0", style=get_label_style(), color=color.new(color.black, 100), textcolor=projection_40_color, size=get_label_size(label_size))
    lower_40_line := line.new(bar_index, na, bar_index + 1, na, color=projection_40_color, style=get_line_style(projection_40_style), width=projection_40_width)
    lower_40_label := label.new(bar_index, na, "-4.0", style=get_label_style(), color=color.new(color.black, 100), textcolor=projection_40_color, size=get_label_size(label_size))

// === Main Logic ===
if is_newbar(session_time)
    if not na(current_open) and array.size(current_data) > 0
        for i = 0 to array.size(current_data) - 1
            array.push(historical_data, array.get(current_data, i))
        array.push(session_sizes, array.size(current_data))
    
    if array.size(session_sizes) > hist_sessions
        while array.size(session_sizes) > hist_sessions
            if array.size(session_sizes) > 0
                int session_size = array.get(session_sizes, 0)
                for i = 1 to session_size
                    if array.size(historical_data) > 0
                        array.shift(historical_data)
                array.shift(session_sizes)
            else
                break
    
    current_open := open
    current_data := array.new_float(0)
    session_start_bar := bar_index
    sd := array.size(historical_data) >= 2 ? array.stdev(historical_data) : na
    
    if not na(current_open) and not na(sd)
        if show_open
            line.set_xy1(open_line, session_start_bar, current_open)
            line.set_xy2(open_line, session_start_bar + projection_offset + 5, current_open)
            label.set_xy(open_label, session_start_bar + projection_offset + 5, current_open)
        
        if show_05_sd
            line.set_xy1(upper_05_line, session_start_bar, current_open + 0.5 * sd)
            line.set_xy2(upper_05_line, session_start_bar + projection_offset, current_open + 0.5 * sd)
            line.set_xy1(lower_05_line, session_start_bar, current_open - 0.5 * sd)
            line.set_xy2(lower_05_line, session_start_bar + projection_offset, current_open - 0.5 * sd)
            label.set_xy(upper_05_label, session_start_bar + projection_offset, current_open + 0.5 * sd)
            label.set_xy(lower_05_label, session_start_bar + projection_offset, current_open - 0.5 * sd)
        
        if show_10_sd
            line.set_xy1(upper_10_line, session_start_bar, current_open + 1.0 * sd)
            line.set_xy2(upper_10_line, session_start_bar + projection_offset, current_open + 1.0 * sd)
            line.set_xy1(lower_10_line, session_start_bar, current_open - 1.0 * sd)
            line.set_xy2(lower_10_line, session_start_bar + projection_offset, current_open - 1.0 * sd)
            label.set_xy(upper_10_label, session_start_bar + projection_offset, current_open + 1.0 * sd)
            label.set_xy(lower_10_label, session_start_bar + projection_offset, current_open - 1.0 * sd)
        
        if show_20_sd
            line.set_xy1(upper_20_line, session_start_bar, current_open + 2.0 * sd)
            line.set_xy2(upper_20_line, session_start_bar + projection_offset, current_open + 2.0 * sd)
            line.set_xy1(lower_20_line, session_start_bar, current_open - 2.0 * sd)
            line.set_xy2(lower_20_line, session_start_bar + projection_offset, current_open - 2.0 * sd)
            label.set_xy(upper_20_label, session_start_bar + projection_offset, current_open + 2.0 * sd)
            label.set_xy(lower_20_label, session_start_bar + projection_offset, current_open - 2.0 * sd)
        
        if show_30_sd
            line.set_xy1(upper_30_line, session_start_bar, current_open + 3.0 * sd)
            line.set_xy2(upper_30_line, session_start_bar + projection_offset, current_open + 3.0 * sd)
            line.set_xy1(lower_30_line, session_start_bar, current_open - 3.0 * sd)
            line.set_xy2(lower_30_line, session_start_bar + projection_offset, current_open - 3.0 * sd)
            label.set_xy(upper_30_label, session_start_bar + projection_offset, current_open + 3.0 * sd)
            label.set_xy(lower_30_label, session_start_bar + projection_offset, current_open - 3.0 * sd)
        
        if show_40_sd
            line.set_xy1(upper_40_line, session_start_bar, current_open + 4.0 * sd)
            line.set_xy2(upper_40_line, session_start_bar + projection_offset, current_open + 4.0 * sd)
            line.set_xy1(lower_40_line, session_start_bar, current_open - 4.0 * sd)
            line.set_xy2(lower_40_line, session_start_bar + projection_offset, current_open - 4.0 * sd)
            label.set_xy(upper_40_label, session_start_bar + projection_offset, current_open + 4.0 * sd)
            label.set_xy(lower_40_label, session_start_bar + projection_offset, current_open - 4.0 * sd)

if is_session(session_time)
    array.push(current_data, close - current_open)
    int current_end = bar_index + projection_offset
    if show_open and not na(open_line)
        line.set_x2(open_line, current_end + 3)
        label.set_x(open_label, current_end + 3)
    if not na(sd)
        if show_05_sd
            line.set_x2(upper_05_line, current_end)
            line.set_x2(lower_05_line, current_end)
            label.set_x(upper_05_label, current_end)
            label.set_x(lower_05_label, current_end)
        if show_10_sd
            line.set_x2(upper_10_line, current_end)
            line.set_x2(lower_10_line, current_end)
            label.set_x(upper_10_label, current_end)
            label.set_x(lower_10_label, current_end)
        if show_20_sd
            line.set_x2(upper_20_line, current_end)
            line.set_x2(lower_20_line, current_end)
            label.set_x(upper_20_label, current_end)
            label.set_x(lower_20_label, current_end)
        if show_30_sd
            line.set_x2(upper_30_line, current_end)
            line.set_x2(lower_30_line, current_end)
            label.set_x(upper_30_label, current_end)
            label.set_x(lower_30_label, current_end)
        if show_40_sd
            line.set_x2(upper_40_line, current_end)
            line.set_x2(lower_40_line, current_end)
            label.set_x(upper_40_label, current_end)
            label.set_x(lower_40_label, current_end)

// === Stats Inputs ===
var g_stats = "Stats Settings"
show_stats = input.bool(true, "Show Stats Table", group=g_stats)
table_position = input.string("Top Right", "Table Position", options=["Top Left", "Top Center", "Top Right", "Middle Left", "Middle Center", "Middle Right", "Bottom Left", "Bottom Center", "Bottom Right"], group=g_stats)
table_text_size = input.string("Small", "Text Size", options=["Tiny", "Small", "Normal", "Large"], group=g_stats)

// === Stats Variables ===
var int mon_sessions = 0
var int tue_sessions = 0
var int wed_sessions = 0
var int thu_sessions = 0
var int fri_sessions = 0

var int mon_upper_05_touches = 0
var int mon_lower_05_touches = 0
var int mon_upper_10_touches = 0
var int mon_lower_10_touches = 0
var int mon_upper_20_touches = 0
var int mon_lower_20_touches = 0
var int mon_upper_30_touches = 0
var int mon_lower_30_touches = 0
var int mon_upper_40_touches = 0
var int mon_lower_40_touches = 0

var int tue_upper_05_touches = 0
var int tue_lower_05_touches = 0
var int tue_upper_10_touches = 0
var int tue_lower_10_touches = 0
var int tue_upper_20_touches = 0
var int tue_lower_20_touches = 0
var int tue_upper_30_touches = 0
var int tue_lower_30_touches = 0
var int tue_upper_40_touches = 0
var int tue_lower_40_touches = 0

var int wed_upper_05_touches = 0
var int wed_lower_05_touches = 0
var int wed_upper_10_touches = 0
var int wed_lower_10_touches = 0
var int wed_upper_20_touches = 0
var int wed_lower_20_touches = 0
var int wed_upper_30_touches = 0
var int wed_lower_30_touches = 0
var int wed_upper_40_touches = 0
var int wed_lower_40_touches = 0

var int thu_upper_05_touches = 0
var int thu_lower_05_touches = 0
var int thu_upper_10_touches = 0
var int thu_lower_10_touches = 0
var int thu_upper_20_touches = 0
var int thu_lower_20_touches = 0
var int thu_upper_30_touches = 0
var int thu_lower_30_touches = 0
var int thu_upper_40_touches = 0
var int thu_lower_40_touches = 0

var int fri_upper_05_touches = 0
var int fri_lower_05_touches = 0
var int fri_upper_10_touches = 0
var int fri_lower_10_touches = 0
var int fri_upper_20_touches = 0
var int fri_lower_20_touches = 0
var int fri_upper_30_touches = 0
var int fri_lower_30_touches = 0
var int fri_upper_40_touches = 0
var int fri_lower_40_touches = 0

// First Touch Flags per Session
var bool upper_05_touched = false
var bool lower_05_touched = false
var bool upper_10_touched = false
var bool lower_10_touched = false
var bool upper_20_touched = false
var bool lower_20_touched = false
var bool upper_30_touched = false
var bool lower_30_touched = false
var bool upper_40_touched = false
var bool lower_40_touched = false

// Table Variable
var table stats_table = na

// Initialize Table (11 columns, 10 rows: Headers, Sessions, ±0.5, ±1, ±2, ±3, ±4)
if bar_index == 0
    stats_table := table.new(get_table_position(table_position), 50, 50, bgcolor=color.white, frame_color=color.black, frame_width=1, border_color=color.black, border_width=1)

// === Stats Logic ===
if is_newbar(session_time)
    string current_day = get_weekday()
    if current_day == "Monday"
        mon_sessions := mon_sessions + 1
    else if current_day == "Tuesday"
        tue_sessions := tue_sessions + 1
    else if current_day == "Wednesday"
        wed_sessions := wed_sessions + 1
    else if current_day == "Thursday"
        thu_sessions := thu_sessions + 1
    else if current_day == "Friday"
        fri_sessions := fri_sessions + 1
    
    upper_05_touched := false
    lower_05_touched := false
    upper_10_touched := false
    lower_10_touched := false
    upper_20_touched := false
    lower_20_touched := false
    upper_30_touched := false
    lower_30_touched := false
    upper_40_touched := false
    lower_40_touched := false

if is_session(session_time) and not na(current_open) and not na(sd)
    string current_day = get_weekday()
    
    float upper_05_level = current_open + 0.5 * sd
    float lower_05_level = current_open - 0.5 * sd
    if not upper_05_touched and high >= upper_05_level
        if current_day == "Monday"
            mon_upper_05_touches := mon_upper_05_touches + 1
        else if current_day == "Tuesday"
            tue_upper_05_touches := tue_upper_05_touches + 1
        else if current_day == "Wednesday"
            wed_upper_05_touches := wed_upper_05_touches + 1
        else if current_day == "Thursday"
            thu_upper_05_touches := thu_upper_05_touches + 1
        else if current_day == "Friday"
            fri_upper_05_touches := fri_upper_05_touches + 1
        upper_05_touched := true
    if not lower_05_touched and low <= lower_05_level
        if current_day == "Monday"
            mon_lower_05_touches := mon_lower_05_touches + 1
        else if current_day == "Tuesday"
            tue_lower_05_touches := tue_lower_05_touches + 1
        else if current_day == "Wednesday"
            wed_lower_05_touches := wed_lower_05_touches + 1
        else if current_day == "Thursday"
            thu_lower_05_touches := thu_lower_05_touches + 1
        else if current_day == "Friday"
            fri_lower_05_touches := fri_lower_05_touches + 1
        lower_05_touched := true
    
    float upper_10_level = current_open + 1.0 * sd
    float lower_10_level = current_open - 1.0 * sd
    if not upper_10_touched and high >= upper_10_level
        if current_day == "Monday"
            mon_upper_10_touches := mon_upper_10_touches + 1
        else if current_day == "Tuesday"
            tue_upper_10_touches := tue_upper_10_touches + 1
        else if current_day == "Wednesday"
            wed_upper_10_touches := wed_upper_10_touches + 1
        else if current_day == "Thursday"
            thu_upper_10_touches := thu_upper_10_touches + 1
        else if current_day == "Friday"
            fri_upper_10_touches := fri_upper_10_touches + 1
        upper_10_touched := true
    if not lower_10_touched and low <= lower_10_level
        if current_day == "Monday"
            mon_lower_10_touches := mon_lower_10_touches + 1
        else if current_day == "Tuesday"
            tue_lower_10_touches := tue_lower_10_touches + 1
        else if current_day == "Wednesday"
            wed_lower_10_touches := wed_lower_10_touches + 1
        else if current_day == "Thursday"
            thu_lower_10_touches := thu_lower_10_touches + 1
        else if current_day == "Friday"
            fri_lower_10_touches := fri_lower_10_touches + 1
        lower_10_touched := true
    
    float upper_20_level = current_open + 2.0 * sd
    float lower_20_level = current_open - 2.0 * sd
    if not upper_20_touched and high >= upper_20_level
        if current_day == "Monday"
            mon_upper_20_touches := mon_upper_20_touches + 1
        else if current_day == "Tuesday"
            tue_upper_20_touches := tue_upper_20_touches + 1
        else if current_day == "Wednesday"
            wed_upper_20_touches := wed_upper_20_touches + 1
        else if current_day == "Thursday"
            thu_upper_20_touches := thu_upper_20_touches + 1
        else if current_day == "Friday"
            fri_upper_20_touches := fri_upper_20_touches + 1
        upper_20_touched := true
    if not lower_20_touched and low <= lower_20_level
        if current_day == "Monday"
            mon_lower_20_touches := mon_lower_20_touches + 1
        else if current_day == "Tuesday"
            tue_lower_20_touches := tue_lower_20_touches + 1
        else if current_day == "Wednesday"
            wed_lower_20_touches := wed_lower_20_touches + 1
        else if current_day == "Thursday"
            thu_lower_20_touches := thu_lower_20_touches + 1
        else if current_day == "Friday"
            fri_lower_20_touches := fri_lower_20_touches + 1
        lower_20_touched := true
    
    float upper_30_level = current_open + 3.0 * sd
    float lower_30_level = current_open - 3.0 * sd
    if not upper_30_touched and high >= upper_30_level
        if current_day == "Monday"
            mon_upper_30_touches := mon_upper_30_touches + 1
        else if current_day == "Tuesday"
            tue_upper_30_touches := tue_upper_30_touches + 1
        else if current_day == "Wednesday"
            wed_upper_30_touches := wed_upper_30_touches + 1
        else if current_day == "Thursday"
            thu_upper_30_touches := thu_upper_30_touches + 1
        else if current_day == "Friday"
            fri_upper_30_touches := fri_upper_30_touches + 1
        upper_30_touched := true
    if not lower_30_touched and low <= lower_30_level
        if current_day == "Monday"
            mon_lower_30_touches := mon_lower_30_touches + 1
        else if current_day == "Tuesday"
            tue_lower_30_touches := tue_lower_30_touches + 1
        else if current_day == "Wednesday"
            wed_lower_30_touches := wed_lower_30_touches + 1
        else if current_day == "Thursday"
            thu_lower_30_touches := thu_lower_30_touches + 1
        else if current_day == "Friday"
            fri_lower_30_touches := fri_lower_30_touches + 1
        lower_30_touched := true
    
    float upper_40_level = current_open + 4.0 * sd
    float lower_40_level = current_open - 4.0 * sd
    if not upper_40_touched and high >= upper_40_level
        if current_day == "Monday"
            mon_upper_40_touches := mon_upper_40_touches + 1
        else if current_day == "Tuesday"
            tue_upper_40_touches := tue_upper_40_touches + 1
        else if current_day == "Wednesday"
            wed_upper_40_touches := wed_upper_40_touches + 1
        else if current_day == "Thursday"
            thu_upper_40_touches := thu_upper_40_touches + 1
        else if current_day == "Friday"
            fri_upper_40_touches := fri_upper_40_touches + 1
        upper_40_touched := true
    if not lower_40_touched and low <= lower_40_level
        if current_day == "Monday"
            mon_lower_40_touches := mon_lower_40_touches + 1
        else if current_day == "Tuesday"
            tue_lower_40_touches := tue_lower_40_touches + 1
        else if current_day == "Wednesday"
            wed_lower_40_touches := wed_lower_40_touches + 1
        else if current_day == "Thursday"
            thu_lower_40_touches := thu_lower_40_touches + 1
        else if current_day == "Friday"
            fri_lower_40_touches := fri_lower_40_touches + 1
        lower_40_touched := true

// Display Stats Table
if show_stats
    ts = get_table_text_size(table_text_size)
    table.cell(stats_table, 0, 0, "Level", text_color=color.black, text_size=ts)
    table.cell(stats_table, 1, 0, "Mon #", text_color=color.black, text_size=ts)
    table.cell(stats_table, 2, 0, "Mon %", text_color=color.black, text_size=ts)
    table.cell(stats_table, 3, 0, "Tue #", text_color=color.black, text_size=ts)
    table.cell(stats_table, 4, 0, "Tue %", text_color=color.black, text_size=ts)
    table.cell(stats_table, 5, 0, "Wed #", text_color=color.black, text_size=ts)
    table.cell(stats_table, 6, 0, "Wed %", text_color=color.black, text_size=ts)
    table.cell(stats_table, 7, 0, "Thu #", text_color=color.black, text_size=ts)
    table.cell(stats_table, 8, 0, "Thu %", text_color=color.black, text_size=ts)
    table.cell(stats_table, 9, 0, "Fri #", text_color=color.black, text_size=ts)
    table.cell(stats_table, 10, 0, "Fri %", text_color=color.black, text_size=ts)
    
    table.cell(stats_table, 0, 1, "Sessions", text_color=color.black, text_size=ts)
    table.cell(stats_table, 1, 1, str.tostring(mon_sessions), text_color=color.black, text_size=ts)
    table.cell(stats_table, 2, 1, "", text_color=color.black, text_size=ts)
    table.cell(stats_table, 3, 1, str.tostring(tue_sessions), text_color=color.black, text_size=ts)
    table.cell(stats_table, 4, 1, "", text_color=color.black, text_size=ts)
    table.cell(stats_table, 5, 1, str.tostring(wed_sessions), text_color=color.black, text_size=ts)
    table.cell(stats_table, 6, 1, "", text_color=color.black, text_size=ts)
    table.cell(stats_table, 7, 1, str.tostring(thu_sessions), text_color=color.black, text_size=ts)
    table.cell(stats_table, 8, 1, "", text_color=color.black, text_size=ts)
    table.cell(stats_table, 9, 1, str.tostring(fri_sessions), text_color=color.black, text_size=ts)
    table.cell(stats_table, 10, 1, "", text_color=color.black, text_size=ts)
    
    table.cell(stats_table, 0, 2, "+0.5 SD", text_color=color.black, text_size=ts)
    table.cell(stats_table, 1, 2, str.tostring(mon_upper_05_touches), text_color=color.black, text_size=ts)
    table.cell(stats_table, 2, 2, mon_sessions > 0 ? str.tostring(math.round(mon_upper_05_touches / mon_sessions * 100, 1)) + "%" : "0%", text_color=color.black, text_size=ts)
    table.cell(stats_table, 3, 2, str.tostring(tue_upper_05_touches), text_color=color.black, text_size=ts)
    table.cell(stats_table, 4, 2, tue_sessions > 0 ? str.tostring(math.round(tue_upper_05_touches / tue_sessions * 100, 1)) + "%" : "0%", text_color=color.black, text_size=ts)
    table.cell(stats_table, 5, 2, str.tostring(wed_upper_05_touches), text_color=color.black, text_size=ts)
    table.cell(stats_table, 6, 2, wed_sessions > 0 ? str.tostring(math.round(wed_upper_05_touches / wed_sessions * 100, 1)) + "%" : "0%", text_color=color.black, text_size=ts)
    table.cell(stats_table, 7, 2, str.tostring(thu_upper_05_touches), text_color=color.black, text_size=ts)
    table.cell(stats_table, 8, 2, thu_sessions > 0 ? str.tostring(math.round(thu_upper_05_touches / thu_sessions * 100, 1)) + "%" : "0%", text_color=color.black, text_size=ts)
    table.cell(stats_table, 9, 2, str.tostring(fri_upper_05_touches), text_color=color.black, text_size=ts)
    table.cell(stats_table, 10, 2, fri_sessions > 0 ? str.tostring(math.round(fri_upper_05_touches / fri_sessions * 100, 1)) + "%" : "0%", text_color=color.black, text_size=ts)
    
    table.cell(stats_table, 0, 3, "-0.5 SD", text_color=color.black, text_size=ts)
    table.cell(stats_table, 1, 3, str.tostring(mon_lower_05_touches), text_color=color.black, text_size=ts)
    table.cell(stats_table, 2, 3, mon_sessions > 0 ? str.tostring(math.round(mon_lower_05_touches / mon_sessions * 100, 1)) + "%" : "0%", text_color=color.black, text_size=ts)
    table.cell(stats_table, 3, 3, str.tostring(tue_lower_05_touches), text_color=color.black, text_size=ts)
    table.cell(stats_table, 4, 3, tue_sessions > 0 ? str.tostring(math.round(tue_lower_05_touches / tue_sessions * 100, 1)) + "%" : "0%", text_color=color.black, text_size=ts)
    table.cell(stats_table, 5, 3, str.tostring(wed_lower_05_touches), text_color=color.black, text_size=ts)
    table.cell(stats_table, 6, 3, wed_sessions > 0 ? str.tostring(math.round(wed_lower_05_touches / wed_sessions * 100, 1)) + "%" : "0%", text_color=color.black, text_size=ts)
    table.cell(stats_table, 7, 3, str.tostring(thu_lower_05_touches), text_color=color.black, text_size=ts)
    table.cell(stats_table, 8, 3, thu_sessions > 0 ? str.tostring(math.round(thu_lower_05_touches / thu_sessions * 100, 1)) + "%" : "0%", text_color=color.black, text_size=ts)
    table.cell(stats_table, 9, 3, str.tostring(fri_lower_05_touches), text_color=color.black, text_size=ts)
    table.cell(stats_table, 10, 3, fri_sessions > 0 ? str.tostring(math.round(fri_lower_05_touches / fri_sessions * 100, 1)) + "%" : "0%", text_color=color.black, text_size=ts)
    
    table.cell(stats_table, 0, 4, "+1.0 SD", text_color=color.black, text_size=ts)
    table.cell(stats_table, 1, 4, str.tostring(mon_upper_10_touches), text_color=color.black, text_size=ts)
    table.cell(stats_table, 2, 4, mon_sessions > 0 ? str.tostring(math.round(mon_upper_10_touches / mon_sessions * 100, 1)) + "%" : "0%", text_color=color.black, text_size=ts)
    table.cell(stats_table, 3, 4, str.tostring(tue_upper_10_touches), text_color=color.black, text_size=ts)
    table.cell(stats_table, 4, 4, tue_sessions > 0 ? str.tostring(math.round(tue_upper_10_touches / tue_sessions * 100, 1)) + "%" : "0%", text_color=color.black, text_size=ts)
    table.cell(stats_table, 5, 4, str.tostring(wed_upper_10_touches), text_color=color.black, text_size=ts)
    table.cell(stats_table, 6, 4, wed_sessions > 0 ? str.tostring(math.round(wed_upper_10_touches / wed_sessions * 100, 1)) + "%" : "0%", text_color=color.black, text_size=ts)
    table.cell(stats_table, 7, 4, str.tostring(thu_upper_10_touches), text_color=color.black, text_size=ts)
    table.cell(stats_table, 8, 4, thu_sessions > 0 ? str.tostring(math.round(thu_upper_10_touches / thu_sessions * 100, 1)) + "%" : "0%", text_color=color.black, text_size=ts)
    table.cell(stats_table, 9, 4, str.tostring(fri_upper_10_touches), text_color=color.black, text_size=ts)
    table.cell(stats_table, 10, 4, fri_sessions > 0 ? str.tostring(math.round(fri_upper_10_touches / fri_sessions * 100, 1)) + "%" : "0%", text_color=color.black, text_size=ts)
    
    table.cell(stats_table, 0, 5, "-1.0 SD", text_color=color.black, text_size=ts)
    table.cell(stats_table, 1, 5, str.tostring(mon_lower_10_touches), text_color=color.black, text_size=ts)
    table.cell(stats_table, 2, 5, mon_sessions > 0 ? str.tostring(math.round(mon_lower_10_touches / mon_sessions * 100, 1)) + "%" : "0%", text_color=color.black, text_size=ts)
    table.cell(stats_table, 3, 5, str.tostring(tue_lower_10_touches), text_color=color.black, text_size=ts)
    table.cell(stats_table, 4, 5, tue_sessions > 0 ? str.tostring(math.round(tue_lower_10_touches / tue_sessions * 100, 1)) + "%" : "0%", text_color=color.black, text_size=ts)
    table.cell(stats_table, 5, 5, str.tostring(wed_lower_10_touches), text_color=color.black, text_size=ts)
    table.cell(stats_table, 6, 5, wed_sessions > 0 ? str.tostring(math.round(wed_lower_10_touches / wed_sessions * 100, 1)) + "%" : "0%", text_color=color.black, text_size=ts)
    table.cell(stats_table, 7, 5, str.tostring(thu_lower_10_touches), text_color=color.black, text_size=ts)
    table.cell(stats_table, 8, 5, thu_sessions > 0 ? str.tostring(math.round(thu_lower_10_touches / thu_sessions * 100, 1)) + "%" : "0%", text_color=color.black, text_size=ts)
    table.cell(stats_table, 9, 5, str.tostring(fri_lower_10_touches), text_color=color.black, text_size=ts)
    table.cell(stats_table, 10, 5, fri_sessions > 0 ? str.tostring(math.round(fri_lower_10_touches / fri_sessions * 100, 1)) + "%" : "0%", text_color=color.black, text_size=ts)
    
    table.cell(stats_table, 0, 6, "+2.0 SD", text_color=color.black, text_size=ts)
    table.cell(stats_table, 1, 6, str.tostring(mon_upper_20_touches), text_color=color.black, text_size=ts)
    table.cell(stats_table, 2, 6, mon_sessions > 0 ? str.tostring(math.round(mon_upper_20_touches / mon_sessions * 100, 1)) + "%" : "0%", text_color=color.black, text_size=ts)
    table.cell(stats_table, 3, 6, str.tostring(tue_upper_20_touches), text_color=color.black, text_size=ts)
    table.cell(stats_table, 4, 6, tue_sessions > 0 ? str.tostring(math.round(tue_upper_20_touches / tue_sessions * 100, 1)) + "%" : "0%", text_color=color.black, text_size=ts)
    table.cell(stats_table, 5, 6, str.tostring(wed_upper_20_touches), text_color=color.black, text_size=ts)
    table.cell(stats_table, 6, 6, wed_sessions > 0 ? str.tostring(math.round(wed_upper_20_touches / wed_sessions * 100, 1)) + "%" : "0%", text_color=color.black, text_size=ts)
    table.cell(stats_table, 7, 6, str.tostring(thu_upper_20_touches), text_color=color.black, text_size=ts)
    table.cell(stats_table, 8, 6, thu_sessions > 0 ? str.tostring(math.round(thu_upper_20_touches / thu_sessions * 100, 1)) + "%" : "0%", text_color=color.black, text_size=ts)
    table.cell(stats_table, 9, 6, str.tostring(fri_upper_20_touches), text_color=color.black, text_size=ts)
    table.cell(stats_table, 10, 6, fri_sessions > 0 ? str.tostring(math.round(fri_upper_20_touches / fri_sessions * 100, 1)) + "%" : "0%", text_color=color.black, text_size=ts)
    
    table.cell(stats_table, 0, 7, "-2.0 SD", text_color=color.black, text_size=ts)
    table.cell(stats_table, 1, 7, str.tostring(mon_lower_20_touches), text_color=color.black, text_size=ts)
    table.cell(stats_table, 2, 7, mon_sessions > 0 ? str.tostring(math.round(mon_lower_20_touches / mon_sessions * 100, 1)) + "%" : "0%", text_color=color.black, text_size=ts)
    table.cell(stats_table, 3, 7, str.tostring(tue_lower_20_touches), text_color=color.black, text_size=ts)
    table.cell(stats_table, 4, 7, tue_sessions > 0 ? str.tostring(math.round(tue_lower_20_touches / tue_sessions * 100, 1)) + "%" : "0%", text_color=color.black, text_size=ts)
    table.cell(stats_table, 5, 7, str.tostring(wed_lower_20_touches), text_color=color.black, text_size=ts)
    table.cell(stats_table, 6, 7, wed_sessions > 0 ? str.tostring(math.round(wed_lower_20_touches / wed_sessions * 100, 1)) + "%" : "0%", text_color=color.black, text_size=ts)
    table.cell(stats_table, 7, 7, str.tostring(thu_lower_20_touches), text_color=color.black, text_size=ts)
    table.cell(stats_table, 8, 7, thu_sessions > 0 ? str.tostring(math.round(thu_lower_20_touches / thu_sessions * 100, 1)) + "%" : "0%", text_color=color.black, text_size=ts)
    table.cell(stats_table, 9, 7, str.tostring(fri_lower_20_touches), text_color=color.black, text_size=ts)
    table.cell(stats_table, 10, 7, fri_sessions > 0 ? str.tostring(math.round(fri_lower_20_touches / fri_sessions * 100, 1)) + "%" : "0%", text_color=color.black, text_size=ts)
    
    table.cell(stats_table, 0, 8, "+3.0 SD", text_color=color.black, text_size=ts)
    table.cell(stats_table, 1, 8, str.tostring(mon_upper_30_touches), text_color=color.black, text_size=ts)
    table.cell(stats_table, 2, 8, mon_sessions > 0 ? str.tostring(math.round(mon_upper_30_touches / mon_sessions * 100, 1)) + "%" : "0%", text_color=color.black, text_size=ts)
    table.cell(stats_table, 3, 8, str.tostring(tue_upper_30_touches), text_color=color.black, text_size=ts)
    table.cell(stats_table, 4, 8, tue_sessions > 0 ? str.tostring(math.round(tue_upper_30_touches / tue_sessions * 100, 1)) + "%" : "0%", text_color=color.black, text_size=ts)
    table.cell(stats_table, 5, 8, str.tostring(wed_upper_30_touches), text_color=color.black, text_size=ts)
    table.cell(stats_table, 6, 8, wed_sessions > 0 ? str.tostring(math.round(wed_upper_30_touches / wed_sessions * 100, 1)) + "%" : "0%", text_color=color.black, text_size=ts)
    table.cell(stats_table, 7, 8, str.tostring(thu_upper_30_touches), text_color=color.black, text_size=ts)
    table.cell(stats_table, 8, 8, thu_sessions > 0 ? str.tostring(math.round(thu_upper_30_touches / thu_sessions * 100, 1)) + "%" : "0%", text_color=color.black, text_size=ts)
    table.cell(stats_table, 9, 8, str.tostring(fri_upper_30_touches), text_color=color.black, text_size=ts)
    table.cell(stats_table, 10, 8, fri_sessions > 0 ? str.tostring(math.round(fri_upper_30_touches / fri_sessions * 100, 1)) + "%" : "0%", text_color=color.black, text_size=ts)
    
    table.cell(stats_table, 0, 9, "-3.0 SD", text_color=color.black, text_size=ts)
    table.cell(stats_table, 1, 9, str.tostring(mon_lower_30_touches), text_color=color.black, text_size=ts)
    table.cell(stats_table, 2, 9, mon_sessions > 0 ? str.tostring(math.round(mon_lower_30_touches / mon_sessions * 100, 1)) + "%" : "0%", text_color=color.black, text_size=ts)
    table.cell(stats_table, 3, 9, str.tostring(tue_lower_30_touches), text_color=color.black, text_size=ts)
    table.cell(stats_table, 4, 9, tue_sessions > 0 ? str.tostring(math.round(tue_lower_30_touches / tue_sessions * 100, 1)) + "%" : "0%", text_color=color.black, text_size=ts)
    table.cell(stats_table, 5, 9, str.tostring(wed_lower_30_touches), text_color=color.black, text_size=ts)
    table.cell(stats_table, 6, 9, wed_sessions > 0 ? str.tostring(math.round(wed_lower_30_touches / wed_sessions * 100, 1)) + "%" : "0%", text_color=color.black, text_size=ts)
    table.cell(stats_table, 7, 9, str.tostring(thu_lower_30_touches), text_color=color.black, text_size=ts)
    table.cell(stats_table, 8, 9, thu_sessions > 0 ? str.tostring(math.round(thu_lower_30_touches / thu_sessions * 100, 1)) + "%" : "0%", text_color=color.black, text_size=ts)
    table.cell(stats_table, 9, 9, str.tostring(fri_lower_30_touches), text_color=color.black, text_size=ts)
    table.cell(stats_table, 10, 9, fri_sessions > 0 ? str.tostring(math.round(fri_lower_30_touches / fri_sessions * 100, 1)) + "%" : "0%", text_color=color.black, text_size=ts)
    
    table.cell(stats_table, 0, 10, "+4.0 SD", text_color=color.black, text_size=ts)
    table.cell(stats_table, 1, 10, str.tostring(mon_upper_40_touches), text_color=color.black, text_size=ts)
    table.cell(stats_table, 2, 10, mon_sessions > 0 ? str.tostring(math.round(mon_upper_40_touches / mon_sessions * 100, 1)) + "%" : "0%", text_color=color.black, text_size=ts)
    table.cell(stats_table, 3, 10, str.tostring(tue_upper_40_touches), text_color=color.black, text_size=ts)
    table.cell(stats_table, 4, 10, tue_sessions > 0 ? str.tostring(math.round(tue_upper_40_touches / tue_sessions * 100, 1)) + "%" : "0%", text_color=color.black, text_size=ts)
    table.cell(stats_table, 5, 10, str.tostring(wed_upper_40_touches), text_color=color.black, text_size=ts)
    table.cell(stats_table, 6, 10, wed_sessions > 0 ? str.tostring(math.round(wed_upper_40_touches / wed_sessions * 100, 1)) + "%" : "0%", text_color=color.black, text_size=ts)
    table.cell(stats_table, 7, 10, str.tostring(thu_upper_40_touches), text_color=color.black, text_size=ts)
    table.cell(stats_table, 8, 10, thu_sessions > 0 ? str.tostring(math.round(thu_upper_40_touches / thu_sessions * 100, 1)) + "%" : "0%", text_color=color.black, text_size=ts)
    table.cell(stats_table, 9, 10, str.tostring(fri_upper_40_touches), text_color=color.black, text_size=ts)
    table.cell(stats_table, 10, 10, fri_sessions > 0 ? str.tostring(math.round(fri_upper_40_touches / fri_sessions * 100, 1)) + "%" : "0%", text_color=color.black, text_size=ts)

// === Reversal Stats Inputs ===
var g_reversal_stats = "Reversal Stats Settings"
show_reversal_stats = input.bool(true, "Show Reversal Stats Table", group=g_reversal_stats)
reversal_table_position = input.string("Bottom Left", "Reversal Table Position", options=["Top Left", "Top Center", "Top Right", "Middle Left", "Middle Center", "Middle Right", "Bottom Left", "Bottom Center", "Bottom Right"], group=g_reversal_stats)
reversal_table_text_size = input.string("Small", "Reversal Table Text Size", options=["Tiny", "Small", "Normal", "Large"], group=g_reversal_stats)

// === Reversal Stats Variables ===
var float[] sd_levels = array.new_float(11, na)
if bar_index == 0
    array.set(sd_levels, 0, -4.0)
    array.set(sd_levels, 1, -3.0)
    array.set(sd_levels, 2, -2.0)
    array.set(sd_levels, 3, -1.0)
    array.set(sd_levels, 4, -0.5)
    array.set(sd_levels, 5, 0.0)
    array.set(sd_levels, 6, 0.5)
    array.set(sd_levels, 7, 1.0)
    array.set(sd_levels, 8, 2.0)
    array.set(sd_levels, 9, 3.0)
    array.set(sd_levels, 10, 4.0)

var int[] total_hits = array.new_int(9, 0)
var int total_days = 0
var int last_day = na

var int[] from_m40 = array.new_int(9, 0)
var int[] from_m30 = array.new_int(9, 0)
var int[] from_m20 = array.new_int(9, 0)
var int[] from_m10 = array.new_int(9, 0)
var int[] from_m05 = array.new_int(9, 0)
var int[] from_00  = array.new_int(9, 0)
var int[] from_p05 = array.new_int(9, 0)
var int[] from_p10 = array.new_int(9, 0)
var int[] from_p20 = array.new_int(9, 0)
var int[] from_p30 = array.new_int(9, 0)
var int[] from_p40 = array.new_int(9, 0)

var bool[] hit_flags = array.new_bool(9, false)
var bool[] reversal_flags_m40 = array.new_bool(9, false)
var bool[] reversal_flags_m30 = array.new_bool(9, false)
var bool[] reversal_flags_m20 = array.new_bool(9, false)
var bool[] reversal_flags_m10 = array.new_bool(9, false)
var bool[] reversal_flags_m05 = array.new_bool(9, false)
var bool[] reversal_flags_00  = array.new_bool(9, false)
var bool[] reversal_flags_p05 = array.new_bool(9, false)
var bool[] reversal_flags_p10 = array.new_bool(9, false)
var bool[] reversal_flags_p20 = array.new_bool(9, false)
var bool[] reversal_flags_p30 = array.new_bool(9, false)
var bool[] reversal_flags_p40 = array.new_bool(9, false)

var table reversal_stats_table = na

if bar_index == 0
    reversal_stats_table := table.new(get_table_position(reversal_table_position), 50, 50, bgcolor=color.white, frame_color=color.black, frame_width=1, border_color=color.black, border_width=1)

// === Reversal Stats Logic ===
if is_newbar(session_time)
    int current_day = dayofmonth(time, time_zone)
    if na(last_day) or current_day != last_day
        total_days := total_days + 1
        last_day := current_day
    
    for i = 0 to 8
        array.set(hit_flags, i, false)
        array.set(reversal_flags_m40, i, false)
        array.set(reversal_flags_m30, i, false)
        array.set(reversal_flags_m20, i, false)
        array.set(reversal_flags_m10, i, false)
        array.set(reversal_flags_m05, i, false)
        array.set(reversal_flags_00, i, false)
        array.set(reversal_flags_p05, i, false)
        array.set(reversal_flags_p10, i, false)
        array.set(reversal_flags_p20, i, false)
        array.set(reversal_flags_p30, i, false)
        array.set(reversal_flags_p40, i, false)

if is_session(session_time) and not na(current_open) and not na(sd)
    float[] price_levels = array.new_float(9, na)
    for i = 0 to 8
        array.set(price_levels, i, current_open + array.get(sd_levels, i) * sd)
    
    for i = 0 to 8
        float level = array.get(price_levels, i)
        if not array.get(hit_flags, i)
            if (i <= 4 and low <= level) or (i > 4 and high >= level)
                array.set(hit_flags, i, true)
                array.set(total_hits, i, array.get(total_hits, i) + 1)
    
    for i = 0 to 8
        if array.get(hit_flags, i)
            var int[] current_counts = na
            var bool[] current_flags = na
            switch i
                0 => current_counts := from_m40, current_flags := reversal_flags_m40
                1 => current_counts := from_m30, current_flags := reversal_flags_m30
                2 => current_counts := from_m20, current_flags := reversal_flags_m20
                3 => current_counts := from_m10, current_flags := reversal_flags_m10
                4 => current_counts := from_m05, current_flags := reversal_flags_m05
                5 => current_counts := from_00,  current_flags := reversal_flags_00
                6 => current_counts := from_p05, current_flags := reversal_flags_p05
                7 => current_counts := from_p10, current_flags := reversal_flags_p10
                8 => current_counts := from_p20, current_flags := reversal_flags_p20
                9 => current_counts := from_p30, current_flags := reversal_flags_p30
                10 => current_counts := from_p40, current_flags := reversal_flags_p40
            
            for j = 0 to 8
                float target_level = array.get(price_levels, j)
                if not array.get(current_flags, j)
                    if (j <= 4 and low <= target_level) or (j > 4 and high >= target_level)
                        array.set(current_counts, j, array.get(current_counts, j) + 1)
                        array.set(current_flags, j, true)

// Display Reversal Stats Table
if show_reversal_stats
    ts = get_table_text_size(reversal_table_text_size)
    table.cell(reversal_stats_table, 0, 0, "From Level", text_color=color.black, text_size=ts)
    for j = 0 to 8
        table.cell(reversal_stats_table, j + 1, 0, "To " + str.tostring(array.get(sd_levels, j)), text_color=color.black, text_size=ts)
    table.cell(reversal_stats_table, 11, 0, "Total Days", text_color=color.black, text_size=ts)
    
    for i = 0 to 8
        table.cell(reversal_stats_table, 0, i + 1, "From " + str.tostring(array.get(sd_levels, i)), text_color=color.black, text_size=ts)
        int hits = array.get(total_hits, i)
        var int[] current_counts = na
        switch i
            0 => current_counts := from_m40
            1 => current_counts := from_m30
            2 => current_counts := from_m20
            3 => current_counts := from_m10
            4 => current_counts := from_m05
            5 => current_counts := from_00
            6 => current_counts := from_p05
            7 => current_counts := from_p10
            8 => current_counts := from_p20
            9 => current_counts := from_p30
            10 => current_counts := from_p40
        
        for j = 0 to 8
            int reversals = array.get(current_counts, j)
            string percentage = hits > 0 ? str.tostring(math.round(reversals / hits * 100, 1)) + "% [" + str.tostring(reversals) + "]" : "0% [0]"
            table.cell(reversal_stats_table, j + 1, i + 1, percentage, text_color=color.black, text_size=ts)
    
    table.cell(reversal_stats_table, 11, 10, str.tostring(total_days), text_color=color.black, text_size=ts)

Overview

Session Standard Deviations with Stats draws the session open line alongside positive and negative standard deviation bands (0.5, 1.0, 2.0, 3.0, and 4.0 SD) directly on the main chart, accompanied by a statistics table. Users can customize the session time window, time zone, historical session lookback period, and the styling, colors, and visibility of each standard deviation level.

Details

Published by

TamaraKGreen on GitHub

Code credit

notprofessorgreen, as written in the script's header

Type

Indicator, Pine Script v6

Draws

On the price chart

Category

Sessions & time, Volatility & bands, Support & resistance, Screeners & dashboards

Markets

Any symbol

Timeframes

M1-M5, M15-H1

Risk Notes

No repainting, lookahead or stacked entries were found in its code or README. That says nothing about how well it works: test it first.

Settings

The inputs you can change from the gear icon on the chart. Defaults are the author's.

SettingTypeDefaultVariable
Session Time (HHMM-HHMM)string0000-1600session_time
Time ZonestringAmerica/New_Yorktime_zone
Historical Sessionsint20hist_sessions
Offset (Bars)int0projection_offset
s0.00booltrueshow_open
StylestringDottedopen_line_style
Colorcolorcolor.grayopen_line_color
Widthint1open_line_width
Label SizestringSmalllabel_size
0.5 SDbooltrueshow_05_sd
StylestringDottedprojection_05_style
Colorcolorcolor.redprojection_05_color
Show all 35 settings
SettingTypeDefaultVariable
Widthint1projection_05_width
1.0 SDbooltrueshow_10_sd
StylestringDottedprojection_10_style
Colorcolorcolor.blueprojection_10_color
Widthint1projection_10_width
2.0 SDbooltrueshow_20_sd
StylestringDottedprojection_20_style
Colorcolorcolor.grayprojection_20_color
Widthint1projection_20_width
3.0 SDbooltrueshow_30_sd
StylestringDottedprojection_30_style
Colorcolorcolor.grayprojection_30_color
Widthint1projection_30_width
4.0 SDbooltrueshow_40_sd
StylestringDottedprojection_40_style
Colorcolorcolor.grayprojection_40_color
Widthint1projection_40_width
Show Stats Tablebooltrueshow_stats
Table PositionstringTop Righttable_position
Text SizestringSmalltable_text_size
Show Reversal Stats Tablebooltrueshow_reversal_stats
Reversal Table PositionstringBottom Leftreversal_table_position
Reversal Table Text SizestringSmallreversal_table_text_size
Third-party code. ForexR did not write, test or endorse this script, and the summary above was generated automatically from its code and README. Indicators describe the past, and a strategy's backtest can look better than live trading, especially when a script repaints. Check it in the Strategy Tester and on a demo account before you rely on it.

Similar Pine Scripts

56 stars

This indicator marks forex trading sessions and opening ranges on the price chart. It can draw Sydney, Tokyo, Frankfurt, London, and New York session boxes or five- and fifteen-minute opening ranges, plus an optional opening channel and a day-open range. Day r

Indicator v5 Sessions & timeSupport & resistance Repaints
Updated 2 months ago · None stated GitHub repository of Day Trading Booster by DGT

Zen Trading Toolkit

by Zen-Tim
29 stars

This indicator plots average bar range (ABR) measured move targets, opening range boxes, and historical key reference levels directly on the price chart. It projects bullish and bearish targets (0.5x, 1.0x, 1.5x, 2.0x ABR) from the previous session's close, al

Indicator v6 Support & resistanceVolatility & bands
Updated 2 weeks ago · MIT GitHub repository of Zen Trading Toolkit
1 stars

This overlay indicator calculates intraday standard deviation sigma bands using live implied volatility from VIX or a manual percentage input. Designed for index options credit spread placement, it projects 1s, 2s, and 3s expected move boundaries anchored to e

Indicator v5 Volatility & bandsSupport & resistance
Updated 3 months ago · None stated GitHub repository of 3s Credit Spread [DTO]
1 stars

This script is a deprecated compatibility stub for the Market Lab indicator. It intentionally plots no data on the price chart and emits no formulas, signals, or alerts. The purpose of this stub is to direct users toward the canonical Market Lab script. The as

Indicator v6 Screeners & dashboardsSupport & resistance
Updated today · AGPL-3.0 GitHub repository of DEPRECATED - use canonical Market Lab
575 stars

This indicator builds Market Profile Time Price Opportunity (TPO) structures across defined session timeframes directly on the main price chart. It visualizes the Point of Control (POC) and Value Area boundaries using dynamic box drawings. An integrated multi-

Indicator v5 Support & resistanceScreeners & dashboards
Updated 1 year ago · MPL-2.0 GitHub repository of Market Profile with Screener

More indicators

Trade Your Charts With a Regulated Broker

Compare brokers that connect to TradingView, with spreads, minimum deposits and leverage side by side.

Compare brokers