Smoothed Volume with MovingAverages.mqh is flickering (SOLVED)

 

It is just going off and on irregularly
Offnow

On


Off
Off

#property indicator_separate_window
#property indicator_buffers   2
#property indicator_plots     2

#property indicator_type1    DRAW_HISTOGRAM
#property indicator_width1    3
#property indicator_color1    clrDarkSlateBlue
#property indicator_label1    "SimpleVolume"

#property indicator_type2    DRAW_LINE
#property indicator_width2    3
#property indicator_color2    clrDarkGoldenrod
#property indicator_label2    "SMASmoothedVolume"

#include <MovingAverages.mqh>

input int               InpMAPeriod = 3;

double   VolumeBuffer[], SmoothedVolumeBuffer[];

//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int OnInit() {
//--- indicator buffers mapping
   
   SetIndexBuffer(0, SmoothedVolumeBuffer, INDICATOR_DATA);
   SetIndexBuffer(1, VolumeBuffer, INDICATOR_DATA);
   ArrayInitialize(VolumeBuffer, 0.0);
   ArrayInitialize(SmoothedVolumeBuffer, 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 limit = prev_calculated;
   if(prev_calculated == rates_total) {
      limit--;
   }
   for(int i = limit; i < rates_total; i++) {
      VolumeBuffer[i] = (double)tick_volume[i];
   }
   int    start = (int)MathMax((double)InpMAPeriod+1, (double)limit);
   int      on_calculate_result = LinearWeightedMAOnBuffer( rates_total,
                                                      prev_calculated,
                                                      start,
                                                      InpMAPeriod,
                                                      VolumeBuffer,
                                                      SmoothedVolumeBuffer);

//--- return value of prev_calculated for next call
   return(on_calculate_result);
}

This uses the buffer function with a source and destination buffer rates_total and prev_calculated. The normal version where it calculates every indexing field one by one works fine. Is this problem commonly known? Thanks for your help.

 
What does LinearWeightedMAOnBuffer return? I doubt that it is array index.
 
William Roeder #:
What does LinearWeightedMAOnBuffer return? I doubt that it is array index.
//+------------------------------------------------------------------+
//|  Linear weighted moving average on price array classic           |
//+------------------------------------------------------------------+
int LinearWeightedMAOnBuffer(const int rates_total,const int prev_calculated,const int begin,const int period,const double& price[],double& buffer[])
  {
//--- check period
   if(period<=1 || period>(rates_total-begin))
      return(0);
//--- save as_series flags
   bool as_series_price=ArrayGetAsSeries(price);
   bool as_series_buffer=ArrayGetAsSeries(buffer);

   ArraySetAsSeries(price,false);
   ArraySetAsSeries(buffer,false);
//--- calculate start position
   int i,start_position;

   if(prev_calculated<=period+begin+2)  // first calculation or number of bars was changed
     {
      //--- set empty value for first bars
      start_position=period+begin;

      for(i=0; i<start_position; i++)
         buffer[i]=0.0;
     }
   else
      start_position=prev_calculated-2;
//--- calculate first visible value
   double sum=0.0,lsum=0.0;
   int    l,weight=0;

   for(i=start_position-period,l=1; i<start_position; i++,l++)
     {
      sum   +=price[i]*l;
      lsum  +=price[i];
      weight+=l;
     }
   buffer[start_position-1]=sum/weight;
//--- main loop
   for(i=start_position; i<rates_total; i++)
     {
      sum      =sum-lsum+price[i]*period;
      lsum     =lsum-price[i-period]+price[i];
      buffer[i]=sum/weight;
     }
//--- restore as_series flags
   ArraySetAsSeries(price,as_series_price);
   ArraySetAsSeries(buffer,as_series_buffer);
//---
   return(rates_total);
  }

It returns rates_total. Also what I forgot to mention is that the problem persists with every MA method but only if they have src and dst buffer.

 

The begin parameter should be constant.

   int      on_calculate_result = LinearWeightedMAOnBuffer( rates_total,
                                                      prev_calculated,
                                                      InpMAPeriod+1,
                                                      InpMAPeriod,
                                                      VolumeBuffer,
                                                      SmoothedVolumeBuffer);
 
Ernst Van Der Merwe #:

The begin parameter should be constant.

Although I appreciate your advice, it would be more satisfying to have a source to back this up. I think the begin parameter is the only way not to calculate everything all over again. Because I am used to doing this:

int limit = prev_calculated;
if(prev_calculated == rates_total) limit--;

Copybuffer(xyzHandle,0,limit,rates_total,xyzBuffer);

It seemed right to use it similarly in the MAOnBuffer function, but may I also point you to the line

   int    start = (int)MathMax((double)InpMAPeriod+1, (double)limit);

where the begin at Period+1 has been taken into account in the start variable depending on which one is greater?


Update:

William Roeder #:
What does LinearWeightedMAOnBuffer return? I doubt that it is array index.

Although LinearWeightedMAOnBuffer does in fact return rates_total according to its function code, it seems not to return it every time or something. So instead of overcomplicating things and relying on it to do that, I just put the normal rates_total back in the OnCalculate return path so it is sure to return it everytime. And it does work now without flickering. But your advice lead me to try it out though, so thanks.


Something is still not right because the values drop to zero and stay there but I will find out why.

 
//+------------------------------------------------------------------+
//| Custom indicator iteration function                              |
//+------------------------------------------------------------------+
int OnCalculate(const int rates_total,
                const int prev_calculated,
                const int begin,
                const double &price[])
  {
//---
   Print(LinearWeightedMAOnBuffer(rates_total,prev_calculated,0,Period,price,MABuffer));
//--- return value of prev_calculated for next call
   return(rates_total);
  }
//+------------------------------------------------------------------+
//|  Linear weighted moving average on price array classic           |
//+------------------------------------------------------------------+
int LinearWeightedMAOnBuffer(const int rates_total,const int prev_calculated,const int begin,const int period,const double& price[],double& buffer[])
  {
//--- check period
   if(period<=1 || period>(rates_total-begin))
      return(0);
//--- save as_series flags
   bool as_series_price=ArrayGetAsSeries(price);
   bool as_series_buffer=ArrayGetAsSeries(buf
fer);

   ArraySetAsSeries(price,false);
   ArraySetAsSeries(buffer,false);
//--- calculate start position
   int i,start_position,count;

   if(prev_calculated<=period+begin+2)  // first calculation or number of bars was changed
     {
      //--- set empty value for first bars
      start_position=period+begin;

      for(i=0; i<start_position; i++)
         buffer[i]=0.0;
     }
   else
      start_position=prev_calculated-2;
//--- calculate first visible value
   double sum=0.0,lsum=0.0;
   int    l,weight=0;

   for(i=start_position-period,l=1; i<start_position; i++,l++)
     {
      sum   +=price[i]*l;
      lsum  +=price[i];
      weight+=l;
     }
   buffer[start_position-1]=sum/weight;
//--- main loop
   for(i=start_position,count=0; i<rates_total; i++,count++)
     {
      sum      =sum-lsum+price[i]*period;
      lsum     =lsum-price[i-period]+price[i];
      buffer[i]=sum/weight;
     }
//--- restore as_series flags
   ArraySetAsSeries(price,as_series_price);
   ArraySetAsSeries(buffer,as_series_buffer);
//---
   return(count);
  }
//+------------------------------------------------------------------+

2021.12.30 08:42:10.782 MA (EURUSD,H1)  100254
2021.12.30 08:42:15.221 MA (EURUSD,H1)  2
2021.12.30 08:42:15.314 MA (EURUSD,H1)  2
2021.12.30 08:42:27.537 MA (EURUSD,H1)  2
2021.12.30 08:42:27.624 MA (EURUSD,H1)  2

It's what this block is for:


   if(prev_calculated<=period+begin+2)  // first calculation or number of bars was changed
     {
      //--- set empty value for first bars
      start_position=period+begin;

      for(i=0; i<start_position; i++)
         buffer[i]=0.0;
     }
 
Ernst Van Der Merwe #:

It's what this block is for:

 if(prev_calculated<=period+begin+2)  // first calculation or number of bars was changed
     {
      //--- set empty value for first bars
      start_position=period+begin;

      for(i=0; i<start_position; i++)
         buffer[i]=0.0;
     }

You are right, I unnecessarily fumbled around with the begin. It is only required when you put it on another indicator buffer that has also a period I guess.

Then inside LiniarWeightedMAOnBuffer() there is this preliminary condition right in the beginning:

//--- check period
   if(period<=1 || period>(rates_total-begin))
      return(0);

I am not sure what it is good for but it returned zero instead of rates_total in my case. Now it seems to work.

As a little bonus if you are interested in a unified MAOnBuffer mqh where you can input the ENUM_MA_METHOD:

Happy New Year.

Files:
 

Wasn't aware of the function without the weight_sum parameter until it was posted above, as I've added a second parameter to avoid recalculating old prices every time.

int LinearWeightedMAOnBuffer(const int rates_total,
                             const int prev_calculated,
                             const int begin,
                             const int period,
                             const double& price[],
                             double& buffer[],
                             double& sum_price,
                             int& sum_weight)
  {
//--- check new bar
   bool newBar=rates_total!=prev_calculated;
   int i,k,limit=(newBar)?prev_calculated:prev_calculated-1;
//--- save as_series flags
   bool as_series_price=ArrayGetAsSeries(price);
   bool as_series_buffer=ArrayGetAsSeries(buffer);
   if(as_series_price) ArraySetAsSeries(price,false);
   if(as_series_buffer) ArraySetAsSeries(buffer,false);
//--- first calculation or number of bars has changed
   if(prev_calculated==0) {
//--- check for data
      if(period<=1 || (limit=begin+period-1)>=rates_total) 
         return(0);
      for(i=0;i<limit;i++)
         buffer[i]=EMPTY_VALUE;
//--- calculate first visible value
      sum_price=sum_weight=0;
      for(i=begin,k=1;i<=limit;i++,k++) {
         sum_price+=k*price[i];
         sum_weight+=k; }
//--- set first value
      buffer[limit++]=sum_price/sum_weight; }
//--- main loop
   for(i=limit;i<rates_total;i++) {
//--- get price sum on new bar
      if(newBar) {
         sum_price=0;
         for(k=1;k<period;k++) 
            sum_price+=price[i-period+k]*k; }
//--- get last bar's value
      buffer[i]=(sum_price+price[i]*period)/sum_weight; }
//--- restore as_series flags
   if(as_series_price) ArraySetAsSeries(price,true);
   if(as_series_buffer) ArraySetAsSeries(buffer,true);
//---
   return(rates_total);
  }

Happy new year to you too.

Files:
VolumeMA.mq5  6 kb