Skip to content

Free TradingView indicator

Volatility Squeeze Momentum

Bollinger-inside-Keltner squeeze dots with a linear-regression momentum histogram and alerts for squeeze start and release.

Volatilityv1.0TradingView (Pine Script v6) Non-repainting signals Alerts

What it does

Volatility Squeeze Momentum highlights periods when volatility contracts unusually tightly, which often come before a larger move, and shows the direction and strength of momentum when that contraction ends.

A row of dots on the zero line shows the squeeze state, and a histogram shows momentum. The idea is simple: Bollinger Bands, which react to standard deviation, usually sit outside Keltner Channels, which react to average range. When the Bollinger Bands shrink inside the Keltner Channels, the market is in a squeeze.

How it works

  • Bollinger Bands: SMA(close, BB length) ± BB deviations × standard deviation.
  • Keltner Channels: SMA(close, KC length) ± KC multiplier × SMA(true range, KC length).
  • Squeeze on (orange dot): both Bollinger Bands are inside the Keltner Channels.
  • Squeeze off (grey dot): both Bollinger Bands are outside the Keltner Channels.
  • Neither (blue dot): one band inside, one outside.

Momentum: the script takes the midpoint of the highest high and lowest low over the momentum length, averages it with the SMA of close, and measures close minus that reference. A linear regression over the same length smooths this into the histogram value. Bright green means positive and rising, pale green positive and falling, bright red negative and falling, pale red negative and rising.

A release is the first closed bar after a squeeze ends. It is labeled bullish if momentum is above zero and bearish if below.

How to use it

  • A run of orange dots means volatility is compressed. It says nothing yet about direction; wait for the release.
  • At release, the sign and slope of the histogram suggest the direction of the expansion. A bright bar in the release direction is a stronger reading than a pale one.
  • Pale bars after a strong run often show momentum fading, which some traders use to manage or scale out of a position.
  • Check the higher-timeframe trend and nearby levels before acting. The Bollinger Bands lesson explains the volatility side.

Settings

InputDefaultWhat it changes
BB length / deviations20 / 2.0Bollinger Band sensitivity.
KC length / range multiplier20 / 1.5Keltner Channel width. Higher multiplier = more squeezes.
Use true range for KCOnTrue range or simple high-low range.
Momentum length20Smoothness of the momentum histogram.
Histogram and dot colorsvariousFour momentum shades and three squeeze states.

Limitations

  • A squeeze shows that volatility is low, not that a breakout is coming soon. Some squeezes last a long time, and some release with a small move.
  • The release direction can reverse quickly; false breakouts are common, especially around news.
  • Momentum is built from moving averages and regression, so it lags turning points.
  • Squeeze frequency depends heavily on the KC multiplier and the market, so the same settings will behave differently on gold and on a quiet major pair.

Pine Script source

In TradingView open Pine Editor, paste the code, click Save and then Add to chart.

FXP_VolatilitySqueezeMomentum.pine

71 lines

// © FXPIPS ONLINE — https://fxpips.online/tradingview-indicators/volatility-squeeze-momentum/
// License: MIT. Educational tool — not financial advice. Trading involves substantial risk.
//@version=6
// Volatility squeeze: Bollinger Bands contracting inside Keltner Channels, shown as dots on the
// zero line, plus a linear-regression momentum histogram. Squeeze releases confirm on bar close.
indicator("FXP Volatility Squeeze Momentum", shorttitle = "FXP Squeeze", overlay = false)

// ── Inputs ─────────────────────────────────────────────────────────────
grpB   = "Bollinger Bands"
bbLen  = input.int(20, "BB length", minval = 2, group = grpB)
bbMult = input.float(2.0, "BB standard deviations", minval = 0.1, step = 0.1, group = grpB)

grpK   = "Keltner Channels"
kcLen  = input.int(20, "KC length", minval = 2, group = grpK)
kcMult = input.float(1.5, "KC range multiplier", minval = 0.1, step = 0.1, group = grpK)
useTr  = input.bool(true, "Use true range for KC", group = grpK, tooltip = "Off = simple high - low range.")

grpM   = "Momentum"
momLen = input.int(20, "Momentum length", minval = 2, group = grpM)

grpV       = "Display"
colUpStr   = input.color(#26a69a, "Momentum up, rising", group = grpV)
colUpWeak  = input.color(#a5d6a7, "Momentum up, falling", group = grpV)
colDnStr   = input.color(#ef5350, "Momentum down, falling", group = grpV)
colDnWeak  = input.color(#ef9a9a, "Momentum down, rising", group = grpV)
colSqzOn   = input.color(#ff9800, "Squeeze on dot", group = grpV)
colSqzOff  = input.color(#9e9e9e, "Squeeze off dot", group = grpV)
colNoSqz   = input.color(#2962ff, "No squeeze dot", group = grpV)

// ── Bands ──────────────────────────────────────────────────────────────
bbBasis = ta.sma(close, bbLen)
bbDev   = bbMult * ta.stdev(close, bbLen)
bbUpper = bbBasis + bbDev
bbLower = bbBasis - bbDev

kcBasis  = ta.sma(close, kcLen)
trueRange = ta.tr(true)
rangeSrc = useTr ? trueRange : high - low
kcRange  = ta.sma(rangeSrc, kcLen)
kcUpper  = kcBasis + kcMult * kcRange
kcLower  = kcBasis - kcMult * kcRange

sqzOn  = bbLower > kcLower and bbUpper < kcUpper
sqzOff = bbLower < kcLower and bbUpper > kcUpper

// ── Momentum: linear regression of price versus its range/average midpoint ──
rangeMid = math.avg(ta.highest(high, momLen), ta.lowest(low, momLen))
midRef   = math.avg(rangeMid, ta.sma(close, momLen))
mom      = ta.linreg(close - midRef, momLen, 0)
momUp    = mom > nz(mom[1])

// ── Signals ────────────────────────────────────────────────────────────
sqzStart    = sqzOn and not sqzOn[1] and barstate.isconfirmed
released    = sqzOn[1] and not sqzOn and barstate.isconfirmed
releaseBull = released and mom > 0
releaseBear = released and mom < 0

// ── Plots ──────────────────────────────────────────────────────────────
histColor = mom >= 0 ? (momUp ? colUpStr : colUpWeak) : (momUp ? colDnWeak : colDnStr)
dotColor  = sqzOn ? colSqzOn : sqzOff ? colSqzOff : colNoSqz
plot(mom, "Momentum", color = histColor, style = plot.style_columns)
plot(0, "Squeeze dots", color = dotColor, style = plot.style_circles, linewidth = 3)

plotshape(releaseBull, "Squeeze release, bullish", style = shape.triangleup, location = location.bottom, color = colUpStr, size = size.small)
plotshape(releaseBear, "Squeeze release, bearish", style = shape.triangledown, location = location.top, color = colDnStr, size = size.small)

// ── Alerts ─────────────────────────────────────────────────────────────
alertcondition(sqzStart, "Squeeze started", "Volatility squeeze started on {{ticker}} ({{interval}})")
alertcondition(releaseBull, "Squeeze released bullish", "Volatility squeeze released with positive momentum on {{ticker}} ({{interval}})")
alertcondition(releaseBear, "Squeeze released bearish", "Volatility squeeze released with negative momentum on {{ticker}} ({{interval}})")

Key inputs

Key inputs of Volatility Squeeze Momentum
InputDefaultDescription
BB length20Length of the Bollinger Band basis and standard deviation.
BB standard deviations2.0Width of the Bollinger Bands.
KC length20Length of the Keltner Channel basis and range average.
KC range multiplier1.5Width of the Keltner Channels in average ranges.
Use true range for KCOnAverage true range instead of simple high-low range.
Momentum length20Window for the momentum midpoint and linear regression.

Educational tools, not advice. Indicators and Expert Advisors describe price; they do not predict it, and past behaviour does not guarantee future results. Test everything on a demo account first and never risk money you cannot afford to lose. See our disclaimer.

Keep exploring

Related tradingview indicators