Kagi-Charts in MQL5 beherrschen (Teil I): Erstellen des Indikators
Einführung
Kagi-Charts sind eine besondere Art von Preischarts, die sich auf reale Marktbewegungen konzentrieren. Sie wurden vor vielen Jahren in Japan entwickelt. Im Gegensatz zu normalen Kerzen-Charts, die in jedem festen Zeitintervall aktualisiert werden, ändert ein Kagi-Chart seine Richtung nur, wenn sich der Kurs um einen bestimmten Wert bewegt. Dadurch sind Kagi-Charts sehr sauber und beseitigen unnötiges Rauschen hervorragend.
Da ein Kagi-Chart nicht von der Zeit abhängt, vermittelt es dem Händler ein klares Bild des Trends. Wenn der Preis steigt, wächst die Linie nach oben. Wenn der Preis fällt, bewegt sich die Linie nach unten. Das Chart ändert auch seinen Stil, wenn die Nachfrage zum Angebot wird. Diese einfache Struktur hilft Händlern, die Trendstärke ohne Ablenkung zu erkennen.
In einem normalen Kerzen-Chart druckt der Markt in festen Zeitabständen einen neuen Balken. Ein Kagi-Chart verhält sich anders. Er erstreckt sich nur nach oben, wenn der Preis steigt, und dreht sich nur nach unten, wenn der Preis um einen bestimmten Umkehrwert fällt. Wenn sich das Kräfteverhältnis zwischen Angebot und Nachfrage ändert, ändert die Kagi-Linie ihre Dicke oder Farbe. Dieses klare Verhalten macht Kagi-Charts so leistungsfähig für die Trendanalyse.
Bevor wir mit dem Aufbau unseres eigenen Kagi-Systems beginnen, ist es hilfreich, sich zu vergegenwärtigen, wie ein Kagi-Chart im Vergleich zu einem Standard-Kerzen-Chart aussieht. Das folgende Beispiel zeigt einen einfachen konzeptionellen Vergleich:
Traditioneller Kerzen-Chart

Kagi-Chart

In dieser zweiteiligen Serie werden wir ein komplettes Kagi-basiertes Handelssystem in MQL5 aufbauen. Im ersten Teil werden wir ein voll funktionsfähiges Kagi-Chart erstellen, das direkt im Hauptfenster des Charts gezeichnet wird und grafische MQL5-Objekte wie OBJ_TREND verwendet. Dadurch erhalten wir eine saubere und genaue Kagi-Visualisierung, die sofort auf Marktbewegungen reagiert.
Im zweiten Teil werden wir diese Arbeit durch die Erkennung von Kagi-Signalen erweitern und sie zur automatischen Eröffnung von Geschäften nutzen. Am Ende der Serie werden Sie genau verstehen, wie Kagi-Charts aufgebaut sind, und Sie werden Ihre eigene Kagi-betriebene Handelsmaschine in MQL5 laufen haben.
Grundlagen der Kagi-Chartkonstruktion
Bevor wir mit der Codierung beginnen, müssen wir verstehen, wie ein Kagi-Chart aufgebaut ist. Es gibt viele ausführliche Erklärungen im Internet, darunter die bekannte Beschreibung auf Wikipedia, sodass wir hier nicht die gesamte Theorie wiederholen werden. Stattdessen werden wir uns auf die wesentlichen Elemente konzentrieren, die für die korrekte Implementierung des Charts in MQL5 erforderlich sind. Diese Konzepte werden uns dabei helfen, die Richtung zu berechnen, Umkehrungen zu erkennen und die Kagi-Linien auf unserem Chart zu zeichnen.
Kursschwankungen und Richtungsänderungen
Ein Kagi-Chart bewegt sich nur dann, wenn sich der Kurs ausreichend bewegt, um einen sinnvollen Swing zu bilden. Wir verfolgen zwei Dinge:
- Aufwärtsbewegung.
- Abwärtsbewegung.
Wenn sich der Kurs in dieselbe Richtung bewegt, wird die Kagi-Linie einfach verlängert. Eine neue Richtung beginnt erst, wenn sich der Kurs um mehr als den gewählten Umkehrwert gegen den bestehenden Trend bewegt.
Schultern (lokale Höchstwerte)
Eine Schulter, die auch als lokales Maximum bezeichnet wird, entsteht, wenn der Kurs einen neuen Höchststand erreicht und dann nach unten dreht. Diese Höhe wird zur Schulterhöhe. Steigt der Kurs später über diese Schulter, bestätigt dies die Aufwärtsstärke und kann eine Veränderung der Dicke von dünn zu dick auslösen.
Taillen (lokale Mindestwerte)
Eine Taille ist ein lokales Minimum. Dies geschieht, wenn der Preis einen neuen Tiefstand erreicht und dann wieder ansteigt. So niedrig wird die Taille. Wenn der Kurs später unter diese Taille fällt, bestätigt dies die Abwärtsstärke und kann eine Änderung der Dicke von dick zu dünn auslösen.
Der Umkehrwert
Der Umkehrwert bestimmt, wann die Kagi-Linie ihre Richtung ändert. Wir werden dem Händler die Wahl lassen zwischen:
- Ein fester Wert.
- Ein Prozentsatz des Preises.
Die Kernidee ist einfach: Wenn sich der Kurs um mehr als diesen Wert gegen die aktuelle Richtung bewegt, zieht die Kagi-Linie ein horizontales Segment und beginnt dann ein neues vertikales Segment in die entgegengesetzte Richtung.
Yin- und Yang-Linien
Kagi-Charts verwenden zwei Linienarten, um Verschiebungen bei Angebot und Nachfrage darzustellen:
- Yin-Linie (dünne Linie): Zeigt eine nachlassende Nachfrage oder eine Dominanz der Verkäufer.
- Yang-Linie (dicke Linie): Zeigt eine verstärkte Nachfrage oder eine Dominanz der Käufer.
Der Wechsel von Yin zu Yang erfolgt, wenn die Kagi-Linie oberhalb einer früheren Schulter durchbricht. In ähnlicher Weise findet ein Wechsel von Yang zu Yin statt, wenn die Kagi-Linie unterhalb einer früheren Taille unterbrochen wird.
Diese Stiländerungen bilden die klassischen Kagi-Handelssignale:
- Dünn => Dick = Kaufsignal.
- Dick => Dünn = Verkaufssignal.
Projektstruktur und Inputs
In diesem Abschnitt werden wir die Grundlage für unser Kagi-Chart-Projekt schaffen. Dieses Programm wird als Expert Advisor erstellt. Es ist wichtig, dies deutlich zu sagen, da MQL5 mehrere Programmtypen anbietet. Dazu gehören Skripte, Indikatoren, Expert Advisors und Dienste. Jede von ihnen dient einem anderen Zweck. Für dieses Projekt ist der Typ des Expert Advisors die beste Option, da es uns erlaubt, mit grafischen Objekten im Haupt Chartfenster mit voller Kontrolle zu arbeiten. Wir verwenden das grafische Trendlinienobjekt OBJ_TREND, um die Kagi-Segmente direkt in das Preis Chart zu zeichnen. So entsteht eine übersichtliche und interaktive Anzeige, ohne dass nutzerdefinierte Indikatorpuffer erforderlich sind. Außerdem bleibt das gesamte Projekt in einer einzigen Datei enthalten.
Jetzt können wir mit dem Schreiben von Code beginnen. Öffnen Sie MetaEditor 5 und erstellen Sie eine neue Expert Advisor-Datei. Nennen Sie es KagiTrader.mq5. Die Datei enthält die üblichen Event-Handler, von denen jeder EA abhängt. Dazu gehören die Initialisierung, die Deinitialisierung, der Tick Handler und der Trade Transaction Handler. Sie bilden die Struktur, auf der unsere Kagi-Chart-Logik läuft. Nachfolgend finden Sie das Minimalgerüst, auf dem wir aufbauen werden.
//+------------------------------------------------------------------+ //| KagiTrader.mq5 | //| Copyright 2025, MetaQuotes Ltd. Developer is Chacha Ian | //| https://www.mql5.com/en/users/chachaian | //+------------------------------------------------------------------+ #property copyright "Copyright 2025, MetaQuotes Ltd. Developer is Chacha Ian" #property link "https://www.mql5.com/en/users/chachaian" #property version "1.00" //+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit(){ return INIT_SUCCEEDED; } //+------------------------------------------------------------------+ //| Expert deinitialization function | //+------------------------------------------------------------------+ void OnDeinit(const int reason){ } //+------------------------------------------------------------------+ //| Expert tick function | //+------------------------------------------------------------------+ void OnTick(){ } //+------------------------------------------------------------------+ //| TradeTransaction function | //+------------------------------------------------------------------+ void OnTradeTransaction(const MqlTradeTransaction& trans, const MqlTradeRequest& request, const MqlTradeResult& result) { } //+------------------------------------------------------------------+
Die Initialisierungsfunktion wird beim Start des EA verwendet. Hier werden wir unsere Einrichtungslogik unterbringen. Die Deinitialisierungsfunktion wird aufgerufen, wenn der EA entfernt wird. Sie wird uns später helfen, grafische Objekte aus dem Chart zu entfernen. In der Ereignisbehandlung der Ticks wird der Kagi-Chart aktualisiert, wenn sich die Marktpreise ändern. Die Funktion der Handelstransaktion ist für den ersten Teil nicht erforderlich, sie bleibt aber der Vollständigkeit halber in der Struktur, da sie im zweiten Teil verwendet wird, wenn wir den automatisierten Handel hinzufügen.
Jetzt, da die Projektstruktur feststeht, können wir die Eingabeparameter definieren. Mit diesen Einstellungen kann der Leser steuern, wie sich das Kagi-Chart verhält. Sie erleichtern auch die Anpassung des Charts ohne Bearbeitung des Codes.
Bevor wir mit der Definition der Eingabeparameter für unseren Kagi-Indikator fortfahren, müssen wir zunächst eine nutzerdefinierte Enumeration erstellen, die angibt, wie die Umkehrbedingungen interpretiert werden. Kagi-Charts ändern ihre Richtung nur, wenn sich der Preis um einen signifikanten Wert umkehrt, und Händler können unterschiedliche Methoden bevorzugen, um zu definieren, was als „signifikant“ gilt.
Um diese Flexibilität zu gewährleisten, führen wir folgende Enumeration ein:
//+------------------------------------------------------------------+ //| Custom Enumerations | //+------------------------------------------------------------------+ enum ENUM_KAGI_REVERSAL_TYPE { REVERSAL_BY_PERCENTAGE, REVERSAL_BY_PRICE_STEP };
Durch diese Enumeration verfügt unser Indikator über zwei Möglichkeiten zur Bestimmung von Umkehrungen:
- REVERSAL_BY_PERCENTAGE
Die Kagi-Linie kehrt sich um, wenn sich der Preis um einen bestimmten Prozentsatz des aktuellen Preises entgegen der aktuellen Richtung bewegt. Diese Option passt sich an die Marktvolatilität an, da die Umkehrschwelle automatisch mit dem Preis steigt.
- REVERSAL_BY_PRICE_STEP
Hier kommt es zu einer Umkehrung, wenn sich der Kurs um einen festgelegten Umkehrwert bewegt. Dadurch wird das Verhalten einheitlicher und vorhersehbarer, unabhängig vom aktuellen Preisniveau.
Nachfolgend finden Sie den Abschnitt über die Eingabeparameter, gefolgt von klaren Erläuterungen.
//+------------------------------------------------------------------+ //| User input variables | //+------------------------------------------------------------------+ input group "Information" input ENUM_TIMEFRAMES kagiTimeframe = PERIOD_M10; input ENUM_KAGI_REVERSAL_TYPE reversalType = REVERSAL_BY_PERCENTAGE; input double reversalValue = 4.0; input color yangLineColor = C'38,166,154'; input color yinLineColor = C'239,83,80'; input bool overlayKagi = true;
Nachstehend finden Sie eine Erläuterung der einzelnen Eingabeparameter:
- kagiTimeframe
Legt den Zeitrahmen fest, der zur Berechnung des Kagi-Charts verwendet wird. Der EA liest Kursdaten aus diesem Zeitrahmen, unabhängig davon, welchen Chart der Nutzer öffnet.
- reversalType
Steuert, wie der Umkehrwert zu interpretieren ist. Der Händler kann einen festen Wert oder einen prozentualen Wert wählen. Dies ermöglicht Flexibilität bei unterschiedlichen Marktbedingungen.
- reversalValue
Liefert den Umkehrwert. Je nach gewählter Stornoart kann dies eine statische Zahl oder ein Prozentsatz sein.
- yangLineColor
Definiert die Farbe, die für die aufwärts gerichteten oder steigende Kagi-Segmente verwendet wird. Diese Segmente stellen Yang-Linien dar.
- yinLineColor
Legt die Farbe fest, die für die abwärts gerichteten oder fallende Kagi-Segmente verwendet wird. Diese Segmente stellen Yin-Linien dar.
- overlayKagi
Ermöglicht es dem Leser, zu entscheiden, ob das Kagi-Chart angezeigt werden soll. Wenn Sie diese Einstellung deaktivieren, wird der EA die Kagi-Segmente nicht mehr zeichnen. Dies ist nützlich, um das Verhalten von Charts zu vergleichen oder die Leistung zu testen.
Später, wenn das Projekt wächst, können wir weitere Inputs hinzufügen. Dazu können Liniendicke, Zeichnungsversatz oder Optionen zur Steuerung der Reinigung von Objekten gehören. Im Moment reichen die oben genannten Parameter aus, um mit der Erstellung der ersten Arbeitsversion unseres Kagi-Charts zu beginnen.
Statusvariablen und interne Datenstruktur des Kagi-Charts
Bevor wir mit der Implementierung der Kagi-Konstruktionslogik beginnen, benötigen wir eine zuverlässige Methode zum Speichern und Aktualisieren des internen Zustands unseres Charts. Ein Kagi-Chart ist von Natur aus dynamisch – es reagiert ständig auf Preisänderungen, Umkehrungen, Trendübergänge und Verschiebungen zwischen Yin- und Yang-Linien. Um all diese beweglichen Teile zu verwalten, werden wir eine nutzerdefinierte Struktur definieren, die alle wichtigen Variablen enthält, die wir bei der Berechnung und beim Zeichnen benötigen.
Unmittelbar nach unseren Eingabeparametern führen wir die folgende Struktur ein:
//+------------------------------------------------------------------+ //| Global Variables | //+------------------------------------------------------------------+ struct MqlKagiData{ double closePrice[]; datetime openTime[]; double referencePrice; datetime referenceTime; double localMaximum; double localMinimum; bool isUptrend; bool isDowntrend; bool isYang; bool isYin; int lookBackBars; datetime lastBarOpenTime; }; //--- Initialize the state container MqlKagiData kagiData;
Diese Struktur wird als interner „Speicher“ unseres Kagi-Indikators dienen. Im Folgenden wird jedes Feld erläutert und erklärt, warum es wichtig ist:
- closePrice[]
Ein Kagi-Chart wird aus den Schlusskursen erstellt, daher speichert dieses dynamische Array die historischen Schlusskurse, die aus dem Chart extrahiert werden. Diese Werte bilden die Grundlage für jedes Kagi-Segment.
- openTime[]
Um mit Objekten wie OBJ_TREND Linien auf dem Chart zu zeichnen, benötigen wir sowohl Preis- als auch Zeitkoordinaten. In diesem Array werden die entsprechenden Eröffnungszeiten für alle historischen Balken gespeichert, damit jedes Kagi-Segment korrekt im Chart verankert werden kann.
- referencePrice
Die Variable enthält den letzten signifikanten Preis, der zur Erstellung der aktuellen Kagi-Linie verwendet wurde. Neue Überprüfungen werden nur bei jedem Balkenschluss durchgeführt, und diese Schlusskurse bestimmen, ob die Linie fortgesetzt wird oder eine Umkehrung erfolgt.
- referenceTime
Ähnlich wie referencePrice speichert diese Variable den Zeitstempel, der mit der letzten Aktualisierung in der Kagi-Struktur verbunden ist. Sie hilft uns, die Änderungen der Kagi-Linie zum richtigen Zeitpunkt auf dem Chart zu positionieren.
- localMaximum
- localMinimum
Hier wird die jüngsteTaille gespeichert – der letzte lokale Tiefststand, der erreicht wurde, bevor der Kurs wieder nach oben dreht. Sie ist wichtig, um Übergänge zurück zu Yin-Linien zu erkennen, wenn der Kurs unter frühere Tiefststände fällt.
- isUptrend
Ein einfaches boolesches Flag, das uns sagt, ob sich die aktuelle Kagi-Linie aufwärts bewegt. Dies wird uns helfen zu entscheiden, ob die eingehenden Preise die aktuelle Linie verlängern oder eine Umkehr auslösen.
- isDowntrend
Ähnlich wie isUptrend bestätigt dieses Flag, ob sich das letzte Kagi-Segment abwärts bewegt. Durch die Beibehaltung beider Flags bleibt die Logik bei Aktualisierungen sehr klar und eindeutig.
- isYang
Yang-Linien stehen für Stärke oder Nachfrage. Dieses Flag wird wahr, wenn der Kurs über die letzte Schulter steigt. Sie hilft uns zu bestimmen, wann wir dicke Linien auf dem Chart zeichnen müssen.
- isYin
- lookBackBars
- lastBarOpenTime
Diese Variable speichert die Öffnungszeit des zuletzt verarbeiteten Balkens. Sie wird uns später helfen, zu erkennen, wann sich ein neuer Balken bildet, damit der Indikator nur bei Bedarf aktualisiert wird.
Mit dieser definierten Struktur und der Initialisierung unserer kagiData-Instanz haben wir nun einen gut organisierten Container für die gesamte interne Zustandsverwaltung. Dies ermöglicht es dem Rest unserer Implementierung, Kagi-Segmente auf eine saubere und konsistente Weise zu lesen, zu aktualisieren und zu zeichnen.
Initialisierung des Kagi-Status und Laden historischer Daten für die Kagi-Verarbeitung
Wenn der Expert Advisor startet, müssen wir seinen internen Status vorbereiten und die historischen Daten laden, die die Grundlage für den Kagi-Chart bilden. Diese Initialisierung wird einmal ausgeführt und setzt alles, was der EA braucht, bevor er mit dem Zeichnen auf dem Chart beginnt.
Zunächst setzen wir einfache boolesche Flags und grundlegende Parameter.
//+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit(){ //--- Initialize global variables kagiData.isUptrend = false; kagiData.isDowntrend = false; kagiData.isYang = false; kagiData.isYin = false; kagiData.lookBackBars = 100; kagiData.lastBarOpenTime = 0; return INIT_SUCCEEDED; }
Wir markieren das Chart als noch nicht nach oben oder unten gerichtet. Wir setzen auch beide Yin- und Yang-Flags auf false, da keine Schulter oder Taille bestätigt wurde. Der Wert von lookBackBars begrenzt, wie viele der letzten Balken zum Zeichnen sichtbar bleiben. Schließlich setzen wir lastBarOpenTime auf null, damit der erste verarbeitete Balken immer als neu erkannt wird.
Als Nächstes bereiten wir die Arrays vor, die die Schlusskurse und Eröffnungszeiten enthalten werden.
//+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit(){ ... //--- Array Set As Series ArraySetAsSeries(kagiData.closePrice, true); ArraySetAsSeries(kagiData.openTime, true); return INIT_SUCCEEDED; }
Durch den Aufruf der Funktion ArraySetAsSeries mit true werden die Arrays mit dem neuesten Element bei Index Null geordnet. Diese Reihenfolge entspricht der Art und Weise, wie MetaTrader 5 kopierte Historien zurückgibt, und vereinfacht die Indexierung bei Aktualisierungen. Die Verwendung von Serien-Arrays beschleunigt auch das Kopieren und den Zugriff, wenn wir die neuesten Balken verarbeiten.
Vor dem Kopieren von Daten prüfen wir, ob eine angemessene Menge an Daten vorhanden ist.
//+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit(){ ... //--- Get the total number of historical bars on both the lower and the higher timeframes int totalNumberOfHistoricalBarsOnKagiTimeframe = Bars(_Symbol, kagiTimeframe); if(totalNumberOfHistoricalBarsOnKagiTimeframe < 10){ return INIT_FAILED; } return INIT_SUCCEEDED; }
Die Funktion Bars gibt zurück, wie viele Kerzen für den gewählten Zeitrahmen und das Symbol geladen sind. Wenn zu wenige Balken vorhanden sind, wird die Initialisierung abgebrochen und ein Fehlerstatus zurückgegeben. Dies verhindert spätere Fehler und vermeidet, dass der EA mit unzureichenden Daten läuft.
Wenn die Geschichte ausreicht, kopieren wir als Nächstes die Schlusskurse.
//+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit(){ ... //--- Get the actual close and opening times int closePricesCount = CopyClose(_Symbol, kagiTimeframe, 0, totalNumberOfHistoricalBarsOnKagiTimeframe, kagiData.closePrice); if(closePricesCount == -1){ Print("Error while copying historical close prices ", GetLastError()); return INIT_FAILED; } return INIT_SUCCEEDED; }
CopyClose füllt das closePrice-Array mit den Closes der Balken aus dem ausgewählten Zeitrahmen. Wir überprüfen die zurückgegebene Anzahl. Wenn die Funktion fehlschlägt, geben wir den Fehler aus und brechen ab. Dieser defensive Schritt stellt sicher, dass der EA nicht mit unvollständigen oder fehlenden Preisdaten fortfährt.
Wir kopieren dann die offenen Zeiten auf die gleiche Weise.
//+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit(){ ... //--- Get the actual close and opening times ... int openTimesCount = CopyTime(_Symbol, kagiTimeframe, 0, totalNumberOfHistoricalBarsOnKagiTimeframe, kagiData.openTime); if(openTimesCount == -1){ Print("Error while copying historical open times ", GetLastError()); return INIT_FAILED; } return INIT_SUCCEEDED; }
Diese Zeitstempel sind erforderlich, weil grafische Objekte wie OBJ_TREND sowohl Preis- als auch Zeitkoordinaten benötigen. Auch hier prüfen wir den Rückgabewert und brechen ab, wenn das Kopieren fehlschlägt.
Zu diesem Zeitpunkt haben wir zwei als Zeitreihen ausgerichtete Arrays. Jedes Element mit dem Index Null ist der jüngste Balken. Der Referenzpreis und die Referenzzeit können nun bei Bedarf aus diesen Arrays initialisiert werden. Wir verwenden diese Werte, um die ersten Kagi-Segmente aus der Historie bis zum letzten abgeschlossenen Balken zu erstellen.
Konfigurieren der Chart-Darstellung
Bevor wir mit der Erstellung des Kagi-Charts beginnen, müssen wir das Chart so vorbereiten, dass unsere Kagi-Linien klar zu erkennen sind. Die Standarddarstellung eines MetaTrader 5-Charts ist nicht ideal für das Zeichnen nutzerdefinierter grafischer Objekte. Es hat oft einen farbigen Hintergrund, ein sichtbares Raster und Kerzen, die unsere nutzerdefinierte Kagi-Linie verdecken oder verzerren können. Aus diesem Grund erstellen wir eine separate Funktion, die das Chart an ein sauberes und einfaches Layout anpasst.
//+------------------------------------------------------------------+ //| This function configures the chart's appearance. | //+------------------------------------------------------------------+ bool ConfigureChartAppearance() { if(!ChartSetInteger(0, CHART_COLOR_BACKGROUND, clrWhite)){ Print("Error while setting chart background, ", GetLastError()); return false; } if(!ChartSetInteger(0, CHART_SHOW_GRID, false)){ Print("Error while setting chart grid, ", GetLastError()); return false; } if(!ChartSetInteger(0, CHART_MODE, CHART_LINE)){ Print("Error while setting chart mode, ", GetLastError()); return false; } if(!ChartSetInteger(0, CHART_COLOR_FOREGROUND, clrBlack)){ Print("Error while setting chart foreground, ", GetLastError()); return false; } return true; }
Die Funktion ConfigureChartAppearance legt diese visuellen Eigenschaften fest. Zunächst wird die Hintergrundfarbe des Charts auf Weiß geändert. Dadurch entsteht ein sauberer Untergrund, der die Lesbarkeit der farbigen Kagi-Linien deutlich verbessert. Wenn diese Aktion fehlschlägt, gibt die Funktion eine Fehlermeldung aus und gibt false zurück.
Anschließend wird das Chartraster deaktiviert. Die Rasterlinien können die Klarheit der Kagi-Struktur beeinträchtigen, sodass ihre Entfernung dazu beiträgt, ein glattes Aussehen zu erhalten. Auch hier gilt: Wenn die Plattform diese Einstellung nicht übernehmen kann, bricht die Funktion ab und gibt false zurück.
Die Funktion setzt dann den Chart-Modus auf den Linien-Modus. Da wir unseren Kagi-Chart mit Hilfe von OBJ_TREND-Objekten zeichnen werden, bietet ein Linien Chart einen klareren Hintergrund als Kerzen oder Balken. Nur unsere eigenen Objekte werden auffallen. Wenn der Chart-Modus nicht angewendet werden kann, gibt die Funktion false zurück.
Schließlich setzt die Funktion die Vordergrundfarbe auf Schwarz. Die Vordergrundfarbe steuert die Achsenbeschriftungen und die grundlegenden Chart-Elemente. Ein schwarzer Vordergrund auf weißem Hintergrund sorgt für gute Lesbarkeit, ohne die Sichtbarkeit der Kagi-Linien zu beeinträchtigen. Wenn diese Aktion fehlschlägt, hält die Funktion an und meldet den Fehler.
Wenn alle Einstellungen erfolgreich sind, gibt die Funktion true zurück. Dies teilt dem EA mit, dass das Chart nun zum Zeichnen bereit ist. Wir müssen diese Funktion aufrufen, bevor wir eine Kagi-Konstruktion durchführen. Dies geschieht am besten gleich zu Beginn der Funktion OnInit. Der Aufruf sieht folgendermaßen aus:
//+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit(){ //--- To configure the chart's appearance if(!ConfigureChartAppearance()){ Print("Error while configuring chart appearance", GetLastError()); return INIT_FAILED; } ... return INIT_SUCCEEDED; }
Auf diese Weise wird das Chart jedes Mal, wenn der EA angehängt wird, richtig vorbereitet. Dadurch wird sichergestellt, dass das Kagi-Chart von Anfang an korrekt angezeigt wird, und es werden Probleme vermieden, bei denen die Zeichenobjekte schwer zu erkennen sind.
Konstruktion des historischen Kagi-Charts
Bevor wir mit der Erstellung des historischen Kagi-Charts beginnen, benötigen wir eine Reihe von Hilfsfunktionen, die Liniensegmente in das Chart zeichnen können. Ein Kagi-Chart ändert seine Form mehrmals, wenn sich der Preis bewegt. Sie biegt sich an Wendepunkten und verdickt sich in Aufwärtsphasen. Aus diesem Grund zeichnen wir jedes Segment mit einem separaten OBJ_TREND-Objekt.
Die folgenden Funktionen übernehmen die gesamte Zeichenarbeit für uns. Sie halten den Code sauber und leicht nachvollziehbar. Alle vier Funktionen erstellen eine Linie auf dem Chart unter Verwendung der angegebenen Zeit- und Preiskoordinaten. Jede Funktion legt auch die Farbe und Breite der Linie fest und stellt sicher, dass das Objekt nicht versehentlich ausgewählt oder verschoben werden kann. Sie alle geben true zurück, wenn die Linie erfolgreich gezeichnet wurde, und false, wenn ein Fehler auftritt.
//+------------------------------------------------------------------+ //| This function is used to draw a new yang line | //+------------------------------------------------------------------+ bool DrawYangLine(string line_name, datetime time1, double price1, datetime time2, double price2, color line_color = clrGreen, int line_width=5) { ResetLastError(); //--- Create a Yang Line if(!ObjectCreate(0, line_name, OBJ_TREND, 0, time1, price1, time2, price2)){ Print("Error while creating a Yin line: ", GetLastError()); return false; } //--- Set some vital object properties ObjectSetInteger(0, line_name, OBJPROP_COLOR, line_color); ObjectSetInteger(0, line_name, OBJPROP_WIDTH, line_width); ObjectSetInteger(0, line_name, OBJPROP_SELECTED, false); ObjectSetInteger(0, line_name, OBJPROP_SELECTABLE, false); ChartRedraw (0); return true; } //+------------------------------------------------------------------+ //| This function is used to draw a new yin line | //+------------------------------------------------------------------+ bool DrawYinLine(string line_name, datetime time1, double price1, datetime time2, double price2, color line_color = clrRed , int line_width=3) { ResetLastError(); //--- Create a Yin Line if(!ObjectCreate(0, line_name, OBJ_TREND, 0, time1, price1, time2, price2)){ Print("Error while creating a Yin line: ", GetLastError()); return false; } //--- Set some vital object properties ObjectSetInteger(0, line_name, OBJPROP_COLOR, line_color); ObjectSetInteger(0, line_name, OBJPROP_WIDTH, line_width); ObjectSetInteger(0, line_name, OBJPROP_SELECTED, false); ObjectSetInteger(0, line_name, OBJPROP_SELECTABLE, false); ChartRedraw (0); return true; } //+------------------------------------------------------------------+ //| This function is used to draw a bend top line | //+------------------------------------------------------------------+ bool DrawBendTop(string line_name, datetime time1, double price1, datetime time2, double price2, color line_color = clrGreen, int line_width=5) { ResetLastError(); //--- Create a Bend Top Line if(!ObjectCreate(0, line_name, OBJ_TREND, 0, time1, price1, time2, price2)){ Print("Error while creating a Bend Top line: ", GetLastError()); return false; } //--- Set some vital object properties ObjectSetInteger(0, line_name, OBJPROP_COLOR, line_color); ObjectSetInteger(0, line_name, OBJPROP_WIDTH, line_width); ObjectSetInteger(0, line_name, OBJPROP_SELECTED, false); ObjectSetInteger(0, line_name, OBJPROP_SELECTABLE, false); ChartRedraw (0); return true; } //+------------------------------------------------------------------+ //| This function is used to draw a bend bottom line | //+------------------------------------------------------------------+ bool DrawBendBottom(string line_name, datetime time1, double price1, datetime time2, double price2, color line_color = clrRed , int line_width=3) { ResetLastError(); //--- Create a Bend Bottom Line if(!ObjectCreate(0, line_name, OBJ_TREND, 0, time1, price1, time2, price2)){ Print("Error while creating a Bend Bottom line: ", GetLastError()); return false; } //-- Set some vital object properties ObjectSetInteger(0, line_name, OBJPROP_COLOR, line_color); ObjectSetInteger(0, line_name, OBJPROP_WIDTH, line_width); ObjectSetInteger(0, line_name, OBJPROP_SELECTED, false); ObjectSetInteger(0, line_name, OBJPROP_SELECTABLE, false); ChartRedraw (0); return true; }
- DrawYangLine
Diese Funktion zeichnet eine Yang-Linie. Eine Yang-Linie steht für Stärke. Sie wird bei der Aufwärtsbewegung verwendet. Die Funktion erzeugt eine dickere Trendlinie, die in der Regel in der vom Nutzer gewählten Yang-Farbe eingefärbt ist. Sie benötigt den Namen der Linie, zwei Zeitpunkte, zwei Preispunkte und optionale Farb- und Breiteneinstellungen. Wenn die Linie erstellt wird, wird das Chart neu gezeichnet, sodass die Aktualisierung sofort erscheint.
- DrawYinLine
Diese Funktion zeichnet eine Yin-Linie. Eine Yin-Linie steht für Schwäche. Sie wird bei der Abwärtsbewegung eingesetzt. Sie funktioniert auf die gleiche Weise wie die Funktion DrawYangLine. Der Hauptunterschied besteht in der Standardfarbe und -linienbreite. Yin-Linien sind in der Regel dünner. Dadurch wird der visuelle Stil eines traditionellen Kagi-Charts beibehalten.
- DrawBendTop
Diese Funktion zeichnet eine Biegung an der Spitze einer Kagi-Struktur. Eine Kurve entsteht, wenn der Kurs einen neuen Höchststand erreicht und dann nach unten abdreht. Die gebogene obere Linie bildet den oberen Teil dieses Wendepunkts. Die Funktion erstellt eine Trendlinie unter Verwendung der angegebenen Koordinaten und wendet den richtigen Stil für die Biegung an. Die Parameter und die interne Logik sind dieselben wie bei den vorherigen Funktionen.
- DrawBendBottom
Diese Funktion zeichnet eine Biegung am unteren Rand einer Kagi-Struktur. Eine Bodenkurve entsteht, wenn der Kurs einen neuen Tiefststand erreicht und sich nach oben umkehrt. Die Funktion verhält sich wie die anderen und erzeugt ein sauberes Liniensegment, das diesen Wendepunkt markiert.
Da alle vier Funktionen nach demselben Muster ablaufen, ist der Code einfach und konsistent, wenn sie in der Konstruktionslogik verwendet werden. Jedes Segment des Kagi-Charts wird mit der am besten geeigneten Funktion gezeichnet, je nach Trendrichtung und Wendepunkttyp.
Jedes Chartobjekt in MQL5 muss einen eigenen, eindeutigen Namen haben. Keine zwei Objekte können denselben Namen haben. Ein Kagi-Chart enthält viele kleine Liniensegmente. Wenn das Chart wächst, kann die Anzahl der Segmente sehr groß werden. Es ist nicht praktikabel, alle Objektnamen in einem Array zu speichern und sie manuell zu überprüfen. Um dieses Problem zu lösen, verwenden wir eine Hilfsfunktion, die jedes Mal, wenn wir ein neues Segment zeichnen müssen, einen neuen eindeutigen Namen erzeugt.
Die Funktion GenerateUniqueName erstellt einen Namen aus einem Präfix und einer Zufallszahl. Dadurch erhöht sich die Wahrscheinlichkeit, dass sich jeder Name von allen vorherigen unterscheidet. Die Funktion führt eine Schleife durch, bis sie einen Namen liefert, der noch nicht in der Tabelle vorhanden ist.
//+------------------------------------------------------------------+ //| Function to generate a unique object name with a given prefix | //+------------------------------------------------------------------+ string GenerateUniqueName(string prefix) { int attempt = 0; string uniqueName; while(true) { uniqueName = prefix + IntegerToString(MathRand() + attempt); if(ObjectFind(0, uniqueName) < 0) break; attempt++; } return uniqueName; }
Die Funktion funktioniert wie folgt:
- Er beginnt mit einem leeren Zähler, der auf Null gesetzt wird.
- Innerhalb der Schleife wird ein Kandidatenname erstellt. Der Name besteht aus dem Präfix plus einer Zufallszahl plus dem Versuchszähler.
- Anschließend wird geprüft, ob ein Objekt mit diesem Namen bereits im Chart vorhanden ist.
- Wenn der Name frei ist, endet die Schleife und die Funktion gibt den neuen Namen zurück.
- Ist der Name vergeben, erhöht sich der Versuchszähler und die Schleife wird fortgesetzt, bis ein freier Name gefunden wird.
Dieser einfache Ansatz garantiert, dass jedes neue Kagi-Segment, das wir erstellen, einen eindeutigen Bezeichner hat. Außerdem bleibt die Zeichenlogik sauber, da wir die Objektnamen nicht mehr manuell verfolgen müssen.
Da unsere Funktion GenerateUniqueName ein String-Präfix benötigt, brauchen wir einen festen und zuverlässigen Wert, den wir jedes Mal verwenden können, wenn wir ein neues Kagi-Segment erstellen. Um dies zu erreichen, definieren wir das folgende Makro direkt unter den Eigenschaftsanweisungen:
//+------------------------------------------------------------------+ //| Macros | //+------------------------------------------------------------------+ #define TRENDLINE "standardKagi"
Das Makro TRENDLINE definiert ein festes Präfix, das wir bei der Generierung von Namen für neue Kagi-Segmente verwenden. Damit haben wir einen einheitlichen Ausgangspunkt für alle Objektnamen und vermeiden Fehler, die durch die wiederholte Eingabe der gleichen Zeichenfolge entstehen können. Durch die Verwendung eines einzigen Präfixes im gesamten Code wird unser Benennungssystem sauberer und zuverlässiger, und es funktioniert reibungslos mit der Funktion GenerateUniqueName, die den endgültigen eindeutigen Namen auf der Grundlage dieses Präfixes erstellt.
Da unsere grundlegenden Komponenten nun vorhanden sind, können wir mit der Erstellung der ersten Rendering-Funktion fortfahren – der Funktion, die für den Aufbau des gesamten Kagi-Charts während der Initialisierung verantwortlich ist. Diese Funktion verarbeitet alle historischen Balken, wendet schrittweise unsere Kagi-Logik an und zeichnet den ersten Satz von Linien in den Chart. Nach Abschluss dieser Phase haben wir eine vollständig ausgearbeitete Kagi-Struktur, die als Grundlage für die Echtzeitaktualisierung dient. Wir werden unsere Funktion ConstructKagiOnInitialization nennen.
//+------------------------------------------------------------------+ //| This function is used to construct Kagi on Initialization | //+------------------------------------------------------------------+ void ConstructKagiOnInitialization() { }
Wir beginnen mit der Festlegung des anfänglichen Referenzpreises und dem Zeitstempel anhand des ältesten verfügbaren Balkens.
//+------------------------------------------------------------------+ //| This function is used to construct Kagi on Initialization | //+------------------------------------------------------------------+ void ConstructKagiOnInitialization() { //--- The very first historical bar serves as the initial reference point kagiData.referencePrice = kagiData.closePrice[ArraySize(kagiData.closePrice) - 1]; kagiData.referenceTime = kagiData.openTime [ArraySize(kagiData.openTime) - 1]; }
Damit wird der ganz linke Balken zum Ausgangspunkt für alle künftigen Vergleiche. Von dieser Referenz aus wird die Funktion durch die Geschichte gehen und die Kagi-Struktur aufbauen.
Die Hauptarbeit findet in einer Schleife statt, die von älteren Balken zum jüngsten abgeschlossenen Balken iteriert.
//+------------------------------------------------------------------+ //| This function is used to construct Kagi on Initialization | //+------------------------------------------------------------------+ void ConstructKagiOnInitialization() { ... for(int i = ArraySize(kagiData.closePrice) - 2; i > 0; i--){ } }
Die Schleife überspringt den allerletzten Balken bei Index Null, da wir nur die Historie bis zum letzten geschlossenen Balken aufbauen. Jeder Durchlauf verarbeitet einen Balkenschluss und seine Eröffnungszeit und behandelt diesen Schlusskurs als Kandidaten für eine Fortsetzung oder Umkehrung.
//+------------------------------------------------------------------+ //| This function is used to construct Kagi on Initialization | //+------------------------------------------------------------------+ void ConstructKagiOnInitialization() { ... for(int i = ArraySize(kagiData.closePrice) - 2; i > 0; i--){ //--- During every iteration, we record the current bar’s close price and open time. double currentClosePrice = kagiData.closePrice[i]; datetime currentOpenTime = kagiData.openTime[i]; } }
Für jeden Balken wird der Umkehrwert berechnet.
//+------------------------------------------------------------------+ //| This function is used to construct Kagi on Initialization | //+------------------------------------------------------------------+ void ConstructKagiOnInitialization() { ... for(int i = ArraySize(kagiData.closePrice) - 2; i > 0; i--){ ... double reversalAmount = 0.0; if(reversalType == 0){ reversalAmount = NormalizeDouble((reversalValue / 100.0) * kagiData.referencePrice, Digits()); } if(reversalType == 1){ reversalAmount = NormalizeDouble(reversalValue, Digits()); } } }
Wenn der Nutzer den prozentualen Modus gewählt hat, entspricht die Umkehrung dem angegebenen Prozentsatz des aktuellen Referenzpreises. Wenn der Nutzer den Preisschrittmodus gewählt hat, ist der Umkehrwert der gelieferte Festwert. Beide Werte sind auf Symbolgenauigkeit normiert.
Die ersten großen Entscheidungstests für einen ersten Trend beginnen, wenn der Kagi-Zustand noch neutral ist.
//+------------------------------------------------------------------+ //| This function is used to construct Kagi on Initialization | //+------------------------------------------------------------------+ void ConstructKagiOnInitialization() { ... for(int i = ArraySize(kagiData.closePrice) - 2; i > 0; i--){ ... //--- Handle the initial execution when the EA is first attached to the chart. if(!kagiData.isUptrend && !kagiData.isDowntrend && !kagiData.isYang && !kagiData.isYin && currentClosePrice >= (kagiData.referencePrice + reversalAmount)){ kagiData.isUptrend = true; kagiData.isYang = true; if(overlayKagi && i < kagiData.lookBackBars){ DrawYangLine(GenerateUniqueName(TRENDLINE), kagiData.referenceTime, kagiData.referencePrice, kagiData.referenceTime, currentClosePrice, yangLineColor); } kagiData.localMinimum = kagiData.referencePrice; kagiData.localMaximum = currentClosePrice; kagiData.referencePrice = currentClosePrice; } if(!kagiData.isUptrend && !kagiData.isDowntrend && !kagiData.isYang && !kagiData.isYin && currentClosePrice <= (kagiData.referencePrice - reversalAmount)){ kagiData.isDowntrend = true; kagiData.isYin = true; if(overlayKagi && i < kagiData.lookBackBars){ DrawYinLine(GenerateUniqueName(TRENDLINE), kagiData.referenceTime, kagiData.referencePrice, kagiData.referenceTime, currentClosePrice, yinLineColor); } kagiData.localMinimum = currentClosePrice; kagiData.localMaximum = kagiData.referencePrice; kagiData.referencePrice = currentClosePrice; } } }
Wenn keine Richtungs- oder Stil-Flags gesetzt sind und der Schlusskurs den Referenzwert um den Umkehrwert nach oben überschreitet, startet die Funktion einen Aufwärtstrend und markiert die Linie als Yang. Wenn sich der Schlusskurs um den Umkehrwert nach unten bewegt, beginnt die Funktion einen Abwärtstrend und markiert die Linie als Yin.
Als Nächstes kommen die einfachen Fortsetzungskontrollen.
//+------------------------------------------------------------------+ //| This function is used to construct Kagi on Initialization | //+------------------------------------------------------------------+ void ConstructKagiOnInitialization() { ... for(int i = ArraySize(kagiData.closePrice) - 2; i > 0; i--){ ... //--- Handle a normal continuation if(kagiData.isUptrend && kagiData.isYang && currentClosePrice >= (kagiData.referencePrice + reversalAmount) && currentClosePrice > kagiData.localMaximum){ if(overlayKagi && i < kagiData.lookBackBars){ DrawYangLine(GenerateUniqueName(TRENDLINE), kagiData.referenceTime, kagiData.referencePrice, kagiData.referenceTime, currentClosePrice, yangLineColor); } kagiData.localMaximum = currentClosePrice; kagiData.referencePrice = currentClosePrice; } if(kagiData.isDowntrend && kagiData.isYin && currentClosePrice <= (kagiData.referencePrice - reversalAmount) && currentClosePrice < kagiData.localMinimum){ if(overlayKagi && i < kagiData.lookBackBars){ DrawYinLine(GenerateUniqueName(TRENDLINE), kagiData.referenceTime, kagiData.referencePrice, kagiData.referenceTime, currentClosePrice, yinLineColor); } kagiData.localMinimum = currentClosePrice; kagiData.referencePrice = currentClosePrice; } } }
Wenn der aktuelle Kagi-Zustand ein aufwärts gerichteter Yang ist, verlängert die Funktion die Yang-Linie nur, wenn der Schlusskurs den Referenzwert plus Umkehrung übersteigt und auch das letzte aufgezeichnete lokale Maximum übertrifft. Bei einem abwärts gerichteten Yin-Zustand verlängert die Funktion die Yin-Linie nur, wenn der Schlusskurs unter die Referenzminusumkehr fällt und auch das letzte aufgezeichnete lokale Minimum unterschreitet.
Die Funktion behandelt dann normale Umkehrungen, die innerhalb der vorherigen Extremwerte bleiben.
//+------------------------------------------------------------------+ //| This function is used to construct Kagi on Initialization | //+------------------------------------------------------------------+ void ConstructKagiOnInitialization() { ... for(int i = ArraySize(kagiData.closePrice) - 2; i > 0; i--){ ... //--- Handle a normal reversal if(kagiData.isUptrend && kagiData.isYang && currentClosePrice <= (kagiData.referencePrice - reversalAmount) && (currentClosePrice >= kagiData.localMinimum)){ if(overlayKagi && i < kagiData.lookBackBars){ DrawBendTop(GenerateUniqueName(TRENDLINE), kagiData.referenceTime, kagiData.referencePrice, currentOpenTime, kagiData.referencePrice, yangLineColor); DrawYangLine(GenerateUniqueName(TRENDLINE), currentOpenTime, kagiData.referencePrice, currentOpenTime, currentClosePrice, yangLineColor); } kagiData.localMaximum = kagiData.referencePrice; kagiData.referencePrice = currentClosePrice; kagiData.referenceTime = currentOpenTime; kagiData.isDowntrend = true; kagiData.isUptrend = false; } if(kagiData.isDowntrend && kagiData.isYin && currentClosePrice >= (kagiData.referencePrice + reversalAmount) && (currentClosePrice <= kagiData.localMaximum)){ if(overlayKagi && i < kagiData.lookBackBars){ DrawBendBottom(GenerateUniqueName(TRENDLINE), kagiData.referenceTime, kagiData.referencePrice, currentOpenTime, kagiData.referencePrice, yinLineColor); DrawYinLine(GenerateUniqueName(TRENDLINE), currentOpenTime, kagiData.referencePrice, currentOpenTime, currentClosePrice, yinLineColor); } kagiData.localMinimum = kagiData.referencePrice; kagiData.referencePrice = currentClosePrice; kagiData.referenceTime = currentOpenTime; kagiData.isDowntrend = false; kagiData.isUptrend = true; } } }
Wenn ein Aufwärtstrend die Umkehrbedingung erfüllt, aber über oder gleich dem lokalen Minimum bleibt, wird ein Knick nach oben gezogen und ein neues vertikales Segment beginnt nach unten. Ein symmetrischer Block behandelt den umgekehrten Fall, bei dem ein Abwärtstrend nach oben umschlägt, aber unter oder gleich dem lokalen Maximum bleibt.
Komplexe Umkehrungen treten auf, wenn die Umkehrung über das vorherige lokale Extrem hinausgeht.
//+------------------------------------------------------------------+ //| This function is used to construct Kagi on Initialization | //+------------------------------------------------------------------+ void ConstructKagiOnInitialization() { ... for(int i = ArraySize(kagiData.closePrice) - 2; i > 0; i--){ ... //--- Handle a complex reversal if(kagiData.isUptrend && kagiData.isYang && currentClosePrice <= (kagiData.referencePrice - reversalAmount) && (currentClosePrice < kagiData.localMinimum)){ if(overlayKagi && i < kagiData.lookBackBars){ DrawBendTop (GenerateUniqueName(TRENDLINE), kagiData.referenceTime, kagiData.referencePrice, currentOpenTime, kagiData.referencePrice, yangLineColor); DrawYangLine(GenerateUniqueName(TRENDLINE), currentOpenTime, kagiData.referencePrice, currentOpenTime, kagiData.localMinimum, yangLineColor); DrawYinLine (GenerateUniqueName(TRENDLINE), currentOpenTime, kagiData.localMinimum, currentOpenTime, currentClosePrice, yinLineColor); } kagiData.localMaximum = kagiData.referencePrice; kagiData.referencePrice = currentClosePrice; kagiData.referenceTime = currentOpenTime; kagiData.localMinimum = currentClosePrice; kagiData.isDowntrend = true; kagiData.isUptrend = false; kagiData.isYang = false; kagiData.isYin = true; } if(kagiData.isDowntrend && kagiData.isYin && currentClosePrice >= (kagiData.referencePrice + reversalAmount) && (currentClosePrice > kagiData.localMaximum)){ if(overlayKagi && i < kagiData.lookBackBars){ DrawBendBottom(GenerateUniqueName(TRENDLINE), kagiData.referenceTime, kagiData.referencePrice, currentOpenTime, kagiData.referencePrice, yinLineColor); DrawYinLine (GenerateUniqueName(TRENDLINE), currentOpenTime, kagiData.referencePrice, currentOpenTime, kagiData.localMaximum, yinLineColor); DrawYangLine (GenerateUniqueName(TRENDLINE), currentOpenTime, kagiData.localMaximum, currentOpenTime, currentClosePrice, yangLineColor); } kagiData.localMinimum = kagiData.referencePrice; kagiData.referencePrice = currentClosePrice; kagiData.referenceTime = currentOpenTime; kagiData.localMaximum = currentClosePrice; kagiData.isDowntrend = false; kagiData.isUptrend = true; kagiData.isYang = true; kagiData.isYin = false; } } }
In diesem Fall zeichnet die Funktion eine Kurve, dann ein kurzes Segment zum vorherigen Extremwert und schließlich das neue Segment, das über den Extremwert hinausgeht. Dadurch entsteht der Wechsel zwischen zwei Segmenten, der visuell eine Umkehrung darstellt, die die vorherige Schulter oder Taille durchbricht.
Nach Umkehrungen enthält der Code viele nuancierte Verzweigungen für Fortsetzungen und Gegenumkehrungen.
//+------------------------------------------------------------------+ //| This function is used to construct Kagi on Initialization | //+------------------------------------------------------------------+ void ConstructKagiOnInitialization() { ... for(int i = ArraySize(kagiData.closePrice) - 2; i > 0; i--){ ... //--- Handle a normal continuation after reversal if(kagiData.isDowntrend && kagiData.isYang && (currentClosePrice <= (kagiData.referencePrice - reversalAmount) && (currentClosePrice >= kagiData.localMinimum))){ if(overlayKagi && i < kagiData.lookBackBars){ DrawYangLine(GenerateUniqueName(TRENDLINE), kagiData.referenceTime, kagiData.referencePrice, kagiData.referenceTime, currentClosePrice, yangLineColor); } kagiData.referencePrice = currentClosePrice; } if(kagiData.isUptrend && kagiData.isYin && (currentClosePrice >= (kagiData.referencePrice + reversalAmount) && (currentClosePrice <= kagiData.localMaximum))){ if(overlayKagi && i < kagiData.lookBackBars){ DrawYinLine(GenerateUniqueName(TRENDLINE), kagiData.referenceTime, kagiData.referencePrice, kagiData.referenceTime, currentClosePrice, yinLineColor); } kagiData.referencePrice = currentClosePrice; } //--- Handle a complex continuation after reversal if(kagiData.isDowntrend && kagiData.isYang && (currentClosePrice <= (kagiData.referencePrice - reversalAmount) && (currentClosePrice < kagiData.localMinimum))){ if(overlayKagi && i < kagiData.lookBackBars){ DrawYangLine(GenerateUniqueName(TRENDLINE), kagiData.referenceTime, kagiData.referencePrice, kagiData.referenceTime, kagiData.localMinimum, yangLineColor); DrawYinLine (GenerateUniqueName(TRENDLINE), kagiData.referenceTime, kagiData.localMinimum, kagiData.referenceTime, currentClosePrice, yinLineColor); } kagiData.localMinimum = currentClosePrice; kagiData.referencePrice = currentClosePrice; kagiData.isYang = false; kagiData.isYin = true; } if(kagiData.isUptrend && kagiData.isYin && (currentClosePrice >= (kagiData.referencePrice + reversalAmount) && (currentClosePrice > kagiData.localMaximum))){ if(overlayKagi && i < kagiData.lookBackBars){ DrawYinLine (GenerateUniqueName(TRENDLINE), kagiData.referenceTime, kagiData.referencePrice, kagiData.referenceTime, kagiData.localMaximum, yinLineColor); DrawYangLine (GenerateUniqueName(TRENDLINE), kagiData.referenceTime, kagiData.localMaximum, kagiData.referenceTime, currentClosePrice, yangLineColor); } kagiData.localMaximum = currentClosePrice; kagiData.referencePrice = currentClosePrice; kagiData.isYang = true; kagiData.isYin = false; } //--- Handle a normal counter-reversal if(kagiData.isDowntrend && kagiData.isYang && currentClosePrice >= (kagiData.referencePrice + reversalAmount) && currentClosePrice <= kagiData.localMaximum){ if(overlayKagi && i < kagiData.lookBackBars ){ DrawBendTop(GenerateUniqueName(TRENDLINE), kagiData.referenceTime, kagiData.referencePrice, currentOpenTime, kagiData.referencePrice, yangLineColor); DrawYangLine(GenerateUniqueName(TRENDLINE), currentOpenTime, kagiData.referencePrice, currentOpenTime, currentClosePrice, yangLineColor); } kagiData.localMinimum = kagiData.referencePrice; kagiData.referencePrice = currentClosePrice; kagiData.referenceTime = currentOpenTime; kagiData.isUptrend = true; kagiData.isDowntrend = false; } if(kagiData.isUptrend && kagiData.isYin && currentClosePrice <= (kagiData.referencePrice - reversalAmount) && currentClosePrice >= kagiData.localMinimum){ if(overlayKagi && i < kagiData.lookBackBars){ DrawBendBottom(GenerateUniqueName(TRENDLINE), kagiData.referenceTime, kagiData.referencePrice, currentOpenTime, kagiData.referencePrice, yinLineColor); DrawYinLine(GenerateUniqueName(TRENDLINE), currentOpenTime, kagiData.referencePrice, currentOpenTime, currentClosePrice, yinLineColor); } kagiData.localMaximum = kagiData.referencePrice; kagiData.referencePrice = currentClosePrice; kagiData.referenceTime = currentOpenTime; kagiData.isUptrend = false; kagiData.isDowntrend = true; } //--- Handle a complex counter-reversal if(kagiData.isDowntrend && kagiData.isYang && currentClosePrice >= (kagiData.referencePrice + reversalAmount) && currentClosePrice > kagiData.localMaximum){ if(overlayKagi && i < kagiData.lookBackBars){ DrawBendTop(GenerateUniqueName(TRENDLINE), kagiData.referenceTime, kagiData.referencePrice, currentOpenTime, kagiData.referencePrice, yangLineColor); DrawYangLine(GenerateUniqueName(TRENDLINE), currentOpenTime, kagiData.referencePrice, currentOpenTime, currentClosePrice, yangLineColor); } kagiData.localMinimum = kagiData.referencePrice; kagiData.referencePrice = currentClosePrice; kagiData.referenceTime = currentOpenTime; kagiData.localMaximum = currentClosePrice; kagiData.isUptrend = true; kagiData.isDowntrend = false; } if(kagiData.isUptrend && kagiData.isYin && currentClosePrice <= (kagiData.referencePrice - reversalAmount) && currentClosePrice < kagiData.localMinimum){ if(overlayKagi && i < kagiData.lookBackBars){ DrawBendBottom(GenerateUniqueName(TRENDLINE), kagiData.referenceTime, kagiData.referencePrice, currentOpenTime, kagiData.referencePrice, yinLineColor); DrawYinLine(GenerateUniqueName(TRENDLINE), currentOpenTime, kagiData.referencePrice, currentOpenTime, currentClosePrice, yinLineColor); } kagiData.localMaximum = kagiData.referencePrice; kagiData.referencePrice = currentClosePrice; kagiData.referenceTime = currentOpenTime; kagiData.localMinimum = currentClosePrice; kagiData.isDowntrend = true; kagiData.isUptrend = false; } //--- Handle a normal continuation after counter-reversal if(kagiData.isUptrend && kagiData.isYang && currentClosePrice >= (kagiData.referencePrice + reversalAmount) && currentClosePrice <= kagiData.localMaximum){ if(overlayKagi && i < kagiData.lookBackBars){ DrawYangLine(GenerateUniqueName(TRENDLINE), kagiData.referenceTime, kagiData.referencePrice, kagiData.referenceTime, currentClosePrice, yangLineColor); } kagiData.referencePrice = currentClosePrice; } if(kagiData.isDowntrend && kagiData.isYin && currentClosePrice <= (kagiData.referencePrice - reversalAmount) && currentClosePrice >= kagiData.localMinimum){ if(overlayKagi && i < kagiData.lookBackBars){ DrawYinLine(GenerateUniqueName(TRENDLINE), kagiData.referenceTime, kagiData.referencePrice, kagiData.referenceTime, currentClosePrice, yinLineColor); } kagiData.referencePrice = currentClosePrice; } //--- Handle a complex continuation after counter-reversal if(kagiData.isUptrend && kagiData.isYang && currentClosePrice >= (kagiData.referencePrice + reversalAmount) && currentClosePrice > kagiData.localMaximum){ if(overlayKagi && i < kagiData.lookBackBars){ DrawYangLine(GenerateUniqueName(TRENDLINE), kagiData.referenceTime, kagiData.referencePrice, kagiData.referenceTime, currentClosePrice, yangLineColor); } kagiData.referencePrice = currentClosePrice; kagiData.localMaximum = currentClosePrice; } if(kagiData.isDowntrend && kagiData.isYin && currentClosePrice <= (kagiData.referencePrice - reversalAmount) && currentClosePrice < kagiData.localMinimum){ if(overlayKagi && i < kagiData.lookBackBars){ DrawYinLine(GenerateUniqueName(TRENDLINE), kagiData.referenceTime, kagiData.referencePrice, kagiData.referenceTime, currentClosePrice, yinLineColor); } kagiData.referencePrice = currentClosePrice; kagiData.localMinimum = currentClosePrice; } //--- Handle a weird scenario if(kagiData.isUptrend && kagiData.isYin && currentClosePrice >= (kagiData.referencePrice + reversalAmount) && currentClosePrice > kagiData.localMaximum){ if(overlayKagi && i < kagiData.lookBackBars){ DrawYinLine(GenerateUniqueName(TRENDLINE), kagiData.referenceTime, kagiData.referencePrice, kagiData.referenceTime, kagiData.localMaximum, yinLineColor); DrawYangLine(GenerateUniqueName(TRENDLINE), kagiData.referenceTime, kagiData.localMaximum, kagiData.referenceTime, currentClosePrice, yangLineColor); } kagiData.isYin = false; kagiData.isYang = true; kagiData.localMaximum = currentClosePrice; kagiData.referencePrice = currentClosePrice; } if(kagiData.isDowntrend && kagiData.isYang && currentClosePrice <= (kagiData.referencePrice - reversalAmount) && currentClosePrice < kagiData.localMinimum){ if(overlayKagi && i < kagiData.lookBackBars){ DrawYangLine(GenerateUniqueName(TRENDLINE), kagiData.referenceTime, kagiData.referencePrice, kagiData.referenceTime, kagiData.localMinimum, yangLineColor); DrawYinLine(GenerateUniqueName(TRENDLINE), kagiData.referenceTime, kagiData.localMinimum, kagiData.referenceTime, currentClosePrice, yinLineColor); } kagiData.isYang = false; kagiData.isYin = true; kagiData.localMinimum = currentClosePrice; kagiData.referencePrice = currentClosePrice; } } }
Diese Blöcke decken Fälle ab wie die Fortsetzung innerhalb früherer Extrema, die Fortsetzung, die über ein Extrem hinausgeht, Gegenumkehrungen, die die Richtung wieder umkehren, und komplexe Gegenumkehrungen, die auch neue lokale Extrema festlegen.
Jede Verzweigung aktualisiert dieselben Kernstatusvariablen, um den internen Speicher konsistent zu halten. Alle Zeichenaufrufe werden durch das Flag OverlayKagi und durch die Begrenzung durch lookBackBars geschützt. Das bedeutet, dass die Funktion die gesamte Kagi-Historie auf Korrektheit prüft, aber nur die jüngsten Segmente, die für die Anzeige benötigt werden, wiedergibt. Die Objektnamen stammen aus dem Generator für eindeutige Namen und jedes gezeichnete Segment verwendet die entsprechende Hilfsfunktion für Yang, Yin, Biegung oben oder Biegung unten.
Während der gesamten Schleife aktualisiert die Funktion diese Schlüsselfelder, wenn die Bedingungen erfüllt sind. referencePrice bewegt sich zum jüngsten Preis, der das letzte Segment definiert, referenceTime zeichnet den Zeitpunkt einer Umkehr auf, wenn sie eintritt, localMaximum und localMinimum zeichnen die letzte Schulter und Taille auf, und die booleschen Flags geben die aktuelle Richtung und den Stil wieder.
Diese Aktualisierungen stellen sicher, dass zur Laufzeit der Updater von einem korrekten Zustand aus fortfahren kann. Am Ende der Schleife enthält das interne kagiData einen vollständigen Kagi-Status, der die gesamte verarbeitete Geschichte bis zum letzten geschlossenen Balken darstellt. Das Chart zeigt die letzten lookBackBars der grafischen Objekte, wenn die Überlagerung aktiviert ist.
Von diesem vorbereiteten Zustand aus kann die Echtzeit-Aktualisierungsfunktion jeden neuen geschlossenen Balken mit dem richtigen Kontext aufgreifen und verarbeiten.
//+------------------------------------------------------------------+ //| This function is used to construct Kagi in real time | //+------------------------------------------------------------------+ void ConstructKagiInRealTime(double bidPr, double askPr){ if(IsNewBar(_Symbol, kagiTimeframe, kagiData.lastBarOpenTime)){ double currentClosePrice = iClose(_Symbol, kagiTimeframe, 1); datetime currentOpenTime = iTime( _Symbol, kagiTimeframe, 1); double reversalAmount = 0.0; if(reversalType == 0){ reversalAmount = NormalizeDouble((reversalValue / 100.0) * kagiData.referencePrice, Digits()); } if(reversalType == 1){ reversalAmount = NormalizeDouble(reversalValue, Digits()); } //--- Handle a normal continuation if(kagiData.isUptrend && kagiData.isYang && currentClosePrice >= (kagiData.referencePrice + reversalAmount) && currentClosePrice > kagiData.localMaximum){ if(overlayKagi){ DrawYangLine(GenerateUniqueName(TRENDLINE), kagiData.referenceTime, kagiData.referencePrice, kagiData.referenceTime, currentClosePrice, yangLineColor); } kagiData.localMaximum = currentClosePrice; kagiData.referencePrice = currentClosePrice; } if(kagiData.isDowntrend && kagiData.isYin && currentClosePrice <= (kagiData.referencePrice - reversalAmount) && currentClosePrice < kagiData.localMinimum){ if(overlayKagi){ DrawYinLine(GenerateUniqueName(TRENDLINE), kagiData.referenceTime, kagiData.referencePrice, kagiData.referenceTime, currentClosePrice, yinLineColor); } kagiData.localMinimum = currentClosePrice; kagiData.referencePrice = currentClosePrice; } //--- Handle a normal reversal if(kagiData.isUptrend && kagiData.isYang && currentClosePrice <= (kagiData.referencePrice - reversalAmount) && (currentClosePrice >= kagiData.localMinimum)){ if(overlayKagi){ DrawBendTop(GenerateUniqueName(TRENDLINE), kagiData.referenceTime, kagiData.referencePrice, currentOpenTime, kagiData.referencePrice, yangLineColor); DrawYangLine(GenerateUniqueName(TRENDLINE), currentOpenTime, kagiData.referencePrice, currentOpenTime, currentClosePrice, yangLineColor); } kagiData.localMaximum = kagiData.referencePrice; kagiData.referencePrice = currentClosePrice; kagiData.referenceTime = currentOpenTime; kagiData.isDowntrend = true; kagiData.isUptrend = false; } if(kagiData.isDowntrend && kagiData.isYin && currentClosePrice >= (kagiData.referencePrice + reversalAmount) && (currentClosePrice <= kagiData.localMaximum)){ if(overlayKagi){ DrawBendBottom(GenerateUniqueName(TRENDLINE), kagiData.referenceTime, kagiData.referencePrice, currentOpenTime, kagiData.referencePrice, yinLineColor); DrawYinLine(GenerateUniqueName(TRENDLINE), currentOpenTime, kagiData.referencePrice, currentOpenTime, currentClosePrice, yinLineColor); } kagiData.localMinimum = kagiData.referencePrice; kagiData.referencePrice = currentClosePrice; kagiData.referenceTime = currentOpenTime; kagiData.isDowntrend = false; kagiData.isUptrend = true; } //--- Handle a complex reversal if(kagiData.isUptrend && kagiData.isYang && currentClosePrice <= (kagiData.referencePrice - reversalAmount) && (currentClosePrice < kagiData.localMinimum)){ if(overlayKagi){ DrawBendTop (GenerateUniqueName(TRENDLINE), kagiData.referenceTime, kagiData.referencePrice, currentOpenTime, kagiData.referencePrice, yangLineColor); DrawYangLine(GenerateUniqueName(TRENDLINE), currentOpenTime, kagiData.referencePrice, currentOpenTime, kagiData.localMinimum, yangLineColor); DrawYinLine (GenerateUniqueName(TRENDLINE), currentOpenTime, kagiData.localMinimum, currentOpenTime, currentClosePrice, yinLineColor); } kagiData.localMaximum = kagiData.referencePrice; kagiData.referencePrice = currentClosePrice; kagiData.referenceTime = currentOpenTime; kagiData.localMinimum = currentClosePrice; kagiData.isDowntrend = true; kagiData.isUptrend = false; kagiData.isYang = false; kagiData.isYin = true; } if(kagiData.isDowntrend && kagiData.isYin && currentClosePrice >= (kagiData.referencePrice + reversalAmount) && (currentClosePrice > kagiData.localMaximum)){ if(overlayKagi){ DrawBendBottom(GenerateUniqueName(TRENDLINE), kagiData.referenceTime, kagiData.referencePrice, currentOpenTime, kagiData.referencePrice, yinLineColor); DrawYinLine (GenerateUniqueName(TRENDLINE), currentOpenTime, kagiData.referencePrice, currentOpenTime, kagiData.localMaximum, yinLineColor); DrawYangLine (GenerateUniqueName(TRENDLINE), currentOpenTime, kagiData.localMaximum, currentOpenTime, currentClosePrice, yangLineColor); } kagiData.localMinimum = kagiData.referencePrice; kagiData.referencePrice = currentClosePrice; kagiData.referenceTime = currentOpenTime; kagiData.localMaximum = currentClosePrice; kagiData.isDowntrend = false; kagiData.isUptrend = true; kagiData.isYang = true; kagiData.isYin = false; } //--- Handle a normal continuation after reversal if(kagiData.isDowntrend && kagiData.isYang && (currentClosePrice <= (kagiData.referencePrice - reversalAmount) && (currentClosePrice >= kagiData.localMinimum))){ if(overlayKagi){ DrawYangLine(GenerateUniqueName(TRENDLINE), kagiData.referenceTime, kagiData.referencePrice, kagiData.referenceTime, currentClosePrice, yangLineColor); } kagiData.referencePrice = currentClosePrice; } if(kagiData.isUptrend && kagiData.isYin && (currentClosePrice >= (kagiData.referencePrice + reversalAmount) && (currentClosePrice <= kagiData.localMaximum))){ if(overlayKagi){ DrawYinLine(GenerateUniqueName(TRENDLINE), kagiData.referenceTime, kagiData.referencePrice, kagiData.referenceTime, currentClosePrice, yinLineColor); } kagiData.referencePrice = currentClosePrice; } //--- Handle a complex continuation after reversal if(kagiData.isDowntrend && kagiData.isYang && (currentClosePrice <= (kagiData.referencePrice - reversalAmount) && (currentClosePrice < kagiData.localMinimum))){ if(overlayKagi){ DrawYangLine(GenerateUniqueName(TRENDLINE), kagiData.referenceTime, kagiData.referencePrice, kagiData.referenceTime, kagiData.localMinimum, yangLineColor); DrawYinLine (GenerateUniqueName(TRENDLINE), kagiData.referenceTime, kagiData.localMinimum, kagiData.referenceTime, currentClosePrice, yinLineColor); } kagiData.localMinimum = currentClosePrice; kagiData.referencePrice = currentClosePrice; kagiData.isYang = false; kagiData.isYin = true; } if(kagiData.isUptrend && kagiData.isYin && (currentClosePrice >= (kagiData.referencePrice + reversalAmount) && (currentClosePrice > kagiData.localMaximum))){ if(overlayKagi){ DrawYinLine (GenerateUniqueName(TRENDLINE), kagiData.referenceTime, kagiData.referencePrice, kagiData.referenceTime, kagiData.localMaximum, yinLineColor); DrawYangLine (GenerateUniqueName(TRENDLINE), kagiData.referenceTime, kagiData.localMaximum, kagiData.referenceTime, currentClosePrice, yangLineColor); } kagiData.localMaximum = currentClosePrice; kagiData.referencePrice = currentClosePrice; kagiData.isYang = true; kagiData.isYin = false; } //--- Handle a normal counter-reversal if(kagiData.isDowntrend && kagiData.isYang && currentClosePrice >= (kagiData.referencePrice + reversalAmount) && currentClosePrice <= kagiData.localMaximum){ if(overlayKagi){ DrawBendTop(GenerateUniqueName(TRENDLINE), kagiData.referenceTime, kagiData.referencePrice, currentOpenTime, kagiData.referencePrice, yangLineColor); DrawYangLine(GenerateUniqueName(TRENDLINE), currentOpenTime, kagiData.referencePrice, currentOpenTime, currentClosePrice, yangLineColor); } kagiData.localMinimum = kagiData.referencePrice; kagiData.referencePrice = currentClosePrice; kagiData.referenceTime = currentOpenTime; kagiData.isUptrend = true; kagiData.isDowntrend = false; } if(kagiData.isUptrend && kagiData.isYin && currentClosePrice <= (kagiData.referencePrice - reversalAmount) && currentClosePrice >= kagiData.localMinimum){ if(overlayKagi){ DrawBendBottom(GenerateUniqueName(TRENDLINE), kagiData.referenceTime, kagiData.referencePrice, currentOpenTime, kagiData.referencePrice, yinLineColor); DrawYinLine(GenerateUniqueName(TRENDLINE), currentOpenTime, kagiData.referencePrice, currentOpenTime, currentClosePrice, yinLineColor); } kagiData.localMaximum = kagiData.referencePrice; kagiData.referencePrice = currentClosePrice; kagiData.referenceTime = currentOpenTime; kagiData.isUptrend = false; kagiData.isDowntrend = true; } //--- Handle a complex counter-reversal if(kagiData.isDowntrend && kagiData.isYang && currentClosePrice >= (kagiData.referencePrice + reversalAmount) && currentClosePrice > kagiData.localMaximum){ if(overlayKagi){ DrawBendTop(GenerateUniqueName(TRENDLINE), kagiData.referenceTime, kagiData.referencePrice, currentOpenTime, kagiData.referencePrice, yangLineColor); DrawYangLine(GenerateUniqueName(TRENDLINE), currentOpenTime, kagiData.referencePrice, currentOpenTime, currentClosePrice, yangLineColor); } kagiData.localMinimum = kagiData.referencePrice; kagiData.referencePrice = currentClosePrice; kagiData.referenceTime = currentOpenTime; kagiData.localMaximum = currentClosePrice; kagiData.isUptrend = true; kagiData.isDowntrend = false; } if(kagiData.isUptrend && kagiData.isYin && currentClosePrice <= (kagiData.referencePrice - reversalAmount) && currentClosePrice < kagiData.localMinimum){ if(overlayKagi){ DrawBendBottom(GenerateUniqueName(TRENDLINE), kagiData.referenceTime, kagiData.referencePrice, currentOpenTime, kagiData.referencePrice, yinLineColor); DrawYinLine(GenerateUniqueName(TRENDLINE), currentOpenTime, kagiData.referencePrice, currentOpenTime, currentClosePrice, yinLineColor); } kagiData.localMaximum = kagiData.referencePrice; kagiData.referencePrice = currentClosePrice; kagiData.referenceTime = currentOpenTime; kagiData.localMinimum = currentClosePrice; kagiData.isDowntrend = true; kagiData.isUptrend = false; } //Handle a normal continuation after counter-reversal if(kagiData.isUptrend && kagiData.isYang && currentClosePrice >= (kagiData.referencePrice + reversalAmount) && currentClosePrice <= kagiData.localMaximum){ if(overlayKagi){ DrawYangLine(GenerateUniqueName(TRENDLINE), kagiData.referenceTime, kagiData.referencePrice, kagiData.referenceTime, currentClosePrice, yangLineColor); } kagiData.referencePrice = currentClosePrice; } if(kagiData.isDowntrend && kagiData.isYin && currentClosePrice <= (kagiData.referencePrice - reversalAmount) && currentClosePrice >= kagiData.localMinimum){ if(overlayKagi){ DrawYinLine(GenerateUniqueName(TRENDLINE), kagiData.referenceTime, kagiData.referencePrice, kagiData.referenceTime, currentClosePrice, yinLineColor); } kagiData.referencePrice = currentClosePrice; } //--- Handle a complex continuation after counter-reversal if(kagiData.isUptrend && kagiData.isYang && currentClosePrice >= (kagiData.referencePrice + reversalAmount) && currentClosePrice > kagiData.localMaximum){ if(overlayKagi){ DrawYangLine(GenerateUniqueName(TRENDLINE), kagiData.referenceTime, kagiData.referencePrice, kagiData.referenceTime, currentClosePrice, yangLineColor); } kagiData.referencePrice = currentClosePrice; kagiData.localMaximum = currentClosePrice; } if(kagiData.isDowntrend && kagiData.isYin && currentClosePrice <= (kagiData.referencePrice - reversalAmount) && currentClosePrice < kagiData.localMinimum){ if(overlayKagi){ DrawYinLine(GenerateUniqueName(TRENDLINE), kagiData.referenceTime, kagiData.referencePrice, kagiData.referenceTime, currentClosePrice, yinLineColor); } kagiData.referencePrice = currentClosePrice; kagiData.localMinimum = currentClosePrice; } //--- Handle a weird scenario if(kagiData.isUptrend && kagiData.isYin && currentClosePrice >= (kagiData.referencePrice + reversalAmount) && currentClosePrice > kagiData.localMaximum){ if(overlayKagi){ DrawYinLine(GenerateUniqueName(TRENDLINE), kagiData.referenceTime, kagiData.referencePrice, kagiData.referenceTime, kagiData.localMaximum, yinLineColor); DrawYangLine(GenerateUniqueName(TRENDLINE), kagiData.referenceTime, kagiData.localMaximum, kagiData.referenceTime, currentClosePrice, yangLineColor); } kagiData.isYin = false; kagiData.isYang = true; kagiData.localMaximum = currentClosePrice; kagiData.referencePrice = currentClosePrice; } if(kagiData.isDowntrend && kagiData.isYang && currentClosePrice <= (kagiData.referencePrice - reversalAmount) && currentClosePrice < kagiData.localMinimum){ if(overlayKagi){ DrawYangLine(GenerateUniqueName(TRENDLINE), kagiData.referenceTime, kagiData.referencePrice, kagiData.referenceTime, kagiData.localMinimum, yangLineColor); DrawYinLine(GenerateUniqueName(TRENDLINE), kagiData.referenceTime, kagiData.localMinimum, kagiData.referenceTime, currentClosePrice, yinLineColor); } kagiData.isYang = false; kagiData.isYin = true; kagiData.localMinimum = currentClosePrice; kagiData.referencePrice = currentClosePrice; } } }
Diese Funktion aktualisiert die Kagi-Struktur, wenn ein neuer Balken geschlossen wird. Sie wird nur ausgeführt, wenn ein neuer geschlossener Balken mit Hilfe der Funktion IsNewBar und der gespeicherten lastBarOpenTime erkannt wurde. Die Funktion liest den Schluss- und Eröffnungszeitpunkt des gerade geschlossenen Balkens aus dem gewählten Kagi-Zeitrahmen unter Verwendung von iClose und iTime.
Als Nächstes wird der Umkehrwert auf die gleiche Weise berechnet wie in der Initialisierungsroutine. Wenn der Umkehrmodus prozentual ist, wird der Prozentsatz des aktuellen Referenzpreises berechnet. Wenn der Modus Preisschritt ist, wird der feste Wert verwendet. Der Wert ist auf die Genauigkeit des Handelsinstruments normiert.
Die Kernlogik spiegelt den historischen Konstruktor wider, allerdings für einen einzelnen neuen Balken. Sie prüft auf Fortsetzung, normale Umkehrung, komplexe Umkehrung, Gegenumkehrung und verwandte Fälle der Fortsetzung. Jede übereinstimmende Bedingung kann ein oder mehrere Objekte zeichnen, wobei die Hilfsfunktionen Draw und GenerateUniqueName für Objektnamen verwendet werden. Die Zeichnung wird nur ausgeführt, wenn der Parameter overlayKagiinput true ist.
Nach jeder Zeichnung aktualisiert die Funktion den internen Kagi-Status. Sie setzt referencePrice und referenceTime, wenn ein neues Segment oder eine Umkehrung eingerichtet wird. Sie passt localMaximum und localMinimum an, um Schultern und Taillen zu erfassen. Sie schaltet die Richtungs- und Stil-Flags isUptrend, isDowntrend, isYang und isYin nach Bedarf um.
Die Funktion akzeptiert den aktuellen Geld- und Briefkurs als Parameter, verwendet aber für Kagi-Entscheidungen nur den Kurs des geschlossenen Balkens. Am Ende der Ausführung ist der interne Status bereit für den nächsten neuen Balken, und das Chart spiegelt die letzten Kagi-Änderungen wider, wenn das Overlay aktiviert ist.
Verknüpfen der Funktionen mit EA-Ereignissen
Nun, da beide Konstruktoren fertig sind, müssen wir sie über die entsprechenden EA-Ereignishandler aufrufen. Wir rufen den historischen Konstruktor einmal während der Initialisierung auf. Dadurch wird der gesamte Kagi-Status aus der Vergangenheit aufgebaut und die ersten Segmente werden gezeichnet.
//+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit(){ ... //--- Construct Kagi On Initialization ConstructKagiOnInitialization(); return INIT_SUCCEEDED; }
Bei jedem Tick lesen wir die aktuellen Marktpreise und rufen dann den Echtzeit-Konstruktor auf. Wir übergeben das aktuelle Kauf- und Verkaufsangebot, da die Echtzeitroutine diese als Parameter akzeptiert. Die Kagi-Logik selbst verwendet den letzten geschlossenen Balken als Entscheidungsgrundlage.
//+------------------------------------------------------------------+ //| Expert tick function | //+------------------------------------------------------------------+ void OnTick(){ //--- Scope variables double askPrice = SymbolInfoDouble (_Symbol, SYMBOL_ASK); double bidPrice = SymbolInfoDouble (_Symbol, SYMBOL_BID); //--- Construct Kagi In Real Time ConstructKagiInRealTime(bidPrice, askPrice); }
Mit diesen Aufrufen baut der EA den Kagi einmal aus der Historie auf und aktualisiert ihn dann, wenn neue Balken schließen.
Der vollständige Quellcode für diesen EA ist am Ende des Artikels beigefügt. Wenn Sie beim Durcharbeiten der Anleitung ein Stück übersehen haben, öffnen Sie die vollständige Datei und sehen Sie sich die Abschnitte der Reihe nach an. Die vollständige Datei enthält den Eigenschaftsblock, Enumerationen, Eingaben, struct, Initialisierungen, Zeichenhilfen, den Generator für eindeutige Namen, beide Konstruktoren und die gerade beschriebene Ereignisverdrahtung.
Evaluierung des EA: Ist die Kagi-Logik haltbar?
Nachdem unser Kagi-Chart EA nun vollständig zusammengebaut ist, besteht der nächste Schritt darin, ihn auf einen Live-Chart zu setzen und zu bestätigen, dass sich alles wie erwartet verhält. Für diese Demonstration haben wir den EA an den Nikkei-Index (JPN225) angehängt und die Konfigurationsdatei mit dem Namen kagitrader.set geladen, die zusammen mit dem Projekt bereitgestellt wird. Nachdem die Einstellungen vorgenommen wurden, starteten wir den EA auf dem Chart – und die Ergebnisse waren sofort sichtbar. Die Kagi-Struktur begann sich genau wie geplant zu formen und wurde reibungslos aktualisiert, sobald neue Stäbe hinzukamen. Unten sehen Sie einen Screenshot des EA in Echtzeit, der zeigt, dass die Kagi-Linien korrekt über der Preisbewegung gezeichnet werden.

Schlussfolgerung
In diesem Teil des Projekts haben wir erfolgreich die vollständige Grundlage für einen funktionalen Kagi-Chart Expert Advisor geschaffen. Sie haben gelernt, wie man die Chartumgebung vorbereitet, eindeutige Objektnamen generiert, den internen Kagi-Status verwaltet und das Chart sowohl mit historischen Daten als auch in Echtzeit erstellt. Durch das Zusammenspiel dieser Komponenten verfügen Sie nun über ein vollständiges Kagi-Visualisierungstool, das Trendschwankungen, Umkehrungen und Übergänge in der Linienstärke genau verfolgt. Damit verfügen Sie über einen Chart-Rahmen, auf den Sie sich verlassen können, um die Marktstruktur zu interpretieren und fundierte Handelsentscheidungen zu treffen.
Indem Sie den EA an ein Live-Symbol anhängen und ihn mit der mitgelieferten Datei kagitrader.set testen, haben Sie auch überprüft, ob die Kagi-Logik in der Praxis korrekt funktioniert. Der EA zeichnet saubere Segmente, aktualisiert bei jedem neuen Balken und reagiert auf Kursbewegungen genau so, wie es die Regeln vorgeben. Jetzt haben Sie eine voll funktionsfähige Kagi-Chart Engine, die direkt in MetaTrader 5 läuft.
In Teil 2 werden wir diese Grundlage weiter ausbauen. Wir werden das System mit Handelslogik erweitern, Konfigurationskontrollen hinzufügen und Entscheidungsfunktionen integrieren, die diesen EA von der Darstellung in ein echtes Handelswerkzeug verwandeln. Das Ziel der nächsten Phase ist es, das Kagi-Chart zum Leben zu erwecken und sein Verhalten zu nutzen, um Signale zu generieren, Ein- und Ausstiege zu steuern und die automatische Strategieentwicklung zu unterstützen.
Übersetzt aus dem Englischen von MetaQuotes Ltd.
Originalartikel: https://www.mql5.com/en/articles/20239
Warnung: Alle Rechte sind von MetaQuotes Ltd. vorbehalten. Kopieren oder Vervielfältigen untersagt.
Dieser Artikel wurde von einem Nutzer der Website verfasst und gibt dessen persönliche Meinung wieder. MetaQuotes Ltd übernimmt keine Verantwortung für die Richtigkeit der dargestellten Informationen oder für Folgen, die sich aus der Anwendung der beschriebenen Lösungen, Strategien oder Empfehlungen ergeben.
Analytical Volume Profile Trading (AVPT): Liquiditätsarchitektur, Marktgedächtnis und algorithmische Ausführung
MetaTrader 5 Machine Learning Blueprint (Teil 6): Entwicklung eines produktionsgerechten Caching-Systems
Implementierung von praktischen Modulen aus anderen Sprachen in MQL5 (Teil 04): Zeit-, Datums- und Datetime-Module aus Python
Die „Griechen“ in Black-Scholes automatisieren: Fortgeschrittenes Scalping und Mikrostrukturhandel
- Freie Handelsapplikationen
- Über 8.000 Signale zum Kopieren
- Wirtschaftsnachrichten für die Lage an den Finanzmärkte
Sie stimmen der Website-Richtlinie und den Nutzungsbedingungen zu.