close series in OnCalculate() routine returns garbage when close[index-1] is used but works with close[index]

 

MT 5.00 build 4755, testing 1 month Dec. 2024 H1 timeframe but the problem is limited to that. 

I consistently was seeing a custom indicator being incorrect when testing but working when only used as an on-screen indicator.  The problem was traced down to a calculation "diff = close[index] - close[index -1]" but the index is well with the range, > than prev_calculated and < rates_total, also no errors are seen in the logs.   Two workarounds are to use iClose(...), which mostly work, or to buffer the close series and then address from the buffer which I haven't seen failed.   A 3rd workout is to rewrite the indictor to buffer price history needed.   However, it seems that none of the workarounds should be needed.  My concern is I'm doing something wrong and I'm unaware of it.   I have a large amount of code to finish and would like to understand what is going on

Example indicator that just draws two lines, one based on the close and the other based on the previous close price (index - 1).  Works correctly when used as an indicator

#property indicator_chart_window
#property indicator_buffers 2
#property indicator_plots   2

//--- indicator settings
#property indicator_label1  "Close"
#property indicator_type1   DRAW_LINE
#property indicator_color1  Gold 
#property indicator_label2  "Pior Close"
#property indicator_type2   DRAW_LINE
#property indicator_color2  Aqua 

enum AccessMode {
   AM_CLOSE  = 0, // close
   AM_ICLOSE = 1, // iClose
   AM_BUFFER = 2  // buffer
};

input AccessMode Mode = AM_CLOSE;

ENUM_TIMEFRAMES TimeFrame=PERIOD_CURRENT;
//--- indicator buffers
double CloseBuffer[];
double PiorCloseBuffer[];

//+------------------------------------------------------------------+
int OnInit() {

  //--- indicator buffers mapping
  SetIndexBuffer(0, CloseBuffer, INDICATOR_DATA);
  SetIndexBuffer(1, PiorCloseBuffer, INDICATOR_DATA);
  
  IndicatorSetInteger(INDICATOR_DIGITS,3);
  //--- sets first bar from what index will be drawn
  return(INIT_SUCCEEDED);
}

//+------------------------------------------------------------------+
//| Custom indicator iteration function                              |
//+------------------------------------------------------------------+
bool firstPass = true;

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[])
{
   if (ArrayIsSeries(close) && !ArrayGetAsSeries(close)) {
      ArraySetAsSeries(close, false);
      ArrayIsSeries(close);
   }
   CloseBuffer[0] = close[0];

   for (int barIndex = MathMax(1, prev_calculated); barIndex < rates_total; barIndex++) {
      switch (Mode)
      {
         case AM_CLOSE: // Often the barIndex-1 assignment seems to return garbage
            CloseBuffer[barIndex] = close[barIndex];
            PiorCloseBuffer[barIndex] = close[barIndex-1];
         break;
         case AM_ICLOSE: // Sometimes fails under testing when accessed through iCustom(...)
            CloseBuffer[barIndex] = iClose(_Symbol, TimeFrame, rates_total - 1 - barIndex);
            PiorCloseBuffer[barIndex] = iClose(_Symbol, TimeFrame, rates_total - barIndex);
         break;
         case AM_BUFFER: // Always works?
            CloseBuffer[barIndex] = close[barIndex];
            PiorCloseBuffer[barIndex] = CloseBuffer[barIndex-1];
         break;
      default:
         break;
      }
   }

  return(rates_total);
}

An example of a test EA that shows the failure

int test_handle = 0;

int OnInit() {
   test_handle = iCustom(_Symbol, PERIOD_CURRENT, "Custom\\TestClose");
   Print("tesh_handle: ", test_handle);

   return(INIT_SUCCEEDED);
}

void OnDeinit(const int reason) {
}

void OnTick() {
   if (!NewBar()) { return; }
}

//+------------------------------------------------------------------+
//| NewBar - true if a new bar has completed                         |
//+------------------------------------------------------------------+
bool NewBar() {
   static datetime lastTime = 0;
   datetime time = iTime(_Symbol, 0, 0);
   if (time != lastTime) {
      lastTime = time;
      return(true);
   }
   return(false);
}

I don't seem to have the option to attach an image, so I've attached a small screen clipping show the failure. 

 
Your topic has been moved to the section: Technical Indicators
Please consider which section is most appropriate — https://www.mql5.com/en/forum/172166/page6#comment_49114893
 
   if (ArrayIsSeries(close) && !ArrayGetAsSeries(close)) {
      ArraySetAsSeries(close, false);
      ArrayIsSeries(close);
   }
If you want to access the array as non-series, just do it:
      ArraySetAsSeries(close, false);
 
Seens to make sense, but on a quick check it didn't work.   I still might be confused and will check further, I'm thinking the ArraySetAsSeries() only effects the indexing order.   Still there is more to try now.  Thanks for the input. 
 
wckdykask #:
Seens to make sense, but on a quick check it didn't work.   I still might be confused and will check further, I'm thinking the ArraySetAsSeries() only effects the indexing order.   Still there is more to try now.  Thanks for the input. 

When I use iCustom in an EA, I use CopyBuffer and ArraySetAsSeries in the EA.

 
Ryan L Johnson #:

When I use iCustom in an EA, I use CopyBuffer and ArraySetAsSeries in the EA.

Ophs, I had an incorrect comment.  I used iClose, not iCustom.   Sorry about the confusion.
 
wckdykask #:
Ophs, I had an incorrect comment.  I used iClose, not iCustom.   Sorry about the confusion.

I think you missed the point of William's Post #2. As he alluded, just call close-[bar] in your loop after setting the close price as series instead of using iClose() nor iCustom().

Also... after setting the close price as series, your loop counts forward (++). When I set price as series, I count backward (--).