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.9
Lexical diversity (MATTR-50)0.83
Consecutive similar sentences2
Repeated paragraph starts (≥3)0
AI-marker phrases0
Demonstrated experience5
Avg sentence length (words)16.2
Repeated 5-grams0
H2 section balance (50–500w)0 off
YMYL risk disclaimeryes
Mechanics verified vs codeyes
Primary keyword coverageyes
Internal links + CTA7
Content visuals10
Every figure has a caption10/10
Alt / caption text coverage100%
Distributed (≥1 per section)5/6 (83%)
Filenames sluggedn/a (inline)
Live interactive chart6 real
Learn · Trading Indicators · Volatility

Bollinger Bands: What %B and Bandwidth Actually Measure

Bollinger Bands wrap a twenty-bar average of the close in a channel two standard deviations wide. Every charting package draws that as three lines. Our engine mostly does not: it computes two normalized numbers, batch_bbands_pctb and batch_bbands_width, and the familiar upper and lower rails are reconstructed from them whenever something needs to plot a picture.

That distinction matters the moment you try to test a rule rather than look at one. A price is not comparable between assets and a channel drawn in dollars is not comparable between years, so any honest scan has to normalize first. This page states the arithmetic in full, shows the port check against the Python implementation the trading indicators engine was built from, and scores the two claims people actually trade on.

What the channel is made of

4 population sigma, end to endquiet: the rails pull inviolent: they throw openuppermid = SMA(close, 20)lowerone price series, one window, three rows
The channel is recomputed every bar, so its width is a report on the last twenty closes rather than a boundary anybody is defending.

Three quantities, computed over the same twenty closes. That is the whole construction.

A simple moving average gives the middle band. Its half-width is a standard deviation of those same closes about that same average, doubled. Add and subtract, and you have the rails. Because the deviation is recomputed every bar, the channel breathes: quiet stretches pull it tight, violent ones throw it open, and neither movement is a forecast of anything.

One detail trips people up when they compare our numbers against their own platform's. Variance here is the population form, dividing by twenty rather than by nineteen — and both implementations in the crate do it that way, deliberately and identically. A package using the sample form draws every band wider by exactly the square root of twenty over nineteen — 2.5978%, which I measured across the tape rather than derived, and the two agreed to six decimals.

Two normalized rows, not three lines

what the engine emitswhat a chart needsbatch_bbands_pctb(close - lower) / (upper - lower)batch_bbands_width(upper - lower) / midtwo rows, comparable across assetsgap = width × midlower = close − pctb × gapupper = lower + gapexact algebra, nothing approximatedand nothing stored twice0.000e+00 between the crate's two implementations, 353,117 values
Normalizing first is not a stylistic choice. A channel measured in dollars cannot be compared between two assets, and %B can.

Here is where our engine parts company with the textbook picture.

batch_bbands_pctb returns one row: where the close sits inside the channel, as a fraction. Zero puts it on the lower rail, one puts it on the upper, and anything outside that range means the close printed beyond a rail. batch_bbands_width returns the other: the distance between rails divided by the middle band, so a channel around a $100,000 asset and one around a $0.004 asset are finally the same kind of number.

Reconstruction is algebra, not approximation. The gap between rails is the width times the middle band; the lower rail is the close minus %B of that gap; the upper rail is the lower one plus it. Nothing is lost and nothing is guessed.

The port check, and what it found. Two independent implementations of these bands live in the crate — batch_bollinger in the base indicator set, and a separate helper the two normalized rows call. Over 353,117 values they agreed to 0.000e+00, bitwise. Against the Python original the story is the one the golden cross page tells too: the crate recomputes each average window while the Python carries a running sum, so 92.93% of values differ somewhere in the final bits, with a largest absolute gap of 1.746e-10 and a relative gap of 6.00e-13. No rule on this page changes sign at that scale.

One guard deserves naming, because it is the kind of thing that quietly corrupts a backtest. When a series goes perfectly flat the channel has zero width, and %B would divide by zero. Both implementations emit nothing at all there. Not a zero, which would read as "pinned to the lower rail" on the one series where price has not moved.

The band is not a wall

break-even 50%45.2%fade the rail22,212 signals48.8%ride the railsame bars, long49.1%base rateany bar47.4%buy the low rail19,683 signals48.8%ride it lowersame bars, short49.1%base rateany barupper band touchlower band touch−3.9 pts−1.7 pts
The comparison that settles it: the same symmetric bracket applied to every eligible bar, which is the number a win rate on its own can never supply.
BAR/USDT daily — a real band event detected 2026-07-18, the close printed above the upper band -> fell 10.7% in 10 days.
ADX/USDT daily — a real band event detected 2026-07-27, the close printed below the lower band -> rose 9.7% in 6 days.

Now the measurement. I scanned 275 Binance pairs out of prod's own archive — 358,342 daily bars, the oldest from August 2017 — and every outcome below resolves first-touch over the next twenty bars against a symmetric 8% bracket. Coverage per symbol ends wherever its own export does, so the bar count is quoted rather than a date range. Break-even is therefore exactly 50% before fees, and a bar that tags both levels is scored a loss.

Start with a number that is not a trade at all. A two-sigma channel would contain 95.45% of observations if returns were normal. Across 347,342 bars it contained 87.94% of closes. Crypto's tails are fat enough that the rail you were told is rare gets breached roughly one day in eight.

So what happens after a breach? The popular reading is reversion: the upper rail is resistance, sell it. Across 22,212 closes at or above the upper band that read returned 45.2% against a 49.1% base rate — 3.9 points worse than entering short on an arbitrary bar. The opposite reading, that price walks the band, came back 48.8%. Both lose. The famous one loses ten times as much.

Lower-rail touches behave the same way in mirror. Buying 19,683 of them returned 47.4% against the same 49.1% base, while the continuation read managed 48.8%. When I see somebody quote a band-touch win rate now, my first question is what an unconditional entry did over the identical bars, because nothing above clears it.

What the squeeze actually predicts

against its OWN past 20 barsagainst ANY bar's next 201.55xsqueeze0.94xany bar33.5%squeeze36.5%any bar7,838 squeezes · direction up 39.6% vs 42.0% on any barexpansion is real; direction is not
Both panels are true. A squeeze is a quiet market getting less quiet, which is not the same claim as a quiet market getting loud.
TKO/USDT daily — a real band event detected 2026-07-26, the width hit a 126-bar low -> +11.2% over the next 20 bars, a 24% range.
BNB/USDT daily — a real band event detected 2026-07-26, the width hit a 126-bar low -> only +5.6% over 20 bars (a squeeze promises movement, not a move).

Squeezes are the other half of the folklore, and this is the half that partly survives contact with data.

Take every bar where batch_bbands_width printed its lowest value in 126 bars, about six months of daily data. That fired 7,838 times. Compared against the same bar's own preceding twenty bars, the following twenty delivered 1.55 times the range, versus 0.94 times for an arbitrary bar. Volatility really does expand after a squeeze, and the effect is not subtle.

Then compare it against a different yardstick and the story flips. Measured against what any bar's next twenty bars delivered, a post-squeeze stretch ran a median 33.5% range versus 36.5%. That is 0.92x — still quieter than average. Both facts are true. A squeeze is a quiet market becoming less quiet, which is not the same thing as a quiet market becoming a loud one.

Direction is worse. Price was higher twenty bars later 39.6% of the time after a squeeze, against 42.0% on any bar, so the squeeze carried no directional information whatsoever. It tells you a range is coming. It has never once told you which way.

Where Bollinger Bands fail

REQ/USDT daily — a real band event detected 2026-07-25, the close printed above the upper band -> ran 19.8% FURTHER up instead (price walked the band).
AI/USDT daily — a real band event detected 2026-07-27, the close printed below the lower band -> fell another 8.1% (a band is not a floor).
Two real losses, one per rail. REQ closed 32% of the way past its upper band and then ran 19.8% further — the channel widened to fit the move instead of capping it. AI closed below the lower rail and fell another 8.1%, which is what a band computed from twenty calm bars does when a forced seller arrives.

Both failures below are real instances this rule produced, and they are the two distinct shapes the failure takes.

First comes the walk. A close prints above the upper rail, the reversion trader shorts it, and price keeps going — because the channel widened to accommodate the move rather than resisting it. The band is derived from recent prices, so a sustained trend drags its own ceiling upward. I learned this one expensively before I learned to measure it.

Then there is the false floor. A close below the lower rail looks like exhaustion and is often just the beginning; a band computed from twenty calm bars has no idea a forced seller has arrived. Pair the level with something that carries no smoothing at all — a candlestick reversal or an RSI divergence — and at least the two instruments can disagree usefully.

Frequently asked questions

Do Bollinger Bands work? Not as a standalone trigger on this evidence. Both readings of a band touch resolved below the base rate across 41,895 real instances, and the reversion reading lost 3.9 points.

What is %B? A fraction, emitted by batch_bbands_pctb, giving the close's position between the two rails. One means it sat on the upper rail, zero the lower, and values outside that span mean it closed beyond one of them.

What settings should I use? Our engine defaults to twenty bars and a multiplier of two, which is what everything here was measured on. Tuning those two numbers on one dataset is the fastest way to build something that works beautifully until you deploy it.

Is the Bollinger squeeze reliable? For expansion, yes: 1.55x the prior range against 0.94x. For direction, no — it beat the coin flip by nothing at all.

How do Bollinger Bands differ from a Keltner Channel? Bollinger measures deviation of the close; the Keltner Channel measures true range, which includes gaps. The Donchian Channel is different again — pure extremes, no averaging.

This is educational material, not financial advice. Every figure here was measured on past bars, 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