Indicator · Pine Script v6 · On the chart

AM TBR — NQ Stats

by TamaraKGreen· code credited to lucymatos

The AM TBR indicator calculates statistical levels for NQ futures using standard deviation.

View on GitHub

GitHub stars46on the repository
Pine versionv6current
Lines of code700
Settings14inputs
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

700 lines of Pine Script v6 from AM TBR - NQ Stats.txt, published under MPL-2.0 (file header). Keep the author's credit and licence lines when you reuse it.

AM TBR - NQ 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/
// © lucymatos

//@version=6
indicator("AM TBR — NQ Stats", "AM-TBR", true,
     max_boxes_count  = 500,
     max_lines_count  = 500,
     max_labels_count = 500)

// ============================================================================
// INPUTS
// ============================================================================

var g_S = "Setup"
i_lookback  = input.int(20, "SDEV Lookback (sessions)", minval=5, maxval=60, group=g_S)
i_mult      = input.float(0.25, "Touch Level (σ)", minval=0.05, step=0.05, group=g_S)
i_col       = input.color(color.new(color.gray, 0), "TBR Colour", group=g_S)
i_lbl_size  = input.string("Tiny", "Label Size", options=["Tiny","Small","Normal"], group=g_S)
i_keep_hist = input.bool(false, "Keep previous sessions on chart", group=g_S)

var g_L = "Levels"
i_show_ladder = input.bool(true,  "Show σ Ladder (±0.5→2.0)", group=g_L)
i_ladder_col  = input.color(color.new(color.gray, 72), "Ladder Colour", group=g_L)
i_show_zones  = input.bool(true,  "Show MAE Zones after touch", group=g_L)
i_zone_lbls   = input.bool(true,  "Zone Labels", group=g_L)
i_show_mfe    = input.bool(true,  "Show MFE Targets after reversion", group=g_L)

var g_TBL = "Dashboard"
i_show_table = input.bool(true, "Show Dashboard", group=g_TBL)
i_show_miles = input.bool(true, "Show Cumulative Milestones", group=g_TBL)
i_tbl_pos    = input.string("Top Right", "Position", options=["Top Right","Top Left","Top Center","Middle Right","Middle Left","Bottom Right","Bottom Left"], group=g_TBL)
i_tbl_size   = input.string("Small", "Text Size", options=["Tiny","Small","Normal"], group=g_TBL)

// ============================================================================

get_stats(band, side) =>
    if band == 8 and side == 1
        array.from(78.4, 732.0, 0.0,
             574.0, 0.553, 0.445, 0.645, 0.957,
             158.0, 1.623, 1.528, 1.882, 2.317,
             574.0, 0.913, 0.687, 1.180, 1.993)
    else if band == 8 and side == 2
        array.from(79.6, 774.0, 0.0,
             616.0, 0.559, 0.441, 0.645, 0.969,
             158.0, 2.005, 1.749, 2.450, 3.208,
             616.0, 0.807, 0.619, 1.131, 1.722)
    else if band == 9 and side == 1
        array.from(69.7, 492.0, 0.0,
             343.0, 0.435, 0.363, 0.508, 0.697,
             149.0, 1.184, 1.095, 1.457, 1.689,
             343.0, 0.628, 0.430, 0.755, 1.515)
    else if band == 9 and side == 2
        array.from(69.3, 488.0, 0.0,
             338.0, 0.469, 0.382, 0.541, 0.769,
             150.0, 1.398, 1.239, 1.702, 2.283,
             338.0, 0.481, 0.362, 0.667, 1.091)
    else if band == 10 and side == 1
        array.from(47.6, 21.0, 1.0,
             10.0, 0.401, 0.335, 0.421, 0.608,
             11.0, 0.620, 0.631, 0.828, 0.896,
             343.0, 0.628, 0.430, 0.755, 1.515)
    else if band == 10 and side == 2
        array.from(33.3, 27.0, 1.0,
             9.0, 0.374, 0.390, 0.413, 0.442,
             18.0, 0.728, 0.656, 0.964, 1.158,
             338.0, 0.481, 0.362, 0.667, 1.091)
    else if side == 1
        array.from(0.0, 7.0, 1.0,
             10.0, 0.401, 0.335, 0.421, 0.608,
             11.0, 0.620, 0.631, 0.828, 0.896,
             343.0, 0.628, 0.430, 0.755, 1.515)
    else
        array.from(25.0, 4.0, 1.0,
             9.0, 0.374, 0.390, 0.413, 0.442,
             18.0, 0.728, 0.656, 0.964, 1.158,
             338.0, 0.481, 0.362, 0.667, 1.091)

// Cumulative reversion milestones [by09, by10, by11, by12]
get_milestones(band, side) =>
    if band == 8 and side == 1
        [27.9, 68.2, 76.1, 78.4]
    else if band == 8 and side == 2
        [28.8, 68.7, 76.7, 79.6]
    else if side == 1
        [16.3, 60.4, 71.2, 74.0]
    else
        [17.2, 60.2, 70.8, 74.6]

// ============================================================================
// HELPERS
// ============================================================================

tbl_pos(s) =>
    switch s
        "Top Left"     => position.top_left
        "Top Center"   => position.top_center
        "Middle Right" => position.middle_right
        "Middle Left"  => position.middle_left
        "Bottom Right" => position.bottom_right
        "Bottom Left"  => position.bottom_left
        => position.top_right

tbl_sz(s) =>
    switch s
        "Tiny"   => size.tiny
        "Normal" => size.normal
        => size.small

zone_status(z) =>
    switch z
        0 => "TYPICAL"
        1 => "DEEP"
        2 => "STRETCHED"
        3 => "RISK"
        => "NON-REV"

zone_fill(z) =>
    switch z
        0 => color.new(color.gray,   80)
        1 => color.new(color.blue,   80)
        2 => color.new(color.orange, 80)
        3 => color.new(color.red,    80)
        => color.new(#37474f,        80)

zone_border(z) =>
    switch z
        0 => color.new(color.gray,   40)
        1 => color.new(color.blue,   40)
        2 => color.new(color.orange, 40)
        3 => color.new(color.red,    40)
        => color.new(#37474f,        40)

// ============================================================================

f_sigma() =>
    chg = (close - close[1]) / close[1] * 100.0
    ta.stdev(chg, i_lookback, false)   // biased=false → SAMPLE stdev

daily_sigma = request.security(syminfo.tickerid, "D", f_sigma()[1], lookahead=barmerge.lookahead_on)

// ============================================================================

ltf_tf = timeframe.in_seconds() >= 60 ? "1" : timeframe.period
[ltf_open_arr, ltf_high_arr, ltf_low_arr, ltf_time_arr] = request.security_lower_tf(syminfo.tickerid, ltf_tf, [open, high, low, time], ignore_invalid_symbol=true)

find_touch(upL, dnL) =>
    side = 0
    t    = int(na)
    idx  = -1
    n = array.size(ltf_high_arr)
    if n > 0
        for i = 0 to n - 1
            h1 = array.get(ltf_high_arr, i)
            l1 = array.get(ltf_low_arr,  i)
            hitU = h1 >= upL
            hitD = l1 <= dnL
            if hitU or hitD
                if hitU and hitD
                    o1 = array.get(ltf_open_arr, i)
                    side := upL - o1 <= o1 - dnL ? 1 : 2
                else
                    side := hitU ? 1 : 2
                t   := array.get(ltf_time_arr, i)
                idx := i
                break
    [side, t, idx]

find_rev(fromIdx, side, openP) =>
    rt = int(na)
    n = array.size(ltf_high_arr)
    if n > 0 and fromIdx >= 0 and fromIdx < n
        for i = fromIdx to n - 1
            l1 = array.get(ltf_low_arr,  i)
            h1 = array.get(ltf_high_arr, i)
            if (side == 1 and l1 <= openP) or (side == 2 and h1 >= openP)
                rt := array.get(ltf_time_arr, i)
                break
    rt

// ============================================================================
// STATE
// ============================================================================

var bool  v_active     = false
var float v_tbr_open   = na
var float v_sigma_pct  = na
var float v_sigma_px   = na
var float v_upL        = na
var float v_dnL        = na
var int   v_touch_side = 0      // 0=none 1=+0.25 2=−0.25
var int   v_touch_time = na
var int   v_touch_band = na     // 8 / 9 / 10 / 11
var int   v_touch_bar  = na     // [FIX-1] chart bar_index of the touch bar
var int   v_rev_time   = na
var int   v_outcome    = 0      // 0 wait 1 pending 2 reverted 3 no-rev 4 no-touch
var float v_max_ext    = 0.0
var int   v_ts_09      = na
var int   v_ts_10      = na
var int   v_ts_11      = na
var int   v_ts_12      = na

var array<float> S = array.new<float>()
var float m_by09 = na
var float m_by10 = na
var float m_by11 = na
var float m_by12 = na

Sget(i) => array.size(S) > 0 ? array.get(S, i) : na

// Drawing handles
var line  d_open_line = na
var label d_open_lbl  = na
var line  d_up_line   = na
var line  d_dn_line   = na
var label d_up_lbl    = na
var label d_dn_lbl    = na
var label d_touch_lbl = na
var array<line>  d_ladder_lines = array.new<line>()
var array<label> d_ladder_lbls  = array.new<label>()
var array<box>   d_zone_boxes   = array.new<box>()
var array<label> d_zone_labels  = array.new<label>()
var array<line>  d_mfe_lines    = array.new<line>()
var array<label> d_mfe_labels   = array.new<label>()

// ============================================================================
// CLEAR HELPERS
// ============================================================================

purge_lines(arr, del) =>
    if array.size(arr) > 0
        if del
            for i = 0 to array.size(arr) - 1
                line.delete(array.get(arr, i))
        array.clear(arr)

purge_labels(arr, del) =>
    if array.size(arr) > 0
        if del
            for i = 0 to array.size(arr) - 1
                label.delete(array.get(arr, i))
        array.clear(arr)

purge_boxes(arr, del) =>
    if array.size(arr) > 0
        if del
            for i = 0 to array.size(arr) - 1
                box.delete(array.get(arr, i))
        array.clear(arr)

clear_all(del) =>
    if del
        line.delete(d_open_line)
        label.delete(d_open_lbl)
        line.delete(d_up_line)
        line.delete(d_dn_line)
        label.delete(d_up_lbl)
        label.delete(d_dn_lbl)
        label.delete(d_touch_lbl)
    purge_lines(d_ladder_lines,  del)
    purge_labels(d_ladder_lbls,  del)
    purge_boxes(d_zone_boxes,    del)
    purge_labels(d_zone_labels,  del)
    purge_lines(d_mfe_lines,     del)
    purge_labels(d_mfe_labels,   del)

// ============================================================================
// DRAW  — MAE zones (anchored at TBR Open, touched side only)
// ============================================================================

draw_zones(side, t_left, t_right) =>
    purge_boxes(d_zone_boxes,   true)
    purge_labels(d_zone_labels, true)
    if i_show_zones and array.size(S) > 0
        bnds = array.from(i_mult, array.get(S, 5), array.get(S, 6), array.get(S, 7), array.get(S, 10), array.get(S, 11))
        for z = 0 to 4
            b_lo = array.get(bnds, z)
            b_hi = array.get(bnds, z + 1)
            if b_hi > b_lo
                float p_lo = na
                float p_hi = na
                if side == 1
                    p_lo := v_tbr_open + b_lo * v_sigma_px
                    p_hi := v_tbr_open + b_hi * v_sigma_px
                else
                    p_lo := v_tbr_open - b_hi * v_sigma_px
                    p_hi := v_tbr_open - b_lo * v_sigma_px

                bx = box.new(t_left, p_hi, t_right, p_lo,
                     xloc=xloc.bar_time,
                     border_color=zone_border(z), bgcolor=zone_fill(z),
                     border_width=1)
                array.push(d_zone_boxes, bx)

                if i_zone_lbls
                    txt = str.tostring(b_hi, "#.##") + "σ · " + zone_status(z)
                    lbl = label.new(t_right, (p_lo + p_hi) / 2.0, txt,
                         xloc=xloc.bar_time, yloc=yloc.price,
                         style=label.style_label_left,
                         color=color.new(color.white, 25),
                         textcolor=zone_border(z), size=tbl_sz(i_lbl_size),
                         text_font_family=font.family_monospace)
                    array.push(d_zone_labels, lbl)

// ============================================================================
// DRAW  — MFE continuation targets (far side of open, after reversion)
// ============================================================================

draw_mfe(side, t_left, t_right) =>
    purge_lines(d_mfe_lines,   true)
    purge_labels(d_mfe_labels, true)
    if i_show_mfe and array.size(S) > 0
        mfe_vals = array.from(array.get(S, 15), array.get(S, 16), array.get(S, 17))
        mfe_lbls = array.from("MFE p50", "MFE p75", "MFE p90")
        mfe_cols = array.from(color.new(color.green, 20), color.new(color.yellow, 20), color.new(color.orange, 20))
        for r = 0 to 2
            mv = array.get(mfe_vals, r)
            float mp = side == 1 ? v_tbr_open - mv * v_sigma_px : v_tbr_open + mv * v_sigma_px
            mc = array.get(mfe_cols, r)
            ln = line.new(t_left, mp, t_right, mp,
                 xloc=xloc.bar_time, color=mc, width=1,
                 style=line.style_dotted)
            array.push(d_mfe_lines, ln)
            lbl = label.new(t_right, mp,
                 array.get(mfe_lbls, r) + " " + str.tostring(mv, "#.##") + "σ",
                 xloc=xloc.bar_time, yloc=yloc.price,
                 style=label.style_label_left,
                 color=color.new(color.white, 25),
                 textcolor=mc, size=tbl_sz(i_lbl_size),
                 text_font_family=font.family_monospace)
            array.push(d_mfe_labels, lbl)

// ============================================================================
// MAIN
// ============================================================================

in_win    = not na(time(timeframe.period, "0800-1200:23456", "America/New_York"))
win_start = in_win and not in_win[1]
win_end   = (not in_win) and in_win[1]

// ── 08:00 — anchor TBR, project levels ───────────────────────────────────────
if win_start and not na(daily_sigma) and daily_sigma > 0
    clear_all(not i_keep_hist)
    v_active     := true
    v_outcome    := 0
    v_touch_side := 0
    v_touch_time := na
    v_touch_band := na
    v_touch_bar  := na
    v_rev_time   := na
    v_max_ext    := 0.0
    array.clear(S)

    v_tbr_open  := open
    v_sigma_pct := daily_sigma
    v_sigma_px  := v_tbr_open * daily_sigma / 100.0
    v_upL       := v_tbr_open + i_mult * v_sigma_px
    v_dnL       := v_tbr_open - i_mult * v_sigma_px

    yy = year(time,       "America/New_York")
    mm = month(time,      "America/New_York")
    dd = dayofmonth(time, "America/New_York")
    v_ts_09 := timestamp("America/New_York", yy, mm, dd,  9, 0)
    v_ts_10 := timestamp("America/New_York", yy, mm, dd, 10, 0)
    v_ts_11 := timestamp("America/New_York", yy, mm, dd, 11, 0)
    v_ts_12 := timestamp("America/New_York", yy, mm, dd, 12, 0)

    d_open_line := line.new(time, v_tbr_open, v_ts_12, v_tbr_open,
         xloc=xloc.bar_time, color=color.fuchsia, width=1,
         style=line.style_dashed)
    d_open_lbl := label.new(v_ts_12, v_tbr_open, "TBR OPEN",
         xloc=xloc.bar_time, yloc=yloc.price,
         style=label.style_label_left,
         color=color.new(color.white, 10),
         textcolor=color.fuchsia, size=tbl_sz(i_lbl_size),
         text_font_family=font.family_monospace)

    d_up_line := line.new(time, v_upL, v_ts_12, v_upL,
         xloc=xloc.bar_time, color=color.new(i_col, 20), width=2)
    d_dn_line := line.new(time, v_dnL, v_ts_12, v_dnL,
         xloc=xloc.bar_time, color=color.new(i_col, 20), width=2)
    d_up_lbl := label.new(v_ts_12, v_upL,
         "+" + str.tostring(i_mult, "#.##") + "σ",
         xloc=xloc.bar_time, yloc=yloc.price,
         style=label.style_label_left,
         color=color.new(color.white, 10),
         textcolor=color.new(i_col, 10), size=tbl_sz(i_lbl_size),
         text_font_family=font.family_monospace)
    d_dn_lbl := label.new(v_ts_12, v_dnL,
         "−" + str.tostring(i_mult, "#.##") + "σ",
         xloc=xloc.bar_time, yloc=yloc.price,
         style=label.style_label_left,
         color=color.new(color.white, 10),
         textcolor=color.new(i_col, 10), size=tbl_sz(i_lbl_size),
         text_font_family=font.family_monospace)

    if i_show_ladder
        mults = array.from(0.5, 0.75, 1.0, 1.5, 2.0)
        for k = 0 to array.size(mults) - 1
            mlt = array.get(mults, k)
            for sgn = 0 to 1
                lp = sgn == 0 ? v_tbr_open + mlt * v_sigma_px : v_tbr_open - mlt * v_sigma_px
                ln = line.new(time, lp, v_ts_12, lp,
                     xloc=xloc.bar_time, color=i_ladder_col, width=1,
                     style=line.style_dotted)
                array.push(d_ladder_lines, ln)
                lbl = label.new(v_ts_12, lp,
                     (sgn == 0 ? "+" : "−") + str.tostring(mlt, "#.##") + "σ",
                     xloc=xloc.bar_time, yloc=yloc.price,
                     style=label.style_none,
                     color=color.new(color.white, 100),
                     textcolor=i_ladder_col, size=size.tiny,
                     text_font_family=font.family_monospace)
                array.push(d_ladder_lbls, lbl)

// ── 08:00–12:00 — touch & reversion tracking ─────────────────────────────────
if v_active and in_win and v_outcome <= 1
    if v_touch_side == 0
        if high >= v_upL or low <= v_dnL
            [t_side0, t_time0, t_idx0] = find_touch(v_upL, v_dnL)
            int f_side = t_side0
            int f_time = t_time0
            int f_idx  = t_idx0
            // [FIX-2] no intrabar data (deep history) → chart-level classify
            if f_side == 0
                hitU = high >= v_upL
                hitD = low <= v_dnL
                if hitU and hitD
                    f_side := v_upL - open <= open - v_dnL ? 1 : 2
                else
                    f_side := hitU ? 1 : 2
                f_time := time
                f_idx  := -1

            v_touch_side := f_side
            v_touch_time := na(f_time) ? time : f_time
            v_touch_band := hour(v_touch_time, "America/New_York")
            v_touch_bar  := bar_index
            v_outcome    := 1
            v_max_ext    := f_side == 1 ? (high - v_tbr_open) / v_sigma_px : (v_tbr_open - low) / v_sigma_px
            S := get_stats(v_touch_band, f_side)
            [b9, b10, b11, b12] = get_milestones(v_touch_band, f_side)
            m_by09 := b9
            m_by10 := b10
            m_by11 := b11
            m_by12 := b12

            label.delete(d_touch_lbl)
            tp = f_side == 1 ? v_upL : v_dnL
            t_txt = (f_side == 1 ? "▼ +" : "▲ −") + str.tostring(i_mult, "#.##") + "σ " + str.format_time(v_touch_time, "HH:mm", "America/New_York")
            t_sty = f_side == 1 ? label.style_label_down : label.style_label_up
            t_bg  = color.new(f_side == 1 ? color.red : color.green, 30)
            d_touch_lbl := label.new(v_touch_time, tp, t_txt,
                 xloc=xloc.bar_time, yloc=yloc.price,
                 style=t_sty, color=t_bg,
                 textcolor=color.white, size=tbl_sz(i_lbl_size),
                 text_font_family=font.family_monospace)

            draw_zones(f_side, v_touch_time, v_ts_12)

            if not na(d_open_lbl)
                label.set_text(d_open_lbl, "TARGET: TBR OPEN")

            // Touch-bar reversion: LTF scan from the touch minute onward is
            // the ONLY authority on this bar ([FIX-1])
            rt = find_rev(f_idx, f_side, v_tbr_open)
            if not na(rt)
                v_rev_time := rt
                v_outcome  := 2

    if v_touch_side != 0 and v_outcome == 1
        ext = v_touch_side == 1 ? (high - v_tbr_open) / v_sigma_px : (v_tbr_open - low) / v_sigma_px
        v_max_ext := math.max(v_max_ext, ext)

        // [FIX-1] skip the sequence-blind chart-level check on the touch bar —
        // a pre-touch dip to open would otherwise register as a reversion
        if bar_index != v_touch_bar
            hit = (v_touch_side == 1 and low <= v_tbr_open) or (v_touch_side == 2 and high >= v_tbr_open)
            if hit
                rt = find_rev(0, v_touch_side, v_tbr_open)
                v_rev_time := na(rt) ? time : rt
                v_outcome  := 2

    if v_outcome == 2 and v_outcome[1] != 2
        if not na(d_open_lbl)
            rev_txt = "REVERTED ✓ " + str.format_time(v_rev_time, "HH:mm", "America/New_York")
            label.set_text(d_open_lbl, rev_txt)
            label.set_textcolor(d_open_lbl, color.green)
        draw_mfe(v_touch_side, v_rev_time, v_ts_12)

// ── 12:00 — hard stop, settle outcome ────────────────────────────────────────
if v_active and win_end
    if v_touch_side == 0
        v_outcome := 4
        if not na(d_open_lbl)
            label.set_text(d_open_lbl, "NO TOUCH")
            label.set_textcolor(d_open_lbl, color.gray)
    else if v_outcome == 1
        v_outcome := 3
        if not na(d_open_lbl)
            label.set_text(d_open_lbl, "NO REVERSION ✗")
            label.set_textcolor(d_open_lbl, color.red)

bgcolor(v_active and in_win ? color.new(i_col, 96) : na)

// ============================================================================
// ALERTS
// ============================================================================

touch_evt = v_touch_side != 0 and v_touch_side[1] == 0
rev_evt   = v_outcome == 2 and v_outcome[1] != 2
alertcondition(touch_evt, "AM TBR — Level Touched", "AM TBR: ±0.25σ level touched. Reversion-to-open study active.")
alertcondition(rev_evt, "AM TBR — Reverted to Open", "AM TBR: price reverted to TBR Open. MFE continuation targets drawn.")

// ============================================================================
// DASHBOARD  — single table, website-style stat blocks
// ============================================================================

f_cellm(t, c0, c1, r, txt, bg, tc, tsz, hal) =>
    table.cell(t, c0, r, txt, bgcolor=bg, text_color=tc, text_size=tsz,
         text_halign=hal, text_font_family=font.family_monospace)
    if c1 > c0
        for c = c0 + 1 to c1
            table.cell(t, c, r, "", bgcolor=bg, text_size=tsz)
        table.merge_cells(t, c0, r, c1, r)

f_info(t, r, lbl, val, val_bg, val_tc, tsz) =>
    f_cellm(t, 0, 2, r, lbl, color.white, color.rgb(40, 40, 40), tsz, text.align_left)
    f_cellm(t, 3, 5, r, val, val_bg, val_tc, tsz, text.align_right)

f_stat(t, r, name, sgn, nv, mn, md, p75, p90, name_tc, tsz) =>
    CEL_TX = color.rgb(40, 40, 40)
    table.cell(t, 0, r, name, bgcolor=color.rgb(245, 245, 245),
         text_color=name_tc, text_size=tsz, text_halign=text.align_left,
         text_font_family=font.family_monospace)
    table.cell(t, 1, r, str.tostring(nv, "#"), bgcolor=color.white,
         text_color=color.rgb(120, 120, 120), text_size=tsz,
         text_font_family=font.family_monospace)
    table.cell(t, 2, r, sgn + str.tostring(mn, "#.##") + "σ", bgcolor=color.white,
         text_color=CEL_TX, text_size=tsz, text_font_family=font.family_monospace)
    table.cell(t, 3, r, sgn + str.tostring(md, "#.##") + "σ", bgcolor=color.white,
         text_color=CEL_TX, text_size=tsz, text_font_family=font.family_monospace)
    table.cell(t, 4, r, sgn + str.tostring(p75, "#.##") + "σ", bgcolor=color.white,
         text_color=CEL_TX, text_size=tsz, text_font_family=font.family_monospace)
    table.cell(t, 5, r, sgn + str.tostring(p90, "#.##") + "σ", bgcolor=color.white,
         text_color=CEL_TX, text_size=tsz, text_font_family=font.family_monospace)

var table tbl = na

if i_show_table and barstate.islast
    table.delete(tbl)
    tsz    = tbl_sz(i_tbl_size)
    HDR_BG = color.rgb(30, 30, 30)
    SUB_BG = color.rgb(60, 60, 60)
    CEL_TX = color.rgb(40, 40, 40)

    tbl := table.new(tbl_pos(i_tbl_pos), 6, 20,
         bgcolor=color.white, border_color=color.rgb(200, 200, 200),
         border_width=1, frame_color=color.rgb(64, 64, 64), frame_width=2)
    row = 0

    // ── Title ────────────────────────────────────────────────────────────────
    title_txt = "AM TBR · ±" + str.tostring(i_mult, "#.##") + "σ REVERSION · 08:00–12:00 NY"
    f_cellm(tbl, 0, 5, row, title_txt, HDR_BG, color.white, tsz, text.align_center)
    row += 1

    // ── Session info ─────────────────────────────────────────────────────────
    string sig_str = "—"
    if not na(v_sigma_pct)
        sig_str := str.tostring(v_sigma_pct, "#.##") + "% ≈ " + str.tostring(v_sigma_px, format.mintick)
    f_info(tbl, row, "σ (" + str.tostring(i_lookback) + "d sample)", sig_str, color.white, CEL_TX, tsz)
    row += 1

    string open_str = na(v_tbr_open) ? "—" : str.tostring(v_tbr_open, format.mintick)
    f_info(tbl, row, "TBR Open (08:00)", open_str, color.white, CEL_TX, tsz)
    row += 1

    string tstr = "Awaiting touch"
    if v_touch_side != 0
        tt = str.format_time(v_touch_time, "HH:mm", "America/New_York")
        tstr := (v_touch_side == 1 ? "+" : "−") + str.tostring(i_mult, "#.##") + "σ @ " + tt
        tstr := tstr + " · " + str.tostring(v_touch_band, "00") + ":xx band"
    f_info(tbl, row, "First Touch", tstr, color.white, CEL_TX, tsz)
    row += 1

    if v_touch_side != 0
        rate   = Sget(0)
        n_t    = Sget(1)
        smalln = Sget(2) > 0.5
        color rate_bg = color.new(color.red, 55)
        if rate >= 75.0
            rate_bg := color.new(color.green, 55)
        else if rate >= 65.0
            rate_bg := color.new(color.teal, 55)
        else if rate >= 45.0
            rate_bg := color.new(color.orange, 55)
        rate_str = str.tostring(rate, "#.#") + "%  (n=" + str.tostring(n_t, "#") + (smalln ? " ⚠)" : ")")
        f_info(tbl, row, "Band Reversion Rate", rate_str, rate_bg, color.white, tsz)
        row += 1

    string outc_str = "Waiting…"
    color  outc_bg  = color.new(color.gray, 85)
    color  outc_tx  = CEL_TX
    if v_outcome == 1
        outc_str := "Touched — Pending"
        outc_bg  := color.new(color.blue, 80)
    else if v_outcome == 2
        outc_str := "Reverted ✓ " + str.format_time(v_rev_time, "HH:mm", "America/New_York")
        outc_bg  := color.new(color.green, 55)
        outc_tx  := color.white
    else if v_outcome == 3
        outc_str := "No Reversion ✗"
        outc_bg  := color.new(color.red, 55)
        outc_tx  := color.white
    else if v_outcome == 4
        outc_str := "No Touch"
    f_info(tbl, row, "Outcome", outc_str, outc_bg, outc_tx, tsz)
    row += 1

    if v_touch_side != 0 and v_max_ext > 0
        string mae_ctx = "≥ non-rev p50 ⚠"
        color  mae_bg  = color.new(color.red, 60)
        if v_max_ext <= Sget(5)
            mae_ctx := "≤ p50"
            mae_bg  := color.new(color.green, 72)
        else if v_max_ext <= Sget(6)
            mae_ctx := "p50–p75"
            mae_bg  := color.new(color.green, 72)
        else if v_max_ext <= Sget(7)
            mae_ctx := "p75–p90"
            mae_bg  := color.new(color.orange, 65)
        else if v_max_ext <= Sget(10)
            mae_ctx := "> p90 ⚠"
        f_info(tbl, row, "MAE (σ from open)", str.tostring(v_max_ext, "#.##") + "σ · " + mae_ctx, mae_bg, CEL_TX, tsz)
        row += 1

    // ── Stat blocks (website style) ──────────────────────────────────────────
    if v_touch_side == 0
        f_cellm(tbl, 0, 5, row, "MAE / MFE stats populate on first touch", color.rgb(245, 245, 245), color.rgb(140, 140, 140), tsz, text.align_center)
        row += 1
    else
        mae_sgn = v_touch_side == 1 ? "+" : "-"
        mfe_sgn = v_touch_side == 1 ? "-" : "+"

        sec_hdr = (v_touch_side == 1 ? "+" : "−") + str.tostring(i_mult, "#.##") + " · " + str.tostring(v_touch_band, "00") + ":xx — |σ| FROM TBR OPEN"
        f_cellm(tbl, 0, 5, row, sec_hdr, SUB_BG, color.white, tsz, text.align_center)
        row += 1

        hdr_txt = array.from("", "n", "Mean", "Median", "P75", "P90")
        for c = 0 to 5
            table.cell(tbl, c, row, array.get(hdr_txt, c), bgcolor=HDR_BG,
                 text_color=color.rgb(160, 160, 160), text_size=tsz,
                 text_font_family=font.family_monospace)
        row += 1

        f_stat(tbl, row, "MAE Rev",   mae_sgn, Sget(3),  Sget(4),  Sget(5),  Sget(6),  Sget(7),  color.new(color.green, 20), tsz)
        row += 1
        f_stat(tbl, row, "MAE N-Rev", mae_sgn, Sget(8),  Sget(9),  Sget(10), Sget(11), Sget(12), color.new(color.red, 20), tsz)
        row += 1
        f_stat(tbl, row, "MFE",       mfe_sgn, Sget(13), Sget(14), Sget(15), Sget(16), Sget(17), color.new(color.blue, 20), tsz)
        row += 1

    // ── Cumulative reversion milestones + hard stop ──────────────────────────
    if i_show_miles and v_touch_side != 0
        f_cellm(tbl, 0, 5, row, "CUMULATIVE REVERSION", SUB_BG, color.white, tsz, text.align_center)
        row += 1

        now_ms   = timenow
        ms_lbls  = array.from("By 09:00", "By 10:00", "By 11:00", "By 12:00")
        ms_rates = array.from(m_by09, m_by10, m_by11, m_by12)
        ms_times = array.from(v_ts_09, v_ts_10, v_ts_11, v_ts_12)

        for pi = 0 to 3
            mts    = array.get(ms_times, pi)
            passed = now_ms >= mts
            rem_m  = math.round((mts - now_ms) / 60000.0)

            string status = str.tostring(rem_m) + "m left"
            color  st_bg  = rem_m > 30 ? color.new(#1a3a5c, 25) : color.new(color.orange, 55)
            color  st_tx  = color.white
            if v_outcome == 2
                status := "✓ done"
                st_bg  := color.new(color.green, 60)
            else if passed
                status := "—"
                st_bg  := color.rgb(240, 240, 240)
                st_tx  := color.rgb(150, 150, 150)

            f_cellm(tbl, 0, 1, row, array.get(ms_lbls, pi), color.white, CEL_TX, tsz, text.align_left)
            f_cellm(tbl, 2, 3, row, str.tostring(array.get(ms_rates, pi), "#.#") + "% cum", color.white, color.rgb(90, 90, 90), tsz, text.align_center)
            f_cellm(tbl, 4, 5, row, status, st_bg, st_tx, tsz, text.align_center)
            row += 1

        hs_rem    = math.round((v_ts_12 - now_ms) / 60000.0)
        hs_passed = now_ms >= v_ts_12
        hs_str = hs_passed ? "EXPIRED" : str.tostring(hs_rem) + "m left"
        hs_bg  = hs_passed ? color.new(color.red, 40) : color.rgb(40, 20, 20)
        hs_tc  = hs_passed ? color.white : color.new(color.red, 20)
        f_cellm(tbl, 0, 1, row, "Hard Stop", color.rgb(255, 235, 235), color.rgb(180, 0, 0), tsz, text.align_left)
        f_cellm(tbl, 2, 3, row, "12:00 NY", color.rgb(255, 235, 235), color.rgb(180, 0, 0), tsz, text.align_center)
        f_cellm(tbl, 4, 5, row, hs_str, hs_bg, hs_tc, tsz, text.align_center)

Overview

AM TBR — NQ Stats plots a sigma ladder showing touch levels at 0.5 to 2.0 standard deviations, marks touch points, displays Mean Absolute Error (MAE) zones after touches, and shows Mean Final Error (MFE) targets after reversion. A dashboard table displays cumulative milestones. Main settings adjust the lookback period, touch level multiplier, colors, label sizes, and visibility of elements. It draws directly on the price chart with optional previous sessions and a dashboard table.

Details

Published by

TamaraKGreen on GitHub

Code credit

lucymatos, as written in the script's header

Type

Indicator, Pine Script v6

Draws

On the price chart

Category

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

Markets

Indices

Timeframes

Any timeframe

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
SDEV Lookback (sessions)int20i_lookback
Touch Level (σ)float0.25i_mult
TBR Colourcolorcolor.new(color.gray, 0)i_col
Label SizestringTinyi_lbl_size
Keep previous sessions on chartboolfalsei_keep_hist
Show σ Ladder (±0.5→2.0)booltruei_show_ladder
Ladder Colourcolorcolor.new(color.gray, 72)i_ladder_col
Show MAE Zones after touchbooltruei_show_zones
Zone Labelsbooltruei_zone_lbls
Show MFE Targets after reversionbooltruei_show_mfe
Show Dashboardbooltruei_show_table
Show Cumulative Milestonesbooltruei_show_miles
Show all 14 settings
SettingTypeDefaultVariable
PositionstringTop Righti_tbl_pos
Text SizestringSmalli_tbl_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
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
257 stars

This indicator overlays an intraday SPX and ES chart with a midnight session range, regular-trading-hours opening levels, and a VWAP projection box. It draws the midnight high, mid, and low, flags edge rejections within an absolute tolerance, and plots optiona

Indicator v6 TrendSupport & resistance
Updated 6 weeks ago · None stated GitHub repository of SPX Midnight Box + Snag Logic

Golden Bullet Strategy

by TradersPost
218 stars

The Golden Bullet Strategy is an overlay signal indicator based on Smart Money Concepts and time-based liquidity models. It identifies liquidity sweeps of a designated reference session range during a specified hunt window. It then confirms Market Structure Sh

Indicator v6 SMC / ICT / price actionSessions & time
Updated 3 months ago · None stated GitHub repository of Golden Bullet Strategy
92 stars

This indicator classifies the market into gamma regime types (negative gamma trend, positive gamma mean-reversion, or transition) by calculating a composite score based on daily ATR ratios, session VWAP crossover frequencies, and opening range displacement. It

Indicator v5 Volatility & bandsTrend
Updated 8 weeks ago · Custom licence GitHub repository of Moon Dev GEX Regime & Trap

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