
Baukasten des Händlers: Gestaltung der Indikatoren
Einleitung
Was ist ein Indikator? Es ist ein Tool, das für die Anzeige einer bestimmten Art von Daten vorgesehen ist. Üblicherweise sind es Informationen über die Eigenschaften von Kursverläufen und genau diese Art von Indikatoren werden wir genauer betrachten.
Jeder Indikator hat auch seine eigenen Eigenschaften und Charakteristiken: zum Beispiel der Bereich der Werte, die überkauft/überverkauft-Zonen, das Kreuzen von Linien, Hochs und Tiefs... Sie sind zahlreich und können erfolgreich zusammen mit den wichtigsten Indikatorwerten verwendet werden. Allerdings sind solche Eigenschaften nicht immer klar. Die Gründe dafür können verschieden sein - geringe Größe des Anzeigefensters, geringe Konzentration, usw.
Der Zweck dieses Artikels ist Ihnen zu helfen, die Darstellung und die Aussagefähigkeit der Indikatoren zu verbessern, sowie die teilweise Automatisierung und die Implementierung des Codes zu vereinfachen. Ich hoffe, dass der unten angeführte Code weder Anfängern noch professionellen Entwicklern Schwierigkeiten bereiten wird.
Der Artikel ist für diejenigen gedacht, die zumindest über ein gewissen MQL4-Wissen verfügen als auch einfache Ideen und Algorithmen in einen Code implementieren können, sowie die Struktur von Codes im Terminal kennen und die Bibliotheken (Experten/Bibliotheken) und Header-Dateien (Experten/Inkludieren) verwenden.
1. Einrichten einer Aufgabe
Unter allen Indikatoren möchte ich die informativsten und am häufigsten genutzten umschreiben:
- Linien-Kreuzung.
Niveau - nicht nur Punkte, die ein Niveau kreuzen, sondern ganze Niveaus werden hervorgehoben.
Hochs/Tiefs in einer einfachen Interpretation.
Unterschiedliche Farben für Aufwärts-/Abwärtsbewegung.
Lassen Sie uns diese diskutieren.
2. Grundbegriffe
Um Missverständnisse zu vermeiden, nehmen wir uns etwas Zeit, um die Indikator-Struktur zu betrachten.
#property indicator_separate_window // number of visible buffers of the indicator #property indicator_buffers 3 // setting the range of indicator values #property indicator_minimum 0 #property indicator_maximum 100 // setting indicator colors #property indicator_color1 White #property indicator_color2 Red #property indicator_color3 Blue // external settings extern int RSIPeriod = 9; extern int AppliedPrice = 0; extern int MAPeriod = 5; // declaring indicator buffers. Here they can be declared in any order. // Any names can be given to buffers, though better meaningful double Values[]; // Values double SmoothedValues[]; // Smoothed values double Crosses[]; // intersections // Significant number of digits after a decimal point in indicator values int DigitsUsed = 5; // Used empty value. In MQL4 there are two empty values -- EMPTY (-1) // -- used as an empty parameter when calling functions // EMPTY_VALUE (0x7FFFFFFF) -- used as an unacceptable value // (or default value) of a variable in indicators and function calls. // The fact is, most built-in indicators return 0 if there is no value // Besides, in custom (iCustom) indicators the empty value can be // set as any, this must be noted. int EmptyValueUsed = 0; // Initialization function. int init() { // Number of used buffers can be larger than that of displayed ones; some // may contain intermediate calculations and additional information. The total // number of buffers including additional ones is displayed here. // If there are no additional buffers, // this line is not needed. Total number must not exceed 8 // IndicatorBuffers(3); // associate buffers. Indexes must go from 0 till the declared number (not including) // buffers are drawn in the order of index growing, this is important and can be // used when righting indicators further. // It means that a buffer with a larger index is drawn above the buffer with lower one SetIndexBuffer(0, Values); SetIndexBuffer(1, SmoothedValues); SetIndexBuffer(2, Crosses); // besides, it is important that additional buffers are located after displayed ones // (i.e. they must have higher index) otherwise problems may occur displaying buffers, // and sometimes the error can be hardly found // This function sets an empty value for the buffer with the preset index // I do not recommend to use this function in order to avoid possible difficulties // Default empty value for buffers -- EMPTY_VALUE. // Empty buffer values are not drawn in a chart (except for DRAW_ZIGZAG) // SetIndexEmptyValue(0, EMPTY_VALUE); // Set parameters for buffers SetIndexStyle(0, DRAW_LINE); // The main signal is a solid line SetIndexStyle(1, DRAW_LINE, STYLE_DASH); // Smoothed -- dotted line SetIndexStyle(2, DRAW_ARROW, STYLE_SOLID, 2); // Intersections -- crosses of the size 2 SetIndexArrow(2, 251); // cross code in Wingdings IndicatorDigits(DigitsUsed); // set number of significant digits after point // Setting the starting plotting point for each indicator. If in terms of the current index // the history depth // is lower than the value written here, the buffer value with this index will not be drawn. SetIndexDrawBegin(0, RSIPeriod); SetIndexDrawBegin(1, RSIPeriod + MAPeriod); SetIndexDrawBegin(2, RSIPeriod + MAPeriod + 1); return(0); } int start() { // counting number of bars for re-calculation int toCount = Bars - IndicatorCounted(); // Calculating values // counting from history start till the current moment for (int i = toCount - 1; i >=0; i--) { // I understood its convenience only when I started to use it // I recommend to conduct the normalization of data at once, // so that later comparison could be easily made Values[i] = NormalizeDouble(iRSI(Symbol(), 0, RSIPeriod, AppliedPrice, i), DigitsUsed); } // Counting smoothed values for (i = toCount - 1; i >=0; i--) { SmoothedValues[i] = NormalizeDouble(iMAOnArray(Values, 0, MAPeriod, 0, MODE_EMA, i), DigitsUsed); } // ... return(0); }
3. Charakteristiken
Lassen Sie uns die Charakteristiken im Detail betrachten.
3.1. Linien-Kreuzung
Möglicherweise hat ein jeder Entwickler schon mal versucht einen Trading-Algorithmus zu implementieren, der die Kreuzung zweier MAs (Moving Averages) oder die Kreuzung der Basislinie und Signallinie vom MACD nutzt. Lassen Sie uns versuchen, dies zu visualisieren und es deutlicher zu machen, indem wir den Kreuzungspunkt im Indikator anzeigen.
Als Beispiel werden wir im gesamten Text den Relative Strength Index verwenden, so dass unser Ziel lautet, einen verbesserten RSI mit einigen neuen Vorteilen zu entwickeln.
3.1.1. Formalisierung der Aufgabe
Es ist notwendig, die Balken, welche die Linie kreuzen, in einem separaten Puffer zu kennzeichnen.
3.1.2. Probleme
Es scheint, dass alles einfach und klar ist. Die Aufgabe ist nicht schwierig und kann durch ein paar Codezeilen gelöst werden.
Wir müssen die Linien-Kreuzung wie zum Beispiel diese hier beschreiben:
if ((x1 > y1 && x2 < y2) || (x1 < y1 && x2 > y2)) { // line crossing here }
Oder wir können es vereinfachen:
if ((x1 - y1)*(x2 - y2) < 0) { // line crossing here }
Aber lassen Sie uns folgenden Fall betrachten:
Beachten Sie, dass die grünen Punkte die gleichen Werte haben. In solch einem Fall haben wir keine durchkreuzte Linie, sondern nur eine Linienberührung.
Aber hier:
ist es nicht so einfach, die Kreuzung zu bestimmen. Dieser Fall ist durchaus möglich.
Es ist auch notwendig, den Fall einer Berührung vom Fall einer Kreuzung korrekt zu unterscheiden, wobei zu berücksichtigen ist, dass wir während der Suche einen leeren Wert in einem Puffer finden können.
3.1.3. Lösung
Das Platzieren der Funktion init() wird nicht berücksichtigt, da diese nicht wichtig ist. Der vollständige Code kann in der Quelle gefunden werden.
Hier ist die Lösung für die einfachen und geglätteten Werte des Relative Strength Index Indikators.
//| RSI_Crosses_Sample.mq4 | extern int RSIPeriod = 9; extern int AppliedPrice = 0; extern int MAPeriod = 5; // buffers double Values[]; // Values double SmoothedValues[]; // Smoothed values double Crosses[]; // Crosses int DigitsUsed = 5; int EmptyValueUsed = 0; int start() { int toCount = Bars - IndicatorCounted(); // Reading the values and normalizing them // Mark the crosses for (i = toCount - 1; i >=0; i--) { // i+1 must be greater or equal bars count in the history if (i + 1 >= Bars) { continue; } // if some of the values are empty, it is not necessary to check if ( Values[i] == EmptyValueUsed || Values[i + 1] == EmptyValueUsed || SmoothedValues[i] == EmptyValueUsed || SmoothedValues[i + 1] == EmptyValueUsed || Values[i] == EMPTY_VALUE || Values[i + 1] == EMPTY_VALUE || SmoothedValues[i] == EMPTY_VALUE || SmoothedValues[i + 1] == EMPTY_VALUE ) { continue; } // clear the current value Crosses[i] = EMPTY_VALUE; // crossing check (simple case) if ((Values[i] - SmoothedValues[i])*(Values[i + 1] - SmoothedValues[i + 1]) < 0) { Crosses[i] = SmoothedValues[i]; continue; } // the crossing condition for a complicated case - // when crossing contain several bars with the same values // if (Values[i + 1] == SmoothedValues[i + 1] && Values[i] != SmoothedValues[i]) { // there is potential crossing - checking it // lets find the second end int index = i + 1; bool found = false; while ( index < Bars && // out of range Values[index] != EmptyValueUsed && // check for empty Values[index] != EMPTY_VALUE && // check for empty SmoothedValues[index] != EmptyValueUsed && // check for empty SmoothedValues[index] != EMPTY_VALUE) // check for empty { if (Values[index] != SmoothedValues[index]) { // ok, we have found the second end found = true; break; } index++; } if (!found) { // the case of the end of history or empty value // anyway, we mean that there is no crossing continue; } // checking the ends for crossing if ((Values[i] - SmoothedValues[i])*(Values[index] - SmoothedValues[index]) < 0) { // crossing found Crosses[i] = SmoothedValues[i]; } // else we have a touching - do not mark it } } return(0); }
3.1.4. Automatisierung
In diesem Abschnitt betrachten wir die Lösung des Problems mit Hilfe der Indicator_Painting Bibliothek.
//| RSI_Crosses_Lib_Sample.mq4 | #include <Indicator_Painting.mqh> extern int RSIPeriod = 9; extern int AppliedPrice = 0; extern int MAPeriod = 5; // buffers double Values[]; // Values double SmoothedValues[]; // Smoothed values double Crosses[]; // Crosses int DigitsUsed = 5; int EmptyValueUsed = 0; int start() { // Reading values // ... // Mark crosses MarkCrosses ( Values, // the fast buffer with values to check SmoothedValues, // the slow buffer with values to check Crosses, // the crosses buffer toCount - 1, // start check index 0, // final check index CROSS_ALL, // use CROSS_UP for up crosses CROSS_DOWN for down crosses CROSS_ALL for all 0); // used empty value return(0); }
3.2. Pegelmarke
Einige der Oszillatoren mit limitiertem und strikt eingestelltem Wertebereich (RSI, Stochastischer Oszillator, DeMarker, Money Flow Index, Williams' Percent Range) benötigen oftmals das Kennzeichnen von Zonen oder Niveaus. Zum Beispiel die flachen Zonen, die überkauft/überverkauft-Zonen, die Trendzonen... Lassen Sie uns versuchen, das definierte Niveau zu skizzieren, indem wir verschiedene Farben dafür verwenden.
3.2.1. Formalisierung der Aufgabe
Es ist notwendig, die Balken mit den Werten außerhalb der definierten Niveaus in einem separaten Puffer zu kennzeichnen.
3.2.2. Probleme
Es ist nicht so einfach, wie es auf den ersten Blick scheint.
Das erste Problem ist das Zeichnen der Balken, auf denen das definierte Niveau gekreuzt wird. Hier ist die Lösung.
//| RSI_Cut_Levels_Sample.mq4 | extern int RSIPeriod = 9; extern int AppliedPrice = 0; extern int HigherLevel = 70; extern int LowerLevel = 30; // buffers double Higher[]; // Overbought double Lower[]; // Oversold double Values[]; // Values int DigitsUsed = 5; int EmptyValueUsed = 0; int start() { int toCount = Bars - IndicatorCounted(); // Reading values // ... // Mark levels - upper for (i = toCount - 1; i >=0; i--) { // check for empty values if (Values[i] == EMPTY_VALUE || Values[i] == EmptyValueUsed) { continue; } // empty current value Higher[i] = EMPTY_VALUE; // greater than high if (Values[i] >= HigherLevel) { Higher[i] = Values[i]; } } // for the levels mark - the code is same // ... return(0); }
Dieser Code löst die definierte Aufgabe, aber hat ein Problem:
Es ist schwierig, den Chart visuell zu analysieren, da das Zeichnen des Signals von dem Wert beginnt, der größer (niedriger) ist als das Niveau. Das ist, warum einige der Signalbalken nicht analysiert werden können, da Besonderheiten gezeichnet werden, die durch schnelle Veränderungen der benachbarten Balken verursacht werden.
Die Lösung ist, nicht nur die Balken zu kennzeichnen, die höher (niedriger) als das definierte Niveau sind, sondern auch jeweils den Balken, der sich vor als auch nach den bereits markierten befindet. Und es ist notwendig, diese nicht mit ihren eigenen Werten, sondern mit den Werten des Niveaus zu kennzeichnen.
Das zweite Problem erscheint nach der Lösung des ersten - der Signalpuffer hat Pseudomarken der "falschen" Niveau-Untergliederungen als Folge der Algorithmus-Komplikation.
Es bedeutet, dass sich der Kurs während der Balkenbildung außerhalb des Niveaus befunden hat, jedoch der letzte Balken einen Wert innerhalb des Niveaus hat. Aufgrund dieser Tatsache können wir ein Bild wie dieses erhalten:
Das Problem tritt nur auf, wenn wir einen Indikator für die Echtzeit-Kurse verwenden. Die Lösung ist einfach - wir überprüfen zwei Balken (0 und 1) während der Verarbeitung, die anderen werden nur wenn nötig geprüft.
Danach werden wir folgendes Bild für den RSI haben:
3.2.3. Lösung
Lassen Sie uns also all dies in einen Code schreiben:
//| RSI_Levels_Sample.mq4 | extern int RSIPeriod = 9; extern int AppliedPrice = 0; extern int HigherLevel = 70; extern int LowerLevel = 30; // buffers double Higher[]; // Overbought double Lower[]; // Oversold double Values[]; // Values int DigitsUsed = 5; int EmptyValueUsed = 0; // looking at least two bars - 0 and 1. int Depth = 2; int start() { int toCount = Bars - IndicatorCounted(); // Reading values // ... toCount = MathMax(toCount, Depth); // Marking levels - upper for (i = toCount - 1; i >=0; i--) { if (Values[i] == EMPTY_VALUE || Values[i] == EmptyValueUsed) continue; Higher[i] = EMPTY_VALUE; // greater than level if (Values[i] >= HigherLevel) { Higher[i] = Values[i]; // if previous is lower if (Values[i + 1] < HigherLevel && Values[i + 1] != EmptyValueUsed) { // mark it also but with the level value Higher[i + 1] = HigherLevel; } } // if current lower else { // if previous is greater if (Values[i + 1] >= HigherLevel && Values[i + 1] != EMPTY_VALUE) { // mark it also but with the level value Higher[i] = HigherLevel; } } } // Mark levels - the code is the same // ... return(0); }
3.2.4. Automatisierung
Das Lösen des gleichen Problems durch die Verwendung der Indicator_Painting Bibliothek.
//| RSI_Levels_Lib_Sample.mq4 | #include <Indicator_Painting.mqh> extern int RSIPeriod = 9; extern int AppliedPrice = 0; extern int HigherLevel = 70; extern int LowerLevel = 30; // buffers double Higher[]; // Overbought double Lower[]; // Oversold double Values[]; // Values int DigitsUsed = 5; int EmptyValueUsed = 0; int Depth = 2; int start() { int toCount = Bars - IndicatorCounted(); // Read values for (int i = toCount - 1; i >= 0; i--) { Values[i] = NormalizeDouble(iRSI(Symbol(), 0, RSIPeriod, AppliedPrice, i), DigitsUsed); } // Mark levels - upper MarkLevel(Values, Higher, 0, toCount - 1, HigherLevel, GREATER_THAN, EmptyValueUsed); // Mark levels - lower MarkLevel(Values, Lower, 0, toCount - 1, LowerLevel, LESS_THAN, EmptyValueUsed); return(0); }
3.3. Hochs und Tiefs
Die Extrempunkte (Extrema) des Indikators können als Signale verwendet werden. In diesem Artikel wird der Begriff "Extremum" in seiner einfachsten Bedeutung verwendet - wenn der Balken einen größeren (niedrigeren) Wert hat als sein Nachbar, dann sprechen wir von einem Extremum.
3.3.1. Formalisierung der Aufgabe
Es ist notwendig, die Balken mit Extremwerten in einem separaten Puffer zu kennzeichnen.
3.3.2. Probleme
Lassen Sie uns einige Beispiele betrachten:
Hier sind die offensichtlichen Extremwerte mit roten Punkten gekennzeichnet:
if ((x1 > x2 && x3 > x2) || (x1 < x2 && x3 < x2)) { // x2 is extremal }
Oder wir können es vereinfachen:
if ((x1 - x2)*(x2 - x3) < 0) { // x2 is extremal }
Aber lassen Sie uns folgenden Fall betrachten:
Die gekennzeichneten Punkte haben die gleichen Werte. Der blaue Punkt ist ein Extremum. Es ist nicht einfach, diesen zu bestimmen. Und im folgenden Fall:
gibt es kein Extremum. Wir nehmen an, dass es eine Biegung ist.
Die Lösung ist in diesen Fällen, das zweite Ende zu finden, wie bei den Fällen von Kreuzungen.
3.3.3. Lösung
Hier ist der Code:
//| RSI_Extremums_Sample.mq4 | extern int RSIPeriod = 9; extern int AppliedPrice = 0; // buffers double Values[]; // Values double Extremums[]; // Extremums int DigitsUsed = 5; int EmptyValueUsed = 0; int start() { int toCount = Bars - IndicatorCounted(); for (int i = toCount - 1; i >=0; i--) { Values[i] = NormalizeDouble(iRSI(Symbol(), 0, RSIPeriod, AppliedPrice, i), DigitsUsed); } for (i = toCount - 1; i >=0; i--) { // check the values relative to the current index. if (i + 2 >= Bars) { continue; } // check for empty values, if there are, it is not necessary to check if ( Values[i] == EmptyValueUsed || Values[i + 1] == EmptyValueUsed || Values[i + 2] == EmptyValueUsed ) { continue; } // fill the current value of the mark buffer Extremums[i + 1] = EMPTY_VALUE; // cross condition - the simple case if ((Values[i] - Values[i + 1])*(Values[i + 1] - Values[i + 2]) < 0) { // we have found the cross Extremums[i + 1] = Values[i + 1]; continue; } // the cross condition in a complicated case - // when top contain several bars with the same value if (Values[i + 1] == Values[i + 2] && Values[i] != Values[i + 1]) { // there is possible extremum - to check it // we have to find the second end int index = i + 2; bool found = false; while (index < Bars && Values[index] != EmptyValueUsed && Values[index] != EMPTY_VALUE) { if (Values[i + 2] != Values[index]) { // ok, we have found the second end found = true; break; } index++; } if (!found) { // we are at the end of the history or have an empty value // for the both cases we assume that there is no extremum continue; } // checking the ends for a cross if ((Values[i] - Values[i + 1])*(Values[i + 1] - Values[index]) < 0) { // there is a cross Extremums[i + 1] = Values[i + 1]; } // else -- there is a bend point, do not mark it } } return(0); }
3.3.4. Automatisierung
Die gleiche Aufgabenlösung durch die Nutzung der Indicator_Painting Bibliothek.
//| RSI_Extremums_Lib_Sample.mq4 | #include <Indicator_Painting.mqh> extern int RSIPeriod = 9; extern int AppliedPrice = 0; // buffers double Values[]; // Values double Extremums[]; // Extremal points int DigitsUsed = 5; int EmptyValueUsed = 0; int start() { int toCount = Bars - IndicatorCounted(); for (int i = toCount - 1; i >=0; i--) { Values[i] = NormalizeDouble(iRSI(Symbol(), 0, RSIPeriod, AppliedPrice, i), DigitsUsed); } MarkExtremums(Values, Extremums, toCount - 1, 0, DIR_ALL, EmptyValueUsed); return(0); }
3.4. Unterschiedliche Farben für die jeweilige Richtung
Diese Visualisierungsmethode wird in einigen Standardindikatoren verwendet und kann auch sehr nützlich sein.
3.4.1. Formalisierung der Aufgabe
Es ist notwendig, einige Reihen von Indikatorwerten mit verschiedenen Farben einzufärben (zum Beispiel Reihen, während einer Richtung nach oben oder unten). Die Richtung kann ganz einfach erklärt werden - wenn der aktuelle Wert größer als der vorige ist, haben wir eine Richtung nach oben und umgekehrt sprechen wir von einer Richtung nach unten.
3.4.2. Probleme
Gehen wir von der Funktion aus. Es wird angenommen, dass wir einen Basisdatenpuffer haben und dieser Puffer grafisch dargestellt wird. Wenn nicht, werden wir es tun, denn wir benötigen für das benutzerdefinierte Malen der Richtung mindestens zwei Farben und mindestens zwei Puffer. Nun die Funktion. Wenn wir eine der Richtungen einfärben, ist es nicht notwendig die andere Richtung einzufärben - wir sehen dies auf den nicht eingefärbten Teilen des Basispuffers.
Der Basispuffer:
Hier ist der Basispuffer mit einer eingefärbten Richtung nach oben:
Deshalb werden wir jetzt nur die eine eingefärbte Richtung betrachten, in diesem Beispiel nach oben. Lassen Sie uns die Probleme betrachten, die auftreten können.
Die naive Implementierung der Funktion ist die folgende:
//| RSI_Simple_Directions_Sample.mq4 | extern int RSIPeriod = 9; extern int AppliedPrice = 0; // buffers double Values[]; // Values double Growing[]; // Growing buffer int DigitsUsed = 5; int EmptyValueUsed = 0; int start() { int toCount = Bars - IndicatorCounted(); // Reading values for (int i = toCount - 1; i >=0; i--) { Values[i] = NormalizeDouble(iRSI(Symbol(), 0, RSIPeriod, AppliedPrice, i), DigitsUsed); } // Mark the growing levels - we will get the falling levels as a result for (i = toCount - 1; i >=0; i--) { // check for empty value, if there are, the further check is not necessary // ... // filling the current values with empty values Growing[i] = EMPTY_VALUE; // if growing if (Values[i] > Values[i + 1]) { Growing[i] = Values[i]; Growing[i + 1] = Values[i + 1]; } } return(0); }
Kompilieren, Anbringen an Charts und... das Ergebnis der Codeausführung sehen:
Es gibt einige Probleme, die mit Punkten gekennzeichnet sind. Lassen Sie uns betrachten, warum es so aussieht. Während wir eine Richtung einfärben, erhalten wir den Effekt, dass bei einigen Teilstücken es keine Werte gibt (EMPTY_VALUE).
Lassen Sie uns folgenden Fall betrachten:
Die zusätzlichen Pufferdaten, die keine leeren Werte haben sollten, sind durch schwarze Punkte gekennzeichnet. Um das Zeichnen der geraden Linie zwischen den Punten zu vermeiden (mit dem Stil DRAW_LINE), ist es notwendig, mindestens einen nicht leeren Wert zwischen ihnen zu haben. Der gesamte dargestellte Bereich hat keine leeren Werte, weshalb der Basispuffer nur bei "Sägezahn"-Stücken eingezeichnet ist.
Die Lösung dieses Problems ist nicht so ganz einfach - beispielsweise würde eine Glättung oder das Verwenden von einigen zusätzlichen Bedingungen es viel komplizierter machen. Als Folge müssten wir vielleicht mehrere Balken neu einfärben oder etwas anderes schwieriges tun.
Die Lösung ist, zwei zusätzliche Puffer dafür zu verwenden. Dann ist es möglich, dass sich die bemalten Stücke abwechseln - wodurch wir die notwendigen leeren Wertte in jedem der Puffer erhalten.
Lassen Sie uns die verschiedenen Farben den zusätzlichen Puffern zuweisen und das Ergebnis ansehen:
Das Hauptproblem ist gelöst, aber es gibt noch ein weiteres kleines Problem mit dem Null-Balken. Er wird jedes Mal neu gezeichnet und in einigen Fällen ist es notwendig, das Stück nach oben zu löschen, wenn sich die Richtung geändert hat.
Betrachten wir die Implementierung.
3.4.3. Lösung
//| RSI_Directions_Sample.mq4 | extern int RSIPeriod = 9; extern int AppliedPrice = 0; // buffers double Values[]; // Values double Growing1[]; // First growing buffer double Growing2[]; // Second growing buffer int DigitsUsed = 5; int EmptyValueUsed = 0; int start() { int toCount = Bars - IndicatorCounted(); // Reading values for (int i = toCount - 1; i >=0; i--) { Values[i] = NormalizeDouble(iRSI(Symbol(), 0, RSIPeriod, AppliedPrice, i), DigitsUsed); } // Mark for the growing levels - we will get the falling levels as a resut for (i = toCount - 1; i >=0; i--) { // check of an empty values // ... // assume that the current values are empty Growing1[i] = EMPTY_VALUE; Growing2[i] = EMPTY_VALUE; // if it growing if (Values[i] > Values[i + 1]) { // if it growing on the previous bar if (Values[i + 1] > Values[i + 2]) { // writing to the current growing buffer if (Growing1[i + 1] != EMPTY_VALUE) Growing1[i] = Values[i]; else Growing2[i] = Values[i]; } // if the previous bar was not increasing else { // write to the buffer which it was not used the last 2 bars // we must have at least one such bar if (Growing2[i + 2] == EMPTY_VALUE) { Growing2[i] = Values[i]; Growing2[i + 1] = Values[i + 1]; } else { Growing1[i] = Values[i]; Growing1[i + 1] = Values[i + 1]; } } } // if the last value does not grow, remove it else if (i == 0) { if (Growing1[i + 1] != EMPTY_VALUE && Growing1[i + 2] == EMPTY_VALUE) { Growing1[i + 1] = EMPTY_VALUE; } if (Growing2[i + 1] != EMPTY_VALUE && Growing2[i + 2] == EMPTY_VALUE) { Growing2[i + 1] = EMPTY_VALUE; } } } return(0); }
3.4.4. Automatisierung
Die gleiche Aufgabenlösung durch die Nutzung der Indicator_Painting Bibliothek.
In der Bibliothek gibt es auch eine ähnliche Implementierung für die Richtung nach unten.
//| RSI_Directions_Lib_Sample.mq4 | #include <Indicator_Painting.mqh> extern int RSIPeriod = 9; extern int AppliedPrice = 0; // buffers double Values[]; // Values double Growing1[]; // First growing buffer double Growing2[]; // Second growing buffer int DigitsUsed = 5; int EmptyValueUsed = 0; int start() { int toCount = Bars - IndicatorCounted(); // Reading values for (int i = toCount - 1; i >=0; i--) { Values[i] = NormalizeDouble(iRSI(Symbol(), 0, RSIPeriod, AppliedPrice, i), DigitsUsed); } // Mark the growing levels - we will get the falling levels automatically MarkGrowing(Values, Growing1, Growing2, toCount - 1, 0, EmptyValueUsed); return(0); }
4. Die Indicator_Painting Bibliothek
Als Ergebnis aller abgeschlossenen Arbeiten gibt es eine Indicator_Painting Bibliothek.
Sie wurde speziell für die Automatisierung der beschriebenen Vorgänge, mit einigen Ergänzungen entworfen.
Hier ist eine Liste der verfügbaren Funktionen:
// ==================================================== // Mark for tops and bottoms // ==================================================== void MarkExtremums( double values[], // Indicator values double& extremums[], // Buffer for extremums int startIndex, // Start index for check (it included) int endIndex, // End index for check (it included) int direction, // DIR_TOP for tops, DIR_BOTTOM for bottoms, DIR_ALL for tops an bottoms double emptyValueUsed); // The value used for "empty" mark // ==================================================== // Mark for crosses // ==================================================== void MarkCrosses( double values1[], // Values of the first indicator double values2[], // Values of the second indicator double& crosses[], // Buffer for their crosses int startIndex, // Start index for check (it included) int endIndex, // End index for check (it included) int direction, // CROSS_UP for up crosses,CROSS_DOWN for down crosses, CROSS_ALL for all crosses double emptyValueUsed); // The value used for "empty" mark // ==================================================== // Mark for level crosses // ==================================================== void MarkLevelCrosses( double values[], // Values of the indicator double level, // Level value for a cross check double& crosses[], // Buffer for the crosses int startIndex, // Start index for check (it included) int endIndex, // End index for check (it included) int direction, // CROSS_UP for up crosses,CROSS_DOWN for down crosses, CROSS_ALL for all crosses double emptyValueUsed); // The value used for "empty" mark // ==================================================== // Mark for levels // ==================================================== void MarkLevel( double values[], // Values of the indicator double& level[], // Buffer for the crosses int startIndex, // Start index for check (it included) int endIndex, // End index for check (it included) double levelValue, // Level value int condition, // Mark condition (LESS_THAN = -1, GREATER_THAN = 1) double emptyValueUsed); // The value used for "empty" mark // ==================================================== // Mark for dynamic levels // ==================================================== void MarkDynamicLevel( double values[], // Values of the indicator double dynamicLevel[], // Dynamical level values for check double& level[], // Buffer for the crosses int startIndex, // Start index for check (it included) int endIndex, // End index for check (it included) int condition, // меньше (LESS_THAN = -1) или больше уровня (GREATER_THAN = 1) double emptyValueUsed); // The value used for "empty" mark // ==================================================== // Mark for direction (upward) // ==================================================== void MarkGrowing( double values[], // Values of the indicator double& growing1[], // The first buffer to mark the direction double& growing2[], // The second buffer to mark the direction int startIndex, // Start index for check (it included) int endIndex, // End index for check (it included) double emptyValueUsed); // The value used for "empty" mark // ==================================================== // Mark for direction (downward) // ==================================================== void MarkReducing( double values[], // Values of the indicator double& reducing1[], // The first buffer to mark the direction double& reducing2[], // The second buffer to mark the direction int startIndex, // Start index for check (it included) int endIndex, // End index for check (it included) double emptyValueUsed); // The value used for "empty" mark
Hier sind einige Beispiele für die Nutzung der Bibliothek:
Um die Bibliothek zu verwenden, sollte folgendes getan werden:
1. Kopieren Sie die Datei "Indicator_Painting.mq4" in den Ordner "experts/libraries"
2. Kopieren Sie die Datei "Indicator_Painting.mqh" in den Ordner "experts/include"
3. Fügen Sie dem Indikator-Code die folgende Befehlszeile hinzu:
#include <Indicator_Painting.mqh>
Jetzt ist es möglich, alle Funktionen der Bibliothek zu nutzen. Weitere Details finden Sie in der Datei "Indicator_Painting.mqh".
Beispiele können Sie in den Dateien finden, die dem Artikel angehängt sind.
Fazit
Der Autor hofft, dass dieser Artikel helfen und die Arbeit einiger Leute vereinfachen wird. Der Autor erachtet das Ziel als erreicht.
Danksagungen
Der Autor möchte sich bei Herrn Viktor Rustamov (granit77) für den Aufgabenvorschlag, die Hilfe und die Kommentare bedanken, die den Artikel verbessert haben.
Übersetzt aus dem Russischen von MetaQuotes Ltd.
Originalartikel: https://www.mql5.com/ru/articles/1569





- 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.