MQL5 CopyBuffer Guide: How to Get Indicator Values in EA Development

目次

1. What Is MQL5 CopyBuffer?

1.1 Basic Concept of CopyBuffer

MQL5 CopyBuffer is a function used to retrieve values, or buffers, calculated internally by an indicator.
In this context, a “buffer” means a numeric array that an indicator stores for each bar.

For example, the following indicators all have internal buffers.

  • Moving Average (iMA)
  • RSI (iRSI)
  • MACD (iMACD)

These values are not only displayed on the chart. You can also retrieve them from a program and use them in trading logic. CopyBuffer is the function used for that purpose.

The key points are as follows.

  • In MQL5, indicators are managed by a handle (identifier)
  • CopyBuffer is a function that copies numeric data from that handle
  • The return value is the “number of data elements successfully retrieved”

1.2 Why CopyBuffer Is Needed

In an EA (Expert Advisor), decisions based on technical indicators are almost always necessary, not just price data.

Common examples include the following logic.

  • Enter a trade on a moving average crossover
  • Sell when RSI is 70 or higher, and buy when RSI is 30 or lower
  • Make decisions based on a MACD signal crossover

To implement these rules, you need a process that retrieves indicator values.

The basic flow is as follows.

// 1. Get the indicator handle
int handle = iMA(_Symbol, PERIOD_CURRENT, 20, 0, MODE_SMA, PRICE_CLOSE);

// 2. Prepare an array
double buffer[];

// 3. Retrieve data
CopyBuffer(handle, 0, 0, 3, buffer);

In this way,
“create an indicator → retrieve values with CopyBuffer → evaluate conditions”
is the basic pattern of EA development in MQL5.

MQL5 CopyBuffer data retrieval flow showing indicator handle creation, buffer array extraction, and trading signal logic using moving average, RSI, and MACD on a trading chart

1.3 Where Beginners Commonly Get Stuck

CopyBuffer is a very important function, but it is also one of the easiest places for beginners to get stuck.
Confusion often happens around the following points.

■ Common confusion 1: Not understanding what a handle means

  • In MQL4, values could be retrieved directly, but in MQL5 they are accessed through a handle
  • A handle is easier to understand if you think of it as an “indicator instance ID”

■ Common confusion 2: Not understanding the buffer number

  • Some indicators have multiple lines
  • Example: MACD
    • 0 → main line
    • 1 → signal line

■ Common confusion 3: Values cannot be retrieved

The main causes are as follows.

  • The handle was not created correctly
  • The return value of CopyBuffer is not checked
  • The indicator calculation has not finished yet, especially right after OnInit
  • The array size is insufficient

■ Common practical mistakes

  • Calling CopyBuffer too often on every tick, which lowers performance
  • Not setting ArraySetAsSeries, which causes reversed-index bugs
  • Misunderstanding the meaning of start_pos

CopyBuffer is not just another function. It is the core data retrieval layer in MQL5.
Once you understand it correctly, EA implementation speed and stability improve significantly.

2. Basic CopyBuffer Syntax and Arguments

2.1 Basic Syntax

CopyBuffer is used in the following format.

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

The return value is the “number of data elements successfully retrieved.”
If retrieval succeeds, the same value as count is returned.


2.2 Detailed Explanation of Each Argument

■ indicator_handle

  • The value obtained from iMA, iRSI, iCustom, and similar functions
  • An ID that identifies the indicator
int handle = iMA(_Symbol, PERIOD_CURRENT, 20, 0, MODE_SMA, PRICE_CLOSE);

Important notes:

  • If it is INVALID_HANDLE, an error occurred
  • It is usually created in OnInit

■ buffer_num

  • Specifies which line value to retrieve

Example:

Indicatorbuffer_num
iMA0
iRSI0
iMACD0 (main) / 1 (signal)

Example:

CopyBuffer(handle, 0, 0, 3, buffer);

Important notes:

  • The meaning differs by indicator
  • If you specify the wrong number, the retrieved values will be misaligned

■ start_pos

  • Specifies which bar to start from, where 0 is the latest bar
ValueMeaning
0Current bar
1Previous bar
2Two bars ago

Example:

CopyBuffer(handle, 0, 0, 1, buffer); // Latest 1 bar
CopyBuffer(handle, 0, 1, 1, buffer); // Previous bar

Important:

  • 0 may refer to an unconfirmed bar that is still forming
  • If you need confirmed values, 1 is often used

■ count

  • The number of data elements to retrieve
CopyBuffer(handle, 0, 0, 3, buffer);

In this case:

buffer[0] → current
buffer[1] → previous bar
buffer[2] → two bars ago

Important notes:

  • An error can occur if you request more elements than the array can hold
  • As a rule, retrieve only the minimum amount needed

■ buffer[]

  • The destination array that receives the result
double buffer[];
ArrayResize(buffer, 3);

Or:

double buffer[3];

2.3 Common Mistakes

Here are the most common failure patterns with CopyBuffer.


■ Mistake 1: Array size is not allocated

double buffer[];
CopyBuffer(handle, 0, 0, 3, buffer); // NG

Fix:

ArrayResize(buffer, 3);

■ Mistake 2: Not checking the return value

CopyBuffer(handle, 0, 0, 3, buffer);

This alone is not enough.

Fix:

int copied = CopyBuffer(handle, 0, 0, 3, buffer);
if(copied <= 0)
{
   Print("CopyBuffer error: ", GetLastError());
}

■ Mistake 3: Misunderstanding start_pos

  • Using 0 while assuming it is a confirmed value causes bugs
  • This is especially likely to create differences between backtesting and live trading

■ Mistake 4: Specifying the wrong buffer_num

  • Lines may be shifted when using indicators such as MACD
  • The result can be an unintended value

■ Mistake 5: Handle creation failed

if(handle == INVALID_HANDLE)
{
   Print("handle error");
}

Minimum Practical Check Template

double buffer[];
ArrayResize(buffer, 3);

int copied = CopyBuffer(handle, 0, 0, 3, buffer);

if(copied != 3)
{
   Print("CopyBuffer failed: ", GetLastError());
   return;
}

CopyBuffer is 80% about understanding its arguments.
If this part is vague, the entire EA logic can break down.

3. Basic CopyBuffer Usage: A Simple Example

3.1 Minimum Setup for Retrieving iMA Moving Average Values

First, as the simplest example, retrieve the value of a moving average (iMA).
This pattern is the fastest way to understand how CopyBuffer works.

// Global variable
int ma_handle;

// Initialization
int OnInit()
{
   ma_handle = iMA(_Symbol, PERIOD_CURRENT, 20, 0, MODE_SMA, PRICE_CLOSE);

   if(ma_handle == INVALID_HANDLE)
   {
      Print("iMA handle error");
      return(INIT_FAILED);
   }

   return(INIT_SUCCEEDED);
}

// Per-tick processing
void OnTick()
{
   double ma_buffer[];
   ArrayResize(ma_buffer, 1);

   int copied = CopyBuffer(ma_handle, 0, 1, 1, ma_buffer);

   if(copied <= 0)
   {
      Print("CopyBuffer error: ", GetLastError());
      return;
   }

   double ma_value = ma_buffer[0];

   Print("MA value: ", ma_value);
}

3.2 Processing Flow: Important

The code above works in the following flow.

  • Create the indicator in OnInit and get the handle
  • Run CopyBuffer in OnTick
  • Store the value in an array
  • Use the value in the trading logic

The especially important points are as follows.

  • Do not create the handle every time. Create it once in OnInit.
  • Call CopyBuffer only when needed
  • Always check the return value

3.3 Why start_pos=1 Is Used

CopyBuffer(ma_handle, 0, 1, 1, ma_buffer);

The reason 1 is used here is very important.

ValueStatus
0Unconfirmed current bar
1Confirmed previous bar

In practice, the basic rule is:

  • Signal decisions → use 1
  • Visualization or reference checks → 0 is acceptable

Reasons:

  • Because 0 changes on every tick, signals can become unstable
  • It can create differences between backtesting and live trading

3.4 Common Failures: The Most Important Part

■ Failure 1: Creating the handle in OnTick

int handle = iMA(...); // NG

Problems:

  • Creates an indicator on every tick, causing performance degradation
  • Can cause memory leaks

■ Failure 2: Forgetting ArrayResize

double buffer[];
CopyBuffer(...); // NG

Always do this:

ArrayResize(buffer, 1);

■ Failure 3: Ignoring the return value

CopyBuffer can fail.

  • Data is not loaded yet
  • The handle is invalid
  • Communication delay occurs

Always check:

if(copied <= 0)

■ Failure 4: Not understanding ArraySetAsSeries

Normal array:

buffer[0] → older data

Series array:

ArraySetAsSeries(buffer, true);
buffer[0] → latest data

In general, it is safer to handle CopyBuffer data in series format.


3.5 Best Practical Template

The following is the minimum structure commonly used in practice.

double buffer[];
ArrayResize(buffer, 2);
ArraySetAsSeries(buffer, true);

int copied = CopyBuffer(ma_handle, 0, 1, 2, buffer);

if(copied != 2)
{
   Print("CopyBuffer failed: ", GetLastError());
   return;
}

double current = buffer[0];
double prev    = buffer[1];

This makes it possible to compare:

  • The latest confirmed value
  • The previous value

3.6 The Core Idea to Understand at This Stage

CopyBuffer has the following roles.

  • Copy the indicator’s “internal array”
  • Build logic through array operations
  • Provide decision data for the EA

In short:

👉 CopyBuffer = the entry point for data retrieval

If this part is unstable, every piece of logic built on top of it becomes unreliable.

4. Practical CopyBuffer Examples: RSI and MACD

4.1 Example of Retrieving iRSI Values

RSI uses a single buffer (buffer_num=0), so the basic CopyBuffer pattern can be applied directly.

// Global
int rsi_handle;

// Initialization
int OnInit()
{
   rsi_handle = iRSI(_Symbol, PERIOD_CURRENT, 14, PRICE_CLOSE);

   if(rsi_handle == INVALID_HANDLE)
   {
      Print("RSI handle error");
      return(INIT_FAILED);
   }

   return(INIT_SUCCEEDED);
}

// Tick processing
void OnTick()
{
   double rsi_buffer[];
   ArrayResize(rsi_buffer, 2);
   ArraySetAsSeries(rsi_buffer, true);

   int copied = CopyBuffer(rsi_handle, 0, 1, 2, rsi_buffer);

   if(copied != 2)
   {
      Print("RSI CopyBuffer error: ", GetLastError());
      return;
   }

   double rsi_current = rsi_buffer[0];
   double rsi_prev    = rsi_buffer[1];

   // Simple condition example
   if(rsi_current > 70)
   {
      Print("Overbought");
   }
   else if(rsi_current < 30)
   {
      Print("Oversold");
   }
}

4.2 Important Notes for RSI

■ Sticking point 1: Using index 0

CopyBuffer(rsi_handle, 0, 0, 1, buffer); // NG (unstable)

→ Always use start_pos=1


■ Sticking point 2: Impact of changing the period

  • The characteristics of RSI values change depending on the RSI period, such as 14
  • This directly affects the trading logic

👉 As a rule, parameters should be external inputs


■ Sticking point 3: Not enough initial bars

  • If there are not enough bars for calculation, values cannot be retrieved
  • This often occurs at the beginning of a test

Fix:

if(Bars(_Symbol, PERIOD_CURRENT) < 50) return;

4.3 Example of Retrieving iMACD Values: Multiple Buffers

MACD is a representative example of an indicator with multiple buffers.

buffer_numContent
0Main line
1Signal line
int macd_handle;

int OnInit()
{
   macd_handle = iMACD(_Symbol, PERIOD_CURRENT, 12, 26, 9, PRICE_CLOSE);

   if(macd_handle == INVALID_HANDLE)
   {
      Print("MACD handle error");
      return(INIT_FAILED);
   }

   return(INIT_SUCCEEDED);
}

void OnTick()
{
   double macd_main[];
   double macd_signal[];

   ArrayResize(macd_main, 2);
   ArrayResize(macd_signal, 2);

   ArraySetAsSeries(macd_main, true);
   ArraySetAsSeries(macd_signal, true);

   int copied_main   = CopyBuffer(macd_handle, 0, 1, 2, macd_main);
   int copied_signal = CopyBuffer(macd_handle, 1, 1, 2, macd_signal);

   if(copied_main != 2 || copied_signal != 2)
   {
      Print("MACD CopyBuffer error: ", GetLastError());
      return;
   }

   double main_current   = macd_main[0];
   double main_prev      = macd_main[1];
   double signal_current = macd_signal[0];
   double signal_prev    = macd_signal[1];

   // Crossover judgment
   if(main_prev < signal_prev && main_current > signal_current)
   {
      Print("Golden Cross");
   }

   if(main_prev > signal_prev && main_current < signal_current)
   {
      Print("Dead Cross");
   }
}

4.4 Important Points for MACD

■ Understanding buffer_num is the most important point

  • If 0 and 1 are reversed, the logic breaks
  • This can cause results that do not match what appears on the chart

■ Timing of simultaneous retrieval

  • Retrieve main and signal at the same timing
  • If retrieved separately, they may become misaligned

■ Array synchronization

ArraySetAsSeries(..., true);

If this is forgotten:

  • The meaning of the index is reversed
  • Crossover logic can malfunction

4.5 Best Practical Pattern

Standard pattern for multiple buffers:

double buf1[], buf2[];
ArrayResize(buf1, 2);
ArrayResize(buf2, 2);

ArraySetAsSeries(buf1, true);
ArraySetAsSeries(buf2, true);

if(CopyBuffer(handle, 0, 1, 2, buf1) != 2) return;
if(CopyBuffer(handle, 1, 1, 2, buf2) != 2) return;

4.6 The Core Idea of This Chapter

  • Single buffer → simple, like RSI
  • Multiple buffers → requires structural understanding, like MACD

The most important point is:

👉 “the meaning of buffer_num” and “how arrays are handled”

If you get this wrong, the EA may look like it is working correctly while actually malfunctioning, which makes this one of the most dangerous areas.

5. CopyBuffer Error Causes and Fixes

5.1 Main Reasons CopyBuffer Fails

CopyBuffer is a stable function, but in real-world use it can fail with some probability.
The important point is to understand structurally why it fails.

The main causes fall into these three categories.

  • Data is not ready, such as unloaded history or unfinished calculation
  • Invalid handle, such as creation failure or an already released handle
  • Argument mistakes, such as array size or buffer number errors

5.2 Common Error 1: Return Value Is 0 or -1

int copied = CopyBuffer(handle, 0, 1, 2, buffer);
if(copied <= 0)
{
   Print("Error: ", GetLastError());
}

■ Cause

  • The data does not exist yet
  • The indicator calculation has not finished
  • The chart history is insufficient

■ Fix

if(Bars(_Symbol, PERIOD_CURRENT) < 100)
{
   return;
}

Or:

if(copied != count)
{
   return;
}

5.3 Common Error 2: ERR_INDICATOR_DATA_NOT_FOUND

■ Cause

  • No data exists for the specified bar
  • start_pos is too large

Example:

CopyBuffer(handle, 0, 10000, 1, buffer); // NG

■ Fix

  • Use a realistic retrieval range
  • Check with Bars
int bars = Bars(_Symbol, PERIOD_CURRENT);
if(bars < 50) return;

5.4 Common Error 3: INVALID_HANDLE

if(handle == INVALID_HANDLE)

■ Cause

  • iMA or iRSI creation failed
  • Invalid parameters
  • Symbol or timeframe issues

■ Fix

if(handle == INVALID_HANDLE)
{
   Print("Handle creation failed: ", GetLastError());
   return(INIT_FAILED);
}

5.5 Common Error 4: Array-Related Problems

■ Case 1: Insufficient size

double buffer[];
CopyBuffer(...); // NG

→ Always use ArrayResize


■ Case 2: Incorrect Series setting

buffer[0] // unclear which position this is

Fix:

ArraySetAsSeries(buffer, true);

5.6 The Most Common Hidden Bug in Practice

■ Case: It runs, but the result is wrong

Causes:

  • Wrong buffer_num
  • Using start_pos=0
  • Series not set

This is the most dangerous pattern.

👉 It is found late because no error appears


5.7 Safe Implementation Template: Recommended for Practice

double buffer[];
ArrayResize(buffer, 2);
ArraySetAsSeries(buffer, true);

// Check the number of bars
if(Bars(_Symbol, PERIOD_CURRENT) < 100)
{
   return;
}

// Run CopyBuffer
int copied = CopyBuffer(handle, 0, 1, 2, buffer);

// Success check
if(copied != 2)
{
   Print("CopyBuffer failed: ", GetLastError());
   return;
}

// Use values
double current = buffer[0];
double prev    = buffer[1];

5.8 Basic Debugging Strategy

When a CopyBuffer problem occurs, check the following in order.

  1. Whether the handle is valid
  2. Whether Bars is sufficient
  3. The CopyBuffer return value
  4. GetLastError
  5. Print the contents of the buffer
Print("value: ", buffer[0]);

5.9 Environment-Dependent Notes

The following behavior can vary depending on the environment.

  • The broker’s available data range
  • The symbol history
  • VPS communication conditions
  • Strategy Tester settings

👉 Be careful, because these can cause “different results from the same code”


The Core Idea of This Chapter

CopyBuffer errors are not random. They occur for structural reasons.

  • Data is not ready
  • Arguments are wrong
  • State management is insufficient

👉 If you eliminate these three causes, CopyBuffer becomes mostly stable

6. CopyBuffer Optimization and Practical Techniques

6.1 Call CopyBuffer the Minimum Number of Times

CopyBuffer is not a lightweight function.
If you call it without limit on every tick, it can lead to higher CPU load, delays, and slower backtests.

■ NG pattern

void OnTick()
{
   CopyBuffer(handle, 0, 1, 1, buffer); // Run every tick
}

■ Recommended pattern: new bar detection

datetime last_time = 0;

void OnTick()
{
   datetime current_time = iTime(_Symbol, PERIOD_CURRENT, 0);

   if(current_time == last_time)
      return;

   last_time = current_time;

   // Run only on a new bar
   CopyBuffer(handle, 0, 1, 1, buffer);
}

👉 Processing only on new bars greatly reduces load


6.2 Retrieve Only the Minimum Required Data

■ NG pattern

CopyBuffer(handle, 0, 0, 100, buffer);

■ Recommended

CopyBuffer(handle, 0, 1, 2, buffer);

Reasons:

  • For many types of logic, “current plus previous bar” is enough
  • Unnecessary data retrieval hurts performance

6.3 Do Not Recreate the Handle

■ NG

void OnTick()
{
   int handle = iMA(...); // Created every time
}

■ Correct

int handle;

int OnInit()
{
   handle = iMA(...);
}

👉 Keep the handle as state


6.4 Managing Multiple Indicators

In practice, multiple indicators are often used at the same time.

■ Recommended structure

int ma_handle;
int rsi_handle;
int macd_handle;

■ Create them together during initialization

ma_handle   = iMA(...);
rsi_handle  = iRSI(...);
macd_handle = iMACD(...);

■ Retrieve them together in OnTick

CopyBuffer(ma_handle,   0, 1, 2, ma_buf);
CopyBuffer(rsi_handle,  0, 1, 2, rsi_buf);
CopyBuffer(macd_handle, 0, 1, 2, macd_buf);

👉 Structuring this improves maintainability


6.5 Caching Strategy: Important

Retrieving the same value repeatedly is inefficient.

■ Improvement example

double ma_value;

void UpdateIndicators()
{
   double buffer[];
   ArrayResize(buffer, 1);

   if(CopyBuffer(ma_handle, 0, 1, 1, buffer) > 0)
   {
      ma_value = buffer[0];
   }
}

👉 Store the value in a variable and reuse it


6.6 Keep ArraySetAsSeries Consistent

■ Recommended rule

ArraySetAsSeries(buffer, true);

Reasons:

  • It unifies the meaning of indexes
  • It prevents logic bugs

👉 Using true consistently is the safest approach


6.7 Strategy Tester Optimization

How CopyBuffer is used affects backtest results.

■ Notes

  • start_pos=0 → can cause repainting
  • Excessive CopyBuffer calls → slower tests

■ Recommended

  • Keep start_pos fixed at 1
  • Retrieve data only when a new bar is detected

6.8 Final Practical Template

double buffer[];
ArrayResize(buffer, 2);
ArraySetAsSeries(buffer, true);

// New bar detection
static datetime last_time = 0;
datetime current_time = iTime(_Symbol, PERIOD_CURRENT, 0);

if(current_time == last_time)
   return;

last_time = current_time;

// Data retrieval
if(CopyBuffer(handle, 0, 1, 2, buffer) != 2)
{
   Print("CopyBuffer error: ", GetLastError());
   return;
}

// Use values
double current = buffer[0];
double prev    = buffer[1];

6.9 The Core Idea of This Chapter

CopyBuffer optimization comes down to the following three points.

  • Reduce the number of calls
  • Minimize the amount of retrieved data
  • Manage state, including handles and values

👉 This helps achieve:

  • Better performance
  • Fewer bugs
  • More stable live operation

at the same time.

7. Relationship Between CopyBuffer and Related Functions

7.1 CopyBuffer Is Not Used Alone

CopyBuffer is an important function, but it is not complete by itself.
In actual MQL5 development, it is used as part of the following flow.

  • Create an indicator
  • Get the handle
  • Extract values with CopyBuffer
  • Use them for condition checks

In other words, CopyBuffer is responsible for extracting values, and there is always “indicator creation” before it.

A typical flow is as follows.

int ma_handle = iMA(_Symbol, PERIOD_CURRENT, 20, 0, MODE_SMA, PRICE_CLOSE);

double ma_buffer[];
ArrayResize(ma_buffer, 2);
ArraySetAsSeries(ma_buffer, true);

if(CopyBuffer(ma_handle, 0, 1, 2, ma_buffer) == 2)
{
   double ma_current = ma_buffer[0];
   double ma_prev    = ma_buffer[1];
}

Understanding this structure prevents you from treating CopyBuffer like a “magic function.”


7.2 Relationship with iMA, iRSI, iMACD, and iCustom

The functions most closely related to CopyBuffer are the functions that return handles.
Representative examples are:

  • iMA: Moving Average
  • iRSI: RSI
  • iMACD: MACD
  • iCustom: custom indicator

All of these return an indicator handle, not the indicator value itself.
Therefore, MQL5 is based on the following two-step process.

■ Steps

    1. Get a handle with iMA or a similar function
    1. Copy the required value into an array with CopyBuffer

For example, with iRSI:

int rsi_handle = iRSI(_Symbol, PERIOD_CURRENT, 14, PRICE_CLOSE);

double rsi_buffer[];
ArrayResize(rsi_buffer, 1);
ArraySetAsSeries(rsi_buffer, true);

if(CopyBuffer(rsi_handle, 0, 1, 1, rsi_buffer) == 1)
{
   double rsi_value = rsi_buffer[0];
}

The same concept applies to iCustom.

int custom_handle = iCustom(_Symbol, PERIOD_CURRENT, "MyIndicator");

double custom_buffer[];
ArrayResize(custom_buffer, 1);
ArraySetAsSeries(custom_buffer, true);

if(CopyBuffer(custom_handle, 0, 1, 1, custom_buffer) == 1)
{
   double custom_value = custom_buffer[0];
}

■ Common misunderstandings

  • Assuming that calling iMA directly returns the value
  • Assuming that iCustom is special and does not need CopyBuffer

Both are wrong.
It is safest to remember that handle-based indicators generally require CopyBuffer to retrieve values.


7.3 Difference from CopyRates and CopyTime

Functions with similar names include CopyRates and CopyTime.
If you confuse these with CopyBuffer, the design can break down.

■ CopyBuffer

  • Target: indicator calculation results
  • Source: indicator buffer
  • Type: usually double[]

■ CopyRates

  • Target: a full set of price data
  • Source: OHLC data, meaning open, high, low, close, and volume
  • Type: MqlRates[]
MqlRates rates[];
ArrayResize(rates, 2);

int copied = CopyRates(_Symbol, PERIOD_CURRENT, 0, 2, rates);

■ CopyTime

  • Target: time data
  • Source: the timestamp of each bar
  • Type: datetime[]
datetime times[];
ArrayResize(times, 2);

int copied = CopyTime(_Symbol, PERIOD_CURRENT, 0, 2, times);

■ How to choose

  • If you need the price itself → CopyRates
  • If you need time → CopyTime
  • If you need indicator values such as RSI or MA → CopyBuffer

These three functions have clearly different purposes.
CopyBuffer is not a price data retrieval function. It is an indicator value retrieval function.


7.4 Relationship with BarsCalculated

One useful function to check before CopyBuffer is BarsCalculated.
This function checks how many bars have already been calculated by the indicator.

int calculated = BarsCalculated(ma_handle);

if(calculated < 20)
{
   return;
}

This is especially useful in the following situations.

  • Right after OnInit
  • When using a custom indicator
  • During multi-timeframe processing
  • When there are not enough initial bars in the tester

■ Common failure

  • Running CopyBuffer and not understanding why it failed
  • The real reason is simply that calculation has not finished yet

For better stability, the following order is effective.

  • Create the handle
  • Check BarsCalculated(handle)
  • If enough bars are calculated, run CopyBuffer

7.5 Relationship with IndicatorRelease

As a rule, a generated handle should be released when it is no longer needed.
The function used for this is IndicatorRelease.

void OnDeinit(const int reason)
{
   if(ma_handle != INVALID_HANDLE)
   {
      IndicatorRelease(ma_handle);
   }
}

■ Why this is needed

  • To avoid keeping unnecessary indicators
  • To make resource management clear
  • To improve stability in long-running EAs

■ Notes

  • Using a released handle with CopyBuffer will fail
  • The handle must be created again after reinitialization

7.6 Practical Positioning

In practical terms, CopyBuffer sits in the following layer.

  • Price data layer: CopyRates / CopyTime
  • Indicator data layer: CopyBuffer
  • Trading decision layer: signal judgment
  • Execution layer: OrderSend and related functions

An EA with this separation is more robust.
By contrast, writing everything directly in OnTick tends to cause the following problems.

  • Bugs are harder to find
  • Replacing indicators becomes troublesome
  • Optimization becomes difficult
  • Reusability is low

For that reason, it is practical to treat CopyBuffer not just as a retrieval function, but as the core of the indicator data retrieval module in EA design.


7.7 Conclusion to Remember from This Chapter

When you organize the functions around CopyBuffer, their roles are as follows.

  • iMA / iRSI / iMACD / iCustom → create a handle
  • BarsCalculated → check how many bars have been calculated
  • CopyBuffer → extract indicator values
  • IndicatorRelease → release the handle
  • CopyRates / CopyTime → retrieve price or time data

Once this relationship is clear, you can see not only CopyBuffer itself but also the overall structure of data retrieval in MQL5.

8. FAQ: Common Questions and Solution Guidelines

8.1 What is CopyBuffer?

Answer guideline:
Explain that it is a function that retrieves indicator calculation results, or buffers, as an array.
Also mention that it is essential when using technical indicators in an EA.


8.2 Why can’t CopyBuffer retrieve values?

Answer guideline:
Organize the main causes into three points.

  • Data is not loaded, such as insufficient Bars
  • The handle is invalid, such as INVALID_HANDLE
  • The CopyBuffer return value is not checked

As fixes, present “return value checking” and “Bars checking.”


8.3 Should start_pos be 0 or 1?

Answer guideline:
Explain why using “1, the confirmed bar” is the general rule.

  • 0 is unconfirmed and changes
  • It can cause differences between backtesting and live trading

Also note that 0 can be used for real-time monitoring purposes.


8.4 What is buffer_num?

Answer guideline:
Explain that it is the line number inside the indicator.

  • RSI → only 0
  • MACD → 0 for main, 1 for signal

Emphasize that this is a critical point because using the wrong value breaks the logic.


8.5 Is ArraySetAsSeries necessary?

Answer guideline:
Explain why “fixed to true” is recommended in practice.

  • buffer[0] becomes the latest data
  • It prevents index confusion

Also clearly state the bug risk if it is not set.


8.6 Is it okay to call CopyBuffer on every tick?

Answer guideline:
Explain that it is technically possible but not recommended.

  • Performance degradation
  • Increased VPS load

As a fix, present “execution only when a new bar is detected.”


8.7 What is the difference between CopyBuffer and CopyRates?

Answer guideline:
Clarify the difference in purpose.

  • CopyBuffer → indicator values
  • CopyRates → price data, including OHLC

Also mention that confusing them leads to design mistakes.


8.8 Is CopyBuffer necessary with iCustom?

Answer guideline:
Clearly state that it is necessary.

  • iCustom only returns a handle
  • CopyBuffer is required to retrieve values

Emphasize this as a common beginner misunderstanding.


8.9 What is the minimum CopyBuffer setup?

Answer guideline:
Present a minimum practical template.

double buffer[];
ArrayResize(buffer, 1);
ArraySetAsSeries(buffer, true);

if(CopyBuffer(handle, 0, 1, 1, buffer) == 1)
{
   double value = buffer[0];
}

Explain that this can be used as the base and expanded as needed.

9. Summary: Key Points for Practical Use

9.1 CopyBuffer in One Sentence

CopyBuffer is the only entry point for retrieving indicator values.
Because MQL5 is based on “indicators managed by handles,” an EA cannot be built correctly without understanding CopyBuffer.


9.2 Practical Rules You Should Follow: Most Important

By following the rules below, you can prevent most bugs.

■ Rule 1: Create the handle in OnInit

int handle;

int OnInit()
{
   handle = iMA(_Symbol, PERIOD_CURRENT, 20, 0, MODE_SMA, PRICE_CLOSE);
}

■ Rule 2: Use start_pos=1 by default

CopyBuffer(handle, 0, 1, 2, buffer);

Reasons:

  • 0 is unconfirmed and unstable
  • It helps prevent gaps between live trading and testing

■ Rule 3: Keep ArraySetAsSeries fixed to true

ArraySetAsSeries(buffer, true);

■ Rule 4: Always check the return value

if(CopyBuffer(handle, 0, 1, 2, buffer) != 2)
{
   return;
}

■ Rule 5: Run only on a new bar

if(current_time == last_time) return;

9.3 Common Failures and How to Avoid Them

■ It looks normal, but the logic is wrong

Causes:

  • Wrong buffer_num
  • Reversed indexes
  • start_pos=0

👉 This is the most dangerous bug


■ Values cannot be retrieved

Causes:

  • Insufficient Bars
  • Handle error
  • CopyBuffer failure

👉 Always verify with the return value and GetLastError


■ The EA is heavy or slow

Causes:

  • Too many CopyBuffer calls
  • Unnecessary data retrieval
  • Running on every tick

👉 Improve this with new-bar processing and minimum data retrieval


9.4 Structural Understanding of CopyBuffer

CopyBuffer is part of the following structure.

  • Handle creation with iMA, iRSI, or iCustom
  • Data retrieval with CopyBuffer
  • Decision logic
  • Order execution

👉 When this separation is in place, EA quality improves one level.


9.5 Final Template: Remember This

double buffer[];
ArrayResize(buffer, 2);
ArraySetAsSeries(buffer, true);

if(Bars(_Symbol, PERIOD_CURRENT) < 100)
   return;

if(CopyBuffer(handle, 0, 1, 2, buffer) != 2)
   return;

double current = buffer[0];
double prev    = buffer[1];

9.6 The Core Message of This Article

The three important points in CopyBuffer are:

  • Not just syntax, but how it is used
  • Not just values, but array handling
  • Not just a function, but design

Once you understand this, you can handle the following with the same structure.

  • RSI, MACD, and Bollinger Bands
  • Custom indicators
  • Multi-timeframe logic