English Русский 中文 Español 日本語 Português 한국어 Français Italiano Türkçe
Test-Performance der Berechnung von gleitenden Mittelwerten in MQL5

Test-Performance der Berechnung von gleitenden Mittelwerten in MQL5

MetaTrader 5Beispiele | 9 März 2016, 11:27
685 0
Sergey Pavlov
Sergey Pavlov

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.

ModusMODE_SMAMODE_EMAMODE_SMMAMODE_LWMAPlattform
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

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 2. MA-Berechnungsleistung im Modus MODE_SMA

Abbildung 3. MA-Berechnungsleistung im Modus MODE_EMA

Abbildung 3. MA-Berechnungsleistung im Modus MODE_EMA

Abbildung 4. MA-Berechnungsleistung im Modus MODE_SMMA

Abbildung 4. MA-Berechnungsleistung im Modus MODE_SMMA

Abbildung 5. MA-Berechnungsleistung im Modus MODE_LWMA

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

Abbildung 6. Vergleichsdiagramm der Berechnungsleistung von MetaTrader 4 und MetaTrader 5

Schlussfolgerungen:

  1. Die neue Plattform MetaTrader 5 ist 40 % schneller als ihr Vorgänger MetaTrader 4.
  2. 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.
  3. 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).
  4. 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.

ModellErgebnisBestes 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 ErgebnisBestes Ergebnis
MODE_SMA0,00280,000140 (Fall 6)
MODE_EMA0,000230,000121 (Fall 6)
MODE_SMMA0,000270,000117 (Fall 6)
MODE_LWMA0,00450,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.

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

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

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

ModellErgebnisBestes 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:

ModusTimeframe Mittelungszeitraum
1М1144
2М5144
3М15144
4М30144
5М121
6М134
7М155
8М189
9М1233
10М1377
11М1610
12М1987

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

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

Abbildung 9. Der Zeitraum-Parameter (Y) und die Berechnungsleistung von gleitenden Mittelwerten (X)

Schlussfolgerungen aus den Ergebnissen der zusätzlichen Untersuchungen:

  1. Der Timeframe-Parameter ist nicht wichtig, er wirkt sich nicht auf die Berechnungsleistung aus (siehe Abb. 8).
  2. 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

Beigefügte Dateien |
autotest__1.zip (10.86 KB)
Der Einsatz von MQL5 Standard Library Handelsklassen beim Schreiben eines Expert Advisors Der Einsatz von MQL5 Standard Library Handelsklassen beim Schreiben eines Expert Advisors
Dieser Beitrag beschreibt die Verwendung der Hauptfunktionalitäten der MQL5 Standard Library Handelsklassen beim Schreiben des Expert Advisors, die das Schließen und die Änderung von Positions, Platzierung und Löschung von pending Orders sowie die Prüfung nach Margen vor dem Platzieren eines Handels implementieren. Es wird auch gezeigt, wie man mit Hilfe von Handelsklassen Details zu Orders und Abschlüssen bekommen kann.
Erstellen eines Expert Advisors, der mit verschiedenen Instrumenten handelt Erstellen eines Expert Advisors, der mit verschiedenen Instrumenten handelt
Das Konzept der Diversifizierung von Vermögenswerten auf Finanzmärkten ist ziemlich alt und war für Neueinsteiger im Handel immer interessant. In diesem Beitrag stellt der Verfasser eine äußerst einfache Vorgehensweise für die Erstellung eines Expert Advisors vor, der mit mehreren Währungen handelt, um diese Strömung von Handelsstrategien vorzustellen.
Verwendung der TesterWithdrawal() Funktion zur Nachahmung der Gewinnentnahme Verwendung der TesterWithdrawal() Funktion zur Nachahmung der Gewinnentnahme
Dieser Beitrag beschreibt die Verwendung der TesterWithDrawal() Funktion zur Abschätzung von Risiken in Handelssystemen, die mit der Entnahme eines gewissen Teils des Vermögens während der Operationen zu tun haben. Zusätzlich wird die Auswirkung dieser Funktion auf den Algorithmus zur Berechnung der Inanspruchnahme von Eigenkapital im Strategie-Tester beschrieben. Diese Funktion ist bei der Optimierung von Parametern Ihres Expert Advisors sehr sinnvoll.
Analyse von Kerzenmustern Analyse von Kerzenmustern
Die Konstruktion japanischer Kerzendiagramme und die Analyse von Kerzenmustern sind ein erstaunlicher Bereich der technischen Analyse. Der Vorteil von Kerzen ist, dass sie Daten auf eine Art darstellen, dank der Sie die Dynamiken innerhalb der Daten verfolgen können. In diesem Beitrag analysieren wir Arten von Kerzen, klassifizieren Kerzenmuster und stellen einen Indikator vor, der Kerzenmuster bestimmen kann.