Работа с датой и временем в MQL4: полезные функции и переменные

Переменная datetime представлена ​​как количество секунд, прошедших с 1 января 1970 года. Например, 15 июня 2009 года в 0:00 будет представлено как 1245024000. Преимущество формата datetime заключается в том, что он позволяет упростить вычисления с временем и математические операции.

Например, если вы хотите проверить, наступает ли одна дата до или после другой, вы должны выполнить простую операцию. Предположим, что StartDate — 15 июня 2009 года в 14:00, а EndDate — 16 июня 2009 года в 5:00.

    if (StartDate < EndDate) // Результат равен true
    if (StartDate > EndDate) // Результат равен false

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

datetime AddDay = StartDate + 86400;

Если вы планируете много математических манипуляций с переменными даты и времени, было бы неплохо объявить некоторые целочисленные константы для представления определенных единиц времени:

#define SEC_H1 3600 // Секунды в часе 
#defineSEC_D186400 // Секунды в течение дня

Недостатком формата datetime является то, что он не очень читабелен. Вы не можете просмотреть значение, например 1245024000, и автоматически сказать, что оно представляет 15 июня 2009 г. 0:00. Для этого мы используем функции преобразования даты и времени.

Константы даты и времени

Константа datetime — это дата и время, представленные в следующем формате: yyyy.mm.dd hh:mm. Например, 15 июня 2009 года в 0:00 будет 2009.06.15 00:00.

Чтобы преобразовать переменную datetime в строковую константу, используйте функцию TimeToStr(). Вот ее синтаксис:

string TimeToStr (datetime Time, int Output = TIME_DATE | TIME_MINUTES);
  • Time — переменная дата-время, выраженная в количестве секунд, прошедших с 1 января, 1970.
  • Output — необязательный параметр, который выводит константу только в виде даты, часа и минуты, часа, минуты и секунды или любая другой комбинации даты и времени. Допустимые значения ввода:

    TIME_DATE — выводит дату, например, 2009.06.15
    TIME_MINUTES — выводит часы и минуты, например, 05:30
    TIME_SECONDS — выводит часы, минуты и секунды, например, 05:30:45

Чтобы вывести строковую константу в формате по умолчанию yyyy.mm.dd hh: mm, оставьте поле Output пустым. Если вам нужна только дата, либо часы и минуты (или секунды), используйте соответствующий аргумент. В этом примере мы предположим, что StartTime равен 2009.06.15 05:30:45.

TimeToStr (StartTime, TIME_DATE) // Возвращает "2009.06.15"
TimeToStr (StartTime, TIME_SECONDS) // Возвращает "05:30:45"
TimeToStr (StartTime, TIME_MINUTES) // Возвращает "05:30"
TimeToStr (StartTime, TIME_DATE | TIME_SECONDS) // Возвращает «2009.06.15 05:30:45»
TimeToStr (StartTime) // Возвращает «2009.06.15 05:30»

Мы можем создать константу даты и времени, используя конкатенацию строк, и преобразовать ее в переменную даты и времени, используя функцию StrToTime(). Синтаксис идентичен TimeToStr() выше, но без параметра Output. Строковая константа должна быть в формате yyyy.mm.dd hh: mm для правильного преобразования.

Сначала мы объявим некоторые внешние переменные для установки времени и даты:

extern int UseMonth = 6; 
extern int UseDay = 15; 
extern int UseHour = 5; 
extern int UseMinute = 30;

Затем мы создаем строковую константу, используя функцию StringConcatenate(), и, наконец, преобразуем строку в формат datetime, используя StrToTime().

string DateConstant = StringConcatenate (Year (), ".", UseMonth, ".", UseDay, "", UseHour, ":", UseMinute); // DateConstant равен "2009.6.15 05:30"
datetime StartTime = StrToTime (DateConstant); // StartTime равен "1245043800"

Обратите внимание, что в функции StringConcatenate() мы используем Year() для возврата текущего года вместо использования внешней переменной. Вы можете использовать такие функции, как Month(), Day() и т. д.

Функции для работы с датой и временем

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

extern bool UseLocalTime = true;

Вот код для назначения текущего локального или текущего времени сервера переменной с именем CurrentTime.

if (UseLocalTime == true) datetime CurrentTime = TimeLocal (); // Местное время
else CurrentTime = TimeCurrent(); // Время сервера

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

  • Year() — текущий год, например, 2009.
  • Month() — текущий месяц года от 1 по 12.
  • Day() — текущий день месяца с 1 по 31.
  • DayOfWeek() — целое число, представляющее текущий день недели. Воскресенье — 0, понедельник — 1, пятница — 5 и т. д.
  • Hour() — текущий час за 24 часа, с 0 до 23. Например, 3 часа ночи — 3, а 3 часа дня — 15.
  • Minute() — текущая минута от 0 до 59.

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

  • TimeYear() — четырехзначный год указанного значения даты и времени.
  • TimeMonth() — месяц указанного значения даты и времени от 1 до 12.
  • TimeDay() — день месяца указанного значения datetime от 1 до 31.
  • TimeDayOfWeek() — целое число, представляющее день недели указанного значения даты и времени. Воскресенье — 0, понедельник — 1, пятница — 5 и т. д.
  • TimeHour() — час указанного значения даты и времени в 24-часовом времени от 0 до 23.
  • TimeMinute() — минута указанного значения даты и времени от 0 до 59.

Вот несколько примеров использования этих функций. Предположим, что TimeLocal() равен 2009.06.15 05:30.

datetime CurrentTime = TimeLocal ();
 
int GetMonth = TimeMonth (CurrentTime); // возвращает 6
int GetHour = TimeHour (CurrentTime); // возвращает 5
int GetWeekday = TimeDayOfWeek (CurrentTime); // Возвращает 1 или понедельник

Создание простого таймера

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

Чтобы создать таймер, нам нужно указать время начала и время окончания. Мы будем использовать внешние целочисленные переменные для ввода параметров времени. Мы создадим строку с константой datetime и преобразуем ее в переменную datetime. Затем мы сравним время начала и окончания с текущим временем. Если текущее время больше времени начала, но меньше времени окончания, торговля будет разрешена.

Вот внешние переменные, которые мы будем использовать. Мы установим переменную для включения и выключения таймера, а также для выбора текущего времени (серверное или локальное). У нас есть настройки месяца, дня, часа и минуты для времени начала и окончания:

extern bool UseTimer = true; 
extern bool UseLocalTime = false;
 
extern int StartMonth = 6; 
extern int StartDay = 15; 
extern int StartHour = 7; 
extern int StartMinute = 0;
 
extern int EndMonth = 6;
extern int EndDay = 15;
extern int EndHour = 2;
extern int EndMinute = 30;

Вот код для проверки. Переменная TradeAllowed определяет, открывать ли новые сделки. Если UseTimer установлено в false, TradeAllowed автоматически устанавливается в true. В противном случае мы оцениваем время начала и окончания по отношению к текущему времени, чтобы узнать, разрешим мы торговать или нет.

if(UseTimer == true) {
 
// Конвертируем время начала
string StartConstant = StringConcatenate(Year(),".",StartMonth,".",StartDay," ",StartHour,":",StartMinute);
datetime StartTime = StrToTime(StartConstant);
if(StartMonth == 12 && StartDay == 31 && EndMonth == 1) int EndYear = Year() + 1; else EndYear = Year();
 
// Конвертируем время окончания
string EndConstant = StringConcatenate(EndYear,".",EndMonth,".",EndDay," ",EndHour,":",EndMinute);
datetime EndTime = StrToTime(EndConstant);
 
// Выбираем локальное или серверное время
if(UseLocalTime == true) datetime CurrentTime = TimeLocal(); else CurrentTime = TimeCurrent();
 
// Проверяем условия торговли
if(StartTime <= CurrentTime && EndTime > CurrentTime) {
bool TradeAllowed = true;
}
else TradeAllowed = false;
}
else TradeAllowed = true;

Мы начинаем с преобразования нашего времени начала в переменную datetime StartTime. Оператор if (StartMonth == 12 && StartDay == 31 && EndMonth == 1) проверяет, является ли начальная дата последним днем года, и является ли конечный день первым первого следующего года. Если это так, он автоматически увеличивает конечный год на 1. В противном случае мы используем текущий год для EndYear.

Затем мы конвертируем время окончания в переменную datetime EndTime и выбираем, какой CurrentTime мы хотим использовать, серверный или локальный. Последний блок if проверяет, находится ли текущее время между временем начала и окончания. Если это так, TradeAllowed имеет значение true.

Теперь нам нужно добавить код для контроля исполнения сделки. Самый простой способ сделать это — добавить блок if вокруг процедуры открытия ордеров:

// Начало торговли
    if(TradeAllowed == true)
{
 
// Ордер на покупку
if(FastMA > SlowMA && BuyTicket == 0 && BuyOrderCount(Symbol(),MagicNumber) == 0)
  {
}
 
// Ордер на продажу
if(FastMA < SlowMA && SellTicket == 0 && SellOrderCount(Symbol(),MagicNumber) == 0)
          {
 
}
      }

Существует много других способов создания таймеров — например, вы можете использовать день недели вместо месяца и дня или установить время торговли относительно текущего дня.

Исполнение кода на открытии бара

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

Торговля один раз за бар также означает, что результаты в тестере стратегий будут более точными и актуальными. Из-за ограничений, присущих тестеру стратегий MetaTrader, использование «каждого тика» в качестве модели тестирования приведет к ненадежным результатам бэк-тестирования из-за того, что тики часто моделируются из данных М1. Сделки, которые происходят в реальной торговле, не обязательно будут соответствовать сделкам, совершенным в тестере стратегий.

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

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

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

Технически, мы проверяем торговые условия на первом тике нового бара, одновременно изучая значение закрытия предыдущего бара.

Вот код, чтобы проверить открытие нового бара. Сначала мы объявляем внешнюю переменную с именем CheckOncePerBar для включения и выключения этой функции. Затем мы объявляем глобальную переменную datetime для хранения метки времени текущего бара — это будет CurrentTimeStamp.

В функции init() мы назначим метку времени текущего бара для CurrentTimeStamp.

 
// Внешние переменные
extern bool CheckOncePerBar = true;
    // Глобальные переменные
    datetime CurrentTimeStamp;
    // Init функция
    int init()
{
CurrentTimeStamp = Time[0];
}

Вот код, который идет в начале нашей функции start() сразу после таймера. Целочисленная переменная BarShift будет определять, устанавливать ли значение Shift нашего индикатора и функции цены на текущий или предыдущий бар. Булевая переменная NewBar определит, будем ли мы проверять наши условия торговли:

if(CheckOncePerBar == true) {
int BarShift = 1; if(CurrentTimeStamp != Time[0])
{
CurrentTimeStamp = Time[0]; bool NewBar = true;
}
else NewBar = false;
} else
{
NewBar = true; BarShift = 0;
}

Если CheckOncePerBar установлен в true, мы сначала установим BarShift в 1. Это установит параметр Shift всех функций индикатора / цены на предыдущий бар.

Затем мы сравниваем значение переменной CurrentTimeStamp с Time [0], который является отметкой времени текущего бара. Если эти два значения не совпадают, мы присвоим значение Time [0] для CurrentTimeStamp и установим для NewBar значение true. Торговые условия будут проверены вскоре после этого.

При последующих запусках CurrentTimeStamp и Time[0] будут совпадать, что означает, что NewBar будет установлен в false. Торговые условия не будут проверяться, пока не откроется новый бар. После открытия нового бара значение Time[0] будет отличаться от значения CurrentTimeStamp, а для NewBar будет установлено значение true.

Если CheckOncePerBar установлен в false, NewBar автоматически будет установлен в true, а BarShift будет установлен в 0. Это будет проверять торговые условия на каждом тике, как и раньше.

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

double FastMA = iMA(NULL,0,FastMAPeriod,0,0,0,BarShift); 
if(Close[BarShift] > Open[BarShift])
double UseLow = iLow(NULL,0,BarShift);

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

double LastFastMA = iMA(NULL,0,FastMAPeriod,0,0,0,BarShift+1);

Если вы не ожидаете когда-либо запускать свой советник один раз за бар, вам не нужно будет добавлять этот код. Но для многих торговых систем, основанных на индикаторах, это может сделать ваши результаты торговли и бэк-тестирования более надежными.

Чтобы контролировать исполнение сделок, нам нужно проверить значение NewBar перед процедурой размещения ордера. Мы можем сделать это, используя блок if, который мы поместили ранее для таймера:

// Начало торговли
if(TradeAllowed == true && NewBar == true) {
// Ордер на покупку
if(FastMA > SlowMA && BuyTicket == 0 && BuyOrderCount(Symbol(),MagicNumber) == 0)
          {
 
}
// Ордер на продажу
if(FastMA < SlowMA && SellTicket == 0 && SellOrderCount(Symbol(),MagicNumber) == 0)
          {
 
          }
      }