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

 
amrali #:
You cannot open a trade on Friday, 3 April 2026, as the market is closed (Good Friday, the day before Easter Sunday).

Sometimes I can open or close a trade.


I believe that 5 out of 6 of the results below are correct.

void OnStart()
{
  const datetime OpenTime1 = D'2026.04.02 11:00';
  const datetime CloseTime1 = D'2026.04.03 02:00';    

  Print(HOLIDAY::GetDealLength(OpenTime1, CloseTime1)); // 54,000
  Print(HOLIDAY::GetDealLength_Cached(OpenTime1, CloseTime1)); // 54,000

  const datetime OpenTime2 = D'2026.04.02 11:00';
  const datetime CloseTime2 = D'2026.04.06 02:00';    

  Print(HOLIDAY::GetDealLength(OpenTime2, CloseTime2)); // 54,000
  Print(HOLIDAY::GetDealLength_Cached(OpenTime2, CloseTime2)); // 54,000

  const datetime OpenTime3 = D'2026.04.03 11:00';
  const datetime CloseTime3 = D'2026.04.06 02:00';    

  Print(HOLIDAY::GetDealLength(OpenTime3, CloseTime3)); // 54,000
  Print(HOLIDAY::GetDealLength_Cached(OpenTime3, CloseTime3)); // -32,400
}
 
fxsaber #:

I believe that 5 out of 6 of the results below are correct.


Correction:

  static int GetDealLength_Cached(
    datetime openTime,
    datetime closeTime)
  {
    int totalSeconds =
      (int)(closeTime - openTime);

    int a = DayIndex(openTime + DAY - 1 - Epoch);
    int b = DayIndex(closeTime - Epoch);

    int holidays = Prefix[b] - Prefix[a];

    return totalSeconds - holidays * DAY;
  }

Result:

void OnStart()
{
  const datetime OpenTime3 = D'2026.04.03 11:00';
  const datetime CloseTime3 = D'2026.04.06 02:00';    

  Print(HOLIDAY::GetDealLength(OpenTime3, CloseTime3)); // 54,000
  Print(HOLIDAY::GetDealLength_Cached(OpenTime3, CloseTime3)); // 54,000
}
Files:
Holiday4.mq5  16 kb
 
amrali #:

Correction:

Yes, it matches now.


I took a real-world task where, at the end of a backtest, I needed to calculate the duration of all positions. I stored the open and close times of the positions in an array. Then I ran the following script on it.

void OnStart()
{
  int Times[];
  const int Size = (int)FileLoad("Times.bin", Times); // https://c.mql5.com/3/490/Times.zip
  
  Print(Size >> 1); // 324,865 positions.

  // Loop 1: Benchmark (non-cached)
  ulong sum1 = 0;
  ulong t1 = GetMicrosecondCount();
  
  for (int i = 0; i < Size; i++)
  {
    const datetime OpenTime = Times[i++];
    const datetime CloseTime = Times[i];
    
    sum1 += HOLIDAY::GetDealLength(OpenTime, CloseTime);
  }

  // GetDealLength: 582 microsec (sum = 187295981)
  PrintFormat("GetDealLength: %llu microsec (sum = %llu)", GetMicrosecondCount() - t1, sum1);

  // Loop 2: Cached benchmark
  ulong sum2 = 0;
  ulong t2 = GetMicrosecondCount();
  for (int i = 0; i < Size; i++)
  {
    const datetime OpenTime = Times[i++];
    const datetime CloseTime = Times[i];
    
    sum2 += HOLIDAY::GetDealLength_Cached(OpenTime, CloseTime);
  }

  // GetDealLength_Cached: 1046 microsec (sum = 187295981)
  PrintFormat("GetDealLength_Cached: %llu microsec (sum = %llu)", GetMicrosecondCount() - t2, sum2);

  Print("CORRECT: ", sum1 == sum2); // true
}

In tasks like this, you only need to go through all the positions once, so I wasn’t able to gain any performance benefit from caching. I suppose the only thing that might help here is table data hard-coded into the programme before compilation. But it’s probably not worth fighting for half a millisecond. Thanks for the research!


P.S. Thanks to you, I’ve spotted a mistake in my own code.

#include <fxsaber\BestInterval\Holiday.mqh>

void OnStart()
{
  const datetime OpenTime1 = D'2026.04.03 01:00';
  const datetime CloseTime1 = D'2026.04.06 02:00';    

  Print(HOLIDAY::GetDealLength(OpenTime1, CloseTime1)); // 3600

  const datetime OpenTime2 = D'2026.04.03 03:00';
  const datetime CloseTime2 = D'2026.04.06 02:00';    

  Print(HOLIDAY::GetDealLength(OpenTime2, CloseTime2)); // 82800
}
Thanks again.
Files:
Times.zip  888 kb
 
fxsaber #:

I took a real-world task where, at the end of a backtest, I needed to calculate the duration of all positions. I stored the open and close times of the positions in an array. Then I ran the following script on it.


The attached file "Time.bin" contains 324,865 positions, of which 324,425 (99.87%) have a short duration < 24 hours (does not span weekends or holidays).

For an objective comparison, increase the ratio of positions with a longer duration, and you will see a real increase in performance thanks to my optimizations.

 
amrali #:

The attached file ‘Time.bin’ contains 324,865 entries, of which 324,425 (99.87 per cent) are of short duration (< 24 hours) (excluding weekends and public holidays).

For an objective comparison, increase the proportion of entries with longer durations, and you will see a real improvement in efficiency thanks to my optimisations.

I’ve done that.
    const datetime OpenTime = Times[i++];
    const datetime CloseTime = Times[i] + 86400;


The result.

GetDealLength: 2746 microsec (sum = 28188499181)
GetDealLength_Cached: 2575 microsec (sum = 28254595181)


The larger this operand, the faster the cached version is.

 
fxsaber #:

P.S. Thanks to you, I’ve spotted a mistake in my own code.

Thanks again.

  static int GetDealLength( const datetime &OpenTime, const datetime &CloseTime )
  {
    int Length = (int)(CloseTime - OpenTime);

    //if (Length > DAY)
    //  for (datetime Time = CloseTime - DAY; Time > OpenTime; Time -= DAY)
    //    if (HOLIDAY::Is(Time))
    //      Length -= DAY;

    if (Length > DAY)
    {
      const datetime EndOfOpenDay = OpenTime - OpenTime % DAY + (DAY - 1);

      for (datetime Time = CloseTime - DAY; Time > EndOfOpenDay; Time -= DAY)
        if (HOLIDAY::Is(Time))
          Length -= DAY;
    }

    return(Length);
  }

Test:

void OnStart()
{
  const datetime OpenTime1 = D'2026.04.03 01:00';
  const datetime CloseTime1 = D'2026.04.06 02:00';

  Print(HOLIDAY::GetDealLength(OpenTime1, CloseTime1)); // 90000

  const datetime OpenTime2 = D'2026.04.03 03:00';
  const datetime CloseTime2 = D'2026.04.06 02:00';

  Print(HOLIDAY::GetDealLength(OpenTime2, CloseTime2)); // 82800
}
 

Good day,

Unfortunately, while running a date/time simulation, I found that none of the faster replacements for StructToTime() and TimeToStruct() posted before here and here worked correctly for years beyond 2100.

I had to revise the implementations to fix the issue so they now work correctly for all years.

In addition, significant performance improvements were achieved (thanks to some micro-optimizations, such as the use of unsigned division and modulus).

MQL's StructToTime() alternative:

datetime StructToTimeFast(const MqlDateTime& st)
  {
   const uint j = st.mon < 3;
   const uint y = st.year - j;
   const uint m = j ? st.mon + 12 : st.mon;
   const uint f = (979 * m - 2918) / 32;  // days before month
   const uint n = st.day + f + 365*y + (y/4) - (y/100) + (y/400) - 306 - 719163;
   return (datetime)n*86400LL + st.hour*3600 + st.min*60 + st.sec;
  }

MQL's TimeToStruct() replacement:

bool TimeToStructFast(datetime time, MqlDateTime& dt_struct)
  {
   uint  n = (uint)(time / 86400ULL)          ;  // Unix day
   uint  N = n + 12699422                     ;  // Computational calendar day
   uint  a = 4 * N + 3                        ;  // N_1
   uint  c = a / 146097                       ;  //  century
   uint  e = a % 146097 / 4                   ;  //  [0, 36524] - dayOfCentury
   uint  b = 4 * e + 3                        ;  // N_2
   uint  z = b / 1461                         ;  //  [0, 99]  - yearOfCentury
   uint  h = b % 1461 / 4                     ;  //  [0, 365] - Days since 1 March
   uint  d = 2141 * h + 197913                ;  // N_3
   uint  M = d / 65536                        ;  //  [3, 14]  - Month
   uint  D = d % 65536 / 2141                 ;  //  [0, 30]  - Day
   uint  Y = 100 * c + z                      ;  // Year
   uint  J = h >= 306                         ;  // JanFeb
   uint  l = z ? (z % 4 == 0) : (c % 4 == 0)  ;  // isLeap

   uint sec = (uint)(time - n * 86400ULL);
   dt_struct.year = (int)(Y - 32800 + J);
   dt_struct.mon  = (int)(J ? M - 12 : M);
   dt_struct.day  = (int)(D + 1);
   dt_struct.hour = (int)(sec / 3600);
   dt_struct.min  = (int)((sec / 60) % 60);
   dt_struct.sec  = (int)(sec % 60);
   dt_struct.day_of_week = (int)((n + 4) % 7);
   dt_struct.day_of_year = (int)(h + (J ? -306 : 59 + l));

   return (true);
  }

Benchmarks:

StructToTimeFast (EURUSD,H1)    Compiler Version: 5836, AVX2 + FMA3
StructToTimeFast (EURUSD,H1)    13th Gen Intel Core i7-13700KF, AVX2 + FMA3
StructToTimeFast (EURUSD,H1)    30.27 ns, checksum = 323450257960585376  /// MQL's StructToTime()
StructToTimeFast (EURUSD,H1)     1.95 ns, checksum = 323450257960585376   // StructToTimeFast
TimeToStructFast (EURUSD,H1)    Compiler Version: 5836, AVX2 + FMA3
TimeToStructFast (EURUSD,H1)    13th Gen Intel Core i7-13700KF, AVX2 + FMA3
TimeToStructFast (EURUSD,H1)    1970.01.01 01:02:44 - 2999.12.31 23:23:34
TimeToStructFast (EURUSD,H1)    21.34 ns, checksum = 6246603919299185333  /// MQL's TimeToStruct()
TimeToStructFast (EURUSD,H1)     2.79 ns, checksum = 6246603919299185333   // TimeToStructFast
Benchmark results are unreliable due to compiler optimizations and testing conditions.
Benchmark results are unreliable due to compiler optimizations and testing conditions.
  • 2024.11.29
  • www.mql5.com
The discussion revolves around the performance of a function in different compiler versions and optimization settings. It highlights the variability in results due to compiler optimizations, the unreliability of certain benchmarks, and the impact of code modifications on performance. The author notes that the function's behavior is influenced by factors like the compiler version, optimization levels, and the presence of specific code elements, making it difficult to draw definitive conclusions. The function is considered unreliable and may be removed from benchmarks due to its inconsistent performance across different environments.
 
amrali #:
did not work properly for the years after 2100

I’m sorry, but it’s 2026 now, and by 2100 there will most likely be no trace of the MT5 terminal left on the internet.

What’s the point of all this?

 
Vitaly Muzichenko #:

What’s the point of all this?

This is an algorithmic solution to the problem of handling alternating patterns that are disrupted by irregular events, such as leap years and century years. Beyond making the date conversion work correctly for all years, the approach demonstrates a general technique that can be reused to solve similar problems involving otherwise regular sequences with periodic exceptions.

The underlying idea is useful for financial markets – Trading cycles that follow regular business days but skip weekends and market holidays.
 

High-performance time functions valid for the full MQL5 datetime range (1/1/1970 to 31/12/3000):

(Derived from the Neri–Schneider calendar conversion algorithm: here)

// return 1..31
int TimeDay(const datetime t)
  {
   uint dayOfCentury = ((((uint)(t / 86400ULL) + 719162 + 306) << 2) | 3U) % 146097U;
   uint daySinceMarch1 = (dayOfCentury | 3U) % 1461U / 4;
   uint monthDayPacked = daySinceMarch1 * 2141 + 197913;
   return (int)(monthDayPacked & 0xFFFF) / 2141 + 1;
  }

// return 1..12
int TimeMonth(const datetime t)
  {
   uint dayOfCentury = ((((uint)(t / 86400ULL) + 719162 + 306) << 2) | 3U) % 146097U;
   uint daySinceMarch1 = (dayOfCentury | 3U) % 1461U / 4;
   uint monthDayPacked = daySinceMarch1 * 2141 + 197913;
   return (int)(monthDayPacked >> 16) - (daySinceMarch1 >= 306 ? 12 : 0);
  }

// return year (e.g., 2019)
int TimeYear(const datetime t)
  {
   uint days = ((((uint)(t / 86400ULL) + 719162) << 2) | 3U);
   uint century = days / 146097U;
   uint year = ((days % 146097U) | 3U) / 1461U;
   return (int)(100 * century + year) + 1;
  }

// return (0 - Sunday, 1 - Monday, ... , 6 - Saturday)
int TimeDayOfWeek(const datetime t)
  {
   return (int)(t / 86400ULL + THURSDAY) % 7;
  }

// return 0...365
int TimeDayOfYear(const datetime t)
  {
   return (int)(((((((uint)(t / 86400ULL) + 719162) << 2) | 3U) % 146097U) | 3U) % 1461U) / 4;
  }

// return 0...23
int TimeHour(const datetime t)
  {
   return (int)((t / 3600ULL) % 24U);
  }

// return 0...59
int TimeMinute(const datetime t)
  {
   return (int)((t / 60ULL) % 60ULL);
  }

// return 0...59
int TimeSeconds(const datetime t)
  {
   return (int)(t % 60ULL);
  }

The attached script file contains the source codes + complete validations.

eaf/algorithms/neri_schneider.hpp at main · cassioneri/eaf
eaf/algorithms/neri_schneider.hpp at main · cassioneri/eaf
  • cassioneri
  • github.com
Supplementary material to "Euclidean Affine Functions and their Application to Calendar Algorithms" - cassioneri/eaf