Need help with custom exponential moving average on array

 

Hello, I'm trying to create a exponential moving average on an array. I seem to get the correct values every time I refresh the chart, but not when I just let it run. The buyEMA returns 0.0 or an insanely high value, once I let it run. Below is my code snippet. I checked the buying_volume array, it never returns 0.0. The indicator seems to be working fine initially, but not when receiving new bars.. the buyEMA array is what im plotting on the chart. 

What do I need to change so that, when running, it updates, and doesn't return 0.0 on new bars?

//------inside oncaculate function. using Calculations based on the current timeframe timeseries.
CalculateBUYEMA(rates_total,prev_calculated,0,buying_volume);

//------seperate 
void CalculateBUYEMA(int rates_total,int prev_calculated,int begin,const double &price[]){
   int    i,start;
   double SmoothFactor=2.0/(1.0+cumulation_length);
//first calculation or number of bars was changed
   if(prev_calculated==0){
      start=cumulation_length+begin;
      buyEMA[begin]=price[begin];
      for(i=begin+1; i<start; i++)
         buyEMA[i]=price[i]*SmoothFactor+buyEMA[i-1]*(1.0-SmoothFactor);
     }
   else
      start=prev_calculated-1;
//main loop
   for(i=start; i<rates_total && !IsStopped(); i++)
      buyEMA[i]=price[i]*SmoothFactor+buyEMA[i-1]*(1.0-SmoothFactor);
  }

thank you for your time. 

Moving Average - Trend Indicators - Technical Indicators - Price Charts, Technical and Fundamental Analysis - MetaTrader 5 Help
Moving Average - Trend Indicators - Technical Indicators - Price Charts, Technical and Fundamental Analysis - MetaTrader 5 Help
  • www.metatrader5.com
The Moving Average Technical Indicator shows the mean instrument price value for a certain period of time. When one calculates the moving average...
 
Elmer013:

Hello, I'm trying to create a exponential moving average on an array. I seem to get the correct values every time I refresh the chart, but not when I just let it run. The buyEMA returns 0.0 or an insanely high value, once I let it run. Below is my code snippet. I checked the buying_volume array, it never returns 0.0. The indicator seems to be working fine initially, but not when receiving new bars.. the buyEMA array is what im plotting on the chart. 

What do I need to change so that, when running, it updates, and doesn't return 0.0 on new bars?

thank you for your time. 

hey

when i run it on the tester like this its okay . also i don't think prev_calculated is an index but an amount 

#property version   "1.00"
#property indicator_chart_window
#property indicator_buffers 1
#property indicator_plots   1
//--- plot ema
#property indicator_label1  "ema"
#property indicator_type1   DRAW_LINE
#property indicator_color1  clrRed
#property indicator_style1  STYLE_SOLID
#property indicator_width1  1
input int cumulation_length=14;
//--- indicator buffers
double         buyEMA[];

int OnInit()
  {
//--- indicator buffers mapping
   SetIndexBuffer(0,buyEMA,INDICATOR_DATA);
   
//---
   return(INIT_SUCCEEDED);
  }
//------inside oncaculate function. using Calculations based on the current timeframe timeseries.


//------seperate 
void CalculateBUYEMA(int rates_total,int prev_calculated,int begin,const double &price[]){
   int    i,start;
   double SmoothFactor=2.0/(1.0+cumulation_length);
//first calculation or number of bars was changed
   if(prev_calculated==0){
      start=cumulation_length+begin;
      buyEMA[begin]=price[begin];
      for(i=begin+1; i<start; i++)
         buyEMA[i]=price[i]*SmoothFactor+buyEMA[i-1]*(1.0-SmoothFactor);
     }
   else
      start=prev_calculated-1;
//main loop
   for(i=start; i<rates_total && !IsStopped(); i++)
      buyEMA[i]=price[i]*SmoothFactor+buyEMA[i-1]*(1.0-SmoothFactor);
  }
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[])
  {
  CalculateBUYEMA(rates_total,prev_calculated,0,close); 
   return(rates_total);
  }
 

It is difficult to debug your code when you don't share all of it.

For example- it could be that your problem is in the calculation of buying_volume and not in the code you've shared here.


Share all of your code mate.

 
Lorentzos Roussos #:

hey

when i run it on the tester like this its okay . also i don't think prev_calculated is an index but an amount 

AMI289 #:

It is difficult to debug your code when you don't share all of it.

For example- it could be that your problem is in the calculation of buying_volume and not in the code you've shared here.


Share all of your code mate.

Thank you for your time and response.

When I try to run the moving average using the close array it does look like its suppose to.. thanks for that. below is how I'm calculating the buying and selling volume values. I ArraySetAsSeries the close, high, open, low and tickvolume. And resized the upper/lower_wick, Cspread, body, percent_upper/lower/body and buying/selling volume by the rates_total.

for(int i=0; i<rates_total; i++){                     
      upper_wick[i] = close[i]>open[i] ? high[i]-close[i]  :  high[i]-open[i];
      lower_wick[i] = close[i]>open[i] ? open[i]-low[i]    :  close[i]-low[i];      
      Cspread[i] = high[i]-low[i];
      body_length[i] = Cspread[i] - (upper_wick[i] + lower_wick[i]);
      
      percent_upper_wick[i]   = upper_wick[i]   /  Cspread[i];
      percent_lower_wick[i]   = lower_wick[i]   /  Cspread[i];
      percent_body_length[i]  = body_length[i]  /  Cspread[i];
    
      buying_volume[i]  = close[i]>open[i] ? (percent_body_length[i] + (percent_upper_wick[i] + percent_lower_wick[i])/2)*tick_volume[i] : ((percent_upper_wick[i] + percent_lower_wick[i])/2) * tick_volume[i];
      selling_volume[i] = close[i]<open[i] ? (percent_body_length[i] + (percent_upper_wick[i] + percent_lower_wick[i])/2)*tick_volume[i] : ((percent_upper_wick[i] + percent_lower_wick[i])/2) * tick_volume[i];  
      }
 

Again mate- share all of your code.

It is really difficult to debug it this way.

For example- you say that you SetAsSeries the close, high, open, low and tickvolume.

What about buying_volume? is this SetAsSeries or not?

What about buyEMA? is this SetAsSeries or not?

Share all of your code.

It will help others help you.

 
AMI289 #:

Again mate- share all of your code.

It is really difficult to debug it this way.

For example- you say that you SetAsSeries the close, high, open, low and tickvolume.

What about buying_volume? is this SetAsSeries or not?

What about buyEMA? is this SetAsSeries or not?

Share all of your code.

It will help others help you.

Again, thanks for your time, here is my full code.... I found that there is a build-in library called MovingAverages. Which is why I decided to use it for ease of use. The code inside that library is pretty much the same as my original for calculating the moving average. It now seems like the indicator isnt drawing the last 200 or so bars.

How would I make it so that it does draw the last bars? Thanks in advance. 

//+------------------------------------------------------------------+
//|                                             CumulativeVolume.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"
#include <MovingAverages.mqh>
//#property indicator_chart_window
#property indicator_separate_window
#property indicator_buffers 2
#property indicator_plots   2
//--- plot cumulative_buying_volume
#property indicator_label1  "buyEMA"
#property indicator_type1   DRAW_LINE
#property indicator_color1  clrGreen
#property indicator_style1  STYLE_SOLID
#property indicator_width1  1
#property indicator_level1  0
//--- plot cumulative_selling_volume
#property indicator_label2  "sellEMA"
#property indicator_type2   DRAW_LINE
#property indicator_color2  clrRed
#property indicator_style2  STYLE_SOLID
#property indicator_width2  1
//--- input parameters
input int      cumulation_length = 12;
//--- indicator buffers
double         upper_wick[], lower_wick[], 
               Cspread[], percent_upper_wick[], percent_lower_wick[], percent_body_length[], body_length[],
               buying_volume[], selling_volume[], cumulative_volume_delta[];
double         buyEMA[], sellEMA[];

//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int OnInit(){
//--- indicator buffers mapping
   SetIndexBuffer(0,buyEMA,INDICATOR_DATA);
   SetIndexBuffer(1,sellEMA,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[]){                         
//set price data arrays as series                
   ArraySetAsSeries(close,true);
   ArraySetAsSeries(high,true);
   ArraySetAsSeries(open,true);
   ArraySetAsSeries(low,true);
   ArraySetAsSeries(tick_volume,true);  
//resize arrays  
   ArrayResize(upper_wick,rates_total);  
   ArrayResize(lower_wick,rates_total); 
   ArrayResize(Cspread,rates_total); 
   ArrayResize(body_length,rates_total);
   ArrayResize(percent_upper_wick,rates_total);  
   ArrayResize(percent_lower_wick,rates_total); 
   ArrayResize(percent_body_length,rates_total);   
   ArrayResize(buying_volume,rates_total);
   ArrayResize(selling_volume,rates_total);  
   ArrayResize(buyEMA,rates_total);
   ArrayResize(sellEMA,rates_total);
//for loop to calculate the buying and selling volume val's
for(int i=0; i<rates_total; i++){                     
      upper_wick[i] = close[i]>open[i] ? high[i]-close[i]  :  high[i]-open[i];
      lower_wick[i] = close[i]>open[i] ? open[i]-low[i]    :  close[i]-low[i];      
      Cspread[i] = high[i]-low[i];
      body_length[i] = Cspread[i] - (upper_wick[i] + lower_wick[i]);
      
      percent_upper_wick[i]   = upper_wick[i]   /  Cspread[i];
      percent_lower_wick[i]   = lower_wick[i]   /  Cspread[i];
      percent_body_length[i]  = body_length[i]  /  Cspread[i];
    
      buying_volume[i]  = close[i]>open[i] ? (percent_body_length[i] + (percent_upper_wick[i] + percent_lower_wick[i])/2)*tick_volume[i] : ((percent_upper_wick[i] + percent_lower_wick[i])/2) * tick_volume[i];
      selling_volume[i] = close[i]<open[i] ? (percent_body_length[i] + (percent_upper_wick[i] + percent_lower_wick[i])/2)*tick_volume[i] : ((percent_upper_wick[i] + percent_lower_wick[i])/2) * tick_volume[i];  
   }
//EMA on buffer
   ExponentialMAOnBuffer(rates_total,prev_calculated,0,cumulation_length,buying_volume,buyEMA);      
   ExponentialMAOnBuffer(rates_total,prev_calculated,0,cumulation_length,selling_volume,sellEMA);
   return(rates_total);
}
 

Just a couple of things I've noticed-

1) You shouldn't resize buyEMA and sellEMA as they are both buffers, so they are of dynamic size and resize automatically.

2) Every tick your are calculating all of the values again, which is really inefficient.

3) The use of arrays of upper_wick, lower_wick, Cspread, body_length, percent_upper_wick, percent_lower_wick and percent_body_length is redundant since you don't need to save their values, as only current values being used in calculations.


Regardless of the above 3 points-

When you've used your function you've had problem with last bar only,

And not that you've switched to using ExponentialMAOnBuffer you have problem with latest 200 bars?

 
AMI289 #:

Just a couple of things I've noticed-

1) You shouldn't resize buyEMA and sellEMA as they are both buffers, so they are of dynamic size and resize automatically.

2) Every tick your are calculating all of the values again, which is really inefficient.

3) The use of arrays of upper_wick, lower_wick, Cspread, body_length, percent_upper_wick, percent_lower_wick and percent_body_length is redundant since you don't need to save their values, as only current values being used in calculations.


Regardless of the above 3 points-

When you've used your function you've had problem with last bar only,

And not that you've switched to using ExponentialMAOnBuffer you have problem with latest 200 bars?

Hey, thank you for your response. Thanks for letting me know those 3 points. I've made some adjustments, commented below.

1) I Removed the ArrayResize for both functions.
2) I should have seen that one coming, I was planning on adding that later. Added per bar calculations now.
3) I don't see what I did wrong there, could you give me an example? I thought using the current way was necessary to calculate past values..

int ExponentialMAOnBuffer(const int rates_total,const int prev_calculated,const int begin,const int period,const double& price[],double& buffer[])


yes. 44 bars now, to be exact. Used to be around 200.. The buyEMA and sellEMA =  -9223372036854775.808 from bar 44 till 0.


Thank you for your time, it is much appreciated.


 

I think you needed to set the buyEMA , sellEMA the buying volume , selling volume , as buffers too and as series as well .

The code below uses a custom ema function -which also works on mt4- and the excellent observations of @AMI289 👍 👍 👍

#property version   "1.00"
#property indicator_separate_window
#property indicator_buffers 4
#property indicator_plots   2
//--- plot cumulative_buying_volume
#property indicator_label1  "buyEMA"
#property indicator_type1   DRAW_LINE
#property indicator_color1  clrGreen
#property indicator_style1  STYLE_SOLID
#property indicator_width1  1
#property indicator_level1  0
//--- plot cumulative_selling_volume
#property indicator_label2  "sellEMA"
#property indicator_type2   DRAW_LINE
#property indicator_color2  clrRed
#property indicator_style2  STYLE_SOLID
#property indicator_width2  1
//--- input parameters
input int      cumulation_length = 12;
//--- indicator buffers
double         upper_wick,lower_wick,Cspread,percent_upper_wick,percent_lower_wick,percent_body_length,body_length;
double         buying_volume[],selling_volume[];
double         buyEMA[], sellEMA[];

//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int OnInit(){
//--- indicator buffers mapping
   SetIndexBuffer(0,buyEMA,INDICATOR_DATA);
   SetIndexBuffer(1,sellEMA,INDICATOR_DATA);
   SetIndexBuffer(2,buying_volume,INDICATOR_DATA);
   PlotIndexSetInteger(2,PLOT_DRAW_TYPE,DRAW_NONE);
   SetIndexBuffer(3,selling_volume,INDICATOR_DATA);   
   PlotIndexSetInteger(3,PLOT_DRAW_TYPE,DRAW_NONE);
   reset();
   return(INIT_SUCCEEDED);
  }
void reset(){
  ArraySetAsSeries(buyEMA,true);
  ArraySetAsSeries(sellEMA,true);
  ArraySetAsSeries(buying_volume,true);
  ArraySetAsSeries(selling_volume,true);
  ArrayFill(buyEMA,0,ArraySize(buyEMA),0.0);
  ArrayFill(sellEMA,0,ArraySize(sellEMA),0.0);
  ArrayFill(buying_volume,0,ArraySize(buying_volume),0.0);
  ArrayFill(selling_volume,0,ArraySize(selling_volume),0.0);
  }
//+------------------------------------------------------------------+
//| 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[]){      
       ArraySetAsSeries(close,true);
       ArraySetAsSeries(high,true);
       ArraySetAsSeries(open,true);
       ArraySetAsSeries(low,true);
       ArraySetAsSeries(tick_volume,true);                        
      int calcs=rates_total-prev_calculated;
      int from=calcs;
      if(calcs>cumulation_length){
      from=rates_total-1-cumulation_length*2;
      reset();
      }            
//for loop to calculate the buying and selling volume val's
for(int i=from; i>=0; i--){                     
      upper_wick = close[i]>open[i] ? high[i]-close[i]  :  high[i]-open[i];
      lower_wick = close[i]>open[i] ? open[i]-low[i]    :  close[i]-low[i];      
      Cspread = high[i]-low[i];
      body_length = Cspread - (upper_wick + lower_wick);
      
      percent_upper_wick   = upper_wick   /  Cspread;
      percent_lower_wick   = lower_wick   /  Cspread;
      percent_body_length  = body_length  /  Cspread;
    
      buying_volume[i]  = close[i]>open[i] ? (percent_body_length + (percent_upper_wick + percent_lower_wick)/2)*tick_volume[i] : ((percent_upper_wick + percent_lower_wick)/2) * tick_volume[i];
      selling_volume[i] = close[i]<open[i] ? (percent_body_length + (percent_upper_wick + percent_lower_wick)/2)*tick_volume[i] : ((percent_upper_wick + percent_lower_wick)/2) * tick_volume[i];  
      
   buyEMA[i]=maOnArray(buying_volume,i,cumulation_length,MODE_EMA,true,(int)MathMin(1,prev_calculated),buyEMA[i+1]);
   sellEMA[i]=maOnArray(selling_volume,i,cumulation_length,MODE_EMA,true,(int)MathMin(1,prev_calculated),sellEMA[i+1]);
   }

   return(rates_total);
}

/* ma of array */
double maOnArray(const double &which_array[],
                    int where,//start from where 
                    int period,
         ENUM_MA_METHOD mode,
                   bool is_series,
                    int calc_index,//this measures which calculation this is starting from 0
                 double previous_ma){//and this receives the previous value 
double result=0.0;
//starting point 
  int from=0;
  int step=0;
  int until=where;
  if(!is_series){
  from=where-period+1;
  step=1;
  }
  else{
  from=where+period-1;
  step=-1;
  }
if(until>=0&&until<ArraySize(which_array)&&from>=0&&from<ArraySize(which_array)){
  //sma or first ema or first smma 
    if(mode==MODE_SMA||(mode==MODE_EMA&&calc_index==0)||(mode==MODE_SMMA&&calc_index==0)){
    int i=from-step;
    while(i!=until)
      {
      i+=step;
      result+=which_array[i];
      }
    result/=((double)period);
    }
  //ema non first 
    else if(mode==MODE_EMA){
    double w=2.00/((double)period+1.0);
    result=which_array[until]*w+previous_ma*(1-w);
    }  
  //smma non first 
    else if(mode==MODE_SMMA){
    result=((previous_ma*((double)period))-previous_ma+which_array[until])/((double)period);
    }
  //lwma
    else if(mode==MODE_LWMA){
    int i=from-step;
    double divider=0.0;
    int weight=0.0;
    while(i!=until)
      {
      i+=step;
      weight+=1.0;
      result+=which_array[i]*weight;
      divider+=weight;
      }
    result/=divider;    
    }
  }
return(result);
}
 

Although Lorentzos Roussos did an awesome job providing his code modifications,

I don't think it will solve your problem.

I've noticed some cases where a candle's high and low were the same (perhaps because no ticks, or bad history data).

Anyway, I've re-written your code in a more clean and efficient way, and added some notes to explain some of the things I've did,

And I've also addressed the issue I've wrote above, that I believe that was the problem you were having.


Enjoy-

//+------------------------------------------------------------------+
//|                                             CumulativeVolume.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"
#include <MovingAverages.mqh>
//#property indicator_chart_window
#property indicator_separate_window
#property indicator_buffers 4
#property indicator_plots   2
//--- plot cumulative_buying_volume
#property indicator_label1  "buyEMA"
#property indicator_type1   DRAW_LINE
#property indicator_color1  clrGreen
#property indicator_style1  STYLE_SOLID
#property indicator_width1  1
#property indicator_level1  0
//--- plot cumulative_selling_volume
#property indicator_label2  "sellEMA"
#property indicator_type2   DRAW_LINE
#property indicator_color2  clrRed
#property indicator_style2  STYLE_SOLID
#property indicator_width2  1
//--- input parameters
input int      cumulation_length = 12;
//--- indicator buffers
double         buyEMA[], sellEMA[], buying_volume[], selling_volume[];

//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int OnInit(){
//--- indicator buffers mapping
   SetIndexBuffer(0,buyEMA,INDICATOR_DATA);
   SetIndexBuffer(1,sellEMA,INDICATOR_DATA);
   SetIndexBuffer(2,buying_volume,INDICATOR_CALCULATIONS);
   SetIndexBuffer(3,selling_volume,INDICATOR_CALCULATIONS);
   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[]){                         

// Check if there is enough data for calculation
if(rates_total < cumulation_length) return 0;

// Check calculation loop start position
// We use prev_calculated-1 to see 'live' indicator update on open candle
int iStart  = MathMax(0, prev_calculated-1);

//for loop to calculate the buying and selling volume val's
for(int i=iStart; i<rates_total; i++){                     
      double upper_wick    = close[i]>open[i] ? high[i]-close[i]  :  high[i]-open[i];
      double lower_wick    = close[i]>open[i] ? open[i]-low[i]    :  close[i]-low[i];      
      double Cspread       = high[i]-low[i];
      double body_length   = Cspread - (upper_wick + lower_wick);
      
      // The below prevents zero division in case of high[i] being equal to low[i]
      // Due to no ticks within candle, or bad candle history
      if(Cspread == 0) {
         buying_volume[i]  = buying_volume[i-1];
         selling_volume[i] = selling_volume[i-1];
         continue;
      }
      
      double percent_upper_wick   = upper_wick   /  Cspread;
      double percent_lower_wick   = lower_wick   /  Cspread;
      double percent_body_length  = body_length  /  Cspread;
    
      buying_volume[i]  = close[i]>open[i] ? (percent_body_length + (percent_upper_wick + percent_lower_wick)/2)*tick_volume[i] : ((percent_upper_wick + percent_lower_wick)/2) * tick_volume[i];
      selling_volume[i] = close[i]<open[i] ? (percent_body_length + (percent_upper_wick + percent_lower_wick)/2)*tick_volume[i] : ((percent_upper_wick + percent_lower_wick)/2) * tick_volume[i];  
   }
//EMA on buffer
   ExponentialMAOnBuffer(rates_total,prev_calculated,0,cumulation_length,buying_volume,buyEMA);      
   ExponentialMAOnBuffer(rates_total,prev_calculated,0,cumulation_length,selling_volume,sellEMA);
   
   return(rates_total);
}
 
Elmer013 #:

3) I don't see what I did wrong there, could you give me an example? I thought using the current way was necessary to calculate past values..


You did nothing wrong.

You just did something that is useless.


Usually when you create arrays it is because you want to make use of several values.

For example- when calculating MA of price you are not only looking at current price, but you are also looking at previous prices, depends on the period of the MA.

In your code you have created several arrays to hold all of the values you've calculated, but you only used them one time, and then didn't need them anymore.

For example-

You've calculated upper_wick,

And then you used the current calculation of upper_wick for calculating percent_upper_wick,

But then you are done with upper_wick, and don't care anymore about its value.

Then you use percent_upper_wick to calculate buying/selling volume,

But then you are done with percent_upper_wick, and don't care anymore about its value.

So there is no need to save those values forever in arrays.

Reason: