Automatisierung·

So baust du einen News-Filter in deinen MT4-Expert-Advisor ein

MetaTrader 4 bietet Expert Advisors keinen Wirtschaftskalender. Mit dieser Anleitung rüstest du ihn in zehn Minuten nach: eine kostenlose Kalender-API, eine Include-Datei und eine Funktion, die du vor jedem Einstieg aufrufst. Vollständiger MQL4-Code, kein API-Schlüssel.

Ein Expert Advisor, der mitten in den US-Arbeitsmarktbericht (Non-Farm Payrolls) oder einen Zinsentscheid hinein handelt, folgt nicht mehr seiner Strategie: Er wirft eine Münze, und das bei breitem Spread. Ein News-Filter verhindert das. Er setzt neue Einstiege einige Minuten vor und nach den Veröffentlichungen aus, die den Markt bewegen, und lässt die Strategie den Rest der Zeit ungestört arbeiten.

MetaTrader 5 hat einen Kalender eingebaut. MetaTrader 4 nicht, der EA muss sich die News also selbst besorgen. Diese Anleitung nutzt die kostenlose Wirtschaftskalender-API von ShowMyTrades: kein Konto, kein API-Schlüssel, eine einzige URL, die du im Terminal freigibst, und eine Include-Datei, die in jeden EA passt.

🧭 Das baust du

alle 15 Minuten              bei jedem Tick
┌──────────────────────┐     ┌─────────────────────────────────┐
│ News der nächsten    │ ──► │ Liegt eine Veröffentlichung     │
│ 2 Tage herunterladen │     │ im Fenster [-30 Min., +30 Min.] │
│ (CSV, eine Anfrage)  │     │ um den aktuellen Zeitpunkt?     │
└──────────────────────┘     │   ja   → keine neuen Einstiege  │
                             │   nein → Strategie ausführen    │
                             └─────────────────────────────────┘
EntscheidungStandard im CodeWarum
Welche WährungenEUR,USDDie beiden Seiten des Paars, das du handelst. Leer heißt: alle neun Hauptwährungen
Welche Wichtigkeithighhigh,medium für einen vorsichtigeren Filter
Pause davor30 MinutenVor der Zahl weiten sich die Spreads, und die Liquidität dünnt aus
Pause danach30 MinutenIn diese Zeit fallen die erste Reaktion und die Gegenbewegung
Download-Intervall15 MinutenDer Kalender ändert sich nur wenige Male am Tag, und die API speichert ohnehin 15 Minuten zwischen

🔑 Schritt 1: Die Adresse in MetaTrader freigeben

WebRequest(), die MQL4-Funktion für Downloads aus dem Internet, spricht nur mit Adressen, die du freigegeben hast. Öffne im Terminal Extras → Optionen → Expert Advisors (Tools → Options → Expert Advisors), setze den Haken bei WebRequest für die aufgeführten URLs erlauben (Allow WebRequest for listed URL) und trage ein:

https://showmytrades.com

Fehlt dieser Schritt, liefert der Download -1 mit Fehler 4060. Die Include-Datei schreibt dann einen Hinweis in den Reiter „Experten“ (Experts), damit du nicht rätseln musst, warum der Filter stumm bleibt.

📄 Schritt 2: Die Include-Datei speichern

Speichere den folgenden Code als SMT_NewsFilter.mqh im Ordner MQL4\Include deines Terminals. Den Ordner öffnest du über Datei → Dateiordner öffnen (File → Open Data Folder). Es ist dieselbe Datei wie in der API-Referenz, und sie lässt sich auch in MQL5 ohne jede Änderung kompilieren.

SMT_NewsFilter.mqh
//+------------------------------------------------------------------+
//| SMT_NewsFilter.mqh - News-Filter für MT4 und MT5                 |
//| Daten: https://showmytrades.com/api/public/calendar/v1 (gratis)  |
//| Doku: https://showmytrades.com/docs/api/economic-calendar        |
//| Setup: Extras > Optionen > Expert Advisors > WebRequest für      |
//|        https://showmytrades.com erlauben                         |
//+------------------------------------------------------------------+
#property strict

input string SMT_Currencies     = "EUR,USD"; // News: Währungen (kommagetrennt, leer = alle)
input string SMT_Impact         = "high";    // News: Wichtigkeit (high | high,medium | all)
input int    SMT_MinutesBefore  = 30;        // News: Pause vor einer Veröffentlichung (Minuten)
input int    SMT_MinutesAfter   = 30;        // News: Pause nach einer Veröffentlichung (Minuten)
input int    SMT_RefreshMinutes = 15;        // News: Download-Intervall (Minuten, min. 15)

datetime smt_eventTime[];
string   smt_eventTitle[];
datetime smt_nextDownload = 0;
long     smt_serverTime   = 0;     // UTC-Zeit des Servers (Unix-Sekunden) beim letzten Download
uint     smt_syncTick     = 0;     // GetTickCount() im selben Moment
bool     smt_clockSynced  = false;

//--- die echte UTC-Zeit: die Uhr des Servers plus die seit dem Download vergangenen
//--- Millisekunden, gezählt mit GetTickCount(). Der Zähler kennt keine Zeitzonen, keine
//--- Sommerzeit und keine Uhrkorrekturen auf dem Rechner (die uint-Subtraktion übersteht
//--- auch seinen Überlauf). Vor dem ersten Download dient TimeGMT() als Ersatz. Nie
//--- TimeCurrent(): Der GMT-Offset des Brokers ist unbekannt und springt mit der Sommerzeit.
datetime SMT_Now()
{
   if(!smt_clockSynced) return TimeGMT();
   uint elapsedMs = GetTickCount() - smt_syncTick;
   return (datetime)(smt_serverTime + elapsedMs / 1000);
}

//--- Abstand der Broker-Uhr zu UTC in Sekunden (7200 = GMT+2). Auch für sich allein
//--- nützlich: Die meisten EAs kennen diesen Wert nie. TimeCurrent() ist die Zeit des
//--- letzten Ticks, also bei geöffnetem Markt auslesen, nicht am Wochenende.
int SMT_BrokerGmtOffsetSeconds()
{
   return (int)(TimeCurrent() - SMT_Now());
}

//--- alles nach dem n-ten Komma (der CSV-Titel, der selbst Kommas enthalten kann)
string SMT_AfterComma(const string line, const int n)
{
   int pos = -1;
   for(int i = 0; i < n; i++)
   {
      pos = StringFind(line, ",", pos + 1);
      if(pos < 0) return "";
   }
   string rest = StringSubstr(line, pos + 1);
   StringReplace(rest, "\"\"", "\"");
   if(StringLen(rest) >= 2 && StringGetCharacter(rest, 0) == '"')
      rest = StringSubstr(rest, 1, StringLen(rest) - 2);
   return rest;
}

//--- lädt die nächsten 2 Tage herunter (das Fenster deckt auch die letzten 6 Stunden ab)
bool SMT_Download()
{
   string url = "https://showmytrades.com/api/public/calendar/v1?format=csv&days=2&impact=" + SMT_Impact;
   if(StringLen(SMT_Currencies) > 0) url += "&currency=" + SMT_Currencies;

   char   post[], result[];
   string headers;
   ResetLastError();
   int status = WebRequest("GET", url, "", NULL, 10000, post, 0, result, headers);
   if(status != 200)
   {
      Print("[SMT] calendar download failed: HTTP ", status, ", error ", GetLastError(),
            ". Is https://showmytrades.com in Tools > Options > Expert Advisors > Allow WebRequest?");
      smt_nextDownload = TimeCurrent() + 60; // neuer Versuch in einer Minute
      return false;
   }

   string body = CharArrayToString(result, 0, WHOLE_ARRAY, CP_UTF8);
   string lines[];
   int n = StringSplit(body, '\n', lines);

   ArrayResize(smt_eventTime, 0);
   ArrayResize(smt_eventTitle, 0);
   for(int i = 1; i < n; i++) // Zeile 0 ist die Kopfzeile
   {
      // letzte Zeile "# server_time=1760702400 server_time_iso=...": die UTC-Uhr des Servers
      if(StringFind(lines[i], "# server_time=") == 0)
      {
         long serverTime = StringToInteger(StringSubstr(lines[i], 14, 10));
         if(serverTime > 0)
         {
            smt_serverTime  = serverTime;
            smt_syncTick    = GetTickCount();
            smt_clockSynced = true;
         }
         continue;
      }
      string f[];
      if(StringSplit(lines[i], ',', f) < 8) continue;
      int k = ArraySize(smt_eventTime);
      ArrayResize(smt_eventTime, k + 1);
      ArrayResize(smt_eventTitle, k + 1);
      smt_eventTime[k]  = (datetime)StringToInteger(f[0]);      // Unix-Sekunden, UTC
      smt_eventTitle[k] = f[2] + " " + f[3] + ": " + SMT_AfterComma(lines[i], 7);
   }
   smt_nextDownload = TimeCurrent() + MathMax(SMT_RefreshMinutes, 15) * 60;
   Print("[SMT] calendar: ", ArraySize(smt_eventTime), " events loaded. UTC now ",
         TimeToString(SMT_Now(), TIME_DATE|TIME_MINUTES), ", broker clock is GMT",
         (SMT_BrokerGmtOffsetSeconds() >= 0 ? "+" : ""), DoubleToString(SMT_BrokerGmtOffsetSeconds() / 3600.0, 1));
   return true;
}

//--- true, solange eine Veröffentlichung im Pausenfenster liegt; reason sagt, welche
bool SMT_IsNewsTime(string &reason)
{
   reason = "";
   if(MQLInfoInteger(MQL_TESTER)) return false;   // kein Netzwerk im Tester
   if(TimeCurrent() >= smt_nextDownload) SMT_Download();

   datetime now = SMT_Now();
   for(int i = 0; i < ArraySize(smt_eventTime); i++)
   {
      if(now >= smt_eventTime[i] - SMT_MinutesBefore * 60 && now <= smt_eventTime[i] + SMT_MinutesAfter * 60)
      {
         reason = smt_eventTitle[i];
         return true;
      }
   }
   return false;
}

Was die Datei der Reihe nach tut: Sie baut die Anfrage aus den Eingabeparametern zusammen, lädt die CSV-Datei herunter, merkt sich pro Termin einen Zeitstempel und eine Bezeichnung und beantwortet die einzige Frage, die den EA interessiert: Liegt gerade eine Veröffentlichung im Fenster?

🔌 Schritt 3: Vor jedem Einstieg abfragen

Binde die Datei in deinen EA ein und lass ihn nachfragen, bevor er eine Position eröffnet:

YourEA.mq4
#include <SMT_NewsFilter.mqh>

void OnTick()
{
   string news;
   if(SMT_IsNewsTime(news))
   {
      Comment("News filter active: ", news);
      return;                       // keine neuen Einstiege im News-Fenster
   }
   Comment("");
   // ... deine Einstiegslogik ...
}

Die fünf Eingabeparameter (SMT_Currencies, SMT_Impact und die drei Minutenwerte) erscheinen wie jede andere Einstellung im Eingabedialog des EA. Jeder Chart kann also eigene Werte haben.

Kompiliere neu, zieh den EA auf den Chart und sieh im Reiter „Experten“ nach: Die erste Zeile sollte [SMT] calendar: N events loaded lauten. An einem ruhigen Tag kann N null sein. Das ist richtig so und kein Fehler.

⏱️ Zeiten, Uhren und der Strategietester

Ob der Filter zur richtigen Minute greift, hängt an drei Details.

Die API rechnet in UTC und sagt, wie spät es istts ist Unix-Zeit in Sekunden, UTC. Jeder Download endet mit der Uhrzeit des Servers (# server_time=…), und SMT_Now() schreibt sie mit dem Tick-Zähler des Rechners fort, der weder Zeitzonen noch Sommerzeit kennt. Nichts hängt von der Zeitzone des Brokers ab, von irgendeiner Sommerzeitumstellung oder davon, ob auf dem VPS die richtige Zeitzone eingestellt ist
Niemals TimeCurrent()Die Broker-Zeit weicht von UTC um einen Betrag ab, den der EA nicht kennen kann und der sich zweimal im Jahr ändert. SMT_BrokerGmtOffsetSeconds() misst ihn für dich, was schon für sich nützlich ist; der Filter selbst verlässt sich nie darauf
Der Strategietester hat kein InternetWebRequest() ist dort deaktiviert, im Backtest meldet die Funktion also „keine News“. Wer einen News-Filter auf historischen Daten testen will, braucht eine lokale Datei mit vergangenen Terminen, und das ist ein eigenes Projekt

🎛️ Feinabstimmung

  • Offene Positionen. Der Code sperrt nur neue Einstiege. Ob du vor einer Veröffentlichung schließt, die Stops enger ziehst oder weiter setzt, ist eine Frage der Strategie; wann es so weit ist, sagt dir dasselbe SMT_IsNewsTime().
  • Nur eine Seite des Paars. Setze SMT_Currencies auf USD, wenn für dich nur Dollar-News zählen.
  • Auch mittlere Wichtigkeit. Mit SMT_Impact = "high,medium" kommen ungefähr dreimal so viele Termine zusammen. Gut für Scalper, für Swing-Systeme oft zu viel.
  • Feiertage. impact=all enthält Handelsfeiertage als ganztägige Termine. Die meisten Filter wollen sie nicht, deshalb lässt die Standardeinstellung sie weg.

🔍 Die Daten prüfen

Denselben Kalender kannst du auch als Seite lesen: Wirtschaftskalender. Meldet der EA, dass er wegen „USD high: Nonfarm Payrolls“ pausiert hat, findest du den Termin dort mit der Prognose und, nach der Veröffentlichung, mit dem tatsächlichen Wert.

❓ Häufige Fragen

Brauche ich dafür ein ShowMyTrades-Konto? Nein. Die Kalender-API ist kostenlos und offen, ganz ohne Schlüssel. Wenn du die verifizierte öffentliche Seite haben willst, verfolge dein Konto auf ShowMyTrades; der News-Filter hängt aber nicht davon ab.

Wie viel Datenverkehr entsteht? Eine Anfrage alle 15 Minuten pro Chart, jeweils ein paar Kilobyte. Zehn EAs auf einem Terminal kommen auf 40 Anfragen pro Stunde. Das Limit liegt bei 600 pro Minute und IP-Adresse.

Warum nicht direkt Forex Factory auslesen? Das geht, und viele EAs machen es so. Die Wochendatei von Forex Factory erlaubt aber nur zwei Downloads alle fünf Minuten pro IP-Adresse. Auf einem VPS mit vielen Terminals reicht das nicht, und actual-Werte enthält die Datei auch nicht. Der Endpunkt von ShowMyTrades filtert schon auf dem Server nach Währung und Wichtigkeit, liefert ein paar Kilobyte und ist für regelmäßiges Abfragen gebaut.

Die Uhr oder die Zeitzone meines VPS stimmt nicht. Greift der Filter trotzdem zur richtigen Minute? Ja. Der Server teilt dem EA bei jedem Download die aktuelle UTC-Zeit mit, und der EA lässt diese Uhr mit seiner eigenen weiterlaufen. Eine falsch eingestellte Zeitzone auf dem VPS oder eine Uhr, die ein paar Minuten danebenliegt, ändert nichts. Nicht einmal eine Sommerzeitumstellung auf dem VPS: Der EA misst die vergangene Zeit mit dem Tick-Zähler, nicht mit der Uhr.

Was passiert, wenn der Download fehlschlägt? Der Filter behält den zuletzt geladenen Kalender und versucht es nach einer Minute erneut. Hat er noch nie einen geladen (gerade gestartet, kein Internet), meldet er „keine News“, und der EA handelt. Wenn du das nicht willst, prüfe ArraySize(smt_eventTime) == 0 und entscheide selbst.

Funktioniert dieselbe Datei in MT5? Ja, ohne Änderung. MT5 hat außerdem einen eigenen Kalender. Nimm, was besser zu dir passt.

🔗 Weiterführende Artikel


Der Filter ist eine Seite Code und eine URL. Wenn dein EA schon auf ShowMyTrades verfolgt wird, zeigen sich die Trades, die er rund um die News auslässt, als Drawdown, der ausgeblieben ist.