Таймер повышенной точности: EventSetMillisecondTimer

Если в программе требуется более частое срабатывание таймера, чем 1 секунда, используйте вместо EventSetTimer функцию EventSetMillisecondTimer.

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

bool EventSetMillisecondTimer(int milliseconds)

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

Функция возвращает признак успешного выполнения (true) или ошибки (false).

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

При штатной работе события таймера генерируются не чаще 1 раза в 10-16 миллисекунд, что связано с аппаратными ограничениями.

Для демонстрации работы с миллисекундным таймером расширим пример индикатора MultipleTimers.mq5. Поскольку активация глобального таймера отдана в ведение прикладной программы, мы легко можем поменять тип таймера, оставив классы логических таймеров без изменений. Разница будет только в том, что их множители будут применяться к базовому периоду в миллисекундах, который мы укажем в функции EventSetMillisecondTimer.

Для выбора типа таймера опишем перечисление и добавим новую входную переменную.

enum TIMER_TYPE
{
   Seconds,
   Milliseconds
};
   
input TIMER_TYPE TimerType = Seconds;

По умолчанию используем секундный таймер. В OnInit запускаем таймер требуемого типа.

void OnInit()
{
   Print(__FUNCSIG__" "BaseTimerPeriod" "EnumToString(TimerType));
   if(TimerType == Seconds)
   {
      EventSetTimer(BaseTimerPeriod);
   }
   else
   {
      EventSetMillisecondTimer(BaseTimerPeriod);
   }
}

Посмотрим, что выведется в журнал при выборе миллисекундного таймера.

                                                //   время      мсек
17:27:54.483  void OnInit() 1 Milliseconds        |             
17:27:54.514  void MyCountableTimer::notify()2 0    |           +31
17:27:54.545  bool OnTimer3()                        |          +31
17:27:54.561  void MyCountableTimer::notify()2 1      |         +16
17:27:54.561  void MyCountableTimer::notify()4 0      |
17:27:54.577  bool OnTimer5()                          |        +16
17:27:54.608  void MyCountableTimer::notify()2 2        |       +31
17:27:54.608  bool OnTimer3()                           |
17:27:54.608  void MySuspendedTimer::notify()1 0        |
17:27:54.623  void MySuspendedTimer::notify()1 1         |      +15
17:27:54.655  void MyCountableTimer::notify()2 3          |     +32
17:27:54.655  void MyCountableTimer::notify()4 1          |
17:27:54.655  void MySuspendedTimer::notify()1 2          |
17:27:54.670  bool OnTimer3()                              |    +15
17:27:54.670  void MySuspendedTimer::notify()1 3           |
17:27:54.686  void MyCountableTimer::notify()2 4            |   +16
17:27:54.686  void MySuspendedTimer::notify()1 4            |
17:27:54.686  Forcing all timers to stop                    |

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

Из-за того, что точность системного таймера ограничена парой десятков миллисекунд, реальный интервал между событиями заметно превосходит недостижимо малую 1 миллисекунду. Кроме того, налицо разброс в размере одного "шага". Таким образом, даже при использовании миллисекундного таймера желательно не закладываться на периоды меньше нескольких десятков миллисекунд.