Advertisement IC Markets

[FIXED] How to Resolve "Array Out of Range" (Error 4002) in MQL5 Scalping EAs

Share, develop, and backtest custom MQL4/MQL5 Expert Advisors, Python data-scraping scripts, trading bots, and automated market alert systems.
FTtrader
Posts: 954
Joined: Mon Aug 03, 2026 2:43 pm

[FIXED] How to Resolve "Array Out of Range" (Error 4002) in MQL5 Scalping EAs

Post by FTtrader »

Hello everyone,

If you are building or running high-frequency scalping EAs in MetaTrader 5, you have likely run into the dreaded "Array out of range" error in your Journal tab. This is a fatal execution error that instantly halts your Expert Advisor—which is the absolute last thing you want to happen during a volatile liquidity spike.

Unlike MQL4, MQL5 is highly asynchronous. When your EA requests historical data (like indicator buffers or price action arrays), the terminal doesn't guarantee that the data is immediately available in memory. If your EA tries to access an index before verifying the array is populated, it crashes.

Here is a breakdown of why this happens and the exact architectural pattern you should use to fix it.

The Root Cause
In a scalping EA, speed is everything. We often rely on CopyBuffer, CopyRates, or CopyTicks inside the OnTick() function to make split-second execution decisions.

The error triggers when you do something like this:

Code: Select all

// ❌ THE FLAWED APPROACH
double rsi_buffer[];
CopyBuffer(rsi_handle, 0, 0, 3, rsi_buffer);

// If the terminal hasn't loaded the data yet, CopyBuffer returns -1.
// The array is empty. Accessing index [0] crashes the EA.
double current_rsi = rsi_buffer[0];
The Solution: Defensive Data Handling
To build an enterprise-grade EA that can run for months without crashing, you must treat every data request as a potential point of failure.

You need to implement a three-step validation check:

Verify the exact number of elements returned by the Copy... function.

Handle terminal data synchronization delays gracefully.

Explicitly verify ArraySize() before mapping data to your execution logic.

Here is the robust implementation:

Code: Select all

// ✅ THE ROBUST APPROACH
void OnTick()
  {
      // 1. Define dynamic array for the indicator or price data
      double rsi_buffer[];
      
      // 2. Set the array as series (index 0 = current candle)
      ArraySetAsSeries(rsi_buffer, true);
      
      // 3. Attempt to copy the data and store the result
      int copied = CopyBuffer(rsi_handle, 0, 0, 3, rsi_buffer);
      
      // 4. Validate the response BEFORE accessing the array
      if(copied <= 0)
        {
            // The terminal is still syncing, or there is a history error.
            // Do NOT proceed. Exit the OnTick and wait for the next incoming tick.
            Print("Data not ready or CopyBuffer error: ", GetLastError());
            return; 
        }
        
      // 5. Final safety check on array bounds
      if(ArraySize(rsi_buffer) < 3)
        {
            Print("Insufficient array size for strategy logic.");
            return;
        }

      // 6. Safe to execute your scalping logic
      double current_rsi = rsi_buffer[0];
      double previous_rsi = rsi_buffer[1];
      
      // ... entry criteria logic ...
  }
Key Takeaways for Robust Scalping Logic:
Never assume the terminal is ready: Always capture the integer return value of CopyBuffer or CopyRates. If it is less than or equal to zero, simply return; out of the tick.

Watch your indexing: Remember that MQL5 arrays are not time-series by default. Always use ArraySetAsSeries(array, true) if you want index [0] to represent the current active candle.

Initialize properly: Make sure your indicator handles (e.g., rsi_handle) are successfully created in OnInit(), not re-created on every tick.

Have you run into any specific edge cases with array indexing during high-impact news events? Drop your snippets below and we can optimize them.

Take a care,
bye bye.
Recommended broker for automated trading & scalping IC Markets
PTScalper
Site Admin
Posts: 3349
Joined: Mon Jul 20, 2026 1:28 pm

Re: [FIXED] How to Resolve "Array Out of Range" (Error 4002) in MQL5 Scalping EAs

Post by PTScalper »

Cool, thank you for sharing :-)

I think that it can be usefull for others.
Preserve your own money. Scale with the market's money. Exponential growth is the ultimate key.
LondonScalper
Posts: 770
Joined: Sat Sep 05, 2026 7:54 am

Re: [FIXED] How to Resolve "Array Out of Range" (Error 4002) in MQL5 Scalping EAs

Post by LondonScalper »

FTtrader wrote:Array out of range" error... MQL5 is highly asynchronous... exact architectural pattern
Good write-up — this belongs in the “read before you go live on M1” pile.

The fix pattern is less about clever indexing and more about honesty under partial data. If CopyBuffer returns fewer bars than you requested, your loop bounds shrink; if it returns -1 / failure, you don’t trade that tick. Scalping EAs that “assume history is warm” will look fine in a visual tester and then faceplant on the first cold start of the week.

Desk checklist I keep next to any MT5 scalp robot:
  • Init warm-up complete flag before enabling order send
  • Every buffer access guarded by returned count
  • On 4002: log and fail closed, never “skip the check this once”
  • Replay a Sunday restart in staging before promoting a build
I see a near-duplicate thread without the [FIXED] tag — might be worth pointing readers here so the canonical fix stays in one place. Did the guard pattern eliminate 4002 entirely for you, or do you still see edge cases on symbol change?
PropScalpDesk
Posts: 364
Joined: Sat Sep 19, 2026 7:50 pm

Re: [FIXED] How to Resolve "Array Out of Range" (Error 4002) in MQL5 Scalping EAs

Post by PropScalpDesk »

4002 will halt you at the worst second

Array-out-of-range on an M1 scalp EA is not a cosmetic warning. It is a hard stop in the journal while spreads are doing the interesting thing. I am not an EA vendor; I am a discretionary scalper who has still had to babysit automation on a VPS next to the Frankfurt book.

Practical rule before any live lot size: every buffer and indicator series is bounds-checked against Bars/Rates total and against the lookback you actually request. CopyRates failures get an early return, not a blind index. I also refuse to run a new build through a news window; first green days are quiet European hours with micro size.

If the EA can die on a thin history load after a weekend gap, it will. That is an ops problem as much as a coding problem — warm-up bars, symbol sync, and a kill-switch from the phone if the journal starts screaming.

I keep a rollback build on the VPS. When a new compile introduces 4002 under live ticks, I want the previous known-good expert one click away, not a debugging session mid-London.

For those who already patched 4002: do you unit-test the indicator path with deliberately short history, or only discover it after a broker Sunday reopen?
PTScalper
Site Admin
Posts: 3349
Joined: Mon Jul 20, 2026 1:28 pm

Re: [FIXED] How to Resolve "Array Out of Range" (Error 4002) in MQL5 Scalping EAs

Post by PTScalper »

FTtrader wrote: Tue Aug 11, 2026 9:55 pm Hello everyone,

If you are building or running high-frequency scalping EAs in MetaTrader 5, you have likely run into the dreaded "Array out of range" error in your Journal tab. This is a fatal execution error that instantly halts your Expert Advisor—which is the absolute last thing you want to happen during a volatile liquidity spike.

Unlike MQL4, MQL5 is highly asynchronous. When your EA requests historical data (like indicator buffers or price action arrays), the terminal doesn't guarantee that the data is immediately available in memory. If your EA tries to access an index before verifying the array is populated, it crashes.

Here is a breakdown of why this happens and the exact architectural pattern you should use to fix it.

The Root Cause
In a scalping EA, speed is everything. We often rely on CopyBuffer, CopyRates, or CopyTicks inside the OnTick() function to make split-second execution decisions.

The error triggers when you do something like this:

Code: Select all

// ❌ THE FLAWED APPROACH
double rsi_buffer[];
CopyBuffer(rsi_handle, 0, 0, 3, rsi_buffer);

// If the terminal hasn't loaded the data yet, CopyBuffer returns -1.
// The array is empty. Accessing index [0] crashes the EA.
double current_rsi = rsi_buffer[0];
The Solution: Defensive Data Handling
To build an enterprise-grade EA that can run for months without crashing, you must treat every data request as a potential point of failure.

You need to implement a three-step validation check:

Verify the exact number of elements returned by the Copy... function.

Handle terminal data synchronization delays gracefully.

Explicitly verify ArraySize() before mapping data to your execution logic.

Here is the robust implementation:

Code: Select all

// ✅ THE ROBUST APPROACH
void OnTick()
  {
      // 1. Define dynamic array for the indicator or price data
      double rsi_buffer[];
      
      // 2. Set the array as series (index 0 = current candle)
      ArraySetAsSeries(rsi_buffer, true);
      
      // 3. Attempt to copy the data and store the result
      int copied = CopyBuffer(rsi_handle, 0, 0, 3, rsi_buffer);
      
      // 4. Validate the response BEFORE accessing the array
      if(copied <= 0)
        {
            // The terminal is still syncing, or there is a history error.
            // Do NOT proceed. Exit the OnTick and wait for the next incoming tick.
            Print("Data not ready or CopyBuffer error: ", GetLastError());
            return; 
        }
        
      // 5. Final safety check on array bounds
      if(ArraySize(rsi_buffer) < 3)
        {
            Print("Insufficient array size for strategy logic.");
            return;
        }

      // 6. Safe to execute your scalping logic
      double current_rsi = rsi_buffer[0];
      double previous_rsi = rsi_buffer[1];
      
      // ... entry criteria logic ...
  }
Key Takeaways for Robust Scalping Logic:
Never assume the terminal is ready: Always capture the integer return value of CopyBuffer or CopyRates. If it is less than or equal to zero, simply return; out of the tick.

Watch your indexing: Remember that MQL5 arrays are not time-series by default. Always use ArraySetAsSeries(array, true) if you want index [0] to represent the current active candle.

Initialize properly: Make sure your indicator handles (e.g., rsi_handle) are successfully created in OnInit(), not re-created on every tick.

Have you run into any specific edge cases with array indexing during high-impact news events? Drop your snippets below and we can optimize them.

Take a care,
bye bye.
Hi FTtrader again,

You are spot on about MQL5's asynchronous architecture and the necessity of defensive data handling. Catching the return value of CopyBuffer is mandatory to avoid the "Array out of range" crash.

However, while your proposed solution prevents crashes, the implementation introduces memory and I/O bottlenecks that will ironically slow down a high-frequency scalping EA during the exact volatility spikes you want to trade.

Here are the specific performance drags in the original approach:

Per-Tick Memory Allocation: Declaring double rsi_buffer[] inside OnTick() forces the terminal to allocate and deallocate memory dynamically on every single price update. At 1,000 ticks per second, this creates severe overhead.

Redundant Flagging: Calling ArraySetAsSeries() inside OnTick() unnecessarily re-applies the series flag millions of times.

I/O Blocking: Using Print() inside OnTick() when data isn't ready is dangerous. During a major news event, the terminal might lag syncing history. If your EA loops and prints an error 5,000 times in two seconds, the disk I/O will freeze the EA entirely.

Redundant Validation: Checking ArraySize() is unnecessary if you strictly validate the integer returned by CopyBuffer.
Preserve your own money. Scale with the market's money. Exponential growth is the ultimate key.
PTScalper
Site Admin
Posts: 3349
Joined: Mon Jul 20, 2026 1:28 pm

Re: [FIXED] How to Resolve "Array Out of Range" (Error 4002) in MQL5 Scalping EAs

Post by PTScalper »

To build a truly enterprise-grade, high-frequency EA, you need a zero-allocation approach on the fast path.

The Optimized Zero-Allocation Approach

Code: Select all

// 1. Declare buffer and handle globally (or as class members)
// This reuses the same memory block tick after tick.
double rsi_buffer[];
int rsi_handle;

int OnInit()
  {
      rsi_handle = iRSI(_Symbol, _Period, 14, PRICE_CLOSE);
      if(rsi_handle == INVALID_HANDLE)
      {
          Print("CRITICAL: Failed to create RSI handle");
          return(INIT_FAILED);
      }
      
      // 2. Set series flag ONCE during initialization
      ArraySetAsSeries(rsi_buffer, true);
      
      return(INIT_SUCCEEDED);
  }

void OnTick()
  {
      // 3. Copy exactly what is needed. 
      // Because rsi_buffer is global, no memory is allocated/freed here.
      int copied = CopyBuffer(rsi_handle, 0, 0, 3, rsi_buffer);
      
      // 4. Single, strict validation check.
      // (copied < 3) catches both -1 (errors) and partial data synchronization.
      if(copied < 3)
      {
          // SILENT FAIL: Do NOT use Print() here in a live scalper. 
          // Journal spamming blocks execution threads during volatility.
          return; 
      }
        
      // 5. Safe to execute logic. No ArraySize() check required.
      double current_rsi = rsi_buffer[0];
      double previous_rsi = rsi_buffer[1];
      
      // ... entry criteria logic ...
  }
Preserve your own money. Scale with the market's money. Exponential growth is the ultimate key.
PTScalper
Site Admin
Posts: 3349
Joined: Mon Jul 20, 2026 1:28 pm

Re: [FIXED] How to Resolve "Array Out of Range" (Error 4002) in MQL5 Scalping EAs

Post by PTScalper »

Why this architecture scales better:

Memory Reuse: By moving the array declaration to the global scope, CopyBuffer simply overwrites the existing memory block. This eliminates garbage collection stutters.

Initialization Offloading: ArraySetAsSeries is processed exactly once when the EA attaches to the chart.

Strict Integer Checking: Checking if(copied < 3) guarantees that the array bounds are safe up to index [2] in a single CPU instruction, eliminating the need to call the ArraySize() function.

Silent Fails on the Fast Path: Dropping the Print() statement ensures that if the terminal lags during Non-Farm Payrolls, your EA quietly skips ticks and waits for sync without bogging down the terminal's journal logs.
Preserve your own money. Scale with the market's money. Exponential growth is the ultimate key.
PTScalper
Site Admin
Posts: 3349
Joined: Mon Jul 20, 2026 1:28 pm

Re: [FIXED] How to Resolve "Array Out of Range" (Error 4002) in MQL5 Scalping EAs

Post by PTScalper »

When scalping raw price action and spread dynamics, you run into a critical microstructure trap: OnTick() drops events.

If three ticks arrive in one millisecond during a volatility sweep, the terminal groups them. OnTick() fires once, and if you only use SymbolInfoTick(), you only see the final tick. You miss the intermediate liquidity absorption entirely.

To reconstruct the true sequence of events without choking the thread, you must use CopyTicks to fetch the delta of missed ticks. The secret to doing this with zero memory allocation is exploiting the reserve_size parameter in ArrayResize(), which acts exactly like a pre-allocated capacity buffer.

Here is the optimal architecture to process every bid/ask change safely.
Preserve your own money. Scale with the market's money. Exponential growth is the ultimate key.
PTScalper
Site Admin
Posts: 3349
Joined: Mon Jul 20, 2026 1:28 pm

Re: [FIXED] How to Resolve "Array Out of Range" (Error 4002) in MQL5 Scalping EAs

Post by PTScalper »

The Zero-Allocation Implementation

Code: Select all

// 1. Declare the struct array globally.
MqlTick tick_buffer[];

// 2. Track the exact millisecond of the last processed tick to prevent duplicates.
ulong last_processed_msc = 0;

int OnInit()
  {
      // 3. THE ZERO-ALLOCATION SECRET:
      // ArrayResize(array, size, reserve_size)
      // Setting size to 0 but reserve_size to 1000 forces the terminal to pre-allocate 
      // a contiguous block of RAM for 1,000 MqlTick structs. 
      // When CopyTicks resizes the array later, it uses this pre-allocated memory 
      // instead of triggering OS-level malloc/free operations.
      ArrayResize(tick_buffer, 0, 1000);
      
      // Ensure array is chronological (Index 0 = Oldest, Index N = Newest)
      ArraySetAsSeries(tick_buffer, false);
      
      return(INIT_SUCCEEDED);
  }

void OnTick()
  {
      // 4. Fetch up to the last 50 ticks. 
      // Because we reserved 1000 slots, this resize operation is purely logical, 
      // costing zero memory allocations.
      int copied = CopyTicks(_Symbol, tick_buffer, COPY_TICKS_ALL, 0, 50);
      
      if(copied <= 0) return; // Silent fail on sync lag
      
      // 5. Iterate chronologically to reconstruct true market flow
      for(int i = 0; i < copied; i++)
        {
            // Skip ticks we already processed in the previous OnTick() event
            if(tick_buffer[i].time_msc <= last_processed_msc) continue;
            
            // 6. Update our tracker
            last_processed_msc = tick_buffer[i].time_msc;
            
            // 7. Extract raw data for execution logic
            double bid = tick_buffer[i].bid;
            double ask = tick_buffer[i].ask;
            uint flags = tick_buffer[i].flags;
            
            // Check if this specific tick was a price change, volume change, etc.
            bool is_bid_change = (flags & TICK_FLAG_BID) == TICK_FLAG_BID;
            bool is_ask_change = (flags & TICK_FLAG_ASK) == TICK_FLAG_ASK;
            
            // ... insert your raw price action logic here ...
        }
  }
Preserve your own money. Scale with the market's money. Exponential growth is the ultimate key.
PTScalper
Site Admin
Posts: 3349
Joined: Mon Jul 20, 2026 1:28 pm

Re: [FIXED] How to Resolve "Array Out of Range" (Error 4002) in MQL5 Scalping EAs

Post by PTScalper »

Why this architecture is bulletproof

Guaranteed Chronology: By looping from 0 to copied, you process the order flow exactly as it happened on the exchange server, even if the local terminal GUI froze for a fraction of a second.

Zero Garbage Collection: Because the array's capacity is pinned at 1,000 via reserve_size, CopyTicks never asks the operating system for new memory. It simply overwrites the struct fields in RAM.

Precision Filtering: Using tick_buffer.time_msc ensures that overlapping tick batches are cleanly filtered. You will never double-process a price level.

Flag Decoding: Checking the bitwise flags allows you to ignore ticks where only the volume updated, saving CPU cycles if your strategy only cares about spread and price dynamics.
Preserve your own money. Scale with the market's money. Exponential growth is the ultimate key.
Post Reply