Nachrichtenhandel leicht gemacht (Teil 4): Leistungsverbesserung
Einführung
Im vorherigen Artikel haben wir die Prozesse zur Umsetzung von Trades auf der Grundlage der Auswirkungen des Nachrichtenereignisses erläutert. Dies ist uns gelungen, aber ein entscheidender Nachteil des letzten Codes des Artikels war die relativ langsame Geschwindigkeit des Backtestings. Dies ist hauptsächlich auf den häufigen Zugriff auf die Datenbank im Speicher während des Backtests der Strategie zurückzuführen. Um dieses Problem zu lösen, werden wir die Anzahl der Zugriffe auf die Datenbank während des Backtests reduzieren. Wir holen uns alle Informationen, die wir brauchen, aus der Datenbank im Speicher für den aktuellen Tag, das heißt, wir greifen im Idealfall nur einmal pro Tag auf die Datenbank zu.
Eine weitere Methode zur Verbesserung der Leistung besteht darin, die Nachrichtenereignisse nach Stunden zu gruppieren. Das bedeutet, dass wir für jede Stunde des Tages ein Array haben werden, das nur die Ereignisinformationen für eine bestimmte Stunde speichert. Wenn wir die Ereignisinformationen für die aktuelle Stunde benötigen, verwenden wir eine Switch-Anweisung, um auf das Array zuzugreifen, das die Ereignisinformationen für die Stunde enthält, die für die aktuelle Zeit relevant ist. Diese Methoden werden die Laufzeit des Experten drastisch verkürzen, insbesondere wenn an einem bestimmten Tag oder in einer bestimmten Stunde viele Nachrichtenereignisse auftreten. In diesem Artikel werden wir die Bausteine zur Implementierung dieser Lösungen für spätere Artikel kodieren, um zu vermeiden, dass wir nur einen langen Artikel haben.
Die Klasse der Zeitvariablen
Zuvor wurde diese Klasse verwendet, um ein Array mit einer festen Größe von 2000 Indizes zu deklarieren, das Kerzenzeiten speichert und in der Klasse Kerzeneigenschaften verwendet wird, um zu prüfen, ob diese gespeicherte Kerzenzeit gleich der aktuellen Kerzenzeit ist, um im Wesentlichen festzustellen, ob sich eine neue Kerze gebildet hat oder nicht. Bei dieser Gelegenheit werden wir diese Klasse erweitern, um Enumerationen für die Zeit und Funktionen zu deklarieren, die Integer-Werte in eine der deklarierten Enumerationen umwandeln, um eine kontrollierte Methode für den Umgang mit Sekunden, Minuten und Stunden in anderen Klassen zu haben, die diese Klasse enthalten.
Der Aufbau der Klasse ist unten dargestellt:
//+------------------------------------------------------------------+ //| NewsTrading | //| Copyright 2024, MetaQuotes Ltd. | //| https://www.mql5.com/en/users/kaaiblo | //+------------------------------------------------------------------+ //--- Enumeration For Hours in a Day enum HOURLY { H1=1,//01 H2=2,//02 H3=3,//03 H4=4,//04 H5=5,//05 H6=6,//06 H7=7,//07 H8=8,//08 H9=9,//09 H10=10,//10 H11=11,//11 H12=12,//12 H13=13,//13 H14=14,//14 H15=15,//15 H16=16,//16 H17=17,//17 H18=18,//18 H19=19,//19 H20=20,//20 H21=21,//21 H22=22,//22 H23=23,//23 H24=0//00 }; //--- Enumeration For Minutes in an Hour enum MINUTELY { M0,//00 M1,//01 M2,//02 M3,//03 M4,//04 M5,//05 M6,//06 M7,//07 M8,//08 M9,//09 M10,//10 M11,//11 M12,//12 M13,//13 M14,//14 M15,//15 M16,//16 M17,//17 M18,//18 M19,//19 M20,//20 M21,//21 M22,//22 M23,//23 M24,//24 M25,//25 M26,//26 M27,//27 M28,//28 M29,//29 M30,//30 M31,//31 M32,//32 M33,//33 M34,//34 M35,//35 M36,//36 M37,//37 M38,//38 M39,//39 M40,//40 M41,//41 M42,//42 M43,//43 M44,//44 M45,//45 M46,//46 M47,//47 M48,//48 M49,//49 M50,//50 M51,//51 M52,//52 M53,//53 M54,//54 M55,//55 M56,//56 M57,//57 M58,//58 M59//59 }; //--- Enumeration For Seconds Pre-event datetime enum PRESECONDLY { Pre_S30=30//30 }; //--- Enumeration For Seconds in a Minute enum SECONDLY { S0,//00 S1,//01 S2,//02 S3,//03 S4,//04 S5,//05 S6,//06 S7,//07 S8,//08 S9,//09 S10,//10 S11,//11 S12,//12 S13,//13 S14,//14 S15,//15 S16,//16 S17,//17 S18,//18 S19,//19 S20,//20 S21,//21 S22,//22 S23,//23 S24,//24 S25,//25 S26,//26 S27,//27 S28,//28 S29,//29 S30,//30 S31,//31 S32,//32 S33,//33 S34,//34 S35,//35 S36,//36 S37,//37 S38,//38 S39,//39 S40,//40 S41,//41 S42,//42 S43,//43 S44,//44 S45,//45 S46,//46 S47,//47 S48,//48 S49,//49 S50,//50 S51,//51 S52,//52 S53,//53 S54,//54 S55,//55 S56,//56 S57,//57 S58,//58 S59//59 }; //+------------------------------------------------------------------+ //|TimeVariables class | //+------------------------------------------------------------------+ class CTimeVariables { private: //--- Array to store candlestick times datetime CandleTime[2000]; public: CTimeVariables(void); //--- Set datetime value for an array index void SetTime(uint index,datetime time); //--- Get datetime value for an array index datetime GetTime(uint index); //--- Convert Integer to the Enumeration HOURLY HOURLY Hourly(uint Hour); //--- Convert Integer to the Enumeration MINUTELY MINUTELY Minutely(uint Minute); //--- Convert Integer to the Enumeration SECONDLY SECONDLY Secondly(uint Second); }; //+------------------------------------------------------------------+ //|Constructor | //+------------------------------------------------------------------+ CTimeVariables::CTimeVariables() { //--- Set default datetime values for all indexes in array CandleTime for(uint i=0; i<CandleTime.Size(); i++) { CandleTime[i]=D'1970.01.01'; } } //+------------------------------------------------------------------+ //|Set datetime value for an array index in array CandleTime | //+------------------------------------------------------------------+ void CTimeVariables::SetTime(uint index,datetime time) { if(index>=0&&index<CandleTime.Size()) { CandleTime[index] = time; } } //+------------------------------------------------------------------+ //|Get the datetime value for an array index in array CandleTime | //+------------------------------------------------------------------+ datetime CTimeVariables::GetTime(uint index) { return (index>=0&&index<CandleTime.Size())?CandleTime[index]:datetime(0); } //+------------------------------------------------------------------+ //|Convert Integer to the Enumeration HOURLY | //+------------------------------------------------------------------+ HOURLY CTimeVariables::Hourly(uint Hour) { return (Hour>23)?HOURLY(0):HOURLY(Hour); } //+------------------------------------------------------------------+ //|Convert Integer to the Enumeration MINUTELY | //+------------------------------------------------------------------+ MINUTELY CTimeVariables::Minutely(uint Minute) { return (Minute>59)?MINUTELY(0):MINUTELY(Minute); } //+------------------------------------------------------------------+ //|Convert Integer to the Enumeration SECONDLY | //+------------------------------------------------------------------+ SECONDLY CTimeVariables::Secondly(uint Second) { return (Second>59)?SECONDLY(0):SECONDLY(Second); } //+------------------------------------------------------------------+
Der folgende Code definiert eine Enumeration namens HOURLY. Eine Enumeration (Enum) ist ein nutzerdefinierter Datentyp, der aus einer Reihe von benannten Integer-Konstanten besteht. Sie wird häufig verwendet, wenn Sie eine bestimmte Gruppe von Werten mit aussagekräftigen Namen darstellen wollen, um Ihren Code lesbarer zu machen.
enum HOURLY { H1=1, // Represents Hour 01 H2=2, // Represents Hour 02 H3=3, // Represents Hour 03 H4=4, // Represents Hour 04 H5=5, // Represents Hour 05 H6=6, // Represents Hour 06 H7=7, // Represents Hour 07 H8=8, // Represents Hour 08 H9=9, // Represents Hour 09 H10=10, // Represents Hour 10 H11=11, // Represents Hour 11 H12=12, // Represents Hour 12 H13=13, // Represents Hour 13 H14=14, // Represents Hour 14 H15=15, // Represents Hour 15 H16=16, // Represents Hour 16 H17=17, // Represents Hour 17 H18=18, // Represents Hour 18 H19=19, // Represents Hour 19 H20=20, // Represents Hour 20 H21=21, // Represents Hour 21 H22=22, // Represents Hour 22 H23=23, // Represents Hour 23 H24=0 // Represents Hour 00 (Midnight) };
Werte:
Jeder Wert in der Enumeration entspricht einer bestimmten Stunde des Tages, beginnend mit H1=1 für die erste Stunde, H2=2 und so weiter bis zu H23=23 für die 23. H24=0 wird verwendet, um Mitternacht (00:00 Uhr) darzustellen. Sie können Namen wie H1, H2 usw. in Ihrem Code verwenden, um den Umgang mit zeitbezogenen Daten intuitiver zu gestalten. Ich ziehe es vor, statt roher Zahlen für die Stunden stündliche Werte zu verwenden.
Beispiel für die Verwendung:
HOURLY current_hour = H10; // Setting the current hour to 10:00 AM if (current_hour == H10) { Print("The current hour is 10:00 AM"); }
Im obigen Beispiel wird current_hour der Wert H10 zugewiesen, und es wird geprüft, ob die aktuelle Stunde H10 ist, und eine entsprechende Meldung ausgegeben. Dies erleichtert die Lesbarkeit und das Verständnis des Codes, insbesondere bei der Arbeit mit zahlreichen zeitbasierten Operationen.
Enumeration für Protokolle - MINUTELY.
Diese Enumeration definiert Konstanten für jede Minute einer Stunde, von M0 (00) bis M59 (59), da es in jeder Stunde 59 Minuten gibt.
enum MINUTELY
{
M0, M1, M2, M3, ..., M59
};
Statt mit rohen ganzen Zahlen (z. B. 0, 1, 2) zu arbeiten, verwenden wir aussagekräftige Bezeichnungen wie M0, M1, usw.
Beispiel:
- M0 steht für Minute 00.
- M30 steht für Minute 30.
Enumeration für die Sekunde vor dem Ereignis - PRESECONDLY.
Diese Enumeration definiert Sekunden relativ zu einer Zeit vor dem Ereignis. Hier wird Pre_S30 speziell für die 30 Sekunden vor dem Eintreten eines Nachrichtenereignisses verwendet. Dies wird als fester Wert verwendet, um das Nachrichtenereignis im Voraus einzugeben. Wenn zum Beispiel um 14:00 Uhr ein neues Ereignis eintritt, werden wir erst um 13:59:30 Uhr einen Handel eingehen. Dies bedeutet, dass die bisherige Möglichkeit, 5 Sekunden vor dem Ereignis einen Handel einzugehen, nicht mehr möglich ist.
Es gibt ein Vor- und Nachteile zu dieser Änderung, die ich vorgenommen habe.
Wichtigste Nachteile
- Weniger Anpassungen: Dies ist ein Nachteil, da der Nutzer/Händler aufgrund der gelegentlich auftretenden Volatilität, die vor dem Eintreten eines wichtigen Ereignisses auftritt, möglicherweise nur 5 Sekunden vor dem Ereignis einen Handel eröffnen möchte, was bedeutet, dass Ihr Stoploss nur aufgrund der Tatsache ausgelöst werden könnte, wie früh Sie Ihren Handel vor dem Eintreten des Ereignisses eröffnen. Ein paar Sekunden können sich also drastisch auf Ihre Rentabilität auswirken, wenn es um den Handel mit Nachrichten geht.
Wichtigste Vorteile
- Fester Zeitplan: Dies ist ein Vorteil, da wir den Experten so einstellen können, dass er nur in 30-Sekunden-Intervallen prüft, ob er einen Handel eingeht, wodurch sich die Backtesting-Geschwindigkeit drastisch verbessert. Der Grund für die Verkürzung der Backtesting-Laufzeit liegt einfach darin, dass wir die Ressourcen des Computers nicht so oft in Anspruch nehmen müssen. In dem Fall, dass wir 5 Sekunden vor dem Ereignis einen Handel eingehen wollen, muss der Experte alle 5 Sekunden oder 1 Sekunde prüfen, ob dies der richtige Zeitpunkt für den Handel ist.
Ein weiterer Faktor, der berücksichtigt werden sollte, ist die Liquidität. Je näher ein wichtiges Ereignis rückt, desto mehr wird der betroffene Vermögenswert illiquid. Das bedeutet, dass der Einstieg in einen Handel im Allgemeinen weniger günstig ist.
Dies sind die Faktoren, die es ungünstig machen, einen Handel in illiquiden Vermögenswerten/Märkten einzugehen:
- Slippage: Die Preise des Vermögenswerts oder Symbols ändern sich so häufig, dass der Preis, zu dem der Nutzer/Händler in den Markt einsteigen/aussteigen wollte, nicht mehr verfügbar ist und ein ungünstigerer/erwarteter Preis verwendet wird. Die Unvorhersehbarkeit ist der größte Nachteil des Schlupfes.
- Spreads: Weite Spreads sind häufiger und schränken das Gewinnpotenzial des Händlers ein.
- Nicht im Angebot/nicht verfügbar: Der Handel kann vollständig abgebrochen werden, was bedeutet, dass der Händler die Möglichkeit verliert, von einer potenziell lukrativen Entwicklung des Vermögenswerts/Marktes zu profitieren.
Durch den Einstieg in 30er-Kontrakte vor dem Nachrichtenereignis wird die Illiquidität mit größerer Wahrscheinlichkeit vermieden als durch den Einstieg in 5er-Kontrakte vor oder nach 30er-Kontrakten.
enum PRESECONDLY { Pre_S30 = 30 // 30 seconds before an event };
Enumeration für Sekunden - SECONDLY.
Ähnlich wie bei MINUTELY steht diese Enumeration für jede Sekunde einer Minute und reicht von S0 (00 Sekunden) bis S59 (59 Sekunden).
enum SECONDLY
{
S0, S1, S2, ..., S59
};
Beispiel:
- S0 steht für Sekunde 00.
- S30 steht für Sekunde 30.
Die folgende Funktion wandelt eine ganze Zahl (Stunde) in den entsprechenden Wert aus der Enumeration HOURLY um. Sie stellt sicher, dass der (in der Variablen Hour) übergebene Wert gültig ist (zwischen 0 und 23) und gibt dann den entsprechenden Enum-Wert von HOURLY zurück.
- Typ der Rückgabe: HOURLY - Dies bedeutet, dass die Funktion einen Wert aus dem HOURLY-Enum zurückgibt, das wir bereits besprochen haben. Diese Enumeration enthält Werte, die den 24 Stunden des Tages entsprechen, wobei H1=1, H2=2, ..., H23=23, und H24=0.
- Name der Funktion: Hourly. Es handelt sich um eine Methode, die zur Klasse CTimeVariables gehört, was durch den Operator zur Auflösung des Anwendungsbereichs (::) angezeigt wird. Sie ist also Teil der Klasse CTimeVariables.
- Parameter:
- uint Hour: Dies ist eine ganze Zahl ohne Vorzeichen, die eine Stunde darstellt und als Parameter an die Funktion übergeben wird.
HOURLY CTimeVariables::Hourly(uint Hour) { return (Hour>23)?HOURLY(0):HOURLY(Hour); }
Logik innerhalb der Funktion:
In dieser Zeile wird der ternäre Operator (? :) verwendet, der eine Kurzform für eine if-else-Anweisung ist. Der ternäre Operator prüft eine Bedingung und gibt einen von zwei Werten zurück, je nachdem, ob die Bedingung wahr oder falsch ist.
Zustand: (Hour > 23)
- Damit wird geprüft, ob der Wert für Hour 23 übersteigt. Da der gültige Stundenbereich von 0 bis 23 reicht (24 Stunden an einem Tag), ist jeder Wert größer als 23 ungültig.
- Wenn Hour > 23 (ungültige Stunde) ist, gibt die Funktion HOURLY(0) zurück, was H24=0 (Mitternacht oder 00:00) entspricht.
Wenn false: HOURLY(Hour)
- Wenn Hour innerhalb des gültigen Bereichs (0 bis 23) liegt, wird die Ganzzahl Hour in den entsprechenden Wert in der Enumeration HOURLY umgewandelt. Wenn z. B. Hour = 10 ist, wird HOURLY(10) zurückgegeben, was H10 entspricht.
return (Hour>23)?HOURLY(0):HOURLY(Hour);
Beispiel:
- Wenn Hourly(10) aufgerufen wird, gibt die Funktion HOURLY(10) zurück (das ist der Enum-Wert für 10:00 AM).
- Wenn Sie Hourly(25) aufrufen, wird die Funktion HOURLY(0) zurückgeben (was 00:00 oder Mitternacht entspricht), da 25 keine gültige Stunde ist.
Wichtige Punkte:
- Umgang mit ungültigen Stunden: Die Funktion stellt sicher, dass, wenn der Wert Hour außerhalb des gültigen Bereichs liegt (größer als 23), der Standardwert HOURLY(0) verwendet wird, was Mitternacht entspricht.
- Umwandlung: Die Funktion wandelt eine ganzzahlige Stunde effizient in einen HOURLY-Enum-Wert um, was die Verwendung in zeitbasierter Logik erleichtert.
Utility-Funktion zur Umwandlung von Integer in MINUTELY.
Diese Funktion wandelt eine Ganzzahl (von 0 bis 59) in einen Enumerationswert von MINUTELY. Wenn die eingegebene ganze Zahl 59 überschreitet (eine ungültige Minute), wird die Funktion auf M0 (Minute 0) zurückgesetzt.
MINUTELY CTimeVariables::Minutely(uint Minute) { return (Minute>59)?MINUTELY(0):MINUTELY(Minute); }
Wenn die Minute größer als 59 ist, wird M0 als Rückfall zurückgegeben, um Fehler zu vermeiden.
Utility-Funktion zur Umwandlung von Integer in SECONDLY.
Diese Funktion führt eine ähnliche Aufgabe für Sekunden aus. Sie wandelt eine ganze Zahl (von 0 bis 59) in einen Enumerationswert von SECONDLY um. Ist die ganze Zahl größer als 59, wird sie standardmäßig auf S0 (Sekunde 0) gesetzt.
SECONDLY CTimeVariables::Secondly(uint Second) { return (Second>59)?SECONDLY(0):SECONDLY(Second); }
Wenn der zweite Wert 59 überschreitet, wird S0 zurückgegeben, um ungültige Eingaben korrekt zu behandeln.
Wichtigste Erkenntnisse:
Zeitliche Präzision: Die Klasse TimeVariables vereinfacht das Zeitmanagement in Handelssystemen und erleichtert die Handhabung der Minuten- und Sekundengenauigkeit, die für Nachrichtenhandelsstrategien unerlässlich ist.
Flexibilität: Durch die Verwendung von Enumerationen können Entwickler die Handelsausführungszeiten im Verhältnis zu Nachrichtenereignissen leicht anpassen, ohne die Zeit für jedes Szenario manuell zu kodieren.
Anwendung in der realen Welt: Im Nachrichtenhandel kann die Fähigkeit, auf zeitkritische Gelegenheiten wie die durch Wirtschaftsmeldungen verursachte Marktvolatilität zu reagieren, die Handelsleistung drastisch verbessern.
Vorhersagen und Einblicke:
Unterschiedliche Marktbedingungen wirken sich auf den Nutzen der Klasse TimeVariables aus. Zum Beispiel:
- Auf volatilen Märkten, insbesondere bei großen Nachrichtenereignissen (wie NFP oder Ankündigungen der Zentralbanken), wird die sekundengenaue Präzision entscheidend, da große Kursbewegungen innerhalb von Millisekunden erfolgen können.
- Bei geringer Volatilität kann die Genauigkeit auf Minutenebene für die Platzierung von Geschäften ausreichend sein, da die Kursbewegungen tendenziell langsamer sind.
DB-Zugriffsreduzierung
Im vorigen Artikel haben wir die Datenbank im Speicher verwendet, um die Ereignisinformationen zu speichern, die unserem Experten helfen, zu wissen, wann er einen Handel in Abhängigkeit vom Ereigniszeitpunkt eröffnen muss. Das Verfahren für den Zugriff auf die Datenbank wird im Folgenden dargestellt.
- Zu Beginn eines jeden neuen Tages laden wir alle Nachrichten, die im Laufe des aktuellen Tages eintreten werden oder eingetreten sind, im Idealfall. Diese Daten werden in einem Strukturarray gespeichert, in dem das Programm die Ereignisobjekte auf dem Chart mit der Uhrzeit des Ereignisses und dem Namen usw. anzeigt.
- Wenn ein Ereignis eingetreten ist oder eintritt, greift der Experte erneut auf die Datenbank im Speicher zu, um die nächsten Ereignisinformationen für denselben Tag abzurufen, falls vorhanden.
Das obige Verfahren ist nicht optimal, da bei mehreren Ereignissen mit unterschiedlichen Uhrzeiten am selben Tag häufig auf die Datenbank zugegriffen werden muss, um die Informationen zum nächsten Ereignis zu erhalten. Eine einfache Lösung wäre die Verwendung desselben Strukturarrays, das an jedem neuen Tag aktualisiert wird, und die Iteration durch das Array, bis wir ein übereinstimmendes Datum zwischen dem Ereignisdatum und dem aktuellen Datum haben, sodass wir den Handel zum richtigen Zeitpunkt eröffnen können und nicht mehr als einmal auf die Datenbank zugreifen müssen, nur um die nächsten Ereignisinformationen zu erhalten.
Diese einfache Lösung wird definitiv die Arbeitsgeschwindigkeit des Experten verbessern, aber ein anderes Problem entsteht, wenn wir durch die gleiche Struktur Array, dass wir für passende Daten zu überprüfen iterieren, aber wir könnten Ereignisdaten, die bereits aufgetreten sind und die Überprüfung für diese Daten, die redundant sind, wird die Arbeitsgeschwindigkeit des Experten zu begrenzen, das gleiche kann für die Ereignisdaten, die viel später am Tag als das, was das aktuelle Datum ist auftreten wird gesagt werden.
Nehmen wir zum Beispiel an, dass wir die folgenden Zeiten in unserem Array unten haben.
time[0] = '14:00' time[1] = '14:15' time[2] = '14:30' time[3] = '14:45' time[4] = '14:50' time[5] = '14:55' time[6] = '15:00' time[7] = '15:05' time[8] = '15:10' time[9] = '15:15' time[10] = '15:30' time[11] = '15:45' time[12] = '15:55'
Wenn die aktuelle Uhrzeit 11 Uhr ist, macht es keinen Sinn zu prüfen, ob die Uhrzeit 14 Uhr übereinstimmt, und die ständige Iteration durch dieses Array für jeden neuen Tick auf dem Chart, wenn die Ereigniszeiten immer noch unerreichbar sind, beeinträchtigt die Arbeitsgeschwindigkeit des Experten zusätzlich. Eine einfache Lösung für dieses Problem wäre es, das Array in verschiedene Stunden des Tages aufzuteilen, sodass der Experte nur das Array mit der passenden Stunde der aktuellen Zeit durchläuft. Auf diese Weise spart der Experte Zeit, indem er nur dann prüft, wenn die Uhrzeit des Ereignisses mindestens eine Stunde vom aktuellen Datum entfernt ist.
Fallstudie:
Anhand eines Beispiels aus der Praxis soll verdeutlicht werden, wie diese Optimierung die Leistung verbessern kann:
- Szenario: Ein Händler setzt einen EA ein, der wichtige Nachrichten, wie z. B. die US-Nachrichten, überwacht. Non-Farm Payrolls (NFP), geplant für 14:30 Uhr. Der EA ist so programmiert, dass er je nach Ergebnis der Nachricht einen Kauf- oder Verkaufs-Stopp-Auftrag erteilt. Ohne Optimierung greift der EA den ganzen Tag über jede Sekunde auf die Datenbank zu und sucht nach dem nächsten Ereignis. Wenn der NFP veröffentlicht wird, kann es sein, dass der EA aufgrund übermäßiger Datenbankabfragen erst mit Verzögerung auf die Nachrichten reagiert, was seine Chancen, optimale Einstiegspunkte für den Handel zu finden, verringert.
- Lösung mit DB-Zugriffsreduzierung: Anstelle eines kontinuierlichen Datenbankzugriffs lädt der EA alle Ereignisse des Tages um 00:00 Uhr und segmentiert sie nach Stunden. Wenn sich die Uhrzeit 14:00 nähert, prüft der EA nur Ereignisse innerhalb des 14-Uhr-Stundenbereichs und überspringt alle Ereignisprüfungen außerhalb dieses Zeitfensters. Um 14:30 Uhr ist der EA bereit, sofort und ohne Verzögerung auf die Veröffentlichung des NFP zu reagieren und den Handel zum richtigen Zeitpunkt zu platzieren.
Wir werden einen neuen Ordner im Projekt mit dem Namen TimeSeries erstellen. Dieser Ordner wird die beiden Klassen enthalten, die die Arrays für jede Stunde des Tages erstellen und diese Array-Werte für jede Stunde des Tages abrufen werden.
Die Klasse TimeByHour
Der nachstehende Code definiert eine Klasse CTimeByHour, die dazu dient, Zeit- und Ereignisdaten für jede Stunde des Tages zu verwalten und abzurufen. Die Klasse verwendet mehrere Komponenten, wie Strukturen, Arrays und das Konzept der objektorientierten Programmierung (OOP). Der Zweck dieser Klasse ist es, Array-Objekte für jede Stunde des Tages zu erstellen, wenn es 24 Stunden gibt, was bedeutet, dass wir 24 Array-Objekte deklarieren werden. Diese Array-Objekte speichern die Integer-Variable Stunde, die Integer-Variable Minute und die Kalenderstruktur-Variable myEData (diese Variable speichert alle Ereignisinformationen für die spezifische Stunde und Minute, die innerhalb des Tages auftreten). Die deklarierte Struktur TimeDate speichert die Stunden und Minuten eines Datums, wobei das Array myTimeData Daten parallel zum Array myEvents speichert.
//+------------------------------------------------------------------+ //| TimeByHour.mqh | //| Copyright 2024, MetaQuotes Ltd. | //| https://www.mql5.com | //+------------------------------------------------------------------+ #property copyright "Copyright 2024, MetaQuotes Ltd." #property link "https://www.mql5.com" #include <Object.mqh> #include <Arrays\ArrayObj.mqh> #include "../TimeVariables.mqh" #include "../CommonVariables.mqh" #include "../TimeManagement.mqh" //--- Structure to store time data in Hour and Minute struct TimeDate { int Hour; int Minute; } myTimeData[]; //--- Structure array to store event data in parallel with myTimeData array Calendar myEvents[]; //+------------------------------------------------------------------+ //|TimeByHour class | //+------------------------------------------------------------------+ class CTimeByHour:public CObject { private: //--- classes' object declarations CTimeManagement CTime; CTimeVariables CTV; protected: //--- class constructor with parameters CTimeByHour(HOURLY myHour,MINUTELY myMinute,Calendar &myEventData): //--- Assign integer variables Hour and Minute with time data from myHour and myMinute respectively Hour(int(myHour)),Minute(int(myMinute)) { //--- Assign variable myEData with event info from variable myEventData myEData = myEventData; } virtual void myTime(Calendar &myNews[]); //--- Array object declarations for each hour of the day CArrayObj *myH1,*myH2,*myH3,...,*myH24; //--- Integer variables to store time data in hour and minute format int Hour; int Minute; //--- Calendar structure variable to store event info Calendar myEData; public: //--- class constructor without parameters CTimeByHour(void) { } //--- Array object variable CArrayObj *getmyTime; //--- Retrieve array object for an individual hour CObject *getTime(HOURLY myHour) { switch(myHour) { case H1: //--- retrieve array obj for 01 Hour return myH1; break; case H2: //--- retrieve array obj for 02 Hour return myH2; break; case H3: //--- retrieve array obj for 03 Hour return myH3; break; // ... default: //--- retrieve array obj for 24|00 Hour return myH24; break; } } //--- class pointer variable CTimeByHour *myClass; //--- class destructor ~CTimeByHour(void) { //--- delete all pointer variables delete getmyTime; delete myClass; delete myH1; delete myH2; delete myH3; // ... } //--- Function to retrieve timedata and calendar info for a specific hour of the day via parameters passed by reference void GetDataForHour(HOURLY myHour,TimeDate &TimeData[],Calendar &Events[]) { //--- Clean arrays ArrayRemove(TimeData,0,WHOLE_ARRAY); ArrayRemove(Events,0,WHOLE_ARRAY); //--- retrieve array object for the specific hour getmyTime = getTime(myHour); // Iterate through all the items in the list. for(int i=0; i<getmyTime.Total(); i++) { // Access class obj via array obj index myClass = getmyTime.At(i); //Re-adjust arrays' sizes ArrayResize(TimeData,i+1,i+2); ArrayResize(Events,i+1,i+2); //--- Assign values to arrays' index TimeData[i].Hour = myClass.Hour; TimeData[i].Minute = myClass.Minute; Events[i] = myClass.myEData; } } }; //+------------------------------------------------------------------+
Strukturen:
- TimeDate: Eine Struktur zum Speichern von Zeitdaten, mit Stunde und Minute als Ganzzahlfelder.
- myTimeData[]: Ein Array der TimeDate-Struktur zur Aufnahme der Zeitdaten für mehrere Stunden.
struct TimeDate { int Hour; int Minute; } myTimeData[];
- myEvents[]: Ein Array vom Typ Kalender, das dazu dient, Ereignisdaten parallel zu myTimeData zu speichern.
Calendar myEvents[];
Deklaration der Klasse CTimeByHour:
- CTimeByHour: Eine Klasse, die CObject erweitert. Diese Klasse verwaltet Zeit- und Ereignisdaten nach Stunden.
class CTimeByHour:public CObject
Private Mitglieder:
- CTimeManagement und CTimeVariables: Dies sind Objekte von nutzerdefinierten Klassen (CTimeManagement, CTimeVariables) aus TimeManagement.mqh und TimeVariables.mqh, die zeitbezogene Daten und Variablen verwalten.
private:
CTimeManagement CTime;
CTimeVariables CTV;
Konstruktor:
- Dies ist ein parametrisierter Konstruktor für die Klasse. Es initialisiert zwei Integer-Variablen (Hour und Minute) unter Verwendung der Enums HOURLY und MINUTELY und weist die Ereignisinformationen (myEventData) myEData zu.
protected: CTimeByHour(HOURLY myHour, MINUTELY myMinute, Calendar &myEventData): Hour(int(myHour)), Minute(int(myMinute)) { myEData = myEventData; }
Daten Mitglieder:
- myH1 ... myH24: Zeiger auf CArrayObj-Objekte, die jeweils einer bestimmten Stunde (01 bis 24) des Tages entsprechen. Jedes CArrayObj enthält ein Array von Objekten für eine bestimmte Stunde.
- Stunde und Minute: Ganzzahlige Variablen zur Speicherung der Zeit.
- myEData: Ein Kalenderobjekt, das Ereignisinformationen speichert.
CArrayObj *myH1, *myH2, ..., *myH24; int Hour; int Minute; Calendar myEData;
Öffentliche Methoden:
- CTimeByHour(void): Ein Standardkonstruktor, der nichts initialisiert.
- getmyTime: Ein Zeiger auf ein Array-Objekt, das die Zeitdaten für eine bestimmte Stunde enthält.
public: CTimeByHour(void) {} CArrayObj *getmyTime;
Abrufen von Array-Objekten für eine Stunde:
- getTime(HOURLY myHour): Eine Methode, die eine Switch-Anweisung verwendet, um das entsprechende CArrayObj-Objekt für eine bestimmte Stunde des Tages auf der Grundlage des HOURLY-Enums abzurufen. Jeder Fall entspricht einer Stunde (z. B. H1, H2, ... H24).
CObject *getTime(HOURLY myHour) { switch(myHour) { case H1: return myH1; case H2: return myH2; ... case H24: return myH24; } }
Destruktor:
- ~CTimeByHour(void): Der Destruktor räumt den dynamisch zugewiesenen Speicher auf, indem er delete für die CArrayObj-Zeiger (myH1 ... myH24) und andere Klassenzeiger aufruft.
~CTimeByHour(void) { delete getmyTime; delete myClass; delete myH1, myH2, ..., myH24; }
Daten für eine bestimmte Stunde abrufen:
- GetDataForHour: Diese Methode ruft Zeit- und Ereignisdaten für eine bestimmte Stunde (myHour) ab.
- ArrayRemove: Löscht die Arrays (TimeData[], Events[]).
- getmyTime = getTime(myHour): Holt das Array-Objekt für die angegebene Stunde.
- for-Schleife: Iteriert über alle Elemente in dem abgerufenen CArrayObj (d. h. Zeit- und Ereignisdaten für jeden Eintrag).
- ArrayResize: Ändert dynamisch die Größe der Arrays (TimeData[], Events[]), um sie an die neuen Daten anzupassen.
- myClass: Verweist auf das aktuelle Objekt, das im Array verarbeitet wird.
Für jedes Objekt weist die Methode die Stunde, Minute und myEData dem entsprechenden Index in den Arrays TimeData[] und Events[] zu.
void GetDataForHour(HOURLY myHour, TimeDate &TimeData[], Calendar &Events[]) { ArrayRemove(TimeData, 0, WHOLE_ARRAY); ArrayRemove(Events, 0, WHOLE_ARRAY); getmyTime = getTime(myHour); for(int i = 0; i < getmyTime.Total(); i++) { myClass = getmyTime.At(i); ArrayResize(TimeData, i + 1); ArrayResize(Events, i + 1); TimeData[i].Hour = myClass.Hour; TimeData[i].Minute = myClass.Minute; Events[i] = myClass.myEData; } }
Die Klasse TimeByDay
Diese Klasse ist verantwortlich für die Zuweisung von Werten zu den Array-Objekten, die zuvor in der TimeByHour-Header-Datei deklariert wurden, sowie für das Abrufen dieser Werte und das Sortieren nach der spezifischen Stunde und Minute, die in dem entsprechenden Array-Objekt gespeichert sind. Der Code beginnt mit dem Import von anderen Dateien: TimeByHour.mqh, das Zeitdaten auf Stundenebene verarbeitet, und CommonVariables.mqh, das gemeinsam genutzte Konstanten und Variablen enthält. Die Klasse CTimeByDay erbt von CTimeByHour. Diese Klasse verarbeitet Zeitdaten nach Tagen und ermöglicht die Interaktion mit stundenbezogenen Zeitdaten, die von CTimeByHour verwaltet werden.
//+------------------------------------------------------------------+ //| TimeByDay.mqh | //| Copyright 2024, MetaQuotes Ltd. | //| https://www.mql5.com | //+------------------------------------------------------------------+ #property copyright "Copyright 2024, MetaQuotes Ltd." #property link "https://www.mql5.com" #include "TimeByHour.mqh" #include "../CommonVariables.mqh" //+------------------------------------------------------------------+ //|TimeByDay class | //+------------------------------------------------------------------+ class CTimeByDay:private CTimeByHour { private: //--- Function to clear all array objects void Clear(); //--- Function to clean array dates in accordance to the current minute void DatePerMinute(TimeDate &TData[],Calendar &EData[],MINUTELY min,TimeDate &TimeData[],Calendar &EventData[]) { //--- Iterate through all the idexes in TData array for(uint i=0;i<TData.Size();i++) { //--- Check if Minutes match if(TData[i].Minute==int(min)) { //--- Resize arrays ArrayResize(TimeData,TimeData.Size()+1,TimeData.Size()+2); ArrayResize(EventData,EventData.Size()+1,EventData.Size()+2); //--- Assign data from each array to the other TimeData[TimeData.Size()-1] = TData[i]; EventData[EventData.Size()-1] = EData[i]; } } } public: //--- Function to set time for array objects based on calendar array myNews void SetmyTime(Calendar &myNews[]) { //--- Clear previous data stored in array objects Clear(); //--- clean arrays in parallel declared in TimeByHour header file ArrayRemove(myTimeData,0,WHOLE_ARRAY); ArrayRemove(myEvents,0,WHOLE_ARRAY); //--- Set new values to each array object accordingly myTime(myNews); } //--- Function to get time for the specific hour and minute for news events void GetmyTime(HOURLY myHour,MINUTELY myMinute,TimeDate &TimeData[],Calendar &Events[]) { //--- clean arrays in parallel declared in TimeByHour header file ArrayRemove(myTimeData,0,WHOLE_ARRAY); ArrayRemove(myEvents,0,WHOLE_ARRAY); //--- Declare temporary arrays to get news data for a specific hour TimeDate myTData[]; Calendar myData[]; //--- Get Data for the specific hour of the day GetDataForHour(myHour,myTData,myData); //--- Filter the Data for a specific Minute of the hour DatePerMinute(myTData,myData,myMinute,TimeData,Events); //--- Clear data from the temporary array variables ArrayRemove(myTData,0,WHOLE_ARRAY); ArrayRemove(myData,0,WHOLE_ARRAY); } public: //--- Class constructor CTimeByDay(void) { //--- Initialize array objects myH1 = new CArrayObj(); myH2 = new CArrayObj(); myH3 = new CArrayObj(); //... } //--- Class destructor ~CTimeByDay(void) { } }; //+------------------------------------------------------------------+ //|Add data to Array Objects for each Hour of the day | //+------------------------------------------------------------------+ void CTimeByHour::myTime(Calendar &myNews[]) { //--- Iterate through myNews calendar array for(uint i=0;i<myNews.Size();i++) { //--- Assign datetime from myNews calendar array datetime Date = datetime(myNews[i].EventDate); //--- Assign HOURLY Enumeration value from datetime variable Date HOURLY myHour = CTV.Hourly(CTime.ReturnHour(Date)); //--- Assign MINUTELY Enumeration value from datetime variable Date MINUTELY myMinute = CTV.Minutely(CTime.ReturnMinute(Date)); //--- Switch statement to identify each value scenario for myHour switch(myHour) { case H1: //--- add array obj values for 01 Hour myH1.Add(new CTimeByHour(myHour,myMinute,myNews[i])); break; case H2: //--- add array obj values for 02 Hour myH2.Add(new CTimeByHour(myHour,myMinute,myNews[i])); break; case H3: //--- add array obj values for 03 Hour myH3.Add(new CTimeByHour(myHour,myMinute,myNews[i])); break; //... default: //--- add array obj values for 24|00 Hour myH24.Add(new CTimeByHour(myHour,myMinute,myNews[i])); break; } } } //+------------------------------------------------------------------+ //|Clear Data in Array Objects | //+------------------------------------------------------------------+ void CTimeByDay::Clear(void) { //--- Empty all array objects myH1.Clear(); myH2.Clear(); myH3.Clear(); //... } //+------------------------------------------------------------------+
Private Funktionen:
- Diese Funktion wird verwendet, um alle Array-Objekte zu löschen (oder zurückzusetzen), die stündliche Zeitdaten speichern.
void Clear();
Die folgende Funktion filtert Zeitdaten (TData[]) und Kalenderereignisdaten (EData[]), um nur die Einträge zu erhalten, die einer bestimmten Minute entsprechen (dargestellt durch das Argument min).
- Es durchläuft TData[] und prüft für jedes Element, ob die Minute mit min übereinstimmt. Wenn dies der Fall ist, werden die Arrays TimeData[] und EventData[] in ihrer Größe verändert und die entsprechenden Daten aus TData[] und EData[] in sie kopiert.
void DatePerMinute(TimeDate &TData[], Calendar &EData[], MINUTELY min, TimeDate &TimeData[], Calendar &EventData[]);
Öffentliche Funktionen:
Mit der folgenden Funktion werden die Zeit- und Ereignisdaten für den Tag zurückgesetzt und neu zugewiesen. Sie verwendet die myTime-Methode von CTimeByHour, die Zeitdaten auf Stundenebene auf der Grundlage der in myNews[] übergebenen Nachrichtenereignisse verarbeitet.
- Zunächst werden die zuvor gespeicherten Zeit- und Ereignisdaten mit Clear() gelöscht.
- Anschließend werden alle Daten aus den parallelen Arrays (myTimeData und myEvents) entfernt und neue Werte mit der von CTimeByHour geerbten Funktion myTime() gesetzt.
void SetmyTime(Calendar &myNews[]);
Die folgende Funktion ruft die Zeitdaten für eine bestimmte Stunde (myHour) und Minute (myMinute) ab.
- Zuerst werden die Arrays myTimeData und myEvents gelöscht.
- Temporäre Arrays myTData[] und myData[] werden deklariert, um Zeit- und Ereignisdaten zu speichern.
- GetDataForHour() wird aufgerufen, um die temporären Arrays mit Daten für die angegebene Stunde aufzufüllen.
- Die Daten werden mit DatePerMinute() weiter nach der spezifischen Minute gefiltert.
void GetmyTime(HOURLY myHour, MINUTELY myMinute, TimeDate &TimeData[], Calendar &Events[]);
Konstruktor:
- Der Konstruktor initialisiert Array-Objekte für jede Stunde des Tages (1 bis 24). In diesen Array-Objekten werden Zeit- und Ereignisdaten für bestimmte Stunden gespeichert.
CTimeByDay(void) { // Initialize array objects for each hour myH1 = new CArrayObj(); myH2 = new CArrayObj(); // (Initializes for all 24 hours) }
CTimeByHour::myTime()
Diese Funktion ist in CTimeByHour definiert und wird von CTimeByDay abgeleitet. Sie verarbeitet das Array myNews[] und ordnet die Ereignisse bestimmten Stunden des Tages zu.
- Für jedes Ereignis in myNews[] werden die Stunde und die Minute des Ereignisses extrahiert.
- Es verwendet eine Switch-Anweisung, um zu entscheiden, welches stündliche Array-Objekt (z.B. myH1, myH2, etc.) die Zeit- und Ereignisdaten speichern soll.
- Jedes Zeitereignis wird als CTimeByHour-Objekt zu dem entsprechenden Array-Objekt hinzugefügt.
for(uint i=0; i<myNews.Size(); i++) { datetime Date = datetime(myNews[i].EventDate); HOURLY myHour = CTV.Hourly(CTime.ReturnHour(Date)); MINUTELY myMinute = CTV.Minutely(CTime.ReturnMinute(Date)); // Switch case to handle different hours }
Die Funktion Clear()
- Diese Funktion löscht alle stündlichen Array-Objekte (myH1, myH2 usw.) und setzt damit die für jede Stunde gespeicherten Zeitdaten zurück.
void CTimeByDay::Clear(void) { // Clears all array objects myH1.Clear(); myH2.Clear(); // (Clears all 24 hours) }
Schlussfolgerung
In diesem Artikel wurden die Methoden zur Verbesserung der Leistung des Experten demonstriert, indem die Ereigniszeiten in separate Arrays für jede Stunde des Tages unterteilt wurden und die Häufigkeit des Zugriffs auf die speicherinterne Kalenderdatenbank verringert wurde. Durch die Strukturierung von Zeitwerten als Enumerationen (MINUTELY, SECONDLY) wird der Code einfacher zu verwalten und zu interpretieren, wodurch das Risiko logischer Fehler verringert wird.
Die Enumerationen MINUTELY, SECONDLY und PRESECONDLY stellen die Minuten, Sekunden bzw. die Zeit vor dem Ereignis dar, was eine bessere Lesbarkeit und Kontrolle über Zeitintervalle ermöglicht. Die Konvertierungsfunktionen erleichtern die Arbeit mit Ganzzahlen als Eingaben und stellen sicher, dass sie in sinnvolle Werte der Enumeration umgewandelt werden. Die Klasse CTimeByHour bietet einen Mechanismus zum Speichern, Abrufen und Verwalten von Zeit- und Ereignisdaten für jede Stunde des Tages. Diese Methoden werden in späteren Artikeln vorgestellt.
Wichtigste Erkenntnisse:
- Effizienter DB-Zugriff: Die Ereignisse werden nur einmal pro Tag geladen und im Speicher abgelegt, wodurch unnötige Datenbankabfragen vermieden werden.
- Zeitliche Segmentierung: Durch die Aufteilung der Ereignisse in stündliche Abschnitte prüft der EA nur relevante Ereignisse, was die Geschwindigkeit erhöht und die CPU-Belastung verringert.
- Skalierbarkeit: Die vorgeschlagene Methode ist für Tage mit vielen Ereignissen skalierbar und gewährleistet eine gleichbleibende Leistung während des gesamten Handelstages.
- Erhöhte Reaktionsfähigkeit: Durch die Konzentration auf Ereignisse im relevanten Zeitrahmen kann der EA schneller auf Marktereignisse reagieren, was für nachrichtenbasierte Strategien entscheidend ist.
Vielen Dank für Ihre Zeit, ich freue mich darauf, im nächsten Artikel mehr Wert zu bieten :)
Übersetzt aus dem Englischen von MetaQuotes Ltd.
Originalartikel: https://www.mql5.com/en/articles/15878
- Freie Handelsapplikationen
- Über 8.000 Signale zum Kopieren
- Wirtschaftsnachrichten für die Lage an den Finanzmärkte
Sie stimmen der Website-Richtlinie und den Nutzungsbedingungen zu.