English
preview
Handel mit dem MQL5 Wirtschaftskalender (Teil 8): Optimierung des nachrichtengesteuerten Backtests mit intelligenter Ereignisfilterung und gezielten Protokollen

Handel mit dem MQL5 Wirtschaftskalender (Teil 8): Optimierung des nachrichtengesteuerten Backtests mit intelligenter Ereignisfilterung und gezielten Protokollen

MetaTrader 5Handel |
20 0
Allan Munene Mutiiria
Allan Munene Mutiiria

Einführung

In diesem Artikel treiben wir die Serie über den MQL5-Wirtschaftskalender voran, indem wir unser Handelssystem für blitzschnelles, visuell intuitive Backtests optimieren und die Datenvisualisierung sowohl für den Live- als auch für den Offline-Modus nahtlos integrieren, um die Entwicklung nachrichtenorientierter Strategien zu verbessern. Aufbauend auf der Grundlage der ressourcenbasierten Ereignisanalyse von Teil 7 für die Kompatibilität mit Strategy Tester führen wir jetzt intelligente Ereignisfilter und gezielte Protokollierung ein, um die Leistung zu optimieren und sicherzustellen, dass wir Strategien in Echtzeit- und historischen Umgebungen mit minimaler Unordnung effizient visualisieren und testen können. Wir gliedern den Artikel in die folgenden Themen:

  1. Ein visueller Chronograph für reibungslosen, nachrichtengesteuerten Handel über Live- und Offline-Bereiche hinweg
  2. Implementierung in MQL5
  3. Tests und Validierung
  4. Schlussfolgerung

Lassen Sie uns diese Fortschritte erkunden!


Ein visueller Chronograph für reibungslosen, nachrichtengesteuerten Handel über Live- und Offline-Bereiche hinweg

In diesem Teil der Serie stellen wir einen visuellen Chronographen vor - eine Metapher für unser optimiertes System zur Verarbeitung und Protokollierung von Ereignissen - der es uns ermöglichen wird, die zeitliche Landschaft des nachrichtengesteuerten Handels mit Präzision und Effizienz zu navigieren.

Durch die Implementierung einer intelligenten Ereignisfilterung werden wir den Rechenaufwand im Strategy Tester drastisch reduzieren, indem wir nur die relevantesten Nachrichtenereignisse innerhalb eines nutzerdefinierten Datumsbereichs vorselektieren, was gewährleistet, dass die Backtests die Geschwindigkeit und Klarheit des Live-Handels widerspiegelt. Dieser Filtermechanismus, der der präzisen Zeitmessung eines Chronographen ähnelt, wird es uns ermöglichen, uns auf kritische Ereignisse zu konzentrieren, ohne irrelevante Daten zu durchsuchen, und ermöglicht einen nahtlosen Übergang zwischen historischen Simulationen und Echtzeit-Marktanalysen.

Ergänzend dazu wird unser zielgerichtetes Protokollierungssystem als Anzeige des Chronographen fungieren und nur wesentliche Informationen - wie Handelsausführungen und Dashboard-Updates - anzeigen, während überflüssige Protokolle ausgeblendet werden, sodass sowohl im Live- als auch im Offline-Modus eine übersichtliche, ablenkungsfreie Oberfläche erhalten bleibt. Diese duale Visualisierungsfähigkeit stellt sicher, dass wir Strategien mit historischen Daten im Strategy Tester testen und dasselbe intuitive Dashboard im Live-Handel anwenden können. Dadurch wird ein einheitlicher Arbeitsablauf gefördert, der die Entscheidungsfindung und die Verfeinerung von Strategien unter allen Marktbedingungen verbessert. Hier ist eine Visualisierung dessen, was wir erreichen wollen.

GEPLANTE OFFLINE-BEREICHSVISUALISIERUNG


Implementierung in MQL5

Um die Verbesserungen in MQL5 vorzunehmen, müssen wir zunächst einige Variablen deklarieren, die wir verwenden werden, um die heruntergeladenen Ereignisse zu verfolgen, die wir dann problemlos im Nachrichten-Dashboard anzeigen werden, wobei wir ein ähnliches Format verwenden, wie wir es in den vorherigen Artikeln beim Live-Handel getan haben, aber zunächst die Ressource einschließen, in der wir die Daten wie untenstehend speichern.

//---- Include trading library
#include <Trade\Trade.mqh>
CTrade trade;

//---- Define resource for CSV
#resource "\\Files\\Database\\EconomicCalendar.csv" as string EconomicCalendarData

Wir beginnen mit der Integration einer Handelsbibliothek, die eine nahtlose Handelsausführung im Live- und Offline-Modus ermöglicht. Wir verwenden die Direktive „#include <Trade\Trade.mqh>“, um die MQL5-Handelsbibliothek einzubinden, die die Klasse „CTrade“ für die Verwaltung von Handelsoperationen bereitstellt. Indem wir ein Objekt vom Typ „CTrade“ mit dem Namen „trade“ deklarieren, ermöglichen wir dem Programm, Kauf- und Verkaufsaufträge programmatisch auszuführen.

Anschließend verwenden wir die Richtlinie „#resource“, um „\Files\Database\EconomicCalendar.csv“ als String-Ressource mit dem Namen „EconomicCalendarData“ zu definieren. Diese durch Kommas getrennten Werte (CSV), die über die Funktion „LoadEventsFromResource“ geladen werden, liefern Ereignisdetails wie Datum, Uhrzeit, Währung und Prognose und bieten eine einheitliche Datendarstellung ohne Abhängigkeit von Live-Datenfeeds. Nun können wir die restlichen Kontrollvariablen definieren.

//---- Event name tracking
string current_eventNames_data[];
string previous_eventNames_data[];
string last_dashboard_eventNames[]; // Added: Cache for last dashboard event names in tester mode
datetime last_dashboard_update = 0; // Added: Track last dashboard update time in tester mode

//---- Filter flags
bool enableCurrencyFilter = true;
bool enableImportanceFilter = true;
bool enableTimeFilter = true;
bool isDashboardUpdate = true;
bool filters_changed = true;        // Added: Flag to detect filter changes in tester mode

//---- Event counters
int totalEvents_Considered = 0;
int totalEvents_Filtered = 0;
int totalEvents_Displayable = 0;

//---- Input parameters (PART 6)
sinput group "General Calendar Settings"
input ENUM_TIMEFRAMES start_time = PERIOD_H12;
input ENUM_TIMEFRAMES end_time = PERIOD_H12;
input ENUM_TIMEFRAMES range_time = PERIOD_H8;
input bool updateServerTime = true; // Enable/Disable Server Time Update in Panel
input bool debugLogging = false;    // Added: Enable debug logging in tester mode

//---- Input parameters for tester mode (from PART 7, minimal)
sinput group "Strategy Tester CSV Settings"
input datetime StartDate = D'2025.03.01'; // Download Start Date
input datetime EndDate = D'2025.03.21';   // Download End Date

//---- Structure for CSV events (from PART 7)
struct EconomicEvent {
   string eventDate;       // Date of the event
   string eventTime;       // Time of the event
   string currency;        // Currency affected
   string event;           // Event description
   string importance;      // Importance level
   double actual;          // Actual value
   double forecast;        // Forecast value
   double previous;        // Previous value
   datetime eventDateTime; // Added: Store precomputed datetime for efficiency
};

//---- Global array for tester mode events
EconomicEvent allEvents[];
EconomicEvent filteredEvents[]; // Added: Filtered events for tester mode optimization

//---- Trade settings
enum ETradeMode {
   TRADE_BEFORE,
   TRADE_AFTER,
   NO_TRADE,
   PAUSE_TRADING
};
input ETradeMode tradeMode = TRADE_BEFORE;
input int tradeOffsetHours = 12;
input int tradeOffsetMinutes = 5;
input int tradeOffsetSeconds = 0;
input double tradeLotSize = 0.01;

//---- Trade control
bool tradeExecuted = false;
datetime tradedNewsTime = 0;
int triggeredNewsEvents[];

Hier werden die Ereignisnamen in „current_eventNames_data“, „previous_eventNames_data“ und „last_dashboard_eventNames“ gespeichert, wobei „last_dashboard_eventNames“ zur Zwischenspeicherung von Dashboard-Aktualisierungen im Tester-Modus und „last_dashboard_update“ zur Planung von Aktualisierungen nur bei Bedarf verwendet wird, um redundante Verarbeitung zu vermeiden.

Wir schalten die Ereignisfilterung mit „enableCurrencyFilter“, „enableImportanceFilter“, „enableTimeFilter“ und „filters_changed“ ein und setzen die Filter zurück, wenn „filters_changed“ wahr ist, um nur relevante Ereignisse zu verarbeiten und „debugLogging“ unter „sinput group ‚General Calendar Settings‘“ zu verwenden, um nur Trades und Updates zu protokollieren.

Wir definieren den Backtests-Zeitraum mit „StartDate“ und „EndDate“ unter „sinput group ‚Strategy Tester CSV Settings‘“, strukturieren Ereignisse in „EconomicEvent“ mit „eventDateTime“ für einen schnellen Zugriff, und filtern „allEvents“ in „filteredEvents“ für eine schnellere Bearbeitung, während wir „tradeMode“ und die zugehörigen Variablen für eine effiziente Ausführung von Handelsgeschäfte setzen. Jetzt können wir den Testzeitraum wählen, von dem wir die Daten herunterladen und denselben Zeitbereich für den Test verwenden wollen. Dies ist die Nutzeroberfläche, die wir haben.

SCHNITTSTELLE FÜR NUTZEREINGABEN

Aus dem Bild ist ersichtlich, dass wir über zusätzliche Eingänge verfügen, um die Anzeige der Ereignisse im Testermodus zu steuern, sowie über kontrollierte Aktualisierungen der Uhrzeit im Panel und der Protokollierung. Wir haben dies getan, um unnötige Ressourcen beim Backtests zu optimieren. Nun müssen wir eine Funktion definieren, die die Filterung der Testereignisse vornimmt.

//+------------------------------------------------------------------+
//| Filter events for tester mode                                    | // Added: Function to pre-filter events by date range
//+------------------------------------------------------------------+
void FilterEventsForTester() {
   ArrayResize(filteredEvents, 0);
   int eventIndex = 0;
   for (int i = 0; i < ArraySize(allEvents); i++) {
      datetime eventDateTime = allEvents[i].eventDateTime;
      if (eventDateTime < StartDate || eventDateTime > EndDate) {
         if (debugLogging) Print("Event ", allEvents[i].event, " skipped in filter due to date range: ", TimeToString(eventDateTime)); // Modified: Conditional logging
         continue;
      }
      ArrayResize(filteredEvents, eventIndex + 1);
      filteredEvents[eventIndex] = allEvents[i];
      eventIndex++;
   }
   if (debugLogging) Print("Tester mode: Filtered ", eventIndex, " events."); // Modified: Conditional logging
   filters_changed = false;
}

Hier implementieren wir eine intelligente Ereignisfilterung, um die Backtests zu beschleunigen, indem wir die Anzahl der im Strategy Tester verarbeiteten Nachrichtenereignisse reduzieren. Wir verwenden die Funktion „FilterEventsForTester“, um das Array „filteredEvents“ mit der Funktion ArrayResize zu leeren und es mit den relevanten Ereignissen aus „allEvents“ neu zu füllen. Für jedes Ereignis wird die „eventDateTime“ mit „StartDate“ und „EndDate“ verglichen, wobei diejenigen, die außerhalb des Bereichs liegen, übersprungen werden, und nur dann mit Print protokolliert, wenn „debugLogging“ wahr ist.

Wir kopieren qualifizierte Ereignisse in „filteredEvents“ mit dem Index „eventIndex“, wobei wir diesen bei jeder Hinzufügung erhöhen, und verwenden die Funktion „ArrayResize“, um dynamisch Platz zuzuweisen. Wir protokollieren die Gesamtzahl der „eventIndex“-Ereignisse über „Print“ nur, wenn „debugLogging“ aktiviert ist, um die Ausgabe des Testers sauber zu halten, und setzen „filters_changed“ auf false, um zu signalisieren, dass die Filterung abgeschlossen ist. Durch diese gezielte Filterung wird der Ereignissatz verkleinert, was die nachfolgende Verarbeitung beschleunigt und eine effiziente Visualisierung von Nachrichtenereignissen im Offline-Modus ermöglicht. Wir rufen diese Funktion dann in OnInit auf, um die Nachrichtendaten vorzufiltern.

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit() {
   //---- Create dashboard UI
   createRecLabel(MAIN_REC,50,50,740,410,clrSeaGreen,1);
   createRecLabel(SUB_REC1,50+3,50+30,740-3-3,410-30-3,clrWhite,1);
   createRecLabel(SUB_REC2,50+3+5,50+30+50+27,740-3-3-5-5,410-30-3-50-27-10,clrGreen,1);
   createLabel(HEADER_LABEL,50+3+5,50+5,"MQL5 Economic Calendar",clrWhite,15);

   //---- Create calendar buttons
   int startX = 59;
   for (int i = 0; i < ArraySize(array_calendar); i++) {
      createButton(ARRAY_CALENDAR+IntegerToString(i),startX,132,buttons[i],25,
                   array_calendar[i],clrWhite,13,clrGreen,clrNONE,"Calibri Bold");
      startX += buttons[i]+3;
   }

   //---- Initialize for live mode (unchanged)
   int totalNews = 0;
   bool isNews = false;
   MqlCalendarValue values[];
   datetime startTime = TimeTradeServer() - PeriodSeconds(start_time);
   datetime endTime = TimeTradeServer() + PeriodSeconds(end_time);
   string country_code = "US";
   string currency_base = SymbolInfoString(_Symbol,SYMBOL_CURRENCY_BASE);
   int allValues = CalendarValueHistory(values,startTime,endTime,NULL,NULL);

   //---- Load CSV events for tester mode
   if (MQLInfoInteger(MQL_TESTER)) {
      if (!LoadEventsFromResource()) {
         Print("Failed to load events from CSV resource.");
         return(INIT_FAILED);
      }
      Print("Tester mode: Loaded ", ArraySize(allEvents), " events from CSV.");
      FilterEventsForTester(); // Added: Pre-filter events for tester mode
   }

   //---- Create UI elements
   createLabel(TIME_LABEL,70,85,"Server Time: "+TimeToString(TimeCurrent(),TIME_DATE|TIME_SECONDS)+
               "   |||   Total News: "+IntegerToString(allValues),clrBlack,14,"Times new roman bold");
   createLabel(IMPACT_LABEL,70,105,"Impact: ",clrBlack,14,"Times new roman bold");
   createLabel(FILTER_LABEL,370,55,"Filters:",clrYellow,16,"Impact");

   //---- Create filter buttons
   string filter_curr_text = enableCurrencyFilter ? ShortToString(0x2714)+"Currency" : ShortToString(0x274C)+"Currency";
   color filter_curr_txt_color = enableCurrencyFilter ? clrLime : clrRed;
   bool filter_curr_state = enableCurrencyFilter;
   createButton(FILTER_CURR_BTN,430,55,110,26,filter_curr_text,filter_curr_txt_color,12,clrBlack);
   ObjectSetInteger(0,FILTER_CURR_BTN,OBJPROP_STATE,filter_curr_state);

   string filter_imp_text = enableImportanceFilter ? ShortToString(0x2714)+"Importance" : ShortToString(0x274C)+"Importance";
   color filter_imp_txt_color = enableImportanceFilter ? clrLime : clrRed;
   bool filter_imp_state = enableImportanceFilter;
   createButton(FILTER_IMP_BTN,430+110,55,120,26,filter_imp_text,filter_imp_txt_color,12,clrBlack);
   ObjectSetInteger(0,FILTER_IMP_BTN,OBJPROP_STATE,filter_imp_state);

   string filter_time_text = enableTimeFilter ? ShortToString(0x2714)+"Time" : ShortToString(0x274C)+"Time";
   color filter_time_txt_color = enableTimeFilter ? clrLime : clrRed;
   bool filter_time_state = enableTimeFilter;
   createButton(FILTER_TIME_BTN,430+110+120,55,70,26,filter_time_text,filter_time_txt_color,12,clrBlack);
   ObjectSetInteger(0,FILTER_TIME_BTN,OBJPROP_STATE,filter_time_state);

   createButton(CANCEL_BTN,430+110+120+79,51,50,30,"X",clrWhite,17,clrRed,clrNONE);

   //---- Create impact buttons
   int impact_size = 100;
   for (int i = 0; i < ArraySize(impact_labels); i++) {
      color impact_color = clrBlack, label_color = clrBlack;
      if (impact_labels[i] == "None") label_color = clrWhite;
      else if (impact_labels[i] == "Low") impact_color = clrYellow;
      else if (impact_labels[i] == "Medium") impact_color = clrOrange;
      else if (impact_labels[i] == "High") impact_color = clrRed;
      createButton(IMPACT_LABEL+string(i),140+impact_size*i,105,impact_size,25,
                   impact_labels[i],label_color,12,impact_color,clrBlack);
   }

   //---- Create currency buttons
   int curr_size = 51, button_height = 22, spacing_x = 0, spacing_y = 3, max_columns = 4;
   for (int i = 0; i < ArraySize(curr_filter); i++) {
      int row = i / max_columns;
      int col = i % max_columns;
      int x_pos = 575 + col * (curr_size + spacing_x);
      int y_pos = 83 + row * (button_height + spacing_y);
      createButton(CURRENCY_BTNS+IntegerToString(i),x_pos,y_pos,curr_size,button_height,curr_filter[i],clrBlack);
   }

   //---- Initialize filters
   if (enableCurrencyFilter) {
      ArrayFree(curr_filter_selected);
      ArrayCopy(curr_filter_selected, curr_filter);
      Print("CURRENCY FILTER ENABLED");
      ArrayPrint(curr_filter_selected);
      for (int i = 0; i < ArraySize(curr_filter_selected); i++) {
         ObjectSetInteger(0, CURRENCY_BTNS+IntegerToString(i), OBJPROP_STATE, true);
      }
   }

   if (enableImportanceFilter) {
      ArrayFree(imp_filter_selected);
      ArrayCopy(imp_filter_selected, allowed_importance_levels);
      ArrayFree(impact_filter_selected);
      ArrayCopy(impact_filter_selected, impact_labels);
      Print("IMPORTANCE FILTER ENABLED");
      ArrayPrint(imp_filter_selected);
      ArrayPrint(impact_filter_selected);
      for (int i = 0; i < ArraySize(imp_filter_selected); i++) {
         string btn_name = IMPACT_LABEL+string(i);
         ObjectSetInteger(0, btn_name, OBJPROP_STATE, true);
         ObjectSetInteger(0, btn_name, OBJPROP_BORDER_COLOR, clrNONE);
      }
   }

   //---- Update dashboard
   update_dashboard_values(curr_filter_selected, imp_filter_selected);
   ChartRedraw(0);
   return(INIT_SUCCEEDED);
}

Wir verwenden die Funktion „createRecLabel“, um die Dashboard-Panels „MAIN_REC“, „SUB_REC1“ und „SUB_REC2“ mit unterschiedlichen Farben und Größen zu erstellen, und die Funktion „createLabel“, um ein „HEADER_LABEL“ hinzuzufügen, das „MQL5 Economic Calendar“ anzeigt, wie wir es zuvor getan haben. Wir erstellen Kalenderschaltflächen dynamisch aus „array_calendar“ mit den Funktionen „createButton“ und ArraySize und positionieren sie mit „startX“ und „buttons“ für die Ereignisanzeige.

Wir bereiten den Live-Modus vor, indem wir Ereignisse mit der Funktion CalendarValueHistory in „values“ abrufen, wobei „startTime“ und „endTime“ über TimeTradeServer und PeriodSeconds berechnet werden, und für den Testermodus verwenden wir die Funktion MQLInfoInteger, um MQL_TESTER zu prüfen, und laden „EconomicCalendarData“ mit der Funktion „LoadEventsFromResource“ in „allEvents“. Wir verwenden die Funktion „FilterEventsForTester“, die hier am wichtigsten ist, um „filteredEvents“ zu füllen und die Ereignisverarbeitung zu optimieren.

Wir fügen UI-Elemente wie „TIME_LABEL“, „IMPACT_LABEL“ und „FILTER_LABEL“ mit „createLabel“ und Filterschaltflächen „FILTER_CURR_BTN“, „FILTER_IMP_BTN“, „FILTER_TIME_BTN“ und „CANCEL_BTN“ mit „createButton“ und ObjectSetInteger, wobei Zustände wie „filter_curr_state“ auf der Grundlage von „enableCurrencyFilter“ gesetzt werden. Wir erstellen Schaltflächen für Auswirkungen und Währungen aus „impact_labels“ und „curr_filter“ mit „createButton“, initialisieren die Filter „curr_filter_selected“ und „imp_filter_selected“ mit ArrayFree und ArrayCopy, und aktualisieren das Dashboard mit „update_dashboard_values“ und ChartRedraw, wobei „INIT_SUCCEEDED“ zurückgegeben wird, um die Einrichtung zu bestätigen. Wenn wir nun das Programm initialisieren, erhalten wir folgendes Ergebnis.

TESTERGEBNIS

Da wir nun die relevanten Daten nach der Filterung in OnTick laden können, müssen wir sicherstellen, dass wir die relevanten Daten innerhalb eines bestimmten Zeitraums erhalten und sie in das Dashboard einfügen, anstatt nur alle Daten, so wie wir es im Live-Modus tun. Hier ist die Logik, die wir anwenden, und bevor wir es vergessen, haben wir den spezifischen und wichtigen Aktualisierungsabschnitten, in denen wir die Änderungen vorgenommen haben, entsprechende Kommentare hinzugefügt.

//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick() {
   UpdateFilterInfo();
   CheckForNewsTrade();
   if (isDashboardUpdate) {
      if (MQLInfoInteger(MQL_TESTER)) {
         datetime currentTime = TimeTradeServer();
         datetime timeRange = PeriodSeconds(range_time);
         datetime timeAfter = currentTime + timeRange;
         if (filters_changed || last_dashboard_update < timeAfter) { // Modified: Update on filter change or time range shift
            update_dashboard_values(curr_filter_selected, imp_filter_selected);
            ArrayFree(last_dashboard_eventNames);
            ArrayCopy(last_dashboard_eventNames, current_eventNames_data);
            last_dashboard_update = currentTime;
         }
      } else {
         update_dashboard_values(curr_filter_selected, imp_filter_selected);
      }
   }
}

In OnTick verwenden wir die Funktion „UpdateFilterInfo“, um die Filtereinstellungen zu aktualisieren, und die Funktion „CheckForNewsTrade“, um Trades auf der Grundlage von Nachrichtenereignissen zu bewerten und auszuführen. Wenn „isDashboardUpdate“ wahr ist, prüfen wir MQL_TESTER mit der Funktion MQLInfoInteger, um testerspezifische Logik anzuwenden, wobei „currentTime“ mit TimeTradeServer, „timeRange“ mit PeriodSeconds auf „range_time“ und „timeAfter“ als „currentTime“ plus „timeRange“ berechnet wird.

Im Testmodus verwenden wir die Bedingung „filters_changed“ oder „last_dashboard_update“ kleiner als „timeAfter“, um die „update_dashboard_values“ Funktion mit „curr_filter_selected“ und „imp_filter_selected“ auszulösen, „last_dashboard_eventNames“ mit der Funktion ArrayFree zu löschen, „current_eventNames_data“ mit ArrayCopy dorthin zu kopieren und „last_dashboard_update“ auf „currentTime“ zu aktualisieren, wodurch Auffrischungen minimiert werden. Im Live-Modus rufen wir direkt „update_dashboard_values“ für kontinuierliche Aktualisierungen auf und gewährleisten so eine optimierte, zielgerichtete Dashboard-Visualisierung in beiden Modi. Wir können nun die Funktionen, die wir verwenden, wie folgt ändern und sicherstellen, dass sie die entsprechenden Änderungen, insbesondere die Zeiteinteilung, enthalten.

//+------------------------------------------------------------------+
//| Load events from CSV resource                                    |
//+------------------------------------------------------------------+
bool LoadEventsFromResource() {
   string fileData = EconomicCalendarData;
   Print("Raw resource content (size: ", StringLen(fileData), " bytes):\n", fileData);
   string lines[];
   int lineCount = StringSplit(fileData, '\n', lines);
   if (lineCount <= 1) {
      Print("Error: No data lines found in resource! Raw data: ", fileData);
      return false;
   }
   ArrayResize(allEvents, 0);
   int eventIndex = 0;
   for (int i = 1; i < lineCount; i++) {
      if (StringLen(lines[i]) == 0) {
         if (debugLogging) Print("Skipping empty line ", i); // Modified: Conditional logging
         continue;
      }
      string fields[];
      int fieldCount = StringSplit(lines[i], ',', fields);
      if (debugLogging) Print("Line ", i, ": ", lines[i], " (field count: ", fieldCount, ")"); // Modified: Conditional logging
      if (fieldCount < 8) {
         Print("Malformed line ", i, ": ", lines[i], " (field count: ", fieldCount, ")");
         continue;
      }
      string dateStr = fields[0];
      string timeStr = fields[1];
      string currency = fields[2];
      string event = fields[3];
      for (int j = 4; j < fieldCount - 4; j++) {
         event += "," + fields[j];
      }
      string importance = fields[fieldCount - 4];
      string actualStr = fields[fieldCount - 3];
      string forecastStr = fields[fieldCount - 2];
      string previousStr = fields[fieldCount - 1];
      datetime eventDateTime = StringToTime(dateStr + " " + timeStr);
      if (eventDateTime == 0) {
         Print("Error: Invalid datetime conversion for line ", i, ": ", dateStr, " ", timeStr);
         continue;
      }
      ArrayResize(allEvents, eventIndex + 1);
      allEvents[eventIndex].eventDate = dateStr;
      allEvents[eventIndex].eventTime = timeStr;
      allEvents[eventIndex].currency = currency;
      allEvents[eventIndex].event = event;
      allEvents[eventIndex].importance = importance;
      allEvents[eventIndex].actual = StringToDouble(actualStr);
      allEvents[eventIndex].forecast = StringToDouble(forecastStr);
      allEvents[eventIndex].previous = StringToDouble(previousStr);
      allEvents[eventIndex].eventDateTime = eventDateTime; // Added: Store precomputed datetime
      if (debugLogging) Print("Loaded event ", eventIndex, ": ", dateStr, " ", timeStr, ", ", currency, ", ", event); // Modified: Conditional logging
      eventIndex++;
   }
   Print("Loaded ", eventIndex, " events from resource into array.");
   return eventIndex > 0;
}

Hier laden wir historische Nachrichtenereignisse aus einer CSV-Ressource, um Offline-Backtests mit optimierter Ereignisbehandlung und gezielter Protokollierung zu ermöglichen. Wir verwenden die Funktion „LoadEventsFromResource“, um „EconomicCalendarData“ in „fileData“ einzulesen, und protokollieren seine Größe mit den Funktionen Print und StringLen. Wir teilen „fileData“ mit der Funktion StringSplit in „lines“ auf, überprüfen „lineCount“, um sicherzustellen, dass Daten vorhanden sind, und löschen „allEvents“ mit der Funktion ArrayResize.

Wir iterieren durch „Zeilen“, überspringen leere Zeilen mit der Funktion „StringLen“ und protokollieren nur, wenn „debugLogging“ wahr ist. Wir verwenden „StringSplit“, um jede Zeile in „fields“ zu zerlegen, „fieldCount“ zu überprüfen und „dateStr“, „timeStr“, „currency“, „event“, „importance“, „actualStr“, „forecastStr“ und „previousStr“, wobei die Ereignisfelder dynamisch kombiniert werden.

Wir konvertieren „dateStr“ und „timeStr“ in „eventDateTime“ mit der Funktion StringToTime und speichern sie in „allEvents[eventIndex].eventDateTime“, füllen „allEvents“ mit „ArrayResize“ und StringToDouble auf, protokollieren erfolgreiche Ladevorgänge bedingt und geben true zurück, wenn „eventIndex“ positiv ist, um einen robusten Ereignisdatensatz für Backtests zu gewährleisten. Jetzt aktualisieren wir noch die Funktion, die für die Aktualisierung der Dashboard-Werte verantwortlich ist, die für die Visualisierung der gespeicherten Ereignisdaten entscheidend ist (siehe unten).

//+------------------------------------------------------------------+
//| Update dashboard values                                          |
//+------------------------------------------------------------------+
void update_dashboard_values(string &curr_filter_array[], ENUM_CALENDAR_EVENT_IMPORTANCE &imp_filter_array[]) {
   totalEvents_Considered = 0;
   totalEvents_Filtered = 0;
   totalEvents_Displayable = 0;
   ArrayFree(current_eventNames_data);

   datetime timeRange = PeriodSeconds(range_time);
   datetime timeBefore = TimeTradeServer() - timeRange;
   datetime timeAfter = TimeTradeServer() + timeRange;

   int startY = 162;

   if (MQLInfoInteger(MQL_TESTER)) {
      if (filters_changed) FilterEventsForTester(); // Added: Re-filter events if filters changed
      //---- Tester mode: Process filtered events
      for (int i = 0; i < ArraySize(filteredEvents); i++) {
         totalEvents_Considered++;
         datetime eventDateTime = filteredEvents[i].eventDateTime;
         if (eventDateTime < StartDate || eventDateTime > EndDate) {
            if (debugLogging) Print("Event ", filteredEvents[i].event, " skipped due to date range."); // Modified: Conditional logging
            continue;
         }

         bool timeMatch = !enableTimeFilter;
         if (enableTimeFilter) {
            if (eventDateTime <= TimeTradeServer() && eventDateTime >= timeBefore) timeMatch = true;
            else if (eventDateTime >= TimeTradeServer() && eventDateTime <= timeAfter) timeMatch = true;
         }
         if (!timeMatch) {
            if (debugLogging) Print("Event ", filteredEvents[i].event, " skipped due to time filter."); // Modified: Conditional logging
            continue;
         }

         bool currencyMatch = !enableCurrencyFilter;
         if (enableCurrencyFilter) {
            for (int j = 0; j < ArraySize(curr_filter_array); j++) {
               if (filteredEvents[i].currency == curr_filter_array[j]) {
                  currencyMatch = true;
                  break;
               }
            }
         }
         if (!currencyMatch) {
            if (debugLogging) Print("Event ", filteredEvents[i].event, " skipped due to currency filter."); // Modified: Conditional logging
            continue;
         }

         bool importanceMatch = !enableImportanceFilter;
         if (enableImportanceFilter) {
            string imp_str = filteredEvents[i].importance;
            ENUM_CALENDAR_EVENT_IMPORTANCE event_imp = (imp_str == "None") ? CALENDAR_IMPORTANCE_NONE :
                                                      (imp_str == "Low") ? CALENDAR_IMPORTANCE_LOW :
                                                      (imp_str == "Medium") ? CALENDAR_IMPORTANCE_MODERATE :
                                                      CALENDAR_IMPORTANCE_HIGH;
            for (int k = 0; k < ArraySize(imp_filter_array); k++) {
               if (event_imp == imp_filter_array[k]) {
                  importanceMatch = true;
                  break;
               }
            }
         }
         if (!importanceMatch) {
            if (debugLogging) Print("Event ", filteredEvents[i].event, " skipped due to importance filter."); // Modified: Conditional logging
            continue;
         }

         totalEvents_Filtered++;
         if (totalEvents_Displayable >= 11) continue;
         totalEvents_Displayable++;

         color holder_color = (totalEvents_Displayable % 2 == 0) ? C'213,227,207' : clrWhite;
         createRecLabel(DATA_HOLDERS+string(totalEvents_Displayable),62,startY-1,716,26+1,holder_color,1,clrNONE);

         int startX = 65;
         string news_data[ArraySize(array_calendar)];
         news_data[0] = filteredEvents[i].eventDate;
         news_data[1] = filteredEvents[i].eventTime;
         news_data[2] = filteredEvents[i].currency;
         color importance_color = clrBlack;
         if (filteredEvents[i].importance == "Low") importance_color = clrYellow;
         else if (filteredEvents[i].importance == "Medium") importance_color = clrOrange;
         else if (filteredEvents[i].importance == "High") importance_color = clrRed;
         news_data[3] = ShortToString(0x25CF);
         news_data[4] = filteredEvents[i].event;
         news_data[5] = DoubleToString(filteredEvents[i].actual, 3);
         news_data[6] = DoubleToString(filteredEvents[i].forecast, 3);
         news_data[7] = DoubleToString(filteredEvents[i].previous, 3);

         for (int k = 0; k < ArraySize(array_calendar); k++) {
            if (k == 3) {
               createLabel(ARRAY_NEWS+IntegerToString(i)+" "+array_calendar[k],startX,startY-(22-12),news_data[k],importance_color,22,"Calibri");
            } else {
               createLabel(ARRAY_NEWS+IntegerToString(i)+" "+array_calendar[k],startX,startY,news_data[k],clrBlack,12,"Calibri");
            }
            startX += buttons[k]+3;
         }

         ArrayResize(current_eventNames_data, ArraySize(current_eventNames_data)+1);
         current_eventNames_data[ArraySize(current_eventNames_data)-1] = filteredEvents[i].event;
         startY += 25;
      }
   } else {

      //---- Live mode: Unchanged

   }
}

Um gefilterte Nachrichtenereignisse effizient anzuzeigen, verwenden wir die Funktion „update_dashboard_values“, um „totalEvents_Considered“, „totalEvents_Filtered“, „totalEvents_Displayable“ zurückzusetzen und „current_eventNames_data“ mit der Funktion ArrayFree zurückzusetzen, „timeRange“ über die Funktion PeriodSeconds auf „range_time“ zu setzen und „timeBefore“ und „timeAfter“ mit TimeTradeServer zu berechnen. Wir prüfen „MQL_TESTER“ mit der Funktion MQLInfoInteger und verwenden, wenn „filters_changed“ wahr ist, die Funktion „FilterEventsForTester“, die wir zuvor vollständig definiert hatten, um „filteredEvents“ zu aktualisieren.

Wir iterieren durch „filteredEvents“ mit der Funktion ArraySize, erhöhen „totalEvents_Considered“ und überspringen Ereignisse außerhalb von „StartDate“ oder „EndDate“ liegen oder die Prüfungen „enableTimeFilter“, „enableCurrencyFilter“ oder „enableImportanceFilter“ nicht bestanden haben, wird die Protokollierung nur übersprungen, wenn „debugLogging“ wahr ist.

Für bis zu 11 übereinstimmende Ereignisse erhöhen wir „totalEvents_Displayable“, verwenden die „createRecLabel“-Funktion, um „DATA_HOLDERS“-Zeilen zu zeichnen, und verwenden die „createLabel“-Funktion, um „news_data“ aus „filteredEvents“ aufzufüllen Felder wie „eventDate“ und „event“, gestylt mit „importance_color“ und „array_calendar“, Größenänderung von „current_eventNames_data“ mit ArrayResize zum Speichern von Ereignisnamen, um eine schnelle, klare Dashboard-Visualisierung zu gewährleisten. Um im Testmodus zu handeln, ändern wir die Funktion, die für die Prüfung und die Eröffnung von Handelsgeschäften verantwortlich ist, wie folgt.

//+------------------------------------------------------------------+
//| Check for news trade (adapted for tester mode trading)           |
//+------------------------------------------------------------------+
void CheckForNewsTrade() {
   if (!MQLInfoInteger(MQL_TESTER) || debugLogging) Print("CheckForNewsTrade called at: ", TimeToString(TimeTradeServer(), TIME_SECONDS)); // Modified: Conditional logging
   if (tradeMode == NO_TRADE || tradeMode == PAUSE_TRADING) {
      if (ObjectFind(0, "NewsCountdown") >= 0) {
         ObjectDelete(0, "NewsCountdown");
         Print("Trading disabled. Countdown removed.");
      }
      return;
   }

   datetime currentTime = TimeTradeServer();
   int offsetSeconds = tradeOffsetHours * 3600 + tradeOffsetMinutes * 60 + tradeOffsetSeconds;

   if (tradeExecuted) {
      if (currentTime < tradedNewsTime) {
         int remainingSeconds = (int)(tradedNewsTime - currentTime);
         int hrs = remainingSeconds / 3600;
         int mins = (remainingSeconds % 3600) / 60;
         int secs = remainingSeconds % 60;
         string countdownText = "News in: " + IntegerToString(hrs) + "h " +
                               IntegerToString(mins) + "m " + IntegerToString(secs) + "s";
         if (ObjectFind(0, "NewsCountdown") < 0) {
            createButton1("NewsCountdown", 50, 17, 300, 30, countdownText, clrWhite, 12, clrBlue, clrBlack);
            Print("Post-trade countdown created: ", countdownText);
         } else {
            updateLabel1("NewsCountdown", countdownText);
            Print("Post-trade countdown updated: ", countdownText);
         }
      } else {
         int elapsed = (int)(currentTime - tradedNewsTime);
         if (elapsed < 15) {
            int remainingDelay = 15 - elapsed;
            string countdownText = "News Released, resetting in: " + IntegerToString(remainingDelay) + "s";
            if (ObjectFind(0, "NewsCountdown") < 0) {
               createButton1("NewsCountdown", 50, 17, 300, 30, countdownText, clrWhite, 12, clrRed, clrBlack);
               ObjectSetInteger(0,"NewsCountdown",OBJPROP_BGCOLOR,clrRed);
               Print("Post-trade reset countdown created: ", countdownText);
            } else {
               updateLabel1("NewsCountdown", countdownText);
               ObjectSetInteger(0,"NewsCountdown",OBJPROP_BGCOLOR,clrRed);
               Print("Post-trade reset countdown updated: ", countdownText);
            }
         } else {
            Print("News Released. Resetting trade status after 15 seconds.");
            if (ObjectFind(0, "NewsCountdown") >= 0) ObjectDelete(0, "NewsCountdown");
            tradeExecuted = false;
         }
      }
      return;
   }

   datetime lowerBound = currentTime - PeriodSeconds(start_time);
   datetime upperBound = currentTime + PeriodSeconds(end_time);
   if (debugLogging) Print("Event time range: ", TimeToString(lowerBound, TIME_SECONDS), " to ", TimeToString(upperBound, TIME_SECONDS)); // Modified: Conditional logging

   datetime candidateEventTime = 0;
   string candidateEventName = "";
   string candidateTradeSide = "";
   int candidateEventID = -1;

   if (MQLInfoInteger(MQL_TESTER)) {
      //---- Tester mode: Process filtered events
      int totalValues = ArraySize(filteredEvents);
      if (debugLogging) Print("Total events found: ", totalValues); // Modified: Conditional logging
      if (totalValues <= 0) {
         if (ObjectFind(0, "NewsCountdown") >= 0) ObjectDelete(0, "NewsCountdown");
         return;
      }

      for (int i = 0; i < totalValues; i++) {
         datetime eventTime = filteredEvents[i].eventDateTime;
         if (eventTime < lowerBound || eventTime > upperBound || eventTime < StartDate || eventTime > EndDate) {
            if (debugLogging) Print("Event ", filteredEvents[i].event, " skipped due to date range."); // Modified: Conditional logging
            continue;
         }

         bool currencyMatch = !enableCurrencyFilter;
         if (enableCurrencyFilter) {
            for (int k = 0; k < ArraySize(curr_filter_selected); k++) {
               if (filteredEvents[i].currency == curr_filter_selected[k]) {
                  currencyMatch = true;
                  break;
               }
            }
            if (!currencyMatch) {
               if (debugLogging) Print("Event ", filteredEvents[i].event, " skipped due to currency filter."); // Modified: Conditional logging
               continue;
            }
         }

         bool impactMatch = !enableImportanceFilter;
         if (enableImportanceFilter) {
            string imp_str = filteredEvents[i].importance;
            ENUM_CALENDAR_EVENT_IMPORTANCE event_imp = (imp_str == "None") ? CALENDAR_IMPORTANCE_NONE :
                                                      (imp_str == "Low") ? CALENDAR_IMPORTANCE_LOW :
                                                      (imp_str == "Medium") ? CALENDAR_IMPORTANCE_MODERATE :
                                                      CALENDAR_IMPORTANCE_HIGH;
            for (int k = 0; k < ArraySize(imp_filter_selected); k++) {
               if (event_imp == imp_filter_selected[k]) {
                  impactMatch = true;
                  break;
               }
            }
            if (!impactMatch) {
               if (debugLogging) Print("Event ", filteredEvents[i].event, " skipped due to impact filter."); // Modified: Conditional logging
               continue;
            }
         }

         bool alreadyTriggered = false;
         for (int j = 0; j < ArraySize(triggeredNewsEvents); j++) {
            if (triggeredNewsEvents[j] == i) {
               alreadyTriggered = true;
               break;
            }
         }
         if (alreadyTriggered) {
            if (debugLogging) Print("Event ", filteredEvents[i].event, " already triggered a trade. Skipping."); // Modified: Conditional logging
            continue;
         }

         if (tradeMode == TRADE_BEFORE) {
            if (currentTime >= (eventTime - offsetSeconds) && currentTime < eventTime) {
               double forecast = filteredEvents[i].forecast;
               double previous = filteredEvents[i].previous;
               if (forecast == 0.0 || previous == 0.0) {
                  if (debugLogging) Print("Skipping event ", filteredEvents[i].event, " because forecast or previous value is empty."); // Modified: Conditional logging
                  continue;
               }
               if (forecast == previous) {
                  if (debugLogging) Print("Skipping event ", filteredEvents[i].event, " because forecast equals previous."); // Modified: Conditional logging
                  continue;
               }
               if (candidateEventTime == 0 || eventTime < candidateEventTime) {
                  candidateEventTime = eventTime;
                  candidateEventName = filteredEvents[i].event;
                  candidateEventID = i;
                  candidateTradeSide = (forecast > previous) ? "BUY" : "SELL";
                  if (debugLogging) Print("Candidate event: ", filteredEvents[i].event, " with event time: ", TimeToString(eventTime, TIME_SECONDS), " Side: ", candidateTradeSide); // Modified: Conditional logging
               }
            }
         }
      }
   } else {

      //---- Live mode: Unchanged

   }
}

Um nachrichtengesteuerte Handelsgeschäfte im Testermodus mit optimierter Ereignisfilterung und gezielter Protokollierung für effiziente Backtests zu bewerten und auszulösen, verwenden wir die Funktion „CheckForNewsTrade“ zum Starten und protokollieren ihre Ausführung nur, wenn „debugLogging“ mit der Funktion Print, TimeToString und „TimeTradeServer“ für den aktuellen Zeitstempel wahr ist, um die Testerprotokolle sauber zu halten. Wir beenden den Handel, wenn „tradeMode“ „NO_TRADE“ oder „PAUSE_TRADING“ ist, suchen mit der Funktion ObjectFind nach „NewsCountdown“ und entfernen es mit ObjectDelete, während wir über „Print“ protokollieren und die Verwaltung der Zustände nach einem Handel durch Berechnung von „currentTime“ mit TimeTradeServer und „offsetSeconds“ aus „tradeOffsetHours“, „tradeOffsetMinutes“ und „tradeOffsetSeconds“.

Wenn „tradeExecuted“ wahr ist, verarbeiten wir Countdown-Timer für „tradedNewsTime“, formatieren „countdownText“ mit IntegerToString, um die verbleibende Zeit anzuzeigen oder die Verzögerung zurückzusetzen, erstellen oder aktualisieren „NewsCountdown“ mit „createButton1“ oder „updateLabel1“ auf der Grundlage von „ObjectFind“, die Formatierung mit ObjectSetInteger und die Protokollierung über „Print“, das Zurücksetzen von „tradeExecuted“ nach 15 Sekunden mit „ObjectDelete“ und „Print“.

Im Testermodus, bestätigt durch MQLInfoInteger, das MQL_TESTER prüft, verarbeiten wir „filteredEvents“ mit ArraySize, um „totalValues“ zu erhalten, protokollieren es bedingt mit „Print“ und beenden, wenn leer, nachdem wir „NewsCountdown“ gelöscht haben. Wir setzen „lowerBound“ und „upperBound“ mit „TimeTradeServer“ und PeriodSeconds auf „start_time“ und „end_time“, protokollieren den Bereich mit „Print“ wenn „debugLogging“ wahr ist, und initialisieren „candidateEventTime“, „candidateEventName“, „candidateEventID“ und „candidateTradeSide“ für die Handelsauswahl.

Wir iterieren über „filteredEvents“, wobei wir Ereignisse außerhalb von „lowerBound“, „upperBound“, „StartDate“ oder „EndDate“ überspringen, oder wenn „enableCurrencyFilter“ gegen „curr_filter_selected“ oder „enableImportanceFilter“ gegen „imp_filter_selected“ unter Verwendung von „ArraySize“, wobei Auslassungen nur dann über Print protokolliert werden, wenn „debugLogging“ aktiviert ist. Wir verwenden ArraySize auf „triggeredNewsEvents“, um gehandelte Ereignisse auszuschließen und bedingt zu protokollieren.

Für den Modus „TRADE_BEFORE“ suchen wir nach Ereignissen innerhalb von „offsetSeconds“ vor „eventDateTime“, validieren „forecast“ und „previous“ und wählen das früheste Ereignis in „candidateEventTime“, „candidateEventName“, „candidateEventID“ und „candidateTradeSide“ („BUY“, wenn „forecast“ „previous“ übersteigt, sonst „SELL“), wobei die Protokollierung mit „Print“ erfolgt, wenn „debugLogging“ wahr ist, was effiziente Handelsentscheidungen mit minimaler Protokollierung gewährleistet. Der Rest der Logik des Live-Modus bleibt unverändert. Nach der Zusammenstellung erhalten wir die folgende Visualisierung der Handelsbestätigung.

HANDELSBESTÄTIGUNG GIF

Aus dem Bild können wir ersehen, dass wir die Daten abrufen, sie filtern und in das Dashboard einfügen können, Countdowns initialisieren können, wenn der entsprechende Zeitbereich bestimmter Daten erreicht wird, und das Nachrichtenereignis handeln können, wodurch wir genau das simulieren, was wir in der Handelsumgebung im Live-Modus haben, und somit unser Integrationsziel erreichen. Was jetzt noch bleibt, sind gründliche Backtests des Systems, das im nächsten Abschnitt behandelt wird.


Tests und Validierung

Wir testen das Programm, indem wir es zunächst in einer Live-Umgebung laden, die gewünschten Daten zu den Nachrichtenereignissen herunterladen und es im MetaTrader 5 Strategy Tester ausführen, wobei „StartDate“ auf „2025.03.01', „EndDate“ auf '2025.03.21', und „debugLogging“ deaktiviert, unter Verwendung einer Datei mit durch Kommas getrennten Werte (CSV) in „EconomicCalendarData“, um Trades über „CheckForNewsTrade“ auf „filteredEvents“ zu simulieren. Ein GIF zeigt das Dashboard, das durch „update_dashboard_values“ nur dann aktualisiert wird, wenn „filters_changed“ oder „last_dashboard_update“ ausgelöst wird, und das gefilterte Ereignisse mit „createLabel“ und saubere Protokolle von Trades und Updates anzeigt. Tests im Live-Modus mit der Funktion CalendarValueHistory bestätigen die identische Darstellung und bestätigen die schnelle und klare Leistung des Programms in beiden Modi. Hier ist die Visualisierung.

FINAL GIF


Schlussfolgerung

Zusammenfassend lässt sich sagen, dass wir die Serie mit dem MQL5-Wirtschaftskalender durch die Optimierung des Backtests mit intelligenter Ereignisfilterung und optimierter Protokollierung verbessert haben, was eine schnelle und klare Strategievalidierung ermöglicht, während die nahtlosen Live-Handelsfunktionen erhalten bleiben. Dieser Fortschritt verbindet effiziente Offline-Tests mit der Echtzeit-Ereignisanalyse und bietet uns ein robustes Tool zur Verfeinerung von nachrichtengesteuerten Strategien, wie in unserer Testvisualisierung zu sehen ist. Sie können es als Grundgerüst verwenden und es weiter ausbauen, um Ihre spezifischen Handelsanforderungen zu erfüllen.

Übersetzt aus dem Englischen von MetaQuotes Ltd.
Originalartikel: https://www.mql5.com/en/articles/17999

Beigefügte Dateien |
Entwicklung des Price Action Analysis Toolkit (Teil 23): Stärkemessung einer Währung Entwicklung des Price Action Analysis Toolkit (Teil 23): Stärkemessung einer Währung
Wissen Sie, was die Richtung eines Währungspaares wirklich bestimmt? Es geht um die Stärke der einzelnen Währungen. In diesem Artikel werden wir die Stärke einer Währung messen, indem wir jedes Paar, in dem sie vorkommt, in einer Schleife durchgehen. Aufgrund dieser Erkenntnisse können wir vorhersagen, wie sich diese Paare auf der Grundlage ihrer relativen Stärke entwickeln werden. Lesen Sie weiter, um mehr zu erfahren.
MQL5-Assistenten-Techniken, die Sie kennen sollten (Teil 63): Verwenden von Mustern der Kanäle von DeMarker und Envelope MQL5-Assistenten-Techniken, die Sie kennen sollten (Teil 63): Verwenden von Mustern der Kanäle von DeMarker und Envelope
Der DeMarker-Oszillator und der Envelope-Indikator sind Momentum- und Unterstützungs-/Widerstands-Tools, die bei der Entwicklung eines Expert Advisors kombiniert werden können. Wir prüfen daher Muster für Muster, was von Nutzen sein könnte und was möglicherweise zu vermeiden ist. Wir verwenden, wie immer, einen von einem Assistenten erstellten Expert Advisor zusammen mit den Funktionen der Musterverwendung, die in der Signalklasse des Expert Advisors integriert sind.
MQL5-Assistenten-Techniken, die Sie kennen sollten (Teil 64): Verwendung von Mustern von DeMarker und Envelope-Kanälen mit dem Kernel des weißen Rauschens MQL5-Assistenten-Techniken, die Sie kennen sollten (Teil 64): Verwendung von Mustern von DeMarker und Envelope-Kanälen mit dem Kernel des weißen Rauschens
Der DeMarker-Oszillator und der Envelopes-Indikator sind Momentum- und Unterstützungs-/Widerstands-Tools, die bei der Entwicklung eines Expert Advisors kombiniert werden können. Wir knüpfen an unseren letzten Artikel an, in dem diese beiden Indikatoren vorgestellt wurden, indem wir das maschinelle Lernen in den Mix aufnehmen. Wir verwenden ein rekurrentes neuronales Netz, das den Kernel des weißen Rauschens nutzt, um die vektorisierten Signale dieser beiden Indikatoren zu verarbeiten. Dies geschieht in einer nutzerdefinierten Signalklassendatei, die mit dem MQL5-Assistenten arbeitet, um einen Expert Advisor zusammenzustellen.
Entwicklung des Price Action Analysis Toolkit (Teil 22): Korrelation Dashboard Entwicklung des Price Action Analysis Toolkit (Teil 22): Korrelation Dashboard
Bei diesem Tool handelt es sich um ein Korrelations-Dashboard, das Korrelationskoeffizienten für mehrere Währungspaare in Echtzeit berechnet und anzeigt. Durch die Visualisierung, wie sich Paare im Verhältnis zueinander bewegen, fügt es Ihrer Preisaktionsanalyse wertvollen Kontext hinzu und hilft Ihnen, die Dynamik zwischen den Märkten zu antizipieren. Lesen Sie weiter, um seine Funktionen und Anwendungen kennenzulernen.