Automatische Suche nach Divergenzen und Konvergenzen
Inhalt
- Einführung
- Divergenz und Konvergenz (Begriffsbestimmung)
- Methoden zur Bestimmung der Richtung der Preise und Indikatoren
- Definition der Hochs/Tiefs mit den Bars
- Definition der Hochs/Tiefs mit einem Schwellenwert
- Maximal-/Minimalwert über/unter der Mittellinie des Indikators
- Klassifizierungssysteme für Divergenzen
- Komplette Systematisierung der Preis- und Indikatorbewegung
- Dreifache Divergenz
- Universalindikator zur Bestimmung der Divergenz
- Auswahl eines Oszillators
- Erstellen eines neuen Indikators
- Anwenden eines Universaloszillators
- Bestimmen der Extrema des Oszillators
- Klasse zur Bestimmung der Extrema
- Bestimmen der Extrema durch Bars
- Bestimmen der Extrema durch einen Schwellenwert
- Bestimmen der Extrema durch ihre Position in Relation zur Mitte des Oszillators
- Methode zur Festlegung des Typs der Divergenz
- Klasse zur Prüfung der Bedingungen für eine Divergenz
- Vorbereitung der Prüfung der Bedingungen für eine Divergenz
- Definition der Divergenz
- Fertigstellung des Indikators
- Schlussfolgerung
- Anlagen
Einführung
Der Begriff "Divergenz" kommt vom lateinischen Wort "divergere" ("Abweichung erkennen"). Die Divergenz wird in der Regel als Abweichung zwischen den Indikatorenwerten und der Kursentwicklung definiert. Der antonyme Begriff ist "Konvergenz", der sich vom lateinischen Wort "Convergo" ("Ich bringe zusammen") ableitet. Es gibt weiter gefasste Klassifizierungssysteme für Divergenz und Konvergenz, einschließlich Definitionen wie "versteckte Divergenz","erweiterte Divergenz", Divergenzen der Klassen A, B und C usw.
In diesem Artikel befassen wir uns zunächst mit den Grundbegriffen Divergenz und Konvergenz. Dann werden wir uns mit anderen Methoden ihrer Klassifizierung beschäftigen, vergleichende Analyse durchführen sowie die Vor- und Nachteile identifizieren. Schließlich werden wir unsere eigene Klassifikation entwickeln, die vollständiger ist und keine offensichtlichen Nachteile hat, sowie einen universellen Indikator für die Suche und Anzeige von Konvergenzen/Divergenzen auf dem Chart.
Divergenz und Konvergenz (Begriffsbestimmung)
Divergenz ist also eine Diskrepanz zwischen den Indikatorenwerten und der Kursentwicklung. Wir haben auch den zweiten Begriff - die Konvergenz - mit der entgegengesetzten Bedeutung. Wenn Divergenz eine Diskrepanz zwischen den Indikatorenwerten und der Kursentwicklung ist, dann bedeutet Konvergenz eine Angleichung. Dies ist jedoch nicht der Fall, da die Angleichung nicht mit Konvergenz gleichgesetzt werden kann.
Damit beide Begriffe - Divergenz und Konvergenz - eine genauere Bedeutung haben, brauchen sie engere Definitionen. Werfen wir einen Blick auf die Kurs- und Indikator-Charts. Wenn der Preis steigt, während der Indikator sinkt, haben wir eine Divergenz. Wenn sich der Kurs nach unten bewegt, während der Indikator steigt, haben wir eine Konvergenz (Abb. 1).
Abb. 1. Diskrepanz der Bewegung von Preis und Indikator. Links steigt der Preis
während der Indikator fällt — Divergenz. Rechts fällt der Preis, während der Indikator steigt — Konvergenz.
Wir sind es gewohnt, dass der Indikator in der Regel unterhalb des Kurscharts platziert wird, so dass diese Definition auf den ersten Blick akzeptabel erscheint. Kehrt man jedoch die Positionen von Indikator und Kurschart um, ändert sich alles radikal: Aus Divergenz wird eine Konvergenz und umgekehrt (Abb. 2).
Abb. 2. Diskrepanz der Bewegung von Preis und Indikator. Links steigt der Preis
während der Indikator fällt — Konvergenz. Rechts fällt der Preis, während der Indikator steigt — Divergenz
Schauen wir uns nun die Abbildungen 1 und 2 aus der Sicht der Wahl der Handelsrichtung an. Angenommen, der Preis bewegt sich nach oben, während der Indikator nach unten bewegt, so haben wir beschlossen, zu verkaufen. Entsprechend sollten wir verkaufen, wenn sich der Kurs nach unten bewegt, während der Indikator nach oben bewegt (Abb. 3).
Abb. 3. Links: Bedingung für Verkauf, Preis und Indikator laufen auseinander. Rechts: Bedingung
für Kauf (ähnlich dem Verkauf), Preis und Indikator laufen zusammen
Es stellt sich heraus, dass im Falle eines Verkaufs der Preis und der Indikator auseinander laufen, während sie beim Kauf zusammen kommen, obwohl die Kauf- und Verkaufskonditionen identisch sind, wenn auch entgegengesetzt. Das bedeutet, dass wir eine der Bedingungen als 'bearish' bezeichnen können, während die zweite 'bullish' ist. Dies bedeutet, dass die Position des Indikators unterhalb der Preise auf dem Chart nicht ausreicht, um Konvergenz und Divergenz zu definieren.
Alle angegebenen Definitionen haben mit der Verkaufsrichtung zu tun, während die Argumentation für den Kauf entgegengesetzt ist. Aber es gibt eine einfachere und präzisere Version der Definition, die auf dem Wesen der technischen Analyse beruht. Wenn wir die Annahme der weiteren Preisbewegung in die Definition von Divergenz und Konvergenz aufnehmen, wird es einfach und genau.
Die Divergenz ist ein Umkehrsignal für die Preise in Form einer unterschiedlichen Entwicklung der Indikatorwerte und der Preise.
Da dies ein Umkehrsignal ist, sollte der Preis für einen Kauf fallen und für einen Verkauf steigen. Damit diese Diskrepanz deutlich wird, sollte sich der Indikator nach oben bzw. unten bewegen. In dieser Definition wird der Verkauf als Referenzrichtung verwendet, während der Indikator unter dem Kurs-Chart platziert werden sollte. Dieser Definition folgt die in Abb. 3 gezeigte Abweichung.
Die Konvergenz verhält sich entgegengesetzt: der Preis sollte sich nach unten bewegen, während gleichzeitig der Indikator steigt. Die geschätzte Preisrichtung ändert sich nicht. Daher die folgende Definition der Konvergenz:
Die Konvergenz ist ein Signal für die Fortsetzung des Trends in Form einer unterschiedlichen Entwicklung der Indikatorwerte und der Preise.
Da dies ein Fortsetzungssignal ist, sollte sich der Preis für den Verkauf nach unten und für einen Kauf nach oben bewegen. Der Indikator sollte sich nach oben bzw. unten bewegen (Abb. 4).
Abb. 4. Konvergenzsignale
Natürlich kann man darüber streiten, ob die Divergenz ein Umkehrsignal ist, und Konvergenz ein Fortsetzungssignal. Aber das ist schon jetzt eine Frage der praktischen Anwendung der technischen Analyse.
Die Abb. 5 zeigt Divergenz- und Konvergenzsignale gleichzeitig, um die Terminologie zu verdeutlichen.
Abb. 5. Divergenz- und Konvergenzsignale
Methoden zur Bestimmung der Richtung der Preise und Indikatoren
Die Linen des Indikators und der Preise sind bisher gerade. Aber das ist eine Vereinfachung, die nichts mit realen Preisbewegung zu tun hat. Betrachten wir also die Methoden, mit denen sich die Richtung der Preise und des Indikators ermitteln und eine Divergenz erkennen lässt. Dann werden wir uns mit den Divergenzen in der Praxis beschäftigen.
Im Allgemeinen müssen wir zuerst die Preise oder die Hochs und Tiefs des Charts identifizieren, um dann die Werte zu vergleichen. Eine 'bullsche' Divergenz (Kaufsignal) wird durch die Tiefs erkannt: Ist ein Tief höher als das vorherige, zeigt der Indikator nach oben und umgekehrt. Die 'bearsche' Divergenz (Verkaufssignale) wird von den Hochs her erkannt.
Es gibt drei Möglichkeiten die Extrema auf dem Chart zu erkennen.
- Durch die Bars.
- Durch Überschreiten eines Schwellwertes aus dem letzten Hoch/Tief.
- Die Maxima/Minima liegt oberhalb/unterhalb der Mittellinie des Indikators.
Erkennen der Hochs/Tief durch die Bars. Wir verwenden die Anzahl der Höchst-/Tiefstbars. Wenn der Parameter z. B. den Wert 2 hat, sollte der Indikatorwert der höchsten Bar die Werte von zwei Bars nach links und zwei nach rechts übersteigen. Entsprechend sollte der Wert der Tiefs niedriger sein als die der benachbarten Bars (Abb. 6).
Abb. 6. Definition der Hochs und Tiefs für zwei Bars. Links ist die Definition des Hochs. Die Bar mit dem Pfeil, wird erkannt
durch die Bar mit dem OK-Häkchen. Rechts ist die Definition eines Tiefs.
Die benötigte Anzahl der Bars links und rechts des Hochs oder Tiefs kann variieren: z. B. 5 nach links und 2 nach rechts (Abb. 7).
Abb. 7. Das Hoch wird durch fünf Bars links und zwei Bars rechts definiert.
Definieren der Hochs und Tiefs durch Schwellenwerte. Wenn der Indikator steigt, überprüft das System den Maximalwert. Bei Bars ohne neues Extrema wird der aktuelle Wert mit dem vorher ermittelten Hoch/Tief verglichen. Überschreitet die Differenz den durch einen externen Parameter eingestellten Schwellwert, wird angenommen, dass der Indikator die Richtung geändert hat, während die Bars, bei dem der Maximal-/Minimalwert erreicht wurde, als die Bar mit einem Hoch/Tief betrachtet wird (Abb. 8).
Abb. 8. Definition der Hochs und Tiefs mit einem Schwellenwert Der Schwellenwert wird in der linken oberen Ecke angezeigt.
Bis zur Bar 2 steigt der Indikator, das Maximum liegt auf der Bars 2, während auf der Bar 5 der Wert
um den Schwellwert verringert wird, d. h. der Indikator ändert seine Richtung. Ab der Bar 6 hat der Indikator
den Schwellwert wieder überschritten und seine Richtung geändert etc.
Die Definition über die Bars ist am einfachsten, da sie überhaupt nicht vom Indikator abhängt. Der Wert des Schwellwertes ist dagegen abhängig vom Indikatortyp. Beim RSI mit dem Oszillationsbereich von 0-100 kann der Schwellwert z. B. etwa 5 betragen. Für ein Momentum wäre ein Schwellwert zwischen 0,1-1, da der Indikator leicht um den Wert von 100 schwankt. Außerdem hängt das Ausmaß dieser Schwankungen vom Zeitrahmen ab. Dies erschwert die Verwendung des Schwellenwertes zusätzlich.
Der Maximal-/Minimalwert liegt oberhalb/unterhalb der Mittellinie des Indikators. Diese Methode wird seltener als andere verwendet. Es hängt auch vom verwendeten Indikator ab, da nicht alle Indikatoren einen Mittelwert von 0 haben (z. B. der Mittelwert von RSI ist 50). Der Hauptnachteil ist jedoch seine starke Verzögerung (Abb. 9).
Abb. 9. Definition der Hochs und Tiefs durch das Kreuzen der Mittellinie. Wir kennen das Hoch 1
erst nachdem die Mittellinie von der Bar 2 gekreuzt wurde. Wir kennen das Tief 3
nach dem Kreuzen der Mittellinie durch die Bar 4.
Klassifizierungssysteme für Divergenzen
Über die Divergenz finden Sie viele Artikel im Internet. Sie beschreiben eine Vielzahl von Ansätzen, die sich sowohl in der Terminologie als auch in den Prinzipien der Systematisierung von Divergenz und Konvergenz unterscheiden. Es gibt einfache, klassische, versteckte und erweiterte Divergenzen. Jemandem teilt sie in die Klassen A, B und C. Wir werden die primären Quellen in diesem Artikel nicht analysieren. Stattdessen werden wir einige der identifizierten Typen durchgehen.
Klassische Divergenz. Diese Art wurde oben bereits beschrieben und in Abb. 5 gezeigt.
Versteckte Divergenz. Die versteckte Divergenz unterscheidet sich von der klassischen durch die Bewegungsrichtung der Preise und des Indikators. Mit anderen Worten, die versteckte Divergenz ähnelt der Konvergenz.
Erweiterte Divergenz. Bisher haben wir nur die Aufwärts- und Abwärtsbewegungen der Preise und des Indikators diskutiert. Wenn wir die horizontale Bewegung hinzufügen, erhöht sich die Anzahl der Optionen. Trotz der vielen Optionen, die wir durch die Kombination von drei Richtungen der Preisbewegung und drei Richtungen der Indikatorbewegung erreichen können, ist nur eine Version der erweiterten Divergenz herausgehoben worden:
- Horizontale Preisbewegung, der Indikator bewegt sich abwärts - erweiterte Abwärtsdivergenz (Verkaufssignal)
- Horizontale Preisbewegung, der Indikator bewegt sich nach oben — erweiterte Aufwärtsivergenz (Kaufsignal).
Klassen: A, B, C. А Klasse ist eine klassische Divergenz, während B-und C-Klassen sind erweiterte Versionen der Divergenz.
Klasse B:
- Horizontale Preisbewegung, der Indikator bewegt sich nach unten — 'bearish' (Verkaufssignal).
- Horizontale Preisbewegung, der Indikator bewegt sich nach oben — 'bullish' (Kaufsignal).
Klasse С:
- Der Preis steigt, die Hochs des Indikators sind auf einer Ebene — 'bearish' (Verkaufssignal).
- Der Preis fällt, die Tiefs des Indikators sind auf einer Ebene — 'bullish' (Kaufsignal).
Wie wir sehen können, sind die Klassen B und С die Varianten der erweiterten Divergenz. Die Klasse B wiederholt komplett die obige Definition.
Die wichtigste Schlussfolgerung, die ich beim Durchsuchen des verfügbaren Materials zur Divergenz gezogen habe, ist das Fehlen einer klaren Terminologie und eine unvollständige Erfassung der möglichen Versionen. Daher analysieren wir die verschiedenen Optionen zur Kombination von Preis- und Indikatorrichtung und systematisieren sie.
Komplette Systematisierung der Preis- und Indikatorbewegung
Zunächst definieren wir zwei mögliche Preis und Indikator Bewegungsrichtungen.
- Zwei Bewegungsrichtungen: nach oben und unten.
- Drei Bewegungsrichtungen: oben, unten und horizontal.
Der erste Fall bietet nur vier mögliche Kombinationen. Sehen wir sie uns am Beispiel eines Verkaufssignal an.
- Preist steigt, Indikator steigt.
- Preis steigt, Indikator fällt (Divergenz).
- Preis fällt, Indikator steigt (Konvergenz).
- Preis fällt, Indikator fällt.
Nachdem wir uns nun mit der Definition der Richtungen beschäftigt haben, lassen Sie uns diese Optionen darstellen (Abb. 10).
Abb. 10. Alle möglichen Kombinationen der Bewegungen von Preis und Indikator im Fall von zwei Richtungen
Im Fall der drei Richtungen, gibt es jetzt neun Kombinationen.
- Preist steigt, Indikator steigt.
- Preis steigt, Indikator horizontal.
- Preis steigt, Indikator fällt (Divergenz).
- Preis horizontal, Indikator steigt.
- Preis horizontal, Indikator horizontal.
- Preis horizontal, Indikator fällt.
- Preis fällt, Indikator steigt (Konvergenz).
- Preis fällt, Indikator horizontal.
- Preis fällt, Indikator fällt.
Alle diese Kombinationen zeigt Abb. 11.
Abb. 11. Alle möglichen Kombinationen der Bewegungen von Preis und Indikator im Fall von drei Richtungen
Wenn Sie einen Indikator erstellen, der es Ihnen erlaubt, eine der betrachteten Optionen auszuwählen, können Sie die Divergenz, Konvergenz, versteckte oder erweiterte Divergenz, die Sie für richtig halten, auswählen. Mit anderen Worten, wir bekommen einen universellen Indikator, der auch für diejenigen nützlich ist, die mit der Systematisierung und den in diesem Artikel gegebenen Definitionen nicht einverstanden sind.
Dreifache Divergenz
Bisher wurde die Bewegungsrichtung der Preise, nach oben, unten und horizontal, und des Indikators durch zwei Werte bestimmt. Wir können den dritten Wert hinzufügen, um die Anzahl der möglichen Optionen für Preis- und Indikatorbewegungen zu erhöhen. Insgesamt gibt es neun Optionen:
- Steigt, steigt.
- Steigt, horizontal.
- Steigt, fällt.
- Horizontal, steigt.
- Horizontal, horizontal.
- Horizontal, fällt.
- Fällt, Steigt.
- Fällt, horizontal.
- Fällt, fällt.
In diesem Fall wäre es korrekter von der Form der Bewegung zu sprechen als von der Richtung. Die Bewegungsarten, definiert durch drei Hochs, sind in Abb. 12 dargestellt.
Abb. 12. Verschiedenen möglichen Bewegungen, basiert auf drei Hochs
Die entsprechenden Bewegungen basierend auf den Tiefs sind dargestellt in Abb. 13.
Abb. 13. Verschiedenen möglichen Bewegungen basiert auf drei Tiefs
Durch die Kombination von 9 Typen der Preisbewegung mit den 9 Typen der Indikatorbewegung erhalten wir 81 Varianten der dreifachen Divergenz.
So können Sie die Bewegung mit beliebig viele Punkte bestimmen. Wenn wir den vierten Punkt hinzufügen, haben wir 81 Variationen der Bewegung der Preise und des Indikators und 6561 (81*81) mögliche Versionen ihrer Kombination. Natürlich, je mehr Möglichkeiten, desto unwahrscheinlicher sind sie. Vielleicht ist es nicht sinnvoll, den vierten Punkt zu übernehmen, aber der Indikator des aktuellen Artikel soll keine Einschränkung der Anzahl der Punkte enthalten, mit denen die Bewegungsart definiert wird.
Universalindikator zur Bestimmung der Divergenz
Nachdem wir uns mit der Theorie beschäftigt haben, beginnen wir mit der Entwicklung des Indikators.
Auswahl eines Oszillators. Um nicht durch einen einzigen Oszillator zur Definition der Divergenz begrenzt zu werden, verwenden wir den in diesem Artikel beschriebenen Universaloszillator. Beigefügt sind: iUniOsc (Universaloszillator) und iUniOscGUI (gleicher Oszillator mit der grafischen Oberfläche). Wir werden die Basisversion - iUniOsc - verwenden.
Erstellen eines neuen Indikators. Erstellen wir den Indikator iDivergence im MetaEditor. Wir verwenden die Funktion OnCalculate(). Die Funktion OnTimer() wird nicht benötigt. Markieren Sie das Ankreuzkästchen "Indikator im separaten Fenster". Wir erstellen drei Puffer: eine Linie für die Oszillatoranzeige Linie, und zwei Puffer mit Pfeilen für das Auftreten einer Divergenz. Nachdem eine neue Datei im Editor geöffnet wurde, ändern Sie die Puffernamen: 1 - buf_osc, 2 - buf_buy, 3 - buf_sell. Die Namen sollten dort geändert werden, wo die Arrays deklariert werden und in der Funktion OnInit(). Wir können auch die Puffereigenschaften anpassen: Indikator_label1, Indikator_label2, Indikator_label3 - die Werte dieser Eigenschaften werden im Tooltip angezeigt, wenn Sie mit der Maus über die Linie oder den Namen des Indikators fahren, sowie im Datenfenster. Nennen wir sie "osc", "buy" und "sell".
Anwenden des Universaloszillators. Fügen Sie alle externen Parameter des Indikators iUniOsc ein. Die Parameter ColorLine1, ColorLine2 und ColorHisto werden im Fenster der Eigenschaften nicht benötigt. Wir werden Sie zu verbergen. Der Parameter 'Type' hat den benutzerdefinierten Typ OscUni_RSI, der in der Datei UniOsc/UniOscDefines.mqh deklariert ist. Wir laden diese Datei. Standardmäßig ist der Parameter 'Typ' auf OscUni_ATR — dem Indikator ATR — gesetzt. Der ATR ist jedoch nicht von der Bewegungsrichtung der Preise abhängig, d. h. er eignet sich nicht für die Definition von Divergenzen. Setzen Sie daher den Indikator OscUni_RSI — der Indikator RSI — als Standard:
#include <UniOsc/UniOscDefines.mqh> input EOscUniType Type = OscUni_RSI; input int Period1 = 14; input int Period2 = 14; input int Period3 = 14; input ENUM_MA_METHOD MaMethod = MODE_EMA; input ENUM_APPLIED_PRICE Price = PRICE_CLOSE; input ENUM_APPLIED_VOLUME Volume = VOLUME_TICK; input ENUM_STO_PRICE StPrice = STO_LOWHIGH; color ColorLine1 = clrLightSeaGreen; color ColorLine2 = clrRed; color ColorHisto = clrGray;
Deklarieren der Handle des Universaloszillators unter den externen Variablen:
int h;
Laden Sie den Universaloszillator zu Beginn der Funktion OnInit():
h=iCustom(Symbol(),Period(),"iUniOsc", Type, Period1, Period2, Period3, MaMethod, Price, Volume, StPrice, ColorLine1, ColorLine2, ColorHisto); if(h==INVALID_HANDLE){ Alert("Can't load indicator"); return(INIT_FAILED); }
In der Funktion OnCalculate() kopieren Sie die Daten des Universaloszillators in den Puffer buf_osc:
int cnt; if(prev_calculated==0){ cnt=rates_total; } else{ cnt=rates_total-prev_calculated+1; } if(CopyBuffer(h,0,0,cnt,buf_osc)<=0){ return(0); }
In diesem Stadium können wir die Korrektheit der durchgeführten Aktionen überprüfen, indem wir den Indikator iDivergence auf den Chart ziehen. Wenn alles richtig gemacht wurde, sehen Sie im Subfenster die Linie des Oszillators.
Definition der Extrema des Oszillators. Wir haben bereits drei Möglichkeiten zur Definition von Extrema in Betracht gezogen. Wir nehmen sie in den Indikator auf und bieten die Möglichkeit, jede von ihnen auszuwählen (externe Variable mit einer Dropdown-Liste). Im Verzeichnis Include erstellen wir das Verzeichnis UniDiver, in dem sich alle weiteren Dateien mit dem Code befinden sollen. Erstellen Sie die Datei UniDiver/UniDiverDefines.mqh und schreiben Sie die Enumeration EExtrType in diese Datei:
enum EExtrType{
ExtrBars,
ExtrThreshold,
ExtrMiddle
};
Enumeration:
- ExtrBars — mit Bars;
- ExtrThreshold — mit einem Schwellenwert vom letzten Hoch/Tief;
- ExtrMiddle — Maximum oder Minimum, wenn der Indikator über oder unter seiner Mittellinie ist.
Legen Sie im Indikator den externen Parameter ExtremumType an und fügen Sie ihn vor alle anderen externen Parameter ein. Bei der Definition der Extrema durch Bars benötigen wir zwei Parameter - Anzahl der Bars links und rechts vom Extremum, während wir bei der Definition durch Schwellwert den Parameter für die Berechnung des Schwellenwertes benötigen:
input EExtrType ExtremumType = ExtrBars; // Typ des Extremums input int LeftBars = 2; // Bars links der Bar des Extremums input int RightBars = -1; // Bars rechts der Bar des Extremum input double MinMaxThreshold = 5; // Schwellenwert für ExtrThreshold
Implementieren wir die Möglichkeit, einen Parameter, RightBars oder LeftBars, zu verwenden, zusätzlich zur gleichzeitigen Verwendung von zwei. RightBars ist standardmäßig gleich -1. Das bedeutet, dass er nicht verwendet wird und ihm der Wert des zweiten Parameters zugewiesen werden soll.
Klassen für die Definition der Extrema. Eine Änderung der Methode für die Definition der Extrema ist während der Laufzeit des Indikators nicht notwendig, daher wäre es sinnvoller, OOP anstelle der Operatoren 'if' und 'switch' zu verwenden. Legen Sie die Basisklasse und drei abgeleitete Klassen für drei Methoden zur Definition eines Extremas an. Eine dieser abgeleiteten Klassen soll beim Start des Kennzeichens ausgewählt werden. Diese Klassen sind sowohl für die Definition der Extremas als auch für die Durchführung aller notwendigen Arbeiten zur Auffindung einer Divergenz zu verwenden. Sie unterscheiden sich nur in der Definition von Extremas, während die Definition der Konvergenz in allen Fällen völlig identisch ist. Daher soll die Funktion mit der Definition der Divergenz in der Basisklasse liegen und von abgeleiteten Klassen aufgerufen werden. Aber zuerst müssen wir einen einfachen Zugang zu allen Extrema der Indikatoren ermöglichen (wie es bei den Hochs der ZigZags im Artikel "Wolfe Wellen" der Fall war).
Die Struktur SExtremum wird dient der Speicherung der Daten eines Extremums. Die Beschreibung der Struktur befindet sich in UniDiverDefines:
struct SExtremum{ int SignalBar; int ExtremumBar; datetime ExtremumTime; double IndicatorValue; double PriceValue; };
Die Felder der Struktur:
- SignalBar — Bar, für die eine gültige Formation eines Extremums erkannt worden ist
- ExtremumBar — Bar mit einem Extremum
- ExtremumTime — Zeitstempel der Bar mit einem Extremum
- IndicatorValue — Indikatorwert der Bar des Extremums
- PriceValue — Preis der Bar mit einem Extremum des Indikators
Zwei Arrays dieser Strukturen sollen dazu dienen, Daten aller Hochs und Tiefs zu speichern. Sie werden Mitglieder der Basisklasse sein.
Die Klassen zur Definition der Extrema befinden sich in der Datei UniDiver/CUniDiverExtremums.mqh, der Name der Basisklasse ist CDiverBase. Betrachten wir zunächst nur die Struktur der Klasse mit den grundlegenden Methoden. Der Rest wird bei Bedarf nachträglich hinzugefügt.
class CDiverBase{ protected: SExtremum m_upper[]; SExtremum m_lower[]; void AddExtremum( SExtremum & a[], int & cnt, double iv, double pv, int mb, int sb, datetime et); void CheckDiver( int i, int ucnt, int lcnt, const datetime & time[], const double &high[], const double &low[], double & buy[], double & sell[], double & osc[] ); public: virtual void Calculate( const int rates_total, const int prev_calculated, const datetime &time[], const double &high[], const double &low[], double & osc[], double & buy[], double & sell[] ); };
Mit der virtuellen Methode Calculate() können Sie die Definition der Extrema auswählen. Die Methoden AddExtremum() und CheckDiver() befinden sich im Abschnitt 'protected' — sie werden von der Methode Calculate() der abgeleiteten Klassen aufgerufen. Die Methode AddExtremum() fügt die Daten über die Hochs und Tiefs in die Arrays m_upper[] und m_lower[]. Die Methode CheckDiver() prüft, ob die Divergenzbedingung erfüllt ist und setzt die Pfeile des Indikators. Nachfolgend werden wir alle diese Methoden näher betrachten, aber für jetzt machen wir uns mit den abgeleiteten Klassen für die anderen Möglichkeiten der Definition der Extrema vertraut.
Bestimmen der Extrema durch Bars. Die Klasse, um die Extrema durch Bars zu bestimmen:
class CDiverBars:public CDiverBase{ private: SPseudoBuffers1 Cur; SPseudoBuffers1 Pre; int m_left,m_right,m_start,m_period; public: void CDiverBars(int Left,int Right); void Calculate( const int rates_total, const int prev_calculated, const datetime &time[], const double &high[], const double &low[], double & osc[], double & buy[], double & sell[] ); };
Die Parameter für die Definition von Extremwerten (die externen Variablen LeftBars und RightBars) werden an den Klassenkonstruktor übergeben, deren Werte überprüft und gegebenenfalls geändert und zusätzliche Parameter berechnet:
void CDiverBars(int Left,int Right){ m_left=Left; m_right=Right; if(m_left<1)m_left=m_right; // Parameter für links nicht angegeben if(m_right<1)m_right=m_left; // Parameter für rechts nicht angegeben if(m_left<1 && m_right<1){ // Beide Parameter nicht angegeben m_left=2; m_right=2; } m_start=m_left+m_right; // verschieben des Startpunkts m_period=m_start+1; // Anzahl der Bars des Intervalls }
Zuerst werden die Parameterwerte verifiziert. Wenn einige von ihnen nicht positiv (nicht gesetzt) sind, wird ihm der Wert des zweiten Parameters zugewiesen. Wenn kein einziger Parameter gesetzt ist, werden ihnen die Standardwerte zugewiesen (2). Danach werden der Abstand der Anfangsbar für die Suche nach einem Extremum (m_start) und die Gesamtzahl der Extrema (m_period) berechnet.
Die Methode Calculate() ist identisch mit der Standardfunktion OnCalculate(), erhält aber nur die benötigten Preisarrays: time[], high[], low[] und die Indikatorpuffer osc[] (Oszillatordaten), buy[] und sell[] (Pfeile). Wie üblich wird der Bereich der berechneten Bars in der Funktion OnCalculte() definiert. Die Extrema der Indikatoren (die Funktionen ArrayMaximum() und ArrayMinimum()) werden anschließend in der Standardschleife des Indikators definiert. Nach dem Erkennen eines Extremums wird die Methode AddExtremum() aufgerufen, um Daten zum Array m_upper[] oder m_lower[] hinzuzufügen. Am Ende wird die Methode CheckDiver() aufgerufen, um Daten aus den Arrays mit Extremwerten zu analysieren. Wenn eine Divergenz erkannt wird, werden die Pfeile platziert.
void Calculate( const int rates_total, const int prev_calculated, const datetime &time[], const double &high[], const double &low[], double & osc[], double & buy[], double & sell[] ){ int start; // Variable de Indexes der Anfangsbar if(prev_calculated==0){ // vollständige Berechnung des Indikators start=m_period; // Definieren der Anfangsbar für die Berechnung m_LastTime=0; // Rücksetzen der Variablen zur Definition einer neuen Bar Cur.Reset(); // Rücksetzen der Hilfsstrukturen Pre.Reset(); // Rücksetzen der Hilfsstrukturen } else{ // Berechnen nur der neuen Bars start=prev_calculated-1; // Berechnen des Index der Bar, ab der die Berechnung fortgesetzt wird } for(int i=start;i<rates_total;i++){ // Hauptschleife des Indikators if(time[i]>m_LastTime){ // neue Bar m_LastTime=time[i]; Pre=Cur; } else{ // Neuberechnen derselben Bar Cur=Pre; } // Berechnen der Suchparameter für die Hochs/Tiefs int sb=i-m_start; // Index der Bar, ab der das Intervall beginnt int mb=i-m_right; // Index der Bar mit einem Hoch/Tief if(ArrayMaximum(osc,sb,m_period)==mb){ // es ist ein Hoch // Hinzufügen des Hochs zum Array this.AddExtremum(m_upper,Cur.UpperCnt,osc[mb],high[mb],mb,i,time[mb]); } if(ArrayMinimum(osc,sb,m_period)==mb){ // es ist ein Tief // Hinzufügen des Tiefs zum Array this.AddExtremum(m_lower,Cur.LowerCnt,osc[mb],low[mb],mb,i,time[mb]); } // Prüfen auf eine Divergenz this.CheckDiver(i,Cur.UpperCnt,Cur.LowerCnt,time,high,low,buy,sell,osc); } }
Wir betrachten diesen Code im Detail. Gleich zu Beginn der Schleife:
if(time[i]>m_LastTime){ // neue Bar m_LastTime=time[i]; Pre=Cur; } else{ // Neuberechnen derselben Bar Cur=Pre; }
In der Basisklasse wird die Variable m_LastTime deklariert. Überschreitet die Bar time[i] die Variable, wird die Bar zum ersten Mal berechnet. Die Zeit der Bar wird der Variablen m_LastTime zugewiesen, während der Variable Pre der Wert der Variablen Cur zugewiesen wird. Umgekehrt wird beim Neuberechnen der gleichen Bars die Variable Pre der Variablen Cur zugewiesen. Die Verwendung der Variablen Cur und Pre wird in diesem Artikel im Detail beschrieben. Die Pre- und Cur-Variablen sind vom Typ SPseudoBuffers1, wie in der Datei UniDiverDefines beschrieben:
struct SPseudoBuffers1{ int UpperCnt; int LowerCnt; void Reset(){ UpperCnt=0; LowerCnt=0; } };
Die Struktur enthält zwei Felder:
- UpperCount — Anzahl der verwendeten Elemente im Array m_upper[];
- LowerCount — Anzahl der verwendeten Elemente im Array m_lower[];
Die Methode Reset() dient dem schnellen Rücksetzen aller Felder der Struktur.
Nach der Arbeit mit den Variablen Cur und Pre werden die Indices für die Suche nach den Extrema berechnet:
// Berechnen der Parameter zur Suche nach Hochs/Tiefs int sb=i-m_start; // Index der Bar, ab der das Intervall beginnt int mb=i-m_right; // Index der Bar mit einem Hoch/Tief
Der Index der Bar, ab dem die Suche nach den Hochs/Tiefs beginnt, wird der Variablen 'sb' zugewiesen. Der Index der Bar, die ein Hoch/Tief aufweisen könnten, wird der Variablen 'mb' zugewiesen.
Definieren Sie das Hoch oder Tief mit den Funktionen ArrayMaximum() und ArrayMinimum():
if(ArrayMaximum(osc,sb,m_period)==mb){ // es ist ein Hoch // Hinzufügen eines Hochs zum Array this.AddExtremum(m_upper,Cur.UpperCnt,osc[mb],high[mb],mb,i,time[mb]); } if(ArrayMinimum(osc,sb,m_period)==mb){ // es ist ein Tief // Hinzufügen eines Tiefs zum Array this.AddExtremum(m_lower,Cur.LowerCnt,osc[mb],low[mb],mb,i,time[mb]); }
Wenn die Funktion ArrayMaximum() oder ArrayMinimum() 'mb' zurückgibt, bedeutet dies, dass die angegebene Anzahl der Bars links und rechts des Hochs oder Tiefs angeordnet ist. Dies wiederum zeigt an, dass sich das gesuchte Hoch/Tief gebildet hat. Die Methode AddExtremum() wird aufgerufen und die Daten werden dem Array m_upper[] oder m_lower[] hinzugefügt.
Betrachten wir die einfache Methode AddExtremum():
void AddExtremum( SExtremum & a[], // Hinzufügen zu dem Array int & cnt, // Anzahl der belegten Arrayelemente double iv, // Indikatorwert double pv, // Preis int mb, // Index der Bar mit einem Extremum int sb, // Index der Bar, für die eine gültige Formation eines Extremums erkannt worden ist datetime et // Zeitstempel der Bar mit einem Extremum ){ if(cnt>=ArraySize(a)){ //das Array ist ausgefüllt // Erhöhen der Arraygröße ArrayResize(a,ArraySize(a)+1024); } // Ergänzen neuer Daten a[cnt].IndicatorValue=iv; // Indikatorwert a[cnt].PriceValue=pv; // Preis a[cnt].ExtremumBar=mb; // Index der Bar mit einem Extremum a[cnt].SignalBar=sb; // Index der Bar, für die eine gültige Formation eines Extremums erkannt worden ist a[cnt].ExtremumTime=et; // Zeitstempel der Bar mit einem Extremum cnt++; // erhöhen des Zählers für die belegten Elemente }
Das Array a[], dem die neuen Daten hinzugefügt werden sollen, wird über die Parameter an die Methode übergeben. Dies kann das Array m_upper[] oder m_lower[] sein. Die Anzahl der belegten Elemente des Arrays a[] wird über die Variable 'cnt' übergeben. Dies kann die Variable Cur.UpperCnt or Cur.LowerCnt sein. Das Array a[] und die Variable 'cnt' werden als Referenz übergeben, da sie in der Methode geändert werden.
iv - Indikatorwert auf Basis des Extremwertes, pv - Preis auf der Bar mit einem Extremwert, mb - Index der Bar mit einem Extremwert, sb - Signalbar (bei der das Extremum bekannt geworden ist), et - Zeitstempel der Bar mit einem Extremum.
Die Arraygröße wird am Anfang der Methode AddExtremum() geprüft. Wenn er voll ist, vergrößert sich seine Größe auf bis zu 1024 Elemente. Die Daten werden hinzugefügt und die Variable 'cnt' wird anschließend erhöht.
Wir werden uns die Methode CheckDiver() später anschauen.
Festlegung einen Extremums durch einen Schwellenwert.
Die Klasse für die Definition durch einen Schwellenwert unterscheidet sich von der Klasse der Definition durch Bars hauptsächlich in den Variablentypen Cur und Pre: dies ist der Typ SPseudoBuffers2, der in der Datei UniDiverDefines.mqh beschrieben wird:
struct SPseudoBuffers2{ int UpperCnt; int LowerCnt; double MinMaxVal; int MinMaxBar; int Trend; void Reset(){ UpperCnt=0; LowerCnt=0; MinMaxVal=0; MinMaxBar=0; Trend=1; } };
Die Struktur SPseudoBuffers2 hat die gleichen Felder wie SPseudoBuffers1 und einige weitere:
- MinMaxVal — Variable für den maximalen oder minimalen Indikatorwert
- MinMaxBar — Variable für den Index der Bar, auf dem der maximale oder minimale Indikatorwert gefunden wird
- Trend — Variabele für die Bewegungsrichtung des Indikators. Der Wert 1 bedeutet, dass sich der Indikator steigt und sein Maximum kontrolliert wird. Bei -1 wird das Minimum verfolgt.
Der externe Parameter mit dem Schwellwert - die Variable MinMaxThreshold - wird an den Klassenkonstruktor übergeben. Sein Wert wird in der im Abschnitt 'private' deklarierten Variablen m_threshold gespeichert.
Die Methode Calculate() dieser Klasse unterscheidet sich in der Methode für die Definition der Extrema:
switch(Cur.Trend){ // aktuelle Richtung des Indikators case 1: // steigt if(osc[i]>Cur.MinMaxVal){ // neues Maximum Cur.MinMaxVal=osc[i]; Cur.MinMaxBar=i; } if(osc[i]<Cur.MinMaxVal-m_threshold){ // Schwellenwert überschritten // Hinzufügen eines Hochs zum Array this.AddExtremum(m_upper,Cur.UpperCnt,Cur.MinMaxVal,high[Cur.MinMaxBar],Cur.MinMaxBar,i,time[Cur.MinMaxBar]); Cur.Trend=-1; // Wechseln der Richtung Cur.MinMaxVal=osc[i]; // Wert des Anfangsminimum Cur.MinMaxBar=i; // Bar mit dem Anfangsminimum } break; case -1: // fällt if(osc[i]<Cur.MinMaxVal){ // neues Minimum Cur.MinMaxVal=osc[i]; Cur.MinMaxBar=i; } if(osc[i]>Cur.MinMaxVal+m_threshold){ // Schwellenwert überschritten // Hinzufügen des Tiefs zum Array this.AddExtremum(m_lower,Cur.LowerCnt,Cur.MinMaxVal,low[Cur.MinMaxBar],Cur.MinMaxBar,i,time[Cur.MinMaxBar]); Cur.Trend=1; // Wechseln der Richtung Cur.MinMaxVal=osc[i]; // Wert des Anfangsmaximum Cur.MinMaxBar=i; // Bar mit dem Anfangsmaximum } break; }
Wenn die Variable Cur.Trend 1 ist, wird der Oszillatorwert mit dem Wert von Cur.MinMaxValue verglichen. Wenn der neue Oszillatorwert den Wert aus der Variablen überschreitet, wird der Variablenwert aktualisiert. Der Index der Bar, die ein neues Hoch aufweist, wird der Variablen Cur.MinMaxBar zugewiesen. Es ist auch sichergestellt, dass der Oszillatorwert nicht um den Wert m_threshold vom zuletzt bekannten Maximum gefallen ist. Wenn doch, hat der Oszillator seine Richtung geändert. Die Methode AddExtremum() wird aufgerufen, die Daten des neuen Extremums werden im Array gespeichert, der aktuelle Trendwert wird durch den entgegengesetzten Wert ersetzt, während die Initialparameter des neuen Minimums in den Variablen Cur.MinMaxVal und Cur.MinMaxBar gesetzt werden. Da sich der Wert der Variable Aktueller Trend geändert hat, wird von nun an ein anderer 'case'-Abschnitt ausgeführt, der die minimalen Oszillatorwerte das Einhalten des Schwellenwertes verfolgt — falls die Ausführung fortgesetzt wird.
Definieren von Extrema durch ihre Position relativ zur Mittellinie des Oszillators. Der Typ des verwendeten Oszillators wird an den Klassenkonstruktor übergeben. Je nach Typ wird der Mittelwert des Oszillators definiert:
void CDiverMiddle(EOscUniType type){ if(type==OscUni_Momentum){ m_level=100.0; } else if(type==OscUni_RSI || type==OscUni_Stochastic){ m_level=50.0; } else if(type==OscUni_WPR){ m_level=-50.0; } else{ m_level=0.0; } }
Für das Momentum ist der Wert 100, für RSI und Stochastic 50, für WPR -50, für andere Oszillatoren der Wert 0.
Die Methode zur Bestimmung der Extrema ähnelt in vielerlei Hinsicht der Schwellwertmethode:
switch(Cur.Trend){ case 1: if(osc[i]>Cur.MinMaxVal){ Cur.MinMaxVal=osc[i]; Cur.MinMaxBar=i; } if(osc[i]<m_level){ this.AddExtremum(m_upper,Cur.UpperCnt,Cur.MinMaxVal,high[Cur.MinMaxBar],Cur.MinMaxBar,i,time[Cur.MinMaxBar]); Cur.Trend=-1; Cur.MinMaxVal=osc[i]; Cur.MinMaxBar=i; } break; case -1: if(osc[i]<Cur.MinMaxVal){ Cur.MinMaxVal=osc[i]; Cur.MinMaxBar=i; } if(osc[i]>m_level){ this.AddExtremum(m_lower,Cur.LowerCnt,Cur.MinMaxVal,low[Cur.MinMaxBar],Cur.MinMaxBar,i,time[Cur.MinMaxBar]); Cur.Trend=1; Cur.MinMaxVal=osc[i]; Cur.MinMaxBar=i; } break; }
Der einzige Unterschied besteht darin, dass die Richtungsänderung durch einen Vergleich mit der mittleren Ebene des Oszillators erfolgt: osc[i]<m_level oder osc[i]>m_level.
Methode zur Festlegung der Divergenzart. Fügen Sie den externen Parametern die Variable zur Auswahl des Typs der erkannten Divergenz hinzu:
input int Number = 3;
Standardmäßig ist der Wert des Parameters 3. Gemäß Abb. 11 bedeutet das eine klassische Divergenz. Insgesamt werden in Abb. 11 9 Kombinationen aus Kurs- und Indikatorbewegung dargestellt. Ergänzen wir jetzt noch eine weitere Option — "not checked". Jetzt haben wir 10 Kombinationen. Mit den normalen Dezimalzahlen können wir also jede beliebige Kombination verschiedener Bewegungen mit einer Zahl beschreiben (einschließlich einer dreifachen Divergenz). Es stellt sich heraus, dass eine einstellige Zahl einer einfachen Divergenz entspricht (auf zwei Hochs oder Tiefs), eine zweistellige Zahl ist eine dreifache Zahl usw. Beispielsweise entspricht die Zahl 13 der in Abb. 14 dargestellten Kombination.
Abb. 14. Kombination von Indikator und
Preisen für Number=13 für ein Verkaufssignal
Klassen zur Prüfung der Divergenzbedingungen. Die Basis- und die ableiteten Klassen werden zur Überprüfung der Divergenzbedingungen angelegt. Der Wert des Parameters Number wird beim Start des Indikators analysiert. Das wichtigste Attribut ist seine Länge, da es die Größe des Arrays von Zeigern auf Klassen beeinflusst. Anschließend wird für jedes Arrayelement entsprechend des Typs der Bedingung das entsprechende Objekt generiert.
Die Klassen für die Verifikation der Bedingungen befinden sich in der Datei UniDiver/CUniDiverConditions.mqh. Die Basisklasse heißt CDiverConditionsBase. Schauen wir sie uns mal an:
class CDiverConditionsBase{ protected: double m_pt; double m_it; public: void SetParameters(double pt,double it){ m_pt=pt; m_it=it; } virtual bool CheckBuy(double i1,double p1,double i2,double p2){ return(false); } virtual bool CheckSell(double i1,double p1,double i2,double p2){ return(false); } };
Die Klasse verfügt über zwei virtuelle Methoden zum Vergleich zweier benachbarter Hochs. Ihre Parameter werden in der Klasse übergeben:
- i1 — Indikatorwert im Punkt 1
- p1 — Preis im Punkt 1
- i2 — Indikatorwert im Punkt 2
- p2 — Preis im Punkt 2
Die Punkte werden von rechts nach links gezählt, beginnend mit 1.
Mit der Methode SetParameters() werden zusätzliche Vergleichsparameter gesetzt: pt - verfügbare Differenz der Preiswerte, bei der angenommen wird, dass die Preise auf einer Ebene liegen, it - ähnlicher Parameter für den Vergleich der Hochs des Indikators. Die Werte dieser Parameter werden über das Fenster der Eigenschaften festgelegt:
input double IndLevel = 0; input int PriceLevel = 0;
Der Code einer der abgeleiteten Klassen ist unten aufgeführt:
class CDiverConditions1:public CDiverConditionsBase{ private: public: bool CheckBuy(double i1,double p1,double i2,double p2){ return((p1>p2+m_pt) && (i1>i2+m_it)); } bool CheckSell(double i1,double p1,double i2,double p2){ return((p1<p2-m_pt) && (i1<i2-m_it)); } };
In der Methode für CheckBuy() wird geprüft, ob der Preis im Punkt 1 den des Punktes 2 überschreitet. Dasselbe gilt für den Indikator: Der Preis im Punkt 1, der den von Punkt 2 übersteigen. Die Methode CheckSell() ist spiegelsymmetrisch zur Methode CheckBuy(). Alle anderen Klassen sind ähnlich und unterscheiden sich nur in logischen Ausdrücken, außer CDiverConditions0. Die Methoden CheckSell() und CheckBuy() liefern in dieser Klasse sofort 'true' zurück. Sie werden verwendet, wenn die Bedingungsprüfung deaktiviert ist (jede Option ist möglich).
Vorbereitung zur Prüfung der Bedingungen von Divergenzen. Das Array und die Variable für seine Größe werden im Abschnitt 'protected' der Klasse CDiverBase deklariert:
CDiverConditionsBase * m_conditions[];
int m_ccnt;
In der Methode SetConditions() wird die Größe des Arrays m_conditions geändert und die Verifikationsobjekte für die Divergenzbedingungen erzeugt:
void SetConditions(int num, // Divergenzzahl double pt, // PriceLevel Parameter double it){ // IndLevel Parameter if(num<1)num=1; // die Divergenzzahl sollte nicht kleiner als 1 sein ArrayResize(m_conditions,10); // Maximalzahl möglicher Bedingungen m_ccnt=0; // Zähler der aktuellen Zahl von Bedingungen while(num>0){ int cn=num%10; // Varianten der Divergenz zwischen neuen Paaren von Extrema m_conditions[m_ccnt]=CreateConditions(cn); // Erstellen eines Objektes m_conditions[m_ccnt].SetParameters(pt,it); // Setzen der Parameter zur Verifikation der Bedingungen num=num/10; // Wechseln zur nächsten Bedingung m_ccnt++; // Zählen der Bedingung } // Anpassen der Arraygröße gemäß der aktuellen Zahl von Bedingungen ArrayResize(m_conditions,m_ccnt); }
Die folgenden Parameter werden der Methode übergeben:
- num — Anzahl, externer Parameter;
- pt — PriceLevel, externer Parameter;
- it — IndLevel, externer Parameter.
Der Parameter 'num' wird als erster geprüft:
if(num<1)num=1; // die Divergenzzahl sollte nicht kleiner als 1 sein
Dann erhöht sich das Array m_conditions bis zur maximal möglichen Größe (10 - Länge des Maximalwertes der Variablen 'int'). Danach wird das Objekt der Zustandsüberprüfung durch die Methode CreateConditions() erstellt und die Parameter werden mit der Methode SetParameters() in der 'while' Schleife gesetzt, abhängig vom Wert der einzelnen Ziffern von 'num'. Nach der Schleife ändert sich die Größe des Arrays entsprechend der tatsächlichen Anzahl der angewandten Bedingungen.
Betrachten wir die Methode CreateConditions():
CDiverConditionsBase * CreateConditions(int i){ switch(i){ case 0: return(new CDiverConditions0()); break; case 1: return(new CDiverConditions1()); break; case 2: return(new CDiverConditions2()); break; case 3: return(new CDiverConditions3()); break; case 4: return(new CDiverConditions4()); break; case 5: return(new CDiverConditions5()); break; case 6: return(new CDiverConditions6()); break; case 7: return(new CDiverConditions7()); break; case 8: return(new CDiverConditions8()); break; case 9: return(new CDiverConditions9()); break; } return(new CDiverConditions0()); }
Die Methode ist einfach: Abhängig vom Parameter i wird das entsprechende Objekt erzeugt und die Referenz darauf zurückgegeben.
Definition der Divergenz. Jetzt können wir die Methode CheckDivergence() der Klasse CDiverBase-Klasse betrachten. Zuerst betrachten wir den Code der Methode insgesamt, dann im Einzelnen:
void CheckDiver( int i, // Berechnen des Index der Bar int ucnt, // Anzahl der Hochs im Array m_upper int lcnt, // Anzahl der Tiefs im Array m_lower const datetime & time[], // Array der Zeitstempel der Bars const double &high[], // Array der Preise der Hochs const double &low[], // Array der Preise der Tiefs double & buy[], // Indikatorpuffer für die Pfeile nach oben double & sell[], // Indikatorpuffer für die Pfeile nach unten double & osc[] // Indikatorpuffer für Werte des Oszillators ){ // Löschen der Puffer für die Pfeile buy[i]=EMPTY_VALUE; sell[i]=EMPTY_VALUE; // Löschen der Grafikobjekte this.DelObjects(time[i]); // für die Hochs (Verkaufssignale) if(ucnt>m_ccnt){ // Anzahl der Hochs genügt if(m_upper[ucnt-1].SignalBar==i){ // ein Hoch erkannt auf der aktuellen Bar bool check=true; // Angenommen, eine Divergenz ist aufgetreten for(int j=0;j<m_ccnt;j++){ // für alle Hoch-Paare // Verifizieren der Bedingungen eines neuen Hoch-Paares bool result=m_conditions[j].CheckSell( m_upper[ucnt-1-j].IndicatorValue, m_upper[ucnt-1-j].PriceValue, m_upper[ucnt-1-j-1].IndicatorValue, m_upper[ucnt-1-j-1].PriceValue ); if(!result){ // Bedingung nicht erfüllt check=false; // keine Divergenz break; } } if(check){ // Divergenz ist aufgetreten // Setzen des Puffers des Indikators der Pfeile sell[i]=osc[i]; // Zeichnen zuständlicher Linien und/oder Pfeil auf dem Chart this.DrawSellObjects(time[i],high[i],ucnt); } } } // für Tiefs (Kaufsignale) if(lcnt>m_ccnt){ if(m_lower[lcnt-1].SignalBar==i){ bool check=true; for(int j=0;j<m_ccnt;j++){ bool result=m_conditions[j].CheckBuy( m_lower[lcnt-1-j].IndicatorValue, m_lower[lcnt-1-j].PriceValue, m_lower[lcnt-2-j].IndicatorValue, m_lower[lcnt-2-j].PriceValue ); if(!result){ check=false; break; } } if(check){ buy[i]=osc[i]; this.DrawBuyObjects(time[i],low[i],lcnt); } } } }
Die folgenden Parameter werden der Methode übergeben:
- i — Index der gerade berechneten Bar;
- ucnt — Anzahl der angewendeten Elemente des Arrays m_upper[];
- lcnt — Anzahl der angewendeten Elemente des Arrays m_lower[];
- time[] — Array mit den Zeitstempel der Bars;
- high[] — Array mit den Hochs der Preisbars;
- low[] — Array mit den Tiefs der Preisbars;
- buy[] — Indikatorpuffer der Pfeile für Kauf;
- sell[] — Indikatorpuffer der Pfeile für Verkauf;
- osc[] — Indikatorpuffer der Werte des Oszillators.
Die Puffer mit den Pfeilen werden zuerst gelöscht:
// Löschen der Puffer für die Pfeile buy[i]=EMPTY_VALUE; sell[i]=EMPTY_VALUE;
Die Grafikobjekte der gerade berechneten Bar werden gelöscht:
// Löschen der Grafikobjekte this.DelObjects(time[i]);
Abgesehen von Pfeilen verwendet iDivergence grafische Objekte, um auf dem Kurschart Pfeile und Linien, die die Extrema der Preise mit denen des Indikators verbinden, zu zeichnen.
Dann gibt es zwei identische Codeteile, um die Bedingungen für Kauf und Verkauf zu überprüfen. Betrachten wir den ersten Abschnitt zum Verkaufen. Die Anzahl der verfügbaren Oszillatorspitzen sollte 1 mehr sein als die Anzahl der geprüften Bedingungen. Daher wird die Prüfung durchgeführt:
// für die Hochs (Verkaufssignale) if(ucnt>m_ccnt){ // es gibt eine ausreichende Zahl von Hochs }
Danach prüfen wir, ob der obere Teil der berechneten Bar vorhanden ist. Dies wird durch die Übereinstimmung zwischen dem Index der berechneten Bar und dem Index aus dem Array mit den Daten von Hoch/Tief bestimmt:
if(m_upper[ucnt-1].SignalBar==i){ // in Hoch erkannt auf der aktuellen Bar }
Eine Hilfsvariable ist für das Ergebnis der Prüfung der Bedingungen erforderlich:
bool check=true; // Angenommen, eine Divergenz ist aufgetreten
Die Schleife 'for' geht über alle Bedingungen und übergibt die Daten der Hochs:
for(int j=0;j<m_ccnt;j++){ // für alle Hoch-Paare // Prüfen, ob die Bedingungen des nächsten Hochpaares erfüllt sind bool result=m_conditions[j].CheckSell( m_upper[ucnt-1-j].IndicatorValue, m_upper[ucnt-1-j].PriceValue, m_upper[ucnt-1-j-1].IndicatorValue, m_upper[ucnt-1-j-1].PriceValue ); if(!result){ // Bedingung nicht erfüllt check=false; // keine Divergenz break; } }
Wenn eine der Bedingung nicht erfüllt ist, wird die die Schleife verlassen, und der Variablen 'check' wird 'false' zugewiesen. Wenn alle Bedienungen erfüllt sind, wird der 'check' 'true' zugewiesen und es wird ein Grafikobjekt gezeichnet:
if(check){ // Divergenz ist aufgetreten // Platzieren des Pfeils des Indikatorpuffers sell[i]=osc[i]; // Zeichne Hilfslinien und/oder einen Pfeil auf dem Preischart this.DrawSellObjects(time[i],high[i],ucnt); }
Darstellung der grafischen Objekte können im Fenster für die Eigenschaften des Indikators der aktiviert/deaktiviert werden. Die folgenden Variablen sind dafür deklariert:
input bool ArrowsOnChart = true; input bool DrawLines = true; input color ColBuy = clrAqua; input color ColSell = clrDeepPink;
- ArrowsOnChart — Aktivieren der Pfeile auf dem Preischart
- DrawLines — Aktivieren der Linien, die die Preise die Hochs/Tiefs des Indikators verbinden
- ColBuy and ColSell — Farben der Grafikobjekte der Signale für Kauf und Verkauf.
Im Abschnitt 'protected' der Klasse CDiverBase sind die entsprechenden Variablen deklariert:
bool m_arrows; // entspricht der Variablen ArrowsOnChart bool m_lines; // entspricht der Variablen DrawLines color m_cbuy; // entspricht der Variablen ColBuy color m_csell; // entspricht der Variablen ColSell
Die Werte dieser Variablen werden in der Methode SetDrawParmeters () bestimmt:
void SetDrawParmeters(bool arrows,bool lines,color cbuy,color csell){ m_arrows=arrows; m_lines=lines; m_cbuy=cbuy; m_csell=csell; }
Betrachten Sie die Methoden, die mit den grafischen Objekten arbeiten. Entfernen:
void DelObjects(datetime bartime){ // Aktivieren des Zeichnens der Linien if(m_lines){ // Bilden des gemeinsamen Präfix string pref=MQLInfoString(MQL_PROGRAM_NAME)+"_"+IntegerToString((long)bartime)+"_"; for(int j=0;j<m_ccnt;j++){ // für die Zahl der Bedingungen der Divergenzen ObjectDelete(0,pref+"bp_"+IntegerToString(j)); // Linie im Preischart im Falle eines Kaufsignals ObjectDelete(0,pref+"bi_"+IntegerToString(j)); // Linie im Indikatorfenster im Falles eines Kaufsignals ObjectDelete(0,pref+"sp_"+IntegerToString(j)); // Linie im Preischart im Falles eines Verkaufssignals ObjectDelete(0,pref+"si_"+IntegerToString(j)); // Linie im Indikatorfenster im Falles eines Verkaufssignals } } if(m_arrows){ // Aktivieren der Pfeile auf dem Preischart // ObjectDelete(0,MQLInfoString(MQL_PROGRAM_NAME)+"_"+IntegerToString((long)bartime)+"_ba"); // ObjectDelete(0,MQLInfoString(MQL_PROGRAM_NAME)+"_"+IntegerToString((long)bartime)+"_sa"); } }
Linien, die die Hochs/Tiefs verbinden, werden getrennt von den Pfeilen entfernt. Die Namen aller grafischen Objekte beginnen mit dem Indikatornamen, gefolgt vom Zeitstempel der Bar, an dem die Divergenz erkannt wurde. Die Namen werden in der Schleife entsprechend der Anzahl der m_ccnt-Bedingungen gebildet. Bei Kaufsignalen wird "bp" auf dem Preischart und "bi" im Indikatorfenster angezeigt. Ebenso werden für Verkaufssignale "sp" und "ip" hinzugefügt. Der Index j wird am Ende des Namens hinzugefügt. Den Pfeilnamen werden entweder "_ba" (Kaufsignalpfeil) oder "_sa" (Verkaufssignalpfeil) angehängt.
Die Erstellung von grafischen Objekten erfolgt in den Methoden DrawSellObjects() und DrawBuyObjects(). Betrachten wir eine davon:
void DrawSellObjects(datetime bartime,double arprice,int ucnt){ if(m_lines){ // aktivieren der Anzeige der Linien // Bilden des gemeinsamen Präfix string pref=MQLInfoString(MQL_PROGRAM_NAME)+"_"+IntegerToString((long)bartime)+"_"; for(int j=0;j<m_ccnt;j++){ // für alle Bedingungen der Divergenzen // Linie auf dem Preischart fObjTrend( pref+"sp_"+IntegerToString(j), m_upper[ucnt-1-j].ExtremumTime, m_upper[ucnt-1-j].PriceValue, m_upper[ucnt-2-j].ExtremumTime, m_upper[ucnt-2-j].PriceValue, m_csell); // Linie im Indikatorfenster fObjTrend( pref+"si_"+IntegerToString(j), m_upper[ucnt-1-j].ExtremumTime, m_upper[ucnt-1-j].IndicatorValue, m_upper[ucnt-2-j].ExtremumTime, m_upper[ucnt-2-j].IndicatorValue, m_csell, ChartWindowFind(0,MQLInfoString(MQL_PROGRAM_NAME))); } } if(m_arrows){ // Pfeile auf dem Chart sind aktiviert fObjArrow(MQLInfoString(MQL_PROGRAM_NAME)+"_"+IntegerToString((long)bartime)+"_sa", bartime, arprice, 234, m_csell, ANCHOR_LOWER); } }
Objektnamen werden gebildet so wie wieder gelöscht werden. Danach werden die grafischen Objekte mit den Funktionen fObjTrend() und fObjArrow() erzeugt. Sie befinden sich in der "Includedatei" UniDiver/UniDiverGObjects.mqh.
Fertigstellung des Indikators. Wir müssen nur die angelegten Klassen in den Indikator übernehmen. Erstellen Sie das entsprechende Objekt in der Funktion OnInit(), abhängig vom gewählten Typ der Definition der Extrema:
switch(ExtremumType){ case ExtrBars: diver=new CDiverBars(LeftBars,RightBars); break; case ExtrThreshold: diver=new CDiverThreshold(MinMaxThreshold); break; case ExtrMiddle: diver=new CDiverMiddle(Type); break; }
Einer der externen Parameter - PriceLevel - wird in Points gemessen, so dass es vorteilhaft ist, ihn in Abhängigkeit von der Anzahl der Nachkommastellen zu korrigieren. Deklarieren Sie dazu eine weitere Variable, um diese Korrektur abschalten zu können:
input bool Auto5Digits = true;
Dann wird eine Hilfsvariable für diese Korrektur in der Funktion OnInit() deklariert:
int pl=PriceLevel; if(Auto5Digits && (Digits()==5 || Digits()==3)){ pl*=10; }
Festlegen der Parameter für die Divergenz und die Darstellung:
diver.SetConditions(Number,Point()*pl,IndLevel);
diver.SetDrawParmeters(ArrowsOnChart,DrawLines,ColBuy,ColSell);
Mehrere Zeichenketten verbleiben in der Funktion OnCalculate(). Aufruf der Basismethode Calculate():
diver.Calculate( rates_total, prev_calculated, time, high, low, buf_osc, buf_buy, buf_sell);
Falls grafischen Objekten verwendet werden, sollten wir das Zeichnen beschleunigen:
if(ArrowsOnChart || DrawLines){ ChartRedraw(); }
Wenn der Indikator seine Arbeit beendet hat, sollten die grafischen Objekte entfernt werden. Dies geschieht im Destruktor der Klasse CDiverBase. Dort werden auch die Verifikationsobjekte der Divergenzbedingungen entfernt:
void ~CDiverBase(){ for(int i=0;i<ArraySize(m_conditions);i++){ // für alle Bedingungen if(CheckPointer(m_conditions[i])==POINTER_DYNAMIC){ delete(m_conditions[i]); // Lösche Objekt } } // Löschen der Grafikobjekte ObjectsDeleteAll(0,MQLInfoString(MQL_PROGRAM_NAME)); ChartRedraw(); }
Zu diesem Punkt ist die Hauptphase der Entwicklung des Indikators abgeschlossen. Abb. 15 zeigt der Chart mit dem dazugehörigen Indikator (im Subfenster), der aktivierten Anzeige von Pfeilen auf dem Kurschart und Linien zwischen den Spitzen.
Abb. 15. Der Divergenzindikator auf dem Kurschart mit Pfeilen, die auf dem Kurs-Chart und der Verbindungslinie zwischen den Extrempunkten angezeigt werden.
Jetzt müssen wir nur noch die Alarmfunktion hinzufügen. Das ist sehr einfach und wurde bereits in anderen Artikeln beschrieben. In den folgenden Anlagen finden Sie ein fertigen Indikator mit Alarmfunktion sowie alle für den Indikator notwendigen Dateien.
Schlussfolgerung
Trotz der Vielseitigkeit des Indikators können wir auch Nachteile feststellen. Der Hauptnachteil ist die Abhängigkeit des Parameters IndLevel von dem Typus des verwendeten Oszillators, sowie die Abhängigkeit des Parameters PriceLevel vom Zeitrahmen. Um diese Abhängigkeit auszuschließen, sind die Standardwerte für diese Parameter 0. Gleichzeitig ist es aber fast unmöglich, die Bedingungen für bestimmte Kombinationen von Preis- und Indikatorbewegung zu erfüllen. Wird die Prüfung auf eine horizontale Bewegung in die Divergenzverifikation einbezogen, ist deren Durchführung unwahrscheinlich. In diesem Fall bleiben die Divergenzoptionen 1,3,7 und 9 erhalten. Dies kann ein Problem nur dann sein, wenn der Tester verwendet wird, um ein EA mit dem Indikator zu optimieren.
Bei der richtigen Vorgehensweise ist dies kein Problem, da die Optimierung in der Regel auf ein einziges Symbol und einen einzigen Zeitrahmen ausgerichtet ist. Zunächst müssen wir das verwendete Symbol und den Zeitrahmen bestimmen und den entsprechenden Wert für den Parameter PriceLevel einstellen. Dann müssen wir den angewandten Oszillator auswählen und den entsprechenden Wert für den Parameter IndLevel einstellen. Es hat keinen Sinn, eine automatische Optimierung des verwendeten Oszillatortyps und der Parameterwerte von PriceLevel und IndLevel anzustreben, da es viele weitere Optimierungsparameter gibt. Dies ist zunächst einmal die Art der Divergenz (die Variable 'Number') und die Periodenlänge des Oszillators.
Anlagen
Für die Verwendung von iDivergence benötigen Sie einen Universaloszillator aus dem Artikel "Der universell Oszillator mit dem graphischen Interface". Der Oszillator und alle notwendigen Dateien befinden sich in der Anwendung.
Alle beigefügte Dateien:
- Include/InDiver/CUniDiverConditions.mqh — Datei mit den Klassen zur Prüfung der Bedingungen einer Divergenz;
- Include/InDiver/CUniDiverExtremums.mqh — Datei mit den Klassen zur Definition der Extrema;
- Include/InDiver/UniDiverDefines.mqh — Beschreibung der Strukturen und Enumerationen;
- Include/InDiver/UniDiverGObjects.mqh — Funktionen, die mit den Grafikobjekten arbeiten;
- Indicators/iDivergence.mq5 — Indikator;
- Indicators/iUniOsc.mq5 — Universaloszillator;
- Include/UniOsc/CUniOsc.mqh — Datei mit den Klassen des Universaloszillators;
- Include/UniOsc/UniOscDefines.mqh — Beschreibung der Strukturen und Enumerationen für den Universaloszillator.
Übersetzt aus dem Russischen von MetaQuotes Ltd.
Originalartikel: https://www.mql5.com/ru/articles/3460
- 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.