The Pine Script v6 code
Bollinger Bands with a basis line that changes color with its slope, a fill between the bands and a dot over bars that close above the upper band.
Checked by Edgoro Pine Check with no errors and no warnings. Pine Check is a static checker, not TradingView's compiler, and this script was not run in TradingView.
//@version=6
indicator("Bollinger Bands: plot, fill and plotshape", overlay = true)
len = input.int(20, "Length", minval = 1)
mult = input.float(2.0, "Standard deviations", minval = 0.1, step = 0.1)
[basis, upper, lower] = ta.bb(close, len, mult)
// a color that changes bar by bar
plot(basis, "Basis", basis > basis[1] ? color.teal : color.red, 2)
p1 = plot(upper, "Upper", color.gray)
p2 = plot(lower, "Lower", color.gray)
fill(p1, p2, color.new(color.gray, 90), "Band fill")
plotshape(close > upper, "Close above upper band", shape.circle, location.abovebar, color.red, size = size.tiny)
The MQL5 program
A custom indicator drawing the same bands, colored basis, fill and dots, from the built-in iBands. It only draws.
Save it as MQL5/Indicators/plot-to-indicator-buffers.mq5 and compile it in MetaEditor (F7): 0 errors, 0 warnings.
//+------------------------------------------------------------------+
//| plot(), fill() and plotshape() as MQL5 indicator buffers |
//| Bollinger Bands from the built-in iBands handle. |
//| Custom indicator: save in MQL5/Indicators and compile (F7). |
//+------------------------------------------------------------------+
#property indicator_chart_window
#property indicator_buffers 7
#property indicator_plots 5
// Plot 1 - fill(p1, p2): DRAW_FILLING uses two buffers. Drawn first so the lines sit on top
#property indicator_label1 "Band fill"
#property indicator_type1 DRAW_FILLING
#property indicator_color1 clrWhiteSmoke,clrWhiteSmoke
// Plot 2 - plot(basis, color = cond ? teal : red): DRAW_COLOR_LINE = value buffer + color index buffer
#property indicator_label2 "Basis"
#property indicator_type2 DRAW_COLOR_LINE
#property indicator_color2 clrTeal,clrRed
#property indicator_width2 2
// Plots 3 and 4 - plot(upper), plot(lower)
#property indicator_label3 "Upper"
#property indicator_type3 DRAW_LINE
#property indicator_color3 clrGray
#property indicator_label4 "Lower"
#property indicator_type4 DRAW_LINE
#property indicator_color4 clrGray
// Plot 5 - plotshape(close > upper, shape.circle, location.abovebar)
#property indicator_label5 "Close above upper band"
#property indicator_type5 DRAW_ARROW
#property indicator_color5 clrRed
input int InpLen = 20; // Length
input double InpMult = 2.0; // Standard deviations
double FillUpBuf[], FillLoBuf[], BasisBuf[], BasisColorBuf[], UpperBuf[], LowerBuf[], SignalBuf[];
int bandsHandle = INVALID_HANDLE;
int OnInit()
{
if(InpLen < 2 || InpMult <= 0.0)
return INIT_PARAMETERS_INCORRECT;
// Buffer order follows the plots: 2 for the filling, 2 for the color line, then 1 each
SetIndexBuffer(0, FillUpBuf, INDICATOR_DATA);
SetIndexBuffer(1, FillLoBuf, INDICATOR_DATA);
SetIndexBuffer(2, BasisBuf, INDICATOR_DATA);
SetIndexBuffer(3, BasisColorBuf, INDICATOR_COLOR_INDEX);
SetIndexBuffer(4, UpperBuf, INDICATOR_DATA);
SetIndexBuffer(5, LowerBuf, INDICATOR_DATA);
SetIndexBuffer(6, SignalBuf, INDICATOR_DATA);
PlotIndexSetInteger(4, PLOT_ARROW, 159); // Wingdings dot, like shape.circle
PlotIndexSetInteger(4, PLOT_ARROW_SHIFT, -10); // above the bar
for(int p = 0; p < 5; p++)
{
PlotIndexSetDouble(p, PLOT_EMPTY_VALUE, EMPTY_VALUE);
PlotIndexSetInteger(p, PLOT_DRAW_BEGIN, InpLen);
}
// ta.bb(close, len, mult): middle = SMA, bands = SMA +/- mult x population standard deviation
bandsHandle = iBands(_Symbol, _Period, InpLen, 0, InpMult, PRICE_CLOSE);
if(bandsHandle == INVALID_HANDLE)
{
Print("Could not create the iBands handle, error ", GetLastError());
return INIT_FAILED;
}
return INIT_SUCCEEDED;
}
void OnDeinit(const int reason)
{
IndicatorRelease(bandsHandle);
}
int OnCalculate(const int rates_total,
const int prev_calculated,
const datetime &time[],
const double &open[],
const double &high[],
const double &low[],
const double &close[],
const long &tick_volume[],
const long &volume[],
const int &spread[])
{
if(BarsCalculated(bandsHandle) < rates_total)
return prev_calculated;
int start = prev_calculated > 0 ? prev_calculated - 1 : 0;
int count = rates_total - start;
// iBands buffers: 0 = BASE_LINE, 1 = UPPER_BAND, 2 = LOWER_BAND (oldest first in these arrays)
double mid[], up[], lo[];
if(CopyBuffer(bandsHandle, BASE_LINE, 0, count, mid) != count ||
CopyBuffer(bandsHandle, UPPER_BAND, 0, count, up) != count ||
CopyBuffer(bandsHandle, LOWER_BAND, 0, count, lo) != count)
return prev_calculated;
for(int k = 0; k < count; k++)
{
int i = start + k;
BasisBuf[i] = mid[k];
UpperBuf[i] = up[k];
LowerBuf[i] = lo[k];
FillUpBuf[i] = up[k];
FillLoBuf[i] = lo[k];
// color = basis > basis[1] ? teal : red -> index 0 or 1 of indicator_color2
BasisColorBuf[i] = (i > 0 && BasisBuf[i] > BasisBuf[i - 1]) ? 0.0 : 1.0;
// plotshape(close > upper): a value where the shape goes, EMPTY_VALUE elsewhere
SignalBuf[i] = close[i] > up[k] ? high[i] : EMPTY_VALUE;
}
return rates_total;
}
//+------------------------------------------------------------------+
Line by line
plot(basis, "Basis", basis > basis[1] ? color.teal : color.red, 2)#property indicator_type2 DRAW_COLOR_LINE
#property indicator_color2 clrTeal,clrRed
...
BasisColorBuf[i] = (i > 0 && BasisBuf[i] > BasisBuf[i - 1]) ? 0.0 : 1.0;The colors are listed once in the property; a second buffer holds, for each bar, the number of the color to use (0 for teal, 1 for red).
p1 = plot(upper, "Upper", color.gray)
p2 = plot(lower, "Lower", color.gray)
fill(p1, p2, color.new(color.gray, 90), "Band fill")#property indicator_type1 DRAW_FILLING
#property indicator_color1 clrWhiteSmoke,clrWhiteSmoke
...
FillUpBuf[i] = up[k];
FillLoBuf[i] = lo[k];DRAW_FILLING takes two buffers of its own (copies of the bands) and fills between them. It is declared first so the lines are drawn over it. There is no transparency, hence a pale color.
plotshape(close > upper, "Close above upper band",
shape.circle, location.abovebar, color.red, size = size.tiny)PlotIndexSetInteger(4, PLOT_ARROW, 159);
PlotIndexSetInteger(4, PLOT_ARROW_SHIFT, -10);
SignalBuf[i] = close[i] > up[k] ? high[i] : EMPTY_VALUE;The condition becomes a price where the symbol is drawn, or EMPTY_VALUE for none. 159 is a dot in the Wingdings font; a negative shift moves it up, above the high.
[basis, upper, lower] = ta.bb(close, len, mult)bandsHandle = iBands(_Symbol, _Period, InpLen, 0, InpMult, PRICE_CLOSE);
...
CopyBuffer(bandsHandle, BASE_LINE, 0, count, mid);
CopyBuffer(bandsHandle, UPPER_BAND, 0, count, up);
CopyBuffer(bandsHandle, LOWER_BAND, 0, count, lo);iBands() computes the same bands as ta.bb(): a simple average plus or minus the population standard deviation. Its three buffers have named numbers.
// each plot() is one series#property indicator_buffers 7
#property indicator_plots 5
SetIndexBuffer(0, FillUpBuf, INDICATOR_DATA);
SetIndexBuffer(1, FillLoBuf, INDICATOR_DATA);
SetIndexBuffer(2, BasisBuf, INDICATOR_DATA);
SetIndexBuffer(3, BasisColorBuf, INDICATOR_COLOR_INDEX);Buffers are bound in plot order, and a plot's extra buffers (the filling's second one, a color index) come right after its first. Seven buffers make five plots here.
plot(upper, "Upper", color.gray)#property indicator_label3 "Upper"
#property indicator_type3 DRAW_LINE
#property indicator_color3 clrGrayThe label is the name shown in the Data Window and the tooltip, like a plot's title.
Pitfalls
- Counts that don't match.
indicator_bufferscounts every array (7 here) andindicator_plotsthe things drawn (5). With too few buffers declared,SetIndexBuffer()fails at run time and plots stay empty, though the code compiles. - No transparency.
color.new(color.gray, 90)has no MetaTrader 5 equivalent:DRAW_FILLINGis opaque and covers the candles unless the chart's "Chart on foreground" option is on. Choose a pale color. - Unwritten elements. A new bar's buffer element holds no meaningful value until you write one. Write every buffer for every bar you calculate,
EMPTY_VALUEincluded, or stray lines and dots appear. - Color numbers are doubles. The color-index buffer holds 0, 1, 2… as doubles, and the order of colors in
indicator_colordecides what each number means. - Expert Advisors have no buffers. An EA draws with chart objects (
ObjectCreate()), as in drawing objects on the chart, or adds an indicator to its chart withChartIndicatorAdd().
Checking that both give the same values
- Read Basis, Upper and Lower in both Data Windows on the same bars: with the same closes they agree to the displayed digits.
- Count the dots over the last 100 bars on each chart; a difference points to bars whose close is within a tick of the upper band.
- The basis should change color on the same bars on both.
These programs show one technique each; they are not trading systems, and none of this is a recommendation to trade. Trading involves substantial risk of loss and is not suitable for every investor. Edgoro is a software tool, not a broker or financial adviser: nothing it produces is investment advice, and past or backtested performance does not guarantee future results.