English Русский 中文 Español 日本語 Português 한국어 Français Italiano Türkçe
Erzeugung eines Indikators mit mehreren Indikator-Buffern für Anfänger

Erzeugung eines Indikators mit mehreren Indikator-Buffern für Anfänger

MetaTrader 5Beispiele | 12 Februar 2016, 11:29
1 332 0
Nikolay Kositsin
Nikolay Kositsin
Erzeugung eines Indikators mit mehreren Indikator-Buffern für Anfänger
Die komplexen Codes bestehen aus einer Reihe einfacher Codes. Kennt man diese, dann sieht alles gleich nicht mehr so kompliziert aus. In diesem Beitrag beschäftigen wir uns mit der Erzeugung eines Indikators mit mehreren Indikator-Buffern. Als Beispiel wird hierzu der Aroon-Indikator detailliert analysiert und zwei unterschiedliche Versionen dieses Codes präsentiert.

Einleitung

In meinen vorherigen Beiträgen "Angepasste Indikatoren in MQL5 für Anfänger" und "Praktische Implementierung von Digital Filers in MQL5 für Anfänger" habe ich mich detailliert mit der Indikatorstruktur mit einem Indikator-Buffer beschäftigt.

Eine derartige Methode kann ganz klar großflächig zum Schreiben von angepassten Indikatoren eingesetzt werden, doch das echte Leben kann ja kaum auf ihre Verwendung beschränkt werden, also ist es an der Zeit, sich komplexere Aufbaumethoden des Indikator-Codes anzusehen. Glücklicherweise sind die Fähigkeiten von MQL5 hier unerschöpflich und nur durch das RAM unserer PCs begrenzt.


Der Aroon Indikator als Beispiel der Code-Verdopplung

Die Formel für diesen Indikator enthält zwei Komponenten: die Hausse- und die Baisse-Indikatoren, die in einem extra Chart-Fenster grafisch dargestellt werden:

HAUSSE =  (1 - (Balken - SHIFT(MAX(HIGH(), AroonPeriod)))/AroonPeriod) * 100
BAISSE = (1 - (Balken - SHIFT(MIN (LOW (), AroonPeriod)))/AroonPeriod) * 100

wobei:

  • HAUSSE - die Stärke der Hausse ist;
  • BAISSE - die Stärke der Baisse ist;
  • SHIFT() - die Funktion zur Festlegung der Indexposition des Balkens ist;
  • MAX() - Die Suchfunktion nach dem Maximum über dem AroonPeriod Zeitraum ist;
  • MIN() - Die Suchfunktion nach dem Minimum über dem AroonPeriod Zeitraum ist;
  • HIGH() und LOW() - die relevanten Kurs-Arrays darstellen;

Wir können direkt aus den Formeln des Indikators ersehen, dass wir beim Aufbau eines Indikators nur zwei Indikator-Buffer haben dürfen und die Indikatorstruktur nur kaum von der im vorigen Beitrag beschriebenen SMA_1.mq5 Struktur abweicht.

Tatsächlich ist das einfach der gleiche duplizierte Code, nur mit einer anderen Anzahl Indikator-Buffers. Öffnen wir also den Code dieses Indikators im MetaEditor und speichern ihn als Aroon.mq5. In den ersten 11 Zeilen des Codes, die mit Copyright und seiner Versionsnummer zu tun haben, ersetzen wir nur den Namen des Indikators:

//+------------------------------------------------------------------+
//|                                                        Aroon.mq5 |
//|                        Copyright 2010, MetaQuotes Software Corp. |
//|                                              http://www.mql5.com |
//+------------------------------------------------------------------+
//---- copyright
#property copyright "2010, MetaQuotes Software Corp."
//---- link to the author's site
#property link      "http://www.mql5.com"
//---- version number
#property version   "1.00"

Danach müssen wir in Zeile 12 des Codes die Zeichnung des Indikators vom einfachen Chart-Fenster in ein separates Fenster verändern:

//---- plot indicator in the separate window
#property indicator_separate_window

Das dieser Indikator einen komplett anderen Bereich an Werten hat, erfolgt seine Zeichnung auch in einem extra Fenster.

Danach verändern wir in den folgenden 4 Codezeilen (die allgemeinen Indikatoreigenschaften) die Menge der verwendeten Indikator-Buffer auf zwei:

//---- two buffers are used
#property indicator_buffers 2
//---- two plots are used 
#property indicator_plots   2

Die folgenden 10 Zeilen des Codes hängen mit der Zeichnung des Indikators von einem spezifischen Indikator-Buffer zusammen, dessen Benennung dupliziert werden muss. Danach müssen wir alle Indices von 1 auf 2 ersetzen. Ebenfalls müssen wir alle Kennzeichnungen der Indikator-Buffer verändern:

//+----------------------------------------------+
//| bullish strength indicator parameters        |
//+----------------------------------------------+
//---- drawing style = line
#property indicator_type1   DRAW_LINE
//---- drawing color = Lime
#property indicator_color1  Lime
//---- line style = solid line
#property indicator_style1  STYLE_SOLID
//---- line width = 1
#property indicator_width1  1
//---- label of the BullsAroon indicator
#property indicator_label1  "BullsAroon"
//+----------------------------------------------+
//|  bearish strength indicator parameters       |
//+----------------------------------------------+
//---- drawing style = line
#property indicator_type2   DRAW_LINE
//---- drawing color = Red
#property indicator_color2  Red
//---- line style = solid line
#property indicator_style2  STYLE_SOLID
//---- line width = 1
#property indicator_width2  1
//---- label of the BearsAroon indicator
#property indicator_label2  "BearsAroon"

Dieser Indikator arbeitet mit drei waagrechten Ebenen mit den Werten 30, 50 und 70.

Um diese Ebenen zeichnen zu können, müssen wir fünf weitere Codezeilen in den Indikator-Code einfügen.

//+----------------------------------------------+
//| Horizontal levels                            |
//+----------------------------------------------+
#property indicator_level1 70.0
#property indicator_level2 50.0
#property indicator_level3 30.0
#property indicator_levelcolor Gray
#property indicator_levelstyle STYLE_DASHDOTDOT

Bei den Eingabeparametern des Indikators bleibt alles unverändert, verglichen mit dem vorigen Indikator, außer kleineren Veränderungen an den Titeln:

//+----------------------------------------------+
//| Indicator input parameters                   |
//+----------------------------------------------+
input int AroonPeriod = 9; // Period 
input int AroonShift = 0// Horizontal shift of the indicator in bars 

 Jetzt gibt es allerdings zwei Arrays, die als Indikator-Buffer verwendet werden und sie müssen passende Namen tragen:

//--- declare the dynamic arrays used further as indicator buffers
double BullsAroonBuffer[];
double BearsAroonBuffer[]; 

Und ganz genauso gehen wir beim Code der OnInit() Funktion vor.

Zunächst verändern wir die Zeilen des Codes für den Buffer "0":

//--- set BullsAroonBuffer dynamic array as indicator buffer 
SetIndexBuffer(0, BullsAroonBuffer, INDICATOR_DATA);
//--- horizontal shift (AroonShift) of the indicator 1
PlotIndexSetInteger(0, PLOT_SHIFT, AroonShift);
//--- plot draw begin (AroonPeriod) of the indicator 1
PlotIndexSetInteger(0, PLOT_DRAW_BEGIN, AroonPeriod);
//--- label shown in DataWindow
PlotIndexSetString(0, PLOT_LABEL, "BearsAroon"); 

Danach wird der gesamte Code in die Windows Zwischenablage kopiert und dann direkt nach dem gleichen Code eingefügt.

Im gerade eingefügten Code ändern wir die Anzahl der Indikator-Buffer von 0 auf 1, geben dem Indikator-Array einen neuen Namen und benennen den Indikator:

//--- set BearsAroonBuffer dynamic array as indicator buffer 
SetIndexBuffer(1, BearsAroonBuffer, INDICATOR_DATA); 
//--- horizontal shift (AroonShift) of the indicator 2 
PlotIndexSetInteger(1, PLOT_SHIFT, AroonShift); 
//--- plot draw begin (AroonPeriod) of the indicator 2 
PlotIndexSetInteger(1, PLOT_DRAW_BEGIN, AroonPeriod); 
//--- label shown in DataWindow
PlotIndexSetString(1, PLOT_LABEL, "BullsAroon");  

Der Kurzname des Indikators hat auch einige kleine Veränderungen erfahren:

//--- initialization of the variable for a short indicator name
string shortname;
StringConcatenate(shortname, "Aroon(", AroonPeriod, ", ", AroonShift, ")"); 

Schauen wir uns jetzt die Genauigkeit der Zeichnung des Indikators an. Der tatsächliche Indikatorbereich geht von 0 - 100, und wird die ganze Zeit gezeigt

Hier kann man gut nur die ganzzahligen Werte des Indikators, die auf dem Chart gezeichnet werden, verwenden. Daher nehmen wir zur Zeichnung des Indikators 0 für alle Ziffern nach der Dezimalstelle:

//--- set accuracy of drawing of indicator values
IndicatorSetInteger(INDICATOR_DIGITS, 0);

Im SMA_1.mq5 Indikator haben wir die erste Aufrufform der OnCalculate() Funktion verwendet

Das ist für den Aroon Indikator nicht gut, da er keine high[] and low[] Kurs-Arrays hat. Diese Arrays bekommen wir jedoch in der zweiten Aufrufform dieser Funktion. Und deshalb müssen wir die Kopfzeile der Funktion ändern:

int OnCalculate( 
                const int rates_total,    // total bars on the current tick
                const int prev_calculated,// total bars on the previous tick
                const datetime& time[],
                const double& open[],    
                const double& high[],     // price array of the maximum prices for the indicator calculations
                const double& low[],      // price array of the minimum prices for the indicator calculations
                const double& close[],
                const long& tick_volume[],
                const long& volume[],
                const int& spread[]
              )

Nach dieser Veränderung macht die Verwendung des Beginn-Parameters keinen Sinn mehr, also muss der aus dem Code verschwinden!

Der Code zur Berechnung der Limits der Variablenveränderungen des Operationszyklus und die Datenüberprüfung für ausreichende Berechnung, sind praktisch unverändert geblieben.

//--- checking the number of bars
if (rates_total < AroonPeriod - 1) return(0);
   
//--- declare the local variables 
int first, bar;
double BULLS, BEARS; 

//--- calculation of the first (staring index) for the main loop
if (prev_calculated == 0)          // checking for the first call of the OnCalculate function
    first = AroonPeriod - 1;       // starting index for calculating all of the bars 
else first = prev_calculated - 1// starting index for calculating new bars

Dennoch treten gewisse Probleme mit den Algorithmen zur Berechnung der Indikator-Werte auf. Das liegt daran, dass MQL5 keine eingebauten Funktionen zur Feststellung der Indices für Maximal und Minimal für den Zeitraum vom aktuellen Balken in der Richtung des abnehmenden Index hat.

Eine Möglichkeit, dies zu lösen, ist diese Funktionen selbst zu schreiben. Doch zum Glück gibt es diese Funktionen bereits im ZigZag.mq5 Indikator in den angepassten Indikatoren im "MetaTrader5\MQL5\Indikators\Examples" Ordner.

Am einfachsten geht das, wenn man den Code dieser Funktionen im ZigZag.mq5 Indikator auswählt, sie dann in die Windows-Ablage kopiert und in unserem Code einfügt, z.B. direkt nach der Beschreibung der OnInit() Funktion auf der globalen Ebene:

//+------------------------------------------------------------------+
//|  searching index of the highest bar                              |
//+------------------------------------------------------------------+
int iHighest(const double &array[], // array for searching for the index of the maximum element
             int count,            // number of the elements in the array (in the decreasing order), 
             int startPos          // starting index
             )                     
  {
//---+
   int index = startPos;
   
   //---- checking the starting index
   if (startPos < 0)
     {
      Print("Incorrect value in the function iHighest, startPos = ", startPos);
      return (0);
     } 
   //---- checking the startPos values
   if (startPos - count < 0) count = startPos;
    
   double max = array[startPos];
   
   //---- index search
   for(int i = startPos; i > startPos - count; i--)
     {
      if(array[i] > max)
        {
         index = i;
         max = array[i];
        }
     }
//---+ return of the index of the largest bar
   return(index);
  }
//+------------------------------------------------------------------+
//|  searching index of the lowest bar                               |
//+------------------------------------------------------------------+
int iLowest(
            const double &array[], // array for searching for the index of the maximum element
            int count,            // number of the elements in the array (in the decreasing order),
            int startPos          // starting index
            ) 
{
//---+
   int index = startPos;
   
   //--- checking the stating index
   if (startPos < 0)
     {
      Print("Incorrect value in the iLowest function, startPos = ",startPos);
      return(0);
     }
     
   //--- checking the startPos value
   if (startPos - count < 0) count = startPos;
    
   double min = array[startPos];
   
   //--- index search
   for(int i = startPos; i > startPos - count; i--)
     {
      if (array[i] < min)
        {
         index = i;
         min = array[i];
        }
     }
//---+ return of the index of the smallest bar
   return(index);
  }

Danach sollte der Code der OnCalculate() Funktion so aussehen:

//+------------------------------------------------------------------+
//| Custom indicator iteration function                              |
//+------------------------------------------------------------------+
int OnCalculate( const int rates_total,    // total number of bars on the current tick
               const int prev_calculated,// number of calculated bars on the previous tick
               const datetime& time[],
               const double& open[],    
               const double& high[],     // price array for the maximum price for the indicator calculation
               const double& low[],      // price array for the minimum price for the indicator calculation
               const double& close[],
               const long& tick_volume[],
               const long& volume[],
               const int& spread[]
             )
  {
//---+   
   //--- checking the number of bars
   if (rates_total < AroonPeriod - 1)
    return(0);
   
   //--- declare the local variables 
   int first, bar;
   double BULLS, BEARS;
   
   //--- calculation of the starting bar number
   if (prev_calculated == 0// checking for the first start of the indicator calculation
     first = AroonPeriod - 1; // starting number for the calculation of all of the bars

   else first = prev_calculated - 1; // starting number for the calculation of new bars

   //--- main loop
   for(bar = first; bar < rates_total; bar++)
    {
     //--- calculation of values
     BULLS = 100 - (bar - iHighest(high, AroonPeriod, bar) + 0.5) * 100 / AroonPeriod;
     BEARS = 100 - (bar - iLowest (low,  AroonPeriod, bar) + 0.5) * 100 / AroonPeriod;

     //--- filling the indicator buffers with the calculated values 
     BullsAroonBuffer[bar] = BULLS;
     BearsAroonBuffer[bar] = BEARS;
    }
//---+     
   return(rates_total);
  }
//+------------------------------------------------------------------+

Aus Gründen der Achsensymmetrie habe ich den Code leicht korrigiert, indem ich die waagrechte Verschiebung des Indikators, verglichen mit dem Original-Code, mit Hilfe des Wertes 0,5 hinzugefügt haben.

Hier sehen Sie die Ergebnisse der Arbeit dieses Indikators auf dem Chart:

                                                                              

Um die Position des Elements mit den maximalen oder minimalen Werten in einem Abstand nicht größer als AroonPeriod vom aktuellen Balken zu finden, verwenden wir die in MQL5 eingebauten ArrayMaximum() und ArrayMinimum() Funktionen, die zugleich auch nach den Extremen suchen. Diese Funktionen führen ihre Suche jedoch in aufsteigenden Reihenfolge aus

Die Suche muss jedoch auch in absteigender Reihenfolge der Indices stattfinden. Die hierfür einfachste Lösung ist die Richtung der Indizierung im Indikator und den Kurs-Buffern zu ändern, und zwar mit Hilfe der ArraySetAsSeries() Funktion.

Doch wir müssen ja auch die Richtung der Balkenanordnung in der Berechnungsschleife und den Berechnungsalgorithmus der ersten Variable ändern.

In diesem Fall sieht die OnCalculate() Funktion so aus:

//+------------------------------------------------------------------+
//| Custom indicator iteration function                              |
//+------------------------------------------------------------------+
int OnCalculate(
                const int rates_total,    // total number of bars on the current tick
                const int prev_calculated,// number of calculated bars on the previous tick
                const datetime& time[],
                const double& open[],    
                const double& high[],     // price array for the maximum price for the indicator calculation
                const double& low[],      // price array for the minimum price for the indicator calculation
                const double& close[],
                const long& tick_volume[],
                const long& volume[],
                const int& spread[]
              )
  {
//---+   
   //--- checking the number of bars
   if (rates_total < AroonPeriod - 1)
    return(0);
    
   //--- set indexation as timeseries
   ArraySetAsSeries(high, true);
   ArraySetAsSeries(low,  true);
   ArraySetAsSeries(BullsAroonBuffer, true);
   ArraySetAsSeries(BearsAroonBuffer, true);
   
   //--- declare the local variables 
   int limit, bar;
   double BULLS, BEARS;
   
   //--- calculation of the starting bar index
   if (prev_calculated == 0)                      // check for the first call of OnCalculate function
       limit = rates_total - AroonPeriod - 1// starting index for the calculation of all of the bars
   else limit = rates_total - prev_calculated; // starting index for the calculation of new bars
   
   //--- main loop
   for(bar = limit; bar >= 0; bar--)
    {
     //--- calculation of the indicator values
     BULLS = 100 + (bar - ArrayMaximum(high, bar, AroonPeriod) - 0.5) * 100 / AroonPeriod;
     BEARS = 100 + (bar - ArrayMinimum(low,  bar, AroonPeriod) - 0.5) * 100 / AroonPeriod;

     //--- filling the indicator buffers with the calculated values 
     BullsAroonBuffer[bar] = BULLS;
     BearsAroonBuffer[bar] = BEARS;
    }
//----+     
   return(rates_total);
  }
//+------------------------------------------------------------------+

Ich habe den Namen der Variabel von "Erste" zu "Limit" geändert, das ist für unser Beispiel besser geeignet.

In diesem Fall wird der Code der primären Schleife ähnlich erzeugt, wie bereits schon in MQL4. Schreibt man die OnCalculate() Funktion auf diese Art, können die Indikatoren bei nur minimalen Codeveränderungen von MQL4 zu MQL5 konvertiert werden.


Fazit

Fertig! Der Indikator ist geschrieben - und sogar in zwei Versionen.

Für eine exakte, konservative und schlaue Möglichkeit zur Lösung derartiger Probleme, erweisen sich die Lösungen nur unwesentlich komplizierter als das Basteln eines Kinderspielzeugs mit dem Lego-Constructor.

Übersetzt aus dem Russischen von MetaQuotes Ltd.
Originalartikel: https://www.mql5.com/ru/articles/48

Beigefügte Dateien |
sma_1.mq5 (3.92 KB)
aroon.mq5 (8.04 KB)
aroons.mq5 (6.28 KB)
zigzag.mq5 (9.17 KB)
Die Interaktion von MetaTrader 5 und MATLAB Die Interaktion von MetaTrader 5 und MATLAB
Dieser Beitrag beschäftigt sich mit den dEtailös der Interaktion zwischen MetaTrader 5 und dem MatLab Mathematik-Paket. Er erklärt die Mechanismen der Datenkonvertierung, den Entwicklungsprozess einer universellen Library, die mit dem MATLAB-Desktop interagieren kann. Zudem wird auch die Verwendung von DLL erklärt, die durch die MatLab Umgebung erzeugt werden. Dieser Beitrag richtet sich an bereits erfahrene Leser, die C++ und MQL5 kennen.
MQL für Anfänger: Wie man Objektklassen entwirft und baut MQL für Anfänger: Wie man Objektklassen entwirft und baut
Durch Erstellung eines Beispielprogramms von visuellen Designs, zeigen wir, wie man in MQL5 Klassen entwirft und baut. Dieser Beitrag richtet sich an Programmierer im Anfängerstadium, die auf MT5 Anwendung arbeiten. Wir schlagen hier eine einfache und leicht zu verstehende Technologie zur Erzeugung von Klassen vor, ohne dass man dazu tief in den Theorie des Objekt-orientieren Progammierens einsteigen muss.
Erzeugung eines Indikators mit graphischen Kontrolloptionen Erzeugung eines Indikators mit graphischen Kontrolloptionen
All diejenigen, die sich mit Stimmungen auf dem Markt auskennen, kennen den MACD Indikator (seiner voller Name lautet Gleitender Durchschnitt Konvergenz/Divergenz) -das leistungsstarke Tool zur Analyse von Kursbewegungen, das von Händlers seit dem ersten Auftauchen von Computer-gestützten Analysemethoden verwendet wird. Dieser Beitrag beschäftigt sich mit möglichen Veränderungen des MACD und ihrer Implementierung in einen Indikator mit der Möglichkeit, zwischen den Veränderungen graphisch hin- und her zu wechseln.
Genetische Algorithmen - Leicht gemacht! Genetische Algorithmen - Leicht gemacht!
Der Verfasser behandelt in diesem Beitrag evolution&auml;re Berechnungen mit Hilfe eines pers&ouml;nlich entwickelten, genetischen Algorithmus. Er zeigt die Funktionsweise dieses Algorithmus anhand von Beispielen und gibt praktische Empfehlungen f&uuml;r seine Verwendung.