Candle bodies · Full-width range · Chart data
Bollinger Bars Pine Script: inspect the whole candle range
Bollinger Bars draw the high-to-low range as a broad colored block, then distinguish the open-to-close body within it. This Pineify example uses blue for the range, green when close exceeds open, and red otherwise. It changes how existing chart values are displayed; it adds no volatility band or forecast.
Two drawing layers, one set of OHLC values
The first call passes high, high, low, low to plotcandle. Its synthetic open is the high and its synthetic close is the low, so its blue body fills the entire range. The second call uses the actual open, high, low and close, with native wicks hidden. Its green or red body covers the middle of that blue range. These remaining blue regions are broad blocks, not ordinary thin wick lines.
| Open / close | Body | Upper / lower blue range | Body / full range |
|---|---|---|---|
| 100 / 103 | Green, 100–103 | 103–105 / 97–100 | 3 / 8 = 37.5% |
| 103 / 100 | Red, 100–103 | 103–105 / 97–100 | 3 / 8 = 37.5% |
| 100 / 100 | Red branch, zero-height body | 100–105 / 97–100 | 0 / 8 = 0% |
Upper remainder = high − max(open, close); lower remainder = min(open, close) − low. The full range is high − low. A zero-range bar has an undefined body-to-range ratio and should not be assigned a percentage by dividing by zero. The supplied indicator does not compute these ratios; they are independent checks of the geometry.
The helper function returns close, but its assigned output is not used to decide color. The actual rule is the strict comparison close > open. There is no previous-close comparison, volume test, moving average or standard deviation in that rule.
A green bar can still be a down day
Suppose the prior close is 110, the next open is 100, and the next close is 103. The body is green because 103 exceeds 100, while the close-to-close change is −7, or −6.36%. A gap is measured relative to the previous close; it is not included in the current bar's open-close color test.
Likewise, three green bars with open/close pairs 100/101, 98/99 and 96/97 have falling closes despite all three intrabar gains. A run of green bodies does not prove an upward trend, buyer-initiated volume or accumulation. Red includes equality in this code, so a red run can include unchanged open-close bars.
On an unfinished bar opening at 100, updates at 101, 99 and 100 give green, red and red respectively. The final color is only known when the bar closes. No closed-bar gate, alert condition or order logic is present in the retained source.
Standard chart values are used directly. On a Heikin-Ashi chart those values are synthetic, so this overlay does not recover traded OHLC prices. A line chart can still supply underlying bar OHLC; hiding candle graphics is not the same as removing those data. Verify the chart type and feed before interpreting the display.
The existing Pine Script example
The source below preserves the two plotting calls and framework helpers. The time-range helpers are not called by this candle display. There are no input declarations or custom signals. Compilation checks syntax; a chart comparison is still needed to inspect stacking and appearance.
// 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 Bars", overlay=true, max_labels_count=500)
p_comm_time_range_to_unix_time(string time_range, int date_time = time, string timezone = syminfo.timezone) =>
int start_unix_time = na
int end_unix_time = na
int start_time_hour = na
int start_time_minute = na
int end_time_hour = na
int end_time_minute = na
if str.length(time_range) == 11
start_time_hour := math.floor(str.tonumber(str.substring(time_range, 0, 2)))
start_time_minute := math.floor(str.tonumber(str.substring(time_range, 3, 5)))
end_time_hour := math.floor(str.tonumber(str.substring(time_range, 6, 8)))
end_time_minute := math.floor(str.tonumber(str.substring(time_range, 9, 11)))
else if str.length(time_range) == 9
start_time_hour := math.floor(str.tonumber(str.substring(time_range, 0, 2)))
start_time_minute := math.floor(str.tonumber(str.substring(time_range, 2, 4)))
end_time_hour := math.floor(str.tonumber(str.substring(time_range, 5, 7)))
end_time_minute := math.floor(str.tonumber(str.substring(time_range, 7, 9)))
start_unix_time := timestamp(timezone, year(date_time, timezone), month(date_time, timezone), dayofmonth(date_time, timezone), start_time_hour, start_time_minute, 0)
end_unix_time := timestamp(timezone, year(date_time, timezone), month(date_time, timezone), dayofmonth(date_time, timezone), end_time_hour, end_time_minute, 0)
[start_unix_time, end_unix_time]
p_comm_time_range_to_start_unix_time(string time_range, int date_time = time, string timezone = syminfo.timezone) =>
int start_time_hour = na
int start_time_minute = na
if str.length(time_range) == 11
start_time_hour := math.floor(str.tonumber(str.substring(time_range, 0, 2)))
start_time_minute := math.floor(str.tonumber(str.substring(time_range, 3, 5)))
else if str.length(time_range) == 9
start_time_hour := math.floor(str.tonumber(str.substring(time_range, 0, 2)))
start_time_minute := math.floor(str.tonumber(str.substring(time_range, 2, 4)))
timestamp(timezone, year(date_time, timezone), month(date_time, timezone), dayofmonth(date_time, timezone), start_time_hour, start_time_minute, 0)
p_comm_time_range_to_end_unix_time(string time_range, int date_time = time, string timezone = syminfo.timezone) =>
int end_time_hour = na
int end_time_minute = na
if str.length(time_range) == 11
end_time_hour := math.floor(str.tonumber(str.substring(time_range, 6, 8)))
end_time_minute := math.floor(str.tonumber(str.substring(time_range, 7, 9)))
else if str.length(time_range) == 9
end_time_hour := math.floor(str.tonumber(str.substring(time_range, 5, 7)))
end_time_minute := math.floor(str.tonumber(str.substring(time_range, 7, 9)))
timestamp(timezone, year(date_time, timezone), month(date_time, timezone), dayofmonth(date_time, timezone), end_time_hour, end_time_minute, 0)
p_comm_timeframe_to_seconds(simple string tf) =>
float seconds = 0
tf_lower = str.lower(tf)
value = str.tonumber(str.substring(tf_lower, 0, str.length(tf_lower) - 1))
if str.endswith(tf_lower, 's')
seconds := value
else if str.endswith(tf_lower, 'd')
seconds := value * 86400
else if str.endswith(tf_lower, 'w')
seconds := value * 604800
else if str.endswith(tf_lower, 'm')
seconds := value * 2592000
else
seconds := str.tonumber(tf_lower) * 60
seconds
p_custom_sources() =>
[open, high, low, close, volume]
p_ta_bollinger_bars() =>
close
p_ind_1 = p_ta_bollinger_bars()
plotcandle(high, high, low, low, title="Bollinger Bars - Wicks", color=color.rgb(13, 52, 158, 0), bordercolor=color.rgb(13, 52, 158, 0), wickcolor=na, display=display.pane)
col = close > open ? color.rgb(8, 153, 129, 0) : color.rgb(242, 54, 69, 0)
plotcandle(open, high, low, close, title="Bollinger Bars - Bodies", color=col, bordercolor=col, wickcolor=na)
Adding this indicator does not delete or replace the original chart series. If its colors are obscured, inspect Visual Order / Bring to Front or the original series visibility. The source does not explicitly request that it appear in front of chart candles.
Builder settings and copied-source settings differ
| Pineify drawing setting | Default | Effect when generated |
|---|---|---|
| Show Wicks | Enabled | Includes the full-range blue plotcandle call |
| Wick Color | #0D349E | Colors that full-range layer |
| Bull Color | #089981 | Body when close > open |
| Bear Color | #F23645 | Body when close <= open |
These are builder drawing controls, not four Inputs in the copied script. Regenerating with Show Wicks disabled omits the first drawing call. That removes the high-low extensions from this overlay, so choosing it trades away range information; it is not a universally better intraday setting. TradingView Style controls and script input declarations are separate mechanisms.
What the name and comparison actually mean
TradingView's Bollinger Bars documentation credits John Bollinger and describes the equal-width body and range sections. This is not an unnamed Pineify invention. The retained example implements that visual convention; no claim of byte-for-byte identity with TradingView's built-in source is made.
Ordinary TradingView candles already support body, border and wick customization, including an option to color by the previous close. A separate indicator is useful for this broad-range presentation or custom logic, not because native candles lack color controls.
Bollinger Bands calculate an average and dispersion envelope. Bollinger Bars do not. Heikin-Ashi candles calculate synthetic OHLC values; applying this display to them preserves those synthetic inputs instead of converting them to market prices.
There is no measured scanning-time advantage, prediction accuracy or strategy win rate attached to these colors. Rendering still consumes resources, even though no rolling indicator calculation is involved. Prefer a color scheme that remains distinguishable against your actual background, and retain numeric OHLC values for checks.
A reproducible chart check before strategy work
- Use a recorded symbol, session, chart type and timeframe. Open the Pine Editor from the chart's Pine control, paste the complete source and add it to the chart.
- Compare a green, red, equal-open-close and zero-range bar against the Data Window. Check both the body and full range, then inspect an open bar separately.
- Compare against native candles with previous-close coloring disabled. If colors differ, check equality handling, chart type and visual order before changing the formula.
- If researching a pattern such as three green bars above EMA(50), define it on completed bars and evaluate the next eligible order execution. This illustrative condition is not present in the script and is not an optimized rule.
- Specify exits, position sizing, spread, commissions, slippage and gap handling. Compare the same condition calculated directly from OHLC; changing its display alone cannot change the underlying signal timestamps.
For example, a proposed long entry at 103 with a stop reference at 99 has planned price risk 4 per unit. A gap to an executable exit at 97 produces loss 6 per unit before costs. Green bodies and a visible stop reference do not cap the realized loss.
Use the official bar-plotting reference to check plotcandle parameters. For an adaptation, specify whether settings should be generated constants or editable inputs, how ties are colored and whether signals wait for the close.
Specify a candle-display adaptationPineify access and usage depend on the selected product and account. This builder indicator is marked as paid in its configuration. Generated adaptations require compilation and chart verification.