Loop with iCustom in Expert Advisor and in Indicator

 

Hi,
I have an Expert Advisor which retrieves, with iCustom() , values from two buffers calculated by an Indicator: one buffer is associated with data get from the current Symbol() , while the other one uses iCustom() to get data from a different SYMBOL.
The indicator prints correctly the two buffers.

The problem is that the Expert Advisor takes several minutes to synchronize all the data so any optimization test is impossible.

Things of which I'm aware:
* in the Expert Advisor, iCustom() , must be called in OnInit() ;
* in the Indicator, iCustom() , must be called in OnCalculate() .

Why the Expert Advisor takes so long to load all the bars? Am I doing something wrong?

The following is a minimal example.

Indicator.mq5

#property indicator_chart_window

#property indicator_buffers 2
#property indicator_plots 2

#property indicator_label1 "avg1"
#property indicator_color1 Green
#property indicator_type1 DRAW_LINE

#property indicator_label2 "avg2"
#property indicator_color2 Red
#property indicator_type2 DRAW_LINE


//+------------------------------------------------------------------+
//| Custom indicator global scope                                    |
//+------------------------------------------------------------------+
// indicator parameters
input string SYMBOL = "";

// handle of the custom indicator
int indicator_handle;

// buffers
double avg1_buffer[];
double avg2_buffer[];


//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int OnInit() {
  SetIndexBuffer(0, avg1_buffer);
  SetIndexBuffer(1, avg2_buffer);

  return(INIT_SUCCEEDED);
}


//+------------------------------------------------------------------+
//| Custom indicator deinitialization function                       |
//+------------------------------------------------------------------+
void OnDeinit(const int reason) {
  if (reason == REASON_REMOVE) {
    if (IndicatorRelease(indicator_handle)) {}
  }
}


//+------------------------------------------------------------------+
//| 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 begin = rates_total - prev_calculated > 1 ? prev_calculated : prev_calculated - 1;

  for (int i = begin; i < rates_total ; i++) {
    // initialize buffers
    avg1_buffer[i] = EMPTY_VALUE;
    avg2_buffer[i] = EMPTY_VALUE;
    
    if (i < rates_total - 1) {
      avg1_buffer[i] = (high[i] + low[i]) / 2;
      
      if (SymbolInfoInteger(SYMBOL, SYMBOL_EXIST) && SYMBOL != "" && Symbol() != SYMBOL) {
        int idx = iBarShift(SYMBOL, Period(), time[i], true);
        if (idx > 0) {
          indicator_handle = iCustom(SYMBOL, Period(), "Me\\indicator", SYMBOL);
          double cp_avg_array[1];
          if (CopyBuffer(indicator_handle, 0, idx, 1, cp_avg_array) != -1) {
            avg2_buffer[i] = cp_avg_buffer[0];
          }
          else {
            avg2_buffer[i] = avg2_buffer[i - 1];
          }
        }
      }
    }
  }    
    
  return(rates_total);
}

Expert.mq5

//+------------------------------------------------------------------+
//| Expert global scope                                              |
//+------------------------------------------------------------------+
// indicator parameters
input string SYMBOL = "";

// expert adviser parameters
// ...

// handle of the custom indicator
int indicator_handle;

// buffers
double avg1_buffer[1];
double avg2_buffer[1];


//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit() {
  indicator_handle = iCustom(Symbol(), Period(), "Me\\indicator", SYMBOL);
  
  return(INIT_SUCCEEDED);
}


//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason) {
  if (reason == REASON_REMOVE) {
    if (IndicatorRelease(indicator_handle)) {}
  }
}


//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick() {
  if (check_new_bar()) {
    
    CopyBuffer(indicator_handle, 0, 1, 1, avg1_buffer);
    CopyBuffer(indicator_handle, 1, 1, 1, avg2_buffer);
    
    // take decision to open/close position
  }   
}
 
I edited your previous post. Don't use tables for code. It makes it difficult to read on mobile devices.
 
  1.   for (int i = begin; i < rates_total ; i++) {
        // initialize buffers
        avg1_buffer[i] = EMPTY_VALUE;
        avg2_buffer[i] = EMPTY_VALUE;
        
        if (i < rates_total - 1) {
          avg1_buffer[i] = (high[i] + low[i]) / 2;
          
          if (SymbolInfoInteger(SYMBOL, SYMBOL_EXIST) && SYMBOL != "" && Symbol() != SYMBOL) {
            int idx = iBarShift(SYMBOL, Period(), time[i], true);
            if (idx > 0) {
              indicator_handle = iCustom(SYMBOL, Period(), "Me\\indicator", SYMBOL);

    Perhaps you should read the manual, especially the examples.
       How To Ask Questions The Smart Way. (2004)
          How To Interpret Answers.
             RTFM and STFW: How To Tell You've Seriously Screwed Up.

    They all (including iCustom) return a handle (an int). You get that in OnInit. In OnTick/OnCalculate (after the indicator has updated its buffers), you use the handle, shift and count to get the data.
              Technical Indicators - Reference on algorithmic/automated trading language for MetaTrader 5
              Timeseries and Indicators Access / CopyBuffer - Reference on algorithmic/automated trading language for MetaTrader 5
              How to start with MQL5 - General - MQL5 programming forum - Page 3 #22 (2020)
              How to start with MQL5 - MetaTrader 5 - General - MQL5 programming forum - Page 7 #61 (2020)
              MQL5 for Newbies: Guide to Using Technical Indicators in Expert Advisors - MQL5 Articles (2010)
              How to call indicators in MQL5 - MQL5 Articles (2010)

  2. You are recursively calling the indicator.

    The following is a minimal example.
    
    Indicator.mq5
    
        #property indicator_chart_window
    
                  indicator_handle = iCustom(SYMBOL, Period(), "Me\\indicator", SYMBOL);
 
William Roeder #:
  1. They all (including iCustom) return a handle (an int). You get that in OnInit. In OnTick/OnCalculate (after the indicator has updated its buffers), you use the handle, shift and count to get the data.

If the Indicator calls iCustom() in OnInit(), then, the Expert Advisor gets the error: stack overflow

2. You are recursively calling the indicator.

Alternatives?

 
dcstoyanov #: Alternatives?

Delete your iCustom call and the handle variable. Code it to compute the result.

 
dcstoyanov #: Alternatives?
int OnCalculate(...) {
//...
  static bool handle = false;
  if (handle == false) {
    indicator_handle = iCustom(SYMBOL, Period(), "Me\\indicator", SYMBOL);
    handle = true;
  }
//...
}
 
dcstoyanov #: Alternatives?
  1. Code fails if the symbol or TF is changed.
  2. Still calling yourself recursively. Don't! You must compute your buffers.
 
William Roeder #:
  1. Code fails if the symbol or TF is changed.

True. The static flag should be declared on the global scope and reset it in OnDenit(reason) depending on the value of the reason parameter. After iCustom(), could be useful to refresh the chart with ChartSetSymbolPeriod().

2. Still calling yourself recursively. Don't! You must compute your buffers.

It's not clear why one shouldn't create an instance of oneself. From my point of view, there is no downside, unless one doesn't enter an infinite loop or overflow the memory or, in general, uses a method in a way that is not recommended by the documentation.

Reason: