English Русский 中文 Español 日本語 Português
preview
Gleitender Durchschnitt in MQL5 von Anfang an: Schlicht und einfach

Gleitender Durchschnitt in MQL5 von Anfang an: Schlicht und einfach

MetaTrader 5Beispiele |
20 4
Artyom Trishkin
Artyom Trishkin

Inhalt



Einführung

Der gleitende Durchschnitt ist der Durchschnittspreis eines Währungspaares über einen bestimmten Zeitraum, ausgedrückt in einer Anzahl von Balken. Er ist einer der ältesten technischen Indikatoren und vielleicht der populärste und am häufigsten verwendete, da eine große Anzahl anderer Indikatoren auf seiner Grundlage aufgebaut ist.

Gleitende Durchschnitte werden häufig zur Lösung verschiedener angewandter Probleme verwendet. Mathematisch gesehen ist ein einfacher gleitender Durchschnitt das übliche arithmetische Mittel einer bestimmten Reihe von Zahlenwerten. Bei der Erstellung verschiedener Indikatoren oder Skripte und EAs für das MetaTrader 5-Terminal werden gleitende Durchschnitte auf die eine oder andere Weise in die Berechnungen einbezogen. Gleitende Durchschnitte sind ein einfaches und sehr effizientes Werkzeug, um die Richtung von Trends zu bestimmen, kleinere Kursschwankungen (Rauschen) zu glätten oder Handelsstrategien zu erstellen, die auf gleitenden Durchschnitten und anderen Indikatoren basieren, die sowohl im Client-Terminal als auch auf mql5.com ausführlich vorgestellt werden.

Interessanterweise werden gleitende Durchschnitte nicht nur auf den Finanzmärkten, sondern auch in anderen Bereichen wie der Meteorologie und den Wirtschaftswissenschaften häufig verwendet. Einige Modelle des maschinellen Lernens verwenden auch das Konzept der gleitenden Durchschnitte als eines der Merkmale für die Zeitreihenprognose.
Gleitende Durchschnitte sind ein leistungsfähiges Instrument, das bei richtiger Anwendung die Handelsergebnisse und die Datenanalyse erheblich verbessern kann.


Einfacher gleitender Durchschnitt (Simple Moving Average, SMA)

Ein einfacher gleitender Durchschnitt wird als arithmetisches Mittel einer bestimmten Anzahl von Zahlenwerten berechnet, z. B. der Schlusskurse von Balken. Bei jedem Berechnungspunkt haben die Werte des gleitenden Durchschnitts das gleiche Gewicht im Verhältnis zu den benachbarten Punkten der berechneten Zahlenreihe. In Bezug auf den SMA-Indikator sehen wir auf jedem Balken der Indikatorlinie (N) den Durchschnittswert der links daneben liegenden Balken (N + N-1 + N-2 + N-3 + Nx) / x, wobei x die Mittelungsperiode (die Anzahl der berechneten Balken) ist.

SMA-Merkmale:

  • Eine einfache Berechnungsmethode, leicht zu berechnen und zu verstehen,
  • Aufgrund ihrer Einfachheit kann die SMA den Preisveränderungen hinterherhinken, insbesondere wenn lange Berechnungszeiträume verwendet werden,
  • Wird oft in Kombination mit anderen Indikatoren verwendet, um Signale zu bestätigen.


Abb. 1. Einfacher gleitender Durchschnitt SMA bei Close mit einem Berechnungszeitraum von 10

Berechnung

Die Berechnung des SMA-Indikators auf der Grundlage der Schlusskurse des Balkens kann wie folgt ausgedrückt werden:

SMA = SUM(CLOSE(i), N) / N 

wobei:

  • SUM - Summe;
  • CLOSE(i) - Schlusskurs der laufenden Periode
  • N - Anzahl der Periodenlängen für die Berechnung.

In dem Indikator kann die Berechnung eines einfachen gleitenden Durchschnitts wie folgt aussehen:

double result=0;
for(int j=0; j<period; j++)
   result+=close[i-j];
result/=period;

wobei:

  • result ist eine Variable, in die das Ergebnis der SMA-Berechnung gesetzt wird
  • period - SMA-Berechnungsperiode: die Anzahl der Daten in der berechneten numerischen Reihe. Hier steht er für die Schlusskurse der Balken.
  • i - Indikator Hauptschleife
  • j - Schleifenindex für die Berechnung des Durchschnittswerts der Schlusskurse der Balken links vom Balken mit dem Index i

Das heißt, bei der Berechnung des Durchschnitts für den Glättungszeitraum von 5 wird die Schleife ab dem Balken mit dem Index i durchlaufen und alle Schlusskurse der Balken mit dem Index i-j (i-0 + i-1 + i-2 + i-3 + i-4) summiert.

Nachdem Sie alle Schlusskurse aller Balken in der Menge „period“ addiert haben, teilen Sie das Ergebnis durch den Wert von period. Als Ergebnis erhalten wir das übliche arithmetische Mittel der Schlusskurse für die Periodenbalken.

Der Indikatorcode:

//+------------------------------------------------------------------+
//|                                           SMAonPriceCloseRAW.mq5 |
//|                                  Copyright 2024, MetaQuotes Ltd. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2024, MetaQuotes Ltd."
#property link      "https://www.mql5.com"
#property version   "1.00"
#property indicator_chart_window
#property indicator_buffers 1
#property indicator_plots   1

//--- plot MA
#property indicator_label1  "SMA"
#property indicator_type1   DRAW_LINE
#property indicator_color1  clrRed
#property indicator_style1  STYLE_SOLID
#property indicator_width1  1

//--- input parameters
input int      InpPeriod   =  10;   // SMA Period

//--- indicator buffers
double         MABuffer[];
//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- indicator buffers mapping
   SetIndexBuffer(0,MABuffer,INDICATOR_DATA);
   
//--- successful initialization
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//| Custom indicator iteration function                              |
//+------------------------------------------------------------------+
int OnCalculate(const int rates_total,
                const int prev_calculated,
                const datetime &time[],
                const double &open[],
                const double &high[],
                const double &low[],
                const double &close[],
                const long &tick_volume[],
                const long &volume[],
                const int &spread[])
  {
//--- in the loop from the value of InpPeriod to the current history bar
   for(int i=InpPeriod; i<rates_total; i++)
     {
      //--- calculate average close prices for InpPeriod bars
      double result=0;
      for(int j=0; j<InpPeriod; j++)
         result+=close[i-j];
      result/=InpPeriod;
      
      //--- Write the calculation result to the buffer
      MABuffer[i]=result;
     }
//--- return value of prev_calculated for the next call
   return(rates_total);
  }

Dies ist der einfachste Indikator, der es Ihnen ermöglicht, auf dem Chart die durchschnittlichen Schlusskurswerte für den in den Einstellungen angegebenen Zeitraum für jeden Balken der Geschichte (InpPeriod) zu sehen. Die Werte werden als Linie auf dem Preischart angezeigt.

Um den Durchschnittspreis für eine bestimmte Anzahl von Balken zu berechnen, müssen wir die erforderliche Anzahl von Schlusskursen in einer Schleife ab dem aktuellen Balken (ebenfalls in einer Schleife) summieren und die sich ergebende Summe durch die Anzahl der Balken dividieren, innerhalb derer das arithmetische Mittel gesucht wird.

So berechnen wir in einer Schleife vom Beginn der Geschichte an für jeden Balken der Schleife i den Durchschnittswert der Schlusskurse der Balken links in der Schleife j. Deshalb beginnt die Hauptschleife nicht bei Null, sondern bei dem Wert der Anzahl der Balken, für die wir den Durchschnitt berechnen - damit die erforderliche Anzahl von Balken gleich zu Beginn der SMA-Berechnung links steht.

Oben haben wir uns den einfachsten und meist nicht optimalen Code angesehen, der die Berechnung eines einfachen gleitenden Durchschnitts zeigt. Diese Konstruktion der Indikatorschleife zwingt sie dazu, die gesamte Historie neu zu berechnen, während sie gleichzeitig eine weitere Schleife zur Berechnung der Durchschnittspreise für jeden Balken durchführt. Mit der oben dargestellten Berechnung werden wir früher oder später (aber wahrscheinlich früher) die folgende Meldung im Protokoll erhalten:

indicator is too slow, 66141 ms. rewrite the indicator, please

Natürlich muss sie optimiert werden. Zunächst einmal sollten wir eine ressourcenschonendere Berechnung des Indikators einführen.

Die Indikatordatei SMAonPriceCloseRAW.mq5 finden Sie in den an den Artikel angehängten Dateien.

Optimierung der Berechnungen

Eine ressourcenschonende Berechnung des Indikators bedeutet folgende Abfolge von Berechnungen:

  • Wenn der Indikator zum ersten Mal gestartet wird (oder wenn es irgendwelche Änderungen in der Symbolhistorie gibt), wird eine vollständige Berechnung des Indikators für die gesamte verfügbare Historie durchgeführt. 
  • Nach der vollständigen Berechnung werden alle berechneten Daten in das Chart eingezeichnet, und jetzt müssen wir die Daten bei jedem neuen Tick nur für den aktuellen Balken berechnen und ändern.

Die Berechnung des Indikators beginnt, wenn das Ereignis Calculate empfangen wird. Die Ereignisbehandlung von OnCalculate() wird aufgerufen.

Dies geschieht normalerweise, wenn ein neuer Tick für das Symbol empfangen wird, für das der Indikator berechnet wird.

Es gibt zwei Optionen für die Definition von OnCalculate(). Beide Funktionsmöglichkeiten können nicht innerhalb eines Indikators verwendet werden.

Die erste Form des Aufrufs ist für die Indikatoren gedacht, die mit einem einzigen Datenpuffer berechnet werden können.

int OnCalculate (const int rates_total,      // price[] array size
                 const int prev_calculated,  // handled bars at the previous call
                 const int begin,            // where significant data starts
                 const double& price[]       // array for calculation
   );

Eine der Preiszeitreihen oder ein berechneter Puffer eines Indikators kann als Array price[] übergeben werden.

Eine benötigte Zeitreihe oder ein Indikator wird vom Nutzer als price[]-Array auf der Registerkarte Parameter ausgewählt, wenn der Indikator gestartet wird.

Die zweite Form des Aufrufs ist für alle anderen Indikatoren vorgesehen, die mehr als eine Zeitreihe zur Berechnung verwenden.

int OnCalculate (const int rates_total,      // size of input time series
                 const int prev_calculated,  // handled bars at the previous call
                 const datetime& time[],     // Time
                 const double& open[],       // Open
                 const double& high[],       // High
                 const double& low[],        // Low
                 const double& close[],      // Close
                 const long& tick_volume[],  // Tick Volume
                 const long& volume[],       // Real Volume
                 const int& spread[]         // Spread
   );
  • Die Parameter open[], high[], low[] und close[] enthalten Arrays mit dem Eröffnungs-, Hoch-, Tief- und Schlusskurs des aktuellen Zeitrahmens.
  • Der Parameter time[] enthält ein Array mit Werten für die Öffnungszeit.
  • Der Parameter spread[] ist ein Array, das die Spread-Historie enthält (falls ein Spread für dieses Handelsinstrument angegeben ist).
  • Die Parameter volume[] und tick_volume[] enthalten die Historie des Handels- bzw. Tickvolumens.
  • Der Parameter rates_total enthält die Anzahl der Balken, die für die Berechnung des Indikators zur Verfügung stehen, und entspricht der Anzahl der im Chart verfügbaren Balken.
  • Wenn die Funktion aufgerufen wird, enthält der Parameter prev_calculated den Wert, den die Funktion OnCalculate() beim vorherigen Aufruf zurückgegeben hat. Dadurch ist es möglich, ressourcenschonende Algorithmen zur Berechnung eines nutzerdefinierten Indikators zu implementieren, um wiederholte Berechnungen für die Bars zu vermeiden, die sich seit dem letzten Start dieser Funktion nicht geändert haben. Dazu reicht es in der Regel aus, den Wert des Parameters rates_total zurückzugeben, der die Anzahl der Balken im aktuellen Funktionsaufruf enthält. Wenn die Preisdaten seit dem letzten Aufruf der Funktion OnCalculate() geändert wurden (eine umfangreichere Historie wurde geladen oder Lücken in der Historie gefüllt), wird der Wert des Eingabeparameters prev_calculated vom Terminal selbst auf Null gesetzt.

Wie wir sehen, benötigen wir den Wert der Variablen prev_calculated, um eine ressourcenschonende Berechnung durchzuführen. Beim ersten Start oder bei der ersten Änderung der historischen Daten enthält diese Variable einen Nullwert. Wir können es so einrichten, dass der Indikator beim ersten Start vollständig berechnet wird und dass nur der aktuelle Balken berechnet wird, nachdem der gesamte Indikator berechnet wurde und die Werte für den gesamten Verlauf in den Puffer geschrieben wurden.

Da die Indikatorschleife die historischen Daten ab einer bestimmten Zahl bis zum Wert rates_total-1 durchläuft, können wir, wenn wir wissen, wie viele Balken beim letzten Lauf des Indikators berechnet wurden (der Wert ist im Wert prev_calculated gespeichert), den Wert bestimmen, ab dem die Berechnungsschleife beginnen soll.

Nehmen wir an, es handelt sich um die Startvariable, die den Index des Balkens enthalten soll, bei dem die Indikatorschleife beginnen soll. Beim ersten Durchlauf wird in der Variablen prev_calculated der Wert Null gespeichert. Das bedeutet, dass wir eine Schleife von Null bis zur Anzahl der Balken des Instruments, auf dem der Indikator läuft, starten können.

Allerdings gibt es hier eine Einschränkung: Um den Wert des gleitenden Durchschnitts zu berechnen, müssen wir vom aktuellen Balken der Schleife zum Anfang der Historie zurückgehen und den durchschnittlichen Kurswert berechnen. Aber wenn wir die Hauptschleife bei Null beginnen, dann gibt es links vom Null-Balken (dem Beginn der historischen Daten) nichts, und wir werden nicht in der Lage sein, die Werte der fehlenden Balken der Geschichte zu erhalten. Daher sollte die Hauptschleife mit einem Versatz vom Beginn der Historie um die Anzahl der Balken, die zur Berechnung des Durchschnitts erforderlich sind, gestartet werden. Dies ist der Berechnungszeitraum des Indikators (z. B. Periode).

Die Einstellung der Anzahl der berechneten Balken auf die Startvariable sollte folgendermaßen aussehen:

//--- If this is the first run or a change in historical data
   if(prev_calculated==0)
     {
      //--- initialize the indicator buffer with an empty value and
      //--- set the start of the calculation to the Period value
      ArrayInitialize(MABuffer,EMPTY_VALUE);
      start=Period;
     }
//--- If this is not the first run, the calculation will start at the current bar
   else
      start=prev_calculated-1;

Wenn der Indikator zum ersten Mal gestartet wird, wird zunächst ein Initialisierungswert in seinen Puffer geschrieben. Hier ist es EMPTY_VALUE - leerer Wert des Indikatorpuffers, der am Rendering nicht teilnimmt. Danach wird der Index des Balkens, mit dem die Hauptschleife des Indikators beginnt, auf die Variable „start“ gesetzt. Wenn dies nicht der erste Start des Indikators ist, enthält die Variable prev_calculated die Anzahl der Balken, die bereits beim vorherigen Aufruf des OnCalculate() Handlers berechnet wurden. Daher legen wir in diesem Fall den Index des aktuellen Balkens als den Balken fest, bei dem die Schleife beginnt. In den meisten Fällen ist dies prev_calculated-1.

Da OnCalculate() einen Wert zurückgibt (in der Regel rates_total), können wir die Anzahl der Balken, die beim nächsten Aufruf der Funktion berechnet werden müssen, regulieren. Wenn wir die Anzahl der berechneten Balken zurückgeben, die in rates_total gespeichert ist, wird dieser Wert beim nächsten Aufruf von OnCalculate in die Variable prev_calculated geschrieben. Wenn wir also die Balkennummer für den Beginn der Schleife als prev_calculated-1 angeben, dann ist dies beim aktuellen Balken der Index des aktuellen Balkens und beim Öffnen eines neuen Balkens der Index des vorherigen Balkens. Auf diese Weise geben wir den Balkenindex für den Start der Schleife an:

  • beim ersten Start - vom Wert des Berechnungszeitraums des Indikators
  • bei der Eröffnung eines neuen Balkens - ab dem vorherigen Balken (der vorherige und der aktuelle Balken werden berechnet)
  • auf dem aktuellen Balken, berechnen wir bei jedem neuen Tick einen aktuellen Balken

Schauen wir uns den vollständigen Code des Indikators an:

//+------------------------------------------------------------------+
//|                                           SMAonPriceCloseECO.mq5 |
//|                                  Copyright 2024, MetaQuotes Ltd. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2024, MetaQuotes Ltd."
#property link      "https://www.mql5.com"
#property version   "1.00"
#property indicator_chart_window
#property indicator_buffers 1
#property indicator_plots   1

//--- plot MA
#property indicator_label1  "SMA"
#property indicator_type1   DRAW_LINE
#property indicator_color1  clrRed
#property indicator_style1  STYLE_SOLID
#property indicator_width1  1

//--- input parameters
input int      InpPeriod   =  10;   // SMA Period

//--- indicator buffers
double         MABuffer[];

//--- global variables
int   ExtPeriod;  // SMA calculation period
//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- indicator buffers mapping
   SetIndexBuffer(0,MABuffer,INDICATOR_DATA);
   
//--- successful initialization
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//| Custom indicator iteration function                              |
//+------------------------------------------------------------------+
int OnCalculate(const int rates_total,
                const int prev_calculated,
                const datetime &time[],
                const double &open[],
                const double &high[],
                const double &low[],
                const double &close[],
                const long &tick_volume[],
                const long &volume[],
                const int &spread[])
  {
//--- Adjust the entered value of the SMA calculation period
   ExtPeriod=(InpPeriod<1 ? 10 : InpPeriod);
   
//--- If there is not enough historical data for calculation, return 0
   if(rates_total<ExtPeriod)
      return 0;
      
   int start=0;   // calculation start bar
   
//--- If this is the first run or a change in historical data
   if(prev_calculated==0)
     {
      //--- initialize the indicator buffer with an empty value and
      //--- set the start of the calculation to ExtPeriod
      ArrayInitialize(MABuffer,EMPTY_VALUE);
      start=ExtPeriod;
     }
//--- If this is not the first run, the calculation will start at the current bar
   else
      start=prev_calculated-1;
      
//--- in the loop from the 'start' value to the current history bar
   for(int i=start; i<rates_total; i++)
     {
      //--- calculate average close prices for ExtPeriod bars
      double result=0;
      for(int j=0; j<ExtPeriod; j++)
         result+=close[i-j];
      result/=ExtPeriod;
      
      //--- Write the calculation result to the buffer
      MABuffer[i]=result;
     }
     
//--- return the number of bars calculated for the next OnCalculate call
   return(rates_total);
  }

Die Indikatordatei SMAonPriceCloseECO.mq5 finden Sie in den an den Artikel angehängten Dateien.

Wenn wir den Indikator nun in einem Symbolchart ausführen, erhalten wir nicht mehr die Meldung, dass er zu langsam ist. Es bleibt jedoch noch ein Optimierungsproblem: Bei der Berechnung des Indikators für den aktuellen Balken wird die Schleife zur Berechnung des Durchschnittspreises für die Balken der Periode weiterhin ausgeführt. Da ein einfacher gleitender Durchschnitt einfach die Summe einer bestimmten Anzahl von Balken geteilt durch diese Anzahl ist, können wir diese Schleife für den aktuellen Balken eliminieren.

Schauen wir uns an, wie der SMA-Indikator in seiner aktuellen Version in der Historie berechnet wird. Als Beispiel zeigen wir den Beginn der Berechnung eines einfachen gleitenden Durchschnitts mit einer Berechnungsperiode von 5 Balken.

Beim Start beginnt die Berechnung des Indikators nicht mit dem Null-Balken, sondern mit dem Balken, der gleich (Anzahl der Balken im SMA-Berechnungszeitraum) - 1 ist:

Die Hauptschleife beginnt bei dem Balken mit dem Index 4. Um den SMA-Wert für diesen Balken zu berechnen, wird eine Schleife über die Balken mit den Indizes 4, 3, 2, 1 und 0 ausgeführt. Die Kurswerte dieser Balken werden addiert, und die sich ergebende Summe wird durch die Anzahl der SMA-Berechnungsperioden geteilt - hier sind es 5. Um die Berechnungen zu vereinfachen, zeigt die Abbildung die einfachen ganzzahligen Werte 2, 4, 6, 8 und 10 auf den entsprechenden Balken als Balkenpreiswerte. Für den Balken mit dem Index 4 beträgt der SMA-Wert folglich 6 = (2+4+6+8+10)/5.

Nach dem Verschieben des Index der Haupt-SMA-Berechnungsschleife wird der SMA-Wert für den nächsten Balken mit dem Index 5 erneut in der Schleife berechnet:

Bei der nächsten Iteration der Hauptschleife wird der SMA wieder für den Balken mit dem Index 6 in der inneren Schleife berechnet:

Als Nächstes wiederholen wir das gleiche Muster - die SMA-Werte werden in den internen Schleifen für jeden nachfolgenden Balken bei jeder nachfolgenden Verschiebung des Index der Hauptschleife berechnet.
Und so weiter, bis die Hauptschleife den aktuellen Balken erreicht:


Bei der Berechnung des SMA für den aktuellen Balken wird dann bei jedem nachfolgenden Tick eine interne Schleife ausgeführt, um den SMA für den aktuellen Balken zu berechnen.

Was ist SMA? Dies ist das arithmetische Mittel der Preise für einen bestimmten Zeitraum. Wir wissen bereits, dass wir zur Berechnung des SMA-Werts für einen Balken die Summe der Preise der vorherigen Periode durch die Berechnungsperiode teilen müssen. Beim Start des Indikators können wir den allerersten SMA-Wert berechnen, indem wir die Preise der vorherigen Periode in der Schleife addieren und diese Summe durch die Periode dividieren. Wenn Sie nun das gleitende Fenster für die SMA-Berechnung nach rechts verschieben, müssen Sie nicht mehr die vorherigen Kurse in der Schleife addieren und durch ihre Periodenzahl dividieren, da wir bereits den berechneten SMA-Wert des vorherigen Balkens haben.

Es genügt, von diesem Wert den allerersten Preis der an der Berechnung des Durchschnittswerts Beteiligten abzuziehen und zu diesem Ergebnis den aktuellen Preis zu addieren, wobei sie vorläufig durch den Zeitraum geteilt werden. Alles ist bereit. Mit anderen Worten: Der allererste SMA-Balken wird berechnet, indem die Preise in der Periodenschleife addiert und das Ergebnis der Addition durch den Periodenwert geteilt wird. Dann haben wir nur die Rechenoperationen Addition, Subtraktion und Division:

Berechnung des ersten SMA-Wertes:


Wenn wir nun den Index der Hauptschleife verschieben, führen wir einfach die Berechnung durch:


Wir wiederholen den Vorgang mit den neuen Daten:


Und bei jedem Offset des Index der Hauptschleife


Jedes Mal, wenn wir einen neuen SMA-Wert aus dem zuvor berechneten Wert erhalten


Betrachten wir den folgenden Indikator:

//+------------------------------------------------------------------+
//|                                              SMAonPriceClose.mq5 |
//|                                  Copyright 2024, MetaQuotes Ltd. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2024, MetaQuotes Ltd."
#property link      "https://www.mql5.com"
#property version   "1.00"
#property indicator_chart_window
#property indicator_buffers 1
#property indicator_plots   1

//--- plot MA
#property indicator_label1  "SMA"
#property indicator_type1   DRAW_LINE
#property indicator_color1  clrRed
#property indicator_style1  STYLE_SOLID
#property indicator_width1  1

//--- input parameters
input int      InpPeriod   =  10;   // SMA Period

//--- indicator buffers
double         MABuffer[];

//--- global variables
int   ExtPeriod;  // SMA calculation period
//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- indicator buffers mapping
   SetIndexBuffer(0,MABuffer,INDICATOR_DATA);
   
//--- successful initialization
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//| Custom indicator iteration function                              |
//+------------------------------------------------------------------+
int OnCalculate(const int rates_total,
                const int prev_calculated,
                const datetime &time[],
                const double &open[],
                const double &high[],
                const double &low[],
                const double &close[],
                const long &tick_volume[],
                const long &volume[],
                const int &spread[])
  {
//--- Adjust the entered value of the SMA calculation period
   ExtPeriod=(InpPeriod<1 ? 10 : InpPeriod);
   
//--- If there is not enough historical data for calculation, return 0
   if(rates_total<ExtPeriod)
      return 0;
      
   int start=0;   // calculation start bar
   
//--- If this is the first run or a change in historical data
   if(prev_calculated==0)
     {
      //--- initialize the indicator buffer with an empty value,
      //--- set the start of the calculation to ExtPeriod and
      //--- calculate average close prices for ExtPeriod bars for the first SMA value on the start-1 bar
      ArrayInitialize(MABuffer,EMPTY_VALUE);
      start=ExtPeriod;
      double value=0;
      for(int i=0; i<start; i++)
         value+=close[i];
      MABuffer[start-1]=value/ExtPeriod;
     }
//--- If this is not the first run, the calculation will start at the current bar
   else
      start=prev_calculated-1;
      
//--- in the loop from the 'start' value to the current history bar
   for(int i=start; i<rates_total; i++)
     {
      //--- define prices for calculating SMA
      double ma_prev=MABuffer[i-1];             // Previous calculated SMA value
      double price_first=close[i-ExtPeriod];    // Close price ExtPeriod bars ago
      //--- Set the current SMA value to the buffer calculated as 
      //--- (past SMA value + (current close price - close price ExtPeriod bars ago) / SMA ExtPeriod calculation period)
      MABuffer[i]=ma_prev+(close[i]-price_first)/ExtPeriod;
     }
     
//--- return the number of bars calculated for the next OnCalculate call
   return(rates_total);
  }

Wir können den Indikator in einem Chart starten und ihn mit dem Standard-SMA vergleichen, nachdem wir zuvor die gleichen Berechnungszeiträume für beide festgelegt haben. Dieser Indikator verwendet beim Start eine Schleife mit der Länge der MA-Perioden-Berechnungsperiode und berechnet dann beim ersten Start in der Hauptschleife die SMA-Werte auf der Grundlage des allerersten berechneten SMA-Wertes für die gesamte Historie des Instruments. Für den aktuellen Balken wird eine mathematische Berechnung des SMA-Wertes für jeden aktuellen Tick durchgeführt.

Die Indikator-Datei SMAonPriceClose.mq5 finden Sie unten.


Exponentieller gleitender Durchschnitt (Exponential Moving Average, EMA)

Der exponentiell geglättete gleitende Durchschnitt wird berechnet, indem ein bestimmter Anteil des aktuellen Schlusskurses zum vorherigen Wert des berechneten gleitenden Durchschnitts addiert wird. Bei dieser Methode erhalten die neuesten Werte die höchste Wertigkeit. Bei der Berechnung des EMA nehmen die Kursgewichte der in die Berechnung einbezogenen Barren exponentiell mit einer durch die Glättungskonstante k (1 > k > 0) festgelegten Rate ab. In den meisten Fällen wird k auf der Grundlage der Berechnungsperiode des gleitenden Durchschnitts berechnet: k = 2,0 / (N + 1), wobei N die Anzahl der Berechnungsperioden ist.

EMA:

  • Er reagiert schneller auf Preisänderungen und ist daher für den kurzfristigen Handel besser geeignet.
  • Er erkennt effektiver Trends sowie Kauf- und Verkaufssignalen, da aktuelle Daten vorrangig berücksichtigt werden,
  • E wird häufig bei Kreuzungen mit anderen EMAs (z. B. 50 und 200) verwendet, um Kauf- oder Verkaufspunkte zu bestimmen.

Abb. 2. Exponentieller gleitender Durchschnitt. EMA bei Close mit dem Berechnungszeitraum von 10

Berechnung

P-Prozent EMA sieht folgendermaßen aus:

EMA = (CLOSE(i) * P) + (EMA(i - 1) * (1 - P))

wobei:

  • CLOSE(i) - Schlusskurs der laufenden Periode
  • EMA(i - 1) - Wert des gleitenden Durchschnitts der Vorperiode;
  • P - Prozentsatz der Verwendung des Preiswertes (Glättungskonstante).

Lassen Sie uns einen einfachen Indikator schreiben, der die Berechnung des exponentiellen gleitenden Durchschnitts zeigt:

//+------------------------------------------------------------------+
//|                                           EMAonPriceCloseRAW.mq5 |
//|                                  Copyright 2024, MetaQuotes Ltd. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2024, MetaQuotes Ltd."
#property link      "https://www.mql5.com"
#property version   "1.00"
#property indicator_chart_window
#property indicator_buffers 1
#property indicator_plots   1
//--- plot MA
#property indicator_label1  "EMA"
#property indicator_type1   DRAW_LINE
#property indicator_color1  clrRed
#property indicator_style1  STYLE_SOLID
#property indicator_width1  1

//--- input parameters
input int      InpPeriod   =  10;   // EMA Period

//--- indicator buffers
double         MABuffer[];
//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- indicator buffers mapping
   SetIndexBuffer(0,MABuffer,INDICATOR_DATA);
   
//--- successful initialization
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//| Custom indicator iteration function                              |
//+------------------------------------------------------------------+
int OnCalculate(const int rates_total,
                const int prev_calculated,
                const datetime &time[],
                const double &open[],
                const double &high[],
                const double &low[],
                const double &close[],
                const long &tick_volume[],
                const long &volume[],
                const int &spread[])
  {
//--- in the loop from zero (the beginning of history) to the current bar
   for(int i=0; i<rates_total; i++)
     {
      double result=0;
      double smf=2.0/(InpPeriod+1.0);                       // smoothing factor
      double prev_value=(i>=1 ? MABuffer[i-1] : close[i]);  // previous calculated EMA value
      
      //--- calculate EMA based on the Close price of the bar, the previous EMA value and the smoothing factor
      result=close[i]*smf+prev_value*(1-smf);
      
      //--- Write the calculation result to the buffer
      MABuffer[i]=result;
     }
//--- return value of prev_calculated for the next call
   return(rates_total);
  }

Genau wie beim einfachen gleitenden Durchschnitt wird hier die Berechnung des EMA ohne Überprüfung der Eingaben wie Berechnungszeitraum und Organisation der ressourcenschonenden Indikatorberechnung gezeigt. Beim ersten Start berechnet der Indikator die gesamte verfügbare Historie und berechnet dann bei jedem Tick die gesamte Historie neu. Dies führt unweigerlich zu der Meldung, dass der Indikator zu langsam ist.

Suchen Sie den Indikator EMAonPriceCloseRAW.mq5 in den Anhängen.

Optimierung der Berechnungen

Lassen Sie uns eine ressourcenschonende Indikatorberechnung durchführen. Beim ersten Start wird der gesamte Verlauf berechnet, danach wird bei jedem neuen Tick nur der aktuelle Balken neu berechnet:

//+------------------------------------------------------------------+
//|                                              EMAonPriceClose.mq5 |
//|                                  Copyright 2024, MetaQuotes Ltd. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2024, MetaQuotes Ltd."
#property link      "https://www.mql5.com"
#property version   "1.00"
#property indicator_chart_window
#property indicator_buffers 1
#property indicator_plots   1
//--- plot MA
#property indicator_label1  "EMA"
#property indicator_type1   DRAW_LINE
#property indicator_color1  clrRed
#property indicator_style1  STYLE_SOLID
#property indicator_width1  1

//--- input parameters
input int      InpPeriod   =  10;   // EMA Period

//--- indicator buffers
double         MABuffer[];

//--- global variables
int   ExtPeriod;  // EMA calculation period
//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- indicator buffers mapping
   SetIndexBuffer(0,MABuffer,INDICATOR_DATA);
   
//--- successful initialization
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//| Custom indicator iteration function                              |
//+------------------------------------------------------------------+
int OnCalculate(const int rates_total,
                const int prev_calculated,
                const datetime &time[],
                const double &open[],
                const double &high[],
                const double &low[],
                const double &close[],
                const long &tick_volume[],
                const long &volume[],
                const int &spread[])
  {
//--- Adjust the entered value of the EMA calculation period
   ExtPeriod=(InpPeriod<1 ? 10 : InpPeriod);
   
//--- If there is not enough historical data for calculation, return 0
   if(rates_total<ExtPeriod)
      return 0;
      
   int start=0;   // calculation start bar
   
//--- If this is the first run or a change in historical data
   if(prev_calculated==0)
     {
      //--- initialize the indicator buffer with an empty value and set the start of the calculation to 0
      ArrayInitialize(MABuffer,EMPTY_VALUE);
      start=0;
     }
//--- If this is not the first run, the calculation will start at the current bar
   else
      start=prev_calculated-1;
      
//--- in the loop from the 'start' value to the current history bar
   for(int i=start; i<rates_total; i++)
     {
      double result=0;
      double smf=2.0/(ExtPeriod+1.0);                       // smoothing factor
      double prev_value=(i>=1 ? MABuffer[i-1] : close[i]);  // previous calculated EMA value
      
      //--- calculate the EMA value for the current loop bar
      result=close[i]*smf+prev_value*(1-smf);
      
      //--- Write the calculation result to the buffer
      MABuffer[i]=result;
     }
     
//--- return the number of bars calculated for the next OnCalculate call
   return(rates_total);
  }

Wenn der eingegebene Wert der berechneten Periode kleiner als 1 ist, dann setzen wir den Standardwert für gleitende Durchschnitte auf 10.
Bei der Berechnung des exponentiellen gleitenden Durchschnitts wird der zuvor berechnete EMA-Wert des vorherigen Balkens verwendet.

Bei der ersten Einführung wurde eine solche Berechnung noch nicht durchgeführt, daher verwenden wir den Preiswert Close als Primärdaten.

Dieser Indikator implementiert eine ressourcensparende Datenberechnung: beim ersten Start wird die gesamte verfügbare Historie berechnet, und bei nachfolgenden OnCalculate()-Aufrufen wird nur der aktuelle Balken berechnet.

Die Indikator-Datei EMAonPriceClose.mq5 finden Sie unten.


Geglätteter gleitender Durchschnitt (Smoothed Moving Average, SMMA)

Der geglättete gleitende Durchschnitt reagiert im Gegensatz zum einfachen Durchschnitt weniger empfindlich auf Kursschwankungen und legt mehr Gewicht auf die Gesamtrichtung der Kursbewegung.

SMMA:

  • Weniger anfällig für Schwankungen als SMA und EMA, was ihn für die Analyse stabilerer Trends nützlich macht,
  • Gut geeignet für die langfristige Analyse, da sie starke Kursschwankungen ausgleicht,
  • Kann verwendet werden, um zuverlässigere Handelssignale zu erzeugen.

Abb. 3. Geglätteter gleitender Durchschnitt. SMMA mit Schlusskursen mit dem Berechnungszeitraum von 10

Berechnung

Der erste Wert des geglätteten gleitenden Durchschnitts wird wie ein einfacher gleitender Durchschnitt berechnet:

SUM1  = SUM(CLOSE(i), N)
SMMA1 = SUM1 / N 

Das zweite gleitende Durchschnitt wird wie folgt berechnet:

SMMA(i) = (SMMA1*(N-1) + CLOSE(i)) / N

Alle nachfolgenden Werte des gleitenden Durchschnitts werden nach der folgenden Gleichung berechnet:

PREVSUM = SMMA(i - 1) * N
SMMA(i) =(PREVSUM - SMMA(i - 1) + CLOSE(i)) / N 

wobei:

  • SUM - Summe;
  • SUM1 - Gesamtsumme der Schlusskurse für N Perioden, beginnend mit dem vorherigen Balken;
  • PREVSUM - geglättete Summe des vorherigen Balkens;
  • SMMA(i-1) - geglätteter gleitender Durchschnitt des vorherigen Balkens;
  • SMMA(i) - geglätteter gleitender Durchschnitt des aktuellen Balkens (außer des ersten Balkens);
  • CLOSE(i) - aktueller Schlusskurs;
  • N - Glättungszeitraum.

Die Formel kann durch arithmetische Umformungen vereinfacht werden:

SMMA(i) = (SMMA(i - 1) * (N - 1) + CLOSE(i)) / N 

Lassen Sie uns einen einfachen Indikator schreiben, der einen geglätteten gleitenden Durchschnitt berechnet:

//+------------------------------------------------------------------+
//|                                          SMMAonPriceCloseRAW.mq5 |
//|                                  Copyright 2024, MetaQuotes Ltd. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2024, MetaQuotes Ltd."
#property link      "https://www.mql5.com"
#property version   "1.00"
#property indicator_chart_window
#property indicator_buffers 1
#property indicator_plots   1

//--- plot MA
#property indicator_label1  "SMMA"
#property indicator_type1   DRAW_LINE
#property indicator_color1  clrRed
#property indicator_style1  STYLE_SOLID
#property indicator_width1  1

//--- input parameters
input int      InpPeriod=10;  // SMMA Period

//--- indicator buffers
double         MABuffer[];
//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- indicator buffers mapping
   SetIndexBuffer(0,MABuffer,INDICATOR_DATA);
   
//---
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//| Custom indicator iteration function                              |
//+------------------------------------------------------------------+
int OnCalculate(const int rates_total,
                const int prev_calculated,
                const datetime &time[],
                const double &open[],
                const double &high[],
                const double &low[],
                const double &close[],
                const long &tick_volume[],
                const long &volume[],
                const int &spread[])
  {
//---
   double result=0.0;
   for(int i=InpPeriod-1; i<rates_total; i++)
     {
      //--- first calculation
      if(i==InpPeriod-1)
        {
         //--- calculate a simple moving average for the InpPeriod first bars
         for(int j=0; j<InpPeriod; j++)
           {
            double price=close[i-j];
            result+=price;
           }
         result/=InpPeriod;
         //--- write the first displayed SMMA value calculated as SMA
         MABuffer[InpPeriod-1]=result;
        }
      //--- all subsequent calculations
      else
         result=(MABuffer[i-1]*(InpPeriod-1)+close[i])/InpPeriod;
      
      //--- Write the calculation result to the buffer
      MABuffer[i]=result;
     }
//--- return value of prev_calculated for the next call
   return(rates_total);
  }

Der Indikator zeigt die SMMA-Berechnung nur beim ersten Start und bei jedem neuen Tick an, wobei alle Balken der gesamten verfügbaren Historie vollständig berechnet werden. Dies ist nicht optimal und falsch, da der Indikator dann zu langsam ist. Wir sollten die Berechnung optimieren.

Optimierung der Berechnungen

Wir müssen den Eingabewert des SMMA-Berechnungszeitraums kontrollieren, eine vollständige Berechnung der gesamten Historie beim ersten Start durchführen und dann die Berechnung nur für den aktuellen Balken bei jedem neuen Tick:

//+------------------------------------------------------------------+
//|                                             SMMAonPriceClose.mq5 |
//|                                  Copyright 2024, MetaQuotes Ltd. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2024, MetaQuotes Ltd."
#property link      "https://www.mql5.com"
#property version   "1.00"
#property indicator_chart_window
#property indicator_buffers 1
#property indicator_plots   1
//--- plot MA
#property indicator_label1  "SMMA"
#property indicator_type1   DRAW_LINE
#property indicator_color1  clrRed
#property indicator_style1  STYLE_SOLID
#property indicator_width1  1

//--- input parameters
input int      InpPeriod=10;

//--- indicator buffers
double         MABuffer[];

//--- global variables
int   ExtPeriod;  // SMMA calculation period
//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- indicator buffers mapping
   SetIndexBuffer(0,MABuffer,INDICATOR_DATA);
   
//--- successful initialization 
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//| Custom indicator iteration function                              |
//+------------------------------------------------------------------+
int OnCalculate(const int rates_total,
                const int prev_calculated,
                const datetime &time[],
                const double &open[],
                const double &high[],
                const double &low[],
                const double &close[],
                const long &tick_volume[],
                const long &volume[],
                const int &spread[])
  {
//--- Adjust the entered value of the SMMA calculation period
   ExtPeriod=(InpPeriod<1 ? 10 : InpPeriod);
   
//--- If there is not enough historical data for calculation, return 0
   if(rates_total<ExtPeriod)
      return 0;
      
   double result=0;
   int    start =0;
   
//--- If this is the first run or a change in historical data
   if(prev_calculated==0)
     {
      //--- initialize the indicator buffer with an empty value and set the start of the calculation to ExtPeriod
      ArrayInitialize(MABuffer,EMPTY_VALUE);
      start=ExtPeriod;

      //--- calculate a simple moving average for the ExtPeriod first bars
      for(int i=0; i<start; i++)
         result+=close[i];
      result/=ExtPeriod;
      //--- write the first displayed SMMA value calculated as SMA
      MABuffer[start-1]=result;
     }
   else
      start=prev_calculated-1;
      
//--- in the loop from the 'start' value to the current history bar
//--- calculate the SMMA value for the loop current bar
   for(int i=start; i<rates_total; i++)
      MABuffer[i]=(MABuffer[i-1]*(ExtPeriod-1)+close[i])/ExtPeriod;
      
//--- return the number of bars calculated for the next OnCalculate call
   return(rates_total);
  }

Jetzt berechnet der Indikator ökonomisch die Historie und den aktuellen Balken. Die Indikator-Datei SMMAonPriceClose.mq5 finden Sie unten.


Linear gewichteter gleitender Durchschnitt (Linear Weighted Moving Average, LWMA)

Bei dieser Methode haben die neuesten Werte die höchste Wertigkeit. Dadurch kann die LWMA besser auf aktuelle Preisänderungen reagieren. In diesem gleitenden Durchschnitt nimmt das Gewicht der Balkenpreise linear ab - für den Null-Balken (aktuellen) ist es maximal, für den Balken mit der Nummer N-1 ist es gleich 0.

LWMA:

  • Reagiert im Vergleich zu traditionellen SMA aktiver auf Preisänderungen,
  • Ermöglicht es Ihnen, Daten so zu analysieren, dass die neuesten Änderungen die größten Auswirkungen auf die Berechnungen haben,
  • Kann zur Entwicklung verschiedener Handelsstrategien verwendet werden, insbesondere bei hoher Volatilität.

Abb. 4. Linear gewichteter gleitender Durchschnitt LWMA bei Close mit einem Berechnungszeitraum von 10

Berechnung

Der gewichtete gleitende Durchschnitt wird berechnet durch das Multiplizieren jedes Schlusspreises innerhalb der Serie mit einem Gewichtungskoeffizienten:

LWMA = SUM(CLOSE(i) * i, N) / SUM(i, N) 

wobei:

  • SUM - Summe;
  • CLOSE(i) - aktueller Schlusskurs;
  • SUM(i, N) - Summe der Gewichtskoeffizienten;
  • N - Glättungszeitraum.

Lassen Sie uns einen einfachen Indikator schreiben, der die Berechnung des linear gewichteten gleitenden Durchschnitts zeigt:

//+------------------------------------------------------------------+
//|                                          LWMAonPriceCloseRAW.mq5 |
//|                                  Copyright 2024, MetaQuotes Ltd. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2024, MetaQuotes Ltd."
#property link      "https://www.mql5.com"
#property version   "1.00"
#property indicator_chart_window
#property indicator_buffers 1
#property indicator_plots   1
//--- plot MA
#property indicator_label1  "LWMA"
#property indicator_type1   DRAW_LINE
#property indicator_color1  clrRed
#property indicator_style1  STYLE_SOLID
#property indicator_width1  1
//--- input parameters
input int      InpPeriod=10;  // LWMA Period
//--- indicator buffers
double         MABuffer[];
//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- indicator buffers mapping
   SetIndexBuffer(0,MABuffer,INDICATOR_DATA);
   
//---
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//| Custom indicator iteration function                              |
//+------------------------------------------------------------------+
int OnCalculate(const int rates_total,
                const int prev_calculated,
                const datetime &time[],
                const double &open[],
                const double &high[],
                const double &low[],
                const double &close[],
                const long &tick_volume[],
                const long &volume[],
                const int &spread[])
  {
//--- in the loop from InpPeriod-1 to the current bar
   double result=0.0;
   for(int i=InpPeriod-1; i<rates_total; i++)
     {
      //--- calculate weights (wsum) and the sum of weight coefficients (sum) of InpPeriod bars
      double sum =0.0;
      int    wsum=0;
      for(int j=InpPeriod; j>0; j--)
        {
         wsum+=j;
         sum +=close[i-j+1]*(InpPeriod-j+1);
        }
      //--- get the LWMA value for the current bar of the loop
      result=sum/wsum;
      //--- Write the calculation result to the buffer
      MABuffer[i]=result;
     }
//--- return value of prev_calculated for the next call
   return(rates_total);
  }

Der Indikator zeigt das Ergebnis der LWMA-Berechnung für jeden Balken in der Historie auf dem Chart an. Bei jedem neuen Tick wird eine vollständige Neuberechnung aller historischen Daten vorgenommen. Dies ist eine sehr langsame und suboptimale Berechnung. Wir werden ihn optimieren, indem wir eine ressourcenschonende Berechnung des Indikators durchführen und eine Überprüfung der eingegebenen Eingaben hinzufügen.

Die Indikatordatei LWMAonPriceCloseRAW.mq5 finden Sie in den an den Artikel angehängten Dateien.

Optimierung der Berechnungen

Optimieren Sie die Berechnung des Indikators, sodass nicht bei jedem Tick die gesamte Historie neu berechnet wird, und fügen Sie eine Überprüfung des eingegebenen Wertes für den LWMA-Berechnungszeitraum hinzu:

//+------------------------------------------------------------------+
//|                                             LWMAonPriceClose.mq5 |
//|                                  Copyright 2024, MetaQuotes Ltd. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2024, MetaQuotes Ltd."
#property link      "https://www.mql5.com"
#property version   "1.00"
#property indicator_chart_window
#property indicator_buffers 1
#property indicator_plots   1
//--- plot MA
#property indicator_label1  "LWMA"
#property indicator_type1   DRAW_LINE
#property indicator_color1  clrRed
#property indicator_style1  STYLE_SOLID
#property indicator_width1  1

//--- input parameters
input int      InpPeriod   =  10;   // LWMA Period

//--- indicator buffers
double         MABuffer[];

//--- global variables
int   ExtPeriod;  // LWMA calculation period
//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- indicator buffers mapping
   SetIndexBuffer(0,MABuffer,INDICATOR_DATA);
   
//--- successful initialization
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//| Custom indicator iteration function                              |
//+------------------------------------------------------------------+
int OnCalculate(const int rates_total,
                const int prev_calculated,
                const datetime &time[],
                const double &open[],
                const double &high[],
                const double &low[],
                const double &close[],
                const long &tick_volume[],
                const long &volume[],
                const int &spread[])
  {
//--- Adjust the entered value of the LWMA calculation period
   ExtPeriod=(InpPeriod<1 ? 10 : InpPeriod);
   
//--- If there is not enough historical data for calculation, return 0
   if(rates_total<ExtPeriod)
      return 0;
      
   double result=0;
   int    start =0;
   
//--- If this is the first run or a change in historical data
   if(prev_calculated==0)
     {
      //--- initialize the indicator buffer with an empty value and set the start of the calculation to ExtPeriod-1
      ArrayInitialize(MABuffer,EMPTY_VALUE);
      start=ExtPeriod-1;
     }
//--- If this is not the first run, the calculation will start at the current bar
   else
      start=prev_calculated-1;
      
//--- in the loop from the 'start' value to the current history bar
//--- calculate the LWMA value for the loop current bar
   for(int i=start; i<rates_total; i++)
     {
      //--- calculate weights (wsum) and the sum of weight coefficients (sum) of ExtPeriod bars
      double sum =0.0;
      int    wsum=0;
      for(int j=ExtPeriod; j>0; j--)
        {
         wsum+=j;
         sum +=close[i-j+1]*(ExtPeriod-j+1);
        }
      //--- get the LWMA value for the current bar of the loop
      result=sum/wsum;
      //--- Write the calculation result to the buffer
      MABuffer[i]=result;
     }
      
//--- return the number of bars calculated for the next OnCalculate call
   return(rates_total);
  }

Die Prinzipien der ressourcenschonend Berechnung von Indikatoren werden im Abschnitt über die einfache gleitende Durchschnittsoptimierung ausführlich beschrieben.

Die Indikator-Datei finden Sie unten.


Vor- und Nachteile der vier Arten von gleitenden Durchschnitten

Im Laufe der Zeit haben sich verschiedene Arten von gleitenden Durchschnitten herausgebildet, wie z. B. einfache, exponentielle, gewichtete und viele andere gleitende Durchschnitte, von denen einige ihre eigenen einzigartigen Berechnungen verwenden und andere auf dem SMA basieren, die jeweils unterschiedlichen Zwecken dienen und neue und interessante Perspektiven auf die zu analysierenden Daten eröffnen können.

Im Folgenden finden Sie eine kurze Zusammenfassung der Vor- und Nachteile der vier grundlegenden gleitenden Durchschnitte:

  1. Der SMA (einfacher gleitender Durchschnitt) wird häufig verwendet, um die allgemeine Richtung eines Trends zu bestimmen und Unterstützungs- und Widerstandsniveaus zu ermitteln. Beliebt bei langfristigen Händlern und Anlegern, da er Kursschwankungen glättet und so einen besseren Überblick über den Gesamttrend ermöglicht.

    Vorteile:
    • Leichte Berechnung und Interpretation,
    • Glättet Preisschwankungen und bietet einen klaren Überblick über Trends,
    • Nützlich für die Erkennung langfristiger Trends.

    Nachteile
    • Reagiert langsam auf Veränderungen, was zu verzögerten Signalen führen kann,
    • Gleiche Empfindlichkeit gegenüber Preisschwankungen über die gesamte Spanne des Berechnungszeitraums (die Gewichte aller Preise sind gleich).

  2. Der EMA (exponentieller gleitender Durchschnitt) gibt den jüngsten Kursen mehr Gewicht, wodurch er empfindlicher auf neue Kursänderungen reagiert. Wird für schnellere Signale zum Öffnen und Schließen von Positionen verwendet. Bevorzugt von kurzfristigen Händlern und Scalper, die einen schnelleren Ein- und Ausstieg in den Markt suchen.

    Vorteile:
    • Ziel ist eine schnellere Reaktion auf Preisänderungen, da den neuesten Daten mehr Gewicht beigemessen wird.
    • Wird in der Regel in Kombination mit anderen Indikatoren verwendet, um genauere Signale zu erhalten.
    • Bietet aktuellere Informationen über den aktuellen Trend.

    Nachteile
    • Kann bei starken Marktschwankungen falsche Signale geben.
    • Schwieriger zu berechnen als SMA.

  3. SMMA (smoothed moving average) gilt als eine glattere Version von EMA und SMA, die es ermöglicht, starke Schwankungen zu vermeiden und gleichzeitig die Relevanz der Informationen zu erhalten. Geeignet für Händler, die falsche Signale vermeiden wollen, praktisch für die Trendanalyse und Bestätigung.

    Vorteile:
    • Glättet die Daten stärker als SMA und EMA.
    • Weniger anfällig für Schwankungen, was auf volatilen Märkten von Vorteil sein kann.
    • Speichert Informationen über frühere Werte und ermöglicht so eine bessere Trendanalyse.

    Nachteile
    • Reagiert möglicherweise nur langsam auf Marktveränderungen, ähnlich wie der SMA.
    • Es kann zu Verzögerungen bei der Erkennung von Trends kommen.

  4. Der LWMA (linear gewichteter gleitender Durchschnitt) gewichtet jeden Preiswert je nach seinem Alter unterschiedlich, wobei neuere Daten stärker gewichtet werden. Wird für detailliertere Analysen verwendet (z. B. bei Strategien, die mehrere Zeitrahmen verwenden), wenn Händler schneller auf Veränderungen reagieren wollen.

    Vorteile:
    • Verleiht den jüngsten Preisen mehr Gewicht, sodass sie die jüngsten Veränderungen genauer widerspiegeln.
    • Flexibler als SMA und EMA, ermöglicht eine bessere Reaktion auf neue Daten,
    • Kann für kurzfristige Strategien nützlich sein.

    Nachteile
    • Schwieriger zu berechnen, insbesondere im Vergleich zu SMA und EMA.
    • Kann auf volatilen Märkten auch falsche Signale geben.



Schlussfolgerung

Wir haben die Grundsätze der Berechnung der wichtigsten Arten von gleitenden Durchschnitten überprüft, die in den Einstellungen des Standardindikators Gleitender Durchschnitt im Client-Terminal verfügbar sind. Die im Artikel vorgestellten Berechnungen können sowohl in Indikatoren mit Berechnungsoptimierung (die ebenfalls im Artikel gezeigt wird) verwendet werden, während die vorgestellten Codes als unabhängige Berechnung von Durchschnittswerten eines sequenziellen Datensatzes in Ihren Programmen verwendet werden können.

Die obige Abbildung zeigt den Unterschied zwischen gleitenden Durchschnitten mit demselben Berechnungszeitraum (10), aber unterschiedlichen Typen:

Rot - SMA, grün - EMA, gold - SMMA, blau - LWMA.

Es wird deutlich, dass der geglättete gleitende Durchschnitt weniger anfällig für den Einfluss kleiner Kursschwankungen ist als die anderen und den allgemeinen Trend der Kursbewegung deutlicher anzeigt.
Exponentielle und linear gewichtete gleitende Durchschnitte reagieren besser auf Marktschwankungen, da sie bei ihren Berechnungen den aktuellen Daten das größte Gewicht beimessen.

Fassen wir das Ganze zusammen:

Der gleitende Durchschnitt (SMA) ist eine statistische Methode zur Analyse von Zeitreihen und glatten Daten, mit der sich Trends und kurzfristige Richtungsänderungen, dynamische Unterstützungs-/Widerstandsniveaus, Kanalgrenzen usw. erkennen lassen. Die ersten Anwendungen des Konzepts des gleitenden Durchschnitts gehen auf das frühe 20. Jahrhundert zurück, als Wirtschaftswissenschaftler und Ingenieure begannen, es zur Analyse von Daten und zur Vorhersage künftiger Werte einzusetzen. Interessanterweise nutzten die Flakhelfer im Zweiten Weltkrieg die SMA-Berechnungsmethoden zur Zielbestimmung.

Gleitende Durchschnitte wurden in der Finanzanalyse weit verbreitet, insbesondere mit der Entwicklung des Börsenhandels in der Mitte des 20. Zu dieser Zeit begannen die Anleger nach Methoden zu suchen, um Schwankungen der Aktienkurse zu glätten und langfristige Trends zu erkennen. Seitdem haben sich gleitende Durchschnitte zu einem Standardinstrument der technischen Analyse entwickelt, das sowohl von Händlern als auch von Analysten genutzt wird.

Gleitende Durchschnitte mit demselben Berechnungszeitraum, aber unterschiedlichen Typen, zeigen die Daten auf demselben Kurschart unterschiedlich an:

  • Der SMA ist glatter und zeigt das Niveau der Unterstützung oder des Widerstands an, allerdings mit einer Verzögerung,
  • Der EMA folgt den Kursen, bleibt näher am Kurs und reagiert schneller auf Trendänderungen,
  • SMMA ist eine glattere Linie als SMA, die weniger auf plötzliche Veränderungen reagiert,
  • Die LWMA reagiert ebenfalls schnell auf Veränderungen, ihr Verhalten ist jedoch im Vergleich zur EMA und SMMA volatiler.

Die Wahl zwischen diesen Arten von gleitenden Durchschnitten hängt von der jeweiligen Strategie und den Marktbedingungen ab. Bei der Verwendung von gleitenden Durchschnitten im Handel ist es wichtig, dass Sie Ihre Handelsziele, den Zeitrahmen und die Volatilität der Vermögenswerte berücksichtigen. Händler kombinieren oft verschiedene Arten von gleitenden Durchschnitten, um die Effizienz ihrer Analyse zu maximieren.


Beigefügte Dateien:

#
Typ
Name 
 Beschreibung
1
Indikator
SMAonPriceCloseRAW.mq5
SMA-Berechnungsbeispiel. Nur grobe Berechnung, keine ressourcenschonende Berechnung, keine Optimierung
2
Indikator
SMAonPriceCloseECO.mq5
SMA-Berechnungsindikator. Ressourcenschonende Indikatorberechnung, keine Optimierung
3
Indikator
SMAonPriceClose.mq5
SMA-Berechnungsindikator. Ressourcenschonende Kennzahlenberechnung mit Optimierung
4
Indikator
EMAonPriceCloseRAW.mq5
Beispiel für eine EMA-Berechnung. Nur überschlägige Berechnung, keine ressourcenschonende Berechnung
5
Indikator
EMAonPriceClose.mq5
EMA-Berechnungsindikator. Berechnung des ressourcenschonenden Indikators
6
Indikator
SMMAonPriceCloseRAW.mq5
SMMA-Berechnungsbeispiel. Nur überschlägige Berechnung, keine ressourcenschonende Berechnung
7
Indikator
SMMAonPriceClose.mq5
SMMA Berechnungsindikator. Berechnung des ressourcenschonenden Indikators
8
Indikator
LWMAonPriceCloseRAW.mq5
Beispiel für eine LWMA-Berechnung. Nur überschlägige Berechnung, keine ressourcenschonende Berechnung
9
Indikator
LWMAonPriceClose.mq5
LWMA Berechnungsindikator. Berechnung des ressourcenschonenden Indikators

Übersetzt aus dem Russischen von MetaQuotes Ltd.
Originalartikel: https://www.mql5.com/ru/articles/16308

Letzte Kommentare | Zur Diskussion im Händlerforum (4)
Alexey Viktorov
Alexey Viktorov | 18 Nov. 2024 in 14:33

Danke für den guten Artikel. Aber ich kann nicht umhin, ihn zu kritisieren. Ich bin heute in so einer Stimmung.

Der Artikel befasst sich weniger mit dem Programmieren als mit Formeln und der Optimierung von Berechnungen. Ich denke aber, dass er für Programmieranfänger und diejenigen, die sich erst seit kurzem mit den Möglichkeiten der Verwendung verschiedener Schleifenoperatoren befassen, nützlicher ist. Ich hoffe, dass die nächsten Artikel dies widerspiegeln werden. Immerhin gibt es mindestens 3 Schleifenoperatoren in MQL5. Und jeder von ihnen kann verwendet werden, um einen Indikator zu erstellen.

lynxntech
lynxntech | 18 Nov. 2024 in 15:11

nützliche Informationen auf einmal und an einem Ort,

und schließlich die Schleife i++

CapeCoddah
CapeCoddah | 3 Juli 2025 in 10:50

Ein guter Artikel für Anfänger, der den Code hinter den 4 Standard-MQ-Durchschnittswerten zeigt.

Sie sollten auch die Bedeutung einer einfachen Optimierung und ihre Auswirkungen erörtern, da 99 % der Ticks zwischen den Taktwechseln auftreten. Die Minimierung der Berechnungen zwischen den einzelnen Taktwechseln führt zu einer viel höheren Effizienz auf Kosten einer geringen Komplexität. Wenn Sie also die Basiswerte einmal beim Taktwechsel berechnen und die Werte speichern, wird die Berechnungszeit erheblich verkürzt. Bei einem SAM beispielsweise ist die Standardberechnung für N Perioden:

Betrachten Sie .

double sm=0;

for(int bar=0;bar<N;bar++) sum+=Close[CurrentBar-bar];

SMA=sum/N;


gegen

statisch double partialsum;

double sum=0;


Beim Barwechsel{

partialsum=0;

for(int bar=0;bar<N-1;bar++) partialsum+=Close[CurrentBar-bar];

partialsum/=(N-1);

}

SMA =partialsum +Close[CurrentBar]/N;

Bei 1000 Ticks in einer Balkenperiode und N=10 spart diese Optimierung etwa 90.000 Berechnungen von sum+=Close[EndingBar-bar] für jeden Balken. Wenn Ihr Chart 1.000 Balken enthält, belaufen sich die Einsparungen auf über 90 Millionen nicht benötigte Berechnungen. Bei modernen CPUs sind die durch dieses Beispiel erzielten Einsparungen trivial und wahrscheinlich nicht spürbar, aber sie summieren sich mit der Zeit, wenn Ihre Programme in Expert Advisors komplexer werden.

Die Bedeutung der manuellen Optimierung liegt darin, dass Sie bessere Programmiertechniken entwickeln, die Ihnen bei zukünftigen Projekten zur zweiten Natur werden.

1120965101
1120965101 | 13 Aug. 2025 in 23:58

Vielen Dank für Ihren Artikel, aber es ist nicht klar, warum ich nach der Verwendung der Funktion OnCalculate() die Transaktionsfunktion OrderSend() nicht mehr verwenden kann. Ich weiß nicht, wie der Autor dieses Problem gelöst hat, ich habe keine andere Wahl, als die Indikatoren in der Standardbibliothek auf folgende Weise zu verwenden:

#property copyright "Copyright 2024, MetaQuotes Ltd."
#property link      "https://www.mql5.com"
#property version   "1.00"

#include <Indicators\Trend.mqh>
CiMA ma;

//+------------------------------------------------------------------+
//| Skript Programmstartfunktion &nbsp Skript Programmstartfunktion
//+------------------------------------------------------------------+
int OnInit(){ 
    ma.DeleteFromChart(ChartID(), 0);
    ma.Create(_Symbol, PERIOD_CURRENT, 14, 0, MODE_SMA, PRICE_CLOSE);
    ma.AddToChart(PERIOD_CURRENT, 0);
    
    return INIT_SUCCEEDED; 
}

void OnTick(){ 
    ma.Refresh();
    double curMA = ma.Main(0);
    
    //Drucken("Aktueller MA-Wert:", maValue);
}
MQL5 beherrschen, vom Anfänger bis zum Profi (Teil IV): Grundlagen der Entwicklung von Expert Advisors MQL5 beherrschen, vom Anfänger bis zum Profi (Teil IV): Grundlagen der Entwicklung von Expert Advisors
Dieser Artikel setzt die Reihe für Anfänger fort. Hier werden wir die grundlegenden Prinzipien der Entwicklung von Expert Advisors (EAs) diskutieren. Wir werden zwei EAs erstellen: der erste wird ohne Indikatoren handeln und schwebende Aufträge verwenden, der zweite wird auf dem Standard-MA-Indikator basieren und Handelsgeschäfte zum aktuellen Preis eröffnen. Hier gehe ich davon aus, dass Sie kein völliger Anfänger mehr sind und den Stoff aus den vorherigen Artikeln relativ gut beherrschen.
Entwicklung eines Expert Advisors für mehrere Währungen (Teil 20): Ordnung in den Ablauf der automatischen Projektoptimierungsphasen bringen (I) Entwicklung eines Expert Advisors für mehrere Währungen (Teil 20): Ordnung in den Ablauf der automatischen Projektoptimierungsphasen bringen (I)
Wir haben bereits eine ganze Reihe von Komponenten entwickelt, die bei der automatischen Optimierung helfen. Bei der Erstellung folgten wir der traditionellen zyklischen Struktur: von der Erstellung eines minimalen funktionierenden Codes bis hin zum Refactoring und dem Erhalt eines verbesserten Codes. Es ist an der Zeit, mit dem Aufräumen unserer Datenbank zu beginnen, die auch eine Schlüsselkomponente in dem von uns geschaffenen System ist.
Neuronale Netze im Handel: Verbesserung des Wirkungsgrads des Transformers durch Verringerung der Schärfe (SAMformer) Neuronale Netze im Handel: Verbesserung des Wirkungsgrads des Transformers durch Verringerung der Schärfe (SAMformer)
Das Training von Transformer-Modellen erfordert große Datenmengen und ist oft schwierig, da die Modelle nicht gut auf kleine Datensätze verallgemeinert werden können. Der SAMformer-Rahmen hilft bei der Lösung dieses Problems, indem er schlechte lokale Minima vermeidet. Dadurch wird die Effizienz der Modelle auch bei begrenzten Trainingsdaten verbessert.
Neuronale Netze im Handel: Optimierung des Transformers für Zeitreihenprognosen (LSEAttention) Neuronale Netze im Handel: Optimierung des Transformers für Zeitreihenprognosen (LSEAttention)
Der LSEAttention-Rahmen bietet Verbesserungen der Transformer-Architektur. Es wurde speziell für langfristige multivariate Zeitreihenprognosen entwickelt. Die von den Autoren der Methode vorgeschlagenen Ansätze können angewandt werden, um Probleme des Entropiekollapses und der Lerninstabilität zu lösen, die bei einem einfachen Transformer häufig auftreten.