reducing Computing time of mq4-Indicator call (performance tuning/test) by buffering needed indicators

Einloggen oder registrieren, um einen Kommentar zu schreiben
Josip Istuk
2778
Josip Istuk  

My EA has become to heavy/ needs too much computing time. Hence an performance tuning has to be done.

The bad (computing time) performance depends mainly on calling a lot of indicators. In my case iMACD.

Hence I have introduced an array buffer for this indicator: I calculate after end of each bar its MACD value and signal value and save it into an array.

Now i have the last x needed iMACD values saved to an array.

Instead of calling 100 times iMACD (for the last 100 bars) in order to analyse it for the EA, i have now the possibility to call the saved values from the array.

To prove if its really faster, i draw a little test program:

   load_macd2();//<<--- loading last iMACD to arrays
   double test=0;
   
   for(int j=0;j<1000;j++)
   for(int i=0;i<MACD_VALUE_COUNT;i++)
   {
      test=macdv.v[i];  //1.CASE WITH ARRAYS
      //test=iMACD(NULL,1,12,27,4,PRICE_CLOSE,0,1); //2.CASE WITH DIRECT INDICATOR CALL
   }

The result was amazing: The solution with arrays is about 30 times faster. When backtesting the arrays solution finished 15 days in 15 secondes. During this time (15 sec) the solution with direct indicator call performs only half day.

Of course the disadvantage is, you need write a set and get method for the array. If not, you will need shift it each bar. And this will not be good for the performance. Instead of shifting all values, only the index of current value can be changed, in order to get best performance. Of course max

MACD_VALUE_COUNT

MACD-Values can be saved. But this is not a real disadvantage, because you can set it to 100 or 1000. Not important, because memory is no problem - we have enough of it.

What do you think about this solution? Is there a better way to achieve the same purpose?

Fedor Arkhipov
49027
Fedor Arkhipov  

You shall fill array in first time of start EA, after You shall add new value of macd in your array (move arrays numbers or increase (resize) your array every new bar).

Also you can calculate ea only one time in new bar (not every tick).

Josip Istuk
2778
Josip Istuk  
Fedor Arkhipov:

You shall fill array in first time of start EA, after You shall add new value of macd in your array (move arrays numbers or increase (resize) your array every new bar).

Also you can calculate ea only one time in new bar (not every tick).

Hi Fedor, thanks for attention :)

I calculate only on a new bar anyway. Resize i dont need, because in my EA i have coding which never need more macd values than MACD_VALUE_COUNT. I have solid array which never moves, and only its overwritten once a bar and a "pointer" which marks which is current bar 1. Example with MACD_VALUE_COUNT=10:

In this case bar1 is on 8, bar2 is in 9 and bar 3 is in 0, ... , last bar9 is in 7. Ant the get and set methods manage this.

[0] [1] [2] [3] [4] [5] [6] [7] [8] [9]
                                 X           <--Pointer of current bar 1

If i need value of current bar 0, of course i need call iMACD directly again. But i dont use it or if i use, very seldom.

I dont know performance of resize of arrays, but it cannot be better performance than solution above, because i do there only the minimum what is necessary: calculate value of bar1 once a bar. change pointer value (int) once a bar. All get-Method calls only read an array value. But the disadvantage is, you need this solution for each indicator which you need. If you need additionaly to iMACD also RSI and Stochastic, Momentum, you need write 3 more getter and setter, but they look very simular and improve performance a lot.

Josip Istuk
2778
Josip Istuk  
double mcd(int tf,int mode,int shift){  if(shift>=MACD_VALUE_COUNT){Alert("Error:MACD_VALUE_COUNT überschritten!"); return(0);}//err   //<<<--- getter method
   int i=0;
   double ret=0;
   int curr=(int)MathMod(macdv[i].curr+shift,MACD_VALUE_COUNT);
   switch(tf){case PERIOD_D1: i=0;break; case 240: i=1;break; case 60: i=2;break; case 30: i=3;break; case 15: i=4;break; case 5: i=5;break; case 1: i=6;break;}
   if(shift==0){
      ret=iMACD(NULL,tf,macdv[i].fast,macdv[i].slow,macdv[i].sig,PRICE_CLOSE,mode,shift);//Index 0 muss direkt zugegriffen werden, wie er aktuellst ist.
   }else{
      if(mode==0)ret=macdv[i].v[curr];
      if(mode==1)ret=macdv[i].s[curr];
   }
   
   return(ret);
}
void mcdSet(int tf,int shift=1, int fast=0,int slow=0,int sig=0)//MACD_VALUE_COUNT   //<<--- setter method moves also pointer
{
   bool next=false;
   int i=getINDEX_fromTF(tf);
   
   if(fast==0)fast=macdv[i].fast;if(slow==0)slow=macdv[i].slow;if(sig==0)sig=macdv[i].sig;
   
   int curr=(int)MathMod(macdv[i].curr+shift,MACD_VALUE_COUNT);
   
   macdv[i].v[curr] = iMACD(NULL,tf,fast,slow,sig,PRICE_CLOSE,0,shift);//mcd(1,0,1)=iMACD(NULL,1,fast,slow,ma,PRICE_CLOSE,0,1);//mcd(1,1,1)=iMACD(NULL,1,fast,slow,ma,PRICE_CLOSE,1,1);
   macdv[i].s[curr] = iMACD(NULL,tf,fast,slow,sig,PRICE_CLOSE,1,shift);//mcd(1,0,1)=iMACD(NULL,1,fast,slow,ma,PRICE_CLOSE,0,1);//mcd(1,1,1)=iMACD(NULL,1,fast,slow,ma,PRICE_CLOSE,1,1);
   
   macdv[i].curr++;macdv[i].curr=(int)MathMod(macdv[i].curr,MACD_VALUE_COUNT);
   cnt_ind(tf,macdv[i]);//Trend zählen (hoch, tief, dauer positiv/ negativ, dauer unter/über signal cnt_neg_sig)
}

void load_macd2()
{
   //Print("mcd(6,9)="+(string)macdv[6].v[9]);
   if( mm!=mm_old                                     ){mcdSet(1);        }//M1
   if((mm!=mm_old && MathMod(mm,5)==0)                ){mcdSet(5);        }//M5
   if((mm!=mm_old && MathMod(mm,15)==0)               ){mcdSet(15);       }//M15   
   if((mm!=mm_old && MathMod(mm,30)==0)               ){mcdSet(30);       }//M30
   if((mm!=mm_old && hh!=hh_old)                      ){mcdSet(60);       }//H1
   if((mm!=mm_old && (hh!=hh_old && MathMod(hh,4)==0))){mcdSet(240);      }//H4
   if((mm!=mm_old && dd!=dd_old)                      ){mcdSet(PERIOD_D1);}//D1
}
Carl Schreiber
Moderator
9812
Carl Schreiber  
How did come to this German forum?
Carl Schreiber
Moderator
9812
Carl Schreiber  
Josip Istuk:

Dein Problem ist wohl, dass Dur den iMACD mit vielen versch. Parameter aufrufst:

iMACD(NULL,tf,macdv[i].fast,macdv[i].slow,macdv[i].sig,PRICE_CLOSE,mode,shift)

Jedes Mal wird der Indikator neu angelegt kostet viel Zeit und Speicher.

berechne den MACD doch einfach selber zB. nutze nur den ema:

#define ema(newVal ,prvVal, cnst) (cnst >= 0.99 ? newVal : (((newVal)-(prvVal))*(cnst) + prvVal))
Josip Istuk
2778
Josip Istuk  

Performance-mäßig läuft's sehr gut - allerdings selbst gebaut. Und die Frage war, ob es da professionellere, bereits vorhandene Lösungen/ Vorgehensweisen gibt.

Ich kenne die ema-Funktionnicht und kann daher nicht beurteilen, ob diese performanter ist. Ich vertraue an sich schon darauf, dass iMACD performant berechnet wird, jedoch dauert eine Berechnung immer mehr als einen vorhandenen Wert abzulesen. Daher die obige Speicherfunktion.

Carl Schreiber
Moderator
9812
Carl Schreiber  

Für Deine Frage zeigst Du nicht genug code.

Man kann zB. nicht erkennen, wie viele verschiedene iMACD rufst Du auf....

iMACD ist sicher schneller als eine Aufruf über iCustom(..), aber zB. ein ema ist schneller als ein sma...

Einloggen oder registrieren, um einen Kommentar zu schreiben