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

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

MetaTrader 5Примеры | 17 января 2022, 15:44
2 619 13
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)
Последние комментарии | Перейти к обсуждению на форуме трейдеров (13)
Valeriy Yastremskiy
Valeriy Yastremskiy | 18 окт. 2022 в 12:00
Вообще перешел на гринвич. И время перехода на зимнее определяю как разницу серверного времени и гринвича. Единственно, что для каждого брокера эту разницу приходится считать самому и забивать в константы советника. Просто когда у пользователей разные сдвиги, у брокеров, у бирж, и время перехода на зимнее не одинаково, гринвич одинаков для всех.
Nikita Chernyshov
Nikita Chernyshov | 19 окт. 2022 в 11:43
Valeriy Yastremskiy #:
Вообще перешел на гринвич. И время перехода на зимнее определяю как разницу серверного времени и гринвича. Единственно, что для каждого брокера эту разницу приходится считать самому и забивать в константы советника. Просто когда у пользователей разные сдвиги, у брокеров, у бирж, и время перехода на зимнее не одинаково, гринвич одинаков для всех.

А как именно вы это сделали? Каким образом высчитывается константы?

Valeriy Yastremskiy
Valeriy Yastremskiy | 19 окт. 2022 в 12:25
Nikita Chernyshov #:

А как именно вы это сделали? Каким образом высчитывается константы?

Время гринвича минус таймкаррент, с переходом на зимнее он изменится на час. Писал как то время начала и окончания работы, с клиентом разница в час +3 и +2, у брокеров +3, +2, -6 по гринвичу.))) А начинать работу надо было в определенное время, одинаковое для всех. Гринвич одинаков, а серверное и локальное время разные. Код из учебника Федосеева переделал.) 

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

// TimeGMT()-TimeCurrent(); летом одна величина, зимой другая.
// TimeGMTOffset() =  TimeGMT() - TimeLocal(); серверным временем нет штатной функции



#property strict
//+------------------------------------------------------------------+ class CTradeTimeGMT{ protected: int StartTime; int EndTime; int GMTShiftTest; int GMTShiftCur; public: void Init(int StartHour, int StartMinute, int EndHour, int EndMinute, int GMTshift){ StartTime=3600*StartHour+60*StartMinute; EndTime=3600*EndHour+60*EndMinute; GMTShiftCur=GMTshift; GMTShiftTest=GMTshift+int((TimeGMTOffset())/3600); //Alert(TimeGMTOffset()); if(MQLInfoInteger( MQL_TESTER))GMTShiftCur=GMTShiftTest; } bool Check(int GMTshift){ int CurTime=(int)((TimeGMT()+(GMTShiftCur*3600))%86400); if(StartTime<EndTime){ return(CurTime>=StartTime && CurTime<EndTime); } else{ return(CurTime>=StartTime || CurTime<EndTime); } } };
Nikita Chernyshov
Nikita Chernyshov | 19 окт. 2022 в 16:48
Valeriy Yastremskiy #:

Время гринвича минус таймкаррент, с переходом на зимнее он изменится на час. Писал как то время начала и окончания работы, с клиентом разница в час +3 и +2, у брокеров +3, +2, -6 по гринвичу.))) А начинать работу надо было в определенное время, одинаковое для всех. Гринвич одинаков, а серверное и локальное время разные. Код из учебника Федосеева переделал.) 

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

Спасибо, да, такое решение есть в голове, оно +- так и реализовано. Но вы в последнем абзаце указали на всю боль)

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 секунда, чтобы получить последнее действительное время открытия последнего бара во времени брокера. Однако некоторые брокеры заканчивают свою валютную сессию на несколько минут раньше, больше не предоставляя цены и не принимая торговые приказы.

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