Nutzerdefinierte Indikatoren (Teil 1): Eine schrittweise Einführung in die Entwicklung von einfachen nutzerdefinierten Indikatoren in MQL5
Einführung
Die visuelle Darstellung von Marktinformationen ist der Eckpfeiler des Handels. Ohne diese visuelle Modellierung von Marktdaten und Preisen wäre der Handel nicht so praktikabel und effektiv. Von den Anfängen der Charttechnik bis hin zu den hochentwickelten technischen Analysetools, die heute zur Verfügung stehen, haben sich Händler auf visuelle Anhaltspunkte verlassen, um fundierte Entscheidungen auf den Finanzmärkten zu treffen.
MQL5-Indikatoren dienen als leistungsstarke Werkzeuge zur Verbesserung dieses visuellen Analyseprozesses. Durch die Nutzung mathematischer Berechnungen und Algorithmen helfen die MQL5-Indikatoren den Händlern, Gewinnchancen zu erkennen, indem sie das Marktverhalten statistisch auswerten. Diese Indikatoren können direkt auf Kurscharts angewendet werden und bieten Händlern wertvolle Einblicke in die Marktdynamik.
In dieser Artikelserie werden wir untersuchen, wie MQL5-Indikatoren erstellt, angepasst und verwendet werden, um Handelsstrategien in MetaTrader 5 zu verbessern. Von der grundlegenden Indikatorlogik bis hin zu fortgeschrittenen Anpassungsoptionen werden wir die Grundlagen abdecken und im weiteren Verlauf der Artikelserie schrittweise in die fortgeschrittenen Konzepte der Indikatorentwicklung einsteigen. Das Hauptziel dieser Artikelserie ist es, Sie in die Lage zu versetzen, Ihre eigenen MQL5-Indikatoren zu erstellen, die auf Ihre Handelsvorlieben und Ziele zugeschnitten sind.
Was ist ein Indikator?
Ein Indikator ist ein Werkzeug oder Instrument, das zur Analyse vergangener Kursdaten und zur Vorhersage künftiger Kursbewegungen verwendet wird. Die Indikatoren konzentrieren sich hauptsächlich auf die Analyse von Marktdaten und nicht auf die Ausführung von Geschäften. Sie können keine Positionen und Aufträge eröffnen, ändern oder schließen. Sie bieten nur Einblicke und führen keine Geschäfte aus.
Im Kern handelt es sich bei Indikatoren um mathematische Berechnungen oder Algorithmen, die auf historische Kursdaten angewendet werden, um visuelle Darstellungen des Marktverhaltens zu erzeugen, wobei ihr Status in Echtzeit aktualisiert wird.
Diese visuellen Darstellungen können verschiedene Formen annehmen, z. B. Liniencharts, Kerzen, Histogramme, Pfeile oder Überlagerungen von Kurscharts. Indikatoren helfen Händlern bei der Interpretation der Marktdynamik, indem sie Trends aufzeigen, potenzielle Umkehrungen identifizieren oder überkaufte oder überverkaufte Bedingungen signalisieren.
Indikatoren können je nach ihrer Funktionalität in verschiedene Kategorien eingeteilt werden, z. B. Trendfolgeindikatoren, Momentum-Indikatoren, Volatilitätsindikatoren und Volumenindikatoren.
Arten von Indikatoren in MQL5
Es gibt zwei Arten von Indikatoren in MQL5, nämlich technische Indikatoren und nutzerdefinierte Indikatoren.
1. Technische Indikatoren
Dies sind die Standardindikatoren, die in MetaTrader 5 vorinstalliert sind. Sie enthalten eine breite Palette von technischen Indikatoren, auf die Händler leicht zugreifen und in ihre MetaTrader 5-Charts laden können, um den Markt zu analysieren. Zu diesen Indikatoren gehören beliebte Instrumente wie Oszillatoren, Trendfolgemarken und volumenbasierte Indikatoren.
Der Quellcode für diese Standardindikatoren ist nicht ohne weiteres zugänglich, da sie in die MetaTrader 5-Plattform integriert sind. Die einzige Möglichkeit, von Ihrem MQL5-Code aus auf sie zuzugreifen, ist die Verwendung von in der Standardsprache vordefinierten MQL5-Funktionen für technische Indikatoren. Diese Funktionalität bietet uns die Möglichkeit, diese Standardindikatoren mit Hilfe von MQL5 zu aktualisieren oder anzupassen, um neue fortschrittliche nutzerdefinierte Indikatoren und Handelsinstrumente zu erstellen. Ich werde Ihnen im weiteren Verlauf des Artikels zeigen, wie Sie die Funktionalität technischer Indikatoren erweitern können, indem wir einen nutzerdefinierten Indikator entwickeln, der auf geglätteten mehrfarbigen Kerzen basiert.
Beispiele für technische Standardindikatoren des MetaTrader 5 sind:
- iMA (Einfacher gleitender Durchschnitt): Berechnet den einfachen gleitenden Durchschnitt einer bestimmten Preisreihe.
- iRSI (Relative Stärke Index): Misst das Ausmaß der jüngsten Kursveränderungen, um überkaufte oder überverkaufte Bedingungen zu bewerten.
- iMACD (Gleitender Durchschnitt Konvergenz Divergenz): Identifiziert die Trendrichtung und mögliche Umkehrungen durch Analyse der Konvergenz und Divergenz zweier gleitender Durchschnitte.
2. Nutzerdefinierte Indikatoren
Wie der Name schon sagt, sind nutzerdefinierte Indikatoren Werkzeuge für die technische Analyse, die Sie selbst erstellen können, um die Finanzmärkte zu analysieren. Sie unterscheiden sich von den integrierten Indikatoren, da sie spezifischere Berechnungen und Visualisierungen auf der Grundlage Ihrer Handelsanforderungen ermöglichen.
Als MQL5-Programmierer können Sie Indikatoren auf der Grundlage aller Ihnen zur Verfügung stehenden Daten aus jeder beliebigen Quelle erstellen. Sie können auch einen bereits erstellten nutzerdefinierten Indikator importieren oder die vorgefertigten technischen MQL5-Indikatoren erweitern und ändern, um einen anspruchsvolleren und fortschrittlicheren nutzerdefinierten Indikator zu erstellen, wie wir es tun werden.
Vorteile von nutzerdefinierten Indikatoren. Hier sind einige Eigenschaften und Vorteile von nutzerdefinierten Indikatoren:
Unerreichte Flexibilität bei den Berechnungen:
- Sie können nutzerdefinierte Indikatoren entwerfen, um jede technische Indikatorformel oder Handelsstrategie zu verwenden, die Sie sich vorstellen.
- Mit MQL5 können Sie eine breite Palette von nutzerdefinierten Indikatorberechnungen und mathematischen Modellen erforschen, die auf Ihre speziellen Bedürfnisse zugeschnitten sind.
- Sie können anpassen, wie die Ergebnisse des Indikators im Chart angezeigt werden.
- MQL5 bietet die Möglichkeit, Linienstile, Kerzen, Pfeile, mehrfarbige Objekte und viele weitere grafische Elemente zu verwenden, um klare und informative Visualisierungen zu erstellen, die auf Ihren Handelsstil abgestimmt sind.
- MQL5 bietet mehr als nur das Erstellen von Indikatoren von Grund auf.
- Sie können externe Datenquellen nutzen, die über die typischen Kursdaten hinausgehen, um eine Reihe von Indikatoren für die technische oder fundamentale Analyse zu erstellen.
- Importieren Sie vorgefertigte nutzerdefinierte Indikatoren, die von anderen MQL5-Programmierern erstellt wurden, um deren Funktionalität zu verbessern oder zu erweitern.
- Erweitern und modifizieren Sie integrierte Indikatoren, um anspruchsvolle, auf Ihre individuellen Handelsanforderungen zugeschnittene Indikatoren zu erstellen.
Durch die Kombination dieser Funktionen können Sie mit MQL5 nutzerdefinierte Indikatoren erstellen, die nicht nur auf Ihre Bedürfnisse im Bereich der technischen Analyse zugeschnitten sind, sondern auch Daten und Berechnungen einbeziehen können, die über die traditionellen preisbasierten Indikatoren hinausgehen.
Es gibt Beispiele für kostenlose nutzerdefinierte Indikatoren im MetaTrader 5-Terminal. Das MetaTrader 5-Terminal bietet auch eine Auswahl an Beispielindikatoren, auf die Sie zugreifen können, um sie zu verwenden oder zu studieren und die Indikatorentwicklung in MQL5 besser zu verstehen. Der Quellcode für die kostenlosen MetaTrader 5-Beispielindikatoren ist leicht zugänglich und stellt eine wertvolle Ressource für MQL5-Programmierer dar, die mit der Erstellung von Indikatoren experimentieren und lernen möchten. Die MQL5-Beispielindikatoren werden in den Verzeichnissen MQL5\Indicators\Examples und MQL5\Indicators\Free Indicators innerhalb des MetaTrader 5-Installationsverzeichnisses gespeichert.
Durch das Untersuchen und Ändern von Beispielindikatoren erhalten Sie Einblicke in MQL5-Programmiertechniken, Indikatorlogik und bewährte Verfahren. Dieser praxisnahe Ansatz fördert das Lernen und ermöglicht es Ihnen, individuelle Indikatoren zu entwickeln, die auf Ihre eigenen spezifischen Handelsziele zugeschnitten sind.
Grundlegende Bausteine für nutzerdefinierte Indikatoren in MQL5
Bevor man sich mit der Entwicklung von nutzerdefinierten Indikatoren in MQL5 beschäftigt, ist es wichtig, die grundlegende Struktur eines MQL5-Indikators zu verstehen. Wenn Sie sich mit den wichtigsten Komponenten und Funktionen eines Indikators vertraut machen, sind Sie besser gerüstet, um Indikatoren in MetaTrader 5 zu erstellen, zu ändern und effektiv zu nutzen.
Nutzerdefinierte Indikator-Datei (.mq5)
Ein MQL5-Indikator wird normalerweise in einer Datei mit der Erweiterung „.mq5“ gespeichert. Diese Datei enthält den in der Programmiersprache MQL5 geschriebenen Quellcode, der die Logik und das Verhalten des Indikators definiert. Alle Indikatoren werden in der Datei MQL5\Indikatoren innerhalb des MetaTrader 5-Installationsverzeichnisses gespeichert.
Verwenden Sie das Navigator-Panel, das Sie sowohl im MetaTrader 5-Handelsterminal als auch in MetaEditor finden, um Zugriff auf den Ordner Indicators zu erhalten. Sie können auch über den Ordner „MQL5“ auf den Ordner „Indicator“ zugreifen, indem Sie die folgenden beiden Methoden verwenden:
Wie man vom MetaTrader 5-Handelsterminal auf Indikator-Dateien zugreift:
- Klicken Sie im oberen Menü auf „Datei“.
- Wählen Sie „Datenordner öffnen“ oder verwenden Sie das Tastaturkürzel (Strg + Umschalt + D).
- Navigieren Sie zum Ordner „MQL5/Indikatoren“.
Wie man von MetaEditor aus auf Indikator-Dateien zugreift:
- Das Panel des MetaEditor-Navigators befindet sich standardmäßig auf der linken Seite des MetaEditor-Fensters und bietet direkten Zugriff auf den MQL5-Ordner.
- Wenn das Navigator-Panel deaktiviert ist, können Sie es mit der Tastenkombination (Strg + D) oder durch Klicken auf die Menüoption Ansicht am oberen Rand des MetaEditor 5-Fensters aktivieren. Wählen Sie dort die Option „Navigator“. Wenn Sie diese Option wählen, wird das Navigationsfeld aktiviert, das Ihnen Zugang zum MQL5-Ordner gewährt.
Beispiel 1: Der Linear Moving Average Histogram Custom Indicator (LinearMovingAverageHistogram.mq5): Lassen Sie uns einen nutzerdefinierten Indikator erstellen, damit wir ein visuelles Verständnis für die verschiedenen Code-Komponenten erhalten, die für die Erstellung eines nutzerdefinierten Indikators erforderlich sind. Wir werden unseren ersten nutzerdefinierten Indikator für diese praktische Demonstration „LinearMovingAverageHistogram“ nennen. Es wird ein linear gewichteter gleitender Durchschnitt als Histogramm und eine Linie, die den aktuellen Preis darstellt, in einem separaten Fenster unterhalb des Preischarts angezeigt.
Beginnen wir mit der Erstellung einer neuen nutzerdefinierten Indikatordatei mit dem MQL5-Assistenten.
Wie man eine neue nutzerdefinierte Indikator-Datei mit dem MQL5-Assistenten erstellt
Schritt 1: Öffnen Sie die MetaEditor-IDE und starten Sie den „MQL-Assistenten“ über die Schaltfläche „Neu“ im Menü.
Schritt 2: Wählen Sie die Option „Custom Indicator“ bzw. „Benutzerdefinierter Indikator“ und klicken Sie auf „Next“ bzw. „Weiter“.
Schritt 3: Geben Sie im Abschnitt „General Properties“ bzw. „Allgemeine Eigenschaften“ den Ordner und den Namen für Ihren neuen nutzerdefinierten Indikator „Indikatoren\Artikel\LinearMovingAverageHistogram“ und klicken Sie auf „Next“ bzw. „Weiter“.
Schritt 4: Wählen Sie im Abschnitt „Event-Handler“ die zweite Option „OnCalculate(...,Preise)“, lassen Sie die Kontrollkästchen „OnTimer“ und „OnChartEvent“ unmarkiert und klicken Sie auf „Weiter“, um fortzufahren.
Schritt 5: Aktivieren Sie im Abschnitt „Zeichnungseigenschaften“ das Kontrollkästchen „Indikator in separatem Fenster“. Deaktivieren Sie die Kontrollkästchen „Minimum“ und „Maximum“ und lassen Sie das Texteingabefeld „Graphen:“ leer. Klicken Sie auf „Fertig stellen“, um die neue nutzerdefinierte MQL5-Indikatordatei zu erstellen.
Im Ordner „MQL5/Indikatoren“ finden Sie einen neuen Unterordner namens „Article“. Dieser Unterordner enthält unsere soeben erstellte nutzerdefinierte Indikatordatei „LinearMovingAverageHistogram.mq5“. Im Rahmen praktischer Demonstrationen werden wir in diesem Artikel mehrere nutzerdefinierte Indikatoren programmieren. Um eine gute Organisation zu gewährleisten, werden wir alle Indikator-Dateien in diesem neuen Ordner „Artikel“ speichern.
Wir haben jetzt eine neue nutzerdefinierte MQL5-Indikatordatei mit nur den obligatorischen Funktionen (OnInit und OnCalculate). Denken Sie daran, die neue Datei zu speichern, bevor Sie fortfahren. So sieht unser neu generierter Code für den nutzerdefinierten Indikator aus:
//+------------------------------------------------------------------+ //| LinearMovingAverageHistogram.mq5 | //| Copyright 2024, Wanateki Solutions Ltd. | //| https://www.wanateki.com | //+------------------------------------------------------------------+ #property copyright "Copyright 2024, Wanateki Solutions Ltd." #property link "https://www.wanateki.com" #property version "1.00" #property indicator_separate_window //+------------------------------------------------------------------+ //| Custom indicator initialization function | //+------------------------------------------------------------------+ int OnInit() { //--- indicator buffers mapping //--- return(INIT_SUCCEEDED); } //+------------------------------------------------------------------+ //| Custom indicator iteration function | //+------------------------------------------------------------------+ int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[]) { //--- //--- return value of prev_calculated for next call return(rates_total); } //+------------------------------------------------------------------+
Grundlegende Bestandteile einer nutzerdefinierten Indikatordatei (.mq5): Die Indikatordatei besteht aus verschiedenen Abschnitten. Analysieren wir nun, wie die verschiedenen Teile des Indikatorcodes funktionieren: Der Kopfteil: Der Kopfteil des Indikator besteht aus vier Teilen, den Kommentaren, den Eigenschaftsanweisungen, externen Include-Dateien und den globalen Variablendefinitionen. Nachfolgend finden Sie eine Aufschlüsselung, was unsere Indikatoren in der Kopfzeile enthalten:
1. Kopfteilkommentare: Dies ist der erste Abschnitt unseres Indikatorcodes. Er enthält als Kommentar Informationen über den Indikator, z. B. den Dateinamen, Copyright-Informationen und einen Link zur Website des Autors. Diese Kommentare wirken sich in keiner Weise auf die Funktionalität des Indikatorcodes aus.
//+------------------------------------------------------------------+ //| LinearMovingAverageHistogram.mq5 | //| Copyright 2024, Wanateki Solutions Ltd. | //| https://www.wanateki.com | //+------------------------------------------------------------------+
2. Eigentumsrichtlinien: Die Eigenschaftsrichtlinien liefern zusätzliche Informationen über den Indikator. Sie enthalten das Copyright, einen Link, der mit dem Indikator oder dem Autor verbunden ist, die aktuelle Version des Indikators und spezifische Anweisungen, wie der Indikator angezeigt werden kann. Die wichtigste Eigenschaftsrichtlinie ist die „#property indicator_separate_window“ die die Plattform anweist, den Indikator in einem separaten Fenster anzuzeigen.
#property copyright "Copyright 2024, Wanateki Solutions Ltd." #property link "https://www.wanateki.com" #property version "1.00" #property indicator_separate_window
Die Eigenschaften über Copyright, dem Link, dem Autor(en) und der Beschreibung sind auf der Registerkarte „Allgemein“ des kleinen Indikator-Setup-Unterfensters (Panel) sichtbar, das erscheint, wenn Sie den Indikator in den Chart laden.
3. Globale Variablen: Alle globalen Variablen befinden sich unterhalb der Eigenschaftsanweisungen. Unser Indikatorcode enthält derzeit keine globalen Variablen, da wir sie bei der Erstellung der Datei mit dem MQL5-Assistenten nicht angegeben haben. Im weiteren Verlauf dieses Artikels werden wir alle unsere globalen und Nutzereingabevariablen unterhalb der #property-Direktiven definieren.
MQL5-Standardfunktionen für nutzerdefinierte Indikatoren: Unterhalb des Kopfbereichs finden Sie verschiedene Funktionen. Alle Indikatoren müssen die MQL5-Standardfunktionen OnInit und OnCalculate enthalten. Vom Nutzer erstellte Funktionen sind optional, werden aber für eine gute Codeorganisation empfohlen. Hier ist eine Aufschlüsselung der verschiedenen Funktionen in unserem Indikatorcode:
1. Funktion zur Initialisierung des Indikators OnInit() Die Funktion OnInit() wird aufgerufen, wenn der Indikator initialisiert wird. Er führt normalerweise Einrichtungsaufgaben aus, wie z. B. die Zuordnung von Indikatorpuffern und die Initialisierung von globalen Variablen. Ich werde Ihnen die Indikatorpuffer vorstellen, wenn wir den Artikel vertiefen. Wenn die Funktion erfolgreich ausgeführt wird, gibt sie INIT_SUCCEEDED zurück, und wenn die Initialisierung fehlschlägt, gibt sie INIT_FAILED zurück.
//+------------------------------------------------------------------+ //| Custom indicator initialization function | //+------------------------------------------------------------------+ int OnInit() { //--- indicator buffers mapping //--- return(INIT_SUCCEEDED); }2. Indikator-Iterationsfunktion OnCalculate(): Die Funktion OnCalculate() in MQL5 ist das Hauptnervenzentrum aller nutzerdefinierten Indikatorberechnungen. Sie wird immer dann aufgerufen, wenn sich die Preisdaten ändern, und veranlasst den Indikator, seine Werte zu aktualisieren. Es gibt zwei Hauptversionen von OnCalculate(), die ich im weiteren Verlauf des Artikels erläutern werde.
//+------------------------------------------------------------------+ //| Custom indicator iteration function | //+------------------------------------------------------------------+ int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[]) { //--- //--- return value of prev_calculated for next call return(rates_total); }3. Funktion zur De-Initialisierung des Indikators OnDeinit(): Die Funktion OnDeinit() wurde nicht in unseren Code aufgenommen, ist aber eine sehr wichtige Funktion. Er wird aufgerufen, wenn der Indikator beendet wird, und ist für die Ausführung der Deinitialisierungsprozeduren verantwortlich. In der Regel führt er alle Aufräumarbeiten durch, wie z. B. die Freigabe aller technischen Indikatorhandles.
//+------------------------------------------------------------------+ //| Indicator deinitialization function | //+------------------------------------------------------------------+ void OnDeinit(const int reason) { //-- deinitialization code } //+------------------------------------------------------------------+
Wenn wir unseren Indikatorcode kompilieren, stoßen wir auf 1 Warnung: „kein Indikatorplot für Indikator definiert“.
Diese Warnung bedeutet, dass unserem nutzerdefinierten Indikator eine entscheidende Definition fehlt, wie seine Daten im Chart angezeigt werden sollen. Wie erwartet, dient unser derzeitiger Indikatorcode als reines Skelett ohne funktionale Komponenten. Um dies zu korrigieren, müssen wir tiefer gehen und die wesentlichen Codesegmente schreiben, die unserem Indikator Leben einhauchen werden.
Eigenschaftsrichtlinien der Beschreibung: Beginnen wir damit, eine kurze Beschreibung des nutzerdefinierten Indikators in den Kopfbereich des Indikators zu schreiben:
#property description "A custom indicator to demonstrate a linear weighted moving average." #property description "A histogram and line are drawn in a separate window to indicate " #property description "the moving average direction."Die Eigenschaftsrichtlinien für Puffer und Plots: Alle nutzerdefinierten Indikatoren haben verschiedene Eigenschaften, die immer am Anfang der Datei stehen, wie ich bereits erläutert habe. Einige sind fakultativ, aber die folgenden drei sind immer obligatorisch:
- indicator_separate_window oder indicator_chart_window: Hier können Sie festlegen, ob ein Indikator in einem separaten Fenster oder direkt im Chart-Fenster dargestellt werden soll.
- indikator_puffer: Gibt die Anzahl der Indikatorpuffer an, die der nutzerdefinierte Indikator verwendet.
- indicator_plots: Gibt die Anzahl der vom nutzerdefinierten Indikator verwendeten Wertedarstellungen an.
Um diese Anforderung zu erfüllen, werden wir die Indikatorpuffer und Plot-Eigenschaften definieren. Unter der Eigenschaft „indicator_separate_window“ platzieren wir den Code, der für die Festlegung der Indikatorpuffer und -darstellungen verantwortlich ist. Indikatorpuffer und -plots werden zur Anzeige der Indikatordaten in den Charts verwendet. Wir verwenden die Eigenschaftsrichtlinie, um die Anzahl der Puffer festzulegen, die im Code für die Berechnung des Indikators zur Verfügung stehen sollen. Diese Zahl ist eine ganze Zahl von 1 bis 512. Da es sich um eine Präprozessor-Direktive handelt, gibt es in der Vorverarbeitungsphase des Quellcodes noch keine Variablen, weshalb wir eine Ziffer (von 1 bis 512) als Wert angeben müssen.
Wir benötigen zwei Indikatorpuffer und zwei für die Darstellung, um unsere neuen, nutzerdefinierten Indikatordaten zu präsentieren. Ein Indikatorpuffer für das Histogramm und ein weiterer für die Linie, die den aktuellen Preis des Symbols anzeigt. Es folgt ein „Plot“ für das Histogramm und ein weiteres „Plot“ für die Preislinie.
//--- indicator buffers and plots #property indicator_buffers 2 #property indicator_plots 2
Label-, Typ- und Stileigenschaftsrichtlinien
Als Nächstes sollten wir die anderen Details wie die Indikatorbezeichnung, den Typ, die Farbe, den Stil und die Breite des Histogramms und der Kurslinie festlegen.
//--- plots1 details for the ma histogram #property indicator_label1 "MA_Histogram" #property indicator_type1 DRAW_HISTOGRAM #property indicator_color1 clrDodgerBlue #property indicator_style1 STYLE_SOLID #property indicator_width1 1 //--- plots2 details for the price line #property indicator_label2 "Current_Price_Line" #property indicator_type2 DRAW_LINE #property indicator_color2 clrGoldenrod #property indicator_style2 STYLE_SOLID #property indicator_width2 2
Globale Nutzereingabevariablen
Als Nächstes legen wir die Nutzereingabevariablen des nutzerdefinierten Indikators fest (Zeitraum des gleitenden Durchschnitts und Verschiebung), die zum Speichern verschiedener Indikatorparameter verwendet werden.
//--- input parameters for the moving averages input int _maPeriod = 50; // MA Period input int _maShift = 0; // MA Shift
Deklaration von dynamischen Arrays des Indikatorpuffers im Global Scope
Als Nächstes deklarieren wir den Indikatorpuffer für das Histogramm des gleitenden Durchschnitts und die Preislinie. Indikatorpuffer gehören zu den Grundpfeilern von nutzerdefinierten Indikatoren und sind für die Speicherung der Indikatordaten in dynamischen Arrays zuständig. Sie sollten damit beginnen, ein dynamisches Array zu deklarieren und es dann als Indikatorpuffer zu registrieren, indem Sie eine spezielle MQL5-Funktion „SetIndexBuffer“ verwenden, um es in ein spezielles Terminal-verwaltetes Array umzuwandeln. Danach ist das Terminal für die Zuweisung von Speicherplatz für das Array und den öffentlichen Zugriff darauf als neues Array mit Zugriff auf Zeitreihen zuständig, auf dessen Grundlage andere Indikatoren berechnet werden können.
Wir werden zunächst die Histogramm- und Linienindikatorpuffer als dynamische Arrays deklarieren und dann später in OnInit() die spezielle MQL5-Funktion „SetIndexBuffer“ verwenden, um sie zu registrieren und in terminalverwaltete Zeitreihen-Arrays umzuwandeln.
//--- indicator buffer double maHistogramBuffer[], priceLineBuffer[];
Nutzerdefinierte Indikator-Initialisierungsfunktion - GetInit()
Als Nächstes werden wir eine nutzerdefinierte Funktion erstellen, die für die Initialisierung unseres nutzerdefinierten Indikators verantwortlich sein wird. Zunächst erstellen wir eine leere Funktion vom Typ void, d. h. sie gibt keine Daten zurück. Wir benennen die Funktion „GetInit()“. Wir platzieren die spezielle Funktion „SetIndexBuffer(...)“, die für die Umwandlung unserer dynamischen Arrays der Indikatorpuffer, die wir zuvor als „maHistogramBuffer“ und „priceLineBuffer“ deklariert hatten, in Terminal-verwaltete Zeitreihen-Arrays verantwortlich sein wird.
//+------------------------------------------------------------------+ //| User custom function for custom indicator initialization | //+------------------------------------------------------------------+ void GetInit() { //--- set the indicator buffer mapping SetIndexBuffer(0, maHistogramBuffer, INDICATOR_DATA); SetIndexBuffer(1, priceLineBuffer, INDICATOR_DATA); } //+------------------------------------------------------------------+Als Nächstes stellen wir die Genauigkeit des Indikators so ein, dass er mit dem Dezimalstellen des Symbols übereinstimmt.
//--- set the indicators accuracy IndicatorSetInteger(INDICATOR_DIGITS, _Digits + 1);Als Nächstes legen wir den ersten Balken fest, ab dem der Index gezeichnet werden soll.
//--- set the first bar from where the index will be drawn PlotIndexSetInteger(0, PLOT_DRAW_BEGIN, _maPeriod); PlotIndexSetInteger(1, PLOT_DRAW_BEGIN, 0);Wir richten die Verschiebung des Indikators für den gleitenden Durchschnitt auf den nutzerdefinierten Wert für den Handle des gleitenden Durchschnitts ein und verwenden Sie einen Nullwert für die Kurslinie. Dieser wird beim Plotten oder Zeichnen des Indikators verwendet.
//--- set the indicator shifts when drawing PlotIndexSetInteger(0, PLOT_SHIFT, _maShift); PlotIndexSetInteger(1, PLOT_SHIFT, 0);Als Nächstes legen wir den Namen fest, der im MT5-Datenfenster für die Indikatorwerte aus dem Indikator-Datenpuffer angezeigt werden soll. Wir verwenden ein Switch-Control, um den Kurznamen des Indikators für das Datenfenster festzulegen.
//--- set the name to be displayed in the MT5 DataWindow IndicatorSetString(INDICATOR_SHORTNAME, "LWMA_Histo" + "(" + string(_maPeriod) + ")");Um die Initialisierungsfunktion des Indikators abzuschließen, setzen wir das Zeichenhistogramm auf einen leeren Wert.
//--- set the drawing histogram and line to an empty value PlotIndexSetDouble(0, PLOT_EMPTY_VALUE, 0.0); PlotIndexSetDouble(1, PLOT_EMPTY_VALUE, 0.0);
Nutzerdefinierte Funktion zur Berechnung des linear gewichteten gleitenden Durchschnitts - GetLWMA()
Als Nächstes müssen wir eine nutzerdefinierte Funktion namens „GetLWMA(..)“ erstellen, die für die Berechnung des linear gewichteten gleitenden Durchschnitts verantwortlich ist. Die Funktion wird vom Typ void sein, da sie keine Daten zurückgeben soll. Sie akzeptiert vier Argumente als Funktionsparameter (rates_total, prev_calculated, begin, &price).
//+------------------------------------------------------------------+ //| Function to calculate the linear weighted moving average | //+------------------------------------------------------------------+ void GetLWMA(int rates_total, int prev_calculated, int begin, const double &price[]) { int weight = 0; int x, l, start; double sum = 0.0, lsum = 0.0; //--- first calculation or number of bars was changed if(prev_calculated <= _maPeriod + begin + 2) { start = _maPeriod + begin; //--- set empty value for first start bars for(x=0; x < start; x++) { maHistogramBuffer[x] = 0.0; priceLineBuffer[x] = price[x]; } } else start = prev_calculated - 1; for(x = start - _maPeriod, l = 1; x < start; x++, l++) { sum += price[x] * l; lsum += price[x]; weight += l; } maHistogramBuffer[start-1] = sum/weight; priceLineBuffer[x] = price[x]; //--- main loop for(x=start; x<rates_total && !IsStopped(); x++) { sum = sum - lsum + price[x] * _maPeriod; lsum = lsum - price[x - _maPeriod] + price[x]; maHistogramBuffer[x] = sum / weight; priceLineBuffer[x] = price[x]; } }
Indikator Haupt-Wiederholungsfunktion - OnCalculate()
Die Iterationsfunktion „OnCalculate(..)“ des nutzerdefinierten Indikators ist das Herzstück unseres nutzerdefinierten Indikators und ist für die Aktualisierung und Darstellung unseres Indikators verantwortlich, wenn es einen neuen Tick oder eine Preisänderung gibt und alle erforderlichen Berechnungen haben hier ihren Ursprung. Wir verwenden derzeit die Kurzform der Funktion OnCalculate():
int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[]) { return(rates_total); }Diese Funktion benötigt vier Eingabeparameter oder Argumente:
- rates_total: Dieser Parameter enthält den Wert der Gesamtzahl der Elemente des Arrays price[]. Er wird als Eingabeparameter für die Berechnung von Indikatorwerten übergeben, wie wir es zuvor mit der Funktion „GetLWMA(..)“ getan haben.
- prev_calculated: Dieser Parameter speichert das Ergebnis der Ausführung von „OnCalculate(..)“ beim vorherigen Aufruf. Sie spielt eine Schlüsselrolle im Algorithmus zur Berechnung der Indikatorwerte und stellt sicher, dass wir nicht bei jedem Aufruf der Funktion „OnCalculate(..)“ oder bei einer neuen Preisänderung Berechnungen für den gesamten historischen Zeitraum durchführen.
- begin: Dieser Parameter speichert die Nummer des Startwertes des Preisfeldes, das keine Daten für die Berechnung enthält. In unserem nutzerdefinierten Indikator ist dieser Wert „_maPeriod“, und er sagt der Funktion „OnCalculte(...)“ einfach, dass sie alle Berechnungen anhalten soll, bis alle Balken den gespeicherten Wert „_maPeriod“ erreichen, damit genügend Balken für die Berechnungen des Indikators vorhanden sind.
- price: Dieser Parameter speichert den angewandten Preis, der für die Berechnung der Indikatordaten verwendet wird. Er wird vom Nutzer beim Laden des nutzerdefinierten Indikators im Chart angegeben. Die Nutzer haben die Möglichkeit, entweder Eröffnungs-, Schluss-, Höchst-, Tiefst-, Medianpreis (HL / 2), typischer Preis (HLC / 3), gewichteter Schlusskurs (HLCC / 4) oder die Werte der zuvor geladenen Indikatordaten auszuwählen.
Hier ist der Code für unsere Funktion „OnCalculate(...)“:
//+------------------------------------------------------------------+ //| Custom indicator iteration function | //+------------------------------------------------------------------+ int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[]) { //--- check if we have enough bars to do the calculations if(rates_total < _maPeriod - 1 + begin) return(0); //--- first calculation or number of bars was changed if(prev_calculated == 0) { ArrayInitialize(maHistogramBuffer, 0); PlotIndexSetInteger(0, PLOT_DRAW_BEGIN, _maPeriod - 1 + begin); } //--- calculate the linear weighted moving average and plot it on the chart GetLWMA(rates_total, prev_calculated, begin, price); //--- return value of prev_calculated for next call return(rates_total); } //+------------------------------------------------------------------+Wir haben nun alle verschiedenen Codesegmente unseres neu erstellten nutzerdefinierten Indikators. Vergewissern Sie sich, dass Ihre Datei „LinearMovingAverageHistogram“ so aussieht und alle Komponenten des unten stehenden Codes enthält:
#property version "1.00" #property indicator_separate_window //--- indicator buffers and plots #property indicator_buffers 2 #property indicator_plots 2 //--- plots1 details for the ma histogram #property indicator_label1 "MA_Histogram" #property indicator_type1 DRAW_HISTOGRAM #property indicator_color1 clrDodgerBlue #property indicator_style1 STYLE_SOLID #property indicator_width1 1 //--- plots2 details for the price line #property indicator_label2 "Current_Price_Line" #property indicator_type2 DRAW_LINE #property indicator_color2 clrGoldenrod #property indicator_style2 STYLE_SOLID #property indicator_width2 2 //--- input parameters for the moving averages input int _maPeriod = 50; // MA Period input int _maShift = 0; // MA Shift //--- indicator buffer double maHistogramBuffer[], priceLineBuffer[]; //+------------------------------------------------------------------+ //| Custom indicator initialization function | //+------------------------------------------------------------------+ int OnInit() { //--- call the custom initialization function GetInit(); //--- return(INIT_SUCCEEDED); } //+------------------------------------------------------------------+ //| Custom indicator iteration function | //+------------------------------------------------------------------+ int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[]) { //--- check if we have enough bars to do the calculations if(rates_total < _maPeriod - 1 + begin) return(0); //--- first calculation or number of bars was changed if(prev_calculated == 0) { ArrayInitialize(maHistogramBuffer, 0); PlotIndexSetInteger(0, PLOT_DRAW_BEGIN, _maPeriod - 1 + begin); } //--- calculate the linear weighted moving average and plot it on the chart GetLWMA(rates_total, prev_calculated, begin, price); //--- return value of prev_calculated for next call return(rates_total); } //+------------------------------------------------------------------+ //+------------------------------------------------------------------+ //| User custom function for custom indicator initialization | //+------------------------------------------------------------------+ void GetInit() { //--- set the indicator buffer mapping SetIndexBuffer(0, maHistogramBuffer, INDICATOR_DATA); SetIndexBuffer(1, priceLineBuffer, INDICATOR_DATA); //--- set the indicators accuracy IndicatorSetInteger(INDICATOR_DIGITS, _Digits + 1); //--- set the first bar from where the index will be drawn PlotIndexSetInteger(0, PLOT_DRAW_BEGIN, _maPeriod); PlotIndexSetInteger(1, PLOT_DRAW_BEGIN, 0); //--- set the indicator shifts when drawing PlotIndexSetInteger(0, PLOT_SHIFT, _maShift); PlotIndexSetInteger(1, PLOT_SHIFT, 0); //--- set the name to be displayed in the MT5 DataWindow IndicatorSetString(INDICATOR_SHORTNAME, "LWMA_Histo" + "(" + string(_maPeriod) + ")"); //--- set the drawing histogram and line to an empty value PlotIndexSetDouble(0, PLOT_EMPTY_VALUE, 0.0); PlotIndexSetDouble(1, PLOT_EMPTY_VALUE, 0.0); } //+------------------------------------------------------------------+ //+------------------------------------------------------------------+ //| Function to calculate the linear weighted moving average | //+------------------------------------------------------------------+ void GetLWMA(int rates_total, int prev_calculated, int begin, const double &price[]) { int weight = 0; int x, l, start; double sum = 0.0, lsum = 0.0; //--- first calculation or number of bars was changed if(prev_calculated <= _maPeriod + begin + 2) { start = _maPeriod + begin; //--- set empty value for first start bars for(x=0; x < start; x++) { maHistogramBuffer[x] = 0.0; priceLineBuffer[x] = price[x]; } } else start = prev_calculated - 1; for(x = start - _maPeriod, l = 1; x < start; x++, l++) { sum += price[x] * l; lsum += price[x]; weight += l; } maHistogramBuffer[start-1] = sum/weight; priceLineBuffer[x] = price[x]; //--- main loop for(x=start; x<rates_total && !IsStopped(); x++) { sum = sum - lsum + price[x] * _maPeriod; lsum = lsum - price[x - _maPeriod] + price[x]; maHistogramBuffer[x] = sum / weight; priceLineBuffer[x] = price[x]; } }Wenn Sie den nutzerdefinierten Indikator speichern und kompilieren, werden Sie feststellen, dass er jetzt keine Warnungen oder Fehler mehr enthält. Öffnen Sie Ihr MetaTrader 5-Handelsterminal, um es zu laden und in Ihren Charts zu testen.
Mehr aktive Beispiele aus der Praxis
Da Sie nun mit den grundlegenden Bausteinen von nutzerdefinierten Indikatoren vertraut sind, sollten wir einige einfache nutzerdefinierte Indikatoren erstellen, um dieses Wissen zu festigen. Wir werden alle Schritte befolgen, die wir zuvor definiert haben, und alle Grundlagen der nutzerdefinierten Indikatoren in den folgenden Beispielen implementieren.
Beispiel 2: Der nutzerdefinierte Indikatoren Spread Monitor - SpreadMonitor.mq5
Lassen Sie uns mit unserem praktischen Ansatz fortfahren und einen weiteren einfachen nutzerdefinierten Indikator erstellen, der Spread-Daten verwendet, um ein mehrfarbiges Histogramm in einem separaten Fenster anzuzeigen. Dieser Indikator ist nützlich für Symbole, die einen gleitenden Spread verwenden, und erleichtert es Ihnen, die Schwankungen oder Spitzen des Spreads im Laufe der Zeit auf visuelle und leicht zu analysierende Weise zu überwachen. Verwenden Sie wie zuvor den MQL-Assistenten, um eine neue nutzerdefinierte Indikatordatei mit dem Namen „SpreadMonitor.mq5“ zu erstellen. Denken Sie daran, sie im Ordner „Artikel“ zu speichern, um eine saubere und organisierte Dateistruktur zu erhalten.
In diesem Beispiel werde ich zeigen, wie man einen mehrfarbigen Indikator erstellt. Wenn die aktuelle Streuung höher ist als die vorherige Streuung, wechselt das Histogramm zu einer roten Farbe, um eine Zunahme der Streuung anzuzeigen, und wenn die aktuelle Streuung niedriger ist als die vorherige Streuung, wechselt das Histogramm zu einer blauen Farbe, um anzuzeigen, dass die Streuung abnimmt. Diese Funktion des nutzerdefinierten Indikators lässt sich am besten bei Symbolen mit einem gleitenden Spread beobachten. Ein kurzer Blick auf den Indikator, wenn er in ein Chart geladen ist, macht es einfach, Perioden zu erkennen, in denen der Spread schnell ansteigt.
Nachdem Sie die neue nutzerdefinierte Indikatordatei „SpreadMonitor.mq5“ mit dem MQL-Assistenten erstellt haben, fügen Sie den folgenden Code hinzu.
Geben Sie zunächst an, wo der Indikator angezeigt werden soll:
//--- indicator window settings #property indicator_separate_window
Geben Sie die Anzahl der Indikatorpuffer und Plots an:
//--- indicator buffers and plots #property indicator_buffers 2 #property indicator_plots 1
Legen Sie den Indikatortyp und -stil fest und bestimmen Sie die verschiedenen Farben:
//--- indicator type and style settings #property indicator_type1 DRAW_COLOR_HISTOGRAM #property indicator_color1 clrDarkBlue, clrTomato #property indicator_style1 0 #property indicator_width1 1 #property indicator_minimum 0.0
Deklarieren Sie die dynamischen Arrays, die als Indikatorpuffer im globalen Bereich verwendet werden sollen:
//--- indicator buffers double spreadDataBuffer[]; double histoColorsBuffer[];
Erstellen Sie die nutzerdefinierte Funktion, die für die Initialisierung unseres Indikators verantwortlich sein wird:
//+------------------------------------------------------------------+ //| User custom function for custom indicator initialization | //+------------------------------------------------------------------+ void GetInit(){ } //+------------------------------------------------------------------+
Innerhalb unserer neuen Indikator-Initialisierungsfunktion „GetInit()“. Registrierung und Zuordnung der Indikatorpuffer:
//--- set and register the indicator buffers mapping SetIndexBuffer(0, spreadDataBuffer, INDICATOR_DATA); SetIndexBuffer(1, histoColorsBuffer, INDICATOR_COLOR_INDEX);
Legen Sie den Namen fest, der im Datenfenster von MetaTrader 5 und als Beschriftung des Unterfensters des Indikators erscheinen soll:
//--- name for mt5 datawindow and the indicator subwindow label IndicatorSetString(INDICATOR_SHORTNAME,"Spread Histogram");
Stellen Sie die Ziffern des Indikators auf Genauigkeit und Präzision ein:
//--- set the indicators accuracy digits IndicatorSetInteger(INDICATOR_DIGITS, 0);
Der nächste Schritt besteht darin, eine nutzerdefinierte Funktion zur Berechnung der Spanne zu erstellen. Wir nennen die Funktion „GetSpreadData()“ und geben an, dass sie drei Parameter oder Argumente enthalten soll. Die Funktion ist vom Typ void , da sie keine Daten zurückgeben muss:
//+------------------------------------------------------------------+ //| Custom function for calculating the spread | //+------------------------------------------------------------------+ void GetSpreadData(const int position, const int rates_total, const int& spreadData[]) { spreadDataBuffer[0] = (double)spreadData[0]; histoColorsBuffer[0] = 0.0; //--- for(int x = position; x < rates_total && !IsStopped(); x++) { double currentSpread = (double)spreadData[x]; double previousSpread = (double)spreadData[x - 1]; //--- calculate and save the spread spreadDataBuffer[x] = currentSpread; if(currentSpread > previousSpread) { histoColorsBuffer[x] = 1.0; //-- set the histogram to clrTomato } else { histoColorsBuffer[x] = 0.0; //-- set the histogram to clrDarkBlue } } //--- } //+------------------------------------------------------------------+
Der nutzerdefinierte Indikator kann nicht mit der leeren Funktion OnCalculate() funktionieren. In diesem Beispiel wird die lange Version von OnCalculate() verwendet, die speziell zehn Parameter für die Speicherung und Verarbeitung der Daten des nutzerdefinierten Indikators verwendet.
//+------------------------------------------------------------------+ //| Custom indicator iteration function | //+------------------------------------------------------------------+ int OnCalculate(const int rates_total, const int prev_calculated, const datetime &time[], const double &open[], const double &high[], const double &low[], const double &close[], const long &tick_volume[], const long &volume[], const int &spread[]) { //--- check if we have enough data start calculating if(rates_total < 2) //--- don't do any calculations, exit and reload function return(0); //--- we have new data, starting the calculations int position = prev_calculated - 1; //--- update the position variable if(position < 1) { spreadDataBuffer[0] = 0; position = 1; } //--- calculate and get the tick volume GetSpreadData(position, rates_total, spread); //--- Exit function and return new prev_calculated value return(rates_total); }
Der nutzerdefinierte SpreadMonitor-Indikator ist fast fertig. Lassen Sie uns nun die verschiedenen Codesegmente zusammenfügen und die Datei speichern, bevor wir sie kompilieren und in ein MetaTrader 5-Chart laden.
//--- indicator window settings #property indicator_separate_window //--- indicator buffers and plots #property indicator_buffers 2 #property indicator_plots 1 //--- indicator type and style settings #property indicator_type1 DRAW_COLOR_HISTOGRAM #property indicator_color1 clrDarkBlue, clrTomato #property indicator_style1 0 #property indicator_width1 1 #property indicator_minimum 0.0 //--- indicator buffers double spreadDataBuffer[]; double histoColorsBuffer[]; //+------------------------------------------------------------------+ //| Custom indicator initialization function | //+------------------------------------------------------------------+ int OnInit() { //--- initialize the indicator GetInit(); return(INIT_SUCCEEDED); } //+------------------------------------------------------------------+ //| Custom indicator iteration function | //+------------------------------------------------------------------+ int OnCalculate(const int rates_total, const int prev_calculated, const datetime &time[], const double &open[], const double &high[], const double &low[], const double &close[], const long &tick_volume[], const long &volume[], const int &spread[]) { //--- check if we have enough data start calculating if(rates_total < 2) //--- don't do any calculations, exit and reload function return(0); //--- we have new data, starting the calculations int position = prev_calculated - 1; //--- update the position variable if(position < 1) { spreadDataBuffer[0] = 0; position = 1; } //--- calculate and get the tick volume GetSpreadData(position, rates_total, spread); //--- Exit function and return new prev_calculated value return(rates_total); } //+------------------------------------------------------------------+ //| Custom function for calculating the spread | //+------------------------------------------------------------------+ void GetSpreadData(const int position, const int rates_total, const int& spreadData[]) { spreadDataBuffer[0] = (double)spreadData[0]; histoColorsBuffer[0] = 0.0; //--- for(int x = position; x < rates_total && !IsStopped(); x++) { double currentSpread = (double)spreadData[x]; double previousSpread = (double)spreadData[x - 1]; //--- calculate and save the spread spreadDataBuffer[x] = currentSpread; if(currentSpread > previousSpread) { histoColorsBuffer[x] = 1.0; //-- set the histogram to clrTomato } else { histoColorsBuffer[x] = 0.0; //-- set the histogram to clrDarkBlue } } //--- } //+------------------------------------------------------------------+ //+------------------------------------------------------------------+ //| User custom function for custom indicator initialization | //+------------------------------------------------------------------+ void GetInit() { //--- set and register the indicator buffers mapping SetIndexBuffer(0, spreadDataBuffer, INDICATOR_DATA); SetIndexBuffer(1, histoColorsBuffer, INDICATOR_COLOR_INDEX); //--- name for mt5 datawindow and the indicator subwindow label IndicatorSetString(INDICATOR_SHORTNAME,"Spread Histogram"); //--- set the indicators accuracy digits IndicatorSetInteger(INDICATOR_DIGITS, 0); } //+------------------------------------------------------------------+
Hier ist der SpreadMonitor-Indikator in einem Fünf-Minuten-Chart des MetaTrader 5 GBPJPY geladen.
Beispiel 3: Geglätteter mehrfarbiger Kerzen Custom Indicator - SmoothedCandlesticks.mq5
Da Indikatoren hauptsächlich visuelle Darstellungen von Handelsstrategien sind, sind mehrfarbige Indikatoren solchen vorzuziehen, die nur visuelle Daten in einer einzigen Farbe anzeigen. Mehrfarbige Indikatoren machen es Händlern leicht, die vom Indikator generierten Handelssignale schnell zu erkennen, was die Effizienz und Nutzerfreundlichkeit des Indikators erhöht.
Die Fähigkeit, mehrfarbige Indikatoren zu erstellen, ist eine sehr nützliche Fähigkeit für jeden MQL5-Entwickler. In diesem Beispiel werde ich Ihnen zeigen, wie Sie einen geglätteten, mehrfarbigen Kerzen-Indikator erstellen können. Die Beseitigung des Marktrauschens war schon immer eine Priorität für Händler, und dieser einfache Indikator erreicht dies, indem er die Berechnungen der geglätteten gleitenden Durchschnitte nutzt, um mehrfarbige Kerzen zu erstellen, die ihre Farbe entsprechend dem Signal der gleitenden Durchschnitte ändern. Auf diese Weise entsteht ein übersichtliches Chart, das leicht zu interpretieren ist und die Verwendung des Fadenkreuzes auf dem Chart überflüssig macht, um zu erkennen, ob ein gleitender Durchschnitt den anderen gekreuzt hat.
Die Kerzen werden grün, wenn der Eröffnungs-, Höchst-, Tiefst- und Schlusskurs über dem geglätteten gleitenden Durchschnitt liegt, und sie werden rot, wenn der Eröffnungs-, Höchst-, Tiefst- und Schlusskurs unter dem geglätteten gleitenden Durchschnitt liegt. Wenn der geglättete, gleitende Durchschnitt einen beliebigen Teil des Kerzenkörpers berührt, d. h. zwischen dem Höchst- und dem Tiefstkurs der Kerze liegt, wird die Kerze dunkelgrau, um anzuzeigen, dass der geglättete Indikator kein Einstiegssignal erzeugt.
In diesem Beispiel wird auch demonstriert, wie die MQL5-Standardindikatoren durch die vordefinierten technischen MQL5-Indikatorfunktionen, die wir bereits besprochen haben, verwendet werden können. Verwenden Sie den MQL5-Assistenten, um eine neue nutzerdefinierte Indikatordatei zu erstellen, und nennen Sie sie „SmoothedCandlesticks.mq5“. Vergessen Sie nicht, sie im Ordner „Artikel“ zusammen mit den anderen nutzerdefinierten Indikator-Dateien zu speichern, die wir zuvor erstellt haben.
Spezifikation der #property-Direktiven des Indikators
Zunächst legen wir fest, wo der Indikator angezeigt werden soll, entweder im Chartfenster oder in einem separaten Fenster unterhalb des Kurscharts. Der Indikator funktioniert in allen Szenarien, und Sie können ihn abwechselnd in einem separaten Fenster und im Chart-Fenster anzeigen lassen, um zu testen, wie er visuell erscheint.
//--- specify where to display the indicator #property indicator_separate_window //#property indicator_chart_window
Angabe der Indikatorpuffer. In diesem Indikator werden wir sechs Indikatorpuffer verwenden, um unsere Daten darzustellen und anzuzeigen. Es gibt vier Indikatorpuffer für den Eröffnungs-, Schluss-, Hoch- und Tiefstkurs der Kerze. Ein Indikatorpuffer für die geglättete gleitende Durchschnittslinie und ein weiterer Indikatorpuffer zum Speichern der Farben für unsere Kerzen. Insgesamt gibt es also sechs Indikatorpuffer.
//--- indicator buffers #property indicator_buffers 6
Wir benötigen zwei Indikatorplots für die Zeichnung unseres Indikators. Ein Chart zum Zeichnen der Kerzen und ein weiteres Chart für die geglättete gleitende Durchschnittslinie. Dies ergibt insgesamt zwei Indikatorplots.
//--- indicator plots #property indicator_plots 2
Festlegen der Details für die Darstellung der geglätteten Kerzen: Dazu gehören Werte für den Typ, die Farbe und die Bezeichnung. Das Etikett wird im Datenfenster zusammen mit den entsprechenden Daten wie dem Preis angezeigt. Unser Indikator verwendet mehrfarbige Kerzen, und wir müssen insgesamt drei Farben bereitstellen, die sich je nach aktuellem Handelssignal ändern, wie ich bereits erklärt hatte. Der Nutzer hat die Möglichkeit, die angegebenen Farben zu ändern, bevor er den Indikator in das Chart im Indikator-Panel lädt.
//--- plots1 details for the smoothed candles #property indicator_type1 DRAW_COLOR_CANDLES #property indicator_color1 clrDodgerBlue, clrTomato, clrDarkGray #property indicator_label1 "Smoothed Candle Open;Smoothed Candle High;Smoothed Candle Low;Smoothed Candle Close;"
Wiederholen Sie den obigen Schritt und geben Sie die zweiten Details für die geglättete gleitende Durchschnittslinie an. Wir werden nur eine Farbe für die geglättete Linie angeben, da es sich nicht um eine mehrfarbige Linie handelt.
//--- plots2 details for the smoothing line #property indicator_label2 "Smoothing Line" #property indicator_type2 DRAW_LINE #property indicator_color2 clrGoldenrod #property indicator_style2 STYLE_SOLID #property indicator_width2 2
Nutzereingabevariablen im globalen Bereich
Deklaration der Nutzereingabevariablen zur Erfassung und Speicherung der geglätteten gleitenden Durchschnittsperiode und des angewandten Preises für die Berechnungen.
//--- user input parameters for the moving averages input int _maPeriod = 50; // Period input ENUM_APPLIED_PRICE _maAppliedPrice = PRICE_CLOSE; // Applied Price
Indikatorvariablen, Puffer und technische Indikatorhandles im Global Scope
Hier werden wir dynamische Arrays deklarieren, um unsere Indikatorpuffer zu speichern. Wir hatten zuvor die #property-Direktive verwendet, um sechs Indikatorpuffer zuzuweisen. Beginnen wir mit der Deklaration von fünf Indikatorpuffern für die Speicherung von Open, Close, High, Low und Kerzenfarbe. Der verbleibende Puffer für die geglättete Linie wird weiter unten deklariert, da wir die Funktion des technischen Standardindikators iMA von MQL5 verwenden werden, um diesen Puffer zu verwalten.
//--- indicator buffers double openBuffer[]; double highBuffer[]; double lowBuffer[]; double closeBuffer[]; double candleColorBuffer[];
Als Nächstes deklarieren wir den Puffer für die geglättete Linie und ein Handle, um Zugang zur Funktion des technischen Indikators iMA zu erhalten. Die Verwendung der bereits erstellten MQL5-Standardfunktion für technische Indikatoren spart uns Zeit und ist effizienter, da alle Glättungsberechnungen für die Kerzen mit weniger Code effizient durchgeführt werden.
//Moving average dynamic array (buffer) and variables double iMA_Buffer[]; int maHandle; //stores the handle of the iMA indicator
Hier deklarieren und initialisieren wir unsere letzte globale Variable „barsCalculated“ mit einem Wert von Null. Diese Integer-Variable wird verwendet, um die Anzahl der Balken zu speichern, die aus dem geglätteten gleitenden Durchschnitt des iMA berechnet wurden. Wir werden sie in der Funktion OnCalculate() verwenden.
//--- integer to store the number of values in the moving average indicator int barsCalculated = 0;
Nutzerdefinierte Funktion für die Initialisierung des Indikators - GetInit()
Nun, da wir mit dem Kopfteil unseres nutzerdefinierten Indikators fertig sind, werden wir eine nutzerdefinierte Funktion erstellen, um alle Initialisierungsaufgaben durchzuführen. Nennen wir die Funktion „GetInit()“ und geben wir an, dass sie einen booleschen Wert zurückgeben soll, der angibt, ob die Initialisierung des Indikators erfolgreich war. Wenn die Initialisierung fehlschlägt, sollte sich der Indikator beenden.
In der Initialisierungsfunktion führen wir einige wichtige Aufgaben aus, wie z. B. das Einrichten und Registrieren der Indikatorpuffer, das Speichern des Kurznamens für den Indikator und das Erstellen des iMA-Handles für die Glättungslinie, neben anderen grundlegenden Initialisierungsaufgaben.
//+------------------------------------------------------------------+ //| User custom function for custom indicator initialization | //+------------------------------------------------------------------+ bool GetInit() { //--- set the indicator buffer mapping by assigning the indicator buffer array SetIndexBuffer(0, openBuffer, INDICATOR_DATA); SetIndexBuffer(1, highBuffer, INDICATOR_DATA); SetIndexBuffer(2, lowBuffer, INDICATOR_DATA); SetIndexBuffer(3, closeBuffer, INDICATOR_DATA); SetIndexBuffer(4, candleColorBuffer, INDICATOR_COLOR_INDEX); //--- buffer for iMA SetIndexBuffer(5, iMA_Buffer, INDICATOR_DATA); //--- set the price display precision to digits similar to the symbol prices IndicatorSetInteger(INDICATOR_DIGITS, _Digits); //--- set the symbol, timeframe, period and smoothing applied price of the indicator as the short name string indicatorShortName = StringFormat("SmoothedCandles(%s, Period %d, %s)", _Symbol, _maPeriod, EnumToString(_maAppliedPrice)); IndicatorSetString(INDICATOR_SHORTNAME, indicatorShortName); //IndicatorSetString(INDICATOR_SHORTNAME, "Smoothed Candlesticks"); //--- set line drawing to an empty value PlotIndexSetDouble(0, PLOT_EMPTY_VALUE, 0.0); //--- create the maHandle of the smoothing indicator maHandle = iMA(_Symbol, PERIOD_CURRENT, _maPeriod, 0, MODE_SMMA, _maAppliedPrice); //--- check if the maHandle is created or it failed if(maHandle == INVALID_HANDLE) { //--- creating the handle failed, output the error code ResetLastError(); PrintFormat("Failed to create maHandle of the iMA for symbol %s, error code %d", _Symbol, GetLastError()); //--- we terminate the program and exit the init function return(false); } return(true); // return true, initialization of the indicator ok } //+------------------------------------------------------------------+
Nachdem Sie die Funktion „GetInit()“ erstellt haben, rufen Sie sie in der Standard-Indikatorfunktion „OnInit()“ auf, damit sie die ihr zugedachte Aufgabe erfüllt.
//+------------------------------------------------------------------+ //| Custom indicator initialization function | //+------------------------------------------------------------------+ int OnInit() { //--- call the custom initialization function if(!GetInit()) { return(INIT_FAILED); //-- if initialization failed terminate the app } //--- return(INIT_SUCCEEDED); }
Indikator Haupt-Wiederholungsfunktion - OnCalculate()
Die nächste Aufgabe besteht darin, die Standardfunktion „OnCalculate(....)“ zu codieren, um alle Berechnungen des Indikators durchzuführen. In diesem Beispiel verwenden wir die lange Version dieser Standardfunktion, die insgesamt zehn Parameter hat. Diese Version von „OnCalculate(...)“ basiert auf Berechnungen aus der aktuellen Zeitreihe. Hier sind die Parameter, die er enthält:
- rates_total: Enthält die Gesamtzahl der Balken im Chart, wenn der Indikator gestartet wird, und wird aktualisiert, um den aktuellen Stand der insgesamt verfügbaren Balken wiederzugeben, wenn neue Balken oder Daten geladen werden.
- prev_calculated: Speichert die Anzahl der bereits bearbeiteten Balken beim vorherigen Aufruf. Es hilft uns zu wissen, welche Daten wir bereits berechnet oder verarbeitet haben, sodass wir nicht jeden Balken bei jedem OnCalculate(...) -Funktionsaufruf oder beim Eintreffen neuer Balken berechnen müssen. OnCalculate(..) gibt bei jedem Aufruf eine aktualisierte Version dieser Variablen zurück.
- time, open, high, low, close, tick_volume, volume und spread (Eröffnungszeit, Eröffnungskurs, Hoch, Tief, Schlusskurs, Tick_Volumen, Volumen und Spread): Es ist leicht zu erkennen, was diese Arrays enthalten, da ihre Namen angeben, welche Balkendaten sie speichern und aufbewahren. Unser nutzerdefinierter Indikator wird von den Daten aus diesen Arrays abhängig sein und er wird zeigen, wie man sie nutzen kann, insbesondere für einen Kerzen-basierten Indikator wie den unseren.
Wir fügen den Funktionskörper OnCalculate(...) zu unserem Code hinzu, da er alle relevanten Berechnungen für die Darstellung der geglätteten mehrfarbigen Kerzen und der Glättungslinie enthält.
//+------------------------------------------------------------------+ //| Custom indicator iteration function | //+------------------------------------------------------------------+ int OnCalculate(const int rates_total, const int prev_calculated, const datetime &time[], const double &open[], const double &high[], const double &low[], const double &close[], const long &tick_volume[], const long &volume[], const int &spread[]) { //--- declare a int to save the number of values copied from the iMA indicator int iMA_valuesToCopy; //--- find the number of values already calculated in the indicator int iMA_calculated = BarsCalculated(maHandle); if(iMA_calculated <= 0) { PrintFormat("BarsCalculated() for iMA handle returned %d, error code %d", iMA_calculated, GetLastError()); return(0); } int start; //--- check if it's the indicators first call of OnCalculate() or we have some new uncalculated data if(prev_calculated == 0) { //--- set all the buffers to the first index lowBuffer[0] = low[0]; highBuffer[0] = high[0]; openBuffer[0] = open[0]; closeBuffer[0] = close[0]; start = 1; if(iMA_calculated > rates_total) iMA_valuesToCopy = rates_total; else //--- copy the calculated bars which are less than the indicator buffers data iMA_valuesToCopy = iMA_calculated; } else start = prev_calculated - 1; iMA_valuesToCopy = (rates_total - prev_calculated) + 1; //--- fill the iMA_Buffer array with values of the Moving Average indicator //--- reset error code ResetLastError(); //--- copy a part of iMA_Buffer array with data in the zero index of the the indicator buffer if(CopyBuffer(maHandle, 0, 0, iMA_valuesToCopy, iMA_Buffer) < 0) { //--- if the copying fails, print the error code PrintFormat("Failed to copy data from the iMA indicator, error code %d", GetLastError()); //--- exit the function with zero result to specify that the indicator calculations were not executed return(0); } //--- iterate through the main calculations loop and execute all the calculations for(int x = start; x < rates_total && !IsStopped(); x++) { //--- save all the candle array prices in new non-array variables for quick access double candleOpen = open[x]; double candleClose = close[x]; double candleHigh = high[x]; double candleLow = low[x]; lowBuffer[x] = candleLow; highBuffer[x] = candleHigh; openBuffer[x] = candleOpen; closeBuffer[x] = candleClose; //--- scan for the different trends signals and set the required candle color candleColorBuffer[x] = 2.0; // set color clrDarkGray - default (signal for no established trend) if(candleOpen > iMA_Buffer[x] && candleClose > iMA_Buffer[x] && candleHigh > iMA_Buffer[x] && candleLow > iMA_Buffer[x]) candleColorBuffer[x]=0.0; // set color clrDodgerBlue - signal for a long/buy trend if(candleOpen < iMA_Buffer[x] && candleClose < iMA_Buffer[x] && candleHigh < iMA_Buffer[x] && candleLow < iMA_Buffer[x]) candleColorBuffer[x]=1.0; // set color clrTomato - signal for a short/sell trend } //--- return the rates_total which includes the prev_calculated value for the next call return(rates_total); }
Funktion zur Deinitialisierung von Indikatoren - OnDeinit()
Die letzte Funktion ist die Standardfunktion „OnDeinit()“ zur Deinitialisierung aller Variablen und Arrays, die freigegeben werden müssen. Alle Puffer-Arrays werden automatisch verwaltet und müssen mit Ausnahme des iMA-Handles nicht freigegeben oder deinitialisiert werden. Um sicherzustellen, dass unser Indikator alle ungenutzten Ressourcen freigibt, wenn er beendet wird, verwenden wir die MQL5-Funktion „IndicatorRelease()“, um alle Ressourcen freizugeben, die von der Variablen „maHandle“ verbraucht werden.
//+------------------------------------------------------------------+ //| Indicator deinitialization function | //+------------------------------------------------------------------+ void OnDeinit(const int reason) { if(maHandle != INVALID_HANDLE) { IndicatorRelease(maHandle);//-- clean up and release the iMA handle } } //+------------------------------------------------------------------+
Unser Indikator ist nun fast vollständig, hier sind die Codesegmente zusammengefasst. Achten Sie darauf, dass Ihr Code die verschiedenen Segmente in dieser Reihenfolge enthält.
//--- specify where to display the indicator #property indicator_separate_window //#property indicator_chart_window //--- indicator buffers #property indicator_buffers 6 //--- indicator plots #property indicator_plots 2 //--- plots1 details for the smoothed candles #property indicator_type1 DRAW_COLOR_CANDLES #property indicator_color1 clrDodgerBlue, clrTomato, clrDarkGray #property indicator_label1 "Smoothed Candle Open;Smoothed Candle High;Smoothed Candle Low;Smoothed Candle Close;" //--- plots2 details for the smoothing line #property indicator_label2 "Smoothing Line" #property indicator_type2 DRAW_LINE #property indicator_color2 clrGoldenrod #property indicator_style2 STYLE_SOLID #property indicator_width2 2 //--- user input parameters for the moving averages input int _maPeriod = 50; // Period input ENUM_APPLIED_PRICE _maAppliedPrice = PRICE_CLOSE; // Applied Price //--- indicator buffers double openBuffer[]; double highBuffer[]; double lowBuffer[]; double closeBuffer[]; double candleColorBuffer[]; //Moving average dynamic array (buffer) and variables double iMA_Buffer[]; int maHandle; //stores the handle of the iMA indicator //--- integer to store the number of values in the moving average indicator int barsCalculated = 0; //+------------------------------------------------------------------+ //| Custom indicator initialization function | //+------------------------------------------------------------------+ int OnInit() { //--- call the custom initialization function if(!GetInit()) { return(INIT_FAILED); //-- if initialization failed terminate the app } //--- return(INIT_SUCCEEDED); } //+------------------------------------------------------------------+ //| Custom indicator iteration function | //+------------------------------------------------------------------+ int OnCalculate(const int rates_total, const int prev_calculated, const datetime &time[], const double &open[], const double &high[], const double &low[], const double &close[], const long &tick_volume[], const long &volume[], const int &spread[]) { //--- declare a int to save the number of values copied from the iMA indicator int iMA_valuesToCopy; //--- find the number of values already calculated in the indicator int iMA_calculated = BarsCalculated(maHandle); if(iMA_calculated <= 0) { PrintFormat("BarsCalculated() for iMA handle returned %d, error code %d", iMA_calculated, GetLastError()); return(0); } int start; //--- check if it's the indicators first call of OnCalculate() or we have some new uncalculated data if(prev_calculated == 0) { //--- set all the buffers to the first index lowBuffer[0] = low[0]; highBuffer[0] = high[0]; openBuffer[0] = open[0]; closeBuffer[0] = close[0]; start = 1; if(iMA_calculated > rates_total) iMA_valuesToCopy = rates_total; else //--- copy the calculated bars which are less than the indicator buffers data iMA_valuesToCopy = iMA_calculated; } else start = prev_calculated - 1; iMA_valuesToCopy = (rates_total - prev_calculated) + 1; //--- fill the iMA_Buffer array with values of the Moving Average indicator //--- reset error code ResetLastError(); //--- copy a part of iMA_Buffer array with data in the zero index of the the indicator buffer if(CopyBuffer(maHandle, 0, 0, iMA_valuesToCopy, iMA_Buffer) < 0) { //--- if the copying fails, print the error code PrintFormat("Failed to copy data from the iMA indicator, error code %d", GetLastError()); //--- exit the function with zero result to specify that the indicator calculations were not executed return(0); } //--- iterate through the main calculations loop and execute all the calculations for(int x = start; x < rates_total && !IsStopped(); x++) { //--- save all the candle array prices in new non-array variables for quick access double candleOpen = open[x]; double candleClose = close[x]; double candleHigh = high[x]; double candleLow = low[x]; lowBuffer[x] = candleLow; highBuffer[x] = candleHigh; openBuffer[x] = candleOpen; closeBuffer[x] = candleClose; //--- scan for the different trends signals and set the required candle color candleColorBuffer[x] = 2.0; // set color clrDarkGray - default (signal for no established trend) if(candleOpen > iMA_Buffer[x] && candleClose > iMA_Buffer[x] && candleHigh > iMA_Buffer[x] && candleLow > iMA_Buffer[x]) candleColorBuffer[x]=0.0; // set color clrDodgerBlue - signal for a long/buy trend if(candleOpen < iMA_Buffer[x] && candleClose < iMA_Buffer[x] && candleHigh < iMA_Buffer[x] && candleLow < iMA_Buffer[x]) candleColorBuffer[x]=1.0; // set color clrTomato - signal for a short/sell trend } //--- return the rates_total which includes the prev_calculated value for the next call return(rates_total); } //+------------------------------------------------------------------+ //| Indicator deinitialization function | //+------------------------------------------------------------------+ void OnDeinit(const int reason) { if(maHandle != INVALID_HANDLE) { IndicatorRelease(maHandle);//-- clean up and release the iMA handle } } //+------------------------------------------------------------------+ //+------------------------------------------------------------------+ //| User custom function for custom indicator initialization | //+------------------------------------------------------------------+ bool GetInit() { //--- set the indicator buffer mapping by assigning the indicator buffer array SetIndexBuffer(0, openBuffer, INDICATOR_DATA); SetIndexBuffer(1, highBuffer, INDICATOR_DATA); SetIndexBuffer(2, lowBuffer, INDICATOR_DATA); SetIndexBuffer(3, closeBuffer, INDICATOR_DATA); SetIndexBuffer(4, candleColorBuffer, INDICATOR_COLOR_INDEX); //--- buffer for iMA SetIndexBuffer(5, iMA_Buffer, INDICATOR_DATA); //--- set the price display precision to digits similar to the symbol prices IndicatorSetInteger(INDICATOR_DIGITS, _Digits); //--- set the symbol, timeframe, period and smoothing applied price of the indicator as the short name string indicatorShortName = StringFormat("SmoothedCandles(%s, Period %d, %s)", _Symbol, _maPeriod, EnumToString(_maAppliedPrice)); IndicatorSetString(INDICATOR_SHORTNAME, indicatorShortName); //IndicatorSetString(INDICATOR_SHORTNAME, "Smoothed Candlesticks"); //--- set line drawing to an empty value PlotIndexSetDouble(0, PLOT_EMPTY_VALUE, 0.0); //--- create the maHandle of the smoothing indicator maHandle = iMA(_Symbol, PERIOD_CURRENT, _maPeriod, 0, MODE_SMMA, _maAppliedPrice); //--- check if the maHandle is created or it failed if(maHandle == INVALID_HANDLE) { //--- creating the handle failed, output the error code ResetLastError(); PrintFormat("Failed to create maHandle of the iMA for symbol %s, error code %d", _Symbol, GetLastError()); //--- we terminate the program and exit the init function return(false); } return(true); // return true, initialization of the indicator ok } //+------------------------------------------------------------------+
Speichern und kompilieren Sie den Indikatorcode, und er wird ohne Fehler oder Warnungen kompiliert. Laden Sie es in MetaTrader 5 und testen Sie, wie es sich mit verschiedenen Nutzereingabeparametern verhält.
Schlussfolgerung
In diesem Artikel haben Sie gelernt, was Indikatoren sind, die verschiedenen Arten von Indikatoren in der MetaTrader 5-Plattform, die verschiedenen Komponenten und Bausteine von nutzerdefinierten Indikatoren, und bekamen aus erster Hand praktische Erfahrung durch die Entwicklung von ein paar nutzerdefinierte Indikatoren mit MQL5 von Grund auf.
Die Entwicklung von nutzerdefinierten Indikatoren mit MQL5 ist ein komplexes Thema, das in einem einzigen Artikel nicht vollständig behandelt werden kann. Daher werden wir in den kommenden Artikeln weitere fortgeschrittene Bereiche behandeln. Mit dem Wissen, das Sie in diesem Artikel erworben haben, sind Sie nun in der Lage, Ihre eigenen einfachen nutzerdefinierten Indikatoren zu entwickeln. Ich fordere Sie auf, Ihre Programmierfähigkeiten weiter zu üben und wünsche Ihnen alles Gute auf Ihrem Weg zum Programmieren.
Übersetzt aus dem Englischen von MetaQuotes Ltd.
Originalartikel: https://www.mql5.com/en/articles/14481
- 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.