Смотри, как бесплатно скачать роботов
Ищи нас в Twitter!
Ставь лайки и следи за новостями
Интересный скрипт?
Поставь на него ссылку - пусть другие тоже оценят
Понравился скрипт?
Оцени его работу в терминале MetaTrader 5
Просмотров:
3054
Рейтинг:
(23)
Опубликован:
Обновлен:
\MQL5\Indicators\ \MQL5\Include\
MQL5 Фриланс Нужен робот или индикатор на основе этого кода? Закажите его на бирже фрилансеров Перейти на биржу

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

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

  • В теле вашей программы не должно быть функции OnTimer, т.к. эта функция уже присутствует в файле Timer.mqh. Начиная с версии 1.08 это можно отключить — см. раздел «Свой OnTimer» ниже.
  • Не надо создавать никаких экземпляров класса CTimer, т.к. один экземпляр уже создан с именем "timers". Этого будет достаточно.
  • Время периодичности каждого таймера задается в миллисекундах, а не в секундах.
  • Каждому таймеру задается собственная функция обработки.
  • Одна функция обработки событий таймера может принадлежать разным таймерам с разной периодичностью. А вот повторный вызов NewTimer с той же функцией и тем же периодом нового таймера не создаст: это считается дубликатом, и NewTimer вернёт 0. Если OnInit у вас отрабатывает дважды, второй таймер именно поэтому и не появляется.
  • Первый вызов обработчика происходит через заданный период, а не сразу в момент создания таймера. До версии 1.07 было наоборот.
  • Если у вас до использования этой библиотеки был уже настроенный таймер и Вы его хотите сохранить в виде одного из таймеров, то :

 1. Переименуйте функцию OnTimer , например, в OnTimer1
 2. Функцию EventSetMillisecondTimer или EventSetTimer замените на:
                    -  EventSetMillisecondTimer(500);  ->  timers.NewTimer(500,  OnTimer1);
                    -  EventSetTimer(2);  ->  timers.NewTimer(2000,  OnTimer1);    // 2 сек = 2000 миллисекунд
 3. Функцию EventKillTimer(); замените на timers.KillTimer(OnTimer1);


Если Вам нужны несколько независимых бесконфликтных таймеров, то необходимо сделать следующее:

1. Включите в свою программу класс CTimer:

#include <Semko\Timer.mqh>

2. Создайте функции-обработчики необходимых таймеров формата void AnyNameFunction() {....}
например с такими именами(функции OnTimer не должно быть):

void Timer1() {x1++;} // функция для таймера например 500 мс
void Timer2() {x2++;} // функция для таймера например 1000 мс
void Timer3() {x3++;} // функция для таймера например 2000 мс

3. Когда вам необходимо создать и запустить таймер, сделайте так из любого места:

timers.NewTimer(500,  Timer1); // создаем новый таймер с периодом обновления 500   миллисекунд и функцией-обработчиком Timer1()
timers.NewTimer(1000, Timer2); // создаем новый таймер с периодом обновления 1000  миллисекунд и функцией-обработчиком Timer2()
timers.NewTimer(2000, Timer3); // создаем новый таймер с периодом обновления 2000  миллисекунд и функцией-обработчиком Timer3()

4. Когда вам таймер больше не нужен, убейте его:

timers.KillTimer(Timer1);       // убивает все таймеры с функцией обработчиком Timer1
или
timers.KillTimer(500, Timer1); // если существует несколько таймеров с функцией Timer1, но с разными периодами, и требуется удалить только таймер определенного периода (в данном случае 500)

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

Вы можете узнать сколько запущено таймеров в данный момент с помощью функции 

timers.GetN()

Одноразовый таймер (с версии 1.08)

Когда нужно не «каждые N миллисекунд», а «один раз через N миллисекунд», заводите таймер через SetTimeout. Он вызовет обработчик один раз и снимется сам — убивать его из обработчика не нужно:

timers.SetTimeout(500, Delayed); // Delayed() будет вызвана один раз через 500 мс

Проверки на дубликаты у SetTimeout нет: одноразовых таймеров с одной и той же функцией и одним и тем же сроком можно завести сколько угодно.

Свой OnTimer (с версии 1.08)

По умолчанию библиотека объявляет OnTimer сама, и своего в программе быть не должно. Если он всё же нужен — например, программа получает события таймера ещё от чего-то — определите TIMER_CUSTOM_ONTIMER до подключения файла и позовите timers.OnTimer() из своего обработчика:

#define TIMER_CUSTOM_ONTIMER
#include <Semko\Timer.mqh>

void OnTimer() {
   timers.OnTimer();   // без этой строки ни один таймер работать не будет
   // ... здесь ваш собственный код
}

Все доступные функции

   uint NewTimer(int milliseconds, TFuncTimer fun);    // Создает таймер с периодичностью milliseconds и функцией обработки fun.
                                                       // Возвращает дескриптор таймера, или 0, если такой таймер уже есть.
   uint SetTimeout(int milliseconds, TFuncTimer fun);  // Одноразовый таймер: вызывает fun один раз через milliseconds и снимается сам.
   void KillTimer(TFuncTimer fun);                     // Удаляет все таймеры с функцией обработки fun
   void KillTimer(int milliseconds, TFuncTimer fun);   // Удаляет таймер с функцией обработки fun и периодичностью milliseconds
   void KillTimer(uint id);                            // Удаляет таймер по дескриптору, полученному от NewTimer
   void NewPeriod(int old_milliseconds, TFuncTimer fun, int new_milliseconds); // Меняет периодичность данного таймера. Это позволяет
                                                       // создавать таймеры с динамическим периодом обращения, или автоподстройкой.
   void NewPeriod(TFuncTimer fun, int new_milliseconds); // Меняет периодичность таймера с функцией обработки fun.
                                                       // Если таких таймеров несколько, остальные удаляются.
   uint GetN();                                        // Получаем текущее количество работающих таймеров
   int  GetLost(int milliseconds, TFuncTimer fun);     // Получаем количество пропущенных событий таймера для контроля стабильности работы.
                                                       // Если ноль, то пропусков нет. Если -1, то не найден такой таймер.
   int  GetNbyStart(uint id);                          // Текущий номер кадра таймера по его дескриптору. -1, если не найден.

Дескриптор, который возвращает NewTimer — это просто уникальный номер таймера. Он нужен, если одну и ту же функцию обслуживают несколько таймеров и снять требуется вполне определённый: тогда KillTimer(id) точнее, чем KillTimer(fun) или KillTimer(ms, fun).


Пример тестового кода:

#property indicator_chart_window
#include <Semko\Timer.mqh>


class CTestTimer { // простой класс для теста с внутренним обработчиком таймера
 private:
   int x;
 public:
   int GetX()   {return x;};
   CTestTimer() {x=0;};
  ~CTestTimer() {};
   void Timer() {x++;};
};


int x1=0;
int x2=0;
int x3=0;
CTestTimer X; // пример, если обработчик таймера - это метод класса.

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
int OnInit() {
   timers.NewTimer(500, Timer1);   // создаем новый таймер с периодом обновления 500   миллисекунд и функцией-обработчиком Timer1()
   timers.NewTimer(700, Timer2);   // создаем новый таймер с периодом обновления 700   миллисекунд и функцией-обработчиком Timer2()
   timers.NewTimer(900, Timer3);   // создаем новый таймер с периодом обновления 900   миллисекунд и функцией-обработчиком Timer3()
   timers.NewTimer(1100,Timer4);   // создаем новый таймер с периодом обновления 1100  миллисекунд и функцией-обработчиком Timer4(), в которой запускаем обработчик класса
   timers.NewTimer(100, Timer5);   // создаем новый таймер с периодом обновления 100   миллисекунд и функцией-обработчиком Timer5()
   return(INIT_SUCCEEDED);
}
//+------------------------------------------------------------------+
void OnDeinit(const int reason) {
   Comment("");
}
//+------------------------------------------------------------------+
int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[]) {
   return(rates_total);
}
//+------------------------------------------------------------------+
void Timer1() {  // данная функция вызывается каждые 500 миллисекунд
   x1++;
   if (x1==10) timers.KillTimer(Timer1); // удаляем этот таймер
}
//+------------------------------------------------------------------+
void Timer2() {  // данная функция вызывается каждые 700 миллисекунд
   x2++;
   if (x2==10) timers.KillTimer(Timer2); // удаляем этот таймер
}
//+------------------------------------------------------------------+
void Timer3() {  // данная функция вызывается каждые 900 миллисекунд
   x3++;
   if (x3==10) timers.KillTimer(Timer3); // удаляем этот таймер
}
//+------------------------------------------------------------------+
void Timer4() {  // данная функция вызывается каждые 1100 миллисекунд
   X.Timer();
   if (X.GetX()==10) timers.KillTimer(Timer4); // удаляем этот таймер
}
//+------------------------------------------------------------------+
void Timer5() {  // данная функция вызывается каждые 100 миллисекунд
   if (X.GetX()==10) timers.KillTimer(Timer5); // удаляем этот таймер
   Comment("x1=" +string(x1)+"\nx2="+string(x2)+"\nx3="+string(x3)+"\nx4="+string(X.GetX())+"\nВсего таймеров - "+string(timers.GetN()));
}

О минимальном шаге таймера

Важно помнить, что хоть таймер и задается в миллисекундах, минимальный шаг таймера это 15.625 миллисекунды (1/64 секунды). 
Т.е. если Вы задали таймер 1 миллисекунду, реально событие таймера будет происходить не 1000 раз в секунду, а 64 раза в секунду.

Отсюда два следствия, о которых стоит помнить:

  • Момент срабатывания округляется вверх до ближайшего системного тика, поэтому опоздание до 15.625 мс — это норма, а не признак загруженного потока. При этом ошибка не накапливается: расписание привязано к моменту создания таймера, а не к предыдущему срабатыванию, так что за сутки таймер никуда не уедет.
  • Период мельче шага раздувает счётчик GetLost. При периоде 1 мс каждый системный тик пропускает около пятнадцати расчётных срабатываний, и GetLost покажет тысячи «потерь», хотя программа ни в чём не виновата — это физический предел, а не её загруженность. Ставить период меньше 16 мс смысла нет.

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

История версий

  • 1.08 — добавлен одноразовый таймер SetTimeout. Снятие таймера больше не перекладывает массив во время обхода: обработчик, снявший сразу несколько таймеров, ронял один из них мимо текущего прохода. Объявление OnTimer можно отключить через TIMER_CUSTOM_ONTIMER.
  • 1.07 — GetTickCount заменён на GetTickCount64. 32-битный счётчик переполняется через 49.7 суток, и таймер после этого замолкал не на один цикл, а навсегда: на круглосуточно работающем терминале это рано или поздно случалось гарантированно. Первый вызов обработчика перенесён на конец первого периода — раньше он происходил сразу при создании таймера. KillTimer перестал пропускать дубликаты. Дескриптором таймера стал собственный счётчик, а не момент создания: два таймера, созданные в одну миллисекунду, раньше были неразличимы. Период меньше 1 мс больше не приводит к делению на ноль.
    Благодарю Dzmitry Kokhanau за замечания, с которых началась эта версия.
  • 1.06 — добавлена проверка обработчика на NULL перед вызовом. NewTimer стал возвращать дескриптор. Добавлены KillTimer(id), GetNbyStart(id) и второй вариант NewPeriod.

TimerNew — следующая ветка

Параллельно с 1.x существует версия 2.0 в отдельном файле TimerNew.mqh. Она не заменяет эту библиотеку и ничего в ней не ломает — переходить на неё имеет смысл, если нужно что-то из следующего:

  • OnTimer остаётся за вами. Библиотека его не объявляет, вы пишете одну строку сами.
  • Обработчиком может быть объект, а не только свободная функция. Указателя на метод в MQL5 нет, поэтому в 1.x класс не мог завести таймер «на себя» — приходилось держать функцию снаружи. В 2.0 достаточно реализовать интерфейс ITimerHandler.
  • Обработчик может узнать, какой таймер сработал — есть вариант с параметром id, и одна функция обслуживает несколько таймеров.
  • Штатный таймер взводится на срок до ближайшего события, а не намертво на 15 мс. Единственный таймер с периодом 10 минут в 1.x будит поток 66 раз в секунду — сорок тысяч холостых пробуждений на одно полезное.
  • Есть пауза, смена периода на лету и снятие всех таймеров объекта разом.

Если же вам достаточно нескольких периодических функций — оставайтесь на 1.x, она проще и проверена временем.

Exponential Weighted Moving Average Exponential Weighted Moving Average

Exponential Weighted Moving Average - экспоненциально взвешенная скользящая средняя (EWMA).

Opened Positions Opened Positions

Утилита: суммарная информация по позициям

Area Chart Area Chart

Индикатор меняет вид графика

Candle closed at the High Candle closed at the High

Анализируем цену закрытия бара #1