Indicator · Pine Script v6 · On the chart

Crypto Context Dashboard

by zabor-crypto

This indicator draws a visual present-state context panel on the price chart.

View on GitHub

GitHub stars20on the repository
Pine versionv6current
Lines of code243
Settings14inputs
LicenceMIT
Last update4 days ago25 Oct 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

243 lines of Pine Script v6 from indicators/tradingview/crypto_context_dashboard/crypto_context_dashboard.pine, published under MIT. Keep the author's credit and licence lines when you reuse it.

crypto_context_dashboard.pine
//@version=6
// Crypto Context Dashboard v2
// Visual-only present-state context panel.
// v2 uses the simplified "Market Lens" UI by default.
// Beginner mode shows Main / Summary / Tags / Changed.
// Trader and Full modes expose more detail.
// Not a strategy. No orders. No trade calls. Not forecasting.
// Formulas remain causal and trailing-only; HTF context uses confirmed candles only.

indicator("Crypto Context Dashboard v2", overlay = true, max_labels_count = 25)

// ============================ Inputs ============================
// v2 "Market Lens" UI: a simple card by default; advanced detail behind UI Mode.
showTable     = input.bool(true,  "Show table")
uiMode        = input.string("Beginner", "UI Mode", options = ["Beginner", "Trader", "Full"])
markerMode    = input.string("Off",  "Context markers", options = ["Off", "Dots", "Labels"])
markerHistory = input.string("Latest only", "Marker history", options = ["Latest only", "Recent changes"])
showStrip     = input.bool(false, "Show context strip")
showBg        = input.bool(false, "Show background")
emaLen      = input.int(50,  "EMA length",            minval = 1)
atrLen      = input.int(14,  "ATR length",            minval = 1)
percLB      = input.int(200, "Percentile lookback",   minval = 10)
bbLen       = input.int(20,  "BB length",             minval = 1)
bbMult      = input.float(2.0, "BB multiplier",       minval = 0.1, step = 0.1)
noiseLen    = input.int(10,  "Noise lookback",        minval = 3)
htfTF       = input.timeframe("1D", "HTF timeframe")
htfEmaLen   = input.int(20,  "HTF EMA length",        minval = 1)

// Fixed structural constants (not exposed as knobs)
SLOPE_LEN = 5
eps       = 1e-9

// ============================ Primitives ============================
atrV   = ta.atr(atrLen)
ema    = ta.ema(close, emaLen)

// ---- 3.1 Volatility State ----
atrPct = ta.percentrank(atrV, percLB)
basis  = ta.sma(close, bbLen)
dev    = bbMult * ta.stdev(close, bbLen)
bbWidth = (basis + dev - (basis - dev)) / close
bbwPct = ta.percentrank(bbWidth, percLB)

volLow  = atrPct <= 25 and bbwPct <= 25
volHigh = atrPct >= 75 or  bbwPct >= 75
volStr  = volLow ? "LOW_VOL" : volHigh ? "HIGH_VOL" : "NORMAL_VOL"
volCode = volLow ? 1 : volHigh ? 2 : 0

// ---- 3.2 Extension From Mean ----
emaDistAtr = (close - ema) / atrV
extStr = emaDistAtr >= 2.5 ? "VERY_EXTENDED_UP" : emaDistAtr >= 2.0 ? "EXTENDED_UP" : emaDistAtr <= -2.5 ? "VERY_EXTENDED_DOWN" : emaDistAtr <= -2.0 ? "EXTENDED_DOWN" : "NORMAL"
extNormal = extStr == "NORMAL"
extCode = extStr == "NORMAL" ? 0 : extStr == "EXTENDED_UP" ? 1 : extStr == "VERY_EXTENDED_UP" ? 2 : extStr == "EXTENDED_DOWN" ? 3 : 4

// ---- 3.3 Noise / Candle Quality (trailing-only) ----
rng       = high - low
bodyRatio = math.abs(close - open) / math.max(rng, eps)
wickRatio = ((high - math.max(open, close)) + (math.min(open, close) - low)) / math.max(rng, eps)

// A: median wick ratio over trailing window
wickNoise = ta.percentile_nearest_rank(wickRatio, noiseLen, 50)
// C: alternation rate over trailing window
flips = 0
pairs = 0
for k = 0 to noiseLen - 2
    d0 = math.sign(close[k] - open[k])
    d1 = math.sign(close[k + 1] - open[k + 1])
    if d0 != 0 and d1 != 0
        pairs += 1
        if d0 != d1
            flips += 1
altRate = pairs > 0 ? flips / pairs : 0.0
// D: efficiency ratio over trailing window
num = math.abs(close - close[noiseLen - 1])
den = 0.0
for k = 0 to noiseLen - 2
    den += math.abs(close[k] - close[k + 1])
effRatio = den > 0 ? num / den : 0.0
// B: fraction of small-body / large-range candles over trailing window
sblrCnt = 0
for k = 0 to noiseLen - 1
    br = math.abs(close[k] - open[k]) / math.max(high[k] - low[k], eps)
    rr = (high[k] - low[k]) / atrV[k]
    if br <= 0.35 and rr >= 0.75
        sblrCnt += 1
sblrFrac = sblrCnt / noiseLen
// E: range instability vs its own trailing 70th percentile (historical)
rangeInstab = ta.stdev(rng / atrV, noiseLen)
riqPct = ta.percentrank(rangeInstab, percLB)

condA = wickNoise   >= 0.55
condB = sblrFrac    >= 0.40
condC = altRate     >= 0.55
condD = effRatio    <= 0.35
condE = riqPct      >= 70
noiseCount = (condA ? 1 : 0) + (condB ? 1 : 0) + (condC ? 1 : 0) + (condD ? 1 : 0) + (condE ? 1 : 0)
noiseStr  = noiseCount >= 3 ? "CHOPPY" : noiseCount == 2 ? "MIXED" : "CLEAN"
noiseCode = noiseCount >= 3 ? 2 : noiseCount == 2 ? 1 : 0
isWicky       = condA
isInefficient = condD

// ---- 3.4 Trend Context ----
emaSlope = ema - ema[SLOPE_LEN]
trendStr = close > ema and emaSlope > 0 ? "UP_CONTEXT" : close < ema and emaSlope < 0 ? "DOWN_CONTEXT" : "MIXED_CONTEXT"
trendCode = trendStr == "UP_CONTEXT" ? 1 : trendStr == "DOWN_CONTEXT" ? 2 : 0

// ---- 3.5 MTF Context (non-repainting: confirmed HTF only) ----
// Read PREVIOUS confirmed HTF values with lookahead disabled so an intrabar LTF
// bar never sees the current incomplete HTF candle.
htfEmaSeries = ta.ema(close, htfEmaLen)
htfClose   = request.security(syminfo.tickerid, htfTF, close[1],            lookahead = barmerge.lookahead_off)
htfEma     = request.security(syminfo.tickerid, htfTF, htfEmaSeries[1],     lookahead = barmerge.lookahead_off)
htfEmaPrev = request.security(syminfo.tickerid, htfTF, htfEmaSeries[2],     lookahead = barmerge.lookahead_off)
htfSlope = htfEma - htfEmaPrev
htfUp   = htfClose > htfEma and htfSlope > 0
htfDown = htfClose < htfEma and htfSlope < 0
htfStr  = htfUp ? "HTF_UP" : htfDown ? "HTF_DOWN" : "HTF_MIXED"

ltfUp   = trendStr == "UP_CONTEXT"
ltfDown = trendStr == "DOWN_CONTEXT"
mtfStr = ltfUp and htfUp ? "ALIGNED_UP" : ltfDown and htfDown ? "ALIGNED_DOWN" : ltfUp and htfDown ? "CONFLICT_UP" : ltfDown and htfUp ? "CONFLICT_DOWN" : "MIXED"
isMtfConflict = mtfStr == "CONFLICT_UP" or mtfStr == "CONFLICT_DOWN"
isMtfAligned  = mtfStr == "ALIGNED_UP" or mtfStr == "ALIGNED_DOWN"
mtfCode = isMtfConflict ? 2 : isMtfAligned ? 1 : 0

// ============================ 4. Final Dashboard State ============================
// Display-priority grouping only (descriptive present-state, not a trade call).
finalStr = isMtfConflict ? "MTF_CONFLICT_CONTEXT" : noiseStr == "CHOPPY" ? "CHOPPY_CONTEXT" : not extNormal ? "EXTENDED_CONTEXT" : volLow ? "LOW_VOL_CONTEXT" : volHigh ? "HIGH_VOL_CONTEXT" : ltfUp ? "CLEAN_UP_CONTEXT" : ltfDown ? "CLEAN_DOWN_CONTEXT" : "MIXED_CONTEXT"
finalCode = isMtfConflict ? 0 : noiseStr == "CHOPPY" ? 1 : not extNormal ? 2 : volLow ? 3 : volHigh ? 4 : ltfUp ? 5 : ltfDown ? 6 : 7

// ============================ 5. Human-readable display text ============================
// Internal enum codes above are unchanged; these strings are user-facing only.
finalDisp = finalStr == "MTF_CONFLICT_CONTEXT" ? "MTF Conflict Context" : finalStr == "CHOPPY_CONTEXT" ? "Choppy Context" : finalStr == "EXTENDED_CONTEXT" ? "Extended Context" : finalStr == "LOW_VOL_CONTEXT" ? "Low Vol Context" : finalStr == "HIGH_VOL_CONTEXT" ? "High Vol Context" : finalStr == "CLEAN_UP_CONTEXT" ? "Clean Up Context" : finalStr == "CLEAN_DOWN_CONTEXT" ? "Clean Down Context" : "Mixed Context"

trendDisp = trendStr == "UP_CONTEXT" ? "Up" : trendStr == "DOWN_CONTEXT" ? "Down" : "Mixed"
mtfDisp   = isMtfConflict ? "Conflict" : isMtfAligned ? "Aligned" : "Mixed"
volDisp   = volLow ? "Low" : volHigh ? "High" : "Normal"
extDisp   = extStr == "VERY_EXTENDED_UP" ? "Very Extended Up" : extStr == "EXTENDED_UP" ? "Extended Up" : extStr == "VERY_EXTENDED_DOWN" ? "Very Extended Down" : extStr == "EXTENDED_DOWN" ? "Extended Down" : "Normal"
noiseBase = noiseStr == "CHOPPY" ? "Choppy" : noiseStr == "MIXED" ? "Mixed" : "Clean"
noiseDisp = noiseBase + (isWicky ? " · Wicky" : "") + (isInefficient ? " · Inefficient" : "")

// Compact neutral component tokens (used by the Tags row).
trendTok = ltfUp ? "Trend Up" : ltfDown ? "Trend Down" : "Mixed Trend"
mtfTok   = isMtfAligned ? "MTF Aligned" : isMtfConflict ? "MTF Conflict" : "MTF Mixed"
volTok   = volLow ? "Low Vol" : volHigh ? "High Vol" : "Normal Vol"

// ---- v2 Market Lens display text (simplified, user-facing) ----
// Main: simplified plain-language label for the present-state context.
mainDisp = finalStr == "LOW_VOL_CONTEXT" ? "Quiet + Mixed" : finalStr == "HIGH_VOL_CONTEXT" ? "Active" : finalStr == "EXTENDED_CONTEXT" ? "Stretched" : finalStr == "CHOPPY_CONTEXT" ? "Noisy + Mixed" : finalStr == "MTF_CONFLICT_CONTEXT" ? "Timeframe Conflict" : finalStr == "CLEAN_UP_CONTEXT" ? "Up Context" : finalStr == "CLEAN_DOWN_CONTEXT" ? "Down Context" : "Mixed"
// Summary: one brief neutral line. Present-state only.
summaryStr = finalStr == "LOW_VOL_CONTEXT" ? "Low-volatility chart, unclear direction." : finalStr == "HIGH_VOL_CONTEXT" ? "Elevated volatility, present-state only." : finalStr == "EXTENDED_CONTEXT" ? "Price is far from EMA, present-state only." : finalStr == "CHOPPY_CONTEXT" ? "Noisy recent candles, unclear direction." : finalStr == "MTF_CONFLICT_CONTEXT" ? "Lower and higher timeframe differ." : finalStr == "CLEAN_UP_CONTEXT" ? "Chart context points upward." : finalStr == "CLEAN_DOWN_CONTEXT" ? "Chart context points downward." : "Mixed chart context."
// Tags: up to 3 compact neutral tags summarizing the detail rows.
tagsStr = mtfTok + " · " + trendTok + " · " + volTok

// Compact two-letter code for chart markers (display-only).
codeStr = finalStr == "LOW_VOL_CONTEXT" ? "LV" : finalStr == "HIGH_VOL_CONTEXT" ? "HV" : finalStr == "EXTENDED_CONTEXT" ? "EX" : finalStr == "CHOPPY_CONTEXT" ? "CH" : finalStr == "MTF_CONFLICT_CONTEXT" ? "MC" : finalStr == "CLEAN_UP_CONTEXT" ? "UP" : finalStr == "CLEAN_DOWN_CONTEXT" ? "DN" : "MX"

// Last Change tracking: remember the display label we changed FROM and the bar of change.
isChange = barstate.isconfirmed and not na(finalCode[1]) and finalCode != finalCode[1]
var string fromDisp  = na
var int    changeBar = na
if isChange
    fromDisp  := finalDisp[1]
    changeBar := bar_index
barsAgo = na(changeBar) ? na : bar_index - changeBar
agoStr  = na(barsAgo) ? "" : barsAgo <= 0 ? " · now" : " · " + str.tostring(barsAgo) + (barsAgo == 1 ? " bar ago" : " bars ago")
// Trader/Full: full previous → current transition. Beginner: simplified.
lastChangeStr   = na(fromDisp) ? "Initializing" : fromDisp + " → " + finalDisp + agoStr
changedSimpleStr = na(fromDisp) ? "Initializing" : barsAgo <= 0 ? "Changed: now" : "Changed: " + str.tostring(barsAgo) + (barsAgo == 1 ? " bar ago" : " bars ago")

// Muted category color per final state (no green/red directional color coding).
stateHue = finalStr == "LOW_VOL_CONTEXT" ? color.blue : finalStr == "HIGH_VOL_CONTEXT" ? color.purple : finalStr == "EXTENDED_CONTEXT" ? color.orange : finalStr == "CHOPPY_CONTEXT" ? color.gray : finalStr == "MTF_CONFLICT_CONTEXT" ? color.rgb(71, 85, 105) : finalStr == "CLEAN_UP_CONTEXT" ? color.teal : finalStr == "CLEAN_DOWN_CONTEXT" ? color.navy : color.gray

// ============================ 6. Visual Layout ============================
// Full background: muted category color, very transparent (>=92), off by default.
cBg = color.new(stateHue, 93)
bgcolor(showBg ? cBg : na, title = "Context background")

// Context strip: subtle per-bar trace at the bottom of the pane (no full-height fill).
plotchar(showStrip ? 0 : na, title = "Context strip", char = "•", location = location.bottom, color = color.new(stateHue, 35), size = size.tiny)

// ---- Market Lens table (title / Main / Summary / Tags / Changed / optional detail) ----
txtCol  = chart.fg_color
titleBg = color.new(color.gray, 78)
mainBg  = color.new(color.gray, 86)

var table dash = table.new(position.top_right, 2, 11, frame_color = color.new(color.gray, 70), frame_width = 1, border_color = color.new(color.gray, 82), border_width = 1)

f_title(int r) =>
    table.cell(dash, 0, r, "Market Lens", text_color = txtCol, text_size = size.normal, text_halign = text.align_left, bgcolor = titleBg)
    table.cell(dash, 1, r, "", bgcolor = titleBg)

// weight: 2 = Main (largest), 1 = secondary card row, 0 = detail row
f_cell(int r, string k, string v, int weight) =>
    bgc = weight >= 1 ? mainBg : color.new(color.gray, 100)
    sz  = weight == 2 ? size.large : weight == 1 ? size.normal : size.small
    table.cell(dash, 0, r, k, text_color = txtCol, text_size = sz, text_halign = text.align_left,  bgcolor = bgc)
    table.cell(dash, 1, r, v, text_color = txtCol, text_size = sz, text_halign = text.align_right, bgcolor = bgc)

if showTable and barstate.islast
    table.clear(dash, 0, 0, 1, 10)
    f_title(0)
    // Card rows shared by every mode.
    f_cell(1, "Main",    mainDisp,    2)
    f_cell(2, "Summary", summaryStr,  1)
    f_cell(3, "Tags",    tagsStr,     1)
    if uiMode == "Beginner"
        f_cell(4, "Changed", changedSimpleStr, 1)
    else if uiMode == "Full"
        f_cell(4, "Changed",        lastChangeStr, 1)
        f_cell(5, "Trend",          trendDisp,     0)
        f_cell(6, "MTF",            mtfDisp,       0)
        f_cell(7, "Volatility",     volDisp,       0)
        f_cell(8, "Extension",      extDisp,       0)
        f_cell(9, "Noise",          noiseDisp,     0)
        f_cell(10, "Internal State", finalStr,     0)
    else
        f_cell(4, "Changed",    lastChangeStr, 1)
        f_cell(5, "Trend",      trendDisp,     0)
        f_cell(6, "MTF",        mtfDisp,       0)
        f_cell(7, "Volatility", volDisp,       0)

// When the table is toggled off, clear any previously drawn cells so it disappears.
if not showTable and barstate.islast
    table.clear(dash, 0, 0, 1, 10)

// ---- Optional compact context-change markers (neutral, no arrows) ----
// Dots: compact two-letter code only. Labels: brief "Context → <state>" text. No drivers.
// "Latest only" keeps a single marker; "Recent changes" keeps history (capped at 25).
var label lastMk = na
if (markerMode == "Dots" or markerMode == "Labels") and isChange
    if markerHistory == "Latest only" and not na(lastMk)
        label.delete(lastMk)
    mkTxt = markerMode == "Dots" ? codeStr : "Context → " + finalDisp
    mkSz  = markerMode == "Dots" ? size.tiny : size.small
    lastMk := label.new(bar_index, high, mkTxt, style = label.style_none, textcolor = txtCol, size = mkSz)

// ============================ 7. Alerts (neutral, state-change only) ============================
alertcondition(finalCode != finalCode[1], "State changed",            "Crypto Context Dashboard: context state changed")
alertcondition(mtfCode   != mtfCode[1],   "MTF context changed",      "Crypto Context Dashboard: MTF context changed")
alertcondition(volCode   != volCode[1],   "Volatility state changed", "Crypto Context Dashboard: volatility state changed")
alertcondition(extCode   != extCode[1],   "Extension state changed",  "Crypto Context Dashboard: extension state changed")
alertcondition(noiseCode != noiseCode[1], "Noise state changed",      "Crypto Context Dashboard: noise state changed")

Overview

Crypto Context Dashboard classifies volatility from ATR and Bollinger bandwidth percentiles, measures how far price sits from an EMA in ATR units, and scores trailing candle noise with wick ratio, direction alternation, and an efficiency ratio. A higher-timeframe EMA uses confirmed candles only. Settings change table detail, optional markers, strip and background, lookbacks, and the higher timeframe. It places no orders and does not forecast.

Details

Published by

zabor-crypto on GitHub

Draws

On the price chart

Markets

Crypto

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.

Topics

pythonrisk-managementcryptocurrencyevent-drivenmarket-datareproducible-researchalgorithmic-tradingbacktestinghyperliquidmarket-making

Settings

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

SettingTypeDefaultVariable
Show tablebooltrueshowTable
UI ModestringBeginneruiMode
Context markersstringOffmarkerMode
Marker historystringLatest onlymarkerHistory
Show context stripboolfalseshowStrip
Show backgroundboolfalseshowBg
EMA lengthint50emaLen
ATR lengthint14atrLen
Percentile lookbackint200percLB
BB lengthint20bbLen
BB multiplierfloat2.0bbMult
Noise lookbackint10noiseLen
Show all 14 settings
SettingTypeDefaultVariable
HTF timeframetimeframe1DhtfTF
HTF EMA lengthint20htfEmaLen
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

This overlay indicator plots a Supertrend from an ATR length and factor, four exponential moving averages, and a selectable VWAP or TWAP taken from a chosen timeframe on the price chart. It also shows MACD and stochastic with adjustable lengths, smoothing, ver

Indicator v5 Oscillators & momentumMoving averages
Updated 1 year ago · MPL-2.0 GitHub repository of Momentum Setup: Vijay Thakare Option Buying Scalping Setup

8zz反指標v2

by hansai-art
121 stars

The 8zz Contrarian Indicator plots reverse sentiment trading signals directly on the price chart based on tracked retail post activity. It generates bullish signals during retail panic and bearish signals during market euphoria. The indicator features adjustab

Indicator v6 Moving averagesOscillators & momentum
Updated 3 months ago · None stated GitHub repository of 8zz反指標v2
43 stars

This indicator evaluates market momentum and risk by calculating trend metrics and volatility stats, displaying them in an on-chart table pane. It measures short-term and long-term trend strength via two MACD configurations built on three customizable EMA leng

Indicator v5 TrendVolatility & bands
Updated 1 year ago · None stated GitHub repository of Trend Strength & Volatility
20 stars

This indicator calculates a base moving average (EMA, SMA, WMA, or HMA) and projects multiple symmetrical envelope bands around it directly on the price chart. Users can adjust the length, source, moving average type, and initial envelope multiplier. Additiona

Indicator v5 Moving averagesVolatility & bands
Updated 1 year ago · None stated GitHub repository of Multiple Moving Average Envelope

Bottom Level Finder

by chris-c-thomas
14 stars

Bottom Level Finder is a support identification indicator designed to locate correction and re-entry zones directly on the price chart. It calculates horizon-adaptive Fibonacci retracements, drawdown percentage levels, moving averages, and integrated VIX volat

Indicator v6 Support & resistanceMoving averages
Updated 6 months ago · MIT GitHub repository of Bottom Level Finder
9 stars

Calculates volume-weighted average price anchored to automatically detected swing pivots, period starts, or a custom manual timestamp. It plots volume-weighted standard deviation bands with configurable multiples and measures price distance from VWAP in standa

Indicator v6 Moving averagesVolatility & bands Repaints
Updated 2 days ago · MIT GitHub repository of Anchored VWAP [WavesUnchained]

More Screeners & dashboards scripts

Related on ForexR

Trade Your Charts With a Regulated Broker

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

Compare brokers