English
preview
Aufbau des Kerzenmodells Trend-Constraint (Teil 7): Verfeinerung unseres Modells für die EA-Entwicklung

Aufbau des Kerzenmodells Trend-Constraint (Teil 7): Verfeinerung unseres Modells für die EA-Entwicklung

MetaTrader 5Handel | 18 September 2024, 09:45
12 0
Clemence Benjamin
Clemence Benjamin

Abschnitte von Interesse:


        Einführung

        Es ist möglich, einen Expert Advisor auf der Grundlage der verfügbaren Indikatoren zu entwickeln. Es gibt zwei Möglichkeiten, die ich in diesem Artikel erwähnen werde:

        1. Die Codierung der Indikatorbedingungen in den Algorithmus des Expert Advisors ist effizient und sehr schnell, auch im Strategy Tester. Mit dieser Methode funktioniert der EA ohne die Notwendigkeit, einen Indikator separat zu verwenden.
        2. Die Vorbereitung eines Expert Advisor-Algorithmus, der auf die Puffer des Indikators achtet, ist ein weiterer Ansatz. Wenn der Puffer True oder False ist, ist der EA so programmiert, dass er auf eine bestimmte Weise reagiert. Damit das System auf der MetaTrader 5-Plattform funktioniert, sind zwei Dateien erforderlich: Sowohl der EA als auch der Indikator müssen sich in bestimmten Verzeichnissen innerhalb des Plattformpfads befinden. Es ist schwierig, einen EA zu veröffentlichen, der auf einem nutzerdefinierten Indikator läuft, da das Validierungssystem Ihren Indikator nicht findet, wenn Sie versuchen, ihn in der MQL5-Community zu veröffentlichen. Ich habe es natürlich ausprobiert und bin auf Fehler gestoßen, sodass ich es nur auf meinem Computer ausführen konnte, ohne es zu veröffentlichen.

        Bevor wir weitermachen, müssen wir unseren Indikator für den EA-Auftrag vorbereiten. Dazu müssen wir sicherstellen, dass unsere Indikatorpuffer gut organisiert sind und ihre Funktionsweise verstehen, damit es einfacher wird, das Konzept zu einem EA zu entwickeln. Beide oben erwähnten Ansätze funktionieren gut, jeder mit seinen eigenen Vor- und Nachteilen, die wir in späteren Beiträgen diskutieren werden. Der Hauptvorteil der Verwendung eines Indikators neben dem EA besteht darin, dass er die Komplexität des EA-Algorithmus reduziert. Der Entwickler kann sich auf einige spezifische Teile des Algorithmus konzentrieren, da die Bedingungen bereits im Indikatorprogramm kodiert sind.

        In Teil 6 war Trend Constraint V1.08 unsere letzte Weiterentwicklung, die zwei wichtige Integrationen in einem Programm vereint. Wir freuen uns über diesen Fortschritt, da wir nun problemlos auf die Signale auf Telegram und WhatsApp zugreifen können. Es sind jedoch einige Fragen zu stellen:

        • Ja, wir empfangen jetzt die Signale, aber sind es auch die besten?
        • Wir können Handelsaufträge erteilen, aber wann sollten wir aussteigen?

        Diese Fragen lassen sich nur beantworten, wenn man sich die Grafik noch einmal ansieht, um zu sehen, wie sich der Indikator in der Vergangenheit entwickelt hat, und wenn man den Quellcode neu kodiert, um neue Funktionen hinzuzufügen oder das aktuelle System zu verbessern. Ich schlage vor, das derzeitige System für Expert Advisors zu verfeinern:

        1. Verstehen Sie den Zweck eines jeden Puffers so gründlich wie möglich.
        2. Wiedereinführung von gleitenden Durchschnittswerten als Einstiegssignale.
        3. Wir zeichnen mit Hilfe der Rechtecke die Chance-Risiko-Verhältnisse: grün für die Darstellung des Gewinnzielbereichs und rot für den Verlustbereich.

        Unser Ziel ist es, den Indikator so zu gestalten, dass er Ein- und Ausstiegskonzepte anzeigt, die der Händler manuell verfolgen kann. Wenn eine Strategie manuell ausgeführt werden kann, kann sie auch automatisiert werden. Das Risiko-Ertrags-Verhältnis (risk-reward ratio, RRR) hilft Händlern, die potenzielle Rentabilität eines Geschäfts im Verhältnis zu seinem Risiko zu bewerten. Wir werden erörtern, wie wir potenzielle Ausstiegsniveaus skizzieren können, indem wir neue Merkmale in unseren Indikator einbeziehen, einschließlich der Formel für die RRR, die lautet:

         


        Risiko-Ertrags-Verhältnis, Formel.

        Wobei:

        RRR ist das Risiko-Ertrags-Verhältnis;
        Der potenzielle Verlust ist der Betrag, den wir verlieren können, wenn sich der Handel oder die Anlage gegen uns entwickelt;   
        Der potenzielle Gewinn ist der Betrag, den wir erwarten zu gewinnen, wenn sich der Handel oder die Investition zu unseren Gunsten entwickelt.

        Entwickeln wir ein Beispiel:

        Angenommen, wir erwägen den Kauf einer Aktie zu 50 $. Wir setzen unseren Stop-Loss auf 48 $ (was einen möglichen Verlust von 2 $ pro Aktie bedeutet) und setzen ein Kursziel von 56 $ (was einen möglichen Gewinn von 6 $ pro Aktie bedeutet). Berechnen des Risiko-Ertrags-Verhältnis für die Aktie.

        • Möglicher Verlust: $50 (Einstiegskurs) - $48 (Stop-Loss) = $2
        • Möglicher Gewinn: $56 (Zielkurs) - $50 (Einstiegskurs) = $6

        Setzen wir die Werte in die Formel ein: Risiko-Ertrags-Verhältnis = 1/3

        Im Einzelnen bedeutet dies, dass wir für jeden $1, den wir riskieren, einen Gewinn von $3 erwarten. Ein niedrigeres Verhältnis (z. B. 1:1 oder weniger) deutet auf ein höheres Risiko im Verhältnis zum potenziellen Ertrag hin, während ein höheres Verhältnis auf ein günstigeres Risiko-Ertrags-Szenario schließen lässt.

        Anhand des obigen Beispiels ist hier eine Illustration mit Rechtecken: ein rotes Rechteck für das Risiko und ein grünes Rechteck für die Belohnung.

        Illustration des Risiko-Ertrags-Verhältnisses


        Wir werden dies in den nächsten Abschnitten des Artikels näher erläutern. Am Ende dieser Diskussion wollen wir uns auf folgende Punkte konzentrieren:

        1. Verständnis für die Bedeutung des Risikomanagements im Algorithmenhandel.
        2. Umsetzung von Risiko-Ertrags-Verhältnissen und deren mathematische Grundlagen.
        3. Entwicklung von dynamischen Ausstiegsstrategien für ein optimales Handelsmanagement.
        4. Verbesserung der visuellen Indikatoren für bessere Handelsentscheidungen.
        5. Prüfung und Validierung des Indikators für die praktische Anwendung.


        Ermittlung der Grenzen des derzeitigen Systems

        Um ein Indikatorensystem mit neuen Merkmalen weiterzuentwickeln, ist es wichtig, wichtige Teile zu überprüfen, um festzustellen, in welchen Bereichen das derzeitige System hinterherhinkt. Um dies zu erreichen, habe ich zwei Techniken in Betracht gezogen, die mir bei der Identifizierung und Lösung von Problemen helfen sollen. Das sind:

        1. Überarbeitung des Charts: Dazu gehört es, den Verlauf des Indikatorfensters zu überprüfen, seine Darstellung zu notieren und etwaige Anomalien zu ermitteln.  
        2. Überarbeitung des Codes: Dies ist die zweite Phase: Nach Abschluss von Schritt 1 werden wir die Codebereiche untersuchen, die mit den festgestellten Problemen zusammenhängen, um sie zu beheben.


        Überarbeitung des Charts

        Hier sehen Sie ein Bild des Charts des Boom 500. Ich werde die Probleme, so wie ich sie sehe, in der Tabelle skizzieren:


        Boom 500 Index M1


        • Aus dem Chart geht hervor, dass es sich bei (A) um eine Spike-Kerze handelt. In diesem Fall wird der Pfeil unterhalb der Kerze angezeigt, obwohl er oberhalb der Kerze stehen sollte. Dieses Problem tritt auf, weil der Pfeil die überkaufte RSI-Zone darstellt, die erst nach dem Schließen der Kerze auftritt.
        • Die Anzeigefunktion DRAW_LINE funktioniert gut und ermöglicht es uns, neue Trends durch die Beobachtung von Farbveränderungen in der Linie zu erkennen. Wir wollen, dass er einen Swing-Trade signalisiert, denn wenn der Markt seine Richtung ändert, bewegt er sich oft deutlich.
        • Blaue Kaufsignale stehen für eine potenzielle Trendfortsetzung und zeigen derzeit die überverkaufte Zone des RSI in einem vorherrschenden Aufwärtstrend an. Denken Sie daran, dass wir unsere Signale auf die D1-Kerze, die Marktstimmung, beschränken wollen. Auch wenn extreme Werte des Relative Strength Index (RSI) gute Einstiegsmöglichkeiten bieten, weisen sie in der Regel eher auf die Marktzone als auf eine garantierte Umkehr hin. Der Markt könnte unabhängig von den RSI-Werten weiterhin im Trend liegen. Ich schlage vor, das Kreuzen der gleitende Durchschnitte für andere Einstiege wieder einzuführen, um die Gewinne zu maximieren, da diese in der Regel gut mit der Kursentwicklung übereinstimmt.
        • Ich kann mir auch vorstellen, dass es von Vorteil ist, Rechtecke für das Risiko-Ertrags-Verhältnis zu zeichnen, wenn der Indikator Einstiegssignale sendet, wie in der Einleitung erwähnt. Diese neue Version muss die Rechtecke genau umreißen, um das Risiko und die Chancen korrekt darzustellen. Sehen Sie sich die Idee in den folgenden Charts an: der erste ist der EURUSD, der zweite der Boom 500, nacheinander.

        EURUSD, M1: Euro gegen US Dollar

        EURUSD


        Boom 500 Index, M1:

        BOOM 500 INDEX M1

        Anhand dieser Charts können wir den neuen Ansatz deutlich erkennen. Wir wollen unseren Indikatorcode so vorbereiten, dass er genau das widerspiegelt, was manuell demonstriert wurde. Laut mehreren technischen Analysten und Händlern werden Pivots üblicherweise als Ziele für Gewinnmitnahmen und Stop-Loss respektiert. Daher kann die Idee eines 1:3-Risiko-Ertrags-Verhältnisses in Szenarien wie den in der Grafik gezeigten, in denen das Risiko manchmal viel größer ist als der potenzielle Gewinn, unanwendbar werden.

        Zusammenfassend lässt sich aus den obigen Abbildungen ablesen:

        1. Pivots dienen als Referenzpunkte für die Festlegung von Ausstiegspunkten, einschließlich Stop-Loss und Take-Profit.
        2. Die Preise für die Einstiegs- und Ausstiegsebenen können später in der Logik verwendet werden.  

        Im nächsten Abschnitt werden wir die kritischen Bereiche des Codes untersuchen und sie angehen.


        Überarbeitung des Codes:

        Hier ist ein Ausschnitt, an dem wir Änderungen vornehmen können:
        #property indicator_type1 DRAW_ARROW
        #property indicator_width1 5
        #property indicator_color1 0xFF3C00
        #property indicator_label1 "Buy" // We are going to change it to "Buy Zone"
        
        #property indicator_type2 DRAW_ARROW
        #property indicator_width2 5
        #property indicator_color2 0x0000FF
        #property indicator_label2 "Sell"// We are going to change it to "Sell Zone"


        In diesem Abschnitt des Programms wird die Position des Pfeils auf dem Kerzenständer festgelegt. Es handelt sich um die Iterationsfunktion von Puffer 2. Der Indikator muss am Hochpunkt der Kerze angezeigt werden, also wechseln wir von Low[i] zu High[i];

        //Indicator Buffer 2 // We are going to set the indicator to display at candlestick high by changing the highlighted text to High[i]
              if(RSI[i] > Overbought
              && RSI[i+1] < Overbought //Relative Strength Index crosses above fixed value
              && Open[barshift_M1[i]] <= Close[1+barshift_D1[i]] //Candlestick Open <= Candlestick Close
              && MA[i] < MA2[i] //Moving Average < Moving Average
              && MA3[i] < MA4[i] //Moving Average < Moving Average
              )
        
                {
                 Buffer2[i] = Low[i]; //Set indicator value at Candlestick Low // change to High[i]
                 if(i == 1 && Time[1] != time_alert) myAlert("indicator", "Sell Zone"); //Alert on next bar open
                 time_alert = Time[1];
                }
              else
                {
                 Buffer2[i] = EMPTY_VALUE;
                }



        Einbeziehung des gleitenden Durchschnitts-Crossovers:

        Lassen Sie uns den folgenden Codeausschnitt besprechen, um den Prozess des Hinzufügens einer neuen Funktion zu unserem Programm aufzudecken. Zusätzlich zu den bestehenden Puffern werden wir zwei neue hinzufügen, nämlich Puffer 6 und Puffer 7.

        Entsprechend der Logik unseres intelligenten Programms werden wir zunächst seine Eigenschaften definieren. Da wir vor kurzem „Sell Zone“ und „Buy Zone“ für den RSI-Indikator eingeführt haben, werden dies die neuen „Sell“ und „Buy“ Signale für den Indikator sein.

        #property indicator_type6 DRAW_ARROW
        #property indicator_width6 1
        #property indicator_color6 0x0000FF
        #property indicator_label6 "Sell"
        
        #property indicator_type7 DRAW_ARROW
        #property indicator_width7 1
        #property indicator_color7 0xFFAA00
        #property indicator_label7 "Buy"

        Wir müssen die Eingaben für die Anpassung festlegen, um die besten Signale zu erhalten. Wir werden einen gleitenden Durchschnitt von 7 und einen gleitenden Durchschnitt von 21 verwenden.

        input int Entry_MA_fast = 7 ;
        input int Entry_MA_slow = 21 ;

        Die OnCalculate-Funktion für die neuen Funktionen wird hier gezeigt. Sie beschreibt, wie das Programm die Ergebnisse anzeigt, wenn die Bedingungen erfüllt sind.

        SetIndexBuffer(5, Buffer6);
           PlotIndexSetDouble(5, PLOT_EMPTY_VALUE, EMPTY_VALUE);
           PlotIndexSetInteger(5, PLOT_DRAW_BEGIN, MathMax(Bars(Symbol(), PERIOD_CURRENT)-PLOT_MAXIMUM_BARS_BACK+1, OMIT_OLDEST_BARS+1));
           PlotIndexSetInteger(5, PLOT_ARROW, 242);
           SetIndexBuffer(6, Buffer7);
           PlotIndexSetDouble(6, PLOT_EMPTY_VALUE, EMPTY_VALUE);
           PlotIndexSetInteger(6, PLOT_DRAW_BEGIN, MathMax(Bars(Symbol(), PERIOD_CURRENT)-PLOT_MAXIMUM_BARS_BACK+1, OMIT_OLDEST_BARS+1));
           PlotIndexSetInteger(6, PLOT_ARROW, 241);


        In dieser Phase werden die Bedingungen für das Überschreiten des gleitenden Durchschnitts festgelegt. In diesem Szenario entsprechen die Bedingungen für Puffer 7 denen von Puffer 6. Der Vorgang ist im Wesentlichen derselbe, aber für jeden Puffer umgekehrt.

        //Indicator Buffer 6
              if(MA8[i] < MA9[i]
              && MA8[i+1] > MA9[i+1] //Moving Average crosses below Moving Average
              && Open2[i] <= Close[1+barshift_D1[i]] //Candlestick Open <= Candlestick Close
              )
                {
                 Buffer6[i] = High[i]; //Set indicator value at Candlestick High
                 if(i == 1 && Time[1] != time_alert) myAlert("indicator", "Sell"); //Alert on next bar open
                 time_alert = Time[1];
                }
              else
                {
                 Buffer6[i] = EMPTY_VALUE;
                }
              //Indicator Buffer 7
              if(MA8[i] > MA9[i]
              && MA8[i+1] < MA9[i+1] //Moving Average crosses above Moving Average
              && Open2[i] >= Close[1+barshift_D1[i]] //Candlestick Open >= Candlestick Close
              )
                {
                 Buffer7[i] = Low[i]; //Set indicator value at Candlestick Low
                 if(i == 1 && Time[1] != time_alert) myAlert("indicator", "Buy"); //Alert on next bar open
                 time_alert = Time[1];
                }
              else
                {
                 Buffer7[i] = EMPTY_VALUE;
                }


        Zeichnen der Rechtecke

        Ich hatte viel Spaß bei der Arbeit mit dem Code. Ich erwog, auf dem bestehenden Programm aufzubauen, indem ich die kürzlich eingefügten Puffer um eine Funktion zur Platzierung von Rechtecken ergänzte, die die Risiko- und Gewinnzonen markieren. Außerdem begann ich damit, die Logik auf dem Papier zu skizzieren, wie die Objekte platziert werden und welche Puffer mit ihnen verbunden sein werden, insbesondere Puffer 6 und Puffer 7.

        Hier ist die Papierlogik vor der eigentlichen Implementierung:

         Bei einem sich schnell bewegenden Durchschnitt, Überkreuzung unter einem sich langsam bewegenden Durchschnitt: 

        Wenn die Bedingungen von Buffer7 erfüllt sind, soll ein Rechteck unter dem Tiefpunkt der aktuellen Kerze platziert werden.
          • Die Breite des Rechtecks muss mindestens 5 Balken betragen.
          • Die Höhe des Rechtecks ist X Punkte nach unten, wobei X die Anzahl der Punkte ist.
          • Das Rechteck muss grün sein.
          • Über dem grünen Rechteck, wenn die Bedingungen von Puffer7 erfüllt sind, platzieren Sie ein rotes Rechteck, das 1/3 der Höhe des grünen Rechtecks hat.
          • Alle Parameter müssen anpassbar sein.

        Bei einem Kreuzen des schnellen, gleitenden Durchschnitts über den langsamen, wenn die Bedingungen von Buffer 6 erfüllt sind, wollen wir ein Rechteck über dem Hoch der aktuellen Balken platzieren.

        Hier sind die Spezifikationen sind wie folgt:

          • Die Breite des Rechtecks sollte 5 Balken oder mehr betragen.
          • Die Höhe des Rechtecks sollte X Punkte nach oben betragen, wobei X die Anzahl der Punkte ist.
          • Das Rechteck muss grün sein.
          • Wenn die Bedingungen von Puffer 6 erfüllt sind, platzieren wir unter dem grünen Rechteck ein rotes Rechteck, das 1/3 der Höhe des grünen Rechtecks hat.
          • Alle Parameter müssen anpassbar sein.

        Erstens brauchen wir die Möglichkeit, Parameter zur Anpassung einzugeben. Hier ist der Codeausschnitt, der das Einbinden zeigt:

        //--- new inputs for rectangles
        input int RectWidth = 5;                 // Width of the rectangle in bars
        input int RectHeightPointsBuy = 50;      // Height of the profit rectangle in points for Buy
        input int RectHeightPointsSell = 50;     // Height of the profit rectangle in points for Sell
        input color ProfitRectColor = clrGreen;  // Color of the profit rectangle
        input color RiskRectColor = clrRed;      // Color of the risk rectangle
        
        Standardmäßig habe ich die Punkte auf 50 gesetzt, aber diese können an Ihre Bedürfnisse und Gewinnziele angepasst werden. Ich habe auch eine Funktion eingebaut, die automatisch die Höhe des Risiko-Rechtecks im Verhältnis zum Gewinnziel anpasst. Dies ist wichtig, damit sich der Indikator an Änderungen der Hintergrundfarbe anpassen kann. Wenn Sie z. B. einen grünen Charthintergrund verwenden, könnte das grüne Rechteck verschwimmen und unsichtbar werden, während ein gelber Hintergrund einen besseren Kontrast bieten würde. Die Anpassung ist entscheidend, um die volle Kontrolle über unsere Tools zu gewährleisten.


        Untersuchen wir nun die Funktion OnCalculate, um zu sehen, wie die Iteration funktioniert.

        if(RSI[i] > Overbought) {
                if(close[i] > MA[i])
                    Buffer6[i] = close[i] - pips * myPoint;
            }
        
            if(RSI[i] < Oversold) {
                if(close[i] < MA[i])
                    Buffer7[i] = close[i] + pips * myPoint;
            }
        
            if(Buffer6[i] > 0) {
                Buffer1[i] = close[i] - pips * myPoint;
                Buffer3[i] = close[i] - pips * myPoint;
                if (Buffer6[i - 1] < 0) {
                    myAlert("indicator", "Sell Signal Detected!");
                    if (Audible_Alerts)
                        Alert(Symbol(), " ", Period(), ": Sell Signal Detected!");
        
                    // Create profit rectangle for Sell
                    double highProfitRect = close[i];
                    double lowProfitRect = close[i] - RectHeightPointsSell * myPoint;
                    string profitRectName = "SellProfitRect" + IntegerToString(i);
                    if (ObjectFind(0, profitRectName) != 0) {
                        ObjectCreate(0, profitRectName, OBJ_RECTANGLE, 0, time[i], highProfitRect, time[i + RectWidth], lowProfitRect);
                        ObjectSetInteger(0, profitRectName, OBJPROP_COLOR, ProfitRectColor);
                        ObjectSetInteger(0, profitRectName, OBJPROP_STYLE, STYLE_SOLID);
                        ObjectSetInteger(0, profitRectName, OBJPROP_WIDTH, 2);
                    }
        
                    // Create risk rectangle for Sell
                    double highRiskRect = close[i];
                    double lowRiskRect = close[i] + (RectHeightPointsSell / 3) * myPoint;
                    string riskRectName = "SellRiskRect" + IntegerToString(i);
                    if (ObjectFind(0, riskRectName) != 0) {
                        ObjectCreate(0, riskRectName, OBJ_RECTANGLE, 0, time[i], highRiskRect, time[i + RectWidth], lowRiskRect);
                        ObjectSetInteger(0, riskRectName, OBJPROP_COLOR, RiskRectColor);
                        ObjectSetInteger(0, riskRectName, OBJPROP_STYLE, STYLE_SOLID);
                        ObjectSetInteger(0, riskRectName, OBJPROP_WIDTH, 2);
                    }
                }
            }
        
            if(Buffer7[i] > 0) {
                Buffer2[i] = close[i] + pips * myPoint;
                Buffer4[i] = close[i] + pips * myPoint;
                if (Buffer7[i - 1] < 0) {
                    myAlert("indicator", "Buy Signal Detected!");
                    if (Audible_Alerts)
                        Alert(Symbol(), " ", Period(), ": Buy Signal Detected!");
        
                    // Create profit rectangle for Buy
                    double highProfitRect = close[i] + RectHeightPointsBuy * myPoint;
                    double lowProfitRect = close[i];
                    string profitRectName = "BuyProfitRect" + IntegerToString(i);
                    if (ObjectFind(0, profitRectName) != 0) {
                        ObjectCreate(0, profitRectName, OBJ_RECTANGLE, 0, time[i], highProfitRect, time[i + RectWidth], lowProfitRect);
                        ObjectSetInteger(0, profitRectName, OBJPROP_COLOR, ProfitRectColor);
                        ObjectSetInteger(0, profitRectName, OBJPROP_STYLE, STYLE_SOLID);
                        ObjectSetInteger(0, profitRectName, OBJPROP_WIDTH, 2);
                    }
        
                    // Create risk rectangle for Buy
                    double highRiskRect = close[i] - (RectHeightPointsBuy / 3) * myPoint;
                    double lowRiskRect = close[i];
                    string riskRectName = "BuyRiskRect" + IntegerToString(i);
                    if (ObjectFind(0, riskRectName) != 0) {
                        ObjectCreate(0, riskRectName, OBJ_RECTANGLE, 0, time[i], highRiskRect, time[i + RectWidth], lowRiskRect);
                        ObjectSetInteger(0, riskRectName, OBJPROP_COLOR, RiskRectColor);
                        ObjectSetInteger(0, riskRectName, OBJPROP_STYLE, STYLE_SOLID);
                        ObjectSetInteger(0, riskRectName, OBJPROP_WIDTH, 2);
                    }
                }
            
        

        Hier findet die Erstellung von Rechtecken statt:

        Gewinn-Rechtecke:

        • Bei Verkaufssignalen erstreckt sich ein grünes Rechteck vom Schlusskurs nach unten.
        • Bei Kaufsignalen erstreckt sich ein grünes Rechteck vom Schlusskurs nach oben.


        Risiko-Rechtecke:

          • Bei Verkaufssignalen erstreckt sich ein rotes Rechteck vom Schlusskurs nach oben, mit einer Höhe von 1/3 des Gewinnrechtecks.
          • Bei Kaufsignalen erstreckt sich ein rotes Rechteck vom Schlusskurs nach unten, mit einer Höhe von 1/3 des Gewinnrechtecks.

        Erläutern wir nun kurz die neuen Funktionen, die eingeführt werden;

        • ObjectFind: Funktion, mit der geprüft wird, ob ein Objekt mit einem bestimmten Namen bereits im Chart vorhanden ist.

        int ObjectFind(
           long   chart_id,   // Chart ID (0 means the current chart)
           string name        // Name of the object to search for
        );
        


        • ObjectCreate: Funktion zur Erstellung eines neuen grafischen Objekts im Chart. Die Art des Objekts muss angegeben werden (z. B. Rechteck, Trendlinie usw.).

        bool ObjectCreate(
           long    chart_id,   // Chart ID (0 means the current chart)
           string  name,       // Name of the object to create
           ENUM_OBJECT type,   // Type of the object (e.g., OBJ_RECTANGLE, OBJ_TREND, etc.)
           int     sub_window, // Number of the subwindow (0 means the main chart window)
           datetime time1,     // First coordinate time
           double  price1,     // First coordinate price
           ...                 // Additional coordinates depending on the object type
        );
        


        • ObjectSetInteger: Funktion zum Einstellen ganzzahliger Eigenschaften eines grafischen Objekts, wie Farbe, Stil und Breite.

        bool ObjectSetInteger(
           long   chart_id,     // Chart ID (0 means the current chart)
           string name,         // Name of the object
           int    prop_id,      // ID of the property to set (e.g., OBJPROP_COLOR, OBJPROP_STYLE, etc.)
           long   value         // Value of the property
        );
        

        Diese Funktion gibt einen booleschen Wert zurück;

        • true: Wenn die Eigenschaft erfolgreich gesetzt wurde.
        • false: Wenn die Einstellung der Eigenschaft fehlschlägt.

        Um tiefer in die Materie dieser Funktionen einzudringen, studieren Sie die MQL5 Dokumentation, die sie erklärt und reich an Beispielen ist. 


        Einführung von Ausstiegspunkten

        Die Einführung von Ausstiegspunkten ist eine entscheidende Verbesserung für Trend Constraint V1.08. Eine wirksame Ausstiegsstrategie schützt nicht nur die Gewinne und minimiert die Verluste. Wir schlagen vor, vordefinierte Ausstiegspunkte auf der Grundlage von wichtigen Unterstützungs- und Widerstandsniveaus zu integrieren, die für die Identifizierung potenzieller Umkehrzonen entscheidend sind. Die bisherige Signallogik beinhaltete nur den Namen des Paares, aber die neue Verfeinerung liefert alle relevanten Preise, einschließlich Entry, Stop-Loss und Take-Profit. Anhand dieser Niveaus können Händler Ausstiegspunkte bestimmen, an denen der Kurs wahrscheinlich umkehren oder auf einen bedeutenden Widerstand stoßen wird, und so ihre Ausstiegsstrategie optimieren.


        Hier ist der Ansatz:

        Um das Programm so zu ändern, dass es Preislinien entlang von Rechtecken einfügt, fügen wir drei Linien auf bestimmten Preisniveaus hinzu:

        • den Signalpreis (Schlusskurs des Signals), 
        • das Gewinnziel, 
        • und das Risikoziel. 

        Diese Zeilen werden unmittelbar nach der Erkennung des Signals eingefügt. Wir werden auch Warnmeldungen einrichten, um den Nutzer über diese spezifischen Preisniveaus zu informieren.

        Zunächst definieren wir die Punkte für Gewinn und Risiko als Eingaben, um sie anpassbar zu machen.

        input double profitPoints = 60; // Points for profit target
        input double riskPoints = 20;   // Points for risk target
        

        An dieser Stelle möchte ich nutzerdefinierte Funktionen für die Platzierung von Linien vorstellen. Der folgende Ausschnitt zeigt die Definition dieser Funktionen. Wenn Sie neu in MQL5 sind, beachten Sie, dass void verwendet wird, um anzuzeigen, dass eine Funktion keinen Wert zurückgibt. 

        void CreatePriceLine(string name, color lineColor, double price, datetime time) {
            if (ObjectFind(0, name) == -1) {
                ObjectCreate(0, name, OBJ_HLINE, 0, time, price);
                ObjectSetInteger(0, name, OBJPROP_COLOR, lineColor);
                ObjectSetInteger(0, name, OBJPROP_WIDTH, 2);
            } else {
                ObjectMove(0, name, 0, time, price);
            }
        }
        
        void PlaceSignalLines(double signalPrice, double profitTarget, double riskTarget, datetime time) {
            CreatePriceLine("SignalPriceLine", clrBlue, signalPrice, time);
            CreatePriceLine("ProfitTargetLine", clrGreen, profitTarget, time);
            CreatePriceLine("RiskTargetLine", clrRed, riskTarget, time);
        }
        


        Schließlich folgt die Logik für die Signalerkennung. Wie Sie sehen können, haben wir Alarme, die auf wichtige Kursniveaus hinweisen, die dem Händler bei der manuellen Ausführung des Programms helfen werden. 

        void CheckSignalAndPlaceLines() {
            for (int i = rates_total - 2; i >= 0; i--) {
                if (Buffer6[i] != 0.0) { // Buy Signal Detected
                    double signalPrice = Close[i];
                    double profitTarget = signalPrice + profitPoints * Point;
                    double riskTarget = signalPrice - riskPoints * Point;
                    PlaceSignalLines(signalPrice, profitTarget, riskTarget, Time[i]);
                    Alert("Buy Signal: Signal Price = ", signalPrice, " Profit Target = ", profitTarget, " Risk Target = ", riskTarget);
                }
                if (Buffer7[i] != 0.0) { // Sell Signal Detected
                    double signalPrice = Close[i];
                    double profitTarget = signalPrice - profitPoints * Point;
                    double riskTarget = signalPrice + riskPoints * Point;
                    PlaceSignalLines(signalPrice, profitTarget, riskTarget, Time[i]);
                    Alert("Sell Signal: Signal Price = ", signalPrice, " Profit Target = ", profitTarget, " Risk Target = ", riskTarget);
                }
            }
        }
        


        Zusammenfassend lässt sich sagen, dass wir drei wichtige Änderungen an unserem Programm vorgenommen haben, die ich im Folgenden erläutern werde:

        1. Wir haben das Trend Constraint V1.08 verfeinert und einen gleitenden Durchschnitts-Crossover für Einstiegssignale eingebaut.
        2. Wir erörterten die Einbeziehung von Rechtecken zur Darstellung von Risiko- und Gewinnzonen.
        3. Außerdem haben wir über die Bedeutung von Ausstiegspunkten in unserem Programm gesprochen.

        Aus der Zusammenfassung ergibt sich die Möglichkeit von drei Versionen unseres Programms, das wir weiter verfeinern werden. Wir haben jetzt Trend Constraint V1.09, V1.10, und V1.11. Als Nächstes werden wir die Testleistung und die Ergebnisse diskutieren.


        Tests und Validierung

        Das Testen und Kompilieren der Version V1.09 verlief erfolgreich, obwohl einige Probleme auftraten, die vollständig gelöst wurden. Hier sind einige Bilder, die ein Panel mit einem erfolgreichen Start zeigen, sowie eine Profiler-Zusammenfassung, die die Leistung der Funktion auf der CPU detailliert darstellt.


        Einführung von Trend Constraint V1.09.


        Das Profilierungsergebnis des Testers:

        MetaEditor Profiler


        Bevor wir den oben genannten Erfolg erzielten, traten Fehler auf, die anschließend behoben wurden. Ich freue mich, Ihnen mitteilen zu können, wie sich dieser Prozess entwickelt hat. Siehe das Bild unten:


        Fehlerprotokoll


        Nach dem Lesen des Fehlerprotokolls war es einfacher, das Programm zu überprüfen und festzustellen, was fehlte. Nach dem Hinzufügen des gleitenden Durchschnitts mussten wir auch die MA8, MA9, MA_handle8 und MA_handle9 für die neuen Puffer neben den bereits vorhandenen deklarieren. Der nachstehende Code zeigt die vorgenommenen Deklarationen. Die hervorgehobenen Zeilen zeigen die Zeilen an, die die Fehler verursacht haben.

        int RSI_handle;
        double RSI[];
        double Open[];
        double Close[];
        int MA_handle;
        double MA[];
        int MA_handle2;
        double MA2[];
        int MA_handle3;
        double MA3[];
        int MA_handle4;
        double MA4[];
        double Low[];
        double High[];
        int MA_handle5;
        double MA5[];
        int MA_handle6;
        double MA6[];
        int MA_handle7;
        double MA7[];
        int MA_handle8;
        double MA8[];
        int MA_handle9;
        double MA9[];
        double Open2[];

        Nachdem die hervorgehobenen Deklarationen hinzugefügt wurden, wurde das Programm erfolgreich kompiliert.

        In V1.10 wurde das Programm nach dem Einfügen von Rechtecken erfolgreich kompiliert, aber im Chart erscheint nichts. Es ist schwierig festzustellen, wo wir etwas übersehen haben, daher werden wir die Fehlersuche Zeile für Zeile fortsetzen, bis das Problem behoben ist, und die Ergebnisse in den nächsten Beiträgen mitteilen.

        In V1.11 traten Kompilierungsfehler auf, und ich werde den gesamten Lösungsprozess in der nächsten Schrift mitteilen.

        Eine Zusammenfassung der Puffer im Programm ist für die EA-Entwicklung wichtig, da sie uns hilft zu verstehen, welcher Puffer die erforderlichen Bedingungen erfüllt. Siehe die nachstehende Tabelle.

        Puffer Beschreibung
        Buffer1. Dieser Puffer wird verwendet, um festzustellen, ob der RSI einen festen Wert überschreitet (überkauft), der aktuelle Eröffnungskurs größer oder gleich dem Schlusskurs eines bestimmten Balkens ist, zwei gleitende Durchschnitte (MA und MA3) beide größer sind als ihre jeweiligen gleitenden Vergleichsdurchschnitte (MA2 und MA4) und eine „Kaufzone“ auf dem Chart markiert ist.
        Buffer2.
        Dieser Puffer wird verwendet, um festzustellen, ob der RSI einen festen Wert überschreitet (überkauft), der aktuelle Eröffnungskurs kleiner oder gleich dem Schlusskurs eines bestimmten Balkens ist, zwei gleitende Durchschnitte (MA und MA3) beide kleiner als ihre jeweiligen gleitenden Vergleichsdurchschnitte (MA2 und MA4) sind und eine „Verkaufszone“ auf dem Chart markiert wird.
        Buffer3.
        Dieser Puffer wird verwendet, um zu erkennen, ob MA5 über MA6 kreuzt und einen „Buy Swing“ auf dem Chart markiert.
        Buffer4.
        Dieser Puffer wird verwendet, um zu erkennen, ob MA5 unter MA6 fällt und einen „Sell Swing“ auf dem Chart markiert.
        Buffer5.
        Dieser Puffer verfolgt, ob MA3 größer als MA7 ist.
        Buffer6.
        Dieser Puffer wird verwendet, um zu erkennen, ob MA8 unter MA9 kreuzt und schreibt dann ein „Sell“ auf den Chart.
        Buffer7.
        Dieser Puffer wird verwendet, um zu erkennen, ob MA8 über MA9 kreuzt und schreibt dann ein „Buy“ auf den Chart.
        Buffer8.  Dieser Puffer verfolgt, ob MA3 kleiner als MA7 ist.


        Schlussfolgerung

        Zusammenfassend lässt sich sagen, dass die Entwicklung und Verfeinerung von Trend Constraint einen bedeutenden Fortschritt im Algorithmenhandel darstellt. Indem wir die Grenzen des derzeitigen Systems angehen und strategische Ausstiegspunkte einführen, wollen wir einen robusteren und wirksameren Indikator schaffen. Die Einbindung von Stop-Loss- und Take-Profit-Levels sowie verbesserte visuelle Darstellungen sorgen dafür, dass der Indikator nutzerfreundlich ist und sich an wechselnde Marktbedingungen anpassen lässt. Diese Funktionen bieten nun eine klare visuelle Darstellung potenzieller Handelsergebnisse und tragen so zu einer besseren Entscheidungsfindung bei.

        Durch strenge Tests und Validierung haben wir die Zuverlässigkeit und Wirksamkeit des Indikators nachgewiesen. Obwohl wir auf Fehler gestoßen sind, sind wir hartnäckig geblieben und haben sie durch gründliche Recherche behoben. Wir werden die Dokumentation nutzen und mit der Entwicklergemeinschaft zusammenarbeiten, um alle verbleibenden Probleme zu lösen.

        Insgesamt bietet das erweiterte Trend Constraint eine umfassende Lösung für Händler, die sich mit Zuversicht und Präzision durch die Komplexität der Finanzmärkte bewegen wollen. Seine fortschrittlichen Funktionen und sein strategischer Ansatz für das Risikomanagement und die Gewinnoptimierung machen ihn zu einer wertvollen Ergänzung für jeden Trader.

        Im Anhang finden Sie die Dateien für die weitere Entwicklung Ihrer Projekte. Viel Spaß beim Entwickeln!

        Dateiname Beschreibung
        Trend Constraint V1.09 Verfeinertes Programm mit kreuzenden MAs.
        Trend Constraint V1.10 Fortgeschrittenes Programm mit Funktionen zum Zeichnen von Objekten. Wir befinden uns noch in der Debugging-Phase.


        Übersetzt aus dem Englischen von MetaQuotes Ltd.
        Originalartikel: https://www.mql5.com/en/articles/15154

        Selbstoptimierende Expert Advisors mit MQL5 und Python erstellen Selbstoptimierende Expert Advisors mit MQL5 und Python erstellen
        In diesem Artikel werden wir erörtern, wie wir Expert Advisors erstellen können, die in der Lage sind, Handelsstrategien auf der Grundlage der vorherrschenden Marktbedingungen eigenständig auszuwählen und zu ändern. Wir werden etwas über Markov-Ketten lernen und wie sie algorithmischen Händler helfen können.
        Kolmogorov-Smirnov-Test bei zwei Stichproben als Indikator für die Nicht-Stationarität von Zeitreihen Kolmogorov-Smirnov-Test bei zwei Stichproben als Indikator für die Nicht-Stationarität von Zeitreihen
        Der Artikel befasst sich mit einem der bekanntesten nichtparametrischen Homogenitätstests – dem Kolmogorov-Smirnov-Test mit zwei Stichproben. Es werden sowohl Modelldaten als auch reale Kurse analysiert. Der Artikel enthält auch ein Beispiel für die Konstruktion eines Nicht-Stationaritätsindikators (iSmirnovDistance).
        Eine alternative Log-datei mit der Verwendung der HTML und CSS Eine alternative Log-datei mit der Verwendung der HTML und CSS
        In diesem Artikel werden wir eine sehr einfache, aber leistungsfähige Bibliothek zur Erstellung der HTML-Dateien schreiben, dabei lernen wir auch, wie man eine ihre Darstellung einstellen kann (nach seinem Geschmack) und sehen wir, wie man es leicht in seinem Expert Advisor oder Skript hinzufügen oder verwenden kann.
        Neuronale Netze leicht gemacht (Teil 87): Zeitreihen-Patching Neuronale Netze leicht gemacht (Teil 87): Zeitreihen-Patching
        Die Vorhersage spielt eine wichtige Rolle in der Zeitreihenanalyse. Im neuen Artikel werden wir über die Vorteile des Zeitreihen-Patchings sprechen.