
Einführung in MQL5 (Teil 15): Ein Anfängerleitfaden zur Erstellung nutzerdefinierter Indikatoren (IV)
Einführung
Willkommen zurück zur Serie Einführung in MQL5! Sie werden feststellen, dass dieser Artikel direkt auf den Ideen und Techniken aufbaut, die wir bereits in früheren Artikeln besprochen haben. Da wir viel von dem verwenden werden, was wir bisher gelernt haben, wird dieser Teil eher eine Fortsetzung als ein Neuanfang sein. Inzwischen sollten Sie ein solides Verständnis der MQL5-Grundlagen haben, und in diesem Artikel gehen wir einen Schritt weiter, indem wir dieses Wissen kombinieren, um interessantere nutzerdefinierte Indikatoren zu entwickeln.
Sie sind nur so gut wie die Projekte, an denen Sie in MQL5 gearbeitet haben. Deshalb ist diese Serie immer projektbezogen. Das ist die nützlichste Methode, um zu lernen und sich weiterzuentwickeln. In diesem Teil der Serie werden wir einen Indikator erstellen, der Trends erkennen, Strukturbrüche nutzen und Kauf- und Verkaufssignale erzeugen kann. Der Einstiegspunkt, der Stop-Loss und mehrere Take-Profit-Levels sind in diesen Signalen enthalten, sodass Sie eine umfassende Strategie erhalten, die Sie testen und ausbauen können. In diesem Beitrag erfahren Sie, wie Sie mit Hilfe von Preisaktionskonzepten nutzerdefinierte Indikatoren in MQL5 entwickeln können. Um eine Trendfolgestrategie zu entwickeln, lernen Sie, wie Sie wichtige Marktstrukturen wie Höhere Hochs, Höhere Tiefs und Tiefere Hochs, Tiefere Tiefs erkennen können.
In diesem Artikel erfahren Sie mehr über Folgendes:
- Wie man einen Preisaktionsindikator erstellt.
- Erkennen von wichtigen Punkten wie Tief (L), Hoch (H), Höheres Tief (HL), Höheres Hoch (HH), Tieferes Tief (LL) und Tieferes Hoch (LH), um die Struktur eines Aufwärts- und Abwärtstrends zu verstehen.
- Einzeichnen der Premium- und Discount-Zone auf Basis der wichtigsten Trendpunkte und Markierung des 50%-Retracement-Niveaus.
- Wie man das Risiko-Ertrags-Verhältnis bei der Berechnung potenzieller Gewinnziele in einem Aufwärtstrend-Setup anwendet.
- Berechnung und Markierung des Einstiegspunkts, des Stop-Loss (SL) und mehrerer Take-Profit (TP)-Niveaus auf der Grundlage der Trendstruktur.
1. Das Projekt einrichten
1.1. Wie der Indikator funktioniert
Der Indikator erkennt ein Tief, ein Hoch, ein Höheres Tief und ein Höheres Hoch, um einen Aufwärtstrend für Kaufsignale anzuzeigen. Anschließend wird das 50%-Retracement-Niveau zwischen dem Höheren Tief und dem Höheren Hoch bestimmt. Ein Durchbruch der Struktur über das Höhere Hoch wird den Einstieg einleiten, und das 50%-Retracement-Niveau wird als Stop-Loss dienen. Ein Risiko-Ertrags-Verhältnis von 1:1 ist das Ziel von Take Profit 1 und ein Verhältnis von 1:2 ist das Ziel von Take Profit 2.
Um einen Abwärtstrend für Verkaufssignale zu erkennen, ermittelt der Indikator zunächst ein Hoch, ein Tief, ein Tieferes Hoch und ein Tieferes Tief. Anschließend wird das 50%-Retracement zwischen dem unteren Hoch und dem unteren Tief berechnet. TP1 wird 1:1 sein, TP2 wird 1:2 sein, der Stop-Loss wird auf dem 50%-Niveau liegen und der Einstieg erfolgt bei einem Durchbruch unter dem unteren Tief.
2. Preisaktionsindikator bauen
Jede Handelsstrategie kann in einen Indikator umgewandelt werden - sie ist nur noch nicht visualisiert worden. Alles, was in eine Reihe von Richtlinien passt, kann kodiert und im Chart angezeigt werden, sei es Angebot und Nachfrage, Preisaktionen oder Unterstützung und Widerstand. An dieser Stelle kommt MQL5 ins Spiel. Für algorithmische Händler ist sie eine der besten und einfachsten Programmiersprachen, mit der sich jede Handelslogik in ein nützliches und visuell ansprechendes Tool verwandeln lässt. In diesem Abschnitt beginnen wir mit der Entwicklung eines Indikators, der Kursbewegungen analysiert, Marktstrukturen wie Hochs, Tiefs, Höhere Hochs und Tiefere Tiefs erkennt und dann diese Daten nutzt, um aufschlussreiche Kauf- und Verkaufssignale zu erzeugen, die Einstiegs-, Stop-Loss- und Take-Profit-Niveaus umfassen.
Im ersten Kapitel habe ich den Zweck des Projekts beschrieben und erläutert, wie der Indikator Trends und strukturelle Brüche erkennt und vollständige Handelssignale erzeugt, die Einstiegs-, Stop-Loss- und Take-Profits enthalten. In diesem Kapitel werden wir nun damit beginnen, alles in MQL5 in die Praxis umzusetzen. Wir nehmen die besprochene Logik und beginnen Schritt für Schritt, sie in Code umzusetzen.
2.1. Erkennen von Hochs und Tiefs
Der erste Schritt bei der Erstellung unseres Preisaktionsindikators besteht darin, die Höchst- und Tiefststände zu ermitteln. Diese wichtigen Wendepunkte des Marktes helfen, die Struktur des Trends zu erkennen. Indem wir das Hoch oder Tief der aktuellen Kerze mit dem der vorangegangenen und nachfolgenden Kerzen vergleichen, können wir sie in MQL5 identifizieren. Die Erkennung von Höheren Hochs, Höheren Tiefs, Tieferen Hochs und Tieferen Tiefs - allesamt entscheidend für die Identifizierung von Mustern und strukturellen Brüchen - basiert auf dieser Grundlage.
Beispiele://+------------------------------------------------------------------+ //| FUNCTION FOR SWING LOWS | //+------------------------------------------------------------------+ bool IsSwingLow(const double &low[], int index, int lookback) { for(int i = 1; i <= lookback; i++) { if(low[index] > low[index - i] || low[index] > low[index + i]) return false; } return true; } //+------------------------------------------------------------------+ //| FUNCTION FOR SWING HIGHS | //+------------------------------------------------------------------+ bool IsSwingHigh(const double &high[], int index, int lookback) { for(int i = 1; i <= lookback; i++) { if(high[index] < high[index - i] || high[index] < high[index + i]) return false; } return true; }
Die beiden Funktionen wurden im vorigen Artikel verwendet, und sie helfen dabei, sowohl Hochs als auch Tiefs zu identifizieren.
2.2. Aufwärtstrend
Anhand der Marktstruktur muss dieser Indikator zunächst prüfen, ob ein Aufwärtstrend vorliegt, bevor er ein Kaufsignal anzeigt. Ein Tief, ein Hoch, ein Höheres Tief und ein Höheres Hoch sind die wichtigsten Preispunkte, die zu diesem Zweck ermittelt werden müssen. Dieses Muster deutet auf einen Aufwärtstrend hin, was bedeutet, dass die Käufer das Sagen haben und dass der Markt wahrscheinlich weiter steigen wird. Nach der Bestätigung dieses Musters wird sich der Indikator darauf vorbereiten, ein legitimes Kaufsignal zu erzeugen.
Beispiel:
// CHART ID long chart_id = ChartID(); // Input parameters input int LookbackBars = 10; // Number of bars to look back/forward for swing points input int bars_check = 1000; // Number of bars to check for swing points input bool show_bullish = true; //Show Buy Signals // Variables for Bullish Market Structure double L; // Low: the starting low point in the up trend datetime L_time; // Time of the low string L_letter; // Label for the low point (e.g., "L") double H; // High: the first high after the low datetime H_time; // Time of the high string H_letter; // Label for the high point (e.g., "H") double HL; // Higher Low: the next low that is higher than the first low datetime HL_time; // Time of the higher low string HL_letter; // Label for the higher low point (e.g., "HL") double HH; // Higher High: the next high that is higher than the first high datetime HH_time; // Time of the higher high string HH_letter; // Label for the higher high point (e.g., "HH") //+------------------------------------------------------------------+ //| Custom indicator initialization function | //+------------------------------------------------------------------+ int OnInit() { //--- indicator buffers mapping //--- return(INIT_SUCCEEDED); } //+------------------------------------------------------------------+ //| Expert deinitialization function | //+------------------------------------------------------------------+ void OnDeinit(const int reason) { ObjectsDeleteAll(chart_id); } //+------------------------------------------------------------------+ //| Custom indicator iteration function | //+------------------------------------------------------------------+ int OnCalculate(const int rates_total, const int prev_calculated, const datetime &time[], const double &open[], const double &high[], const double &low[], const double &close[], const long &tick_volume[], const long &volume[], const int &spread[]) { //--- if(show_bullish == true) // Check if the bullish trend is to be displayed { if(rates_total >= bars_check) // Ensure enough bars are available for analysis { // Loop through the price data starting from a certain point based on bars_check and LookbackBars for(int i = rates_total - bars_check; i < rates_total - LookbackBars; i++) { // Check if the current bar is a swing low if(IsSwingLow(low, i, LookbackBars)) { // Store the values for the swing low L = low[i]; L_time = time[i]; L_letter = StringFormat("Low%d", i); // Loop through further to find a swing high after the low for(int j = i; j < rates_total - LookbackBars; j++) { // Check if the current bar is a swing high and occurs after the identified swing low if(IsSwingHigh(high, j, LookbackBars) && time[j] > L_time) { // Store the values for the swing high H = high[j]; H_time = time[j]; H_letter = StringFormat("High%d", j); // Loop further to find a higher low after the swing high for(int k = j; k < rates_total - LookbackBars; k++) { // Check if the current bar is a swing low and occurs after the swing high if(IsSwingLow(low, k, LookbackBars) && time[k] > H_time) { // Store the values for the higher low HL = low[k]; HL_time = time[k]; HL_letter = StringFormat("Higher Low%d", j); // Loop further to find a higher high after the higher low for(int l = j ; l < rates_total - LookbackBars; l++) { // Check if the current bar is a swing high and occurs after the higher low if(IsSwingHigh(high, l, LookbackBars) && time[l] > HL_time) { // Store the values for the higher high HH = high[l]; HH_time = time[l]; HH_letter = StringFormat("Higher High%d", l); // Check if the pattern follows the expected bullish structure: Low < High, Higher Low < High, Higher High > High if(L < H && HL < H && HL > L && HH > H) { // Create and display text objects for Low, High, Higher Low, and Higher High on the chart ObjectCreate(chart_id, L_letter, OBJ_TEXT, 0, L_time, L); ObjectSetString(chart_id, L_letter, OBJPROP_TEXT, "L"); ObjectSetInteger(chart_id, L_letter, OBJPROP_COLOR, clrDarkGreen); ObjectSetInteger(chart_id, L_letter, OBJPROP_FONTSIZE, 15); ObjectCreate(chart_id, H_letter, OBJ_TEXT, 0, H_time, H); ObjectSetString(chart_id, H_letter, OBJPROP_TEXT, "H"); ObjectSetInteger(chart_id, H_letter, OBJPROP_COLOR, clrDarkGreen); ObjectSetInteger(chart_id, H_letter, OBJPROP_FONTSIZE, 15); ObjectCreate(chart_id, HL_letter, OBJ_TEXT, 0, HL_time, HL); ObjectSetString(chart_id, HL_letter, OBJPROP_TEXT, "HL"); ObjectSetInteger(chart_id, HL_letter, OBJPROP_COLOR, clrDarkGreen); ObjectSetInteger(chart_id, HL_letter, OBJPROP_FONTSIZE, 15); ObjectCreate(chart_id, HH_letter, OBJ_TEXT, 0, HH_time, HH); ObjectSetString(chart_id, HH_letter, OBJPROP_TEXT, "HH"); ObjectSetInteger(chart_id, HH_letter, OBJPROP_COLOR, clrDarkGreen); ObjectSetInteger(chart_id, HH_letter, OBJPROP_FONTSIZE, 15); } break; // Exit the loop once the pattern is found } } break; // Exit the loop once the higher low is found } } break; // Exit the loop once the higher high is found } } } } } } //--- return value of prev_calculated for next call return(rates_total); }
Ausgabe:
Erläuterung:
Die Eingabeparameter, die zu Beginn des Codes festgelegt werden, dienen zur Untersuchung der Marktstruktur. Um hohe oder tiefe Umkehrpunkte zu ermitteln und die Relevanz im aktuellen Markt zu gewährleisten, gibt LookbackBars die Anzahl der Bars an, die vor und nach dem aktuellen Bar ausgewertet werden. bars_check steuert die Anzahl der Balken, die insgesamt untersucht werden, sodass das Skript bis zu 1000 Balken auf der Suche nach möglichen Aufwärts-Mustern durchsuchen kann.
Obwohl dies auch mehr Rechenleistung erfordert, deuten größere Werte für bars_check darauf hin, dass der Algorithmus bei der Suche nach diesen Orten ein breiteres Spektrum an Daten berücksichtigen wird. Eine boolesche Eingabe namens show_bullish steuert, ob die Kaufsignale oder die Aufwärtssignale angezeigt werden sollen. Wenn show_bullish auf true gesetzt ist, analysiert die Anwendung die Preisbewegung und bestimmt die Aufwärtsmarkt-Marktstruktur anhand der Umkehrpunkte. Selbst wenn die Anforderungen erfüllt sind, wird das Skript keine Aufwärts-Strukturen darstellen oder hervorheben, wenn es auf false gesetzt ist.
Die Prüfung auf show_bullish == true ist die erste Aktion, die in Bezug auf die Logik des Indikators stattfindet. Dies garantiert, dass die Aufwärts-Struktur nur dann erkannt wird, wenn Sie den Wunsch haben, Kaufsignale zu sehen. Das Programm prüft dann, ob genügend Preisdaten für die Analyse vorhanden sind, wenn die Kriterien erfüllt sind. Die Bedingung if (rates_total >= bars_check) wird verwendet, um dies zu überprüfen. Um Fehler aufgrund unzureichender Daten zu vermeiden, wird die Analyse übersprungen, wenn weniger Balken als nötig vorhanden sind. Das Skript durchläuft dann eine Schleife über die Kursdaten, um Umkehrpunkte zu finden, wenn das Kriterium erfüllt ist.
Die äußere Schleife beginnt mit i, das nach legitimen Swing-Lows sucht, indem es die Kursdaten ab dem letzten Balken rückwärts scannt. Die Funktion IsSwingLow() ermittelt, ob der aktuelle Balken der niedrigste in dem von LookbackBars angegebenen Bereich ist, und identifiziert somit einen tiefen Umkehrpunkt. Der Preis und die Zeit des Tiefs werden in den Variablen L und L_time gespeichert, sobald ein tiefer Umkehrpunkt identifiziert wird. Damit ist die Grundlage für die nachfolgende Phase der Entdeckung eines Aufwärts-Musters geschaffen. Nach der Identifizierung des Tiefpunkts sucht das Programm nach einem weiteren hohen Umkehrpunkt. Mit IsSwingHigh() sucht die zweite Schleife, indiziert durch j, nach einem hohen Umkehrpunkt in jedem nachfolgenden Balken. Die Werte aller auf das Tief folgenden Hochs werden in H und H_time aufgezeichnet. Dadurch entsteht das erste Segment eins Aufwärtsmarktes, die aus einem Tief und einem Hoch besteht.
Nach dem hohen Umkehrpunkt wird in der dritten Schleife, die durch k indiziert ist, nach einem Höheren Tief gesucht. Das Skript verwendet IsSwingLow() ein weiteres Mal, um ein Höheres Tief zu finden, das als ein Tief definiert ist, das größer ist als das anfängliche Tief L. Wenn ein Höheres Tief entdeckt wird, werden HL und HL_time mit seinem Wert und seiner Zeit aktualisiert. Nach der Identifizierung dieses Höheren Tiefs sucht das Programm weiter nach dem folgenden Höheren Hoch. In der vierten Schleife, die durch l indiziert ist, wird der auf das Höhere Tief folgende hohe Umkehrpunkt gesucht. Seine Werte werden in HH und HH_time gespeichert, wenn dieser größere Wert entdeckt wird. Der Code bestimmt, ob die vier kritischen Punkte - Tief, Hoch, Höheres Tief und Höheres Hoch - einem legitimen Aufwärts-Muster folgen, nachdem sie gefunden wurden. Das erste Tiefs sollte kleiner als das erste Hoch sein, das Höhere Tief sollte kleiner als der erste Hoch sein, das Höhere Tief sollte größer als das erste Tief sein und das höhere Hoch sollte größer als das erste Hoch sein. Diese Kriterien werden durch die Bedingung if (L < H && HL < H && HL > L && HH > H) überprüft. Dies bestätigt einen Aufwärtstrend, indem es sicherstellt, dass das Muster der erwarteten Abfolge von Höheren Hochs und Höheren Tiefs folgt.
Das Programm erstellt dann Textobjekte und zeigt sie im Chart an, um die Punkte hervorzuheben, die identifiziert wurden, wenn alle diese Anforderungen erfüllt sind. Das Chart zeigt die Punkte als Beschriftungen zu den entsprechenden Zeiträumen und Preisen: Tief (L), Hoch (H), Höheres Tief (HL) und Höheres Hoch (HH). ObjectCreate() wird verwendet, um die Textobjekte zu erstellen, während ObjectSetInteger() und ObjectSetString() verwendet werden, um ihre Eigenschaften, einschließlich Schriftgröße und -farbe, festzulegen. Anhand der deutlich angezeigten Punkte kann der Nutzer nun leicht die Aufwärts-Struktur auf dem Chart erkennen. Zusammenfassend lässt sich sagen, dass das Ziel des Programms darin besteht, in den Kursdaten ein Muster aus Höheren Hochs und Höheren Tiefs zu finden, um es auf eine steigende Marktstruktur hin zu bewerten. Dies geschieht durch die Betrachtung von Umkehrpunkte innerhalb eines vorgegebenen Bereichs von Balken, die Aufzeichnung der relevanten Informationen und die Feststellung, ob die Struktur das entsprechende Muster aufweist. Wenn das Muster verifiziert ist, kann der Nutzer es auf dem Chart sehen. Die Eingabeparameter, die eine Anpassung an die Präferenzen des Nutzers ermöglichen, bestimmen das gesamte Verfahren.
2.2.1. Darstellung der Premium- und Discount-Zonen von höherem Tiefststand zu höherem Höchststand
Die Abteilung hilft bei der Definition der Begriffe „Premium-Zone“ und „Discount-Zone“. Unterhalb der 50 %-Marke bezeichnet die Discount-Zone den Bereich der Preise, die für mögliche Käufe als vorteilhafter oder „billiger“ angesehen werden. Die Premium-Zone hingegen liegt oberhalb der 50%-Marke und bezeichnet vergleichsweise „teure“ Tarife. Um das Risiko-Ertrags-Verhältnis zu optimieren, entscheiden sich Händler bei vielen Handelsmethoden für Käufe aus der Discount-Zone; bei diesem speziellen Indikator verfolgen wir jedoch eine etwas andere Strategie.
In diesem Fall sind wir nur dann an Käufen interessiert, wenn der Markt oberhalb der Premium-Zone handelt oder die Struktur der Höheren Hochs durchbricht. Dieses Muster deutet darauf hin, dass die Aufwärts-Struktur weiterhin besteht und der Kurs wahrscheinlich weiter steigen wird. Um im Einklang mit dem Trend zu bleiben und das Risiko von Käufen bei einem Pullback oder einer Umkehrung zu verringern, empfiehlt es sich, den Durchbruch über das Höhere Hoch oder die Premium-Zone abzuwarten.
Beispiel:
// CHART ID long chart_id = ChartID(); // Input parameters input int LookbackBars = 10; // Number of bars to look back/forward for swing points input int bars_check = 1000; // Number of bars to check for swing points input bool show_bullish = true; //Show Buy Signals // Variables for Bullish Market Structure double L; // Low: the starting low point in the up trend datetime L_time; // Time of the low string L_letter; // Label for the low point (e.g., "L") double H; // High: the first high after the low datetime H_time; // Time of the high string H_letter; // Label for the high point (e.g., "H") double HL; // Higher Low: the next low that is higher than the first low datetime HL_time; // Time of the higher low string HL_letter; // Label for the higher low point (e.g., "HL") double HH; // Higher High: the next high that is higher than the first high datetime HH_time; // Time of the higher high string HH_letter; // Label for the higher high point (e.g., "HH") // Variables for Premium and Discount string pre_dis_box; // Name/ID for the premium-discount zone box (rectangle object on chart) double lvl_50; // The price level representing the 50% retracement between Higher Low and Higher High string lvl_50_line; // Name/ID for the horizontal line marking the 50% level //+------------------------------------------------------------------+ //| Custom indicator initialization function | //+------------------------------------------------------------------+ int OnInit() { //--- indicator buffers mapping //--- return(INIT_SUCCEEDED); } //+------------------------------------------------------------------+ //| Expert deinitialization function | //+------------------------------------------------------------------+ void OnDeinit(const int reason) { ObjectsDeleteAll(chart_id); } //+------------------------------------------------------------------+ //| Custom indicator iteration function | //+------------------------------------------------------------------+ int OnCalculate(const int rates_total, const int prev_calculated, const datetime &time[], const double &open[], const double &high[], const double &low[], const double &close[], const long &tick_volume[], const long &volume[], const int &spread[]) { //--- if(show_bullish == true) // Check if the bullish trend is to be displayed { if(rates_total >= bars_check) // Ensure enough bars are available for analysis { // Loop through the price data starting from a certain point based on bars_check and LookbackBars for(int i = rates_total - bars_check; i < rates_total - LookbackBars; i++) { // Check if the current bar is a swing low if(IsSwingLow(low, i, LookbackBars)) { // Store the values for the swing low L = low[i]; L_time = time[i]; L_letter = StringFormat("Low%d", i); // Loop through further to find a swing high after the low for(int j = i; j < rates_total - LookbackBars; j++) { // Check if the current bar is a swing high and occurs after the identified swing low if(IsSwingHigh(high, j, LookbackBars) && time[j] > L_time) { // Store the values for the swing high H = high[j]; H_time = time[j]; H_letter = StringFormat("High%d", j); // Loop further to find a higher low after the swing high for(int k = j; k < rates_total - LookbackBars; k++) { // Check if the current bar is a swing low and occurs after the swing high if(IsSwingLow(low, k, LookbackBars) && time[k] > H_time) { // Store the values for the higher low HL = low[k]; HL_time = time[k]; HL_letter = StringFormat("Higher Low%d", j); // Loop further to find a higher high after the higher low for(int l = j ; l < rates_total - LookbackBars; l++) { // Check if the current bar is a swing high and occurs after the higher low if(IsSwingHigh(high, l, LookbackBars) && time[l] > HL_time) { // Store the values for the higher high HH = high[l]; HH_time = time[l]; HH_letter = StringFormat("Higher High%d", l); // Check if the pattern follows the expected bullish structure: Low < High, Higher Low < High, Higher High > High if(L < H && HL < H && HL > L && HH > H) { // Create and display text objects for Low, High, Higher Low, and Higher High on the chart ObjectCreate(chart_id, L_letter, OBJ_TEXT, 0, L_time, L); ObjectSetString(chart_id, L_letter, OBJPROP_TEXT, "L"); ObjectSetInteger(chart_id, L_letter, OBJPROP_COLOR, clrDarkGreen); ObjectSetInteger(chart_id, L_letter, OBJPROP_FONTSIZE, 15); ObjectCreate(chart_id, H_letter, OBJ_TEXT, 0, H_time, H); ObjectSetString(chart_id, H_letter, OBJPROP_TEXT, "H"); ObjectSetInteger(chart_id, H_letter, OBJPROP_COLOR, clrDarkGreen); ObjectSetInteger(chart_id, H_letter, OBJPROP_FONTSIZE, 15); ObjectCreate(chart_id, HL_letter, OBJ_TEXT, 0, HL_time, HL); ObjectSetString(chart_id, HL_letter, OBJPROP_TEXT, "HL"); ObjectSetInteger(chart_id, HL_letter, OBJPROP_COLOR, clrDarkGreen); ObjectSetInteger(chart_id, HL_letter, OBJPROP_FONTSIZE, 15); ObjectCreate(chart_id, HH_letter, OBJ_TEXT, 0, HH_time, HH); ObjectSetString(chart_id, HH_letter, OBJPROP_TEXT, "HH"); ObjectSetInteger(chart_id, HH_letter, OBJPROP_COLOR, clrDarkGreen); ObjectSetInteger(chart_id, HH_letter, OBJPROP_FONTSIZE, 15); // Calculate the 50% retracement level between the Higher Low and Higher High lvl_50 = HL + ((HH - HL)/2); // Generate unique names for the premium-discount box and the 50% level line using the current loop index pre_dis_box = StringFormat("Premium and Discount Box%d", i); lvl_50_line = StringFormat("Level 50 Line%d", i); // Create a rectangle object representing the premium-discount zone from the Higher Low to the Higher High ObjectCreate(chart_id, pre_dis_box, OBJ_RECTANGLE, 0, HL_time, HL, time[l + LookbackBars], HH); // Create a trend line (horizontal line) marking the 50% retracement level ObjectCreate(chart_id, lvl_50_line, OBJ_TREND, 0, HL_time, lvl_50, time[l + LookbackBars], lvl_50); // Set the color of the premium-discount box to dark green ObjectSetInteger(chart_id, pre_dis_box, OBJPROP_COLOR, clrDarkGreen); // Set the color of the 50% level line to dark green ObjectSetInteger(chart_id, lvl_50_line, OBJPROP_COLOR, clrDarkGreen); // Set the width of the premium-discount box for better visibility ObjectSetInteger(chart_id, pre_dis_box, OBJPROP_WIDTH, 2); // Set the width of the 50% level line for better visibility ObjectSetInteger(chart_id, lvl_50_line, OBJPROP_WIDTH, 2); } break; // Exit the loop once the pattern is found } } break; // Exit the loop once the higher low is found } } break; // Exit the loop once the higher high is found } } } } } } //--- return value of prev_calculated for next call return(rates_total); }
Ausgabe:
Erläuterung:
Der Code bestimmt das 50%-Retracement-Level, das auf halbem Weg zwischen dem Höheren Tief (HL) und dem Höheren Hoch (HH) liegt. Die Premium- (oberhalb) und die Discount-Zone (unterhalb) werden durch dieses Niveau getrennt. Der Mittelwert zwischen HL und HH wird mit der Formel lvl_50 = HL + ((HH - HL)/2); bestimmt. Anschließend wird der Schleifenindex i in die Namen der beiden String-Variablen pre_dis_box und lvl_50_line aufgenommen. Diese dienen als eindeutige Bezeichner für die visuellen Elemente, die wir auf dem Chart zeichnen werden. Jede Zeichnung ist einmalig und ersetzt frühere Zeichnungen nicht, wenn der Schleifenindex enthalten ist.
Mit der Zeile ObjectCreate(chart_id, pre_dis_box, OBJ_RECTANGLE, 0, HL_time, HL, time[l + LookbackBars], HH) wird im Chart ein Rechteck erstellt, das den Übergang vom Höheren Tief zum Höheren Hoch grafisch darstellt. Mit Hilfe dieser Box können Händler schnell den Bereich des letzten Aufwärtsbewegung bestimmen. Das Ende des Rechtecks wird an einem Future-Bar (l + LookbackBars) unter Verwendung des HH-Kurses verankert, während sein Anfang an der Zeit und dem Kurs des HL verankert wird. Dadurch bleibt das Feld sichtbar, da es leicht in die Zukunft verlängert wird.
Dann wird eine horizontale Linie quer durch den Chart auf der 50%-Ebene durch die Linie ObjectCreate(chart_id, lvl_50_line, OBJ_TREND, 0, HL_time, lvl_50, time[l + LookbackBars], lvl_50); gezeichnet. Dieser Schwellenwert ist wichtig, denn nach der Logik dieses Indikators suchen wir nur dann nach möglichen Kaufsignalen, wenn der Markt über der Premium-Zone oder über der 50%-Marke des letzten Aufschwungs notiert. Die Farben des Kastens und der Linie werden auf clrDarkGreen gesetzt, und die Funktion ObjectSetInteger wird verwendet, um ihre Dicke (oder Breite) auf 2 zu erhöhen, damit sie beide visuell klar sind. Der Markt muss vollständig über HH ausbrechen, damit ein Kaufsignal als gültig erachtet wird; mit anderen Worten, der Kurs muss außerhalb und über der gesamten Premium-Zone schließen. Anders ausgedrückt: Wir wollen nur kaufen, wenn die Marktstruktur eindeutig nach oben tendiert und der vorherige hohe Umkehrpunkt (HH) überschritten hat.
2.2.2. Angabe von Einstiegspunkt, Stop-Loss und Take-Profit
Die Suche nach möglichen Kaufsignalen folgt, sobald die Premium- und Discount-Zonen zwischen dem Höheren Tief (HL) und dem Höheren Hoch (HH) korrekt markiert wurden. Der Schlüssel zu einer legitimen Kaufentscheidung ist das Warten auf einen Aufwärts-Ausbruchsbalken, der über das Höhere Hoch (HH) ausbricht, das anzeigt, dass sich der Markt weiterhin stark nach oben bewegt.
Aber die Überquerung von HH allein ist nicht ausreichend. Der Aufwärtsbalken muss oberhalb der HH schließen, damit der Einstieg verifiziert werden kann, denn wir müssen sicher sein, dass der Ausbruch real ist. Die Tatsache, dass der Kurs oberhalb der HH geschlossen hat, deutet darauf hin, dass eine anhaltende Kaufnachfrage besteht und der Kurs wahrscheinlich weiter steigen wird. Der Einstiegspunkt wird beim Schluss des Aufwärtsbalkens festgelegt, der über den HH bricht, nachdem der Ausbruch bestätigt wurde. Wir sind sicher, dass der Markt derzeit genügend Stärke zeigt, um in den Handel einzusteigen.
Wir setzen den Stop-Loss (SL) auf das 50%-Niveau (lvl_50), das in der Mitte zwischen HL und HH liegt, um eine mögliche Umkehr zu verhindern. Um einen möglichen Rückzug in die Discount-Zone (unter die 50%-Marke) zu vermeiden, der auf einen Stimmungsumschwung am Markt hindeuten kann, haben wir den SL hier platziert. Unsere Methode basiert auf dem Risiko-Ertrags-Verhältnis (R:R) für die Take Profit (TP)-Ebenen. Das Gewinnziel für die erste Take-Profit-Ebene, TP1, ist gleich dem Risikoabstand zwischen dem Einstiegspunkt und dem SL, da es auf ein R:R von 1:1 festgelegt ist. Das Gewinnziel für die zweite Take-Profit-Ebene (TP2) ist das Doppelte des Abstands zwischen dem Einstiegspunkt und dem SL, mit einer R:R-Einstellung von 1:2. Für Händler, die lieber Teilgewinne bei TP1 sichern möchten, bieten diese beiden Take-Profit-Levels Flexibilität. Sie ermöglichen es ihnen, einen Teil des Handels offen zu lassen, um von zusätzlichen Gewinnen zu profitieren, sollte der Markt seinen positiven Trend fortsetzen.
Beispiel:
// Variables for Entry, Stop Loss, and Take Profit string entry_line; // Line object to represent the entry point on the chart string entry_txt; // Text object for displaying "BUY" at the entry point double lvl_SL; // Stop Loss level (set at the 50% retracement level) string lvl_sl_line; // Line object for representing the Stop Loss level string lvl_sl_txt; // Text object for labeling the Stop Loss level double TP1; // Take Profit 1 level (1:1 risk-reward ratio) double TP2; // Take Profit 2 level (1:2 risk-reward ratio) string lvl_tp_line; // Line object for representing the Take Profit 1 level string lvl_tp2_line; // Line object for representing the Take Profit 2 level string lvl_tp_txt; // Text object for labeling the Take Profit 1 level string lvl_tp2_txt; // Text object for labeling the Take Profit 2 level string buy_object; // Arrow object to indicate the Buy signal on the chart
if(show_bullish == true) // Check if the bullish trend is to be displayed { if(rates_total >= bars_check) // Ensure enough bars are available for analysis { // Loop through the price data starting from a certain point based on bars_check and LookbackBars for(int i = rates_total - bars_check; i < rates_total - LookbackBars; i++) { // Check if the current bar is a swing low if(IsSwingLow(low, i, LookbackBars)) { // Store the values for the swing low L = low[i]; L_time = time[i]; L_letter = StringFormat("Low%d", i); // Loop through further to find a swing high after the low for(int j = i; j < rates_total - LookbackBars; j++) { // Check if the current bar is a swing high and occurs after the identified swing low if(IsSwingHigh(high, j, LookbackBars) && time[j] > L_time) { // Store the values for the swing high H = high[j]; H_time = time[j]; H_letter = StringFormat("High%d", j); // Loop further to find a higher low after the swing high for(int k = j; k < rates_total - LookbackBars; k++) { // Check if the current bar is a swing low and occurs after the swing high if(IsSwingLow(low, k, LookbackBars) && time[k] > H_time) { // Store the values for the higher low HL = low[k]; HL_time = time[k]; HL_letter = StringFormat("Higher Low%d", j); // Loop further to find a higher high after the higher low for(int l = j ; l < rates_total - LookbackBars; l++) { // Check if the current bar is a swing high and occurs after the higher low if(IsSwingHigh(high, l, LookbackBars) && time[l] > HL_time) { // Store the values for the higher high HH = high[l]; HH_time = time[l]; HH_letter = StringFormat("Higher High%d", l); // Check if the pattern follows the expected bullish structure: Low < High, Higher Low < High, Higher High > High if(L < H && HL < H && HL > L && HH > H) { // Create and display text objects for Low, High, Higher Low, and Higher High on the chart ObjectCreate(chart_id, L_letter, OBJ_TEXT, 0, L_time, L); ObjectSetString(chart_id, L_letter, OBJPROP_TEXT, "L"); ObjectSetInteger(chart_id, L_letter, OBJPROP_COLOR, clrDarkGreen); ObjectSetInteger(chart_id, L_letter, OBJPROP_FONTSIZE, 15); ObjectCreate(chart_id, H_letter, OBJ_TEXT, 0, H_time, H); ObjectSetString(chart_id, H_letter, OBJPROP_TEXT, "H"); ObjectSetInteger(chart_id, H_letter, OBJPROP_COLOR, clrDarkGreen); ObjectSetInteger(chart_id, H_letter, OBJPROP_FONTSIZE, 15); ObjectCreate(chart_id, HL_letter, OBJ_TEXT, 0, HL_time, HL); ObjectSetString(chart_id, HL_letter, OBJPROP_TEXT, "HL"); ObjectSetInteger(chart_id, HL_letter, OBJPROP_COLOR, clrDarkGreen); ObjectSetInteger(chart_id, HL_letter, OBJPROP_FONTSIZE, 15); ObjectCreate(chart_id, HH_letter, OBJ_TEXT, 0, HH_time, HH); ObjectSetString(chart_id, HH_letter, OBJPROP_TEXT, "HH"); ObjectSetInteger(chart_id, HH_letter, OBJPROP_COLOR, clrDarkGreen); ObjectSetInteger(chart_id, HH_letter, OBJPROP_FONTSIZE, 15); // Calculate the 50% retracement level between the Higher Low and Higher High lvl_50 = HL + ((HH - HL)/2); // Generate unique names for the premium-discount box and the 50% level line using the current loop index pre_dis_box = StringFormat("Premium and Discount Box%d", i); lvl_50_line = StringFormat("Level 50 Line%d", i); // Create a rectangle object representing the premium-discount zone from the Higher Low to the Higher High ObjectCreate(chart_id, pre_dis_box, OBJ_RECTANGLE, 0, HL_time, HL, time[l + LookbackBars], HH); // Create a trend line (horizontal line) marking the 50% retracement level ObjectCreate(chart_id, lvl_50_line, OBJ_TREND, 0, HL_time, lvl_50, time[l + LookbackBars], lvl_50); // Set the color of the premium-discount box to dark green ObjectSetInteger(chart_id, pre_dis_box, OBJPROP_COLOR, clrDarkGreen); // Set the color of the 50% level line to dark green ObjectSetInteger(chart_id, lvl_50_line, OBJPROP_COLOR, clrDarkGreen); // Set the width of the premium-discount box for better visibility ObjectSetInteger(chart_id, pre_dis_box, OBJPROP_WIDTH, 2); // Set the width of the 50% level line for better visibility ObjectSetInteger(chart_id, lvl_50_line, OBJPROP_WIDTH, 2); for(int m = l; m < rates_total-1; m++) { if(close[m] > open[m] && close[m] > HH && time[m] >= time[l+LookbackBars]) { TP1 = close[m] + (close[m] - lvl_50); TP2 = TP1 + (close[m] - lvl_50); entry_line = StringFormat("Entry%d", m); lvl_sl_line = StringFormat("SL%d", m); lvl_tp_line = StringFormat("TP%d", m); lvl_tp2_line = StringFormat("TP 2%d", m); ObjectCreate(chart_id,entry_line,OBJ_TREND,0,HL_time,close[m],time[m],close[m]); ObjectCreate(chart_id,lvl_sl_line,OBJ_TREND,0,HL_time,lvl_50,time[m],lvl_50); ObjectCreate(chart_id,lvl_tp_line,OBJ_TREND,0,HL_time,TP1,time[m],TP1); ObjectCreate(chart_id,lvl_tp2_line,OBJ_TREND,0,HL_time,TP2,time[m],TP2); ObjectSetInteger(chart_id,entry_line,OBJPROP_WIDTH,2); ObjectSetInteger(chart_id,lvl_sl_line,OBJPROP_WIDTH,2); ObjectSetInteger(chart_id,lvl_tp_line,OBJPROP_WIDTH,2); ObjectSetInteger(chart_id,lvl_tp2_line,OBJPROP_WIDTH,2); ObjectSetInteger(chart_id,entry_line,OBJPROP_COLOR,clrDarkGreen); ObjectSetInteger(chart_id,lvl_sl_line,OBJPROP_COLOR,clrDarkGreen); ObjectSetInteger(chart_id,lvl_tp_line,OBJPROP_COLOR,clrDarkGreen); ObjectSetInteger(chart_id,lvl_tp2_line,OBJPROP_COLOR,clrDarkGreen); entry_txt = StringFormat("Entry Text%d", m); lvl_sl_txt = StringFormat("SL Text%d", m); lvl_tp_txt = StringFormat("TP 1 Text%d", m); lvl_tp2_txt = StringFormat("TP 2 Text%d", m); ObjectCreate(chart_id, lvl_sl_txt, OBJ_TEXT, 0,time[m],lvl_50); ObjectSetString(chart_id, lvl_sl_txt, OBJPROP_TEXT, "SL"); ObjectSetInteger(chart_id,lvl_sl_txt,OBJPROP_COLOR,clrDarkGreen); ObjectSetInteger(chart_id,lvl_sl_txt,OBJPROP_FONTSIZE,15); ObjectCreate(chart_id, entry_txt, OBJ_TEXT, 0,time[m],close[m]); ObjectSetString(chart_id, entry_txt, OBJPROP_TEXT, "BUY"); ObjectSetInteger(chart_id,entry_txt,OBJPROP_COLOR,clrDarkGreen); ObjectSetInteger(chart_id,entry_txt,OBJPROP_FONTSIZE,15); ObjectCreate(chart_id, lvl_tp_txt, OBJ_TEXT, 0,time[m],TP1); ObjectSetString(chart_id, lvl_tp_txt, OBJPROP_TEXT, "TP1"); ObjectSetInteger(chart_id,lvl_tp_txt,OBJPROP_COLOR,clrDarkGreen); ObjectSetInteger(chart_id,lvl_tp_txt,OBJPROP_FONTSIZE,15); ObjectCreate(chart_id, lvl_tp2_txt, OBJ_TEXT, 0,time[m],TP2); ObjectSetString(chart_id, lvl_tp2_txt, OBJPROP_TEXT, "TP2"); ObjectSetInteger(chart_id,lvl_tp2_txt,OBJPROP_COLOR,clrDarkGreen); ObjectSetInteger(chart_id,lvl_tp2_txt,OBJPROP_FONTSIZE,15); buy_object = StringFormat("Buy Object%d", m); ObjectCreate(chart_id,buy_object,OBJ_ARROW_BUY,0,time[m],close[m]); break; } } } break; // Exit the loop once the pattern is found } } break; // Exit the loop once the higher low is found } } break; // Exit the loop once the higher high is found } } } } } }Ausgabe:
Sie können sehen, dass die Premium- und Discount-Zonen sowie die Kaufsignalmarkierungen in der obigen Abbildung nicht genau eingezeichnet sind. Dies liegt daran, dass bestimmte Bedingungen übersehen wurden, bevor die Objekte auf dem Chart gezeichnet wurden. Um den Ansatz zu verbessern, müssen wir mehr Überprüfungen durchführen, um sicherzustellen, dass das Kaufsignal legitim ist. Bevor wir irgendwelche Objekte auf dem Chart skizzieren, müssen wir uns zunächst vergewissern, dass eine Kerze wirklich das Höhere Hoch (HH) durchbrochen hat. Ein Bruch der HH bedeutet eine Fortsetzung des Aufwärtstrends, der für die Legitimität des Kaufsignals erforderlich ist, und ist somit ein entscheidendes Kriterium. Erst wenn diese Bedingung erfüllt ist, sollten wir mit den Berechnungen für die Erfassung und das Risikomanagement beginnen.
Dann muss die Anzahl der Balken vom Höheren Tief (HL) bis zum Ende der Premium- und Discount-Box gezählt werden. Dies garantiert, dass die Kursbewegung innerhalb einer akzeptablen Spanne liegt und hilft uns zu verstehen, wie weit sich der Markt bewegt hat. Nachdem diese Zählung abgeschlossen ist, müssen wir bestätigen, dass der Schlusskurs des Aufwärts-Balkens, der das Höhere Hoch (HH) durchbrochen hat, in der Nähe der Premium- und Discount-Box liegt. Dies garantiert, dass das Kaufsignal nicht zu weit von der erwarteten Marktstruktur entfernt ist und innerhalb einer fairen Preisspanne erfolgt.
Beispiel:
// Declare variables to count bars int n_bars; // Number of bars from Higher Low to the end of the Premium/Discount box int n_bars_2; // Number of bars from the end of the Premium/Discount box to the bullish bar that broke HH
if(show_bullish == true) // Check if the bullish trend is to be displayed { if(rates_total >= bars_check) // Ensure enough bars are available for analysis { // Loop through the price data starting from a certain point based on bars_check and LookbackBars for(int i = rates_total - bars_check; i < rates_total - LookbackBars; i++) { // Check if the current bar is a swing low if(IsSwingLow(low, i, LookbackBars)) { // Store the values for the swing low L = low[i]; L_time = time[i]; L_letter = StringFormat("Low%d", i); // Loop through further to find a swing high after the low for(int j = i; j < rates_total - LookbackBars; j++) { // Check if the current bar is a swing high and occurs after the identified swing low if(IsSwingHigh(high, j, LookbackBars) && time[j] > L_time) { // Store the values for the swing high H = high[j]; H_time = time[j]; H_letter = StringFormat("High%d", j); // Loop further to find a higher low after the swing high for(int k = j; k < rates_total - LookbackBars; k++) { // Check if the current bar is a swing low and occurs after the swing high if(IsSwingLow(low, k, LookbackBars) && time[k] > H_time) { // Store the values for the higher low HL = low[k]; HL_time = time[k]; HL_letter = StringFormat("Higher Low%d", j); // Loop further to find a higher high after the higher low for(int l = j ; l < rates_total - LookbackBars; l++) { // Check if the current bar is a swing high and occurs after the higher low if(IsSwingHigh(high, l, LookbackBars) && time[l] > HL_time) { // Store the values for the higher high HH = high[l]; HH_time = time[l]; HH_letter = StringFormat("Higher High%d", l); // Loop through the bars to check for the conditions for entry for(int m = l; m < rates_total-1; m++) { // Check if the current bar is a bullish bar and if the price has broken the higher high (HH) if(close[m] > open[m] && close[m] > HH && time[m] >= time[l+LookbackBars]) { // Count the bars between HL_time and the end of the Premium/Discount box n_bars = Bars(_Symbol, PERIOD_CURRENT, HL_time, time[l + LookbackBars]); // Count the bars between the end of the Premium/Discount box and the candle that broke HH n_bars_2 = Bars(_Symbol, PERIOD_CURRENT, time[l + LookbackBars], time[m]); // Check if the pattern follows the expected bullish structure: Low < High, Higher Low < High, Higher High > High if(L < H && HL < H && HL > L && HH > H && open[l+LookbackBars] <= HH && n_bars_2 < n_bars) { // Create and display text objects for Low, High, Higher Low, and Higher High on the chart ObjectCreate(chart_id, L_letter, OBJ_TEXT, 0, L_time, L); ObjectSetString(chart_id, L_letter, OBJPROP_TEXT, "L"); ObjectSetInteger(chart_id, L_letter, OBJPROP_COLOR, clrDarkGreen); ObjectSetInteger(chart_id, L_letter, OBJPROP_FONTSIZE, 15); ObjectCreate(chart_id, H_letter, OBJ_TEXT, 0, H_time, H); ObjectSetString(chart_id, H_letter, OBJPROP_TEXT, "H"); ObjectSetInteger(chart_id, H_letter, OBJPROP_COLOR, clrDarkGreen); ObjectSetInteger(chart_id, H_letter, OBJPROP_FONTSIZE, 15); ObjectCreate(chart_id, HL_letter, OBJ_TEXT, 0, HL_time, HL); ObjectSetString(chart_id, HL_letter, OBJPROP_TEXT, "HL"); ObjectSetInteger(chart_id, HL_letter, OBJPROP_COLOR, clrDarkGreen); ObjectSetInteger(chart_id, HL_letter, OBJPROP_FONTSIZE, 15); ObjectCreate(chart_id, HH_letter, OBJ_TEXT, 0, HH_time, HH); ObjectSetString(chart_id, HH_letter, OBJPROP_TEXT, "HH"); ObjectSetInteger(chart_id, HH_letter, OBJPROP_COLOR, clrDarkGreen); ObjectSetInteger(chart_id, HH_letter, OBJPROP_FONTSIZE, 15); // Calculate the 50% retracement level between the Higher Low and Higher High lvl_50 = HL + ((HH - HL)/2); // Generate unique names for the premium-discount box and the 50% level line using the current loop index pre_dis_box = StringFormat("Premium and Discount Box%d", i); lvl_50_line = StringFormat("Level 50 Line%d", i); // Create a rectangle object representing the premium-discount zone from the Higher Low to the Higher High ObjectCreate(chart_id, pre_dis_box, OBJ_RECTANGLE, 0, HL_time, HL, time[l + LookbackBars], HH); // Create a trend line (horizontal line) marking the 50% retracement level ObjectCreate(chart_id, lvl_50_line, OBJ_TREND, 0, HL_time, lvl_50, time[l + LookbackBars], lvl_50); // Set the color of the premium-discount box to dark green ObjectSetInteger(chart_id, pre_dis_box, OBJPROP_COLOR, clrDarkGreen); // Set the color of the 50% level line to dark green ObjectSetInteger(chart_id, lvl_50_line, OBJPROP_COLOR, clrDarkGreen); // Set the width of the premium-discount box for better visibility ObjectSetInteger(chart_id, pre_dis_box, OBJPROP_WIDTH, 2); // Set the width of the 50% level line for better visibility ObjectSetInteger(chart_id, lvl_50_line, OBJPROP_WIDTH, 2); // Calculate Take Profit levels based on the 50% retracement TP1 = close[m] + (close[m] - lvl_50); // TP1 at 1:1 risk-reward ratio TP2 = TP1 + (close[m] - lvl_50); // TP2 at 1:2 risk-reward ratio // Create unique object names for Entry, Stop Loss, and Take Profit lines and text entry_line = StringFormat("Entry%d", m); lvl_sl_line = StringFormat("SL%d", m); lvl_tp_line = StringFormat("TP%d", m); lvl_tp2_line = StringFormat("TP 2%d", m); // Create the lines on the chart for Entry, Stop Loss, and Take Profit levels ObjectCreate(chart_id, entry_line, OBJ_TREND, 0, HL_time, close[m], time[m], close[m]); ObjectCreate(chart_id, lvl_sl_line, OBJ_TREND, 0, HL_time, lvl_50, time[m], lvl_50); ObjectCreate(chart_id, lvl_tp_line, OBJ_TREND, 0, HL_time, TP1, time[m], TP1); ObjectCreate(chart_id, lvl_tp2_line, OBJ_TREND, 0, HL_time, TP2, time[m], TP2); // Set the properties for the lines (width, color, etc.) ObjectSetInteger(chart_id, entry_line, OBJPROP_WIDTH, 2); ObjectSetInteger(chart_id, lvl_sl_line, OBJPROP_WIDTH, 2); ObjectSetInteger(chart_id, lvl_tp_line, OBJPROP_WIDTH, 2); ObjectSetInteger(chart_id, lvl_tp2_line, OBJPROP_WIDTH, 2); ObjectSetInteger(chart_id, entry_line, OBJPROP_COLOR, clrDarkGreen); ObjectSetInteger(chart_id, lvl_sl_line, OBJPROP_COLOR, clrDarkGreen); ObjectSetInteger(chart_id, lvl_tp_line, OBJPROP_COLOR, clrDarkGreen); ObjectSetInteger(chart_id, lvl_tp2_line, OBJPROP_COLOR, clrDarkGreen); // Create the text labels for Entry, Stop Loss, and Take Profit levels entry_txt = StringFormat("Entry Text%d", m); lvl_sl_txt = StringFormat("SL Text%d", m); lvl_tp_txt = StringFormat("TP 1 Text%d", m); lvl_tp2_txt = StringFormat("TP 2 Text%d", m); // Create the text objects for the Entry, Stop Loss, and Take Profit labels ObjectCreate(chart_id, lvl_sl_txt, OBJ_TEXT, 0, time[m], lvl_50); ObjectSetString(chart_id, lvl_sl_txt, OBJPROP_TEXT, "SL"); ObjectSetInteger(chart_id, lvl_sl_txt, OBJPROP_COLOR, clrDarkGreen); ObjectSetInteger(chart_id, lvl_sl_txt, OBJPROP_FONTSIZE, 15); ObjectCreate(chart_id, entry_txt, OBJ_TEXT, 0, time[m], close[m]); ObjectSetString(chart_id, entry_txt, OBJPROP_TEXT, "BUY"); ObjectSetInteger(chart_id, entry_txt, OBJPROP_COLOR, clrDarkGreen); ObjectSetInteger(chart_id, entry_txt, OBJPROP_FONTSIZE, 15); ObjectCreate(chart_id, lvl_tp_txt, OBJ_TEXT, 0, time[m], TP1); ObjectSetString(chart_id, lvl_tp_txt, OBJPROP_TEXT, "TP1"); ObjectSetInteger(chart_id, lvl_tp_txt, OBJPROP_COLOR, clrDarkGreen); ObjectSetInteger(chart_id, lvl_tp_txt, OBJPROP_FONTSIZE, 15); ObjectCreate(chart_id, lvl_tp2_txt, OBJ_TEXT, 0, time[m], TP2); ObjectSetString(chart_id, lvl_tp2_txt, OBJPROP_TEXT, "TP2"); ObjectSetInteger(chart_id, lvl_tp2_txt, OBJPROP_COLOR, clrDarkGreen); ObjectSetInteger(chart_id, lvl_tp2_txt, OBJPROP_FONTSIZE, 15); // Create a Buy arrow object to indicate the Buy signal on the chart buy_object = StringFormat("Buy Object%d", m); ObjectCreate(chart_id, buy_object, OBJ_ARROW_BUY, 0, time[m], close[m]); break; // Exit the loop once a Buy signal is found } } } break; // Exit the loop once the pattern is found } } break; // Exit the loop once the higher low is found } } break; // Exit the loop once the higher high is found } } } } } }
Ausgabe:
Erläuterung:
Zwei Integer-Variablen, n_bars und n_bars_2, wurden im globalen Bereich des Codes deklariert. Die Anzahl der Kerzen (Balken) zwischen wichtigen Punkten in einem Aufwärtsmarkt wird durch diese Variablen bestimmt. Insbesondere ist n_bars die Anzahl der Balken, die das Ende der Premium-/Discount-Box (time[l + LookbackBars]) vom Höheren Tief (HL) trennen. Allerdings zählt n_bars_2 die Anzahl der Balken, die die Aufwärtskerze, die das Höheren Hochs (HH) durchbrach, vom Ende der Premium-/Discount-Box trennen. Diese Zählung wird verwendet, um zu beurteilen, ob sich die Kursbewegung zu weit von der optimalen Handelszone entfernt hat oder ob das Kaufsignal noch gültig ist.
Diese Variablen werden später im Code als Teil einer zusätzlichen Validierungsbedingung verwendet, die das Argument für einen Aufwärtsmarkt stärkt. Eine zusätzliche Bedingung n_bars_2 < n_bars wird geprüft, nachdem das Tief, das Hoch, das Höhere Tief und das Höhere Hoch bestimmt wurden (wobei sichergestellt wird, dass sie der Struktur L < H, HL < H, HL > L und HH > H entsprechen) und überprüft wird, dass der Eröffnungskurs der Kerze, die die Premium-/Discount-Box beendet hat, nicht höher ist als das Höhere Hoch. Dadurch wird sichergestellt, dass die Kerze des Aufwärtsausbruchs (die Kerze, die über HH ausbricht) nicht zu weit nach der Formation auftaucht, was auf ein schwaches oder ungültiges Setup hindeuten könnte, und dass sie ziemlich nahe am Muster auftaucht.
Alle Routinen wie ObjectCreate() und ObjectSet*(), die zuvor zum Zeichnen des Tiefs, des Hochs, des Höheren Tiefs, des Höheren Hochs, der Premium-/Discount-Box, der 50%-Linie und der Eröffnungs-/SL-/TP-Marken des Charts verwendet wurden, wurden innerhalb dieser if-Anweisung übertragen, um diese strengere Prüfung durchzusetzen. Dies bedeutet, dass diese visuellen Komponenten nur dann produziert und angezeigt werden, wenn alle Anforderungen an die Struktur und die Zeit erfüllt sind. Auf diese Weise bleibt das Chart übersichtlich und wird nicht mit fehlerhaften oder verfrühten Einträgen aufgrund irreführender Signale überfrachtet.
2.3. Abwärts-Trend
Dieser Indikator muss zunächst die Marktstruktur nutzen, um einen Abschwung zu bestätigen, bevor er einen Verkauf anzeigen kann. Dies wird erreicht, indem eine Reihe signifikanter Preispunkte - ein Hoch, ein Tief, ein Tieferes Hoch und ein Tieferes Tief - gefunden werden. Dieses Muster zeigt, dass die Verkäufer die Kontrolle haben und dass sich der Markt wahrscheinlich weiter abwärts bewegen wird, was das Abwärts-Momentum unterstützt. Sobald diese Struktur verifiziert ist, beginnt der Indikator mit der Suche nach legitimen Verkaufssignalen.
Beispiel:
// Variables for Bearish Market Structure double LH; // Lower High: the high formed after the initial low in a downtrend datetime LH_time; // Time of the Lower High string LH_letter; // Label used to display the Lower High on the chart (e.g., "LH") double LL; // Lower Low: the new low formed after the Lower High in a downtrend datetime LL_time; // Time of the Lower Low string LL_letter; // Label used to display the Lower Low on the chart (e.g., "LL") string sell_object; // Arrow object to indicate the Sell signal on the chart
// BEARISH TREND if(show_bearish == true) // Check if the user enabled the bearish trend display { if(rates_total >= bars_check) // Ensure enough candles are available for processing { // Loop through historical bars to find a swing high (potential start of bearish structure) for(int i = rates_total - bars_check; i < rates_total - LookbackBars; i++) { if(IsSwingHigh(high, i, LookbackBars)) // Detect first swing high { H = high[i]; H_time = time[i]; H_letter = StringFormat("High B%d", i); // Label for the high // From the swing high, look for the next swing low for(int j = i; j < rates_total - LookbackBars; j++) { if(IsSwingLow(low, j, LookbackBars) && time[j] > H_time) // Confirm next swing low { L = low[j]; L_time = time[j]; L_letter = StringFormat("Low B%d", j); // Label for the low // From the swing low, look for the Lower High for(int k = j; k < rates_total - LookbackBars; k++) { if(IsSwingHigh(high, k, LookbackBars) && time[k] > L_time) { LH = high[k]; LH_time = time[k]; LH_letter = StringFormat("Lower High%d", k); // Label for the Lower High // From the LH, find a Lower Low for(int l = j ; l < rates_total - LookbackBars; l++) { if(IsSwingLow(low, l, LookbackBars) && time[l] > LH_time) { LL = low[l]; LL_time = time[l]; LL_letter = StringFormat("Lower Low%d", l); // Label for Lower Low // Calculate 50% retracement level from LH to LL lvl_50 = LL + ((LH - LL)/2); // Prepare object names pre_dis_box = StringFormat("Gan Box B%d", i); lvl_50_line = StringFormat("Level 50 Line B%d", i); // Search for a bearish entry condition for(int m = l; m < rates_total-1; m++) { // Confirm bearish candle breaking below the LL if(close[m] < open[m] && close[m] < LL && time[m] >= time[l+LookbackBars]) { // Count bars for pattern distance validation n_bars = Bars(_Symbol,PERIOD_CURRENT,LH_time, time[l+LookbackBars]); // From LH to box end n_bars_2 = Bars(_Symbol,PERIOD_CURRENT,time[l+LookbackBars], time[m]); // From box end to break candle // Confirm valid bearish structure and proximity of break candle if(H > L && LH > L && LH < H && LL < L && open[l+LookbackBars] >= LL && n_bars_2 < n_bars) { // Draw the Premium/Discount box ObjectCreate(chart_id,pre_dis_box, OBJ_RECTANGLE,0,LH_time,LH, time[l+LookbackBars],LL); ObjectCreate(chart_id,lvl_50_line, OBJ_TREND,0,LH_time,lvl_50, time[l+LookbackBars],lvl_50); ObjectSetInteger(chart_id,pre_dis_box,OBJPROP_WIDTH,2); ObjectSetInteger(chart_id,lvl_50_line,OBJPROP_WIDTH,2); // Label the structure points ObjectCreate(chart_id, H_letter, OBJ_TEXT, 0, H_time, H); ObjectSetString(chart_id, H_letter, OBJPROP_TEXT, "H"); ObjectSetInteger(chart_id,H_letter,OBJPROP_FONTSIZE,15); ObjectCreate(chart_id, L_letter, OBJ_TEXT, 0, L_time, L); ObjectSetString(chart_id, L_letter, OBJPROP_TEXT, "L"); ObjectSetInteger(chart_id,L_letter,OBJPROP_FONTSIZE,15); ObjectCreate(chart_id, LH_letter, OBJ_TEXT, 0, LH_time, LH); ObjectSetString(chart_id, LH_letter, OBJPROP_TEXT, "LH"); ObjectSetInteger(chart_id,LH_letter,OBJPROP_FONTSIZE,15); ObjectCreate(chart_id, LL_letter, OBJ_TEXT, 0, LL_time, LL); ObjectSetString(chart_id, LL_letter, OBJPROP_TEXT, "LL"); ObjectSetInteger(chart_id,LL_letter,OBJPROP_FONTSIZE,15); ObjectSetInteger(chart_id,H_letter,OBJPROP_WIDTH,2); ObjectSetInteger(chart_id,L_letter,OBJPROP_WIDTH,2); ObjectSetInteger(chart_id,LL_letter,OBJPROP_WIDTH,2); ObjectSetInteger(chart_id,LH_letter,OBJPROP_WIDTH,2); // Calculate Take Profits based on 1:1 and 1:2 RR TP1 = close[m] - (lvl_50 - close[m]); TP2 = TP1 - (lvl_50 - close[m]); // Generate entry, SL and TP object names entry_line = StringFormat("Entry B%d", m); lvl_sl_line = StringFormat("SL B%d", m); lvl_tp_line = StringFormat("TP B%d", m); lvl_tp2_line = StringFormat("TP 2 B%d", m); // Draw entry, SL, TP1, TP2 levels ObjectCreate(chart_id,entry_line,OBJ_TREND,0,LH_time,close[m],time[m],close[m]); ObjectCreate(chart_id,lvl_sl_line, OBJ_TREND,0,LH_time,lvl_50, time[m],lvl_50); ObjectCreate(chart_id,lvl_tp_line, OBJ_TREND,0,LH_time,TP1, time[m],TP1); ObjectCreate(chart_id,lvl_tp2_line, OBJ_TREND,0,LH_time,TP2, time[m],TP2); ObjectSetInteger(chart_id,entry_line,OBJPROP_WIDTH,2); ObjectSetInteger(chart_id,lvl_sl_line,OBJPROP_WIDTH,2); ObjectSetInteger(chart_id,lvl_tp_line,OBJPROP_WIDTH,2); ObjectSetInteger(chart_id,lvl_tp2_line,OBJPROP_WIDTH,2); // Generate text labels entry_txt = StringFormat("Entry Text B%d", m); lvl_sl_txt = StringFormat("SL Text B%d", m); lvl_tp_txt = StringFormat("TP Text B%d", m); lvl_tp2_txt = StringFormat("TP 2 Text B%d", m); ObjectCreate(chart_id, entry_txt, OBJ_TEXT, 0,time[m],close[m]); ObjectSetString(chart_id, entry_txt, OBJPROP_TEXT, "SELL"); ObjectSetInteger(chart_id,entry_txt,OBJPROP_FONTSIZE,15); ObjectCreate(chart_id, lvl_sl_txt, OBJ_TEXT, 0,time[m],lvl_50); ObjectSetString(chart_id, lvl_sl_txt, OBJPROP_TEXT, "SL"); ObjectSetInteger(chart_id,lvl_sl_txt,OBJPROP_FONTSIZE,15); ObjectCreate(chart_id, lvl_tp_txt, OBJ_TEXT, 0,time[m],TP1); ObjectSetString(chart_id, lvl_tp_txt, OBJPROP_TEXT, "TP1"); ObjectSetInteger(chart_id,lvl_tp_txt,OBJPROP_FONTSIZE,15); ObjectCreate(chart_id, lvl_tp2_txt, OBJ_TEXT, 0,time[m],TP2); ObjectSetString(chart_id, lvl_tp2_txt, OBJPROP_TEXT, "TP2"); ObjectSetInteger(chart_id,lvl_tp2_txt,OBJPROP_FONTSIZE,15); // Draw sell arrow sell_object = StringFormat("Sell Object%d", m); ObjectCreate(chart_id,sell_object,OBJ_ARROW_SELL,0,time[m],close[m]); } break; // Exit loop after valid setup } } break; // Exit LL search } } break; // Exit LH search } } break; // Exit L search } } } } } }
Ausgabe:
Erläuterung:
Der Code beginnt mit if(show_bearish == true), um festzustellen, ob der Nutzer die Logik des Abwärtstrends aktiviert hat. Um einen legitimen Abwärtsmarkt zu finden, durchläuft der Indikator historische Balken, wenn er aktiviert ist und genügend Balken vorhanden sind (rates_total >= bars_check). Der erste Schritt in diesem Prozess besteht darin, einen hohen Umkehrpunkt (H) zu bestimmen. Nach der Identifizierung eines hohen Umkehrpunkts sucht der Code nach einem tiefen Umkehrpunkt (L), der auf dem Hoch folgt. Wird er entdeckt, sucht er weiter nach einem Tieferen Tief (LL), das die Abwärts-Struktur bestätigt, gefolgt von einem Tieferen Hoch (LH), das ein Swing-Hoch unterhalb des ersten Hochs H ist. Mit diesen Werten und den zugehörigen Zeitstempeln werden auf dem Chart Bezeichnungen wie „H“, „L“, „LH“ und „LL“ erstellt.
Als Nächstes wird die Premium/Discount-Zone von der LH zur LL gezeichnet und das 50%-Retracement-Level zwischen LH und LL bestimmt (lvl_50 = LL + ((LH - LL)/2). Der Indikator sucht nach einer Abwärtskerze (Close < Open), die unterhalb der LL schließt, bevor er irgendwelche handelsbezogenen Objekte (Entry, SL, TP1, TP2) platziert. Durch die Verwendung von zwei Variablen, n_bars, die die Balken vom Tiefen Hoch (LH) bis zum Ende der Premium-/Discount-Box zählt, und n_bars_2, die vom Ende der Box bis zur Abwärtskerze zählt, die das Tiefe Tief (LL) durchbricht, stellt der Code außerdem sicher, dass der Strukturbruch innerhalb einer angemessenen Anzahl von Balken erfolgt. Der Code zieht die Einstiegslinie nur beim Kerzenschluss, setzt den Stop Loss (SL) auf das 50%-Niveau und positioniert TP1 und TP2 auf die Risiko-Ertrags-Niveaus 1:1 bzw. 1:2, wenn alle Anforderungen erfüllt sind, einschließlich der richtigen Struktur, eines gültigen Breaks und eines angemessenen Abstands.
Außerdem wird die Abwärtskerze mit einem Verkaufspfeil und der Aufschrift „SELL“ versehen. Bei einem Abwärtstrend ist die Logik im Grunde dieselbe wie bei einem positiven Trendmuster, nur wird sie umgekehrt. Da der Abwärtstrend nur das Gegenteil des Aufwärtstrends ist, der bereits ausführlich beschrieben wurde, ist die Erklärung kurz. Die Struktur und die Argumentation sind dieselben, nur dass sie umgekehrt sind, um einen Abwärtstrend und nicht einen Aufwärtstrend zu zeigen.
Schlussfolgerung
In diesem Artikel haben wir einen nutzerdefinierten MQL5-Indikator entwickelt, der die Marktstruktur durch die Erkennung von Schlüsselpunkten wie Tief (L), Tieferes Tief (LL), Höheres Tief (HL), Hoch (H) und Höheres Hoch (HH) identifiziert. Anhand dieser Punkte bestimmt der Indikator Aufwärts- oder Abwärtstrends und zieht automatisch Einstiegspunkte, Stop-Loss bei Level 50 und Take-Profit-Level (TP1 und TP2) auf der Grundlage eines strukturierten Musters. Es markiert auch die Premium- und Discount-Zonen, um visuell hervorzuheben, wo der Preis wahrscheinlich reagieren wird. Alle Chartobjekte werden nur dann gezeichnet, wenn bestimmte Bedingungen erfüllt sind, damit die Signale sauber und zuverlässig bleiben.
Übersetzt aus dem Englischen von MetaQuotes Ltd.
Originalartikel: https://www.mql5.com/en/articles/17689





- 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.