General conditions on the indexes of an indicator (e.g. MA) with a minimum required bars present before the calculation can be performed

 

The question is probably a stupid one and I'm missing something, however, I've already asked it in a comment section to another post and haven't got an answer yet.
So I create this topic.

OK. Let's consider this article. One reads:


rates_total

    [in]  Size of the price[] array or input series available to the indicator for calculation. In the second function type, the parameter value corresponds to the number of bars on the chart it is launched at.


and then below the following fragment of code:

//--- initial position for calculations
   int StartCalcPosition=(IntPeriod-1)+begin;
//---- if calculation data is insufficient
   if(rates_total<StartCalcPosition)
      return(0);  // exit with a zero value - the indicator is not calculated

Hm. It's strange because it's obvious that the necessary number of bars to calculate the SM is IntPeriod bars. OK. Maybe I don't understand something because I see the same approach everywhere. But let's check it on a test data.  Here is the entire OnCalculate() function:

int OnCalculate(const int rates_total,     // price[] array size 
                const int prev_calculated, // number of previously handled bars
                const int begin,           // where significant data start from 
                const double &price[])     // value array for handling
  {
//--- initial position for calculations
   int StartCalcPosition=(IntPeriod-1)+begin;
//---- if calculation data is insufficient
   if(rates_total<StartCalcPosition)
      return(0);  // exit with a zero value - the indicator is not calculated
//--- correct draw begin
   if(begin>0)
      PlotIndexSetInteger(0,PLOT_DRAW_BEGIN,StartCalcPosition+(IntPeriod-1));
//--- start calculations, define the starting position
   int pos=prev_calculated-1;
   if(pos<StartCalcPosition)
      pos=begin+IntPeriod;
//--- main calculation loop
   for(int i=pos;i<rates_total && !IsStopped();i++)
      MomentumBuffer[i]=price[i]*100/price[i-IntPeriod];
//--- OnCalculate execution is complete. Return the new prev_calculated value for the subsequent call
   return(rates_total);
  }


Suppose we've launched our indicator (for simplicity let IntPeriod == 3) just now and we have zero bars so far. So

1. rates_total == 0, prev_calculated == 0, begin == 0

So the first condition is evaluated to true and we return 0. Now suppose we have one bar. So

2. rates_total == 1, prev_calculated == 0, begin == 0

The first condition again is true and we return 0. Then we have two bars. So

3. rates_total == 2, prev_calculated == 0, begin == 0

But now the first condition is false because

if(rates_total<StartCalcPosition)// false as rates_total == 2, which is not less than StartCalcPosition == 2

OK. We fall through it and meet the following:

int pos=prev_calculated-1;//pos == -1
   if(pos<StartCalcPosition)//true
      pos=begin+IntPeriod;//pos = 2

Then:

for(int i=pos;i<rates_total && !IsStopped();i++)//NOT evaluated because pos == 2 == rates_total

So we haven't calculate anything and return 2 (rates_total)

OK. Now we have 3 bars available. So

3. rates_total == 3, prev_calculated == 2, begin == 0


So we have

 int pos=prev_calculated-1;//pos == 1
   if(pos<StartCalcPosition)//true as 1 < 2
      pos=begin+IntPeriod;//pos == 3

And the main loop:

for(int i=pos;i<rates_total && !IsStopped();i++)//Nothing happens again as 3 < 3 is false
      MomentumBuffer[i]=price[i]*100/price[i-IntPeriod];
//--- OnCalculate execution is complete. Return the new prev_calculated value for the subsequent call

We haven't calculate anything again but the number of available bars is sufficient to calculate the SM. But we return 3 this time.

What's going on here ? We return the number which will be the value of the prev_calculated parameter next call, but we've not actually calculated anything yet.

Why just not to change the first condition from:

if(rates_total<StartCalcPosition)

to:

if(rates_total<IntPeriod)


Where do I wrong ?
Documentation on MQL5: Constants, Enumerations and Structures / Chart Constants / Chart Properties
Documentation on MQL5: Constants, Enumerations and Structures / Chart Constants / Chart Properties
  • www.mql5.com
Chart Properties - Chart Constants - Constants, Enumerations and Structures - MQL5 Reference - Reference on algorithmic/automated trading language for MetaTrader 5
 
The ' begin ' parameter is well described in the Custom Indicators documentation.
Documentation on MQL5: Custom Indicators
Documentation on MQL5: Custom Indicators
  • www.mql5.com
Custom Indicators - MQL5 Reference - Reference on algorithmic/automated trading language for MetaTrader 5
 
Vladimir Karputov #:
The ' begin ' parameter is well described in the Custom Indicators documentation.
The begin parameter is irrelevant here. Without loss of generality we can let it be zero.
 
LRDPRDX # :
The begin parameter is irrelevant here. Without loss of generality we can let it be zero.

Forget the previous example. I suggest using the following standard:

//+------------------------------------------------------------------+
//|                                               Temp Indicator.mq5 |
//|                                  Copyright 2022, MetaQuotes Ltd. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2022, MetaQuotes Ltd."
#property link      "https://www.mql5.com"
#property version   "1.00"
#property indicator_chart_window
#property indicator_buffers 1
#property indicator_plots   1
//--- plot Highest
#property indicator_label1  "Highest"
#property indicator_type1   DRAW_LINE
#property indicator_color1  clrMediumSeaGreen
#property indicator_style1  STYLE_SOLID
#property indicator_width1  1
//--- input parameters
input int      Input1=9;
//--- indicator buffers
double   HighestBuffer[];
//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- indicator buffers mapping
   SetIndexBuffer(0,HighestBuffer,INDICATOR_DATA);
//---
   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[])
  {
   if(rates_total<3)
      return(0);
//---
   int limit=prev_calculated-1;
   if(prev_calculated==0)
      limit=0;
//--- main loop
   for(int i=limit; i<rates_total; i++)
     {
      HighestBuffer[i]=high[i];
     }
//--- return value of prev_calculated for next call
   return(rates_total);
  }
//+------------------------------------------------------------------+

Result:

Files:
 
Vladimir Karputov #:

Forget the previous example. I suggest using the following standard:

Result:

Agreed ! Thank you !

More over, it seems that in the example I provided there is an illegal index access : see what happens if

rates_total == 3, prev_calculated == 3, begin == 0

Then :

int pos=prev_calculated-1;//pos == 2
   if(pos<StartCalcPosition)//false
      pos=begin+IntPeriod;
//--- main calculation loop
   for(int i=pos;i<rates_total && !IsStopped();i++)
      MomentumBuffer[i]=price[i]*100/price[i-IntPeriod];//at the first iteration (i == 2) there is an index access price[2 - 3]. Very bad !
 
Vladimir Karputov #:

Forget the previous example. I suggest using the following standard:

Result:

But what if your indicator would require previous prices for calculation (like MA) Should the HighestBuffer store the indicator values from 0 or from 2 ? I mean should it be (suppose it is now the average over three prices) :


HighestBuffer[0] = (high[0] + high[1] + high[2]) / 3

or

HighestBuffer[2] = (high[0] + high[1] + high[2]) / 3

?

 
LRDPRDX # :

But what if your indicator would require previous prices for calculation (like MA) Should the HighestBuffer store the indicator values from 0 or from 2 ? I mean should it be (suppose it is now the average over three prices) :


or

?

If you need to compare the current and PREVIOUS bar, you do the following:

//+------------------------------------------------------------------+
//|                                               Temp Indicator.mq5 |
//|                                  Copyright 2022, MetaQuotes Ltd. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2022, MetaQuotes Ltd."
#property link      "https://www.mql5.com"
#property version   "1.00"
#property indicator_chart_window
#property indicator_buffers 1
#property indicator_plots   1
//--- plot Highest
#property indicator_label1  "Highest"
#property indicator_type1   DRAW_LINE
#property indicator_color1  clrMediumSeaGreen
#property indicator_style1  STYLE_SOLID
#property indicator_width1  1
//--- input parameters
input int      Input1=9;
//--- indicator buffers
double   HighestBuffer[];
//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- indicator buffers mapping
   SetIndexBuffer(0,HighestBuffer,INDICATOR_DATA);
//---
   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[])
  {
   if(rates_total<3)
      return(0);
//---
   int limit=prev_calculated-1;
   if(prev_calculated==0)
     {
      HighestBuffer[0]=0.0;
      limit=1;
     }
//--- main loop
   for(int i=limit; i<rates_total; i++)
     {
      if(close[i-1]>close[i])
         HighestBuffer[i]=close[i-1];
      else
         HighestBuffer[i]=close[i];
     }
//--- return value of prev_calculated for next call
   return(rates_total);
  }
//+------------------------------------------------------------------+
Files:
 
Vladimir Karputov #:

If you need to compare the current and PREVIOUS bar, you do the following:

I see, You assign the first values of the HighestBuffer to zero, as I thought. Thank you for your support.
 
Vladimir Karputov #:

If you need to compare the current and PREVIOUS bar, you do the following:

Actually, it seems you're wrong anyway.

Suppose you have the following values of arguments :

rates_total == 4, prev_calculated == 1

then according to your code:


int limit=prev_calculated-1;    //limit == 0
   if(prev_calculated==0)       //false
     {
      HighestBuffer[0]=0.0;
      limit=1;
     }
//--- main loop
   for(int i=limit; i<rates_total; i++) //the first iteration with i == 0
     {
      if(close[i-1]>close[i])           //BOOM ! illegal index access (-1)
         HighestBuffer[i]=close[i-1];
      else
         HighestBuffer[i]=close[i];
     }
//--- return value of prev_calculated for next call
   return(rates_total);


I think the condition

if(prev_calculated==0)


Should turn into


if(limit < 1)


It makes sense as you start the "real" calculation from the second index (1).

 

If you are working not with the current bar (bar 'i'), but with bar 'i-1' then you should shift the index:

int limit=prev_calculated-1;    //limit == 0
   if(prev_calculated==0)       //false
     {
      HighestBuffer[0]=0.0;
      limit=1;
     }


If you are working not with the current bar (bar 'i'), but with bar 'i-2' then you should shift the index:

int limit=prev_calculated-1;    //limit == 0
   if(prev_calculated==0)       //false
     {
      HighestBuffer[0]=0.0;
      HighestBuffer[1]=0.0;
      limit=2 ;
     }
 
Vladimir Karputov #:

If you are working not with the current bar (bar 'i'), but with bar 'i-1' then you should shift the index:


If you are working not with the current bar (bar 'i'), but with bar 'i-2' then you should shift the index:


OK, we're losing the thread here, I think :)

Suppose my indicator at the i-th bar depends on the current bar (i) and the previous bar (i-1). Period.

In other words

    MyInd[ i ] = Func( price[ i ], price[ i - 1 ] )


Then I should start the calculation of Func with the bar 1 (assuming they're numbered from 0), and let   MyInd[ 0 ]    be 0.0. Eventually,

    MyInd[ i ] = 0.0, if i == 0

    MyInd[ i ] = Func( price[ i ], price[ i - 1 ] ), if i > 0


That's all I need. From this definition this code should be used (I think) :

if( rates_total < 2 )
      return( 0 );
//---
int limit=prev_calculated-1;
if( limit < 1)
{
   HighestBuffer[0] = 0.0;
   limit = 1;
}
//--- main loop
for( int i = limit; i < rates_total; i++)
   HighestBuffer[i] = Func( price[ i ], price[ i-1 ] );
//--- return value of prev_calculated for next call
return(rates_total);
Reason: