Price envelope · %B · BandWidth
Bollinger Bands Pine Script: calculate the envelope, then define the rule
Bollinger Bands place a moving average between two standard-deviation offsets. The lines show price location and dispersion over a selected window. They do not, by themselves, identify a profitable reversal or assign a probability to the next close. This example exposes the bands, %B and BandWidth so their units and uses can be checked separately.
Three lines, two normalized outputs
For the default SMA construction, calculate the mean and population standard deviation of the same source window. Multiply the deviation by k and add or subtract it from the mean. Here k is 2 and the live example window is 20.
σ = √(Σ(source − window mean)² / n)
Upper = basis + kσ; Lower = basis − kσ
%B = (source − lower) / (upper − lower)
BandWidth = (upper − lower) / basis
A short synthetic window [98, 100, 102] makes the arithmetic visible. Its basis is 100 and population deviation is √(8/3), approximately 1.632993. At k = 2, the bands are 103.265986 and 96.734014. For the last source value 102, %B is approximately 0.806186. BandWidth is approximately 0.0653197, or 6.53197%.
The helper returns %B before BandWidth. The receiving variables below follow that order; reversing their names would silently attach the wrong meaning to each value. Only the first three returned values are plotted in this example, so a naming error in the last two would not be visible in the envelope.
If source equals the lower, middle or upper band, %B is 0, 0.5 or 1 respectively, provided width is nonzero. Values outside the bands can lie below 0 or above 1. %B is not the probability of a profitable trade. BandWidth is a decimal ratio here, not a percent or a price distance.
With constant positive closes of 100, both bands equal 100: absolute width and BandWidth are zero, while %B is undefined. If basis is zero, BandWidth is undefined. Negative basis reverses its normalized sign. Any downstream alert or ranking needs an explicit policy for those cases.
What the inventor's rules actually support
John Bollinger's published rules describe bands as a relative definition of high and low, distinguish band tags from trading signals, and identify 20 periods with two deviations as defaults. They also explicitly caution against statistical assumptions based on the deviation calculation. A 95% containment claim is therefore not a property of this script.
The same source distinguishes fully exponential bands from changing only the middle average: an exponential construction uses exponential calculations for both the average and deviation. Pineify's helper changes the center through its MA selector but leaves ta.stdev unchanged. Its EMA-center variant is a different construction.
A band can be touched repeatedly as price trends. A flat middle line also does not guarantee a reversal. Squeeze, continuation and re-entry rules are different hypotheses and need separate evaluation; no win-rate estimate follows from the three plotted lines.
Pine Script with explicit return ordering
The calculation is retained from Pineify's helper. The receiving names identify its fourth output as %B and fifth as BandWidth. This page corrects that naming only; it does not change the core formula. The example contains no strategy orders, alert conditions or input declarations.
// This source code is subject to the terms of the Mozilla Public License 2.0 at https://mozilla.org/MPL/2.0/
// © Pineify
//@version=6
indicator(title="Bollinger Bands", overlay=true, max_labels_count=500)
p_ta_ma(series float source, simple int length, simple string ma_type) =>
switch ma_type
"SMA" => ta.sma(source, length)
"EMA" => ta.ema(source, length)
"RMA" => ta.rma(source, length)
"HMA" => ta.hma(source, length)
"WMA" => ta.wma(source, length)
"TMA" => ta.sma(ta.sma(source, math.ceil(length / 2)), math.floor(length / 2) + 1)
p_ta_bb(simple int length, simple string ma_type, series float source, series float mult) =>
basis = p_ta_ma(source, length, ma_type)
dev = mult * ta.stdev(source, length)
upper = basis + dev
lower = basis - dev
bbr = (source - lower)/(upper - lower)
bbw = (upper-lower)/basis
[basis, upper, lower, bbr, bbw]
[p_ind_1_middle, p_ind_1_upper, p_ind_1_lower, p_ind_1_bbr, p_ind_1_bbw] = p_ta_bb(20, "SMA", close, 2)
plot(p_ind_1_middle, "BB - Middle", color.rgb(255, 152, 0, 0), 1)
p_ind_1_upper_plot = plot(p_ind_1_upper, "BB - Upper", color.rgb(41, 98, 255, 0), 1)
p_ind_1_lower_plot = plot(p_ind_1_lower, "BB - Lower", color.rgb(41, 98, 255, 0), 1)
fill(p_ind_1_upper_plot, p_ind_1_lower_plot, title = "BB - Band", color=color.rgb(33, 150, 243, 95))
Use the Pine icon on TradingView's right toolbar to open the editor, paste the complete script and compare values on completed bars. Orange is the basis, blue lines are the offsets and the fill shows their separation. The screenshot is a display example, not a backtest.
The helper supports SMA, EMA, RMA, HMA, WMA and TMA centers. Standard deviation is still calculated from the source window. Changing the center therefore changes price position and normalized width even when the deviation offset is unchanged. The copied call fixes 20, SMA, close and 2; it does not expose an Inputs panel.
A squeeze needs a time window and a release rule
Define narrowness before calling something a squeeze. One test is current BandWidth below the minimum of the previous 60 completed bars. Another is equality with the minimum of a 60-bar window that includes the current bar. These are not interchangeable: a value cannot be strictly below a minimum that includes itself.
For a synthetic prior minimum of 0.04 and current BandWidth of 0.03, the previous-window test is true. That says current normalized width is smaller than those earlier values. It supplies neither a breakout direction nor a deadline. Sixty bars are also not always three calendar months.
A reproducible candidate could arm a setup after that closed-bar test, then expire it after five bars. A later close above its upper band could trigger a long candidate; a close below its lower band could trigger a short candidate. Specify whether the test uses each bar's current band or the fixed band from the arming bar. The five-bar expiry is an illustrative rule, not an optimized setting.
If a volume filter is added, distinguish current volume divided by a previous completed-window mean from a mean that includes the current observation. Volume alone is not signed buying or selling pressure. Record how missing or zero volume affects eligibility.
Separate three testable entry ideas
| Hypothesis | Example condition to specify | Main ambiguity to resolve |
|---|---|---|
| Squeeze breakout | Prior narrowness, then a closed-bar break outside a band | Arming period, expiration and current versus fixed band |
| Mean reversion | Previous close below its lower band, current close back inside | Re-entry is not the same as a wick touch; trend can continue |
| Trend pullback | A predefined rising basis and a subsequent return above it | Slope length, equality, entry fill and invalidation |
An RSI threshold can be an additional condition. It does not automatically remove a known proportion of false signals. Define any pivot-based divergence with its confirmation delay; a pivot drawn on an earlier bar was not necessarily known on that bar.
For an illustrative lower band at 450 and ATR at 2.5, a one-ATR reference below that band is 447.5. If a long fills at 452, the distance to that reference is 4.5. A later fill at 446 produces a loss of 6 per unit before costs, not 4.5. Bands move, so specify whether stops and targets are frozen at entry or updated later.
Evaluate each idea with a fixed symbol, interval, session, source and cost model, then examine a later period excluded from parameter selection. Compare trade count, average gain and loss, drawdown and exposure. A higher win rate alone can hide larger losing trades.
Compare the quantity each envelope measures
Bollinger width uses dispersion of the selected source values. An ATR-based Keltner Channel uses a smoothed true-range offset, with the exact smoothing depending on the implementation. A Donchian Channel uses window highs and lows. Their response cannot be ranked by a universal lag or number of signals per day.
Consider repeated closes at 100 with highs of 110 and lows of 90. Close-based standard deviation can be zero while true range and the high/low channel remain wide. Conversely, an old unusual close leaving a rolling window can narrow Bollinger width even if today's high-low range has not changed.
Changing k from 2 to 3 with the same source, window and center increases absolute width by 50%. It does not establish a 50% change in signal count. Changing length alters both center and deviation, so its effect is not monotonic. Normalized BandWidth can also rise when its positive basis falls, without an increase in absolute width.
Pineify's BB indicator configuration is marked free. AI requests and account entitlements are separate. For an adaptation, specify source, average, deviation convention, output order, denominator guards and closed-bar behavior, then compile and compare the generated output.
Specify a Bollinger Bands adaptation