MACD-V was conceived and developed by Alex Spiroglou CMT. His generous decision to publish the formula and share his research openly has made tools like this possible. For the original methodology, see his published work and presentations. This implementation is an educational adaptation — all intellectual credit belongs to him.
// ╔══════════════════════════════════════════════════════════════════════════════╗
// ║ MP-Spiro Trend EIS Model v6.0 — Alerts Edition ║
// ╠══════════════════════════════════════════════════════════════════════════════╣
// ║ ║
// ║ CORE METHODOLOGY — MACD-V by Alex Spiroglou CMT ║
// ║ ───────────────────────────────────────────────────────────────────────── ║
// ║ The volatility-normalised MACD at the heart of this indicator was ║
// ║ conceived and developed by Alex Spiroglou CMT. His decision to publish ║
// ║ the MACD-V formula and share his research openly with the trading ║
// ║ community is an act of genuine generosity. Without that openness, tools ║
// ║ like this would simply not exist. All intellectual credit for the core ║
// ║ MACD-V methodology belongs to him. For the original research and ║
// ║ methodology, seek out Alex Spiroglou's published work and presentations. ║
// ║ ║
// ║ ADDITIONAL METHODOLOGY ║
// ║ ───────────────────────────────────────────────────────────────────────── ║
// ║ Elder Impulse System (EIS) — Dr. Alexander Elder ║
// ║ Anchored VWAP concepts — Brian Shannon (AVWAP) ║
// ║ ║
// ║ IMPLEMENTATION ║
// ║ ───────────────────────────────────────────────────────────────────────── ║
// ║ © Gemini Thought Partner / Spiroglou Synthesis with Northnode ║
// ║ Developed for educational purposes. This script is not financial advice. ║
// ║ Nothing here constitutes a recommendation to buy or sell any instrument. ║
// ║ ║
// ║ HOW TO READ THIS SCRIPT ║
// ║ ───────────────────────────────────────────────────────────────────────── ║
// ║ The indicator has four interconnected layers: ║
// ║ 1. MACD-V — Volatility-normalised momentum (the core engine) ║
// ║ 2. 8-Stage Model — Classifies market phase using price + MACD-V ║
// ║ 3. Z-Score Intensity — Statistical measure of how extreme momentum is ║
// ║ 4. Elder Impulse System (EIS) — Short-term momentum confirmation ║
// ║ ║
// ╚══════════════════════════════════════════════════════════════════════════════╝
// This Pine Script® code is subject to the terms of the Mozilla Public License 2.0
// at https://mozilla.org/MPL/2.0/
//@version=6
indicator("MP-Spiro Trend EIS Model v6.0 Alerts", overlay=false, max_bars_back=2000)
// Note: overlay=false because this is a standalone pane indicator (not drawn on price).
// max_bars_back=2000 ensures the long EMAs (200-period MA, Z-score) have sufficient
// historical data to initialise correctly on instruments with deep history.
// ════════════════════════════════════════════════════════════════════════════════
// SECTION 1: INPUTS & SETTINGS
// ════════════════════════════════════════════════════════════════════════════════
// All user-facing controls are grouped here for clarity.
// Changes here propagate automatically throughout the script.
// ────────────────────────────────────────────────────────────────────────────────
// ── CORE ENGINE ─────────────────────────────────────────────────────────────────
// The two moving averages used to define the primary trend direction (bull/bear).
// Default: 50 SMA (intermediate trend) vs 200 SMA (long-term trend).
// The 8-stage engine uses this as its top-level filter before assessing MACD-V.
gp_engine = "📊 CORE ENGINE"
ma_fast_p = input.int(50, "Fast MA Period", group=gp_engine)
ma_slow_p = input.int(200, "Slow MA Period", group=gp_engine)
// ── MACD-V (OFFICIAL SPIROGLOU PARAMETERS) ──────────────────────────────────────
// MACD-V differs from standard MACD by dividing the MACD line by the ATR,
// normalising momentum for volatility. This makes readings comparable across
// instruments and time periods. The default parameters (12/26/9) match the
// classic MACD settings; the ATR length of 26 matches the slow EMA period,
// which is Spiroglou's recommended configuration.
//
// Key MACD-V thresholds used in this script:
// ±50 → Primary bull/bear momentum boundary
// ±150 → Extreme momentum (historically rare; often precedes exhaustion)
gp_macdv = "⚡ MACD-V (OFFICIAL)"
mv_fast = input.int(12, "Fast Len", group=gp_macdv)
mv_slow = input.int(26, "Slow Len", group=gp_macdv)
mv_atr = input.int(26, "ATR Len", group=gp_macdv)
mv_sig = input.int(9, "Signal Len", group=gp_macdv)
// ── STATISTICAL INTENSITY (EMA Z-SCORE) ─────────────────────────────────────────
// This module measures how statistically extreme the current MACD-V reading is
// relative to its own recent history. It converts the raw MACD-V into a
// Z-Score (standard deviations from the mean), then maps that to a 0–100%
// percentile using a logistic sigmoid function.
//
// Why EMA rather than a rolling window?
// An EMA-based variance uses all available history with exponential decay
// weighting, giving recent data more influence without a hard cutoff.
// This avoids the "cliff edge" effect of rolling-window lookbacks, where a
// single outlier bar dropping out of the window can cause sharp jumps in
// the statistic. The tradeoff is a slight initialisation lag on short histories.
//
// 'Decay Speed' (z_len): Controls how quickly the EMA "forgets" older data.
// A length of 50 means the EMA has a half-life of roughly 35 bars.
// 'Score Smoothing' (z_smooth): A short SMA over the final percentile output
// to reduce noise without distorting the signal.
gp_rel = "📊 STATISTICAL INTENSITY (EMA Z-SCORE)"
z_len = input.int(50, "Decay Speed (EMA Length)", minval=20, group=gp_rel,
tooltip="Uses an EMA (Infinite Memory) approach. A length of 50 roughly equates to a 50-bar memory.")
z_smooth = input.int(3, "Score Smoothing", minval=1, group=gp_rel)
show_bar = input.bool(true, "Paint Candles", group=gp_rel)
// Note: Candle painting applies the stage colour to the main chart candles,
// giving an at-a-glance view of the market phase without needing to watch
// the indicator pane constantly.
// ── DASHBOARD VISUALS ────────────────────────────────────────────────────────────
// A small on-chart table displaying the current stage name and intensity reading.
// Position and size are adjustable to avoid overlapping other indicators.
gp_dash = "🖥️ DASHBOARD VISUALS"
dash_pos = input.string("Top Right", "Dashboard Position",
options=["Top Right","Bottom Right","Top Left","Bottom Left","Middle Right","Bottom Center"],
group=gp_dash)
dash_size = input.string("Small", "Text Size",
options=["Tiny","Small","Normal","Large","Huge"],
group=gp_dash)
// ── HELPER FUNCTIONS: POSITION & SIZE MAPPING ───────────────────────────────────
// These convert the user's string selection into Pine Script's internal constants.
get_pos(p) =>
p == "Top Right" ? position.top_right :
p == "Bottom Right" ? position.bottom_right :
p == "Top Left" ? position.top_left :
p == "Bottom Left" ? position.bottom_left :
p == "Middle Right" ? position.middle_right :
position.bottom_center
get_size(s) =>
s == "Tiny" ? size.tiny :
s == "Small" ? size.small :
s == "Normal" ? size.normal :
s == "Large" ? size.large :
size.huge
// ════════════════════════════════════════════════════════════════════════════════
// SECTION 2: CORE CALCULATIONS
// ════════════════════════════════════════════════════════════════════════════════
// All momentum and trend calculations are performed here before the stage
// classification logic in Section 3. Order matters: these values feed Section 3.
// ────────────────────────────────────────────────────────────────────────────────
// ── MACD-V CALCULATION ───────────────────────────────────────────────────────────
// Standard MACD formula: fast EMA minus slow EMA.
// MACD-V modification: divide by ATR to normalise for volatility.
// Multiplying by 100 scales the result into a readable integer range.
// The ATR guard (vola != 0) prevents a division-by-zero error on instruments
// with zero price movement (e.g. on the very first bar of a new chart).
vola = ta.atr(mv_atr)
macd_v = vola != 0 ? (ta.ema(close, mv_fast) - ta.ema(close, mv_slow)) / vola * 100 : 0
// Signal line: EMA of MACD-V (smoothed version, used for crossover detection)
sig_v = ta.ema(macd_v, mv_sig)
// Histogram: distance between MACD-V and its signal line.
// When positive: MACD-V is above signal → accelerating momentum.
// When negative: MACD-V is below signal → decelerating momentum.
hist_v = macd_v - sig_v
// ── PRIMARY TREND FILTER (SMA-BASED) ────────────────────────────────────────────
// Determines whether price is in a bull or bear regime.
// Both conditions must be true for "bull": close above slow MA AND fast MA
// above slow MA (i.e. price and the intermediate trend agree with the
// long-term trend). This avoids classifying a brief price spike as bullish.
ma_fast = ta.sma(close, ma_fast_p)
ma_slow = ta.sma(close, ma_slow_p)
is_bull = close > ma_slow and ma_fast > ma_slow
// ── ELDER IMPULSE SYSTEM (EIS) ──────────────────────────────────────────────────
// Dr. Alexander Elder's Impulse System identifies bars where both trend and
// momentum are aligned in the same direction.
//
// IMPORTANT: The EIS here uses a STANDARD MACD (not MACD-V). This is by design —
// Elder's original system was built on standard MACD, and the EIS histogram is
// used purely as a momentum direction indicator (rising vs. falling), not for
// its absolute level. Using standard MACD here preserves the original meaning
// of the EIS while keeping MACD-V as the primary trend stage engine.
//
// EIS Green: EMA is rising AND MACD histogram is rising → full alignment long
// EIS Red: EMA is falling AND MACD histogram is falling → full alignment short
// EIS Blue: Mixed signals → no strong directional commitment
//
// The 13-period EMA is Elder's standard choice for the Impulse System.
eis_ema = ta.ema(close, 13)
eis_macd = ta.ema(close, 12) - ta.ema(close, 26)
eis_sig = ta.sma(eis_macd, 9) // Elder uses SMA for the signal line, not EMA
eis_hist = eis_macd - eis_sig
eis_green = eis_ema > eis_ema[1] and eis_hist > eis_hist[1]
eis_red = eis_ema < eis_ema[1] and eis_hist < eis_hist[1]
// ════════════════════════════════════════════════════════════════════════════════
// SECTION 2.5: STATISTICAL INTENSITY (EMA Z-SCORE)
// ════════════════════════════════════════════════════════════════════════════════
// This section converts the raw MACD-V reading into a statistical context score.
// The question it answers: "Is this momentum reading extreme relative to history,
// or is it just normal noise?" This helps avoid trading on momentum that merely
// looks large in absolute terms but is routine for this particular instrument.
//
// Pipeline:
// MACD-V → EMA Mean → EMA Variance → Std Dev → Z-Score → Percentile → Smoothed %
// ────────────────────────────────────────────────────────────────────────────────
// Step 1: Calculate the rolling mean using EMA
// This is the "expected" level of MACD-V given recent history.
mean_v = ta.ema(macd_v, z_len)
// Step 2: Calculate variance using EMA of squared deviations
// variance = E[(X - μ)²] — the EMA approximation applies exponential weighting
// to each bar's squared deviation from the mean, giving a smooth, lag-tolerant
// measure of dispersion. Note: mean_v and macd_v are both live on each bar,
// so there is a minor simultaneous-update bias — this is inherent to Pine Script's
// single-pass execution model and is negligible in practice.
variance_v = ta.ema(math.pow(macd_v - mean_v, 2), z_len)
std_v = math.sqrt(variance_v)
// Step 3: Z-Score — how many standard deviations from the mean?
// Positive Z = momentum above average; Negative Z = below average.
// The std_v guard prevents division-by-zero on flat/new instruments.
z_score = std_v != 0 ? (macd_v - mean_v) / std_v : 0
// Step 4: Convert Z-Score to a 0.0–1.0 percentile using a logistic sigmoid
// The sigmoid function (1 / (1 + e^(-k·z))) maps any real number smoothly to (0,1).
// The constant 1.702 is chosen so that z=±1 maps to approximately the 84th/16th
// percentile, matching the normal distribution. This gives an intuitive scale:
// ~50% = neutral (Z ≈ 0)
// ~85% = one standard deviation above mean (strong momentum)
// ~97% = two standard deviations above mean (extreme / climax)
pct_rank = 1.0 / (1.0 + math.exp(-1.702 * z_score))
// Step 5: Smooth the percentile output to reduce bar-to-bar noise.
// A short SMA is used here intentionally — it applies equal weight to the
// last z_smooth bars without introducing additional lag bias.
rel_mom = ta.sma(pct_rank, z_smooth)
// ════════════════════════════════════════════════════════════════════════════════
// SECTION 3: THE 8-STAGE ENGINE
// ════════════════════════════════════════════════════════════════════════════════
// The stage model classifies the current bar into one of eight market phases,
// using a two-level decision tree:
//
// Level 1: Is the primary trend bullish or bearish? (SMA filter from Section 2)
// Level 2: What is MACD-V doing within that trend? (momentum phase)
//
// BULL STAGES (price above 200 SMA, fast MA above slow MA):
// Stage 1 — Bull Resumption : MACD-V ≥ 50, but below signal. Momentum strong
// yet decelerating. Often a pause before leg higher.
// Stage 2 — Bull Rally : MACD-V ≥ 50 AND above signal. Full acceleration.
// The highest-conviction bull phase.
// Stage 3 — Bull Retrace : MACD-V positive (0–50). Trend intact but momentum
// has cooled. Typical healthy pullback territory.
// Stage 4 — Bull Correction : MACD-V negative in a bull trend. Price has
// pulled back meaningfully. Potential re-entry zone.
//
// BEAR STAGES (price below 200 SMA or fast MA below slow MA):
// Stage 5 — Bear Resumption : MACD-V ≤ -50, but above signal. Bearish momentum
// strong yet decelerating. Possible bear pause.
// Stage 6 — Bear Rundown : MACD-V ≤ -50 AND below signal. Full bearish
// acceleration. The highest-conviction bear phase.
// Stage 7 — Bear Bounce : MACD-V negative (-50 to 0). Bear trend intact but
// momentum is recovering slightly. Dead cat territory.
// Stage 8 — Bear Rally : MACD-V positive in a bear trend. A counter-trend
// rally. Not yet confirmed as a trend reversal.
//
// Note: Stage numbering is ordered for logical grouping (bull 1-4, bear 5-8),
// not by chronological sequence within a market cycle.
// ────────────────────────────────────────────────────────────────────────────────
int stage = 0
string s_name = ""
if is_bull
if macd_v >= 50 and macd_v > sig_v
stage := 2, s_name := "Bull Rally"
else if macd_v >= 50 and macd_v <= sig_v
stage := 1, s_name := "Bull Resumption"
else if macd_v < 50 and macd_v > 0
stage := 3, s_name := "Bull Retrace"
else
stage := 4, s_name := "Bull Correction"
else
if macd_v <= -50 and macd_v < sig_v
stage := 6, s_name := "Bear Rundown"
else if macd_v <= -50 and macd_v >= sig_v
stage := 5, s_name := "Bear Resumption"
else if macd_v > -50 and macd_v < 0
stage := 7, s_name := "Bear Bounce"
else
stage := 8, s_name := "Bear Rally"
// ════════════════════════════════════════════════════════════════════════════════
// SECTION 4: VISUALIZATION
// ════════════════════════════════════════════════════════════════════════════════
// Each stage has a distinct colour. The colour scheme follows a traffic-light
// and temperature logic:
// Greens → Bullish acceleration (bright = stronger)
// Yellows/Oranges → Bullish but weakening / correcting
// Reds → Bearish acceleration (bright = stronger)
// Blue/Teal → Counter-trend moves within a bear market
// ────────────────────────────────────────────────────────────────────────────────
color stage_c = switch stage
1 => #004d00 // Dark Green — Bull Resumption (strong but pausing)
2 => #00FF00 // Bright Green — Bull Rally (full acceleration)
3 => #FFD700 // Yellow — Bull Retrace (healthy pullback)
4 => #FF8C00 // Orange — Bull Correction (deeper retracement)
5 => #4d0000 // Dark Red — Bear Resumption (strong bear, pausing)
6 => #FF0000 // Bright Red — Bear Rundown (full bear acceleration)
7 => #00CED1 // Teal — Bear Bounce (counter-trend pop)
8 => #1E90FF // Blue — Bear Rally (extended counter-trend)
=> color.gray // Fallback — should never display under normal conditions
// ── MAIN MACD-V LINE ────────────────────────────────────────────────────────────
// The primary line is coloured by stage, making the current market phase
// immediately identifiable without reading the dashboard.
plot(macd_v, "MACD-V", color=stage_c, linewidth=2)
// ── SIGNAL LINE ─────────────────────────────────────────────────────────────────
// Plotted in black to stand out against the coloured MACD-V line.
// Crossovers between MACD-V and this line trigger the crossover alerts.
plot(sig_v, "Signal", color=#000000, linewidth=1)
// ── EIS MOMENTUM PULSE (HISTOGRAM) ──────────────────────────────────────────────
// Coloured by Elder Impulse System state:
// Green → EIS aligned bullish (EMA rising + histogram rising)
// Red → EIS aligned bearish (EMA falling + histogram falling)
// Grey → Mixed / no alignment (EIS "blue" state)
// The histogram uses hist_v (MACD-V minus signal), but is coloured by EIS state,
// combining both signals in a single visual element.
hist_c = eis_green ? color.new(color.green, 40) : eis_red ? color.new(color.red, 40) : color.new(color.gray, 60)
plot(hist_v, "Momentum Pulse", color=hist_c, style=plot.style_columns)
// ── CANDLE COLOURING ─────────────────────────────────────────────────────────────
// When enabled, paints the main chart candles with the current stage colour.
// This lets you assess the market phase without switching panes.
// Can be toggled off if you prefer to keep candle colours unaffected.
barcolor(show_bar ? stage_c : na)
// ── KEY HORIZONTAL LEVELS ───────────────────────────────────────────────────────
// These reference lines divide the MACD-V scale into actionable zones.
// They are fixed empirical thresholds from Spiroglou's research:
// ±50 → The primary bull/bear momentum boundary (used in the stage engine)
// ±150 → Extreme momentum zone. Readings here are historically rare and often
// precede exhaustion or mean reversion. Not a short signal by itself,
// but a flag to tighten risk management.
// 0 → The zero line. Crossing from negative to positive (or vice versa)
// is a meaningful momentum shift, though stage logic takes precedence.
hline(150, "Risk High", color=#0051ff, linestyle=hline.style_dotted, linewidth=2)
hline(50, "Trend Bull", color=color.new(color.green, 0), linestyle=hline.style_dotted, linewidth=2)
hline(0, "Zero", color=color.new(#000000, 0))
hline(-50, "Trend Bear", color=color.new(color.red, 0), linestyle=hline.style_dotted, linewidth=2)
hline(-150, "Risk Low", color=#0051ff, linestyle=hline.style_dotted, linewidth=2)
// ════════════════════════════════════════════════════════════════════════════════
// SECTION 5: ALERTS
// ════════════════════════════════════════════════════════════════════════════════
// Alerts are designed as "tap on the shoulder" notifications — they fire once
// at the moment of a change, not continuously while a condition is true.
// This is achieved by comparing the current bar's state to the prior bar's state.
//
// Alert categories:
// A) Stage group transitions (Green / Red / Blue / Correction)
// B) MACD-V signal crossovers
// C) Z-Score extremes and mean-reversion exits
// D) Elder Impulse System alignment signals
// E) Any stage change ("catch-all" tap on the shoulder)
// ────────────────────────────────────────────────────────────────────────────────
// ── A) STAGE GROUP TRANSITION ALERTS ────────────────────────────────────────────
// Groups: Green (stages 1-2), Red (5-6), Blue (7-8), Correction (3-4).
// Alert fires on the first bar that enters the group, not every bar within it.
bool is_green_stage = (stage == 1 or stage == 2)
bool start_of_green = is_green_stage and not is_green_stage[1]
alertcondition(start_of_green, title="🟢 Alert: Start of Green Trend",
message="Market has flipped to GREEN (Bullish) Stage!")
bool is_red_stage = (stage == 5 or stage == 6)
bool start_of_red = is_red_stage and not is_red_stage[1]
alertcondition(start_of_red, title="🔴 Alert: Start of Red Trend",
message="Market has flipped to RED (Bearish) Stage!")
bool is_blue_stage = (stage == 7 or stage == 8)
bool start_of_blue = is_blue_stage and not is_blue_stage[1]
alertcondition(start_of_blue, title="🔵 Alert: Start of Blue Trend",
message="Market has flipped to BLUE (Bear Bounce) Stage!")
bool is_corr_stage = (stage == 3 or stage == 4)
bool start_of_corr = is_corr_stage and not is_corr_stage[1]
alertcondition(start_of_corr, title="🟡 Alert: Start of Correction",
message="Market has flipped to Correction (Gold/Orange) Stage!")
// ── B) MACD-V SIGNAL LINE CROSSOVER ALERTS ──────────────────────────────────────
// Fires when MACD-V crosses above or below its signal line.
// These are classic momentum shift signals — not entry signals by themselves,
// but useful confirmation tools within the context of the current stage.
bool bull_cross = ta.crossover(macd_v, sig_v)
bool bear_cross = ta.crossunder(macd_v, sig_v)
alertcondition(bull_cross, title="⚔️ Alert: MACD-V Bullish Cross",
message="MACD-V crossed ABOVE Signal Line. Potential momentum shift UP.")
alertcondition(bear_cross, title="⚔️ Alert: MACD-V Bearish Cross",
message="MACD-V crossed BELOW Signal Line. Potential momentum shift DOWN.")
// ── C) Z-SCORE INTENSITY & MEAN REVERSION ALERTS ────────────────────────────────
// Two distinct alert types:
// Extreme Entry → Z-Score first crosses ±2.0 (momentum is statistically extreme)
// Reversion Exit → Z-Score crosses back inside ±2.0 (exhaustion signal)
//
// Reading guidance:
// Z > +2.0 doesn't mean "sell immediately" — extreme momentum can persist.
// It means: be aware, tighten stops, don't add to positions chasing strength.
// The reversion signal (dropping back below +2.0) is often the more actionable
// of the two, as it confirms momentum has peaked and is normalising.
bool z_high = z_score > 2.0
bool z_low = z_score < -2.0
alertcondition(z_high and not z_high[1], title="🔥 Alert: Intensity Extreme (High)",
message="Momentum Z-Score > +2.0. Market is statistically overheated.")
alertcondition(z_low and not z_low[1], title="❄️ Alert: Intensity Extreme (Low)",
message="Momentum Z-Score < -2.0. Market is statistically oversold.")
bool z_revert_high = ta.crossunder(z_score, 2.0)
bool z_revert_low = ta.crossover(z_score, -2.0)
alertcondition(z_revert_high, title="⚠️ Alert: Bullish Exhaustion (Peak Reversal)",
message="Z-Score dropped below +2.0. Upward momentum is fading; potential peak reached.")
alertcondition(z_revert_low, title="⚠️ Alert: Bearish Exhaustion (Floor Reversal)",
message="Z-Score rose above -2.0. Downward momentum is fading; potential bottom reached.")
// ── D) ELDER IMPULSE SYSTEM (EIS) ALERTS ────────────────────────────────────────
// EIS Green = both EMA direction and MACD histogram direction are bullish.
// EIS Red = both EMA direction and MACD histogram direction are bearish.
// These aligned states are Elder's highest-conviction entry zones.
// Alert fires on the first bar of alignment, not every bar that remains aligned.
bool start_of_eis_green = eis_green and not eis_green[1]
bool start_of_eis_red = eis_red and not eis_red[1]
alertcondition(start_of_eis_green, title="🟩 Alert: EIS Green (Bullish)",
message="Elder Impulse System turned GREEN. Both EMA and MACD Histogram are rising. Momentum is aligned for a long entry.")
alertcondition(start_of_eis_red, title="🟥 Alert: EIS Red (Bearish)",
message="Elder Impulse System turned RED. Both EMA and MACD Histogram are falling. Momentum is aligned for a short/exit.")
// ── E) CATCH-ALL: ANY STAGE CHANGE ──────────────────────────────────────────────
// A simple "something changed" alert — useful as a low-friction notification
// to check the chart when you're not actively watching it.
// The (stage != 0) guard is a safety check to prevent a spurious fire on the
// very first calculated bar if stage initialisation produces an unexpected value.
bool stage_changed = (stage != stage[1]) and (stage != 0)
alertcondition(stage_changed, title="🔔 Alert: Stage/Color Changed",
message="The MACD-V trend stage (color) has shifted! Check the chart for the new phase.")
// ════════════════════════════════════════════════════════════════════════════════
// SECTION 6: INTELLIGENT DASHBOARD
// ════════════════════════════════════════════════════════════════════════════════
// A 2×2 on-chart table showing:
// Row 1: Current stage name, coloured by stage (instant visual reference)
// Row 2: Intensity percentile + narrative label (Z-Score context at a glance)
//
// The table is only updated on the last bar (barstate.islast) to avoid
// unnecessary recalculation on every historical bar — a Pine Script best practice
// for table performance.
//
// Intensity labels map rel_mom (0.0–1.0) to narrative descriptions:
// 97%+ → CLIMAX (statistically extreme, very rare)
// 85%+ → ACCELERATED (one full std dev above mean)
// 60%+ → GROWING (above-average momentum)
// 40%+ → NEUTRAL (mean-range, no statistical edge)
// 15%+ → WEAK (below-average momentum)
// <15% → OVERSOLD (statistically extreme low)
// ────────────────────────────────────────────────────────────────────────────────
var table dash = table.new(get_pos(dash_pos), 2, 2, bgcolor=#bfc3cfcc, border_width=1, border_color=color.gray)
t_size = get_size(dash_size)
// Determine intensity narrative label from the smoothed percentile
string i_label = ""
string i_icon = ""
if rel_mom >= 0.97
i_label := "CLIMAX", i_icon := " 🔥"
else if rel_mom >= 0.85
i_label := "ACCELERATED", i_icon := " 🔥"
else if rel_mom >= 0.60
i_label := "GROWING"
else if rel_mom >= 0.40
i_label := "NEUTRAL"
else if rel_mom >= 0.15
i_label := "WEAK"
else
i_label := "OVERSOLD", i_icon := " ❄️"
if barstate.islast
// Row 1: Current Stage — name on left, stage colour on right cell background
table.cell(dash, 0, 0, "Current Stage", text_color=color.white, text_size=t_size)
table.cell(dash, 1, 0, s_name, bgcolor=stage_c, text_color=color.black, text_size=t_size)
// Row 2: Intensity — percentile and narrative label
// Format example: "72% | GROWING" or "97% | CLIMAX 🔥"
table.cell(dash, 0, 1, "Intensity (Z)", text_color=color.white, text_size=t_size)
table.cell(dash, 1, 1, str.tostring(math.round(rel_mom * 100)) + "% | " + i_label + i_icon,
text_color=color.white, text_size=t_size)