WaveTrend Advanced Smoothing is a separate-pane momentum oscillator built on the classic WaveTrend algorithm with eight selectable smoothing techniques, including SuperSmoother, T3, KAMA, and JMA.
//@version=6
// ============================================================================
// WaveTrend Advanced Smoothing [WavesUnchained]
// Version: 1.1.2
// Author: WavesUnchained
// Build: 2026-10-02 00:00:00
// ============================================================================
// WaveTrend oscillator with pluggable smoothing kernels and rich visualization.
//
// Features:
// ✓ 8 smoothing kernels: EMA, SMA, RMA, WMA, SuperSmoother, T3, KAMA, JMA
// ✓ Three scale modes: Classic, Clamp ±100, Adaptive ±100
// ✓ Gradient line color (bull ↔ bear based on oscillator level)
// ✓ Gradient shadow fills between WT and zero line
// ✓ 4-state color-coded histogram (momentum direction + strength)
// ✓ Optional fill between WT main and signal lines
// ✓ OB/OS zone background fills
// ✓ Extreme cross signal markers + subtle dots for all crosses
// ✓ Sentiment Bar — live ±100 OB/OS extremity score + mini bar
// ✓ Alert conditions for all cross events
//
// ============================================================================
indicator("WaveTrend Advanced Smoothing [WavesUnchained]", "WT·AS", format=format.price, precision=2, overlay=false, max_labels_count=500)
// ─────────────────────────────────────────────────────────────────────────────
// INPUTS
// ─────────────────────────────────────────────────────────────────────────────
g_wt = "1 · WaveTrend"
src = input.source(hlc3, "Source", group=g_wt)
channelLen = input.int(10, "Channel Length", minval=1, group=g_wt)
averageLen = input.int(21, "Average Length", minval=1, group=g_wt)
signalLen = input.int(4, "Signal Length", minval=1, group=g_wt)
g_smooth = "2 · Smoothing"
smoothType = input.string("EMA", "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_scale = "3 · Scale"
scaleMode = input.string("Classic", "Mode",
options=["Classic", "Clamp ±100", "Adaptive ±100"], group=g_scale,
tooltip="Classic: raw oscillator values. Clamp ±100: hard clips to ±100. Adaptive ±100: rescales to ±100 based on recent range.")
scaleLookback = input.int(200, "Adaptive Lookback", minval=20, group=g_scale,
tooltip="Lookback used for Adaptive ±100 normalization.")
g_levels = "4 · Levels"
ob1 = input.float( 60, "Overbought 1", group=g_levels)
ob2 = input.float( 100, "Overbought 2", group=g_levels)
os1 = input.float(-60, "Oversold 1", group=g_levels)
os2 = input.float(-100, "Oversold 2", group=g_levels)
g_col = "5 · Colors"
colBull = input.color(#00bcd4, "Bull", group=g_col)
colBear = input.color(#f06292, "Bear", group=g_col)
colHistBull = input.color(#26a69a, "Histogram Bull", group=g_col)
colHistBear = input.color(#ef5350, "Histogram Bear", group=g_col)
colZoneOB = input.color(color.new(#ef5350, 90), "OB Zone Fill", group=g_col)
colZoneOS = input.color(color.new(#26a69a, 90), "OS Zone Fill", group=g_col)
g_disp = "6 · Display"
showShadow = input.bool(true, "Shadow Fill (WT vs Zero)", group=g_disp)
showFill = input.bool(true, "Fill WT / Signal", group=g_disp)
showZoneFill = input.bool(true, "Fill OB / OS Zones", group=g_disp)
showHist = input.bool(true, "Show Histogram", group=g_disp)
showSignals = input.bool(true, "Show Extreme Signal Markers", group=g_disp)
showDots = input.bool(true, "Show Cross Dots", group=g_disp)
g_score = "7 · Score"
showSentiment = input.bool(true, "Show Sentiment Bar", group=g_score,
tooltip="Live label at the panel's right edge — how far WT sits inside its own OB/OS zone, signed ±100. Not a fixed Bullish/Bearish claim. Mirrors Williams VIX Fix Advanced's Sentiment Bar. No Signal Quality score here — this variant has no trend-context/stall layer for the quality weighting to draw on.")
// ─────────────────────────────────────────────────────────────────────────────
// 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)
// ─────────────────────────────────────────────────────────────────────────────
// WAVETREND CORE
// ─────────────────────────────────────────────────────────────────────────────
esa = f_smooth(src, channelLen, smoothType)
devRaw = f_smooth(math.abs(src - esa), channelLen, smoothType)
dev = math.max(math.abs(devRaw), 1e-10)
ci = (src - esa) / (0.015 * dev)
wt1Raw = f_smooth(ci, averageLen, smoothType)
wt2Raw = f_smooth(wt1Raw, signalLen, smoothType)
// ─────────────────────────────────────────────────────────────────────────────
// SCALE
// ─────────────────────────────────────────────────────────────────────────────
// Shared normalization factor — same denominator for both lines so their
// relationship (and therefore crossovers) stays proportional after scaling.
float scaleAbs = ta.highest(math.max(math.abs(wt1Raw), math.abs(wt2Raw)), scaleLookback)
f_scale(series float x, float sharedMax) =>
if scaleMode == "Clamp ±100"
math.max(math.min(x, 100.0), -100.0)
else if scaleMode == "Adaptive ±100"
sharedMax != 0.0 ? 100.0 * x / sharedMax : 0.0
else
x
wt1 = f_scale(wt1Raw, scaleAbs)
wt2 = f_scale(wt2Raw, scaleAbs)
hist = wt1 - wt2
// ─────────────────────────────────────────────────────────────────────────────
// SIGNALS
// ─────────────────────────────────────────────────────────────────────────────
// Signals on raw values — adaptive scaling must not create or destroy crossovers.
// Extreme thresholds also compare against raw values so OB/OS levels stay meaningful
// regardless of scale mode.
bullCross = ta.crossover(wt1Raw, wt2Raw)
bearCross = ta.crossunder(wt1Raw, wt2Raw)
bullExtreme = bullCross and wt1Raw < os1
bearExtreme = bearCross and wt1Raw > ob1
// ─────────────────────────────────────────────────────────────────────────────
// SCORE — Sentiment Bar only (live extremity read). Computed on raw wt1Raw/ob1/
// os1, same convention as the signal logic above — scale-mode agnostic. No
// Signal Quality score: that weighting needs a trend-context line and a stall/
// absorption layer, neither of which exists in this variant.
// ─────────────────────────────────────────────────────────────────────────────
float sentBullPct = ob1 != 0.0 ? math.max(0.0, wt1Raw) / ob1 * 100.0 : 0.0
float sentBearPct = os1 != 0.0 ? math.max(0.0, -wt1Raw) / math.abs(os1) * 100.0 : 0.0
float sentScore = math.max(-100.0, math.min(100.0, sentBullPct - sentBearPct))
float sentAbsScore = math.abs(sentScore)
// ─────────────────────────────────────────────────────────────────────────────
// DERIVED COLORS
// ─────────────────────────────────────────────────────────────────────────────
// Gradient: bull color at OS level, bear color at OB level
color lineCol1 = color.from_gradient(wt1, os1, ob1, colBull, colBear)
color lineCol2 = color.from_gradient(wt2, os1, ob1, color.new(colBull, 40), color.new(colBear, 40))
// 4-state histogram: rising/falling × positive/negative
bool histRising = hist >= hist[1]
color histColor = hist >= 0.0
? (histRising ? colHistBull : color.new(colHistBull, 55))
: (not histRising ? colHistBear : color.new(colHistBear, 55))
// ─────────────────────────────────────────────────────────────────────────────
// HLINES + ZONE FILLS
// ─────────────────────────────────────────────────────────────────────────────
hline(0, "Zero", color=color.new(color.gray, 60), linestyle=hline.style_dotted, linewidth=1)
hOB2 = hline(ob2, "OB 2", color=color.new(color.red, 45), linestyle=hline.style_solid, linewidth=1)
hOB1 = hline(ob1, "OB 1", color=color.new(color.red, 65), linestyle=hline.style_dashed, linewidth=1)
hOS1 = hline(os1, "OS 1", color=color.new(#26a69a, 65), linestyle=hline.style_dashed, linewidth=1)
hOS2 = hline(os2, "OS 2", color=color.new(#26a69a, 45), linestyle=hline.style_solid, linewidth=1)
fill(hOB1, hOB2, color=showZoneFill ? colZoneOB : na, title="OB Zone")
fill(hOS2, hOS1, color=showZoneFill ? colZoneOS : na, title="OS Zone")
// ─────────────────────────────────────────────────────────────────────────────
// PLOTS — Histogram (rendered first, behind lines)
// ─────────────────────────────────────────────────────────────────────────────
plot(showHist ? hist : na, "Histogram", style=plot.style_columns, color=histColor)
// ─────────────────────────────────────────────────────────────────────────────
// PLOTS — Main lines
// ─────────────────────────────────────────────────────────────────────────────
pZero = plot(0.0, display=display.none)
pWT2 = plot(wt2, "WT Signal", color=lineCol2, linewidth=1)
pWT1 = plot(wt1, "WT Main", color=lineCol1, linewidth=2)
// Gradient shadow between WT1 and zero: bear tint above, bull tint below
fill(pWT1, pZero, ob1, 0.0, color.new(colBear, 78), color.new(colBear, 100),
title="Bear Shadow", display=showShadow ? display.all : display.none)
fill(pWT1, pZero, 0.0, os1, color.new(colBull, 100), color.new(colBull, 78),
title="Bull Shadow", display=showShadow ? display.all : display.none)
// Fill between WT main and signal
fill(pWT1, pWT2,
color=showFill ? (wt1 >= wt2 ? color.new(colBull, 88) : color.new(colBear, 88)) : na,
title="WT Fill")
// ─────────────────────────────────────────────────────────────────────────────
// SIGNAL MARKERS
// ─────────────────────────────────────────────────────────────────────────────
// Extreme crosses — triangle markers at pane edges
plotshape(showSignals and bullExtreme, "Bull Extreme",
style=shape.triangleup, location=location.bottom,
color=color.new(colHistBull, 0), size=size.small,
text="WT↑", textcolor=color.new(colHistBull, 0))
plotshape(showSignals and bearExtreme, "Bear Extreme",
style=shape.triangledown, location=location.top,
color=color.new(colHistBear, 0), size=size.small,
text="WT↓", textcolor=color.new(colHistBear, 0))
// Regular (non-extreme) crosses — small dots on the WT1 line
plotshape(showDots and bullCross and not bullExtreme ? wt1 : na, "Bull Cross",
style=shape.circle, location=location.absolute,
color=color.new(colBull, 15), size=size.tiny)
plotshape(showDots and bearCross and not bearExtreme ? wt1 : na, "Bear Cross",
style=shape.circle, location=location.absolute,
color=color.new(colBear, 15), size=size.tiny)
// ─────────────────────────────────────────────────────────────────────────────
// SCORE DISPLAY — Sentiment Bar, always-on read of current extremity
// ─────────────────────────────────────────────────────────────────────────────
f_scoreColor(score, absS) =>
base = score > 0 ? colBear : colBull
color result = na
if absS < 15
result := color.new(color.gray, 40)
else if absS >= 80
result := color.new(base, 10)
else if absS >= 60
result := color.new(base, 30)
else if absS >= 40
result := color.new(base, 50)
else
result := color.new(base, 70)
result
int scoreBarSegments = 8
int scoreFilled = int(math.round(sentAbsScore / 100.0 * scoreBarSegments))
string scoreBarStr = str.repeat("■", scoreFilled) + str.repeat("·", scoreBarSegments - scoreFilled)
string scoreSignStr = sentScore > 0 ? "+" : ""
string sentTxt = scoreSignStr + str.tostring(sentScore, "#") + " " + scoreBarStr
color sentColor = f_scoreColor(sentScore, sentAbsScore)
var label sentLabel = na
if showSentiment and barstate.islast
label.delete(sentLabel)
sentLabel := label.new(bar_index + 3, 0.0, sentTxt,
xloc=xloc.bar_index, yloc=yloc.price, style=label.style_label_left,
color=sentColor, textcolor=color.white, size=size.normal,
tooltip="How far WT sits inside its own OB/OS zone: 100×WT/Overbought1 when bullish, -100×WT/Oversold1 when bearish, clamped to ±100. |score| < 15 reads as balanced.")
else if not showSentiment and barstate.islast
label.delete(sentLabel)
// ─────────────────────────────────────────────────────────────────────────────
// ALERTS
// ─────────────────────────────────────────────────────────────────────────────
bool alertsOnBarClose = input.bool(true, "Alerts only on bar close (confirmed)", group="8 · Alerts")
bool alertOk = not alertsOnBarClose or barstate.isconfirmed
alertcondition(alertOk and (bullExtreme), "Bull Extreme Cross", "WTAS · BULL CROSS OVERSOLD · {{ticker}} {{interval}}")
alertcondition(alertOk and (bearExtreme), "Bear Extreme Cross", "WTAS · BEAR CROSS OVERBOUGHT · {{ticker}} {{interval}}")
alertcondition(alertOk and (bullCross), "WaveTrend Cross Up", "WTAS · CROSS UP · {{ticker}} {{interval}}")
alertcondition(alertOk and (bearCross), "WaveTrend Cross Down", "WTAS · CROSS DOWN · {{ticker}} {{interval}}")
Overview
WaveTrend Advanced Smoothing [WavesUnchained] provides three normalization scale modes: Classic, Clamp, and Adaptive ±100. The indicator plots main and signal lines alongside a four-state directional histogram, crossing markers, overbought and oversold background zones, and a live sentiment display. Users can adjust channel lengths, signal periods, and threshold levels to fine-tune sensitivity across different trading instruments.
8 selectable smoothing kernels including JMA, T3, and SuperSmoother
Three scale modes: Classic, Clamp ±100, and Adaptive ±100
4-state momentum histogram and signal cross markers
Customizable overbought and oversold zone background fills
The inputs you can change from the gear icon on the chart. Defaults are the author's.
Setting
Type
Default
Variable
Source
source
hlc3
src
Channel Length
int
10
channelLen
Average Length
int
21
averageLen
Signal Length
int
4
signalLen
Type
string
EMA
smoothType
JMA Phase
int
50
jmaPhase
JMA Power
float
2.0
jmaPower
Mode
string
Classic
scaleMode
Adaptive Lookback
int
200
scaleLookback
Overbought 1
float
60
ob1
Overbought 2
float
100
ob2
Oversold 1
float
-60
os1
Show all 27 settings
Setting
Type
Default
Variable
Oversold 2
float
-100
os2
Bull
color
#00bcd4
colBull
Bear
color
#f06292
colBear
Histogram Bull
color
#26a69a
colHistBull
Histogram Bear
color
#ef5350
colHistBear
OB Zone Fill
color
color.new(#ef5350, 90)
colZoneOB
OS Zone Fill
color
color.new(#26a69a, 90)
colZoneOS
Shadow Fill (WT vs Zero)
bool
true
showShadow
Fill WT / Signal
bool
true
showFill
Fill OB / OS Zones
bool
true
showZoneFill
Show Histogram
bool
true
showHist
Show Extreme Signal Markers
bool
true
showSignals
Show Cross Dots
bool
true
showDots
Show Sentiment Bar
bool
true
showSentiment
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.
The Derivative Oscillator calculates a double-exponentially smoothed Relative Strength Index (RSI) and subtracts its Simple Moving Average (SMA) to form the main oscillator line. A secondary signal line is plotted using an SMA of the main oscillator values. Th
This indicator calculates a modified MACD oscillator and signal line alongside a trend EMA filter, plotting the output as a stylized column histogram in a separate pane. Instead of plotting true MACD distance, it quantifies positive and negative histogram mome
This indicator calculates and plots the standard Moving Average Convergence Divergence oscillator in a separate pane. It computes the difference between a fast and slow moving average, derives a signal line through smoothing, and plots a histogram representing
Multi-Timeframe Analysis Suite calculates trend direction and momentum conditions across five user-selected timeframes using moving averages, RSI, and Stochastic oscillators. It evaluates moving average slope and oscillator levels to compute trend alignment, m
MACD-V is a volatility-normalized momentum indicator based on research by Alex Spiroglou. It calculates the standard MACD histogram and normalizes it by dividing by the Average True Range (ATR). Plotted in a separate pane as a colored histogram, it includes us
KamaFama is an indicator that translates a Freqtrade trading strategy into Pine Script for visualization. It combines Kaufman Adaptive Moving Average (KAMA), MAMA/FAMA calculations derived via Hilbert Transform, Williams %R, and correlation trend filters to ev