Dual Bollinger Bands · Band geometry · Histogram
BBTrend Pine Script: understand the sign before using the signal
BBTrend compares two Bollinger envelopes and plots a normalized difference between their lower-band and upper-band distances. Its sign depends on both the moving centers and their widths. Inspecting that geometry explains why a zero crossing is not identical to a moving-average crossover, and why changing the deviation multiplier does not automatically create faster zero-line signals.
The formula compares absolute band distances
TradingView's BBTrend documentation attributes the indicator to John Bollinger and presents the dual-band formula. It describes positive and negative values as bullish and bearish interpretations. The arithmetic below shows the limits of equating those labels with a simple ordering of the two moving averages.
Let d be shortMiddle minus longMiddle, and e be shortHalfWidth minus longHalfWidth. Each half-width is the common multiplier times that window's population standard deviation. The numerator becomes |d − e| − |d + e|.
- If the short basis is positive, the result is positive when d and e have opposite signs, and negative when they have the same sign.
- If either d or e is zero, the numerator is zero. A zero value can therefore occur with different moving averages or with different widths.
- The numerator's absolute size is twice the smaller of |d| and |e|. A large change in one component need not increase the result when the other component is limiting it.
These are consequences of the displayed formula, not empirical claims about market performance. The percentage scale has no fixed ±100 bound. A zero short basis is undefined, a near-zero basis can magnify the ratio and a negative basis reverses its sign. The retained helper has no explicit denominator guard.
Two synthetic windows with a higher short average
Use short length 2, long length 4 and multiplier 2 only to make the arithmetic small. These independent examples are not historical trades. The displayed indicator below retains its 20/50 defaults.
| Quantity | A: [95, 97, 103, 105] | B: [100, 100, 100, 104] |
|---|---|---|
| Short / long mean | 104 / 100 | 102 / 101 |
| Short / long deviation | 1 / √17 ≈ 4.123106 | 2 / √3 ≈ 1.732051 |
| Short lower / upper | 102 / 106 | 98 / 106 |
| Long lower / upper | 91.753789 / 108.246211 | 97.535898 / 104.464102 |
| Lower distance − upper distance | 10.246211 − 2.246211 = 8 | 0.464102 − 1.535898 ≈ −1.071797 |
| BBTrend | 8 / 104 × 100 ≈ +7.692308 | −1.071797 / 102 × 100 ≈ −1.050781 |
Both cases have a higher short mean. In B, both short outer bands are also above the corresponding long bands, yet BBTrend is negative. The short width is larger, so the upper-band distance exceeds the lower-band distance. This is why an unconditional statement that higher short bands produce positive BBTrend is wrong.
Reducing the common positive multiplier from 2 to 1.5 leaves A at approximately +7.692308 and changes B to approximately −0.788086. Their signs do not change. More generally, the sign of e is the sign of short deviation minus long deviation for any positive multiplier, so changing only that multiplier cannot move mathematical zero-crossing times. It can change threshold events such as a cross above +1 and can affect rounding near zero.
The retained Pine Script calculation
The two ta.bb calls use the same close source and multiplier, with short length 20 and long length 50. The example plots the formula directly. It has no inputs, confirmed-bar filter, alert conditions or trade orders.
// 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="BBTrend", overlay=false, max_labels_count=500)
// ──── Dual Bollinger Bands Function ────
p_ta_bbtrend(series float source, simple int short_bb_length, simple int long_bb_length, simple float mult) =>
[short_middle, short_upper, short_lower] = ta.bb(source, short_bb_length, mult)
[long_middle, long_upper, long_lower] = ta.bb(source, long_bb_length, mult)
bbtrend = (math.abs(short_lower - long_lower) - math.abs(short_upper - long_upper)) / short_middle * 100
bbtrend
// ──── Indicator Calculation ────
p_ind_1 = p_ta_bbtrend(close, 20, 50, 2)
// ──── Histogram Color Logic ────
p_ind_1_hist_color = switch
p_ind_1 > 0 and p_ind_1 >= p_ind_1[1] => color.rgb(8, 153, 129, 25)
p_ind_1 > 0 and p_ind_1 < p_ind_1[1] => color.rgb(8, 153, 129, 50)
p_ind_1 < 0 and p_ind_1 > p_ind_1[1] => color.rgb(242, 54, 69, 50)
p_ind_1 < 0 and p_ind_1 <= p_ind_1[1] => color.rgb(242, 54, 69, 25)
=> color.rgb(8, 153, 129, 50)
// ──── Plots ────
plot(p_ind_1, "BBTrend", style = plot.style_columns, color = p_ind_1_hist_color)
hline(0, "BBTrend - Zero line")
Open TradingView's editor from the right-side Pine icon and compile the complete source. Compare the two bands and the resulting distances on the same completed bar. The screenshot shows a display; it does not demonstrate accuracy, returns or a reliable lead over price changes.
Read color as sign plus one-bar change
| Current reading | Compared with previous | Displayed branch |
|---|---|---|
| Positive | Greater than or equal | Green, 25% transparency |
| Positive | Less | Green, 50% transparency |
| Negative | Greater | Red, 50% transparency |
| Negative | Less than or equal | Red, 25% transparency |
Positive equality receives the darker green branch, and negative equality receives the darker red branch. The default color branch is lighter green, including an exact zero; a zero-height column is not a positive signal. Missing values also need to be distinguished from zero.
A move from −4 to −2 is numerically rising but decreasing in absolute height. A move from +4 to +2 is falling but remains positive. Neither one-bar comparison is a mathematical second derivative or proof of acceleration in the asset's price.
Both bands recalculate while the live close changes. A green or red intrabar state can disappear before the bar closes. Fifty available observations initialize the long rolling calculation on a complete series; changing from daily to five-minute bars changes the horizon, not the requirement to use 50 observations.
Make a candidate rule reproducible
- Record source, both lengths, multiplier, session and chart type. Require valid previous and current values before evaluating a cross.
- For an illustrative upward event, define previous BBTrend at or below zero and current BBTrend above zero on a confirmed bar. A positive state on every bar is different from this event.
- If requiring three positive closed bars, evaluate on the third bar and define whether the condition triggers once or repeats. Do not label the earlier cross bar as an entry based on later confirmation.
- If comparing bar height with earlier observations, choose signed value or absolute magnitude explicitly. A positive cross is automatically numerically above a negative previous bar, so that comparison alone adds no independent confirmation.
- Choose a price trend condition separately if needed. A price above an SMA, a rising SMA and short SMA above long SMA are distinct rules.
- Specify execution after the signal, stop updates, exits, costs and unseen evaluation dates. Report the whole trade distribution rather than assigning a win rate to the histogram color.
A reference at a prior three-bar low does not cap realized downside: a gap can fill below it. Divergence tests need fixed pivot definitions and confirmation delays. A stop beyond the last pivot does not prevent another stop-out or establish that the next price move will reverse.
To check an adaptation, request the short and long centers, deviations, upper/lower distances and final ratio in the Data Window. That separates an output-color issue from a calculation issue. Equal lengths should cancel to zero after initialization; a zero multiplier should do the same when the denominator is valid.
Specify a BBTrend adaptationAccount access and AI usage depend on the selected product and plan. Generated changes need compilation and numerical checks; no timing or performance guarantee is established here.