Implementación de módulos prácticos de otros lenguajes en MQL5 (Parte 03): Módulo schedule de Python, una versión mejorada del evento OnTimer
Contenido
- Introducción
- ¿Qué es el módulo Schedule que ofrece Python?
- La clase Schedule en MQL5
- Ejecutar funciones en un momento específico
- Pasar argumentos a una tarea
- Ejecutar una tarea hasta un tiempo determinado
- Ejecutar todas las tareas independientemente de su programación
- Gestión de horarios
- Cómo gestionar las zonas horarias
- Aplicaciones del módulo Schedule en tus aplicaciones de trading
- Schedule vs. OnTimer
- Conclusión
Introducción
La programación tiene como objetivo facilitarnos la vida, permitiéndonos automatizar muchas de las tareas cruciales y, a veces, aburridas o repetitivas que solemos querer que realicen los ordenadores sin ninguna interacción humana. Un buen ejemplo es la función de guardado automático que vemos en muchos editores de texto. En lugar de tener que preocuparse por guardar los documentos cada vez que se escribe una palabra nueva, los editores de texto se encargan del proceso de guardado automáticamente para que uno pueda concentrarse en escribir sin preocuparse por perder el trabajo cuando ocurre algo inesperado.
En el ámbito del trading, esto no es diferente, ya que numerosas actividades y tareas repetitivas facilitan las operaciones y queremos automatizarlas mediante unas pocas líneas de código.

En el lenguaje de programación MQL5, contamos con la famosa función OnTimer, que permite ejecutar determinadas funciones y líneas de código tras un intervalo de tiempo específico establecido por el programador.
A continuación se muestra un ejemplo sencillo: ejecutar la función OnTimer cada 10 segundos.
//+------------------------------------------------------------------+ //| 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 }
Resultados.
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
Esta función cumple su función, pero sigue siendo bastante básica y no lo suficientemente flexible como para permitir que varios horarios diferentes se ejecuten simultáneamente en el mismo programa.
Una vez que configure el controlador de eventos OnTimer en su Asesor Experto o Indicador, solo podrá depender de esa única programación del temporizador. Esto resulta muy limitante porque a menudo tenemos diferentes tareas que queremos que se ejecuten en diferentes momentos (intervalos) en nuestros programas.
Por ejemplo, imprimir o enviar informes de operaciones diarios, semanales o mensuales a los usuarios.
En el lenguaje de programación Python, existe un módulo similar a la función OnTimer, pero es mucho mejor para ejecutar funciones de manera oportuna. En este artículo, lo analizaremos e implementaremos un módulo similar en el lenguaje de programación MQL5.
¿Qué es el módulo Schedule que ofrece Python?
Conocido como: «Python job scheduling for humans» (Programación de tareas en Python para personas)
Este es un módulo de Python fácil de usar que ayuda a programar tareas específicas para que se ejecuten después de una hora determinada del día, de la semana, etc. Este módulo es sencillo de usar y ligero, lo que lo convierte en un módulo necesario que todo desarrollador de Python debe conocer.
A diferencia de la función OnTimer que tenemos en MQL5, el módulo schedule no solo nos permite programar tareas para que se ejecuten tras un intervalo de tiempo específico, sino que también nos ofrece la flexibilidad necesaria para definir con mayor precisión cuándo y cómo debe ejecutarse una tarea (función) concreta.
A continuación se enumeran algunas de las funciones que ofrece este módulo.
import schedule
| Función | Descripción |
|---|---|
schedule.every(10).minutes.do(job) | Al igual que en el evento OnTimer, cada 10 minutos se ejecutará la función denominada job. |
schedule.every().hour.do(job) | La función denominada job se ejecutará cada hora desde el inicio del script. |
schedule.every().day.at("10:30").do(job) | La función denominada job se ejecutará todos los días a las 10:30, hora local, en formato de 24 horas. |
schedule.every().monday.do(job) | La función denominada job se ejecutará todos los lunes a la misma hora exacta a la que se ejecutó inicialmente el script. |
schedule.every().wednesday.at("13:15").do(job) | La función denominada job se ejecutará todos los miércoles a las 13:15. |
schedule.every().day.at("12:42", "Europe/Amsterdam").do(job) | La función denominada job se ejecutará todos los días a las 12:42, según la zona horaria Europa/Amsterdam. |
schedule.every().minute.at(":17").do(job) | La función denominada job se ejecutará cada minuto, en el segundo 17. |
Estas son solo algunas de las funciones, aunque fundamentales, que ofrece este módulo. Implementemos una clase que nos resulte familiar en MQL5.
La clase Schedule en MQL5
El módulo schedule en Python está diseñado para utilizar funciones separadas para cada tarea que se desee programar en un intervalo específico. La función denominada do es el punto final de toda la cadena de funciones de la clase «schedule».
schedule.every(10).minutes.do Para conseguir una sintaxis similar en MQL5, debemos hacer que algunas funciones de la clase CSchedule devuelvan una instancia de toda la clase, excepto la función denominada dO, que es el punto final.
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); }
Esta sintaxis nos permite disponer de una interfaz fluida similar a la que ofrece el módulo schedule de Python.
#include <schedule.mqh> CSchedule schedule; //+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit() { schedule.every().minutes().dO(runthis); }
La función denominada every es fundamental para establecer un intervalo específico. Por ejemplo:
schedule.every(10).minutes()
Esto significa que, cada 10 minutos, debe activarse una función específica recibida por la función denominada dO.
En esencia, esta función toma un period dado y asigna esa variable a una variable llamada m_period — almacenada dentro de la clase.
CSchedule* CSchedule::every(int period = 1) { m_period = period; return GetPointer(this); }
Las funciones: segundos, minutos, horas, etc., asignan la variable de intervalo de tiempo según todas las opciones de intervalos de tiempo disponibles que proporciona el enumerador denominado 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);
} Antes de abordar la función denominada dO, que es el punto final de todas las funciones de programación. Veamos cómo se gestiona y se almacena cada tarea (función) dentro del archivo denominado 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; }
Dado que la clase CSchedule tiene funciones que hacen referencia a sí misma, gestionar cada objeto de tarea dentro de esta clase resulta complicado o confuso, y propicio a errores.
Disponer de una matriz global denominada m_jobs ofrece una forma universal de almacenar y gestionar todos los trabajos utilizados dentro de la clase. Lo comentaremos dentro de un momento.
La función denominada dO recibe una función que se ejecuta repetidamente según un «schedule» recibido. Calcula cuándo se ejecutó por última vez una función y cuándo se prevé que se ejecute la próxima vez.
Una función recibida se almacena en la estructura denominada jobs_struct, junto con otras propiedades de los trabajos, como la última vez que se ejecutó y la próxima vez que se ejecutará.
A continuación, todos estos valores se almacenan en un array denominado m_jobs, que es un array de tipo 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 }
Una vez que una tarea se ha almacenado en la matriz correspondiente, necesitamos una función universal que la supervise constantemente y ejecute su función cuando llegue el momento programado.
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)); } } }
Programemos nuestra primera tarea utilizando esta clase.
#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()); } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+
Queremos llamar a la función runthis cada 10 segundos. El bucle «while» infinito se utiliza únicamente para que el script siga ejecutándose hasta que se detenga.
A continuación se muestra la salida registrada cuando se ejecutó el script en modo de depuración.
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
Podemos tener varios horarios que utilicen la misma clase.
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"); }
Resultados.
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
Ejecutar tareas (funciones) a una hora concreta
A menudo queremos ejecutar nuestras funciones en un momento muy concreto. Por ejemplo, ejecutar una función encargada de abrir una operación a una hora determinada según una sesión de trading; por ejemplo, abrir una operación a las 19:00, hora local.
En la clase CSchedule, todas las funciones denominadas at se encargan de realizar esta tarea cuando se les proporciona «una hora válida» concreta.
Por ejemplo.
schedule.every().day().at(19, 10).dO(job);
Esto programa la ejecución de la función denominada job todos los días a las 19:10 horas.
Implementar esta función en MQL5 resulta complicado, ya que necesitamos que se devuelvan clases distintas para cada función asociada al tiempo, es decir, segundo, minuto, hora, día y semana.
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();
Dentro de cada clase «Builder» (todas las clases que terminan con la palabra Builder), hay una función llamada at, que se encarga de establecer un intervalo de tiempo específico.
También disponemos de la función denominada dO,, que hereda una función homónima de la clase CSchedule.
Por ejemplo, la clase 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); } };
Todas las funciones denominadas at reciben una hora concreta como argumento, calculan cuánto falta hasta la primera ejecución y asignan ese valor en segundos a la variable m_fixed_time.
Dentro de la función denominada dO, introducimos una condición para comprobar si el valor de tiempo recibido es un valor de tiempo fijo (p. ej., 19:00) o un número determinado de segundos, minutos, etc., hasta la próxima ejecución de la función, ya que ambas condiciones requieren un enfoque ligeramente diferente.
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 }
A continuación se explica cómo configurar varias programaciones para que se ejecuten de forma periódica a una hora determinada.
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()); }
Cómo pasar argumentos a una tarea (Job)
Como se ha podido observar en los registros de salida del ejemplo anterior, resulta difícil identificar y realizar un seguimiento del progreso de un trabajo, sobre todo cuando hay varias funciones programadas ejecutándose al mismo tiempo. Para solucionar esto, necesitamos una variable opcional llamada jobs_name en la función llamada dO,; esta variable sirve para etiquetar todas las tareas programadas.
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 }
Ahora podemos hacer un seguimiento del avance de cada trabajo de forma más eficaz.
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!"); }
Resultados.
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]
Ejecutar una tarea hasta cierta hora
A veces tenemos tareas programadas que no queremos que se ejecuten indefinidamente. En este caso, establecer una fecha límite para estos trabajos sería de gran ayuda.
Vamos a presentar la función denominada until. Una función similar a la que ofrece el módulo «schedule» de Python.
CSchedule* CSchedule::until(datetime expiry_date) { m_expiry_date = expiry_date; return GetPointer(this); }
Dentro de la función denominada dO, tomamos la fecha de vencimiento de nuestra clase (obtenida de la función denominada «until») y la asignamos a la estructura del trabajo.
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 }
Antes de ejecutar un trabajo dentro de la función denominada run_pending, debemos comprobar que no haya caducado.
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 }
Por último, ejecutamos una tarea y fijamos la fecha de caducidad en 5 minutos a partir de la hora actual.
#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!"); }
Resultados.
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
Ejecutar todas las tareas independientemente de su programación
A veces puede que tengas que ejecutar todas las funciones de forma inmediata, independientemente de sus horarios. Normalmente, para realizar pruebas, y a veces simplemente queremos forzar que todas las operaciones programadas se ejecuten a la vez, por ejemplo, durante el inicio del programa.
En estas situaciones, resulta muy útil la función denominada 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()); }
Ejemplo de uso.
#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!"); }
Resultados.
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!
A pesar de haber configurado estas cuatro funciones para que se ejecutaran después de su cuarto intervalo de tiempo, todas se ejecutaron al mismo tiempo.
Gestión de horarios
Necesitamos diferentes maneras de acceder y cancelar diferentes programaciones mediante programación, ya que algunas programaciones podrían quedar obsoletas con el paso del tiempo.
Obtener todos los "jobs"
| Función | Descripción |
|---|---|
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]; } | Esta función proporciona un argumento que devuelve, por referencia, un array que contiene una estructura con todas las propiedades de todas las tareas. |
uint get_jobs() { return m_jobs.Size(); } | Devuelve el número de Jobs programados. |
Cancelación de Jobs programados
| Función | Descripción |
|---|---|
bool Cancel(const string jobs_name); | Esto cancela una tarea programada utilizando su nombre. |
bool Cancel(const uint jobs_index); | Cancela una tarea programada utilizando su número de índice (de 0 a +infinito), es decir, si el trabajo fue el primero en programarse, su número de índice es 0. |
bool Clear() { return ArrayResize(m_jobs, 0)==-1?false:true; } | Esto borra (elimina) todas las tareas programadas de la memoria. No se ejecutará ningún trabajo ni tarea tras la llamada a esta función. |
Ejemplo de uso.
#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!"); }
Resultados.
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
Cómo gestionar las zonas horarias
En todos los ejemplos e implementaciones de código que hemos comentado anteriormente en nuestra biblioteca, hemos utilizado la hora local. Sin embargo, esto resulta muy limitante si tenemos en cuenta que disponemos de múltiples opciones de tiempo que los desarrolladores pueden utilizar en el lenguaje de programación MQL5. Por ejemplo, es posible que desee programar una operación comercial para una hora determinada según la hora del servidor del bróker o la hora UTC.
Dentro del constructor de la clase CSchedule, añadimos una variable opcional que permite a los desarrolladores seleccionar el tipo de tiempo que se utilizará para todas las operaciones de programación.
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));
} A continuación se muestra la «Enumeración de fuentes de tiempo» y su función correspondiente.
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 } }
Ejemplo de uso.
#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!"); }
Resultados.
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]
Aplicaciones del módulo «Schedule» en tus aplicaciones de trading
Hemos visto cómo se puede utilizar este módulo en funciones sencillas y en ejemplos que muestran la ejecución programada de funciones. A continuación se muestran algunos ejemplos prácticos sobre cómo puedes utilizar esta biblioteca en tus aplicaciones de trading.
Una detección más eficaz del evento NewBar
No siempre es fácil escribir una función que detecte eficazmente la apertura de un nuevo bar, ya que la clase CSchedule tiene diferentes maneras de programar una tarea en un momento muy específico, podemos usarla para realizar ciertas acciones al comienzo de un segundo, minuto, hora, día, etc.
Dentro del archivo 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() ); //--- }
Resultados en el Probador de estrategias.

Debemos configurar explícitamente las programaciones dentro de la función OnInit, de forma similar a EventSetTimer, utilizando la función dO .
Dado que la función schedule_pending se encarga de supervisar constantemente las programaciones, debe ejecutarse dentro de la función OnTick en los Expert Advisors, dentro de la función OnCalculate en los indicadores y dentro de un bucle infinito en un script MQL5.
Envío de informes diarios de operaciones
Al realizar un seguimiento durante unos minutos o segundos antes del cierre del mercado (por ejemplo, 5 minutos antes de las 00:00 horas), podemos imprimir o enviar informes diarios de operaciones a los usuarios.
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); }
Resultados en el Probador de estrategias.
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
La misma idea puede extenderse a la generación de informes semanales todos los viernes, justo antes del cierre del mercado, y a la generación de informes mensuales.
Schedule vs. OnTimer
Si bien sería injusto comparar directamente estos dos métodos, ya que ambos funcionan bien a su manera, es bueno conocer sus diferencias y cuándo usar uno u otro y cuándo no.
| OnTimer | Schedule |
|---|---|
| Dado que permite un único evento de temporizador (una sola programación), esta funcionalidad integrada resulta útil cuando se desea programar una sola tarea. | Permite realizar múltiples tareas y establecer diferentes intervalos para cada una. Esta biblioteca personalizada resulta útil cuando se tienen varios horarios que se desean ejecutar simultáneamente. |
| Es rápido y eficaz. | No es tan rápido como el OnTimer integrado; su eficacia aún está por comprobarse. |
| Está limitado únicamente a Asesores Expertos e Indicadores. | Funciona en todos los programas MQL5: Asesores Expertos (EA), Indicadores y Scripts. |
Funciona las 24 horas del día, los 7 días de la semana (siempre fiable). | Se basa en las funciones de negociación (OnTick y OnCalculate), que solo se activan cuando el mercado está abierto. A menos que se utilice dentro de un bucle infinito en un script, la función de monitorización denominada run_pending funciona durante menos de 24 (horas)/5 (días de la semana), dependiendo del mercado. |
Para solucionar el problema de fiabilidad que surge al utilizar esta biblioteca, tal y como se describe en la última fila de la tabla comparativa, debes ejecutar la clase CSchedule dentro de la función 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(); //✅ }
Conclusión
Para los operadores algorítmicos, es fundamental poder establecer horarios específicos y ejecutar tareas a horas concretas. No solo muchas estrategias de negociación dependen de momentos concretos del día, sino que también hay numerosas actividades que a menudo queremos automatizar para que se ejecuten de forma repetida, como el envío de informes diarios de operaciones, actualizaciones mensuales y otras tareas similares.
La clase implementada ofrece una forma sencilla de configurar eventos recurrentes en tus programas MQL5, de forma similar a la que ofrece el módulo de programación de Python.
Aunque la función OnTimer es bastante buena, carece de algunas de las opciones fundamentales para configurar horarios fáciles de definir y entender por las personas. Así pues, no dudes en utilizar esta biblioteca en aquellos casos en los que OnTimer no sea suficiente.
Un saludo.
Tabla de archivos adjuntos
| Nombre del archivo | Descripción y uso |
|---|---|
| Experts\Schedule testing EA.mq5 | Un Asesor Experto (EA) para programar operaciones de trading. |
| Include\schedule.mqh | Contiene la clase CSchedule, útil para programar la ejecución de funciones en momentos e intervalos específicos. |
| Scripts\schedule test.mq5 | Un script para probar la clase CSchedule. |
Traducción del inglés realizada por MetaQuotes Ltd.
Artículo original: https://www.mql5.com/en/articles/18913
Advertencia: todos los derechos de estos materiales pertenecen a MetaQuotes Ltd. Queda totalmente prohibido el copiado total o parcial.
Este artículo ha sido escrito por un usuario del sitio web y refleja su punto de vista personal. MetaQuotes Ltd. no se responsabiliza de la exactitud de la información ofrecida, ni de las posibles consecuencias del uso de las soluciones, estrategias o recomendaciones descritas.
Utilizando redes neuronales en MetaTrader
Desarrollo de un kit de herramientas para el análisis de la acción del precio (Parte 33): Herramienta de teoría del rango de velas
Particularidades del trabajo con números del tipo double en MQL4
Análisis de la dinámica horaria de los símbolos de trading y de sus spreads en MetaTrader 5
- Aplicaciones de trading gratuitas
- 8 000+ señales para copiar
- Noticias económicas para analizar los mercados financieros
Usted acepta la política del sitio web y las condiciones de uso