Test-Performance der Berechnung von gleitenden Mittelwerten in MQL5
Einleitung
Die Nutzung von gleitenden Mittelwerten (Moving Averages) ist bei der Analyse von Zeitreihen von Märkten und in der Programmierung von Indikatoren und Expert Advisors weit verbreitet. Sie sind die beliebteste Methode zur Datenglättung. In der neuen Version der MQL-Sprache steht ein Dutzend Moving-Average-Algorithmen zur Verfügung.
Worin unterscheiden sie sich? Hängt die Berechnungsgeschwindigkeit wirklich von einem bestimmten Algorithmus von gleitenden Mittelwerten ab? Welcher Algorithmus ist schneller?
Hat sich die Berechnungsgeschwindigkeit von gleitenden Mittelwerten in MetaTrader 5 im Vergleich zu MetaTrader 4 erhöht? Es gibt viele solche Fragen. Lassen Sie uns einen Großteil davon beantworten.
Die Geschwindigkeit der neuen Plattform ist natürlich beeindruckend, doch sie sollte lieber experimentell geprüft werden.
1. Testbedingungen
Die Berechnungsgeschwindigkeit hängt von zahlreichen Faktoren ab. Deshalb unterscheiden sich die Daten, die wir aus dieser Nachforschung erhalten, von Daten, die unter anderen Bedingungen entstanden wären. In anderen Worten: Die absoluten Performance-Werte sind unterschiedlich, doch die relativen Werte sollten ähnlich sein (für eine bestimmte Plattform).
Da die iMA-Funktion in MQL5 nicht die Berechnungsergebnisse selbst ausgibt (sie gibt das Handle eines Indikators aus), testen wir die Geschwindigkeit von zwei Funktionen: iMA und CopyBuffer.
Testbedingungen:- Prozessor: Core i7 965
- Symbol: "EURUSD"
- Größe der Preisdaten: 10000 Elemente
- Client Terminal: autonom, die maximale Anzahl von Balken im Diagramm ist auf 10000 eingestellt
- Modelle des gleitenden Mittelwerts: MODE_SMA, MODE_EMA, MODE_SMMA, MODE_LWMA
- Die Genauigkeit der Berechnungsgeschwindigkeit ist auf zwei signifikante Stellen begrenzt
- Mögliche Menge der Aufrufe der Funktionen der gleitenden Mittelwerte: 7
2. Wie wir getestet haben
Um die Dauer der Berechnung der gleitenden Mittelwerte zu messen, haben wir die Funktion GetTickCount(), die in Millisekunden rechnet. Dieser Genauigkeitsgrad reicht nicht aus. Deshalb müssen wir Zyklen einrichten, die die Qualität der Messungen erhöhen.
Führen wir die Schleife allerdings viele Male mit derselben Berechnung und denselben Eingangsdaten aus, erhalten wir verfälschte Ergebnisse. Der Grund dafür ist folgender: Die Funktion iMA erstellt eine Kopie des entsprechenden technischen Indikators im globalen Zwischenspeicher des Client Terminals. Wenn die Kopie des Indikators (mit den gleichen Parametern) bereits im globalen Zwischenspeicher vorhanden ist, wird die neue Kopie nicht erstellt, sondern es wird der Referenzzähler der Kopie des Indikators erhöht.
In anderen Worten: Der gesamte Indikatorpuffer wird nur einmal beim ersten Aufruf berechnet und bei allen nachfolgenden Aufrufen nimmt er nur die vorhandenen Werte und berechnet nur die neuen Daten.
Deshalb muss die Schleife so eingerichtet werden, dass die Eingabeparameter des Indikators während des Zyklus eindeutig sind. Wir haben drei solche Parameter ausgesucht: Mittelungszeitraum, Timeframe und verwendeter Preis.
Parameter | Wertbereich |
---|---|
Mittelungszeitraum | von 1 bis 100 |
Timeframe | М1, М5, М15, М30 |
Angewendeter Preis | PRICE_CLOSE, PRICE_OPEN, PRICE_HIGH, PRICE_LOW, PRICE_MEDIAN, PRICE_TYPICAL, PRICE_WEIGHTED |
Tabelle 1. Bereiche der Eingabeparameter
Wir berechnen die Werte des gleitenden Mittelwerts für das Array mit 10000 Elementen mithilfe der sieben unterschiedlichen Aufrufmethoden (siehe Details in Abschnitt 4).
3. Ergebnisse der Studie
Wir haben alle Ergebnisse in Tabelle 1 gesammelt. Die Berechnungsperformance wird anhand der Berechnungsdauer (siehe Tabelle 1) in Sekunden geschätzt. Das Programm berechnet 100x4x7=2800 Typen von gleitenden Mittelwerten und wir bestimmen die Berechnungsdauer für das Preisarray mit 10000 Elementen. Die Berechnungsdauer eines einzelnen Durchlaufs (Zyklus) entspricht in etwa der Gesamtdauer geteilt durch 2800. Für den Fall 1 und Modus SMA entspricht sie beispielsweise ~0,0028/2800.
Modus | MODE_SMA | MODE_EMA | MODE_SMMA | MODE_LWMA | Plattform |
---|---|---|---|---|---|
0 (siehe Abschnitt 4.1) | 0,0041 | 0,0040 | 0,0043 | 0,0041 | MetaTrader 4 |
1 (siehe Abschnitt 4.2) | 0,0028 | 0,00023 | 0,00027 | 0,0045 | MetaTrader 5 |
2 (siehe Abschnitt 4.3) | 0,0029 | 0,0029 | 0,0029 | 0,0029 | MetaTrader 5 |
3 (siehe Abschnitt 4.4) | 0,0998 | 0,0997 | 0,0998 | 0,0998 | MetaTrader 5 |
4 (siehe Abschnitt 4.5) | 0,0996 | 0,0996 | 0,0996 | 0,0996 | MetaTrader 5 |
5 (siehe Abschnitt 4.6) | 0,0030 | 0,0029 | 0,0029 | 0,0029 | MetaTrader 5 |
6 (siehe Abschnitt 4.7) | 0,000140 | 0,000121 | 0,000117 | 0,0035 | MetaTrader 5 |
Tabelle 2. Ergebnisse
Die Bedeutung der Testfälle wird im weiteren Verlauf erläutert (Abschnitte 4.1-4.7). Lassen Sie uns die Berechnungsleistung des gleitenden Mittelwerts als Gesamtbild betrachten.
Zur Veranschaulichung sind die Ergebnisse in Diagrammform dargestellt (siehe Abbildungen 1-5). Der Aufruftyp des gleitenden Mittelwerts bildet die X-Achse (siehe Tabelle 2), die Werte der Y-Achse werden auf einer logarithmischen Skala multipliziert mit -1 dargestellt, sodass größere Werte für höhere Performance stehen. Jedes der Berechnungsmodelle (SMA, EMA, SMMA, LWMA) entspricht einer Spalte im Diagramm.
Abbildung 1. Ergebnisse des Performance-Tests für unterschiedliche Algorithmen des gleitenden Mittelwerts
Es ist ein deutlicher Unterschied der Berechnungsgeschwindigkeiten der verschiedenen Varianten der Berechnung der gleitenden Mittelwerte zu erkennen. Was bedeutet das? Die verschiedenen von den MQL5-Entwicklern bereitgestellten Algorithmen zur Berechnung von gleitenden Mittelwerten haben unterschiedliche Berechnungsleistungen: Es gibt einen schnellen Algorithmus (Fall 6) und langsamere Methoden (Fall 3 und 4). Beim Schreiben von Programmen in MQL5, die gleitende Mittelwerte nutzen, müssen also die korrekten Algorithmen gewählt werden.
Die Berechnungsdauer der jeweiligen Modelle des gleitenden Mittelwerts (0-6) wird in den nachfolgenden Abbildungen im Detail vorgeführt, siehe Tabelle 2.
Abbildung 2. MA-Berechnungsleistung im Modus MODE_SMA
Abbildung 3. MA-Berechnungsleistung im Modus MODE_EMA
Abbildung 4. MA-Berechnungsleistung im Modus MODE_SMMA
Abbildung 5. MA-Berechnungsleistung im Modus MODE_LWMA
Es ist interessant, die Berechnungsleistung der zwei Plattformen zu vergleichen: MetaTrader 4 und MetaTrader 5. Die Ergebnisse werden in Tabelle 2 vorgeführt, Fall Nr. 0 (MQL4) und Fall Nr. 2 (MQL5).
Als Hilfestellung kombinieren wir die Berechnungsergebnisse des Standardindikators iMA in einem separaten Diagramm und einer eigenen Tabelle (siehe Abb. 6). Die Berechnungsdauer des Tests bildet die Y-Achse.
Abbildung 6. Vergleichsdiagramm der Berechnungsleistung von MetaTrader 4 und MetaTrader 5
Schlussfolgerungen:
- Die neue Plattform MetaTrader 5 ist 40 % schneller als ihr Vorgänger MetaTrader 4.
- Die beste Performance wurde für die Modelle SMA, EMA und SMMA mit Fall Nr. 6 erzielt, für LWMA mit Fall Nr. 2 und Nr. 5.
- Bei Testfällen, bei denen der Standardindikator iMA verwendet wird, ist die Berechnungsleistung der verschiedenen Modelle praktisch gleich. Dies gilt nicht für Funktionen der Bibliothek MovingAverages.mqh. Die Performance unterscheidet sich für die unterschiedlichen Modelle um fast eine ganze Größenordnung (0,00023~0,0045).
- Die vorgestellten Ergebnisse resultieren aus einem "Kaltstart", da sich im globalen Zwischenspeicher des Client Terminals keine vorberechneten Daten befinden.
4. Fallstudien
Die MQL5-Entwickler empfehlen die folgende Vorgehensweise zum Abrufen der Werte technischer Standardindikatoren:
//---- indicator buffers double MA[]; // array for iMA indicator values //---- handles for indicators int MA_handle; // handle of the iMA indicator //+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit() { //--- creating handle of the iMA indicator MA_handle=iMA(NULL,0,21,0,MODE_EMA,PRICE_CLOSE); //--- print message if there was an error if(MA_handle<0) { Print("The iMA object is not created: MA_handle= ",INVALID_HANDLE); Print("Runtime error = ",GetLastError()); //--- forced termination of program return(-1); } return(0); } //+------------------------------------------------------------------+ //| Expert tick function | //+------------------------------------------------------------------+ void OnTick() { //--- filling the MA[] array with current values of the iMA indicator //--- we will copy 100 elements, or return if there was an error if(CopyBuffer(MA_handle,0,0,100,MA)<=0) return; //--- set ordering of MA[] as timeseries ArraySetAsSeries(MA,true); //--- here you can do anything with these data }
Diese Vorgehensweise wird im Beitrag "MQL5 für Neueinsteiger: Leitfaden zur Verwendung technischer Indikatoren in Expert Advisors" detailliert beschrieben.
Zum Testen der Berechnungsleistung von gleitenden Mittelwerten empfiehlt sich die Verwendung des Scripts, da es alle Berechnungen durchführen kann, ohne auf Ereignisse zu warten (zum Beispiel Eingang eines neuen Ticks usw.).
Es muss kein separates universelles Programm für alle Testfälle erstellt werden, deshalb erstellen wir ein eigenes Script für jeden Testfall der MA-Berechnung.
Sehen wir uns die Fälle der Berechnungen von gleitenden Mittelwerten im Detail an.
4,1. Fall Nr. 0
In diesem Fall haben wir die Berechnungsleistung des technischen Indikators iMA aus MQL4 gemessen. Die Berechnung wird in MetaTrader 4 durchgeführt und auf alle Daten angewendet.
Modell | Ergebnis | Bestes Ergebnis |
---|---|---|
MODE_SMA | 0,0041 | 0,000140 (Fall 6) |
MODE_EMA | 0,0040 | 0,000121 (Fall 6) |
MODE_SMMA | 0,0043 | 0,000117 (Fall 6) |
MODE_LWMA | 0,0041 | 0,0029 (Fälle 2, 5) |
Der Code dieses Falles ist der folgende (MQL4):
int M[4]= { PERIOD_M1, PERIOD_M5, PERIOD_M15, PERIOD_M30 }; int P[7]= { PRICE_CLOSE, PRICE_OPEN, PRICE_HIGH, PRICE_LOW, PRICE_MEDIAN, PRICE_TYPICAL, PRICE_WEIGHTED }; int periodMA; double buf[]; double time; int count=10000; int startGTC,endGTC; int m,p; //+------------------------------------------------------------------+ //| script program start function | //+------------------------------------------------------------------+ int start() { if(ArrayResize(buf,count)<0) return(-1); Print("START "); startGTC=GetTickCount(); //---- for(m=0;m<=3;m++) { for(p=0;p<=6;p++) { for(periodMA=1;periodMA<=100;periodMA++) { Test0(); } } } //---- endGTC=GetTickCount(); time=endGTC-startGTC; Print("Total time [msec] ",time); time=time/1000/m/p/periodMA; Print("Performance [sec] ",DoubleToStr(time, 10)); return(0); } //+------------------------------------------------------------------+ void Test0() { //--- Model: MODE_SMA; MODE_EMA; MODE_SMMA; MODE_LWMA for(int i=0;i<count;i++) { buf[i]=iMA(NULL,M[m],periodMA,0,MODE_SMA,P[p],i); } }
Hinweis: Dieser Code funktioniert nicht in MetaTrader 5, da er in MQL4 geschrieben ist. Er muss im Client Terminal von MetaTrader 4 ausgeführt werden.
4,2. Fall Nr. 1
In diesem Fall haben wir 4 Modelle berechnet: Nr. 1 (SMA), Nr. 2 (EMA), Nr. 3 (SMMA) und Nr. 4 (LWMA), mithilfe der Funktionen der Bibliothek MovingAverages.mqh.
Die Berechnung wird auf alle Daten-Arrays angewendet.
Modell | Ergebnis | Bestes Ergebnis |
---|---|---|
MODE_SMA | 0,0028 | 0,000140 (Fall 6) |
MODE_EMA | 0,00023 | 0,000121 (Fall 6) |
MODE_SMMA | 0,00027 | 0,000117 (Fall 6) |
MODE_LWMA | 0,0045 | 0,0029 (Fälle 2 und 5) |
#include <MovingAverages.mqh> ENUM_TIMEFRAMES M[4]= { PERIOD_M1, PERIOD_M5, PERIOD_M15, PERIOD_M30 }; ENUM_APPLIED_PRICE P[7]= { PRICE_CLOSE, PRICE_OPEN, PRICE_HIGH, PRICE_LOW, PRICE_MEDIAN, PRICE_TYPICAL, PRICE_WEIGHTED }; int periodMA; double buf[],close[]; double time; int count=10000; int startGTC,endGTC; int m,p; //+------------------------------------------------------------------+ //| script program start function | //+------------------------------------------------------------------+ int OnStart() { if(ArrayResize(buf,count)<0) return(-1); ArraySetAsSeries(buf,false); ArraySetAsSeries(close,false); startGTC=GetTickCount(); //--- for(m=0;m<=3;m++) { for(p=0;p<=6;p++) { CopyClose(_Symbol,M[m],0,count,close); for(periodMA=1;periodMA<=100;periodMA++) { Test1(); // the test is here } } } //--- endGTC=GetTickCount(); time=endGTC-startGTC; time=time/1000/m/p/periodMA; //--- return(0); } //+------------------------------------------------------------------+ void Test1() { for(int i=0;i<count;i++) { buf[i]=SimpleMA(i,periodMA,close); } } //+------------------------------------------------------------------+ void Test2() { buf[0]=close[0]; for(int i=1;i<count;i++) { buf[i]=ExponentialMA(i,periodMA,buf[i-1],close); } } //+------------------------------------------------------------------+ void Test3() { buf[0]=close[0]; for(int i=1;i<count;i++) { buf[i]=SmoothedMA(i,periodMA,buf[i-1],close); } } //+------------------------------------------------------------------+ void Test4() { for(int i=0;i<count;i++) { buf[i]=LinearWeightedMA(i,periodMA,close); } }
Hinweis. Wir wollten verschiedene Typen von Daten im Array nutzen, doch zur Vereinfachung haben wir nur ein Array mit Schließungspreisdaten verwendet (dies wirkt sich nicht auf die Performance der Berechnungen aus).
4,3. Fall Nr. 2
In diesem Fall haben wir den technischen Standardindikator iMA genutzt und Test Nr. 5 durchgeführt.
Die Berechnung wird auf alle Daten-Arrays angewendet.
Modell | Ergebnis | Bestes Ergebnis |
---|---|---|
MODE_SMA | 0,0029 | 0,000140 (Fall 6) |
MODE_EMA | 0,0029 | 0,000121 (Fall 6) |
MODE_SMMA | 0,0029 | 0,000117 (Fall 6) |
MODE_LWMA | 0,0029 | 0,0029 (Fälle 2 und 5) |
ENUM_TIMEFRAMES M[4]= { PERIOD_M1, PERIOD_M5, PERIOD_M15, PERIOD_M30 }; ENUM_APPLIED_PRICE P[7]= { PRICE_CLOSE, PRICE_OPEN, PRICE_HIGH, PRICE_LOW, PRICE_MEDIAN, PRICE_TYPICAL, PRICE_WEIGHTED }; int periodMA; double time; int count=10000; int startGTC,endGTC; int m,p; double MA[]; // array for the iMA indicator int MA_handle; // handle of the iMA indicator //+------------------------------------------------------------------+ //| script program start function | //+------------------------------------------------------------------+ int OnStart() { startGTC=GetTickCount(); //--- for(m=0;m<=3;m++) { for(p=0;p<=6;p++) { for(periodMA=1;periodMA<=100;periodMA++) { Test5(); } } } //--- endGTC=GetTickCount(); time=endGTC-startGTC; time=time/1000/m/p/periodMA; //--- return(0); } //+------------------------------------------------------------------+ void Test5() { //--- Model: MODE_SMA; MODE_EMA; MODE_SMMA; MODE_LWMA MA_handle=iMA(NULL,M[m],periodMA,0,MODE_SMA,P[p]); while(BarsCalculated(MA_handle)<count){} CopyBuffer(MA_handle,0,0,count,MA); }
4,4. Fall Nr. 3
In Fall Nr. 3 werden die mit Indikatoren arbeitenden Klassen unter den Klassen der Standardbibliothek verwendet.
Daten werden nach Element kopiert. Die Berechnung wird auf alle Daten-Arrays angewendet.
Modell | Ergebnis | Bestes Ergebnis |
---|---|---|
MODE_SMA | 0,0998 | 0,000140 (Fall 6) |
MODE_EMA | 0,0997 | 0,000121 (Fall 6) |
MODE_SMMA | 0,0998 | 0,000117 (Fall 6) |
MODE_LWMA | 0,0998 | 0,0029 (Fälle 2 und 5) |
#include <Indicators\Trend.mqh> ENUM_TIMEFRAMES M[4]= { PERIOD_M1, PERIOD_M5, PERIOD_M15, PERIOD_M30 }; ENUM_APPLIED_PRICE P[7]= { PRICE_CLOSE, PRICE_OPEN, PRICE_HIGH, PRICE_LOW, PRICE_MEDIAN, PRICE_TYPICAL, PRICE_WEIGHTED }; int periodMA; double buf[]; double time; int count=10000; int startGTC,endGTC; int m,p; CiMA objMA; //+------------------------------------------------------------------+ //| script program start function | //+------------------------------------------------------------------+ int OnStart() { if(ArrayResize(buf,count)<0) return(-1); ArraySetAsSeries(buf,false); startGTC=GetTickCount(); //--- for(m=0;m<=3;m++) { for(p=0;p<=6;p++) { for(periodMA=1;periodMA<=100;periodMA++) { Test6(); } } } //--- endGTC=GetTickCount(); time=endGTC-startGTC; time=time/1000/m/p/periodMA; //--- return(0); } //+------------------------------------------------------------------+ void Test6() { //--- Model: MODE_SMA; MODE_EMA; MODE_SMMA; MODE_LWMA objMA.Create(NULL,M[m],periodMA,0,MODE_SMA,P[p]); objMA.BuffSize(count); objMA.Refresh(1); for(int i=0;i<count;i++) { buf[i]=objMA.Main(i); } }
4,5. Fall Nr. 4
In Fall Nr. 4 werden die mit Indikatoren arbeitenden Klassen unter den Klassen der Standardbibliothek verwendet.
Das Array des Indikatorpuffers wird vollständig kopiert. Die Berechnung wird auf alle Daten-Arrays angewendet.
Modell | Ergebnis | Bestes Ergebnis |
---|---|---|
MODE_SMA | 0,0996 | 0,000140 (Fall 6) |
MODE_EMA | 0,0996 | 0,000121 (Fall 6) |
MODE_SMMA | 0,0996 | 0,000117 (Fall 6) |
MODE_LWMA | 0,0996 | 0,0029 (Fälle 2, 5) |
#include <Indicators\Trend.mqh> ENUM_TIMEFRAMES M[4]= { PERIOD_M1, PERIOD_M5, PERIOD_M15, PERIOD_M30 }; ENUM_APPLIED_PRICE P[7]= { PRICE_CLOSE, PRICE_OPEN, PRICE_HIGH, PRICE_LOW, PRICE_MEDIAN, PRICE_TYPICAL, PRICE_WEIGHTED }; int periodMA; double buf[]; double time; int count=10000; int startGTC,endGTC; int m,p; CiMA objMA; //+------------------------------------------------------------------+ //| script program start function | //+------------------------------------------------------------------+ int OnStart() { if(ArrayResize(buf,count)<0) return(-1); ArraySetAsSeries(buf,false); startGTC=GetTickCount(); //--- for(m=0;m<=3;m++) { for(p=0;p<=6;p++) { for(periodMA=1;periodMA<=100;periodMA++) { Test7(); } } } //--- endGTC=GetTickCount(); time=endGTC-startGTC; time=time/1000/m/p/periodMA; //--- return(0); } //+------------------------------------------------------------------+ void Test7() { //--- Models: MODE_SMA; MODE_EMA; MODE_SMMA; MODE_LWMA objMA.Create(NULL,M[m],periodMA,0,MODE_SMA,P[p]); objMA.BuffSize(count); objMA.Refresh(1); objMA.GetData(0,count,0,buf); }
4,6. Fall Nr. 5
Es wird Test Nr. 8 angewendet: Das Handle des Indikators wird mithilfe der Funktion IndicatorCreate erstellt.
Die Berechnung wird auf alle Daten-Arrays angewendet.Modell | Ergebnis | Bestes Ergebnis |
---|---|---|
MODE_SMA | 0,0030 | 0,000140 (Fall 6) |
MODE_EMA | 0,0029 | 0,000121 (Fall 6) |
MODE_SMMA | 0,0029 | 0,000117 (Fall 6) |
MODE_LWMA | 0,0029 | 0,0029 (Fälle 2 und 5) |
ENUM_TIMEFRAMES M[4]= { PERIOD_M1, PERIOD_M5, PERIOD_M15, PERIOD_M30 }; ENUM_APPLIED_PRICE P[7]= { PRICE_CLOSE, PRICE_OPEN, PRICE_HIGH, PRICE_LOW, PRICE_MEDIAN, PRICE_TYPICAL, PRICE_WEIGHTED }; int periodMA; double time; int count=10000; int startGTC,endGTC; int m,p; double MA[]; // array for the iMA indicator int MA_handle; // handle of the iMA indicator MqlParam params[]; //+------------------------------------------------------------------+ //| script program start function | //+------------------------------------------------------------------+ int OnStart() { ArrayResize(params,4); startGTC=GetTickCount(); //--- for(m=0;m<=3;m++) { for(p=0;p<=6;p++) { for(periodMA=1;periodMA<=100;periodMA++) { Test8(); } } } //--- endGTC=GetTickCount(); time=endGTC-startGTC; time=time/1000/m/p/periodMA; //--- return(0); } //+------------------------------------------------------------------+ void Test8() { //--- Model: MODE_SMA; MODE_EMA; MODE_SMMA; MODE_LWMA //--- set ma_period params[0].type =TYPE_INT; params[0].integer_value=periodMA; //--- set ma_shift params[1].type =TYPE_INT; params[1].integer_value=0; //--- set ma_method params[2].type =TYPE_INT; params[2].integer_value=MODE_SMA; //--- set applied_price params[3].type =TYPE_INT; params[3].integer_value=P[p]; //--- create MA MA_handle=IndicatorCreate(NULL,M[m],IND_MA,4,params); while(BarsCalculated(MA_handle)<count){} CopyBuffer(MA_handle,0,0,count,MA); }
4,7. Fall Nr. 6
In diesem Fall haben wir 4 Modelle berechnet: Nr. 9 (SMA), Nr. 10 (EMA), Nr. 11 (SMMA) und Nr. 12 (LWMA), mithilfe der Funktionen der Bibliothek MovingAverages.mqh (der Puffer funktioniert wie iMAOnArray in MQL4).
Die Berechnung wird auf alle Daten-Arrays angewendet.
Modell | Ergebnis | Bestes Ergebnis |
---|---|---|
MODE_SMA | 0,000140 | 0,000140 (Fall 6) |
MODE_EMA | 0,000121 | 0,000121 (Fall 6) |
MODE_SMMA | 0,000117 | 0,000117 (Fall 6) |
MODE_LWMA | 0,00350 | 0,0029 (Fälle 2 und 5) |
#include <MovingAverages.mqh> ENUM_TIMEFRAMES M[4]= { PERIOD_M1, PERIOD_M5, PERIOD_M15, PERIOD_M30 }; ENUM_APPLIED_PRICE P[7]= { PRICE_CLOSE, PRICE_OPEN, PRICE_HIGH, PRICE_LOW, PRICE_MEDIAN, PRICE_TYPICAL, PRICE_WEIGHTED }; int periodMA; double buf[],arr[]; double close[]; double time; int count=10000,total; int startGTC,endGTC; int m,p; //+------------------------------------------------------------------+ //| script program start function | //+------------------------------------------------------------------+ int OnStart() { CopyClose(_Symbol,_Period,0,count,close); total=ArrayCopy(arr,close); if(ArrayResize(buf,total)<0) return(-1); //--- ArraySetAsSeries(close,false); ArraySetAsSeries(arr,false); ArraySetAsSeries(buf,false); startGTC=GetTickCount(); //--- for(m=0;m<=3;m++) { for(p=0;p<=6;p++) { CopyClose(_Symbol,M[m],0,count,close); total=ArrayCopy(arr,close); for(periodMA=1;periodMA<=100;periodMA++) { Test9(); // the test is here } } } //--- endGTC=GetTickCount(); time=endGTC-startGTC; time=time/1000/m/p/periodMA; //--- return(0); } //+------------------------------------------------------------------+ void Test9() { SimpleMAOnBuffer(total,0,0,periodMA,arr,buf); } //+------------------------------------------------------------------+ void Test10() { ExponentialMAOnBuffer(total,0,0,periodMA,arr,buf); } //+------------------------------------------------------------------+ void Test11() { SmoothedMAOnBuffer(total,0,0,periodMA,arr,buf); } //+------------------------------------------------------------------+ void Test12() { LinearWeightedMAOnBuffer(total,0,0,periodMA,arr,buf); }
Hinweis. Wir wollten verschiedene Typen von Daten im Array nutzen, doch zur Vereinfachung haben wir nur ein Array mit Schließungspreisdaten verwendet (dies wirkt sich nicht auf die Performance der Berechnungen aus).
5. Ausgabe der Ergebnisse
Für die Ausgabe der Ergebnisse und die Überprüfung der gleitenden Mittelwerte habe ich die Funktion PrintTest genutzt:
void PrintTest(const int position, const double &price[]) { Print("Total time [msec] ",(endGTC-startGTC)); Print("Performance [sec] ",time); Print(position," - array element = ",price[position]); }
Sie kann auf die folgende Weise aufgerufen werden (die Balkenposition und das Daten-Array sind Parameter der Funktion):
//--- ArraySetAsSeries(buf,false); ArraySetAsSeries(close,false); startGTC=GetTickCount(); //--- for(m=0;m<=3;m++) { for(p=0;p<=6;p++) { for(periodMA=1;periodMA<=100;periodMA++) { Test(); } } } //--- endGTC=GetTickCount(); time=endGTC-startGTC; time=time/1000/m/p/periodMA; //--- Output of results ArraySetAsSeries(buf,true); ArraySetAsSeries(close,true); PrintTest(0,buf); PrintTest(0,close); //---
Beachten Sie, dass sich die Indexierung der Arrays vor und nach den Berechnungen unterscheidet.
WICHTIG. Das Flag AsSeries ist während der Berechnungen false und ändert sich beim Ausgeben der Ergebnisse in true.
6. Zusätzliche Untersuchungen
Um die Frage zur Auswirkung der Ausgangsparameter auf die Performance der Berechnung zu beantworten, wurden einige zusätzliche Messungen durchgeführt.
Da Fall Nr. 6 die beste Performance hatte, werden wir diesen Fall nutzen.
Testparameter:
Modus | Timeframe | Mittelungszeitraum |
---|---|---|
1 | М1 | 144 |
2 | М5 | 144 |
3 | М15 | 144 |
4 | М30 | 144 |
5 | М1 | 21 |
6 | М1 | 34 |
7 | М1 | 55 |
8 | М1 | 89 |
9 | М1 | 233 |
10 | М1 | 377 |
11 | М1 | 610 |
12 | М1 | 987 |
Tabelle 3. Zusätzliche Untersuchungen
Quellcode der Tests:
//+------------------------------------------------------------------+ //| Test_SMA Model: MODE_SMA | //+------------------------------------------------------------------+ void Test_SMA(int periodMA,ENUM_TIMEFRAMES periodTF) { CopyClose(_Symbol,periodTF,0,count,close); int total=ArrayCopy(arr,close); SimpleMAOnBuffer(total,0,0,periodMA,arr,buf); } //+------------------------------------------------------------------+ //| Test_EMA Model: MODE_EMA | //+------------------------------------------------------------------+ void Test_EMA(int periodMA,ENUM_TIMEFRAMES periodTF) { CopyClose(_Symbol,periodTF,0,count,close); int total=ArrayCopy(arr,close); ExponentialMAOnBuffer(total,0,0,periodMA,arr,buf); } //+------------------------------------------------------------------+ //| Test_SMMA Model: MODE_SMMA | //+------------------------------------------------------------------+ void Test_SMMA(int periodMA,ENUM_TIMEFRAMES periodTF) { CopyClose(_Symbol,periodTF,0,count,close); int total=ArrayCopy(arr,close); SmoothedMAOnBuffer(total,0,0,periodMA,arr,buf); } //+------------------------------------------------------------------+ //| Test_LWMA Model: MODE_LWMA | //+------------------------------------------------------------------+ void Test_LWMA(int periodMA,ENUM_TIMEFRAMES periodTF) { CopyClose(_Symbol,periodTF,0,count,close); int total=ArrayCopy(arr,close); LinearWeightedMAOnBuffer(total,0,0,periodMA,arr,buf); }
Für die zusätzlichen Tests nutzen wir das Autotest-Programm. Seine grafische Benutzeroberfläche wird in Abb. 7 illustriert.
Abbildung 7. Autotest-Programm für automatisiertes Testen
Ergebnisse: (die X-Achse ist eine logarithmische Zeitskala)
Abbildung 8. Der Timeframe-Parameter (Y) und die Berechnungsleistung von gleitenden Mittelwerten (X)
Abbildung 9. Der Zeitraum-Parameter (Y) und die Berechnungsleistung von gleitenden Mittelwerten (X)
Schlussfolgerungen aus den Ergebnissen der zusätzlichen Untersuchungen:
- Der Timeframe-Parameter ist nicht wichtig, er wirkt sich nicht auf die Berechnungsleistung aus (siehe Abb. 8).
- Der Zeitraum ist kein wichtiger Parameter für die Leistung der Berechnung von gleitenden Mittelwerten für die Modelle SMA, EMA und SMMA. Allerdings sorgt er für eine wesentliche Verlangsamung (von 0,00373 auf 0,145 Sekunden) der Berechnungen für das LWMA-Modell (siehe Abb. 9).
Fazit
Die Auswahl des falschen Algorithmus für den gleitenden Mittelwert kann die Berechnungsleistung Ihres Programms senken.
Übersetzt aus dem Russischen von MetaQuotes Ltd.
Originalartikel: https://www.mql5.com/ru/articles/106
- 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.