Desk Tool Nº 02 · Free · TradingView

Extremes Clock

Your chart already shows you where the session high and low are. It does not show you how long they have stood — and a high that has held since 10:05 is not the same object as one printed ninety seconds ago. This puts a clock on both of them.

What it draws

One idea
3h 12m 4m STOOD high 3h 12m low 4m

Each session's high and low are drawn as a ray from the bar that made them, carrying a running clock. The ray starts faint and thin and gains colour and weight as the extreme ages, so the two are never confusable at a glance. When a new extreme prints, the clock resets and the ray goes pale again.

What it is not

It does not forecast

There is no probability on this chart, no target, no bias and no signal. The clock is a lookup on something the chart already knows — it reports elapsed time and nothing else.

It does not repaint

An extreme is stamped at the bar that made it and the age is measured to that bar's own time on history, to the live clock only on the last bar. Scroll back and the chart reads exactly as it did at the time.

Install

About 30 seconds
  1. Copy the source below.
  2. In TradingView, open Pine Editor (bottom panel of any chart).
  3. Paste over whatever is in the editor, then click Add to chart.
  4. Optionally Save it so it appears under your own indicators.
Pine version
v6
Licence
MPL 2.0
Default session
0930–1600 ET
Works on
Any intraday chart
AXIS-Extremes-Clock.pine
// This work is licensed under the Mozilla Public License 2.0
// https://mozilla.org/MPL/2.0/
// AXIS Desk

//@version=6
indicator("AXIS Extremes Clock", "AXIS EXTREMES", overlay = true, max_lines_count = 500, max_labels_count = 500)

// =============================================================================
//  AXIS EXTREMES CLOCK
//
//  The chart already tells you WHERE the session high and low are.
//  It does not tell you HOW LONG they have stood.
//
//  This draws each session's high and low as a ray from the bar that made it,
//  and puts a running clock on it. The ray strengthens as the extreme ages, so
//  a high that has held all morning does not look like one printed a minute ago.
//
//  That is the whole study. It forecasts nothing and it signals nothing.
//  It is a clock on something the chart already knows.
// =============================================================================


// ---- inputs -----------------------------------------------------------------
g_s   = "Session"
i_sess = input.session("0930-1600", "Session",  group = g_s, tooltip = "The window whose high and low are tracked. Regular US cash hours by default. Use 1800-1700 for a full Globex day.")
i_tz   = input.string("America/New_York", "Timezone", options = ["America/New_York", "America/Chicago", "America/Los_Angeles", "Europe/London", "Europe/Berlin", "Asia/Tokyo", "Asia/Shanghai", "Australia/Sydney", "UTC"], group = g_s)

g_d   = "Display"
i_hist  = input.int(5, "Sessions kept", minval = 1, maxval = 60, group = g_d, tooltip = "How many past sessions stay drawn.")
i_lbl   = input.bool(true,  "Clock labels on the rays", group = g_d)
i_table = input.bool(true,  "Corner readout",           group = g_d)
i_pos   = input.string("Top right", "Readout corner", options = ["Top right", "Top left", "Bottom right", "Bottom left"], group = g_d)
i_size  = input.string("Small", "Text size", options = ["Tiny", "Small", "Normal", "Large"], group = g_d)

g_a   = "Age ramp"
i_full  = input.int(180, "Full intensity at (minutes)", minval = 5, maxval = 1440, group = g_a, tooltip = "A fresh extreme draws faint and thin. It reaches full colour and full width once it has stood this long.")
i_wmin  = input.int(1, "Width when fresh", minval = 1, maxval = 4, group = g_a)
i_wmax  = input.int(3, "Width when settled", minval = 1, maxval = 4, group = g_a)

g_c   = "Colour"
i_cHi   = input.color(color.new(#2FA98A, 0), "High", group = g_c, inline = "c1")
i_cLo   = input.color(color.new(#C9705F, 0), "Low",  group = g_c, inline = "c1")
i_fade  = input.int(72, "Fade when fresh (%)", minval = 0, maxval = 95, group = g_c)


// ---- session state ----------------------------------------------------------
inSess  = not na(time(timeframe.period, i_sess, i_tz))
newSess = inSess and not inSess[1]

var float hiPx   = na
var float loPx   = na
var int   hiMs   = na
var int   loMs   = na
var int   hiBar  = na
var int   loBar  = na
var line  hiLine = na
var line  loLine = na
var label hiLab  = na
var label loLab  = na


// ---- helpers ----------------------------------------------------------------
// Elapsed milliseconds rendered as "4h 07m" / "38m". Nothing below a minute:
// a sub-minute extreme is noise and a ticking seconds field reads as precision
// this study does not have.
f_dur(int ms) =>
    int mins = na(ms) or ms < 0 ? 0 : int(math.floor(ms / 60000))
    int h    = int(math.floor(mins / 60))
    int m    = mins % 60
    h > 0 ? str.tostring(h) + "h " + str.format("{0,number,00}", m) + "m" : str.tostring(m) + "m"

// 0.0 the instant it prints, 1.0 once it has stood i_full minutes.
f_ramp(int ms) =>
    na(ms) ? 0.0 : math.min(1.0, math.max(0.0, ms / (i_full * 60000.0)))

f_size(string s) =>
    switch s
        "Tiny"   => size.tiny
        "Small"  => size.small
        "Normal" => size.normal
        =>          size.large

txtSize = f_size(i_size)


// ---- track the extremes -----------------------------------------------------
if newSess
    hiPx  := high
    loPx  := low
    hiMs  := time
    loMs  := time
    hiBar := bar_index
    loBar := bar_index

    hiLine := line.new(bar_index, high, bar_index, high, xloc = xloc.bar_index, color = i_cHi, width = i_wmin)
    loLine := line.new(bar_index, low,  bar_index, low,  xloc = xloc.bar_index, color = i_cLo, width = i_wmin)

    if i_lbl
        hiLab := label.new(bar_index, high, "", xloc = xloc.bar_index, yloc = yloc.price, style = label.style_label_left, color = #00000000, textcolor = i_cHi, size = txtSize)
        loLab := label.new(bar_index, low,  "", xloc = xloc.bar_index, yloc = yloc.price, style = label.style_label_left, color = #00000000, textcolor = i_cLo, size = txtSize)

    // Retire anything older than the kept window.
    if array.size(line.all) > 0
        while array.size(line.all) > i_hist * 2
            line.delete(array.shift(line.all))
    if array.size(label.all) > 0
        while array.size(label.all) > i_hist * 2
            label.delete(array.shift(label.all))

else if inSess and not na(hiPx)
    if high > hiPx
        hiPx  := high
        hiMs  := time
        hiBar := bar_index
    if low < loPx
        loPx  := low
        loMs  := time
        loBar := bar_index


// ---- draw -------------------------------------------------------------------
// Age is measured to the live clock on the last bar and to the bar's own time
// everywhere else, so replayed history reads exactly as it did at the time.
nowMs = barstate.islast ? timenow : time

if inSess and not na(hiLine)
    hiAge = nowMs - hiMs
    loAge = nowMs - loMs
    hiR   = f_ramp(hiAge)
    loR   = f_ramp(loAge)

    line.set_xy1(hiLine, hiBar, hiPx)
    line.set_xy2(hiLine, bar_index, hiPx)
    line.set_color(hiLine, color.new(i_cHi, int(i_fade * (1 - hiR))))
    line.set_width(hiLine, int(math.round(i_wmin + (i_wmax - i_wmin) * hiR)))

    line.set_xy1(loLine, loBar, loPx)
    line.set_xy2(loLine, bar_index, loPx)
    line.set_color(loLine, color.new(i_cLo, int(i_fade * (1 - loR))))
    line.set_width(loLine, int(math.round(i_wmin + (i_wmax - i_wmin) * loR)))

    if i_lbl and not na(hiLab)
        label.set_xy(hiLab, bar_index, hiPx)
        label.set_text(hiLab, "  " + f_dur(hiAge))
        label.set_textcolor(hiLab, color.new(i_cHi, int(i_fade * (1 - hiR))))
        label.set_xy(loLab, bar_index, loPx)
        label.set_text(loLab, "  " + f_dur(loAge))
        label.set_textcolor(loLab, color.new(i_cLo, int(i_fade * (1 - loR))))


// ---- corner readout ---------------------------------------------------------
tPos = switch i_pos
    "Top right"    => position.top_right
    "Top left"     => position.top_left
    "Bottom right" => position.bottom_right
    =>                position.bottom_left

var table t = na
if i_table and barstate.islast
    if na(t)
        t := table.new(tPos, 2, 3, border_width = 1)
    hiAge = nowMs - hiMs
    loAge = nowMs - loMs

    table.cell(t, 0, 0, "STOOD", text_color = color.new(color.gray, 0), text_size = txtSize, text_halign = text.align_left)
    table.cell(t, 1, 0, "", text_size = txtSize)

    table.cell(t, 0, 1, "high", text_color = i_cHi, text_size = txtSize, text_halign = text.align_left)
    table.cell(t, 1, 1, inSess ? f_dur(hiAge) : "--", text_color = i_cHi, text_size = txtSize, text_halign = text.align_right)

    table.cell(t, 0, 2, "low", text_color = i_cLo, text_size = txtSize, text_halign = text.align_left)
    table.cell(t, 1, 2, inSess ? f_dur(loAge) : "--", text_color = i_cLo, text_size = txtSize, text_halign = text.align_right)


// ---- plots for alerts / data window -----------------------------------------
// Exposed as minutes so an alert or another study can read the clock directly.
plot(inSess ? (nowMs - hiMs) / 60000.0 : na, "High stood (min)", display = display.data_window)
plot(inSess ? (nowMs - loMs) / 60000.0 : na, "Low stood (min)",  display = display.data_window)
plot(inSess ? hiPx : na, "Session high", display = display.data_window)
plot(inSess ? loPx : na, "Session low",  display = display.data_window)

Futures traders: set the session to 1800-1700 for a full Globex day, or leave it at cash hours to clock the RTH extremes only. The two behave very differently and it is worth looking at both.

The rest of the desk

AXIS Desk publishes research on the NQ auction — what was measured, what the measurement was tested against, and what died. Nulls get published too.

Drops — dated findings · Notes — prose on craft · Tools — instruments · Join