Indicator · Pine Script v6 · On the chart

Signal Quality Engine [WavesUnchained]

by casoon

Signal Quality Engine [WavesUnchained] is a range-fade indicator that identifies exhaustion at range extremes and signals potential reversals.

View on GitHub

GitHub stars9on the repository
Pine versionv6current
Lines of code435
Settings12inputs
LicenceMIT
Last updateyesterday27 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

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

signal_quality_engine.pine
//@version=6
// ============================================================================
// Signal Quality Engine [WavesUnchained]
// Version: 3.4
// Author: WavesUnchained
// Build: 2026-10-02 00:00:00
// ============================================================================
// One thing done well: a range-fade signal engine for commodities, futures,
// stocks and indices. It fades the range edges — long the exhausted lows, short
// the exhausted highs — when price is stretched, structure says "edge", and a
// candle rejects. Built for ranging markets: a regime gate (ADX + EMA slope)
// stands the tool down in strong trends, so it never fades a trend pullback.
// No regime switching, no MTF, no divergence, no pivot signals.
//
// The one logic (mirrored for both sides):
//   Range regime ok → price stretched to an edge (distance + structure) →
//   momentum turns back → candle rejects → score high enough → Trigger (fade).
//
// Features:
//   ✓ Range regime gate (ADX + EMA slope) — only fades when actually ranging
//   ✓ Edge bias (which side is exhausted) → Setup → Watch → Trigger, one logic
//   ✓ Exhaustion score (0–100): Distance + Structure + Momentum
//   ✓ Candle-rejection confirmation (sweep of the range edge & reclaim); confirmation window
//   ✓ Market profiles tune band width / edge sensitivity
//   ✓ Strictness presets (score threshold + cooldown)
//   ✓ ATR risk levels (SL beyond the edge / TP toward the mean)
//   ✓ Range channel fill + outer-edge zone shading (where a fade can trigger)
//   ✓ Pivot reference markers (control overlay, never a signal input)
//   ✓ Light-theme dashboard with a "why no trigger" status line + alerts
//
// ============================================================================

indicator("Signal Quality Engine [WavesUnchained]", "SQE", overlay=true, max_labels_count=500)

// ─────────────────────────────────────────────────────────────────────────────
// Inputs
// ─────────────────────────────────────────────────────────────────────────────

string profile = input.string(
     "Natural Gas",
     "Market Profile",
     options=[
         "Natural Gas",
         "Oil",
         "Gold / Silver",
         "Broad Commodities",
         "Stocks / Indices",
         "Generic Futures"
     ],
     group="Core"
)

string strictness = input.string(
     "Strict",
     "Signal Strictness",
     options=["Very Strict", "Strict", "Balanced"],
     group="Core"
)

int confirmWindow = input.int(3, "Confirmation Window (bars)", minval=1, maxval=8, group="Core", tooltip="A candle rejection within this many bars after an exhaustion extreme still triggers — so a one-bar timing miss doesn't void the setup. Momentum is already inside the exhaustion score, so there is no separate momentum gate.")

bool showStates     = input.bool(true, "Show Setup / Watch Markers", group="Visuals", tooltip="Faint gray triangle = exhaustion building at an edge, bright yellow triangle = extreme reached, rejection pending. Both point the pending fade direction.")
bool onlyActionable = input.bool(false, "Only Watch / Trigger (hide Setup)", group="Visuals", tooltip="Hides the gray Setup markers so a choppy range doesn't clutter the chart — Watch and Trigger still show.")
bool showBands      = input.bool(true, "Show Mean & Bands", group="Visuals")
bool showEdgeZones  = input.bool(true, "Show Range Fill & Edge Zones", group="Visuals", tooltip="Shades the band channel and tints the outer 20% of the 80-bar range where a fade can trigger.")
bool showTable      = input.bool(false, "Show Table", group="Visuals")
bool showRiskLevels = input.bool(true, "Show ATR Risk Levels", group="Visuals")
bool showPivots     = input.bool(false, "Show Pivot Reference", group="Visuals", tooltip="Confirmed swing high/low markers. Control overlay only — never feeds the signals.")
bool colorBars      = input.bool(false, "Color Bars", group="Visuals")

// ─────────────────────────────────────────────────────────────────────────────
// Profile presets — what actually differs by asset class for a range-fade tool:
//   bbMult       band width — how stretched before an "edge" (spiky commodities
//                overshoot more, so they need wider bands than indices)
//   nearAtrMult  edge proximity tolerance
//   adxMax       regime tolerance — how trendy the tape can be and still be faded.
//                Commodities mean-revert through choppy trends (fade readily);
//                indices trend persistently, so they need a stricter range filter.
//   slopeMax     EMA50-slope ceiling (ATR/bar) for the regime gate, same logic.
// ─────────────────────────────────────────────────────────────────────────────

float bbMult = switch profile
    "Natural Gas"       => 2.5
    "Oil"               => 2.3
    "Gold / Silver"     => 2.0
    "Broad Commodities" => 2.2
    "Stocks / Indices"  => 1.9
    => 2.1

float nearAtrMult = switch profile
    "Natural Gas"       => 0.95
    "Oil"               => 0.85
    "Gold / Silver"     => 0.75
    "Broad Commodities" => 0.82
    "Stocks / Indices"  => 0.68
    => 0.80

float adxMax = switch profile
    "Natural Gas"       => 28.0
    "Oil"               => 26.0
    "Gold / Silver"     => 23.0
    "Broad Commodities" => 25.0
    "Stocks / Indices"  => 20.0
    => 24.0

float slopeMax = switch profile
    "Natural Gas"       => 0.55
    "Oil"               => 0.50
    "Gold / Silver"     => 0.42
    "Broad Commodities" => 0.48
    "Stocks / Indices"  => 0.35
    => 0.45

// Exhaustion thresholds — note: weighted exhaustion scores top out lower than a
// trend score, so these are deliberately below the old 76–88 band.
float scoreMin = switch strictness
    "Very Strict" => 56.0
    "Strict"      => 48.0
    => 38.0

float setupMin = scoreMin - 12.0

int cooldownBars = switch strictness
    "Very Strict" => 18
    "Strict"      => 12
    => 8

// ─────────────────────────────────────────────────────────────────────────────
// Helpers
// ─────────────────────────────────────────────────────────────────────────────

f_clamp(float x, float lo, float hi) =>
    math.max(lo, math.min(hi, x))

f_safeDiv(float a, float b) =>
    b == 0.0 ? 0.0 : a / b

f_score01(float x) =>
    f_clamp(x, 0.0, 1.0) * 100.0

// Grade is relative to the active trigger threshold: B = triggerable (>= base),
// A/A+ progressively above it, C just below. Keeps the dashboard consistent with
// whatever strictness preset (and thus scoreMin) is selected.
f_grade(float score, float base) =>
    string g = "—"
    if score >= base + 24
        g := "A+"
    else if score >= base + 12
        g := "A"
    else if score >= base
        g := "B"
    else if score >= base - 10
        g := "C"
    g

// ─────────────────────────────────────────────────────────────────────────────
// Core data — mean, bands, distance
// ─────────────────────────────────────────────────────────────────────────────

float atr = ta.atr(14)
float ema = ta.ema(close, 50)

float basis = ta.sma(close, 20)
float dev   = ta.stdev(close, 20)
float upper = basis + dev * bbMult
float lower = basis - dev * bbMult

float distAtr = f_safeDiv(close - ema, atr)
float bbZ     = f_clamp(f_safeDiv(close - basis, dev), -4.0, 4.0)   // clamp so a tiny dev can't spike the score

// ─────────────────────────────────────────────────────────────────────────────
// Regime gate — the tool only fades when the market is actually ranging.
// A strong trend turns every pullback into a "false edge"; this stands it down.
// ─────────────────────────────────────────────────────────────────────────────

[diPlus, diMinus, adx] = ta.dmi(14, 14)
float emaSlope = f_safeDiv(ema - ema[10], atr)
bool rangeOk = adx < adxMax and math.abs(emaSlope) < slopeMax

// ─────────────────────────────────────────────────────────────────────────────
// Momentum — overbought/oversold readings (feed the exhaustion score, not a gate)
// ─────────────────────────────────────────────────────────────────────────────

float rsi = ta.rsi(close, 14)
float stochRsiRaw = ta.stoch(rsi, rsi, rsi, 14)
float k = ta.sma(stochRsiRaw, 3)

float ap = hlc3
float esa = ta.ema(ap, 10)
float de = ta.ema(math.abs(ap - esa), 10)
float ci = f_safeDiv(ap - esa, 0.015 * de)
float wt1 = ta.ema(ci, 21)

// ─────────────────────────────────────────────────────────────────────────────
// Structure — recent high/low proximity and wick rejection
// ─────────────────────────────────────────────────────────────────────────────

float recentHigh = ta.highest(high, 80)
float recentLow  = ta.lowest(low, 80)
float rangeSize  = recentHigh - recentLow
float rangePos   = f_safeDiv(close - recentLow, rangeSize)   // 0 = at low, 1 = at high
// Edge = near the extreme AND in the outer fifth of the 80-bar range (not just
// "close to a trending high" — that is true constantly in a trend).
bool nearHigh = rangePos > 0.80 and recentHigh - close <= atr * nearAtrMult
bool nearLow  = rangePos < 0.20 and close - recentLow <= atr * nearAtrMult

float upperWick = f_safeDiv(high - math.max(open, close), high - low)
float lowerWick = f_safeDiv(math.min(open, close) - low, high - low)

// Candle rejection — sweep the actual range edge (not just the prior candle)
// and close back inside it (liquidity-grab style), or a strong opposing wick.
bool sweptHigh = high > recentHigh[1] and close < recentHigh[1]
bool sweptLow  = low < recentLow[1] and close > recentLow[1]
bool topCandleReject = sweptHigh or (close < open and upperWick >= 0.40 and close < hl2)
bool botCandleReject = sweptLow or (close > open and lowerWick >= 0.40 and close > hl2)

// Pivot reference — control overlay only. NOT used in any signal/grade/gate.
int pivotLen = 5
bool pivotLowConfirmed  = not na(ta.pivotlow(low, pivotLen, pivotLen))
bool pivotHighConfirmed = not na(ta.pivothigh(high, pivotLen, pivotLen))

// ─────────────────────────────────────────────────────────────────────────────
// Exhaustion score (0–100) per edge — Distance + Structure + Momentum
// ─────────────────────────────────────────────────────────────────────────────

// Distance — how far price is stretched past the mean (top = high, bot = low)
float distTop = f_score01(math.max(f_safeDiv(distAtr, 3.0), f_safeDiv(bbZ, 2.5)))
float distBot = f_score01(math.max(f_safeDiv(-distAtr, 3.0), f_safeDiv(-bbZ, 2.5)))

// Structure — at the edge of the 80-bar range. Close beyond the band is already
// measured by Distance, and wick rejection belongs to the candle trigger, so
// neither is counted here a second time.
float structTop = f_score01(nearHigh ? 1.0 : 0.0)
float structBot = f_score01(nearLow ? 1.0 : 0.0)

// Momentum — overbought/oversold extreme
float momTop = f_score01(0.40 * f_safeDiv(rsi - 60, 20) + 0.30 * f_safeDiv(k - 75, 25) + 0.30 * f_safeDiv(wt1 - 45, 25))
float momBot = f_score01(0.40 * f_safeDiv(40 - rsi, 20) + 0.30 * f_safeDiv(25 - k, 25) + 0.30 * f_safeDiv(-45 - wt1, 25))

// Weighted exhaustion (Distance 40%, Structure 30%, Momentum 30%)
float topScore = distTop * 0.40 + structTop * 0.30 + momTop * 0.30
float botScore = distBot * 0.40 + structBot * 0.30 + momBot * 0.30

// Edge bias — the dominant exhausted side (situation-driven, not a trend guess)
int edgeDir = botScore >= setupMin and botScore >= topScore ? 1 : topScore >= setupMin and topScore > botScore ? -1 : 0
float edgeScore = edgeDir == 1 ? botScore : edgeDir == -1 ? topScore : math.max(topScore, botScore)

// ─────────────────────────────────────────────────────────────────────────────
// Three tiers: Setup → Watch → Trigger (fade the dominant edge)
// One logic, mirroring the Exhaustion Scanner: the exhaustion score (which already
// includes momentum) reaching its threshold is the "extreme"; the candle rejection
// confirms it. No separate momentum gate — that was what starved the triggers.
// ─────────────────────────────────────────────────────────────────────────────

// Extreme: exhaustion score clears the threshold on the dominant edge
bool botExtreme = botScore >= scoreMin and botScore >= topScore
bool topExtreme = topScore >= scoreMin and topScore > botScore

// An extreme stays "armed" for confirmWindow bars so the rejection need not be same-bar.
// barssince() is na until the first occurrence — guard it explicitly.
int barsSinceBotExtreme = ta.barssince(botExtreme)
int barsSinceTopExtreme = ta.barssince(topExtreme)
bool botArmed = not na(barsSinceBotExtreme) and barsSinceBotExtreme <= confirmWindow
bool topArmed = not na(barsSinceTopExtreme) and barsSinceTopExtreme <= confirmWindow

// Setup (gray): exhaustion building, not yet at the extreme threshold
bool longSetup  = edgeDir == 1  and botScore >= setupMin and not botExtreme
bool shortSetup = edgeDir == -1 and topScore >= setupMin and not topExtreme

// Trigger: armed extreme + candle rejects back off the edge + cooldown
var int lastLongBar = na
var int lastShortBar = na
bool longCooldownOk = na(lastLongBar) or bar_index - lastLongBar > cooldownBars
bool shortCooldownOk = na(lastShortBar) or bar_index - lastShortBar > cooldownBars

// Trigger requires the structure side to still hold (price still in the lower/upper
// portion of the range), so a stale armed extreme alone can't fire off the edge.
bool finalLong = rangeOk and botArmed and rangePos < 0.35 and botCandleReject and longCooldownOk
bool finalShort = rangeOk and topArmed and rangePos > 0.65 and topCandleReject and shortCooldownOk

if finalLong
    lastLongBar := bar_index
if finalShort
    lastShortBar := bar_index

// Watch (yellow): extreme reached / armed, rejection still pending
bool longWatch  = rangeOk and botArmed and not finalLong
bool shortWatch = rangeOk and topArmed and not finalShort

// Markers — only the run-up that has NOT yet triggered
bool showLongSetup  = showStates and not onlyActionable and rangeOk and longSetup and not longWatch and not finalLong
bool showLongWatch  = showStates and rangeOk and longWatch
bool showShortSetup = showStates and not onlyActionable and rangeOk and shortSetup and not shortWatch and not finalShort
bool showShortWatch = showStates and rangeOk and shortWatch

// ─────────────────────────────────────────────────────────────────────────────
// Risk levels — SL beyond the edge, TP toward the mean
// ─────────────────────────────────────────────────────────────────────────────

float longSL = low - atr * 1.2
float longTP1 = basis
float longTP2 = upper

float shortSL = high + atr * 1.2
float shortTP1 = basis
float shortTP2 = lower

// Latch the last trigger's levels so SL/TP stay visible for a window of bars,
// not just on the single trigger candle.
var float activeSL    = na
var float activeTP1   = na
var float activeTP2   = na
var int   activeUntil = na
if finalLong
    activeSL := longSL
    activeTP1 := longTP1
    activeTP2 := longTP2
    activeUntil := bar_index + 20
if finalShort
    activeSL := shortSL
    activeTP1 := shortTP1
    activeTP2 := shortTP2
    activeUntil := bar_index + 20
bool showActiveRisk = showRiskLevels and not na(activeUntil) and bar_index <= activeUntil

// ─────────────────────────────────────────────────────────────────────────────
// Visuals
// ─────────────────────────────────────────────────────────────────────────────

plot(showBands ? ema : na, "Mean (EMA50)", color=color.new(color.gray, 10), linewidth=2)
p_upper = plot(showBands ? upper : na, "Upper Band", color=color.new(color.gray, 60))
p_lower = plot(showBands ? lower : na, "Lower Band", color=color.new(color.gray, 60))
fill(p_upper, p_lower, color=showBands and showEdgeZones ? color.new(color.gray, 96) : na, title="Range Channel")

// Outer-20% edge zones (the actual fade area) get a light tint while the regime is a range.
bgcolor(showEdgeZones and rangeOk and (rangePos > 0.80 or rangePos < 0.20) ? color.new(color.gray, 94) : na, title="Edge Zone")

// Signal labels — compact (tiny) so neighbouring signals don't overlap. Full score on hover.
if finalLong
    label.new(bar_index, low, "L " + str.tostring(math.round(botScore)), style=label.style_label_up, yloc=yloc.belowbar, color=color.new(color.lime, 0), textcolor=color.black, size=size.tiny, tooltip="LONG fade (exhausted low) · score " + str.tostring(math.round(botScore)) + "/100")
if finalShort
    label.new(bar_index, high, "S " + str.tostring(math.round(topScore)), style=label.style_label_down, yloc=yloc.abovebar, color=color.new(color.red, 0), textcolor=color.white, size=size.tiny, tooltip="SHORT fade (exhausted high) · score " + str.tostring(math.round(topScore)) + "/100")

// Setup (faint gray triangle, "forming") and Watch (bright yellow triangle, "armed") —
// one shape language, same direction as the eventual Trigger, escalating in weight
// (faint/tiny -> bright/small -> label) so the buildup reads at a glance.
plotshape(showLongSetup,  title="Long Setup",  style=shape.triangleup,   location=location.belowbar, color=color.new(color.gray, 55),   size=size.tiny)
plotshape(showLongWatch,  title="Long Watch",  style=shape.triangleup,   location=location.belowbar, color=color.new(color.yellow, 0),  size=size.small)
plotshape(showShortSetup, title="Short Setup", style=shape.triangledown, location=location.abovebar, color=color.new(color.gray, 55),   size=size.tiny)
plotshape(showShortWatch, title="Short Watch", style=shape.triangledown, location=location.abovebar, color=color.new(color.yellow, 0),  size=size.small)

// Pivot reference markers — plotted at the confirmed pivot bar (control overlay)
plotshape(showPivots and pivotLowConfirmed, title="Pivot Low", style=shape.triangleup, location=location.belowbar, offset=-pivotLen, color=color.new(color.gray, 40), size=size.tiny)
plotshape(showPivots and pivotHighConfirmed, title="Pivot High", style=shape.triangledown, location=location.abovebar, offset=-pivotLen, color=color.new(color.gray, 40), size=size.tiny)

barcolor(colorBars ? finalLong ? color.lime : finalShort ? color.red : na : na)

plot(showActiveRisk ? activeSL : na, "Active SL", color=color.new(color.red, 0), style=plot.style_linebr)
plot(showActiveRisk ? activeTP1 : na, "Active TP1", color=color.new(color.green, 0), style=plot.style_linebr)
plot(showActiveRisk ? activeTP2 : na, "Active TP2", color=color.new(color.green, 30), style=plot.style_linebr)

// ─────────────────────────────────────────────────────────────────────────────
// Table (light theme — repo standard)
// ─────────────────────────────────────────────────────────────────────────────

color tc = color.new(color.gray, 20)   // standard text color
color hd = color.new(color.gray, 90)   // header row background

var table t = table.new(position.top_right, 2, 9,
     bgcolor=color.new(color.white, 5),
     border_color=color.new(color.gray, 60),
     border_width=1,
     frame_color=color.new(color.gray, 40),
     frame_width=1)

if showTable and barstate.islast
    // Status — the "why no trigger" line, top tier first
    string status = "Mid-range"
    string action = "Wait"
    if not rangeOk
        status := "Trending — fade off"
        action := "Stand aside"
    else if finalLong or finalShort
        status := "Trigger active"
        action := "Fade it"
    else if longWatch or shortWatch
        status := "Watch — rejection pending"
        action := "Prepare"
    else if longSetup or shortSetup
        status := "Exhaustion building"
        action := "Watch"

    string grade = f_grade(edgeScore, scoreMin)
    bool pass = edgeScore >= scoreMin
    string edgeTxt = edgeDir == 1 ? "Low -> Long" : edgeDir == -1 ? "High -> Short" : "Mid-range"
    color edgeColor = edgeDir == 1 ? #00c853 : edgeDir == -1 ? #d50000 : tc
    color statusColor = (finalLong or finalShort) ? #00c853 : (longWatch or shortWatch) ? #ffd600 : (longSetup or shortSetup) ? tc : #ff6f00

    table.cell(t, 0, 0, "SQE", text_color=tc, bgcolor=hd, text_size=size.small)
    table.cell(t, 1, 0, grade, text_color=pass ? color.white : tc, bgcolor=pass ? #00c853 : color.new(color.gray, 80), text_size=size.small)

    table.cell(t, 0, 1, "Profile", text_color=tc, text_size=size.small)
    table.cell(t, 1, 1, profile, text_color=tc, text_size=size.small)

    table.cell(t, 0, 2, "Mode", text_color=tc, text_size=size.small)
    table.cell(t, 1, 2, strictness, text_color=tc, text_size=size.small)

    table.cell(t, 0, 3, "Edge", text_color=tc, text_size=size.small)
    table.cell(t, 1, 3, edgeTxt + "  " + str.tostring(math.round(edgeScore)), text_color=edgeColor, text_size=size.small)

    table.cell(t, 0, 4, "Status", text_color=tc, text_size=size.small)
    table.cell(t, 1, 4, status, text_color=statusColor, text_size=size.small)

    table.cell(t, 0, 5, "Action", text_color=tc, text_size=size.small)
    table.cell(t, 1, 5, action, text_color=tc, text_size=size.small)

    table.cell(t, 0, 6, "Top / Bot", text_color=tc, text_size=size.small)
    table.cell(t, 1, 6, str.tostring(math.round(topScore)) + " / " + str.tostring(math.round(botScore)), text_color=tc, text_size=size.small)

    table.cell(t, 0, 7, "Dist (ATR)", text_color=tc, text_size=size.small)
    table.cell(t, 1, 7, str.tostring(distAtr, "#.##"), text_color=tc, text_size=size.small)

    table.cell(t, 0, 8, "TF Hint", text_color=tc, text_size=size.small)
    table.cell(t, 1, 8, timeframe.isintraday and timeframe.multiplier < 60 ? "Low TF noisy" : timeframe.period == "240" ? "4H OK" : "Check context", text_color=tc, text_size=size.small)

// ─────────────────────────────────────────────────────────────────────────────
// Alerts
// ─────────────────────────────────────────────────────────────────────────────

bool alertsOnBarClose = input.bool(true, "Alerts only on bar close (confirmed)", group="Alerts")
bool alertOk = not alertsOnBarClose or barstate.isconfirmed
alertcondition(alertOk and (finalLong), "SQE Long Signal", "SQE · LONG · {{ticker}} {{interval}}")
alertcondition(alertOk and (finalShort), "SQE Short Signal", "SQE · SHORT · {{ticker}} {{interval}}")
alertcondition(alertOk and (finalLong or finalShort), "SQE Any Signal", "SQE · SIGNAL · {{ticker}} {{interval}}")
alertcondition(alertOk and (rangeOk and (longWatch or shortWatch)), "SQE Watch", "SQE · WATCH · {{ticker}} {{interval}}")

Overview

Signal Quality Engine [WavesUnchained] applies an ADX and EMA slope regime gate to stand down during strong trends. Using distance, structure, and momentum, it computes an exhaustion score confirmed by candle rejections within a selectable window. It plots mean bands, channel fills, edge zones, ATR risk levels, and progressive state markers directly on the price chart, with an optional status table. Settings adjust market profiles, strictness tiers, and visual layers.

Details

Published by

casoon on GitHub

Type

Indicator, Pine Script v6

Draws

On the price chart

Category

Mean reversion, Support & resistance, Volatility & bands, Candlestick patterns

Markets

Oil & commodities, Gold, Stocks, 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.

Topics

pine-scriptpinescripttechnical-analysistrading-indicatorstrading-strategiestradingview

Settings

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

SettingTypeDefaultVariable
Market ProfilestringNatural Gasprofile
Signal StrictnessstringStrictstrictness
Confirmation Window (bars)int3confirmWindow
Show Setup / Watch MarkersbooltrueshowStates
Only Watch / Trigger (hide Setup)boolfalseonlyActionable
Show Mean & BandsbooltrueshowBands
Show Range Fill & Edge ZonesbooltrueshowEdgeZones
Show TableboolfalseshowTable
Show ATR Risk LevelsbooltrueshowRiskLevels
Show Pivot ReferenceboolfalseshowPivots
Color BarsboolfalsecolorBars
Alerts only on bar close (confirmed)booltruealertsOnBarClose
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

Candlestick Patterns

by harryguiacorn
219 stars

This indicator plots optional candlestick pattern signals on the price chart. It detects engulfing bars, inside bars, HOLP and LOHP, kicker patterns, body-mass extremes, higher highs and lower lows, ATR warnings, and four-bar fractals. Optional pivot marks use

Indicator v5 Candlestick patternsVolatility & bands
Updated 1 year ago · None stated GitHub repository of Candlestick Patterns
55 stars

This Pine Script indicator overlays institutional liquidity levels on the price chart. It identifies order block levels using pivot highs and lows, then draws mean reversion lines from the 20 and 50 period simple moving averages, a volatility range midpoint pr

Indicator v6 Support & resistanceSMC / ICT / price action Repaints
Updated 6 months ago · Apache-2.0 GitHub repository of AAPL Institutional Liquidity Grid

VWAP Trader NXi

by eddiebelaval
7 stars

VWAP Trader NXi calculates a daily session-anchored Volume Weighted Average Price along with two custom standard deviation bands on the main price chart. It plots the core VWAP, Value Area bands, outer extension zones with background shading, and the previous

Indicator v5 VolumeVolatility & bands
Updated 3 months ago · None stated GitHub repository of VWAP Trader NXi

ICT Liquidity Void Fill

by ArunKBhaskar
575 stars

This overlay indicator marks ICT liquidity void fills on the price chart and can show long and short signals. It places a Fibonacci level inside a displacement candle, defaulting to the midpoint, and draws lines in a chosen style. Optional filters limit the fi

Indicator v6 SMC / ICT / price actionSupport & resistance
Updated 1 year ago · MPL-2.0 GitHub repository of ICT Liquidity Void Fill

This indicator plots trade entry and exit signals on the price chart by combining Bollinger Bands with optional filter conditions. It calculates standard Bollinger Bands along with RSI, Aroon, and moving average filters. Reversal signals trigger when price bre

Indicator v4 Volatility & bandsOscillators & momentum
Updated 2 months ago · None stated GitHub repository of Bollinger Bands Winner IND [Alorse]
285 stars

Mean Reversion Study is an indicator that identifies trade setups combining exponential moving averages, RSI extremes, and engulfing candlestick patterns. It highlights bullish setups when a bullish engulfing candle forms below the EMA while RSI is oversold, a

Indicator v4 Mean reversionCandlestick patterns
Updated 3 years ago · None stated GitHub repository of Mean Reversion Study

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