Erstellen von nutzerdefinierten Indikatoren in MQL5 (Teil 4): Smart WaveTrend Crossover mit zwei Oszillatoren
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:
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.

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.

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.

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:

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.

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.

Übersetzt aus dem Englischen von MetaQuotes Ltd.
Originalartikel: https://www.mql5.com/en/articles/20811
Warnung: Alle Rechte sind von MetaQuotes Ltd. vorbehalten. Kopieren oder Vervielfältigen untersagt.
Dieser Artikel wurde von einem Nutzer der Website verfasst und gibt dessen persönliche Meinung wieder. MetaQuotes Ltd übernimmt keine Verantwortung für die Richtigkeit der dargestellten Informationen oder für Folgen, die sich aus der Anwendung der beschriebenen Lösungen, Strategien oder Empfehlungen ergeben.
Die Übertragung der Trading-Signale in einem universalen Expert Advisor.
Python-MetaTrader 5 Strategie-Tester (Teil 02): Umgang mit Balken, Ticks und Überladung eingebauter Funktionen in einem Simulator
Eine alternative Log-datei mit der Verwendung der HTML und CSS
Larry Williams Marktgeheimnisse (Teil 5): Automatisieren der Strategie des Volatilitätsausbruchs in MQL5
- Freie Handelsapplikationen
- Über 8.000 Signale zum Kopieren
- Wirtschaftsnachrichten für die Lage an den Finanzmärkte
Sie stimmen der Website-Richtlinie und den Nutzungsbedingungen zu.