Indikator für Renko-Diagramme

Dmitriy Zabudskiy | 20 Mai, 2016

Einleitung

Die Beiträge Indikator für Punkt- und Zeichendiagramme und Indikator für Kagi-Diagramme beschrieben die Prinzipien für die Zeichnung von Diagramm-Indikatoren der Typen Punkte und Zeichen und "Kagi". Betrachten wir nun die Programmierung von -Diagrammen.

Der Name "Renko" leitet sich vom japanischen Wort "renga" (Ziegel) ab. Das Renko-Diagramm wird aus einer Reihe von Ziegeln konstruiert, deren Erstellung durch Preisschwankungen definiert wird. Wenn ein Preis steigt, wird ein aufwärts gerichteter Ziegel im Diagramm platziert, bei sinkendem Preis ein abwärts gerichteter. "Renko" heißt im Japanischen "langsame Geschwindigkeit". Das Renko-Diagramm ist in Japan vermutlich irgendwann im 19. Jahrhundert erschienen. In den USA und Europa hörte man erstmals davon, nachdem Steve Nison 1994 sein Buch Beyond Candlesticks: New Japanese Charting Techniques Revealed veröffentlichte.

Das Renko-Diagramm und die oben erwähnten Diagramme ignorieren die Zeitskala und richten sich ausschließlich nach der Preisbewegung. Im Gegensatz zum Punkt- und Zeichendiagramm platziert das Renko jeden "Ziegel" in einer neuen Spalte (in einer neuen vertikalen Ebene), für den Rest gibt es eine gemeinsame Erstellungsmethode: Der Preis eines "Ziegels" ("Punkt", "Zeichen") ist fix, die Analyse des Preises und der Aufbau der Zeichen geschieht ähnlich.

Also ist das Renko-Diagramm ein Satz von vertikalen Balken ("Ziegeln"). Weiße (hohle) Ziegel werden verwendet, wenn der Trend aufwärts gerichtet ist, schwarze (ausgefüllte) Ziegel, wenn er abwärts gerichtet ist. Der Aufbau wird durch das Verhalten des Preises gesteuert. Der aktuelle Preis des beobachteten Zeitraums wird mit dem Minimum und Maximum des vorherigen Ziegels (weiß oder schwarz) verglichen. Wenn die Aktie mit einem höheren Preis als ihrem Öffnungspreis schließt, wird ein hohler (weißer) Ziegel gezeichnet, bei dem der untere Teil des Körpers den Öffnungspreis und der obere Teil den Schließungspreis darstellt. Wenn die Aktie mit einem niedrigeren Preis als ihrem Öffnungspreis schließt, wird ein ausgefüllter (schwarzer) Ziegel gezeichnet, bei dem der obere Teil des Körpers den Öffnungspreis und der untere Teil den Schließungspreis darstellt.

Der erste Ziegel des Diagramms wird abhängig vom Verhalten des Preises gezeichnet, der Öffnungspreis des Balkens wird dem Maximum und Minimum des vorherigen Ziegels entnommen.

Ein Beispiel eines standardmäßigen Renko-Diagramms sehen Sie in Abb. 1:

Abb. 1. Beispiel eines standardmäßigen Renko-Diagramms

Abb. 1. Beispiel eines standardmäßigen Renko-Diagramms

1. Beispiel der Diagrammzeichnung

Ein standardmäßiges Renko-Diagramm wird basierend auf dem Schließungspreis gezeichnet. Wählen Sie als Erstes den Timeframe und die Box-Größe.

In diesem Beispiel wird EURUSD (H4-Timeframe) mit einer Box-Größe von 30 Punkten verwendet. Das Ergebnis der Zeichnung des Renko-Diagramms von 03.01.2014 bis 31.01.2014 (etwa ein Monat) sehen Sie in Abb. 2. Auf der linken Seite ist das Diagramm eines bestimmten Timeframes abgebildet (hier sehen Sie die horizontale Ausdehnung von Ziegeln), auf der rechten sehen Sie das Ergebnis der Zeichnung des Renko-Diagramms:


Abb. 2. Ergebnis der Zeichnung des Renko-Diagramms auf EURUSD (H4, Box-Größe 30 Punkte) 

Abb. 2 Ergebnis der Zeichnung des Renko-Diagramms auf EURUSD (H4, Box-Größe 30 Punkte)

Sehen wir uns das Prinzip der Diagrammzeichnung näher an. In Abb. 2 zeigen rote horizontale Linien die Größe jedes Ziegels gemäß den Preisveränderungen (30 Punkte). Die interessantesten Daten sind blau gekennzeichnet.

Wie Sie im Diagramm sehen können, schließt eine Kerze am Ende des 03.01.2014 unter 1,3591 der vorher definierten Preisbereiche (rote horizontale Linien) bei 1,3589 (mit Preis markiert), wodurch ein nach unten gerichteter Ziegel im Diagramm erstellt wird.

Anschließend bleibt der Preis flach (er kommt nicht unter 1,3561 oder über 1,3651), er bleibt bis 10.01.2014, 20:00 geöffnet (die um 16:00 erstellte Kerze schließt) und schließt (oberhalb der Preismarkierung bei 1,3651) bei 1,3663 (mit Preis markiert). Anschließend wird der Preis bis 14.01.2014, 20:00 wieder flach (die um 16:00 erstellte Kerze schließt) und übersteigt dann den Preisbereich, erstellt einen neuen Ziegel und schließt bei 1,3684.

Im Anschluss sehen Sie einen nach unten gerichteten Tick, bei dem der Preis bei seiner Abwärtsbewegung im Diagramm vier Preisbereiche überschreitet. Am 23.01.2014 um 12:00 (die um 08:00 geöffnete Kerze schließt) gibt es einen aufwärts gerichteten Durchbruch zweier Preisbereiche, wodurch wiederum zur Schließung bei 1,3639 zwei Ziegel geöffnet werden. Der erste Ziegel ist deutlich sichtbar, der zweite wird zu einer langen vertikalen Linie gezogen (aufgrund der gleichzeitigen Öffnung mit dem ersten Ziegel). Der weitere Aufbau folgt den gleichen Prinzipien.


2. Prinzip der Zeichnung des Renko-Diagramms

Während der Entwicklung dieses Indikators wurden alle Funktionen so unabhängig wie möglich umgesetzt. Eins der Hauptziele war es, das Potenzial des Indikators für die einfachere Durchführung der Marktanalyse zu maximieren.

Die Berechnungen finden nicht innerhalb des aktuellen Timeframes statt, d. h. der Timeframe wird in den Einstellungen ausgewählt und wird unabhängig von dem Timeframe, auf dem der Indikator ausgeführt wird, die festgelegten Daten anzeigen. Dies lässt sich durch Kopieren der Daten des festgelegten Zeitraums in separate Puffer-Arrays bewerkstelligen. Später werden Berechnungen durchgeführt und die Ausgabepuffer des Indikators gefüllt.

Das standardmäßige Renko-Diagramm wird gemäß den Close-Preisen erstellt, allerdings werden die Werte von Open, High und Low genutzt, um die Analyse zu verbessern.

Da Ziegel im Renko-Diagramm die gleiche Größe haben, ist es nützlich, die dynamischsten Punkte des Marktes anhand des am stärksten ausgeprägten Preisverhaltens (in wenigen Ziegeln) zu kennen. Zu diesem Zweck gibt es eine (deaktivierte) Anzeige, die durch einen kleinen vertikalen Schatten eines Ziegels dargestellt wird (wie bei japanischen Kerzen), der zum letzten Ziegelniveau des Balkens des ausgewählten Timeframes steigt oder sinkt.

Die Möglichkeit, ZigZag im Hauptdiagramm zu erstellen, erweitert die grafische Analyse.

Abb. 3 zeigt den Indikator mit seiner vollen Funktionalität:

Abbildung 3. Indikator für das EURUSD-Diagramm (täglich, Schrittgröße 25 Punkte)

Abbildung 3. Indikator für das EURUSD-Diagramm (täglich, Schrittgröße 25 Punkte)


3. Code und Algorithmus des Indikators

Der Code des Indikators ist mit 900 Zeilen ziemlich groß. Wie bereits erwähnt, können maximal getrennte Funktionen das Verständnis des Algorithmus erschweren. Einige Funktionen aus dem vorherigen Beitrag werden als Basis verwendet. Falls Sie bestimmte Aspekte nicht verstehen, können Sie sich auf den Beitrag Indikator für die Erstellung eines Kagi-Diagramms beziehen oder mir eine E-Mail schreiben.

Jede Funktion des Codes wird im Beitrag erklärt. Funktionen werden an der entsprechenden Stelle beschrieben.


3,1. Indikator Eingabe-Parameter

Das Renko-Diagramm ist eine Ansammlung aufwärts und abwärts gerichteter verschiedenfarbiger Ziegel. Diese Art der Konstruktion erfordert nur fünf Puffer, kombiniert in der grafischen Konstruktion "farbige Kerzen". Die verbleibenden vier Puffer sammeln Daten, die für die Berechnung des Indikators benötigt werden.

Betrachten wir die in Gruppen eingeteilten Eingabeparameter (25).

  • step – Größe oder Schrittweite eines Ziegels;
  • type_step – Art des Schritts, in Punkten oder als prozentualer Anteil (letzterer wird abhängig vom aktuellen Preis berechnet);
  • magic_numb – magische Nummer, die zum Unterscheiden grafischer Objekte benötigt wird und genutzt wird, um sie aus dem Diagramm zu entfernen;
  • levels_number – Ebenen (0: keine Ebenen) zum Unterteilen von Ziegeln im Indikatorfenster;
  • levels_color – Farbe der Ebenen im Indikatorfenster;
  • time_frame – Festlegung eines Zeitraums für die Konstruktion des Diagramms (zu analysierender Zeitraum);
  • time_redraw – Aktualisierungszeit des Diagramms;
  • first_date_start – Anfangsdatum der Diagrammzeichnung;
  • type_price – Preisarten für die Konstruktion: Close – Standardmethode auf Basis des Schließungspreises; Open – Öffnungspreis; High – Höchstpreis; Low – Mindestpreis;
  • shadow_print – ist diese Option true, stellen Schatten den Höchst- oder Mindestpreis zum gleichzeitigen Öffnen mehrerer Ziegel dar;
  • filter_number – Wert der Ziegel für eine Umkehrung des Diagramms (eine Zusatzoption, die für die Menge der Ziegel verantwortlich ist, die für eine Umkehrung des Diagramms erforderlich sind);
  • zig_zag – Zeichnung von ZigZags im Hauptdiagramm (eine zusätzliche Darstellung im Hauptdiagramm, die die Analyse erleichtert oder für eine Aktualisierung des Diagramms genutzt wird);
  • zig_zag_shadow – Zeichnung von ZigZags gemäß den Höchst- und Mindestpreisen (nutzt die nächstgelegenen Höchst- und Mindestpreise für die Konstruktion von ZigZags an Randpunkten);
  • zig_zag_width – Linienstärke des ZigZag;
  • zig_zag_color_up – Farbe der aufwärts gerichteten Linien des ZigZag;
  • zig_zag_color_down – Farbe der abwärts gerichteten Linien des ZigZag;
  • square_draw – Zeichnung von Ziegeln im Hauptdiagramm (in diesem Modus sehen Sie die Preisbewegungen, die die Ziegel öffnen);
  • square_color_up – Farbe der Ziegel im Hauptdiagramm nach oben;
  • square_color_down – Farbe der Ziegel im Hauptdiagramm nach unten;
  • square_fill – Färbung der Ziegel im Hauptdiagramm;
  • square_width – Breite der Ziegellinien im Hauptdiagramm;
  • frame_draw – Zeichnung von Ziegelrändern (stellt Ränder von Ziegeln dar, selten verwendete Zusatzfunktion);
  • frame_width – Breite der Ziegellinien;
  • frame_color_up – Farbe der Ränder aufwärts gerichteter Ziegel;
  • frame_color_down – Farbe der Ränder abwärts gerichteter Ziegel.

Anschließend deklariert der Code Puffer: Fünf Hauptpuffer werden für den grafischen Aufbau verwendet, während vier zum Speichern des Designs und der Berechnung von Daten genutzt werden. Price[] – Puffer zum Speichern der kopierten Preise für den Aufbau, Date[] – Puffer zum Speichern kopierter Daten für das Zeichnen im Hauptdiagramm, Price_high[] und Price_low[] – Puffer zum Speichern der Höchst- und Mindestwerte für die Zeichnung von ZigZags im Hauptdiagramm.

Anschließend werden Arrays von Berechnungspuffern und Hilfsvariablen für Funktionen deklariert: func_draw_renko, func_draw_zig_zag, func_draw_renko_main_chart. Sie werden später erläutert.

//+------------------------------------------------------------------+
//|                                                         ABCR.mq5 |
//|                                   Azotskiy Aktiniy ICQ:695710750 |
//|                          https://www.mql5.com/ru/users/Aktiniy |
//+------------------------------------------------------------------+
//--- Auto Build Chart Renko
#property copyright "Azotskiy Aktiniy ICQ:695710750"
#property link      "https://www.mql5.com/ru/users/Aktiniy"
#property version   "1.00"
#property description "Auto Build Chart Renko"
#property description "   "
#property description "This indicator used to draw Renko chart in the indicator window, and in the main chart window"
#property indicator_separate_window
#property indicator_buffers 9
#property indicator_plots   1
//--- plot RENKO
#property indicator_label1  "RENKO"
#property indicator_type1   DRAW_COLOR_CANDLES
#property indicator_color1  clrRed,clrBlue,C'0,0,0',C'0,0,0',C'0,0,0',C'0,0,0',C'0,0,0',C'0,0,0'
#property indicator_style1  STYLE_SOLID
#property indicator_width1  1
//--- construction method
enum type_step_renko
  {
   point=0,   // Point
   percent=1, // Percent
  };
//--- type of price
enum type_price_renko
  {
   close=0, // Close
   open=1,  // Open
   high=2,  // High
   low=3,   // Low
  };
//--- input parameters
input double           step=10;                                 // Step
input type_step_renko  type_step=point;                         // Type of step
input long             magic_numb=65758473787389;               // Magic number
input int              levels_number=1000;                      // Number of levels (0-no levels)
input color            levels_color=clrLavender;                // Color of levels
input ENUM_TIMEFRAMES  time_frame=PERIOD_CURRENT;               // Calculation period
input ENUM_TIMEFRAMES  time_redraw=PERIOD_M1;                   // Chart redraw period
input datetime         first_date_start=D'2013.09.13 00:00:00'; // Start date
input type_price_renko type_price=close;                        // Price for construction
input bool             shadow_print=true;                       // Show shadows
input int              filter_number=0;                         // Bricks number needed to reversal
input bool             zig_zag=true;                            // Whether ZigZag should be drawn on the main chart
input bool             zig_zag_shadow=true;                     // Draw ZigZag at highs and lows of the price
input int              zig_zag_width=2;                         // ZigZag line width
input color            zig_zag_color_up=clrBlue;                // ZigZag up line color
input color            zig_zag_color_down=clrRed;               // ZigZag down line color
input bool             square_draw=true;                        // Whether bricks should be drawn on the main chart
input color            square_color_up=clrBlue;                 // Up brick color on the main chart
input color            square_color_down=clrRed;                // Down brick color on the main chart
input bool             square_fill=true;                        // Brick filling on the main chart
input int              square_width=2;                          // Brick line width on the main chart
input bool             frame_draw=true;                         // Whether to draw frames of the bricks
input int              frame_width=2;                           // Brick frame line width
input color            frame_color_up=clrBlue;                  // Up brick frames color
input color            frame_color_down=clrRed;                 // Down brick frames color
//--- indicator buffers
double         RENKO_open[];
double         RENKO_high[];
double         RENKO_low[];
double         RENKO_close[];
double         RENKO_color[];

double         Price[];      // copy price data to the buffer
double         Date[];       // copy data to the buffer
double         Price_high[]; // copy high prices to the buffer
double         Price_low[];  // copy low prices to the buffer
//--- calculation buffer arrays
double         up_price[];    // up brick price
double         down_price[];  // down brick price
char           type_box[];    // brick type (up, down)
datetime       time_box[];    // brick copy time
double         shadow_up[];   // up high price
double         shadow_down[]; // down low price
int            number_id[];   // Index of Price_high and Price_low arrays
//--- calculation global variables
int obj=0;           //variable for storing number of graphics objects
int a=0;             // variable to count bricks
int bars;            // number of bars
datetime date_stop;  // current data
datetime date_start; // start date variable, for calculations
bool date_change;    // variable for storing details about time changes


3,2. Initialisierungsfunktion des Indikators

Indikatorpuffer werden mit eindimensionalen dynamischen Arrays verbunden. Die Adressierung wird genauso wie bei Zeitreihen in den Puffern INDICATOR_DATA und INDICATOR_COLOR_INDEX festgelegt. Die Adressierung der verbleibenden dynamischen Arrays (Price[], Date[], Price_high[], Price_low[]) bleibt unverändert, da diese nur zum Speichern von Daten verwendet werden.

Werte, die nicht im Diagramm angezeigt werden, werden festgelegt. Anschließend wird der Name dem Indikator zugewiesen, die Genauigkeit der Anzeige festgelegt und die Anzeige der aktuellen numerischen Werte im Indikatorfenster verboten.

Danach wird der Wert der Variable date_start (Datum für den Beginn der Berechnungen) zugewiesen. Der Wert wird der Variable zugewiesen, der Eingabewert wird nicht verwendet, da das Diagramm sich als zu groß für den Indikatorpuffer erweisen kann. Das Anfangsdatum wird korrigiert und der Benutzer gewarnt. Die Funktion der Analyse des Startdatums, "func_calc_date_start", übernimmt die Korrektur der Zeit.

//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- indicator buffers mapping
   SetIndexBuffer(0,RENKO_open,INDICATOR_DATA);
   ArraySetAsSeries(RENKO_open,true);
   SetIndexBuffer(1,RENKO_high,INDICATOR_DATA);
   ArraySetAsSeries(RENKO_high,true);
   SetIndexBuffer(2,RENKO_low,INDICATOR_DATA);
   ArraySetAsSeries(RENKO_low,true);
   SetIndexBuffer(3,RENKO_close,INDICATOR_DATA);
   ArraySetAsSeries(RENKO_close,true);
   SetIndexBuffer(4,RENKO_color,INDICATOR_COLOR_INDEX);
   ArraySetAsSeries(RENKO_color,true);
//---
   SetIndexBuffer(5,Price,INDICATOR_CALCULATIONS);      // initialize price buffer
   SetIndexBuffer(6,Date,INDICATOR_CALCULATIONS);       // initialize data buffer
   SetIndexBuffer(7,Price_high,INDICATOR_CALCULATIONS); // initialize high price
   SetIndexBuffer(8,Price_low,INDICATOR_CALCULATIONS);  // initialize low price
//--- set data which will not be drawn
   PlotIndexSetDouble(0,PLOT_EMPTY_VALUE,0);
//--- set the indicator appearance
   IndicatorSetString(INDICATOR_SHORTNAME,"ABCR "+IntegerToString(magic_numb)); // indicator name
//--- display accuracy
   IndicatorSetInteger(INDICATOR_DIGITS,_Digits);
//--- prohibit display of the results of the indicator current values
   PlotIndexSetInteger(0,PLOT_SHOW_DATA,false);
//--- assign start date variable value
   date_start=first_date_start;
//---
   return(INIT_SUCCEEDED);
  }

3,3. Funktion der Berechnung des Anfangsdatums der Analyse

Die Funktion ist klein und besteht hauptsächlich aus einer Schleife. Es gibt nur zwei Eingabeparameter – die erste Festlegung des Anfangsdatums und das Enddatum der Berechnung (aktuelles Datum). Das Anfangsdatum wird in der Funktion geändert und als Antwort ausgegeben.

Der Körper der Funktion beginnt bei der Messung des empfangenden Puffer-Arrays (alle Puffer haben die gleiche Größe, die der Menge der Balken des ausgewählten Timeframes entspricht). Anschließend wird die Menge der Balken auf dem ausgewählten Timeframe gemessen.

Die Menge der Balken des ausgewählten Timeframes und die Größe des Puffer-Arrays werden in der Bedingung der Schleife verglichen. Falls Sie mehr Balken haben, sodass nicht alle im Puffer-Array platziert werden können, wird der ausgewählte Timeframe zu zehn Tagen gekürzt. Das bedeutet, dass zehn Tage zum Startdatum der Analyse hinzugefügt werden. Dies setzt sich fort, bis das Puffer-Array nicht mehr alle Daten der Balken aufnehmen kann. Die Funktion gibt das errechnete Datum aus.

//+------------------------------------------------------------------+
//| Func Calculate Date Start                                        |
//+------------------------------------------------------------------+
datetime func_calc_date_start(datetime input_data_start,// initially start date set
                              datetime data_stop)       // calculation end date (current date)
//---
  {
   int Array_Size=ArraySize(Price);
   int Bars_Size=Bars(_Symbol,time_frame,input_data_start,data_stop);
   for(;Bars_Size>Array_Size;input_data_start+=864000) // 864000 = 10 days
     {
      Bars_Size=Bars(_Symbol,time_frame,input_data_start,data_stop);
     }
   return(input_data_start);
//---
  }

3,4. Funktionen zum Kopieren von Daten

Als Erstes werden die Daten mit den Funktionen zum Kopieren der Daten kopiert (func_copy_price und func_copy_date).

Betrachten wir die Funktion zum Kopieren von Preisen, func_copy_price, die es Ihnen ermöglicht, Open-, Close-, High- und Low-Preise des festgelegten Zeitraums und Timeframes in das Array zu kopieren. Bei erfolgreichem Kopieren gibt die Funktion "true" aus.

Am Anfang des Funktionsaufrufs wird der Wert false initialisiert, anschließend wird eine Variable des Ergebnisses der kopieren Daten initialisiert und ein negativer Wert zugewiesen. Das gemeinsame Array price_interim[] zum Speichern temporärer kopierter Daten und die Variable bars_to_copy werden deklariert, um ein Speichern kopierter Daten zu vermeiden.

Weiterhin setzt die Funktion früher deklarierte Variablen zum Speichern der kopierten Daten zurück, berechnet die Menge der Balken auf dem Timeframe und weist in Übereinstimmung mit dem gewählten Preis (0-Close, 1-Open, 2-High und 3-Low) und dem Operator switch den Wert der vorher kopierten Daten anhand der Preise der Variable bars_copied zu. Anschließend wird die Menge der zu kopierenden Daten berechnet. Falls die Daten vorher kopiert wurden, werden die Informationen über den letzten kopierten Balken gelöscht, um Änderungen im Diagramm zu vermeiden.

Ein Umschalter kopiert die erforderlichen Preisdaten in das Zeit-Array price_interim[]. Anschließend wird das Ergebnis des Kopiervorgangs geprüft und ein Umschalter befüllt die Variablen kopierter Daten.

//+------------------------------------------------------------------+
//| Func Copy Price                                                  |
//+------------------------------------------------------------------+
bool func_copy_price(double &result_array[],
                     ENUM_TIMEFRAMES period,// Timeframe
                     datetime data_start,
                     datetime data_stop,
                     char price_type) // 0-Close, 1-Open, 2-High, 3-Low
  {
//---
   int x=false;        // Variable for answering
   int result_copy=-1; // copied data number
//---
   static double price_interim[]; // Temporal dynamic array for storing copied data
   static int bars_to_copy;       // number of bars to copy
   static int bars_copied_0;      // number of copied bars from Close start date
   static int bars_copied_1;      // number of copied bars from Open start date
   static int bars_copied_2;      // number of copied bars from High start date
   static int bars_copied_3;      // number of copied bars from Low start date
   static int bars_copied;        // number of copied bars from the common variable start date
//--- variables reset due to changes in a start date
   if(date_change==true)
     {
      ZeroMemory(price_interim);
      ZeroMemory(bars_to_copy);
      ZeroMemory(bars_copied_0);
      ZeroMemory(bars_copied_1);
      ZeroMemory(bars_copied_2);
      ZeroMemory(bars_copied_3);
      ZeroMemory(bars_copied);
     }
//--- get an information about the current bars number on the timeframe
   bars_to_copy=Bars(_Symbol,period,data_start,data_stop);
//--- assign a copied function value to a common variable
   switch(price_type)
     {
      case 0:
         //--- Close
         bars_copied=bars_copied_0;
         break;
      case 1:
         //--- Open
         bars_copied=bars_copied_1;
         break;
      case 2:
         //--- High
         bars_copied=bars_copied_2;
         break;
      case 3:
         //--- Low
         bars_copied=bars_copied_3;
         break;
     }
//--- calculate number of bars required to be copied
   bars_to_copy-=bars_copied; 
//--- if it is not the first time the data has been copied
   if(bars_copied!=0) 
     {
      bars_copied--;
      bars_to_copy++;
     }
//--- change the size of the recieving array
   ArrayResize(price_interim,bars_to_copy); 
//--- copy data to the recieving array
   switch(price_type)
     {
      case 0:
         //--- Close
        {
         result_copy=CopyClose(_Symbol,period,0,bars_to_copy,price_interim);
        }
      break;
      case 1:
         //--- Open
        {
         result_copy=CopyOpen(_Symbol,period,0,bars_to_copy,price_interim);
        }
      break;
      case 2:
         //--- High
        {
         result_copy=CopyHigh(_Symbol,period,0,bars_to_copy,price_interim);
        }
      break;
      case 3:
         //--- Low
        {
         result_copy=CopyLow(_Symbol,period,0,bars_to_copy,price_interim);
        }
      break;
     }
//--- check the result of data copying
   if(result_copy!=-1) // if copying to the intermediate array is successful
     {
      ArrayCopy(result_array,price_interim,bars_copied,0,WHOLE_ARRAY); // copy the data from the temporary array to the main one
      x=true;                   // assign the positive answer to the function
      bars_copied+=result_copy; // increase the value of the processed data
     }
//--- return the information about the processed data with one of the copied variables
   switch(price_type)
     {
      case 0:
         //--- Close
         bars_copied_0=bars_copied;
         break;
      case 1:
         //--- Open
         bars_copied_1=bars_copied;
         break;
      case 2:
         //--- High
         bars_copied_2=bars_copied;
         break;
      case 3:
         //--- Low
         bars_copied_3=bars_copied;
         break;
     }
//---
   return(x);
  }

 "func_copy_date", die Funktion zum Kopieren des Datums. Der Code der Funktion ähnelt dem obigen Block. Der Unterschied ist der Typ der kopierten Daten.

//+------------------------------------------------------------------+
//| Func Copy Date                                                   |
//+------------------------------------------------------------------+
bool func_copy_date(double &result_array[],
                    ENUM_TIMEFRAMES period,// timeframe
                    datetime data_start,
                    datetime data_stop)
  {
//---
   int x=false;                    // variable for answer
   int result_copy=-1;             // number of copied data
   static datetime time_interim[]; // temporaty dynamic array for storing the copied data
   static int bars_to_copy;        // bars number required to be copied
   static int bars_copied;         // copied bars with start date
//--- variables reset due to the start date change
   if(date_change==true)
     {
      ZeroMemory(time_interim);
      ZeroMemory(bars_to_copy);
      ZeroMemory(bars_copied);
     }
//---
   bars_to_copy=Bars(_Symbol,period,data_start,data_stop); // Find out the current number of bars on the time interval
   bars_to_copy-=bars_copied; // Calculate the number of bars to be copied
//---
   if(bars_copied!=0) // If it is not the first time the data has been copied
     {
      bars_copied--;
      bars_to_copy++;
     }
//---
   ArrayResize(time_interim,bars_to_copy); // Change the size of the receiving array
   result_copy=CopyTime(_Symbol,period,0,bars_to_copy,time_interim);
//---
   if(result_copy!=-1) // If copying to the intermediate array is successful
     {
      ArrayCopy(result_array,time_interim,bars_copied,0,WHOLE_ARRAY); // Copy the data from the temporary array to the main one
      x=true; // assign the positive answer to the function
      bars_copied+=result_copy; // Increase the value of the processed data
     }
//---
   return(x);
  }

3,5. Funktion zur Berechnung von Ziegeln

Wie Sie anhand der Parameter des Indikators sehen, kann die Größe eines Ziegels sowohl in Punkten als auch als prozentualer Anteil des aktuellen Preises ausgedrückt werden. Punkte sind ein fester Wert, doch wie wird der prozentuale Anteil berechnet? Zu diesem Zweck gibt es die Funktion zum Berechnen von Ziegeln, "func_calc_dorstep".

Es gibt drei Eingabeparameter: den aktuellen Preis (zum Berechnen des prozentualen Anteils des Preises, wenn die Ziegelgröße in Prozent angegeben wird), die Berechnungsmethode (Punkte oder Prozent) und die Schrittgröße (Eingabe als ein Wert, der in Prozent oder Punkten ausgedrückt werden kann).

Am Anfang der Funktion wird die Variable für die Antwort des typen double initialisiert und je nach ausgewählter Berechnungsmethode, die durch eine bedingte Wenn-Dann-Anweisung geprüft wird, in Punkten zugewiesen. Anschließend wird die Antwortvariable in int konvertiert, um den Wert ganzzahlig zu halten, auch wenn die Berechnung einen nicht ganzzahligen Wert ergibt.

//+------------------------------------------------------------------+
//| Func Calculate Doorstep                                          |
//+------------------------------------------------------------------+
int func_calc_dorstep(double price,      // price
                      char type_doorstep,// step type
                      double doorstep)   // step
  {
   double x=0;          // variable for answer

   if(type_doorstep==0) // If the calculation is to be performed in points
     {
      x=doorstep;
     }

   if(type_doorstep==1) // If the calculation is to be performed in percentage
     {
      x=price/_Point*doorstep/100;
     }

   return((int)x);
  }

3,6. Hauptfunktion – Aufbau des Renko-Diagramms

Die Hauptfunktion des Aufbaus des Renko-Diagramms – "func_draw_renko". Diese Funktion ist für die Befüllung von Grafikpuffern (Indikatorpuffern) und der Arrays von Berechnungspuffern verantwortlich. Berechnungspuffer speichern die Informationen über jeden Ziegel.

Die Eingabeparameter der Funktion sind Daten-Arrays von Preisen und Daten des Aufbaus von Balken. Hier finden Sie die Information über die Art des Schritts und dessen Parameter, den Umkehrfilter und den Parameter der Zeichnung von Schatten.

Die Funktion lässt sich in zwei Abschnitte unterteilen: den Teil für die Berechnung der Menge der Ziegel und den Teil für die Berechnung und Befüllung von Grafikpuffern.

Am Anfang der Funktion werden die Puffer zurückgesetzt, um leere Zellen zu deaktivieren. Danach werden Hilfsvariablen eingegeben: Die Variable "doorstep_now" wird für den Schritt verwendet (bei der Änderung seiner Größe beim Schritt als prozentualer Anteil), "point_go" speichert Informationen über die Distanz zum zuletzt aufgebauten Ziegel, "a" wird bei der Berechnung von Ziegeln verwendet, "up_price_calc" und "down_price_calc" sind die zuletzt analysierten Höchst- und Mindestpreise, "type_box_calc" ist der zuletzt analysierte Ziegeltyp (aufwärts oder abwärts).

Beide Funktionsteile bestehen aus einer Schleife, der zweite Teil vervollständigt den ersten. Analysieren wir den Prozess im Detail.

Die erste Schleife wird über alle kopierten Werte verarbeitet, der Wert "bars" ist für die Menge der kopierten Daten verantwortlich (Berechnung in der Funktion "func_concolidation", die später betrachtet wird). Im weiteren Verlauf der Schleife beginnt die Berechnung der Ziegelgröße. Wenn die Schrittgröße als Prozentsatz angegeben wird, muss sie für jeden Balken separat berechnet werden, da jeder Balken einen anderen Schließungspreis hat.

Die bedingte if-Anweisung prüft die Richtung des Preises, wobei der Preis eine Distanz von mindestens einem Schritt zurücklegen muss. Nach der Bestimmung der Richtung der Preisbewegung wird die Bedingung der vorherigen Bewegung (des letzten Ziegels) geprüft. Das liegt daran, dass die Parameter des Indikators einen Filterparameter enthalten (Menge der für eine Umkehrung erforderlichen Ziegel). Nach der Prüfung aller Bedingungen beginnt die Schleife. Sie wird so oft verarbeitet, wie die aktuelle Preisbewegung durch Ziegel dargestellt wird.

Die anzuzeigenden Balken werden berechnet, die Größe der Berechnungspuffer wird geändert und sie werden zurückgesetzt. Daraufhin werden den ersten paar (beim ersten Vergleich verwendeten) Berechnungs-Arrays die ersten Werte zugewiesen.

Wenn die maximal zulässige Menge der angezeigten Balken weniger ist als die mögliche Menge von Ziegeln, werden die überschüssigen Ziegel berechnet und eine Benachrichtigung über den zu geringen Wert angezeigt. Das geschieht, um eine falsche Darstellung des Diagramms zu vermeiden.

Die Variable der Berechnung der Menge von Ziegeln wird zurückgesetzt und die Hauptschleife beginnt. Im Gegensatz zur vorherigen Schleife ist die Hauptschleife ebenso für das Befüllen von Arrays von Berechnungspuffern und das Zurücksetzen des Ziegelzählers verantwortlich.

Am Ende der Funktion werden die Grafikpuffer befüllt.

//+------------------------------------------------------------------+
//| Func Draw Renko                                                  |
//+------------------------------------------------------------------+
void func_draw_renko(double &price[],   // prices array
                     double &date[],    // date array
                     int number_filter, // bricks number for reversal
                     bool draw_shadow,  // draw shadow
                     char type_doorstep,// step type
                     double doorstep)   // step
  {
//--- arrays reset
//--- drawing buffer arrays
   ZeroMemory(RENKO_close);
   ZeroMemory(RENKO_color);
   ZeroMemory(RENKO_high);
   ZeroMemory(RENKO_low);
   ZeroMemory(RENKO_open);
//--- additional variables
   int doorstep_now; // current step
   int point_go;     // passed points
//--- additional variables for bricks number calculating
   a=0;
   double up_price_calc=price[0];
   double down_price_calc=price[0];
   char type_box_calc=0;

   for(int z=0; z<bars; z++) //---> bricks calculating loop
     {
      //--- calculate step according to the current price
      doorstep_now=func_calc_dorstep(price[z],type_doorstep,doorstep);
      //--- if price rises
      if((price[z]-up_price_calc)/_Point>=doorstep_now)
        {
         //--- calculate points passed
         point_go=int((price[z]-up_price_calc)/_Point);
         //--- prices was rising or unknown price behavour
         if(type_box_calc==1 || type_box_calc==0)
           {
            for(int y=point_go; y>=doorstep_now; y-=doorstep_now)
              {
               //--- add the next brick 
               a++;
               //--- add value of the next brick low price
               down_price_calc=up_price_calc;
               //--- add value of the next brick up price
               up_price_calc=down_price_calc+(doorstep_now*_Point);
               //--- set the brick type (up)
               type_box_calc=1;
              }
           }
         //--- price went down
         if(type_box_calc==-1)
           {
            if((point_go/doorstep_now)>=number_filter)
              {
               for(int y=point_go; y>=doorstep_now; y-=doorstep_now)
                 {
                  //--- add the next brick
                  a++;
                  //--- set the next brick down price
                  down_price_calc=up_price_calc;
                  //--- set the next brick up price
                  up_price_calc=down_price_calc+(doorstep_now*_Point);
                  //--- set the brick type (up)
                  type_box_calc=1;
                 }
              }
           }
        }
      //--- if the price moves downwards
      if((down_price_calc-price[z])/_Point>=doorstep_now)
        {
         //--- calculate the points passed
         point_go=int((down_price_calc-price[z])/_Point);
         //--- if the price went downwards or the direction is unknown
         if(type_box_calc==-1 || type_box_calc==0)
           {
            for(int y=point_go; y>=doorstep_now; y-=doorstep_now)
              {
               //--- add the next brick
               a++;
               //--- set the next brick low price value
               up_price_calc=down_price_calc;
               //--- set the next brick up price value
               down_price_calc=up_price_calc-(doorstep_now*_Point);
               //--- set the britck type (up)
               type_box_calc=-1;
              }
           }
         //--- the price moved upwards
         if(type_box_calc==1)
           {
            if((point_go/doorstep_now)>=number_filter)
              {
               for(int y=point_go; y>=doorstep_now; y-=doorstep_now)
                 {
                  //--- add the next brick
                  a++;
                  //--- set the next brick down price value
                  up_price_calc=down_price_calc;
                  //--- set the next brick up price value
                  down_price_calc=up_price_calc-(doorstep_now*_Point);
                  //--- set the brick type (up)
                  type_box_calc=-1;
                 }
              }
           }
        }
     } //---< bricks calculate loop
//--- calculate the number of display bars
   int b=Bars(_Symbol,PERIOD_CURRENT);
//--- resize arrays
   ArrayResize(up_price,b);
   ArrayResize(down_price,b);
   ArrayResize(type_box,b);
   ArrayResize(time_box,b);
   ArrayResize(shadow_up,b);
   ArrayResize(shadow_down,b);
   ArrayResize(number_id,b);
//--- resize calculation buffers array
   ZeroMemory(up_price);
   ZeroMemory(down_price);
   ZeroMemory(type_box);
   ZeroMemory(time_box);
   ZeroMemory(shadow_up);
   ZeroMemory(shadow_down);
   ZeroMemory(number_id);
//--- fill arrays with the initial values
   up_price[0]=price[0];
   down_price[0]=price[0];
   type_box[0]=0;
//--- calculate odd bricks number
   int l=a-b;
   int turn_cycle=l/(b-1);
   int turn_rest=(int)MathMod(l,(b-1))+2;
   int turn_var=0;
//--- message of partially displayed bricks
   if(a>b)Alert("More bricks than can be placed on the chart, the step is small");

   a=0; //--- reset bricks claculating variable
   for(int z=0; z<bars; z++) //---> Main loop
     {
      //--- calculate the step according to the price
      doorstep_now=func_calc_dorstep(price[z],type_doorstep,doorstep);
      //---if the price moves upwards
      if((price[z]-up_price[a])/_Point>=doorstep_now)
        {
         //--- calculate the points passed
 point_go=int((price[z]-up_price[a])/_Point);
         //--- price moved upwards or its behavour is unknown
         if(type_box[a]==1 || type_box[a]==0)
           {
            for(int y=point_go; y>=doorstep_now; y-=doorstep_now)
              {
               a++; //--- add the next brick
               if((a==b && turn_var<turn_cycle) || (turn_var==turn_cycle && turn_rest==a))
                 {
                  up_price[0]=up_price[a-1];
                  a=1;        // bricks calculator reset
                  turn_var++; // calculator of loops reset
                 }
               //--- the next brick low price value
               down_price[a]=up_price[a-1];
               //--- set the brick up price 
               up_price[a]=down_price[a]+(doorstep_now*_Point);

               //--- set the up shadow value
               if(shadow_print==true) shadow_up[a]=price[z]; //to the upper price level
               else shadow_up[a]=up_price[a];                // to the up price level

               //--- set the low price value(to the brick price level)
               shadow_down[a]=down_price[a];
               //--- value of the brick closing time
               time_box[a]=(datetime)Date[z];
               //--- set the brick type (up)
               type_box[a]=1;
               //--- set the index
               number_id[a]=z;
              }
           }
         //--- the price moved downwards
         if(type_box[a]==-1)
           {
            if((point_go/doorstep_now)>=number_filter)
              {
               for(int y=point_go; y>=doorstep_now; y-=doorstep_now)
                 {
                  a++; //--- add the next brick

                  if((a==b && turn_var<turn_cycle) || (turn_var==turn_cycle && turn_rest==a))
                    {
                     up_price[0]=up_price[a-1];
                     a=1;        // bricks counter reset
                     turn_var++; // loops reset cycle
                    }
                  //--- set the next brick low price value
                  down_price[a]=up_price[a-1];
                  //--- set the next brick up price
                  up_price[a]=down_price[a]+(doorstep_now*_Point);

                  //--- set the up shadow value
                  if(shadow_print==true) shadow_up[a]=price[z]; // at the up price level
                  else shadow_up[a]=up_price[a];                // the brick up price level

                  //--- set of the down price value (the brick price level)
                  shadow_down[a]=down_price[a];
                  //--- set the close time
                  time_box[a]=(datetime)Date[z];
                  //--- set the up brick
                  type_box[a]=1;
                  //--- set index
                  number_id[a]=z;
                 }
              }
           }
        }

      //--- if price moves upwards
      if((down_price[a]-price[z])/_Point>=doorstep_now)
        {
         //--- calculate the points passed
         point_go=int((down_price[a]-price[z])/_Point);
         //--- price moved downwards or the direction is unknown
         if(type_box[a]==-1 || type_box[a]==0)
           {
            for(int y=point_go; y>=doorstep_now; y-=doorstep_now)
              {
               a++; //--- add the next brick
               if((a==b && turn_var<turn_cycle) || (turn_var==turn_cycle && turn_rest==a))
                 {
                  down_price[0]=down_price[a-1];
                  a=1;        // set the bricks counter to zero
                  turn_var++; // reset loop counter
                 }
               //--- set the next brick down price
               up_price[a]=down_price[a-1];
               //--- set the next brick up price
               down_price[a]=up_price[a]-(doorstep_now*_Point);

               //--- set the down shadow value 
               if(shadow_print==true) shadow_down[a]=price[z]; //--- the last lowest price level
               else shadow_down[a]=down_price[a];              //--- low price level

               //--- set the up price value
               shadow_up[a]=up_price[a];
               //--- set the brick close time
               time_box[a]=set the down shadow value];
               //--- set the brick type (down)
               type_box[a]=-1;
               //--- set index
               number_id[a]=z;
              }
           }
         //--- price moved upwards
         if(type_box[a]==1)
           {
            if((point_go/doorstep_now)>=number_filter)
              {
               for(int y=point_go; y>=doorstep_now; y-=doorstep_now)
                 {
                  a++; //--- add the next brick
                  if((a==b && turn_var<turn_cycle) || (turn_var==turn_cycle && turn_rest==a))
                    {
                     down_price[0]=down_price[a-1];
                     a=1;        // reset bricks counter
                     turn_var++; // reset loop counter
                    }

                  up_price[a]=down_price[a-1]; //--- set the next brick down price
                  down_price[a]=up_price[a]-(doorstep_now*_Point); //--- set the up price value

                  //--- set the down shadow value 
                  if(shadow_print==true) shadow_down[a]=price[z]; // at the lowest price level
                  else shadow_down[a]=down_price[a];              // at the down price level

                  //--- set the up price level
                  shadow_up[a]=up_price[a];
                  //--- set the brick close time
                  time_box[a]=(datetime)Date[z];
                  //--- set the brick type (down)
                  type_box[a]=-1;
                  //--- index set
                  number_id[a]=z;
                 }
              }
           }
        }
     } //---< Main loop

//--- fill the draw buffer
   int y=a;
   for(int z=0; z<a; z++)
     {
      if(type_box[y]==1)RENKO_color[z]=0;
      else RENKO_color[z]=1;
      RENKO_open[z]=down_price[y];
      RENKO_close[z]=up_price[y];
      RENKO_high[z]=shadow_up[y];
      RENKO_low[z]=shadow_down[y];
      y--;
     }
  }


3,7. Funktionen zum Erstellen der grafischen Objekte "Trendlinie" und "Rechteck"

Die Funktion "func_create_trend_line" zum Erstellen des grafischen Objekts "Trendlinie" und die Funktion "func_create_square_or_rectangle" zum Erstellen des grafischen Objekts "Rechteck" basieren auf den Daten aus dem Leitfaden zu OBJ_RECTANGLE und OBJ_TREND. Sie werden zum Erstellen von grafischen Objekten im Renko-Diagramm und zum Aufbau von ZigZag im Hauptdiagramm genutzt.

//+------------------------------------------------------------------+
//| Func Create Trend Line                                           |
//+------------------------------------------------------------------+
void func_create_trend_line(string name,
                            double price1,
                            double price2,
                            datetime time1,
                            datetime time2,
                            int width,
                            color color_line)
  {
   ObjectCreate(0,name,OBJ_TREND,0,time1,price1,time2,price2);
//--- set the line color
   ObjectSetInteger(0,name,OBJPROP_COLOR,color_line);
//--- set the line display style
   ObjectSetInteger(0,name,OBJPROP_STYLE,STYLE_SOLID);
//--- set the width of the line
   ObjectSetInteger(0,name,OBJPROP_WIDTH,width);
//--- display in the foreground (false) or in the (true) background
   ObjectSetInteger(0,name,OBJPROP_BACK,false);
//--- enable (true) or disable (false) the mode of the left line display
   ObjectSetInteger(0,name,OBJPROP_RAY_LEFT,false);
//--- enable (true) or disable (false) the right line display
   ObjectSetInteger(0,name,OBJPROP_RAY_RIGHT,false);
  }
//+------------------------------------------------------------------+
//| Func Create Square or Rectangle                                  |
//+------------------------------------------------------------------+
void func_create_square_or_rectangle(string name,
                                     double price1,
                                     double price2,
                                     datetime time1,
                                     datetime time2,
                                     int width,
                                     color color_square,
                                     bool fill)
  {
//--- create rectangle according to the setpoints 
   ObjectCreate(0,name,OBJ_RECTANGLE,0,time1,price1,time2,price2);
//--- set the rectangle color
   ObjectSetInteger(0,name,OBJPROP_COLOR,color_square);
//--- set style of rectangle color
   ObjectSetInteger(0,name,OBJPROP_STYLE,STYLE_SOLID);
//--- set lines width
   ObjectSetInteger(0,name,OBJPROP_WIDTH,width);
//--- activate (true) or disactivate (false) mode of rectangle colouring
   ObjectSetInteger(0,name,OBJPROP_FILL,fill);
//--- display in the foreground (false) or in the background (true)
   ObjectSetInteger(0,name,OBJPROP_BACK,false);
  }


3,8. Aufbau des Renko-Diagramms im Hauptdiagramm

Aufgrund der Verwendung gemeinsamer Arrays für Berechnungspuffer ist die Funktion "func_draw_renko_main_chart" für die Zeichnung des Renko-Diagramms ziemlich kompakt.

Zu den Eingabeparametern gehören: aufwärts und abwärts gerichtete Ziegel mit Rahmen, zwei Arten der Rahmenbreite (die erste wird für den Ziegel benutzt, die zweite für dessen Rahmen), drei Anzeigeoptionen (von Ziegeln sowie deren Farben und Rahmen).

Als Erstes werden die Variablen mit Namen von Objekten deklariert, dann wird die Schleife mit dem erzeugten Namen jedes Objekts geöffnet und abhängig vom Typen des vorherigen Ziegels werden die Funktionen der grafischen Objekte "Trendlinie" und "Rechteck" ausgeführt. Die Parameter werden aus den Arrays der Berechnungspuffer bezogen.

//+------------------------------------------------------------------+
//| Func Draw Renko Main Chart                                       |
//+------------------------------------------------------------------+
void func_draw_renko_main_chart(color color_square_up,
                                color color_square_down,
                                color color_frame_up,
                                color color_frame_down,
                                int width_square,
                                int width_frame,
                                bool square,
                                bool fill,
                                bool frame)
  {
   string name_square;
   string name_frame;

   for(int z=2; z<=a; z++)
     {
      name_square=IntegerToString(magic_numb)+"_Square_"+IntegerToString(z);
      name_frame=IntegerToString(magic_numb)+"_Frame_"+IntegerToString(z);
      if(type_box[z]==1)
        {
         if(square==true)func_create_square_or_rectangle(name_square,up_price[z],down_price[z],time_box[z-1],time_box[z],width_square,color_square_up,fill);
         if(frame==true)func_create_square_or_rectangle(name_frame,up_price[z],down_price[z],time_box[z-1],time_box[z],width_frame,color_frame_up,false);
        }
      if(type_box[z]==-1)
        {
         if(square==true)func_create_square_or_rectangle(name_square,up_price[z],down_price[z],time_box[z-1],time_box[z],width_square,color_square_down,fill);
         if(frame==true)func_create_square_or_rectangle(name_frame,up_price[z],down_price[z],time_box[z-1],time_box[z],width_frame,color_frame_down,false);
        }
     }
  }

3,9. Aufbau des ZigZag-Diagramms im Hauptdiagramm

Die nächste Ergänzung des Indikators ist die Funktion "func_draw_zig_zag" für die Erstellung des ZigZag-Diagramms.

Eingabeparameter: Art der Zeichnung (nach Höchst- oder Mindestpreisen oder nach Diagrammpunkten), Linienbreite, Farbe der aufwärts oder abwärts gerichteten Linien.

Die Änderung des Parameters "zig_zag_shadow" ist in Abbildung 4 sichtbar. Beim Wert "true" zeichnet der Indikator die ZigZag-Linien nach den Punkten der Schatten (Mindest- und Höchstpreise), bei "false" werden die ZigZag-Linien nach den Höchst- und Mindestpunkten des Renko-Diagramms gezeichnet.


Abb. 4. Wirkung des Parameters "zig_zag_shadow" auf EURUSD, H1, 10 Punkte.

Abb. 4. Wirkung des Parameters "zig_zag_shadow" auf EURUSD, H1, 10 Punkte. 

Für den Aufbau des Objekts "Trendlinie" werden zwei Punkte (Beginn und Ende) benötigt. Geben Sie zwei Variablen für den Preis-Parameter und zwei für den Datum-Parameter ein. Bedingte if-Anweisungen legen den ersten Punkt abhängig vom Typen des ursprünglichen Balkens fest.

Die Schleife, die alle Objekte aufbaut, wird gestartet. Wie Sie sehen können, beginnt die Schleife bei der Analyse des zweiten Ziegels, da der erste Punkt bereits festgelegt ist. Anschließend prüft die bedingte if-Anweisung die Art des Ziegels (das Preisverhalten). Die Variable des Objektnamens wird befüllt und die Schleife wird abhängig von der Bewegungsänderung aufgeteilt. Diese Aufteilung wird abhängig von der Zeichnungsmethode ebenfalls in zwei Varianten unterteilt.

Bei der Anzeige auf den Höchst- und Mindestpreisen suchen die Daten-Arrays Price_high[] und Price_low[] nach den nächstliegenden Mindest- und Höchstpunkten. Die Suche wird durch die benachbarten Balken eingegrenzt.

Bei einem Aufbau nach Diagrammpunkten werden die Daten aus den Arrays der Puffer zugewiesen.

Die Funktion zum Aufbau der Trendlinie wird aufgerufen. Die Funktion beendet die Analyse und Zeichnung des ZigZag.

//+------------------------------------------------------------------+
//| Func Draw Zig Zag                                                |
//+------------------------------------------------------------------+
void func_draw_zig_zag(bool price_shadow,
                       int line_width,
                       color line_color_up,
                       color line_color_down)
  {
   double price_1=0;
   double price_2=0;
   datetime date_1=0;
   datetime date_2=0;

   if(type_box[1]==1)price_1=down_price[1];
   if(type_box[1]==-1)price_1=up_price[1];
   date_1=time_box[1];
   int id=0; //  Low & High array storing variable
   int n=0;  // variable for name forming

   string name_line; //--- variable responsible for the "trend line" name

   for(int z=2; z<=a; z++)
     {
      if(type_box[z]!=type_box[z-1])
        {
         n++;
         name_line=IntegerToString(magic_numb)+"_Line_"+IntegerToString(n);
         if(type_box[z]==1)
           {
            if(price_shadow==true)
              {
               id=number_id[z-1];
               if((id-1)>0 && Price_low[id-1]<Price_low[id])id--;
               if(Price_low[id+1]<Price_low[id])id++;
               price_2=Price_low[id];
               date_2=(datetime)Date[id];
              }
            else
              {
               price_2=down_price[z-1];
               date_2=time_box[z-1];
              }
            func_create_trend_line(name_line,price_1,price_2,date_1,date_2,line_width,line_color_down);
            price_1=price_2;
            date_1=date_2;
           }
         if(type_box[z]==-1)
           {
            if(price_shadow==true)
              {
               id=number_id[z-1];
               if((id-1)>0 && Price_high[id-1]>Price_high[id])id--;
               if(Price_high[id+1]>Price_high[id])id++;
               price_2=Price_high[id];
               date_2=(datetime)Date[id];
              }
            else
              {
               price_2=up_price[z-1];
               date_2=time_box[z-1];
              }
            func_create_trend_line(name_line,price_1,price_2,date_1,date_2,line_width,line_color_up);
            price_1=price_2;
            date_1=date_2;
           }
        }
     }
  }

3,10. Funktion zum Löschen von grafischen Objekten

Zum Bestimmen der Objekte des Indikators wird die magische Nummer verwendet. Dies vereinfacht die Ausführung mehrerer Indikatoren auf einem Diagramm und den Prozess der Löschung von Objekten.

Die nächste Funktion ist die Funktion "func_delete_objects" zum Löschen von Objekten. Die zwei Eingabeparameter sind der Name (Festlegung abhängig von den Objekten – Trendlinie oder Rechteck) und die Menge der Objekte. Die Funktion wählt Objekte aus und löscht Objekte mit bereits zugewiesenem Namen.

//+------------------------------------------------------------------+
//| Func Delete Objects                                              |
//+------------------------------------------------------------------+
void func_delete_objects(string name,
                         int number)
  {
   string name_del;
   for(int x=0; x<=number; x++)
     {
      name_del=name+IntegerToString(x);
      ObjectDelete(0,name_del);
     }
  }

Zur Vereinfachung wurde eine Funktion erstellt, die alle Funktionen zum Löschen aller Indikatorobjekte vereint.

//+------------------------------------------------------------------+
//| Func All Delete                                                  |
//+------------------------------------------------------------------+
void func_all_delete()
  {
//--- the graphical objects calculating
   obj=ObjectsTotal(0,-1,-1);
//--- all indicator graphical objects deleting
   func_delete_objects(IntegerToString(magic_numb)+"_Line_",obj);
   func_delete_objects(IntegerToString(magic_numb)+"_Square_",obj);
   func_delete_objects(IntegerToString(magic_numb)+"_Frame_",obj);
//--- the chart redrawing
   ChartRedraw(0);
  }


3,11. Funktion zum Erstellen von Ebenen

Die Funktion "func_create_levels" zum Erstellen von Ebenen erleichtert die Darstellung des Diagramms im Indikatorfenster. Sie hat nur zwei Eingabeparameter: die Anzahl der erstellten Ebenen und deren Farbe.

Im Körper der Funktion wird die Funktion IndicatorSetInteger verwendet, um die Menge der anzuzeigenden Ebenen festzulegen. Anschließend werden für jede Ebene der Preis und die Farbe bestimmt.

//+------------------------------------------------------------------+
//| Func Create Levels                                               |
//+------------------------------------------------------------------+
void func_create_levels(int level_number,
                        color level_color)
  {
//--- set the number of levels in the indicator window
   IndicatorSetInteger(INDICATOR_LEVELS,level_number);
 which brick is taken to draw levels
   int k=0;
   if(a>level_number)k=a-level_number;
//--- set levels prices
   for(int z=0;(z<=level_number && k<=a); z++,k++)
     {
      IndicatorSetDouble(INDICATOR_LEVELVALUE,z,up_price[k]);
      IndicatorSetInteger(INDICATOR_LEVELCOLOR,z,level_color);
     }
  }

3,12. Konsolidierungsfunktion

Die Funktion "func_consolidation" wurde zum Konsolidieren aller Funktionen erstellt.

Diese Funktion ruft alle auszuführenden Funktionen auf.

//+------------------------------------------------------------------+
//| Func Consolidation                                               |
//+------------------------------------------------------------------+
void func_concolidation()
  {
//--- deleting all the graphical objects of the indicator
   func_all_delete();
//--- the current date
   date_stop=TimeCurrent();
//--- the initial date changing due to the restricted buffer size
   if((bars=Bars(_Symbol,time_frame,date_start,date_stop))>ArraySize(Price))
     {
      date_start=func_calc_date_start(date_start,date_stop);
      Alert("The initial date was changed due to the lack of the chart size");
      date_change=true;
      //--- calculation of bars on the taken timeframe
      bars=Bars(_Symbol,time_frame,date_start,date_stop);
     }
//---
   bool result_copy_price=func_copy_price(Price,time_frame,date_start,date_stop,type_price);
   bool result_copy_date=func_copy_date(Date,time_frame,date_start,date_stop);
//--- change the date parameter
   if(result_copy_price=true && result_copy_date==true)date_change=false;
//---
   if(zig_zag_shadow==true)
     {
      func_copy_price(Price_high,time_frame,date_start,date_stop,2);
      func_copy_price(Price_low,time_frame,date_start,date_stop,3);
     }
//---
   func_draw_renko(Price,Date,filter_number,shadow_print,type_step,step);
   if(zig_zag==true)func_draw_zig_zag(zig_zag_shadow,zig_zag_width,zig_zag_color_up,zig_zag_color_down);
//---
   func_draw_renko_main_chart(square_color_up,square_color_down,frame_color_up,frame_color_down,square_width,frame_width,square_draw,square_fill,frame_draw);
   func_create_levels(levels_number,levels_color);
//--- redraw the chart
   ChartRedraw(0);
  }

3,13. Funktionen OnCalculate() und OnChartEvent()

Bevor wir mit der Funktion OnCalculate() fortfahren, sehen wir uns die Funktion "func_new_bar" an, die den neuen Balken analysiert.

Dabei handelt es sich um die vereinfachte, in IsNewBar beschriebene Funktion.

//+------------------------------------------------------------------+
//| Func New Bar                                                     |
//+------------------------------------------------------------------+
bool func_new_bar(ENUM_TIMEFRAMES period_time)
  {
//---
   static datetime old_times; // array for storing old values
   bool res=false;            // analysis result variable 
   datetime new_time[1];      // new bar time
//---
   int copied=CopyTime(_Symbol,period_time,0,1,new_time); // copy the time of the new bar into the new_time box 
//---
   if(copied>0) // все ок. data have been copied
     {
      if(old_times!=new_time[0])    // if the bar's old time is not equal to new one
        {
         if(old_times!=0) res=true; // if it is not the first launch, true = new bar
         old_times=new_time[0];     // store the bar's time
        }
     }
//---
   return(res);
  }

Die Funktion OnCalculate() startet die Konsolidierung aller Funktionen, wenn während der Aktualisierung des Diagramms ein neuer Balken entsteht. 

//+------------------------------------------------------------------+
//| Custom indicator iteration function                              |
//+------------------------------------------------------------------+
int OnCalculate(const int rates_total,
                const int prev_calculated,
                const int begin,
                const double &price[])
  {
//---
   if(func_new_bar(time_redraw)==true)
     {
      func_concolidation();
     }
//--- return value of prev_calculated for next call
   return(rates_total);
  }

Die Funktion OnChartEvent() löscht alle grafischen Objekte durch Drücken der Taste "C". Mit der Taste "R" wird die Neuzeichnung des Diagramms gestartet (Konsolidierungsfunktion).

//+------------------------------------------------------------------+
//| OnChartEvent                                                     |
//+------------------------------------------------------------------+
void OnChartEvent(const int id,         // event ID 
                  const long& lparam,   // long type event parameter
                  const double& dparam, // double type event parameter
                  const string& sparam) // string type event parameter
  {
//--- Keyboard button pressing event
   if(id==CHARTEVENT_KEYDOWN)
     {
      if(lparam==82) //--- "R" key has been pressed
        {
         //--- call of the consolidation function
         func_concolidation();
        }
      if(lparam==67) //--- "C" key has been pressed
        {
         //--- deletion of all objects of the indicator
         func_all_delete();
        }
     }
  }


3,14. Funktion OnDeinit()

Zu guter Letzt folgt die Funktion OnDeinit(). Diese Funktion startet die Funktion zum Löschen aller grafischen Objekte des Indikators.

//+------------------------------------------------------------------+
//| Custom indicator deinitialization function                       |
//+------------------------------------------------------------------+ 
void OnDeinit(const int reason)
  {
//--- delete all graphical objects of the indicator
   func_all_delete();
  }


4. Praktische Anwendung des Renko-Diagramms

Das Renko-Diagramm wird in Übereinstimmung mit der Strategie der Preisbewegungen aufgebaut.

Beginnen wir mit der populärsten Strategie: Verkaufen, wenn der aufwärts gerichtete Ziegel beginnt, sich abwärts zu bewegen, und kaufen, wenn das Gegenteil eintritt.

Dies wird in Abb. 5 gezeigt:


Abb. 5. Standardmäßiges Renko-Diagramm (EURUSD, H4, 20 Punkte)

Abb. 5. Standardmäßiges Renko-Diagramm (EURUSD, H4, 20 Punkte)

Abb. 5 zeigt sechs Punkte des Markteintritts (A,B,C,D,E,F).

Bei Punkt "A" wird aus dem aufwärts gerichteten Ziegel ein abwärts gerichteter.

Der umkehrende Ziegel in B,C,D wird durch eine Bewegung erstellt. Allerdings wurden bei Punkt "E" zwei Ziegel durch eine Bewegung erstellt. Dies ist anhand der abwärts gerichteten Schatten erkennbar, die auf der gleichen Ebene erstellt wurden.

In diesem Fall liegt der mögliche Eintrittspunkt zwischen den Punkten "E" und "F". Dabei handelt es sich nicht um einen erfolgreichen Eintritt, da sich der Preis in entgegengesetzter Richtung bewegt. Bei Punkt "F" liegt eine ähnliche Situation vor, in der eine Bewegung ebenfalls zwei Ziegel erstellt. Die aufwärts gerichteten Schatten befinden sich auf der gleichen Ebene. Trotz der starken Bewegung ändert der Preis nicht seine Richtung.

Daraus lässt sich schlussfolgern, das der günstigste Moment für den Markteintritt dann vorliegt, wenn ein umkehrender Ziegel (achten Sie auf die Schatten) durch eine Bewegung erstellt wird. Wenn zwei Ziegel gleichzeitig erstellt werden, ist der Eintritt möglicherweise unsicher.

Der Aufbau von ZigZag in diesem Diagramm kann für die grafische Analyse genutzt werden. Abb. 6 zeigt ein paar Beispiele: die Unterstützungs- und Widerstandslinien sowie das Modell "Kopf und Schultern".


Abb. 6. Grafische Analyse (GBPUSD, H4, 20 Punkte)

Abb. 6. Grafische Analyse (GBPUSD, H4, 20 Punkte)

Die grafische Analyse "Abstandsgleicher Kanal" wird in Abb. 7 gezeigt.

Der Indikator ist für die Analyse des Timeframes vorgesehen und der Aufbau wird auf dem vierstündigen Timeframe angezeigt.

Solche Einstellungen ermöglichen es dem Kunden, Signale auf unterschiedlichen Timeframes gleichzeitig zu verfolgen, was bedeutet, dass ein Indikator auf einem Timeframe und der andere auf dem zweiten verwendet werden kann.


Abb. 7. Analyse des "Abstandsgleichen Kanals" USDCHF, H4, Einstellungen auf H1, 20 Punkte.

Abb. 7. Analyse des "Abstandsgleichen Kanals" USDCHF, H4, Einstellungen auf H1, 20 Punkte.

Abb. 8 zeigt ein weiteres Beispiel für unterschiedliche Timeframes in einem Diagramm.

Das Zeitdiagramm zeigt die möglichen nächstliegenden Umkehrungen. Das vierstündige Diagramm löscht nutzlose Signale, das tägliche Diagramm bestätigt die langfristigen Tendenzen der Bewegung.


Abb. 8. Renko-Indikator auf GBPUSD, H1, H4 und D1

Abb. 8. Renko-Indikator auf GBPUSD, H1, H4 und D1

Ein weiteres Beispiel des Indikators sehen Sie in Abb. 9. Die Regel ist: Errichten Sie die aufsteigende Linie zwischen den nächstgelegenen roten Ziegeln mit mindestens einem blauen Ziegel zwischen ihnen und verkaufen Sie, nachdem ein Ziegel unter der Linie entsteht.

Und umgekehrt: Errichten Sie die absteigende Linie zwischen den nächstgelegenen blauen Ziegeln mit mindestens einem roten Ziegel zwischen ihnen und verkaufen Sie, nachdem ein Ziegel über der Linie entsteht.

Die aufgezählten Farben entsprechen Abb. 9. Abb. 9. Blaue und rote Pfeile kennzeichnen die Orte der Zeichnung der Linie und große Pfeile kennzeichnen Signale zum Verkaufen und Kaufen.

Abb. 9. Beispiel des Indikators für GBPUSD, H4, 25 Punkte

Abb. 9. Beispiel des Indikators für GBPUSD, H4, 25 Punkte

Fazit

Das Renko-Diagramm ist sowohl für Einsteiger als auch für professionelle Händler von Interesse. Nach vielen Jahren wird es immer noch auf den Märkten genutzt.

In diesem Beitrag wollte ich Ihre Aufmerksamkeit auf dieses Diagramm richten und die Analyse von Renko-Diagrammen verbessern. Ich habe versucht, die Methode des Aufbaus von Renko-Diagrammen im Detail zu erläutern.

Über neue Ideen und Verbesserungsvorschläge für den Indikator würde ich mich sehr freuen und versuchen, sie in Zukunft umzusetzen. Es gibt viele Arten, den Indikator umzusetzen. Vielleicht finden Sie Ihre eigenen Methoden, ihn zu implementieren.

Vielen Dank für Ihr Interesse! Ich wünsche Ihnen erfolgreiches Handeln und das Entdecken und Umsetzen neuer Handelsstrategien.