MQL5 iBands Guide: Get Bollinger Bands Values with CopyBuffer

目次

1. What Is iBands in MQL5?

iBands is the standard MQL5 function used to access Bollinger Bands.
In MetaTrader 5 (MT5), when an EA (Expert Advisor) or script needs values from a technical indicator, it first creates an indicator handle, which is an ID used to access the indicator calculation results. The EA then reads data through that handle.

The usual workflow is as follows.

  1. Create an indicator handle for Bollinger Bands with iBands
  2. Retrieve band values with CopyBuffer
  3. Use the retrieved values in trading logic

This structure is one of the major differences from how indicators are retrieved in MQL4.
In MQL4, many indicator functions could return values directly. In MQL5, the process uses the following two-step model.

  • Create a handle
  • Read values from buffers

If beginners do not understand this difference, they often run into the following problems.

Common mistakes

  • Assuming that simply calling iBands returns a band value
  • Trying to get band values without using CopyBuffer
  • Creating iBands on every call inside OnTick(), which makes the EA heavier

The third mistake is especially important because it can reduce EA performance.
The basic rule is to create the indicator handle only once, usually in OnInit().

1.1 Basics of Bollinger Bands

Bollinger Bands are a technical indicator that visualizes price volatility.
They were developed by John Bollinger and are widely used in trading.

Bollinger Bands consist of the following three lines.

LineDescription
Middle lineMoving average, usually SMA
Upper bandMoving average + standard deviation
Lower bandMoving average – standard deviation

Here, standard deviation is a statistical value that shows how far price is from its average.
In other words, Bollinger Bands have the following behavior.

  • When market volatility is high, the bands widen
  • When the market is quiet, the bands narrow

Traders use this behavior to make the following kinds of decisions.

Typical uses

  • Near the upper band: potentially overbought
  • Near the lower band: potentially oversold
  • Band expansion: possible trend development
  • Band contraction: range-bound market conditions

However, these are not absolute buy or sell signals.
Behavior changes greatly depending on the market environment and timeframe, so in EA development Bollinger Bands are commonly combined with other indicators.

1.2 Main Uses in EA Development

The purpose of using iBands in MQL5 is to implement trading logic based on Bollinger Bands.
In EAs, it is mainly used for the following purposes.

Mean-Reversion Strategy

When price reaches the outside of a band, this method treats the move as possibly overextended and looks for a reversal.

Example

  • Price reaches the upper band: sell
  • Price reaches the lower band: buy

This strategy often works better in range-bound markets, but it may fail during strong trends.

Breakout Strategy

When price breaks outside a band, this method treats it as a possible start of a trend.

Example

  • Price breaks above the upper band: buy
  • Price breaks below the lower band: sell

This approach is often used in trend-following EAs.

Volatility Detection

By calculating band width, an EA can judge the market state.

Example

  • Narrow band width: quiet market
  • Expanding band width: possible trend start

This information can be used for the following purposes.

  • Trade entry timing
  • Lot size adjustment
  • Trade suspension filters

Important Notes for Beginners

Bollinger Bands are not a directional indicator.
They indicate the range of price movement.

The following misunderstandings are common.

Common misunderstandings

  • Touching the upper band means price will always fall
  • Touching the lower band means price will always rise

In reality, during a strong trend, price can continue moving along the band.
EA logic often combines Bollinger Bands with other indicators such as RSI or ATR.

MQL5 iBands indicator workflow diagram showing how to create an indicator handle in OnInit, retrieve Bollinger Bands values using CopyBuffer (upper, middle, lower buffers), and apply them to trading logic decisions on a MetaTrader chart with code and data flow arrows.

2. iBands Syntax and Parameters

iBands is a function that creates a handle, or ID, for the Bollinger Bands indicator.
When this function runs, MetaTrader internally creates the Bollinger Bands calculation indicator and returns its handle as an integer value.

This handle is then used with CopyBuffer to retrieve band values.

The basic MQL5 syntax is as follows.

int iBands(
   string           symbol,
   ENUM_TIMEFRAMES  period,
   int              bands_period,
   int              bands_shift,
   double           deviation,
   ENUM_APPLIED_PRICE applied_price
);

Return value

  • Success: indicator handle, an integer of 0 or greater
  • Failure: INVALID_HANDLE

For this reason, EAs usually check for errors like this.

int bandsHandle = iBands(_Symbol, PERIOD_CURRENT, 20, 0, 2.0, PRICE_CLOSE);

if(bandsHandle == INVALID_HANDLE)
{
   Print("Failed to create iBands");
}

If you skip this error check, a later CopyBuffer call may produce an unclear error.

2.1 Meaning of Each Parameter

iBands has six parameters.
Understanding each one is very important in EA development.

ParameterDescription
symbolCurrency pair, for example EURUSD
periodTimeframe
bands_periodMoving average period
bands_shiftBand shift
deviationStandard deviation
applied_pricePrice type to use

Each parameter is explained below.

symbol

This specifies the currency pair used for indicator calculation.

Example

_Symbol

This means the currency pair of the current chart.

Example

iBands("EURUSD", PERIOD_CURRENT, 20, 0, 2.0, PRICE_CLOSE);

In multi-currency EAs, you can also specify a different currency pair.

Notes

  • If the symbol name is wrong, the result may be INVALID_HANDLE
  • Some brokers add suffixes such as EURUSD.m

period

This is the timeframe used to calculate Bollinger Bands.

Main options

PERIOD_CURRENT
PERIOD_M1
PERIOD_M5
PERIOD_M15
PERIOD_H1
PERIOD_D1

Example

PERIOD_H1

This means Bollinger Bands on the 1-hour timeframe.

Common mistakes

  • Confusing the EA chart timeframe with the indicator timeframe
  • Using multi-timeframe processing without understanding it

For beginners, using

PERIOD_CURRENT

is a safer starting point.

bands_period

This is the moving average period for the middle line.

Example

20

This is a common Bollinger Bands setting.

Typical settings

PeriodUse
20General use
14Short-term trading
50Medium-term trends

The smaller the period, the faster the response, but the more noise it can include.

bands_shift

This is the value that shifts the indicator display position to the right.

Normally, use

0

This value is mainly for chart display purposes and is rarely used in EA logic.

Beginners can usually keep it fixed at 0.

deviation

This is the standard deviation multiplier that determines band width.

Typical value

2.0

This is the most common setting.

Example

deviationFeature
1.0Narrow bands
2.0Standard
3.0Very wide

The larger the value, the wider the bands become.

applied_price

This is the type of price used for indicator calculation.

Main values

PRICE_CLOSE
PRICE_OPEN
PRICE_HIGH
PRICE_LOW
PRICE_MEDIAN
PRICE_TYPICAL
PRICE_WEIGHTED

Beginners usually use

PRICE_CLOSE

This means Bollinger Bands based on closing prices.

2.2 Basic Code Using iBands

In an EA, you usually create the handle in OnInit().

Example

int bandsHandle;

int OnInit()
{
   bandsHandle = iBands(
      _Symbol,
      PERIOD_CURRENT,
      20,
      0,
      2.0,
      PRICE_CLOSE
   );

   if(bandsHandle == INVALID_HANDLE)
   {
      Print("iBands creation error");
      return(INIT_FAILED);
   }

   return(INIT_SUCCEEDED);
}

With this method, the EA creates the indicator only once when it starts.

Common Mistakes

The following are mistakes beginners often make.

Creating iBands on Every OnTick()

// Bad example
void OnTick()
{
   int handle = iBands(...);
}

This creates an indicator on every tick, which makes the EA heavier.

Skipping Error Checks

int handle = iBands(...);

With only this line, you will not notice if creation fails.

Not Checking the Handle Before CopyBuffer

If the handle is invalid, it can cause errors such as

array out of range

3. How to Get Bollinger Bands Values with CopyBuffer

iBands creates the Bollinger Bands calculation indicator, but this function alone does not retrieve band values.

To get actual values, you must use CopyBuffer to copy data from the indicator buffer.

In MQL5, indicators internally have multiple buffers, or data arrays.
For Bollinger Bands, the following three lines are stored in separate buffers.

Buffer numberDescription
0Upper Band
1Middle Band / Moving Average
2Lower Band

By specifying this buffer number when calling CopyBuffer, an EA can retrieve any desired band value.

3.1 Basic Syntax of CopyBuffer

The basic syntax of CopyBuffer is as follows.

int CopyBuffer(
   int       indicator_handle,
   int       buffer_num,
   int       start_pos,
   int       count,
   double    buffer[]
);

Each parameter means the following.

ParameterDescription
indicator_handleThe handle obtained from iBands
buffer_numBuffer number
start_posStart position for retrieval
countNumber of data items to retrieve
bufferArray that stores the copied data

Return value

  • Number of copied elements
  • -1 on failure

For this reason, checking the return value is important in real EA code.

3.2 Code Example for Getting the Upper Band

First, here is an example that retrieves the Upper Band.

double upperBand[];

if(CopyBuffer(bandsHandle, 0, 0, 1, upperBand) < 0)
{
   Print("CopyBuffer error");
}

This code means the following.

ParameterMeaning
bandsHandleiBands handle
0Upper band
0Latest bar
1Retrieve one item

You can then read the result like this.

double upper = upperBand[0];

3.3 How to Get the Lower Band and Middle Line

You can retrieve the middle line and lower band in the same way.

Middle Line (Moving Average)

double middleBand[];

CopyBuffer(bandsHandle, 1, 0, 1, middleBand);

Lower Band

double lowerBand[];

CopyBuffer(bandsHandle, 2, 0, 1, lowerBand);

3.4 Practical Code Example

In an EA, you usually retrieve all three bands at the same time.

double upper[1];
double middle[1];
double lower[1];

if(CopyBuffer(bandsHandle,0,0,1,upper) < 0) return;
if(CopyBuffer(bandsHandle,1,0,1,middle) < 0) return;
if(CopyBuffer(bandsHandle,2,0,1,lower) < 0) return;

double upperBand  = upper[0];
double middleBand = middle[0];
double lowerBand  = lower[0];

This lets you use all three Bollinger Bands lines inside the EA.

3.5 Common Mistakes and Notes

CopyBuffer is one of the areas where beginners most often get stuck.
The following mistakes are especially common.

Not Preparing the Array Size

The following code is risky.

double upper[];

CopyBuffer(handle,0,0,1,upper);

If the array size is not prepared, an error may occur.

Safer method

double upper[1];

or

ArrayResize(upper,1);

Using the Wrong Buffer Number

This is an easy point for beginners to confuse.

BufferDescription
0Upper band
1Middle line
2Lower band

If you mix up this order, the trading logic can become completely wrong.

Data Is Not Ready Yet

Right after an EA starts, the indicator calculation may not be complete.

In that case, CopyBuffer returns -1.

Safe handling

if(CopyBuffer(...) <= 0)
   return;

Confusing the Latest Bar and the Closed Bar

start_pos = 0 means the currently forming bar.

EA logic may use the following distinction.

start_posMeaning
0Latest bar
1Closed bar

Many trading rules use the closed bar, position 1.

4. Implementation Examples Using iBands in an EA

iBands is not just a function for displaying Bollinger Bands.
In an EA, it is used as input for mechanically judging trading conditions by comparing retrieved band values with current price.

However, Bollinger Bands are not a complete trading signal by themselves.
In real use, they are often separated into roles such as the following.

  • Mean-reversion judgment
  • Breakout judgment
  • Quiet or volatile market judgment
  • Entry prohibition filter

This section covers the two most basic patterns so beginners can understand them clearly.

  • A mean-reversion type that looks for reversal after band contact
  • A breakout type that follows price after it breaks through a band

4.1 Basic Example of Mean-Reversion Logic

In mean-reversion logic, when price reaches the outside of a band, the EA looks for a return after an overextended move.

A typical example is as follows.

  • Current price is at or above the upper band: consider selling
  • Current price is at or below the lower band: consider buying

The code can look like this.

double upper[1];
double lower[1];

if(CopyBuffer(bandsHandle, 0, 0, 1, upper) <= 0) return;
if(CopyBuffer(bandsHandle, 2, 0, 1, lower) <= 0) return;

double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID);

if(bid >= upper[0])
{
   Print("Sell signal candidate: price reached the upper band");
}

if(ask <= lower[0])
{
   Print("Buy signal candidate: price reached the lower band");
}

This code does not place orders yet.
At first, it is easier to verify the logic by only printing condition checks to the log.

Notes on Mean-Reversion Logic

Mean-reversion logic is visually easy to understand, but the following failures are common in live use.

  • Taking countertrend trades in a strong trend market
  • Entering immediately just because price touched a band
  • Using an unclosed bar and catching false signals

The key point is that band contact does not mean immediate reversal.
When the trend is strong, price may keep moving along the band.
For this reason, real systems often add supporting conditions such as the following.

  • Confirming overbought or oversold conditions with RSI
  • Judging only on closed bars
  • Preventing trades against the higher-timeframe trend

4.2 Basic Example of Breakout Logic

In breakout logic, price moving outside a band is treated as possible momentum, and the EA follows that direction.

A typical example is as follows.

  • Close price breaks above the upper band: buy candidate
  • Close price breaks below the lower band: sell candidate

In this case, it is safer to use a closed bar instead of the currently forming bar.
Therefore, start_pos = 1 is often used with CopyBuffer.

double upper[1];
double lower[1];
double closePrice[1];

if(CopyBuffer(bandsHandle, 0, 1, 1, upper) <= 0) return;
if(CopyBuffer(bandsHandle, 2, 1, 1, lower) <= 0) return;
if(CopyClose(_Symbol, PERIOD_CURRENT, 1, 1, closePrice) <= 0) return;

if(closePrice[0] > upper[0])
{
   Print("Buy breakout candidate: closed candle closed above the upper band");
}

if(closePrice[0] < lower[0])
{
   Print("Sell breakout candidate: closed candle closed below the lower band");
}

The advantage of this method is that it more easily excludes temporary moves outside the band on an unclosed bar.

Notes on Breakout Logic

Breakouts can be effective in trending markets, but they are more likely to fail in range-bound markets.

Common failures include the following.

  • Misreading a brief spike as a real breakout
  • Reacting to noise when band width is extremely narrow
  • Getting caught in sharp moves around economic releases

For this reason, real systems may add conditions such as the following.

  • Allow trades only when band width is above a certain level
  • Confirm volatility with ATR
  • Avoid trading when spreads widen

4.3 Volatility Detection Using Band Width

Bollinger Bands can quantify the size of market movement by taking the difference between the upper and lower bands.

The formula is simple.

double bandWidth = upper[0] - lower[0];

Using this bandWidth, an EA can make judgments such as the following.

  • Narrow band width: market is relatively stagnant
  • Wide band width: market is active
  • Do not enter if the width is below a threshold
  • Enable breakout logic if the width is above a threshold

Code example

double upper[1];
double lower[1];

if(CopyBuffer(bandsHandle, 0, 1, 1, upper) <= 0) return;
if(CopyBuffer(bandsHandle, 2, 1, 1, lower) <= 0) return;

double bandWidth = upper[0] - lower[0];

if(bandWidth < 0.0010)
{
   Print("Skip: band width is too small");
}
else
{
   Print("Trade candidate: band width is sufficient");
}

However, this threshold depends on the currency pair and number of digits.
For example, EURUSD and USDJPY use different price scales, so the same numeric threshold cannot be applied blindly.

Notes on Band Width Filters

  • Do not reuse one fixed threshold for every currency pair
  • Do not ignore differences between 5-digit and 3-digit brokers
  • A value that works in the tester may shift in live trading

For beginners, a safer approach is to first print bandWidth to the log, observe it, and turn it into a condition only after understanding the natural range for the target symbol.

4.4 Common Implementation Mistakes

The following are mistakes beginners often make when adding iBands to an EA.

Connecting Every Tick Signal Directly to Orders

If an order is sent every time a signal condition becomes true, the EA may place repeated orders under the same condition.

Possible countermeasures

  • Judge only on a new bar
  • Do not place another order while a position is already open
  • Store the previous signal state in a variable

Confusing bid and ask

Buy orders usually need awareness of Ask, while sell-side judgment needs awareness of Bid.
If this is unclear, condition checks may not match the actual execution price.

Mixing Closed and Unclosed Bars

start_pos = 0 is the unclosed bar, and start_pos = 1 is the most recent closed bar.
If this distinction changes in the middle of the logic, backtest results can diverge from live results.

Skipping Failure Handling for CopyBuffer

If the EA continues to reference an array after it failed to retrieve values, it can cause logic errors or other errors.

Always check as follows.

if(CopyBuffer(bandsHandle, 0, 1, 1, upper) <= 0)
   return;

5. Notes and Common Real-World Errors When Using iBands

iBands is a standard MQL5 function, and its basic usage is not difficult.
However, when it is added to a real EA, beginners often get stuck on handle management, data retrieval timing, and price handling.

Beginners are especially likely to face a situation where the code appears to run, but the result is wrong, rather than a simple syntax error.
This section organizes the main points to watch when using iBands in practice.

5.1 Causes of INVALID_HANDLE

If iBands succeeds, it returns an indicator handle. If it fails, it returns INVALID_HANDLE.
If you skip this check, later CopyBuffer calls can fail and make the cause difficult to isolate.

The basic check code is as follows.

int bandsHandle = iBands(_Symbol, PERIOD_CURRENT, 20, 0, 2.0, PRICE_CLOSE);

if(bandsHandle == INVALID_HANDLE)
{
   Print("Failed to create iBands");
   return INIT_FAILED;
}

The main causes of INVALID_HANDLE are as follows.

  • Incorrect symbol specification
  • Invalid timeframe specification
  • Insufficient terminal state or data loading
  • Initialization timing problems

Common Failure

For example, if a multi-currency EA directly writes "EURUSD", some brokers may use symbol names such as "EURUSDm" or "EURUSD.". In that case, the intended currency pair may not exist.

For beginners, it is safer to start with

_Symbol

Another typical mistake is creating the handle inside OnTick().
That creates a new indicator on every tick, increases terminal load, and can make behavior unstable.

5.2 Causes of CopyBuffer Failure

Even if iBands succeeds, CopyBuffer does not always succeed.
In MQL5, CopyBuffer can fail when it is called before the indicator calculation is ready.

For example, the following code is risky because it does not check the return value.

double upper[1];
CopyBuffer(bandsHandle, 0, 0, 1, upper);
double value = upper[0];

In this case, even if copying fails, the EA still uses upper[0], which can cause abnormal logic or wrong judgments.

The safer way is as follows.

double upper[1];

if(CopyBuffer(bandsHandle, 0, 0, 1, upper) <= 0)
{
   Print("Failed to retrieve upper band");
   return;
}

Main Causes of CopyBuffer Failure

  • Invalid handle
  • Wrong buffer number
  • The required number of bars has not been calculated yet
  • Insufficient data for the symbol or timeframe
  • History data is not fully loaded right after startup

5.3 Mixing Up Buffer Numbers

iBands handles three lines, and beginners most often get confused by the order of the buffer numbers.

This article uses the following mapping.

Buffer numberDescription
0Upper band
1Middle line
2Lower band

If you remember this in reverse, the code may compile and run, but the trading judgment can be completely wrong.

For example, if you intend to judge the lower band but specify buffer_num = 1, you are actually comparing against the middle line, so the entry condition becomes something else.

Common Situations

  • Confusing the upper band with the middle line
  • Code comments and actual buffer numbers do not match
  • Reusing old code or other articles where the order is mixed

This kind of mistake is troublesome because it is hard to notice visually.
During debugging, print all three retrieved values to the log and confirm that their order is correct.

Print("Upper=", upper[0], " Middle=", middle[0], " Lower=", lower[0]);

5.4 Confusing Unclosed and Closed Bars

When start_pos = 0 is specified in CopyBuffer, it retrieves the currently forming bar.
By contrast, start_pos = 1 is the most recent closed bar.

This difference is very important in EA logic.

Characteristics of start_pos = 0

  • High real-time responsiveness
  • The value changes constantly
  • Signals can appear earlier
  • False signals can also increase

Characteristics of start_pos = 1

  • Uses values after bar close
  • Logic tends to be more stable
  • Backtest and live results are less likely to differ

A common beginner mistake is using an unclosed bar in the entry logic while explaining or testing the rule as if it used a closed bar.
This reduces the reproducibility of the logic.

Use them clearly as follows.

// Check the unclosed bar
CopyBuffer(bandsHandle, 0, 0, 1, upper);

// Check the closed bar
CopyBuffer(bandsHandle, 0, 1, 1, upper);

If you want more stable live behavior, it is usually safer to start with a closed-bar basis.

5.5 Heavy Processing on Every OnTick()

iBands is useful, but the EA can become heavy if it is used incorrectly.
The following two patterns are especially common problems.

  • Creating iBands inside OnTick() every time
  • Running many CopyBuffer calls on every tick

A bad example is the following.

void OnTick()
{
   int handle = iBands(_Symbol, PERIOD_CURRENT, 20, 0, 2.0, PRICE_CLOSE);
   double upper[1];
   CopyBuffer(handle, 0, 0, 1, upper);
}

This code creates a new indicator each time a tick arrives, causing unnecessary load.

The basic approach is as follows.

  • Create the handle once in OnInit()
  • Retrieve values only when needed
  • If possible, calculate only on a new bar

For example, you can reduce unnecessary calculation by adding new-bar detection.

datetime lastBarTime = 0;

void OnTick()
{
   datetime currentBarTime = iTime(_Symbol, PERIOD_CURRENT, 0);
   if(currentBarTime == lastBarTime)
      return;

   lastBarTime = currentBarTime;

   // Retrieve iBands values only on a new bar
}

This structure makes it easier to avoid excessive calculation on every tick.

5.6 Ignoring Price Digits and Currency Pair Differences

Bollinger Band values are prices.
Therefore, if you ignore price scale differences between currency pairs, condition settings can become wrong.

For example, EURUSD and USDJPY have very different price ranges.

  • EURUSD is around 1.x
  • USDJPY is above the 100-yen range

Because of this difference, if you set a fixed band-width threshold, it may work on one pair but not work at all on another.

Common Mistakes

  • Using bandWidth < 0.0010 for every symbol
  • Treating 5-digit and 3-digit symbols the same way
  • Ignoring Point or Digits

A safer method is to handle price differences in points.

double bandWidthPoints = (upper[0] - lower[0]) / _Point;

This format can absorb some symbol differences.
However, the best value still differs by symbol and timeframe, so adjustment through backtesting and log observation is required.

5.7 Summary of Common Mistakes

Here are the typical mistakes when using iBands.

  • Assuming a value is available just by calling iBands
  • Not checking INVALID_HANDLE
  • Ignoring the return value of CopyBuffer
  • Mixing up buffer numbers
  • Confusing unclosed and closed bars
  • Creating the handle on every OnTick()
  • Using fixed conditions without considering price scale differences

iBands is not a difficult function, but it can easily cause implementation mistakes if you are not used to MQL5’s handle-based indicator model.

For beginners, the following order is safer.

  • Confirm that handle creation succeeds
  • Confirm that CopyBuffer succeeds
  • Print retrieved values to the log and verify them
  • Then add them to trading logic

6. Common Mistakes and Stumbling Blocks

The syntax of iBands is short and looks simple.
However, once it is added to an EA, it often causes problems where the code compiles but does not behave as intended.

Many causes come from implementing the following MQL5-specific mechanisms without fully understanding them.

  • Handle creation
  • Value retrieval with CopyBuffer
  • Management of bar-close timing

This section organizes the beginner mistakes with the highest practical impact.

6.1 Writing It Like MQL4

One of the most common mistakes is confusing MQL4 and MQL5 indicator retrieval methods.

In MQL4, many indicator functions could return values directly when called.
Because of that, using iBands() in MQL5 with the same mindset can lead to misunderstanding.

Beginners often assume the following.

  • iBands() returns the upper band value itself
  • The return value of iBands() can be compared as a price
  • CopyBuffer() is unnecessary

However, MQL5 iBands() returns a handle, not a price.
That means the following code has a different meaning than beginners may expect.

int bandsHandle = iBands(_Symbol, PERIOD_CURRENT, 20, 0, 2.0, PRICE_CLOSE);

This bandsHandle is not a price such as 1.08452.
It is only an ID used to access the internal indicator.

Common Wrong Implementation

if(Bid &gt; iBands(_Symbol, PERIOD_CURRENT, 20, 0, 2.0, PRICE_CLOSE))
{
   // This is wrong
}

This does not perform the intended comparison.

The correct flow is as follows.

  1. Create a handle with iBands()
  2. Retrieve band values with CopyBuffer()
  3. Compare the retrieved array element with price

If this basic flow is broken, all later logic becomes unstable.

6.2 Using Buffers Without a Clear Understanding

iBands has three lines, so if you do not correctly understand which buffer belongs to which line, the entire logic can break.

This article uses the following mapping.

Buffer numberDescription
0Upper band
1Middle line
2Lower band

Common mistakes include the following.

  • Confusing the upper band and middle line
  • Confusing the middle line and lower band
  • Code comments do not match the actual number

The difficult part is that the code can appear to work normally.
It does not cause a compile error, and CopyBuffer() may still succeed, so it is easy to miss.

How to Check in Practice

After retrieval, print the values to the log and confirm the size relationship.

Print("Upper=", upper[0], " Middle=", middle[0], " Lower=", lower[0]);

Normally, for the same bar:

  • Upper band > middle line
  • Middle line > lower band

If this relationship is broken, you should review the buffer specification or data retrieval method.

6.3 Handling Arrays Too Loosely

CopyBuffer() copies data into an array.
Therefore, if you use it with an unclear understanding of array preparation and references, it can cause errors or incorrect behavior.

Typical mistakes include the following.

  • Using arrays without considering their size
  • Referencing values without knowing how many items were retrieved
  • Assuming [0] always means the latest closed candle

For example:

double upper[];
CopyBuffer(bandsHandle, 0, 0, 1, upper);
Print(upper[0]);

This may work in some environments, but it is not always safe. Beginners should first specify the size clearly.

double upper[1];

if(CopyBuffer(bandsHandle, 0, 0, 1, upper) &lt;= 0)
   return;

Print(upper[0]);

When retrieving multiple bars, you must clearly understand which index corresponds to which bar.

double upper[3];
CopyBuffer(bandsHandle, 0, 0, 3, upper);

In this case, upper[0] is usually the first item from the retrieval start position, and upper[1] is the next item.
However, time-series direction can easily become confusing depending on array settings and retrieval assumptions. For beginners, retrieving one bar at a time explicitly reduces mistakes.

6.4 Misunderstanding start_pos

The start_pos argument of CopyBuffer() indicates which bar position to start from.
If this meaning is unclear, signal judgment becomes unstable.

The typical meanings are as follows.

start_posMeaning
0Currently forming bar
1Most recent closed bar
2The closed bar before that

A common beginner mistake is explaining the backtest as if it uses closed candles while the implementation uses 0.

For example:

  • The article says, “Buy when the close price exceeds the upper band”
  • The implementation uses start_pos = 0

In that case, the EA actually catches temporary price movement before the close is confirmed.

Safer Thinking

  • Prioritize signal stability: use start_pos = 1
  • Prioritize early signals: use start_pos = 0

For beginners, using 1 first is safer.
Unclosed bars can look attractive in real time, but they increase false signals and condition fluctuation.

6.5 Repeating the Same Work on Every OnTick()

In an EA, OnTick() is called very frequently.
If iBands processing is written carelessly inside OnTick(), unnecessary calculation and duplicate judgments can occur.

Typical bad patterns include the following.

  • Creating iBands() on every tick
  • Calling CopyBuffer() many times on every tick
  • Judging the same signal repeatedly within the same bar

These can cause the following problems.

  • The terminal becomes heavy
  • The log becomes very large
  • The EA may enter multiple times under the same condition
  • Backtest results and live trading behavior may feel different

Basic Improvement Policy

  • Create the handle once in OnInit()
  • Retrieve values only as much as needed
  • Run signal judgment only on a new bar

A basic new-bar detection example is as follows.

datetime lastBarTime = 0;

void OnTick()
{
   datetime currentBarTime = iTime(_Symbol, PERIOD_CURRENT, 0);
   if(currentBarTime == lastBarTime)
      return;

   lastBarTime = currentBarTime;

   // Retrieve and judge iBands values here
}

This structure makes it easier to reduce unnecessary repeated judgment.

6.6 Overtrusting Band Contact

A common beginner mistake with Bollinger Bands is assuming that touching the upper or lower band alone is a strong trading reason.

In reality, Bollinger Bands have the following characteristics.

  • They do not directly show trend direction
  • They do not guarantee reversal
  • They do not guarantee breakout continuation

Therefore, the following deterministic logic is risky.

  • Upper band contact: always sell
  • Lower band contact: always buy
  • Band breakout: always trade with the breakout

In reality, the meaning changes with the market environment.

Examples

  • Mean reversion tends to work better in range-bound markets
  • Countertrend trading is risky in strong trends
  • Breakouts may work better after band contraction and expansion
  • Noise can increase sharply around economic releases

When adding Bollinger Bands to an EA, stability often improves if you add at least one of the following.

  • RSI
  • ATR
  • Moving average direction
  • Higher-timeframe trend
  • Spread filter

6.7 Skipping Log Checks

A surprisingly common beginner mistake is connecting retrieved values directly to trading conditions without checking them.

For example, they skip the following sequence.

  1. Confirm handle creation
  2. Confirm CopyBuffer() success
  3. Print retrieved values to the log
  4. Verify the condition expression
  5. Add order placement processing

If these checks are skipped, it becomes hard to understand why an entry happened at a particular point.

Example Logs to Check During Initial Implementation

Print(
   "Upper=", upper[0],
   " Middle=", middle[0],
   " Lower=", lower[0],
   " Bid=", SymbolInfoDouble(_Symbol, SYMBOL_BID),
   " Ask=", SymbolInfoDouble(_Symbol, SYMBOL_ASK)
);

This check helps you separate the following issues.

  • Whether price really exceeded the upper band
  • Whether the price comparison direction is correct
  • Whether the use of Bid and Ask is appropriate

6.8 A Safer Process for Beginners

When using iBands, adding it to a complex EA from the beginning often leads to failure.
Beginners should proceed in the following order.

  1. Create the handle in OnInit()
  2. Retrieve only one value with CopyBuffer()
  3. Print the value to the log and confirm it
  4. Compare price with the upper and lower bands
  5. If the condition matches, print only a log message
  6. Add order processing afterward

This order makes it easier to identify where the problem occurred.
By contrast, if you write all of the following at once, troubleshooting becomes difficult.

  • iBands
  • CopyBuffer
  • Entry conditions
  • Lot calculation
  • OrderSend

7. FAQ

7.1 What does iBands return?

iBands returns an indicator handle, or ID, not the Bollinger Bands price itself.
To retrieve actual band values, you must use CopyBuffer to copy data from the indicator buffer.

7.2 How are iBands buffer numbers mapped?

Bollinger Bands consist of three lines, mapped to the following buffer numbers.

Buffer numberDescription
0Upper Band
1Middle Band / Moving Average
2Lower Band

By specifying this number as the second argument of CopyBuffer, you can retrieve the target line.

7.3 Where should iBands be created?

Usually, it is recommended to create it only once inside OnInit().

If you create iBands on every OnTick(), the EA creates an indicator on every tick, which can make processing heavier and behavior less stable.

7.4 What does start_pos in CopyBuffer mean?

start_pos specifies which bar position to retrieve data from.

start_posMeaning
0Currently forming bar
1Most recent closed bar
2The previous bar before that

If you want to avoid real-time fluctuation, using the closed bar, position 1, is more stable.

7.5 Why does CopyBuffer fail?

The main causes are as follows.

  • The indicator handle is invalid
  • Bar history has not loaded yet
  • The buffer number is wrong
  • The indicator calculation is not complete

For this reason, always check the return value of CopyBuffer and stop processing when retrieval fails.

7.6 Does price always reverse after touching a Bollinger Band?

No, price does not always reverse.

Bollinger Bands are an indicator of price volatility, not an indicator that guarantees trend direction.

During a strong trend, price can continue moving along the band, often called a band walk.

For this reason, EAs commonly combine Bollinger Bands with supporting conditions such as the following.

  • RSI
  • ATR
  • Moving average trend
  • Higher-timeframe direction

7.7 How is band width calculated?

Bollinger Band width can be calculated as the difference between the upper band and the lower band.

double bandWidth = upper[0] - lower[0];

This value can support judgments such as the following.

  • Narrow band width: the market may be stagnant
  • Wide band width: the market may be active

However, the value scale differs by currency pair and price digits, so fixed thresholds must be adjusted for the trading environment.

7.8 What is the difference between iBands and iCustom?

iBands is a function that calls the standard Bollinger Bands indicator built into MetaTrader.

By contrast, iCustom is a function that calls a custom indicator created by the user or provided externally.

For standard Bollinger Bands, iBands is simpler and easier to use. For custom calculation logic, use iCustom.