MQL5 CopyRates Guide: Syntax, Examples, Errors, and EA Best Practices

目次

1. What Is CopyRates in MQL5?

The CopyRates function in MQL5 is a core function used to retrieve historical price data (OHLC) in bulk.
In EA (automated trading) and indicator development, any process that makes decisions based on past prices needs historical data, and CopyRates is one of the main functions used for that purpose.

The data you can retrieve includes the following.

  • Open
  • High
  • Low
  • Close
  • Time
  • TickVolume

These values are stored in an array as the MqlRates structure, a data format that groups multiple values together.

In simple terms, you can understand CopyRates as
a function that lets your program handle candlestick data from the chart directly.

MQL5 CopyRates example showing OHLC data retrieval and trade signal generation, with code retrieving MqlRates array, comparison of closed bars (rates[1] vs rates[5]), and trading chart highlighting forming bar (rates[0]) vs confirmed bars, illustrating signal processing flow from data retrieval to buy and sell decisions.

1.1 Basic Features of CopyRates

The biggest feature of CopyRates is that it can retrieve data for multiple candlesticks at once.

For example, you can use it for the following tasks.

  • Retrieve the latest 100 bars of price data
  • Compare highs and lows over the past N bars
  • Use the data for trend detection

The basic flow is simple.

MqlRates rates[];
int copied = CopyRates(_Symbol, PERIOD_M1, 0, 100, rates);

What this code does:

  • _Symbol: the current currency pair
  • PERIOD_M1: the 1-minute timeframe
  • 0: retrieve from the latest bar, where 0 is the newest
  • 100: retrieve 100 bars
  • rates: the destination array for the data

After retrieval, you can access the data as follows.

double close_price = rates[0].close;
datetime time = rates[0].time;

Key points

  • rates[0] is the latest bar
  • The larger the index, the further back in time the bar is

1.2 Differences Between CopyRates and Related Functions

Beginners often get confused by the differences between the Copy-related functions.

FunctionData RetrievedUse Case
CopyRatesAll OHLC dataPrice analysis
CopyTimeTime onlyTime management
CopyBufferIndicator valuesIndicator data retrieval

The most important points are:

  • CopyRates = price data itself
  • CopyBuffer = calculated indicator values

For example:

  • Moving average -> CopyBuffer
  • Candlesticks -> CopyRates

If this distinction is unclear, the EA design can break down.


1.3 Common Use Cases

CopyRates is used in almost every EA. Common use cases include the following.

Signal generation

  • Checking conditions based on historical data
  • Example: detecting a new high or judging a trend

Backtest logic

  • Verification using historical data
  • Maintaining reproducibility of the logic

Multi-timeframe analysis

  • Retrieving data from a higher timeframe
  • Example: checking direction on the 1-hour chart

Common Mistakes and Notes

Beginners usually run into similar problems.

1. Wrong array type

double arr[]; // NG

-> CopyRates supports only the MqlRates type.


2. Confusing it with CopyBuffer

  • Using CopyRates when you actually need an indicator value
  • Result: unintended values

3. Misunderstanding the index

  • rates[0] = latest
  • rates[1] = one bar ago

If you understand this in reverse, your logic will fail.


4. Data not retrieved yet

  • History may be missing on the first run
  • Be especially careful on a VPS or immediately after startup

CopyRates is simple, but it can cause bugs if you do not understand
arrays, data direction, and the difference between use cases.

2. Basic Syntax and Arguments of CopyRates

To use CopyRates correctly, the first priority is to understand the syntax and the meaning of each argument accurately.
If this part remains unclear, it can lead to data retrieval mistakes, broken logic, and frequent errors.


2.1 Basic Syntax

The definition of CopyRates is as follows.

int CopyRates(
   string symbol,
   ENUM_TIMEFRAMES timeframe,
   int start_pos,
   int count,
   MqlRates rates_array[]
);

The return value is the number of data items successfully retrieved.

  • Success: number of retrieved bars, such as 100
  • Failure: -1

2.2 Meaning of Each Argument

symbol (currency pair)

Specify the symbol to retrieve.

"EURUSD"
_Symbol // Current chart

In practical code, _Symbol is the standard choice.


timeframe

Specify which timeframe to retrieve data from.

PERIOD_M1
PERIOD_M5
PERIOD_H1
PERIOD_D1

Examples:

  • Scalping -> M1
  • Trend judgment -> H1

start_pos (starting position)

This is the most important point.

  • 0: latest bar
  • 1: one bar ago
  • 2: two bars ago

In other words:

start_pos = 0 -> Retrieve from the latest bar

It is fully linked to the array index.


count (number of bars)

This is the number of bars to retrieve.

100 -> Retrieve 100 bars

Notes:

  • If the number is too large, retrieval may fail
  • The practical rule is to request only the minimum needed

rates_array (destination array)

MqlRates rates[];

The retrieved data is stored here.

You must define it as the MqlRates type.


2.3 Handling the Return Value (Most Important)

Always check the return value from CopyRates.

int copied = CopyRates(_Symbol, PERIOD_M1, 0, 100, rates);

if(copied <= 0)
{
   Print("CopyRates failed: ", GetLastError());
}

Meaning of the Return Value

ValueMeaning
>0Retrieval succeeded
0No data
-1Error

Practical rule

  • Return value checks are required
  • Ignoring errors can cause trouble in live operation

Common Mistakes and Notes

1. Misunderstanding start_pos

start_pos = 0 // Latest

-> Many people mistakenly think 0 means the oldest bar.


2. Array size not allocated

MqlRates rates[];

This alone may not be enough in some cases.

ArrayResize(rates, 100);

Depending on the environment, CopyRates may resize the array, but
explicit allocation is safer.


3. Symbol not selected

SymbolSelect(_Symbol, true);

Without this, retrieval may fail in some cases, especially for another symbol.


4. History not loaded

  • First startup
  • VPS environment

-> The data may not exist yet.

Countermeasure:

RefreshRates();

5. Requesting too many bars

CopyRates(..., 10000, ...)

-> This can cause failure or delay.


Practical Safe Template

MqlRates rates[];
int count = 100;

ArrayResize(rates, count);

int copied = CopyRates(_Symbol, PERIOD_M1, 0, count, rates);

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

CopyRates has simple syntax, but
understanding its arguments and handling errors is everything.

3. Practical CopyRates Sample Code for Beginners

This section provides practical code that you can use as-is.
Instead of showing only a simple example, it explains a safe writing style that can be reused in real projects.


3.1 Minimal Example: Get It Running First

First, here is code that uses CopyRates to retrieve and display the latest 100 bars of data.

void OnTick()
{
   MqlRates rates[];
   int count = 100;

   ArrayResize(rates, count);

   int copied = CopyRates(_Symbol, PERIOD_M1, 0, count, rates);

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

   // Display the latest close price
   Print("Latest Close: ", rates[0].close);

   // Close price of the previous bar
   Print("Previous Close: ", rates[1].close);
}

What this code does

  • Retrieves the latest 100 bars from the 1-minute timeframe
  • Prints the latest close price to the log
  • Makes comparison with the previous bar possible

Beginner points

  • rates[0] -> latest
  • rates[1] -> one bar ago
  • Runs inside OnTick() -> always updates with the latest tick

3.2 Common Pattern 1: Getting Highs and Lows

The basic analysis using historical data is maximum and minimum detection.

double highest = rates[0].high;
double lowest  = rates[0].low;

for(int i = 1; i < copied; i++)
{
   if(rates[i].high > highest)
      highest = rates[i].high;

   if(rates[i].low < lowest)
      lowest = rates[i].low;
}

Print("Highest: ", highest);
Print("Lowest: ", lowest);

Use cases

  • Breakout detection
  • Range analysis
  • Support and resistance

3.3 Common Pattern 2: Simple Trend Detection

if(rates[0].close > rates[10].close)
{
   Print("Uptrend");
}
else
{
   Print("Downtrend");
}

Logic

  • Compare the current close with the close 10 bars ago
  • Determine whether the market is rising or falling

Notes

  • There is a lot of noise, so a filter is needed for live use
  • Simple comparison often causes false judgments

3.4 Common Pattern 3: Multi-Timeframe Data

This example retrieves data from a higher timeframe.

MqlRates h1_rates[];

ArrayResize(h1_rates, 50);

int copied = CopyRates(_Symbol, PERIOD_H1, 0, 50, h1_rates);

if(copied > 0)
{
   Print("H1 Close: ", h1_rates[0].close);
}

Use cases

  • Check the trend on a higher timeframe
  • Enter on a lower timeframe

Common Mistakes and Notes

1. Array access mistake

rates[100] // Out of range

-> Loop only within copied.


2. Ignoring copied

for(int i = 0; i < 100; i++)

-> NG
Always use:

for(int i = 0; i < copied; i++)

3. Data not updated

  • Retrieving only in OnInit -> old data
  • Update in OnTick or OnCalculate

4. The latest bar is not confirmed

rates[0]

-> Unconfirmed bar, still forming

Countermeasure:

rates[1] // Closed bar

5. Performance gets worse

  • Retrieving a large amount of data on every tick
  • Delay on a VPS

Countermeasures:

  • Retrieve only the required number of bars
  • Control the update frequency

Basic Design for Practical Use

At minimum, follow these rules:

  • Use CopyRates only for the minimum required data
  • Always check copied
  • Respect array bounds
  • Clearly define how you handle the unconfirmed bar

It is easy to write CopyRates code that runs, but
small design details matter if you want code that works in practice.

4. Advanced CopyRates Examples: Integrating It into EA Logic

This section explains how to use CopyRates not just for data retrieval, but as part of actual trading logic.
The important point is to make the flow clear: retrieve data, evaluate conditions, and then execute.


4.1 Basic Design: Data Retrieval -> Condition Check -> Order

The basic EA structure has three stages.

  1. Data retrieval with CopyRates
  2. Condition check in the logic
  3. Order processing, such as OrderSend

Here is a simple example.

void OnTick()
{
   MqlRates rates[];
   int count = 20;

   ArrayResize(rates, count);

   int copied = CopyRates(_Symbol, PERIOD_M5, 0, count, rates);

   if(copied <= 0)
      return;

   // Signal judgment
   bool buy_signal = rates[1].close > rates[5].close;
   bool sell_signal = rates[1].close < rates[5].close;

   if(buy_signal)
   {
      Print("BUY signal");
   }
   else if(sell_signal)
   {
      Print("SELL signal");
   }
}

Key points

  • Use rates[1] for closed-bar logic
  • Compare past bars for a simple trend judgment
  • Separate the signal from execution

4.2 Advanced Example 1: Breakout Strategy

This example enters when the price breaks above a past high.

double highest = rates[1].high;

for(int i = 2; i < copied; i++)
{
   if(rates[i].high > highest)
      highest = rates[i].high;
}

// If the current price exceeds the past high
if(rates[0].high > highest)
{
   Print("Breakout BUY");
}

Meaning of the logic

  • Get the highest value in the past data
  • If the current price breaks it, an upward trend may be starting

Notes

  • False breakouts are common
  • Volatility must be considered

4.3 Advanced Example 2: Range Detection

This example determines whether the market is trending or ranging.

double highest = rates[1].high;
double lowest  = rates[1].low;

for(int i = 2; i < copied; i++)
{
   if(rates[i].high > highest)
      highest = rates[i].high;

   if(rates[i].low < lowest)
      lowest = rates[i].low;
}

double range = highest - lowest;

if(range < 0.0010)
{
   Print("Range market");
}
else
{
   Print("Trending market");
}

Key points

  • Judge the market using the difference between the high and low
  • The threshold depends on the currency pair and the environment

4.4 Advanced Example 3: Multi-Timeframe Strategy

This pattern decides direction on a higher timeframe and enters on a lower timeframe.

MqlRates h1[];
ArrayResize(h1, 50);

int copied_h1 = CopyRates(_Symbol, PERIOD_H1, 0, 50, h1);

if(copied_h1 <= 0)
   return;

// Higher timeframe trend
bool uptrend = h1[1].close > h1[10].close;

// Lower timeframe (M5)
MqlRates m5[];
ArrayResize(m5, 20);

int copied_m5 = CopyRates(_Symbol, PERIOD_M5, 0, 20, m5);

if(copied_m5 <= 0)
   return;

// Entry condition
if(uptrend && m5[1].close > m5[2].close)
{
   Print("MTF BUY");
}

Logic structure

  • H1 -> direction
  • M5 -> timing

Common Mistakes and Notes

1. Using the unconfirmed bar

rates[0]

-> This contains a lot of noise and can cause false signals.

Countermeasure:

rates[1]

2. Mixing logic and data retrieval

  • Calling CopyRates many times
  • Reducing readability

Countermeasures:

  • Retrieve data together at the beginning
  • Separate the logic

3. Conditions are too simple

rates[1].close > rates[2].close

-> This is not a profitable strategy by itself.


4. Retrieving too much historical data

  • Performance decreases
  • VPS load increases

5. Symbol and timeframe mismatch

  • Comparing different timeframes incorrectly
  • Getting unintended results

Practical Design Principles

When integrating CopyRates into an EA, follow these principles.

  • Minimize the number of data retrieval calls
  • Make decisions based on closed bars
  • Separate the logic
  • Clearly define the role of each timeframe

CopyRates is not just a retrieval function.
It is the data layer that supports EA logic.

5. Performance Optimization and Design Notes When Using CopyRates

CopyRates is useful, but if you use it incorrectly, it can make an EA slow.
In particular, retrieving a large amount of data on every tick is bad for backtest speed, VPS load, and live stability.

This chapter organizes optimization points for CopyRates based on a practical design that is less likely to break.


5.1 Retrieve Only the Number of Bars You Need

The most basic and important rule is to avoid retrieving more bars than necessary.

For example, if your logic uses only the latest 20 bars, retrieving 500 or 1000 bars every time creates waste.

Bad example

MqlRates rates[];
ArrayResize(rates, 1000);
int copied = CopyRates(_Symbol, PERIOD_M5, 0, 1000, rates);

Good example

MqlRates rates[];
int count = 20;

ArrayResize(rates, count);
int copied = CopyRates(_Symbol, PERIOD_M5, 0, count, rates);

Practical thinking

  • For high-low detection, about 20 to 100 bars is often enough
  • Increase the number only when using a long-term filter
  • Decide the count based on the minimum required by the logic

5.2 Do Not Re-fetch Data Wastefully on Every Tick

A common beginner mistake is retrieving the same data again on every tick even when no new bar has formed.
If you call CopyRates every time the price moves once, the EA becomes unnecessarily heavy.

Improvement policy

  • Retrieve data only when a new bar appears
  • If tick-by-tick decisions are not needed, reduce the update frequency

Example: update only on a new bar

datetime last_bar_time = 0;

void OnTick()
{
   MqlRates rates[];
   ArrayResize(rates, 2);

   int copied = CopyRates(_Symbol, PERIOD_M5, 0, 2, rates);
   if(copied <= 0)
      return;

   if(rates[0].time == last_bar_time)
      return;

   last_bar_time = rates[0].time;

   Print("New bar detected");
}

Benefits of this method

  • Reduces unnecessary calculation
  • Makes backtests lighter
  • Improves logic reproducibility

5.3 Make Decisions Based on Closed Bars

The rates[0] value retrieved by CopyRates is usually the currently forming bar.
Because a forming bar changes, using it directly for trade decisions can make signals unstable.

Basic rule

  • Use rates[1] and later bars for trade decisions
  • Separate rates[0] for monitoring and rates[1] for confirmed decisions

Example

bool buy_signal = rates[1].close > rates[2].high;

Common mistakes

  • Judging by the close of rates[0], which makes entries unstable
  • Logic that looks good in backtests but breaks in live trading

This is not a CopyRates problem. It is a design mistake that ignores how bars work.


5.4 Standardize How You Handle Arrays

CopyRates stores data in a MqlRates array.
If the array direction and reference rules are unclear, bugs become more likely later.

Recommended rules

  • Use a consistent assumption that rates[0] is the newest bar
  • Use the same reference rule across the entire logic
  • Leave comments explaining the meaning

Example

// rates[0] = current forming bar
// rates[1] = last closed bar
// rates[2] = previous closed bar

Notes

  • This is especially important in team development or later maintenance
  • Avoid code that only you can understand

5.5 Be Careful with First Retrieval for Other Symbols and Timeframes

When using a symbol or timeframe other than the current chart, the first retrieval may fail because history is missing.
Be especially careful right after moving to a VPS or immediately after terminal startup.

Example

MqlRates h1_rates[];
ArrayResize(h1_rates, 50);

int copied = CopyRates("EURUSD", PERIOD_H1, 0, 50, h1_rates);
if(copied <= 0)
{
   Print("H1 data not ready: ", GetLastError());
   return;
}

Practical countermeasures

  • Always check the return value
  • Consider using SymbolSelect() when using another symbol
  • Design the EA so it can retry after an initial failure

5.6 Separate Logic Functions from Data Retrieval

If you write CopyRates directly inside the logic many times, the code becomes harder to read.
In practical development, it is easier to maintain when you separate data retrieval from decision logic.

Bad example

  • Calling CopyRates many times inside conditions
  • Combining retrieval, judgment, and order handling into one block

Better direction

  • Retrieve the required data first
  • Then evaluate signals
  • Finally process orders

Concept

bool LoadRates(MqlRates &rates[], int count)
{
   ArrayResize(rates, count);
   int copied = CopyRates(_Symbol, PERIOD_M5, 0, count, rates);
   return (copied > 0);
}

This separation gives you the following benefits:

  • Easier debugging
  • Easier isolation of error causes
  • Easier replacement of the logic

Common Mistakes and Notes

1. Retrieving a large amount of data on every tick

  • Creates waste
  • Increases VPS load

2. Confusing closed and unconfirmed bars

  • Signals become unstable in live operation

3. Not checking the return value

  • The process continues even when data was not retrieved
  • This often causes array out of range

4. Not assuming first-retrieval failure for other timeframes or symbols

  • It may work in testing but stop in production

5. Mixing retrieval and judgment

  • The code becomes easy to break during later changes
  • Bug investigation takes longer

CopyRates is a fast basic function, but
poor design lowers the quality of the entire EA.
On the other hand, simply organizing the number of retrieved bars, update frequency, and closed-bar judgment can make the implementation much more stable.

6. Common CopyRates Errors and How to Fix Them

CopyRates has simple syntax, but in real development, problems such as data not being retrieved, values looking wrong, or array errors happen often.
This chapter organizes the errors beginners most often face in the order of cause -> fix.


6.1 The Return Value Is -1

If the return value of CopyRates is -1, retrieval failed.
In this case, do not read the array as-is.

Failure example

MqlRates rates[];
ArrayResize(rates, 100);

int copied = CopyRates(_Symbol, PERIOD_M5, 0, 100, rates);

// Using it even when copied is -1
Print(rates[0].close);

Safe writing style

MqlRates rates[];
ArrayResize(rates, 100);

int copied = CopyRates(_Symbol, PERIOD_M5, 0, 100, rates);

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

Main causes

  • The symbol is not available
  • History for the specified timeframe has not loaded
  • The requested number of bars is too large
  • Data preparation is not complete right after startup

6.2 Only 0 Data Items Are Retrieved

If the return value is 0 instead of -1, it is often not a hard error. It usually means the target data does not exist yet.

Typical examples

  • First retrieval for another symbol
  • A VPS immediately after startup
  • History not fully reflected while the market is closed
  • Custom symbols or special environments

Fix example

MqlRates rates[];
ArrayResize(rates, 50);

int copied = CopyRates("EURUSD", PERIOD_H1, 0, 50, rates);

if(copied == 0)
{
   Print("No data loaded yet");
   return;
}

Practical thinking

  • 0 items is not unusual
  • Design the first run on the assumption that it may fail
  • Allow retry after a few seconds or on the next tick

6.3 array out of range Appears

This is one of the most common runtime errors around CopyRates.
The cause is simple: the code is reading more bars than were actually retrieved.

Failure example

MqlRates rates[];
ArrayResize(rates, 100);

int copied = CopyRates(_Symbol, PERIOD_M5, 0, 100, rates);

if(copied <= 0)
   return;

// copied is only 2, but the code reads 10 bars ago
Print(rates[10].close);

Safe writing style

if(copied > 10)
{
   Print(rates[10].close);
}

Be careful with loops too

for(int i = 0; i < copied; i++)
{
   Print(rates[i].close);
}

Common causes

  • Assuming that specifying count=100 guarantees 100 bars
  • Only a few bars exist because history is missing
  • Using fixed indexes without checking copied

6.4 Confusing the Latest Bar and the Closed Bar

If the values are retrieved but the logic behaves strangely, the cause is often incorrect use of
rates[0] and rates[1].

Basic rule

  • rates[0]: currently forming bar
  • rates[1]: latest closed bar

Failure-prone example

if(rates[0].close > rates[1].high)
{
   Print("BUY");
}

This condition changes many times while the bar is forming.
It may look good in backtesting but can be unstable in live operation.

Safer example

if(rates[1].close > rates[2].high)
{
   Print("BUY");
}

Notes

  • For discretionary trading support, using rates[0] may be acceptable
  • For automated trading conditions, rates[1] and later bars are the basic choice

6.5 Data Cannot Be Retrieved for Another Symbol or Timeframe

When retrieving data from something other than the current chart, it may not behave the same way as the active chart.

Example

MqlRates usdjpy[];
ArrayResize(usdjpy, 50);

int copied = CopyRates("USDJPY", PERIOD_H1, 0, 50, usdjpy);

This may fail.

Countermeasures

  • Check whether the symbol is available in the terminal
  • Use SymbolSelect() when needed
  • Assume that first retrieval may fail

Example

SymbolSelect("USDJPY", true);

MqlRates usdjpy[];
ArrayResize(usdjpy, 50);

int copied = CopyRates("USDJPY", PERIOD_H1, 0, 50, usdjpy);

if(copied <= 0)
{
   Print("USDJPY H1 not ready: ", GetLastError());
   return;
}

6.6 Retrieving Too Many Bars Makes It Slow

If the code works but feels slow, the CopyRates design may be wasteful.

Heavy example

MqlRates rates[];
ArrayResize(rates, 5000);
int copied = CopyRates(_Symbol, PERIOD_M1, 0, 5000, rates);

Improvement policy

  • Use the minimum required number of bars
  • Do not retrieve again on every tick
  • Update only when a new bar is confirmed

Example

int count = 50;
ArrayResize(rates, count);
int copied = CopyRates(_Symbol, PERIOD_M5, 0, count, rates);

6.7 Do Not Decide the Root Cause from GetLastError Alone

When CopyRates fails, GetLastError() is useful, but
it is safer not to decide the root cause from that alone.

Reasons:

  • The effect of another previous process may remain
  • The real cause may be unloaded history or environment-dependent behavior
  • Behavior may differ depending on the VPS, broker, and terminal state

Practical view

It is more reproducible to check these items together:

  • Return value
  • Number of retrieved bars
  • Symbol
  • Timeframe
  • Execution timing

6.8 Minimum Safe Template for Practical Use

If you are unsure, start with the following pattern to reduce accidents.

MqlRates rates[];
int count = 50;

ArrayResize(rates, count);

int copied = CopyRates(_Symbol, PERIOD_M5, 0, count, rates);

if(copied <= 0)
{
   Print("CopyRates failed. error=", GetLastError());
   return;
}

if(copied < 3)
{
   Print("Not enough bars");
   return;
}

// Use only closed bars
double last_close = rates[1].close;
double prev_close = rates[2].close;

Print("last_close=", last_close, " prev_close=", prev_close);

This pattern prevents at least the following:

  • Continuing after retrieval failure
  • Out-of-range access due to insufficient bars
  • False decisions caused by the unconfirmed bar

Common Mistakes and Notes

1. Assuming the specified count is always retrieved

-> In practice, copied is the truth.

2. Treating rates[0] as a confirmed value

-> It is often a forming bar.

3. Continuing after failure

-> This causes array out of range.

4. Treating other-symbol retrieval like the current chart

-> Initial loading failures are easy to miss.

5. Retrieving a large amount of data on every tick

-> This makes the EA heavier and slows verification.


Most CopyRates problems fall into three groups:
not checking the return value, ignoring insufficient bars, and misusing the unconfirmed bar.
If you handle these points, implementation stability improves significantly.

7. Difference Between CopyRates and CopyBuffer and How to Use Them

One of the most commonly confused topics in MQL5 is the difference between CopyRates and CopyBuffer.
If you use these two functions incorrectly, the logic itself may no longer work, so you need to separate their roles clearly.


7.1 Conclusion: They Retrieve Completely Different Data

First, here is the essential difference.

FunctionData RetrievedUse Case
CopyRatesCandlesticks (OHLC)Price analysis
CopyBufferIndicator valuesIndicator analysis

CopyRates

  • Retrieves price data itself
  • Market fact data

CopyBuffer

  • Retrieves calculated indicator results
  • Processed market data

7.2 Role of CopyRates: Base Data

CopyRates is the base data layer of an EA.

MqlRates rates[];
CopyRates(_Symbol, PERIOD_M5, 0, 50, rates);

You can retrieve:

  • Open
  • High
  • Low
  • Close
  • Time

Use examples

  • Detecting a new high
  • Trend analysis
  • Volatility measurement

7.3 Role of CopyBuffer: Indicator Data

CopyBuffer retrieves calculated indicator values.

int handle = iMA(_Symbol, PERIOD_M5, 20, 0, MODE_SMA, PRICE_CLOSE);

double ma[];
CopyBuffer(handle, 0, 0, 50, ma);

Use examples

  • Moving average (MA)
  • RSI
  • MACD

7.4 Common Misuse Patterns

1. Trying to get indicator values with CopyRates

// NG: Trying to retrieve MA with CopyRates

-> CopyRates contains only price data.


2. Trying to get prices with CopyBuffer

// NG: Retrieving price with CopyBuffer

-> CopyBuffer is for indicators.


3. Unclear roles

  • The logic becomes confused
  • The design breaks down

7.5 Practical Use Cases

Pattern 1: Judge by price only

-> Use only CopyRates.

if(rates[1].close > rates[2].high)
{
   Print("Breakout");
}

Pattern 2: Indicator-based logic

-> Use CopyBuffer.

if(ma[1] > ma[2])
{
   Print("MA Uptrend");
}

Pattern 3: Combination, the most common in practice

// Retrieve price
CopyRates(_Symbol, PERIOD_M5, 0, 50, rates);

// Retrieve MA
CopyBuffer(handle, 0, 0, 50, ma);

// Condition check
if(rates[1].close > ma[1])
{
   Print("BUY");
}

7.6 Design-Level Differences: Important

CopyRates

  • Data retrieval cost: low
  • Processing load: light
  • Reproducibility: high

CopyBuffer

  • Requires indicator calculation
  • Requires handle management
  • Initial loading can be slow

Practical judgment

  • Lightweight EA -> mainly CopyRates
  • Complex logic -> combine with CopyBuffer

7.7 Which Should You Prioritize?

Option A: CopyRates-centered design

  • Simple
  • Fast
  • Highly reproducible

-> Suitable for scalping and lightweight EAs.


Option B: CopyBuffer combined design

  • More features
  • More expressive
  • More complex design

-> Suitable for swing trading and combined logic.


Comparison points

ViewpointCopyRatesCopyBuffer
SpeedExcellentFair
FlexibilityFairExcellent
StabilityExcellentGood
Implementation difficultyLowMedium

Common Mistakes and Notes

1. Not checking the CopyBuffer handle

-> Using it while it is still INVALID_HANDLE.


2. Time axis mismatch between indicator and price

  • CopyRates and CopyBuffer use different timeframes
  • The judgment becomes unintended

3. Data update timing mismatch

  • CopyRates is current
  • CopyBuffer may lag in some cases

4. Array synchronization mistake

  • The indexes of rates and ma shift
  • The judgment breaks

Best Practical Structure

The most stable structure is as follows.

  • Retrieve base data with CopyRates
  • Use CopyBuffer only for the required indicators
  • Keep judgments unified around closed bars

CopyRates and CopyBuffer look similar, but
they are completely different functions with different roles.

Whether you separate them correctly has a major effect on EA design quality.

8. Checklist for Using CopyRates in Practice

CopyRates is a simple function, but in practical work you must handle it together with design, operation, and error management.
This section organizes a practical checklist for development, testing, and live operation.


8.1 Pre-Implementation Check: Design Stage

Data design

  • Is the timeframe you will use clear?
  • Have you limited the number of required bars to the minimum?
  • Is the design based on closed bars, such as rates[1]?

Logic design

  • Are CopyRates data and the logic separated?
  • Have you decided the data retrieval timing, such as every tick or new bar?
  • Have you organized whether other timeframes or symbols are used?

Risk

  • Have you defined behavior when data is insufficient?
  • Do you assume that initial retrieval may fail?
  • Have you considered operation in a VPS environment?

8.2 Implementation Check: Code Quality

Basic rule

if(copied <= 0)
   return;
  • Do you always check the return value?
  • Do you manage array bounds using copied?

Array management

  • Are you using ArrayResize appropriately?
  • Are you avoiding unsafe fixed-index access?

Data handling

  • Do you distinguish rates[0] (unconfirmed) from rates[1] (closed)?
  • Have you left comments explaining the meaning of indexes?

8.3 Performance Check

Remove unnecessary processing

  • Are you calling CopyRates on every tick?
  • Are you retrieving more bars than necessary?

Improvement points

  • Have you added new-bar detection logic?
  • Have you separated heavy processing?

8.4 Live Operation Check: VPS and Production

Initial behavior

  • Is the design safe even if CopyRates fails immediately after startup?
  • Does it avoid stopping unexpectedly when data has not been retrieved?

Environment dependency

  • Have you considered history differences between brokers?
  • Can spread and liquidity differences affect behavior?

Reproducibility

  • Does the same logic apply in backtesting and forward testing?
  • Is there any processing that depends on an unconfirmed bar?

8.5 Error Handling Check

Minimum defensive code

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

if(copied < required_bars)
{
   Print("Not enough data");
   return;
}

Items to check

  • Are you logging GetLastError?
  • Can the EA stop safely during abnormal conditions?
  • Does it avoid infinite loops or repeated errors?

8.6 Recommended Practical Template Structure

The most stable structure is as follows.

MqlRates rates[];
int count = 50;

ArrayResize(rates, count);

int copied = CopyRates(_Symbol, PERIOD_M5, 0, count, rates);

if(copied <= 0)
   return;

if(copied < 3)
   return;

// Use only closed bars
double close1 = rates[1].close;
double close2 = rates[2].close;

// Logic
if(close1 > close2)
{
   Print("Up");
}

8.7 Final Check: Important

In practical development, the following three points matter most.

1. Data retrieval is stable

  • Retrieval failure is considered
  • No problems occur on first startup or in a VPS environment

2. The logic is reproducible

  • Judgment is based on closed bars
  • It matches the backtest

3. Performance is appropriate

  • There is no unnecessary retrieval
  • The EA is lightweight and stable

Common Mistakes and Notes

1. Stopping at “it works”

-> This can cause failures in production.


2. Ignoring copied

-> This is a common source of array errors.


3. Depending on unconfirmed bars

-> This can create misleading backtest results.


4. Not assuming initial retrieval failure

-> The EA may stop on a VPS.


5. Overusing CopyRates

-> Performance decreases.


CopyRates is simple by itself, but
it becomes practical only when design, error management, and operation are handled together.

By satisfying this checklist, you can move from beginner-level code to a more stable EA design.

9. FAQ

9.1 What is CopyRates in MQL5?

CopyRates is a function that retrieves candlestick data (OHLC) in bulk for a specified symbol and timeframe. It is a basic function used when an EA or indicator works with past prices.


9.2 What data can CopyRates retrieve?

It can retrieve the following information.

  • Open
  • High
  • Low
  • Close
  • Time
  • TickVolume

These values are stored in an array as the MqlRates structure.


9.3 What is the difference between CopyRates and CopyBuffer?

  • CopyRates: price data, or candlesticks
  • CopyBuffer: calculated indicator values

Use CopyRates when you need price data itself.
Use CopyBuffer for indicators such as moving averages and RSI.


9.4 What is the difference between rates[0] and rates[1]?

  • rates[0]: the current forming bar, not confirmed
  • rates[1]: the latest closed bar

In automated trading, it is generally safer to make decisions using rates[1].


9.5 Why does CopyRates return -1?

Common causes include the following.

  • History data has not loaded
  • The symbol has not been selected
  • The requested number of bars is too large
  • Data is still being prepared immediately after startup

As a countermeasure, return value checks and retry design are important.


9.6 Why does CopyRates cause an array out of range error?

The cause is accessing an index larger than the number of bars actually retrieved, which is stored in copied.

if(copied > 10)
{
   Print(rates[10].close);
}

Always manage array bounds based on copied.


9.7 Should CopyRates be called on every tick?

Usually, no.

  • It is more efficient to retrieve data only when a new bar is confirmed
  • Retrieving on every tick can reduce performance

In EA design, controlling the update frequency is important.


9.8 Can CopyRates retrieve data for other currency pairs or timeframes?

Yes.

CopyRates("USDJPY", PERIOD_H1, 0, 50, rates);

However, the first retrieval may fail if history has not loaded, so it is safer to combine it with
SymbolSelect and retry processing.