English 中文 Español Deutsch 日本語 Português
preview
Работаем со временем (Часть 2): Функции

Работаем со временем (Часть 2): Функции

MetaTrader 5Примеры |
2 872 37
Carl Schreiber
Carl Schreiber

Глобальные переменные

Вместо того, чтобы обращаться к брокеру, который скорее всего даст недостаточно полный ответ (а кто захочет объяснять, куда пропал торговый час?), мы сами посмотрим, по какому времени приходят от них котировки в те недели, когда переводят часы. Но конечно же, это мы будем делать не вручную — пусть за нас работает программа.

В этой статье мы продолжим работать с include-файлом DealingWithTime.mqh, с которым работали в первой части. В этом файле до функций и после макро-подстановок идут необходимые переменные, которые объявлены как глобальные переменные:

//--- глобальные переменные для смены времени
int      DST_USD=0,                             // фактическое смещение времени для США
         DST_EUR=0,                             // фактическое смещение времени для Европы
         DST_AUD=0,                             // фактическое смещение времени для Австралии
         DST_RUS=0;                             // D'2014.10.26 02:00', -10800,


Эти переменные DST_USD, DST_EUR, и т.д. будут принимать фактическое значение смещения времени в соответствующих странах и регионах — США, Европа и т.д. Значения переменных будут заполняться функциями. В нормальное зимнее время значения равны нулю: время в этот период не смещается.

Далее у нас идут переменные с датой предстоящего перевода часов. Они указывают, когда нужно будет рассчитывать новые значения переменных, чтобы не проводить ненужные расчеты до этого и не тратить вычислительные ресурсы:

datetime nxtSwitch_USD,                         // дата следующего перехода
         nxtSwitch_EUR,                         // дата следующего перехода
         nxtSwitch_AUD,                         // дата следующего перехода
         nxtSwitch_RUB = D'2014.10.26 02:00';   // для России по-другому :(


Тут есть небольшое отличие в настройках для России, мы рассмотрим ее чуть позже.

Данная структура и ее глобальная переменная являются сердцем всей программы. :)

struct _OffsetBroker
  {
   int   USwinEUwin,                            // US=Winter & EU=Winter
         USsumEUsum,                            // US=Summer & EU=Summer
         USsumEUwin,                            // US=Summer & EU=Winter
         actOffset,                             // фактическое смещение времени брокера
         secFxWiWi,                             // продолжительность работы рынка в сек
         secFxSuSu,                             // продолжительность работы рынка в сек
         secFxSuWi,                             // продолжительность работы рынка в сек
         actSecFX;                              // фактическая продолжительность работы рынка в сек
   bool  set;                                   // вес установили?
  };
_OffsetBroker OffsetBroker;


Задаем значения смещения у брокера для трех соответствующих периодов, а также продолжительность работы рынка в эти периоды. Также пишем фактическое значение продолжительности и проверяем, установлены ли все необходимые значения. Название глобальной переменной — OffsetBroker, мы встретим ее несколько раз.


Основная функция для определения перевода часов у брокера

С помощью функции

setBokerOffset();

советник, индикатор или скрипт сможет самостоятельно определить, когда у брокера происходит перевод часов и как. Она добавлена в начало кода в приложенном скрипте, после функции Start(). Она определяет эти значения у брокера для соответствующих периодов (летнее время, зимнее время и промежуточное время), на основе которых определяются все остальные значения времени. Эта функция, как и все другие, что мы рассматривали в первой статье, содержатся в файле. После объявления переменных, инициализации и обнуления соответствующих глобальных переменных:

   datetime dateEUR,dateUSD,dateAUD,dateNxt,                   // дата переходя для EU, AU и US
            arrTme[];                                          // массив для копирования времени
   int b;
   OffsetBroker.USsumEUwin =
      OffsetBroker.USsumEUsum =
         OffsetBroker.USwinEUwin = INT_MIN;
   nxtSwitch_USD = nxtSwitch_EUR = nxtSwitch_AUD = 0;          // сбросить переменные


получаем выходные, когда переводят часы:

//--- переход на зимнее время в Австралии, Европе и США в 2020
   if(IS_DEBUG_MODE)
      Print("\n2nd half-year 2020 for ",AccountInfoString(ACCOUNT_COMPANY), "DebugMode: ",IS_DEBUG_MODE);

   nextDST("EUR", D'2020.06.21 14:00');                        // EUR: получаем DST и устанавливаем дату след перехода
   b = CopyTime("EURUSD",PERIOD_H1,nxtSwitch_EUR,1,arrTme);    // получаем время последнего бара H1 перед переводом часов в Европе
   dateEUR = arrTme[0];                                        // последний час работы в пятницу перед выходными

   nextDST("USD", D'2020.06.21 14:00');                        // USD: получаем DST и устанавливаем дату след перехода
   b = CopyTime("EURUSD",PERIOD_H1,nxtSwitch_USD,1,arrTme);    // получаем время последнего бара H1 перед переводом часов в США
   dateUSD = arrTme[0];                                        // последний час работы в пятницу перед выходными

   nextDST("AUD", D'2020.06.21 14:00');                        // AUD: получаем DST и устанавливаем дату след перехода
   b = CopyTime("EURUSD",PERIOD_H1,nxtSwitch_AUD,1,arrTme);    // получаем время последнего бара H1 перед переводом часов в Австралии
   dateAUD = arrTme[0];                                        // последний час работы в пятницу перед выходными

   dateNxt = fmax(nxtSwitch_EUR,nxtSwitch_USD)+WeekInSec;      // получаем след выходные
   b = CopyTime("EURUSD",PERIOD_H1,dateNxt,1,arrTme);          // получаем время последнего бара H1 перед выходными
   dateNxt = arrTme[0];                                        // последний час работы в пятницу перед выходными


Для простоты большая часть выполняется автоматически в режиме отладки: if(IS_DEBUG_MODE). Таким образом, если запустить скрипт в режиме отладки (F5), можно посмотреть все детали, а если запустить его прямо на графике, будут показано только самое важное.

Для всех трех часовых поясов вызов функции, например nextDST("EUR", D'2020.06.21 14:00'), сначала рассчитывает соответствующие разницы во времени для Европы, а потом — следующих перевод часов. Июнь попадает на летнее время, а следующий переход будет на зимнее время. Следующей строкой получаем время открытия последнего часового бара H1 в пятницу, предшествующую этим выходным — это будет основа для наших расчетов. См. пункт 4 предположений в конце первой статьи:

          4. Если отсутствует какие-либо часы между 17:00 в пятницу и 17:00 в воскресенье, тогда будет не хватать котировок воскресенья до получения самой первой котировки, а не последних котировок пятницы.

Я решил использовать время H1 и EURUSD. У этого символа, пожалуй, самая длинная история котировок. Поэтому, если торговля на рынке закрывается в 17:00 по Нью-Йорку, последний рабочий час (или последняя часовая свеча) открывается в 16:00. Именно этот час нас и интересует. Получается, что первый час после выходных — 17:00 воскресенья в Нью-Йорке. Для полноты картины также определим смену часов в Австралии, однако использовать его не будем. Далее, в первые выходные после смены часов, определяем время следующего перевода часов.

Затем используется функция chckFriday(...) для расчета смещения времени брокера в соответствии с выбранным периодом. Расчет происходит основе времени 16:00 по Нью-Йорку. Функция также включена в include-файл.

   chckFriday(dateEUR,"EUR");                                  // функция определения смещения брокера для указанной пятницы
   chckFriday(dateUSD,"USD");                                  // функция определения смещения брокера для указанной пятницы
   chckFriday(dateNxt,"NXT");                                  // функция определения смещения брокера для указанной пятницы


По тому же принципу рассчитываются оставшиеся смены часов для второго полугодия, которые нам, собственно, не нужны, и которые можно закомментировать:

   if(IS_DEBUG_MODE)
      Print("\n1st half-year 2021 for ",AccountInfoString(ACCOUNT_COMPANY), "DebugMode: ",IS_DEBUG_MODE);
   nxtSwitch_USD = nxtSwitch_EUR = nxtSwitch_AUD = 0;

   nextDST("AUD", D'2021.01.21 14:00');                        // AUD: получаем DST и устанавливаем дату след перехода
   b = CopyTime("EURUSD",PERIOD_H1,nxtSwitch_AUD,1,arrTme);    // получаем время последнего бара H1 перед переводом часов в Европе
   dateAUD = arrTme[0];                                        // последний час работы в пятницу перед выходными

...
   chckFriday(dateUSD,"USD");                                  // функция определения смещения брокера для указанной пятницы
   chckFriday(dateEUR,"EUR");                                  // функция определения смещения брокера для указанной пятницы
   chckFriday(dateNxt,"NXT");                                  // функция определения смещения брокера для указанной пятницы


Наконец после того, как определили сдвиг времени брокера и присвоили значения соответствующим полям, значения перевода часов (nxtSwitch_USD = nxtSwitch_EUR = nxtSwitch_AUD = 0) обнуляются для последующего использования. И хотя перерасчет должен быть только в том случае, если в ходе истории "случались" выходные с переводом часов, все же лучше сбросить заранее, чтобы потом не забыть. Затем проверяем, заданы ли все необходимые переменные. Результат выводится в журнал экспертов:

   nxtSwitch_USD = nxtSwitch_EUR = nxtSwitch_AUD = 0;          // сбросим все переменные
   if(OffsetBroker.USsumEUwin != INT_MIN
      && OffsetBroker.USsumEUsum != INT_MIN
      && OffsetBroker.USwinEUwin != INT_MIN
     )
      OffsetBroker.set = true;
   else
      OffsetBroker.set = false;
   if(OffsetBroker.set)
      Print("\nTime Offset of ",AccountInfoString(ACCOUNT_COMPANY),": ",
            "\nUS=Winter & EU=Winter (USwinEUwin) = ",OffsetBroker.USwinEUwin,
            "\nUS=Summer & EU=Summer (USsumEUsum) = ",OffsetBroker.USsumEUsum,
            "\nUS=Summer & EU=Winter (USsumEUwin) = ",OffsetBroker.USsumEUwin,
            "\n");
   else
      Print(__FILE__,"[",__LINE__,"] Assigning the broker offset went wrong - somehow.");
   return(OffsetBroker.set);


Если все хорошо, появится запись наподобие такой:

Time Offset of MetaQuotes Software Corp.:
US=Winter & EU=Winter (USwinEUwin)       =   -7200
US=Summer & EU=Summer (USsumEUsum) = -10800
US=Summer & EU=Winter (USsumEUwin)    =   -7200

Любой пользователь сможет использовать эти значения для входных переменных вместо того, чтобы добавлять функции до начала работы самого советника в тестере стратегий или в реальном времени на графике. Пример я покажу в конце статьи.


Определение и установка сдвига времени брокера

Давайте поближе посмотрим на функцию chckFriday(...). Она определяет сдвиг времени брокера в разные периоды и назначает значение соответствующему полю глобальной переменной OffsetBroker на основе структуры _OffsetBroker. В структуре три поля:

   int   USwinEUwin,                            // US=Winter & EU=Winter
         USsumEUsum,                            // US=Summer & EU=Summer
         USsumEUwin,                            // US=Summer & EU=Winter


Им назначаются значения сдвига времени брокера в соответствующие периоды. Это периоды:

  • Одинаковое время для обоих регионов — или зимнее (стандартное), или летнее.
  • В США уже (еще) летнее время, а в Европе еще (уже) зимнее.

Обратной ситуации, когда в США уже (еще) зимнее, а в Европе еще (уже) летнее, не существует. Возникает вопрос: почему нет четвертой категории и что с Австралией?

Вот правила перехода:

  • Европа: последнее воскресенье октября и последнее воскресенье марта
  • США: первое воскресенье ноября и второе воскресенье марта
  • Австралия: первое воскресенье ноября и последнее воскресенье марта


Итак, что с первым вопросом. Европа переходит на зимнее время на одну-две недели раньше США: там в США еще летнее время, а в Европе уже зимнее, поэтому значение присваивается полю USsumEUwin переменной OffsetBroker. Затем весной США раньше переходит на летнее время. Получается, в США уже неделю-две летнее время, а в Европе еще зимнее. Значение так же присваивается полю USsumEUwin переменной OffsetBroker. Так что обратной ситуации, когда в Европе летнее время, а в США еще зимнее, не существует. Это фактически устраняет необходимость расчета смещения времени брокера для обоих периодов перевода часов, осенью и весной. Тем не менее мы производим расчет, просто для полноты и контроля.

Что касается второго вопроса. Австралия переводит часы в ноябре одновременно с США, а весной переводит часы как Европа. То есть нет новых выходных дней, в которые совершался бы переход. Однако часы в Австралии переводят вперед на 1 час, когда в Европе и США зима, потому что Рождество и Новый год там приходятся на лето.

Располагая временем перевода часов для определенного периода, можно также посчитать длительность работы форекс-рынка в соответствующие недели: эти значения хранятся в полях secFxWiWiВи, secFxSuSu и secFxSuWi, а текущее фактическое значение — в ActSecFX. В разделе "Применение" в конце статьи показано, что с этим делать.

Чтобы присвоить значения, их надо сначала рассчитать. После объявления переменных и сброса глобальных переменных рассчитываются значения смещения в Европе и США для выбранного времени брокера tB:

//+------------------------------------------------------------------+
//| вспомогательная функция для определения смещения времени брокера |
//+------------------------------------------------------------------+
int chckFriday(
   datetime tB,                                                // время брокера: последний час пятницы
   string cmt=""                                               // текст для начала строки
)
  {

   int hNY, hGMT, hTC, hDiff;
   nxtSwitch_AUD = nxtSwitch_USD = nxtSwitch_EUR = 0;          // сбрасываем
   nextDST("EUR",tB);                                          // получаем смещение в Европе указанного времени tB
   nextDST("USD",tB);                                          // получаем смещение в США указанного времени tB

Здесь tB — это начало последнего часа в пятницу, когда в Нью-Йорке 16:00. Это основа дальнейших расчетов, для этого времени мы можем рассчитать GMT:

tGMT = tNY + (NYShift + DST_USD)

то есть, сдвиг времени брокера по GMT. Вот как определяется сдвиг: от последнего часа в пятницу по времени tB отнимаем количество секунд, прошедших с начала дня SoB(tB). Получаем 00:00 дня, а затем добавляем количество секунд до времени 16:00 (16*3600). Так получаем время по Нью-Йорку, на основе которого рассчитываем время GMT: NYShift + DST_USD. Теперь можно легко определить смещение времени брокера от GMT, а полученное значение записать в соответствующее поле переменной OffsetBroker.
В функции расчеты идут в часах, а не секундах, с макроподстановкой HoD() (Hour of Day, час суток), чтобы было удобнее работать:

   hNY   = HoD(tB - SoD(tB) + 16*3600);                        // получаем час по Нью-Йорку
   hGMT  = HoD(tB - SoD(tB) + 16*3600 + NYShift + DST_USD);    // получаем час по GMT
   hTC   = HoD(tB);                                            // получаем час заданного времени
   hDiff = hGMT - HoD(tB);                                     // разница между GMT и временем брокера


Не так и сложно.

Следующая часть добавлена для проверки. Он проверяет, чтобы не возникла невозможная ситуация, когда в США лето, а в Европе зима:

   if(DST_USD==0 && DST_EUR!=0)                                // такого быть не должно
      Alert(__LINE__," ",TOSTR(DST_USD),TOSTR(DST_EUR),"  USwin && EUsum");


Теперь можем использовать найденную разницу и продолжительность работы рынка:

//--- установим смещение брокера для различных временных ситуаций:
   if(DST_USD+DST_EUR==0)                                      // оба значения в зимнем времени
     {
      OffsetBroker.actOffset = OffsetBroker.USwinEUwin = hDiff*3600;
      OffsetBroker.actSecFX  = OffsetBroker.secFxWiWi = SoW(tB);
     }
   else
      if(DST_USD == DST_EUR)                                   // оба в летнем времени
        {
         OffsetBroker.actOffset = OffsetBroker.USsumEUsum = hDiff*3600;
         OffsetBroker.actSecFX  = OffsetBroker.secFxSuSu = SoW(tB);
        }
      else
         if(DST_USD!=0 && DST_EUR==0)                          // US:лето EU:зима
           {
            OffsetBroker.actOffset = OffsetBroker.USsumEUwin = hDiff*3600;
            OffsetBroker.actSecFX  = OffsetBroker.secFxSuWi = SoW(tB);
           }



Далее записываем в журнал все найденные значения и возвращаем последнее фактическое смещение:

//--- по кругу рассчитываем NY->GMT->Broker->GMT->NY <= последнее NY должно быть всегда 16!!
   Print(cmt,": ",DoWs(tB),TimeToString(tB),": ",TOSTR(hNY),TOSTR(hGMT),TOSTR(hTC),TOSTR(hDiff),
         " BrokerTime => GMT: ",TimeToString(tB+OffsetBroker.actOffset),
         " => tNY: ",TimeToString((tB + OffsetBroker.actOffset)-(NYShift + DST_USD)),
         "  End-FX after: ",OffsetBroker.actSecFX/3600,"h"
        );
   return(OffsetBroker.actOffset);


Выглядит это следующим образом:

EUR: Fr.2020.10.23 23:00: hNY:16  hGMT:20  hTC:23  hDiff:-3   BrokerTime => GMT: 2020.10.23 20:00 => tNY: 2020.10.23 16:00  End FX in: 143h
USD: Fr.2020.10.30 22:00: hNY:16  hGMT:20  hTC:22  hDiff:-2   BrokerTime => GMT: 2020.10.30 20:00 => tNY: 2020.10.30 16:00  End FX in: 142h
NXT: Fr.2020.11.06 23:00: hNY:16  hGMT:21  hTC:23  hDiff:-2   BrokerTime => GMT: 2020.11.06 21:00 => tNY: 2020.11.06 16:00  End FX in: 143h

Давайте посмотрим, что же тут выводится. Сначала Европа переводит часы 25 октября. Через неделю, 1 ноября, часы переводят в США. В период между двумя этими датами последний час пятницы на сервере MetaQuotes начинается в 22:00, а не в 23:00, а заканчивается неделя этой свечой, то есть через 142часа вместо обычных 143. 143 или 142 часа? В неделе на рынке форекс 120 часов: 5*24=120? Секунды недели (SoW(), Seconds of Week) и похожие функции работают с календарной неделей, которая начинается в воскресенье в 00:00. Но с воскресенья 00:00 по пятницу 23:00 теперь 6* 24-1 = 143 часа. На основе это значения для любого заданного момента недели можно рассчитать оставшееся времени работы ранка до конца недели.

На основе этих трех строчек также проверяется логика и расчеты. Также можно определить местное время по Гринвичу (GMT). Слева направо: таймстамп брокера, расчетное время по Нью-Йорку, hNY: 16, час по Гринвичу на основе времени в Нью-Йорке, час времени брокера и сдвиг: -2 или -3. Во второй части расчет GMT от времени брокера (tB+OffsetBroker.actOffset), а затем на основе GMT - снова время в Нью-Йорке ((tB + OffsetBroker.actOffset)-(NYShift + DST_USD)). Значение для Нью-Йорка всегда должно получаться tNY=16:00. Ниже выполняется вторая проверка для рандомного времени в истории и для других брокеров.


Расчет выходных, когда переводят часы

Прежде чем перейти к работе со временем на истории EURUSD, давайте посмотрим на основополагающую функцию расчетов - nextDST(...).
Функция вызывается с параметром zone для часового пояса "USD", "EUR" или "AUD" (хотя по факту AUD нам и не нужно) и параметром t - текущее время, обычно это текущее время брокера TimeCurrent(). Сначала проверяем, нужен ли вообще пересчет (в данном примере для EUR):

void nextDST(string zone, datetime t)
  {
   if((zone == "EUR") && t < nxtSwitch_EUR)
     {
      if(IS_DEBUG_MODE)
         Print("no change as time < nxtSwitch_EUR");
      return;
     }
...

Здесь становится понятно, почему так важно сбрасывать значения nxtSwitch_EUR в начале теста — в противном случае никаких пересчетов может не выполняться на протяжении всего теста.

После объявления переменной и инициализации подходим к сути функции. Эта функция не моя. Некоторое время назад я где-то в интернете нашел алгоритм, определяющий определенный день в месяце. Он используется для определения летнего или зимнего времени для выбранного момента. Сам по себе алгоритм несложный:

  1. Определяем день месяца - воскресенье, когда переводят часы.
  2. На основе него создаем дату.
  3. Находим ближайшее следующее воскресенье, когда будут переводить часы.
  4. Устанавливаем сдвиг, 0h или -1h, и воскресенье следующего перевода часов.

Магия этого алгоритма заключается в строке кода, которая определяет день месяца, когда переводят часы. В Европе это последнее воскресенье марта, рассчитывается так (как я уже сказала, идея формулы не моя):

d = (int)(31 - MathMod((4 + MathFloor(5*y/4)), 7));         // найдем последнее воскресенье марта - переход на летнее в Европе
В результате для 2021 года последнее воскресенье марта выводится как d=25:

31 - MOD(ROUNDDOWN(5*2021/4);7) = 25.


Затем формируется временная метка дня перевода часов в Европе: 25 марта 2021 г., последнее воскресенье марта:
spr = StringToTime(""+(string)y+".03."+(string)d+" 03:00"); // преобразуем в datetime

Все даты рассчитываются аналогичным образом.
Далее идет большой раздел кода, в котором определяется текущий переход на летнее или зимнее время и следующая дата перехода в Европе для выбранной даты. В итоге нам нужно найти три периода в году: перед первым переходом, между первым и вторым, после второго перехода. Следующий переход будет уже в следующем году, и это необходимо учитывать:

   if(zone == "EUR")
     {
      d = (int)(31 - MathMod((4 + MathFloor(5*y/4)), 7));         // найдем последнее воскресенье марта - переход на летнее в Европе
      spr = StringToTime(""+(string)y+".03."+(string)d+" 03:00"); // преобразуем в datetime
      if(t < spr)
        {
         DST_EUR = 0;                                             // нет смещения
         nxtSwitch_EUR = spr;                                     // время следующего перевода часов
         if(IS_DEBUG_MODE)
            Print(zone,"-DST for ",TimeToString(t)," DST: ",StringFormat("% 5i",DST_EUR),"  nxtSwitch: ",DoWs(nxtSwitch_EUR)," ",TimeToString(nxtSwitch_EUR));
         return;
        }
      d = (int)(31 - MathMod((1 + MathFloor(5*y/4)), 7));         // determine the last Sunday in October for the EU switch
      aut = StringToTime(""+(string)y+".10."+(string)d+" 03:00"); // преобразуем в datetime
      if(t < aut)
        {
         DST_EUR =-3600;                           // = +1h => 09:00 London time = GMT+05h+DST_EU = GMT+0+1 = GMT+1;
         nxtSwitch_EUR = aut;                                     // время след перехода
         if(IS_DEBUG_MODE)
            Print(zone,"-DST for ",TimeToString(t)," DST: ",StringFormat("% 5i",DST_EUR),"  nxtSwitch: ",DoWs(nxtSwitch_EUR)," ",TimeToString(nxtSwitch_EUR));
         return;
        }
      y++;                                                        // пересчитываем переход весной для след года
      d = (int)(31 - MathMod((4 + MathFloor(5*y/4)), 7));         // найдем последнее воскресенье марта - переход на летнее в Европе
      spr = StringToTime(""+(string)y+".03."+(string)d+" 03:00"); // преобразуем в datetime
      if(t < spr)
        {
         DST_EUR = 0;                                             // нет смещения
         nxtSwitch_EUR = spr;                                     // время следующего перевода часов
         if(IS_DEBUG_MODE)
            Print(zone,"-DST for ",TimeToString(t)," DST: ",StringFormat("% 5i",DST_EUR),"  nxtSwitch: ",DoWs(nxtSwitch_EUR)," ",TimeToString(nxtSwitch_EUR));
         return;
        }
      Print("ERROR for ",zone," @ ",TimeToString(t)," DST: ",StringFormat("% 5i",DST_EUR),"  nxtSwitch: ",DoWs(nxtSwitch_EUR)," ",TimeToString(nxtSwitch_EUR),"  winter: ",TimeToString(aut),"  spring: ",TimeToString(spr));
      return;
     }


Показаны все три периода в одном году:

  1. Перед переходом на летнее время в марте.
  2. До перехода на зимнее время в октябре/ноябре.
  3. Зимнее время до перехода на летнее в следующем году.

Периоды рассчитываются для EUR, USD и AUD.

Одиночный вызов функции nextDST(..), например:

nextDST("EUR", D'2019.02.05 20:00');
nextDST("EUR", D'2019.06.05 20:00');
nextDST("EUR", D'2019.11.20 20:00');
        
nextDST("USD", D'2019.02.05 20:00');
nextDST("USD", D'2019.06.05 20:00');
nextDST("USD", D'2019.11.20 20:00');
        
nextDST("AUD", D'2019.02.05 20:00');
nextDST("AUD", D'2019.06.05 20:00');
nextDST("AUD", D'2019.11.20 20:00');


проверяет все три момента в году для трех регионов. И вот какой результат:

Европа: последнее воскресенье марта и последнее воскресенье октября:
    EUR-DST for 2019.02.05 20:00 DST:        0   nxtSwitch: Su. 2019.03.31 03:00
    EUR-DST for 2019.06.05 20:00 DST: -3600   nxtSwitch: Su. 2019.10.27 03:00
    EUR-DST for 2019.11.20 20:00 DST:        0   nxtSwitch: Su. 2020.03.29 03:00

США: 2-е воскресенье марта и 1-е воскресенье ноября:
    USD-DST for 2019.02.05 20:00 DST:        0   nxtSwitch: Su. 2019.03.10 03:00
    USD-DST for 2019.06.05 20:00 DST: -3600   nxtSwitch: Su. 2019.11.03 03:00
    USD-DST for 2019.11.20 20:00 DST:        0   nxtSwitch: Su. 2020.03.08 03:00

Австралия: первое воскресенье ноября и последнее воскресенье марта
    AUD-DST for 2019.02.05 20:00 DST: -3600   nxtSwitch: Su. 2019.03.31 03:00
    AUD-DST for 2019.06.05 20:00 DST:        0   nxtSwitch: Su. 2019.11.03 03:00
    AUD-DST for 2019.11.20 20:00 DST: -3600   nxtSwitch: Su. 2020.03.29 03:00

Перевод времени в Австралии может сбивать с толку, но это связано с тем, что Австралия расположена в южном полушарии, где начало года приходится на середину лета, поэтому переход на летнее время происходит, когда в Европе зима.


Перевод часов в России

Отдельно пришлось написать код для работы со временем в России. Поскольку в России были изменения, связанные с переводом часов, я решил использовать двумерный массив, который содержит время переходов. Запросить ее можно с помощью этой функции:

long RussiaTimeSwitch[][2] =
  {
   D'1970.01.00 00:00', -10800,
   D'1980.01.00 00:00', -10800,
   D'1981.04.01 00:00', -14400,
...
   D'2012.01.00 00:00', -14400,
   D'2014.10.26 02:00', -10800,
   D'3000.12.31 23:59', -10800
  };
int SzRussiaTimeSwitch = 67;                    // ArraySize of RussiaTimeSwitch

//+------------------------------------------------------------------+
//| Russian Time Switches                                            |
//+------------------------------------------------------------------+
void offsetRubGMT(const datetime t)
  {
   int i = SzRussiaTimeSwitch; //ArrayRange(RussiaTimeSwitch,0); 66
   while(i-->0 && t < RussiaTimeSwitch[i][0])
      continue;
// t >= RussiaTimeSwitch[i][0]
   nxtSwitch_RUB  = (datetime)RussiaTimeSwitch[fmin(SzRussiaTimeSwitch-1,i+1)][0];
   DST_RUS        = (int)RussiaTimeSwitch[fmin(SzRussiaTimeSwitch-1,i+1)][1];
   return;
  }
//+------------------------------------------------------------------+




Функция, обновляющая значения

Мы подошли к последней функции в этом проекте. Эта функция обеспечивает актуальность всех важных значений:

//+------------------------------------------------------------------+
//| функция определения смещения брокера для указанного tB           |
//+------------------------------------------------------------------+
void checkTimeOffset(datetime tB)
  {
   if(tB < nxtSwitch_USD && tB < nxtSwitch_EUR && tB < nxtSwitch_AUD)
      return;                                                  // nothing has changed, return


В самом начале функция спрашивает, нужно ли установить смещение времени (и дату следующего перехода). Если нет, работа функции завершается.

В противном случае функция nextDST() рассчитывает соответствующие значения для EUR, USD, AUD и RUB (описано выше):

   if(tB>nxtSwitch_USD)
      nextDST("USD", tB);                                      // был переход в США
   if(tB>nxtSwitch_EUR)
      nextDST("EUR", tB);                                      // был переход в Европе
   if(tB>nxtSwitch_AUD)
      nextDST("AUD", tB);                                      // был переход в Австралии
   if(tB>nxtSwitch_RUB)
      nextDST("RUB", tB);                                      // был переход в России


Для определения сдвига времени брокера нужны только USD и EUR. А AUD и RUB просто дают актуальные значения для соответствующих стран. Если они вам не нужны, можете закомментировать //.

Далее, в зависимости от периода, в поле OffsetBroker.actOffset прописывается сдвиг времени брокера, действующий с соответствующего периода, а в OffsetBroker.actSecFX — время работы рынка в текущий момент:

   if(DST_USD+DST_EUR==0)                                      // оба значения в зимнем времени
     {
      OffsetBroker.actOffset = OffsetBroker.USwinEUwin;
      OffsetBroker.actSecFX  = OffsetBroker.secFxWiWi;
     }
   else
      if(DST_USD == DST_EUR)                                   // оба в летнем времени
        {
         OffsetBroker.actOffset = OffsetBroker.USsumEUsum;
         OffsetBroker.actSecFX  = OffsetBroker.secFxSuSu;
        }
      else
         if(DST_USD != DST_EUR)                                // US:лето EU:зима
           {
            OffsetBroker.actOffset = OffsetBroker.USsumEUwin;
            OffsetBroker.actSecFX  = OffsetBroker.secFxSuWi;
           }


И все. Мы рассмотрели все необходимые функции: функция определения смещения времени брокера по котировке и функция, которая всегда определяет текущее смещение, на основе которого можно легко определить время по Гринвичу (GMT) и, соответственно, локальное время, даже при работе в тестере стратегий.

Есть два способа работы с ними.


Скрипт, демонстрирующий настройку и использование

К статье приложен файл скрипта DealingWithTimeScript.mq5:

#include <DealingWithTime.mqh>
//+------------------------------------------------------------------+
//| Finding the broker offsets                                       |
//+------------------------------------------------------------------+
void OnStart()
  {
//--- шаг 1: устанавливаем смещение у брокера зимой, летом и в промежутке
   bool isTimeSet = setBokerOffset();
   if(!isTimeSet)
     {
      Alert("setBokerOffset failed");
      return;
     }


В журнал советника будет выведено:

EUR: Fr.2020.10.23 23:00: hNY:16  hGMT:20  hTC:23  hDiff:-3   BrokerTime => GMT: 2020.10.23 20:00 => tNY: 2020.10.23 16:00  End-FX after: 143h
USD: Fr.2020.10.30 22:00: hNY:16  hGMT:20  hTC:22  hDiff:-2   BrokerTime => GMT: 2020.10.30 20:00 => tNY: 2020.10.30 16:00  End-FX after: 142h
NXT: Fr.2020.11.06 23:00: hNY:16  hGMT:21  hTC:23  hDiff:-2   BrokerTime => GMT: 2020.11.06 21:00 => tNY: 2020.11.06 16:00  End-FX after: 143h
USD: Fr.2021.03.12 23:00: hNY:16  hGMT:21  hTC:23  hDiff:-2   BrokerTime => GMT: 2021.03.12 21:00 => tNY: 2021.03.12 16:00  End-FX after: 143h
EUR: Fr.2021.03.26 22:00: hNY:16  hGMT:20  hTC:22  hDiff:-2   BrokerTime => GMT: 2021.03.26 20:00 => tNY: 2021.03.26 16:00  End-FX after: 142h
NXT: Fr.2021.04.02 23:00: hNY:16  hGMT:20  hTC:23  hDiff:-3   BrokerTime => GMT: 2021.04.02 20:00 => tNY: 2021.04.02 16:00  End-FX after: 143h

Offset for MetaQuotes Software Corp.: 
US=Winter & EU=Winter (USwinEUwin)      =  -7200
US=Summer & EU=Summer (USsumEUsum) = -10800
US=Summer & EU=Winter (USsumEUwin)   =   -7200

Найденные значения можно присваивать входным переменным. Это пример для следующей главы. Мы уже разобрали выше, как работает эта часть. Моделируем путь от прошлого к настоящему часу работы брокера, рассчитываем и печатаем значения GMT New York и оставшееся время, в течение которого рынок форекс будет открытым для случайно выбранных временных меток.

Всю историю для таймфрейма 1h по EURUSD можно получить с помощью функции CopyTime("EURUSD",PERIOD_H1,datetime(0),TimeCurrent(),arr1h).

Чтобы не утонуть в куче данных, посмотрим данные для случайно определенных баров. Для этого выберем количество данных. Если задать 5, баров будет около 10, потому что в случайном среднем расстояние шага составляет примерно половину sz/5:

//--- шаг 2: получаем котировки (у нас только H1)
   datetime arr1h[], tGMT, tNY, tLeft;
   CopyTime("EURUSD",PERIOD_H1,datetime(0),TimeCurrent(),arr1h);
   int b       = 0,
       sz      = ArraySize(arr1h)-1,                  // oldest time stamp
       nChecks = sz/5,                                // ~2*5+1 randomly chosen bars
       chckBar = MathRand()%nChecks;                  // index of the first bar to check


Пройдем по всем барам, от самого старшего до текущего, как при тестировании или оптимизации в тестере стратегий: while(++b<=sz). Будем проверять ситуацию на каждом новом баре: checkTimeOffset(arr1h[b]). Мы помним, что в этой функции в первую очередь проверяется необходимость пересчета, поэтому вызов не очень ресурсоемкий, несмотря на частоту:

//--- шаг 3: смоделируем, как советник или индикатор проходит во времени из прошлого в настоящее
   while(++b<=sz)
     {
      //--- проверим время, обычно на первом баре после выходных
      checkTimeOffset(arr1h[b]);


Теперь мы вычисляем только для бара, определенного случайным значением GMT (tGMT), tNY — нью-йоркское время и tLeft — оставшееся время до закрытия рынка. Значения выводятся и вычисляется следующий бар:

      //--- for a randomly selected bar calc. the times of GMT, NY & tLeft and print them
      if(b>=chckBar || b==sz)
        {
         tGMT  = arr1h[b] + OffsetBroker.actOffset;         // GMT
         tNY   = tGMT - (NYShift+DST_USD);                  // время в Нью-Йорке
         tLeft = OffsetBroker.actSecFX - SoW(arr1h[b]);     // время до закрытия FX
         PrintFormat("DST_EUR:%+ 6i  DST_EUR:%+ 6i  t[%6i]  tBrk: %s%s  "+
                     "GMT: %s%s  NY: %s%s  End-FX: %2ih => left: %2ih ",
                     DST_EUR,DST_USD,b,
                     DoWs(arr1h[b]),TimeToString(arr1h[b],TIME_DATE|TIME_MINUTES),
                     DoWs(tGMT),TimeToString(tGMT,TIME_DATE|TIME_MINUTES),
                     DoWs(tNY),TimeToString(tNY,TIME_DATE|TIME_MINUTES),
                     OffsetBroker.actSecFX/3600,tLeft/3600
                    );
         chckBar += MathRand()%nChecks;               //  индекс следующего бара для проверки
        }


Вот что вывел скрипт для демо-счета на сервере MetaQuotes:

EUR: Fr.2020.10.23 23:00: hNY:16  hGMT:20  hTC:23  hDiff:-3   BrokerTime => GMT: 2020.10.23 20:00 => tNY: 2020.10.23 16:00  End-FX after: 143h
USD: Fr.2020.10.30 22:00: hNY:16  hGMT:20  hTC:22  hDiff:-2   BrokerTime => GMT: 2020.10.30 20:00 => tNY: 2020.10.30 16:00  End-FX after: 142h
NXT: Fr.2020.11.06 23:00: hNY:16  hGMT:21  hTC:23  hDiff:-2   BrokerTime => GMT: 2020.11.06 21:00 => tNY: 2020.11.06 16:00  End-FX after: 143h
USD: Fr.2021.03.12 23:00: hNY:16  hGMT:21  hTC:23  hDiff:-2   BrokerTime => GMT: 2021.03.12 21:00 => tNY: 2021.03.12 16:00  End-FX after: 143h
EUR: Fr.2021.03.26 22:00: hNY:16  hGMT:20  hTC:22  hDiff:-2   BrokerTime => GMT: 2021.03.26 20:00 => tNY: 2021.03.26 16:00  End-FX after: 142h
NXT: Fr.2021.04.02 23:00: hNY:16  hGMT:20  hTC:23  hDiff:-3   BrokerTime => GMT: 2021.04.02 20:00 => tNY: 2021.04.02 16:00  End-FX after: 143h

Сдвиг для MetaQuotes Software Corp.: 
US=Winter & EU=Winter (USwinEUwin)      =  -7200
US=Summer & EU=Summer (USsumEUsum) = -10800
US=Summer & EU=Winter (USsumEUwin)   =   -7200

DST_EUR: -3600  DST_EUR: -3600  t[ 28194]  tBrk: Mo.2002.05.20 22:00  GMT: Mo.2002.05.20 19:00  NY: Mo.2002.05.20 15:00  End-FX: 143h => left: 97h 
DST_EUR: -3600  DST_EUR: -3600  t[ 40805]  tBrk: We.2004.05.26 06:00  GMT: We.2004.05.26 03:00  NY: Tu.2004.05.25 23:00  End-FX: 143h => left: 65h 
DST_EUR: -3600  DST_EUR: -3600  t[ 42882]  tBrk: Th.2004.09.23 19:00  GMT: Th.2004.09.23 16:00  NY: Th.2004.09.23 12:00  End-FX: 143h => left: 28h 
DST_EUR:      +0  DST_EUR:     +0  t[ 44752]  tBrk: Tu.2005.01.11 17:00  GMT: Tu.2005.01.11 15:00  NY: Tu.2005.01.11 10:00  End-FX: 143h => left: 78h 
DST_EUR:      +0  DST_EUR: -3600  t[ 64593]  tBrk: We.2008.03.26 03:00  GMT: We.2008.03.26 01:00  NY: Tu.2008.03.25 21:00  End-FX: 142h => left: 67h 
DST_EUR:      +0  DST_EUR:     +0  t[ 88533]  tBrk: Tu.2012.02.07 13:00  GMT: Tu.2012.02.07 11:00  NY: Tu.2012.02.07 06:00  End-FX: 143h => left: 82h 
DST_EUR:      +0  DST_EUR:     +0  t[118058]  tBrk: We.2016.11.16 06:00  GMT: We.2016.11.16 04:00  NY: Tu.2016.11.15 23:00  End-FX: 143h => left: 65h 
DST_EUR: -3600  DST_EUR: -3600  t[121841]  tBrk: Mo.2017.06.26 05:00  GMT: Mo.2017.06.26 02:00  NY: Su.2017.06.25 22:00  End-FX: 143h => left: 114h 
DST_EUR:      +0  DST_EUR: -3600  t[144995]  tBrk: Mo.2021.03.22 06:00  GMT: Mo.2021.03.22 04:00  NY: Mo.2021.03.22 00:00  End-FX: 142h => left: 112h 
DST_EUR: -3600  DST_EUR: -3600  t[148265]  tBrk: Tu.2021.09.28 15:00  GMT: Tu.2021.09.28 12:00  NY: Tu.2021.09.28 08:00  End-FX: 143h => left: 80h 

Первые два блока мы уже обсудили. Третья и последняя часть показывает для случайно выбранных точек соответствующую разницу во времени между ЕС и США, индекс временной точки, время брокера, время по Гринвичу и Нью-Йорку, время открытия рынка на данный момент времени и оставшееся время - для наглядности секунды переведены в часы. Все это можно быстро проверить: 5/20/2002, время брокера (MQ) 22:00, с переходом на летнее время, GMT = broker-3 ч = 19:00, а нью-йоркское = GMT - (5-1) = 15:00, рынок закроется через 97 часов. 97 = 4*24 (Mon.22:00-Fri.22:00 = 96h) +1h (Fri.22:00-23:00).

Таким образом, в советнике или индикаторе нужно добавить всего два вызова функции:

   bool isTimeSet = setBokerOffset();
   if(!isTimeSet)
     {
      Alert("setBokerOffset failed");
      return;
     }
..
   checkTimeOffset(TimeCurrent());


Альтернативное использование через входные переменные

Рассмотрим пример работы через входные переменные. Скрипт дал такие значения:

Time Offset of MetaQuotes Software Corp.:
US=Winter & EU=Winter (USwinEUwin)      =  -7200
US=Summer & EU=Summer (USsumEUsum) = -10800
US=Summer & EU=Winter (USsumEUwin)   =   -7200

В советнике это может выглядеть так:

#include <DealingWithTime.mqh>
// offsets of MetaQuotes demo account: DO NOT USE THEM FOR DIFFERENT BROKERS!!
input int   USwinEUwin=  -7200;    // US=Winter & EU=Winter
input int   USsumEUsum= -10800;    // US=Summer & EU=Summer
input int   USsumEUwin=  -7200;    // US=Summer & EU=Winter

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
//---
   OffsetBroker.USwinEUwin = USwinEUwin;
   OffsetBroker.USsumEUsum = USsumEUsum;
   OffsetBroker.USsumEUwin = USsumEUwin;
   OffsetBroker.actOffset  = WRONG_VALUE; 
   
   nxtSwitch_USD = nxtSwitch_EUR = nxtSwitch_AUD = 0;
   //--- Just a simple test if not set or changed
   if(OffsetBroker.USwinEUwin+OffsetBroker.USsumEUsum+OffsetBroker.USsumEUwin==0)
      OffsetBroker.set     = false;
   else
      OffsetBroker.set     = true;
   //...
//---
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
  {
//---
   checkTimeOffset(TimeCurrent());
   tGMT  = TimeCurrent() + OffsetBroker.actOffset;    // GMT
   tNY   = tGMT - (NYShift+DST_USD);                  // время в Нью-Йорке
   tLon  = tGMT - (LondonShift+DST_EUR);              // время в Лондоне
   tSyd  = tGMT - (SidneyShift+DST_AUD);              // время в Сиднее
   tMosc = tGMT - (MoskwaShift+DST_RUS);              // время в Москве
   tTok  = tGMT - (TokyoShift);                       // время в Токио - нет перехода на летнее время

   //...
   
  }


Я указал значения, полученные для сервера MetaQuotes. Убедитесь, что используете значения для вашего брокера!

В OnTick() сначала вычисляются смещения времени, а затем сразу после этого GMT и местное время Нью-Йорка, Лондона, Сиднея, Москвы и Токио. Все очень просто, но не забывайте про скобки.


Заключение

Вместо заключения я просто покажу результаты функции setBokerOffset() на демо-счетах разных брокеров:

EUR: Fr.2020.10.23 23:00: hNY:16  hGMT:20  hTC:23  hDiff:-3   BrokerTime => GMT: 2020.10.23 20:00 => tNY: 2020.10.23 16:00  End-FX after: 143h
USD: Fr.2020.10.30 22:00: hNY:16  hGMT:20  hTC:22  hDiff:-2   BrokerTime => GMT: 2020.10.30 20:00 => tNY: 2020.10.30 16:00  End-FX after: 142h
NXT: Fr.2020.11.06 23:00: hNY:16  hGMT:21  hTC:23  hDiff:-2   BrokerTime => GMT: 2020.11.06 21:00 => tNY: 2020.11.06 16:00  End-FX after: 143h
USD: Fr.2021.03.12 23:00: hNY:16  hGMT:21  hTC:23  hDiff:-2   BrokerTime => GMT: 2021.03.12 21:00 => tNY: 2021.03.12 16:00  End-FX after: 143h
EUR: Fr.2021.03.26 22:00: hNY:16  hGMT:20  hTC:22  hDiff:-2   BrokerTime => GMT: 2021.03.26 20:00 => tNY: 2021.03.26 16:00  End-FX after: 142h
NXT: Fr.2021.04.02 23:00: hNY:16  hGMT:20  hTC:23  hDiff:-3   BrokerTime => GMT: 2021.04.02 20:00 => tNY: 2021.04.02 16:00  End-FX after: 143h

Time Offset of MetaQuotes Software Corp.: 
US=Winter & EU=Winter (USwinEUwin) = -7200
US=Summer & EU=Summer (USsumEUsum) = -10800
US=Summer & EU=Winter (USsumEUwin) = -7200



EUR: Fr.2020.10.23 23:00: hNY:16  hGMT:20  hTC:23  hDiff:-3   BrokerTime => GMT: 2020.10.23 20:00 => tNY: 2020.10.23 16:00  End-FX after: 143h
USD: Fr.2020.10.30 22:00: hNY:16  hGMT:20  hTC:22  hDiff:-2   BrokerTime => GMT: 2020.10.30 20:00 => tNY: 2020.10.30 16:00  End-FX after: 142h
NXT: Fr.2020.11.06 23:00: hNY:16  hGMT:21  hTC:23  hDiff:-2   BrokerTime => GMT: 2020.11.06 21:00 => tNY: 2020.11.06 16:00  End-FX after: 143h
USD: Fr.2021.03.12 23:00: hNY:16  hGMT:21  hTC:23  hDiff:-2   BrokerTime => GMT: 2021.03.12 21:00 => tNY: 2021.03.12 16:00  End-FX after: 143h
EUR: Fr.2021.03.26 22:00: hNY:16  hGMT:20  hTC:22  hDiff:-2   BrokerTime => GMT: 2021.03.26 20:00 => tNY: 2021.03.26 16:00  End-FX after: 142h
NXT: Fr.2021.04.02 23:00: hNY:16  hGMT:20  hTC:23  hDiff:-3   BrokerTime => GMT: 2021.04.02 20:00 => tNY: 2021.04.02 16:00  End-FX after: 143h

Time Offset of RoboForex Ltd: 
US=Winter & EU=Winter (USwinEUwin) = -7200
US=Summer & EU=Summer (USsumEUsum) = -10800
US=Summer & EU=Winter (USsumEUwin) = -7200


EUR: Fr.2020.10.23 23:00: hNY:16  hGMT:20  hTC:23  hDiff:-3   BrokerTime => GMT: 2020.10.23 20:00 => tNY: 2020.10.23 16:00  End-FX after: 143h
USD: Fr.2020.10.30 22:00: hNY:16  hGMT:20  hTC:22  hDiff:-2   BrokerTime => GMT: 2020.10.30 20:00 => tNY: 2020.10.30 16:00  End-FX after: 142h
NXT: Fr.2020.11.06 23:00: hNY:16  hGMT:21  hTC:23  hDiff:-2   BrokerTime => GMT: 2020.11.06 21:00 => tNY: 2020.11.06 16:00  End-FX after: 143h
USD: Fr.2021.03.12 23:00: hNY:16  hGMT:21  hTC:23  hDiff:-2   BrokerTime => GMT: 2021.03.12 21:00 => tNY: 2021.03.12 16:00  End-FX after: 143h
EUR: Fr.2021.03.26 22:00: hNY:16  hGMT:20  hTC:22  hDiff:-2   BrokerTime => GMT: 2021.03.26 20:00 => tNY: 2021.03.26 16:00  End-FX after: 142h
NXT: Fr.2021.04.02 23:00: hNY:16  hGMT:20  hTC:23  hDiff:-3   BrokerTime => GMT: 2021.04.02 20:00 => tNY: 2021.04.02 16:00  End-FX after: 143h

Time Offset of Alpari International: 
US=Winter & EU=Winter (USwinEUwin) = -7200
US=Summer & EU=Summer (USsumEUsum) = -10800
US=Summer & EU=Winter (USsumEUwin) = -7200


EUR: Fr.2020.10.23 23:00: hNY:16  hGMT:20  hTC:23  hDiff:-3   BrokerTime => GMT: 2020.10.23 20:00 => tNY: 2020.10.23 16:00  End-FX after: 143h
USD: Fr.2020.10.30 23:00: hNY:16  hGMT:20  hTC:23  hDiff:-3   BrokerTime => GMT: 2020.10.30 20:00 => tNY: 2020.10.30 16:00  End-FX after: 143h
NXT: Fr.2020.11.06 23:00: hNY:16  hGMT:21  hTC:23  hDiff:-2   BrokerTime => GMT: 2020.11.06 21:00 => tNY: 2020.11.06 16:00  End-FX after: 143h
USD: Fr.2021.03.12 23:00: hNY:16  hGMT:21  hTC:23  hDiff:-2   BrokerTime => GMT: 2021.03.12 21:00 => tNY: 2021.03.12 16:00  End-FX after: 143h
EUR: Fr.2021.03.26 23:00: hNY:16  hGMT:20  hTC:23  hDiff:-3   BrokerTime => GMT: 2021.03.26 20:00 => tNY: 2021.03.26 16:00  End-FX after: 143h
NXT: Fr.2021.04.02 23:00: hNY:16  hGMT:20  hTC:23  hDiff:-3   BrokerTime => GMT: 2021.04.02 20:00 => tNY: 2021.04.02 16:00  End-FX after: 143h

Time Offset of Pepperstone Group Limited: 
US=Winter & EU=Winter (USwinEUwin) = -7200
US=Summer & EU=Summer (USsumEUsum) = -10800
US=Summer & EU=Winter (USsumEUwin) = -10800


EUR: Fr.2020.10.23 23:00: hNY:16  hGMT:20  hTC:23  hDiff:-3   BrokerTime => GMT: 2020.10.23 20:00 => tNY: 2020.10.23 16:00  End-FX after: 143h
USD: Fr.2020.10.30 23:00: hNY:16  hGMT:20  hTC:23  hDiff:-3   BrokerTime => GMT: 2020.10.30 20:00 => tNY: 2020.10.30 16:00  End-FX after: 143h
NXT: Fr.2020.11.06 23:00: hNY:16  hGMT:21  hTC:23  hDiff:-2   BrokerTime => GMT: 2020.11.06 21:00 => tNY: 2020.11.06 16:00  End-FX after: 143h
USD: Fr.2021.03.12 23:00: hNY:16  hGMT:21  hTC:23  hDiff:-2   BrokerTime => GMT: 2021.03.12 21:00 => tNY: 2021.03.12 16:00  End-FX after: 143h
EUR: Fr.2021.03.26 23:00: hNY:16  hGMT:20  hTC:23  hDiff:-3   BrokerTime => GMT: 2021.03.26 20:00 => tNY: 2021.03.26 16:00  End-FX after: 143h
NXT: Fr.2021.04.02 23:00: hNY:16  hGMT:20  hTC:23  hDiff:-3   BrokerTime => GMT: 2021.04.02 20:00 => tNY: 2021.04.02 16:00  End-FX after: 143h

Time Offset of Eightcap Pty Ltd: 
US=Winter & EU=Winter (USwinEUwin) = -7200
US=Summer & EU=Summer (USsumEUsum) = -10800
US=Summer & EU=Winter (USsumEUwin) = -10800

EUR: Fr.2020.10.23 23:00: hNY:16  hGMT:20  hTC:23  hDiff:-3   BrokerTime => GMT: 2020.10.23 20:00 => tNY: 2020.10.23 16:00  End-FX after: 143h
USD: Fr.2020.10.30 22:00: hNY:16  hGMT:20  hTC:22  hDiff:-2   BrokerTime => GMT: 2020.10.30 20:00 => tNY: 2020.10.30 16:00  End-FX after: 142h
NXT: Fr.2020.11.06 23:00: hNY:16  hGMT:21  hTC:23  hDiff:-2   BrokerTime => GMT: 2020.11.06 21:00 => tNY: 2020.11.06 16:00  End-FX after: 143h
USD: Fr.2021.03.12 23:00: hNY:16  hGMT:21  hTC:23  hDiff:-2   BrokerTime => GMT: 2021.03.12 21:00 => tNY: 2021.03.12 16:00  End-FX after: 143h
EUR: Fr.2021.03.26 22:00: hNY:16  hGMT:20  hTC:22  hDiff:-2   BrokerTime => GMT: 2021.03.26 20:00 => tNY: 2021.03.26 16:00  End-FX after: 142h
NXT: Fr.2021.04.02 23:00: hNY:16  hGMT:20  hTC:23  hDiff:-3   BrokerTime => GMT: 2021.04.02 20:00 => tNY: 2021.04.02 16:00  End-FX after: 143h

Time Offset of InstaForex Companies Group: 
US=Winter & EU=Winter (USwinEUwin) = -7200
US=Summer & EU=Summer (USsumEUsum) = -10800
US=Summer & EU=Winter (USsumEUwin) = -7200


EUR: Fr.2020.10.23 21:00: hNY:16  hGMT:20  hTC:21  hDiff:-1   BrokerTime => GMT: 2020.10.23 20:00 => tNY: 2020.10.23 16:00  End-FX after: 141h
USD: Fr.2020.10.30 21:00: hNY:16  hGMT:20  hTC:21  hDiff:-1   BrokerTime => GMT: 2020.10.30 20:00 => tNY: 2020.10.30 16:00  End-FX after: 141h
NXT: Fr.2020.11.06 21:00: hNY:16  hGMT:21  hTC:21  hDiff: 0   BrokerTime => GMT: 2020.11.06 21:00 => tNY: 2020.11.06 16:00  End-FX after: 141h
USD: Fr.2021.03.12 21:00: hNY:16  hGMT:21  hTC:21  hDiff: 0   BrokerTime => GMT: 2021.03.12 21:00 => tNY: 2021.03.12 16:00  End-FX after: 141h
EUR: Fr.2021.03.26 21:00: hNY:16  hGMT:20  hTC:21  hDiff:-1   BrokerTime => GMT: 2021.03.26 20:00 => tNY: 2021.03.26 16:00  End-FX after: 141h
NXT: Fr.2021.04.02 21:00: hNY:16  hGMT:20  hTC:21  hDiff:-1   BrokerTime => GMT: 2021.04.02 20:00 => tNY: 2021.04.02 16:00  End-FX after: 141h

Time Offset of JFD Group Ltd: 
US=Winter & EU=Winter (USwinEUwin) = 0
US=Summer & EU=Summer (USsumEUsum) = -3600
US=Summer & EU=Winter (USsumEUwin) = -3600


Желаю вам успешной торговли!



Перевод с английского произведен MetaQuotes Ltd.
Оригинальная статья: https://www.mql5.com/en/articles/9929

Прикрепленные файлы |
DealingWithTime.mqh (52.89 KB)
Последние комментарии | Перейти к обсуждению на форуме трейдеров (37)
Carl Schreiber
Carl Schreiber | 5 июл. 2023 в 19:08

Версия DealingWithTime.mqh v. 1.01 из статьи Работа со временем (Часть 2): Функции ( https://www.mql5.com/ru/articles/9929 ) перестала работать, так как MQ изменил поведение CopyTime( ) через некоторое время после публикации этой статьи. Теперь эта функция больше не возвращает будущие значения времени, если они больше, чем TimeCurrent() , указанный для параметров start_time и/или stop_time. Вместо этого возвращается максимально возможное значение времени открытия последнего текущего бара.

Так как окончание валютной сессии было определено таким образом, чтобы определить смещение времени брокера, теперь это приводит к неверным значениям!

Этот расчет был изменен в версии 2.03. Эта версия теперь доступна в CodeBase здесь: https://www.mql5.com/ru/code/45287 .

Но также был полностью изменен расчет перевода времени, так что теперь охвачены сложные времена перевода времени из Сиднея (Австралия) обратно в 70-е годы.

Также прилагается таблица DST 1975 - 2030.xlsx в виде zip-файла со всеми временными изменениями с 70-х годов, чтобы каждый мог проверить правильность работы формул, вот пример серии таблицы:

1 января 1982 г. - стандартное время в США (DST == 0), а следующее изменение произойдет 25 апреля 1982 г., в последнее (25-е число месяца) воскресенье апреля (4). Таблица уже отсортирована по географическому часовому поясу (столбец A), затем по часовому поясу года (столбец L, spr=весна, aut=осень) и, наконец, по дате запроса (столбец C). Таблица может быть создана автоматически включенным советником (скрипт не может быть запущен в режиме отладки). Test_DST 2.mq5, если вы запустите его в режиме отладки и скопируете строки журнала журнала в отладчике и вставите их в электронную таблицу; Разделителем ячеек будет пробел.

Кроме того, теперь есть новая простая функция SecTillClose() , которая дает вам оставшееся время в секундах (валюта времени MQ) до закрытия рынка форекс - без CopyTime() . Это интересно для тех, кто хочет закрыть свои позиции до выходных или не хочет открывать новую позицию в определенный период до выходных.

Включенный индикатор DealingWithTime_TestIndi.mq5, как комментарий к графику, показывает не только летнее время в Европе, США и Австралии (Сидней), но также текущее время и разницу во времени различных городов. Здесь вы можете найти таблицу с разным местным временем крупных городов для сравнения: https://www.timeanddate.com/worldclock/ . Таким образом, вы можете проверить значения в любое время. Этот индикатор также показывает, как эти значения определяются и используются (что от чего вычитается или прибавляется), что облегчает самостоятельное использование — копирование и вставка, самая быстрая форма программирования.

Последние две строки также показывают последнюю секунду текущей FX-сессии и оставшееся время в часах (о чем проще судить) и в секундах. В Нью-Йорке, когда сессия FX закрывается в 17:00 по местному времени в пятницу, нет действительного бара, открытого в 17:00 по нью-йоркскому времени. Поэтому в этой функции вычитается 1 секунда, чтобы получить последнее действительное время открытия последнего бара во времени брокера. Однако некоторые брокеры заканчивают свою валютную сессию на несколько минут раньше, больше не предоставляя цены и не принимая торговые приказы.

Daniel K
Daniel K | 18 февр. 2024 в 16:37

Привет @Anil Varma

Я прочитал последнее сообщение автора @Carl Schreiber о функции CopyTime(), но так как мне более понятна 1-я версия, я все еще использую DealingWithTime.mqh v . 1.01.

В своем индикаторе я хочу, чтобы:

Присвойте каждому бару сырое время NY секунды), час NY и минуту NY, используя следующие буферы для отображения их в окне данных:
double NyRawTimeBuffer[];
double NyHourBuffer[];
double NyMinuteBuffer[];
 void AssignNyTime (const datetime& time[],int rates_total)
   {
      
      MqlDateTime dT_struc;
      
      //--- Присвойте каждой свече: NY raw Time (в секундах), NY hour, NY min
      ArraySetAsSeries(time,true);
      for(int z=0;z<rates_total;z++)
         { 
            checkTimeOffset(time[z]);                   // проверьте изменения DST
            datetime tC, tGMT, tNY;
            tC    = time[z];
            tGMT  = time[z] + OffsetBroker.actOffset;   // GMT
            tNY   = tGMT - (NYShift+DST_USD);           // время в нью-йорке
            int j = int (tNY);                          // преобразование datetime в int 
            NyRawTimeBuffer[z]=j;
            
            TimeToStruct(tNY,dT_struc);
            NyHourBuffer[z]=dT_struc.hour;
            NyMinuteBuffer[z]=dT_struc.min;
 
         }        
       
   return;
   }

Функция работает только тогда, когда на графике таймфрейма, на котором запущен терминал, установлено значение H1.

Если я закрываю терминал и таймфрейм устанавливается, допустим, на M5, а затем я перезапускаю терминал, то он выдает следующую ошибку:

2024.02.18 15:33:38.048 MyFractals_V4 (EURUSD,M5) 240: CopyTime() FAILED for EURUSD H1: нужны времена от 2024.02.12 02:00:00, а есть только от 1970.01.01 00:00:00 error: 4401

Вы уже предложили мне через сообщения использовать CheckLoadHistory() из этой статьи ( https://www.mql5.com/ru/code/1251 ) и разместить ее перед функцией CopyXXXX() в вашей библиотеке:

//--- найдите смещение брокера
    OffsetBroker.set = false;
    
    CheckLoadHistory("EURUSD",PERIOD_H1,TERMINAL_MAXBARS,true);
      
    b = CopyTime("EURUSD",PERIOD_H1,BegWk+26*3600,5,arrTme);      // получаем время последнего 1h бара перед переключением в EU

Но проблема осталась.

В файле checkhistory.mqh (строка 19) я заметил следующий комментарий, но я не понимаю, может ли это быть проблемой. Я попытался закомментировать его и снова протестировать программу, но ничего не вышло.

//--- не запрашивайте загрузку собственных данных, если это индикатор
   if(MQL5InfoInteger(MQL5_PROGRAM_TYPE)==PROGRAM_INDICATOR && Period()==period && Symbol()==symbol) return(true);

Есть ли способ исправить ошибку, не переходя на обновленную библиотеку DealingWithTimeV2.03.mqh , не переписывая весь код?




Anil Varma
Anil Varma | 19 февр. 2024 в 05:53
Nauris Zukas тестере стратегий, но она не работает.


Является ли "Альтернативный вариант использования через входные переменные" единственным способом получить правильное время в тестере стратегий?

Привет

Я попытался изменить код, как показано ниже, и пока что он работает для меня. Обратите внимание, что я преобразовал класс с конструкторами и все методы являются частью класса. Класс должен быть вызван и инициализирован в классе вашего эксперта/стратегии.

CDealWithTime.OnTick() должен быть помещен в EA/Strategy OnTick()

//+-----------------------------------------------------------------------------------------------------------------------------+
//| функция для определения смещения брокера для заданного времени (tB)
//+-----------------------------------------------------------------------------------------------------------------------------+
void CDealWithTime::OnTick(void) {

                string vMethod = __FUNCTION__ + " Line[" + (string)__LINE__ + "] ";

                // Выполняем почасовое обновление TimeH01[]
                if(IsNewBarH01()) {
      // ... BegWk = BoW(tC), исходный код
                        datetime weekBegin = BoW(TimeCurrent());
                        // Оригинальный код добавил 5 баров к времени WeakBegin.
                        // Прибавление 5 дней * 3600 секунд в час к слабому времени начала не равно пяти барам, так как баров может не быть на
                        // выходные/праздник. Следовательно, WeekBegin+5*3600 может привести к времени, когда на нем нет бара.
                        int                      barShift  = iBarShift(mSymbol,mTimeFrame,weekBegin+(5*3600));
                        // Вернет первый доступный бар в момент Time(weekBegin+(5*3600)
                        datetime timeStop        = iTime(mSymbol,mTimeFrame,barShift);          // Последний часовой бар пятницы перед переходом в ЕС.
                        // Результат при добавлении секунд (weekBegin)+(5*3600): CDealWithTime::OnTick Line[229] : GetLastError[0] copiedTime EURUSD-PERIOD_H1 for [0] bars weekBegin[2024.01.01 02:00] to timeStop[2023.12.31 21:00] time5th[2023.12.29 23:00]
                        int bars = Bars(mSymbol,mTimeFrame,weekBegin,timeStop);

                        // Нам нужен цикл while...loop, так как IsNewBarH01() будет проверяться только один раз на тике, если true, то до следующего нового бара проверка не будет продолжена
                        ResetLastError();
                        int attempt = 0;
                        while(CopyTime(mSymbol,mTimeFrame,weekBegin,timeStop,TimeH01) != bars && attempt <= 10) {
                                Sleep(100);
                                attempt++;
                        }
                        if(attempt > 0) {
                                PrintFormat("%s: GetLastError[%i] copiedTime %s-%s for [%i] bars weekBegin[%s] to timeStop[%i][%s]",vMethod,GetLastError(),mSymbol,EnumToString(mTimeFrame),bars,TimeToString(weekBegin),barShift,TimeToString(timeStop));
                        }
                }

                // Выполняйте еженедельную проверку, если изменилось время перехода на летнее освещение (DST).
                if(IsNewBarW01()) {
                        checkTimeOffset(TimeCurrent());
                        int  attempt  = 0;
                        bool isOffset = false;
                        do{
                                isOffset = setBokerOffset();
                                attempt++;
                        } while(!isOffset && attempt <= 10);
                }

} // Конец OnTick()
Anil Varma
Anil Varma | 19 февр. 2024 в 05:58
amrali #:

Этот код автоматически рассчитывает DST для европейских и американских брокеров:

https://www.mql5.com/ru/code/27860

Приведенный выше код был использован в Forex Market Hours https://www.mql5.com/ru/code/27771 для расчета изменения времени перехода на летнее время.

Аналогичные функции можно построить для разных регионов мира.

Привет Амрали

Хороший и простой код в качестве альтернативы статье DealingWithTime v2.03. Я рассмотрю его для более детального изучения.

Anil Varma
Anil Varma | 19 февр. 2024 в 06:08
Daniel K #:
DealingWithTime.mqh v . 1.01.

Привет, Дэниел

DealingWithTime.mqh v . 1.01. Эта статья и ее код больше не работают из-за изменений в методах расчета MQL, как объяснил Карл в статье DealingWithTime.mqh v 2.03 https://www.mql5.com/ru/code/45287.

Вам не следует использовать ее вообще.

Стать хорошим программистом (Часть 6): 9 привычек для эффективной разработки Стать хорошим программистом (Часть 6): 9 привычек для эффективной разработки
Качество разработки — это не только про написание кода. На своем опыте я выявил определенные привычки, которые помогают повысить эффективность разработки. О некоторых из них мы поговорим в этой статье. Статья обязательна к прочтению для всех, кто хочет улучшить навыки написания сложных алгоритмов.
Как правильно выбирать советник в Маркете? Как правильно выбирать советник в Маркете?
В данной статье рассмотрим моменты, на которые следует обращать внимание при покупке советника в первую очередь. А также поищем способы повышения прибыли и, что самое, главное, как потратить деньги с умом и еще заработать на этом. Кроме того, после прочтения вы поймете, что заработать можно даже на простых и бесплатных продуктах.
Пишем глубокую нейронную сеть с нуля на языке MQL Пишем глубокую нейронную сеть с нуля на языке MQL
Статья познакомит вас с глубокой нейронной сетью, написанной на MQL, и с различными функциями активации этой сети, такими как функция гиперболического тангенса для скрытых слоев и Softmax для выходного слоя. Мы будем изучать нейросеть постепенно, двигаясь от первого шага до последнего, и вместе создадим глубокую нейронную сеть.
Разработка торгового советника с нуля Разработка торгового советника с нуля
Давайте разберемся, как разработать советник для торговли с минимальным количеством программирования