Download MetaTrader 5

Quality and efficiency of mql coding.

To add comments, please log in or register
Alain Verleyen
Moderator
33334
Alain Verleyen  

There was recently an interesting topic on Russian forum, where the discussion started from a poll on the quality of code produced by freelancer to finally shift to a discussion on the best way to retrieve the name of a timeframe (as s string) from its mql4 value. 

The big divergence start from the following code, posted as a "proof" of bad quality code :

string TFtoString(int tf)
  {
   string TF="";
   if(tf==0)tf=Period();
   if(tf==1)TF="M1";
   if(tf==5)TF="M5";
   if(tf==15)TF="M15";
   if(tf==30)TF="M30";
   if(tf==60)TF="H1";
   if(tf==240)TF="H4";
   if(tf==1440)TF="D1";
   if(tf==10080)TF="W1";
   if(tf==43200)TF="MN1";
   return(TF);
  }

Unfortunately, as it's often the case on Russian forum , there was not any useful conclusion. So I decide to check by myself.

I created a script to benchmark all solutions I could find on this topic, on the forums in general, including my own solution. There was 8 proposed methods, one being discarded as not working, remains 7 methods. These methods are presented "as is", the author doesn't necessarily had efficiency in mind when posting the code.

The methods :

//+------------------------------------------------------------------+
//| Method 1 : using static arrays (no enums, no switch)             |
//+------------------------------------------------------------------+
string PeriodToString_StaticArrays(int tf)
  {
   static string tfLabel[]={"M1","M5","M15","M30","H1","H4","D1","W1","MN1"};
   static int tfInt[]={PERIOD_M1,PERIOD_M5,PERIOD_M15,PERIOD_M30,PERIOD_H1,PERIOD_H4,PERIOD_D1,PERIOD_W1,PERIOD_MN1};
   int index=-1;

//--- check for 0 : current
   if(tf==0) tf=_Period;
//--- search index
   for(int i=0;i<9;i++)
     {
      if(tfInt[i]==tf)
        {
         index=i;
         break;
        }
     }
//--- check if found
   if(index==-1)
     {
      return("Unknown timeframe");
     }
//--- result
   return(tfLabel[index]);
  }
//+------------------------------------------------------------------+
//| Method x : using ternary operator                                |
//| https://www.mql5.com/en/docs/basis/operators/ternary             |
//| https://www.mql5.com/ru/forum/40117/page29#comment_1324723       |
//+------------------------------------------------------------------+
/* REMOVED FROM THE TEST AS NOT WORKING (AND COMPILING WITH WARNING)
string PeriodToString_Bit(int tf)
  {
   return(0xC000 & tf ?(0xC001 == tf ? "MN1":(0x8001 == tf ?"W1":("H" + (tf&0x1F)))) : "M" + (tf&0x1F));
  }
*/
//+------------------------------------------------------------------+
//| Method 2 : using EnumToString                                    |
//| https://www.mql5.com/ru/forum/40117/page21#comment_1324379       |
//| Возвращает строковое представление переданного таймфрейма.       |
//| В случае неудачи возвращает NULL.                                |
//| INPUT:                                                           |
//|     timeframe - Таймфрейм, строковое значение которого           |
//|                 необходимо получить. Может принимать одно из     |
//|                 значений перечисления ENUM_TIMEFRAMES.           |
//| RESULT:                                                          |
//|     Строковое представление таймфрейма. NULL в случае неудачи.   |
//+------------------------------------------------------------------+
string PeriodToString_EnumToString(ENUM_TIMEFRAMES timeframe)
  {
   string results[];
   if(!ArrayResize(results,2))
      return NULL;
   StringSplit(EnumToString(timeframe),'_',results);
   return results[ArraySize(results)-1];
  }
//+------------------------------------------------------------------+
//| Method 3 : using if (Wahoo(old MT4) presented by Abolk           |
//| https://www.mql5.com/ru/forum/40117/page5#comment_1322071        |
//+------------------------------------------------------------------+
string PeriodToString_If(int tf)
  {
   string TF="";
   if(tf==0)tf=Period();
   if(tf==1)TF="M1";
   if(tf==5)TF="M5";
   if(tf==15)TF="M15";
   if(tf==30)TF="M30";
   if(tf==60)TF="H1";
   if(tf==240)TF="H4";
   if(tf==1440)TF="D1";
   if(tf==10080)TF="W1";
   if(tf==43200)TF="MN1";
   return(TF);
  }
//+------------------------------------------------------------------+
//| Method 4 : using EnumToString and a macro                        |
//+------------------------------------------------------------------+
//--- macros : METHOD 4
#define TIMEFRAME(tf)      StringSubstr(EnumToString((ENUM_TIMEFRAMES)tf),7)

//+------------------------------------------------------------------+
//| Method 5 : using if improved                                     |
//+------------------------------------------------------------------+
string PeriodToString_IfImproved(int tf)
  {
   string TF="Unknown";
   if(tf==0)tf=Period();
   if(tf==1)TF="M1";
   else if(tf==5)TF="M5";
   else if(tf==15)TF="M15";
   else if(tf==30)TF="M30";
   else if(tf==60)TF="H1";
   else if(tf==240)TF="H4";
   else if(tf==1440)TF="D1";
   else if(tf==10080)TF="W1";
   else if(tf==43200)TF="MN1";
   return(TF);
  }
//+------------------------------------------------------------------+
//| Method 6 : using switch (presented by Voldemar)                  |
//| https://www.mql5.com/ru/forum/40117/page20#comment_1324339       |
//| MOdified to process tf=0 (current)                               |
//+------------------------------------------------------------------+
string PeriodToString_Switch(int tf)
  {
   string TF=NULL;
   if(tf==0) tf=_Period;
   switch(tf)
     {
      case 1:    TF="M1"; break;
      case 5:    TF="M5"; break;
      case 15:   TF="M15";break;
      case 30:   TF="M30";break;
      case 60:   TF="H1"; break;
      case 240:  TF="H4"; break;
      case 1440: TF="D1"; break;
      case 10080:TF="W1"; break;
      case 43200:TF="MN1";break;
      default:   TF="Unknown period";
     }
   return(TF);
  }
//+------------------------------------------------------------------+
//| Method 7 : using arrays (presented by WHRoeder)                  |
//| http://forum.mql4.com/54003#758193                               |
//| Modified to remove . from variable names                         |
//+------------------------------------------------------------------+
string PeriodToString_Arrays(int tf)
  {
   static int iTF,chrt_tf[]=
     {
      0,PERIOD_M1,PERIOD_M5,PERIOD_M15,PERIOD_M30,
      PERIOD_H1,PERIOD_H4,PERIOD_D1,PERIOD_W1,PERIOD_MN1
     };
   static string   text_tf[]=
     {
      "n/a","M1","M5","M15","M30",
      "H1","H4","D1","W1","MN1"
     };
   for(iTF=0; chrt_tf[iTF]!=tf; iTF++){}
   return(text_tf[iTF]);
  }

The results :

2015.03.01 13:50:06.514    METHOD 1 : using Static Arrays. Executed 11000000 times in 2109 ms.
2015.03.01 13:50:13.735    METHOD 2 : using EnumToString. Executed 11000000 times in 7219 ms.
2015.03.01 13:50:15.282    METHOD 3 : using if(s) original. Executed 11000000 times in 1547 ms.
2015.03.01 13:50:17.580    METHOD 4 : using EnumToString(macro). Executed 11000000 times in 2297 ms.
2015.03.01 13:50:19.017    METHOD 5 : using if(s) improved. Executed 11000000 times in 1437 ms.
2015.03.01 13:50:20.377    METHOD 6 : using switch. Executed 11000000 times in 1360 ms.
2015.03.01 13:50:22.112    METHOD 7 : using arrays. Executed 11000000 times in 1734 ms.

As you can see the more efficient is the "switch" one.

Personally I am using method 4, with only 1 line of code, efficiency doesn't matter, as most of the time you can use that only 1 once during initialization, and saved the result in a global variable. Of course, all depends of the context.

Make your choice.

Attached the full script, you can change the #define NO_TESTING to TESTING, to check the results of each method.

Files:
Pasi Hakamaki
6314
Pasi Hakamaki  
An interesting study! I myself am a bit obsessed with trying to find the shortest and most efficient code. Thank you very much for sharing your findings and thoughts with the community :)
Alexandr Bryzgalov
39428
Alexandr Bryzgalov  

add method (https://www.mql5.com/ru/forum/40117/page30#comment_1399820):

void OnStart()
  {
//---
   string s="1     M1 5     M5 15    M15 30    M30 60    H1 240   H4 1440  D1 10080 W1 43200 MN1 ";
   int pos=StringFind(s,string(_Period));
   if(pos>=0)Print(StringSubstr(s,pos+6,3));

  }
//+----------


проходов 11000000 на каждой распечатке
2015.03.01 17:48:55.229 Tf EURUSD,Daily: 1359
2015.03.01 18:20:55.765 PeriodToString2 EURUSD,Daily: METHOD 7 : using arrays. Executed 11000000 times in 875 ms.
2015.03.01 18:20:54.890 PeriodToString2 EURUSD,Daily: METHOD 6 : using switch. Executed 11000000 times in 344 ms.
2015.03.01 18:20:54.546 PeriodToString2 EURUSD,Daily: METHOD 5 : using if(s) improved. Executed 11000000 times in 406 ms.
2015.03.01 18:20:54.140 PeriodToString2 EURUSD,Daily: METHOD 4 : using EnumToString(macro). Executed 11000000 times in 1672 ms.
2015.03.01 18:20:52.468 PeriodToString2 EURUSD,Daily: METHOD 3 : using if(s) original. Executed 11000000 times in 406 ms.
2015.03.01 18:20:52.062 PeriodToString2 EURUSD,Daily: METHOD 2 : using EnumToString. Executed 11000000 times in 4687 ms.
2015.03.01 18:20:47.374 PeriodToString2 EURUSD,Daily: METHOD 1 : using Static Arrays. Executed 11000000 times in 1172 ms.


Alexandr Bryzgalov
39428
Alexandr Bryzgalov  

)

string Period_[43200];
//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
void OnStart()
  {
//---
   Period_[0]="M1";
   Period_[4]="M5";
   Period_[14]="M15";
   Period_[29]="M30";
   Period_[59]="H1";
   Period_[239]="H4";
   Period_[1439]="D1";
   Period_[10079]="W1";
   Period_[43199]="MN";
   Print( Period_[_Period-1]);      
  } 
To add comments, please log in or register