Внедрение в MQL5 практических модулей из других языков (Часть 03): Модуль schedule из Python — расширенные возможности OnTimer
Разделы
- Введение
- Что представляет собой модуль schedule в Python??
- Класс schedule в MQL5
- Запуск функций в определенное время
- Передача аргументов в задачу
- Выполнение задачи до определенного времени
- Выполнение всех заданий независимо от их расписаний
- Управление расписаниями
- Работа с часовыми поясами
- Применение модуля schedule в ваших торговых приложениях
- Модуль schedule vs событие OnTimer
- Заключение
Введение
Программирование призвано облегчить нам жизнь, позволяя автоматизировать многие важные, а иногда и скучные или повторяющиеся задачи, которые мы часто хотим поручить компьютерам выполнить без участия человека. Хороший пример — функция автосохранения, которую можно увидеть во многих текстовых редакторах. Вместо того чтобы беспокоиться о сохранении документа каждый раз, когда вы пишете новое слово, текстовые редакторы обрабатывают процесс сохранения автоматически, так что вы можете сосредоточиться на создании контента и не беспокоиться о потере своего труда в случае непредвиденных обстоятельств.
В сфере торговли ситуация ничем не отличается: многочисленные повторяющиеся действия и задачи, необходимые для совершения сделок, мы хотим автоматизировать с помощью нескольких строк кода.

В языке программирования MQL5 существует хорошо известная функция OnTimer, которая помогает запускать определенные функции и строки кода через заданный программистом интервал времени.
Ниже приведен простой пример — запуск функции OnTimer через каждые 10 секунд.
//+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit() { EventSetTimer(10); //Creates a timer with 10 seconds period //--- return(INIT_SUCCEEDED); } //+------------------------------------------------------------------+ //| Expert deinitialization function | //+------------------------------------------------------------------+ void OnDeinit(const int reason) { EventKillTimer(); //Destroy the timer after completing the work } //+------------------------------------------------------------------+ //| Expert tick function | //+------------------------------------------------------------------+ void OnTick() { //--- } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ void OnTimer(void) { Print("Ontimer called: ",TimeLocal()); //This line of code will be run after every 10 seconds }
Результаты.
MN 0 10:16:39.455 Schedule test (XAUUSD,D1) Ontimer called: 2025.07.21 10:16:39 CD 0 10:16:49.459 Schedule test (XAUUSD,D1) Ontimer called: 2025.07.21 10:16:49
Эта функция вполне добротна, но примитивна и недостаточно гибка, чтобы позволить одновременно запускать несколько различных расписаний в одной и той же программе.
После того как вы настроите обработчик событий OnTimer в своем советнике или индикаторе, вы можете полагаться только на это единственное «расписание таймера». Это очень ограничивает возможности, поскольку в наших программах часто необходимо выполнять разные задачи в разное время (с разными интервалами).
Например, печать или отправка пользователям ежедневных, еженедельных и ежемесячных отчетов о торговых операциях.
В языке программирования Python существует модуль, похожий на функцию OnTimer, но он гораздо лучше подходит для своевременного выполнения функций. В этой статье мы рассмотрим его и реализуем аналогичный модуль на языке программирования MQL5.
Что представляет собой модуль schedule в Python?
Dubbed as — планирование задач в Python для человека
Это удобный для пользователя модуль в языке Python, который помогает планировать выполнение конкретных задач в определенное время дня, недели и т. д. Этот модуль прост в использовании и имеет небольшой вес, что делает его необходимым компонентом, о котором должен знать каждый разработчик на Python.
В отличие от функции OnTimer, которая есть в MQL5, модуль schedule не только позволяет планировать выполнение задач через определенный промежуток времени, но и обеспечивает гибкость, позволяя более точно указывать, когда и как должна выполняться та или иная задача (функция).
Ниже перечислены некоторые функции, которые предлагает этот модуль.
расписание import | Функция | Описание |
|---|---|
schedule.every(10).minutes.do(job) | Аналогично событию OnTimer, каждые 10 минут будет запускаться функция с именем job. |
schedule.every().hour.do(job) | Функция с именем job будет запускаться каждый час с момента начала выполнения скрипта. |
schedule.every().day.at("10:30").do(job) | Функция с именем job будет запускаться ежедневно в определенное местное время — в 10:30 в 24-часовом формате. |
schedule.every().monday.do(job) | Функция с именем job будет вводиться в действие каждый понедельник в точное время, когда скрипт был запущен в первый раз. |
schedule.every().wednesday.at("13:15").do(job) | Функция с именем job будет вводиться в действие каждую среду в 13:15. |
schedule.every().day.at("12:42", "Europe/Amsterdam").do(job) | Функция с именем job будет вызываться ежедневно в 12:42 по европейскому/амстердамскому времени. |
schedule.every().minute.at(":17").do(job) | Функция с именем job будет вызываться каждую минуту на 17-й секунде. |
Это лишь некоторые из важнейших функций, предлагаемых в этом модуле. Реализуем хорошо знакомый нам класс на языке MQL5.
Класс schedule в MQL5
Модуль schedule в языке Python предназначен для использования отдельных функций при выполнении каждой задачи, которую вы хотите запланировать на определенный интервал времени. Функция с именем doявляется конечной точкой всей цепочки функций из класса schedule.
schedule.every(10).minutes.do Для реализации аналогичного синтаксиса в MQL5 нам необходимо заставить несколько функций в классе CSchedule вернуть экземпляр всего класса, за исключением функции с имени do, которая является конечной точкой.
class CSchedule { private: int m_period; //the number of seconds, minutes, etc to use time_intervals_enum m_unit; //time interval: minutes, hours, etc int m_time_seconds; //datetime in seconds JobFunction m_func; //The function to run for the current schedule public: int m_fixed_time; // time from midnight in seconds CSchedule(void); ~CSchedule(void); CSchedule* every(int period = 1); CSchedule* seconds(); CSchedule* minutes(); CSchedule* hours(); CSchedule* days(); CSchedule* weeks(); CSchedule* months(); CSchedule* years(); void dO(JobFunction func); }
Этот синтаксис позволяет иметь интерфейс, аналогичный тому, который предлагает модуль schedule из языка Python.
#include <schedule.mqh> CSchedule schedule; //+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit() { schedule.every().minutes().dO(runthis); }
Функция с именем every имеет решающее значение для установки интервала конкретного таймфрейма. Для примера:
schedule.every(10).minutes()
Это означает, что каждые 10 минут должна срабатывать какая-то отдельная функция, полученная функцией с именем do.
По своей сути, эта функция принимает заданное значение period и присваивает эту переменную переменной с именемm_period—, которая хранится внутри класса.
CSchedule* CSchedule::every(int period = 1) { m_period = period; return GetPointer(this); }
Функции: seconds, minutes, hours и т. д., присваивает переменную timeframe в соответствии со всеми доступными вариантами таймфреймов, заданными перечислителем под именем time_intervals_enum.
enum time_intervals_enum
{
SECONDS,
MINUTES,
HOURS,
DAYS,
WEEKS,
MONTHS,
YEARS
}; CSchedule* CSchedule::seconds()
{
m_unit = SECONDS;
return GetPointer(this);
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
CSchedule* CSchedule::minutes()
{
m_unit = MINUTES;
return GetPointer(this);
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
CSchedule* CSchedule::hours()
{
m_unit = HOURS;
return GetPointer(this);
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
CSchedule* CSchedule::days()
{
m_unit = DAYS;
return GetPointer(this);
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
CSchedule* CSchedule::weeks(void)
{
m_unit = WEEKS;
return GetPointer(this);
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
CSchedule* CSchedule::months(void)
{
m_unit = MONTHS;
return GetPointer(this);
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
CSchedule* CSchedule::years(void)
{
m_unit = YEARS;
return GetPointer(this);
} Прежде чем мы разберемся с функцией, названной dO, которая является конечной точкой всех функций планирования, постараемся понять, как обрабатывается и хранится каждая задача (функция) в файле с именем schedule.mqh.
//+------------------------------------------------------------------+ //| Handling and storing every job (task) used in the class CSchedule| //+------------------------------------------------------------------+ typedef void (*JobFunction)(); // For global functions struct jobs_struct { int prev_run; int next_run; int interval; JobFunction func; // Store the function pointer }; jobs_struct m_jobs[]; // Global job list void jobs_add(jobs_struct &jobs_array[], const jobs_struct &job) { uint size = ArraySize(jobs_array); ArrayResize(jobs_array, size + 1); jobs_array[size] = job; }
Поскольку в классе CSchedule есть функции, которые ссылаются на него же, обработка каждого объекта задания внутри этого класса становится сложной или запутанной и чреватой ошибками.
Наличие глобального массива с именем m_jobs предоставляет универсальный способ хранения и обработки всех задач, используемых внутри класса. Мы обсудим это чуть позже.
Функция с именем dO получает функцию, которая должна выполняться многократно в соответствии с полученным «расписанием». Она вычисляет время последнего выполнения функции и время следующего ожидаемого ее выполнения.
Полученная функция сохраняется в структуре с именем jobs_struct наряду со свойствами других задач, такими как время последнего выполнения и время следующего выполнения задания.
Все эти значения затем сохраняются в массиве с именем m_jobs, который представляет собой массив типа jobs_struct.
jobs_struct m_jobs[]; // Global job list void CSchedule::dO(JobFunction func) { m_func = func; jobs_struct job; job.func = m_func; datetime now = TimeLocal(); job.prev_run = (int)now; job.interval = timedelta(m_period, m_unit); //Get configs from the every() method and above job.next_run = job.prev_run + timedelta(m_period, m_unit); if (MQLInfoInteger(MQL_DEBUG)) Print("The first function run is schedule at: ", TimeToString((datetime)job.next_run, TIME_DATE | TIME_SECONDS)); //--- jobs_add(m_jobs, job); //store the job object to the list of jobs }
После того как задача сохранена в соответствующем массиве, нам необходима универсальная функция для постоянного мониторинга этого массива и запуска его функции при наступлении соответствующего времени по расписанию.
void CSchedule::run_pending() { int now = (int)TimeLocal(); for(int i = 0; i < ArraySize(m_jobs); i++) { if(now >= m_jobs[i].next_run) { if(m_jobs[i].func != NULL) m_jobs[i].func(); m_jobs[i].prev_run = m_jobs[i].next_run; // Recalculate next_run m_jobs[i].next_run += m_jobs[i].interval; if (MQLInfoInteger(MQL_DEBUG)) printf("Prev run: %s Next run: %s", TimeToString((datetime)m_jobs[i].prev_run, TIME_DATE|TIME_SECONDS), TimeToString((datetime)m_jobs[i].next_run, TIME_DATE|TIME_SECONDS)); } } }
Составим расписание для самой первой нашей задачи с помощью этого класса.
#include <schedule.mqh> CSchedule schedule; //+------------------------------------------------------------------+ //| Script program start function | //+------------------------------------------------------------------+ void OnStart() { //--- schedule.every(10).seconds().dO(runthis); while (true) { schedule.run_pending(); } } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ void runthis() { Print(__FUNCTION__," called at: ",TimeLocal()); } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+
Мы хотим вызывать функцию runthis каждые 10 секунд. Бесконечный цикл while используется просто для того, чтобы скрипт продолжал работать, пока его не остановят.
Ниже приведен результат, записанный в лог при выполнении скрипта в режиме отладки.
NO 0 14:51:56.301 schedule test (XAUUSD,D1) The first function run is schedule at: 2025.07.21 14:52:06 GS 0 14:52:06.000 schedule test (XAUUSD,D1) runthis called at: 2025.07.21 14:52:06 PJ 0 14:52:06.000 schedule test (XAUUSD,D1) Prev run: 2025.07.21 14:52:06 Next run: 2025.07.21 14:52:16 QH 0 14:52:16.000 schedule test (XAUUSD,D1) runthis called at: 2025.07.21 14:52:16 GR 0 14:52:16.000 schedule test (XAUUSD,D1) Prev run: 2025.07.21 14:52:16 Next run: 2025.07.21 14:52:26 KP 0 14:52:26.000 schedule test (XAUUSD,D1) runthis called at: 2025.07.21 14:52:26 FJ 0 14:52:26.000 schedule test (XAUUSD,D1) Prev run: 2025.07.21 14:52:26 Next run: 2025.07.21 14:52:36
Мы можем составить несколько расписаний для одного и того же класса.
void OnStart() { //--- schedule.every(10).seconds().dO(runthis); //run after every 10 seconds schedule.every().minute().dO(runthis2); //run on every minute while (true) { schedule.run_pending(); } } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ void runthis() { Print(__FUNCTION__," called at: ",TimeLocal()); } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ void runthis2() { Print("Hello world!, This function is called after a minute has passed"); }
Результаты.
FK 0 15:00:55.079 schedule test (XAUUSD,D1) The first function run is schedule at: 2025.07.21 15:01:05 IL 0 15:00:55.079 schedule test (XAUUSD,D1) The first function run is schedule at: 2025.07.21 15:01:55 ER 0 15:01:05.000 schedule test (XAUUSD,D1) runthis called at: 2025.07.21 15:01:05 RK 0 15:01:05.000 schedule test (XAUUSD,D1) Prev run: 2025.07.21 15:01:05 Next run: 2025.07.21 15:01:15 OK 0 15:01:15.000 schedule test (XAUUSD,D1) runthis called at: 2025.07.21 15:01:15 ES 0 15:01:15.000 schedule test (XAUUSD,D1) Prev run: 2025.07.21 15:01:15 Next run: 2025.07.21 15:01:25 IS 0 15:01:25.000 schedule test (XAUUSD,D1) runthis called at: 2025.07.21 15:01:25 LJ 0 15:01:25.000 schedule test (XAUUSD,D1) Prev run: 2025.07.21 15:01:25 Next run: 2025.07.21 15:01:35 CK 0 15:01:35.000 schedule test (XAUUSD,D1) runthis called at: 2025.07.21 15:01:35 KR 0 15:01:35.000 schedule test (XAUUSD,D1) Prev run: 2025.07.21 15:01:35 Next run: 2025.07.21 15:01:45 MP 0 15:01:45.000 schedule test (XAUUSD,D1) runthis called at: 2025.07.21 15:01:45 FJ 0 15:01:45.000 schedule test (XAUUSD,D1) Prev run: 2025.07.21 15:01:45 Next run: 2025.07.21 15:01:55 GH 0 15:01:55.000 schedule test (XAUUSD,D1) runthis called at: 2025.07.21 15:01:55 NM 0 15:01:55.000 schedule test (XAUUSD,D1) Prev run: 2025.07.21 15:01:55 Next run: 2025.07.21 15:02:05 KR 0 15:01:55.000 schedule test (XAUUSD,D1) Hello world!, This function is called after a minute has passed MH 0 15:01:55.000 schedule test (XAUUSD,D1) Prev run: 2025.07.21 15:01:55 Next run: 2025.07.21 15:02:55 NJ 0 15:02:05.001 schedule test (XAUUSD,D1) runthis called at: 2025.07.21 15:02:05 HP 0 15:02:05.001 schedule test (XAUUSD,D1) Prev run: 2025.07.21 15:02:05 Next run: 2025.07.21 15:02:15 GR 0 15:02:15.000 schedule test (XAUUSD,D1) runthis called at: 2025.07.21 15:02:15 LK 0 15:02:15.000 schedule test (XAUUSD,D1) Prev run: 2025.07.21 15:02:15 Next run: 2025.07.21 15:02:25 RK 0 15:02:25.001 schedule test (XAUUSD,D1) runthis called at: 2025.07.21 15:02:25 FS 0 15:02:25.001 schedule test (XAUUSD,D1) Prev run: 2025.07.21 15:02:25 Next run: 2025.07.21 15:02:35 OS 0 15:02:35.004 schedule test (XAUUSD,D1) runthis called at: 2025.07.21 15:02:35 JK 0 15:02:35.004 schedule test (XAUUSD,D1) Prev run: 2025.07.21 15:02:35 Next run: 2025.07.21 15:02:45 EK 0 15:02:45.000 schedule test (XAUUSD,D1) runthis called at: 2025.07.21 15:02:45 KR 0 15:02:45.000 schedule test (XAUUSD,D1) Prev run: 2025.07.21 15:02:45 Next run: 2025.07.21 15:02:55 OP 0 15:02:55.000 schedule test (XAUUSD,D1) runthis called at: 2025.07.21 15:02:55 EJ 0 15:02:55.000 schedule test (XAUUSD,D1) Prev run: 2025.07.21 15:02:55 Next run: 2025.07.21 15:03:05 FK 0 15:02:55.000 schedule test (XAUUSD,D1) Hello world!, This function is called after a minute has passed
Запуск задач (функций) в определенное время
Нам часто требуется запускать наши функции в строго определенное время. Например, нужно запустить функцию, отвечающую за открытие сделки в определенное время в соответствии с торговой сессией, предположим, открытие сделки в 19:00 по местному времени.
В классе CSchedule все функции, названные at, несут ответственность за это, если им задано конкретное «законное время».
Например:
schedule.every().day().at(19, 10).dO(job);
Это задает для функции с именем job расписание, в соответствии с которым она запускается ежедневно в 19:10.
Реализация этой функции в MQL5 представляет собой сложную задачу, поскольку нам необходимо, чтобы отдельные классы возвращались для каждой «привязанной ко времени» функции, то есть функции секунды, минуты, часа, дня, недели.
class CSchedule { private: int m_period; //the number of seconds, minutes, etc to use time_intervals_enum m_unit; //time interval: minutes, hours, etc int m_time_seconds; //datetime in seconds JobFunction m_func; //The function to run for the current schedule bool has_fixed_time() const { return m_fixed_time > 0; } datetime TodaysDate(datetime dt) { // Extract year, month, day — and build a new datetime at 00:00:00 MqlDateTime tm; TimeToStruct(dt, tm); tm.hour = 0; tm.min = 0; tm.sec = 0; return StructToTime(tm); } public: int m_fixed_time; // time from midnight in seconds CSchedule(void); ~CSchedule(void); CSchedule* every(int period = 1); CSchedule* seconds(); CSchedule* minutes(); CSchedule* hours(); CSchedule* days(); CSchedule* weeks(); MinuteScheduleBuilder* minute(); HourScheduleBuilder* hour(); DayScheduleBuilder* day(); WeekScheduleBuilder* week();
Внутри каждого «строительного» класса (все классы, заканчивающиеся на словоBuilder) у нас есть функция с именем at, которая отвечает за установку определенного временного интервала.
Кроме того, у нас есть функция с именем dO, которая наследует одноименную функцию от класса CSchedule.
Например, класс WeekScheduleBuilder.
class CSchedule; //forward declaration | VERY IMPORTANT class WeekScheduleBuilder { protected: CSchedule *m_schedule; public: WeekScheduleBuilder(CSchedule *schedule_) { m_schedule = schedule_; } ~WeekScheduleBuilder(void) {}; CSchedule* at(ENUM_DAY_OF_WEEK dayofweek, uint hour=0, uint minutes = 0, uint seconds = 0) { if (CheckPointer(m_schedule) == POINTER_INVALID || m_schedule == NULL) return NULL; datetime now = TimeLocal(); MqlDateTime tm; TimeToStruct(now, tm); int today_dow = tm.day_of_week; //--- Compute days until target day (next week if it's the same day or already passed) int days_ahead = (int)dayofweek - today_dow; if (days_ahead < 0) days_ahead += 7; // ensure it's next week datetime next_target_date = now + timedelta(days_ahead, DAYS); MqlDateTime target_tm; TimeToStruct(next_target_date, target_tm); //--- setting the correct time target_tm.hour = (int)hour; target_tm.min = (int)minutes; target_tm.sec = (int)seconds; m_schedule.m_fixed_time = (int)StructToTime(target_tm); return m_schedule; } void dO(JobFunction func) { m_schedule.dO(func); } };
Все функции с именем at принимают заданное время, переданное в качестве аргумента, — время до первого выполнения функции, — и присваивают значение времени в секундах переменной с именем m_fixed_time.
Внутри функции с именем dO вводим условие для проверки, является полученное значение времени фиксированным значением времени (например, 19:00) или запланированным количеством секунд, минут и т. д. для следующего запуска функции, поскольку для решения обеих проблем требуется несколько иной подход.
void CSchedule::dO(JobFunction func) { m_func = func; jobs_struct job; job.func = m_func; datetime now = TimeLocal(); job.prev_run = (int)now; job.interval = timedelta(m_period, m_unit); //Get configs from the every() method and above if (has_fixed_time()) { datetime scheduled_time = (datetime)m_fixed_time; //we add today's date to the fixed_time calculated //Add interval repeatedly until scheduled_time >= now while (scheduled_time <= now) scheduled_time += job.interval; //Schedule for the next time if the current time has passed job.next_run = (int)scheduled_time; } else { job.next_run = job.prev_run + job.interval; } if (MQLInfoInteger(MQL_DEBUG)) Print("The first function run is schedule at: ", TimeToString((datetime)job.next_run, TIME_DATE | TIME_SECONDS)); //--- jobs_add(m_jobs, job); //store the job object to the list of jobs }
Ниже описано, как настроить несколько расписаний для многократного выполнения в определенное время.
void OnStart() { //--- schedule.every().minute().at(10).dO(job); //Runs on every minute at the 10th second schedule.every().hour().at(10).dO(job); //runs on every hour at the 10th minute schedule.every().day().at(19, 10).dO(job); //runs every day at 19:10 hours schedule.every().week().at(MONDAY).dO(job); //Runs every week on Monday at 00:00 (by default) while (true) { schedule.run_pending(); } } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ void job() { Print(__FUNCTION__," run at: ",TimeLocal()); }
Передача аргументов в задачу
Как видно из приведенных ранее примеров логов с результатами, сложно идентифицировать и отслеживать ход выполнения задачи, особенно когда одновременно выполняется несколько запланированных функций. Чтобы это исправить, нам нужна необязательная переменная с именем jobs_name в функции с именем dO, причем эта переменная помогает при маркировке всех запланированных задач.
void CSchedule::dO(JobFunction func, const string jobs_name="") { jobs_struct job; job.func = func; //Assigns the function to job's sturucture job.name = jobs_name; //Assigns the name to job's structure datetime now = TimeLocal(); job.prev_run = (int)now; job.interval = timedelta(m_period, m_unit); //Get configs from the every() method and above if (has_fixed_time()) { datetime today_midnight = TodaysDate(now); //Get todays date at 00:00 datetime scheduled_time = today_midnight + m_fixed_time; //we add today's date to the fixed_time calculated //Add interval repeatedly until scheduled_time >= now while (scheduled_time <= now) scheduled_time += job.interval; //Schedule for the next time job.next_run = (int)scheduled_time; } else { job.next_run = job.prev_run + job.interval; } if (MQLInfoInteger(MQL_DEBUG)) printf("Job: %s -> first run schedule at: [%s]",job.name, TimeToString((datetime)job.next_run, TIME_DATE | TIME_SECONDS)); //--- jobs_add(m_jobs, job); //store the job object to the list of jobs }
Теперь мы можем более эффективно отслеживать ход выполнения каждой задачи.
void OnStart() { //--- schedule.every().minute().at(10).dO(Greet, "Jacob"); schedule.every().hour().at(10).dO(Greet, "Anne"); schedule.every().day().at(08, 10).dO(Greet, "Chriss"); schedule.every().week().at(MONDAY).dO(Greet, "Nobody"); while (true) { schedule.run_pending(); } } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ void Greet() { Print("Hello there!"); }
Результаты.
JI 0 06:57:54.817 schedule test (XAUUSD,D1) Job: Jacob -> first run schedule at: [2025.07.22 06:58:10] MH 0 06:57:54.817 schedule test (XAUUSD,D1) Job: Anne -> first run schedule at: [2025.07.22 07:10:00] FL 0 06:57:54.817 schedule test (XAUUSD,D1) Job: Chriss -> first run schedule at: [2025.07.22 08:10:00] JO 0 06:57:54.817 schedule test (XAUUSD,D1) Job: Nobody -> first run schedule at: [2025.07.28 00:00:00] GN 0 06:58:10.014 schedule test (XAUUSD,D1) Hello there! LF 0 06:58:10.014 schedule test (XAUUSD,D1) Job: Jacob -> Prev run: [2025.07.22 06:58:10] Next run: [2025.07.22 06:59:10]
Выполнение задачи до определенного времени
Иногда у нас есть запланированные задачи, но мы не хотим, чтобы они выполнялись бесконечно. В данном случае помогло бы установление крайнего срока для выполнения этих работ.
Представим функцию с именем until. Функция, аналогичная той, что доступна в модуле schedule в языке Python.
CSchedule* CSchedule::until(datetime expiry_date) { m_expiry_date = expiry_date; return GetPointer(this); }
Внутри функции с именемdO мы берем дату истечения срока действия из нашего класса (полученную из функции с именем until) и присваиваем ее структуре задачи.
void CSchedule::dO(JobFunction func, const string jobs_name="") { jobs_struct job; job.func = func; //Assigns the function to job's sturucture job.name = jobs_name; //Assigns the name to job's structure datetime now = TimeLocal(); job.prev_run = (int)now; job.interval = timedelta(m_period, m_unit); //Get configs from the every() method and above job.expiry_date = m_expiry_date; //Other lines of code }
Перед запуском задачи внутри функции с именем run_pending нам нужно проверить, не истек ли срок ее действия.
void CSchedule::run_pending() { int now = (int)TimeLocal(); for(int i = 0; i < ArraySize(m_jobs); i++) { if (now >= (int)m_jobs[i].expiry_date && expiry_date != 0) //Check if the job hasn't expired { if (MQLInfoInteger(MQL_DEBUG)) printf("Job: %s -> Expired",m_jobs[i].name); continue; //skip all expired jobs } //... other checks }
Наконец, запускаем задачу и устанавливаем дату истечения срока действия через 5 минут после текущего момента.
#include <schedule.mqh> CSchedule schedule; //+------------------------------------------------------------------+ //| Script program start function | //+------------------------------------------------------------------+ void OnStart() { //--- schedule.every(1).minutes().until(D'22.7.2025 10:15').dO(Greet, "Greet"); //The current time was 10:10, in the same date while (true) { schedule.run_pending(); Sleep(1000); } } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ void Greet() { Print("Hello there!"); }
Результаты.
EM 0 10:10:02.849 schedule test (XAUUSD,D1) Job: Greet -> first run schedule at: [2025.07.22 10:11:02] CI 0 10:11:02.864 schedule test (XAUUSD,D1) Hello there! NS 0 10:11:02.864 schedule test (XAUUSD,D1) Job: Greet -> Prev run: [2025.07.22 10:11:02] Next run: [2025.07.22 10:12:02] FR 0 10:12:02.873 schedule test (XAUUSD,D1) Hello there! EJ 0 10:12:02.873 schedule test (XAUUSD,D1) Job: Greet -> Prev run: [2025.07.22 10:12:02] Next run: [2025.07.22 10:13:02] ND 0 10:13:02.861 schedule test (XAUUSD,D1) Hello there! OL 0 10:13:02.861 schedule test (XAUUSD,D1) Job: Greet -> Prev run: [2025.07.22 10:13:02] Next run: [2025.07.22 10:14:02] GM 0 10:14:02.922 schedule test (XAUUSD,D1) Hello there! PG 0 10:14:02.922 schedule test (XAUUSD,D1) Job: Greet -> Prev run: [2025.07.22 10:14:02] Next run: [2025.07.22 10:15:02] LH 0 10:15:00.945 schedule test (XAUUSD,D1) Job: Greet -> Expired
Выполнение всех заданий независимо от их расписаний
Иногда может возникнуть необходимость запустить все функции мгновенно, несмотря на их расписание. Обычно это делается для тестирования, а иногда нам просто нужно принудительно запустить все запланированные операции одновременно, например, во время запуска программы.
В подобных ситуациях удобно использовать функцию с именем run_all.
void CSchedule::run_all(void) { datetime now = TimeLocal(); for(int i = 0; i < ArraySize(m_jobs); i++) { if(m_jobs[i].func != NULL) m_jobs[i].func(); m_jobs[i].prev_run = m_jobs[i].next_run; // Recalculate next_run m_jobs[i].next_run += m_jobs[i].interval; if (MQLInfoInteger(MQL_DEBUG)) printf("%s run at: %s",m_jobs[i].name, TimeToString(now, TIME_DATE|TIME_SECONDS)); } if (MQLInfoInteger(MQL_DEBUG)) printf("%s -> All %I64u Jobs have been executed!",__FUNCTION__, m_jobs.Size()); }
Пример использования.
#include <schedule.mqh> CSchedule schedule; //+------------------------------------------------------------------+ //| Script program start function | //+------------------------------------------------------------------+ void OnStart() { //--- schedule.every(4).minutes().dO(Greet, "Greet every minute"); schedule.every(4).hours().dO(Greet, "Greet hourly"); schedule.every(4).days().dO(Greet, "Greet daily"); schedule.every(4).weeks().dO(Greet, "Greet weekly"); schedule.run_all(); } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ void Greet() { Print("Hello there!"); }
Результаты.
CM 0 11:04:06.695 schedule test (XAUUSD,D1) Job: Greet every minute -> first run schedule at: [2025.07.22 11:08:06] RR 0 11:04:06.695 schedule test (XAUUSD,D1) Job: Greet hourly -> first run schedule at: [2025.07.22 15:04:06] HR 0 11:04:06.695 schedule test (XAUUSD,D1) Job: Greet daily -> first run schedule at: [2025.07.26 11:04:06] MN 0 11:04:06.695 schedule test (XAUUSD,D1) Job: Greet weekly -> first run schedule at: [2025.08.19 11:04:06] NI 0 11:04:06.695 schedule test (XAUUSD,D1) Hello there! QL 0 11:04:06.695 schedule test (XAUUSD,D1) Greet every minute run at: 2025.07.22 11:04:06 RN 0 11:04:06.695 schedule test (XAUUSD,D1) Hello there! GF 0 11:04:06.695 schedule test (XAUUSD,D1) Greet hourly run at: 2025.07.22 11:04:06 RL 0 11:04:06.695 schedule test (XAUUSD,D1) Hello there! KJ 0 11:04:06.695 schedule test (XAUUSD,D1) Greet daily run at: 2025.07.22 11:04:06 DR 0 11:04:06.695 schedule test (XAUUSD,D1) Hello there! QK 0 11:04:06.695 schedule test (XAUUSD,D1) Greet weekly run at: 2025.07.22 11:04:06 FL 0 11:04:06.695 schedule test (XAUUSD,D1) CSchedule::run_all -> All 4 Jobs have been executed!
Несмотря на то, что для этих четырех функций было задано время выполнения после четвертого временного интервала, все они были выполнены в одно и то же текущее время.
Управление расписаниями
Нам необходимы различные способы программного доступа к различным расписаниям и их отмены программным методом, поскольку некоторые расписания со временем могут устареть.
Получение всех задач
| Функция | Описание |
|---|---|
void get_jobs(jobs_struct &jobs_struct_array[]) { ArrayResize(jobs_struct_array, m_jobs.Size()); for (uint i=0; i<m_jobs.Size(); i++) jobs_struct_array[i] = m_jobs[i]; } | Эта функция передает аргумент, который по ссылке возвращает массив, содержащий структуру со всеми свойствами для всех задач. |
uint get_jobs() { return m_jobs.Size(); } | Она возвращает количество запланированных задач. |
Отмена запланированных задач
| Функция | Описание |
|---|---|
bool Cancel(const string jobs_name); | Это отменяет запланированную задачу с помощью ее имени. |
bool Cancel(const uint jobs_index); | Она отменяет запланированную задачу, используя ее индексный номер (от 0 до +бесконечности), т.е., если задача была запланирована первой, ее порядковый номер равен 0. |
bool Clear() { return ArrayResize(m_jobs, 0)==-1?false:true; } | Это действие очищает (удаляет) все запланированные задачи из памяти. После вызова этой функции никакие задачи запускаться не будут. |
Пример использования.
#include <schedule.mqh> CSchedule schedule; //+------------------------------------------------------------------+ //| Script program start function | //+------------------------------------------------------------------+ void OnStart() { //--- schedule.every().minute().at(0).dO(Greet, "EveryMin Greetings"); //Job is set at index 0 schedule.every().hour().at(20,10).dO(Greet, "Hourly Greetings"); //Job is set at index 1 schedule.every().day().at(13,20,10).dO(Greet, "Daily Greetings"); //JOb is set at index 2 schedule.every().week().at(MONDAY, 13, 56).dO(Greet, "Weekly Greetings"); //Job is set at index 3 schedule.Cancel(0); //Cancel the job at index 0, the first one Print("Jobs remaining: ",schedule.get_jobs()); schedule.Cancel("Hourly Greetings"); //Cancel the job with this name Print("Jobs remaining: ",schedule.get_jobs()); schedule.Clear(); //Clear all schedules Print("Jobs remaining: ",schedule.get_jobs()); while (true) { schedule.run_pending(); Sleep(1000); } } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ void Greet() { Print("Hello there!"); }
Результаты.
LK 0 16:01:02.017 schedule test (XAUUSD,D1) Job: EveryMin Greetings -> first run schedule at: [2025.07.22 16:02:00] HH 0 16:01:02.017 schedule test (XAUUSD,D1) Job: Hourly Greetings -> first run schedule at: [2025.07.22 17:20:10] EH 0 16:01:02.017 schedule test (XAUUSD,D1) Job: Daily Greetings -> first run schedule at: [2025.07.23 13:20:10] PE 0 16:01:02.017 schedule test (XAUUSD,D1) Job: Weekly Greetings -> first run schedule at: [2025.07.28 13:56:00] OS 0 16:01:02.017 schedule test (XAUUSD,D1) CSchedule::Cancel Job at index [0] removed HS 0 16:01:02.017 schedule test (XAUUSD,D1) Jobs remaining: 3 RG 0 16:01:02.018 schedule test (XAUUSD,D1) CSchedule::Cancel Job 'Hourly Greetings' removed DL 0 16:01:02.018 schedule test (XAUUSD,D1) Jobs remaining: 2 DD 0 16:01:02.018 schedule test (XAUUSD,D1) Jobs remaining: 0
Работа с часовыми поясами
Во всех ранее рассмотренных примерах и реализациях кода в нашей библиотеке мы использовали местное время. Но это очень ограничивает возможности, учитывая, что у нас несколько временных опций, которые разработчики могут использовать в языке программирования MQL5. Например, вам может потребоваться запланировать торговую операцию на определенное время в соответствии со временем на серверах брокера или по времени UTC.
Внутри класса, конструктора CSchedule, добавляем необязательную переменную, которая позволяет разработчикам выбирать тип времени, используемого для всех операций планирования расписаний.
CSchedule::CSchedule(TIME_SOURCE_ENUM time_source=TIME_SOURCE_LOCAL):
m_time_source(time_source)
{
if (MQLInfoInteger(MQL_DEBUG))
printf("Schedule class initialized using %s, current time -> %s",EnumToString(time_source), (string)GetTime(m_time_source));
} Ниже приведен элемент Time source enumeration («Перечисление источников времени») и соответствующая ей функция.
enum TIME_SOURCE_ENUM { TIME_SOURCE_LOCAL, // TimeLocal() TIME_SOURCE_CURRENT, // TimeCurrent() TIME_SOURCE_TRADE_SERVER, // TimeTradeServer() TIME_SOURCE_GMT // TimeGMT() }; datetime GetTime(TIME_SOURCE_ENUM source) { switch(source) { case TIME_SOURCE_LOCAL: return TimeLocal(); case TIME_SOURCE_CURRENT: return TimeCurrent(); case TIME_SOURCE_TRADE_SERVER: return TimeTradeServer(); case TIME_SOURCE_GMT: return TimeGMT(); default: return TimeLocal(); // Fallback } }
Пример использования.
#include <schedule.mqh> CSchedule schedule(TIME_SOURCE_GMT); //Using GMT //+------------------------------------------------------------------+ //| Script program start function | //+------------------------------------------------------------------+ void OnStart() { //--- schedule.every().minute().at(0).dO(Greet, "EveryMin Greetings"); //Job is set at index 0 schedule.every().hour().at(20,10).dO(Greet, "Hourly Greetings"); //Job is set at index 1 schedule.every().day().at(13,20,10).dO(Greet, "Daily Greetings"); //JOb is set at index 2 schedule.every().week().at(MONDAY, 13, 56).dO(Greet, "Weekly Greetings"); //Job is set at index 3 while (true) { schedule.run_pending(); Sleep(1000); } } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ void Greet() { Print("Hello there!"); }
Результаты.
LP 0 16:57:34.961 schedule test (XAUUSD,D1) Schedule class initialized using TIME_SOURCE_GMT, current time -> 2025.07.22 13:57:34 QL 0 16:57:34.964 schedule test (XAUUSD,D1) Job: EveryMin Greetings -> first run schedule at: [2025.07.22 13:58:00] RM 0 16:57:34.964 schedule test (XAUUSD,D1) Job: Hourly Greetings -> first run schedule at: [2025.07.22 14:20:10] RE 0 16:57:34.964 schedule test (XAUUSD,D1) Job: Daily Greetings -> first run schedule at: [2025.07.23 13:20:10] KK 0 16:57:34.964 schedule test (XAUUSD,D1) Job: Weekly Greetings -> first run schedule at: [2025.07.28 13:56:00] HK 0 16:58:00.161 schedule test (XAUUSD,D1) Hello there! KO 0 16:58:00.161 schedule test (XAUUSD,D1) Job: EveryMin Greetings -> Prev run: [2025.07.22 13:58:00] Next run: [2025.07.22 13:59:00]
Применение модуля schedule в ваших торговых приложениях
Мы рассмотрели, как можно использовать этот модуль в простых функциях и на примерах, демонстрирующих своевременный запуск функций. Ниже приведены несколько реальных примеров того, как вы можете использовать эту библиотеку в своих торговых приложениях.
Более эффективная обработка событий в NewBar
Написать функцию, которая бы эффективно отслеживала открытие нового бара, не всегда легко, поскольку у класса CSchedule есть различные способы планирования задач на очень конкретное время, и мы можем использовать его для выполнения определенных действий в начале секунды, минуты, часа, дня и т. д.
Внутри файла Schedule Testing EA.mq5.
#include <Trade\Trade.mqh> #include <Trade\SymbolInfo.mqh> #include <Trade\PositionInfo.mqh> CTrade m_trade; CSymbolInfo m_symbol; CPositionInfo m_position; //--- #include <schedule.mqh> CSchedule schedule(TIME_SOURCE_CURRENT); //Use the current broker's time //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ input int magic_number = 22072025; input uint slippage = 100; input uint stoploss = 500; input uint takeprofit = 700; //+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit() { //--- m_trade.SetExpertMagicNumber(magic_number); m_trade.SetTypeFillingBySymbol(Symbol()); m_trade.SetDeviationInPoints(slippage); if (!m_symbol.Name(Symbol())) { printf("%s -> Failed to select a symbol '%s'. Error = %d", __FUNCTION__,Symbol(),GetLastError()); return INIT_FAILED; } //--- Schedule schedule.every().hour().at(0,0).dO(MainTradingFunction); //every hour when the minute == 0 and second == 0 //--- return(INIT_SUCCEEDED); } //+------------------------------------------------------------------+ //| Expert deinitialization function | //+------------------------------------------------------------------+ void OnDeinit(const int reason) { } //+------------------------------------------------------------------+ //| Expert tick function | //+------------------------------------------------------------------+ void OnTick() { //--- schedule.run_pending(); //Constanly monitor all the scheduled tasks } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ bool PosExists(ENUM_POSITION_TYPE type) { for (int i=PositionsTotal()-1; i>=0; i--) if (m_position.SelectByIndex(i)) if (m_position.Symbol()==Symbol() && m_position.Magic() == magic_number && m_position.PositionType()==type) return (true); return (false); } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ void CloseAllTrades() { for (int i = PositionsTotal() - 1; i >= 0; i--) if (m_position.SelectByIndex(i)) if (m_position.Magic() == magic_number && m_position.Symbol() == Symbol()) m_trade.PositionClose(m_position.Ticket(), slippage); } //+------------------------------------------------------------------+ //| The main function for opening trades and performing other | //| trading related tasks | //+------------------------------------------------------------------+ void MainTradingFunction() { printf("New bar detected!"); //--- if (!m_symbol.RefreshRates()) return; if (!PosExists(POSITION_TYPE_BUY)) m_trade.Buy(m_symbol.LotsMin(), Symbol(), m_symbol.Ask(), m_symbol.Ask()-stoploss*m_symbol.Point(), m_symbol.Ask()+takeprofit*m_symbol.Point() ); if (!PosExists(POSITION_TYPE_SELL)) m_trade.Sell(m_symbol.LotsMin(), Symbol(), m_symbol.Bid(), m_symbol.Bid()+stoploss*m_symbol.Point(), m_symbol.Bid()-takeprofit*m_symbol.Point() ); //--- }
Результаты в тестере стратегий.

Нам необходимо явно задать все параметры внутри функции OnInit аналогично EventSetTimer — с применением функции dO.
Поскольку функция schedule_pending отвечает за постоянный мониторинг расписаний, она должна запускаться внутри функции OnTick в советниках, внутри функции OnCalculate в индикаторах, а также в бесконечном цикле внутри скрипта MQL5.
Отправка ежедневных отчетов о торговых операциях
Отслеживая ситуацию за несколько минут или секунд до закрытия рынка (например, за 5 минут до 00:00), мы можем распечатывать или отправлять пользователям ежедневные отчеты о торговых операциях.
int OnInit() { //... other lines of code //--- Schedule schedule.every().hour().at(0,0).dO(MainTradingFunction); schedule.every().day().at(23, 55).dO(SendDailyTradingReport); //every day 5 minutes before market closing //--- return(INIT_SUCCEEDED); } //+------------------------------------------------------------------+ //| Expert deinitialization function | //+------------------------------------------------------------------+ void OnDeinit(const int reason) { } //+------------------------------------------------------------------+ //| Expert tick function | //+------------------------------------------------------------------+ void OnTick() { //--- schedule.run_pending(); } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ void SendDailyTradingReport() { string sdate = TimeToString (TimeCurrent(), TIME_DATE); datetime start = StringToTime(sdate); if (!HistorySelect(start, TimeCurrent())) { printf("%s, line %d failed to obtain closed deals from history error =%d",__FUNCTION__,__LINE__,GetLastError()); return; } Comment(""); //--- double pl = 0.0; int trades_count=0; string report_body = ""; for(int i = 0; i < HistoryDealsTotal(); i++) { if (m_deal.SelectByIndex(i)) if (m_deal.Entry() == DEAL_ENTRY_OUT && m_deal.Magic() == magic_number) { pl += m_deal.Profit(); trades_count++; report_body += StringFormat("Trade[%d] -> | ticket: %I64u | type: %s | entry: %.5f | volume: %.3f | commision: %.3f\n", trades_count, m_deal.Ticket(), EnumToString(m_deal.DealType()), m_deal.Entry(), m_deal.Volume(), m_deal.Commission() ); } } string report_header = StringFormat("<<< Daily Trading Report >>> \r\n\r\nAC Balance: %.3f\r\nAC Equity: %.3f\r\nPL: %.3f\r\nTotal Trades: %d \r\n\r\n", m_account.Balance(), m_account.Equity(), pl, trades_count ); //--- You might choose to send the reports instead of printing Comment(report_header+report_body); Print(report_header+report_body); }
Результаты в тестере стратегий.
CS 0 11:33:47.902 Schedule testing EA (EURUSD,H1) 2025.03.13 23:55:00 <<< Daily Trading Report >>> CS 0 11:33:47.902 Schedule testing EA (EURUSD,H1) 2025.03.13 23:55:00 CS 0 11:33:47.902 Schedule testing EA (EURUSD,H1) 2025.03.13 23:55:00 AC Balance: 2983.830 CS 0 11:33:47.902 Schedule testing EA (EURUSD,H1) 2025.03.13 23:55:00 AC Equity: 2983.200 CS 0 11:33:47.902 Schedule testing EA (EURUSD,H1) 2025.03.13 23:55:00 PL: -2.960 CS 0 11:33:47.902 Schedule testing EA (EURUSD,H1) 2025.03.13 23:55:00 Total Trades: 3 CS 0 11:33:47.902 Schedule testing EA (EURUSD,H1) 2025.03.13 23:55:00 CS 0 11:33:47.902 Schedule testing EA (EURUSD,H1) 2025.03.13 23:55:00 Trade[1] -> | ticket: 166 | type: DEAL_TYPE_SELL | entry: 1.00000 | volume: 0.010 | commision: 0.000 CS 0 11:33:47.902 Schedule testing EA (EURUSD,H1) 2025.03.13 23:55:00 Trade[2] -> | ticket: 168 | type: DEAL_TYPE_BUY | entry: 1.00000 | volume: 0.010 | commision: 0.000 CS 0 11:33:47.902 Schedule testing EA (EURUSD,H1) 2025.03.13 23:55:00 Trade[3] -> | ticket: 169 | type: DEAL_TYPE_SELL | entry: 1.00000 | volume: 0.010 | commision: 0.000 CS 0 11:33:47.902 Schedule testing EA (EURUSD,H1) 2025.03.13 23:55:00
Тот же принцип можно распространить и на составление еженедельных отчетов каждую пятницу, непосредственно перед закрытием рынка, а также на подготовку ежемесячных отчетов.
Модуль schedule vs событие OnTimer
Хотя прямое сравнение этих двух методов было бы необоснованным, поскольку оба, каждый по-своему, работают хорошо, полезно знать их различия и понимать, когда следует использовать тот или иной метод, а когда нет.
| OnTimer | Расписание |
|---|---|
| Поскольку эта встроенная функциональность позволяет запускать только одно событие таймера (одно расписание), она полезна, когда вам нужно запланировать выполнение одной задачи. | Это позволяет выполнять несколько задач одновременно и устанавливать разные интервалы для каждой задачи. Такая пользовательская библиотека пригодится, когда у вас будет несколько задач, которые необходимо выполнить одновременно. |
| Это быстро и эффективно. | Она работает не так быстро, как встроенный OnTimer; ее эффективность еще предстоит изучить. |
| Ее использование ограничивается только советниками и индикаторами. | Она работает во всех программах MQL5: советниках, индикаторах и скриптах. |
Работает круглосуточно (и всегда надежно). | Она основана на торговых функциях (OnTick и OnCalculate), которые срабатывают только при открытии рынка. Если функция мониторинга с именем run_pending не используется внутри бесконечного цикла в скрипте, она работает менее чем 24 (часа) / 5 (дней недели) — в зависимости от рынка. |
Для устранения проблемы с надежностью, возникающей при использовании этой библиотеки, как описано в последней строке сравнительной таблицы, необходимо запустить класс CSchedule внутри функции OnTimer.
#include <schedule.mqh> CSchedule schedule(TIME_SOURCE_CURRENT); //Use the current broker's time //+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit() { //... other functions & lines of code //--- Schedule schedule.every().hour().at(0,0).dO(MainTradingFunction); schedule.every().day().at(23, 55).dO(SendDailyTradingReport); //every day 5 minutes before market closing //--- Ontimer EventSetTimer(1); //Run the Ontimer function after every 1 second (pretty much always) return(INIT_SUCCEEDED); } //+------------------------------------------------------------------+ //| Expert deinitialization function | //+------------------------------------------------------------------+ void OnDeinit(const int reason) { EventKillTimer(); //Delete the timer } //+------------------------------------------------------------------+ //| Expert tick function | //+------------------------------------------------------------------+ void OnTick() { //--- //schedule.run_pending(); //❎ } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ void OnTimer(void) { schedule.run_pending(); //✅ }
Заключение
Для алгоритмических трейдеров крайне важно умение устанавливать конкретные расписания и запускать задачи в точно заданное время. Многие торговые стратегии зависят не только от точного времени суток, но и от множества действий, которые мы часто хотим автоматизировать для многократного выполнения, таких как отправка ежедневных торговых отчетов, ежемесячных обновлений и многого другого.
Реализованный класс предоставляет простой способ установки повторяющихся событий в ваших программах на языке MQL5 аналогично тому, как это предлагается в модуле schedule языка Python.
Хотя функция OnTimer работает неплохо, ей не хватает некоторых важных способов настройки удобного для пользователя расписания. Поэтому смело используйте эту библиотеку в тех случаях, когда возможностей OnTimer недостаточно.
Всем добра.
Таблица вложений
| Имя файла | Описание и использование |
|---|---|
| Experts\Schedule testing EA.mq5 | Советник для планирования торговых операций. |
| Include\schedule.mqh | Содержит класс CSchedule, полезный для планирования запуска функций в определенное время и через заданные интервалы. |
| Scripts\schedule test.mq5 | Скрипт для тестирования класса CSchedule. |
Перевод с английского произведен MetaQuotes Ltd.
Оригинальная статья: https://www.mql5.com/en/articles/18913
Предупреждение: все права на данные материалы принадлежат MetaQuotes Ltd. Полная или частичная перепечатка запрещена.
Данная статья написана пользователем сайта и отражает его личную точку зрения. Компания MetaQuotes Ltd не несет ответственности за достоверность представленной информации, а также за возможные последствия использования описанных решений, стратегий или рекомендаций.
Машинное обучение и Data Science (Часть 42): Прогнозирование фондовых рынков с использованием N-BEATS в Python
Неопределенность как модель (Часть 3): Математическая статистика — как извлекать знания из данных
Нейросети в трейдинге: Адаптивное масштабирование представлений (Окончание)
Роевой оптимизатор с иерархией суброев — Flock by Leader
- Бесплатные приложения для трейдинга
- 8 000+ сигналов для копирования
- Экономические новости для анализа финансовых рынков
Вы принимаете политику сайта и условия использования