Bollinger-Keltner Squeeze — Volatility Drop
# Bollinger-Keltner Squeeze — Volatility Drop
When Bollinger Bands tuck inside the Keltner Channels, the market is coiling — realized volatility has dropped below the typical ATR-driven envelope, and a directional move tends to follow once the bands expand back out. This drop plots both envelopes, marks the squeeze on the basis line, and fires the moment the coil releases so you can read the setup without eyeballing the bands.
## What it does
- Plots Bollinger Bands (20, 2.0σ) and Keltner Channels (20-period SMA ± 1.5×ATR) on the same chart with the BB basis as the midline
- Detects **Squeeze ON** = both BB lines are inside the corresponding KC lines (low-vol coil)
- Drops red basis-line dots every bar a squeeze is active and a single yellow "fire" dot the bar the squeeze releases — the actionable trigger
- Tints the chart background red while the squeeze is on so you can see the coil at a glance, even zoomed out
- Built-in `alertcondition` hooks for **Squeeze ON** (just-triggered) and **Squeeze FIRED** (just-released), ready to wire into TradingView alerts
## Recommended settings
- BB length: **20**
- BB std-dev multiplier: **2.0**
- KC length: **20**
- KC ATR multiplier: **1.5**
- Source: **close**
- Show bands: **on**
- Tint background on squeeze: **on**
## Ideal timeframe
Works on anything from 5m intraday up to the daily, but the cleanest setups show up on **15m–4h** for swing entries and **Daily** for position-level breakouts where a multi-bar coil resolves into a real trend leg.
## How to use
Use the squeeze as a *setup*, not a signal — direction is decided by whatever trend / structure read you trust. When the chart is tinted red and you see a string of squeeze dots, the market is winding up; tighten stops, lift size, and wait. The trade trigger is the **yellow fire dot**, which prints the first bar BB pushes back outside KC. Take the breakout in the direction price exits the squeeze (close above prior BB upper = long bias, close below prior BB lower = short bias), and use the basis line as your trail. If the bands re-contract within a few bars without follow-through, treat it as a failed expansion and stand down — these are common on lower timeframes and are the main thing the basis-line trail saves you from. For more conservative entries, drop the KC multiplier to 1.0 (rarer but higher-quality coils) or raise it to 2.0 (more squeezes, more chop). Pair with a higher-timeframe trend filter to bias direction.
## Pine Script v5 code
```
//@version=5
// ─────────────────────────────────────────────
// │ MarketFragments.com | DNA & Market │
// │ info@marketfragments.com │
// │ www.marketfragments.com │
// ─────────────────────────────────────────────
// Time →
// │
// █ █ █│ █
// █ █ █ │ █ █
// █ █ █ │ █ █ █ ╭─╮
// █ █ █ │ █ █ █ █ ╭─╯ ╰─╮
// █ █ █ │ █ █ █ █ █ █ ╭─╯ ╰─╮
// █ █ █ │ █ █ █ █ █ █ █ █ ╭─╯ ╰─╮
// █ █ █ │ █ █ █ █ █ █ █ █ █ █╭─╯ ╰─╮
// █ █ █ │ █ █ █ █ █ █ █ █ ╰─╮ ╭─╯
// █ █ █ │ █ █ █ █ █ █ ╰─╮ ╭─╯
// █ █ █ │ █ █ █ █ ╰─╮ ╭─╯
// █ █ █ │ █ █ ╰─╮ ╭─╯
// █ █ █ │ █ ╰─────╯
// ──────┴──────────────────────────────────────────────────────────────
// T1 T2 T3 T4 T5 T6
//
// Bollinger-Keltner Squeeze (BKS) — MarketFragments free drop
// Detects volatility compression: Bollinger Bands contained inside Keltner Channels.
// Squeeze ON = coil forming. Squeeze FIRE = first bar BB expands back outside KC (breakout trigger).
// Free for non-commercial use. Attribution appreciated.
indicator("Bollinger-Keltner Squeeze (BKS)", shorttitle="BKS", overlay=true)
src = input.source(close, "Source")
bbLen = input.int(20, "BB Length", minval=1)
bbMult = input.float(2.0, "BB StDev Mult", minval=0.1, step=0.1)
kcLen = input.int(20, "KC Length", minval=1)
kcMult = input.float(1.5, "KC ATR Mult", minval=0.1, step=0.1)
showBands = input.bool(true, "Show Bands")
tintBg = input.bool(true, "Tint Background on Squeeze")
// --- Bollinger Bands ---
bbBasis = ta.sma(src, bbLen)
bbDev = ta.stdev(src, bbLen)
bbU = bbBasis + bbMult * bbDev
bbL = bbBasis - bbMult * bbDev
// --- Keltner Channels (SMA + ATR) ---
kcBasis = ta.sma(src, kcLen)
kcRange = ta.atr(kcLen)
kcU = kcBasis + kcMult * kcRange
kcL = kcBasis - kcMult * kcRange
// --- Squeeze state ---
squeezeOn = bbU < kcU and bbL > kcL
squeezeFire = not squeezeOn and squeezeOn[1]
squeezeBegin = squeezeOn and not squeezeOn[1]
// --- Plot bands ---
plot(showBands ? bbU : na, "BB Upper", color=color.new(color.aqua, 20))
plot(showBands ? bbL : na, "BB Lower", color=color.new(color.aqua, 20))
plot(showBands ? kcU : na, "KC Upper", color=color.new(color.orange, 20))
plot(showBands ? kcL : na, "KC Lower", color=color.new(color.orange, 20))
plot(bbBasis, "Basis", color=color.new(color.gray, 40))
// --- Squeeze dots drawn on the basis line ---
plotshape(squeezeOn ? bbBasis : na, title="Squeeze ON", style=shape.circle, location=location.absolute, color=color.new(color.red, 0), size=size.tiny)
plotshape(squeezeFire ? bbBasis : na, title="Squeeze FIRE", style=shape.circle, location=location.absolute, color=color.new(color.yellow, 0), size=size.small)
// --- Background tint while squeeze is active ---
bgcolor(tintBg and squeezeOn ? color.new(color.red, 88) : na, title="Squeeze BG")
// --- Alerts ---
alertcondition(squeezeBegin, title="Squeeze ON", message="BKS: Squeeze ON")
alertcondition(squeezeFire, title="Squeeze FIRED", message="BKS: Squeeze FIRED")
```
Drop your tweaks, screenshots, and questions below — that's how the library gets better.

