Implementierung von praktischen Modulen aus anderen Sprachen in MQL5 (Teil 03): Zeitplan-Modul von Python, das OnTimer-Ereignis auf Steroiden
Inhalt
- Einführung
- Was bietet das Schedule-Modul in Python?
- Die Klasse Schedule in MQL5
- Funktionen zu einer bestimmten Zeit ausführen
- Übergabe von Argumenten an eine Aufgabe
- Ausführung einer Aufgabe bis zu einer bestimmten Zeit
- Ausführung aller Aufträge unabhängig von ihren Zeitplänen
- Zeitpläne verwalten
- Umgang mit Zeitzonen
- Anwendungen des Zeitplanmoduls in Ihren Handelsanwendungen
- Zeitplan vs. OnTimer
- Schlussfolgerung
Einführung
Das Programmieren soll unser Leben erleichtern, indem es uns ermöglicht, viele der wichtigen und manchmal langweiligen/wiederkehrenden Aufgaben zu automatisieren, die wir oft von Computern ohne menschliche Interaktion erledigen lassen wollen. Ein gutes Beispiel ist die automatische Speicherfunktion, die in vielen Texteditoren zu finden ist. Anstatt sich jedes Mal, wenn Sie ein neues Wort schreiben, um das Speichern von Dokumenten kümmern zu müssen, erledigen Texteditoren den Speichervorgang automatisch, sodass Sie sich auf das Schreiben konzentrieren können und sich keine Sorgen machen müssen, dass Ihre Arbeit verloren geht, wenn etwas Unerwartetes passiert.
Das ist im Handelsbereich nicht anders, wo zahlreiche sich wiederholende Tätigkeiten und Aufgaben beim Handel helfen, die wir mit einigen Zeilen Code automatisieren wollen.

In der Programmiersprache MQL5 gibt es die weit bekannte Funktion OnTimer, die dabei hilft, bestimmte Funktionen und Codezeilen nach einem bestimmten, vom Programmierer festgelegten Zeitintervall auszuführen.
Nachfolgend finden Sie ein einfaches Beispiel, bei dem die Funktion OnTimer alle 10 Sekunden ausgeführt wird.
//+------------------------------------------------------------------+ //| 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 }
Ausgabe:
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
Diese Funktion ist gut, aber sie ist grob und nicht flexibel genug, um mehrere/unterschiedliche Zeitpläne gleichzeitig im selben Programm zu ermöglichen.
Sobald Sie die Ereignisbehandlung von OnTimer in Ihrem Expert Advisor oder Indikator eingestellt haben, brauchen Sie sich nur noch auf diesen einen „Zeitplan“ verlassen. Dies ist sehr einschränkend, da wir in unseren Programmen oft verschiedene Aufgaben haben, die zu unterschiedlichen Zeiten (Intervallen) ausgeführt werden sollen.
Zum Beispiel das Drucken oder Versenden von täglichen, wöchentlichen oder monatlichen Handelsberichten an die Nutzer.
In der Programmiersprache Python gibt es ein Modul, das der Funktion OnTimer ähnelt, das aber viel besser in der Lage ist, Funktionen zeitgerecht auszuführen. In diesem Artikel werden wir es diskutieren und ein ähnliches Modul in der Programmiersprache MQL5 implementieren.
Was bietet das Modul Schedule von Python?
Bezeichnet als – Auftragsplanung in Python für Menschen
Dies ist ein menschenfreundliches Python-Modul, das bei der Planung bestimmter Aufgaben hilft, die nach einer bestimmten Zeit des Tages, der Woche usw. ausgeführt werden sollen. Dieses Modul ist einfach zu nutzen und leichtgewichtig, was es zu einem notwendigen Modul macht, das jeder Python-Entwickler kennen sollte.
Im Gegensatz zu OnTimer von MQL5 können wir mit dem Schedule-Modul nicht nur Aufgaben so planen, dass sie nach einem bestimmten Zeitintervall ausgeführt werden, sondern es gibt uns auch die Flexibilität, genauer festzulegen, wann und wie eine bestimmte Aufgabe (Funktion) ausgeführt werden soll.
Im Folgenden finden Sie einige der Funktionen, die dieses Modul bietet.
import schedule | Funktion | Beschreibung |
|---|---|
schedule.every(10).minutes.do(job) | Ähnlich wie beim OnTimer-Ereignis wird nach jeweils 10 Minuten die Funktion namens job ausgeführt. |
schedule.every().hour.do(job) | Die Funktion namens job wird ab dem Start des Skripts jede Stunde ausgeführt. |
schedule.every().day.at("10:30").do(job) | Die Funktion mit dem Namen job wird jeden Tag zu einer bestimmten Ortszeit um 10:30 Uhr in 24-Stunden-Zeit ausgeführt. |
schedule.every().monday.do(job) | Die Funktion mit dem Namen job wird jeden Montag genau zu dem Zeitpunkt aufgerufen, zu dem das Skript ursprünglich ausgeführt wurde. |
schedule.every().wednesday.at("13:15").do(job) | Die Funktion namens job wird jeden Mittwoch um 13:15 Uhr aufgerufen. |
schedule.every().day.at("12:42", "Europe/Amsterdam").do(job) | Die Funktion mit dem Namen job wird jeden Tag um 12:42 Uhr europäischer/Amsterdamer Zeit aufgerufen. |
schedule.every().minute.at(":17").do(job) | Die Funktion mit dem Namen job wird jede Minute zur 17. Sekunde aufgerufen. |
Dies sind nur einige wenige, aber entscheidende Funktionen, die dieses Modul bietet. Lassen Sie uns eine bekannte Klasse in MQL5 implementieren.
Die Schedule-Klasse in MQL5
Das Schedule-Modul in Python ist so aufgebaut, dass es für jede Aufgabe, die Sie in einem bestimmten Intervall planen wollen, eine eigene Funktion bereitstellt. Die Funktion do ist der Endpunkt der gesamten Funktionsverkettung der Klasse schedule.
schedule.every(10).minutes.do Um eine ähnliche Syntax in MQL5 zu erreichen, müssen wir einige Funktionen in der Klasse CSchedule dazu bringen, eine Instanz der gesamten Klasse zurückzugeben, mit Ausnahme der Funktion dO, die der Endpunkt ist.
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); }
Diese Syntax ermöglicht uns eine ähnlich flüssige Schnittstelle wie die des Schedule-Moduls von Python.
#include <schedule.mqh> CSchedule schedule; //+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit() { schedule.every().minutes().dO(runthis); }
Die Funktion mit dem Namen every ist entscheidend für die Festlegung eines Intervalls für einen bestimmten Zeitrahmen. Zum Beispiel:
schedule.every(10).minutes()
Das bedeutet, dass alle 10 Minuten eine bestimmte Funktion, die von der Funktion mit dem Namen dO empfangen wird, ausgelöst werden soll.
Im Kern nimmt diese Funktion eine gegebene Periode und ordnet diese Variable einer Variablen namens m_period zu, die innerhalb der Klasse gespeichert ist.
CSchedule* CSchedule::every(int period = 1) { m_period = period; return GetPointer(this); }
Die Funktionen: seconds, minutes, hours usw. ordnen die Zeitrahmenvariable allen verfügbaren Zeitintervalloptionen zu, die durch den Enumerator time_intervals_enum angegeben sind.
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);
} Bevor wir die Funktion namens dO verstehen, die der Endpunkt aller Planungsfunktionen ist. Wir wollen verstehen, wie jeder Auftrag (Funktion) gehandhabt und in der Datei schedule.mqh gespeichert wird.
//+------------------------------------------------------------------+ //| 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; }
Da die Klasse CSchedule über Funktionen verfügt, die auf sich selbst verweisen, wird die Behandlung der einzelnen Auftragsobjekte innerhalb dieser Klasse kompliziert/verwirrend und fehleranfällig.
Ein globales Array mit dem Namen m_jobs bietet eine universelle Möglichkeit, alle innerhalb der Klasse verwendeten Jobs zu speichern und zu verwalten. Wir werden gleich darüber sprechen.
Die Funktion mit dem Namen dO empfängt eine Funktion, die nach einem empfangenen „Zeitplan“ wiederholt ausgeführt werden soll. Es wird berechnet, wann eine Funktion zuletzt ausgeführt wurde und wann sie voraussichtlich das nächste Mal ausgeführt wird.
Eine empfangene Funktion wird in der Struktur jobs_struct zusammen mit den Eigenschaften anderer Aufträge gespeichert, z. B. wann der Auftrag das letzte Mal ausgeführt wurde und wann er das nächste Mal ausgeführt werden soll.
Alle diese Werte werden dann in einem Array namens m_jobs gespeichert, das ein Array vom Typ jobs_struct ist.
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 }
Nachdem ein Auftrag in einem entsprechenden Array gespeichert wurde, benötigen wir eine universelle Funktion, um ihn ständig zu überwachen und seine Funktion auszuführen, wenn der geplante Zeitpunkt erreicht ist.
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)); } } }
Lassen Sie uns unseren ersten Auftrag mit dieser Klasse planen.
#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()); } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+
Wir wollen die Funktion runthis nach jeweils 10 Sekunden aufrufen. Eine unendliche while-Schleife dient nur dazu, das Skript so lange laufen zu lassen, bis es angehalten wird.
Nachfolgend sehen Sie die protokollierte Ausgabe, wenn das Skript im Debug-Modus ausgeführt wurde.
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
Wir können mehrere Zeitpläne haben, die dieselbe Klasse verwenden.
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"); }
Ausgabe:
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
Aufträge (Funktionen) zu einer bestimmten Zeit ausführen
Oft wollen wir unsere Funktionen zu einer ganz bestimmten Zeit ausführen. Zum Beispiel die Ausführung einer Funktion, die für die Eröffnung eines Geschäfts zu einem bestimmten Zeitpunkt entsprechend einer Handelssitzung verantwortlich ist, z.B. die Eröffnung eines Geschäfts um 19:00 Uhr Ortszeit.
In der Klasse CSchedule sind alle Funktionen mit dem Namen at dafür verantwortlich, dies zu tun, wenn eine bestimmte „legitime Zeit“ angegeben wird.
Zum Beispiel.
schedule.every().day().at(19, 10).dO(job);
Damit wird festgelegt, dass die Funktion namens Job jeden Tag um 19:10 Uhr ausgeführt wird.
Die Implementierung dieser Funktion in MQL5 ist knifflig, da wir für jede „zeitliche“ Funktion, d.h. Sekunde, Minute, Stunde, Tag, Woche, separate Klassen benötigen, die zurückgegeben werden.
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();
In jeder „Builder“-Klasse (alle Klassen, die mit dem Wort Builder enden) gibt es eine Funktion namens at, die für die Einstellung eines bestimmten Zeitintervalls verantwortlich ist.
Wir haben auch eine Funktion namens dO, die eine namensgleiche Funktion von der Klasse CSchedule erbt.
Zum Beispiel die Klasse 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); } };
Alle Funktionen mit dem Namen at nehmen eine bestimmte Zeit, die ihren Argumenten übergeben wird, die Zeit vor dem ersten Funktionsdurchlauf, und weisen den Zeitwert in Sekunden einer Variablen namens m_fixed_time zu.
Innerhalb der Funktion mit dem Namen dO wird eine Bedingung eingeführt, um zu prüfen, ob der empfangene Zeitwert ein fester Zeitwert ist (z. B.. 19:00) oder eine geplante Anzahl von Sekunden, Minuten usw. für den nächsten Funktionslauf, da beide Bedingungen eine etwas andere Vorgehensweise erfordern.
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 }
Im Folgenden wird beschrieben, wie Sie mehrere Zeitpläne so einstellen, dass sie wiederholt zu einer bestimmten Zeit ausgeführt werden.
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()); }
Übergabe von Argumenten an eine Task (Job)
Wie in den vorherigen Beispiel-Ausgabeprotokollen zu sehen ist, ist es schwierig, den Fortschritt eines Auftrags zu erkennen und zu verfolgen, insbesondere wenn mehrere geplante Funktionen gleichzeitig laufen. Um dies zu beheben, benötigen wir eine optionale Variable namens jobs_name in der Funktion dO. Diese Variable hilft bei der Kennzeichnung aller geplanten Aufgaben.
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 }
Jetzt können wir die Fortschritte der einzelnen Aufträge besser verfolgen.
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!"); }
Ausgabe:
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]
Ausführen einer Aufgabe bis zu einer bestimmten Zeit
Manchmal haben wir geplante Aufgaben, die wir nicht ewig laufen lassen wollen. In diesem Fall wäre es hilfreich, eine Frist für diese Aufträge zu setzen.
Lassen Sie uns die Funktion until vorstellen. Eine Funktion, die derjenigen des Schedule-Moduls in Python ähnelt.
CSchedule* CSchedule::until(datetime expiry_date) { m_expiry_date = expiry_date; return GetPointer(this); }
In der Funktion mit dem Namen dO übernehmen wir das Ablaufdatum aus unserer Klasse (das wir von der Funktion mit dem Namen until erhalten haben) und weisen es der Struktur des Auftrags zu.
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 }
Bevor wir einen Auftrag in der Funktion run_pending ausführen, müssen wir prüfen, ob er noch nicht abgelaufen ist.
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 }
Schließlich führen wir eine Aufgabe aus und setzen das Ablaufdatum auf 5 Minuten ab der aktuellen Uhrzeit.
#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!"); }
Ausgabe:
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
Alle Aufträge unabhängig von ihren Zeitplänen ausführen
Es kann vorkommen, dass Sie alle Funktionen trotz ihrer Zeitpläne sofort ausführen müssen. Normalerweise zu Testzwecken, manchmal aber auch, um alle geplanten Vorgänge auf einmal auszuführen, z. B. beim Programmstart.
In solchen Situationen ist die Funktion run_all sehr nützlich.
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()); }
Beispiel für die Verwendung:
#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!"); }
Ausgabe:
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!
Obwohl diese vier Funktionen so eingestellt sind, dass sie nach dem vierten Zeitrahmenintervall ausgeführt werden, wurden sie alle zum gleichen Zeitpunkt ausgeführt.
Zeitpläne verwalten
Wir brauchen verschiedene Möglichkeiten, um auf verschiedene Zeitpläne programmatisch zuzugreifen und sie zu stornieren, da einige Zeitpläne im Laufe der Zeit veraltet sein könnten.
Alle Jobs bekommen
| Funktion | Beschreibung |
|---|---|
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]; } | Diese Funktion liefert ein Argument, das als Referenz ein Array mit einer Struktur mit allen Eigenschaften für alle Aufträge/Aufgaben zurückgibt. |
uint get_jobs() { return m_jobs.Size(); } | Sie gibt die Anzahl der geplanten Aufträge zurück. |
Geplante Jobs stornieren
| Funktion | Beschreibung |
|---|---|
bool Cancel(const string jobs_name); | Hiermit wird ein geplanter Auftrag unter seinem Namen abgebrochen. |
bool Cancel(const uint jobs_index); | Er bricht einen geplanten Auftrag unter Verwendung seiner Indexnummer (von 0 bis +unendlich) ab, d.h., wenn der Auftrag der erste war, der geplant wurde, ist seine Indexnummer 0. |
bool Clear() { return ArrayResize(m_jobs, 0)==-1?false:true; } | Dadurch werden alle geplanten Aufträge aus dem Speicher gelöscht (entfernt). Nach dem Aufruf dieser Funktion wird kein Auftrag/keine Aufgabe ausgeführt. |
Beispiel für die Verwendung:
#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!"); }
Ausgabe:
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
Umgang mit Zeitzonen
In allen bisher besprochenen Beispielen und Code-Implementierungen in unserer Bibliothek haben wir die lokale Zeit verwendet. Dies ist jedoch sehr einschränkend, wenn man bedenkt, dass wir mehrere Zeitoptionen haben, die Entwickler in der Programmiersprache MQL5 verwenden können. Sie könnten zum Beispiel einen Handelsvorgang zu einer bestimmten Zeit entsprechend der Serverzeit des Brokers oder der UTC-Zeit einplanen wollen.
Im Konstruktor der Klasse CSchedule fügen wir eine optionale Variable hinzu, die es den Entwicklern ermöglicht, die Art der Zeit auszuwählen, die für alle Zeitplanungsvorgänge verwendet werden soll.
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));
} Nachstehend finden Sie die Enumeration der Zeitquellen und die entsprechende Funktion.
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 } }
Beispiel für die Verwendung:
#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!"); }
Ausgabe:
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]
Anwendungen des Schedule-Moduls in Ihren Handelsanwendungen
Wir haben gesehen, wie Sie dieses Modul in einfachen Funktionen und Beispielen verwenden können, die zeitnahe Funktionsabläufe zeigen. Im Folgenden finden Sie einige Beispiele aus der Praxis, wie Sie diese Bibliothek in Ihren Handelsanwendungen einsetzen können.
Eine effektivere Ereignisbehandlung von NewBar
Es ist nicht immer einfach, eine Funktion zu schreiben, die das Öffnen eines neuen Balkens effektiv erkennt. Da die Klasse CSchedule über verschiedene Möglichkeiten verfügt, eine Aufgabe zu einem ganz bestimmten Zeitpunkt zu planen, können wir sie bei der Durchführung bestimmter Aktionen zum Zeitpunkt des Öffnens einer Sekunde, Minute, Stunde, eines Tages usw. verwenden.
In der Datei 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() ); //--- }
Ausgaben im Strategie-Tester.

Wir müssen alles explizit in der Funktion OnInit setzen, ähnlich wie bei EventSetTimer – unter Verwendung der Funktion dO.
Da die Funktion schedule_pending für die ständige Überwachung der Zeitpläne verantwortlich ist, sollte sie innerhalb von OnTick im Expert Advisor, innerhalb der Funktion OnCalculate in Indikatoren und unter einer Endlosschleife innerhalb eines MQL5-Skripts ausgeführt werden.
Versenden von täglichen Handelsberichten
Indem wir einige Minuten oder Sekunden vor Börsenschluss (z.B. 5 Minuten vor 00:00 Uhr) verfolgen, können wir tägliche Handelsberichte drucken oder an die Nutzer senden.
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); }
Ausgaben auf dem Strategieprüfgerät.
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
Dieselbe Idee lässt sich auf die Erstellung von Wochenberichten an jedem Freitag kurz vor Börsenschluss und auf die Erstellung von Monatsberichten übertragen.
Zeitplan vs. OnTimer
Ein direkter Vergleich zwischen diesen beiden Methoden wäre zwar unfair, da beide auf ihre Weise gut funktionieren, aber es ist gut, die Unterschiede zu kennen und zu wissen, wann man die eine oder die andere Methode anwenden sollte und wann nicht.
| OnTimer | Schedule |
|---|---|
| Da es nur ein Timer-Ereignis (einen Zeitplan) zulässt, ist diese eingebaute Funktion nützlich, wenn Sie eine einzelne Aufgabe planen möchten. | Es erlaubt mehrere Aufgaben und unterschiedliche Intervalle für jede Aufgabe. Diese nutzerdefinierte Bibliothek ist nützlich, wenn Sie mehrere Zeitpläne haben, die Sie gleichzeitig ausführen möchten. |
| Es ist schnell und effektiv | Nicht so schnell wie der integrierte OnTimer; seine Wirksamkeit muss noch erforscht werden. |
| Sie ist nur auf Expert Advisors und Indikatoren beschränkt. | Es funktioniert in allen MQL5-Programmen, EAs, Indikatoren und Skripten. |
Es funktioniert 24/7 (immer zuverlässig) | Es stützt sich auf die Handelsfunktionen (OnTick und OnCalculate), die nur ausgelöst werden, wenn der Markt geöffnet wird. Sofern sie nicht in einer Endlosschleife in einem Skript verwendet wird, arbeitet die Überwachungsfunktion mit dem Namen run_pending in weniger als 24 (Stunden)/5 (Wochentagen) – je nach Markt. |
Um das Zuverlässigkeitsproblem zu beheben, das bei der Verwendung dieser Bibliothek auftritt, wie in der letzten Zeile der Vergleichstabelle beschrieben, müssen Sie die Klasse CSchedule innerhalb der Funktion OnTimer ausführen.
#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(); //✅ }
Schlussfolgerung
Für algorithmische Händler ist es von entscheidender Bedeutung, dass sie bestimmte Zeitpläne festlegen und Aufgaben zu genauen Zeiten ausführen können. Viele Handelsstrategien hängen nicht nur von genauen Tageszeiten ab, sondern es gibt auch zahlreiche Aktivitäten, die wir für eine wiederholte Ausführung automatisieren möchten, wie z. B. das Versenden von täglichen Handelsberichten, monatlichen Aktualisierungen und mehr.
Die implementierte Klasse bietet eine einfache Möglichkeit, wiederkehrende Ereignisse in Ihren MQL5-Programmen zu setzen, ähnlich wie es das Schedule-Modul in Python bietet.
Die OnTimer-Funktion ist zwar anständig, aber es fehlen einige der wichtigsten Möglichkeiten, um menschenfreundliche Zeitpläne zu erstellen. Sie können diese Bibliothek also gerne in Bereichen verwenden, in denen OnTimer nicht ausreicht.
Peace out.
Tabelle der Anhänge
| Dateiname | Beschreibung und Verwendung |
|---|---|
| Experts\Schedule testing EA.mq5 | Ein Expert Advisor (EA) für die Planung von Handelsgeschäften. |
| Include\schedule.mqh | Enthält die Klasse CSchedule, die für die Planung von Funktionen zur Ausführung zu bestimmten Zeiten und in bestimmten Intervallen nützlich ist. |
| Scripts\schedule test.mq5 | Ein Skript zum Testen der Klasse CSchedule. |
Übersetzt aus dem Englischen von MetaQuotes Ltd.
Originalartikel: https://www.mql5.com/en/articles/18913
Warnung: Alle Rechte sind von MetaQuotes Ltd. vorbehalten. Kopieren oder Vervielfältigen untersagt.
Dieser Artikel wurde von einem Nutzer der Website verfasst und gibt dessen persönliche Meinung wieder. MetaQuotes Ltd übernimmt keine Verantwortung für die Richtigkeit der dargestellten Informationen oder für Folgen, die sich aus der Anwendung der beschriebenen Lösungen, Strategien oder Empfehlungen ergeben.
MQL5-Assistenten-Techniken, die Sie kennen sollten (Teil 77): Verwendung des Gator-Oszillators und des Akkumulations-/Distributions-Oszillators
Vom Neuling zum Experten: Reporting EA – Einrichten des Arbeitsablaufs
Aufbau eines Handelssystems (Teil 1): Ein quantitativer Ansatz
MQL5-Assistenten-Techniken, die Sie kennen sollten (Teil 76): Verwendung von Mustern des Awesome Oszillators und der Envelope-Kanäle mit überwachtem Lernen
- Freie Handelsapplikationen
- Über 8.000 Signale zum Kopieren
- Wirtschaftsnachrichten für die Lage an den Finanzmärkte
Sie stimmen der Website-Richtlinie und den Nutzungsbedingungen zu.