The NLMS Volatility Trail is an adaptive trend-following overlay built on a Normalized Least Mean Squares (NLMS) filter — a machine-learning-derived signal processing algorithm that continuously learns from recent price action to produce a smoothed, predictive estimate of price. Unlike a simple moving average, the NLMS filter adjusts its internal weights bar-by-bar using gradient descent, making it more responsive to genuine trend changes while suppressing noise. Typically a flat Trail line indicates a choppy market while a moving Trail line is trending. The middle gray shaded areas are normal non-trending trading areas. Alerts, cloud fill, and paint bars are adjustable in the input settings.
The core Trail line is derived by wrapping that NLMS output in an ATR-based ratchet mechanism: the line can only move in the direction of the trend by at least one ATR step, preventing it from whipsawing on minor retracements. When the Trail reverses direction, it signals a trend change and recolors green (bullish) or red (bearish), with bar coloring and a price/trail fill zone reinforcing the bias visually.
Surrounding the Trail is a five-level ATR Envelope — bands at 2, 4, 6, 8, and 10 ATR multiples above and below — providing a dynamic map of how extended price is relative to its adaptive center. The inner bands (2–4 ATR) define normal trend extension; the middle bands (4–6 ATR) flag elevated momentum; the outer bands (8–10 ATR) mark statistically extreme moves where mean reversion risk is elevated. The asymmetric color scheme (warm colors above, cool colors below) makes overbought/oversold zones immediately readable at a glance.
Best used for: trend identification, trailing stop reference, identifying overextension, and framing re-entry zones after pullbacks to the inner ATR bands.
Conversion and adaptation inspired by this original code: https://www.tradingview.com/script/UUXdk60N-NLMS-Volatility-Trail-BackQuant/
Upper indicator and Watchlist Column code are below:
Here is a picture of SPY on the 1m chart.
CODE:
WATCHLIST COLUMN: This indicates when lows or highs are in the 3 to 5 or -3 to -5 bands.
The core Trail line is derived by wrapping that NLMS output in an ATR-based ratchet mechanism: the line can only move in the direction of the trend by at least one ATR step, preventing it from whipsawing on minor retracements. When the Trail reverses direction, it signals a trend change and recolors green (bullish) or red (bearish), with bar coloring and a price/trail fill zone reinforcing the bias visually.
Surrounding the Trail is a five-level ATR Envelope — bands at 2, 4, 6, 8, and 10 ATR multiples above and below — providing a dynamic map of how extended price is relative to its adaptive center. The inner bands (2–4 ATR) define normal trend extension; the middle bands (4–6 ATR) flag elevated momentum; the outer bands (8–10 ATR) mark statistically extreme moves where mean reversion risk is elevated. The asymmetric color scheme (warm colors above, cool colors below) makes overbought/oversold zones immediately readable at a glance.
Best used for: trend identification, trailing stop reference, identifying overextension, and framing re-entry zones after pullbacks to the inner ATR bands.
Conversion and adaptation inspired by this original code: https://www.tradingview.com/script/UUXdk60N-NLMS-Volatility-Trail-BackQuant/
Upper indicator and Watchlist Column code are below:
Here is a picture of SPY on the 1m chart.
CODE:
Code:
# NLMS Volatility Trail-ATR Envelope
# Converted and Adapted by Chewie 6/27/2026
# Original Concept: https://www.tradingview.com/script/UUXdk60N-NLMS-Volatility-Trail-BackQuant/
declare upper;
# ─── Inputs ───────────────────────────────────────────────────────────────────
input taps = 72;
input mu = 0.195;
input eps = 0.6;
input periodAtr = 14;
input factorAtr = 1.7;
input lineW = 3;
input showFill = no;
input paintBar = yes;
input alerts = no;
DefineGlobalColor("Long", CreateColor(0, 255, 0));
DefineGlobalColor("Short", CreateColor(255, 0, 0));
# ─── NLMS Filter (fold-based M-tap approximation) ─────────────────────────────
def src = close;
def M = taps;
def power = fold i = 0 to M with p = 0 do
p + Sqr(GetValue(src, i + 1));
def pred_raw = fold j = 0 to M with s = 0 do
s + GetValue(src, j + 1);
def pred = pred_raw / M;
def err = src - pred[1];
def k = mu / (eps + power);
def raw = pred + k * err * src[1];
# ─── ATR Band Trail ───────────────────────────────────────────────────────────
def atr = ATR(periodAtr);
def trueRange = atr * factorAtr;
def trueRangeUpper = raw + trueRange;
def trueRangeLower = raw - trueRange;
def nlmsAtr;
nlmsAtr = if IsNaN(nlmsAtr[1]) then raw
else if trueRangeLower > nlmsAtr[1] then trueRangeLower
else if trueRangeUpper < nlmsAtr[1] then trueRangeUpper
else nlmsAtr[1];
# ─── Trend Direction ──────────────────────────────────────────────────────────
def nlmsAtrLong = nlmsAtr > nlmsAtr[1] and nlmsAtr[1] <= nlmsAtr[2];
def nlmsAtrShort = nlmsAtr < nlmsAtr[1] and nlmsAtr[1] >= nlmsAtr[2];
def trendDir;
trendDir = if nlmsAtrLong then 1
else if nlmsAtrShort then -1
else trendDir[1];
# ─── Trail Plot ───────────────────────────────────────────────────────────────
plot Trail = nlmsAtr;
Trail.SetLineWeight(lineW);
Trail.AssignValueColor(
if trendDir == 1 then GlobalColor("Long")
else if trendDir == -1 then GlobalColor("Short")
else Color.GRAY
);
# ─── Fill Anchor Plots ─────────────────────────
plot FillPriceBull = if showFill and close > nlmsAtr then close else Double.NaN;
plot FillTrailBull = if showFill and close > nlmsAtr then nlmsAtr else Double.NaN;
plot FillTrailBear = if showFill and close < nlmsAtr then nlmsAtr else Double.NaN;
plot FillPriceBear = if showFill and close < nlmsAtr then close else Double.NaN;
FillPriceBull.SetDefaultColor(Color.DARK_GREEN);
FillPriceBull.SetPaintingStrategy(PaintingStrategy.LINE);
FillPriceBull.SetLineWeight(1);
FillPriceBull.Hide();
FillTrailBull.SetDefaultColor(Color.DARK_GREEN);
FillTrailBull.Hide();
FillTrailBear.SetDefaultColor(Color.DARK_RED);
FillTrailBear.Hide();
FillPriceBear.SetDefaultColor(Color.DARK_RED);
FillPriceBear.Hide();
# Bull fill: price on top, trail on bottom
AddCloud(FillPriceBull, FillTrailBull, CreateColor(0, 180, 0), CreateColor(0, 40, 0));
# Bear fill: trail on top, price on bottom
AddCloud(FillTrailBear, FillPriceBear, CreateColor(180, 0, 0), CreateColor(40, 0, 0));
# ─── Bar Coloring ─────────────────────────────────────────────────────────────
AssignPriceColor(
if !paintBar then Color.CURRENT
else if trendDir == 1 then GlobalColor("Long")
else if trendDir == -1 then GlobalColor("Short")
else Color.GRAY
);
# ─── Alerts ───────────────────────────────────────────────────────────────────
Alert(alerts and nlmsAtrLong, "NLMS ATR Trend turned Bullish", Alert.BAR, Sound.Ding);
Alert(alerts and nlmsAtrShort, "NLMS ATR Trend turned Bearish", Alert.BAR, Sound.Ding);
# ─── Trail ATR Bands ──────────────────────────────────────────────────────────
input bandAtrPeriod = 200;
input bandMult1 = 2.0;
input bandMult2 = 4.0;
input bandMult3 = 6.0;
input bandMult4 = 8.0;
input bandMult5 = 10.0;
input showBands = yes;
def bATR = ATR(bandAtrPeriod);
plot Band1Up = if showBands then nlmsAtr + bATR * bandMult1 else Double.NaN;
plot Band1Dn = if showBands then nlmsAtr - bATR * bandMult1 else Double.NaN;
plot Band2Up = if showBands then nlmsAtr + bATR * bandMult2 else Double.NaN;
plot Band2Dn = if showBands then nlmsAtr - bATR * bandMult2 else Double.NaN;
plot Band3Up = if showBands then nlmsAtr + bATR * bandMult3 else Double.NaN;
plot Band3Dn = if showBands then nlmsAtr - bATR * bandMult3 else Double.NaN;
plot Band4Up = if showBands then nlmsAtr + bATR * bandMult4 else Double.NaN;
plot Band4Dn = if showBands then nlmsAtr - bATR * bandMult4 else Double.NaN;
plot Band5Up = if showBands then nlmsAtr + bATR * bandMult5 else Double.NaN;
plot Band5Dn = if showBands then nlmsAtr - bATR * bandMult5 else Double.NaN;
# 1 ATR —
Band1Up.SetDefaultColor(Color.gray);
Band1Up.SetLineWeight(1);
Band1Dn.SetDefaultColor(Color.gray);
Band1Dn.SetLineWeight(1);
# 2 ATR —
Band2Up.SetDefaultColor(Color.dark_orange);
Band2Up.SetLineWeight(1);
#Band2Up.SetStyle(Curve.SHORT_DASH);
Band2Dn.SetDefaultColor(Color.lime);
Band2Dn.SetLineWeight(1);
# 3 ATR —
Band3Up.SetDefaultColor(Color.red);
Band3Up.SetLineWeight(1);
Band3Dn.SetDefaultColor(Color.green);
Band3Dn.SetLineWeight(1);
# 4 ATR —
Band4Up.SetDefaultColor(Color.magenta);
Band4Up.SetLineWeight(1);
Band4Dn.SetDefaultColor(Color.cyan);
Band4Dn.SetLineWeight(1);
# 5 ATR —
Band5Up.SetDefaultColor(Color.white);
Band5Up.SetLineWeight(1);
Band5Dn.SetDefaultColor(Color.white);
Band5Dn.SetLineWeight(1);
# Zone fills between bands
AddCloud(Band3Up, Band4Up, color.dark_red, color.dark_red);
AddCloud(Band4Up, Band5Up, color.magenta, color.magenta);
AddCloud(Band5Dn, Band4Dn, color.cyan, color.cyan);
AddCloud(Band4Dn, Band3Dn, color.dark_green, color.dark_green);
AddCloud(Band1Dn, Band1Up, color.gray, color.gray);
AddCloud(Band2Dn, Band2Up, color.gray, color.gray);
WATCHLIST COLUMN: This indicates when lows or highs are in the 3 to 5 or -3 to -5 bands.
Code:
# NLMS Volatility Trail-ATR Envelope Watchlist
# Converted and Adapted by Chewie 6/27/2026
# Original Concept: https://www.tradingview.com/script/UUXdk60N-NLMS-Volatility-Trail-BackQuant/
declare upper;
# ─── Inputs ───────────────────────────────────────────────────────────────────
input taps = 72;
input mu = 0.195;
input eps = 0.6;
input periodAtr = 14;
input factorAtr = 1.7;
input lineW = 3;
# ─── NLMS Filter (fold-based M-tap approximation) ─────────────────────────────
def src = close;
def M = taps;
def power = fold i = 0 to M with p = 0 do
p + Sqr(GetValue(src, i + 1));
def pred_raw = fold j = 0 to M with s = 0 do
s + GetValue(src, j + 1);
def pred = pred_raw / M;
def err = src - pred[1];
def k = mu / (eps + power);
def raw = pred + k * err * src[1];
# ─── ATR Band Trail ───────────────────────────────────────────────────────────
def atr = ATR(periodAtr);
def trueRange = atr * factorAtr;
def trueRangeUpper = raw + trueRange;
def trueRangeLower = raw - trueRange;
def nlmsAtr;
nlmsAtr = if IsNaN(nlmsAtr[1]) then raw
else if trueRangeLower > nlmsAtr[1] then trueRangeLower
else if trueRangeUpper < nlmsAtr[1] then trueRangeUpper
else nlmsAtr[1];
# ─── Trend Direction ──────────────────────────────────────────────────────────
def nlmsAtrLong = nlmsAtr > nlmsAtr[1] and nlmsAtr[1] <= nlmsAtr[2];
def nlmsAtrShort = nlmsAtr < nlmsAtr[1] and nlmsAtr[1] >= nlmsAtr[2];
# ─── Trail Plot ───────────────────────────────────────────────────────────────
plot Trail = nlmsAtr;
Trail.SetLineWeight(lineW);
# ─── Trail ATR Bands ──────────────────────────────────────────────────────────
input bandAtrPeriod = 200;
input bandMult1 = 2.0;
input bandMult2 = 4.0;
input bandMult3 = 6.0;
input bandMult4 = 8.0;
input bandMult5 = 10.0;
input showBands = yes;
def bATR = ATR(bandAtrPeriod);
plot Band1Up = if showBands then nlmsAtr + bATR * bandMult1 else Double.NaN;
plot Band1Dn = if showBands then nlmsAtr - bATR * bandMult1 else Double.NaN;
plot Band2Up = if showBands then nlmsAtr + bATR * bandMult2 else Double.NaN;
plot Band2Dn = if showBands then nlmsAtr - bATR * bandMult2 else Double.NaN;
plot Band3Up = if showBands then nlmsAtr + bATR * bandMult3 else Double.NaN;
plot Band3Dn = if showBands then nlmsAtr - bATR * bandMult3 else Double.NaN;
plot Band4Up = if showBands then nlmsAtr + bATR * bandMult4 else Double.NaN;
plot Band4Dn = if showBands then nlmsAtr - bATR * bandMult4 else Double.NaN;
plot Band5Up = if showBands then nlmsAtr + bATR * bandMult5 else Double.NaN;
plot Band5Dn = if showBands then nlmsAtr - bATR * bandMult5 else Double.NaN;
def UL = high > Band3Up and high < Band4Up;
def UL1 = high > Band4Up and high < Band5Up;
def UL2 = high > Band5Up;
def LL = low < Band3Dn and low > Band4Dn;
def LL1 = low < Band4Dn and low > Band5Dn;
def LL2 = low < Band5Dn;
AddLabel(yes, if UL then "3" else if UL1 then "4" else if UL2 then "5" else if LL then "-3" else if LL1 then "-4" else if LL2 then "-5" else " ",if UL2 or LL2 then COLOR.black else if LL or UL then color.yellow else if UL1 or LL1 then color.blue else Color.BLACK);
AssignBACKGROUNDColor(if UL then color.dark_red else if UL1 then color.magenta else if UL2 then color.white else if LL then color.dark_green else if LL1 then color.cyan else if LL2 then color.white else color.black);
Last edited: