MetaTrader 5 et le Calendrier Économique MQL5 : comment transformer les actualités en un système de trading reproductible
Introduction
Les principaux problèmes du trader d'actualités moderne sont la fragmentation des outils et l'absence d’un processus de trading structuré et algorithmique. Il est assez difficile de répartir son attention entre son navigateur internet (pour consulter des sites d'actualités) et son terminal de trading lorsqu'on passe des ordres.
Le flux de travail d'un trader sur actualités ressemble à ceci : Ouvrir rapidement le calendrier des actualités dans son navigateur Web et vérifier les changements d'événements → Évaluer rapidement les événements à venir et décider quoi et comment trader → Accéder au terminal MetaTrader 5 – placer des ordres en attente ou rester devant le terminal et attendre la publication des actualités pour prendre une décision. Dans ce scénario, le trader perd souvent le fil de l’analyse, ce qui entraîne des retards dans sa réaction aux actualités, ce qui à son tour engendre des pertes.
Et si le trading d’actualités fonctionnait comme un problème d’ingénierie — avec des règles claires, des résultats reproductibles et des tests automatisés ? L'objectif de cet article est de démontrer l'architecture opérationnelle d'un module d’actualités pour MetaTrader 5 : une source de données unique, une utilisation appropriée de l'API Calendar, un mécanisme de filtrage et de mise en cache, l'export des événements historiques vers une ressource pour le testeur et la bascule automatique entre les modes Live et Tester — afin que le même code produise des résultats déterministes à la fois en temps réel et avec des données historiques.
Fig. 1 : le principal problème d'un trader manuel est la fragmentation des outils.
Le trading manuel sur actualités est obsolète
Premièrement, une stratégie de trading sur actualités qui dépend de facteurs externes ne peut pas être testée. Une grande part du succès en trading repose sur l’évaluation historique de la stratégie. L'impossibilité de tester une stratégie engendre une subjectivité dans les décisions prises. La stratégie restera au stade de l’hypothèse, et vous passerez beaucoup de temps – des mois, voire des années – à la tester.
Deuxièmement, il est très difficile de développer le trading manuel à grande échelle. Essayez de développer une entreprise instable et générant des bénéfices irréguliers. L'étendre ne fera qu'aggraver les problèmes. Sans structure claire, sans systématisation ni optimisation, toute tentative de l’industrialiser est vouée à l’échec.
Troisièmement, les délais de réaction aux actualités constituent un problème majeur du trading manuel. Imaginez que vous êtes un trader FOREX et que vous tradez la publication des Non-Farm Payrolls (NFP). Les données NFP sont généralement publiées une fois par mois. Vous avez été distrait et avez manqué la publication du NFP, perdant ainsi une opportunité de gain. Il s'agit d'une situation familière pour de nombreux traders.
Conclusion : l'automatisation est la seule voie vers un trading d'actualités reproductible.

Fig. 2 : un robot est toujours plus rapide
Calendrier Économique intégré à MetaTrader 5 : une source d’informations unique
Pour faire du trading sur actualités un processus algorithmique — et donc rendre les tests historiques possibles — utilisez le produit unique de notre société : un Calendrier Économique intégré à MetaTrader 5 avec accès aux actualités via l'API MQL5. C’est ce qui transformera votre trading d’actualités, en le faisant passer de l’improvisation à un processus algorithmique vérifiable. Les événements actuels en temps réel et les événements historiques sont disponibles pour les tests hors ligne.
Tableau 1 :
ce qui rend le calendrier intégré à MetaTrader 5 unique :
| Paramètre | |
|---|---|
| Vitesse d'accès | <100 ms — une fois les données du calendrier chargées dans le terminal |
| Intégration | Natif — au niveau du noyau du terminal + API MQL |
| Test | Entièrement pris en charge - après téléchargement des actualités dans le terminal (accès dans le testeur via les fichiers, les ressources ou SQLite) |
| Fiabilité | Haute |
MetaTrader 5 dispose d'un excellent testeur de stratégie : rapide, multi-actifs et capable d'utiliser des ordinateurs sur des réseaux locaux et mondiaux pour les tests et l'optimisation. Essayez d'exécuter votre système de trading d'actualités sur des données historiques. Cela pourrait améliorer votre approche de trading.
Fonctions du Calendrier Économique de MetaTrader 5 : Présentation de l'API MQL5
La transition de l'analyse manuelle à l'analyse algorithmique commence par la compréhension de l'architecture des données. En MQL5, le Calendrier Économique n'est pas simplement un tableau, mais une base de données structurée accessible via une API native. Nous verrons comment interroger correctement les événements, la différence entre événement et valeur, et pourquoi la synchronisation temporelle est essentielle à la réussite d'une stratégie.
Le cœur du calendrier MQL5 se compose de plusieurs fonctions clés, chacune ayant un rôle spécifique. Il est important de comprendre qu'il n'existe pas de fonction universelle permettant de fournir simultanément toutes les données du Calendrier Économique. Il faut donc combiner plusieurs fonctions.
- CalendarValueHistory — l’outil principal pour le chargement initial. Cette fonction permet d'obtenir un tableau de valeurs d'événements pour un intervalle de temps spécifié. Il s'agit de « l'artillerie lourde » utilisée lors de l'initialisation de l'EA pour remplir le cache avec des données historiques ou des données pour la semaine à venir.
- CalendarValueLast — la fonction principale pour les EA fonctionnant en temps réel. Elle renvoie uniquement les valeurs modifiées ou nouvelles depuis la dernière requête (via le mécanisme change_id). Cela permet d'économiser du trafic et des ressources serveur en évitant de demander l'intégralité du tableau de données à chaque tick.
- CalendarEventByCountry — retourne la description de tous les événements associés à un pays donné. Cette fonction renvoie une liste de descriptions d'événements pour un pays spécifique spécifié par code selon la norme ISO 3166-1 alpha-2. Elle est nécessaire pour créer des filtres (par exemple, afficher uniquement les événements pour « US » (États-Unis), « RU » (Russie), « CA » (Canada), etc.).
- CalendarEventByCurrency — retourne la description de tous les événements par devise. Cette fonction renvoie une liste de descriptions d'événements pour une devise spécifique spécifiée par code (« USD », « EUR », etc.).
- CalendarCountryById — retourne les propriétés du pays par « id ».
- CalendarEventById — retourne les propriétés de l'événement par « id ».
- CalendarValueById — retourne une valeur spécifique par « id ».
Structures de données : que renvoie l'API MQL5 Calendar ?
Toutes les fonctions de l'API du Calendrier Économique renvoient soit un tableau de structures, soit une variable contenant une seule structure. Énumérons ici l'ensemble des structures et de leurs champs.
Descriptions des événements - utilisées dans les fonctions CalendarEventById, CalendarEventByCountry et 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) };
Descriptions des pays — utilisées dans les fonctions CalendarCountryById et 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 };
Valeurs des événements - utilisées dans les fonctions CalendarValueById, CalendarValueHistoryByEvent, CalendarValueHistory, CalendarValueLastByEvent et 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 };
Note :
Notez que la structure MqlCalendarValue fournit des méthodes pour vérifier et obtenir les valeurs des champs actual_value, forecast_value, prev_value et revised_prev_value. Les champs listés peuvent ne pas avoir de valeurs ; par exemple, le champ actual_value peut être vide car l’indicateur n’a pas encore été publié. La meilleure façon d'obtenir les valeurs est d'effectuer la vérification et d'obtenir les valeurs en utilisant les méthodes de la structure elle-même.
Les structures sont liées entre elles par les relations suivantes :

Fig. 3 : relations entre les structures du calendrier
La structure MqlCalendarCountry est liée à MqlCalendarEvent via un identifiant de pays. La forme de la relation est « un à plusieurs » (1..*).
La structure MqlCalendarEvent est liée à MqlCalendarValue via un ID d'événement. La forme de la relation est « un à plusieurs » (1..*).
Heure de publication de l'actualité et heure du serveur
Toutes les fonctions permettant de travailler avec le Calendrier Économique utilisent l'heure du serveur de trading TimeTradeServer(). Cela signifie que l'heure dans la structure MqlCalendarValue et les entrées temporelles dans les fonctions CalendarValueHistoryByEvent()et CalendarValueHistory() sont définies dans le fuseau horaire du serveur de trading, plutôt que dans l'heure locale de l'utilisateur.
Aucune conversion n'est nécessaire : les valeurs temporelles, telle que l'heure de l'événement dans MqlCalendarValue::period, peuvent être comparées directement à l'heure renvoyée par TimeCurrent() ou TimeTradeServer(). Dans le testeur, la fonction TimeTradeServer() renvoie une heure simulée identique à celle des données historiques. La logique de fonctionnement avec les fenêtres temporelles (« 30 minutes avant les actualités ») fonctionne de la même manière en temps réel et dans l'historique. Si le courtier tient compte du passage à l'heure d'été/d'hiver, le calendrier s'ajuste automatiquement à ce changement.
Exemple pratique — obtenir la liste des événements du jour (aujourd'hui) :
//+------------------------------------------------------------------+ //| 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); } } } //+------------------------------------------------------------------+
Cette fonction affiche une liste des événements en devise USD pour la journée en cours ainsi que le contenu des principaux champs des structures récupérées via l'API MQL5 Calendar. Le code complet du script est joint à l’article, dans le fichier GetTodayEvents-S.mq5.
Les résultats de l'exécution de cette fonction peuvent être consultés dans l'onglet Boîte à Outils\Experts du terminal MetaTrader 5. Nous constatons que deux événements ont été reçus pour l’USD. Au moment de la demande, ces événements ne s'étaient pas encore produits (la statistique n'avait pas encore été diffusée). Par conséquent, le champ MqlCalendarValue::actual_value ne contient pas de valeur — la fonction HasActualValue() renvoie 'false'.
Pour ces types d'événements, le champ MqlCalendarValue::forecast_value vérifié par HasForecastValue() est absent (ne contient aucune valeur). Pour d'autres événements, les quatre champs (prev_value, actual_value, forecast_value et revised_prev_value) peuvent être manquants.
Received events for USD: 2
✅ Event #0
Event ID: 840220005
Event name: 3-month treasury bills auction
Sector: 1
Source: https://home.treasury.gov/
Country name: USA
Country URL: united-states
Time: 2026.04.20 18:30:00
Impact: 0
Previous: 3.62
✅ Event #1
Event ID: 840220006
Event name: 6-month treasury bills auction
Sector: 1
Source: https://home.treasury.gov/
Country name: USA
Country URL: united-states
Time: 2026.04.20 18:30:00
Impact: 0
Previous: 3.61
Explications du code fourni :
Tout d'abord, nous obtenons un tableau de valeurs pour tous les événements dans une plage horaire donnée avec le filtre de devise « USD ». Dans une boucle, nous parcourons le tableau de valeurs et demandons la description de l'événement par l'ID d'événement MqlCalendarValue::event_id en utilisant la fonction CalendarEventById(). Ensuite, nous demandons une description du pays par son identifiant MqlCalendarEvent::country_id en utilisant la fonction CalendarCountryById().
Si la fonction CalendarValueHistory() renvoie « false », mais que GetLastError() renvoie zéro (aucune erreur), cela indique qu'il n'y a pas d'événements avec les paramètres demandés.
Gestion des erreurs et limites lors de l'utilisation du calendrier
Travailler avec des données distantes comporte toujours un risque d'interruptions de connexion ou de restrictions d'accès. Les fonctions de calendrier renvoient la valeur « false » ou 0 en cas d'échec. Pour comprendre la raison, il est nécessaire d'utiliser GetLastError(). La documentation met en évidence un groupe d'erreurs distinct pour le module calendrier.
Codes d'erreur :
- ERR_CALENDAR_TIMEOUT (code 5200) — le délai d'attente de la réponse du serveur a expiré. Panne de réseau ou surcharge du serveur. Solution : Répétez la demande après une pause de 5 à 10 secondes.
- ERR_CALENDAR_NO_DATA (code 5201) — les données du calendrier n'ont pas encore été chargées. Le calendrier est initialisé de manière asynchrone. Solution : Attendez 1 à 2 secondes et répétez la demande.
- ERR_CALENDAR_INVALID_DATE (code 5202) — plage de dates invalide. Erreur de code (par exemple, la date de début est postérieure à la date de fin). Solution : Corrigez la logique, répéter la demande est inutile.
- ERR_CALENDAR_INVALID_COUNTRY (code 5203) — code pays/devise inconnu. Erreur dans les paramètres de la requête. Solution : Vérifiez le code (par exemple, « US » au lieu de « USA »).
- ERR_CALENDAR_TOO_MANY_REQUESTS (code 5204) — limite de requêtes dépassée. Erreur critique. Solution : Augmentez l'intervalle entre les requêtes.
Limitation du taux de requêtes :
Les serveurs auxquels se connecte le terminal MetaTrader 5 protègent l'infrastructure contre la surcharge. Si l'EA appelle CalendarValueHistory() à chaque tick ou dans une boucle sans délai, le serveur renvoie l'erreur 5204 et bloque temporairement l'accès au calendrier pour votre terminal. Meilleure pratique : charger les données une seule fois au démarrage dans OnInit() pour la période requise.
Pour les mises à jour en temps réel, utilisez CalendarValueLast() tout en stockant la variable change_id renvoyée — cela permet de recevoir uniquement les modifications, plutôt que l'ensemble du tableau de données. Mettez à jour les données via une minuterie dans OnTimer() à des intervalles de 5 à 10 minutes maximum.
Examinons quelques exemples de solutions aux problèmes pouvant survenir lors du chargement d'un calendrier :
Lorsque vous démarrez le terminal (démarrage à froid) et l'EA, le calendrier n'est pas immédiatement prêt. Les données sont chargées depuis le serveur en arrière-plan. Si nous appelons la fonction CalendarValueHistory() dans la toute première ligne de OnInit(), vous obtiendrez très probablement l'erreur 5201 — aucune donnée. Il est nécessaire de mettre en œuvre un mécanisme de vérification de disponibilité avec un délai d'expiration exponentiel ou fixe.
Exemple pratique — fonction de chargement de données de calendrier avec gestion des erreurs :
//+------------------------------------------------------------------+ //| 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; } //+------------------------------------------------------------------+
Explications du code fourni :
Tenez compte de la possibilité d'erreurs « Aucune donnée » (5201) et « Délai d'attente dépassé » (5200). Ce sont des erreurs corrigibles. Gérez-les en faisant une pause de 1 à 5 secondes et en demandant à nouveau les données. En cas d'erreurs irrécupérables, arrêtez immédiatement le téléchargement. Les erreurs irrécupérables comprennent les dates invalides, les codes de devise ou de pays incorrects, etc. Vous trouverez le code source complet du script dans le fichier GetTodayEvents-S.mq5 joint à cet article.
Pour éviter les limites et garantir une vitesse de réaction maximale, dans OnTimer(), nous devrions utiliser le mécanisme change_id. Au démarrage, chargez l'historique complet des événements et mémorisez le dernier change_id. Dans le minuteur, ne demandez que de nouvelles données à l'aide de la fonction CalendarValueLast(). Le serveur ne renverra que les données modifiées (ou « false » s'il n'y a pas de modifications), sans gaspiller de ressources à transmettre d'anciennes données.
Exemple pratique — fonction de mise à jour des données du calendrier avec gestion des erreurs :
//+------------------------------------------------------------------+ //| 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++; } } } //+------------------------------------------------------------------+
Explications du code donné :
La mise à jour incrémentale des événements s'effectue via le gestionnaire d'événements du minuteur OnTimer(). La fonction CalendarValueLast() de l'API Calendar est appelée : si de nouveaux événements apparaissent, les éléments correspondants dans le tableau de structures MqlCalendarValue précédemment chargé sont mis à jour et les données sont écrites dans le journal du terminal. Si un événement n'est pas trouvé dans le tableau chargé, il y est ajouté.
Le code complet du script se trouve dans le fichier CalendarEventMonitor-EA.mq5, joint à l'article.
Filtrage des événements : du général au spécifique
Pourquoi filtrer :
Le Calendrier Économique publie quotidiennement entre 60 et 90 événements. C'est comme écouter un flot continu de centaines d'indicateurs mineurs, de fermeture des marchés et de discours officiels qui n'ont aucun impact immédiat sur le marché. Trader sur chacun de ces évènements est un moyen infaillible de multiplier inutilement les opérations et de perdre son dépôt. La tâche d'un trader algorithmique spécialisé dans l'actualité est de ne retenir que 3 à 5 événements qui influencent réellement le marché.
Le filtrage dans le trading algorithmique d'actualités équivaut à augmenter le rapport signal/bruit à un niveau adapté à la prise de décision automatisée. Dans l'API MQL5 Calendar, ce processus est implémenté comme un système de filtre à plusieurs niveaux à travers lequel passent les tableaux de structures MqlCalendarEvent et MqlCalendarValue.
Critères de filtrage :
Le marché ne réagit pas à l'annonce elle-même, mais à l'écart entre les données réelles et les prévisions, ainsi qu'au niveau d'influence macroéconomique. Les événements à faible impact (MqlCalendarValue::impact_type < 3) sont souvent ignorés par les algorithmes de trading et les teneurs de marché.
Sans filtrage, vous obtenez :
- des déclenchements erronés de la stratégie sur des actualités secondaires ;
- des opérations exécutées pendant des périodes de spread large sans volatilité réelle ;
- une surcharge de la logique de l'EA avec des vérifications inutiles.
L'objectif du filtrage est de réduire le flux de données de 90%, ne laissant que les événements à fort impact (ENUM_CALENDAR_EVENT_IMPORTANCE::CALENDAR_IMPORTANCE_HIGH) pour la devise cible sélectionnée par le trader dans une fenêtre temporelle donnée.
Voyons comment construire un filtre fiable qui fonctionnera de manière tout aussi fiable en temps réel qu'en mode test.
Système de filtration à plusieurs niveaux :
Les données d'événements « brutes » téléchargées depuis le terminal sont comme du minerai non traité : elles contiennent des milliers d'entrées, allant de rapports statistiques insignifiants à des jours fériés qui n'apportent aucun avantage à la stratégie algorithmique.
Pour transformer ce flux de données en un signal propre, nous avons besoin d'un système de filtrage à plusieurs niveaux. Le filtrage des événements agit comme un « tamis » à travers lequel passent les données brutes, ne laissant en sortie que les événements qui répondent aux critères spécifiés : devise, importance, code et fenêtre temporelle.
Un filtre efficace doit avoir une structure hiérarchique : partant d’une coupure initiale approximative par zone géographique et se terminant par un réglage fin par fenêtre temporelle.
Une caractéristique importante de l’API MQL5 Calendar : la structure MqlCalendarValue ne contient pas les champs currency_id et country_id. Elle ne stocke que l’identifiant de l'événement, l'heure, les valeurs de l'indicateur et le type d'influence. Quel est l'impact sur la filtration ?
Le filtrage des devises est effectué au niveau de la requête API. Lorsque vous appelez CalendarValueHistory(..., NULL, "USD"), le terminal lui-même ignore tout sauf USD. Le tableau que vous recevez est déjà filtré par devise. Il n'est donc pas nécessaire de vérifier currency_id en temps réel ; il suffit de filtrer par date, importance et la présence d’une prévision et, le cas échéant, sa pertinence.
Niveau 1 : Filtre de devises
Le premier critère est la zone géographique concernée par l’événement. Lors du trading de l'EUR/USD, l'EA ne doit réagir qu'aux événements qui affectent la zone euro (EUR) et les États-Unis (USD). Le filtrage consiste à demander des événements avec les codes de devise de l'instrument négocié. Pour les paires croisées (par exemple "GBPJPY"), nous devons demander des événements pour les deux devises ("GBP" et "JPY") et "USD".
Niveau 2 : Importance de l'événement
Chaque évènement macro-économique a une importance variable en termes d'impact sur l'économie — et notamment sur les prix. L'indice des prix à la consommation (IPC) peut faire varier le marché de 100 points, tandis que l'indice de confiance des consommateurs ZEW ne peut le faire varier que de 10 points.
L'API MQL5 utilise l'énumération ENUM_CALENDAR_EVENT_IMPORTANCE pour définir le degré d'importance :
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 };
Les EA d'actualités doivent ignorer tout ce qui est inférieur à HIGH afin d'éviter les faux positifs sur les actualités à faible liquidité.
Niveau 3 : Code d'événement
Même parmi les événements de grande importance, il existe des exceptions. Par exemple, un discours de la BCE ou du FMI peut être jugé important, mais son traitement algorithmique est plus complexe que celui de la publication de chiffres précis. Il nous faut donc trier par code d'événement : Le champ event_code de MqlCalendarEvent contient un identifiant unique (par exemple, « NONFARM », « CPI », « GDP »). Nous sélectionnons uniquement les événements comportant des données économiques, et non les sondages et les discours.
Ce n'est qu'après avoir passé les trois premiers niveaux de vérification que l'événement est copié dans le tableau résultant et utilisé pour les transactions.
Fig. 4 : le filtrage à plusieurs niveaux est la clé du succès dans le trading d'actualités.
Après les trois premiers niveaux de filtrage, l'EA vérifie si l'événement se situe dans la fenêtre temporelle configurée. Cela se produit pendant l'exécution du robot de trading.
Niveau 4 : Période
La dernière étape consiste à vérifier la pertinence. Nous n'avons pas besoin d'actualités qui paraîtront dans un mois. Celle parue hier a déjà été intégrée par le marché. Par conséquent, un événement est considéré comme actif si TimeCurrent() tombe dans l'intervalle : le temps spécifié avant la publication de l'actualité et le temps spécifié après la publication. En général, les paramètres suivants sont utilisés : 15 à 30 minutes avant la sortie – pour réduire ou fermer les positions, et 60 minutes après – pour analyser la volatilité et la direction des mouvements de prix.
Ressources binaires MQL5 : déplacer les événements vers des tests hors ligne pour une mise en cache efficace
Un problème lié aux tests dans le cadre du trading algorithmique basé sur l'actualité est que le Testeur de Stratégie n'a pas accès à Internet. Cette solution accélère les tests et réduit l’influence des facteurs non déterministes, mais elle pose un problème pour les tests de stratégies basées sur l'actualité.
Si l'EA dans le testeur appelle la fonction CalendarValueHistory(), il obtient une erreur et un tableau vide. Pour tester cette stratégie, nous devons intégrer des données d'actualités historiques dans le fichier exécutable .ex5. Pour cela, nous utilisons l'option avec les ressources binaires MQL5. Pourquoi cette option en particulier ? C'est la méthode la plus rapide pour accéder aux données structurées ; la vitesse d'accès n'est limitée que par les performances du système de fichiers de l'ordinateur.
Voyons comment transformer le tableau MqlCalendarValue[] en un fichier binaire compact et comment l'intégrer au code afin que le testeur lise les données de la RAM très rapidement. La première étape consiste à créer un script d'export qui téléchargera les données actuelles du calendrier et les enregistrera dans un fichier binaire.
Exemple pratique — un script pour exporter des événements vers un fichier binaire :
Le code complet du script se trouve dans le fichier ExportCalendarForTester.mq5 joint à l'article.
Étape 1 : assemblage et filtrage du tableau
Nous ne stockerons pas tous les événements mondiaux dans la ressource. Lors de l'export vers un fichier binaire, nous appliquons les mêmes filtres que dans l'EA : devise, importance et type d'événement. Les paramètres du script spécifient la liste des devises, l'intervalle de temps pour le chargement des événements, les codes d'événements et l'importance minimale.
La liste des codes d'événements (paramètre InpEventCodes) est vide par défaut ; cela signifie que tous les types d'événements sont chargés. Nous pouvons affiner ce filtre. Par exemple, uniquement les « non-farm payrolls » - la liste des codes sera alors « NONFARM ».
L'indicateur InpUseCommonDir spécifie où enregistrer le fichier binaire : true — enregistrer dans le dossier partagé commun à tous les terminaux clients « \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;
La partie du script qui charge et filtre les événements en fonction des valeurs spécifiées dans les entrées est présentée ci-dessous :
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 } } }
Explications du code fourni :
- Le tableau values_size[] stocke le nombre d'événements filtrés pour chaque code de devise spécifié dans les entrées.
- La valeur zéro du type datetime MQL5 correspond à la date du 1970.01.01 00:00:00.
- Un filtrage supplémentaire est effectué par type d'événement. Seuls les évènements de type ENUM_CALENDAR_EVENT_TYPE::CALENDAR_TYPE_INDICATOR sont retenus — ce sont des événements économiques (pas des discours, mais des chiffres). Toutes les autres actualités sont supprimées. Cela se fait par validation dès le début du filtrage :
//--- take only indicators if(event.type != CALENDAR_TYPE_INDICATOR) continue; // Nothing else to filter
Exécutons le script avec différents ensembles de paramètres de filtrage et voyons à quelle vitesse fonctionne le script de chargement/filtrage des événements. Il est clair que la vitesse dépend de la puissance de l'ordinateur et de la vitesse de connexion aux serveurs de données via Internet. Néanmoins, nous allons effectuer un test de charge. Les résultats sont présentés ci-dessous.
Tous les événements possibles en dollars américains d'une importance haute — sur l’ensemble de l’historique disponible :
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 → Of these, filtered: 9009
13:59:45.511 ----------------------------------
13:59:45.513 ✅ Saved: USD_calendar_test_res.bin Size: 1153152 bytes (9009 events)
Temps d'obtention du tableau de valeurs filtrées (structures MqlCalendarValue) : 1,80 sec (0,20 ms / par filtration).
Tous les évènements possibles sur les devises USD, EUR et JPY d'importance haute — sur l’ensemble de l’historique disponible :
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 → Of these, filtered: 9009
14:03:10.947 ✅ Received values BY CURRENCY "EUR": 45139 → Of these, filtered: 1102
14:03:11.404 ✅ Received values BY CURRENCY "JPY": 18907 → Of these, filtered: 937
14:03:11.404 ----------------------------------
14:03:11.406 ✅ Saved: USD_calendar_test_res.bin Size: 1153152 bytes (9009 events)
14:03:11.407 ✅ Saved: EUR_calendar_test_res.bin Size: 141056 bytes (1102 events)
14:03:11.408 ✅ Saved: JPY_calendar_test_res.bin Size: 119936 bytes (937 events)
Temps d'obtention du tableau de valeurs filtrées (structures MqlCalendarValue) : 2,68 sec (0,24 ms / par filtration).
Événements NFP (« non agricoles ») par USD d'importance haute — sur l’ensemble de l’historique disponible :
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 → Of these, filtered: 473
14:07:22.290 ----------------------------------
14:07:22.291 ✅ Saved: USD_calendar_test_res.bin Size: 60544 bytes (473 events)
Temps d'obtention du tableau de valeurs filtrées (structures MqlCalendarValue) : 0,09 sec (0,18 ms / par filtration).
Remarques :
- Il n'est pas nécessaire de spécifier l'intégralité du code d'événement dans le filtre. Il suffit de spécifier une partie unique de la chaîne contenant le code. Par exemple, pour les « emplois non-agricoles », le code complet est « NONFARM-PAYROLLS » — spécifier « NONFARM » suffit.
- Lors de l'exécution d'un système de trading en temps réel dans le terminal MetaTrader 5, il est peu probable que nous ayons besoin de télécharger tous les événements pour toute la période. En règle générale, les actualités sont téléchargées pour la journée et la semaine à venir. Par conséquent, le temps de chargement dans l'algorithme de l’EA d'actualités peut être négligé.
Étape 2 : exporter le tableau d'événements vers un fichier binaire
La fonction SaveToBinary enregistre les événements filtrés dans un fichier binaire. Le nombre de fichiers est égal au nombre de codes de devise spécifiés dans les entrées ; chaque devise est exportée dans son propre fichier binaire. Le nom du fichier commence par le préfixe du code de devise. Par exemple, « USD_calendar_test_res.bin ». De cette manière, nous obtenons une correspondance directe entre le code de la devise et la liste des événements associés.
//+------------------------------------------------------------------+ //| Save an array to a binary file | //+------------------------------------------------------------------+ void SaveToBinary(MqlCalendarValue &values[], const int &vls_size[], const string filename, const string ¤cies[]) { 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]; } }
Après l'exécution réussie du script d'export des événements vers des fichiers binaires, des messages comme le suivant apparaissent dans l'onglet Experts de MetaTrader 5 :
14:28:42.077 ----------------------------------
14:28:42.078 ✅ Saved: USD_calendar_test_res.bin Size: 58240 bytes (455 events)
14:28:42.079 ✅ Saved: EUR_calendar_test_res.bin Size: 7680 bytes (60 events)
Étape 3 : compiler la ressource dans l'EA
Après avoir lancé le script ExportCalendarForTester.mq5 dans MQL5/Files/, les fichiers USD_calendar_test_res.bin et EUR_calendar_test_res.bin apparaissent. Il faut maintenant les « intégrer » à l'EA. Au début du fichier de l’EA (après les directives #property), ajoutez une (si vous utilisez un fichier pour une seule devise) ou plusieurs lignes du type suivant :
// 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[]
Note :
- Le chemin d'accès à #resource est spécifié par rapport au dossier MQL5/Files/. Le préfixe \\Files\\ est obligatoire.
- Lors de la compilation de l'EA, des messages comme les suivants devraient apparaître dans l'onglet Erreurs de MetaEditor :
- '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]'
Lors du démarrage de l'EA, les structures des événements enregistrés pendant la phase d'export sont stockées dans les tableaux USD_res_calendar_data[] et EUR_res_calendar_data[].
Intégration dans l'EA : changement de mode automatique
L'objectif est de faire fonctionner le même code de l’EA à la fois en temps réel et dans l'environnement de test, en sélectionnant automatiquement la source de données. La fonction MQLInfoInteger(MQL_TESTER) renvoie 'true' si l'EA est en cours d'exécution dans le Testeur de Stratégie ou dans l'Optimiseur.
//+------------------------------------------------------------------+ //| 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; }
Explications du code fourni :
La vérification principale est effectuée dans le gestionnaire d'événements d'initialisation EA 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()) { ... } }
Explications du code fourni :
Nous déterminons dans quel mode fonctionne le programme MQL5 en cours d'exécution (ici, notre EA). Nous nous intéressons aux drapeaux suivants :
- MQL_TESTER — indicateur du programme lancé s'exécutant dans le testeur ;
- MQL_OPTIMIZATION — indicateur du programme lancé pendant l'optimisation ;
- MQL_VISUAL_MODE — indicateur du programme lancé en mode de test visuel.
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 (data from API)
Validation de la chaîne de transformation des données : Événements → Fichier binaire → Compilation sous forme de ressource → Récupération des événements de la ressource
Il ne reste plus qu'à vérifier la récupération des événements dans l'environnement de test et à s'assurer qu'ils correspondent parfaitement à la liste reçue en temps réel. Cela confirmera la possibilité de tester et d'optimiser automatiquement les algorithmes de trading d'actualités.
Pour ce faire, ajoutez la sortie des événements reçus du serveur/téléchargés depuis la ressource au journal du terminal/testeur de stratégie. Le fichier texte complet ImportTesterLog.txt contenant les résultats de l'exécution du test est joint ci-dessous. Ci-dessous figure un extrait du fichier journal : le début et la fin du tableau d’événements pour chaque code de devise.
Résultat d'une exécution unique dans le Testeur de Stratégie, en mode visuel :
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 (data from resource)
Il est clair que tous les tableaux d'événements ont été chargés à partir de la ressource en environ 10 ms, ce qui n'est pas surprenant : la ressource est intégrée au code de l'EA et se charge en même temps que lui au démarrage des tests.
Le lancement du même Expert Advisor en mode Live dans le terminal MetaTrader 5 produit un fichier journal similaire avec une liste d'événements chargés. Le fichier LoadLiveLog.txt complet contenant les résultats du chargement en direct est joint à l'article. Ci-dessous figure un extrait du fichier journal : le début et la fin du tableau d’événements pour chaque code de devise.
Résultats du téléchargement en direct des événements depuis le serveur :
17:27:25.250 🔄 Initializing news module:
17:27:25.258 ✅ Received values BY CURRENCY "USD": 3589 → Of these, filtered: 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 (data from API)
Un chargement complet avec filtrage et écriture dans le journal prend environ 50 ms, ce qui est encore suffisamment rapide pour ignorer le temps de téléchargement/filtrage des événements. En comparant les deux fichiers journaux, on constate que la liste des événements est parfaitement identique, tant en termes de nombre d'événements que de valeur des champs. La validation de la chaîne de transformation des données s'est terminée avec succès.
Analyse des erreurs typiques et des fausses attentes
Examinons les six erreurs les plus courantes commises par les développeurs lors de l'intégration du Calendrier Économique MQL5. Chacune de ces erreurs a été observée en conditions réelles et pourrait coûter très cher au trader, voire conduire à la perte du capital.
1. « Le calendrier prédit la direction du marché »
Fausse attente :
Si une actualité d'une importance capitale est publiée et que le résultat réel diffère sensiblement des prévisions, le prix évoluera forcément dans le sens de l'écart. Achetez simplement si la valeur publiée est supérieure aux prévisions pour la devise de base.
Réalité :
Le Calendrier Économique est une source de données, et non un générateur de signaux de trading. Il vous indique ce qui s'est passé, mais pas comment le marché va réagir. Pourquoi le prix pourrait-il aller à l'encontre d'une logique « évidente » ?
- Le marché avait déjà intégré les anticipations plusieurs jours avant la publication de l’actualité. Au moment même où l'actualité est diffusée, la « vente de l'actualité » a lieu.
- Des données inflationnistes solides pendant une période de resserrement de la politique monétaire des banques centrales peuvent renforcer la monnaie, mais pendant une période d'assouplissement, elles peuvent déclencher une vague de ventes par crainte de surchauffe.
- La statistique publiée peut être bonne, mais si la valeur précédente est révisée à la baisse, le signal global devient ambigu.
- La publication simultanée de données pour plusieurs devises crée des effets croisés qui ne peuvent pas être interprétés de manière linéaire.
La bonne approche consiste à utiliser le calendrier comme filtre de volatilité, et non comme déclencheur d'entrée :
//--- instead of if(actual > forecast) OrderSend(...); //--- use: if(IsHighImpactNewsComingSoon(30)) { //--- reduce the position size or temporarily suspend trading ReduceRiskExposure(); }
2. « Tous les événements HIGH sont d'égale importance »
Fausse attente :
Le champ importance == CALENDAR_IMPORTANCE_HIGH signifie que l'événement est garanti de faire bouger le marché de plus de 50 pips. Nous pouvons exploiter toutes ces actualités en utilisant le même algorithme.
Réalité :
Les valeurs spécifiques de l'énumération ENUM_CALENDAR_EVENT_IMPORTANCE sont une évaluation subjective des éditeurs du calendrier, et non une mesure quantitative de l'impact sur le marché. Prenons par exemple deux événements dont l’importance est qualifiée de HAUTE, mais ayant une importance différente sur le marché.
Le premier événement, les créations d'emplois non agricoles (États-Unis), produit une réaction de volatilité de 80 à 150 points, car il s'agit d'un indicateur clé de l'emploi et il influence la politique de la Fed. Le deuxième événement, l'indice PMI manufacturier (zone euro), donne une réponse de 10 à 30 points, puisqu'il s'agit d'un indicateur sectoriel restreint et qu'il est secondaire pour la BCE.
La bonne approche consiste à compléter le filtrage par importance par une liste de codes d'événements prioritaires :
//--- 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 }
Recommandation :
Créez votre propre classement des événements basé sur l'analyse de la volatilité historique — plus fiable que n'importe quelle étiquette prédéfinie.
3. « Utilisation de TimeLocal() au lieu de TimeTradeServer() »
Fausse attente :
L'heure de l'événement dans la structure MqlCalendarValue::time est spécifiée dans mon fuseau horaire local (ou UTC), je peux donc la comparer avec TimeLocal() ou TimeGMT().
Réalité :
Toutes les fonctions du Calendrier Économique renvoient l'heure dans le fuseau horaire du serveur de trading (TimeTradeServer()). Il s'agit d'un choix architectural qui élimine le besoin de conversion manuelle, mais qui exige de la rigueur de la part du développeur.
Les conséquences de cette erreur sont graves : si votre serveur se trouve dans le fuseau horaire EET (UTC+2) et que vous utilisez TimeLocal() (par exemple MSK, UTC+3), vous vérifierez des événements avec un décalage d'une heure. L'EA peut manquer la publication de l'actualité ou, à l'inverse, y réagir après coup.
La bonne approche consiste à toujours utiliser TimeTradeServer() pour toutes les comparaisons de temps :
//--- 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
Recommandation :
Ajoutez la sortie de TimeToString(TimeTradeServer(), TIME_MINUTES) au journal de débogage et comparez-la avec l'heure de l'événement - elles devraient correspondre sans conversion.
4. « J'ai oublié que les champs d'événement peuvent être vides »
Fausse attente :
Les champs actual_value, forecast_value et autres contiennent toujours des valeurs numériques correctes. Nous pouvons les diviser sans risque par 1 000 000 et comparer.
Réalité :
Selon la documentation, les champs numériques de la structure MqlCalendarValue stockent des valeurs multipliées par un million ou par la constante LONG_MIN si aucune valeur n'est spécifiée. Que se passe-t-il si nous ignorons la vérification :
//--- 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();
La bonne approche consiste à utiliser les méthodes intégrées de la structure MqlCalendarValue pour obtenir des valeurs en toute sécurité :
//--- 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 } } //+------------------------------------------------------------------+
Note :
Les fonctions GetActualValue(), GetForecastValue() et autres renvoient NaN s'il n'y a pas de données. Vérifiez toujours le résultat via MathIsValidNumber() ou MathIsNaN() avant de l'utiliser dans des calculs.
5. « Appeler les fonctions de l'API Calendar dans OnTick() »
Fausse attente :
Pour avoir toujours des données à jour, j'appellerai CalendarValueHistory() à chaque tick. Cela permet à l'EA de ne manquer aucune actualité importante.
Réalité :
Les fonctions du calendrier fonctionnent via un serveur distant auquel MetaTrader 5 se connecte, et sont soumises à des limites strictes en termes de débit de requêtes. L'appel à OnTick() (qui peut se déclencher des dizaines de fois par seconde) entraînera des problèmes :
- Erreur 5204 (ERR_CALENDAR_TOO_MANY_REQUESTS) — blocage temporaire de l'accès au calendrier.
- Retards d'exécution - une requête réseau dans le cycle de trading augmente le glissement.
- Trafic excessif – téléchargement répété des mêmes données.
//--- 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); }
Recommandation :
La fonction OnTick() ne devrait fonctionner qu'avec des données locales. Requêtes réseau - uniquement dans OnInit(), OnTimer() ou par événement utilisateur.
6. « J'ai oublié de recompiler l'expert pour tester sur de nouveaux événements »
Fausse attente :
J'ai mis à jour le fichier de ressources calendar_test_res.bin dans le dossier Files ; le testeur verra désormais automatiquement les nouveaux événements. Il n'est pas nécessaire de recompiler l'EA.
Réalité :
La directive #resource intègre des données dans le fichier exécutable .ex5 au moment de la compilation. Les modifications apportées au fichier .bin externe ne sont pas prises en compte dynamiquement ; l’EA continue d’utiliser la version de la ressource compilée au moment de la compilation.
Symptômes d'erreur :
- Le journal du testeur n'inclut pas les événements ajoutés à la ressource après la dernière compilation.
- La stratégie « ne voit pas » les actualités importantes, même si elles figurent dans le dossier.
- Les résultats des tests diffèrent des attentes, bien que le code soit logiquement correct.
La bonne approche consiste à toujours recompiler l'EA après la mise à jour de la ressource :
//--- 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.
Recommandation :
Ajoutez la sortie de version de la ressource (par exemple, le hachage ou la date de dernière mise à jour) au journal de compilation pour surveiller visuellement la pertinence des données dans .ex5.
Remarques :
Les erreurs évoquées dans cette section ne proviennent pas d'un manque de connaissance de la syntaxe MQL5, mais d'une compréhension simplifiée de la nature des données économiques (indicateurs). Quiconque apprend à éviter ces six erreurs obtiendra non seulement un code fonctionnel, mais aussi un système de trading stable, capable de s'adapter aux fluctuations du marché et aux exigences des régulateurs économiques. C’est cette approche qui distingue un amateur d’un professionnel du trading algorithmique.
Conclusion
Le Calendrier Économique du terminal MetaTrader 5 est un outil puissant. Mais comme tout outil, il nécessite de comprendre le contexte, la discipline dans son utilisation et de prendre en compte les limitations de la plateforme.
Nous avons envisagé une structure d'implémentation et d'utilisation spécifique pour l'API d'actualités de l'EA dans MetaTrader 5. L'article propose un ensemble d'éléments pratiques qui, pris ensemble, transforment le trading manuel d'actualités en un module reproductible :
- comprendre l'API Calendar (Événement vs Valeur, structures MqlCalendarEvent/MqlCalendarValue, heure du serveur) ;
- méthodes de chargement et de mise à jour sécurisées (CalendarValueHistory pour le chargement initial, CalendarValueLast + change_id pour les mises à jour incrémentales) ;
- gestion des erreurs typiques et respect des limites des requêtes ;
- filtrage à plusieurs niveau par devise, importance, code d'événement et fenêtres temporelles pour réduire le bruit et ne retenir que 3 à 5 événements significatifs ;
- un mécanisme d'export des données filtrées vers une ressource binaire et de bascule automatique de la source de données « temps réel » ↔ « Testeur de Stratégie », garantissant un comportement identique en temps réel et lors des backtests.
Critères de préparation du module : les événements de la période spécifiée sont chargés avec succès ; les mises à jour incrémentales fonctionnent via change_id sans dépasser les limites ; dans le Testeur, l’EA lit la ressource et produit les mêmes décisions qu’en conditions réelles avec des données d’entrée identiques.
Étapes suivantes recommandées : implémenter le script d’export, intégrer en dur le fichier .bin dans #resource, tester la logique de mise à jour OnInit/OnTimer et exécuter des backtests contrôlés (par exemple, scénario « ne pas trader pendant 30 minutes / reprendre après 60 minutes »).
Cela vous permettra de passer des hypothèses à un système de trading d'actualités testable et évolutif. Développer des EA de trading sur actualités ne se limite pas à programmer. Il s'agit de construire un pont entre la théorie macroéconomique et la pratique du marché.
Ressources recommandées pour une étude approfondie des fonctions de l'API Calendar de MetaTrader 5 et de leur utilisation dans le trading d'actualités :
- Documentation — Fonctions du Calendrier Économique ;
- Article Trader avec le Calendrier Économique MQL5 (partie 1) : maîtriser les fonctions du Calendrier Économique MQL5 ;
- Article Trader avec le Calendrier Économique MQL5 (partie 7) : préparation aux tests de stratégies grâce à l'analyse des événements d'actualité depuis des ressources ;
- Article Trader avec le Calendrier Économique MQL5 (partie 8) : optimisation du backtesting basé sur les actualités grâce au filtrage intelligent des événements et aux journaux ciblés ;
- Article MQL5 Cookbook – Calendrier Économique.
Liste des fichiers joints à l'article :
| Nom de fichier | Description |
|---|---|
| CalendarEventMonitor-EA.mq5 | Code de test de l’Expert Advisor pour la fonction de mise à jour des données du calendrier |
| ExportCalendarForTester-S.mq5 | Code du script de test permettant de vérifier l'export d'événements avec des filtres spécifiés vers un fichier binaire |
| GetTodayEvents-S.mq5 | Code du script de test permettant de vérifier la récupération des événements du jour. |
| ImportCalendarValidation-EA.mq5 | Code de l’EA de test pour vérifier la récupération des actualités dans le Testeur de Stratégie à partir d'une ressource |
| ImportTesterLog.txt | Résultats d'une seule exécution de l'EA ImportCalendarValidation-EA.mq5 dans le Testeur en mode visuel |
| LoadLiveLog.txt | Résultats du chargement en direct des événements dans l'EA ImportCalendarValidation-EA.mq5 |
Traduit du russe par MetaQuotes Ltd.
Article original : https://www.mql5.com/ru/articles/22196
Avertissement: Tous les droits sur ces documents sont réservés par MetaQuotes Ltd. La copie ou la réimpression de ces documents, en tout ou en partie, est interdite.
Du CPU au GPU en MQL5 : un framework OpenCL pratique pour accélérer la recherche, l’optimisation et la détection de motifs
Trading algorithmique sans tâches routinières : analyse rapide des opérations dans MetaTrader 5 avec SQLite
L'Histogramme des prix (Profile du Marché) et son implémentation en MQL5
Python + MetaTrader 5 : Сadre de recherche rapide pour les données, les variables et les prototypes
- Applications de trading gratuites
- Plus de 8 000 signaux à copier
- Actualités économiques pour explorer les marchés financiers
Vous acceptez la politique du site Web et les conditions d'utilisation
Écrit :
Corrigez-moi si quelque chose a changé dans l'API MQL5, mais auparavant, le calendrier s'adaptait au fuseau horaire ACTUEL en tenant compte de l'heure d'été : ainsi, en été, les appels de fonctions renvoyaient des horodatages, par exemple, dans le fuseau UTC+3, tandis qu’une requête portant sur la même période historique en hiver renverrait des événements horodatés dans le fuseau UTC+2, si le serveur effectue le passage à l’heure d’été et inversement. Ainsi, pour exporter avec précision l’historique du calendrier sur une période de six mois ou plus, il est nécessaire d’analyser l’historique des changements de fuseau horaire du courtier. Plus de détails dans la base de code.
De plus, l’approche consistant à mettre en cache le calendrier sous forme de lien vers une ressource semble peu pratique et, en particulier, provoque des erreurs mentionnées dans l’article lui-même (du type : « n’oubliez pas de recompiler l’expert »). Pourquoi ne pas définir le cache du calendrier dans le paramètre d’entrée sous la forme d’un fichier issu du dossier « Common », accessible à tous les agents ?
Et pourquoi les cotations (et plus généralement tous les horodatages) ne sont-elles pas enregistrées, par exemple, en UTC ?
Pourquoi les ticks des courtiers ont-ils des représentations numériques différentes pour un même repère temporel ?
C'est une question philosophique :-) C'est ainsi que cela s'est fait, même si ce n'est pas correct et que cela engendre des problèmes sans raison.
P.S. C’est pourquoi il vaut mieux puiser les « informations historiques » dans d’autres sources.
Et il ne faut jamais « analyser soi-même l’historique des conversions d’heure » : tout cela existe déjà, c’est une fonctionnalité du système d’exploitation ou, au pire, des bibliothèques système. Cherchez « tzdata » sur Google
Combien de vélos peut-on fabriquer, surtout des vélos tordus ?
Auparavant, le calendrier s'adaptait au fuseau horaire ACTUEL en tenant compte de l'heure d'été ; ainsi, en été, les appels de fonctions renvoyaient des horodatages, par exemple dans le fuseau horaire UTC+3, tandis qu’une requête portant sur la même période historique en hiver renverra des événements horodatés dans le fuseau horaire UTC+2, si le serveur effectue le passage à l’heure d’été et inversement.
P.S. : c'est pourquoi il vaut mieux puiser les « informations historiques » dans d'autres sources.
Et il ne faut jamais « analyser soi-même l'historique des conversions d'horloge » : tout cela existe déjà, c'est une fonction du système d'exploitation ou, tout au plus, des bibliothèques système. Cherchez « tzdata » sur Google.
Combien de vélos peut-on fabriquer, surtout des vélos tordus ?
Les autres sources ne résoudront pas le problème, car celui-ci est ancré dans la manière dont les cotations sont stockées dans MT5.
Commencez par nous montrer votre vélo tout droit, puis donnez-nous des conseils.
Les principaux problèmes auxquels est confronté le trader d'actualité moderne sont la fragmentation de ses outils et l'absence d'un processus de trading systématique et algorithmique. Il est en effet assez difficile de partager son attention entre son navigateur Internet (pour consulter les sites d'actualités) et son terminal de trading lorsqu'on effectue des transactions.
Le flux de travail d’un trader réactif aux actualités se présente ainsi : ouvrir rapidement le calendrier des actualités dans son navigateur web et vérifier s’il y a des changements dans les événements → évaluer rapidement les événements à venir et décider quoi et comment trader → se rendre sur le terminal MetaTrader 5 – soit passer des ordres en attente, soit rester devant le terminal et attendre la publication de l’actualité pour prendre une décision. Dans ce scénario, le trader perd souvent le fil de la situation, ce qui entraîne des retards dans sa réaction aux actualités, et donc des pertes.
Souhaitez-vous que le trading sur l’actualité fonctionne comme un problème d’ingénierie — avec des règles claires, des résultats reproductibles et des tests automatisés ? L’objectif de cet article est de présenter l’architecture fonctionnelle d’une couche d’actualités pour MetaTrader 5 : une source de données unique, une utilisation appropriée de l’API du calendrier, un mécanisme de filtrage et de mise en cache, l’exportation des événements historiques vers une ressource pour le testeur, et le basculement automatique entre le mode Live et le mode Tester — afin que le même code produise des résultats déterministes aussi bien en temps réel qu’avec des données historiques.
Oui, c’est un véritable casse-tête pour le trading sur l’actualité.
Passer du navigateur au calendrier puis au terminal brise la concentration et ralentit considérablement les réactions. Disposer d’un flux de travail ou d’un système unifié qui intègre directement les actualités dans l’environnement de trading rendrait sans aucun doute l’exécution plus rapide et plus cohérente.
L’idée d’en faire une configuration structurée et reproductible, de type « ingénierie », avec des tests et de l’automatisation, est en réalité tout à fait pertinente pour éviter les décisions émotionnelles ou tardives.