
LifeHack für Händler: Fast-Food aus Indikatoren
Wenn etwas unmöglich ist, Sie es aber unbedingt wollen, dann geht es doch. Russisches Sprichwort
Einfachheit vs. Zuverlässigkeit
Bereits 2005, im neu veröffentlichten MetaTrader 4, wurde die einfache MQL-II-Skriptsprache durch MQL4 ersetzt. So skurril es heute auch erscheinen mag, viele Händler begegneten der neuen C-ähnlichen Sprache mit Vorbehalten. Es gab viele, heftige Debatten und Anschuldigungen, die sich gegen MetaQuotes Software Corp. richteten. Kritiker behaupteten, dass die Sprache sehr kompliziert sei und es unmöglich sei, sie zu beherrschen.
Jetzt, nach 12 Jahren, erscheinen solche Behauptungen seltsam, aber die Geschichte wiederholt sich. Genau wie im Jahr 2005 erklären einige Händler, dass MQL5 im Vergleich zu MQL4 zu kompliziert ist, wenn es darum geht, Strategien zu lernen und zu entwickeln. Dies bedeutet, dass das Gesamtniveau der Entwicklung von Handelsrobotern im Laufe der Jahre beträchtlich zugenommen hat, da der Entwickler keine Angst davor hatte, algorithmische Händler mit noch leistungsfähigeren Werkzeugen der Sprache C++ umzusetzen. Die neue MQL5 erlaubt es Programmierern, die Ergebnisse aller Operationen bis ins kleinste Detail zu überprüfen (dies ist besonders wichtig für die Abwicklung von Positionen) und den Speicherbedarf (RAM) zu kontrollieren. Das alte MQL4 bot viel weniger Möglichkeiten dieser Art, bevor es auf das Niveau von MQL5 verbessert wurde. Außerdem war die Syntax selbst weniger streng.
Ich glaube, dass Debatten über die Komplexität von MQL5 nach kurzer Zeit auch in Vergessenheit geraten werden. Aber da viele Händler immer noch nostalgisch am "good old MQL4" hängen, werden wir versuchen zu zeigen, wie die vertrauten MQL4-Funktionen aussehen können, wenn sie in MQL5 implementiert sind.
Wenn Sie gerade erst auf MQL5 umgestiegen sind, dann wird Ihnen dieser Artikel helfen. Erstens erfolgt der Zugriff auf die Indikatorendaten und -serien im üblichen MQL4-Stil. Zweitens ist diese ganze Einfachheit in MQL5 implementiert. Alle Funktionen sind so übersichtlich wie möglich und eignen sich perfekt für ein schrittweise Debugging.
1. Ist es möglich, in MQL5 mit Indikatoren im Stile von MQL4 zu arbeiten?
Der Hauptunterschied bei der Arbeit mit Indikatoren besteht darin, dass in MQL4 der Indikator-Befehl in einem Programm (iMACD(NULL,0,12,26,9,PRICE_CLOSE) das Anfordern der Indikatordaten eines Puffers - zB. ("MODE_MAIN") und dem Index (1) - gleichzeitig auch der Befehl für das Erstellen des Indikators ist.
//+------------------------------------------------------------------+ //| iMACd.mq4 | //| Copyright © 2018, Vladimir Karputov | //| http://wmua.ru/slesar/ | //+------------------------------------------------------------------+ #property copyright "Copyright © 2018, Vladimir Karputov" #property link "http://wmua.ru/slesar/" #property version "1.00" #property strict //+------------------------------------------------------------------+ //| Initialisierungsfunktion des Experten | //+------------------------------------------------------------------+ int OnInit() { //--- //--- return(INIT_SUCCEEDED); } //+------------------------------------------------------------------+ //| Deinitialisierungsfunktion des Experten | //+------------------------------------------------------------------+ void OnDeinit(const int reason) { //--- } //+------------------------------------------------------------------+ //| Experten Funktion OnTick | //+------------------------------------------------------------------+ void OnTick() { //--- double macd_main_1=iMACD(NULL,0,12,26,9,PRICE_CLOSE,MODE_MAIN,1); } //+------------------------------------------------------------------+
Folglich steht eine einzelne Zeichenkette in der Programmzeile nur für einen einzigen Schritt.
In MQL5 benötigt das Äquivalent dieses Codes mehrere Schritte:
- Deklarieren der Variablen, in der der Indikator-Handle gespeichert werden soll;
- Anlegen und Prüfen des Indikator-Handle;
- Eigenständige Funktion, die den Indikatorwert liefert.
//+------------------------------------------------------------------+ //| iMACD.mq5 | //| Copyright © 2018, Vladimir Karputov | //| http://wmua.ru/slesar/ | //+------------------------------------------------------------------+ #property copyright "Copyright © 2018, Vladimir Karputov" #property link "http://wmua.ru/slesar/" #property version "1.000" int handle_iMACD; // Variable des Handles für den Indikator des MACD //+------------------------------------------------------------------+ //| Initialisierungsfunktion des Experten | //+------------------------------------------------------------------+ int OnInit() { //--- Erstellen des Handles für den Indikator iMACD handle_iMACD=iMACD(Symbol(),Period(),12,26,9,PRICE_CLOSE); //--- wenn die Erstellung fehlschlug if(handle_iMACD==INVALID_HANDLE) { //--- Grund des Fehlers und die Ausgabe des Fehler-Nummer PrintFormat("Failed to create handle of the iMACD indicator for the symbol %s/%s, error code %d", Symbol(), EnumToString(Period()), GetLastError()); //--- Der Indikator wurde zu früh beendet return(INIT_FAILED); } //--- return(INIT_SUCCEEDED); } //+------------------------------------------------------------------+ //| Deinitialisierungsfunktion des Experten | //+------------------------------------------------------------------+ void OnDeinit(const int reason) { //--- } //+------------------------------------------------------------------+ //| Experten Funktion OnTick | //+------------------------------------------------------------------+ void OnTick() { //--- double macd_main_1=iMACDGet(MAIN_LINE,1); } //+------------------------------------------------------------------+ //| Get value of buffers for the iMACD | //| the buffer numbers are the following: | //| 0 - MAIN_LINE, 1 - SIGNAL_LINE | //+------------------------------------------------------------------+ double iMACDGet(const int buffer,const int index) { double MACD[1]; //--- Fehlernummer zurücksetzen ResetLastError(); //--- Füllen eine Teils des iMACDBuffer-Arrays mit Werten aus dem Indikatorpuffer mit dem Index 0. if(CopyBuffer(handle_iMACD,buffer,index,1,MACD)<0) { //--- Im Fehlerfall von CopyBuffer, drucke die Fehlernummer PrintFormat("Failed to copy data from the iMACD indicator, error code %d",GetLastError()); //--- Beenden mit Ergebnis Null - das heißt, anscheinend hat der Indikator nicht berechnet return(0.0); } return(MACD[0]); } //+------------------------------------------------------------------+
Lassen Sie uns den Code im MQL4-Stil umschreiben.
Das Erstellen des Indikator-Handles und das Anfordern der Indikatorendaten werden in einer einzigen Funktion realisiert:
//+------------------------------------------------------------------+ //| iMACD function in MQL4 notation | //+------------------------------------------------------------------+ double iMACD( string symbol, // Symbolname ENUM_TIMEFRAMES period, // Zeitrahmen int fast_ema_period, // Periodenlänge der schnellen Glättung int slow_ema_period, // Periodenlänge der langsamen Glättung int signal_period, // Periodenlänge der Glättung der Differenz ENUM_APPLIED_PRICE applied_price, // Preistyp int buffer, // Puffer int shift // Verschub ) { double result=NULL; //--- int handle=iMACD(symbol,period,fast_ema_period,slow_ema_period,signal_period, applied_price); double val[1]; int copied=CopyBuffer(handle,buffer,shift,1,val); if(copied>0) result=val[0]; return(result); }
Hinweis! Nach dem Schreiben der Funktion erstellen wir das Indikator-Handle BEI JEDEM Tick. Man kann sagen, dass eine solche "Kreativität" in der Dokumentation nicht empfohlen wird. Werfen wir einen Blick auf den Abschnitt Funktionen für die Arbeit mit technischen Indikatoren:
Man kann nicht Daten des Indikators sofort nah seiner Erzeugung verwenden, denn man braucht einige Zeit für Berechnung der Indikatorwerte. Darum ist es am besten Handles der Indikatoren in OnInit() zu erzeugen..
Warum funktioniert dieser Code ohne Speicherbedarf? Die Antwort ist im selben Abschnitt:
Hinweis. Wiederholter Aufruf der Funktion des Indikators mit denselben Parametern führt nicht zu vielfachen Steigerung des Counters der Verweise, Counter wird nur einmal um 1 vergrößert. Es wird jedoch empfohlen, die Indikator-Handles in der Funktion OnInit() oder im Klassenkonstruktor zu erstellen und diese Handles in anderen Funktionen weiter zu verwenden. Der Referenzzähler reduziert sich, wenn ein mql5-Programm deinitialisiert wird.
Mit anderen Worten, MQL5 ist optimal konzipiert: Es steuert die Erstellung der Handles und erlaubt nicht, den gleichen Indikator mit denselben Parametern mehrmals zu erstellen. Bei wiederholten Versuchen, ein Handle zu erzeugen, das nur eine Kopie des Indikators ist, erhalten Sie einfach das Handle des zuvor angelegten Indikators mit den entsprechenden Einstellungen. Trotzdem ist es immer noch empfehlenswert, die Handles einmalig in OnInit() zu erstellen und zu sichern. Die Gründe werden später bekannt gegeben.
Hinweis: Es findet keine Überprüfung der Gültigkeit des erzeugten Handles statt.
Nun sieht der Code, der die iMACD-Indikatorwerte anfordert, so aus:
//+------------------------------------------------------------------+ //| MACD MQL4 style EA.mq5 | //| Copyright © 2018, Vladimir Karputov | //| http://wmua.ru/slesar/ | //+------------------------------------------------------------------+ #property copyright "Copyright © 2018, Vladimir Karputov" #property link "http://wmua.ru/slesar/" #property version "1.000" #define MODE_MAIN 0 #define MODE_SIGNAL 1 //+------------------------------------------------------------------+ //| Initialisierungsfunktion des Experten | //+------------------------------------------------------------------+ int OnInit() { //--- //--- return(INIT_SUCCEEDED); } //+------------------------------------------------------------------+ //| Deinitialisierungsfunktion des Experten | //+------------------------------------------------------------------+ void OnDeinit(const int reason) { //--- } //+------------------------------------------------------------------+ //| Experten Funktion OnTick | //+------------------------------------------------------------------+ void OnTick() { //--- double macd_main_1=iMACD(NULL,0,12,26,9,PRICE_CLOSE,MODE_MAIN,1); } //+------------------------------------------------------------------+ //| iMACD function in MQL4 notation | //+------------------------------------------------------------------+ double iMACD( string symbol, // Symbolname ENUM_TIMEFRAMES period, // Zeitrahmen int fast_ema_period, // Periodenlänge der schnellen Glättung int slow_ema_period, // Periodenlänge der langsamen Glättung int signal_period, // Periodenlänge der Glättung der Differenz ENUM_APPLIED_PRICE applied_price, // Preistyp int buffer, // Puffer int shift // Verschub ) { double result=NULL; //--- int handle=iMACD(symbol,period,fast_ema_period,slow_ema_period,signal_period, applied_price); double val[1]; int copied=CopyBuffer(handle,buffer,shift,1,val); if(copied>0) result=val[0]; return(result); } //+------------------------------------------------------------------+
HINWEIS: Der Wunsch, auf Indikatoren im MQL4-Stil zuzugreifen, beraubt uns der Möglichkeit, den Rückgabewert zu überprüfen, da alle Funktionen im MQL4-Stil NUR Werte des Formats 'double' zurückgeben. Eine mögliche Lösung wird im Abschnitt 1.1 beschrieben.
Es sieht bisher ziemlich umständlich aus, deshalb implementierten wir den Block 'define' und die Funktion iMACD(), die einen 'double'-Wert zurückgibt, in einer separaten Include-Datei IndicatorsMQL5.mqh, die sich im eigenen Ordner "[Datenordner]\MQL5\Include\SimpleCall". befindet. In diesem Fall wird der Code ziemlich kurz. Bitte beachten Sie, dass wir die Datei IndicatorsMQL5.mqh mit 'include' laden müssen. Das bedeutet, dass die Namen der Indikatorlinien beim Zugriff auf den MACD in Form von MQL5 MAIN_LINE und nicht MQL4 MODE_MAIN übergeben werden sollten:
//+------------------------------------------------------------------+ //| MACD MQL4 style EA short.mq5 | //| Copyright © 2018, Vladimir Karputov | //| http://wmua.ru/slesar/ | //+------------------------------------------------------------------+ #property copyright "Copyright © 2018, Vladimir Karputov" #property link "http://wmua.ru/slesar/" #property version "1.000" #include <SimpleCall\IndicatorsMQL5.mqh> //+------------------------------------------------------------------+ //| Initialisierungsfunktion des Experten | //+------------------------------------------------------------------+ int OnInit() { //--- //--- return(INIT_SUCCEEDED); } //+------------------------------------------------------------------+ //| Deinitialisierungsfunktion des Experten | //+------------------------------------------------------------------+ void OnDeinit(const int reason) { //--- } //+------------------------------------------------------------------+ //| Experten Funktion OnTick | //+------------------------------------------------------------------+ void OnTick() { //--- double macd_main_1=iMACD(NULL,0,12,26,9,PRICE_CLOSE,MAIN_LINE,1); Comment("MACD, main buffer, index 1: ",DoubleToString(macd_main_1,Digits()+1)); } //+------------------------------------------------------------------+
Ich habe "Comment" nur zur Verifizierung implementiert. Sie können die Arbeit im Tester überprüfen, wenn Sie "MACD MQL4 style EA short.mq5" im visuellen Modus starten und den Cursor auf dei Bar mit dem Index #1 stellen:
Abb. 1. "MACD MQL4 style EA short.mh5" im Tester
1.1. Einige Feinheiten bei der Arbeit mit "IndicatorsXXXXXX.mqh".
Fehlerbehandlung in einem Rückgabewert
Alle Indikatoren geben ihre Daten 'double'-Werte zurück. Hier geht es darum, eine Nachricht an einen Benutzer zu senden, wenn es plötzlich nicht mehr möglich ist, Daten vom Indikator zu erhalten. Dies kann passieren, wenn das Indikatorhandle nicht erzeugt wird (z.B. wenn ein nicht vorhandenes Symbol angegeben ist) oder wenn beim Aufruf von CopyBuffer ein Kopierfehler aufgetreten ist.
Die einfache Übergabe von "0.0" im Fehlerfall ist keine Option, da für die meisten Indikatoren "0.0" ein ganz normaler Wert ist (z.B. für MACD). Die Rückgabe der Konstante EMPTY_VALUE (mit dem Wert von DBL_MAX) ist ebenfalls keine Option, da der Indikator Fractals seine Puffer mit dem Wert EMPTY_VALUE indiziert, was bedeutet, dass dies kein Fehler ist.
Die einzige verbleibende Option ist, "Nicht-Zahl" - NaN - zu übergeben. Um dies zu erreichen, wird die Variable NaN auf globaler Ebene angelegt. Die Variable wird als "Nicht-Zahl" initialisiert:
double NaN=double("nan"); //+------------------------------------------------------------------+ //| iAC function in MQL4 notation | //+------------------------------------------------------------------+ double iAC( string symbol, // Symbolname ENUM_TIMEFRAMES timeframe, // Zeitrahmen int shift // Verschub ) { double result=NaN; //--- int handle=iAC(symbol,timeframe); if(handle==INVALID_HANDLE) { Print(__FUNCTION__,": INVALID_HANDLE error=",GetLastError()); return(result); } double val[1]; int copied=CopyBuffer(handle,0,shift,1,val); if(copied>0) result=val[0]; else Print(__FUNCTION__,": CopyBuffer error=",GetLastError()); return(result); }
Der Vorteil dieses Ansatzes besteht auch darin, dass NaN im Fehlerfall zurückgegeben wird und das Ergebnis des Vergleichs mit jeder beliebigen Zahl 'falsch' ist.
//+------------------------------------------------------------------+ //| Script Programm Start Funktion | //+------------------------------------------------------------------+ void OnStart() { //--- Beispiel eines NaN-Vergleiches double NaN=double("nan"); double a=10.3; double b=-5; double otherNaN=double("nan"); Print("NaN>10.3=",NaN>a); Print("NaN<-5=",NaN<b); Print("(NaN==0)=",NaN==0); Print("(NaN==NaN)=",NaN==otherNaN); //--- Ergebnis NaN>10.3=false NaN<-5=false (NaN==0)=false (NaN==NaN)=false //--- }
Wenn wir also diese Funktionen im MQL4-Stil verwenden wollen, dann ist es notwendig, Handelsoperationen (wie auch alle anderen wichtigen Aktionen) nur dann durchzuführen, wenn das Ergebnis des Vergleichs true ist. Obwohl ich in diesem Fall darauf bestehe, den Rückgabewert mit der Funktion MathIsValidNumber zu überprüfen.
Bezeichner der Indikatorzeilen in MQL4 und MQL5
Es gibt ein Kompatibilitätsproblem im Teil der Werte von Konstanten, die die Indikatorzeilen beschreiben. Nehmen wir zum Beispiel den iAlligator:
- MQL4: 1 - MODE_GATORJAW, 2 - MODE_GATORTEETH, 3 - MODE_GATORLIPS
- MQL5: 0 - GATORJAW_LINE, 1 - GATORTEETH_LINE, 2 - GATORLIPS_LINE
Das Problem ist, dass die Indikatorzeile in der Funktion "IndicatorsXXXXXX.mqh" eine Zahl ist. Wenn diese Zahl z.B. 1 ist, dann kann niemand sagen, was der Benutzer meinte: Entweder sie arbeiteten im MQL4-Stil (und hatten im Kopf 1 - MODE_GATORJAW), oder sie arbeiteten im MQL5-Stil (und hatten im Kopf eine ganz andere Indikatorzeile 1 - GATORTEETH_LINE).
In diesem Zusammenhang habe ich beschlossen, zwei Include-Dateien zu erstellen - praktisch Zwillinge: "IndicatorsMQL4.mqh" und "IndicatorsMQL5.mqh". Ihr Unterschied besteht darin, dass die Datei "IndicatorsMQL4.mqh" Indikatorzeilen NUR im MQL4-Stil versteht, während die Datei "IndicatorsMQL5.mqh" Indikatorzeilen NUR im MQL5-Stil versteht. In "IndicatorsMQL4.mqh" erfolgt die Transformation der Indikatorzeile im Eingabeparameter direkt innerhalb der Funktionen iADX, iAlligator... - diese Transformationen können nicht mittels einem #define erreicht werden.
Lassen Sie mich den Grund dafür am Beispiel von iBands und iEnvelopes erläutern:
//+------------------------------------------------------------------+ //| iBands function in MQL4 notation | //| The buffer numbers are the following: | //| MQL4 0 - MODE_MAIN, 1 - MODE_UPPER, 2 - MODE_LOWER | //| MQL5 0 - BASE_LINE, 1 - UPPER_BAND, 2 - LOWER_BAND | //+------------------------------------------------------------------+ double iBands( ... //+------------------------------------------------------------------+ //| iEnvelopes function in MQL4 notation | //| The buffer numbers are the following: | //| MQL4 0 - MODE_MAIN, 1 - MODE_UPPER, 2 - MODE_LOWER | ??? //| MQL5 0 - UPPER_LINE, 1 - LOWER_LINE, -/- | //+------------------------------------------------------------------+ double iEnvelopes(
In MQL4 wird, für den Indikator iBands, MODE_UPPER in 1 transformiert, während für den den Indikator iEnvelopes es in 0 transformiert wird.
2. Was ist der Speicherbedarf, wenn wir Indikatoren im MQL4-Stil bei jedem Tick anwenden?
Vergleichen wir den Speicherbedarf der beiden EAs: "iMACD.mq5" - der EA mit korrektem Zugriff auf die Indikatoren und der "MACD MQL4 style EA short.mq5" - mit Zugriff auf MQL4 style Indikatoren. Die maximale Anzahl der Bars im Fenster ist in den Terminaleinstellungen auf "100 000" eingestellt. Erstellen Sie zwei Profile von 14 Charts:
- Das Profil "iMACd" - der EA "iMACd.mq5" ist auf 13 Charts gestartet worden, alle Charts haben den Zeitrahmen M30;
- Das Profil "MACD MQL4 style EA short" - der EA "MACD MQL4 style EA short.mq5" wird auch auf 13 Charts gestartet.
Der Indikator "Terminal Memory used.mq5" wird auf dem vierzehnten gestartet. Sein Ziel ist es, den Wert von TERMINAL_MEMORY_USED alle 10 Sekunden zu drucken.
Wir werden zwei Werte vergleichen: die Menge des vom Terminal benötigten RAMs (Task-Manager-Daten) und die angezeigten Werte von TERMINAL_MEMORY_USED. Die Beobachtung wird für 10 Minuten durchgeführt - wir werden sehen, ob zu viel Speicher verwendet wird. Die wichtigste Bedingung: Nach dem Starten des Terminals nichts tun - keine neuen Registerkarten öffnen oder den Chat lesen.
Profil | Task-Manager | TERMINAL_MEMORY_USED | Task-Manager (in 10 Minuten) | TERMINAL_MEMORY_USED (in 10 Minuten) |
---|---|---|---|---|
iMACd | 279.7 MB | 745 MB | 279.7 MB | 745 MB |
MACD MQL4 style EA short | 279.9 MB | 745 MB | 280.0 MB | 745 MB |
Ändern wir nun den Test: Nach einer Laufzeit von 10 Minuten stellen wir die Zeitrahmen aller Charts auf H1 um.
Profil | Task-Manager | TERMINAL_MEMORY_USED | Task-Manager (in 10 Minuten) | TERMINAL_MEMORY_USED (in 10 Minuten) |
---|---|---|---|---|
iMACd | 398.0 MB | 869 MB | 398.3 MB | 869 MB |
MACD MQL4 style EA short | 319.2 MB | 874 MB | 330.5 MB | 874 MB |
Übersichtstabelle zur besseren Übersichtlichkeit des Speicherbedarfs:
Profil | Task Manager (M30), MB |
TERMINAL_MEMORY_USED (M30), MB |
Task Manager (H1), MB |
TERMINAL_MEMORY_USED (H1), MB |
||||
---|---|---|---|---|---|---|---|---|
Start | nach 10 Minuten | Start | nach 10 Minuten | Start | nach 10 Minuten | Start | nach 10 Minuten | |
iMACd | 279.7 | 279.7 | 745 | 745 | 398.0 | 869 | 398.3 | 869 |
MACD MQL4 style EA short | 279.9 | 280.0 | 745 | 745 | 319.2 | 874 | 330.5 | 874 |
3. Das neue Leben des EAs MACD Sample.mq4
Lassen Sie uns die Ausführungsgeschwindigkeit, den Speicherbedarf und den EA [Datenordner]\MQL4\Experts\MACD Sample.mq4 (entwickelt in MQL5, aber im MQL4-Stil wie "MACD MQL4 style EA short.mq5") überprüfen.
3.1. Ändern wir den EA "MACD Sample.mq5", so dass er einen Wert nach dem anderen erhält.
Der standardmäßige "MACD Sample.mq5" erhält zwei Indikatorwerte auf einmal:
//+------------------------------------------------------------------+ //| main function returns true if any position processed | //+------------------------------------------------------------------+ bool CSampleExpert::Processing(void) { //--- neue Kurse abfragen if(!m_symbol.RefreshRates()) return(false); //--- Indikatorwerte neu berechnen if(BarsCalculated(m_handle_macd)<2 || BarsCalculated(m_handle_ema)<2) return(false); if(CopyBuffer(m_handle_macd,0,0,2,m_buff_MACD_main) !=2 || CopyBuffer(m_handle_macd,1,0,2,m_buff_MACD_signal)!=2 || CopyBuffer(m_handle_ema,0,0,2,m_buff_EMA) !=2) return(false); // m_indicators.Refresh(); //--- um den Code zu vereinfachen und den Zugriff zu beschleunigen //--- Datenspeicherung in internen Variablen m_macd_current =m_buff_MACD_main[0]; m_macd_previous =m_buff_MACD_main[1]; m_signal_current =m_buff_MACD_signal[0]; m_signal_previous=m_buff_MACD_signal[1]; m_ema_current =m_buff_EMA[0]; m_ema_previous =m_buff_EMA[1];
Danach werden den Variablen die Daten der Arrays mit der Dimension "2" zugewiesen. Warum wird das so gemacht? Unabhängig davon, ob wir mit einem oder zwei Werten pro Zeit kopieren, verwenden wir immer noch CopyBuffer. Wenn wir jedoch zwei Werte auf einmal kopieren, sparen wir uns eine Operation, wenn wir die Werte dem Array zuweisen.
Aber "MACD Sample.mq4" EA erhält jedes Mal nur einen Indikatorwert:
//--- um den Coe zu vereinfachen und die Ausführung zu beschleunigen, werden die Daten internen Variablen zugewiesen MacdCurrent=iMACD(NULL,0,12,26,9,PRICE_CLOSE,MODE_MAIN,0); MacdPrevious=iMACD(NULL,0,12,26,9,PRICE_CLOSE,MODE_MAIN,1); SignalCurrent=iMACD(NULL,0,12,26,9,PRICE_CLOSE,MODE_SIGNAL,0); SignalPrevious=iMACD(NULL,0,12,26,9,PRICE_CLOSE,MODE_SIGNAL,1); MaCurrent=iMA(NULL,0,MATrendPeriod,0,MODE_EMA,PRICE_CLOSE,0); MaPrevious=iMA(NULL,0,MATrendPeriod,0,MODE_EMA,PRICE_CLOSE,1);
Die Hauptlinie, die Signallinie und der gleitende Durchschnitt des MACD werden jeweils zweimal abgefragt. Daher sollte "MACD Sample.mq5" in die gleiche Form gebracht werden. Nennen wir diese EA-Version "MACD Sample One value at a time.mq5". Hier ist er jetzt so verändert, dass wir einen Wert nach dem anderen erhalten:
//--- Indikatorwerte neu berechnen if(BarsCalculated(m_handle_macd)<2 || BarsCalculated(m_handle_ema)<2) return(false); // if(CopyBuffer(m_handle_macd,0,0,2,m_buff_MACD_main) !=2 || // CopyBuffer(m_handle_macd,1,0,2,m_buff_MACD_signal)!=2 || // CopyBuffer(m_handle_ema,0,0,2,m_buff_EMA) !=2) // return(false); // m_indicators.Refresh(); //--- um den Code zu vereinfachen und den Zugriff zu beschleunigen //--- Datenspeicherung in internen Variablen CopyBuffer(m_handle_macd,0,0,1,m_buff_MACD_main); m_macd_current=m_buff_MACD_main[0]; CopyBuffer(m_handle_macd,0,1,1,m_buff_MACD_main); m_macd_previous=m_buff_MACD_main[0]; CopyBuffer(m_handle_macd,1,0,1,m_buff_MACD_signal); m_signal_current=m_buff_MACD_signal[0]; CopyBuffer(m_handle_macd,1,1,1,m_buff_MACD_signal); m_signal_previous=m_buff_MACD_signal[0]; CopyBuffer(m_handle_ema,0,0,1,m_buff_EMA); m_ema_current=m_buff_EMA[0]; CopyBuffer(m_handle_ema,0,1,1,m_buff_EMA); m_ema_previous=m_buff_EMA[0];
Dieser Code wird in der datei "MACD Sample One value at a time.mq5" gespeichert, die am Ende des Artikels angehängt ist.
3.2. Konvertieren von "MACD Sample.mq4" nach MQL5
Um auf die Indikatoren im MQL4-Stil zugreifen zu können, sowie mit Positionen und Handel arbeiten zu können, sollten wir die Datei "IndicatorsMQL4.mqh" (wie Sie sich erinnern, versteht diese Datei nur die MQL4-Namen der Indikatorlinien) und die Handelsklassen CPositionInfo, CTrade, CSymbolInfo und CAccountInfo einbinden. Außerdem sollte der Block von 'define' - Namen der Zeilen im Indikator - dem EA hinzugefügt werden, um einen korrekten Zugriff auf die Indikatoren in "IndicatorsMQL4.mqh" zu ermöglichen:
#property description " and the indicators are accessed in the style of MQL4" #define MODE_MAIN 0 #define MODE_SIGNAL 1 #include <SimpleCall\IndicatorsMQL4.mqh> //--- #include <Trade\PositionInfo.mqh> #include <Trade\Trade.mqh> #include <Trade\SymbolInfo.mqh> #include <Trade\AccountInfo.mqh> CPositionInfo m_position; // Objekt der Position CTrade m_trade; // Handelsobjekt CSymbolInfo m_symbol; // Objekt der Symbolinfo. CAccountInfo m_account; // Kontoobjekt //--- input double TakeProfit =50;
Darüber hinaus wird ein spezieller Faktor für die Anpassung an drei- und fünfstellige Kurse benötigt:
input double MACDCloseLevel=2; input int MATrendPeriod =26; //--- double m_adjusted_point; // Wert eines Points für 3 und 5 Stellen //+------------------------------------------------------------------+ //| Initialisierungsfunktion des Experten | //+------------------------------------------------------------------+
Um aktuelle Preise zu erhalten, verwende ich das Objekt m_symbol der Handelsklasse CSymbolInfo:
//+------------------------------------------------------------------+ //| Initialisierungsfunktion des Experten | //+------------------------------------------------------------------+ int OnInit() { //--- if(!m_symbol.Name(Symbol())) // Festlegen des Symbolnamens return(INIT_FAILED); RefreshRates();
Die Methode RefreshRates() aktualisiert die Preise und stellt sicher, dass keine Preise mit dem Wert "0.0" existieren:
//+------------------------------------------------------------------+ //| Refreshes the symbol quotes data | //+------------------------------------------------------------------+ bool RefreshRates(void) { //--- neue Kurse abfragen if(!m_symbol.RefreshRates()) { Print("RefreshRates error"); return(false); } //--- Schutz vor der Rückgabe von "Null" if(m_symbol.Ask()==0 || m_symbol.Bid()==0) return(false); //--- return(true); }
Der Faktor m_adjusted_point wird in OnInit () initialisiert, nachdem das Objekt m_symbol initialisiert wurde:
//+------------------------------------------------------------------+ //| Initialisierungsfunktion des Experten | //+------------------------------------------------------------------+ int OnInit() { //--- if(!m_symbol.Name(Symbol())) // Festlegen des Symbolnamens return(INIT_FAILED); RefreshRates(); //--- Bestimmen von 3 oder 5 Stellen int digits_adjust=1; if(m_symbol.Digits()==3 || m_symbol.Digits()==5) digits_adjust=10; m_adjusted_point=m_symbol.Point()*digits_adjust; //--- return(INIT_SUCCEEDED); }
In OnTick(), dank der angehängten Datei "IndicatorsMQL4Style.mqh", greifen wir auf die Indikatoren im MQL4-Stil zu:
if(!RefreshRates()) return; //--- um den Coe zu vereinfachen und die Ausführung zu beschleunigen, werden die Daten internen Variablen zugewiesen MacdCurrent=iMACD(NULL,0,12,26,9,PRICE_CLOSE,MAIN_LINE,0); MacdPrevious=iMACD(NULL,0,12,26,9,PRICE_CLOSE,MAIN_LINE,1); SignalCurrent=iMACD(NULL,0,12,26,9,PRICE_CLOSE,SIGNAL_LINE,0); SignalPrevious=iMACD(NULL,0,12,26,9,PRICE_CLOSE,SIGNAL_LINE,1); MaCurrent=iMA(NULL,0,MATrendPeriod,0,MODE_EMA,PRICE_CLOSE,0); MaPrevious=iMA(NULL,0,MATrendPeriod,0,MODE_EMA,PRICE_CLOSE,1);
3.2.1. Die Arbeit mit Positionen
Der Sicherheit halber überprüfen wir, ob es keine offene Positionen gibt.
total=PositionsTotal(); if(total<1) {
Obwohl dieser Ansatz nicht ganz korrekt ist, da er die Existenz offener Positionen mit anderen Symbolen und/oder mit anderen Identifikatoren (Magicnummer) nicht berücksichtigt.
3.2.2. Kaufpositionen werden mit der Methode Buy der Klasse CTrade eröffnet, während die Korrektheit der Ausführung mit der Methode ResultDeal derselben Klasse überprüft wird. ResultDeal gibt eine Ticketnummer der Position zurück, wenn es ausgeführt wird.
//--- Prüfen, ob eine Kaufposition eröffnet werden kann if(MacdCurrent<0 && MacdCurrent>SignalCurrent && MacdPrevious<SignalPrevious && MathAbs(MacdCurrent)>(MACDOpenLevel*m_adjusted_point) && MaCurrent>MaPrevious) { m_trade.Buy(Lots,m_symbol.Name(),m_symbol.Ask(), 0.0, m_symbol.NormalizePrice(m_symbol.Ask()+TakeProfit*m_adjusted_point), "macd sample"); if(m_trade.ResultDeal()!=0) Print("BUY position opened : ",m_trade.ResultPrice()); else Print("Error opening BUY position : ",m_trade.ResultRetcodeDescription()); return; }
Beachten Sie, dass der Preis der Handelsanfrage mit der Methode NormalizePrice der Handelsklasse CSymbolInfo normalisiert wird. Diese Methode erlaubt es, die Quantisierung zu berücksichtigen: minimale Preisänderung und die Anzahl der Dezimalstellen.
Die gleichen Methoden werden verwendet, um Verkaufspositionen zu eröffnen.
3.2.3. Positionen Bypass-Block: Schließen oder Modifizieren.
Die Schleife selbst wird von der Anzahl aller Positionen minus eins bis einschließlich Null durchlaufen. Um mit einer Position arbeiten zu können, müssen wir sie zunächst in der allgemeinen Liste nach Index selektieren:
for(int i=PositionsTotal()-1;i>=0;i--) if(m_position.SelectByIndex(i)) // wählt die Position nach Index für den weiteren Zugriff auf seine Eigenschaften aus
Die Position wird mit der Methode PositionClose geschlossen, während eine Modifikation mit PositionModify durchgeführt wird. Beachten Sie, dass die Modifikation die Verwendung der Methode NormalizePrice der Handelsklasse CSymbolInfo erlaubt.
Der gesamte Block, um die Position zu umgehen:
//--- es ist wichtig, den Markt richtig zu betreten, aber es ist wichtiger, ihn richtig zu verlassen... for(int i=PositionsTotal()-1;i>=0;i--) if(m_position.SelectByIndex(i)) // wählt die Position nach Index für den weiteren Zugriff auf seine Eigenschaften aus. if(m_position.Symbol()==m_symbol.Name()) { //--- Kaufposition eröffnet if(m_position.PositionType()==POSITION_TYPE_BUY) { //--- Schließen? if(MacdCurrent>0 && MacdCurrent<SignalCurrent && MacdPrevious>SignalPrevious && MacdCurrent>(MACDCloseLevel*m_adjusted_point)) { //--- Schließen und verlassen if(!m_trade.PositionClose(m_position.Ticket())) Print("PositionClose error ",m_trade.ResultRetcodeDescription()); return; } //--- Prüfen auf einen Trailing-Stop if(TrailingStop>0) { if(m_position.PriceCurrent()-m_position.PriceOpen()>m_adjusted_point*TrailingStop) { if(m_position.StopLoss()<m_symbol.Bid()-m_adjusted_point*TrailingStop) { //--- Position ändern und verlassen if(!m_trade.PositionModify(m_position.Ticket(), m_symbol.NormalizePrice(m_position.PriceCurrent()-m_adjusted_point*TrailingStop), m_position.TakeProfit())) Print("PositionModify error ",m_trade.ResultRetcodeDescription()); return; } } } } if(m_position.PositionType()==POSITION_TYPE_SELL) { //--- Schließen? if(MacdCurrent<0 && MacdCurrent>SignalCurrent && MacdPrevious<SignalPrevious && MathAbs(MacdCurrent)>(MACDCloseLevel*m_adjusted_point)) { //--- Schließen und verlassen if(!m_trade.PositionClose(m_position.Ticket())) Print("PositionClose error ",m_trade.ResultRetcodeDescription()); return; } //--- Prüfen auf einen Trailing-Stop if(TrailingStop>0) { if((m_position.PriceOpen()-m_position.PriceCurrent())>(m_adjusted_point*TrailingStop)) { if((m_position.StopLoss()>(m_symbol.Ask()+m_adjusted_point*TrailingStop)) || (m_position.StopLoss()==0.0)) { //--- Position ändern und verlassen if(!m_trade.PositionModify(m_position.Ticket(), m_symbol.NormalizePrice(m_symbol.Ask()+m_adjusted_point*TrailingStop), m_position.TakeProfit())) Print("PositionModify error ",m_trade.ResultRetcodeDescription()); return; } } } } }
Wir sind mit allen Änderungen fertig. Die endgültige Datei "MACD Sample 4 to 5 MQL4 style.mq5" ist unten angehängt.
3.3. Vergleichen wir die Geschwindigkeit, mit die MACD-basierten EAs ausgeführt werden.
Die folgenden EAs werden zum Vergleich herangezogen:
- "MACD Sample.mq5" - der standardmäßige EA des Terminals mit der korrekten Indikatorverwendung
- "MACD Sample One value at a time.mq5" - das Äquivalent zu "MACD Sample.mq5", wobei wir immer nur einen Wert des Indikators abfragen.
- "MACD Sample 4 to 5 MQL4 style.mq5" - der MQL4 EA, konvertiert nach MQL5, mit minimalen Modifikationen und Zugriff auf die Indikatoren im Stile von MQL4
Der Test wurde mit USDJPY M30 von 2017.02.01 bis 2018.01.16 auf dem MetaQuotes-Demo-Server durchgeführt. Das Terminal wurde nach jedem Test zurückgesetzt (je nachdem, ob der EAs gewechselt wurde oder die Tick-Generierung). PC-Konfiguration:
Windows 10 (build 16299) x64, IE 11, UAC, Intel Core i3-3120M @ 2.50GHz, Memory: 4217 / 8077 Mb, Disk: 335 / 464 Gb, GMT+2
# | Expert Advisor | Jeder Tick auf Basis von Real-Ticks | Jeder Tick | OHLC | ||||||
---|---|---|---|---|---|---|---|---|---|---|
Testdauer | Positionen | Transaktionen | Testdauer | Positionen | Transaktionen | Testdauer | Positionen | Transaktionen | ||
1 | MACD Sample.mq5 | 0:01:19.485 | 122 | 244 | 0:00:53.750 | 122 | 244 | 0:00:03.735 | 119 | 238 |
2 | MACD Sample One value at a time.mq5 | 0:01:20.344 | 122 | 244 | 0:00:56.297 | 122 | 244 | 0:00:03.687 | 119 | 238 |
3 | MACD Sample 4 to 5 MQL4 style.mq5 | 0:02:37.422 | 122 | 244 | 0:01:52.171 | 122 | 244 | 0:00:06.312 | 119 | 238 |
Alle drei EAs zeigen ähnliche Diagramme im Modus "Jeder Tick":
Abb. 2. MACD Sample XXXX im Strategietester
FAZIT: Der EA "MACD Sample 4 to 5 MQL4 style.mq5" EA mit Zugriff auf Indikatoren im MQL4-Stil ist doppelt so langsam wie vergleichbare EAs mit korrektem Zugriff auf Indikatoren.
3.4. Vergleichen wir den Speicherbedarf von MACD-basierten EAs.
Dazu werden die gleichen 14 Diagramme verwendet, wie in Punkt 2. Was passiert mit dem Speicherbedarf, wenn wir Indikatoren im MQL4-Stil bei jedem Tick anwenden? Der Indikator "Terminal Memory used.mq5" wird immer auf dem ersten Chart gestartet. Es erneuert alle 10 Sekunden den Wert von TERMINAL_MEMORY_USED ID, während die EAs auf den restlichen 13 Charts einzeln gestartet werden. Das Terminal wird vor jeder Messung zurückgesetzt.
# | Expert Advisor | Task Manager, MB | TERMINAL_MEMORY_USED, Мб |
---|---|---|---|
1 | MACD Sample.mq5 | 334.6 | 813 |
2 | MACD Sample One value at a time.mq5 | 335.8 | 813 |
3 | MACD Sample 4 to 5 MQL4 style.mq5 | 342.2 | 818 |
FAZIT: Die MACD-basierten EAs mit korrektem Zugriff auf die Indikatoren und MACD-basierte EAs mit Zugriff auf die Indikatoren im MQL4-Stil sind in Bezug auf den Speicherbedarf vergleichbar. Sie verwenden etwas gleich viel Speicherplatz.
4. Das neue Leben des EAs [Datenordner]\MQL4\Experten\Moving Average.mq4
Im vorigen Abschnitt haben wir MQL4 in MQL5 konvertiert. Was Movinge Average.mq4 angeht, schlage ich vor, einfach den Moving Average.mq5 zu ändern, so dass die Datei "IndicatorsMQL5.mqh" geladen wird.
#property version "1.00" #include <SimpleCall\IndicatorsMQL5.mqh> #include <Trade\Trade.mqh>
und CopyBuffer zu ersetzen
//--- Abfrage des gleitenden Durchschnitts double ma[1]; if(CopyBuffer(ExtHandle,0,0,1,ma)!=1) { Print("CopyBuffer from iMA failed, no data"); return; }
und der Zugriff auf den Indikator im Stile von MQL4:
//--- Abfragen des gleitenden Durchschnitts ma=iMA(NULL,0,MovingPeriod,MovingShift,MODE_SMA,PRICE_CLOSE,0);
Damit bleibt uns nur noch eine Möglichkeit, das Operationsergebnis zu überprüfen - vergleichen wir die erhaltenen Daten mit Null. Vor diesem Hintergrund sah der letzte Eintrag in den Blöcken "CheckForOpen" und "CheckForClose" wie folgt aus:
//--- Abfrage des gleitenden Durchschnitts double ma[1]; if(CopyBuffer(ExtHandle,0,0,1,ma)!=1) { Print("CopyBuffer from iMA failed, no data"); return; }
und wird so ausschauen:
//--- Abfrage des gleitenden Durchschnitts double ma[1]; ma[0]=iMA(_Symbol,_Period,MovingPeriod,MovingShift,MODE_SMA,PRICE_CLOSE,0); //if(CopyBuffer(ExtHandle,0,0,1,ma)!=1) if(ma[0]==0.0) { //Print("CopyBuffer from iMA failed, no data"); Print("Get iMA in MQL4 style failed, no data"); return; }
Dies sind die Änderungen, die wir im EA "Moving Average MQL4 style.mq5" speichern werden. Der EA ist unten angefügt. Messen wir die Leistung und den Speicherbedarf zwischen dem standardmäßigen "Moving Average.mq5" und "Moving Average MQL4 style.mq5".
Wie Sie sich erinnern können, wurden die Tests auf folgendem System durchgeführt
Windows 10 (build 16299) x64, IE 11, UAC, Intel Core i3-3120M @ 2.50GHz, Memory: 4217 / 8077 Mb, Disk: 335 / 464 Gb, GMT+2
Das Terminal wurde nach jedem Test zurückgesetzt. Die Tests wurden mit EURUSD M15 vom 01.02.2017 bis zum 01.01.2018.01.16 auf dem MetaQuotes-Demo-Server durchgeführt.
# | Expert Advisor | Jeder Tick auf Basis von Real-Ticks | Jeder Tick | OHLC | ||||||
---|---|---|---|---|---|---|---|---|---|---|
Testdauer | Positionen | Transaktionen | Testdauer | Positionen | Transaktionen | Testdauer | Positionen | Transaktionen | ||
1 | Moving Average.mq5 | 0:00:33.359 | 1135 | 2270 | 0:00:22.562 | 1114 | 2228 | 0:00:02.531 | 1114 | 2228 |
2 | Moving Average MQL4 style.mq5 | 0:00:34.984 | 1135 | 2270 | 0:00:23.750 | 1114 | 2228 | 0:00:02.578 | 1114 | 2228 |
FAZIT: Der MQL5-Kern musste wahrscheinlich zwischen zwei Handles in MACD Sample bei jedem Tick suchen, wenn er auf die Indikatoren im MQL4-Stil zugreift. Es war diese Suche, die die meiste Zeit in Anspruch nahm.
Im Falle des Moving Average EA verbringt der MQL5-Kern beim Zugriff auf den Indikator im MQL4-Stil keine Zeit mit der Suche nach einer benötigten Kerze, da sie die einzige ist.
Vergleichen wir den Speicherbedarf des Moving Average-basierten EAs.
Dazu werden die gleichen 14 Diagramme verwendet, wie in Punkt 2. Der Indikator "Terminal Memory used.mq5" wird immer auf dem ersten Chart gestartet. Es erneuert alle 10 Sekunden den Wert von TERMINAL_MEMORY_USED ID, während die EAs auf den restlichen 13 Charts einzeln gestartet werden. Das Terminal wird vor jeder Messung zurückgesetzt.
# | Expert Advisor | Task Manager, MB | TERMINAL_MEMORY_USED, Мб |
---|---|---|---|
1 | Moving Average.mq5 | 295.6 | 771 |
2 | Moving Average MQL4 style.mq5 | 283.6 | 760 |
FAZIT: Der Speicherbedarf ist nahezu identisch. Kleine Unterschiede lassen sich auf das "Innenleben" des Terminals zurückführen: Newsupdates, etc.
5. Äquivalente der iXXXX Serie
Da wir die Abfrage der Indikatorwerte im MQL4-Stil durchgeführt haben, schreiben wir die Funktionen des Abschnitts "Zugriff auf Zeitreihen und Indikatorendaten". Die Implementierung erfolgt in [Datenordner]\MQL5\Include\SimpleCall\Series.mqh.
Die Liste der Funktionen in Series.mqh, die den Zugriff auf Zeitreihenwerte wie in MQL4 ermöglichen:
Für die Funktionen iHighest und iLowest stehen die vordefinierten Enumerationen MODE_OPEN, MODE_LOW, MODE_HIGH, MODE_CLOSE, MODE_VOLUME und MODE_TIME zur Verfügung.
Beispiel für die Implementierung der Funktion iClose:
//+------------------------------------------------------------------+ //| iClose function in MQL4 notation | //+------------------------------------------------------------------+ double iClose( string symbol, // Symbol ENUM_TIMEFRAMES timeframe, // Zeitrahmen int shift // Verschub ) { double result=0.0; //--- double val[1]; ResetLastError(); int copied=CopyClose(symbol,timeframe,shift,1,val); if(copied>0) result=val[0]; else Print(__FUNCTION__,": CopyClose error=",GetLastError()); //--- return(result); }
Der Wert für shift der Schlusskurse wird über CopyClose umgesetzt — der ersten Form des Aufrufs (Zugriff über die Ausgangsposition und die Anzahl der benötigten Elemente):
int CopyClose( string symbol_name, // Symbolname ENUM_TIMEFRAMES timeframe, // Zeitrahmen int start_pos, // Anfangsposition int count, // Anzahl der Kopien double close_array[] // Array der kopierten Preise );
Schlussfolgerung
Wie wir sehen können, erlaubt MQL5 MQL4-Fans, die Werte von Indikatoren und Zeitreihen in ihrem bevorzugten Stil zu erhalten. Manche sagen, dass dieser Code kürzer und leichter zu lesen ist. Plattformentwickler benötigen jedoch eine sorgfältigere Arbeit mit einem Code und maximale Prüfungen beim Aufruf von Funktionen (und ich stimme ihnen voll und ganz zu). Lassen Sie uns kurz die Vor- und Nachteile der im Artikel beschriebenen Funktionen aufzählen.
Nachteile
- Einschränkung bei der Verarbeitung des zurückgegebenen Fehlers beim Zugriff auf Indikatoren;
- Verringerung der Testgeschwindigkeit bei gleichzeitigem Zugriff auf mehr als einen Indikator;
- Die Notwendigkeit, die Indikatorzeilen korrekt hervorzuheben, je nachdem, ob IndicatorsMQL5.mqh oder IndicatorsMQL4.mqh geladen wurden.
- Einfachheit des Code-Schreibens - eine Zeichenkette statt mehrerer;
- Sichtbarkeit und Prägnanz - je kürzer der Code, desto leichter ist das Verständnis.
Übersetzt aus dem Russischen von MetaQuotes Ltd.
Originalartikel: https://www.mql5.com/ru/articles/4318





- 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.