QA GATE · GOOD 18/18 automated ai-check.js · 18 readability, YMYL and image gates · mechanics verified against vike-indicators · live real-data chart embedded
Burstiness (sentence-length SD)10.4
Lexical diversity (MATTR-50)0.82
Consecutive similar sentences2
Repeated paragraph starts (≥3)0
AI-marker phrases0
Demonstrated experience3
Avg sentence length (words)16.5
Repeated 5-grams0
H2 section balance (50–500w)0 off
YMYL risk disclaimeryes
Mechanics verified vs codeyes
Primary keyword coverageyes
Internal links + CTA6
Content visuals8
Every figure has a caption8/8
Alt / caption text coverage100%
Distributed (≥1 per section)5/6 (83%)
Filenames sluggedn/a (inline)
Live interactive chart4 real
Learn · Trading Indicators · Volatility

TTM Squeeze: The Two Channels Our Engine Ships, and the One It Does Not

The TTM squeeze is not really an indicator. It is a comparison between two other indicators, and both of those are in our crate while the comparison itself is not.

Here is the whole idea in one sentence: when a Bollinger channel shrinks until it fits entirely inside a Keltner channel, volatility measured one way has fallen below volatility measured another way. John Carter named that state a squeeze. Our indicator engine computes both channels and has no opinion about their relationship at all.

What the squeeze condition actually says

no squeezebollinger outside keltnersqueezebollinger entirely inside keltner
The shaded block is the Keltner channel and the outlined one is Bollinger. Nothing about price direction enters the comparison — both channels are symmetric about their own middle.

Two ways of measuring width, compared against each other. That is the entire mechanism.

Bollinger measures width with a standard deviation of closes. Keltner measures it with an average true range, which reads highs and lows and counts the gaps between bars. These respond to different things: a market that closes in the same place every day while swinging wildly intraday will have a narrow Bollinger channel and a wide Keltner one. The squeeze fires there.

So the condition is not "volatility is low" but something narrower and more interesting — closing prices have gone quiet relative to the range the bars are still covering. That distinction gets lost in most descriptions, and it is why the state resolves so differently on a 24-hour venue than on an exchange with an overnight gap.

The two channels our engine ships

batch_bollingerbase.rs:53sma(close, 20)±2.0 × stddev(close, 20)divided by n, not n − 1batch_keltnerbase.rs:87ema(close, 20)±2.0 × atr(10)a different window from the midone reads CLOSES, the other reads RANGES and the gaps between barswhich is the only reason the comparison carries information
Both registered in registry.rs with the defaults shown. The Keltner half averages 20 bars for its middle and 10 for its width, which is a split most platforms do not make.

Both live in base.rs, and neither knows the other exists.

How Vike computes them. batch_bollinger at base.rs:53 takes a simple moving average over closing prices, then adds a population standard deviation — divided by n, not by n − 1 — with registered defaults of 20 and 2.0. batch_keltner at base.rs:87 builds its middle from an exponential average and its width from an average true range, registered as ema_length = 20, atr_length = 10, mult = 2.0. Different average, different width, different lookback for the width.

Note the second one carefully. Keltner's smoothing window is 20 bars and its ATR window is 10, so out of the box the two halves of that channel are averaging different amounts of history. That is a deliberate split in our registry, and it is not what most platforms do.

When I ported both kernels, I checked each of them against the Python engine that the crate mirrors, on every symbol. Bollinger matched exactly — 353,117 values on each of the upper, middle and lower rows, maximum difference 0.000e+00.

Searching every crate for squeeze returns six hits. Five are a variable called squeezed inside a Polymarket order-cancellation test, and the sixth is interface text about a column that has to fit. ttm returns nothing anywhere. There is no squeeze row in the registry, so nothing can build one by name.

How often it fires, and why the multiplier decides

keltner mult 2.0 — our registered default49.3%median run 9 barskeltner mult 1.5 — Carter’s original22.1%median run 5 bars · a strict subset of the row above347,067 daily bars across 275 pairslongest unbroken run: 1,178 bars, on USDC
Same rule, same bars, one parameter apart. The stablecoin at the bottom is not a bug in the measurement — a peg really is the most compressed thing on the tape.

This is the number nobody publishes, and it changes what the tool means.

Across 275 pairs and 347,067 daily bars carrying both channels, the squeeze was on for 49.3% of them at our registered Keltner multiplier of 2.0. Half the tape. A condition that describes half of everything is a description of the average day, not a rare setup.

Carter's original uses 1.5 rather than 2.0. Rerun at that setting the same tape reads 22.1%, and every one of those bars is also a squeeze at 2.0 — a strict subset, verified on all of them. Median duration falls from nine bars to five. So a "squeeze indicator" from two platforms can disagree about half its firings while both implementations are correct, because they were never computing the same thing.

One more figure worth keeping. The longest unbroken squeeze on the tape ran 1,178 bars, on USDC — a stablecoin. An indicator that flags a peg as maximum compression is telling the truth; it just is not telling you anything you can trade.

The momentum histogram, and what it regresses

6,105 agreed6,010 did notdid the momentum histogram call the next 20 bars?50.4% against a 50% referencethe regression is real; the forecast in it is notmeasured on all 12,115 releases, nothing filtered
The histogram is linearreg over a real window and it computes exactly what it claims to. What it does not do is predict, and this is the measurement that says so.

Timing is all the condition gives you. It never says which way, and Carter bolted a second component on for that.

That component is a linear regression of the close's distance from a midpoint — the average of the 20-bar range midpoint and the 20-bar simple average. Its sign is the reading. We ship the regression itself as linearreg in the statistics family, which the linear regression slope page covers in full, so this page runs that same kernel rather than a second copy of it.

Does the histogram call the direction? Over 12,115 releases at our own defaults, the following 20 bars agreed with its sign 6,105 times and disagreed 6,010 times. That is a coin flip, measured, and it is the most useful thing on this page.

Where the construction breaks

PYR/USDT daily — a real squeeze release detected 2026-07-21, a 28-bar squeeze released with the histogram at -0.04585 -> -39.4% over 20 bars.
BEAM/USDT daily — a real squeeze release detected 2026-07-09, a 19-bar squeeze released with the histogram at +0.0001223 -> -7.1%, the other way.
ADX/USDT daily — a real squeeze release detected 2026-07-02, a 32-bar squeeze released with the histogram at -0.009109 -> +6.1%, the other way.
AUDIO/USDT daily — a real squeeze release detected 2026-06-05, a 29-bar squeeze released with the histogram at -0.001978 -> -10.7% over 20 bars.
Two of the four releases below went against the histogram, which is what a 50.4% record looks like when you stop averaging it. The dotted marks under the candles are the bars where the Bollinger channel was inside the Keltner one.

Four real releases below, and two of them went the wrong way.

One failure mode is the one the count above already shows: release is a statement about volatility, not about direction, and bolting a direction estimate onto it does not fix that. The second is subtler. Because Bollinger reads closes and Keltner reads ranges, a market can leave the squeeze purely by printing one wide bar with an ordinary close — the Keltner channel widens, the Bollinger channel does not, and the condition ends without anything happening to the trend.

Early on I made the opposite mistake, treating a long squeeze as a bigger promise than a short one. Duration measures how long closes stayed quiet. It does not measure stored energy, which is a metaphor rather than a quantity, and no line on the chart is accumulating anything.

Frequently asked questions

What are the standard TTM squeeze settings? Bollinger at 20 and 2.0, Keltner at 20 with a 1.5 multiplier in Carter's original. Our registry defaults the Keltner multiplier to 2.0, which more than doubles how often the condition holds.

Is the squeeze bullish or bearish? Neither. It reports that one volatility measure has fallen below another, and both channels are symmetric, so the state carries no direction at all.

Can I use it on any timeframe? The arithmetic works on any bars you have. All the percentages above come from daily candles, and they will move on intraday data because gaps behave differently there.

Why does my platform show a different squeeze? Almost always the Keltner multiplier, and after that the ATR length. Ours splits the two lookbacks at 20 and 10; a platform using one number for both is drawing a different channel.

What happens when the squeeze releases? Sometimes a trend, often nothing. On our tape the momentum histogram's sign was right about the next 20 bars 50.4% of the time, which is what a fair coin looks like.

This is educational material, not financial advice. Every figure here was measured on bars that have already closed, past behavior generalizes poorly to future bars, and trading carries real risk of loss — size any position so that being wrong stays survivable.

Read the Keltner Channel breakdown