Calculates continuous structural market bias by detecting swing high and low pivots (higher highs, higher lows, lower highs, and lower lows) using configurable left and right bar lookbacks.
//@version=6
// ============================================================================
// Market Structure Advanced [WavesUnchained]
// Version: 1.0.3
// Author: WavesUnchained
// Build: 2026-10-02 00:00:00
// ============================================================================
// Swing-pivot classification (HH/HL/LH/LL) mapped to a bounded score oscillator.
// Provides a continuous structural bias reading without pattern-matching or labels.
//
// Features:
// ✓ Swing-pivot detection via configurable left/right bars
// ✓ Pivot classification: HH (+2), HL (+1), LH (−1), LL (−2)
// ✓ Windowed score bounded to [−100, +100]
// ✓ Signal line + fill + 4-state histogram
// ✓ Gradient line color (bearish ↔ bullish structural bias)
// ✓ Shadow fills between score and zero
// ✓ Optional pivot labels on the price chart (overlay)
// ✓ Structural shift signal markers
// ✓ Alert conditions
//
// ============================================================================
indicator("Market Structure Advanced [WavesUnchained]", "MS·ADV", format=format.price, precision=2, overlay=false, max_labels_count=500)
// ─────────────────────────────────────────────────────────────────────────────
// INPUTS
// ─────────────────────────────────────────────────────────────────────────────
g_pivot = "1 · Pivot Detection"
pivLeft = input.int(5, "Left Bars", minval=1, group=g_pivot)
pivRight = input.int(5, "Right Bars", minval=1, group=g_pivot)
scoreLen = input.int(10, "Score Window (pivots)", minval=2, group=g_pivot,
tooltip="Number of most recent pivots used in the score. Larger = smoother, more lagged.")
showLabels = input.bool(false, "Show Pivot Labels on Chart", group=g_pivot,
tooltip="Draws HH/HL/LH/LL labels on the price chart (requires overlay=true in a separate pane or you can enable this in an overlay indicator). Labels appear on the source pane.")
g_smooth = "2 · Smoothing"
sigLen = input.int(3, "Signal Length", minval=1, group=g_smooth)
smoothType = input.string("EMA", "Signal Type",
options=["EMA", "SMA", "RMA", "WMA", "SuperSmoother", "T3", "KAMA", "JMA Approx"],
group=g_smooth)
jmaPhase = input.int(50, " JMA Phase", minval=-100, maxval=100, group=g_smooth)
jmaPower = input.float(2.0, " JMA Power", minval=1.0, maxval=4.0, step=0.25, group=g_smooth)
g_col = "3 · Colors"
colBull = input.color(#00bcd4, "Bull", group=g_col)
colBear = input.color(#f06292, "Bear", group=g_col)
g_disp = "4 · Display"
showShadow = input.bool(true, "Shadow Fill (Score vs Zero)", group=g_disp)
showFill = input.bool(true, "Fill Score / Signal", group=g_disp)
showHist = input.bool(true, "Show Histogram", group=g_disp)
showSignals = input.bool(true, "Show Signal Markers", group=g_disp)
// ─────────────────────────────────────────────────────────────────────────────
// SMOOTHING FUNCTIONS
// ─────────────────────────────────────────────────────────────────────────────
f_supersmoother(series float x, simple int len) =>
float pi = math.pi
float a1 = math.exp(-1.414 * pi / len)
float b1 = 2.0 * a1 * math.cos(1.414 * pi / len)
float c2 = b1
float c3 = -(a1 * a1)
float c1 = 1.0 - c2 - c3
var float ss = na
ss := c1 * (x + nz(x[1])) / 2.0 + c2 * nz(ss[1]) + c3 * nz(ss[2])
ss
f_t3(series float x, simple int len) =>
float b = 0.7
float e1 = ta.ema(x, len)
float e2 = ta.ema(e1, len)
float e3 = ta.ema(e2, len)
float e4 = ta.ema(e3, len)
float e5 = ta.ema(e4, len)
float e6 = ta.ema(e5, len)
float c1 = -(b * b * b)
float c2 = 3.0 * b * b + 3.0 * b * b * b
float c3 = -6.0 * b * b - 3.0 * b - 3.0 * b * b * b
float c4 = 1.0 + 3.0 * b + b * b * b + 3.0 * b * b
c1 * e6 + c2 * e5 + c3 * e4 + c4 * e3
f_kama(series float x, simple int len) =>
float change = math.abs(x - x[len])
float vol = math.sum(math.abs(x - x[1]), len)
float er = vol != 0.0 ? change / vol : 0.0
float sc = math.pow(er * (2.0 / 3.0 - 2.0 / 31.0) + 2.0 / 31.0, 2)
var float kama = na
kama := nz(kama[1], x) + sc * (x - nz(kama[1], x))
kama
f_jma(series float x, simple int len, simple int phase, simple float power) =>
var float jma = na
var float e0 = na
var float e1 = na
var float e2 = na
float phaseRatio = phase < -100 ? 0.5 : phase > 100 ? 2.5 : phase / 100.0 + 1.5
float beta = 0.45 * (len - 1) / (0.45 * (len - 1) + 2.0)
float alpha = math.pow(beta, power)
e0 := (1.0 - alpha) * x + alpha * nz(e0[1], x)
e1 := (x - e0) * (1.0 - beta) + beta * nz(e1[1], 0.0)
e2 := (e0 + phaseRatio * e1 - nz(jma[1], x)) * math.pow(1.0 - alpha, 2.0) + math.pow(alpha, 2.0) * nz(e2[1], 0.0)
jma := nz(jma[1], x) + e2
jma
f_smooth(series float x, simple int len, simple string mode) =>
switch mode
"SMA" => ta.sma(x, len)
"RMA" => ta.rma(x, len)
"WMA" => ta.wma(x, len)
"SuperSmoother" => f_supersmoother(x, len)
"T3" => f_t3(x, len)
"KAMA" => f_kama(x, len)
"JMA Approx" => f_jma(x, len, jmaPhase, jmaPower)
=> ta.ema(x, len)
// ─────────────────────────────────────────────────────────────────────────────
// PIVOT DETECTION
// ─────────────────────────────────────────────────────────────────────────────
pivHigh = ta.pivothigh(high, pivLeft, pivRight)
pivLow = ta.pivotlow(low, pivLeft, pivRight)
// ─────────────────────────────────────────────────────────────────────────────
// PIVOT CLASSIFICATION
// ─────────────────────────────────────────────────────────────────────────────
var float lastHigh = na
var float lastLow = na
var float prevHigh = na
var float prevLow = na
var int pivScore = na
float curScore = na
if not na(pivHigh)
prevHigh := lastHigh
lastHigh := pivHigh
if not na(prevHigh)
curScore := pivHigh > prevHigh ? 2.0 : -1.0
if not na(pivLow)
prevLow := lastLow
lastLow := pivLow
if not na(prevLow)
curScore := na(curScore) ? (pivLow > prevLow ? 1.0 : -2.0) :
curScore + (pivLow > prevLow ? 1.0 : -2.0)
// ─────────────────────────────────────────────────────────────────────────────
// SCORE WINDOW
// ─────────────────────────────────────────────────────────────────────────────
var float[] scoreRing = array.new_float(0)
if not na(curScore)
array.push(scoreRing, curScore)
if array.size(scoreRing) > scoreLen
array.shift(scoreRing)
float rawScore = 0.0
int n = array.size(scoreRing)
if n > 0
float s = 0.0
for i = 0 to n - 1
s := s + array.get(scoreRing, i)
rawScore := s
float maxPossible = float(scoreLen) * 2.0
float score = maxPossible > 0.0 ? 100.0 * rawScore / maxPossible : 0.0
score := math.max(math.min(score, 100.0), -100.0)
signal = f_smooth(score, sigLen, smoothType)
hist = score - signal
// ─────────────────────────────────────────────────────────────────────────────
// SIGNALS (on raw score)
// ─────────────────────────────────────────────────────────────────────────────
bullCross = ta.crossover(score, signal)
bearCross = ta.crossunder(score, signal)
bullZero = ta.crossover(score, 0.0)
bearZero = ta.crossunder(score, 0.0)
// ─────────────────────────────────────────────────────────────────────────────
// COLORS
// ─────────────────────────────────────────────────────────────────────────────
color lineCol = color.from_gradient(score, -50, 50, colBear, colBull)
color lineCol2 = color.from_gradient(signal, -50, 50, color.new(colBear, 40), color.new(colBull, 40))
histRising = hist >= hist[1]
histColor = hist >= 0.0
? (histRising ? colBull : color.new(colBull, 55))
: (not histRising ? colBear : color.new(colBear, 55))
// ─────────────────────────────────────────────────────────────────────────────
// HLINES
// ─────────────────────────────────────────────────────────────────────────────
hline( 50, "+50", color=color.new(#00bcd4, 65), linestyle=hline.style_dashed, linewidth=1)
hline( 0, "Zero", color=color.new(color.gray, 60), linestyle=hline.style_dotted, linewidth=1)
hline(-50, "−50", color=color.new(#f06292, 65), linestyle=hline.style_dashed, linewidth=1)
// ─────────────────────────────────────────────────────────────────────────────
// PLOTS
// ─────────────────────────────────────────────────────────────────────────────
plot(showHist ? hist : na, "Histogram", style=plot.style_columns, color=histColor)
pZero = plot(0.0, display=display.none)
pSig = plot(signal, "Signal", color=lineCol2, linewidth=1)
pMS = plot(score, "Score", color=lineCol, linewidth=2)
fill(pMS, pZero, 50.0, 0.0, color.new(colBull, 78), color.new(colBull, 100),
title="Bull Shadow", display=showShadow ? display.all : display.none)
fill(pMS, pZero, 0.0, -50.0, color.new(colBear, 100), color.new(colBear, 78),
title="Bear Shadow", display=showShadow ? display.all : display.none)
fill(pMS, pSig,
color=showFill ? (score >= signal ? color.new(colBull, 88) : color.new(colBear, 88)) : na,
title="Score Fill")
// ─────────────────────────────────────────────────────────────────────────────
// PIVOT LABELS (overlay on price chart)
// ─────────────────────────────────────────────────────────────────────────────
if showLabels and not na(pivHigh)
bool isHH = not na(prevHigh) and pivHigh > prevHigh
label.new(bar_index - pivRight, pivHigh,
text=isHH ? "HH" : "LH",
style=label.style_label_down,
color=isHH ? color.new(colBull, 80) : color.new(colBear, 80),
textcolor=isHH ? colBull : colBear,
size=size.small)
if showLabels and not na(pivLow)
bool isHL = not na(prevLow) and pivLow > prevLow
label.new(bar_index - pivRight, pivLow,
text=isHL ? "HL" : "LL",
style=label.style_label_up,
color=isHL ? color.new(colBull, 80) : color.new(colBear, 80),
textcolor=isHL ? colBull : colBear,
size=size.small)
// ─────────────────────────────────────────────────────────────────────────────
// SIGNAL MARKERS
// ─────────────────────────────────────────────────────────────────────────────
plotshape(showSignals and bullCross ? score : na, "Bull Signal Cross",
style=shape.triangleup, location=location.absolute,
color=colBull, size=size.small)
plotshape(showSignals and bearCross ? score : na, "Bear Signal Cross",
style=shape.triangledown, location=location.absolute,
color=colBear, size=size.small)
plotshape(showSignals and bullZero ? score : na, "Bull Zero Cross",
style=shape.circle, location=location.absolute,
color=color.new(colBull, 20), size=size.tiny)
plotshape(showSignals and bearZero ? score : na, "Bear Zero Cross",
style=shape.circle, location=location.absolute,
color=color.new(colBear, 20), size=size.tiny)
// ─────────────────────────────────────────────────────────────────────────────
// ALERTS
// ─────────────────────────────────────────────────────────────────────────────
bool alertsOnBarClose = input.bool(true, "Alerts only on bar close (confirmed)", group="Alerts")
bool alertOk = not alertsOnBarClose or barstate.isconfirmed
alertcondition(alertOk and (bullCross), "MS Bull Cross", "MSA · BULL CROSS LONG · {{ticker}} {{interval}}")
alertcondition(alertOk and (bearCross), "MS Bear Cross", "MSA · BEAR CROSS SHORT · {{ticker}} {{interval}}")
alertcondition(alertOk and (bullZero), "MS Cross Above Zero", "MSA · CROSS ABOVE ZERO LONG · {{ticker}} {{interval}}")
alertcondition(alertOk and (bearZero), "MS Cross Below Zero", "MSA · CROSS BELOW ZERO SHORT · {{ticker}} {{interval}}")
Overview
Market Structure Advanced [WavesUnchained] assigns weighted values to classified pivots within a rolling window to produce a bounded score oscillator ranging from -100 to +100. The oscillator is smoothed using customizable moving average algorithms (including EMA, JMA, and SuperSmoother) to generate a signal line, histogram, and structural shift markers in a separate pane. Note that pivot confirmation depends on right-bar lookbacks, introducing a confirmation delay.
Swing pivot classification (HH, HL, LH, LL)
Bounded oscillator score ranging from -100 to +100
Multiple smoothing types including EMA, SuperSmoother, T3, and JMA
Visual histogram, shadow fills, and structural shift markers
The inputs you can change from the gear icon on the chart. Defaults are the author's.
Setting
Type
Default
Variable
Left Bars
int
5
pivLeft
Right Bars
int
5
pivRight
Score Window (pivots)
int
10
scoreLen
Show Pivot Labels on Chart
bool
false
showLabels
Signal Length
int
3
sigLen
Signal Type
string
EMA
smoothType
JMA Phase
int
50
jmaPhase
JMA Power
float
2.0
jmaPower
Bull
color
#00bcd4
colBull
Bear
color
#f06292
colBear
Shadow Fill (Score vs Zero)
bool
true
showShadow
Fill Score / Signal
bool
true
showFill
Show all 15 settings
Setting
Type
Default
Variable
Show Histogram
bool
true
showHist
Show Signal Markers
bool
true
showSignals
Alerts only on bar close (confirmed)
bool
true
alertsOnBarClose
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.
This indicator detects equal highs and lows by comparing prices within user-defined left and right bars using a tolerance percentage. It plots horizontal lines on the price chart for equal highs in one color and lows in another. A screener allows input of mult
This Pine Script indicator plots a ZigZag on the price chart. It identifies reversal points when price moves more than a set percentage from the last pivot. The script calculates forecasts using zigzag data and labels detected geometric patterns including harm
This indicator plots market structure on the price chart by identifying swing highs and lows. It connects these points with lines to visualize trend direction and detect Break of Structure (BOS) when price breaks a prior swing in the current trend. Change of C
This indicator implements the Fractal Model concept to plot market structure shifts, liquidity sweeps, and Change in State of Delivery (CISD) levels. It overlays custom higher-timeframe candle previews, open prices, and time markers directly on the price chart
BigBeluga Smart Money Concepts identifies and overlays market structure and volumetric order blocks directly on the price chart. It analyzes market swings, trend sweeps, and mapping structure using customizable pivot lengths and calculation modes. The indicato
Apex Trend & Liquidity Master calculates a trend cloud using customizable moving averages expanded by a volatility multiplier directly on the main price chart. It identifies dynamic support and resistance liquidity zones from swing high and low pivots, extendi