English Русский
preview
MetaTrader 5 und der MQL5-Wirtschaftskalender: Wie sich News in ein reproduzierbares Handelssystem umwandeln lassen

MetaTrader 5 und der MQL5-Wirtschaftskalender: Wie sich News in ein reproduzierbares Handelssystem umwandeln lassen

MetaTrader 5Beispiele |
24 8
MetaQuotes
MetaQuotes

Einführung

Die Hauptprobleme des modernen News-Traders sind das fragmentierte Instrumentarium und das Fehlen eines systematischen, algorithmischen Handelsablaufs. Es ist ziemlich schwierig, Ihre Aufmerksamkeit zwischen Ihrem Internetbrowser (Surfen auf Nachrichtenseiten) und Ihrem Handelsterminal aufzuteilen, während Sie Trades eröffnen oder verwalten.

Der Arbeitsablauf eines News-Traders sieht folgendermaßen aus: Öffnen Sie schnell den Nachrichtenkalender in Ihrem Webbrowser und prüfen Sie, ob sich bei den Ereignissen etwas geändert hat → Bewerten Sie schnell die anstehenden Ereignisse und entscheiden Sie, was und wie Sie handeln möchten → Wechseln Sie zum MetaTrader 5-Terminal – platzieren Sie entweder Pending Orders oder warten Sie am Terminal auf die Veröffentlichung der Nachrichten, um eine Entscheidung zu treffen. In diesem Szenario verliert der Händler oft den Kontext, was zu Verzögerungen bei der Reaktion auf Nachrichten führt, was wiederum Verluste zur Folge hat.

Möchten Sie, dass der Handel auf Basis von Nachrichten wie ein technischer Prozess funktioniert – mit klaren Regeln, reproduzierbaren Ergebnissen und automatisierten Tests? Der Zweck dieses Artikels ist es, die funktionierende Architektur eines News-Layers für MetaTrader 5 zu demonstrieren: eine einzige Datenquelle, die korrekte Nutzung der Kalender-API, ein Filter- und Caching-Mechanismus, der Export von historischen Ereignissen in eine Ressource für den Tester und das automatische Umschalten zwischen Live und Tester – sodass derselbe Code deterministische Ergebnisse sowohl in Echtzeit als auch in historischen Daten erzeugt.

Hauptproblem eines Händlers

Abb. 1. Das Hauptproblem eines manuellen Händlers ist die Fragmentierung der Werkzeuge


Das manuelle News-Trading ist überholt.

Erstens kann eine Nachrichtenstrategie, die von externen Faktoren abhängt, nicht getestet werden. Ein wesentlicher Teil des Erfolgs im Trading hängt davon ab, die Performance der Strategie anhand historischer Daten zu testen. Die Unmöglichkeit, eine Strategie zu testen, führt zu einer Subjektivität der getroffenen Entscheidungen. Die Strategie wird eine Hypothese bleiben, und Sie werden viel Zeit – Monate und sogar Jahre – damit verbringen, sie zu testen.

Zweitens ist der manuelle Handel sehr schwer zu skalieren. Versuchen Sie, ein Unternehmen zu skalieren, das unbeständig ist und unregelmäßige Gewinne erzielt. Eine Ausweitung des Systems kann die Probleme nur noch verschärfen. Wenn es keine klare Struktur, Systematisierung und Optimierung gibt, dann ist es sinnlos, über Skalierung nachzudenken.

Drittens sind Verzögerungen bei der Reaktion auf Nachrichten ein wesentlicher Schwachpunkt beim manuellen Handel. Stellen Sie sich vor, Sie sind ein FOREX-Trader und handeln mit den Daten von Non-Farm-Payrolls (NFP). Die NFP-Daten werden in der Regel einmal im Monat veröffentlicht. Sie haben sich ablenken lassen und den NFP verpasst, und haben dadurch die Gelegenheit verpasst, Geld zu verdienen. Diese Situation ist vielen Händlern vertraut.

Fazit: Automatisierung ist der einzige Weg zum reproduzierbaren News-Trading.

Vergleich der Reaktionen

Abb. 2. Ein Roboter ist immer schneller


Integrierter Wirtschaftskalender in MetaTrader 5: Eine einzige Informationsquelle

Um das News-Trading algorithmisch abzubilden – und damit historische Tests zu ermöglichen – verwenden Sie das einzigartige Produkt unseres Unternehmens – einen in MetaTrader 5 integrierten Wirtschaftskalender mit Zugang zu Nachrichten über die MQL5 API. So wird Ihr News-Trading von einer Improvisation zu einem überprüfbaren algorithmischen Prozess. Sowohl aktuelle Ereignisse in Echtzeit als auch historische Ereignisse sind für Offline-Tests verfügbar.

Tabelle 1:

Was macht den in MetaTrader 5 integrierten Kalender so einzigartig?

Parameter
Zugriffsgeschwindigkeit
<100 ms – nach dem Laden von Nachrichten in das Terminal
Integration Nativ – auf der Ebene des Terminal-Kerns + MQL-API
Test Vollständige Unterstützung – nach dem Herunterladen von Nachrichten auf das Terminal (Zugriff im Tester über Dateien, Ressourcen oder SQLite)
Zuverlässigkeit Hoch

MetaTrader 5 verfügt über einen hervorragenden Strategietester – schnell, Multi-Asset und in der Lage, Computer in lokalen und globalen Netzwerken zum Testen und Optimieren zu nutzen. Testen Sie Ihr News-Trading-System mit historischen Daten. Dies könnte Ihren Handelsansatz zum Positiven verändern.


MetaTrader 5 Wirtschaftskalender-Funktionen: Ein Überblick über die MQL5-API

Der Übergang von der manuellen zur algorithmischen Analyse beginnt mit dem Verständnis der Datenarchitektur. In MQL5 ist der Wirtschaftskalender nicht nur eine Tabelle, sondern eine strukturierte Datenbank, die über eine eigene API zugänglich ist. Wir werden untersuchen, wie man Ereignisse korrekt abfragt, was der Unterschied zwischen Ereignis und Wert ist und warum die Zeitsynchronisation für eine erfolgreiche Strategie unerlässlich ist.

Der MQL5-Kalender basiert auf mehreren zentralen Funktionen, die jeweils eine eigene Aufgabe erfüllen. Es ist wichtig zu verstehen, dass es keine universelle Funktion gibt, die alle Wirtschaftskalenderdaten auf einmal liefert. Hier ist ein kombinierter Ansatz erforderlich.

  • CalendarValueHistory – Hauptinstrument für das anfängliche Laden. Ermöglicht das Abrufen eines Arrays von Ereigniswerten für ein bestimmtes Zeitintervall. Dies ist die „schwere Artillerie“, die während der Initialisierung des EA verwendet wird, um den Cache mit historischen Daten oder Daten für die kommende Woche zu füllen.
  • CalendarValueLast – Hauptfunktion für EAs, die in Echtzeit arbeiten. Gibt nur geänderte oder neue Werte seit der letzten Anfrage zurück (über den Mechanismus des Parameters change_id). Auf diese Weise können Datenverkehr und Serverressourcen eingespart werden, da nicht bei jedem Tick das gesamte Datenfeld angefordert wird.
  • CalendarEventByCountry – liefert Beschreibungen aller Ereignisse in einem Land. Gibt eine Liste von Ereignisbeschreibungen für ein bestimmtes Land zurück, das durch einen Code gemäß ISO 3166-1 alpha-2 angegeben ist. Erforderlich für die Erstellung von Filtern (z. B. nur Ereignisse für „US“ (USA), „RU“ (Russland), „CA“ (Kanada) usw. anzeigen).
  • CalendarEventByCurrency – liefert Beschreibungen aller Ereignisse nach Währung. Gibt eine Liste von Ereignisbeschreibungen für eine bestimmte, durch einen Code („USD“, „EUR“ usw.) angegebene Währung zurück.
  • CalendarCountryById – erhält Ländereigenschaften nach „id“.
  • CalendarEventById – erhält Ereigniseigenschaften nach „id“.
  • CalendarValueById – einen bestimmten Wert nach „id“ abrufen.

Datenstrukturen: Was gibt die Kalender-MQL5-API zurück?

Alle Wirtschaftskalender-API-Funktionen geben entweder ein Array von Strukturen oder eine Variable mit einer einzelnen Struktur zurück. Wir wollen die Strukturen und ihre Felder vollständig auflisten.

Ereignisbeschreibungen – verwendet in den Funktionen CalendarEventById, CalendarEventByCountry und CalendarEventByCurrency:

struct MqlCalendarEvent
  {
   ulong                               id;                    // event ID
   ENUM_CALENDAR_EVENT_TYPE            type;                  // event type from the ENUM_CALENDAR_EVENT_TYPE enumeration
   ENUM_CALENDAR_EVENT_SECTOR          sector;                // sector an event is related to
   ENUM_CALENDAR_EVENT_FREQUENCY       frequency;             // event frequency (periodicity)
   ENUM_CALENDAR_EVENT_TIMEMODE        time_mode;             // event time mode
   ulong                               country_id;            // country ID
   ENUM_CALENDAR_EVENT_UNIT            unit;                  // economic indicator value's unit of measure
   ENUM_CALENDAR_EVENT_IMPORTANCE      importance;            // event importance
   ENUM_CALENDAR_EVENT_MULTIPLIER      multiplier;            // economic indicator value multiplier
   uint                                digits;                // number of decimal places
   string                              source_url;            // URL of a source where an event is published
   string                              event_code;            // event code
   string                              name;                  // event text name in the terminal language (in the current terminal encoding)
  };


Länderbeschreibungen – verwendet in den Funktionen CalendarCountryById und CalendarCountries:

struct MqlCalendarCountry
  {
   ulong                               id;                    // country ID (ISO 3166-1)
   string                              name;                  // country text name (in the current terminal encoding)
   string                              code;                  // country code name (ISO 3166-1 alpha-2)
   string                              currency;              // country currency code
   string                              currency_symbol;       // country currency symbol
   string                              url_name;              // country name used in the mql5.com website URL
  };


Ereigniswerte – verwendet in den Funktionen CalendarValueById, CalendarValueHistoryByEvent, CalendarValueHistory, CalendarValueLastByEvent und CalendarValueLast.

struct MqlCalendarValue
  {
   ulong                               id;                    // value ID
   ulong                               event_id;              // event ID
   datetime                            time;                  // event date and time
   datetime                            period;                // event reporting period
   int                                 revision;              // revision of the published indicator relative to the reporting period
   long                                actual_value;          // actual value in ppm or LONG_MIN if the value is not set
   long                                prev_value;            // previous value in ppm or LONG_MIN if the value is not set
   long                                revised_prev_value;    // revised previous value in ppm or LONG_MIN if the value is not set
   long                                forecast_value;        // forecast value in ppm or LONG_MIN if the value is not set
   ENUM_CALENDAR_EVENT_IMPACT          impact_type;           // potential impact on the currency rate
   //--- functions for checking the values
   bool                         HasActualValue(void) const;   // returns 'true' if actual_value is set
   bool                         HasPreviousValue(void) const; // returns 'true' if prev_value is set
   bool                         HasRevisedValue(void) const;  // returns 'true' if revised_prev_value is set
   bool                         HasForecastValue(void) const; // returns 'true' if forecast_value is set
   //--- functions for getting values
   double                       GetActualValue(void) const;   // return actual_value or nan if the value is not set
   double                       GetPreviousValue(void) const; // return prev_value or nan if the value is not set
   double                       GetRevisedValue(void) const;  // returns revised_prev_value or nan if the value is not set
   double                       GetForecastValue(void) const; // returns forecast_value or nan if the value is not set
  };

Anmerkung:

Beachten Sie, dass die Struktur MqlCalendarValue Methoden zum Prüfen und Abrufen von Werten aus den Feldern actual_value, forecast_value, prev_value und revised_prev_value bietet. Die aufgelisteten Felder haben möglicherweise keine Werte – so kann beispielsweise das Feld actual_value leer sein, weil die Nachricht noch nicht veröffentlicht wurde. Der beste Weg, um Werte zu erhalten, ist, die Prüfung durchzuführen und die Werte mit den Methoden der Struktur selbst zu erhalten.

Die Strukturen sind durch die folgenden Beziehungen miteinander verbunden:

Kalenderbeziehungen

Abb. 3. Beziehungen zwischen Kalenderstrukturen


Die Struktur MqlCalendarCountry ist mit MqlCalendarEvent über eine Länder-ID verknüpft. Die Beziehungsform ist „one-to-many“ (1..*).

Die Struktur MqlCalendarEvent ist mit MqlCalendarValue über eine Ereignis-ID verknüpft. Die Beziehungsform ist „one-to-many“ (1..*).


Veröffentlichungszeit der News und Serverzeit

Alle Funktionen zur Arbeit mit dem Wirtschaftskalender verwenden TimeTradeServer(), die Uhrzeit des Handelsservers. Dies bedeutet, dass die Zeit in der Struktur MqlCalendarValue und die Zeitangaben in der CalendarValueHistoryByEvent() und CalendarValueHistory() in der Zeitzone des Handelsservers und nicht in der Ortszeit des Nutzers angegeben werden.

Eine Umwandlung ist nicht erforderlich: Die Zeit des Ereignisses MqlCalendarValue::period kann direkt mit der Zeit verglichen werden, die durch den Aufruf der Funktion TimeCurrent() oder TimeTradeServer() erhalten werden kann. In der Testumgebung gibt TimeTradeServer() eine modellierte Zeit zurück, die mit der Zeit in den historischen Daten identisch ist. Die Logik für die Arbeit mit Zeitfenstern („30 Minuten vor den Nachrichten“) funktioniert in Echtzeit und in der Vergangenheit gleich. Wenn der Makler die Umstellung auf Sommer-/Winterzeit berücksichtigt, passt sich der Kalender automatisch an diese Umstellung an.


Praktisches Beispiel: Abrufen einer Liste von Ereignissen für heute (den aktuellen Tag):

//+------------------------------------------------------------------+
//| Get calendar values for the current day                          |
//+------------------------------------------------------------------+
void GetTodayUSD_Events()
 {
//--- define the period boundaries in server time
  datetime server_now = TimeTradeServer();
  datetime day_start  = server_now - (server_now % 86400);
  datetime day_end    = day_start + 86400;

  MqlCalendarValue    values[];
  MqlCalendarEvent    event;
  MqlCalendarCountry  country;

//--- request values only for USD
  if(CalendarValueHistory(values, day_start, day_end, NULL, "USD"))
   {
    Print("  Events received for USD: ", ArraySize(values));

    //--- iterate over the array of values
    for(int i = 0; i < ArraySize(values); i++)
     {
      //--- get event description
      if(CalendarEventById(values[i].event_id, event))
      {
        //--- get country description
        if(CalendarCountryById(event.country_id, country))
        {
          Print("✅ Event #", i);
          Print("Event ID:       ", values[i].event_id);

          Print("Event name: ", event.name);
          Print("Sector:           ", event.sector);
          Print("Source:         ", event.source_url);

          Print("Country name:  ", country.name);
          Print("Country URL:       ", country.url_name);

          Print("Time:            ", TimeToString(values[i].time, TIME_DATE | TIME_SECONDS));
          Print("Impact:          ", values[i].impact_type);

          // CHECK AND OUTPUT VALUES
          if(values[i].HasActualValue())
            Print("Actual:             ", values[i].GetActualValue());

          if(values[i].HasRevisedValue())
            Print("Revised:     ", values[i].GetRevisedValue());

          if(values[i].HasForecastValue())
            Print("Forecast:          ", values[i].GetForecastValue());

          if(values[i].HasPreviousValue())
            Print("Previous:       ", values[i].GetPreviousValue());
        }
      }
     }
   }
  else
   {
    int error = GetLastError();
    if(error == 0)
     {
      Print("❌ CalendarValueHistory: No Events");
     }
    else
     {
      Print("❌ Error CalendarValueHistory: ", error);
     }
   }
 }
//+------------------------------------------------------------------+

Die Funktion zeigt eine Liste von Ereignissen für die Währung USD für den aktuellen Tag und den Inhalt der Hauptfelder der Strukturen an, die über die MQL-API des Kalenders abgerufen werden. Der vollständige Skriptcode ist in der Datei GetTodayEvents-S.mq5 enthalten, die dem Artikel beigefügt ist.

Die Ergebnisse der Funktion können in der Registerkarte Werkzeuge\Experts des MetaTrader 5-Terminals eingesehen werden. Wir sehen, dass zwei Ereignisse für USD empfangen wurden. Zum Zeitpunkt der Anfrage waren diese Ereignisse noch nicht eingetreten (die Nachricht war noch nicht veröffentlicht worden). Daher enthält das Feld MqlCalendarValue::actual_value keinen Wert – die Funktion HasActualValue() gibt „false“ zurück.

Bei diesen Ereignistypen wird dem Feld MqlCalendarValue::forecast_value, das von der Funktion HasForecastValue() überprüft wird, nichts zugewiesen (es enthält keinen Wert). Bei anderen Ereignissen können alle vier Felder (prev_value, actual_value, forecast_value und revised_prev_value) fehlen.

  Empfangene Ereignisse für USD: 2
✅ Event #0
Ereignis-ID:            840220005
Ereignisname:      Auktion von 3-Monats-Schatzanweisungen
Sektor:              1
Quelle: https://home.treasury.gov/
Name des Landes:   USA
Länder-URL: united-states
Time:                2026.04.20 18:30:00
Impact:             0
Vorherige:           3.62
✅ Event #1
Ereignis-ID:           840220006
Ereignisname:      Auktion von 6-Monats-Schatzanweisungen
Sektor:              1
Quelle: https://home.treasury.gov/
Name des Landes:   USA
Länder-URL: united-states
Time:                2026.04.20 18:30:00
Impact:             0
Vorherige:           3.61

Erklärungen für den angegebenen Code:

Zunächst erhalten wir ein Array von Werten für alle Ereignisse in einem bestimmten Zeitbereich mit dem Währungsfilter „USD“. In der Schleife durchlaufen wir das Wertearray und rufen für jeden Wert anhand der Ereignis-IDMqlCalendarValue::event_id die Ereignisbeschreibung mit der Funktion CalendarEventById() ab. Anschließend rufen wir anhand der ID MqlCalendarEvent::country_id die Länderbeschreibung mit der Funktion CalendarCountryById() ab.

Wenn die CalendarValueHistory() „false“ zurückgibt, GetLastError() aber Null (kein Fehler), bedeutet dies, dass es keine Ereignisse mit den gewünschten Einstellungen gibt.


Fehlerbehandlung und Einschränkungen bei der Arbeit mit dem Kalender

Die Arbeit mit entfernten Daten birgt immer das Risiko von Verbindungsunterbrechungen oder Zugriffsbeschränkungen. Kalenderfunktionen geben im Fehlerfall false oder 0 zurück. Um den Grund zu verstehen muss GetLastError() verwendet werden. In der Dokumentation wird auf eine separate Gruppe von Fehlern für das Kalendermodul hingewiesen.

Fehlercodes:

  • ERR_CALENDAR_TIMEOUT (Code 5200) – Der Timeout für die Serverantwort ist abgelaufen. Netzwerkausfall oder Serverüberlastung. Lösung: Wiederholen Sie die Anfrage nach einer Pause von 5...10 Sekunden.
  • ERR_CALENDAR_NO_DATA (Code 5201) – Die Kalenderdaten wurden noch nicht geladen. Die Initialisierung des Kalenders erfolgt asynchron. Lösung: 1...2 Sekunden warten und wiederholen.
  • ERR_CALENDAR_INVALID_DATE (Code 5202) – ungültiger Datumsbereich. Code-Fehler (z.B. das Startdatum ist größer als das Enddatum). Lösung: Korrigieren Sie die Logik, eine Wiederholung der Anfrage ist nutzlos.
  • ERR_CALENDAR_INVALID_COUNTRY (Code 5203) – unbekannter Länder-/Währungscode. Fehler in den Anfrageparametern. Lösung: Überprüfen Sie den Code (z. B. „US“ anstelle von „USA“).
  • ERR_CALENDAR_TOO_MANY_REQUESTS (Code 5204) – Anfragelimit überschritten. Kritischer Fehler. Lösung: Vergrößern Sie den Abstand zwischen den Anfragen.

Begrenzung der Anfragerate:

Die Server, mit denen das MetaTrader 5-Terminal verbunden ist, schützen die Infrastruktur vor Überlastung. Wenn der EA die Funktion CalendarValueHistory() bei jedem Tick oder in einer Schleife ohne Verzögerungen aufruft, gibt der Server den Fehler 5204 zurück und blockiert vorübergehend den Zugriff auf den Kalender für Ihr Terminal. Beste Praxis: Laden Sie die Daten einmal beim Start in OnInit() für den erforderlichen Zeitraum.

Für Echtzeitaktualisierungen verwenden Sie CalendarValueLast() und speichern Sie die zurückgegebene Variable change_id – so werden nur Änderungen statt des gesamten Datenarrays geladen. Aktualisieren Sie die Daten eines Timers in OnTimer() in Intervallen von nicht mehr als 5-10 Minuten.

Sehen wir uns nun einige Beispiele für Lösungen von Problemen an, die beim Laden eines Kalenders auftreten können:

Wenn Sie das Terminal (Kaltstart) und den EA starten, ist der Kalender nicht sofort bereit. Die Daten werden im Hintergrund vom Server geladen. Wenn wir CalendarValueHistory() in der allerersten Zeile von OnInit() aufrufen, wird höchstwahrscheinlich der Fehler 5201 – keine Daten – angezeigt. Es ist notwendig, einen Mechanismus zur Prüfung der Bereitschaft mit einem exponentiellen oder festen Timeout zu implementieren.

Praktisches Beispiel – Funktion zum Laden von Kalenderdaten mit Fehlerbehandlung:

//+------------------------------------------------------------------+
//| Calendar loading function                                        |
//+------------------------------------------------------------------+
bool LoadCalendar(MqlCalendarValue& values[], const datetime from, const datetime to, const string country_code = NULL, const string currency = NULL, const int max_retries = 5)
 {
  int retry_count = 0;

  while(retry_count < max_retries)
   {
    ResetLastError();
    //--- download attempt
    if(CalendarValueHistory(values, from, to, country_code, currency))
      return true; // Success

    int error = GetLastError();

    //--- in case of "No data" (5201) or "Timeout" (5200) error — wait and repeat
    if(error == 5201 || error == 5200)
     {
      retry_count++;
      Sleep(1000); // 1 second pause before repeating
      continue;
     }

    //--- if the error is critical (for example, an invalid date), interrupt the download immediately
    Print("❌ Critical Calendar Error: ", error);
    return false;
   }

  Print("❌ Failed to load calendar after ", max_retries, " attempts.");
  return false;
 }
//+------------------------------------------------------------------+

Erklärungen für den angegebenen Code:

Berücksichtigen Sie die Möglichkeit der Fehler „Keine Daten“ (5201) und „Timeout“ (5200). Diese Fehler sind behebbar. Machen Sie eine Pause von 1...5 Sekunden und fordern Sie die Daten erneut an. Wenn nicht behebbare Fehler auftreten, brechen Sie den Download sofort ab. Zu den nicht behebbaren Fehlern gehören ungültige Daten, falsche Währungs- oder Ländercodes usw. Den vollständigen Skriptcode finden Sie in der Datei GetTodayEvents-S.mq5 im Anhang zu diesem Artikel.


Um Begrenzungen zu vermeiden und eine maximale Reaktionsgeschwindigkeit zu gewährleisten, sollte in OnTimer() der Mechanismus change_id verwendet werden. Beim Start laden wir den vollständigen Ereignisverlauf und speichern die letzte change_id. Im Timer fordern wir mit der Funktion CalendarValueLast() nur neue Daten an. Der Server gibt nur die geänderten Daten zurück (oder „false“, wenn keine Änderungen vorliegen), ohne Ressourcen für die Übertragung alter Daten zu verschwenden.

Praktisches Beispiel – die Funktion zur Aktualisierung von Kalenderdaten mit Fehlerbehandlung:

//+------------------------------------------------------------------+
//| Timer: incremental update                                        |
//+------------------------------------------------------------------+
void OnTimer()
 {
  if(!is_initialized)
    return;

  MqlCalendarValue updates[];
  ResetLastError();

//--- API automatically updates last_change_id by reference
  if(!CalendarValueLast(last_change_id, updates,
                        (InpCountryCode == "") ? NULL : InpCountryCode,
                        (InpCurrency    == "") ? NULL : InpCurrency))
   {
    int err = GetLastError();
    // 0 = SUCCESS/NO_NEW_DATA, 5402 = ERR_CALENDAR_NO_CHANGES
    if(err != 0 && err != 5402)
      Print("🔴️ CalendarValueLast error: ", err);
    return;
   }

  int cnt = ArraySize(updates);
  if(cnt == 0)
    return;

  Print("🟢 Received ", cnt, " updates. New change_id: ", last_change_id);
  if(InpPrintChanges)
    ArrayPrint(updates);
  SyncCache(updates);
 }
//+------------------------------------------------------------------+
//| Cache synchronization                                            |
//+------------------------------------------------------------------+
void SyncCache(const MqlCalendarValue &updates[])
 {
  int upd_cnt  = ArraySize(updates);
  int cache_sz = ArraySize(calendar_cache);

  for(int u = 0; u < upd_cnt; u++)
   {
    bool found = false;
    for(int c = 0; c < cache_sz; c++)
     {
      if(calendar_cache[c].id == updates[u].id)
       {
        calendar_cache[c] = updates[u];
        found = true;
        break;
       }
     }
    if(!found)
     {
      ArrayResize(calendar_cache, cache_sz + 1);
      calendar_cache[cache_sz] = updates[u];
      cache_sz++;
      total_events++;
     }
   }
 }
//+------------------------------------------------------------------+

Erklärungen für den angegebenen Code:

Die inkrementelle Aktualisierung von Ereignissen erfolgt über den Event-Handler des Timers OnTimer(). Die Kalender-API-Funktion CalendarValueLast() wird aufgerufen – sollten neue Ereignisse auftreten, werden die entsprechenden Elemente im zuvor geladenen Array MqlCalendarValue der Strukturen aktualisiert und die Informationen im Terminal-Protokoll ausgegeben. Ist ein solches Ereignis im geladenen Array nicht vorhanden, wird es dem Array hinzugefügt.

Der vollständige Skriptcode ist in CalendarEventMonitor-EA.mq5 enthalten, das dem Artikel beigefügt ist.


Ereignisse filtern: Vom Allgemeinen zum Speziellen

Warum filtern:

Im Wirtschaftskalender werden täglich 60-90 Ereignisse veröffentlicht. Es ist, als würde man ständig Hunderten von unbedeutenden Indikatoren, Feiertagen und offiziellen Reden zuhören, die keine unmittelbare Wirkung auf den Markt haben. Der Handel mit jedem einzelnen von ihnen ist ein todsicherer Weg, zu viel zu handeln und Ihre Einlage zu verlieren. Die Aufgabe eines algorithmischen News-Traders besteht darin, sich auf 3-5 Ereignisse zu konzentrieren, die den Markt wirklich bewegen.

Die Filterung im algorithmischen News-Trading ist gleichbedeutend mit der Erhöhung des Signal-Rausch-Verhältnisses auf ein Niveau, das für die automatische Entscheidungsfindung geeignet ist. In der MQL5-Kalender-API ist dieser Prozess wie ein mehrstufiges Sieb implementiert, das die Arrays der Strukturen MqlCalendarEvent und MqlCalendarValue durchlaufen.

Kriterien für die Filterung:

Der Markt reagiert nicht auf die Pressemitteilung selbst, sondern auf die Abweichung der tatsächlichen Daten von den Prognosen und den Grad des makroökonomischen Einflusses. Ereignisse mit geringer Wirkung (MqlCalendarValue::impact_type < 3) werden von Handelsalgorithmen und Market Makern häufig ignoriert.

Ohne Filterung entstehen Ihnen folgende Probleme:

  • Falsche Auslösungen der Strategie aufgrund irrelevanter Statistiken;
  • Handel in Phasen mit erhöhtem Spread ohne Volatilität;
  • Überlastung der EAs-Logik durch unnötige Überprüfungen.

Das Ziel der Filterung ist es, den Datenfluss um 90 % zu reduzieren, sodass nur noch Ereignisse mit hoher Wirkung (ENUM_CALENDAR_EVENT_IMPORTANCE::CALENDAR_IMPORTANCE_HIGH) für die vom Händler ausgewählte Zielwährung in einem bestimmten Zeitfenster übrig bleiben. 

Sehen wir uns an, wie man einen zuverlässigen Filter baut, der sowohl im Echtzeit- als auch im Testmodus gleichermaßen zuverlässig arbeitet.

Mehrstufiges Filtersystem:

Die vom Terminal heruntergeladenen „rohen“ Ereignisdaten sind wie unverarbeitetes Erz: Sie enthalten Tausende von Einträgen, die von unbedeutenden statistischen Berichten bis hin zu Feiertagen reichen, die keinen Nutzen für die algorithmische Strategie bieten.

Um diesen Datenstrom in ein sauberes Signal zu verwandeln, benötigen wir ein mehrstufiges Filtersystem. Die Ereignisfilterung fungiert als „Sieb“, durch das die Rohdaten hindurchgehen, sodass am Ausgang nur die Ereignisse übrig bleiben, die die angegebenen Kriterien erfüllen: Währung, Wichtigkeit, Code und Zeitfenster.

Ein wirksamer Filter sollte hierarchisch aufgebaut sein: Er beginnt mit einer groben Abgrenzung nach geografischen Gesichtspunkten und endet mit einer Feinabstimmung nach Zeitfenstern.

Ein wichtiges Merkmal der News-MQL5-API – der Struktur MqlCalendarValue fehlen die Felder currency_id und country_id. Sie speichert nur event_id, Zeit, Indikatorwerte und Wirkungstyp. Wie wirkt sich das auf die Filtration aus?

Die Währungsfilterung wird auf der Ebene der API-Anfrage durchgeführt. Wenn Sie CalendarValueHistory(..., NULL, „USD“) aufrufen, verwirft das Terminal selbst alles außer USD. Das Array, das Sie erhalten, ist bereits nach Währung gefiltert. Daher besteht keine Notwendigkeit, die currency_id in Echtzeit zu überprüfen – es genügt, nach Zeit, Wichtigkeit und prognostizierter Präsenz/Signifikanz zu filtern.

Stufe 1: Währungsfilter

Die erste Filterstufe ist die geografische Zuordnung des Ereignisses. Beim Handel mit EURUSD sollte der EA nur auf Ereignisse reagieren, die die Eurozone (EUR) und die USA (USD) betreffen. Die Filterung beruht auf der Abfrage von Ereignissen mit den Währungscodes des gehandelten Instruments. Bei Cross-Rates (z. B. „GBPJPY“), müssen wir Ereignisse für beide Währungen („GBP“ und „JPY“) und „USD“ anfordern.

Stufe 2: Wichtigkeit des Ereignisses

Alle Nachrichten sind von unterschiedlicher Wichtigkeit für die Wirtschaft, ja sogar für die Preise. Der Verbraucherpreisindex (VPI) kann den Markt um 100 Punkte bewegen, während der ZEW-Verbrauchervertrauensindex ihn nur um 10 Punkte bewegen kann.

MQL5-API verwendet die Enumeration ENUM_CALENDAR_EVENT_IMPORTANCE zur Einstellung des Wichtigkeitsgrades:

enum ENUM_CALENDAR_EVENT_IMPORTANCE
{
   CALENDAR_IMPORTANCE_NONE,      // importance level is not set
   CALENDAR_IMPORTANCE_LOW,       // low importance
   CALENDAR_IMPORTANCE_MODERATE,  // medium importance
   CALENDAR_IMPORTANCE_HIGH       // high importance
};

News-EAs sollten alles unter „HIGH“ ignorieren, um Fehlalarme bei News mit geringer Liquidität zu vermeiden.

Stufe 3: Ereignis-Code

Selbst bei Ereignissen von großer Wichtigkeit gibt es Ausnahmen. So kann beispielsweise eine Rede der EZB oder des IWF als wichtig eingestuft werden, ist aber algorithmisch schwieriger zu verarbeiten als die Veröffentlichung bestimmter Zahlen. Daher ist eine Sortierung nach Ereigniscode erforderlich: MqlCalendarEvent::event_code Feld enthält eine eindeutige ID (zum Beispiel „NONFARM“, „CPI“, „GDP“). Wir wählen nur Ereignisse mit Wirtschaftszahlen aus, nicht Umfragen und Reden.

Erst nachdem das Ereignis die ersten drei Überprüfungsstufen durchlaufen hat, wird es in das resultierende Array kopiert und im Handel verwendet.

Filter-Levels

Abb. 4. Mehrstufiges Filtern ist der Schlüssel zum Erfolg im News-Trading

Nach den ersten drei Filterstufen prüft der EA, ob das Ereignis in das konfigurierte Zeitfenster fällt. Dies geschieht, während der Handels-EA läuft.

Stufe 4: Zeitfenster

Der letzte Schritt ist die Überprüfung der Relevanz. Wir brauchen keine Nachrichten, die erst in einem Monat veröffentlicht werden. Eine Nachricht, die gestern veröffentlicht wurde, ist vom Markt bereits eingepreist. Daher wird ein Ereignis als aktiv betrachtet, wenn TimeCurrent() innerhalb des folgenden Intervalls liegt: der angegebenen Zeit vor der Pressemitteilung und der angegebenen Zeit nach der Pressemitteilung. In der Regel werden die folgenden Einstellungen verwendet: 15-30 Minuten vor dem Veröffentlichung – für das Schließen von Positionen und 60 Minuten danach – für die Analyse der Volatilität und der Richtung der Preisbewegung.


MQL5-Binärressourcen: Verschieben von Ereignissen in Offline-Tests für eine effiziente Zwischenspeicherung

Ein Problem beim Testen im nachrichtenbasierten algorithmischen Handel ist, dass der Strategietester keinen Internetzugang hat. Diese Lösung beschleunigt das Testen und schützt vor nicht-deterministischen Faktoren, schafft aber ein Problem für das Testen von auf Nachrichten basierenden Strategien.

Wenn der EA im Tester die Funktion CalendarValueHistory() aufruft, erhält er einen Fehler und ein leeres Array. Um die Strategie zu testen, müssen wir historische Nachrichtendaten in die ausführbare Datei .ex5 einbetten. Hierfür verwenden wir die Option mit MQL5-Binärressourcen. Warum gerade diese Option? Dies ist der schnellste Weg, um auf strukturierte Daten zuzugreifen – die Zugriffsgeschwindigkeit wird nur durch die Leistung des Dateisystems des Computers begrenzt.

Schauen wir uns an, wie man das Array MqlCalendarValue[] in eine kompakte Binärdatei umwandelt und in den Code einbettet, sodass der Tester die Daten blitzschnell aus dem RAM liest. Der erste Schritt besteht darin, ein Exporter-Skript zu erstellen, das die aktuellen Daten aus dem Kalender herunterlädt und in einer Binärdatei speichert.

Praktisches Beispiel – ein Skript zum Exportieren von Ereignissen in eine Binärdatei:

Der vollständige Skriptcode befindet sich in der Datei ExportCalendarForTester.mq5, die dem Artikel beigefügt ist.


Schritt 1: Zusammenstellung und Filterung des Arrays

Wir werden nicht alle Weltereignisse in der Ressource speichern. Beim Export in eine Binärdatei werden dieselben Filter wie im EA angewendet: Währung, Wichtigkeit und Ereignistyp. Die Skripteingaben geben die Liste der Währungen, das Zeitintervall für das Laden von Ereignissen, die Ereigniscodes und die Mindestbedeutung an.

Liste der Ereigniscodes (InpEventCodes) ist standardmäßig leer – das bedeutet, dass alle Ereignistypen geladen werden. Wir können diesen Filter einschränken. Zum Beispiel nur "Non-Farms“ – dann wird die Liste der Codes „NONFARM“ sein.

InpUseCommonDir gibt an, wo die Binärdatei gespeichert werden soll: true – Speichern im gemeinsamen Ordner für alle Client-Terminals „\Terminal\Common\Files“.

//--- input parameters
input string                          InpCurrencies     = "USD";
input datetime                        InpDateFrom       = D'2025.01.01';
input datetime                        InpDateTo         = 0;
input string                          InpEventCodes     = "";
input ENUM_CALENDAR_EVENT_IMPORTANCE  InpMinImportance  = CALENDAR_IMPORTANCE_HIGH;
input string                          InpOutputFile     = "calendar_test_res.bin";
input bool                            InpUseCommonDir   = true;

Der Teil des Skripts, der Ereignisse mit den in den Eingaben angegebenen Werten lädt und filtert, ist unten dargestellt:

  Print("🔄 Calendar export:");
  Print("----------------------------------");
  Print("Filtering options:");
  Print(" 🟨 Interval = ", InpDateFrom, " — ", InpDateTo);
  Print(" 🟨 Currencies = ", InpCurrencies, "\n 🟨 Event Codes = ", InpEventCodes, "\n 🟨 Min Importance = ", EnumToString(InpMinImportance));
  Print("----------------------------------");

//--- initialize filter by currencies
  ArrayResize(currencies, 0);
  if(InpCurrencies == "")
    return;
  StringSplit(InpCurrencies, ',', currencies);
  currencies_size = ArraySize(currencies);
  for(int i = 0; i < currencies_size; i++)
    StringToUpper(currencies[i]);

//--- initialize the filter by event codes
  ArrayResize(event_codes, 0);
  if(InpEventCodes != "")
    StringSplit(InpEventCodes, ',', event_codes);
  event_codes_Size = ArraySize(event_codes);

//--- load calendar values with the CURRENCY FILTER (if specified)
  if(currencies_size > 0)
   {
    ArrayResize(values_size, currencies_size);
    ArrayFill(values_size, 0, currencies_size, 0);

    for(int i = 0; i < currencies_size; i++)
     {
      if(LoadCalendar(raw_values, InpDateFrom, InpDateTo, "", currencies[i]))
       {
        raw_values_size = ArraySize(raw_values);
        //--- load events with an IMPORTANCE AND EVENT CODE FILTER
        for(int k = 0; k < raw_values_size; k++)
         {
          //--- get event description
          if(CalendarEventById(raw_values[k].event_id, event))
           {
            //--- take indicators only
            if(event.type != CALENDAR_TYPE_INDICATOR)
              continue;                                   // nothing else to filter

            //--- check by IMPORTANCE
            if(event.importance < InpMinImportance)
              continue;                                   // nothing else to filter

            //--- check by EVENT CODE (if specified)
            if(event_codes_Size > 0)
             {
              bool code_allowed = false;
              for(int c = 0; c < event_codes_Size; c++)
               {
                StringToUpper(event.event_code);
                if(StringFind(event.event_code, event_codes[c]) >= 0)
                 {
                  code_allowed = true;
                  break;
                 }
               }
              if(code_allowed == false)
                continue;
             }

            //--- replenish the array of filtered events
            int event_index = ArraySize(values);
            ArrayResize(values, event_index + 1);
            values[event_index] = raw_values[k];
            values_size[i]++;
           }
         }
        Print("✅ Received values BY CURRENCY \"", currencies[i], "\": ", raw_values_size, " → Of these, filtered: ", values_size[i]);
       }
      else
       {
        int error = GetLastError();
        if(error == 0)
          Print("⚠ LoadCalendar Info: No Events for ", currencies[i]);
        else
          Print("❌ LoadCalendar Error: ", error, " for ", currencies[i]);
        return;                                     // nothing else to filter
       }
     }
   }

Erklärungen für den angegebenen Code:

  • Im Array values_size[] wird die Anzahl der gefilterten Ereignisse für jede in den Eingabeparametern des Codes angegebene Währung gespeichert.
  • Dem Wert Null für den MQL5-Datentypdatetime entspricht das Datum 01.01.1970 00:00:00.
  • Eine zusätzliche Filterung erfolgt nach Ereignistyp. Es werden nur Ereignisse vom Typ ENUM_CALENDAR_EVENT_TYPE::CALENDAR_TYPE_INDICATOR gefiltert – das sind wirtschaftliche Ereignisse (keine Reden, sondern Zahlen). Alle anderen Nachrichten werden aussortiert. Dies geschieht durch eine Validierung gleich zu Beginn der Filterung:
//--- take only indicators
if(event.type != CALENDAR_TYPE_INDICATOR)
  continue;                                   // Nothing else to filter


Führen wir das Skript mit verschiedenen Filterparametern aus, um zu sehen, wie schnell das Skript zum Laden und Filtern von Ereignissen funktioniert. Es ist klar, dass die Geschwindigkeit von der Leistung des Computers und der Geschwindigkeit der Verbindung zu den Datenservern über das Internet abhängt. Dennoch werden wir einen Stresstest durchführen. Die Ergebnisse werden im Folgenden vorgestellt.

Alle möglichen USD-Ereignisse – über den gesamten verfügbaren historischen Zeitraum hinweg – mit großer Wichtigkeit:

13:59:43.724    🔄 Calendar export:
13:59:43.724    ----------------------------------
13:59:43.724    Filtering options:
13:59:43.724      🟨 Interval = 1970.01.01 00:00:00 — 1970.01.01 00:00:00
13:59:43.724      🟨 Currencies = USD
13:59:43.724      🟨 Event Codes = 
13:59:43.724      🟨 Min Importance = CALENDAR_IMPORTANCE_HIGH
13:59:43.724    ----------------------------------
13:59:45.511    ✅ Received values BY CURRENCY "USD": 53346 → Davon gefiltert: 9009
13:59:45.511    ----------------------------------
13:59:45.513    ✅ Saved: USD_calendar_test_res.bin Größe: 1153152 Bytes (9009 Ereignisse)

Dauer zum Abrufen des Arrays gefilterter Werte (Strukturen MqlCalendarValue): 1,80 Sekunden (0,20 ms / pro Filtration).


Alle möglichen USD, EUR und JPY von großer Wichtigkeit – über die gesamte verfügbare Historie:

14:03:08.724    🔄 Calendar export:
14:03:08.724    ----------------------------------
14:03:08.724    Filtering options:
14:03:08.724      🟨 Interval = 1970.01.01 00:00:00 — 1970.01.01 00:00:00
14:03:08.724      🟨 Currencies = USD,EUR,JPY
14:03:08.724      🟨 Event Codes = 
14:03:08.724      🟨 Min Importance = CALENDAR_IMPORTANCE_HIGH
14:03:08.724    ----------------------------------
14:03:10.488    ✅ Received values BY CURRENCY "USD": 53346 → Davon gefiltert: 9009
14:03:10.947    ✅ Received values BY CURRENCY "EUR": 45139 → Davon gefiltert: 1102
14:03:11.404    ✅ Received values BY CURRENCY "JPY": 18907 → Davon gefiltert: 937
14:03:11.404    ----------------------------------
14:03:11.406    ✅ Saved: USD_calendar_test_res.bin Größe: 1153152 Bytes (9009 Ereignisse)
14:03:11.407    ✅ Saved: EUR_calendar_test_res.bin Größe: 141056 Bytes (1102 Ereignisse)
14:03:11.408    ✅ Saved: JPY_calendar_test_res.bin Größe: 119936 Bytes (937 Ereignisse)

Dauer zum Abrufen des Arrays gefilterter Werte (Strukturen MqlCalendarValue): 2,68 Sekunden (0,24 ms / pro Filtration).


NFP-Ereignisse („Non-Farms“) in USD von großer Wichtigkeit – in der gesamten verfügbaren Geschichte:

14:07:22.203    🔄 Calendar export:
14:07:22.203    ----------------------------------
14:07:22.203    Filtering options:
14:07:22.203      🟨 Interval = 1970.01.01 00:00:00 — 1970.01.01 00:00:00
14:07:22.203      🟨 Currencies = USD
14:07:22.203      🟨 Event Codes = NONFARM
14:07:22.203      🟨 Min Importance = CALENDAR_IMPORTANCE_HIGH
14:07:22.203    ----------------------------------
14:07:22.290    ✅ Received values BY CURRENCY "USD": 53346 → Davon gefiltert: 473
14:07:22.290    ----------------------------------
14:07:22.291    ✅ Saved: USD_calendar_test_res.bin Größe: 60544 Bytes (473 Ereignisse)

Dauer zum Abrufen des Arrays gefilterter Werte (Strukturen MqlCalendarValue): 0,09 Sekunden (0,18 ms / pro Filtration).

Bemerkung:

  1. Es ist nicht erforderlich, den gesamten Ereigniscode im Filter anzugeben. Es reicht aus, einen eindeutigen Teil der Zeichenkette anzugeben, der den Code enthält. Für „Non-Farms“ lautet der vollständige Code beispielsweise „NONFARM-PAYROLLS“ – die Angabe von „NONFARM“ ist ausreichend.
  2. Beim Betrieb eines Echtzeit-Handelssystems im MetaTrader 5-Terminal ist es unwahrscheinlich, dass wir alle Ereignisse für den gesamten Zeitraum herunterladen müssen. In der Regel werden aktuelle Ereignisse für den Tag und die kommende Woche heruntergeladen. Daher kann die Ladezeit im News-EA-Algorithmus vernachlässigt werden.


Schritt 2: Exportieren wir das Ereignis-Array in eine Binärdatei

Die Funktion SaveToBinary speichert gefilterte Ereignisse in einer Binärdatei. Die Anzahl der Dateien entspricht der Anzahl der in den Eingaben angegebenen Währungscodes – jede Währung wird in eine eigene Binärdatei exportiert. Der Dateiname beginnt mit dem Präfix des Währungscodes. Zum Beispiel: „USD_calendar_test_res.bin“. Auf diese Weise erhalten wir eine Eins-zu-eins-Entsprechung zwischen dem Währungscode und der Liste der Ereignisse für diesen Code.

//+------------------------------------------------------------------+
//| Save an array to a binary file                                   |
//+------------------------------------------------------------------+
void SaveToBinary(MqlCalendarValue &values[], const int &vls_size[], const string filename, const string &currencies[])
 {
  if(ArraySize(values) == 0)
   {
    Print("⚠️ Nothing to save");
    return;
   }

  Print("----------------------------------");
  int offset = 0;
  for(int i = 0; i < ArraySize(vls_size); i++)
   {
    int file_handle = FileOpen(currencies[i] + "_" + filename, FILE_WRITE | FILE_BIN | (InpUseCommonDir ? FILE_COMMON : 0));

    if(file_handle == INVALID_HANDLE)
     {
      Print("❌ FileSave failed: ", GetLastError());
      return;
     }

    FileWriteArray(file_handle, values, offset, vls_size[i]);
    FileFlush(file_handle);
    FileClose(file_handle);

    Print("✅ Saved: ", currencies[i] + "_" + filename, "   Size: ", vls_size[i] * sizeof(MqlCalendarValue), " bytes", "  (", vls_size[i], " events)");

    offset += vls_size[i];
   }
 }

Nachdem das Skript zum Exportieren von Ereignissen in Binärdateien erfolgreich ausgeführt wurde, erscheinen Meldungen wie die folgende in der Registerkarte der Experten von MetaTrader 5:

14:28:42.077    ----------------------------------
14:28:42.078    ✅ Saved: USD_calendar_test_res.bin Größe: 58240 Bytes (455 Ereignisse)
14:28:42.079    ✅ Saved: EUR_calendar_test_res.bin Größe: 7680 Bytes (60 Ereignisse)


Schritt 3: Kompilieren der Ressource in den EA

Nach dem Start des Skripts ExportCalendarForTester.mq5 in MQL5/Files/ erscheinen die Dateien USD_calendar_test_res.bin und EUR_calendar_test_res.bin. Jetzt müssen sie in den EA „eingebettet“ werden. Fügen Sie am Anfang der EA-Datei (nach der Direktive #property) eine (wenn die Datei für eine Währung verwendet wird) oder mehrere Zeilen folgender Art ein:

// Embed the binary file as a static resource at the compilation stage
#resource "\\Files\\USD_calendar_test_res.bin" as MqlCalendarValue USD_res_calendar_data[]
#resource "\\Files\\EUR_calendar_test_res.bin" as MqlCalendarValue EUR_res_calendar_data[]

Hinweis:

  1. Der Pfad zu #resource wird relativ zum Ordner MQL5/Files/ angegeben. Die Vorsilbe \\Files\ ist obligatorisch.
  2. Bei der Kompilierung des EA sollten Meldungen wie die folgenden auf der Registerkarte „Fehler“ von MetaEditor erscheinen:
  • 'USD_calendar_test_res.bin' as 'const MqlCalendarValue USD_res_calendar_data[455]' 
  • 'EUR_calendar_test_res.bin' as 'const MqlCalendarValue EUR_res_calendar_data[60]' 

Beim Starten des EA werden die Strukturen der Ereignisse, die während der Exportphase gespeichert wurden, in den Arrays USD_res_calendar_data[] und EUR_res_calendar_data[] gespeichert.


Integration in den EA: Automatische Betriebsartumschaltung

Ziel ist es, dass derselbe EA-Code sowohl in Echtzeit als auch im Tester funktioniert und die Datenquelle automatisch ausgewählt wird. Die Funktion MQLInfoInteger (MQL_TESTER) gibt „true“ zurück, wenn der EA im Strategy Tester oder Optimizer läuft.

//+------------------------------------------------------------------+
//| Initialization: select a data source                             |
//+------------------------------------------------------------------+
int OnInit()
 {
  Print("🔄 Initializing the news module:");

//--- initialize filter by currencies
  ArrayResize(currencies, 0);
  if(InpCurrencies == "")
    return INIT_FAILED;
  StringSplit(InpCurrencies, ',', currencies);
  currencies_size = ArraySize(currencies);
  for(int i = 0; i < currencies_size; i++)
    StringToUpper(currencies[i]);

//--- initialize the filter by event codes
  ArrayResize(event_codes, 0);
  if(InpEventCodes != "")
    StringSplit(InpEventCodes, ',', event_codes);
  event_codes_Size = ArraySize(event_codes);

//--- define the execution environment
  bool is_tester = MQLInfoInteger(MQL_TESTER) || MQLInfoInteger(MQL_OPTIMIZATION) || MQLInfoInteger(MQL_VISUAL_MODE);
  if(is_tester)
   {
    //--- TESTER MODE: Load from resource
    if(!LoadFromResource())
     {
      Print("❌ ERROR: Failed to load calendar from resource");
      return INIT_FAILED;
     }
    is_live_mode = false;
    Print("⚠️ Mode: TESTER (data from resource)");
   }
  else
   {
    //--- LIVE MODE: Load from API
    if(!LoadFromCalendarAPI())
     {
      int error = GetLastError();
      Print("❌ ERROR: Failed to load calendar from API - ", error);
      return INIT_FAILED;
     }
    is_live_mode = true;

    Print("⚠️ Mode: LIVE (data from API)");
   }

  return INIT_SUCCEEDED;
 }


Erklärungen für den angegebenen Code:

Die Hauptprüfung erfolgt in der Initialisierung des EAs OnInit():

   //--- define the execution environment
   bool is_tester = MQLInfoInteger(MQL_TESTER) || MQLInfoInteger(MQL_OPTIMIZATION) || MQLInfoInteger(MQL_VISUAL_MODE);
   if(is_tester)
    {
      //--- TESTER MODE: Load from resource
      if(!LoadFromResource())
       {
        ...
       }
    }
   else
    {
      //--- LIVE MODE: Load from API
      if(!LoadFromCalendarAPI())
       {
        ...
       }
    }

Erklärungen für den angegebenen Code:

Wir bestimmen, in welchem Modus das laufende MQL5-Programm (hier ist es unser EA) arbeitet. Wir sind an den folgenden Flags interessiert:

  • MQL_TESTER – Flag des gestarteten Programms, das im Tester läuft;
  • MQL_OPTIMIZATION – Flag des gestarteten Programms während der Optimierung;
  • MQL_VISUAL_MODE – Flag des gestarteten Programms im visuellen Testmodus.
Wenn mindestens eines dieser Flags aktiv ist, laden wir die Liste der Ereignisse aus der Ressource. Andernfalls laden wir die Liste der Ereignisse wie üblich mit der Kalender-API vom Server.


14:42:18.894    🔄 Initializing news module:
14:42:18.905    ✅ Events from Server Loaded: 455 events for: USD
14:42:18.914    ✅ Events from Server Loaded: 60 events for: EUR
14:42:18.914    ⚠️ Mode: LIVE (Daten aus API)



Validierung der Datenumwandlungskette: Ereignisse → Binärdatei → Kompilieren zur Ressource → Abrufen von Ereignissen aus der Ressource

Es bleibt nur noch, den Abruf der Ereignisse im Tester zu kontrollieren und sicherzustellen, dass sie vollständig mit der in Echtzeit empfangenen Liste übereinstimmen. Damit wird die Möglichkeit bestätigt, Algorithmen für das News-Trading automatisch zu testen und zu optimieren.

Fügen Sie dazu die Ausgabe von Ereignissen, die vom Server empfangen bzw. von der Ressource heruntergeladen wurden, in das Protokoll des Terminals/Strategietesters ein. Die vollständige Textdatei ImportTesterLog.txt mit den Ergebnissen des Testlaufs ist unten angefügt. Nachfolgend sehen Sie einen Ausschnitt aus der Protokolldatei – den Anfang und das Ende der Reihe von Ereignissen für jeden Währungscode.

Ergebnis eines einzelnen Laufs im Strategietester, im visuellen Modus:

16:48:22.527    EURUSD,M5: testing of Experts\ImportCalendarValidation-EA.ex5 from 2026.01.01 00:00 to 2026.03.07 00:00 started with inputs:
16:48:22.527      InpCurrencies=USD,EUR
16:48:22.527      InpDateFrom=1735689600
16:48:22.527      InpDateTo=1767225600
16:48:22.527      InpEventCodes=
16:48:22.527      InpMinImportance=3
16:48:22.546   🔄 Initializing news module:
16:48:22.546   ✅ Events from Resource Loaded: 455 events for: USD
16:48:22.546   ✅ Event #0
16:48:22.546   ID:                    230215
16:48:22.546   Event ID:          840140001
16:48:22.546   Time:               2025.01.02 16:30:00
16:48:22.546   Impact:            CALENDAR_IMPACT_POSITIVE
16:48:22.546   Revision:          0
16:48:22.546   Actual:             211.0
16:48:22.546   Revised:           220.0
16:48:22.546   Forecast:          219.0
16:48:22.546   Previous:          219.0
16:48:22.546   ✅ Event #1
16:48:22.546   ID:                   230641
16:48:22.546   Event ID:          840500001
16:48:22.546   Time:               2025.01.02 17:45:00
16:48:22.546   Impact:            CALENDAR_IMPACT_NEGATIVE
16:48:22.546   Revision:          3
16:48:22.546   Actual:             49.4
16:48:22.546   Forecast:          50.5
16:48:22.546   Previous:          49.7
 ...
16:48:22.553   ✅ Event #453
16:48:22.553   ID:                   274419
16:48:22.553   Event ID:          840510001
16:48:22.553   Time:               2025.12.30 17:45:00
16:48:22.553   Impact:            CALENDAR_IMPACT_POSITIVE
16:48:22.553   Revision:          0
16:48:22.553   Actual:             43.5
16:48:22.553   Forecast:          42.4
16:48:22.553   Previous:          36.3
16:48:22.553   ✅ Event #454
16:48:22.553   ID:                   274484
16:48:22.553   Event ID:          840140001
16:48:22.553   Time:               2025.12.31 16:30:00
16:48:22.553   Impact:            CALENDAR_IMPACT_POSITIVE
16:48:22.553   Revision:          0
16:48:22.553   Actual:             199.0
16:48:22.553   Revised:           215.0
16:48:22.553   Forecast:          227.0
16:48:22.553   Previous:          214.0
...
16:48:22.553   ✅ Events from Resource Loaded: 60 events for: EUR
16:48:22.553   ✅ Event #0
16:48:22.553   ID:                   231322
16:48:22.553   Event ID:         999030013
16:48:22.553   Time:              2025.01.07 13:00:00
16:48:22.553   Impact:           CALENDAR_IMPACT_NA
16:48:22.553   Revision:         1
16:48:22.553   Actual:            2.4
16:48:22.553   Forecast:         2.4
16:48:22.553   Previous:         2.2
16:48:22.553   ✅ Event #1
16:48:22.553   ID:                  187384
16:48:22.553   Event ID:         999040007
16:48:22.553   Time:              2025.01.08 13:00:00
16:48:22.553   Impact:           CALENDAR_IMPACT_POSITIVE
16:48:22.553   Revision:         0
16:48:22.553   Actual:            21.0
16:48:22.553   Revised:          17.8
16:48:22.553   Forecast:         15.7
16:48:22.553   Previous:         17.7
...
16:48:22.555   ✅ Event #58
16:48:22.555   ID:                  204746
16:48:22.555   Event ID:         999010006
16:48:22.555   Time:              2025.12.18 16:15:00
16:48:22.555   Impact:           CALENDAR_IMPACT_NA
16:48:22.555   Revision:         0
16:48:22.555   Actual:            2.0
16:48:22.555   Previous:         2.0
16:48:22.555   ✅ Event #59
16:48:22.555   ID:                  204762
16:48:22.555   Event ID:         999010007
16:48:22.555   Time:              2025.12.18 16:15:00
16:48:22.555   Impact:           CALENDAR_IMPACT_NA
16:48:22.555   Revision:         0
16:48:22.555   Actual:            2.15
16:48:22.555   Previous:         2.15
16:48:22.555   ⚠️ Mode: TESTER (Daten aus der Ressource)

Es ist klar, dass alle Ereignis-Arrays in etwa 10 ms von der Ressource geladen werden, was nicht überraschend ist – die Ressource ist in den EA-Code integriert und wird zusammen mit ihm geladen, wenn der Test beginnt.

Der gleiche Durchlauf (Start) eines Expert Advisors im Live-Modus im MetaTrader 5-Terminal erzeugt eine ähnliche Protokolldatei mit einer Liste der geladenen Ereignisse. Die vollständige Datei LoadLiveLog.txt mit den Ergebnissen des Live-Ladens ist dem Artikel beigefügt. Nachfolgend sehen Sie einen Ausschnitt aus der Protokolldatei – den Anfang und das Ende der Reihe von Ereignissen für jeden Währungscode.

Ergebnisse des Live-Downloads von Ereignissen vom Server:

17:27:25.250    🔄 Initializing news module:
17:27:25.258    ✅ Received values BY CURRENCY "USD": 3589 → Davon gefiltert: 455
17:27:25.258    ✅ Events from Server Loaded: 455 events for: USD
17:27:25.258    ✅ Event #0
17:27:25.258    ID:                   230215
17:27:25.258    Event ID:          840140001
17:27:25.258    Time:               2025.01.02 16:30:00
17:27:25.258    Impact:            CALENDAR_IMPACT_POSITIVE
17:27:25.258    Revision:          0
17:27:25.258    Actual:             211.0
17:27:25.258    Revised:           220.0
17:27:25.258    Forecast:          219.0
17:27:25.258    Previous:          219.0
17:27:25.258    ✅ Event #1
17:27:25.258    ID:                   230641
17:27:25.258    Event ID:          840500001
17:27:25.258    Time:               2025.01.02 17:45:00
17:27:25.258    Impact:            CALENDAR_IMPACT_NEGATIVE
17:27:25.258    Revision:          3
17:27:25.258    Actual:             49.4
17:27:25.258    Forecast:         50.5
17:27:25.258    Previous:          49.7
...
17:27:25.288    ✅ Event #453
17:27:25.288    ID:                   274419
17:27:25.288    Event ID:          840510001
17:27:25.288    Time:               2025.12.30 17:45:00
17:27:25.288    Impact:            CALENDAR_IMPACT_POSITIVE
17:27:25.288    Revision:          0
17:27:25.288    Actual:             43.5
17:27:25.288    Forecast:          42.4
17:27:25.288    Previous:          36.3
17:27:25.288    ✅ Event #454
17:27:25.288    ID:                    274484
17:27:25.288    Event ID:          840140001
17:27:25.288    Time:               2025.12.31 16:30:00
17:27:25.288    Impact:            CALENDAR_IMPACT_POSITIVE
17:27:25.288    Revision:          0
17:27:25.288    Actual:             199.0
17:27:25.288    Revised:           215.0
17:27:25.288    Forecast:          227.0
17:27:25.288    Previous:          214.0

17:27:25.301    ✅ Received values BY CURRENCY "EUR": 3116 → Of these, filtered: 0
17:27:25.301    ✅ Events from Server Loaded: 60 events for: EUR
17:27:25.301    ✅ Event #0
17:27:25.301    ID:                   231322
17:27:25.301    Event ID:          999030013
17:27:25.301    Time:               2025.01.07 13:00:00
17:27:25.301    Impact:            CALENDAR_IMPACT_NA
17:27:25.301    Revision:          1
17:27:25.301    Actual:             2.4
17:27:25.301    Forecast:          2.4
17:27:25.301    Previous:          2.2
17:27:25.301    ✅ Event #1
17:27:25.301    ID:                   187384
17:27:25.301    Event ID:          999040007
17:27:25.301    Time:               2025.01.08 13:00:00
17:27:25.301    Impact:            CALENDAR_IMPACT_POSITIVE
17:27:25.301    Revision:          0
17:27:25.301    Actual:             21.0
17:27:25.301    Revised:           17.8
17:27:25.301    Forecast:         15.7
17:27:25.301    Previous:          17.7
...
17:27:25.303    ✅ Event #58
17:27:25.303    ID:                   204746
17:27:25.303    Event ID:          999010006
17:27:25.303    Time:               2025.12.18 16:15:00
17:27:25.303    Impact:            CALENDAR_IMPACT_NA
17:27:25.303    Revision:          0
17:27:25.303    Actual:             2.0
17:27:25.303    Previous:          2.0
17:27:25.303    ✅ Event #59
17:27:25.303    ID:                   204762
17:27:25.303    Event ID:          999010007
17:27:25.303    Time:               2025.12.18 16:15:00
17:27:25.303    Impact:            CALENDAR_IMPACT_NA
17:27:25.303    Revision:          0
17:27:25.303    Actual:            2.15
17:27:25.303    Previous:         2.15
17:27:25.303    ⚠️ Mode: LIVE (Daten aus API)

Ein vollständiger Download mit Filterung und Protokollierung dauert etwa 50 ms – immer noch schnell genug, um die Download-/Filterungszeit von Ereignissen zu ignorieren. Der Vergleich der beiden Protokolldateien zeigt, dass die Liste der Ereignisse sowohl hinsichtlich der Anzahl der Ereignisse als auch der Werte der Felder völlig identisch ist. Validierung der Datenumwandlungskette erfolgreich abgeschlossen.

Analyse von typischen Fehlern und falschen Erwartungen

Sehen wir uns die sechs häufigsten Fehler an, die Entwickler bei der Integration des MQL5-Wirtschaftskalenders machen. Jeder dieser Fehler wurde in der Praxis bestätigt und kann für manche zum Verlust ihres Guthabens führen.

1. „Der Kalender sagt Bewegung voraus

Falsche Erwartung:

Wenn Nachrichten mit GROSSER Wichtigkeit veröffentlicht werden und das tatsächliche Ergebnis erheblich von der Prognose abweicht, wird sich der Kurs garantiert in Richtung der Abweichung bewegen. Kaufen Sie einfach, wenn der tatsächliche Wert der Basiswährung über dem prognostizierten Wert liegt.

Die Realität:

Der Wirtschaftskalender ist eine Datenquelle, kein Generator für Handelssignale. Er sagt Ihnen, was passiert ist, aber nicht, wie der Markt reagieren wird. Warum könnte der Preis der „offensichtlichen“ Logik widersprechen?

  • Der Markt hat die Erwartungen schon Tage vor der Veröffentlichung eingepreist. Genau in dem Moment, in dem die Nachricht veröffentlicht wird, findet der „Verkauf der Nachricht“ statt.
  • Starke Inflationsdaten während einer Phase der Straffung der Zentralbankpolitik können die Währung stärken, aber während einer Phase der Lockerung können sie einen Ausverkauf aufgrund von Befürchtungen einer Überhitzung auslösen.
  • Die Nachrichten mögen gut sein, aber wenn der vorherige Wert nach unten korrigiert wird, wird das Gesamtsignal mehrdeutig.
  • Die gleichzeitige Veröffentlichung von Daten für mehrere Währungen führt zu Crossover-Effekten, die nicht linear interpretiert werden können.

Der richtige Ansatz ist, den Kalender als Volatilitätsfilter zu verwenden, nicht als Einstiegsauslöser:

//--- instead of
if(actual > forecast)
 OrderSend(...);

//--- use:
if(IsHighImpactNewsComingSoon(30)) 
 {
   //--- reduce the position size or temporarily suspend trading
   ReduceRiskExposure();
 }
Goldene Regel: Der Kalender beantwortet die Frage „Wann ist mit erhöhter Volatilität zu rechnen?“, nicht „In welche Richtung sollte man handeln?“.


2. „Alle Ereignisse mit HOHER Wichtigkeit sind gleich wichtig

Falsche Erwartung:

Das Feld importance == CALENDAR_IMPORTANCE_HIGH bedeutet, dass das Ereignis den Markt garantiert um mehr als 50 Pips bewegen wird. Wir können alle diese Nachrichten mit demselben Algorithmus handeln.

Die Realität:

Bestimmte Werte in der ENUM_CALENDAR_EVENT_IMPORTANCE sind eine subjektive Einschätzung der Kalenderredakteure und kein quantitativer Maßstab für die Marktwirkung. Nehmen wir zum Beispiel zwei Ereignisse, die als HOCH bezeichnet werden, aber eine unterschiedliche Marktbedeutung haben.

Das erste Ereignis, die Non-Farm Payrolls (USA), führt zu einer Volatilitätsreaktion von 80-150 Punkten, da es sich um einen wichtigen Beschäftigungsindikator handelt, der die Fed-Politik beeinflusst. Das zweite Ereignis, der PMI des verarbeitenden Gewerbes (Eurozone), führt typischerweise zu einer Bewegung von 10 – 30 Punkten, da es sich um einen eng gefassten Indikator handelt, der für die EZB zweitrangig ist.

Der richtige Ansatz besteht darin, die Wichtigkeitsfilterung durch eine Liste von Prioritätsereigniscodes zu ergänzen:

//--- a list of events that are really worth reacting to
bool IsGoodEvent(const string event_code)
 {
   static const string tier1_codes[] = 
   {
      "NONFARM", "CPI", "GDP", "RATE", "FOMC", "ECB_RATE", 
      "RETAIL_SALES", "UNEMPLOYMENT", "PMI_MANUFACTURING"
   };
   
   for(int i = 0; i < ArraySize(tier1_codes); i++)
      if(StringFind(event_code, tier1_codes[i]) != -1)
         return true;
   return false;
 }

//--- use in filter
if(event.importance == CALENDAR_IMPORTANCE_HIGH && IsGoodEvent(event.event_code))
 {
   //--- handle only truly significant news
 }

Empfehlung:

Erstellen Sie Ihr eigenes Ereignis-Ranking auf der Grundlage historischer Volatilitätsanalysen – zuverlässiger als jedes vorgegebene Label.


3. „Verwendet wird TimeLocal() anstelle von TimeTradeServer()

Falsche Erwartung:

Ereigniszeit in der Struktur MqlCalendarValue::time ist in meiner lokalen Zeitzone (oder UTC) angegeben, sodass ich sie mit TimeLocal() oder TimeGMT() vergleichen kann.

Die Realität:

Alle Wirtschaftskalenderfunktionen geben die Zeit in der Zeitzone des Handelsservers zurück (TimeTradeServer()). Dies ist eine architektonische Entscheidung, die eine manuelle Konvertierung überflüssig macht, aber vom Entwickler Disziplin verlangt.

Dieser Fehler hat schwerwiegende Folgen: Wenn sich Ihr Server in der EET-Zeitzone (UTC+2) befindet und Sie TimeLocal() verwenden (z. B. MSK, UTC+3), werden Sie Ereignisse mit einem Versatz von 1 Stunde prüfen. Der EA kann die Nachricht verpassen oder umgekehrt erst im Nachhinein auf sie reagieren.

Der richtige Ansatz ist, immer den TimeTradeServer() für alle Zeitvergleiche zu verwenden:

//--- WRONG — risk of desynchronization
datetime now = TimeLocal();
if(event.time - now < 1800)
 { ... }

//--- RIGHT — guaranteed synchronization
datetime now = TimeTradeServer();
if(event.time - now < 1800)
 { ... }

//--- in the tester, TimeTradeServer() returns the model time, so the logic works identically to live

Empfehlung:

Fügen Sie die Ausgabe von TimeToString(TimeTradeServer(), TIME_MINUTES) zum Debug-Protokoll hinzu und vergleichen Sie sie mit der Ereigniszeit – sie sollten ohne Umrechnung übereinstimmen.


4. „Vergessen, dass Ereignisfelder leer sein können

Falsche Erwartung:

Die Felder actual_value, forecast_value und andere Felder enthalten immer korrekte numerische Werte. Man kann sie bedenkenlos durch 1 000 000 teilen und vergleichen.

Die Realität:

Der Dokumentation zufolge sind die numerischen Felder der Struktur MqlCalendarValue Werte multipliziert mit einer Million oder der Konstante LONG_MIN, wenn kein Wert angegeben ist. Was passiert, wenn wir die Prüfung ignorieren?

//--- error
double actual = values[i].actual_value / 1000000.0; // if actual_value == LONG_MIN, result: -9223372036.854776

if(actual > forecast) // comparison with a "garbage" number causes a false alarm
  OpenBuy();

Der richtige Ansatz ist die Verwendung der integrierten Methoden der Struktur MqlCalendarValue zu verwenden, um Werte sicher zu erhalten:

//--- the right approach is built-in methods
if(values[i].HasActualValue() && values[i].HasForecastValue())
 {
  double actual = values[i].GetActualValue();
  double forecast = values[i].GetForecastValue();

  if(!MathIsNaN(actual) && !MathIsNaN(forecast))
   {
    double deviation = actual - forecast;
    // ... analysis logic
   }
 }
//+------------------------------------------------------------------+

Hinweis:

GetActualValue(), GetForecastValue() und andere Funktionen geben NaN zurück, wenn es keine Daten gibt. Prüfen Sie das Ergebnis immer mit MathIsValidNumber() oder MathIsNaN(), bevor Sie es in Berechnungen verwenden.


5. „Die Kalender-API-Funktionen bei jedem OnTick() aufrufen

Falsche Erwartung:

„Um immer aktuelle Daten zu haben, sollte man CalendarValueHistory() bei jedem Tick aufrufen. Auf diese Weise wird sichergestellt, dass der EA keine aktuellen Nachrichten verpasst“.

Die Realität:

Die Kalenderfunktionen laufen über einen Remote-Server, mit dem MetaTrader 5 verbunden ist, und unterliegen strengen Begrenzungen der Abfragefrequenz. Zu häufige Aufrufe von OnTick() (kann Dutzende Male pro Sekunde passieren) führen zu Problemen:

  • Error 5204 (ERR_CALENDAR_TOO_MANY_REQUESTS) – vorübergehende Blockierung des Zugriffs auf den Kalender.
  • Ausführungsverzögerungen – eine Netzanfrage im Handelszyklus erhöht das Slippage.
  • Übermäßiger Datenverkehr – wiederholtes Herunterladen der gleichen Daten.
Der richtige Ansatz ist die Verwendung von Caching und inkrementeller Aktualisierung:
//--- global variables
MqlCalendarValue calendar_cache[];
bool cache_initialized = false;
long last_change_id = 0;

//+------------------------------------------------------------------+
int OnInit()
 {
//--- initial history loading (once at startup)
  datetime from = TimeCurrent() - 7 * 86400;
  datetime to = TimeCurrent() + 30 * 86400;

  if(CalendarValueHistory(calendar_cache, from, to, "USD"))
   {
    cache_initialized = true;
   }

//--- set a timer for periodic updates (no more than 5-10 minutes)
  EventSetTimer(300);
  return INIT_SUCCEEDED;
 }
//+------------------------------------------------------------------+
void OnTimer()
 {
  if(!cache_initialized)
    return;

  MqlCalendarValue updates[];

//--- only request changes since the last update
  if(CalendarValueLast(last_change_id, updates, "USD") > 0)
   {
    if(ArraySize(updates) > 0)
     {
      //--- merge new data with the cache
      MergeUpdates(calendar_cache, updates);
      last_change_id = updates[ArraySize(updates) - 1].change_id;
     }
   }
 }
//+------------------------------------------------------------------+
void OnTick()
 {
//--- work only with local cache - no network delays
  if(cache_initialized)
    ProcessNewsSignals(calendar_cache);
 }

Empfehlung:

OnTick() sollte nur mit lokalen Daten funktionieren. Netzwerkanfragen – nur in OnInit(), OnTimer() oder durch Nutzerereignis.


6. „Vergessen, den Experten neu zu kompilieren, um ihn auf neue Ereignisse zu testen

Falsche Erwartung:

„Ich habe die Ressourcendatei calendar_test_res.bin im Ordner Files aktualisiert, sodass der Tester nun automatisch die neuen Ereignisse sieht. Es besteht keine Notwendigkeit, den EA neu zu kompilieren.“

Die Realität:

Die Direktive #resource bettet zur Kompilierungszeit Daten in die ausführbare .ex5-Datei ein. Änderungen an der externen .bin-Datei werden nicht dynamisch übernommen – der EA verwendet weiterhin die Version der Ressource, die zum Zeitpunkt der Erstellung kompiliert wurde.

Fehlersymptome:

  • Das Prüfprotokoll enthält keine Ereignisse, die der Ressource nach der letzten Kompilierung hinzugefügt wurden.
  • Die Strategie „sieht“ wichtige Nachrichten nicht, obwohl sie in der Datei enthalten sind.
  • Die Testergebnisse sind anders als erwartet, obwohl der Code logisch korrekt ist.

Der richtige Ansatz ist, den EA nach der Aktualisierung der Ressource immer neu zu kompilieren:

//--- at the beginning of the EA file
#resource "\\Files\\USD_calendar_test_res.bin" as MqlCalendarValue USD_res_calendar_data[]

//--- after USD_calendar_test_res.bin update:
// 1. Save changes to the resource file.
// 2. Press F7 in MetaEditor (or Compile in the menu).
// 3. Make sure there are no errors in the compilation log.
// 4. Run the test again.

Empfehlung:

Fügen Sie die Ausgabe der Ressourcenversion (z. B. Hash oder Datum der letzten Aktualisierung) dem Kompilierungsprotokoll hinzu, um die Relevanz der Daten in .ex5 visuell zu überwachen.

Bemerkung:

Die in diesem Abschnitt diskutierten Fehler sind nicht auf mangelnde Kenntnisse der MQL5-Syntax zurückzuführen, sondern auf ein vereinfachtes Verständnis der Natur von Wirtschaftsdaten (Indikatoren). Wer lernt, diese sechs Fehler zu vermeiden, erhält nicht nur einen funktionierenden Code, sondern ein stabiles Handelssystem, das in der Lage ist, sich an Marktveränderungen und die Anforderungen der Wirtschaftsregulierer anzupassen. Es ist dieser Ansatz, der einen Amateur von einem Profi im algorithmischen Handel unterscheidet.


Schlussfolgerung

Der Wirtschaftskalender im MetaTrader 5-Terminal ist ein leistungsfähiges Instrument. Aber wie bei jedem anderen Werkzeug auch, muss man den Kontext verstehen, diszipliniert vorgehen und die Grenzen der Plattform berücksichtigen.

Wir haben uns eine konkrete Struktur für die Implementierung und Nutzung der News-API für den EA in MetaTrader 5 vorgestellt. Der Artikel enthält eine Reihe praktischer Elemente, die zusammengenommen das manuelle News-Trading in ein reproduzierbares Modul verwandeln:

  • Verständnis der Kalender-API (Ereignis vs. Wert, MqlCalendarEvent/MqlCalendarValue-Strukturen, Serverzeit);
  • sichere Lade- und Aktualisierungsmethoden (CalendarValueHistory für das erste Laden, CalendarValueLast + change_id für inkrementelle Aktualisierungen);
  • Umgang mit typischen Fehlern und Einhaltung von Anfragegrenzen;
  • mehrstufige Filterung nach Währung, Wichtigkeit, Ereigniscode und Zeitfenstern, um Rauschen zu reduzieren und 3-5 signifikante Ereignisse übrig zu lassen;
  • einen Mechanismus für den Export gefilterter Daten in eine binäre Ressource und den automatischen Wechsel der Datenquelle „Echtzeit“ ↔ „Strategietester“, wodurch ein identisches Verhalten in Echtzeit und beim Backtesting gewährleistet wird.

Kriterien für die Einsatzbereitschaft des Moduls: Ereignisse für den angegebenen Zeitraum werden erfolgreich geladen; inkrementelle Aktualisierungen funktionieren über change_id ohne Überschreitung von Grenzwerten; im Tester liest EA die Ressource und produziert die gleichen Entscheidungen wie in Live mit identischen Eingabedaten.

Empfohlene nächste Schritte: Implementierung des Exporters, die .bin-Datei per #resource fest einzubinden, Test der OnInit/OnTimer-Aktualisierungslogik und Durchführung kontrollierter Backtests (z. B. Szenario „30 Minuten lang nicht handeln / nach 60 Minuten wieder aufnehmen“).

So können Sie von Hypothesen zu einem testbaren, skalierbaren News-Tradingssystem übergehen. Bei der Entwicklung von Nachrichten-EAs geht es nicht nur um Programmierung, sondern auch darum, eine Brücke zwischen makroökonomischer Theorie und Marktpraxis zu schlagen. 

Empfohlene Ressourcen für ein eingehendes Studium der MetaTrader 5 Kalender-API-Funktionen und deren Verwendung im News-Trading:


Liste der dem Artikel beigefügten Dateien:

Dateiname Beschreibung
CalendarEventMonitor-EA.mq5  Testskript mit dem Code zum Testen der Funktion zur Aktualisierung der Kalenderdaten
ExportCalendarForTester-S.mq5 Der Code eines Testskripts zur Überprüfung des Exports von Ereignissen mit bestimmten Filtern in eine Binärdatei
GetTodayEvents-S.mq5 Der Code eines Testskripts zur Überprüfung des Abrufs von Ereignissen für den aktuellen Tag
ImportCalendarValidation-EA.mq5 Der Code eines Test-EA zur Überprüfung des Nachrichtenabrufs im Strategietester aus einer Ressource
ImportTesterLog.txt  Ergebnisse eines einzelnen Laufs des EA ImportCalendarValidation-EA.mq5 im Tester im visuellen Modus
LoadLiveLog.txt  Ergebnisse des Live-Ladens von Ereignissen durch den EA ImportCalendarValidation-EA.mq5

Übersetzt aus dem Russischen von MetaQuotes Ltd.
Originalartikel: https://www.mql5.com/ru/articles/22196

Letzte Kommentare | Zur Diskussion im Händlerforum (8)
Stanislav Korotky
Stanislav Korotky | 1 Mai 2026 in 13:32

Geschrieben von:

Если брокер учитывает переход на летнее/зимнее время — календарь автоматически подстраивается под этот переход

Korrigieren Sie mich, wenn sich in der MQL5-API etwas geändert hat, aber früher wurde der Kalender an die aktuelle Zeitzone unter Berücksichtigung der Sommerzeit angepasst, d.h. im Sommer geben Funktionsanfragen Zeitstempel z.B. in der UTC+3-Zone zurück, während eine Anfrage desselben Verlaufsabschnitts im Winter Ereignisse mit Stempeln in der UTC+2-Zone erhält, wenn der Server auf Sommerzeit umschaltet und umgekehrt. Um also einen Kalenderverlauf für sechs Monate oder länger hochzuladen, müssen Sie den Verlauf der Zeitzonenumstellung des Brokers analysieren. Weitere Einzelheiten in der Codobase.

Auch der Ansatz, den Kalender-Cache als Ressource zu verknüpfen, erscheint wenig praktikabel und provoziert vor allem die im Artikel selbst erwähnten Fehler (z. B. vergessen Sie nicht, den EA neu zu kompilieren). Warum nicht den Kalender-Cache im Input-Parameter als Datei aus dem Common-Ordner für alle Agenten verfügbar machen?

Maxim Kuznetsov
Maxim Kuznetsov | 3 Mai 2026 in 17:44
Korrigieren Sie mich, wenn sich in der MQL5-API etwas geändert hat, aber früher wurde der Kalender unter Berücksichtigung der Sommerzeit an die aktuelle Zeitzone angepasst, d. h. im Sommer geben Funktionsanfragen beispielsweise Zeitstempel in der UTC+3-Zone zurück, während eine Anfrage für denselben Verlaufsabschnitt im Winter Ereignisse mit Bezeichnungen in der UTC+2-Zone erhält, wenn der Server auf Sommerzeit umschaltet und umgekehrt. Um den Kalenderverlauf für sechs Monate oder länger genau hochzuladen, muss also die Übertragungshistorie des часового пояса Maklers. Mehr Details in der Codobase.

und warum werden die Kurse (im Allgemeinen alle Zeitstempel) nicht in UTC gehalten?

warum haben die Ticks der Händler unterschiedliche numerische Darstellungen ein und derselben Zeitreferenz?

eine philosophische Frage :-) es ist die Art und Weise, wie es gemacht wird, obwohl es falsch ist und Probleme vor Ort verursacht.


ZЫ. "Historische Neuigkeiten" sollte man also besser aus anderen Quellen entnehmen.

Und man muss nie "die Geschichte der Uhrenumrechnung analysieren" - es ist alles da, es ist eine Funktion des Betriebssystems oder der Systembibliotheken. Google tzdata

wie viele Fahrräder hergestellt werden können, und noch dazu krumme

fxsaber
fxsaber | 3 Mai 2026 in 19:30
Stanislav Korotky #:

Bisher wurde der Kalender unter Berücksichtigung der Sommerzeit an die aktuelle Zeitzone angepasst, d.h. im Sommer werden bei Funktionsanfragen z.B. Zeitstempel in der Zone UTC+3 zurückgegeben, und eine Anfrage für denselben Abschnitt der Historie im Winter liefert Ereignisse mit Stempeln in der Zone UTC+2, wenn der Server auf Sommerzeit und zurück umschaltet.

Ich habe die gleiche Idee.
Stanislav Korotky
Stanislav Korotky | 4 Mai 2026 in 16:09
Maxim Kuznetsov #:


ZЫ. "Historische Nachrichten" sollte man also besser aus anderen Quellen entnehmen.

Und man sollte niemals selbst "die Geschichte der Uhrenübersetzung analysieren" - es ist alles da, es ist eine Funktion des Betriebssystems oder der Systembibliotheken. Google tzdata

wie viele Fahrräder hergestellt werden können, und dazu noch krumme


Andere Quellen werden das Problem nicht lösen, denn es liegt in der Art und Weise begraben, wie Zitate in MT5 gespeichert werden.

Zeigen Sie zuerst Ihr gerades Fahrrad, und geben Sie dann Ratschläge.

ilex044
ilex044 | 11 Mai 2026 in 10:09
MetaQuotes:

Die Hauptprobleme des modernen Nachrichtenhändlers sind das fragmentierte Instrumentarium und das Fehlen eines systematischen, algorithmischen Handelsablaufs. Es ist ziemlich schwierig, seine Aufmerksamkeit zwischen dem Internetbrowser (Surfen auf Nachrichtenseiten) und dem Handelsterminal aufzuteilen, während man Geschäfte tätigt.

Der Arbeitsablauf eines Nachrichtenhändlers sieht folgendermaßen aus: Schnelles Öffnen des Nachrichtenkalenders in Ihrem Webbrowser und Prüfen auf Änderungen bei den Ereignissen → Schnelles Bewerten der anstehenden Ereignisse und Entscheiden, was und wie gehandelt werden soll → Zum MetaTrader 5-Terminal gehen - entweder Pending Orders platzieren oder am Terminal sitzen und auf die Nachrichtenmeldung warten, um eine Entscheidung zu treffen. In diesem Szenario geht dem Händler oft der Kontext verloren, was zu Verzögerungen bei der Reaktion auf Nachrichten führt, was wiederum Verluste zur Folge hat.

Möchten Sie, dass der Nachrichtenhandel wie ein technisches Problem funktioniert - mit klaren Regeln, wiederholbaren Ergebnissen und automatischen Tests? Der Zweck dieses Artikels ist es, die funktionierende Architektur einer Nachrichtenschicht für MetaTrader 5 zu demonstrieren: eine einzige Datenquelle, die korrekte Verwendung der Kalender-API, ein Filter- und Zwischenspeichermechanismus, der Export historischer Ereignisse in eine Ressource für den Tester und das automatische Umschalten zwischen Live und Tester - so dass derselbe Code sowohl in Echtzeit als auch bei historischen Daten deterministische Ergebnisse liefert.

Ja, das ist ein echter Schmerzpunkt für den Nachrichtenhandel.

Das Umschalten zwischen Browser, Kalender und Terminal unterbricht den Fokus und verlangsamt die Reaktionen erheblich. Ein einheitlicher Workflow oder ein System, das die Nachrichten direkt in die Handelsumgebung zieht, würde die Ausführung definitiv schneller und konsistenter machen.

Die Idee, dies in ein strukturiertes, wiederholbares "ingenieurmäßiges" Setup mit Tests und Automatisierung umzuwandeln, macht tatsächlich viel Sinn, um emotionale oder verzögerte Entscheidungen zu vermeiden.

Arbitragehandel im Forex-Markt: Ein Matrix-Handelssystem mit Rückkehr zum fairen Wert mit Risikokontrolle Arbitragehandel im Forex-Markt: Ein Matrix-Handelssystem mit Rückkehr zum fairen Wert mit Risikokontrolle
Der Artikel enthält eine detaillierte Beschreibung des Berechnungsalgorithmus für Cross-Rates, eine Visualisierung der Ungleichgewichtsmatrix und Empfehlungen zur optimalen Einstellung der Parameter MinDiscrepancy und MaxRisk für einen effizienten Handel. Das System berechnet automatisch den „fairen Wert“ jedes Währungspaares anhand der Cross-Rates und generiert Kaufsignale im Falle negativer Abweichungen und Verkaufssignale im Falle positiver Abweichungen.
Kamelalgorithmus (CA) Kamelalgorithmus (CA)
Der 2016 entwickelte Kamelalgorithmus simuliert das Verhalten von Kamelen in der Wüste, um Optimierungsprobleme unter Berücksichtigung von Temperatur, Versorgung und Ausdauer zu lösen. In diesem Artikel wird auch eine modifizierte Version des Algorithmus (CAm) mit wesentlichen Verbesserungen vorgestellt: die Verwendung einer Normalverteilung bei der Generierung von Lösungen und die Optimierung der Parameter für den Oaseneffekt.
Eine alternative Log-datei mit der Verwendung der HTML und CSS Eine alternative Log-datei mit der Verwendung der HTML und CSS
In diesem Artikel werden wir eine sehr einfache, aber leistungsfähige Bibliothek zur Erstellung der HTML-Dateien schreiben, dabei lernen wir auch, wie man eine ihre Darstellung einstellen kann (nach seinem Geschmack) und sehen wir, wie man es leicht in seinem Expert Advisor oder Skript hinzufügen oder verwenden kann.
Einsatz spieltheoretischer Ansätze in Handelsalgorithmen Einsatz spieltheoretischer Ansätze in Handelsalgorithmen
Wir entwickeln einen adaptiven, selbstlernenden Expert Advisor für den algorithmischen Handel, der auf Deep-Q-Learning (DQN) mit mehrdimensionaler kausaler Inferenz basiert. Der EA kann erfolgreich mit 7 Währungspaaren gleichzeitig handeln, und die Agenten verschiedener Paare tauschen untereinander Informationen aus.