Ставь лайки и следи за новостями
Поставь на него ссылку - пусть другие тоже оценят
Оцени его работу в терминале MetaTrader 5
- Просмотров:
- 3054
- Рейтинг:
- Опубликован:
- Обновлен:
-
Нужен робот или индикатор на основе этого кода? Закажите его на бирже фрилансеров Перейти на биржу
Данная библиотека Вам пригодится, когда Вам необходимы несколько таймеров с независимой обработкой и неконфликтующие между собой.
Для работы с данной библиотекой важно запомнить следующие правила:
- В теле вашей программы не должно быть функции 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 - экспоненциально взвешенная скользящая средняя (EWMA).
Opened Positions
Утилита: суммарная информация по позициям