Ein Indikator zum Anlegen eines Kagi-Diagramms

Dmitriy Zabudskiy | 1 Juni, 2016


Einleitung

In dem Beitrag Ein Indikator zum Anlegen eines Punkt und Zeichen-Diagramms wird eine der Möglichkeiten für das programmgestützte Anlegen eines Punkt und Zeichen-Diagramms vorgestellt. Diese Art Diagramme kennt man seit dem 19. Jahrhundert. Aber es handelt sich nicht um die einzigen Diagramme, die aus grauer Vorzeit zu uns gelangt sind. Ein weiterer illustrer Vertreter überkommener Vorstellungen von dem Finanzmarkt ist das Kagi-Diagramm. Und genau um dieses geht es in dem hier vorliegenden Beitrag.

Im Mai 1878 wurde das erste, bis dahin in Japan noch unbekannte Finanzinstitut eröffnet, das heute als Tokioter Börse weltweite Bekanntheit genießt. Eben dieses Ereignis hat wesentlich zur Erstellung und anschließenden Weiterentwicklung der Kagi-Diagramme beigetragen. In den USA und Europa wurden die Kagi-Diagramme jedoch erst nach der Veröffentlichung des Buches Jenseits der Kerzen: Wiederentdeckte neue japanische Abbildungsverfahren (Beyond Candlesticks: New Japanese Charting Techniques Revealed) von Steve Nison im Jahr 1994 bekannt.

Seinen Namen verdankt das Diagramm dem japanischen Wort „Kagi“, das seinerzeit einen (heute aus der Mode gekommenen) L-förmigen Schlüssel bezeichnete. Außerdem gibt es eine Abwandelung dieser Bezeichnung, sie lautet: „Schlüsseldiagramm“. In Nisons Buch (s. o.) werden ebenfalls alternative Bezeichnungen für das Diagramm aufgeführt: Kursbereichsdiagramm, Hakendiagramm, Delta- oder Kettendiagramm.

Aber was verbirgt sich denn nun hinter diesem Diagramm? Sein wesentlicher Unterschied von den uns geläufigen Diagrammen (Folgen von japanischen Kerzen, Balken und unterbrochenen Linien) besteht darin, dass es den zeitlichen Maßstab ignoriert und nur den des Kurses wiedergibt. Dadurch entsteht eine eigentümliche Art von Diagramm, das unbedeutende Kursschwankungen herausfiltert und nur die aussagekräftigsten belässt.

Das Diagramm stellt die Gesamtheit der einander je nach Marktlage abwechselnden starken „Yang“- und der feinen „Yin“-Linien dar. Bei einer Fortsetzung der Marktbewegung in eine bestimmte Richtung wird die Linie verlängert, wobei sie sich den neuen Kursbereich unterordnet. Wenn der Markt jedoch einen vorgegebenen Wert annimmt, wird in einer neuen Spalte eine Kagi-Linie in gegenläufiger Richtung angelegt. Der vorgegebene Wert wird entweder in Punkten (bei Währungspaaren üblich) oder in Prozent des aktuellen Kurses (bei Wertpapieren) festgelegt. Die Linienstärke ändert sich je nach Durchbruch des nächst gelegenen Extremwertes (Minimum oder Maximum).


1. Beispiel für das Anlegen eines Diagramms

Als Beispiel verwenden wir die Daten für den Zeitraum vom 8. bis 11. Oktober aus dem Kursverlauf für das Kürzel EURUSD, H1.

Das Beispiel in der Abbildung 1 veranschaulicht die Erstellung eines klassischen Diagramms mit einer 15 Punkte umfassenden Umkehrschwelle:

Beispiel der Erstellung eines Kagi-Diagramms für das Kürzel EURUSD mit einstündigem Zeitmaßstab (H1)

Abb. 1. Beispiel der Erstellung eines Kagi-Diagramms für das Kürzel EURUSD mit einstündigem Zeitmaßstab (H1)

Wie wir sehen begann der Kursrückgang um 17:00 Uhr, setzte sich bis 21:00 Uhr fort. Um 22:00 Uhr beginnt der Kurs von 1,3566 zu steigen und schließt mit 1.3574. Anders ausgedrückt: der Kurs durchläuft 11 Punkte, was zwar nicht für eine Trendumkehr ausreicht, aber auch keinen neuen Tiefstwert darstellt. Während der beiden folgenden Stunden verläuft der Kurs gleichbleibend (flat), bis wir nach 3 Stunden, um 01:00 Uhr (am 9. Oktober) eine stark aufsteigende Bewegung sehen, die bei 1,3591 schließt, also 25 Punkte (1,3591-1,3566) umfasst, was bedeutet, dass der Kurs sich nach oben umgekehrt hat.

In der nächsten Stunde hält dieser Aufwärtstrend an. Er schließt bei 1,3599, was durch die starke Yang-Linie bestätigt wird. Wir sehen, dass der Kurs um 03:00 Uhr beginnt, stark zu fallen, bis er bei 1,3578 schließt, was einen Abstand von 21 Punkten von dem vorhergehenden Höchstwert bedeutet (1,3599-1,3578), was für eine Trendumkehr mehr als ausreichend ist. Die Linie verläuft abwärts, behält jedoch ihre Form bei (als eine starke Yang-Linie).

Bis 16:00 Uhr ist weiterhin ein absteigender Kursverlauf zu beobachten, ehe die Linie hier die nächstgelegene Untergrenze schneidet und sich von einer starken Yang- in eine schmale Yin-Linie verwandelt. Als Wert für den Umkehrpunkt hat der oben bereits beschriebene Tiefst- oder Minimumwert von 1,3566 gedient. Der Kurs setzt seine Bewegung in Form einer Yin-Linie fort und ändert sich am 10. Oktober um 14:00 Uhr in eine Yang-Linie, indem er bei 1,3524 die Linie des nächsten, um 23:00 Uhr (am 9. Oktober) gebildeten Höchstwertes schneidet. Meiner Ansicht nach sollte dieses kleine Beispiel deutlich gemacht haben, wie ein „Kagi“-Diagramm angelegt wird.


2. Grundsatz zur Erstellung des „Kagi“-Indikators

Um den Indikator von dem aktuellen Zeitraum unabhängig zu machen, haben wir uns entschieden, die Daten des angegebenen Zeitraums, in dem die Erstellung erfolgen soll, getrennt zu kopieren und das Diagramm anschließend anhand der gewonnenen Daten anzulegen.

Das ermöglicht die gleichzeitige Analyse mehrerer Zeiträume in einem Diagramm, wodurch die Grenzen der technischen Analyse anhand von Kagi-Diagrammen erweitert werden. Der Indikator selbst erscheint in einem eigenen Fenster mit der Möglichkeit zur Ausgabe von Informationen in dem Hauptdiagramm. Das heißt, die grundlegende Erstellung seines Diagramms (in klassischer oder überarbeiteter Form) erfolgt in dem Fenster des Indikators, während in das Hauptdiagramm eine Kopie seiner Abbildung sowie (je nach den gewählten Einstellungen) Kurs- und Zeitmarkierungen eingefügt werden.

Wie bereits erwähnt legt der Indikator das Diagramm sowohl in der klassischen als auch in der überarbeiteten Form an. Die klassische Variante wurde bereits behandelt, jetzt kommen wir zu der überarbeiteten Form.

Ich kann nicht sagen, ob es sich dabei um eine neue Idee handelt oder nicht, aber ich habe davon noch nie gehört. Das Herzstück des hinzugefügten zusätzlichen Filters besteht darin, dass bei diesem Verfahren nicht nur die Umkehrpunkte herausgefiltert werden, sondern jede Bewegung des Diagramms. Mit anderen Worten, muss der Kurs, um einen neuen Tiefst- oder Höchstpunkt der Bewegung (nicht zu verwechseln mit der Umkehr von Schulter und Taille) zu bezeichnen, zunächst einen vorgegebenen Wert durchbrechen. Allgemein gesprochen muss der Kurs, wohin er auch geht, zunächst eine bestimmte Größe durchbrechen, und erst danach wird ermittelt, ob es sich um eine Fortsetzung oder eine Umkehr der Bewegung handelt.

Wie das aussieht, zeigt die Abbildung 2, in der die überarbeitete Ansicht des Diagramm in blau wiedergegeben wird, und die klassische in rot. Wie wir sehen, reagiert die überarbeitete Form nur langsam auf eine Änderung der Kursbewegung, wodurch ein Großteil der unbedeutenden Signale herausgefiltert wird.

Die klassische und die überarbeitete Form der Erstellung eines „Kagi“-Diagramms

Abb. 2. Die überarbeitete (blaue Linie) und die klassische (rote Linie) Form der Erstellung eines „Kagi“-Diagramms

In dem Indikator werden außer dem „Kagi“-Diagramm sowohl in Fenster des Indikators als auch im Hauptfenster einige Hilfsabbildungen angelegt.

Im Indikatorfenster können (je nach Einstellungen) Markierungen gesetzt werden, die Angaben zu den Kursen enthalten, bei denen eine Richtungsumkehr erfolgt. Dieselbe Funktion wird anhand der Kursgrenzen umgesetzt, die (je nach Einstellungen) in dem Fenster gleichmäßig über den gesamten zum Anlegen des Indikators verwendeten Kursbereich oder auf alle Wendepunkte des Diagramms verteilt sein können. Dabei können die Farben in drei Varianten eingestellt werden: nach der Art des Richtungswechsels (auf- bzw. absteigend), der Art der Linie (Yin bzw. Yang) oder ohne Farbwechsel.

In dem Hauptdiagramm werden die Kursmarkierungen sowie die zeitlichen Markierungen der Umkehrpunkte umgesetzt, die (je nach Einstellungen) lediglich eine Farbe aufweisen oder ihre Farbe in die Farbe der Yin- oder der Yang-Linie ändern können.

Der gesamte Indikatorcode wird von Funktionen umgesetzt, deren Interaktion über globale Variablen verläuft.

Den Code können wir bedingt in drei Hauptfunktionen und elf Zusatzfunktionen untergliedern. Die Hauptlast der Berechnungen sowie der Füllung der Zwischenspeicher (Puffer) nicht nur der wesentlichen grafischen Konstruktionen sondern auch der ergänzenden Pufferdatenfelder ruht auf der Funktion zur Erstellung des Kagi-Diagramms im Fenster des Indikators. Die beiden anderen Hauptfunktionen reagieren auf den Eingang von Daten, die eine kopiert alle Zeitdaten, die andere alle Daten zu den Kursen eines jeden Balken des ausgewählten Zeitraums.

Die restlichen Zusatzfunktionen reagieren auf Ausführung aller Abbildungen, die Löschung von Objekten, die Entfernung des Indikators unter Löschung seiner Objekte, die Berechnung der Umkehrparameter, das Anlegen von Markierungen im Hauptdiagramm und im Indikatorfenster, das Anlegen grafischer Objekte der Art „Trendlinie“, die Erstellung des Kagi-Diagramms im Hauptdiagramm sowie die Funktion zur Ermittlung der Bildung eines neuen Balkens zur Auslösung des Erstellungsvorgangs.


3. Der Code und der Algorithmus des Indikators

Jetzt schauen wir uns den Indikatorcode und den Algorithmus, um ihn anzulegen, etwas genauer an. Der Code ist recht lang und für unerfahrene Neuprogrammierer möglicherweise schwer zu verstehen, da er angesichts der Interaktion zwischen den Funktionen mithilfe globaler Variablen ziemlich verwirrend aussieht. Dieser Teil des Artikels ist so aufgebaut, dass jede Funktion bzw. jeder Codeabschnitt gesondert erläutert wird. Zunächst werden die Indikatoreinstellungen vorgestellt, dann folgen die Erläuterung der ersten Funktionen zum Kopieren von Daten sowie die Berechnung des Umkehrparameters, danach wird die Hauptfunktion zur Erstellung und Berechnung des Kagi-Diagramms beleuchtet und zu guter Letzt die übrigen Zusatzfunktionen.

3.1. Die Eingangsparameter des Indikators

Den Anfang des Codes bildet die Deklarierung des Indikators in einem gesonderten Fenster sowie von 12 Zwischenspeichern (Puffern) und 8 grafischen Abbildungen des Indikators. Allem voran sehen wir uns an, warum 8 grafische Abbildungen verwendet werden, von denen zwei Histogramme und sechs Linien sind. Jedes Histogramm bildet eine eigene waagerechte Linie, von denen eine auf die Yin-Linie reagiert und andere auf die Yang-Linie.

Mit den „Linien“ sieht die Sache dagegen etwas vertrackter aus, da sie stets zu dritt auftreten. Das liegt daran, dass eine Linie gezeichnet wird, wenn neben einem Punkt ein anderer bereits eingezeichneter Punkt vorhanden ist. Das heißt, um zwei Linien nebeneinander (Schulter, Taille) zu zeichnen, benötigen wir zwei grafische Elemente der Art Linie, die wir der Reihe nach durchwechseln müssen. Damit sie jedoch die erforderlichen Punkte ausblenden, benötigen wir ein drittes Element, das ebenfalls durchgewechselt wird.

Zur Veranschaulichung versuche ich, das anhand der Abbildung 3 zu erläutern, in der gezeigt wird, was passiert, wenn nur zwei grafische Elemente der Art „Linie“ verwendet werden.


 Abb. 3. Beispiel für die Verwendung von zwei oder drei grafischen Elementen der Art „Linie“ zur Wiedergabe der Schulter- und der Taillenlinie.

Weiter geht es mit der Anlage des Einstellungsmenüs: Hier haben wir fünf Aufzählungen (wir betrachten sie bei den Eingangsparametern).

Der erste Eingangsparameter, „period“, gibt den Zeitraum an, für den die Abbildung erstellt wird, ihm folgt „period_to_redraw“, der Zeitraum bis zur Aktualisierung der Abbildung, und dann der letzte zeitliche Parameter, „start_data“, mit der gleichnamigen Zweckbestimmung, er gibt den Zeitpunkt an, ab dem die Abbildung umgesetzt wird.

Danach folgen die Parameter zur Erstellung des Diagramms sowie der ergänzenden Deklarierungen:

Es folgen die Deklaration der Zwischenspeicher des Indikators und der Hilfsspeicher für die Kurs- und Zeitwerte (Price, Time) sowie die Hilfsvariablen (stop_data, bars_copied, bars_copied_time, copy_history, copy_time), die Datenfelder (Arrays) zur Speicherung der Angaben dazu, auf welcher Yin- oder Yang-Linie die Änderung der Diagrammbewegung stattgefunden hat, der Zeitpunkt der Änderung, der Kurs bei der Änderung und der Mittelkurs (wenn in dem Balken ein Wechsel von Yin zu Yang oder umgekehrt stattfindet). Zum Schluss wird eine der verbreitetsten globalen Variablen deklariert, die Angaben zu der Anzahl „a“ der Diagrammbewegungsänderungen enthält.

//+------------------------------------------------------------------+
//|                                                         BKCV.mq5 |
//|                                   Azotskiy Aktiniy ICQ:695710750 |
//|                          https://www.mql5.com/ru/users/Aktiniy |
//+------------------------------------------------------------------+
//--- Build Kagi Chart Variable
#property copyright "Azotskiy Aktiniy ICQ:695710750"
#property link      "https://www.mql5.com/en/users/Aktiniy"
#property version   "1.00"
#property description "Build Kagi Chart Variable"
#property description " "
#property description "This indicator makes drawing a chart Kagi as a matter of indicator window, and in the main chart window"
#property indicator_separate_window
#property indicator_buffers 12
#property indicator_plots   8
//--- plot Yin
#property indicator_label1  "Yin"
#property indicator_type1   DRAW_HISTOGRAM2
#property indicator_color1  clrRed
#property indicator_style1  STYLE_SOLID
#property indicator_width1  1
//--- plot Yin1
#property indicator_label2  "Yin1"
#property indicator_type2   DRAW_LINE
#property indicator_color2  clrRed
#property indicator_style2  STYLE_SOLID
#property indicator_width2  1
//--- plot Yin2
#property indicator_label3  "Yin2"
#property indicator_type3   DRAW_LINE
#property indicator_color3  clrRed
#property indicator_style3  STYLE_SOLID
#property indicator_width3  1
//--- plot Yin3
#property indicator_label4  "Yin3"
#property indicator_type4   DRAW_LINE
#property indicator_color4  clrRed
#property indicator_style4  STYLE_SOLID
#property indicator_width4  1
//--- plot Yang
#property indicator_label5  "Yang"
#property indicator_type5   DRAW_HISTOGRAM2
#property indicator_color5  clrRed
#property indicator_style5  STYLE_SOLID
#property indicator_width5  2
//--- plot Yang1
#property indicator_label6  "Yang1"
#property indicator_type6   DRAW_LINE
#property indicator_color6  clrRed
#property indicator_style6  STYLE_SOLID
#property indicator_width6  2
//--- plot Yang2
#property indicator_label7  "Yang2"
#property indicator_type7   DRAW_LINE
#property indicator_color7  clrRed
#property indicator_style7  STYLE_SOLID
#property indicator_width7  2
//--- plot Yang3
#property indicator_label8  "Yang3"
#property indicator_type8   DRAW_LINE
#property indicator_color8  clrRed
#property indicator_style8  STYLE_SOLID
#property indicator_width8  2
//--- Enumerations as input data (for more attractive setting)
//--- Kagi charting type
enum kagi_type_enum
  {
   classic=0,  // Classic
   modified=1, // Modified
  };
//--- Type of the price used for construction
enum price_type_enum
  {
   c=0, // Close
   o=1, // Open
   h=2, // High
   l=3, // Low
  };
//--- Type of the used reversal
enum type_doorstep_enum
  {
   point=0,   // Point
   procent=1, // Percent
  };
//--- Type of levels location
enum levels_type_enum
  {
   cor=0, // Cornering
   equ=1, // Equal distance
  };
//--- Level colors change type (works when "Type of levels location"="Cornering")
enum levels_change_color_enum
  {
   up_down=0,  // Up & Down
   yin_yang=1, // Yin & Yang
   no=2,       // Don't change
  };
//--- input parameters
input ENUM_TIMEFRAMES period=PERIOD_CURRENT;                // Calculation period to build the chart
input ENUM_TIMEFRAMES period_to_redraw=PERIOD_M1;           // Refresh period chart
input datetime start_data=D'2013.07.10 00:00:00';           // Start time to build the chart
input kagi_type_enum kagi_type=classic;                     // The type to build Kagi chart
input price_type_enum price_type=c;                         // Price used to build chart
input type_doorstep_enum type_doorstep=point;               // Type calculate doorstep
input double   doorstep=25;                                 // Doorstep reversal
input color    color_yin=clrRed;                            // Color Yin line (indicator window)
input color    color_yang=clrRed;                           // Color Yang line (indicator window)
input char     width_yin=1;                                 // Width Yin line (indicator window)
input char     width_yang=2;                                // Width Yang line (indicator window)
input bool     levels_on_off=false;                         // Draw level (indicator window)
input levels_type_enum levels_type=cor;                     // Type of drawing levels (indicator window)
input uint     levels_number=6;                             // Number of levels  (indicator window)
input levels_change_color_enum levels_change_color=up_down; // Type change color of levels (indicator window)
input color    levels_first_color=clrBeige;                 // The first color of level (indicator window)
input color    levels_second_color=clrCoral;                // The second color of level (indicator window)
input bool     label_1=true;                                // Draw price label on (indicator window)
input uint     label_1_number=10;                           // The number of labels (indicator window)
input color    label_1_color=clrGreenYellow;                // The color of labels (indicator window)
input bool     label_2=true;                                // Draw price label on (main chart)
input color    label_2_color=clrGreenYellow;                // The color of labels (main chart)
input bool     time_line_draw=true;                         // Draw a timeline reversal (main chart)
input bool     time_separate_windows=false;                 // Draw a timeline reversal on indicator window
input bool     time_line_change_color=true;                 // Different color timeline on the Yin and Yang lines (main chart)
input color    time_first_color=clrRed;                     // The first color of timeline (main chart)
input color    time_second_color=clrGreenYellow;            // The second color of timeline (main chart)
input bool     kagi_main_chart=true;                        // Draw Kagi on main chart (main chart)
input color    color_yin_main=clrRed;                       // Color Yin line (main chart)
input color    color_yang_main=clrRed;                      // Color Yang line (main chart)
input char     width_yin_main=1;                            // Width Yin line (main chart)
input char     width_yang_main=2;                           // Width Yang line (main chart)
input long     magic_numb=65758473787389;                   // The magic number for drawing objects
//--- indicator buffers
double         YinBuffer1[];
double         YinBuffer2[];
double         Yin1Buffer[];
double         Yin2Buffer[];
double         Yin3Buffer[];
double         YangBuffer1[];
double         YangBuffer2[];
double         Yang1Buffer[];
double         Yang2Buffer[];
double         Yang3Buffer[];
//--- additional variables
double Price[]; // Buffer for storing the copied price data
double Time[];  // Buffer for storing the copied time data
//---
datetime stop_data;      // Current time
int bars_copied=0;       // Number of the already copied bars from the initial date
int bars_copied_time;    // Number of the already copied bars having the initial date
bool copy_history=false; // Price history copying result
bool copy_time=false;    // Time history copying result
//---
datetime time_change[];      // Array for writing the time when the chart movement started changing (up or down)
char time_line[];            // Array for storing the data on what line (Yin=0 or Yang=1) direction has changed
double time_change_price[];  // Array for writing the chart movement change price
double time_central_price[]; // Array for writing the average price during the chart movement change

uint a=0; // Variable for building the chart, number of chart reversals is fixed


  

3.2. Funktion zur Bereitstellung des Indikators

Als Nächstes folgt die Funktion zur Bereitstellung des Indikators. In dieser werden die Indikatorpuffer und ihre Kennziffern ausgewiesen (hauptsächlich in Form von Zeitreihen, da das Kagi-Diagramm kürzer ist als das Hauptdiagramm, ist es besser, es von hinten nach vorne zu zeichnen). Die nicht auf dem Bildschirm anzuzeigenden Werte werden ebenfalls festgelegt (EMPTY_VALUE=-1).

Wir weisen die Indikatorbezeichnung und die Wiedergabegenauigkeit zu. Wie bereits gesagt wird die Bezeichnung um die magische Zahl erweitert. Das geschieht, damit die Funktion ChartWindowFind() korrekt ausgeführt wird. Andernfalls werden in dem Indikatorfenster angelegten grafischen Objekte nur bei dem zuerst aufgerufenen Indikator (wenn in einem Diagramm mehrere Indikatoren verwendet werden) abgebildet.

Des Weiteren weisen wir den abgebildeten Linien Bezeichnungen zu, unterdrücken die Wiedergabe der aktuellen Zahlenwerte im Indikatorfenster, stellen die Farbe und Stärke der Yin- und Yang-Linien ein und legen die Anzahl der dort wiederzugebenden Kursgrenzen bzw. Kursebenen fest.

//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- indicator buffers mapping
   SetIndexBuffer(0,YinBuffer1,INDICATOR_DATA);
   ArraySetAsSeries(YinBuffer1,true);
   SetIndexBuffer(1,YinBuffer2,INDICATOR_DATA);
   ArraySetAsSeries(YinBuffer2,true);
   SetIndexBuffer(2,Yin1Buffer,INDICATOR_DATA);
   ArraySetAsSeries(Yin1Buffer,true);
   SetIndexBuffer(3,Yin2Buffer,INDICATOR_DATA);
   ArraySetAsSeries(Yin2Buffer,true);
   SetIndexBuffer(4,Yin3Buffer,INDICATOR_DATA);
   ArraySetAsSeries(Yin3Buffer,true);
//---
   SetIndexBuffer(5,YangBuffer1,INDICATOR_DATA);
   ArraySetAsSeries(YangBuffer1,true);
   SetIndexBuffer(6,YangBuffer2,INDICATOR_DATA);
   ArraySetAsSeries(YangBuffer2,true);
   SetIndexBuffer(7,Yang1Buffer,INDICATOR_DATA);
   ArraySetAsSeries(Yang1Buffer,true);
   SetIndexBuffer(8,Yang2Buffer,INDICATOR_DATA);
   ArraySetAsSeries(Yang2Buffer,true);
   SetIndexBuffer(9,Yang3Buffer,INDICATOR_DATA);
   ArraySetAsSeries(Yang3Buffer,true);
//--- add the buffer for copying data on prices for calculation
   SetIndexBuffer(10,Price,INDICATOR_CALCULATIONS);
//--- add the buffer for copying data on bar open time for construction
   SetIndexBuffer(11,Time,INDICATOR_CALCULATIONS);

//--- set what values are not to be drawn
   for(char x=0; x<8; x++)
     {
      PlotIndexSetDouble(x,PLOT_EMPTY_VALUE,-1);
     }
//--- set the indicator's look
   IndicatorSetString(INDICATOR_SHORTNAME,"BKCV "+IntegerToString(magic_numb)); // Indicator name
   IndicatorSetInteger(INDICATOR_DIGITS,_Digits); // Display accuracy
//--- assign names to graphical constructions
   PlotIndexSetString(0,PLOT_LABEL,"Yin");
   PlotIndexSetString(1,PLOT_LABEL,"Yin");
   PlotIndexSetString(2,PLOT_LABEL,"Yin");
   PlotIndexSetString(3,PLOT_LABEL,"Yin");
   PlotIndexSetString(4,PLOT_LABEL,"Yang");
   PlotIndexSetString(5,PLOT_LABEL,"Yang");
   PlotIndexSetString(6,PLOT_LABEL,"Yang");
   PlotIndexSetString(7,PLOT_LABEL,"Yang");
//--- prohibit display of the results of the current values for graphical constructions
   PlotIndexSetInteger(0,PLOT_SHOW_DATA,false);
   PlotIndexSetInteger(1,PLOT_SHOW_DATA,false);
   PlotIndexSetInteger(2,PLOT_SHOW_DATA,false);
   PlotIndexSetInteger(3,PLOT_SHOW_DATA,false);
   PlotIndexSetInteger(4,PLOT_SHOW_DATA,false);
   PlotIndexSetInteger(5,PLOT_SHOW_DATA,false);
   PlotIndexSetInteger(6,PLOT_SHOW_DATA,false);
   PlotIndexSetInteger(7,PLOT_SHOW_DATA,false);
//--- set color for Yin line
   PlotIndexSetInteger(0,PLOT_LINE_COLOR,color_yin);
   PlotIndexSetInteger(1,PLOT_LINE_COLOR,color_yin);
   PlotIndexSetInteger(2,PLOT_LINE_COLOR,color_yin);
   PlotIndexSetInteger(3,PLOT_LINE_COLOR,color_yin);
//--- set color for Yang line
   PlotIndexSetInteger(4,PLOT_LINE_COLOR,color_yang);
   PlotIndexSetInteger(5,PLOT_LINE_COLOR,color_yang);
   PlotIndexSetInteger(6,PLOT_LINE_COLOR,color_yang);
   PlotIndexSetInteger(7,PLOT_LINE_COLOR,color_yang);
//--- set Yin line width
   PlotIndexSetInteger(0,PLOT_LINE_WIDTH,width_yin);
   PlotIndexSetInteger(1,PLOT_LINE_WIDTH,width_yin);
   PlotIndexSetInteger(2,PLOT_LINE_WIDTH,width_yin);
   PlotIndexSetInteger(3,PLOT_LINE_WIDTH,width_yin);
//--- set Yang line width
   PlotIndexSetInteger(4,PLOT_LINE_WIDTH,width_yang);
   PlotIndexSetInteger(5,PLOT_LINE_WIDTH,width_yang);
   PlotIndexSetInteger(6,PLOT_LINE_WIDTH,width_yang);
   PlotIndexSetInteger(7,PLOT_LINE_WIDTH,width_yang);
//--- set the number of levels in the indicator window
   IndicatorSetInteger(INDICATOR_LEVELS,levels_number);
//---
   return(INIT_SUCCEEDED);
  }


3.3. Die Funktionen zum Kopieren der Daten

Wir kommen jetzt zu den Funktionen zum Kopieren der Daten.

Wir haben hier zwei davon: Eine zum Kopieren der Kurse und eine zum Kopieren der Eröffnungszeiten aller Balken. Beide Funktionen speichern ihre Werte in den (zuvor deklarierten) Rechenpuffern des Indikators.

Wir beginnen mit der Funktion zum Kopieren der Kurse. Die Eingangsparameter der Funktion sind: eine Datenfeld (Array) zur Speicherung der Daten, der Zeitpunkt für den Beginn des Kopiervorgangs und den Zeitpunkt seiner Beendigung (die aktuelle Zeit). Der Hauptteil der Funktion enthält die Variablen für die Reaktion auf die Funktion, die Anzahl der in das Zwischenspeicherdatenfeld kopierten Daten (Balken), das dynamische Zwischenspeicherdatenfeld selbst sowie die Anzahl der in das Zwischenspeicherdatenfeld zu kopierenden Balken. Die Anzahl der Balken wird ausgehend von der Gesamtzahl der Balken in dem zu untersuchenden Zeitraum und der Anzahl der beim letzten Aufruf der Funktion kopierten Balken (globale Variable) berechnet.

Wenn die Daten nicht zum ersten Mal kopiert werden, müssen die Daten bezüglich des zuletzt kopierten Balkens aktualisiert werden. Dazu wird die Anzahl der kopierten Balken um eins herab- und die der neu zu kopierenden um eins heraufgesetzt. Zudem wird die Größe des Zwischenspeicherdatenfeldes geändert, um es für das Kopieren der Balken vorzubereiten.

Entsprechend den Einstellungen werden die Kurse in das Zwischenspeicherdatenfeld kopiert. Wenn der Kopiervorgang erfolgreich war, kopieren wir anschließend die Daten aus dem Zwischenspeicherdatenfeld ans Ende des Pufferdatenfeldes (des Datenfeldes für die Reaktion auf die Funktion), weisen der Funktion eine positive Antwort zu und aktualisieren die globale Variable zur Speicherung der Angaben zur Anzahl der kopierten Balken. Diese Art des Kopierens ermöglicht das Kopieren lediglich der letzten Balken, was die für den Kopiervorgang benötigte Zeit verkürzt.

//+------------------------------------------------------------------+
//| Func Copy History                                                |
//+------------------------------------------------------------------+
bool func_copy_history(double &result_array[],
                       datetime data_start,
                       datetime data_stop)
  {
//---
   int x=false; // Variable for answer

   int result_copy=-1; // Number of copied data

   static double price_interim[]; // Temporary dynamic array for storing copied data
   static int bars_to_copy;       // Number of bars for copying

   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(price_interim,bars_to_copy); // Change the size of the receiving array

   switch(price_type)
     {
      case 0:
         result_copy=CopyClose(_Symbol,period,0,bars_to_copy,price_interim);
         break;
      case 1:
         result_copy=CopyOpen(_Symbol,period,0,bars_to_copy,price_interim);
         break;
      case 2:
         result_copy=CopyHigh(_Symbol,period,0,bars_to_copy,price_interim);
         break;
      case 3:
         result_copy=CopyLow(_Symbol,period,0,bars_to_copy,price_interim);
         break;
     }

   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(x);
  }

Die nächste Funktion ist die zum Kopieren der Daten bezüglich der Zeit. Sie unterscheidet sich von der vorhergehenden dadurch, dass sie mit einer Variablen anderer Art operiert: datetime (sie führt beim Kopieren in das Pufferdatenfeld „Time“, das Datenfeld für die Reaktion auf die Funktion, ihre Umwandlung in „double“ aus). Ein weiterer Unterschied liegt darin, dass keine Notwendigkeit zur Verwendung des Operators switch() besteht, da keine Auswahl der kopierten Daten vorgenommen werden muss.

//+------------------------------------------------------------------+
//| Func Copy Time                                                   |
//+------------------------------------------------------------------+
bool func_copy_time(double &result_array[],
                    datetime data_start,
                    datetime data_stop)
  {
//---
   int x=false; // Variable for answer
   int result_copy=-1; // Number of copied data

   static datetime time_interim[]; // Temporary dynamic array for storing copied data
   static int bars_to_copy_time; // Number of bars for copying

   bars_to_copy_time=Bars(_Symbol,period,data_start,data_stop); // Find out the current number of bars on the time interval
   bars_to_copy_time-=bars_copied_time; // Calculate the number of bars to be copied

   if(bars_copied_time!=0) // If it is not the first time the data has been copied
     {
      bars_copied_time--;
      bars_to_copy_time++;
     }
   ArrayResize(time_interim,bars_to_copy_time); // Change the size of the receiving array
   result_copy=CopyTime(_Symbol,period,0,bars_to_copy_time,time_interim);

   if(result_copy!=-1) // If copying to the intermediate array is successful
     {
      ArrayCopy(result_array,time_interim,bars_copied_time,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_time+=result_copy; // Increase the value of the processed data
     }
//---
   return(x);
  }

3.4. Die Funktion zur Berechnung des Umkehrparameters

Da der Umkehrparameter ein Punkt- oder Prozentwert sein kann, benötigen wir eine Funktion zur Berechnung des Umkehrparameters in Abhängigkeit von den Indikatoreinstellungen. Diese Funktion verfügt über lediglich einen Eingangsparameter, den Kurs zur Berechnung des prozentualen Umkehrpunktes. Die Variable für die Reaktion wird zunächst in der Art „double“ bereitgestellt und nach den Berechnungen für die Reaktion stillschweigend in die Art „int“ umgewandelt.

Das geschieht, weil in den Berechnungen mit Gleitkommazahlen operiert wird, die Reaktion jedoch in ganzen Zahlen wiedergegeben werden muss. Der Auswahlvorgang wird in der Funktion mithilfe des bedingten Ausdrucks if-else umgesetzt. Der Vergleich wird unmittelbar mit der externen Variablen „input“ (der Indikatorparameter) ausgeführt. Die Berechnung der Punkte erfolgt anhand einer simplen Formel: Am Anfang steht die Gesamtzahl der Punkte, die der Kurs durchläuft, aus dieser Menge wird dann der vorgegeben Prozentsatz errechnet und der auszugebenden Variablen zugewiesen.

//+------------------------------------------------------------------+
//| Func Calculate Doorstep                                          |
//+------------------------------------------------------------------+
int func_calc_dorstep(double price)
  {
   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.5. Die Hauptfunktion - das Anlegen des Kagi-Diagramms

Damit haben wir alle Funktionen beisammen, die für die Arbeit der Hauptfunktion, der Erstellung des Kagi-Diagramms im Indikatorfenster (das heißt das Füllen der Indikatorpuffer) benötigen. Die Eingangsparameter der Funktion sind Datenfelder, von denen zwei die uns bereits bekannten, zuvor kopierten Rechenpuffer „Price“ und „Zeit“ sind, bei allen übrigen handelt es sich um Datenfelder der Puffer für die grafische Darstellung des Indikators.

In der Funktion werden die zur Speicherung der Angaben bezüglich der Erstellung des Diagramms erforderlichen Variablen deklariert. Da das Diagramm mithilfe des zyklischen Ausdrucks „for“ angelegt wird, benötigen wir Angaben dazu, wo der vorherige Durchlauf beendet wurde. Das wird mithilfe von sechs Variablen umgesetzt: line_move - wohin hat sich der Kurs im letzten Durchgang bewegt; line_gauge - das Linienformat (die Stärke der Linie) - Yin oder Yang, price_1 und price_2 - vorheriger und aktuell zu betrachtender Kurs, price_down und price_up - vorheriger Kurs von Schulter und Taille. Wie wir sehen, wird price_1 sofort mit dem ersten Element des Datenfeldes mit den kopierten Kursen gleichgesetzt, weil diese Variable vom äußersten Anfang des Zyklus an noch vor dem Vergleich in die Berechnungen einbezogen wird.

Da die Pufferdatenfelder der grafischen Darstellung des Indikators das Kennziffernkennzeichen AS_SERIES tragen, müssen sie in umgekehrter Reihenfolge gefüllt werden. Dazu werden Zeitdatenfelder in der entsprechenden Größe eingeführt, des Weiteren werden die globalen Variablen zur Speicherung der Zeitangaben, der Art der Linie, der „Schulter-“ und „Taillen“-Kurse sowie der Umkehrkurse auf dieselbe Weise umgewandelt.

Anschließend müssen alle Datenfelder mit „leeren“ Werten (-1) gefüllt werden. Das geschieht mithilfe zweier kleiner Zyklen, die auch zu einem einzigen zusammengefasst werden könnten. Aber so wird alles wesentlich verständlicher, da sich insbesondere der Zeitpunkt der Ausführung nicht ändert. Das heißt, die Grafikspeicher und die Datenfelder für die Zeitberechnungen werden voneinander getrennt gefüllt.

Jetzt sind alle Variablen deklariert, umgewandelt und gefüllt, also kann mit dem Hauptarbeitsgang begonnen werden. Er ist ziemlich umfangreich (obwohl er recht zügig durchgerechnet wird) und schließt die Erfassung aller zuvor kopierten Balken ein.

Der Arbeitsgang durchläuft alle kopierten Balken und füllt die benötigten vorher deklarierten Datenfelder zur weiteren Verwendung. Also legen wir zunächst alle in dem Arbeitsgang (Zyklus) verwendeten Datenfelder fest:

Vor jedem Durchlauf des Arbeitsgangs wird der Variablen price_2 für den anschließenden Vergleich in den bedingten if-else-Ausdrücken der Wert des zu untersuchenden aktuellen Kurses aus dem Puffer „Price“ zugeordnet. Danach erfolgen schrittweise die Analyse des Pufferdatenfeldes mit den kopierten Daten sowie die Auffüllung der oben vorgestellten Datenfelder. Jede bedingte if-else-Aussage erledigt je nach der jeweiligen Bedingung bestimmte Aufgaben: die vorherige Bewegungsrichtung der Diagrammlinie (auf- oder absteigend) sowie die vorherige Art der Linie (Yin oder Yang). Danach erfolgt je nach Art der Abbildung (klassisch oder überarbeitet) die Überprüfung der Bedingungen für eine Bewegung (m.a.W., ob der Kurs die vorgegebene Anzahl von Punkten durchlaufen hat)

Und wenn das alles geschehen ist, erfolgt die Umformung der alten oder die Festlegung neuer Variablen (Datenfeldelemente). Ganz zu Anfang wird die ursprüngliche Bewegung festgelegt, bzw. welche Linie die erste sein wird, Yin oder Yang. Danach erfolgt bereits in Abhängigkeit von der Bewegung und der vorhergehenden Maßnahmen die Verteilung.

Es gibt zwei mögliche Kursbewegungen:

  1. der Kurs steigt oder
  2. der Kurs fällt.

Zudem gibt es vier Arten des vorherigen Verhaltens in jede Richtung:

  1. bei der vorherigen Linie hat es sich um eine Yin-Linie mit aufsteigendem Verlauf gehandelt;
  2. die vorherige Linie war eine Yang-Linie mit aufsteigendem Verlauf;
  3. die vorherige Linie war eine Yin-Linie mit absteigendem Verlauf; oder
  4. die vorherige Linie war eine Yang-Linie mit absteigendem Verlauf.

Insgesamt erhalten wir also unter Außerachtlassung der ersten beiden Festlegungen der ursprünglichen Diagrammbewegung (der Form der ersten Linie) acht Varianten.

Danach ist der Hauptarbeitsgang abgeschlossen. Es erfolgt die Neuverteilung (Umkehrung) und Füllung der Zwischenspeicher (Puffer) zum Anlegen eines Diagramms mit einem etwas kleineren Zyklus im Umfang der vorher in dem Hauptarbeitsgang bestimmten und in die Variable „a“ eingeschriebenen Umkehrpunkte des Kagi-Diagramms. Was die Verteilungen der oberen und unteren Kurswerte sowie der senkrechten Linien betrifft, verhält sich alles ganz einfach: es erfolgt eine simple Umkehrung, das heißt, die Übernahme der zuvor ermittelten primären Werte (der Datenfelder mit den Kennziffern 0, 1, 2, 3...) durch die abschließenden Werte der Zwischenspeicher (als abschließende Werte fungieren Elemente mit der Kennziffer „a“, das heißt a, a-1, a-2, a-3...). Um zu verhindern, dass die (waagerechten) Linien der Umkehrpunkte wie oben beschrieben aneinander haften bleiben, werden sie mithilfe des Umschaltoperators switch nacheinander angelegt.

Damit beendet die Hauptfunktion zum Anlegen des Kagi-Diagramms ihre Arbeit.

//+------------------------------------------------------------------+
//| Func Draw Kagi                                                   |
//+------------------------------------------------------------------+
void func_draw_kagi(double &array_input[],
                    double &arr_yin_1[],
                    double &arr_yin_2[],
                    double &arr_yin_lin1[],
                    double &arr_yin_lin2[],
                    double &arr_yin_lin3[],
                    double &arr_yang_1[],
                    double &arr_yang_2[],
                    double &arr_yang_lin1[],
                    double &arr_yang_lin2[],
                    double &arr_yang_lin3[],
                    double &arr_time[])
  {
//---
   a=0; // Variable for the chart construction fixing the number of chart reversals
   char line_move=0; // Previous price direction 1-up, -1-down
   char line_gauge=0; // Previous look of the line 1-thick yang, -1-thin yin
   double price_1=0,price_2=0; // Auxiliary variables for defining the price movement
   double price_down=-99999,price_up=99999; // Auxiliary variables for storing the reversal price values
   price_1=array_input[0];
//--- auxiliary arrays for the initial data storing before the reversal (transferring to the buffers)
   double yin_int_1[];
   double yin_int_2[];
   double lin_yin[];
   double yang_int_1[];
   double yang_int_2[];
   double lin_yang[];
//--- change the sizes of dynamic arrays
   ArrayResize(yin_int_1,bars_copied);
   ArrayResize(yin_int_2,bars_copied);
   ArrayResize(yang_int_1,bars_copied);
   ArrayResize(yang_int_2,bars_copied);
   ArrayResize(lin_yin,bars_copied);
   ArrayResize(lin_yang,bars_copied);
//--- time data storing arrays
   ArrayResize(time_change,bars_copied_time);
   ArrayResize(time_line,bars_copied_time); // Look of the line Yin = 0 or Yang = 1
   ArrayResize(time_change_price,bars_copied_time);
   ArrayResize(time_central_price,bars_copied_time);
//--- assign -1 (not displayed) value to the transferred buffers
   for(int z=0; z<bars_copied; z++)
     {
      arr_yin_1[z]=-1;
      arr_yin_2[z]=-1;
      arr_yin_lin1[z]=-1;
      arr_yin_lin2[z]=-1;
      arr_yin_lin3[z]=-1;
      arr_yang_1[z]=-1;
      arr_yang_2[z]=-1;
      arr_yang_lin1[z]=-1;
      arr_yang_lin2[z]=-1;
      arr_yang_lin3[z]=-1;
     }
//--- equate -1 (not displayed) value to the arrays
   for(int z=0; z<bars_copied; z++)
     {
      yin_int_1[z]=-1;
      yin_int_2[z]=-1;
      lin_yin[z]=-1;
      yang_int_1[z]=-1;
      yang_int_2[z]=-1;
      lin_yang[z]=-1;
      time_change[z]=-1;
      time_line[z]=-1;
      time_change_price[z]=-1;
      time_central_price[z]=-1;
     }
//--- function's main loop
   for(int z=0; z<bars_copied; z++)
     {
      price_2=array_input[z];
      //--- first, let's define the initial market direction
      //--- first THIN DESCENDING line
      if(((price_1-price_2)/_Point>func_calc_dorstep(price_2)) && line_move==0)
        {
         yin_int_1[a]=price_1;
         yin_int_2[a]=price_2;

         line_move=-1;
         line_gauge=-1;

         price_1=price_2;

         time_change[a]=(datetime)arr_time[z];
         time_line[a]=0;
        }
      //--- first THICK ASCENDING line
      if(((price_1-price_2)/_Point<-func_calc_dorstep(price_2)) && line_move==0)
        {
         yang_int_1[a]=price_1;
         yang_int_2[a]=price_2;

         line_move=1;
         line_gauge=1;

         price_1=price_2;

         time_change[a]=(datetime)arr_time[z];
         time_line[a]=1;
        }
      //--- price moves DOWN
      //--- if the price moved DOWN before that, the line is THIN
      if(line_move==-1 && line_gauge==-1)
        {
         if(((price_1-price_2)/_Point>func_calc_dorstep(price_2)) || (kagi_type==0 && (price_1-price_2)/_Point>0))
           {
            yin_int_2[a]=price_2;

            line_move=-1;
            line_gauge=-1;

            price_1=price_2;

            time_change[a]=(datetime)arr_time[z];
            time_line[a]=0;
           }
        }
      //--- if the price moved DOWN before that, the line is THICK
      if(line_move==-1 && line_gauge==1)
        {
         if(((price_1-price_2)/_Point>func_calc_dorstep(price_2)) || (kagi_type==0 && (price_1-price_2)/_Point>0))
           {
            if(price_2<price_down) // If the thick line crossed the lower shoulder when moving downwards
              {
               yin_int_1[a]=price_down;
               yin_int_2[a]=price_2;

               yang_int_2[a]=price_down;

               line_move=-1;
               line_gauge=-1;

               price_1=price_2;

               time_change[a]=(datetime)arr_time[z];
               time_central_price[a]=price_down;
               time_line[a]=0;
              }
            else //if(price_2>=price_down) // If the thick line has not crossed the lower shoulder when moving downwards
              {
               yang_int_2[a]=price_2;

               line_move=-1;
               line_gauge=1;

               price_1=price_2;

               time_change[a]=(datetime)arr_time[z];
               time_line[a]=1;
              }
           }
        }
      //--- if the price has moved UPWARDS before that, the line is THIN
      if(line_move==1 && line_gauge==-1)
        {
         if((price_1-price_2)/_Point>func_calc_dorstep(price_2))
           {
            a++;
            yin_int_1[a]=price_1;
            yin_int_2[a]=price_2;

            lin_yin[a]=price_1;

            line_move=-1;
            line_gauge=-1;

            price_up=price_1;

            price_1=price_2;

            time_change[a]=(datetime)arr_time[z];
            time_line[a]=0;
            time_change_price[a]=lin_yin[a];
           }
        }
      //--- if the price has moved UPWARDS before that, the line is THICK
      if(line_move==1 && line_gauge==1)
        {
         if((price_1-price_2)/_Point>func_calc_dorstep(price_2))
           {
            a++;
            if(price_2<price_down) // If the thick line has crossed the lower shoulder when moving downwards
              {
               yin_int_1[a]=price_down;
               yin_int_2[a]=price_2;

               yang_int_1[a]=price_1;
               yang_int_2[a]=price_down;

               lin_yang[a]=price_1;

               line_move=-1;
               line_gauge=-1;

               price_up=price_1;

               price_1=price_2;

               time_change[a]=(datetime)arr_time[z];
               time_line[a]=0;
               time_change_price[a]=lin_yang[a];
               time_central_price[a]=price_down;
              }
            else//if(price_2>=price_down) // If the thick line has not crossed the lower shoulder when moving downwards
              {
               yang_int_1[a]=price_1;
               yang_int_2[a]=price_2;

               lin_yang[a]=price_1;

               line_move=-1;
               line_gauge=1;

               price_up=price_1;

               price_1=price_2;

               time_change[a]=(datetime)arr_time[z];
               time_line[a]=1;
               time_change_price[a]=lin_yang[a];
              }
           }
        }
      //--- the price moves UP
      //--- if the price has moved UPWARDS before that, the line is THICK
      if(line_move==1 && line_gauge==1)
        {
         if(((price_1-price_2)/_Point<-func_calc_dorstep(price_2)) || (kagi_type==0 && (price_1-price_2)/_Point<0))
           {
            yang_int_2[a]=price_2;

            line_move=1;
            line_gauge=1;

            price_1=price_2;

            time_change[a]=(datetime)arr_time[z];
            time_line[a]=1;
           }
        }

      //--- if the price has moved UPWARDS before that, the line is THIN
      if(line_move==1 && line_gauge==-1)
        {
         if(((price_1-price_2)/_Point<-func_calc_dorstep(price_2)) || (kagi_type==0 && (price_1-price_2)/_Point<0))
           {
            if(price_2>price_up) // If the thin line has not crossed the upper shoulder when moving upwards
              {
               yin_int_2[a]=price_up;

               yang_int_1[a]=price_up;
               yang_int_2[a]=price_2;

               line_move=1;
               line_gauge=1;

               price_1=price_2;

               time_change[a]=(datetime)arr_time[z];
               time_central_price[a]=price_up;
               time_line[a]=1;
              }
            else//if(price_2<=price_up) // If the thin line has not crossed the upper shoulder when moving upwards
              {
               yin_int_2[a]=price_2;

               line_move=1;
               line_gauge=-1;

               price_1=price_2;

               time_change[a]=(datetime)arr_time[z];
               time_line[a]=0;
              }
           }
        }

      //--- if the price has moved DOWNWARDS before that, the line is THICK
      if(line_move==-1 && line_gauge==1)
        {
         if((price_1-price_2)/_Point<-func_calc_dorstep(price_2))
           {
            a++;

            yang_int_1[a]=price_1;
            yang_int_2[a]=price_2;

            lin_yang[a]=price_1;

            line_move=1;
            line_gauge=1;

            price_down=price_1;
            price_1=price_2;

            time_change[a]=(datetime)arr_time[z];
            time_line[a]=1;
            time_change_price[a]=lin_yang[a];
           }
        }

      //--- if the price has moved DOWNWARDS before that, the line is THIN
      if(line_move==-1 && line_gauge==-1)
        {
         if((price_1-price_2)/_Point<-func_calc_dorstep(price_2))
           {
            a++;
            if(price_2>price_up) // If the thin line has crossed the upper shoulder when moving upwards
              {
               yin_int_1[a]=price_1;
               yin_int_2[a]=price_up;

               yang_int_1[a]=price_up;
               yang_int_2[a]=price_2;

               lin_yin[a]=price_1;

               line_move=1;
               line_gauge=1;

               price_down=price_1;
               price_1=price_2;

               time_change[a]=(datetime)arr_time[z];
               time_line[a]=1;
               time_change_price[a]=lin_yin[a];
               time_central_price[a]=price_up;
              }
            else //if(price_2<=price_up) // If the thin line has not crossed the upper shoulder when moving upwards
              {
               yin_int_1[a]=price_1;
               yin_int_2[a]=price_2;

               lin_yin[a]=price_1;

               line_move=1;
               line_gauge=-1;

               price_down=price_1;
               price_1=price_2;

               time_change[a]=(datetime)arr_time[z];
               time_line[a]=0;
               time_change_price[a]=lin_yin[a];
              }
           }
        }

     }
//--- function's main loop
//--- assign actual values to drawing buffers
   uint y=a;
//--- auxiliary variables for storing data on filling the current buffer
   char yin=1;
   char yang=1;
   for(uint z=0; z<=a; z++)
     {
      arr_yin_1[z]=yin_int_1[y];
      arr_yin_2[z]=yin_int_2[y];

      switch(yin)
        {
         case 1:
           {
            arr_yin_lin1[z]=lin_yin[y];
            arr_yin_lin1[z+1]=lin_yin[y];
            yin++;
           }
         break;
         case 2:
           {
            arr_yin_lin2[z]=lin_yin[y];
            arr_yin_lin2[z+1]=lin_yin[y];
            yin++;
           }
         break;
         case 3:
           {
            arr_yin_lin3[z]=lin_yin[y];
            arr_yin_lin3[z+1]=lin_yin[y];
            yin=1;
           }
         break;
        }

      arr_yang_1[z]=yang_int_1[y];
      arr_yang_2[z]=yang_int_2[y];

      switch(yang)
        {
         case 1:
           {
            arr_yang_lin1[z]=lin_yang[y];
            arr_yang_lin1[z+1]=lin_yang[y];
            yang++;
           }
         break;
         case 2:
           {
            arr_yang_lin2[z]=lin_yang[y];
            arr_yang_lin2[z+1]=lin_yang[y];
            yang++;
           }
         break;
         case 3:
           {
            arr_yang_lin3[z]=lin_yang[y];
            arr_yang_lin3[z+1]=lin_yang[y];
            yang=1;
           }
         break;
        }
      y--;
     }
//---
  }


3.6. Die Funktion zur Erstellung des grafischen Objektes „Trendlinie“

Wir kommen jetzt zur Betrachtung der Funktion zur Erstellung eines grafischen Objektes der Art „Trendlinie“. Diese Funktion wird zum Anlegen des Kagi-Diagramms im Hauptdiagramm benötigt.

Sie ist überaus schlicht und enthält die zum Anlegen des grafischen Objektes „Trendlinie“ erforderlichen Eingangsparameter: die Objektbezeichnung, jeweils den ersten und zweiten Kurs- und Zeitpunkt sowie die Stärke und die Farbe der Linie. Im Hauptteil der Funktion befindet sich die Funktion zum Anlegen des grafischen Objektes sowie sechs Funktionen zur Änderung seiner Eigenschaften.

//+------------------------------------------------------------------+
//| Func Object 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 line width
   ObjectSetInteger(0,name,OBJPROP_WIDTH,width);
//--- display in the foreground (false) or background (true)
   ObjectSetInteger(0,name,OBJPROP_BACK,false);
//--- enable (true) or disable (false) the mode of continuing the line display to the left
   ObjectSetInteger(0,name,OBJPROP_RAY_LEFT,false);
//--- enable (true) or disable (false) the mode of continuing the line display to the right
   ObjectSetInteger(0,name,OBJPROP_RAY_RIGHT,false);
  }

3.7. Anlegen des Kagi-Diagramms im Hauptdiagramm

Die nächste Funktion, die mehrfach auf die vorhergehende zurückgreift, ist die Funktion zum Anlegen des Kagi-Diagramms im Hauptdiagramm. Als Eingangsvariablen werden die in der (oben bereits vorgestellten) Hauptfunktion zum Anlegen des Kagi-Diagramms gefüllten globalen Datenfelder verwendet: das Datenfeld mit den Kursen der Umkehrpunkte („Schultern“ und „Taillen“), das Datenfeld mit den Übergangs- und Mittelkursen (den Kursen, bei denen der Wechsel von der Yin- zur Yang-Linie oder umgekehrt stattfindet), das Datenfeld mit den Umkehrzeitpunkten (in Echtzeit, zur Bezeichnung des Beginns der Umkehr wird die Kennziffer des Datenfeldes [z-1] verwendet) sowie das Datenfeld mit der Art der Linie, auf der die Umkehr erfolgt ist, es schreitet wie das Zeitdatenfeld ebenfalls um ein Element voran.

Der Hauptteil der Funktion verkörpert einen Arbeitsgang oder Zyklus, der aus zwei Teilen besteht: dem Anlegen der senkrechten und dem Anlegen der waagerechten Linien. der erste zerfällt wiederum in zwei Teile: das Einzeichnen der Senkrechten unter Berücksichtigung des Linienwechsels (des Vorliegens eines zentralen Übergangskurses) sowie unter der Annahme, dass die Linien sich nicht ändern. Beachten Sie bitte die weitergegebenen Parameter der Funktion zum Anlegen des grafischen Objektes „Trendlinie“.

Die Benennung erfolgt zyklisch, dabei beginnt die Objektbezeichnung mit der magischen Zahl (die zum Löschen der Objekte nur eines bestimmten Indikators benötigt wird), danach wird seine Art eingetragen, und zum Schluss wird ihm eine Kennziffer zugewiesen, die bei jedem Durchlauf des Arbeitsgangs aktualisiert wird.

//+------------------------------------------------------------------+
//| Func Kagi Main Chart                                             |
//+------------------------------------------------------------------+
void func_kagi_main_chart(double &price[],         // Shoulder prices array
                          double &central_price[], // Array of the prices of passing through the shoulders
                          datetime &time[],        // Current location time array ([-1] - start of shoulder)
                          char &type_line_end[])   // Line type by the start of shoulder formation
  {
//--- start of the loop
   for(uint z=1; z<=a; z++)
     {
      //--- check for the pass conditions (no pass)
      if(central_price[z]==-1)
        {
         if(type_line_end[z-1]==0 && price[z+1]!=-1)
           {
            func_create_trend_line(IntegerToString(magic_numb)+"_trend_yin_v"+IntegerToString(z),
                                   price[z],price[z+1],time[z],time[z],width_yin_main,color_yin_main);
           }
         if(type_line_end[z-1]==1 && price[z+1]!=-1)
           {
            func_create_trend_line(IntegerToString(magic_numb)+"_trend_yang_v"+IntegerToString(z),
                                   price[z],price[z+1],time[z],time[z],width_yang_main,color_yang_main);
           }
        }
      else //--- check for the pass conditions (pass is present)
        {
         if(type_line_end[z-1]==0 && price[z+1]!=-1)
           {
            func_create_trend_line(IntegerToString(magic_numb)+"_trend_yin_v"+IntegerToString(z),
                                   central_price[z],price[z],time[z],time[z],width_yin_main,color_yin_main);
            func_create_trend_line(IntegerToString(magic_numb)+"_trend_yang_v"+IntegerToString(z),
                                   central_price[z],price[z+1],time[z],time[z],width_yang_main,color_yang_main);
           }
         if(type_line_end[z-1]==1 && price[z+1]!=-1)
           {
            func_create_trend_line(IntegerToString(magic_numb)+"_trend_yin_v"+IntegerToString(z),
                                   central_price[z],price[z+1],time[z],time[z],width_yin_main,color_yin_main);
            func_create_trend_line(IntegerToString(magic_numb)+"_trend_yang_v"+IntegerToString(z),
                                   central_price[z],price[z],time[z],time[z],width_yang_main,color_yang_main);
           }
        }
      //--- check for the pass conditions (pass is present)
      //--- draw the horizontals
      if(type_line_end[z-1]==0)
        {
         func_create_trend_line(IntegerToString(magic_numb)+"_trend_h"+IntegerToString(z),
                                price[z],price[z],time[z-1],time[z],width_yin_main,color_yin_main);
        }
      if(type_line_end[z-1]==1)
        {
         func_create_trend_line(IntegerToString(magic_numb)+"_trend_h"+IntegerToString(z),
                                price[z],price[z],time[z-1],time[z],width_yang_main,color_yang_main);
        }
      //--- draw the horizontals
     }
  }

3.8. Anlegen weiterer Markierungen

Es wurde bereits gesagt, dass der Indikator das Anlegen weiterer Markierungen ermöglicht. Wir betrachten die Funktion zum Anlegen dieser Markierungen in dem Hauptdiagramm. Es gibt hier insgesamt zwei Arten von Markierungen: die mithilfe der Objekte Kursnotierung und senkrechte Linie wiedergegebenen Markierungen für den Kurs und den Zeitpunkt der Umkehr. Als Eingangsparameter werden folgende Kenngrößen weitergegeben: die Kennung der Art der Darstellung der Markierung des Umkehrkurses und ihrer Farbe, die Kennung der Art der Darstellung der Markierung des Zeitpunktes der Umkehr und die Kennung der Änderung ihrer Farbe sowie die erste und zweite Farbe zur Wiedergabe der Umkehrzeitpunkte.

Die gesamte Funktion ist in zwei Teile untergliedert: der erste Teil ist für die zeitlichen Markierungen zuständig, der zweite für die Kursmarkierungen. Beide Teile der Funktion bestehen aus durch die Anzahl der Umkehrpunkte des Diagramms (die Variable „a“) beschränkten Zyklen. Jedem Zyklus ist der bedingte Ausdruck if-else vorgeschaltet, der die Notwendigkeit seiner Wiedergabe entsprechend der Indikatoreinstellungen überprüft.

Der erste Zyklus legt die Zeitmarken an, an seinem Anfang erfolgt die Festlegung der Objektbezeichnung (über die Grundsätze zur Erstellung der Bezeichnung wurde bereits gesprochen), dann wird die die Farbe je nach Linie aus dem global deklarierten Datenfeld mit den Linienarten ausgewählt (wenn dieser Parameter eingestellt ist) und es werden weitere Parameter auf die Linie angewendet.

Dem zweiten Zyklus obliegt das Anlegen der Kursmarkierungen der Umkehrpunkte. Zunächst kommt wieder die Erstellung der Objektbezeichnung, danach folgt, je nachdem, ob das Kagi-Diagramm in dem Hauptdiagramm angelegt werden soll oder nicht, wird die Auswahl der Kennziffer des Zeitdatenfeldes festgelegt. Geschieht das nicht, so hängen die Markierungen „in der Luft“, ohne dass ganz klar wird, an welcher Stelle sich die Umkehr ereignet hat. Weiterhin wird ein Objekt der Art Kursnotierung angelegt und eingerichtet.

//+------------------------------------------------------------------+
//| Func Label Main Chart                                            |
//+------------------------------------------------------------------+
void func_label_main_chart(bool label_print,
                           color label_color,
                           bool time_change_print,
                           bool time_change_color,
                           color time_color_first,
                           color time_color_second)
  {
   if(time_change_print==true)
     {
      for(uint z=1; z<=a; z++)
        {
         string name=IntegerToString(magic_numb)+"_time_2_"+IntegerToString(z);
         //--- create an object of a vertical line type
         ObjectCreate(0,name,OBJ_VLINE,0,time_change[z],0);
         //--- set the line color
         color color_line=clrBlack;
         if(time_change_color==true)
           {
            if(time_line[z]==0)color_line=time_color_first;
            if(time_line[z]==1)color_line=time_color_second;
           }
         else color_line=time_color_first;
         ObjectSetInteger(0,name,OBJPROP_COLOR,color_line);
         //--- set the line display style
         ObjectSetInteger(0,name,OBJPROP_STYLE,STYLE_SOLID);
         //--- set the line width
         ObjectSetInteger(0,name,OBJPROP_WIDTH,1);
         //--- display on the foreground (false) or background (true)
         ObjectSetInteger(0,name,OBJPROP_BACK,false);
         //--- enable (true) or disable (false) the line display mode in the chart subwindows
         ObjectSetInteger(0,name,OBJPROP_RAY,time_separate_windows);
        }
     }
   if(label_print==true)
     {
      for(uint z=1; z<=a; z++)
        {
         string name=IntegerToString(magic_numb)+"_label_2_"+IntegerToString(z);
         uint numb_time;
         if(kagi_main_chart==true)numb_time=z;
         else numb_time=z-1;
         //--- create a label type object
         ObjectCreate(0,name,OBJ_ARROW_RIGHT_PRICE,0,time_change[numb_time],time_change_price[z]);
         //--- set the label color
         ObjectSetInteger(0,name,OBJPROP_COLOR,label_color);
         //--- set the edging line style
         ObjectSetInteger(0,name,OBJPROP_STYLE,STYLE_SOLID);
         //--- set the label size
         ObjectSetInteger(0,name,OBJPROP_WIDTH,1);
         //--- display on the foreground (false) or background (true)
         ObjectSetInteger(0,name,OBJPROP_BACK,false);
        }
     }
  }

Mit dem Anlegen von Markierungen im Hauptdiagramm haben wir uns befasst, jetzt sehen wir, wie Markierungen im Indikatorfenster angelegt werden.

Alle Markierungen im Indikatorfenster sind vorzugsweise Kursmarkierungen, von denen es wiederum zwei Arten gibt: Markierungen der Umkehrkurse und Kursgrenzen (Kursebenen). Kursgrenzen können auf zweierlei Weise dargestellt werden: anhand der Umkehrpunkte des Diagramms sowie in gleichbleibendem Abstand über den gesamten Kursbereich des Diagramms verteilt. Die erste Art kann die Farbe der Grenzen in zwei Varianten ändern: je nach Linienart (Yin oder Yang) oder nach Art der Umkehr (auf- oder absteigend).

Somit zerfällt die Funktion selbst in zwei Arbeitsgänge: der erste ist für die Erstellung der Kursmarkierungen für die Umkehrpunkte verantwortlich, während der zweite die Kursgrenzen ausweist. Letzteres erfolgt wiederum auf zweierlei Weise: eine Markierung an jedem Umkehrpunkt oder Markierungen über den gesamten Kursbereich auf einer einheitlichen Ebene.

Der Unterschied dieser Funktion zu der vorherigen besteht in dem Vorhandensein einer Begrenzung der Anzahl der Kursmarkierungen und -grenzen angesichts des Umstands, dass sie, wenn sie in großer Zahl vorliegen, das Diagramm schlicht überfrachten und somit sein Verständnis erschweren.

Wegen dieser Besonderheit sind beide Arbeitsgänge (Zyklen) durch die in den Indikatoreinstellungen (Anzahl der Kursmarkierungen oder -grenzen) festgelegte Anzahl der Durchläufe begrenzt. Ein solches Herangehen an die Erstellung ist deshalb gefährlich, weil die Anzahl der Umkehrpunkte sich als wesentlich geringer erweisen kann als die Anzahl der in den Einstellungen festgelegten Kursmarkierungen, weshalb im Verlauf jedes Durchlaufs eines Zyklus die Bedingung des Vorliegens eines Umkehrpunktes für das Anlegen einer Kursmarkierung oder -grenze überprüft wird.

Eine Ausnahme bildet die Einzeichnung von Kursgrenzen über den gesamten Kursbereich in gleichbleibendem Abstand. Die Erstellung grafischer Objekte der Art Kursnotierung erfolgt anhand der Koordinaten in umgekehrter Reihenfolge, das heißt, die Markierungen werden ab dem aktuellen Datum zurück in die Vergangenheit festgelegt. Dasselbe gilt auch für die Kursgrenzen, als erste werden die aktuellen Kursgrenzen angelegt, danach die etwas älteren mit Ausnahme derjenigen, die von den Kehrtwenden des Diagramms unabhängig sind.

Die Farbwechsel der Kursgrenze erfolgen mithilfe des bedingten Ausdrucks if-else entsprechend den Einstellungen des Indikators.

//+------------------------------------------------------------------+
//| Func Label Indicator Window                                      |
//+------------------------------------------------------------------+
void func_label_indicator_window(bool label_print,         // Draw price labels
                                 bool levels_print,        // Draw levels
                                 char levels_type_draw,    // Type of drawing the levels by reversals or at an equal distance of the entire price range
                                 char levels_color_change) // Change line color
  {
   uint number=a;
   if(label_print==true)
     {
      for(uint z=0; z<=label_1_number; z++)
        {
         if(z<number)
           {
            string name=IntegerToString(magic_numb)+"_label_1_"+IntegerToString(z);
            //--- create label type object
            ObjectCreate(0,name,OBJ_ARROW_RIGHT_PRICE,ChartWindowFind(),(datetime)Time[(bars_copied_time-z-2)],time_change_price[number-z]);
            //--- set the label color
            ObjectSetInteger(0,name,OBJPROP_COLOR,label_1_color);
            //--- set the style of the edging line
            ObjectSetInteger(0,name,OBJPROP_STYLE,STYLE_SOLID);
            //--- set the label size
            ObjectSetInteger(0,name,OBJPROP_WIDTH,1);
            //--- display on the foreground (false) or background (true)
            ObjectSetInteger(0,name,OBJPROP_BACK,false);
           }
        }
     }
   if(levels_print==true)
     {
      if(levels_type_draw==0)
        {
         for(uint z=0; z<=levels_number; z++)
           {
            if(z<number)
              {
               IndicatorSetDouble(INDICATOR_LEVELVALUE,z,time_change_price[number-z]);
               if(levels_change_color==0)
                 {
                  double numb_even=z;
                  if(MathMod(numb_even,2)==0)
                    {
                     IndicatorSetInteger(INDICATOR_LEVELCOLOR,z,levels_first_color);
                    }
                  if(MathMod(numb_even,2)!=0)
                    {
                     IndicatorSetInteger(INDICATOR_LEVELCOLOR,z,levels_second_color);
                    }
                 }
               if(levels_change_color==1)
                 {
                  if(time_line[number-z]==0)IndicatorSetInteger(INDICATOR_LEVELCOLOR,z,levels_first_color);
                  if(time_line[number-z]==1)IndicatorSetInteger(INDICATOR_LEVELCOLOR,z,levels_second_color);
                 }
               if(levels_change_color==2)
                 {
                  IndicatorSetInteger(INDICATOR_LEVELCOLOR,z,levels_first_color);
                 }
              }
           }
        }
      if(levels_type_draw==1)
        {
         double max_price=Price[ArrayMaximum(Price)];
         double min_price=Price[ArrayMinimum(Price,1,ArrayMinimum(Price)-1)];
         double number_difference=(max_price-min_price)/levels_number;
         NormalizeDouble(number_difference,_Digits);
         for(uint z=0; z<=levels_number; z++)
           {
            IndicatorSetDouble(INDICATOR_LEVELVALUE,z,(min_price+(z*number_difference)));
            IndicatorSetInteger(INDICATOR_LEVELCOLOR,z,levels_first_color);
           }
        }
     }
  }

3.9. Löschen zuvor angelegter grafischer Objekte

Es ist kein Geheimnis mehr, dass dieser Indikator reich an grafischen Objekten ist, deshalb ist es jetzt an der Zeit, sich Gedanken über ihre schnelle und praktikable Entfernung zu machen.

Dazu gibt es die Funktion zum Löschen grafischer Objekte. Als Parameter der Funktion dienen die ursprüngliche Bezeichnung sowie die Anzahl der Objekte. Die Objektbezeichnung muss wie bei ihrer Erstellung die magische Zahl und die Objektart enthalten. Der Aufruf der Funktion im Programm ist durch eine Anzahl von Objekten begrenzt, die viel größer ist, als tatsächlich vorhanden sind, was sich allerdings nicht auf die Funktionalität des Indikators auswirkt.

//+------------------------------------------------------------------+
//| 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);
     }
  }

3.10. Die Funktion zum Ausführen der Diagrammerstellung

Jetzt, nachdem wir alle Funktionen zu den Berechnungen und zum Anlegen des Kagi-Diagramms sowie zur Erstellung und zum Löschen grafischer Objekte durchgegangen sind, kommen wir zu einer weiteren kleinen Funktion zur Überprüfung des Auftretens eines neuen Balkens. Die Funktion ist recht schlicht und weist lediglich einen Eingangsparameter auf, nämlich den Beobachtungszeitraum. Die Antwort der Funktion ist ebenfalls banal einfach und von der Art bool. Sie gibt an, ob ein neuer Balken vorliegt oder nicht. Das Kernstück des Hauptteils der Funktion ist Umschaltausdruck switch, der die Verantwortung je nach Zeitraum an einen seiner vielfältigen Operatoren weitergibt.

In unserem Beispiel umfasst die Funktion den gesamten Bereich der Zeiträume, obwohl man ihn auch auf einen beschränken könnte.

Der Algorithmus stammt aus dem Code IsNewBar: es wird der Wert für den Eröffnungszeitpunkt des letzten Balkens geprüft und mit dem vorher ermittelten Zeitwert verglichen. Sind die Werte unterschiedlich, so liegt ein neuer Balken vor. Letztlich wird der neue Wert wie der vorher bestimmte angenommen, und die Antwort der Funktion gilt als positiv. Wenn der Wert der Eröffnungszeit des letzten Balkens mit dem vorher bestimmten Zeitwert übereinstimmt, ist kein neuer Balken entstanden, und die Antwort der Funktion ist negativ.

//+------------------------------------------------------------------+
//| Func New Bar                                                     |
//+------------------------------------------------------------------+
bool func_new_bar(ENUM_TIMEFRAMES period_time)
  {
//----
   static datetime old_Times[22];// array for storing old values
   bool res=false;               // analysis result variable  
   int  i=0;                     // old_Times[] array cell index    
   datetime new_Time[1];         // new bar time

   switch(period_time)
     {
      case PERIOD_M1:  i= 0; break;
      case PERIOD_M2:  i= 1; break;
      case PERIOD_M3:  i= 2; break;
      case PERIOD_M4:  i= 3; break;
      case PERIOD_M5:  i= 4; break;
      case PERIOD_M6:  i= 5; break;
      case PERIOD_M10: i= 6; break;
      case PERIOD_M12: i= 7; break;
      case PERIOD_M15: i= 8; break;
      case PERIOD_M20: i= 9; break;
      case PERIOD_M30: i=10; break;
      case PERIOD_H1:  i=11; break;
      case PERIOD_H2:  i=12; break;
      case PERIOD_H3:  i=13; break;
      case PERIOD_H4:  i=14; break;
      case PERIOD_H6:  i=15; break;
      case PERIOD_H8:  i=16; break;
      case PERIOD_H12: i=17; break;
      case PERIOD_D1:  i=18; break;
      case PERIOD_W1:  i=19; break;
      case PERIOD_MN1: i=20; break;
      case PERIOD_CURRENT: i=21; break;
     }
   // copy the time of the last bar to new_Time[0] cell  
   int copied=CopyTime(_Symbol,period_time,0,1,new_Time);
  
   if(copied>0) // all is well. Data has been copied
      {
      if(old_Times[i]!=new_Time[0])       // if the bar's old time is not equal to new one
         {
         if(old_Times[i]!=0) res=true;    // if it is not the first launch, true = new bar
         old_Times[i]=new_Time[0];        // store the bar's time
         }
      }
//----
   return(res);
  }

 

3.11. Die Funktionen OnCalculate() und OnChartEvent()

Die Zusammenfassung aller oben vorgestellten Funktionen erfolgt in der Konsolidierungsfunktion Func Consolidation. Diese Funktion wird jedes Mal aufgerufen, wenn in der Funktion OnCalculate() ein neuer Balken auftritt sowie bei Betätigung der Tastaturtaste „R“ aus der Funktion OnChartEvent() heraus. 

Vor dem Anlegen oder der Aktualisierung des Diagramms erfolgt in der Konsolidierungsfunktion (Func Consolidation) der Aufruf der Funktion zum Löschen aller grafischen Objekte. Da es recht viele Objekte gibt, die sich über die Kursmarkierungen des Hauptdiagramms und des Indikatorfensters, senkrechte Linien zur Bezeichnung der Zeitpunkte der Umkehr sowie über die senk- und waagerechten Trendlinien der Arten Yin und Yang verteilen, beträgt die Gesamtzahl der Aufrufe dieser Funktionen 7.

Danach werden die Verlaufsdaten nach Kurs und Zeit kopiert. Anschließend erfolgt der Aufruf der Hauptfunktion zum Anlegen eines Kagi-Diagramms. Wiederum danach wird die Funktion zum Anlegen aller Kursmarkierungen im Hauptdiagramm sowie im Fenster des Indikators aufgerufen, und zum Schluss erfolgen die Erstellung des Kagi-Diagramms im Hauptdiagramm und die Auslösung der Funktion zum erneuten Einzeichnen der Objekte.

//+------------------------------------------------------------------+
//| Func Consolidation                                               |
//+------------------------------------------------------------------+
void func_consolidation()
  {
//--- date of construction end
   stop_data=TimeCurrent();

//--- deleting all graphical objects belonging to the indicator
   func_delete_objects(IntegerToString(magic_numb)+"_label_2_",ObjectsTotal(0,-1,-1));
   func_delete_objects(IntegerToString(magic_numb)+"_label_1_",ObjectsTotal(0,-1,-1));
   func_delete_objects(IntegerToString(magic_numb)+"_time_2_",ObjectsTotal(0,-1,-1));
   func_delete_objects(IntegerToString(magic_numb)+"_trend_yin_v",ObjectsTotal(0,-1,-1));
   func_delete_objects(IntegerToString(magic_numb)+"_trend_yang_v",ObjectsTotal(0,-1,-1));
   func_delete_objects(IntegerToString(magic_numb)+"_trend_h",ObjectsTotal(0,-1,-1));
   func_delete_objects(IntegerToString(magic_numb)+"_trend_h",ObjectsTotal(0,-1,-1));

//--- copy price data to the main buffer
   copy_history=func_copy_history(Price,start_data,stop_data);

//--- display information about the error when copying price data
   if(copy_history==false)Alert("Error of copy history Price");

//--- copy time data to the main buffer
   copy_time=func_copy_time(Time,start_data,stop_data);

//--- display a notification of the error occurred while copying time data
   if(copy_time==false)Alert("Error of copy history Time");

//--- construct Kagi chart in the indicator window
   func_draw_kagi(Price,YinBuffer1,YinBuffer2,Yin1Buffer,Yin2Buffer,Yin3Buffer,
                  YangBuffer1,YangBuffer2,Yang1Buffer,Yang2Buffer,Yang3Buffer,Time);

//--- draw labels on the main chart
   func_label_main_chart(label_2,label_2_color,time_line_draw,time_line_change_color,time_first_color,time_second_color);

//--- draw labels on the indicator chart
   func_label_indicator_window(label_1,levels_on_off,levels_type,levels_change_color);

//--- construct Kagi chart in the main window
   if(kagi_main_chart==true)func_kagi_main_chart(time_change_price,time_central_price,time_change,time_line);

//--- redraw the chart
   ChartRedraw(0);
//---
  }
//+------------------------------------------------------------------+
//| Custom indicator iteration function                              |
//+------------------------------------------------------------------+
int OnCalculate(const int rates_total,
                const int prev_calculated,
                const int begin,
                const double &price[])
  {
//---
   if(func_new_bar(period_to_redraw)==true)
     {
      func_consolidation();
     }
//--- return value of prev_calculated for next call
   return(rates_total);
  }
//+------------------------------------------------------------------+
//| 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
  {
   if(id==CHARTEVENT_KEYDOWN) // Keyboard button pressing event
     {
      if(lparam==82) // "R" key has been pressed
        {
         func_consolidation();
        }
     }
  }

 

3.12. Die Funktion OnDeinit()

Das Löschen aller Objekte obliegt der Funktion zur Entfernung (Deinitialisierung) des Indikators.

//+------------------------------------------------------------------+
//| OnDeinit                                                         |
//+------------------------------------------------------------------+ 
void OnDeinit(const int reason)
  {
//--- delete all graphical objects belonging to the indicator
   func_delete_objects(IntegerToString(magic_numb)+"_label_2_",ObjectsTotal(0,-1,-1));
   func_delete_objects(IntegerToString(magic_numb)+"_label_1_",ObjectsTotal(0,-1,-1));
   func_delete_objects(IntegerToString(magic_numb)+"_time_2_",ObjectsTotal(0,-1,-1));
   func_delete_objects(IntegerToString(magic_numb)+"_trend_yin_v",ObjectsTotal(0,-1,-1));
   func_delete_objects(IntegerToString(magic_numb)+"_trend_yang_v",ObjectsTotal(0,-1,-1));
   func_delete_objects(IntegerToString(magic_numb)+"_trend_h",ObjectsTotal(0,-1,-1));
   func_delete_objects(IntegerToString(magic_numb)+"_trend_h",ObjectsTotal(0,-1,-1));
//--- redraw the chart
   ChartRedraw(0);
  }

Damit ist die Erläuterung der Zusammensetzung des Indikatorcodes abgeschlossen, und wir kommen zur Beantwortung der Fragen, wozu man ihn braucht und wie man ihn im Devisenhandel einsetzen kann.


4. Die praktische Anwendung von Kagi-Diagrammen

Es gibt zahlreiche Strategien für den Handel anhand von Kagi-Diagrammen. Wir werden uns einige von ihnen ansehen.

Den Anfang macht die verbreitetste Strategie: „Verkaufe beim Übergang von Yang zu Yin und kaufe beim Übergang von Yin zu Yang“. Ich versuche, das in der Abbildung 4 zu veranschaulichen:

Veranschaulichung der Strategie: „Verkaufe beim Übergang von Yang zu Yin und kaufe beim Übergang von Yin zu Yang“

Abb. 4. Veranschaulichung der Strategie: „Verkaufe beim Übergang von Yang zu Yin und kaufe beim Übergang von Yin zu Yang“ 

Wie die Abbildung 4 (EURUSD M30, 5 Punkte) zeigt, liefert diese Strategie erfreulich gute Ergebnisse. In der Abbildung werden 8 Punkte für 4 Signale wiedergegeben, das erste Signal (1) gibt an, dass bei 1,3518 gekauft werden sollte, was im Grunde zutreffend ist, da der Kurs dann einen Wert von etwa 1,3560 erreicht, was 42 Punkten an einem Tag entspricht. Das ist gar nicht so schlecht.

Der nächste Punkt (2) signalisiert den Verkauf bei 1,3519, und wie wir sehen, bewegt sich der Kurs nach unten und durchbricht die Grenze von 1,3485, was 34 Punkten entspricht, für ungefähr 2 Stunden.

Kommen wir zu Punkt (3), hier sehen wir, dass der Kauf bei 1,3538 erfolgt und der Kurs weiter steigt, bis er 1,3695 erreicht. Das entspricht bereits 157 Punkten in anderthalb Tagen. Das ist natürlich nicht der höchstmögliche Gewinn, aber trotzdem nicht übel, oder.

Die nächste Handelsstrategie, das Abstoßen von der Trendlinie, wird in der Abbildung 5 (EURUSD M30, 5 Punkte) am Beispiel des Zeitraums vom 7. - 18. Oktober vorgestellt:

Veranschaulichung der Strategie: Abstoßen von der Trendlinie

Abb. 5. Veranschaulichung der Strategie: Abstoßen von der Trendlinie

Außerdem können wir anhand von Kanälen handeln. Ein Beispiel für die Ermittlung der Kanäle zeigt die Abbildung 6 (EURUSD H1, 5 Punkte) für etwa denselben Zeitraum:

Veranschaulichung der Strategie: Handel mittels Kanälen

Abb. 6. Veranschaulichung der Strategie: Handel mittels Kanälen

Eine etwas weniger verbreitete Strategie beruht darauf, dass nach 7 - 10 fortlaufend wachsenden „Schultern“ oder schrumpfenden „Taillen“ zwangsläufig eine gegenläufige Bewegung (ab- oder aufsteigend) einsetzen muss.

Das wird in der Abbildung 7 (GBPUSD H4, 25 Punkte) für den Zeitraum vom 10. Juli bis zum 18. Oktober veranschaulicht:

Veranschaulichung der Strategie: 7 - 10 fortlaufend wachsende „Schultern“ oder schrumpfende „Taillen“

Abb. 7. Veranschaulichung der Strategie: 7 - 10 fortlaufend wachsende „Schultern“ oder schrumpfende „Taillen“

Wie die Abbildung zeigt, folgt auf sieben immer höher steigende Schultern ein recht befriedigender Rückgang um etwa die Hälfte des vorherigen Anstiegs (etwa 300 Punkte).

Zur Veranschaulichung der Notwendigkeit der Verwendung zusätzlicher Indikatoreinstellungen betrachten wir die Strategie: „Handle anhand von Kursmarkierungen!“ Der Kern der Strategie besteht darin, in den Markt einzutreten, wenn der Kurs die vorhergehende Kursmarkierung über- (Kauf) oder unterschreitet (Verkauf).

Anschaulich werden diese Handlungen in der Abbildung 8 (GBPUSD H4, 30 Punkte, überarbeitete Anlage) wiedergegeben:

Veranschaulichung der Strategie: „Handle anhand von Kursmarkierungen!“

Abb. 8. Veranschaulichung der Strategie: „Handle anhand von Kursmarkierungen!“ 

In der Abbildung 8 wird mithilfe der roten langen Pfeile signalisiert, wann ge- oder verkauft werden muss. Die Pfeile selbst beginnen bei der jeweils vorhergehenden Kursmarkierung und weisen die Stellen aus, an denen diese überwunden werden.

Die Zeitmarken dienen im Wesentlichen zur Anzeige der Trendrichtung. Da in den Einstellungen die Farbe der Zeitmarken entsprechend der Linienart geändert werden kann, und die Arten Yin oder Yang die Richtung oder die Umkehr des Trends anzeigen, lässt die Farbe Rückschlüsse auf die aktuelle Stimmung am Markt zu.

Nehmen wir beispielsweise das in der Abbildung 9 wiedergegebene Wertpapierdiagramm #IBM (H4, 1%, in klassischer Form):

Das Aussehen der Abgabe eines Trendrichtungssignals mittels Zeitmarken

Abb. 9. Das Aussehen der Abgabe eines Trendrichtungssignals mittels Zeitmarken

In dem Diagramm ist zu sehen, dass die blauen Linien im Wesentlichen auf den Höhen des Diagramms liegen, die roten dagegen in den Senken.


Fazit

Damit komme ich zu dem Schluss, dass auch dieser Beitrag einmal enden muss. Genau genommen hat das Kagi-Diagramm seine Leistungsfähigkeit auf dem Markt noch nicht voll ausgeschöpft und kann als Grundlage einer Strategie oder als Hilfsmittel für eine genauere und zuverlässigere Analyse immer noch dazu beitragen, auf dem Markt erfolgreich zu handeln.

Ich habe versucht, den Großteil der Aufmerksamkeit auf den Code selbst sowie auf die Besonderheiten beim Anlegen des Indikators zu richten. Das wesentliche Ziel war die Schaffung eines Mehrzweckindikators, der alle für die Darstellung erforderlichen Besonderheiten aufweist und über einige weitere, abschaltbare, Möglichkeiten verfügen könnte.

Ich würde mich freuen, neue Vorschläge und Ergänzungen zu dem Indikator zu sehen, möglicherweise lassen sie sich in der Zukunft umsetzen. Überdies erwarte ich Ihre Rückmeldungen und würde mich freuen, Fragen zu dem Indikator zu beantworten.

Damit ist der nächste Beitrag aus der Reihe zur Entwicklung von Indikatoren zum Anlegen von Diagrammen aus älteren Verlaufsdaten an seinem Ende angelangt. Den vorhergehenden Artikel finden Sie unter dieser Verknüpfung. Ich hoffe, Sie bald bei dem nächsten Beitrag aus dieser Reihe wiederzusehen. Vielen Dank für Ihr Interesse! Ich wünsche Ihnen einen erfolgreichen Handel sowie einen optimierten und stabilen Programmcode.