Get datetime from index of a bar that has not yet occured

Benoît Zuber  

I am writing an EA that places limit orders with expiration. Expiration is set to the datetime corresponding to a given number of bars after the current bar. I can get datetime from a past bar index using datetime CiTime::GetData(int index). However, I do not know how to get datetime from the index of a future bar. If markets were open 24/7, it would be straightforward to calculate it. But markets are not open 24/7 and therefore it would be easy to make mistakes in the calculation. Is there any existing function prediticting datetime from future bar index? Possibly querying the server?

Benoît Zuber  

Is there any existing function prediticting datetime from future bar index? Possibly querying the server?

This seems not to be the case. Therefore I wrote a set of functions to do the job. It assumes markets are open 24/7 except during weekends (from Friday 22:00:00 to Sunday 21:59:59), as well as on Christmas Day (24.12 22:00:00 - 25.12 21:59:59) and on New Year's Day (31.12 22:00:00 - 1.1 21:59:59). Timezone is GMT ( = UTC). When using this set of function, we need to take care of the offset to GMT of the server.

Here is the code. Hopefully this can be useful to somebody else.


//+------------------------------------------------------------------+
//|                                                     TimeTest.mq5 |
//|                                                     Benoit Zuber |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Benoit Zuber"
#property link      "https://www.mql5.com"
#property version   "1.00"

const int SECONDS_PER_MINUTE = 60;
const int SECONDS_PER_HOUR = SECONDS_PER_MINUTE*60;
const int SECONDS_PER_DAY = SECONDS_PER_HOUR*24;
const int SECONDS_PER_TRADE_WEEK = SECONDS_PER_DAY*5;
const int SECONDS_PER_CALENDAR_WEEK = SECONDS_PER_DAY*7;
const int FIRST_MARKET_CLOSE = SECONDS_PER_DAY + 22 * SECONDS_PER_HOUR;
const int CLOSE_MARKET_DURATION = 2 * SECONDS_PER_DAY;

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void OnInit()
  {
//---
   for(int i=1; i<20; i++)
     {

      datetime finalDT = DateAfterBars(startingDT, i);
      Print(i," ",startingDT,"   ",finalDT);
     }
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
int TimeSinceLastMarketClose(datetime time)
  {
   int timeSinceLastMarketClose = ((int) time - FIRST_MARKET_CLOSE) % SECONDS_PER_CALENDAR_WEEK;
   return timeSinceLastMarketClose;
  }

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
MqlDateTime GetXmasStartMql(datetime myDT)
  {
   MqlDateTime xmasStartMql, myMql;
   TimeToStruct(myDT, myMql);
   xmasStartMql.day=24;
   xmasStartMql.mon=12;
   xmasStartMql.hour=22;
   xmasStartMql.min=0;
   xmasStartMql.sec=0;
   xmasStartMql.year=myMql.year;
   return xmasStartMql;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
MqlDateTime GetNewYearStartMql(datetime myDT)
  {
   MqlDateTime newYearStartMql, myMql;
   TimeToStruct(myDT, myMql);
   newYearStartMql.day=31;
   newYearStartMql.mon=12;
   newYearStartMql.hour=22;
   newYearStartMql.min=0;
   newYearStartMql.sec=0;
   newYearStartMql.year=myMql.year;
   return newYearStartMql;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
int HolidayNumber(datetime initialDT, datetime finalDT)
  {
   if(PeriodSeconds() >= PeriodSeconds(PERIOD_W1))
      return 0;
   int nHoliday = 0;
   MqlDateTime tempInitialDTMql, finalDTMql, xmasStartMql, newYearStartMql;
   datetime xmasStart, tempInitialDT, newYearStart;
   tempInitialDT = initialDT;
   TimeToStruct(tempInitialDT, tempInitialDTMql);
   TimeToStruct(finalDT, finalDTMql);
   xmasStartMql = GetXmasStartMql(tempInitialDT);
   newYearStartMql = GetNewYearStartMql(tempInitialDT);
   while(tempInitialDTMql.year<=finalDTMql.year)
     {
      tempInitialDT=StructToTime(tempInitialDTMql);
      xmasStart = StructToTime(xmasStartMql);
      newYearStart = StructToTime(newYearStartMql);
      if((tempInitialDT <= xmasStart) && (finalDT >= xmasStart))
         nHoliday++;
      if((tempInitialDT <= newYearStart) && (finalDT >= newYearStart))
         nHoliday++;
      xmasStartMql.year++;
      newYearStartMql.year++;
      tempInitialDTMql.year++;
     }
   return nHoliday;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool IsHoliday(datetime myDT)
  {
   MqlDateTime xmasStartMql, newYearStart0Mql, newYearStart1Mql;
   datetime xmasStartDT, newYearStart0DT, newYearStart1DT;
   xmasStartMql = GetXmasStartMql(myDT);
   xmasStartDT = StructToTime(xmasStartMql);
   newYearStart1Mql = GetNewYearStartMql(myDT);
   newYearStart1DT = StructToTime(newYearStart1Mql);
   newYearStart0Mql = newYearStart1Mql;
   newYearStart0Mql.year = newYearStart1Mql.year - 1;
   newYearStart0DT = StructToTime(newYearStart0Mql);
   bool holiday = false;
   if((myDT >= xmasStartDT) && (myDT < (xmasStartDT + SECONDS_PER_DAY)))
      holiday = true;
   if((myDT >= newYearStart0DT) && (myDT < (newYearStart0DT + SECONDS_PER_DAY)))
      holiday = true;
   if((myDT >= newYearStart1DT) && (myDT < (newYearStart1DT + SECONDS_PER_DAY)))
      holiday = true;
   return holiday;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
datetime DateAfterBars(datetime initialDT, int nBars)
  {
   int nSeconds = nBars * PeriodSeconds();
   int nWeeks = nSeconds / SECONDS_PER_TRADE_WEEK;
   datetime finalDT = initialDT + nWeeks * SECONDS_PER_CALENDAR_WEEK + (nSeconds % SECONDS_PER_TRADE_WEEK);
   int nHoliday = HolidayNumber(initialDT, finalDT);
   finalDT = finalDT + nHoliday * SECONDS_PER_DAY;
   int timeBetweenFinalAndLastClose = TimeSinceLastMarketClose(finalDT);
   int timeBetweenInitialAndLastClose = TimeSinceLastMarketClose(initialDT);
   if(timeBetweenFinalAndLastClose < timeBetweenInitialAndLastClose)
      finalDT = finalDT +  CLOSE_MARKET_DURATION;
   if(IsHoliday(finalDT))
      finalDT = finalDT + SECONDS_PER_DAY;
   return finalDT;
  }
//+------------------------------------------------------------------+
Benoît Zuber  
Benoît Zuber:

This seems not to be the case. Therefore I wrote a set of functions to do the job. It assumes markets are open 24/7 except during weekends (from Friday 22:00:00 to Sunday 21:59:59), as well as on Christmas Day (24.12 22:00:00 - 25.12 21:59:59) and on New Year's Day (31.12 22:00:00 - 1.1 21:59:59). Timezone is GMT ( = UTC). When using this set of function, we need to take care of the offset to GMT of the server.

Here is the code. Hopefully this can be useful to somebody else.


I forgot to declare startingDT as an input in the previously posted code. Moreover, monthly timeframe must be dealt with differently. 

//+------------------------------------------------------------------+
//|                                                     TimeTest.mq5 |
//|                                                     Benoit Zuber |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Benoit Zuber"
#property link      "https://www.mql5.com"
#property version   "1.00"

const int SECONDS_PER_MINUTE = 60;
const int SECONDS_PER_HOUR = SECONDS_PER_MINUTE*60;
const int SECONDS_PER_DAY = SECONDS_PER_HOUR*24;
const int SECONDS_PER_TRADE_WEEK = SECONDS_PER_DAY*5;
const int SECONDS_PER_CALENDAR_WEEK = SECONDS_PER_DAY*7;
const int FIRST_MARKET_CLOSE = SECONDS_PER_DAY + 22 * SECONDS_PER_HOUR;
const int FIRST_MARKET_OPEN = 3 * SECONDS_PER_DAY + 22 * SECONDS_PER_HOUR;
const int CLOSE_MARKET_DURATION = 2 * SECONDS_PER_DAY;

input datetime startingDT = D'18.12.2017 00:00';

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void OnInit()
  {
//---
   for(int i=1; i<20; i++)
     {
      datetime finalDT = DateAfterBars(startingDT, i);
      Print(i," ",startingDT,"   ",finalDT);
     }
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
int TimeSinceLastMarketClose(datetime time)
  {
   int timeSinceLastMarketClose = ((int) time - FIRST_MARKET_CLOSE) % SECONDS_PER_CALENDAR_WEEK;
   return timeSinceLastMarketClose;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
int TimeSinceLastMarketOpen(datetime time)
  {
   int timeSinceLastMarketOpen = ((int) time - FIRST_MARKET_OPEN) % SECONDS_PER_CALENDAR_WEEK;
   return timeSinceLastMarketOpen;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
MqlDateTime GetXmasStartMql(datetime myDT)
  {
   MqlDateTime xmasStartMql, myMql;
   TimeToStruct(myDT, myMql);
   xmasStartMql.day=24;
   xmasStartMql.mon=12;
   xmasStartMql.hour=22;
   xmasStartMql.min=0;
   xmasStartMql.sec=0;
   xmasStartMql.year=myMql.year;
   return xmasStartMql;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
MqlDateTime GetNewYearStartMql(datetime myDT)
  {
   MqlDateTime newYearStartMql, myMql;
   TimeToStruct(myDT, myMql);
   newYearStartMql.day=31;
   newYearStartMql.mon=12;
   newYearStartMql.hour=22;
   newYearStartMql.min=0;
   newYearStartMql.sec=0;
   newYearStartMql.year=myMql.year;
   return newYearStartMql;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
int HolidayNumber(datetime initialDT, datetime finalDT)
  {
   if(PeriodSeconds() >= PeriodSeconds(PERIOD_W1))
      return 0;
   int nHoliday = 0;
   MqlDateTime tempInitialDTMql, finalDTMql, xmasStartMql, newYearStartMql;
   datetime xmasStart, tempInitialDT, newYearStart;
   tempInitialDT = initialDT;
   TimeToStruct(tempInitialDT, tempInitialDTMql);
   TimeToStruct(finalDT, finalDTMql);
   xmasStartMql = GetXmasStartMql(tempInitialDT);
   newYearStartMql = GetNewYearStartMql(tempInitialDT);
   while(tempInitialDTMql.year<=finalDTMql.year)
     {
      tempInitialDT=StructToTime(tempInitialDTMql);
      xmasStart = StructToTime(xmasStartMql);
      newYearStart = StructToTime(newYearStartMql);
      if((tempInitialDT <= xmasStart) && (finalDT >= xmasStart))
         nHoliday++;
      if((tempInitialDT <= newYearStart) && (finalDT >= newYearStart))
         nHoliday++;
      xmasStartMql.year++;
      newYearStartMql.year++;
      tempInitialDTMql.year++;
     }
   return nHoliday;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool IsHoliday(datetime myDT)
  {
   MqlDateTime xmasStartMql, newYearStart0Mql, newYearStart1Mql;
   datetime xmasStartDT, newYearStart0DT, newYearStart1DT;
   xmasStartMql = GetXmasStartMql(myDT);
   xmasStartDT = StructToTime(xmasStartMql);
   newYearStart1Mql = GetNewYearStartMql(myDT);
   newYearStart1DT = StructToTime(newYearStart1Mql);
   newYearStart0Mql = newYearStart1Mql;
   newYearStart0Mql.year = newYearStart1Mql.year - 1;
   newYearStart0DT = StructToTime(newYearStart0Mql);
   bool holiday = false;
   if((myDT >= xmasStartDT) && (myDT < (xmasStartDT + SECONDS_PER_DAY)))
      holiday = true;
   if((myDT >= newYearStart0DT) && (myDT < (newYearStart0DT + SECONDS_PER_DAY)))
      holiday = true;
   if((myDT >= newYearStart1DT) && (myDT < (newYearStart1DT + SECONDS_PER_DAY)))
      holiday = true;
   return holiday;
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
datetime DateAfterBars(datetime initialDT, int nBars)
  {
   int nSeconds = nBars * PeriodSeconds();
   int nWeeks = nSeconds / SECONDS_PER_TRADE_WEEK;
   datetime finalDT;
   if(PeriodSeconds() < PeriodSeconds(PERIOD_MN1))
      finalDT = initialDT + nWeeks * SECONDS_PER_CALENDAR_WEEK + (nSeconds % SECONDS_PER_TRADE_WEEK);
   else
      finalDT = DateAfterNMonths(initialDT, nBars);
   int nHoliday = HolidayNumber(initialDT, finalDT);
   finalDT = finalDT + nHoliday * SECONDS_PER_DAY;
   int timeBetweenFinalAndLastClose = TimeSinceLastMarketClose(finalDT);
   int timeBetweenInitialAndLastClose = TimeSinceLastMarketClose(initialDT);
   if(timeBetweenFinalAndLastClose < timeBetweenInitialAndLastClose)
      finalDT = finalDT +  CLOSE_MARKET_DURATION;
   if(IsHoliday(finalDT))
      finalDT = finalDT + SECONDS_PER_DAY;
   return finalDT;
  }
//+------------------------------------------------------------------+
//| we add n Months, reset day of month to 1 and make sure we are not+
//| in a market closed period                                        +
//+------------------------------------------------------------------+
datetime DateAfterNMonths(datetime initialDT, int nBars)
  {
   MqlDateTime initialMQL, finalMQL;
   datetime finalDT;
   TimeToStruct(initialDT, initialMQL);
   int nYears = nBars / 12;
   int nRestMonths = nBars % 12;
   int finalMonth;
   finalMQL = initialMQL;
   finalMonth = finalMQL.mon + nRestMonths;
   if(finalMonth > 12)
     {
      finalMQL.mon = finalMonth - 12;
      nYears ++;
     }
   else
      finalMQL.mon = finalMonth;
   finalMQL.year = finalMQL.year + nYears;
   finalMQL.hour=22;
   finalMQL.min=0;
   finalMQL.sec=0;
   if(finalMQL.mon == 1)
      finalMQL.day = 2;
   else
      finalMQL.day = 1;
   finalDT = StructToTime(finalMQL);
   while(TimeSinceLastMarketClose(finalDT) < TimeSinceLastMarketOpen(finalDT))
      finalDT = finalDT + SECONDS_PER_DAY;
   return finalDT;
  }
//+------------------------------------------------------------------+

//+------------------------------------------------------------------+
Reason: