Aufbau eines professionellen Handelssystems mit Heikin Ashi (Teil 1): Entwickeln eines nutzerdefinierten Indikators
Einführung
Technische Indikatoren sind das Herzstück des algorithmischen Handels, und die Beherrschung ihrer Erstellung ist ein wichtiger Schritt auf dem Weg eines jeden MQL5-Entwicklers. In diesem Artikel werden wir die Entwicklung von nutzerdefinierten Indikatoren praktisch angehen, indem wir eines der beliebtesten Tools zur Trendglättung erstellen: den Heikin Ashi Chart.
Dieser Artikel ist der erste Teil einer zweiteiligen Serie. Im ersten Teil werden wir die Theorie hinter den Heikin Ashi-Kerzen erforschen, erklären, wie sie berechnet werden, und Schritt für Schritt den Prozess der Codierung eines Heikin Ashi-Indikators von Grund auf mit MQL5 durchgehen. Ziel ist es nicht, einfach nur den Quellcode zu kopieren, sondern wirklich zu verstehen, warum jede Zeile des Quellcodes so ist, dass Sie diese bewährten Verfahren bei der Erstellung Ihrer nutzerdefinierten Indikatoren anwenden können.
Im zweiten Teil werden wir einen Expert Advisor entwickeln, der den Heikin Ashi-Indikator als Grundlage für seine Ein- und Ausstiege verwendet. Beginnen wir damit, zu verstehen, was Heikin Ashi-Charts von traditionellen Kerzen-Charts unterscheidet.
Bevor wir beginnen, sollten Sie sich mit einigen Dingen vertraut machen. Dieser Artikel geht davon aus, dass Sie:
- über solide Kenntnisse der Programmiersprache MQL5 verfügen.
- wissen, wie man MetaTrader 5 und MetaEditor 5 nutzt,
- Indikatoren und Expert Advisors an ein Chart anhängen können.
Wie unterscheidet sich Heikin Ashi von den traditionellen Kerzen?
Betrachten wir zunächst zwei Charts nebeneinander.
Ein traditionelles Kerzen-Chart:

Ein Heikin-Ashi-Chart, der aus denselben Kursdaten erstellt wurde:

Beide Charts basieren auf der jüngsten Goldpreisentwicklung. Achten Sie beim Vergleich darauf, wie sich die Kerzen in Trendphasen verhalten. Während herkömmliche Kerzen-Charts mit jedem neuen Kursbalken aktualisiert werden, können sie oft unruhig erscheinen und häufig die Farbe wechseln, selbst wenn der Gesamttrend intakt bleibt. Dies kann zu visuellem Rauschen führen und es schwieriger machen, die Dynamik auf einen Blick zu erfassen. Im Gegensatz dazu werden Heikin-Ashi-Kerzen anhand geglätteter Werte berechnet, was zu einem wesentlich saubereren Erscheinungsbild führt. In Trendphasen druckt der Heikin Ashi in der Regel lange Sequenzen gleichfarbiger Kerzen, was die visuelle Identifizierung von Auf- und Abwärtstrends erleichtert.
Einfach ausgedrückt: Heikin-Ashi-Kerzen bleiben während Aufwärtstrends grün und während Abwärtstrends rot und filtern so kleinere Rücksetzer heraus, die auf einem traditionellen Chart wie Umkehrungen aussehen könnten. Dies macht sie zu einem leistungsstarken Instrument für Händler, die dem Trend folgen und vorübergehende Schwankungen ignorieren wollen.
Im nächsten Abschnitt werden wir genau aufschlüsseln, wie diese Kerzen berechnet werden.
Wie Heikin Ashi-Kerzen berechnet werden
Bevor wir uns ansehen, wie Heikin Ashi-Kerzen berechnet werden, sollten wir zunächst verstehen, was sie von normalen Kerzen unterscheidet. In einem traditionellen Kerzen-Chart wird jede Kerze anhand von vier Schlüsselwerten des Marktes gebildet.

- Der Eröffnungskurs ist der Kurs zu Beginn der Kerzenbildung.
- Das Hoch ist der höchste während der Kerzenbildung erreichte Kurs.
- Das Tief ist der niedrigste erreichte Preis.
- Der Schlusskurs ist der endgültige Preis am Ende der Kerzenformation.
Diese Werte spiegeln direkt wider, was während eines bestimmten Zeitraums, z. B. einer Stunde oder eines Tages, geschehen ist. Die Heikin Ashi Kerzen verwenden diese Rohwerte jedoch nicht direkt. Stattdessen verwenden sie einen modifizierten Satz von Werten, um das Chart zu glätten und den allgemeinen Trend deutlicher hervorzuheben.
Die Heikin Ashi-Kerze besteht aus
- Der Heikin Ashi-Eröffnung, die eine geglättete Version der Kerzen-Eröffnung ist.
- Der Heikin Ashi-Hoch, d. h. der höchste Wert aus einem Satz, der sowohl reale als auch geglättete Daten enthält.
- Der Heikin Ashi-Tief, der den niedrigsten Wert der gleichen Art von Menge darstellt.
- Der Heikin Ashi-Schlusskurs, der dem Durchschnittspreis der aktuellen realen Kerze entspricht.
Lassen Sie uns nun genau untersuchen, wie jeder dieser Heikin Ashi Kerzen-Werte berechnet wird.
Die Heikin Ashi-Formeln
Heikin Ashi close = (Open + High + Low + Close) / 4.
Dies ist der Durchschnittspreis der aktuellen Kerze. Sie liefert eine einzige ausgewogene Zahl, die die gesamte Preisentwicklung in diesem Zeitraum widerspiegelt.
Heikin Ashi open = (open der vorherige Heikin Ashi Kerze + close der vorherige Heikin Ashi Kerze) / 2.
Das bedeutet, dass die Eröffnung der aktuellen Heikin Ashi-Kerze nicht die aktuelle Markteröffnung ist, sondern der Mittelpunkt des Körpers der vorherigen Heikin Ashi-Kerze, was dazu beiträgt, die Übergänge zwischen den Kerzen zu glätten und das „fließende“ Aussehen zu erzeugen.
Heikin Ashi high = Der höchste Wert aus High, Heikin Ashi open und Heikin Ashi close.
Heikin Ashi low = Der niedrigste Wert aus Low, Heikin Ashi open und Heikin Ashi close.
Die beiden vorherigen Werte kombinieren die tatsächlichen Hochs und Tiefs des Marktes mit den geglätteten Eröffnungs- und Schlusskursen, wodurch die Kerze realistische Dochte erhält und dennoch von der Glättung profitiert.
Kurz gesagt:
Der Heikin Ashi-Schlusskurs spiegelt den Durchschnitt der Kurse der aktuellen Kerze wider. Die Eröffnung des Heikin Ashi hängt von den geglätteten Werten der vorherigen Kerze ab. Heikin Ashi High/Heikin Ashi Low verwenden sowohl rohe als auch geglättete Werte, um Kursextreme darzustellen.
Diese Berechnungsmethode verleiht den Heikin Ashi-Kerzen ihr klares, trendorientiertes Aussehen. In der Regel bleiben sie in Aufwärtstrends grün und in Abwärtstrends rot, wodurch kurzfristige Schwankungen, die das Auge verwirren können, herausgefiltert werden.
In den nächsten Abschnitten werden wir dieses Wissen in die Tat umsetzen, indem wir Schritt für Schritt einen nutzerdefinierten Heikin Ashi-Indikator mit MQL5 erstellen.
Planung der Indikatorlogik
Bevor wir uns in den Code stürzen, ist es wichtig, die Logik unseres Heikin Ashi-Indikators kurz zu erläutern. Stellen wir zunächst fest, dass wir einen nutzerdefinierten Indikator erstellen werden, der Heikin Ashi-Kerzen direkt auf dem Chart anzeigt, genau wie normale Kerzen, jedoch auf der Grundlage geglätteter Preisberechnungen.
Zusätzlich zur Standardvisualisierung werden wir das Heikin Ashi-Chart verbessern, indem wir die Kerzen in drei verschiedenen Farben einfärben: eine für Aufwärtskerzen, eine für Abwärtskerzen und eine dritte für neutrale Kerzen.
Um diese visuelle Logik zu unterstützen, wird unser Indikator fünf Puffer verwenden:
- Heikin Ashi Open. Dies ist ein Array, das die Heikin Ashi-Eröffnungskurse speichert.
- Heikin Ashi High. Dies ist ein Array, das die Heikin Ashi-Hochs speichert.
- Heikin Ashi Low. Dies ist ein Array, das die Heikin Ashi-Tiefs speichert.
- Heikin Ashi Close. Dies ist ein Array, das die Heikin Ashi-Schlusskurse speichert.
- Color Buffer. Dies ist ein Array, das den Farbindex für jede Heikin Ashi-Kerze speichert, je nachdem, ob sie aufwärts, abwärts oder neutral ist.
Jeder Puffer spielt eine wesentliche Rolle bei der Darstellung der Heikin Ashi-Kerze und der dynamischen Anwendung der entsprechenden Farbe.
Um diese Kerzen zu rendern, werden wir eine einzelne grafische Darstellung mit DRAW_COLOR_CANDLES verwenden, die es uns ermöglicht, eine Darstellungs-ID und einen separaten Puffer für die Farbgebung zu verwenden. Eine Sache, die wir sorgfältig behandeln müssen, ist der allererste Balken im Chart, da er keinen vorherigen Heikin Ashi-Wert hat. Wir werden die Werte für den ersten Balken manuell initialisieren, damit der Rest der Kerzen genau auf der Grundlage der richtigen Formel erstellt werden kann.
Um den Code sauber und anfängerfreundlich zu halten, werden wir ihn in einem modularen Programmierstil verfassen und die wichtigsten Teile der Logik in kleine, überschaubare Funktionen aufteilen. Dadurch wird der Code leichter zu verstehen, zu debuggen und in zukünftigen Tutorials zu erweitern sein.
Schritt-für-Schritt-Anleitung zur Kodierung des Heikin Ashi-Indikators
Zur Erstellung unseres nutzerdefinierten Heikin Ashi-Indikators werden wir die Programmiersprache MQL5 verwenden. Dies ist die Muttersprache für die Entwicklung automatisierter Handelswerkzeuge auf MetaTrader 5.
Da wir davon ausgehen, dass die Leser bereits wissen, wie man mit MetaTrader 5, MetaEditor und den Grundlagen des Anhängens von Indikatoren an ein Chart arbeitet, überspringen wir die Details der Einrichtung und gehen direkt zur Entwicklung unseres nutzerdefinierten Heikin Ashi-Indikators über.
Im Folgenden finden Sie den anfänglichen Standardcode, mit dem wir als Grundlage für die Erstellung unseres nutzerdefinierten Indikators arbeiten werden. Von diesem Ausgangspunkt aus werden wir Schritt für Schritt die Kernlogik implementieren.
//+------------------------------------------------------------------+ //| heikinAshiIndicator.mq5 | //| Copyright 2025, MetaQuotes Ltd. | //| https://www.mql5.com/en/users/chachaian | //+------------------------------------------------------------------+ #property copyright "Copyright 2025, MetaQuotes Ltd." #property link "https://www.mql5.com/en/users/chachaian" #property version "1.00" //+------------------------------------------------------------------+ //| Custom indicator initialization function | //+------------------------------------------------------------------+ int OnInit() { return INIT_SUCCEEDED; } //+------------------------------------------------------------------+ //| Custom indicator iteration function | //+------------------------------------------------------------------+ int OnCalculate(const int32_t rates_total, const int32_t 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 int32_t &spread []) { return(rates_total); } //+------------------------------------------------------------------+
Wir wollen diesen Code in einfache, logische Teile zerlegen, damit er leichter zu verstehen ist.
Property Direktiven:
... #property copyright "Copyright 2025, MetaQuotes Ltd. Developer is Chacha Ian" #property link "https://www.mql5.com/en/users/chachaian" #property version "1.00" ...
Diese Zeilen enthalten Meta-Informationen über den Indikator, die im MetaTrader 5-Navigator und im Beschreibungsfenster angezeigt werden.
- #property copyright. Damit wird der Inhaber des Urheberrechts und der Entwickler angegeben. Gut für das Branding und das Eigentum am Code
- #property link. Dies bietet einen klickbaren Link zu Ihrer Projektdatei oder Projektseite.
- #property version. Dies ist hilfreich für die Versionskontrolle bei zukünftigen Aktualisierungen.
... //+------------------------------------------------------------------+ //| Custom indicator initialization function | //+------------------------------------------------------------------+ int OnInit() { return(INIT_SUCCEEDED); } ...
Die Initialisierungsfunktion (OnInit()) wird nur einmal aufgerufen, wenn der Indikator zum ersten Mal mit einem Chart verbunden wird. Sie dient zur Abwicklung von Einrichtungs- und Konfigurationsaufgaben. Rückgabe des Signals INIT_SUCCEEDED an MetaTrader 5, dass der Indikator erfolgreich geladen wurde. In den folgenden Schritten werden wir OnInit() verwenden, um Indikatorpuffer zu registrieren und die visuellen Eigenschaften unserer Charts zu definieren.
Die Funktion OnCalculate:
... //+------------------------------------------------------------------+ //| Custom indicator iteration function | //+------------------------------------------------------------------+ int OnCalculate(const int32_t rates_total, const int32_t 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 int32_t &spread[]) { return(rates_total); } ...
Dies ist das Herzstück Ihres Indikators. Er wird immer wieder aufgerufen, wenn sich neue Kerzen bilden. Seine Parameter ermöglichen den Zugang zu:
- rates_total. Dies bezieht sich auf die insgesamt verfügbaren Balken.
- prev_calculated. Dies ist die Anzahl der Balken, die beim letzten Aufruf der Funktion 'OnCalculate' (zur Optimierung) verarbeitet wurden.
- Die Variablen time, open, high, low und close, die im Wesentlichen unsere historischen Preiszeitreihen darstellen.
- Volume, tick_volume, and spread, die zusätzliche Handelsdaten darstellen.
Ein wichtiger Punkt ist, dass die Funktion „OnCalculate“ automatisch aufgerufen wird, wenn neue Tickdaten eintreffen oder wenn historische Daten aktualisiert werden. Zum Beispiel, wenn es eine Lücke in den historischen Daten gibt und diese gefüllt wird. Hier wird der größte Teil der Logik unseres Indikators angesiedelt sein.
Im MetaTrader 5 können Indikatoren auf zwei Arten angezeigt werden: entweder direkt im Fenster des Haupt-Charts (über den Preiskerzen) oder in einem separaten Unterfenster unterhalb des Haupt-Charts.
Um festzulegen, wie ein Indikator angezeigt werden soll, verwenden wir entweder #property indicator_chart_window für Chart-Overlays oder #property indicator_sub_window für auf Unterfenstern basierende Indikatoren. Da wir einen Indikator erstellen, der den Preis überlagert, soll er direkt im Haupt-Chart erscheinen.
In diesem Sinne fügen wir die folgende Zeile direkt unter den bestehenden #property-Anweisungen ein:
#property copyright "Copyright 2025, MetaQuotes Ltd. Developer is Chacha Ian" #property link "https://www.mql5.com/en/users/chachaian" #property version "1.00" #property indicator_chart_window ...
In MQL5 speichern nutzerdefinierte Indikatoren ihre berechneten Werte normalerweise in speziellen dynamischen Arrays, den sogenannten Indikatorpuffern. Diese Puffer werden mit einer eingebauten MQL5-Funktion namens SetIndexBuffer() registriert und sind für die Visualisierung von Daten im Chart, wie das Zeichnen von Linien, Histogrammen, Pfeilen und mehr, unerlässlich.
Bevor Sie sie in Ihrem Quellcode verwenden können, müssen Sie angeben, wie viele Puffer Ihr Indikator verwenden soll. Dies geschieht mit der Direktive #property indicator_buffers N. Dabei steht N für die Gesamtzahl der Puffer, die Ihr Indikator benötigt. In unserem Fall werden wir 5 Puffer verwenden, also deklarieren wir sie wie folgt: #property indicator_buffers 5.
Fügen wir die folgende Zeile direkt unter den anderen vorhandenen Direktiven in unseren Quellcode ein.
... #property indicator_chart_window #property indicator_buffers 5 ...
Indikatoren verwenden in der Regel grafische Konstruktionen, um ihre Berechnungen visuell im Chart darzustellen. Diese visuellen Elemente, wie Linien, Histogramme oder Pfeile, werden auch als grafische Darstellungen bezeichnet. Sie helfen den Händlern, die Entwicklung des Indikators auf einen Blick zu erfassen.
Die Anzahl der grafischen „Plots“, die ein Indikator anzeigt, wird mit der Direktive #property indicator_plots P festgelegt, wobei P die Anzahl der Grafik-Plots ist, die der Indikator anzeigen soll. In unserem Fall arbeiten wir nur mit einem einzigen grafischen „Plot“.
Fügen wir die folgende Codezeile direkt unter den anderen vorhandenen #property-Direktiven ein.
... #property indicator_chart_window #property indicator_buffers 5 #property indicator_plots 1 ...
Als Nächstes werden die Arrays deklariert, die als Indikatorpuffer fungieren sollen. Normalerweise wird dies im globalen Bereich durchgeführt. Der Datentyp dieser Arrays ist fast immer auf double eingestellt, da Indikatorwerte in der Regel Fließkommazahlen sind. Wir deklarieren sie jetzt direkt über der Funktion OnInit(), damit sie im gesamten Code des Indikators verfügbar sind.
... #property indicator_plots 1 //Global variables double haOpen []; double haHigh []; double haLow []; double haClose[]; double colorBuffer []; ...
Nun, da wir unsere Arrays deklariert haben, besteht der nächste Schritt darin, jedes einzelne von ihnen als Indikatorpuffer zu registrieren, indem wir die eingebaute MQL5-Funktion SetIndexBuffer() verwenden. Diese Funktion verknüpft unsere deklarierten Arrays mit dem Indikatorensystem, sodass MetaTrader sie für Zeichnungen und Berechnungen verwenden kann. Wir werden dies innerhalb der Funktion OnInit() tun und jedes Array einzeln registrieren.
... //+------------------------------------------------------------------+ //| Custom indicator initialization function | //+------------------------------------------------------------------+ int OnInit() { // Registration of indicator buffers if(!SetIndexBuffer(0, haOpen, INDICATOR_DATA)){ Print("Error while registering an indicator buffer: ", GetLastError()); return INIT_FAILED; } if(!SetIndexBuffer(1, haHigh, INDICATOR_DATA)){ Print("Error while registering an indicator buffer: ", GetLastError()); return INIT_FAILED; } if(!SetIndexBuffer(2, haLow, INDICATOR_DATA)){ Print("Error while registering an indicator buffer: ", GetLastError()); return INIT_FAILED; } if(!SetIndexBuffer(3, haClose, INDICATOR_DATA)){ Print("Error while registering an indicator buffer: ", GetLastError()); return INIT_FAILED; } if(!SetIndexBuffer(4, colorBuffer, INDICATOR_COLOR_INDEX)){ Print("Error while registering an indicator buffer: ", GetLastError()); return INIT_FAILED; } } ...
Als Nächstes wollen wir die Kernlogik unseres Indikators in der Funktion OnCalculate() einrichten. Diese Funktion wird jedes Mal aufgerufen, wenn der Indikator aktualisiert wird. Sei es, wenn er zum ersten Mal an ein Chart angehängt wird, sich eine neue Kerze bildet oder ein neuer Preistick eintrifft.
Wir strukturieren diese Funktion in der Regel in drei bedingte Hauptblöcke, um jedes dieser Szenarien effizient zu behandeln:
- Wenn der Indikator zum ersten Mal an das Chart angehängt wird, ist dies, wenn prev_calculated == 0. Wir verwenden diesen Block, um jegliche Initialisierungslogik auszuführen, wie z. B. das Eintragen von historischen Werten oder das Einrichten von Startbedingungen.
- Wenn eine neue Kerze geöffnet wird, ist dies der Fall, wenn prev_calculated != rates_total und prev_calculated != 0. Hier können Sie eine Logik ausführen, die nur einmal pro neuem Takt ausgeführt werden muss. Sie ist nützlich, um sich wiederholende Berechnungen zu reduzieren.
- Wenn ein neuer Kurs-Tick auf dem aktuellen Balken eintrifft, ist dies der Fall, wenn prev_calculated == rates_total. In diesem Block werden die Echtzeitwerte für die aktuelle Kerze anhand der neuesten Kursdaten aktualisiert.
Nachfolgend finden Sie die Grundstruktur, die wir in der Funktion OnCalculate() einbauen werden:
... //+------------------------------------------------------------------+ //| Custom indicator iteration function | //+------------------------------------------------------------------+ int OnCalculate(const int32_t rates_total, const int32_t 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 int32_t &spread[]) { // This block is executed when the indicator is initially attached on a chart if(prev_calculated == 0){ } // This block is executed on every new bar open if(prev_calculated != rates_total && prev_calculated != 0){ } // This block is executed on arrival of new price (tick) data if(prev_calculated == rates_total){ } return(rates_total); } ...
Als Nächstes werden wir eine Farbpalette für unsere Heikin Ashi-Kerzen definieren, indem wir die Direktive #property indicator_color1 verwenden. Mit dieser Direktive können wir die Standardfarben für unseren grafischen Chart festlegen, das wir zuvor definiert haben. Für unsere Indikatoren werden wir drei Farben verwenden:
- clrDarkGreen für steigende Heikin Ashi-Kerzen.
- clrDarkRed für fallende Heikin Ashi-Kerzen.
- clrYellow für neutrale Kerzen.
Wir fügen diese Farbeinstellung direkt unter unseren anderen #property-Direktiven ein, damit sie bei der Initialisierung des Indikators angewendet wird. Nachstehend finden Sie die hinzuzufügende Codezeile:
... #property indicator_plots 1 #property indicator_color1 clrDarkGreen, clrDarkRed, clrYellow ...
Wir werden nun eine Richtlinie hinzufügen, die jedem unserer Indikatorpuffer einen sprechenden Namen gibt. Dies hilft den Nutzern zu verstehen, was die einzelnen Linien darstellen, wenn sie Daten aus dem Datenfenster im MetaTrader betrachten. Fügen Sie die folgende Codezeile direkt unter den bestehenden #property-Direktiven ein:
... #property indicator_color1 clrDarkGreen, clrDarkRed, clrYellow #property indicator_label1 "HeikinAshiOpen;HeikinAshiHigh;HeikinAshiLow;HeikinAshiClose" ...
Bevor wir die gesamte Logik im OnCalculate implementieren, definieren wir zunächst einige Hilfsfunktionen, die Heikin Ashi-Berechnungen und visuelle Interpretationen durchführen. Diese Nutzenfunktionen werden:
- Berechnung der Heikin Ashi-Werte (OHLC) aus den regulären Kursdaten.
- Bestimmen der Visualisierungen der Richtung (Farbe) jeder Heikin Ashi-Kerze.
- Trennen der vollständige historische Verarbeitung von Echtzeit-Aktualisierungen, um die Effizienz zu erhöhen.
Fügen wir nun die folgenden Funktionen direkt in der Funktion OnCalculate() hinzu. Sobald sie eingerichtet sind, rufen wir sie bei Bedarf Schritt für Schritt in OnCalculate auf.
... //+------------------------------------------------------------------+ //| Custom indicator iteration function | //+------------------------------------------------------------------+ int OnCalculate(const int32_t rates_total, const int32_t 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 int32_t &spread []) { ... return(rates_total); } // Utility functions //+----------------------------------------------------------------------------------+ //| Calculates Heikin Ashi values for all historical candles using price data arrays.| //+----------------------------------------------------------------------------------+ void GetHeikinAshiValues(const double &open[], const double &high[], const double &low[], const double &close[], const int32_t rates_total) { if(ArraySize(open) < rates_total){ return; } // Run a loop through all historical bars for(int i=0; i<rates_total; i++){ if(i == 0){ haOpen [i] = (open[i] + close[i]) / 2.0; haClose[i] = (open[i] + high[i] + low[i] + close[i]) / 4.0; haHigh [i] = MathMax(high[i], MathMax(open[i], close[i])); haLow [i] = MathMin(low [i], MathMin(open[i], close[i])); }else{ haOpen [i] = (haOpen[i-1] + haClose[i-1]) / 2.0; haClose[i] = (open[i] + high[i] + low[i] + close[i]) / 4.0; haHigh [i] = MathMax(high[i], MathMax(haOpen[i], haClose[i])); haLow [i] = MathMin(low [i], MathMin(haOpen[i], haClose[i])); } } } //+---------------------------------------------------------------------------------------+ //| Calculates Heikin Ashi values for the most recent candle only (for real-time updates).| //+---------------------------------------------------------------------------------------+ void GetCurrentHeikinAshiValue(const double &open[], const double &high[], const double &low[], const double &close[], const int32_t rates_total) { haOpen [rates_total - 1] = (haOpen[rates_total-2] + haClose[rates_total-2]) / 2.0; haClose[rates_total - 1] = (open[rates_total - 1] + high[rates_total - 1] + low[rates_total - 1] + close[rates_total - 1]) / 4.0; haHigh [rates_total - 1] = MathMax(high[rates_total - 1], MathMax(haOpen[rates_total - 1], haClose[rates_total - 1])); haLow [rates_total - 1] = MathMin(low [rates_total - 1], MathMin(haOpen[rates_total - 1], haClose[rates_total - 1])); } //+------------------------------------------------------------------------------------------------------------------+ //| Assigns a color code to each historical Heikin Ashi candle based on its direction (bullish, bearish, or neutral).| //+------------------------------------------------------------------------------------------------------------------+ void GetHeikinAshiColors(const int32_t rates_total){ for(int i=0; i<rates_total; i++){ if(haOpen[i] < haClose[i]){ colorBuffer[i] = 0; } else if(haOpen[i] > haClose[i]){ colorBuffer[i] = 1; } else { colorBuffer[i] = 2; } } } //+-----------------------------------------------------------------------------------------------+ //| Assigns a color code to the latest Heikin Ashi candle only (used for real-time color updates).| //+-----------------------------------------------------------------------------------------------+ void GetCurrentHeikinAshiColor(const int32_t rates_total){ if(haOpen[rates_total - 1] < haClose[rates_total - 1]){ colorBuffer[rates_total - 1] = 0; } else if(haOpen[rates_total - 1] > haClose[rates_total - 1]){ colorBuffer[rates_total - 1] = 1; } else { colorBuffer[rates_total - 1] = 2; } } ...
Wir rufen nun die Funktion GetHeikinAshiValues() innerhalb der Ereignisbehandlung von OnCalculate() auf, und zwar unter den folgenden Bedingungen:
- Wenn prev_calculated == 0, d.h. der Indikator wird zum ersten Mal geladen und wir müssen alle historischen Kerzen verarbeiten.
- Wenn prev_calculated != rates_total und prev_calculated != 0 ist, bedeutet dies, dass neue Balken hinzugefügt wurden oder das Chart aktualisiert wurde, sodass eine Neuberechnung an der Stelle erfolgen muss, an der wir aufgehört haben.
Diese Funktion ist für die Berechnung der gesamten Serie von Heikin-Ashi-Kerzen verantwortlich und generiert deren Eröffnungs-, Höchst-, Tiefst- und Schlusswerte auf der Grundlage der Standard-Preisdaten-Arrays. Sie stellt sicher, dass unsere Puffer von Anfang an korrekt gefüllt sind bzw. aktualisiert werden, wenn neue Daten eintreffen.
... //+------------------------------------------------------------------+ //| Custom indicator iteration function | //+------------------------------------------------------------------+ int OnCalculate(const int32_t rates_total, const int32_t 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 int32_t &spread []) { // This block is executed when the indicator is initially attached on a chart if(prev_calculated == 0){ GetHeikinAshiValues(open, high, low, close, rates_total); } // This block is executed on every new bar open if(prev_calculated != rates_total && prev_calculated != 0){ GetHeikinAshiValues(open, high, low, close, rates_total); } // This block is executed on arrival of new price (tick) data if(prev_calculated == rates_total){ } return(rates_total); } ...
Als Nächstes rufen wir die Funktion GetHeikinAshiColors() in denselben bedingten Blöcken innerhalb von OnCalculate() auf. Das heißt:
- Wenn prev_calculated == 0 ist, werden alle historischen Kerzen beim ersten Laden eingefärbt.
- Wenn prev_calculated != rates_total und prev_calculated != 0, werden die Farben für die aktualisierten Daten aktualisiert.
Diese Funktion weist jeder Heikin Ashi-Kerze einen numerischen Farbcode zu, der auf dem Verhältnis zwischen Eröffnungs- und Schlusskurs basiert:
- 0 für Aufwärtskerzen.
- 1 für Abwärtskerzen.
- 2 für neutrale Kerzen.
Diese Farbcodes werden später den tatsächlichen visuellen Farben auf dem Chart zugeordnet, sodass wir in der endgültigen Anzeige des Indikators visuell zwischen bullischen, bearischen und neutralen Kursbewegungen unterscheiden können.
... //+------------------------------------------------------------------+ //| Custom indicator iteration function | //+------------------------------------------------------------------+ int OnCalculate(const int32_t rates_total, const int32_t 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 int32_t &spread []) { // This block is executed when the indicator is initially attached on a chart if(prev_calculated == 0){ ... GetHeikinAshiColors(rates_total); } // This block is executed on every new bar open if(prev_calculated != rates_total && prev_calculated != 0){ ... GetHeikinAshiColors(rates_total); } // This block is executed on arrival of new price (tick) data if(prev_calculated == rates_total){ } return(rates_total); } ...
Schließlich rufen wir die Funktionen GetCurrentHeikinAshiValue() und GetCurrentHeikinAshiColor() innerhalb des Blocks if(prev_calculated == rates_total){} auf. Dieser Block wird ausgeführt, wenn der Indikator in Echtzeit aktualisiert wird. Das heißt, wenn ein neuer Tick eintrifft, sich aber noch keine neue Kerze gebildet hat.
Durch einen Aufruf:
- GetCurrentHeikinAshiValue() stellen wir sicher, dass die jüngsten Heikin Ashi-Werte für die aktuelle Kerze anhand der neuesten Tickdaten neu berechnet werden.
- GetCurrentHeikinAshiColor() weisen wir sofort den entsprechenden Farbcode für die letzte Heikin Ashi Kerze zu.
So bleibt der Indikator reaktionsschnell und stellt sicher, dass die jüngste Kerze immer visuell korrekt im Chart angezeigt wird, selbst in der Mitte der Formation.
... //+------------------------------------------------------------------+ //| Custom indicator iteration function | //+------------------------------------------------------------------+ int OnCalculate(const int32_t rates_total, const int32_t 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 int32_t &spread []) { ... // This block is executed on arrival of new price (tick) data if(prev_calculated == rates_total){ GetCurrentHeikinAshiValue(open, high, low, close, rates_total); GetCurrentHeikinAshiColor(rates_total); } return(rates_total); } ...
Nun werden wir unsere grafische Darstellung mit der Funktion PlotIndexSetInteger() nacheinander so konfigurieren, dass unser Heikin Ashi-Indikator beim Start angezeigt wird. Wir werden eine Einstellung nach der anderen vornehmen, um festzulegen, wie unsere Puffer auf dem Chart dargestellt werden, einschließlich ihres Zeichnungstyps.
Diese Einstellungen sind wichtig für das MetaTrader 5-Terminal, um zu verstehen, wie die von uns berechneten Werte dargestellt werden sollen. Sobald wir diesen Schritt abgeschlossen haben, wird unser Heikin Ashi-Indikator schließlich visuell aktiv und über dem Chart angezeigt, wenn er angewendet wird. Lassen Sie uns diese Konfigurationen jetzt durchgehen. Zunächst konfigurieren wir unsere grafische Darstellung, indem wir die Funktion PlotIndexSetInteger() aufrufen, um den Zeichnungstyp festzulegen. Diese spezielle Zeile weist MetaTrader an, unser Chart mit farbigen Kerzen darzustellen. Wir platzieren dies direkt unter unseren Aufrufen von SetIndexBuffer() innerhalb der Ereignisbehandlung von OnInit(). Wenn diese Konfiguration fehlschlägt, wird zu Debugging-Zwecken eine Fehlermeldung im Protokoll des Experten ausgegeben.
... //+------------------------------------------------------------------+ //| Custom indicator initialization function | //+------------------------------------------------------------------+ int OnInit() { ... if(!SetIndexBuffer(4, colorBuffer, INDICATOR_COLOR_INDEX)){ Print("Error while registering an indicator buffer: ", GetLastError()); return INIT_FAILED; } // Configuration of Graphic Plots if(!PlotIndexSetInteger(0, PLOT_DRAW_TYPE, DRAW_COLOR_CANDLES)){ Print("Error while configuring graphic plots: ", GetLastError()); return INIT_FAILED; } } ...
Stellen wir nun sicher, dass die Heikin Ashi-Daten im Datenfenster angezeigt werden, wenn Sie mit der Maus über das Chart fahren. Dies geschieht mit PlotIndexSetInteger(), wobei PLOT_SHOW_DATA auf true gesetzt ist. Diese Zeile sollte auch direkt nach der letzten Zeile in der Funktion OnInit() stehen. Wenn es nicht funktioniert, wird eine Fehlermeldung ausgegeben.
... //+------------------------------------------------------------------+ //| Custom indicator initialization function | //+------------------------------------------------------------------+ int OnInit() { ... // Configuration of Graphic Plots if(!PlotIndexSetInteger(0, PLOT_DRAW_TYPE, DRAW_COLOR_CANDLES)){ Print("Error while configuring graphic plots: ", GetLastError()); return INIT_FAILED; } if(!PlotIndexSetInteger(0, PLOT_SHOW_DATA, true)){ Print("Error while configuring graphic plots: ", GetLastError()); return INIT_FAILED; } } ...
Als Nächstes werden wir einige allgemeine Einstellungen für den Indikator vornehmen. Die erste Einstellung legt fest, wie viele Dezimalstellen (Ziffern) die Indikatorwerte haben sollen, indem IndicatorSetInteger() mit INDICATOR_DIGITS verwendet wird. Dadurch wird sichergestellt, dass die Werte des Indikators mit der Genauigkeit des Symbols übereinstimmen. Wenn etwas schief geht, geben wir eine Fehlermeldung aus, um die Fehlersuche zu erleichtern.
Fügen wir die folgenden Codezeilen unterhalb der PlotIndexSetInteger()-Funktionen im Abschnitt OnInit() hinzu.
... //+------------------------------------------------------------------+ //| Custom indicator initialization function | //+------------------------------------------------------------------+ int OnInit() { ... if(!PlotIndexSetInteger(0, PLOT_SHOW_DATA, true)){ Print("Error while configuring graphic plots: ", GetLastError()); return INIT_FAILED; } // Configure Indicator if(!IndicatorSetInteger(INDICATOR_DIGITS, Digits())){ Print("Error while setting indicator values accuracy: ", GetLastError()); return INIT_FAILED; } } ...
Als Nächstes geben wir unserem Indikator mit IndicatorSetString() einen kurzen Namen. Der Name „HeikinAshi“ wird im Datenfenster und im Chart angezeigt, damit die Nutzer den Indikator leicht erkennen können. Wenn der Name nicht gesetzt werden kann, geben wir eine Fehlermeldung aus, damit Sie wissen, was schief gelaufen ist.
... //+------------------------------------------------------------------+ //| Custom indicator initialization function | //+------------------------------------------------------------------+ int OnInit() { ... // Configure Indicator if(!IndicatorSetInteger(INDICATOR_DIGITS, Digits())){ Print("Error while setting indicator values accuracy: ", GetLastError()); return INIT_FAILED; } if(!IndicatorSetString(INDICATOR_SHORTNAME, "HeikinAshi")){ Print("Error while setting indicator shortname: ", GetLastError()); return INIT_FAILED; } return INIT_SUCCEEDED; } ...
Zu diesem Zeitpunkt haben wir alle Kernkomponenten unseres nutzerdefinierten Heikin Ashi-Indikators erfolgreich geschrieben. Klicken Sie nun in MetaEditor auf die Schaltfläche Kompilieren. Wenn alles richtig gemacht wurde, sollte der Indikator ohne Fehler kompiliert werden und für die Verwendung in Ihrem Chart bereit sein. Wenn Sie alle Schritte korrekt befolgt haben, sollte Ihr vollständiger Quelltext jetzt so aussehen. Falls etwas schief gelaufen ist oder Sie Kompilierfehler erhalten, können Sie Ihren Code mit der untenstehenden Version vergleichen, um eventuelle Fehler zu finden und zu korrigieren.
//+------------------------------------------------------------------+ //| heikinAshiIndicator.mq5 | //| Copyright 2025, MetaQuotes Ltd. | //| https://www.mql5.com/en/users/chachaian | //+------------------------------------------------------------------+ #property copyright "Copyright 2025, MetaQuotes Ltd." #property link "https://www.mql5.com/en/users/chachaian" #property version "1.00" #property indicator_chart_window #property indicator_buffers 5 #property indicator_plots 1 #property indicator_color1 C'38,166,154', C'239,83,80', clrYellow #property indicator_label1 "HeikinAshiOpen;HeikinAshiHigh;HeikinAshiLow;HeikinAshiClose" //Global variables double haOpen []; double haHigh []; double haLow []; double haClose []; double colorBuffer []; //+------------------------------------------------------------------+ //| Custom indicator initialization function | //+------------------------------------------------------------------+ int OnInit() { //--- Registration of indicator buffers if(!SetIndexBuffer(0, haOpen, INDICATOR_DATA)){ Print("Error while registering an indicator buffer: ", GetLastError()); return INIT_FAILED; } if(!SetIndexBuffer(1, haHigh, INDICATOR_DATA)){ Print("Error while registering an indicator buffer: ", GetLastError()); return INIT_FAILED; } if(!SetIndexBuffer(2, haLow, INDICATOR_DATA)){ Print("Error while registering an indicator buffer: ", GetLastError()); return INIT_FAILED; } if(!SetIndexBuffer(3, haClose, INDICATOR_DATA)){ Print("Error while registering an indicator buffer: ", GetLastError()); return INIT_FAILED; } if(!SetIndexBuffer(4, colorBuffer, INDICATOR_COLOR_INDEX)){ Print("Error while registering an indicator buffer: ", GetLastError()); return INIT_FAILED; } //--- Configuration of graphic plots if(!PlotIndexSetInteger(0, PLOT_DRAW_TYPE, DRAW_COLOR_CANDLES)){ Print("Error while configuring graphic plots: ", GetLastError()); return INIT_FAILED; } if(!PlotIndexSetInteger(0, PLOT_SHOW_DATA, true)){ Print("Error while configuring graphic plots: ", GetLastError()); return INIT_FAILED; } //--- General indicator configurations if(!IndicatorSetInteger(INDICATOR_DIGITS, Digits())){ Print("Error while setting indicator values accuracy: ", GetLastError()); return INIT_FAILED; } if(!IndicatorSetString(INDICATOR_SHORTNAME, "HeikinAshi")){ Print("Error while setting indicator shortname: ", GetLastError()); return INIT_FAILED; } return INIT_SUCCEEDED; } //+------------------------------------------------------------------+ //| Custom indicator iteration function | //+------------------------------------------------------------------+ int OnCalculate(const int32_t rates_total, const int32_t 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 int32_t &spread []) { //--- This block is executed when the indicator is initially attached on a chart if(prev_calculated == 0){ GetHeikinAshiValues(open, high, low, close, rates_total); GetHeikinAshiColors(rates_total); } //--- This block is executed on every new bar open if(prev_calculated != rates_total && prev_calculated != 0){ GetHeikinAshiValues(open, high, low, close, rates_total); GetHeikinAshiColors(rates_total); } //--- This block is executed on arrival of new price (tick) data if(prev_calculated == rates_total){ GetCurrentHeikinAshiValue(open, high, low, close, rates_total); GetCurrentHeikinAshiColor(rates_total); } return(rates_total); } //--- Utility functions //+----------------------------------------------------------------------------------+ //| Calculates Heikin Ashi values for all historical candles using price data arrays.| //+----------------------------------------------------------------------------------+ void GetHeikinAshiValues(const double &open[], const double &high[], const double &low[], const double &close[], const int32_t rates_total) { if(ArraySize(open) < rates_total){ return; } //--- Run a loop through all historical bars for(int i=0; i<rates_total; i++){ if(i == 0){ haOpen [i] = (open[i] + close[i]) / 2.0; haClose[i] = (open[i] + high[i] + low[i] + close[i]) / 4.0; haHigh [i] = MathMax(high[i], MathMax(open[i], close[i])); haLow [i] = MathMin(low [i], MathMin(open[i], close[i])); }else{ haOpen [i] = (haOpen[i-1] + haClose[i-1]) / 2.0; haClose[i] = (open[i] + high[i] + low[i] + close[i]) / 4.0; haHigh [i] = MathMax(high[i], MathMax(haOpen[i], haClose[i])); haLow [i] = MathMin(low [i], MathMin(haOpen[i], haClose[i])); } } } //+---------------------------------------------------------------------------------------+ //| Calculates Heikin Ashi values for the most recent candle only (for real-time updates).| //+---------------------------------------------------------------------------------------+ void GetCurrentHeikinAshiValue(const double &open[], const double &high[], const double &low[], const double &close[], const int32_t rates_total) { haOpen [rates_total - 1] = (haOpen[rates_total-2] + haClose[rates_total-2]) / 2.0; haClose[rates_total - 1] = (open[rates_total - 1] + high[rates_total - 1] + low[rates_total - 1] + close[rates_total - 1]) / 4.0; haHigh [rates_total - 1] = MathMax(high[rates_total - 1], MathMax(haOpen[rates_total - 1], haClose[rates_total - 1])); haLow [rates_total - 1] = MathMin(low [rates_total - 1], MathMin(haOpen[rates_total - 1], haClose[rates_total - 1])); } //+------------------------------------------------------------------------------------------------------------------+ //| Assigns a color code to each historical Heikin Ashi candle based on its direction (bullish, bearish, or neutral).| //+------------------------------------------------------------------------------------------------------------------+ void GetHeikinAshiColors(const int32_t rates_total) { for(int i=0; i<rates_total; i++){ if(haOpen[i] < haClose[i]){ colorBuffer[i] = 0; } if(haOpen[i] > haClose[i]){ colorBuffer[i] = 1; } if(haOpen[i] == haClose[i]){ colorBuffer[i] = 2; } } } //+-----------------------------------------------------------------------------------------------+ //| Assigns a color code to the latest Heikin Ashi candle only (used for real-time color updates).| //+-----------------------------------------------------------------------------------------------+ void GetCurrentHeikinAshiColor(const int32_t rates_total) { if(haOpen[rates_total - 1] < haClose[rates_total - 1]){ colorBuffer[rates_total - 1] = 0; } else if(haOpen[rates_total - 1] > haClose[rates_total - 1]){ colorBuffer[rates_total - 1] = 1; } else { colorBuffer[rates_total - 1] = 2; } } //+------------------------------------------------------------------+
Prüfung und visuelle Abstimmung
Bevor wir mit dem visuellen Test des Heikin Ashi-Indikators beginnen, ist es ratsam, das Chart aufzuräumen, damit alles gut zu sehen ist. Wir werden eine kleine Funktion definieren, die genau das tut. Sie passt den Hintergrund, das Raster und die Farbeinstellungen an, um das Chart übersichtlich zu gestalten und die Kerzen gut sichtbar zu machen. Hier ist der Code:
... //+-----------------------------------------------------------------------------------------------+ //| Assigns a color code to the latest Heikin Ashi candle only (used for real-time color updates).| //+-----------------------------------------------------------------------------------------------+ void GetCurrentHeikinAshiColor(const int32_t rates_total){ if(haOpen[rates_total - 1] < haClose[rates_total - 1]){ colorBuffer[rates_total - 1] = 0; } else if(haOpen[rates_total - 1] > haClose[rates_total - 1]){ colorBuffer[rates_total - 1] = 1; } else { colorBuffer[rates_total - 1] = 2; } } //+-------------------------------------------------+ //| This function configures the chart's appearance.| //+-------------------------------------------------+ bool ConfigureChartAppearance() { if(!ChartSetInteger(0, CHART_COLOR_BACKGROUND, clrWhite)){ Print("Error while setting chart background, ", GetLastError()); return false; } if(!ChartSetInteger(0, CHART_SHOW_GRID, false)){ Print("Error while setting chart grid, ", GetLastError()); return false; } if(!ChartSetInteger(0, CHART_MODE, CHART_LINE)){ Print("Error while setting chart mode, ", GetLastError()); return false; } if(!ChartSetInteger(0, CHART_COLOR_FOREGROUND, clrBlack)){ Print("Error while setting chart foreground, ", GetLastError()); return false; } return true; }
Betrachten Sie diese Funktion als den persönlichen Stylisten Ihres Charts. Das Programm hat folgende Aufgaben:
- Es legt einen weißen Hintergrund für die Übersichtlichkeit fest.
- Es entfernt das Raster, damit alles sauberer aussieht.
- Es ändert die Art des Charts in ein Linien-Chart, damit es nicht mit Ihren nutzerdefinierten Kerzen kollidiert.
- Es sorgt dafür, dass der Vordergrund schwarz ist, um einen guten Kontrast zu erzielen.
Wenn eine dieser Aktionen fehlschlägt, wird eine Fehlermeldung im Terminal ausgegeben, um die Fehlersuche zu erleichtern. Rufen wir nun die Funktion ConfigureChartAppearance() innerhalb von OnInit() auf, damit das Chart automatisch angepasst wird, wenn der Indikator geladen wird. So wird es gemacht:
... //+------------------------------------------------------------------+ //| Custom indicator initialization function | //+------------------------------------------------------------------+ int OnInit() { if(!ConfigureChartAppearance()){ Print("Error while configuring chart appearance: ", GetLastError()); return INIT_FAILED; } // Registration of indicator buffers if(!SetIndexBuffer(0, haOpen, INDICATOR_DATA)){ Print("Error while registering an indicator buffer: ", GetLastError()); return INIT_FAILED; } ... } ...
Wir haben die Erstellung unseres nutzerdefinierten Heikin Ashi-Indikators erfolgreich abgeschlossen. Von der Berechnung der Heikin-Ashi-Kerzen bis hin zum sauberen Zeichnen der Kerzen auf dem Chart ist alles an seinem Platz und sieht gut aus. Nun ist es an der Zeit, den Indikator in ein Chart einzubinden und ihn in Aktion zu sehen.

Wir haben jetzt unseren nutzerdefinierten Heikin Ashi-Indikator an den H1-Zeitrahmen für Gold angehängt, und er funktioniert perfekt. Alles wird wie erwartet angezeigt, was bedeutet, dass unser Code korrekt funktioniert und das Chart für die visuelle Abstimmung und weitere Tests bereit ist.
Schlussfolgerung
In diesem Teil haben wir erfolgreich einen voll funktionsfähigen Heikin Ashi-Indikator in MQL5 erstellt. Wir haben jeden Schritt durchlaufen, von der Einrichtung der Puffer über die Berechnung der Werte und die Anpassung des Charts bis hin zum Anhängen des Indikators an den Gold-H1-Zeitrahmen. Wir haben bestätigt, dass alles wie erwartet funktioniert. Um Ihnen das Nachvollziehen oder die Fehlersuche zu erleichtern, haben wir sowohl den vollständigen Quellcode als auch die kompilierte Version in dieses Tutorial aufgenommen.
Im nächsten Teil dieser Serie gehen wir noch einen Schritt weiter und erstellen einen Expert Advisor, der unseren Heikin Ashi-Indikator verwendet, um Handelsentscheidungen zu treffen. Bleiben Sie dran.
Übersetzt aus dem Englischen von MetaQuotes Ltd.
Originalartikel: https://www.mql5.com/en/articles/19260
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.
Entwicklung des Price Action Analysis Toolkit (Teil 39): Automatisierung der BOS- und ChoCH-Erkennung in MQL5
Automatisieren von Handelsstrategien in MQL5 (Teil 31): Erstellung eines Price Action 3 Drives Harmonic Pattern Systems
Die Grenzen des maschinellen Lernens überwinden (Teil 3): Eine neue Perspektive auf irreduzible Fehler
Vom Neuling zum Experten: Animierte Schlagzeilen mit MQL5 (X) – Multiple Symbol Chart View für den Nachrichtenhandel
- 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.