MQL4: "array out of range" error in the strategy tester with my personal Expert Advisor

 

I am having a difficulty in solving "array out of range" error, which occurs constantly when I perform strategy tester with my EA under development.

I tried to figure out the cause and fix it by referring to MQL4 reference and other sources available in cyber network, but so far no progress for almost 2 weeks.

I hope anyone can direct me to the source that help me fix the issue or provide a straight answer to my problem.

The below is my EA under development and the highlighted in pink is where the error occurs.

//+------------------------------------------------------------------+
//|                              EA_ZigZag_Peak and Valley_Forum.mq4 |
//|                                  Copyright 2023, MetaQuotes Ltd. |
//|                                           scott_5409jp@nifty.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2023, MetaQuotes Ltd."
#property link      "scott_5409jp@nifty.com"
#property version   "1.00"
#property strict
#property indicator_chart_window
#property indicator_buffers 1

//--- input parameters
input int      ExtDepth=12;
input int      ExtDeviation=5;
input int      ExtBackstep=3;
//--- indicator buffers
double      High_recent[];

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- indicator buffers mapping
   SetIndexBuffer(0,High_recent);
//---
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
  {
   if(IsTradeAllowed()==false)
      return;
   if(CalculateCurrentOrders()==0)
      CheckForOpen();
  }
//+------------------------------------------------------------------+
//|  Check orders                                                    |
//+------------------------------------------------------------------+
int CalculateCurrentOrders()
  {
   int positions=0;
   for(int i=0; i<OrdersTotal(); i++)
     {
      if(OrderSelect(i,SELECT_BY_POS,MODE_TRADES)==false)
         break;
      if(OrderSymbol()==Symbol())
         positions++;
     }
   return positions;
  }
//+------------------------------------------------------------------+
//|  Open orders                                                     |
//+------------------------------------------------------------------+
void CheckForOpen()
  {
   int b=Bars-1;
   for(int shift=0; shift<=b-ExtDepth; shift++)      // looping backward
     {
      High_recent[shift] = iCustom(NULL,0,"Scott\\ZigZag_iCustom_for_EA",ExtDepth,ExtDeviation,ExtBackstep,0,shift);         // error(array out of range) occurs ???
      Print("High_recent[",shift,"]=",High_recent[shift]);
     }
  }
//+------------------------------------------------------------------+


Journal of the strategy tester;

2023.12.05 10:38:07.321 USDJPY,M5: 1 tick events (1 bars, 66910698 bar states) processed in 0:00:00.078 (total time 0:00:05.922)
2023.12.05 10:38:07.321 2020.08.03 00:00:01  Testing pass stopped due to a critical error in the EA
2023.12.05 10:38:07.321 2020.08.03 00:00:01  EA_ZigZag_Peak and Valley_Forum USDJPY,M5: array out of range in 'EA_ZigZag_Peak and Valley_Forum.mq4' (63,18)
2023.12.05 10:38:07.252 2020.08.01 00:00:00  EA_ZigZag_Peak and Valley_Forum inputs: ExtDepth=12; ExtDeviation=5; ExtBackstep=3; 
2023.12.05 10:38:01.684 TestGenerator: spread set to 5
2023.12.05 10:38:01.400 Expert Scott\EA_ZigZag_Peak and Valley_Forum USDJPY,M5: loaded successfully


The Indicator calling from EA is below. It shows ZigZag peak and valley points on a chart.

//+------------------------------------------------------------------+
//|                                        ZigZag_iCustom_for_EA.mq4 |
//|                                  Copyright 2023, MetaQuotes Ltd. |
//|                                           scott_5409jp@nifty.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2023, MetaQuotes Ltd."
#property link      "scott_5409jp@nifty.com"
#property version   "1.00"
#property strict
#property indicator_chart_window
#property indicator_buffers 2
#property indicator_plots   2
//--- plot ExtHighBuffer
#property indicator_label1  "ExtHighBuffer"
#property indicator_color1  clrRed
//--- plot ExtLowBuffer
#property indicator_label2  "ExtLowBuffer"
#property indicator_color2  clrAqua
//--- input parameters
input int      ExtDepth=5;
input int      ExtDeviation=0;
input int      ExtBackstep=0;
//--- indicator buffers ----  all these buffers are not timeseries(AS_SERIES flag not set), but indexed same as timeseries in the program.
double         ExtZigZagBuffer[];              // for calculation
double         ExtZigZagBuffer2[];             // for calculation >> for drawing zigzag line
double         ExtHighBuffer[];
double         ExtLowBuffer[];
//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- indicator buffers
   IndicatorBuffers(4);
//--- indicator buffers mapping
   SetIndexBuffer(0,ExtHighBuffer);
   SetIndexBuffer(1,ExtLowBuffer);
   SetIndexBuffer(2,ExtZigZagBuffer);     // ZigZag temp low buffer >> zig zag final buffer
   SetIndexBuffer(3,ExtZigZagBuffer2);    // zigzag temp high buffer
//PlotIndexSetInteger(0,PLOT_ARROW,159);
//PlotIndexSetInteger(1,PLOT_ARROW,159);
   SetIndexStyle(0,DRAW_ARROW,EMPTY,2);
   SetIndexArrow(0,159);
   SetIndexStyle(1,DRAW_ARROW,EMPTY,2);
   SetIndexArrow(1,159);
   SetIndexEmptyValue(0,0.0);             // define 0.0 as empty value(0 value will not be drawn)
   SetIndexEmptyValue(1,0.0);
//---
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//| Custom indicator iteration function                              |
//+------------------------------------------------------------------+
int OnCalculate(const int rates_total,
                const int prev_calculated,
                const datetime &time[],
                const double &open[],
                const double &high[],
                const double &low[],
                const double &close[],
                const long &tick_volume[],
                const long &volume[],
                const int &spread[])
  {
//---
   int    b,shift,back,lasthighpos,lastlowpos;
   double val,res,curlow=0.0,curhigh=0.0,lasthigh=0.0,lastlow=0.0;

   b=Bars-1;

   for(shift=b-ExtDepth; shift>=0; shift--)           // first cutting for storing temp high and low data into buffer
     {
      ExtHighBuffer[shift]=0.0;       // data clear
      ExtLowBuffer[shift]=0.0;
      //--- low
      val=Low[Lowest(NULL,0,MODE_LOW,ExtDepth,shift)];   // lowest price in post ExtDepth(12) of bars
      if(val==lastlow)
         val=0.0;
      else
        {
         lastlow=val;
         if((Low[shift]-val)>(ExtDeviation*Point))       // if lowest price is not so lower than low price of reference bar, neglect it. ( 1 point = 0.1 pip)
            val=0.0;
         else
           {
            for(back=1; back<=ExtBackstep; back++)       // delete Buffer data once stored as temporary low price if they are higher than val looking back ExtBakcstep(3) of bars
              {
               res=ExtZigZagBuffer[shift+back];
               if((res!=0)&&(res>val))
                  ExtZigZagBuffer[shift+back]=0.0;
              }
           }
        }
      ExtZigZagBuffer[shift]=val;
      //--- high
      val=High[Highest(NULL,0,MODE_HIGH,ExtDepth,shift)];   // highest price in post ExtDepth(12) of bars
      if(val==lasthigh)
         val=0.0;
      else
        {
         lasthigh=val;
         if((val-High[shift])>(ExtDeviation*Point))      // if highest price is not so hihger than high price of reference bar, neglect it. ( 1 point = 0.1 pip)
            val=0.0;
         else
           {
            for(back=1; back<=ExtBackstep; back++)       // delete Buffer data once stored as temporary high price if they are loweer than val looking back ExtBakcstep(3) of bars
              {
               res=ExtZigZagBuffer2[shift+back];
               if((res!=0)&&(res<val))
                  ExtZigZagBuffer2[shift+back]=0.0;
              }
           }
        }
      ExtZigZagBuffer2[shift]=val;
      //looks OK: Print("tempHigh[",shift,"]=",ExtZigZagBuffer2[shift],"  tempLow[",shift,"]=",ExtZigZagBuffer[shift]);
     }
// final cutting
   for(shift=b-ExtDepth; shift>=0; shift--)     // deleting inappropriate data to keep only valid data that change high and low alternately
     {
      curlow=ExtZigZagBuffer[shift];                  // zigzag temp low buffer
      curhigh=ExtZigZagBuffer2[shift];                // zigzag temp high buffer
      if((curlow==0)&&(curhigh==0))
         continue;
      // reset position on entering peak or valley data from the other
      if(lasthigh==0 && curhigh>0)
         lasthighpos=shift;
      if(lastlow==0 && curlow>0)
         lastlowpos=shift;
      // delete invalid data
      if(lasthigh!=0 && curhigh!=0)
        {
         if(curhigh>ExtZigZagBuffer2[lasthighpos])
           {
            ExtZigZagBuffer2[lasthighpos]=0.0;
            lasthighpos=shift;
           }
         else
            ExtZigZagBuffer2[shift]=0.0;
        }
      if(lastlow!=0 && curlow!=0)
        {
         if(curlow<ExtZigZagBuffer[lastlowpos])
           {
            ExtZigZagBuffer[lastlowpos]=0.0;
            lastlowpos=shift;
           }
         else
            ExtZigZagBuffer[shift]=0.0;
        }
      lasthigh=ExtZigZagBuffer2[shift];
      lastlow=ExtZigZagBuffer[shift];
     }

   for(shift=300; shift>=0; shift--)
     {
      //if(ExtZigZagBuffer2[shift]!=0 || ExtZigZagBuffer[shift]!=0)
      //Print("finalHigh[",shift,"]=",ExtZigZagBuffer2[shift],"  finalLow[",shift,"]=",ExtZigZagBuffer[shift]);
     }


// set valid high and low data into each buffer
   int n_high=0;
   int n_low=0;

   for(shift=0; shift<=b-ExtDepth; shift++)      // looping backward
     {
      res=ExtZigZagBuffer2[shift];
      if(res!=0)
        {
         ExtHighBuffer[shift]=res;
         //Print("HighBuffer[",shift,"]=",res,"  HighPos[",shift,"]=",shift);
         n_high++;
        }
      else
         ExtHighBuffer[shift]=0.0;
      res=ExtZigZagBuffer[shift];
      if(res!=0)
        {
         ExtLowBuffer[shift]=res;
         //Print("LowBuffer[",shift,"]=",res,"  LowPos[",shift,"]=",shift);
         n_low++;
        }
      else
         ExtLowBuffer[shift]=0.0;
     }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
   return(0);
  }
//+------------------------------------------------------------------+


Options setting;

Max bars in chart is 250,000 and oldest date on 5 minutes chart is July 29,2020. So I believe that the period I used for strategy tester would not be an issue.


I would appreciate any help.

Regards,

 
I don't understand how SetIndexBuffer works, so I just put it like this
int OnInit()
  {
//--- indicator buffers mapping
   //SetIndexBuffer(0,High_recent);
   ArrayResize(High_recent, 0, Bars - 1);
   ArrayResize(High_recent, Bars - 1, Bars - 1);
//---
   return(INIT_SUCCEEDED);
  }
 
Nguyen Kim Thanh #:
I don't understand how SetIndexBuffer works, so I just put it like this

If you want to emulate an indicator buffer, then usually the size is changed in OnTick, and not once in OnInit.

cloudwandering:


You cannot use SetIndexBuffer in an EA. This is for indicator only. Therefore the size of your High_recent[] array is zero.

 
Nguyen Kim Thanh #:
I don't understand how SetIndexBuffer works, so I just put it like this

I understand.  Thank you for your support.

 
Vladislav Boyko #:

If you want to emulate an indicator buffer, then usually the size is changed in OnTick, and not once in OnInit.

You cannot use SetIndexBuffer in an EA. This is for indicator only. Therefore the size of your High_recent[] array is zero.

I did not know SetIndexBuffer is for indicator only. I think I fully understand how to emulate an indicator buffer in EA. Thank you.
 
cloudwandering:
void CheckForOpen()
  {
   int b=Bars-1;
   for(int shift=0; shift<=b-ExtDepth; shift++)      // looping backward
     {
      High_recent[shift] = iCustom(NULL,0,"Scott\\ZigZag_iCustom_for_EA",ExtDepth,ExtDeviation,ExtBackstep,0,shift);         // error(array out of range) occurs ???
      Print("High_recent[",shift,"]=",High_recent[shift]);
     }
  }

With each tick you try to copy the entire indicator buffer into your array again. It might work, but it will be quite slow.

In this topic I gave my example of emulating indicator buffers in an advisor. But this can be relatively difficult to understand (and to implement), because in that example I meant placing the indicator algorithm in a separate file. But perhaps you will find something useful for yourself there.

 
Vladislav Boyko #:

With each tick you try to copy the entire indicator buffer into your array again. It might work, but it will be quite slow.

In this topic I gave my example of emulating indicator buffers in an advisor. But this can be relatively difficult to understand (and to implement), because in that example I meant placing the indicator algorithm in a separate file. But perhaps you will find something useful for yourself there.

The topic you taught me is very useful. I will use the idea to implement indicator buffer like array into an EA with much saving calculation time. Thank you so much.
 
cloudwandering #:
The topic you taught me is very useful. I will use the idea to implement indicator buffer like array into an EA with much saving calculation time. Thank you so much.

Also to consider - there are certain scenarios which will stop your code dead - such as divide by zero or array out of range.

Always worth coding a "if" check before attempting these operations and reacting accordingly if the situation is not as expected

 
R4tna C #:

Also to consider - there are certain scenarios which will stop your code dead - such as divide by zero or array out of range.

Always worth coding a "if" check before attempting these operations and reacting accordingly if the situation is not as expected

I really appreciate your comments. Any professional code seems to contain double or tripple space of error handling than its main code. I will certainly keep this in mind. Again, thank you.
Reason: