English
preview
Erstellen von nutzerdefinierten Indikatoren in MQL5 (Teil 4): Smart WaveTrend Crossover mit zwei Oszillatoren

Erstellen von nutzerdefinierten Indikatoren in MQL5 (Teil 4): Smart WaveTrend Crossover mit zwei Oszillatoren

MetaTrader 5Handel |
29 0
Allan Munene Mutiiria
Allan Munene Mutiiria

Einführung

In unserem vorherigen Artikel (Teil 3) haben wir einen Multi-Messuhr-Indikator in MetaQuotes Language 5 (MQL5) mit Erweiterungen für Sektor- und Rundstile entwickelt, der eine dynamische Visualisierung mehrerer Datenpunkte durch anpassbare Gauge-Anzeigen und farbcodierte Sektoren ermöglicht. In Teil 4 entwickeln wir den Indikator Smart WaveTrend Crossover, der zwei Oszillatoren verwendet – einen für Signale und einen für die Trendfilterung –, um auf dem Kreuzen basierende Kauf- und Verkaufswarnungen mit optionaler Trendbestätigung zu generieren. Dieser Indikator färbt Kerzen nach Trendrichtung, zeichnet Pfeilsignale bei Überkreuzungen und unterstützt anpassbare Parameter für Länge und visuellen Stil. Wir werden die folgenden Themen behandeln:

  1. Verstehen des Systems von Smart WaveTrend Crossover
  2. Implementation in MQL5
  3. Backtests
  4. Schlussfolgerung

Am Ende haben Sie einen funktionsfähigen MQL5-Indikator für das Kreuzen der WaveTrends, der zur Anpassung bereit ist – los geht's!


Verstehen des Systems von Smart WaveTrend Crossover

Der Smart WaveTrend Crossover basiert auf dem WaveTrend-Oszillator, einem momentumbasierten Instrument, das überkaufte und überverkaufte Bedingungen anhand geglätteter Kursdurchschnitte misst. Dies hilft uns, potenzielle Umkehrungen oder Fortsetzungen der Marktdynamik zu erkennen. Er berechnet einen Quellpreis aus Höchst-, Tiefst- und Schlusskursen und wendet dann exponentielle gleitende Durchschnitte an, um zwei Linien zu erstellen: eine schnellere, oszillierende und eine langsamere, glattere Linie. Überkreuzungen zwischen diesen Linien signalisieren Kauf- oder Verkaufsgelegenheiten. Durch die Verwendung der Konfigurationen von zwei WaveTrends – eine mit kürzerer Periodenlänge für die Generierung empfindlicher Signale und eine mit längerer Periodenlänge für die allgemeine Trenderkennung – können wir schnelle Einstiegshinweise mit einem breiteren Marktkontext kombinieren. Dies hilft, falsche Signale bei unruhigen Bedingungen herauszufiltern.

Bei einer Aufwärtsentwicklung muss die schnellere Linie über der langsameren Linie des Signaloszillators liegen. Dies deutet auf eine zunehmende Aufwärtsdynamik hin, insbesondere wenn sie mit einem Aufwärtstrend des langsameren Oszillators übereinstimmt. Bei einer Abwärtsentwicklung deutet die schnellere Linie, die die langsamere Linie unterschreitet, auf eine Abwärtsdynamik hin. Idealerweise wird dies durch einen Abwärtstrend des langsameren Oszillators bestätigt, um gegenläufige Trades zu vermeiden. Dieser Ansatz ermöglicht es uns, von Momentumverschiebungen zu profitieren und gleichzeitig den vorherrschenden Trend zu respektieren. Zusätzliche visuelle Elemente, wie farbige Kerzen, heben die Trendrichtung hervor, und Pfeile markieren präzise Signalpunkte.

Wir planen, die WaveTrend-Werte separat für Signale und Trends zu berechnen und dabei anpassbare Längen für Kanäle, Durchschnitte und gleitende Durchschnitte zu verwenden, Überkreuzungen auf der Signalseite zu erkennen und einen optionalen Trendfilter anzuwenden, um sicherzustellen, dass die Signale der Trendrichtung entsprechen. Wir werden visuelle Elemente wie die Einfärbung von Kerzen auf der Grundlage des Trendstatus und versetzte Pfeile für Kauf- oder Verkaufsindikationen einbeziehen und so ein umfassendes System schaffen, das klare, umsetzbare Erkenntnisse für den Momentum-Handel liefert. Kurz gesagt, hier ist eine visuelle Darstellung unserer Ziele.

INDIKATORSYSTEM


Implementation in MQL5

Um den Indikator in MQL5 zu erstellen, öffnen Sie einfach den MetaEditor, gehen Sie zum Navigator, suchen Sie den Ordner Indikatoren, klicken Sie auf die Registerkarte „Neu“ und folgen Sie den Anweisungen, um die Datei zu erstellen. Sobald der Indikator erstellt ist, werden wir in der Programmierumgebung die Eigenschaften und Einstellungen des Indikators festlegen, z. B. die Anzahl der Puffer, die Darstellungen und die Eigenschaften der einzelnen Linien, wie Farbe, Breite und Beschriftung.

//+------------------------------------------------------------------+
//|                           1. Smart WaveTrend Crossover PART1.mq5 |
//|                           Copyright 2026, Allan Munene Mutiiria. |
//|                                   https://t.me/Forex_Algo_Trader |
//+------------------------------------------------------------------+
#property copyright "Copyright 2026, Allan Munene Mutiiria."
#property link "https://t.me/Forex_Algo_Trader"
#property version "1.00"

#property indicator_chart_window
#property indicator_buffers 23
#property indicator_plots 3
#property indicator_label1 "Colored Candles"
#property indicator_type1 DRAW_COLOR_CANDLES
#property indicator_color1 clrTeal, clrRed
#property indicator_style1 STYLE_SOLID
#property indicator_width1 1
#property indicator_label2 "Buy Signals"
#property indicator_type2 DRAW_ARROW
#property indicator_color2 clrForestGreen
#property indicator_style2 STYLE_SOLID
#property indicator_width2 1
#property indicator_label3 "Sell Signals"
#property indicator_type3 DRAW_ARROW
#property indicator_color3 clrOrangeRed
#property indicator_style3 STYLE_SOLID
#property indicator_width3 1

Wir beginnen damit, den Indikator so zu konfigurieren, dass er direkt im Hauptchart angezeigt wird, indem wir „#property indicator_chart_window“ verwenden, um sicherzustellen, dass er die Preisdaten überlagert, ohne ein separates Unterfenster zu öffnen. Dann reservieren wir uns insgesamt 23 Puffer mit „#property indicator_buffers 23“, um alle notwendigen Datenarrays für Berechnungen und Visualisierungen zu speichern, und legen 3 Plots mit „#property indicator_plots 3“ fest, um die sichtbaren Elemente im Chart zu definieren.

Für das erste Plot geben wir die Bezeichnung „Colored Candles“ an und setzen den Typ auf DRAW_COLOR_CANDLES, um die Kursbalken mit dynamischen Farben darzustellen, wobei wir „clrTeal“ für aufwärts und „clrRed“ für abwärts verwenden, gestylt als „solid“ mit einer Breite von 1. Das zweite Plot trägt die Bezeichnung „Buy Signals“ mit dem Typ „DRAW_ARROW“, der Farbe clrForestGreen, dem Stil „solid“ und der Breite 1, um potenzielle Einstiegspunkte visuell zu markieren. In ähnlicher Weise erstellen wir das dritte Plot mit der Bezeichnung „Sell Signals“ den Typ DRAW_ARROW, die Farbe „clrOrangeRed“, den Stil „solid“ und die Breite 1, um Verkaufschancen anzuzeigen. Wir definieren nun einige Eingaben zur Steuerung des Indikators.

//+------------------------------------------------------------------+
//| Inputs                                                           |
//+------------------------------------------------------------------+
input group "Colors"
input color col_up = clrTeal;                // Bull Color
input color col_dn = clrRed;                 // Bear Color

input group "WaveTrend Settings for Signals"
input int wt_channel_len = 5;                // Signal Channel Length
input int wt_average_len = 10;               // Signal Average Length
input int wt_ma_len = 4;                     // Signal MA Length

input group "WaveTrend Settings for Trend"
input int wt_trend_channel_len = 10;         // Trend Channel Length
input int wt_trend_average_len = 100;        // Trend Average Length
input int wt_trend_ma_len = 10;              // Trend MA Length

input group "Signal Settings"
input bool use_trend_filter = true;          // Use Trend Filter?
input color signal_buy_col = clrForestGreen; // Buy Signal Color
input color signal_sell_col = clrOrangeRed;  // Sell Signal Color
input int base_offset = 10;                  // Base Signal Offset from Candle

input group "Visual Settings"
input bool color_candles = true;             // Color Candles by Trend?

Hier organisieren wir die Nutzereingaben in logischen Gruppen mit „input groupe“, um die Konfiguration im Einstellungsdialog des Indikators intuitiver zu gestalten. In der Gruppe „Colors“ können wir zunächst die Farbe für Aufwärtstrends (Standardeinstellung: Türkis) und für Abwärtstrends (Standardeinstellung: Rot) auswählen, die für die Einfärbung der Kerzen verwendet wird. In der Gruppe „WaveTrend Settings for Signals“ werden ganzzahlige Eingaben für die Kanallänge des Signaloszillators (5), die Durchschnittslänge (10) und die Länge des gleitenden Durchschnitts (4) vorgenommen, um die Empfindlichkeit für die Erzeugung von Kreuzungssignalen anzupassen.

Die Gruppe „WaveTrend Settings for Trend“ enthält ähnliche ganzzahlige Eingaben, jedoch mit längeren Standardwerten: Kanallänge bei 10, Durchschnittslänge bei 100 und Länge des gleitenden Durchschnitts bei 10, die auf die Erkennung des breiteren Markttrends zugeschnitten sind. In der Gruppe „Signaleinstellungen“ finden Sie eine boolesche Option, die standardmäßig aktiviert ist, um die Trendfilterung zu verwenden, sowie eine Farbauswahl für Kaufsignale, die standardmäßig forest green und für Verkaufssignale orange-red sind, sowie eine Ganzzahl für den Basisversatz von Kerzen, der auf 10 Punkte eingestellt ist und die Platzierung der Signalpfeile steuert. Schließlich bietet die Gruppe „Visual Settings“ eine boolesche Eingabe, die standardmäßig aktiviert ist, um Kerzen auf der Grundlage des erkannten Trends zu färben und dem Nutzer die Kontrolle darüber zu geben, ob er dynamische Kerzenvisualisierungen anwenden möchte. Nach der Kompilierung erhalten wir den folgenden Eingabebereich.

INDIKATOREINGABEBEN

Damit können wir nun einige globale Variablen für die Indikatorpuffer und Hilfsfunktionen definieren, die global verwendet werden sollen.

//+------------------------------------------------------------------+
//| Buffers                                                          |
//+------------------------------------------------------------------+
double esa_signal[];          //--- Signal ESA buffer
double d_signal[];            //--- Signal D buffer
double ci_signal[];           //--- Signal CI buffer
double wt1_signal[];          //--- Signal WT1 buffer
double wt2_signal[];          //--- Signal WT2 buffer
double signal_hist[];         //--- Signal histogram buffer
double signal_bull_cross[];   //--- Signal bull cross buffer
double signal_bear_cross[];   //--- Signal bear cross buffer

double esa_trend[];           //--- Trend ESA buffer
double d_trend[];             //--- Trend D buffer
double ci_trend[];            //--- Trend CI buffer
double wt1_trend[];           //--- Trend WT1 buffer
double wt2_trend[];           //--- Trend WT2 buffer
double trend_hist[];          //--- Trend histogram buffer
double trend_is_bull[];       //--- Trend bull indicator buffer
double trend_is_bear[];       //--- Trend bear indicator buffer

double openBuf[];             //--- Open price buffer
double highBuf[];             //--- High price buffer
double lowBuf[];              //--- Low price buffer
double closeBuf[];            //--- Close price buffer
double candleColorBuf[];      //--- Candle color buffer
double buyArrowBuf[];         //--- Buy arrow buffer
double sellArrowBuf[];        //--- Sell arrow buffer

//+------------------------------------------------------------------+
//| Calculate EMA manually                                           |
//+------------------------------------------------------------------+
double CalcEMA(double prev, double val, int period) {
   if (period < 1) return val;              //--- Handle invalid period
   double alpha = 2.0 / (period + 1);       //--- Compute alpha factor
   return alpha * val + (1 - alpha) * prev; //--- Return EMA value
}

Wir deklarieren eine Reihe von Double-Arrays, die als Puffer für die Speicherung von Zwischen- und Enddaten während der Indikatorberechnungen dienen. Dazu gehören Puffer für die WaveTrend-Signalkomponenten, z. B. für exponentielle gleitende Durchschnitte, Differenzen, Kanalindizes und die beiden Hauptlinien, die zum Erkennen des Kreuzens verwendet werden, sowie die Flags für Histogramm und Kreuzungen für Auf- und Abwärtssignale. In ähnlicher Weise richten wir parallele Puffer für den Trend WaveTrend ein, der seine eigene Glättung, Differenzen, Indizes, Linien, Histogramme und boolesche Flags zur Identifizierung von Auf- und Abwärtstrends umfasst. Ferner werden Puffer für Preisdaten zugewiesen, um farbige Kerzen zu zeichnen, einschließlich Eröffnungs-, Höchst-, Tiefst- und Schlusskursen und einem Farbindexpuffer, sowie separate Puffer für die Darstellung von Kauf- und Verkaufspfeilen an Signalpunkten.

Zur Unterstützung der WaveTrend-Berechnungen definieren wir die Funktion „CalcEMA“, die manuell einen exponentiellen gleitenden Durchschnitt berechnet, indem sie zunächst auf ungültige Perioden prüft und in diesem Fall den Wert direkt zurückgibt, dann einen Alpha-Glättungsfaktor als 2,0 geteilt durch die Periode plus eins berechnet und ihn schließlich anwendet, um den aktuellen Wert mit dem vorherigen EMA für ein geglättetes Ergebnis zu mischen. Wir werden die Funktion verwenden, wenn wir die Berechnungen pro Tick durchführen, aber für den Moment wollen wir den Indikator mit dem folgenden Ansatz initialisieren.

//+------------------------------------------------------------------+
//| Initialize indicator                                             |
//+------------------------------------------------------------------+
int OnInit() {
   IndicatorSetInteger(INDICATOR_DIGITS, _Digits);   //--- Set indicator digits
   IndicatorSetString(INDICATOR_SHORTNAME, "Smart WaveTrend Crossover PART1"); //--- Set short name

   SetIndexBuffer(0, openBuf, INDICATOR_DATA);       //--- Bind open buffer
   SetIndexBuffer(1, highBuf, INDICATOR_DATA);       //--- Bind high buffer
   SetIndexBuffer(2, lowBuf, INDICATOR_DATA);        //--- Bind low buffer
   SetIndexBuffer(3, closeBuf, INDICATOR_DATA);      //--- Bind close buffer
   SetIndexBuffer(4, candleColorBuf, INDICATOR_COLOR_INDEX); //--- Bind color buffer
   PlotIndexSetInteger(0, PLOT_SHOW_DATA, color_candles); //--- Set candle visibility

   SetIndexBuffer(5, buyArrowBuf, INDICATOR_DATA);   //--- Bind buy arrow buffer
   SetIndexBuffer(6, sellArrowBuf, INDICATOR_DATA);  //--- Bind sell arrow buffer
   PlotIndexSetInteger(1, PLOT_ARROW, 233);          //--- Set buy arrow symbol
   PlotIndexSetInteger(1, PLOT_SHOW_DATA, true);     //--- Enable buy arrow display
   PlotIndexSetInteger(1, PLOT_DRAW_BEGIN, 0);       //--- Set buy draw begin
   PlotIndexSetInteger(1, PLOT_LINE_COLOR, 0, signal_buy_col); //--- Set buy color

   PlotIndexSetInteger(2, PLOT_ARROW, 234);          //--- Set sell arrow symbol
   PlotIndexSetInteger(2, PLOT_SHOW_DATA, true);     //--- Enable sell arrow display
   PlotIndexSetInteger(2, PLOT_DRAW_BEGIN, 0);       //--- Set sell draw begin
   PlotIndexSetInteger(2, PLOT_LINE_COLOR, 0, signal_sell_col); //--- Set sell color

   SetIndexBuffer(7, esa_signal, INDICATOR_CALCULATIONS);  //--- Bind signal ESA
   SetIndexBuffer(8, d_signal, INDICATOR_CALCULATIONS);    //--- Bind signal D
   SetIndexBuffer(9, ci_signal, INDICATOR_CALCULATIONS);   //--- Bind signal CI
   SetIndexBuffer(10, wt1_signal, INDICATOR_CALCULATIONS); //--- Bind signal WT1
   SetIndexBuffer(11, wt2_signal, INDICATOR_CALCULATIONS); //--- Bind signal WT2
   SetIndexBuffer(12, signal_hist, INDICATOR_CALCULATIONS); //--- Bind signal hist
   SetIndexBuffer(13, signal_bull_cross, INDICATOR_CALCULATIONS); //--- Bind bull cross
   SetIndexBuffer(14, signal_bear_cross, INDICATOR_CALCULATIONS); //--- Bind bear cross

   SetIndexBuffer(15, esa_trend, INDICATOR_CALCULATIONS); //--- Bind trend ESA
   SetIndexBuffer(16, d_trend, INDICATOR_CALCULATIONS);   //--- Bind trend D
   SetIndexBuffer(17, ci_trend, INDICATOR_CALCULATIONS);  //--- Bind trend CI
   SetIndexBuffer(18, wt1_trend, INDICATOR_CALCULATIONS); //--- Bind trend WT1
   SetIndexBuffer(19, wt2_trend, INDICATOR_CALCULATIONS); //--- Bind trend WT2
   SetIndexBuffer(20, trend_hist, INDICATOR_CALCULATIONS); //--- Bind trend hist
   SetIndexBuffer(21, trend_is_bull, INDICATOR_CALCULATIONS); //--- Bind trend bull
   SetIndexBuffer(22, trend_is_bear, INDICATOR_CALCULATIONS); //--- Bind trend bear

   return(INIT_SUCCEEDED);                                 //--- Return success
}

In der Ereignisbehandlung von OnInit konfigurieren wir die Eigenschaften des Indikators, indem wir mit IndicatorSetInteger die Anzeigestellen so einstellen, dass sie mit der Genauigkeit des Symbols übereinstimmen, und mit IndicatorSetString einen kurzen Namen für eine einfache Identifizierung in der Plattform zuweisen. Wir binden die preisbezogenen Puffer für Eröffnungs-, Höchst-, Tiefst- und Schlusskurse als Datentypen an die anfänglichen Plot-Indizes und fügen den Farbpuffer als Farbindex hinzu, um das dynamische Rendering von Kerzen zu unterstützen, während wir die Sichtbarkeit des Plots über PlotIndexSetInteger auf der Grundlage der Nutzerpräferenz steuern. Für die Plots der Pfeile verknüpfen wir die Puffer der Kauf- und Verkaufspfeile mit ihren jeweiligen Indizes, definieren Pfeilsymbole mit spezifischen Codes wie 233 für Käufe und 234 für Verkäufe, aktivieren die Anzeige ab dem Start des Charts und wenden nutzerdefinierte Farben mit der Funktion „PlotIndexSetInteger“ an. Sie können alle Codes Ihrer Wahl aus dem MQL5 Wingdings wie unten beschrieben verwenden.

MQL5 WINGDINGS-CODES

Anschließend werden alle Berechnungspuffer für die Signal- und Trend-WaveTrend-Elemente – wie Glättungsdurchschnitte, Differenzen, Indizes, Linien, Histogramme, Kreuzungen und Trendindikatoren – als nicht sichtbare Berechnungsdaten mit höheren Indizes verknüpft. Zum Abschluss geben wir INIT_SUCCEEDED zurück, um zu signalisieren, dass die Initialisierung ohne Probleme abgeschlossen wurde. Jetzt müssen wir unsere Berechnungen nur noch bei Bedarf bei jedem Tick durchführen. Zunächst werden die Puffer für alle Balken initialisiert, wenn der Indikator zum ersten Mal für alle Kerzen geladen wird, und später werden dann die erforderlichen Berechnungen durchgeführt.

//+------------------------------------------------------------------+
//| Calculate indicator values                                       |
//+------------------------------------------------------------------+
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[]) {
   int start = prev_calculated - 1;               //--- Set start index
   if (start < 0) start = 0;                      //--- Adjust invalid start

   if (prev_calculated == 0) {                    //--- Handle initial calculation
      ArrayInitialize(esa_signal, EMPTY_VALUE);   //--- Init signal ESA
      ArrayInitialize(d_signal, EMPTY_VALUE);     //--- Init signal D
      ArrayInitialize(ci_signal, EMPTY_VALUE);    //--- Init signal CI
      ArrayInitialize(wt1_signal, EMPTY_VALUE);   //--- Init signal WT1
      ArrayInitialize(wt2_signal, EMPTY_VALUE);   //--- Init signal WT2
      ArrayInitialize(signal_hist, EMPTY_VALUE);  //--- Init signal hist
      ArrayInitialize(signal_bull_cross, 0);      //--- Init bull cross
      ArrayInitialize(signal_bear_cross, 0);      //--- Init bear cross

      ArrayInitialize(esa_trend, EMPTY_VALUE);    //--- Init trend ESA
      ArrayInitialize(d_trend, EMPTY_VALUE);      //--- Init trend D
      ArrayInitialize(ci_trend, EMPTY_VALUE);     //--- Init trend CI
      ArrayInitialize(wt1_trend, EMPTY_VALUE);    //--- Init trend WT1
      ArrayInitialize(wt2_trend, EMPTY_VALUE);    //--- Init trend WT2
      ArrayInitialize(trend_hist, EMPTY_VALUE);   //--- Init trend hist
      ArrayInitialize(trend_is_bull, 0);          //--- Init trend bull
      ArrayInitialize(trend_is_bear, 0);          //--- Init trend bear

      ArrayInitialize(buyArrowBuf, EMPTY_VALUE);  //--- Init buy arrows
      ArrayInitialize(sellArrowBuf, EMPTY_VALUE); //--- Init sell arrows
   }

   return(rates_total);                           //--- Return total rates
}

In der Ereignisbehandlung von OnCalculate erhalten wir Parameter wie die Gesamtzahl der Balken, zuvor berechnete Balken und Arrays für Zeit, Preise, Volumen und Spreads, um neue Daten effizient zu verarbeiten. Wir bestimmen den Startindex für die Berechnungen, indem wir ihn um eins kleiner als den zuvor berechneten Wert setzen, und passen ihn auf Null an, wenn er unter Null fällt, um eine ungültige Indexierung zu vermeiden.

Wenn der Indikator zum ersten Mal geladen oder von Grund auf neu berechnet wird, was dadurch angezeigt wird, dass prev_calculated gleich Null ist, werden alle Puffer mit ArrayInitialize initialisiert: Signal- und Trendkomponentenpuffer wie die für die exponentielle Glättung, Differenzen, Indizes, Linien und Histogramme werden auf EMPTY_VALUE gesetzt, die Puffer der Flags für Kreuzungen und Trend auf Null und Pfeilpuffer auf „EMPTY_VALUE“ für einen sauberen Start. Schließlich geben wir die Gesamtzahl der Balken zurück, um das Ende dieses Berechnungszyklus zu signalisieren. Wir können nun eine Schleife über die Balken ziehen und unsere Berechnungen durchführen. Hier ist der Ansatz, den wir verwendet haben, um dies zu erreichen.

for (int i = start; i < rates_total; i++) {   //--- Loop through bars
   double src_wt = (high[i] + low[i] + close[i]) / 3.0; //--- Compute source

   if (i == 0) {                               //--- Handle first bar signal
      esa_signal[i] = src_wt;                  //--- Set initial ESA
      d_signal[i] = MathAbs(src_wt - esa_signal[i]); //--- Set initial D
      double denom_signal = 0.015 * d_signal[i]; //--- Compute denominator
      ci_signal[i] = (denom_signal != 0.0) ? (src_wt - esa_signal[i]) / denom_signal : 0.0; //--- Set CI
      wt1_signal[i] = ci_signal[i];            //--- Set initial WT1
      wt2_signal[i] = wt1_signal[i];           //--- Set initial WT2
   } else {                                    //--- Handle subsequent bars
      esa_signal[i] = CalcEMA(esa_signal[i-1], src_wt, wt_channel_len); //--- Update ESA
      d_signal[i] = CalcEMA(d_signal[i-1], MathAbs(src_wt - esa_signal[i]), wt_channel_len); //--- Update D
      double denom_signal = 0.015 * d_signal[i]; //--- Compute denominator
      ci_signal[i] = (denom_signal != 0.0) ? (src_wt - esa_signal[i]) / denom_signal : 0.0; //--- Update CI
      wt1_signal[i] = CalcEMA(wt1_signal[i-1], ci_signal[i], wt_average_len); //--- Update WT1
      wt2_signal[i] = 0;                       //--- Reset WT2
      int cnt_ma = 0;                          //--- Init MA count
      for (int k = 0; k < wt_ma_len; k++) {    //--- Loop for MA
         if (i - k < 0) break;                 //--- Skip invalid index
         wt2_signal[i] += wt1_signal[i - k];   //--- Accumulate WT1
         cnt_ma++;                             //--- Increment count
      }
      if (cnt_ma > 0) wt2_signal[i] /= cnt_ma; //--- Average WT2
   }

   signal_hist[i] = wt1_signal[i] - wt2_signal[i]; //--- Compute signal hist
   signal_bull_cross[i] = (wt1_signal[i] > wt2_signal[i] && (i == 0 || wt1_signal[i-1] <= wt2_signal[i-1])) ? 1 : 0; //--- Detect bull cross
   signal_bear_cross[i] = (wt1_signal[i] < wt2_signal[i] && (i == 0 || wt1_signal[i-1] >= wt2_signal[i-1])) ? 1 : 0; //--- Detect bear cross

   double src_wt_trend = src_wt;              //--- Set trend source
   if (i == 0) {                              //--- Handle first bar trend
      esa_trend[i] = src_wt_trend;            //--- Set initial ESA
      d_trend[i] = MathAbs(src_wt_trend - esa_trend[i]); //--- Set initial D
      double denom_trend = 0.015 * d_trend[i]; //--- Compute denominator
      ci_trend[i] = (denom_trend != 0.0) ? (src_wt_trend - esa_trend[i]) / denom_trend : 0.0; //--- Set CI
      wt1_trend[i] = ci_trend[i];             //--- Set initial WT1
      wt2_trend[i] = wt1_trend[i];            //--- Set initial WT2
   } else {                                   //--- Handle subsequent bars
      esa_trend[i] = CalcEMA(esa_trend[i-1], src_wt_trend, wt_trend_channel_len); //--- Update ESA
      d_trend[i] = CalcEMA(d_trend[i-1], MathAbs(src_wt_trend - esa_trend[i]), wt_trend_channel_len); //--- Update D
      double denom_trend = 0.015 * d_trend[i]; //--- Compute denominator
      ci_trend[i] = (denom_trend != 0.0) ? (src_wt_trend - esa_trend[i]) / denom_trend : 0.0; //--- Update CI
      wt1_trend[i] = CalcEMA(wt1_trend[i-1], ci_trend[i], wt_trend_average_len); //--- Update WT1
      wt2_trend[i] = 0;                       //--- Reset WT2
      int cnt_ma_trend = 0;                   //--- Init MA count
      for (int k = 0; k < wt_trend_ma_len; k++) { //--- Loop for MA
         if (i - k < 0) break;                //--- Skip invalid index
         wt2_trend[i] += wt1_trend[i - k];    //--- Accumulate WT1
         cnt_ma_trend++;                      //--- Increment count
      }
      if (cnt_ma_trend > 0) wt2_trend[i] /= cnt_ma_trend; //--- Average WT2
   }

   trend_hist[i] = wt1_trend[i] - wt2_trend[i]; //--- Compute trend hist
   trend_is_bull[i] = (wt1_trend[i] > wt2_trend[i]) ? 1 : 0; //--- Set bull trend
   trend_is_bear[i] = (wt1_trend[i] < wt2_trend[i]) ? 1 : 0; //--- Set bear trend

   if (color_candles) {                       //--- Check candle coloring
      openBuf[i] = open[i];                   //--- Set open
      highBuf[i] = high[i];                   //--- Set high
      lowBuf[i] = low[i];                     //--- Set low
      closeBuf[i] = close[i];                 //--- Set close
      candleColorBuf[i] = trend_is_bull[i] == 1 ? 0 : 1; //--- Set color index
   }

   buyArrowBuf[i] = EMPTY_VALUE;              //--- Reset buy arrow
   sellArrowBuf[i] = EMPTY_VALUE;             //--- Reset sell arrow
   if (signal_bull_cross[i] == 1 && (!use_trend_filter || trend_is_bull[i] == 1)) { //--- Check buy condition
      buyArrowBuf[i] = low[i] - _Point * base_offset; //--- Place buy arrow
   }
   if (signal_bear_cross[i] == 1 && (!use_trend_filter || trend_is_bear[i] == 1)) { //--- Check sell condition
      sellArrowBuf[i] = high[i] + _Point * base_offset; //--- Place sell arrow
   }
}

Hier wird jeder Balken vom Startindex bis zu den verfügbaren Gesamtkursen in einer Schleife durchlaufen, wobei der Ausgangskurs als Durchschnitt des Höchst-, Tiefst- und Schlusskurses für diesen Balken berechnet wird, um als Eingabe für beide WaveTrend-Oszillatoren zu dienen.

Für das Signal WaveTrend initialisieren wir beim ersten Balken den exponentiellen Glättungsdurchschnitt direkt aus der Quelle, berechnen die absolute Differenz für den Abweichungspuffer mit der Funktion MathAbs, leiten einen Nenner ab, indem wir die Abweichung mit 0,015 multiplizieren, und setzen den Kanalindex, indem wir die Differenz zwischen Quelle und Glättungsdurchschnitt durch den Nenner dividieren, falls sie ungleich Null ist, andernfalls Null; dann setzen wir beide Hauptlinien auf den Kanalindexwert. Für die nachfolgenden Balken aktualisieren wir den exponentiell glättenden Durchschnitt mit „CalcEMA“ mit dem vorherigen Wert, der Quelle und der Kanallänge, aktualisieren die Abweichung mit einem weiteren „CalcEMA“ auf die absolute Differenz von „MathAbs“ und derselben Länge, berechnen den Nenner neu, aktualisieren den Kanalindex auf ähnliche Weise, glätten die erste Hauptzeile mit „CalcEMA“ unter Verwendung der Durchschnittslänge und berechnen die zweite Hauptzeile durch Mittelwertbildung der jüngsten Werte der ersten Zeile über die Länge des gleitenden Durchschnitts mittels einer verschachtelten Schleife, die akkumuliert und dividiert und dabei ungültige Indizes überspringt.

Wir leiten dann das Signalhistogramm als Differenz zwischen den beiden Hauptsignallinien ab, markieren ein Aufwärtskreuzen, wenn die erste Linie über der zweiten liegt und nicht im vorherigen Balken (oder auf dem ersten Balken) war, und markieren ein Abwärtskreuzen, wenn die erste Linie unter der zweiten liegt, aber vorher nicht. Wenn wir einen ähnlichen Vorgang für den Trend „WaveTrend“ unter Verwendung derselben Quelle wiederholen, behandeln wir den ersten Balken, indem wir dessen exponentielle Glättung, die Abweichung mit „MathAbs“, den Nenner, den Kanalindex und beide Hauptlinien identisch initialisieren, verwenden für die nachfolgenden Balken jedoch trendspezifische Parameter: Aktualisierung über „CalcEMA“ mit Trendkanal- und Durchschnittslängen sowie Mittelwertbildung der zweiten Linie über die Länge des Trend-Gleitenden Durchschnitts in einer verschachtelten Schleife. Wir berechnen das Trendhistogramm als Differenz zwischen seinen Hauptlinien und setzen das Flag für einen Aufwärtstrend auf 1, wenn die erste Linie über der zweiten liegt, und das Flag für einen Abwärtstrend auf 1, wenn sie darunter liegt.

Wenn die Kerzeneinfärbung aktiviert ist, kopieren wir die Eröffnungs-, Höchst-, Tiefst- und Schlusswerte des Balkens in ihre Puffer und weisen ihnen einen Farbindex von 0 für einen Aufwärtstrend oder 1 für einen Abwärtstrend zu. Abschließend setzen wir die Puffer für die Kauf- und Verkaufspfeile auf EMPTY_VALUE zurück und platzieren dann einen Kaufpfeil unterhalb des Tiefs, und zwar um den Basis-Offset multipliziert mit _Point, sofern ein Aufwärtskreuz aufgetreten ist und entweder kein Trendfilter verwendet wird oder der Trend nach oben zeigt; ebenso platzieren wir einen Verkaufspfeil oberhalb des Hochs, wenn unter den entsprechenden Bedingungen ein Abwärtskreuz aufgetreten ist. Nach dem Kompilieren erhalten wir folgendes Ergebnis:

FINALER TEST GIF

Anhand der Visualisierung können wir sehen, dass wir den Indikator berechnen, die farbigen Kerzen zeichnen und die Puffer für den Indikator festlegen und somit unsere Ziele erreichen. Bleibt nur noch der Backtest des Programms, und das wird im nächsten Abschnitt behandelt.


Backtests

Wir haben die Tests durchgeführt, und unten sehen Sie die kompilierte Visualisierung in einem einzigen Graphics Interchange Format (GIF) Bitmap-Bildformat.

BACKTEST GIF


Schlussfolgerung

Zusammenfassend haben wir den Indikator Smart WaveTrend Crossover in MQL5 entwickelt, der zwei WaveTrend-Oszillatoren verwendet – einen für Kreuzungssignale und einen anderen für die Trendfilterung – mit anpassbaren Parametern für Kanal-, Durchschnitts- und gleitende Durchschnittslängen. Der Indikator färbt Kerzen trendbasiert ein, generiert Kauf- und Verkaufspfeil-Signale bei Überkreuzungen und enthält Optionen zur Trendbestätigung sowie visuelle Anpassungen wie Farben und Offsets. Diese Konfiguration liefert visuelle Indikatoren für Momentumverschiebungen, die mit breiteren Trends übereinstimmen.

Im nächsten Teil werden wir den Indikator mit fortschrittlichen visuellen Elementen erweitern, darunter Signalboxen, Nebelüberlagerungen zur Trendvisualisierung, anpassbare Kauf-/Verkaufsmarkierungen und integrierte Take-Profit- und Stop-Loss-Anzeigen für ein besseres Risikomanagement, wie unten dargestellt. Bleiben Sie dran.

FORTSCHRITTLICHE VERGLEICHSFUNKTIONEN

Übersetzt aus dem Englischen von MetaQuotes Ltd.
Originalartikel: https://www.mql5.com/en/articles/20811

Die Übertragung der Trading-Signale in einem universalen Expert Advisor. Die Übertragung der Trading-Signale in einem universalen Expert Advisor.
In diesem Artikel wurden die verschiedenen Möglichkeiten beschrieben, um die Trading-Signale von einem Signalmodul des universalen EAs zum Steuermodul der Positionen und Orders zu übertragen. Es wurden die seriellen und parallelen Interfaces betrachtet.
Python-MetaTrader 5 Strategie-Tester (Teil 02): Umgang mit Balken, Ticks und Überladung eingebauter Funktionen in einem Simulator Python-MetaTrader 5 Strategie-Tester (Teil 02): Umgang mit Balken, Ticks und Überladung eingebauter Funktionen in einem Simulator
In diesem Artikel stellen wir Funktionen vor, die denen des Moduls Python-MetaTrader 5 ähneln und einen Simulator mit einer vertrauten Schnittstelle und einer eigenen Art der internen Handhabung von Balken und Ticks bieten.
Eine alternative Log-datei mit der Verwendung der HTML und CSS Eine alternative Log-datei mit der Verwendung der HTML und CSS
In diesem Artikel werden wir eine sehr einfache, aber leistungsfähige Bibliothek zur Erstellung der HTML-Dateien schreiben, dabei lernen wir auch, wie man eine ihre Darstellung einstellen kann (nach seinem Geschmack) und sehen wir, wie man es leicht in seinem Expert Advisor oder Skript hinzufügen oder verwenden kann.
Larry Williams Marktgeheimnisse (Teil 5): Automatisieren der Strategie des Volatilitätsausbruchs in MQL5 Larry Williams Marktgeheimnisse (Teil 5): Automatisieren der Strategie des Volatilitätsausbruchs in MQL5
Dieser Artikel zeigt, wie man die Volatilitätsausbruchsstrategie von Larry Williams in MQL5 mit einem praktischen, schrittweisen Ansatz automatisieren kann. Sie lernen, wie Sie die tägliche Ausweitung der Spannweite berechnen, Kauf- und Verkaufsniveaus ableiten, Risiken mit Range-basierten Stopps und Ertrags-basierten Zielen managen und einen professionellen Expert Advisor für MetaTrader 5 aufbauen. Entwickelt für Händler und Entwickler, die die Marktkonzepte von Larry Williams in ein vollständig testbares und einsatzfähiges automatisiertes Handelssystem umwandeln möchten.