Features of the mql5 language, subtleties and tricks - page 274

 

Forum on trading, automated trading systems and testing trading strategies

Features of mql5 language, subtleties and techniques of work

amrali, 2024.11.26 15:58

The fastest replacement for the built-in TimeToStruct() function,

I recommend using this function:

bool TimeToStructFast(datetime time, MqlDateTime& dt_struct)
  {
//   static const int Months[13] = {0, -1, 30, 58, 89, 119, 150, 180, 211,242, 272, 303, 333};  
   static const uint Months[] = {0, 11512676, 11512180, 11511728, 11511232, 11510750, 11510256,
                                    11509774, 11509280, 11508781, 11508302, 11507806, 11507326};
   uint t       = (uint)time;
   int  n       = (int)(t / (24 * 3600));
   int  tn      = (n << 2) + 2;
   int  dow     = (n + 4) % 7;
   int  doy     = (tn % 1461) / 4;
   int  year    = (tn / 1461) + 1970; // to 2100
//   int  isleap  = ((year & 3) == 0);
//   int  leapadj = ((doy < (isleap + 59)) ? 0 : (2 - isleap));
//   int  mon     = ((((doy + leapadj) * 12) + 373) / 367);
//   int  day     = doy - Months[mon] - (isleap && doy > 59);
   int  leapadj = (doy < 31) ? 0 : (2 - !(year & 3));
   int  mon     = ((doy + leapadj) * 67 + 2075) >> 11;       
   int  day     = n - (int)((year * 5844 - Months[mon]) >> 4); // https://www.mql5.com/ru/forum/170952/page254#comment_53104383
   int  HH      = (int)(t / 3600) % 24;
   int  MM      = (int)(t / 60) % 60;
   int  SS      = (int)(t % 60);

   dt_struct.year           = year;
   dt_struct.mon            = mon;
   dt_struct.day            = day;
   dt_struct.hour           = HH;
   dt_struct.min            = MM;
   dt_struct.sec            = SS;
   dt_struct.day_of_week    = dow;
   dt_struct.day_of_year    = doy;
   return (true);
  } 

Even faster.

 

Forum on trading, automated trading systems and testing trading strategies

Features of mql5 language, subtleties and techniques of work

amrali, 2024.11.26 15:58

   int  isleap  = ((year & 3) == 0);
   int  leapadj = ((doy < (isleap + 59)) ? 0 : (2 - isleap));
   int  mon     = ((((doy + leapadj) * 12) + 373) / 367);

Here is the error of the 31st of January.

void OnStart()
{
  int doy = 31;
  int isleap = 0;
  
  int  leapadj = ((doy < (isleap + 59)) ? 0 : (2 - isleap));
  int  mon     = ((((doy + leapadj) * 12) + 373) / 367);

  Print(mon); // 2
}
 
fxsaber #:

Here is the error of the 31st of January.

Which is correct :-)

You missed that day_of_year is zero-based index. So, doy=31 means actually 1 February.

algo_checker.mq5:

void OnStart()
  {
   int dm[2][13] = {};
   for(int isleap = 0; isleap <= 1; isleap++)
     {
      for(int doy = 0; doy <= 364+isleap; doy++)
        {
         // the core algorithm to calculate month and days
         static const int Months[13] = {0, -1, 30, 58, 89, 119, 150, 180, 211,242, 272, 303, 333};
         int  leapadj = ((doy < (isleap + 59)) ? 0 : (2 - isleap));
         int  mon     = ((((doy + leapadj) * 12) + 373) / 367);
         int  day     = doy - Months[mon] - (isleap && doy > 59);

         PrintFormat("isleap: %d, doy: %.3d, date: %.2d/%.2d", isleap, doy, day, mon);
         dm[isleap][mon]++;
        }
     }
   ArrayPrint(dm);
   Print("done.");
  }
 
fxsaber #:

Even faster.

bravo, it seems faster!

  5.96 ns, checksum = 81874529667266   // TimeToStruct2100
  5.98 ns, checksum = 81874529667266   // TimeToStructFast
  5.27 ns, checksum = 81874519667266   // TimeToStructFast_fxsaber
  5.82 ns, checksum = 81874529667266   // TimeToCalendar
 13.09 ns, checksum = 81874529667266   // TimeToJulian
 25.98 ns, checksum = 81874529667266  /// MQL's TimeToStruct()

I should do a larger test before applying these changes to my function.

Here is the improved script for benchmarks:

 
amrali #:

You missed that day_of_year is an index based on zero. So doy=31 actually means 1 February.

Thanks, realised this before your post, so deleted it.

 
amrali #:

Also, in real life (not in synthetic benchmarks), caching once a day can be useful to skip repeated calculations of the same year, month and day.

In regular TimeToStruct, caching is every hour.
 

Forum on trading, automated trading systems and testing trading strategies

Features of mql5 language, subtleties and techniques of work

amrali, 2024.11.26 15:58

The fastest replacement for the built-in TimeToStruct() function,

I recommend using this function:

bool TimeToStructFast(datetime time, MqlDateTime& dt_struct)
  {
//   static const int Months[13] = {0, -1, 30, 58, 89, 119, 150, 180, 211,242, 272, 303, 333};  
   static const int Months[] = {0, 11512692, 11512196, 11511744, 11511248, 11510766, 11510272,
                                   11509790, 11509296, 11508797, 11508318, 11507822, 11507342};
   uint t       = (uint)time;
   int  n       = (int)(t / (24 * 3600));
   int  tn      = (n << 2) + 2;
   int  dow     = (n + 4) % 7;
   int  doy     = (tn % 1461) / 4;
   int  year    = (tn / 1461) + 1970; // to 2100
//   int  doy     = n - ((year * 5844 - 11512676) >> 4);

//   int  isleap  = ((year & 3) == 0);
//   int  leapadj = ((doy < (isleap + 59)) ? 0 : (2 - isleap));
//   int  mon     = ((((doy + leapadj) * 12) + 373) / 367);
//   int  day     = doy - Months[mon] - (isleap && doy > 59);
   int  mon     = (doy < 59) ? ((doy + 1) >> 5) + 1 : (((doy - !(year & 3)) * 67 + 2209 ) >> 11);   
   int  day     = n - (int)((year * 5844 - Months[mon]) >> 4); // https://www.mql5.com/ru/forum/170952/page254#comment_53104383
   int  HH      = (int)(t / 3600) % 24;
   int  MM      = (int)(t / 60) % 60;
   int  SS      = (int)(t % 60);

   dt_struct.year           = year;
   dt_struct.mon            = mon;
   dt_struct.day            = day;
   dt_struct.hour           = HH;
   dt_struct.min            = MM;
   dt_struct.sec            = SS;
   dt_struct.day_of_week    = dow;
   dt_struct.day_of_year    = doy;
   return (true);
  }

My previous version had a bug, so removed it.

Changed Months[] and got rid of the isleap calculation on any date. Now it's correct and faster.

 
fxsaber #:

My previous version had a bug, so removed it.

Changed Months[] and got rid of the isleap calculation on any date. Now it's correct and faster.

It is still there: doy - !(year & 3)
 
amrali #:
It's still there: doy - !(year & 3)

Only when doy >= 59.

 
amrali #:

Thanks Alain for your test. The replacement for built-in TimeToStruct() is only 4-5 times faster as I reported before.

However, the replacements for TimeYear(), TimeDayOfWeek() is at least 10-20 times faster than using MQL's TimeToStruct()

This code executes slow while using wrappers of built-in StructToTime.

Using the optimized functions really helps here.

Another thing: decompressing (TimeToStruct) is always faster than compressing (StructToTime). Built-in StructToTime is really slow than TimeToStruct().

If your code is creating a lot of 'datetime' variables from time components, you will find CreateDateTime() is much faster than MQL's StructToTime().

I faced the problem of slow StructToTime() while constructing datetime variables for Daylight switch times in TimeZoneInfo library. Replacing it for this custom CreateDateTime() made a nice improvement in speeds.

Thanks for the tips. I don't use "TimeHour" as it's trivial to get it, but I will check for the other usage of TimeToStruct().