- 1 1. What Is CopyRates in MQL5?
- 2 2. Basic Syntax and Arguments of CopyRates
- 3 3. Practical CopyRates Sample Code for Beginners
- 4 4. Advanced CopyRates Examples: Integrating It into EA Logic
- 5 5. Performance Optimization and Design Notes When Using CopyRates
- 5.1 5.1 Retrieve Only the Number of Bars You Need
- 5.2 5.2 Do Not Re-fetch Data Wastefully on Every Tick
- 5.3 5.3 Make Decisions Based on Closed Bars
- 5.4 5.4 Standardize How You Handle Arrays
- 5.5 5.5 Be Careful with First Retrieval for Other Symbols and Timeframes
- 5.6 5.6 Separate Logic Functions from Data Retrieval
- 5.7 Common Mistakes and Notes
- 6 6. Common CopyRates Errors and How to Fix Them
- 6.1 6.1 The Return Value Is -1
- 6.2 6.2 Only 0 Data Items Are Retrieved
- 6.3 6.3 array out of range Appears
- 6.4 6.4 Confusing the Latest Bar and the Closed Bar
- 6.5 6.5 Data Cannot Be Retrieved for Another Symbol or Timeframe
- 6.6 6.6 Retrieving Too Many Bars Makes It Slow
- 6.7 6.7 Do Not Decide the Root Cause from GetLastError Alone
- 6.8 6.8 Minimum Safe Template for Practical Use
- 6.9 Common Mistakes and Notes
- 7 7. Difference Between CopyRates and CopyBuffer and How to Use Them
- 7.1 7.1 Conclusion: They Retrieve Completely Different Data
- 7.2 7.2 Role of CopyRates: Base Data
- 7.3 7.3 Role of CopyBuffer: Indicator Data
- 7.4 7.4 Common Misuse Patterns
- 7.5 7.5 Practical Use Cases
- 7.6 7.6 Design-Level Differences: Important
- 7.7 7.7 Which Should You Prioritize?
- 7.8 Common Mistakes and Notes
- 7.9 Best Practical Structure
- 8 8. Checklist for Using CopyRates in Practice
- 9 9. FAQ
- 9.1 9.1 What is CopyRates in MQL5?
- 9.2 9.2 What data can CopyRates retrieve?
- 9.3 9.3 What is the difference between CopyRates and CopyBuffer?
- 9.4 9.4 What is the difference between rates[0] and rates[1]?
- 9.5 9.5 Why does CopyRates return -1?
- 9.6 9.6 Why does CopyRates cause an array out of range error?
- 9.7 9.7 Should CopyRates be called on every tick?
- 9.8 9.8 Can CopyRates retrieve data for other currency pairs or timeframes?
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.](http://finance.trgy.co.jp/wp-content/uploads/2026/03/547bd14ca89d613ee5a44b56abd681cf-1024x539.png)
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 pairPERIOD_M1: the 1-minute timeframe0: retrieve from the latest bar, where 0 is the newest100: retrieve 100 barsrates: 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.
| Function | Data Retrieved | Use Case |
|---|---|---|
| CopyRates | All OHLC data | Price analysis |
| CopyTime | Time only | Time management |
| CopyBuffer | Indicator values | Indicator 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]= latestrates[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 bar1: one bar ago2: 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
| Value | Meaning |
|---|---|
| >0 | Retrieval succeeded |
| 0 | No data |
| -1 | Error |
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]-> latestrates[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.
- Data retrieval with CopyRates
- Condition check in the logic
- 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 andrates[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 itemsis 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=100guarantees 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 ofrates[0] and rates[1].
Basic rule
rates[0]: currently forming barrates[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.
| Function | Data Retrieved | Use Case |
|---|---|---|
| CopyRates | Candlesticks (OHLC) | Price analysis |
| CopyBuffer | Indicator values | Indicator 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
| Viewpoint | CopyRates | CopyBuffer |
|---|---|---|
| Speed | Excellent | Fair |
| Flexibility | Fair | Excellent |
| Stability | Excellent | Good |
| Implementation difficulty | Low | Medium |
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
ArrayResizeappropriately? - Are you avoiding unsafe fixed-index access?
Data handling
- Do you distinguish
rates[0](unconfirmed) fromrates[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 confirmedrates[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.