General conditions on the indexes of an indicator (e.g. MA) with a minimum required bars present before the calculation can be performed
- www.mql5.com
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.
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:
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 !
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
?
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); } //+------------------------------------------------------------------+
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 ; }
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);
- Free trading apps
- Over 8,000 signals for copying
- Economic news for exploring financial markets
You agree to website policy and terms of use
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:
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:
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
OK. We fall through it and meet the following:
Then:
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 == 0So we have
And the main loop:
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:
to:
if(rates_total<IntPeriod)
Where do I wrong ?