MQL5: Erstellen Ihres eigenen Indikators
Einleitung
Was ist ein Indikator? Es ist ein Satz berechneter Werte, die auf praktische Weise auf dem Bildschirm angezeigt werden sollen. Sätze von Werten werden in Programmen als Arrays dargestellt. Somit bedeutet das Erstellen eines Indikators, einen Algorithmus zu schreiben, der bestimmte Arrays bearbeitet (Preis-Arrays) und die Ergebnisse der Bearbeitung für andere Arrays (Indikator-Arrays) aufzeichnet.
Obwohl es zahlreiche fertige Indikatoren gibt, die bereits als Klassiker gelten, wird immer die Notwendigkeit bleiben, eigene Indikatoren zu erstellen. Solche Indikatoren, die wir mithilfe unserer eigenen Algorithmen erstellen, heißen benutzerdefinierte Indikatoren. In diesem Beitrag besprechen wir, wie ein einfacher benutzerdefinierter Indikator erstellt wird.
Indikatoren sind unterschiedlich
Ein Indikator kann in Form von farbigen Linien oder Bereichen dargestellt oder durch spezielle Labels, die auf günstige Momente für die Eingabe von Positionen hindeuten, angezeigt werden. Diese Typen können auch kombiniert werden, was noch mehr Typen von Indikatoren ermöglicht. Wir konzentrieren uns auf die Erstellung eines Indikators basierend auf dem Beispiel des bekannten, von William Blau entwickelten True Strength Index.
True Strength Index
Der TSI-Indikator basiert auf einem doppelt geglätteten Momentum, um Trends sowie überverkaufte/überkaufte Bereiche zu erkennen. Die mathematische Erklärung finden Sie in Momentum, Direction, and Divergence von William Blau. Hier berücksichtigen wir nur die Berechnungsformel.
TSI(CLOSE,r,s) =100*EMA(EMA(mtm,r),s) / EMA(EMA(|mtm|,r),s)
Dabei bedeutet:
- mtm = CLOSEcurrent - CLOSprev, Bereich von Werten, der die Differenz zwischen Schließungspreisen des aktuellen Bars und denen des vorherigen kennzeichnet;
- EMA(mtm,r) = exponentielle Glättung von mtm-Werten mit Periodenlänge gleich r;
- EMA(EMA(mtm,r),s)= exponentielle Glättung von EMA(mtm,r)-Werten mit Periode s;
- |mtm| = absolute mtm-Werte;
- r = 25,
- s = 13.
Aus dieser Formel können wir drei Parameter ableiten, die die Berechnung des Indikators beeinflussen. Dies sind die Perioden r und s sowie der Typ von Preisen in den Berechnungen. In unserem Fall wählen wir den CLOSE-Preis.
MQL5 Wizard
Stellen wir den TSI als blaue Linie dar. An dieser Stelle müssen wir den MQL5 Wizard starten. Als Erstes müssen wir den Typ von Programm angeben, den wir erstellen wollen – benutzerdefinierter Indikator. Als Zweites geben wir den Namen des Programms, die Parameter r und s und ihre Werte ein.
Anschließend legen wir fest, dass der Indikator als blaue Linie in einem separaten Fenster angezeigt werden soll, und kennzeichnen diese Linie als TSI.
Alle Eingangsdaten wurden eingegeben, also klicken wir auf Fertig und erhalten einen Entwurf unseres Indikators.
//+------------------------------------------------------------------+ //| True Strength Index.mq5 | //| Copyright 2009, MetaQuotes Software Corp. | //| https://www.mql5.com | //+------------------------------------------------------------------+ #property copyright "2009, MetaQuotes Software Corp." #property link "https://www.mql5.com" #property version "1.00" #property indicator_separate_window #property indicator_buffers 1 #property indicator_plots 1 //---- plot TSI #property indicator_label1 "TSI" #property indicator_type1 DRAW_LINE #property indicator_color1 Blue #property indicator_style1 STYLE_SOLID #property indicator_width1 1 //--- input parameters input int r=25; input int s=13; //--- indicator buffers double TSIBuffer[]; //+------------------------------------------------------------------+ //| Custom indicator initialization function | //+------------------------------------------------------------------+ int OnInit() { //--- indicator buffers mapping SetIndexBuffer(0,TSIBuffer,INDICATOR_DATA); //--- return(0); } //+------------------------------------------------------------------+ //| 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[]) { //--- //--- return value of prev_calculated for next call return(rates_total); } //+------------------------------------------------------------------+
Der MQL5 Wizard erstellt den Header des Indikators, in den er die Eigenschaften des Indikators schreibt:
- Indikator wird in einem separaten Fenster angezeigt;
- Anzahl der Indikatorpuffer, indicator_buffers=1;
- Anzahl der grafischen Darstellungen, indicator_plots= 1;
- Name der Darstellung Nr. 1, indicator_label1="TSI";
- Stil der ersten Darstellung, Linie, indicator_type1=DRAW_LINE;
- Farbe der Darstellung Nr. 1, indicator_color1=Blue;
- Stil der Linie, indicator_style1=STYLE_SOLID;
- Linienbreite für die Darstellung 1, indicator_width1=1.
Alle Vorbereitungen sind abgeschlossen. Nun können wir unseren Code verfeinern und verbessern.
OnCalculate()
Die Funktion OnCalculate() ist der Handler des Calculate-Ereignisses, der bei Bedarf in Aktion tritt, um Indikatorwerte neu zu berechnen und das Diagramm neu zu zeichnen. Das ist das Ereignis eines neuen Tick-Eingangs, einer Aktualisierung der Symbol-Historie usw. Deshalb muss der Hauptcode für alle Berechnungen von Indikatorwerten sich in genau dieser Funktion befinden.
Natürlich können Hilfsberechnungen in anderen Funktionen implementiert werden, aber diese Funktionen müssen im OnCalculate-Handler verwendet werden.
Standardmäßig erstellt der MQL5 Wizard die zweite Form von OnCalculate(), die Zugriff auf alle Typen von Zeitreihen ermöglicht:
- Open-, High-, Low-, Close-Preise;
- Volumina (real und/oder Tick);
- Differenzen;
- Perioden-Eröffnungszeit.
In unserem Fall benötigen wir allerdings nur ein Daten-Array. Deshalb ändern wir OnCalculate() zur ersten Form des Aufrufs.
int OnCalculate (const int rates_total, // size of the price[] array const int prev_calculated, // number of available bars at the previous call const int begin, // from what index in price[] authentic data start const double& price[]) // array, on which the indicator will be calculated { //--- //--- return value of prev_calculated for next call return(rates_total); }
Dadurch können wir den Indikator weiterhin nicht nur auf Preisdaten anwenden, sondern auch einen Indikator erstellen, der auf Werten anderer Indikatoren basiert.
Wählen wir in der Registerkarte Parameters Close (Standardauswahl), enthält das Array price[], das an OnCalculate() übergeben wird, Schließungspreise. Wählen wir beispielsweise Typical Price (Typischer Preis), enthält das Array price[] die Preise (High+Low+Close)/3 für jede Periode.
Der Parameter rates_total kennzeichnet die Größe des Arrays price[]. Dies wird für die zyklusmäßige Organisation von Berechnungen nützlich sein. Die Indizierung der Elemente in price[] beginnt bei Null und bewegt sich von der Vergangenheit in Richtung Zukunft. Das bedeutet, das Element price[0] enthält den ältesten wert, während price[rates_total-1] das aktuellste Array-Element enthält.
Organisieren von Indikator-Hilfspuffern
Es wird nur eine Linie im Diagramm angezeigt, d. h. die Daten eines Indikator-Arrays. Vorher müssen wir allerdings noch die Zwischenberechnungen organisieren. Zwischendaten werden in Indikator-Arrays gespeichert, die durch das Attribut INDICATOR_CALCULATIONS gekennzeichnet werden. In der Formel sehen wir, dass wir zusätzliche Arrays benötigen:
- für mtm-Werte – Array MTMBuffer[];
- für |mtm|-Werte – Array AbsMTMBuffer[];
- für EMA(mtm,r) – Array EMA_MTMBuffer[];
- für EMA(EMA(mtm,r),s) – Array EMA2-MTMBuffer[];
- für EMA(|mtm|,r) – Array EMA_AbsMTMBuffer[];
- für EMA(EMA(|mtm|,r),s) – Array EMA2_AbsMTMBuffer[].
Insgesamt müssen wir 6 weitere double-Arrays auf globaler Ebene hinzufügen und diese Arrays mit den Indikatorpuffern in der OnInit()-Funktion verbinden. Vergessen Sie nicht, die neue Anzahl von Indikatorpuffern anzugeben. Die Eigenschaft indicator_buffers muss gleich 7 sein (es gab 1 und 6 weitere Puffer wurden hinzugefügt).
#property indicator_buffers 7
Jetzt sieht der Indikatorcode folgendermaßen aus:
#property indicator_separate_window #property indicator_buffers 7 #property indicator_plots 1 //---- plot TSI #property indicator_label1 "TSI" #property indicator_type1 DRAW_LINE #property indicator_color1 Blue #property indicator_style1 STYLE_SOLID #property indicator_width1 1 //--- input parameters input int r=25; input int s=13; //--- indicator buffers double TSIBuffer[]; double MTMBuffer[]; double AbsMTMBuffer[]; double EMA_MTMBuffer[]; double EMA2_MTMBuffer[]; double EMA_AbsMTMBuffer[]; double EMA2_AbsMTMBuffer[]; //+------------------------------------------------------------------+ //| Custom indicator initialization function | //+------------------------------------------------------------------+ int OnInit() { //--- indicator buffers mapping SetIndexBuffer(0,TSIBuffer,INDICATOR_DATA); SetIndexBuffer(1,MTMBuffer,INDICATOR_CALCULATIONS); SetIndexBuffer(2,AbsMTMBuffer,INDICATOR_CALCULATIONS); SetIndexBuffer(3,EMA_MTMBuffer,INDICATOR_CALCULATIONS); SetIndexBuffer(4,EMA2_MTMBuffer,INDICATOR_CALCULATIONS); SetIndexBuffer(5,EMA_AbsMTMBuffer,INDICATOR_CALCULATIONS); SetIndexBuffer(6,EMA2_AbsMTMBuffer,INDICATOR_CALCULATIONS); //--- return(0); } //+------------------------------------------------------------------+ //| Custom indicator iteration function | //+------------------------------------------------------------------+ int OnCalculate (const int rates_total, // size of the price[] array; const int prev_calculated,// number of available bars; // during the previous call; const int begin, // from what index in // price[] authentic data start; const double& price[]) // array, on which the indicator will be calculated; { //--- //--- return value of prev_calculated for next call return(rates_total); }
Zwischenberechnungen
Es ist sehr einfach, die Berechnung der Werte für die Puffer MTMBuffer[] und AbsMTMBuffer[] zu organisieren. Gehen Sie in der Schleife die Werte von price[1] bis price[rates_total-1] Stück für Stück durch und schreiben Sie die Differenz in ein Array und den absoluten Differenzwert in das zweite.
//--- calculate values of mtm and |mtm| for(int i=1;i<rates_total;i++) { MTMBuffer[i]=price[i]-price[i-1]; AbsMTMBuffer[i]=fabs(MTMBuffer[i]); }
Der nächste Schritt ist die Berechnung des exponentiellen Durchschnitts dieser Arrays. Dies kann auf zwei Arten bewerkstelligt werden. Entweder versuchen wir, den gesamten Algorithmus fehlerfrei zu schreiben, oder wir nutzen fertige Funktionen, die bereits debuggt und genau für diese Zwecke gedacht sind.
In MQL5 gibt es keine integrierten Funktionen für die Berechnung gleitender Mittelwerte nach Array-Werten, doch mit MovingAverages.mqh gibt es eine fertige Bibliothek von Funktionen. Der vollständige Pfad lautet terminal_directory/MQL5/Include/MovingAverages.mqh. terminal_directory ist ein Katalog, in dem das MetaTrader-5-Terminal installiert ist. Die Bibliothek ist eine Include-Datei. Sie enthält Funktionen für die Berechnung von gleitenden Mittelwerten in Arrays mithilfe einer von vier herkömmlichen Methoden:
- einfache Mittelung;
- exponentielle Mittelung;
- geglättete Mittelung;
- lineare gewichtete Mittelung.
Fügen Sie in jedem beliebigen MQL5-Programm Folgendes in das Code-Heading ein, um diese Funktionen zu nutzen:
#include <MovingAverages.mqh>
Wir brauchen die Funktion ExponentialMAOnBuffer(), die den exponentiellen gleitenden Mittelwert in einem Array von Werten berechnet und Durchschnittswerte in einem anderen Array aufzeichnet.
Glätten von Arrays
Insgesamt beinhaltet die Include-Datei MovingAverages.mqh acht Funktionen, die in zwei Funktionsgruppen mit je 4 Funktionen aufgeteilt werden können. Die erste Gruppe besteht aus Funktionen, die ein Array erhalten und einfach einen Wert eines gleitendenden Mittelwerts bei einer festgelegten Funktion ausgeben:
- SimpleMA() – für die Berechnung des Wertes eines einfachen Mittelwerts;
- ExponentialMA() – für die Berechnung des Wertes eines exponentiellen Mittelwerts;
- SmoothedMA() – für die Berechnung des Wertes eines geglätteten Mittelwerts;
- LinearWeightedMA() – für die Berechnung des Wertes eines linearen geglätteten Mittelwerts.
Diese Funktionen dienen dazu, einmalig den Wert eines Mittelwerts eines Arrays zu erhalten, und sind nicht für mehrere Aufrufe optimiert. Falls Sie eine Funktion aus dieser Gruppe in einer Schleife verwenden müssen (zum Berechnen von Werten eines Mittelwerts und Schreiben jedes berechneten Wertes in ein Array), müssen Sie einen optimalen Algorithmus organisieren.
Die zweite Funktionsgruppe dient dem Befüllen eines Empfänger-Arrays mit Werten eines gleitenden Mittelwerts auf Basis der Ausgangswerte des Arrays:
- SimpleMAOnBuffer() – befüllt das Ausgabe-Array buffer[] mit Werten eines einfachen Mittelwerts aus dem Array price[];
- ExponentialMAOnBuffer() – befüllt das Ausgabe-Array buffer[] mit Werten eines exponentiellen Mittelwerts aus dem Array price[];
- SmoothedMAOnBuffer() – befüllt das Ausgabe-Array buffer[] mit Werten eines geglätteten Mittelwerts aus dem Array price[];
- LinearWeightedMAOnBuffer() – befüllt das Ausgabe-Array buffer[] mit Werten eines linearen gewichteten Mittelwerts aus dem Array price[].
Alle angegebenen Funktionen mit Ausnahme der Arrays buffer[] und price[] und des Mittelungszeitraums period erhalten 3 weitere Parameter, deren Zweck dem der Parameter der OnCalculate()-Funktion entspricht – rates_total, prev_calculated und begin. Funktionen dieser Gruppe verarbeiten die übergebenen Arrays price[] und buffer[] korrekt unter Berücksichtigung der Richtung der Indizierung (Flag AS_SERIES) weiter.
Der Parameter begin kennzeichnet den Index eines Quell-Arrays, ab dem sinnvolle Daten beginnen, d. h. Daten, die bearbeitet werden müssen. Bei dem Array MTMBuffer[] beginnen die realen Daten mit dem Index 1, weil MTMBuffer[1]=price[1]-price[0]. Der Wert von MTMBuffer[0] ist nicht definiert, deshalb begin=1.
//--- calculate the first moving ExponentialMAOnBuffer(rates_total,prev_calculated, 1, // index, starting from which data for smoothing are available r, // period of the exponential average MTMBuffer, // buffer to calculate average EMA_MTMBuffer); // into this buffer locate value of the average ExponentialMAOnBuffer(rates_total,prev_calculated, 1,r,AbsMTMBuffer,EMA_AbsMTMBuffer);
Bei der Mittelung sollte der Periodenwert berücksichtigt werden, da die berechneten Werte im Ausgabe-Array mit einer geringen Verzögerung ausgefüllt werden, die bei größeren Mittelungszeiträumen größer ist. Zum Beispiel: Ist period=10, beginnen die Werte im resultierenden Array mit begin+period-1=begin+10-1. Dies sollte bei weiteren Aufrufen von buffer[] berücksichtigt werden und die Bearbeitung sollte mit dem Index begin+period-1 beginnen.
So erhalten wir einfach den zweiten exponentiellen Mittelwert aus den Arrays MTMBuffer[] und AbsMTMBuffer:
//--- calculate the second moving average on arrays
ExponentialMAOnBuffer(rates_total,prev_calculated,
r,s,EMA_MTMBuffer,EMA2_MTMBuffer);
ExponentialMAOnBuffer(rates_total,prev_calculated,
r,s,EMA_AbsMTMBuffer,EMA2_AbsMTMBuffer);
Der Wert von begin entspricht nun r, weil begin=1+r-1 (r ist der Zeitraum der primären exponentiellen Mittelung, die Bearbeitung beginnt mit dem Index 1). In den Ausgabe-Arrays EMA2_MTMBuffer[] und EMA2_AbsMTMBuffer[] beginnen die berechneten Werte mit dem Index r+s-1, weil wir die Bearbeitung von Eingabe-Arrays mit dem Index r begonnen haben und der Zeitraum für die zweite exponentielle Mittelung gleich s ist.
Alle Vorabberechnungen sind abgeschlossen. Nun können wir die Werte des Indikatorpuffers TSIBuffer[] berechnen, die im Diagramm wiedergegeben werden.
//--- now calculate values of the indicator for(int i=r+s-1;i<rates_total;i++) { TSIBuffer[i]=100*EMA2_MTMBuffer[i]/EMA2_AbsMTMBuffer[i]; }Kompilieren Sie den Code, indem Sie F5 drücken, und starten Sie ihn im MetaTrader-5-Terminal. Es funktioniert!
Es sind allerdings immer noch Fragen offen.
Optimieren von Berechnungen
Tatsächlich reicht es nicht aus, einfach einen funktionierenden Indikator zu schreiben. Wenn wir die aktuelle Implementierung von OnCalculate() genau betrachten, sehen wir, dass sie nicht optimal ist.
int OnCalculate (const int rates_total, // size of the price[] array; const int prev_calculated,// number of available bars; // at the previous call; const int begin,// from what index of the // price[] array true data start; const double &price[]) // array, at which the indicator will be calculated; { //--- calculate values of mtm and |mtm| MTMBuffer[0]=0.0; AbsMTMBuffer[0]=0.0; for(int i=1;i<rates_total;i++) { MTMBuffer[i]=price[i]-price[i-1]; AbsMTMBuffer[i]=fabs(MTMBuffer[i]); } //--- calculate the first moving average on arrays ExponentialMAOnBuffer(rates_total,prev_calculated, 1, // index, starting from which data for smoothing are available r, // period of the exponential average MTMBuffer, // buffer to calculate average EMA_MTMBuffer); // into this buffer locate value of the average ExponentialMAOnBuffer(rates_total,prev_calculated, 1,r,AbsMTMBuffer,EMA_AbsMTMBuffer); //--- calculate the second moving average on arrays ExponentialMAOnBuffer(rates_total,prev_calculated, r,s,EMA_MTMBuffer,EMA2_MTMBuffer); ExponentialMAOnBuffer(rates_total,prev_calculated, r,s,EMA_AbsMTMBuffer,EMA2_AbsMTMBuffer); //--- now calculate values of the indicator for(int i=r+s-1;i<rates_total;i++) { TSIBuffer[i]=100*EMA2_MTMBuffer[i]/EMA2_AbsMTMBuffer[i]; } //--- return value of prev_calculated for next call return(rates_total); }
Bei jedem Start der Funktion berechnen wir die Werte in den Arrays MTMBuffer[] und AbsMTMBuffer[]. In diesem Fall können unnötig wiederholte Berechnungen sämtliche CPU-Ressourcen beanspruchen, so leistungsstark die CPU auch ist, wenn die Größe von price[] hunderttausende oder gar Millionen beträgt.
Zum Organisieren optimaler Berechnungen nutzen wir den Eingabeparameter prev_calculated, der dem von OnCalculate() beim vorherigen Aufruf ausgegebenen Wert entspricht. Beim ersten Aufruf der Funktion ist der Wert von prev_calculated immer gleich 0. In diesem Fall berechnen wir alle Werte im Indikatorpuffer. Beim nächsten Aufruf müssen wir nicht den gesamten Puffer berechnen. Es wird nur der letzte Wert berechnet. Schreiben wir es uns so auf:
//--- if it is the first call if(prev_calculated==0) { //--- set zero values to zero indexes MTMBuffer[0]=0.0; AbsMTMBuffer[0]=0.0; } //--- calculate values of mtm and |mtm| int start; if(prev_calculated==0) start=1; // start filling out MTMBuffer[] and AbsMTMBuffer[] from the 1st index else start=prev_calculated-1; // set start equal to the last index in the arrays for(int i=start;i<rates_total;i++) { MTMBuffer[i]=price[i]-price[i-1]; AbsMTMBuffer[i]=fabs(MTMBuffer[i]); }
Berechnungsblöcke von EMA_MTMBuffer[], EMA_AbsMTMBuffer[], EMA2_MTMBuffer[] und EMA2_AbsMTMBuffer[] benötigen keine Optimierung der Berechnungen, weil ExponentialMAOnBuffer() bereits optimal geschrieben ist. Wir müssen nur die Berechnung der Werte des Arrays TSIBuffer[] optimieren. Wir nutzen die gleiche Methode wie für MTMBuffer[].
//--- now calculate the indicator values if(prev_calculated==0) start=r+s-1; // set the starting index for input arrays for(int i=start;i<rates_total;i++) { TSIBuffer[i]=100*EMA2_MTMBuffer[i]/EMA2_AbsMTMBuffer[i]; } //--- return value of prev_calculated for next call return(rates_total);
Die letzte Bemerkung für den Optimierungsvorgang: OnCalculate() gibt den Wert von rates_total aus. Das ist die Zahl der Elemente im Eingabe-Array price[], die für Indikatorberechnungen verwendet wird.
Der von OnCalculate() ausgegebene Wert wird im Terminal-Speicher gespeichert und wird beim nächsten Aufruf von OnCalculate() als Wert des Eingabeparameters prev_calculated an die Funktion übergeben.
Damit kennen wir immer die Größe des Eingabe-Arrays beim vorherigen Aufruf von OnCalculate() und starten die Berechnung von Indikatorpuffern bei einem korrekten Index ohne unnötige Neuberechnungen.
Überprüfen von Eingabedaten
Es gibt noch eines, was wir erledigen müssen, damit OnCalculate() perfekt funktioniert. Fügen wir eine Überprüfung des Arrays price[] hinzu, anhand dessen die Indikatorwerte berechnet werden. Wenn die Größe des Arrays (rates_total) zu gering ist, sind keine Berechnungen erforderlich. Wir müssen bis zum nächsten Aufruf von OnCalculate(), bei dem ausreichende Daten vorliegen, warten.
//--- if the size of price[] is too small if(rates_total<r+s) return(0); // do not calculate or draw anything //--- if it's the first call if(prev_calculated==0) { //--- set zero values for zero indexes MTMBuffer[0]=0.0; AbsMTMBuffer[0]=0.0; }
Da die exponentielle Glättung zweimal sequentiell zur Berechnung des True Strength Index verwendet wird, muss die Größe von price[] mindestens so groß sein wie die Summe der Perioden r und s. Andernfalls wird die Ausführung beendet und OnCalculate() gibt 0 aus. Der ausgegebene Nullwert bedeutet, dass der Indikator nicht im Diagramm wiedergegeben wird, weil seine Werte nicht berechnet werden.
Einrichten der Darstellung
Was die Richtigkeit der Berechnungen betrifft, ist der Indikator einsatzbereit. Rufen wir ihn allerdings über ein anderes mql5-Programm auf, basiert er standardmäßig auf Close-Preisen. Wir können einen anderen Standard-Preistyp angeben. Geben Sie einen Wert der Aufzählung ENUM_APPLIED_PRICE in der Eigenschaft indicator_applied_price des Indikators an.
Schreiben wir beispielsweise Folgendes, um einen typischen Preis ( (high+low+close)/3) als Preis einzurichten:
#property indicator_applied_price PRICE_TYPICAL
Falls wir nur die Werte mithilfe von iCustom() oder IndicatorCreate() verwenden möchten, sind keine weiteren Anpassungen erforderlich. Allerdings werden bei direkter Verwendung, d. h. bei Darstellung im Diagramm, zusätzliche Einstellungen empfohlen:
- Nummer des Bars, ab dem ein Indikator dargestellt wird;
- Kennzeichnung von Werten in TSIBuffer[], die in DataWindow wiedergegeben werden;
- Kurzbezeichnung des Indikators, der in einem separaten Fenster und in der Popup-Hilfe angezeigt wird, wenn der Cursor über die Indikatorlinie gehalten wird;
- Anzahl von Zeichen nach dem Dezimalkomma, die in Indikatorwerten angezeigt werden (dies beeinflusst nicht die Genauigkeit).
Diese Einstellungen können mithilfe von Funktionen der Gruppe Benutzerdefinierte Indikatoren im Handler OnInit() angepasst werden. Fügen Sie neue Zeilen ein und speichern Sie den Indikator als True_Strength_Index_ver2.mq5.
//+------------------------------------------------------------------+ //| Custom indicator initialization function | //+------------------------------------------------------------------+ int OnInit() { //--- indicator buffers mapping SetIndexBuffer(0,TSIBuffer,INDICATOR_DATA); SetIndexBuffer(1,MTMBuffer,INDICATOR_CALCULATIONS); SetIndexBuffer(2,AbsMTMBuffer,INDICATOR_CALCULATIONS); SetIndexBuffer(3,EMA_MTMBuffer,INDICATOR_CALCULATIONS); SetIndexBuffer(4,EMA2_MTMBuffer,INDICATOR_CALCULATIONS); SetIndexBuffer(5,EMA_AbsMTMBuffer,INDICATOR_CALCULATIONS); SetIndexBuffer(6,EMA2_AbsMTMBuffer,INDICATOR_CALCULATIONS); //--- bar, starting from which the indicator is drawn PlotIndexSetInteger(0,PLOT_DRAW_BEGIN,r+s-1); string shortname; StringConcatenate(shortname,"TSI(",r,",",s,")"); //--- set a label do display in DataWindow PlotIndexSetString(0,PLOT_LABEL,shortname); //--- set a name to show in a separate sub-window or a pop-up help IndicatorSetString(INDICATOR_SHORTNAME,shortname); //--- set accuracy of displaying the indicator values IndicatorSetInteger(INDICATOR_DIGITS,2); //--- return(0); }
Wenn wir beide Versionen starten und im Diagramm zu seinem Beginn blättern, sehen wir alle Unterschiede.
Fazit
Auf Basis des Beispiels der Erstellung des Indikators True Strength Index können wir die wichtigsten Momente beim Schreiben eines Indikators in MQL5 erläutern:
- Um Ihren eigenen benutzerdefinierten Indikator zu schreiben, nutzen Sie den MQL5 Wizard, der Sie bei der Durchführung vorhergehender Routinehandgriffe bei der Einrichtung des Indikators unterstützt. Wählen Sie die benötigte Variante der OnCalculate()-Funktion.
- Fügen Sie, falls erforderlich, weitere Arrays für Zwischenberechnungen hinzu und verbinden Sie sie mit den entsprechenden Indikatorpuffern mithilfe der SetIndexBuffer()-Funktion. Geben Sie den INDICATOR_CALCULATIONS-Typ für diese Puffer an.
- Optimieren Sie die Berechnungen in OnCalculate(), da diese Funktion jedes Mal aufgerufen wird, wenn sich Preisdaten ändern. Verwenden Sie fertig debuggte Funktionen, um das Schreiben von Codes einfacher und leserlicher zu machen.
- Führen Sie zusätzliche optische Anpassungen am Indikator durch, damit das Programm sowohl für andere mql5-Programme als auch Benutzer einfach zu verwenden ist.
Übersetzt aus dem Russischen von MetaQuotes Ltd.
Originalartikel: https://www.mql5.com/ru/articles/10
- 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.