English Русский 中文 Español 日本語 Português
preview
Aufbau des Kerzenmodells Trend-Constraint (Teil 8): Entwicklung eines Expert Advisors (I)

Aufbau des Kerzenmodells Trend-Constraint (Teil 8): Entwicklung eines Expert Advisors (I)

MetaTrader 5Tester | 18 Oktober 2024, 10:30
113 0
Clemence Benjamin
Clemence Benjamin

Inhalt:


Einführung

Die MetaEditor-Software enthält einen Compiler, der Fehler, die bei der Profilerstellung entdeckt werden, effektiv verwaltet. Mit Hilfe dieses Tools konnte ich herausfinden, warum die vorherige Version der Rechtecke von Risiko und Gewinn nicht wie vorgesehen anzeigte. Obwohl das Programm erfolgreich kompiliert wurde, lag das Problem nicht am Code selbst. Die Herausforderung bestand vielmehr darin, dass innerhalb des Bereichs der Rückblick-Kerzen nichts angezeigt wurde, was in erster Linie auf bestimmte technische Gegebenheiten zurückzuführen war.
  1. Der Wert für den Kerzenrückblick war mit 5.000 Takten standardmäßig zu hoch eingestellt.
  2. Die Verwendung mehrerer Puffer in einem einzigen Programm erhöht die Komplexität der Berechnungen, was die Anzeige des Indikatorfensters verlangsamen kann.

    Nachdem wir kurz erörtert haben, wie wir die aufgetretenen Probleme gelöst haben, wenden wir uns dem Hauptziel dieses Artikels zu: der Entwicklung eines Expert Advisors auf der Grundlage des verfeinerten Trend Constraint Indikators. Das folgende Bild zeigt, wie ein separates Skript das Problem, das wir ursprünglich mit dem Hauptindikator lösen wollten, erfolgreich gelöst hat.

    Risiko-Gewinn-Verhältnis für das Kreuzen gleitender Durchschnitte

    Automatisch gezeichnete Risiken und Gewinne mit Rechtecken


    Lösung für die früheren Herausforderungen beim Zeichnen der Risiken und Gewinne mit Rechtecken

    Bewältigung der Herausforderungen im Rahmen des Indikatorprogramms:

    1. Wir haben den Rückblickzeitraum von 5000 Balken auf 1000 Balken reduziert und damit die zu berechnende Datenmenge erheblich verringert.
    2. Wir wollten den Arbeitsaufwand für das Programm verringern, indem wir ein eigenständiges Skript als Teil des Werkzeugsatzes erstellten. Dieses Skript prüft speziell die Bedingungen, die von Puffer 6 und Puffer 7 im Indikator behandelt werden. Sobald diese Bedingungen erfüllt sind, zeichnet das Skript die erforderlichen Risiko-Gewinn-Rechtecke und platziert Linien mit Preisbeschriftungen für den Einstiegskurs, den Stop Loss und den Take Profit. Es ist jedoch wichtig zu beachten, dass das Skript eine einmalige Aufgabe ausführt und nicht ständig läuft. Der Nutzer muss das Skript manuell in das Chart einfügen, um die Handelsstufen zu visualisieren, die durch die gezeichneten Objekte und Kursmarkierungen dargestellt werden.

    Unten sehen Sie ein Bild, das den Start des Skriptprogramms zeigt:

    Trend Constraint R-R Skript-Start

    Trend Constraint R-R Skript: Zum Zeichnen des Risiko- und Gewinnrechtecks beim Kreuzen der gleitenden Durchschnitte

    Durch die Isolierung dieser Funktion haben wir sichergestellt, dass unser Indikator reibungslos funktioniert und der Computer oder das Handelsterminal nicht einfriert. Durch die Einbindung von Risiko-Ertrags-Rechtecken und die Markierung der Ausstiegsebenen können Händler die Handelsrichtung und die Ziele im Voraus visuell einschätzen, was den manuellen Handel ohne einen Expert Advisor ermöglicht. Das Skriptprogramm mit der erforderlichen Logik funktionierte einwandfrei, perfekt. Hier finden Sie unser vollständiges Skriptprogramm.

    //+------------------------------------------------------------------+
    //|                                        Trend Constraint R-R.mq5  |
    //|                                                  Script program  |
    //+------------------------------------------------------------------+
    #property strict
    #property script_show_inputs
    #property copyright "2024 Clemence Benjamin"
    #property version "1.00"
    #property link "https://www.mql5.com/en/users/billionaire2024/seller"
    #property description "A script program for drawing risk and rewars rectangles based on Moving Averaage crossover."
    
    
    
    //--- input parameters
    input int FastMAPeriod = 14;
    input int SlowMAPeriod = 50;
    input double RiskHeightPoints = 5000.0; // Default height of the risk rectangle in points
    input double RewardHeightPoints = 15000.0; // Default height of the reward rectangle in points
    input color RiskColor = clrIndianRed; // Default risk color
    input color RewardColor = clrSpringGreen; // Default reward color
    input int MaxBars = 500; // Maximum bars to process
    input int RectangleWidth = 10; // Width of the rectangle in bars
    input bool FillRectangles = true; // Option to fill rectangles
    input int FillTransparency = 128; // Transparency level (0-255), 128 is 50% transparency
    
    //+------------------------------------------------------------------+
    //| Script program start function                                    |
    //+------------------------------------------------------------------+
    void OnStart()
    {
      //--- delete existing rectangles and lines
      DeleteExistingObjects();
    
      //--- declare and initialize variables
      int i, limit;
      double FastMA[], SlowMA[];
      double closePrice, riskLevel, rewardLevel;
    
      //--- calculate moving averages
      if (iMA(NULL, 0, FastMAPeriod, 0, MODE_SMA, PRICE_CLOSE) < 0 || iMA(NULL, 0, SlowMAPeriod, 0, MODE_SMA, PRICE_CLOSE) < 0)
      {
        Print("Error in calculating moving averages.");
        return;
      }
    
      ArraySetAsSeries(FastMA, true);
      ArraySetAsSeries(SlowMA, true);
    
      CopyBuffer(iMA(NULL, 0, FastMAPeriod, 0, MODE_SMA, PRICE_CLOSE), 0, 0, MaxBars, FastMA);
      CopyBuffer(iMA(NULL, 0, SlowMAPeriod, 0, MODE_SMA, PRICE_CLOSE), 0, 0, MaxBars, SlowMA);
    
      limit = MathMin(ArraySize(FastMA), ArraySize(SlowMA));
    
      for (i = 1; i < limit - 1; i++)
      {
        //--- check for crossover
        if (FastMA[i] > SlowMA[i] && FastMA[i - 1] <= SlowMA[i - 1])
        {
          //--- long position entry point (bullish crossover)
          closePrice = iClose(NULL, 0, i);
          riskLevel = closePrice + RiskHeightPoints * Point();
          rewardLevel = closePrice - RewardHeightPoints * Point();
    
          //--- draw risk rectangle
          DrawRectangle("Risk_" + IntegerToString(i), i, closePrice, i - RectangleWidth, riskLevel, RiskColor);
    
          //--- draw reward rectangle
          DrawRectangle("Reward_" + IntegerToString(i), i, closePrice, i - RectangleWidth, rewardLevel, RewardColor);
    
          //--- draw entry, stop loss, and take profit lines
          DrawPriceLine("Entry_" + IntegerToString(i), i, closePrice, clrBlue, "Entry: " + DoubleToString(closePrice, _Digits));
          DrawPriceLine("StopLoss_" + IntegerToString(i), i, riskLevel, clrRed, "Stop Loss: " + DoubleToString(riskLevel, _Digits));
          DrawPriceLine("TakeProfit_" + IntegerToString(i), i, rewardLevel, clrGreen, "Take Profit: " + DoubleToString(rewardLevel, _Digits));
        }
        else if (FastMA[i] < SlowMA[i] && FastMA[i - 1] >= SlowMA[i - 1])
        {
          //--- short position entry point (bearish crossover)
          closePrice = iClose(NULL, 0, i);
          riskLevel = closePrice - RiskHeightPoints * Point();
          rewardLevel = closePrice + RewardHeightPoints * Point();
    
          //--- draw risk rectangle
          DrawRectangle("Risk_" + IntegerToString(i), i, closePrice, i - RectangleWidth, riskLevel, RiskColor);
    
          //--- draw reward rectangle
          DrawRectangle("Reward_" + IntegerToString(i), i, closePrice, i - RectangleWidth, rewardLevel, RewardColor);
    
          //--- draw entry, stop loss, and take profit lines
          DrawPriceLine("Entry_" + IntegerToString(i), i, closePrice, clrBlue, "Entry: " + DoubleToString(closePrice, _Digits));
          DrawPriceLine("StopLoss_" + IntegerToString(i), i, riskLevel, clrRed, "Stop Loss: " + DoubleToString(riskLevel, _Digits));
          DrawPriceLine("TakeProfit_" + IntegerToString(i), i, rewardLevel, clrGreen, "Take Profit: " + DoubleToString(rewardLevel, _Digits));
        }
      }
    }
    
    //+------------------------------------------------------------------+
    //| Function to delete existing rectangles and lines                 |
    //+------------------------------------------------------------------+
    void DeleteExistingObjects()
    {
      int totalObjects = ObjectsTotal(0, 0, -1);
      for (int i = totalObjects - 1; i >= 0; i--)
      {
        string name = ObjectName(0, i, 0, -1);
        if (StringFind(name, "Risk_") >= 0 || StringFind(name, "Reward_") >= 0 ||
            StringFind(name, "Entry_") >= 0 || StringFind(name, "StopLoss_") >= 0 ||
            StringFind(name, "TakeProfit_") >= 0)
        {
          ObjectDelete(0, name);
        }
      }
    }
    
    //+------------------------------------------------------------------+
    //| Function to draw rectangles                                      |
    //+------------------------------------------------------------------+
    void DrawRectangle(string name, int startBar, double startPrice, int endBar, double endPrice, color rectColor)
    {
      if (ObjectFind(0, name) >= 0)
        ObjectDelete(0, name);
    
      datetime startTime = iTime(NULL, 0, startBar);
      datetime endTime = (endBar < 0) ? (TimeCurrent() + (PeriodSeconds() * (-endBar))) : iTime(NULL, 0, endBar);
    
      if (!ObjectCreate(0, name, OBJ_RECTANGLE, 0, startTime, startPrice, endTime, endPrice))
        Print("Failed to create rectangle: ", name);
    
      // Set the color with transparency (alpha value)
      int alphaValue = FillTransparency; // Adjust transparency level (0-255)
      color fillColor = rectColor & 0x00FFFFFF | (alphaValue << 24); // Combine alpha with RGB
    
      ObjectSetInteger(0, name, OBJPROP_COLOR, rectColor);
      ObjectSetInteger(0, name, OBJPROP_STYLE, STYLE_SOLID);
      ObjectSetInteger(0, name, OBJPROP_WIDTH, 1);
      ObjectSetInteger(0, name, OBJPROP_BACK, true); // Set to background
    
      if (FillRectangles)
      {
        ObjectSetInteger(0, name, OBJPROP_COLOR, fillColor); // Fill color with transparency
      }
      else
      {
        ObjectSetInteger(0, name, OBJPROP_COLOR, rectColor & 0x00FFFFFF); // No fill color
      }
    }
    
    //+------------------------------------------------------------------+
    //| Function to draw price lines                                     |
    //+------------------------------------------------------------------+
    void DrawPriceLine(string name, int barIndex, double price, color lineColor, string labelText)
    {
      datetime time = iTime(NULL, 0, barIndex);
      datetime endTime = (barIndex - 2 * RectangleWidth < 0) ? (TimeCurrent() + (PeriodSeconds() * (-barIndex - 2 * RectangleWidth))) : iTime(NULL, 0, barIndex - 2 * RectangleWidth); // Extend line to the right
    
      if (!ObjectCreate(0, name, OBJ_TREND, 0, time, price, endTime, price))
        Print("Failed to create price line: ", name);
    
      ObjectSetInteger(0, name, OBJPROP_COLOR, lineColor);
      ObjectSetInteger(0, name, OBJPROP_STYLE, STYLE_SOLID);
      ObjectSetInteger(0, name, OBJPROP_WIDTH, 1);
      ObjectSetInteger(0, name, OBJPROP_BACK, true); // Set to background
    
      // Create text label
      string labelName = name + "_Label";
      if (ObjectFind(0, labelName) >= 0)
        ObjectDelete(0, labelName);
    
      if (!ObjectCreate(0, labelName, OBJ_TEXT, 0, endTime, price))
        Print("Failed to create label: ", labelName);
    
      ObjectSetInteger(0, labelName, OBJPROP_COLOR, lineColor);
      ObjectSetInteger(0, labelName, OBJPROP_ANCHOR, ANCHOR_LEFT);
      ObjectSetString(0, labelName, OBJPROP_TEXT, labelText);
      ObjectSetInteger(0, labelName, OBJPROP_FONTSIZE, 10);
      ObjectSetInteger(0, labelName, OBJPROP_CORNER, CORNER_RIGHT_UPPER);
      ObjectSetInteger(0, labelName, OBJPROP_XOFFSET, 5);
      ObjectSetInteger(0, labelName, OBJPROP_YOFFSET, 0);
    }

    Bevor wir mit der Entwicklung des Expert Advisors fortfahren, wollen wir die Leistung des Skripts gründlich besprechen:

    • Wir haben die Möglichkeit geschaffen, die wichtigsten Aspekte der Handelsstrategie durch die Definition von Eingabeparametern anzupassen. Wir haben Optionen bereitgestellt, um die Perioden der schnellen und langsamen Durchschnitte anzupassen, die Abmessungen und Farben der Risiko- und Gewinnrechtecke festzulegen, die maximale Anzahl der zu verarbeitenden Balken zu bestimmen und zu wählen, ob die Rechtecke mit Farbe gefüllt werden sollen. Auf diese Weise kann das Skript je nach den Präferenzen des Nutzers auf verschiedene Handelsstrategien zugeschnitten werden, die den Abschnitt der Eingabeparameter unseres Programms bilden:

    //---- Input Parameters
    input int FastMAPeriod = 14;
    input int SlowMAPeriod = 50;
    input double RiskHeightPoints = 5000.0;
    input double RewardHeightPoints = 15000.0;
    input color RiskColor = clrIndianRed;
    input color RewardColor = clrSpringGreen;
    input int MaxBars = 500;
    input int RectangleWidth = 10;
    input bool FillRectangles = true;
    input int FillTransparency = 128;

    • In der Funktion (OnStart) haben wir dafür gesorgt, dass das Chart sauber bleibt, indem wir das Skript so programmiert haben, dass es zunächst alle vorhandenen Risiko/Ertrags-Rechtecke und Kurslinien löscht. Anschließend berechneten wir mit der Funktion (iMA) die schnellen und langsamen Mittelwerte und speicherten diese Werte zur weiteren Verarbeitung in Arrays. Während das Skript die Balken im Chart durchläuft, haben wir die Bedingungen für die Erkennung eines Aufwärtskreuzens festgelegt, bei denen der sich schnell bewegende Durchschnitt den langsamen Durchschnitt übersteigt. Wenn diese Bedingungen erfüllt sind, berechnet das Skript den Einstiegskurs, das Risikoniveau (Stop Loss) und das Gewinnniveau (Take Profit). Es zeichnet dann Rechtecke und Preislinien auf dem Chart, die diese kritischen Handelsniveaus effektiv markieren, wie wir weiter unten in den Untercode-Schnipseln erklären werden:

    //----Onstart Function
    void OnStart()
    {
      //--- delete existing rectangles and lines
      DeleteExistingObjects();
    
      //--- declare and initialize variables
      int i, limit;
      double FastMA[], SlowMA[];
      double closePrice, riskLevel, rewardLevel;
    
      //--- calculate moving averages
      if (iMA(NULL, 0, FastMAPeriod, 0, MODE_SMA, PRICE_CLOSE) < 0 || iMA(NULL, 0, SlowMAPeriod, 0, MODE_SMA, PRICE_CLOSE) < 0)
      {
        Print("Error in calculating moving averages.");
        return;
      }
    

    • Um die Übersichtlichkeit des Charts zu wahren, haben wir die Funktion „Delete Existing Objects“ (Vorhandene Objekte löschen) entwickelt, mit der das Skript alle zuvor gezeichneten Objekte im Zusammenhang mit Handelssignalen entfernt. Durch die Überprüfung der Namen aller Objekte im Chart stellte das Skript sicher, dass nur die aktuellsten und relevantesten Informationen angezeigt wurden, sodass das Chart konzentriert und übersichtlich blieb:

    //---- DeleteAllExistingObjects Function
    void DeleteExistingObjects()
    {
      int totalObjects = ObjectsTotal(0, 0, -1);
      for (int i = totalObjects - 1; i >= 0; i--)
      {
        string name = ObjectName(0, i, 0, -1);
        if (StringFind(name, "Risk_") >= 0 || StringFind(name, "Reward_") >= 0 ||
            StringFind(name, "Entry_") >= 0 || StringFind(name, "StopLoss_") >= 0 ||
            StringFind(name, "TakeProfit_") >= 0)
        {
          ObjectDelete(0, name);
        }
      }
    }

    • In der Funktion „Draw Rectangle“ (Rechteck zeichnen) haben wir sichergestellt, dass das Skript die Risiko- und Gewinnstufen visuell darstellen kann, indem wir zunächst alle vorhandenen Rechtecke mit demselben Namen entfernt haben, um Doppelungen zu vermeiden. Dann ließen wir das Skript die Start- und Endzeiten für die Rechtecke auf der Grundlage der Balkenindizes berechnen und legten sorgfältig die Farben und Transparenzstufen fest. Dadurch konnten die Rechtecke auf der Karte hervorgehoben werden, ohne andere wichtige Details zu verdecken:
    ///---Draw rectangle function
    void DrawRectangle(string name, int startBar, double startPrice, int endBar, double endPrice, color rectColor)
    {
      if (ObjectFind(0, name) >= 0)
        ObjectDelete(0, name);
    
      datetime startTime = iTime(NULL, 0, startBar);
      datetime endTime = (endBar < 0) ? (TimeCurrent() + (PeriodSeconds() * (-endBar))) : iTime(NULL, 0, endBar);
    
      if (!ObjectCreate(0, name, OBJ_RECTANGLE, 0, startTime, startPrice, endTime, endPrice))
        Print("Failed to create rectangle: ", name);
    
      int alphaValue = FillTransparency;
      color fillColor = rectColor & 0x00FFFFFF | (alphaValue << 24);
    
      ObjectSetInteger(0, name, OBJPROP_COLOR, rectColor);
      ObjectSetInteger(0, name, OBJPROP_STYLE, STYLE_SOLID);
      ObjectSetInteger(0, name, OBJPROP_WIDTH, 1);
      ObjectSetInteger(0, name, OBJPROP_BACK, true);
    
      if (FillRectangles)
      {
        ObjectSetInteger(0, name, OBJPROP_COLOR, fillColor);
      }
      else
      {
        ObjectSetInteger(0, name, OBJPROP_COLOR, rectColor & 0x00FFFFFF);
      }
    }

      • Schließlich haben wir die Funktion „Draw Price Line“ (Preislinie zeichnen) implementiert, um das Skript anzuweisen, horizontale Linien auf dem Einstiegs-, Stop Loss- und Take Profit-Niveau einzufügen. Das Skript verlängerte diese Linien im Chart und fügte Textbeschriftungen hinzu, die die entsprechenden Kursniveaus anzeigten. Auf diese Weise wurde eine visuelle Referenz geschaffen, die es den Nutzern ermöglichte, auf der Grundlage der von den gleitenden Durchschnitten generierten Signale schnell Schlüsselpunkte für den Handel zu identifizieren und zu verwalten:

      ///---- Draw Price Lines Function
      void DrawPriceLine(string name, int barIndex, double price, color lineColor, string labelText)
      {
        datetime time = iTime(NULL, 0, barIndex);
        datetime endTime = (barIndex - 2 * RectangleWidth < 0) ? (TimeCurrent() + (PeriodSeconds() * (-barIndex - 2 * RectangleWidth))) : iTime(NULL, 0, barIndex - 2 * RectangleWidth);
      
        if (!ObjectCreate(0, name, OBJ_TREND, 0, time, price, endTime, price))
          Print("Failed to create price line: ", name);
      
        ObjectSetInteger(0, name, OBJPROP_COLOR, lineColor);
        ObjectSetInteger(0, name, OBJPROP_STYLE, STYLE_SOLID);
        ObjectSetInteger(0, name, OBJPROP_WIDTH, 1);
        ObjectSetInteger(0, name, OBJPROP_BACK, true);
      
        string labelName = name + "_Label";
        if (ObjectFind(0, labelName) >= 0)
          ObjectDelete(0, labelName);
      
        if (!ObjectCreate(0, labelName, OBJ_TEXT, 0, endTime, price))
          Print("Failed to create label: ", labelName);
      
        ObjectSetInteger(0, labelName, OBJPROP_COLOR, lineColor);
        ObjectSetInteger(0, labelName, OBJPROP_FONTSIZE, 10);
        ObjectSetString(0, labelName, OBJPROP_TEXT, labelText);
        ObjectSetInteger(0, labelName, OBJPROP_BACK, true);
      }
      

      Als Teil des Trading-Kits kann dieses Skript nun regelmäßig gestartet werden, um die visualisierten Handelsstufen der Vergangenheit und der aktuellen Zeit zu sehen. Wir gehen nun dazu über, unseren einzigartigen Expert Advisor zu erstellen. Ich werde mich darauf konzentrieren, die gesamte Entwicklung bis zum endgültigen EA zu erläutern. In diesem Artikel werden wir uns nur darauf konzentrieren, dass es mit dem Indikator funktioniert, den wir zuvor erstellt haben, Trend Constraint V1.09.


      Erstellen eines Expert Advisors, der auf der Grundlage eines Indikators arbeitet:

      Um einen Expert Advisor (EA) in MQL5 mit einem nutzerdefinierten Indikator zu erstellen, müssen wir sicherstellen, dass der nutzerdefinierte Indikator, die Datei (.ex5) im Ordner „Indicators“ innerhalb der MetaTrader 5-Plattform verfügbar ist, in diesem Fall ist es Trend Constraint V1.09. Mit dem MetaEditor können wir unseren EA schreiben und dabei MQL5-Funktionen für den Zugriff auf die Pufferwerte des Indikators einbauen. Wir verwenden die Funktion (iCustom()), um den nutzerdefinierten Indikator innerhalb des EA aufzurufen, wobei wir die erforderlichen Parameter wie Symbol und Zeitrahmen angeben.

      Um Daten aus den Indikatorpuffern zu extrahieren, verwenden wir die Funktion „CopyBuffer()“, die die Pufferwerte der zu analysierenden Handelssignale abruft. Anschließend implementieren wir unsere Handelslogik auf der Grundlage dieser Pufferwerte und definieren Bedingungen, um Aufträge entsprechend unserer Strategie zu öffnen, zu schließen oder zu ändern. Wir integrieren Risikomanagementfunktionen wie Stop Loss und Take Profit für ein umsichtiges Handelsmanagement. Beim Backtesting des EA mit dem MetaTrader 5 Strategy Tester, um seine Leistung zu bewerten und seine Parameter fein abzustimmen. Schließlich werden wir die Funktionalität des EAs in einer Demokonto-Umgebung überprüfen, bevor wir zum Live-Handel übergehen, um sicherzustellen, dass er unter realen Marktbedingungen effektiv funktioniert.

      Wir können damit beginnen, eine Expert Advisor-Vorlage in MetaEditor zu starten und sie dann anhand des in diesem Bild gezeigten Beispiels zu entwickeln oder zu ändern:

      Starten einer EA-Vorlage in MetaEditor

      Starten einer Expert Advisor-Vorlage in MetaEditor

      Um Sie durch den Aufbau unseres Expert Advisors (EA) zu führen, unterteilen wir den Prozess in sechs Phasen. Im weiteren Verlauf empfehle ich, die Code-Abschnitte direkt in MetaEditor einzugeben. Diese praktische Herangehensweise wird Ihnen helfen, die Schritte besser zu verstehen und zu verinnerlichen, insbesondere wenn Sie neu in der EA-Entwicklung sind.

      1. Kopfzeile und Metadaten

      Im Kopfbereich definieren wir den Namen und den Zweck unseres Expert Advisors (EA). Durch die Angabe des Copyrights, einen Link zu unserem Profil und die Angabe der Version stellen wir sicher, dass unser EA leicht identifizierbar und nachvollziehbar ist. Diese Metadaten helfen uns und anderen, die Herkunft und den Zweck des EA zu verstehen, insbesondere wenn er weitergegeben oder geändert wird:

      //You can replace the author details with yours.
      //+------------------------------------------------------------------+
      //|                                    Trend Constraint Expert.mq5   |
      //|                                Copyright 2024, Clemence Benjamin |
      //|                                             https://www.mql5.com |
      //+------------------------------------------------------------------+
      #property strict
      #property copyright "Copyright 2024, Clemence Benjamin"
      #property link      "https://www.mql5.com/en/users/billionaire2024/seller"
      #property version   "1.0"
      #property description "An Expert based on the buffer6 and buffer7 of Trend Constraint V1.09"

      2. Eingabe-Parameter

      Hier definieren wir die wichtigsten Eingabeparameter, mit denen wir das Verhalten des EA anpassen können, ohne den Code selbst zu verändern. Durch die Einstellung von Parametern wie (Lots, Slippage, Stop Loss, Take Profit und Magic Number) machen wir den EA flexibel und anpassungsfähig an verschiedene Handelsstrategien. Die „Magic Number“ (magische Zahl) ist besonders wichtig, da sie es uns ermöglicht, die von diesem EA getätigten Trades eindeutig zu identifizieren, was entscheidend ist, wenn mehrere EAs oder manuelle Trades beteiligt sind:

      ///----------- EA inputs parameters for customizations
      input double Lots = 0.1;          // Lot size
      input int Slippage = 3;           // Slippage
      input double StopLoss = 50;       // Stop Loss in points
      input double TakeProfit = 100;    // Take Profit in points
      input int MagicNumber = 123456;   // Magic number for orders

      3. Die Initialisierungsfunktion (OnInit)

      In der Funktion OnInit werden die Voraussetzungen für den Betrieb des EA geschaffen, indem die erforderlichen Komponenten initialisiert werden. Wir beginnen mit dem Versuch, ein Handle für unseren nutzerdefinierten Indikator „Trend Constraint V1.09“ zu erhalten. Mit diesem Handle können wir programmatisch mit dem Indikator interagieren. Wenn der Handle erfolgreich erhalten wurde, werden die Puffer-Arrays (Buffer6 und Buffer7) in Reihe geschaltet, damit die Indikatorwerte gespeichert und bearbeitet werden können. Wenn das Handle jedoch nicht abgerufen werden kann, stellen wir sicher, dass der EA einen Initialisierungsfehler mit einer Fehlermeldung zurückgibt, die uns hilft, das Problem zu diagnostizieren:

      ////-------Initialization Function
      int OnInit()
        {
         //--- Get the indicator handle
         indicator_handle = iCustom(Symbol(), PERIOD_CURRENT, "Trend Constraint V1.09");
         if (indicator_handle < 0)
           {
            Print("Failed to get the indicator handle. Error: ", GetLastError());
            return(INIT_FAILED);
           }
      
         //--- Set the buffer arrays as series
         ArraySetAsSeries(Buffer6, true);
         ArraySetAsSeries(Buffer7, true);
      
         return(INIT_SUCCEEDED);
        }
      

      4. Die Deinitialisierungsfunktion (OnDeinit)

      Wenn unser EA aus dem Chart entfernt oder die Plattform geschlossen wird, wird die Funktion (OnDeinit) ausgeführt. Hier achten wir darauf, das Indikator-Handle freizugeben, um sicherzustellen, dass wir alle Ressourcen freigeben, die während der EA-Operation zugewiesen wurden. Dieser Bereinigungsschritt ist entscheidend für die Aufrechterhaltung der Effizienz und Stabilität unserer Handelsumgebung, da er einen unnötigen Ressourcenverbrauch verhindert:

      ///------Deinitialization Function(OnDeinit)
      void OnDeinit(const int reason)
        {
         //--- Release the indicator handle
         IndicatorRelease(indicator_handle);
        }
      

      5. Die Hauptausführungsfunktion (OnTick)

      Die Funktion (OnTick) ist der Ort, an dem die eigentliche Handelsaktion stattfindet. Jedes Mal, wenn ein neuer Markttick empfangen wird, wird diese Funktion aufgerufen. Zunächst prüfen wir, ob es bereits eine offene Position mit derselben magischen Zahl gibt, um sicherzustellen, dass wir keine doppelten Abschlüsse tätigen. Als Nächstes kopieren wir die neuesten Werte aus den Indikatorpuffern (Buffer6 und Buffer7), um Handelsentscheidungen zu treffen. Wenn unsere Bedingungen für ein Kauf- oder Verkaufssignal erfüllt sind, erstellen und versenden wir den entsprechenden Handelsauftrag. Wir achten darauf, alle notwendigen Parameter wie Auftragsart, Preis, Stop Loss, Take Profit und Slippage festzulegen, um unsere Handelsstrategie effektiv umzusetzen:

      ///---Main Execution Function(OnTick)
      void OnTick()
        {
         //--- Check if there is already an open position with the same MagicNumber
         if (PositionSelect(Symbol()))
           {
            if (PositionGetInteger(POSITION_MAGIC) == MagicNumber)
              {
               return; // Exit OnTick if there's an open position with the same MagicNumber
              }
           }
      
         //--- Calculate the indicator
         if (CopyBuffer(indicator_handle, 5, 0, 2, Buffer6) <= 0 || CopyBuffer(indicator_handle, 6, 0, 2, Buffer7) <= 0)
           {
            Print("Failed to copy buffer values. Error: ", GetLastError());
            return;
           }
      
         //--- Check for a buy signal
         if (Buffer7[0] != EMPTY_VALUE)
           {
            double ask = SymbolInfoDouble(Symbol(), SYMBOL_ASK);
            double sl = NormalizeDouble(ask - StopLoss * _Point, _Digits);
            double tp = NormalizeDouble(ask + TakeProfit * _Point, _Digits);
      
            //--- Prepare the buy order request
            MqlTradeRequest request;
            MqlTradeResult result;
            ZeroMemory(request);
            ZeroMemory(result);
      
            request.action = TRADE_ACTION_DEAL;
            request.symbol = Symbol();
            request.volume = Lots;
            request.type = ORDER_TYPE_BUY;
            request.price = ask;
            request.sl = sl;
            request.tp = tp;
            request.deviation = Slippage;
            request.magic = MagicNumber;
            request.comment = "Buy Order";
      
            //--- Send the buy order
            if (!OrderSend(request, result))
              {
               Print("Error opening buy order: ", result.retcode);
              }
            else
              {
               Print("Buy order opened successfully! Ticket: ", result.order);
              }
           }
      
         //--- Check for a sell signal
         if (Buffer6[0] != EMPTY_VALUE)
           {
            double bid = SymbolInfoDouble(Symbol(), SYMBOL_BID);
            double sl = NormalizeDouble(bid + StopLoss * _Point, _Digits);
            double tp = NormalizeDouble(bid - TakeProfit * _Point, _Digits);
      
            //--- Prepare the sell order request
            MqlTradeRequest request;
            MqlTradeResult result;
            ZeroMemory(request);
            ZeroMemory(result);
      
            request.action = TRADE_ACTION_DEAL;
            request.symbol = Symbol();
            request.volume = Lots;
            request.type = ORDER_TYPE_SELL;
            request.price = bid;
            request.sl = sl;
            request.tp = tp;
            request.deviation = Slippage;
            request.magic = MagicNumber;
            request.comment = "Sell Order";
      
            //--- Send the sell order
            if (!OrderSend(request, result))
              {
               Print("Error opening sell order: ", result.retcode);
              }
            else
              {
               Print("Sell order opened successfully! Ticket: ", result.order);
              }
           }
        }
      

      6. Andere Funktionen

      Wir haben auch mehrere andere Funktionen, um verschiedene Ereignisse zu behandeln, die unsere EA begegnen könnte, und sie kamen als Teil einer EA-Vorlage und derzeit verwenden wir sie nicht der Einfachheit halber:

      • (OnTrade): Hier können wir alle spezifischen Aktionen durchführen, die beim Eintreten eines Handelsereignisses stattfinden müssen. Diese Funktion ist derzeit noch leer, bietet aber Platz, um die Handelslogik hinzuzufügen.
      • (OnTester): Diese Funktion wird für nutzerdefinierte Berechnungen beim Backtesting verwendet. Indem wir einen Wert zurückgeben, können wir unsere Strategie auf der Grundlage spezifischer Metriken optimieren.
      • (OnTesterInit, OnTesterPass, OnTesterDeinit): Diese Funktionen sind in den Optimierungsprozess innerhalb des Strategieprüfers eingebunden. Sie ermöglichen die Initialisierung von Einstellungen, die Durchführung von Aktionen nach jedem Optimierungsdurchlauf und das anschließende Aufräumen von Ressourcen.
      • (OnChartEvent): Mit dieser Funktion können wir verschiedene Chartereignisse wie Mausklicks oder Tastendrucke verarbeiten. Obwohl sie derzeit leer ist, können wir diese Funktion nutzen, um unserem EA interaktive Funktionen hinzuzufügen:
      ///----Other Template functions available
      void OnTrade()
        {
         //--- Handle trade events if necessary
        }
      
      double OnTester()
        {
         double ret = 0.0;
         //--- Custom calculations for strategy tester
         return (ret);
        }
      
      void OnTesterInit()
        {
         //--- Initialization for the strategy tester
        }
      
      void OnTesterPass()
        {
         //--- Code executed after each pass in optimization
        }
      
      void OnTesterDeinit()
        {
         //--- Cleanup after tester runs
        }
      
      void OnChartEvent(const int id,
                        const long &lparam,
                        const double &dparam,
                        const string &sparam)
        {
         //--- Handle chart events here
        }
      

      Unser endgültiges Programm sieht folgendermaßen aus:

      //+------------------------------------------------------------------+
      //|                                    Trend Constraint Expert.mq5   |
      //|                                Copyright 2024, Clemence Benjamin |
      //|                                             https://www.mql5.com |
      //+------------------------------------------------------------------+
      #property strict
      #property copyright "Copyright 2024, Clemence Benjamin"
      #property link      "https://www.mql5.com/en/users/billionaire2024/seller"
      #property version   "1.0"
      #property description "An Expert based on the buffer6 and buffer7 of Trend Constraint V1.09"
      
      //--- Input parameters for the EA
      input double Lots = 0.1;          // Lot size
      input int Slippage = 3;           // Slippage
      input double StopLoss = 50;       // Stop Loss in points
      input double TakeProfit = 100;    // Take Profit in points
      input int MagicNumber = 123456;   // Magic number for orders
      
      //--- Indicator handle
      int indicator_handle;
      double Buffer6[];
      double Buffer7[];
      
      //+------------------------------------------------------------------+
      //| Expert initialization function                                   |
      //+------------------------------------------------------------------+
      int OnInit()
        {
         //--- Get the indicator handle
         indicator_handle = iCustom(Symbol(), PERIOD_CURRENT, "Trend Constraint V1.09");
         if (indicator_handle < 0)
           {
            Print("Failed to get the indicator handle. Error: ", GetLastError());
            return(INIT_FAILED);
           }
      
         //--- Set the buffer arrays as series
         ArraySetAsSeries(Buffer6, true);
         ArraySetAsSeries(Buffer7, true);
      
         return(INIT_SUCCEEDED);
        }
      
      //+------------------------------------------------------------------+
      //| Expert deinitialization function                                 |
      //+------------------------------------------------------------------+
      void OnDeinit(const int reason)
        {
         //--- Release the indicator handle
         IndicatorRelease(indicator_handle);
        }
      
      //+------------------------------------------------------------------+
      //| Expert tick function                                             |
      //+------------------------------------------------------------------+
      void OnTick()
        {
         //--- Check if there is already an open position with the same MagicNumber
         if (PositionSelect(Symbol()))
           {
            if (PositionGetInteger(POSITION_MAGIC) == MagicNumber)
              {
               return; // Exit OnTick if there's an open position with the same MagicNumber
              }
           }
      
         //--- Calculate the indicator
         if (CopyBuffer(indicator_handle, 5, 0, 2, Buffer6) <= 0 || CopyBuffer(indicator_handle, 6, 0, 2, Buffer7) <= 0)
           {
            Print("Failed to copy buffer values. Error: ", GetLastError());
            return;
           }
      
         //--- Check for a buy signal
         if (Buffer7[0] != EMPTY_VALUE)
           {
            double ask = SymbolInfoDouble(Symbol(), SYMBOL_ASK);
            double sl = NormalizeDouble(ask - StopLoss * _Point, _Digits);
            double tp = NormalizeDouble(ask + TakeProfit * _Point, _Digits);
      
            //--- Prepare the buy order request
            MqlTradeRequest request;
            MqlTradeResult result;
            ZeroMemory(request);
            ZeroMemory(result);
      
            request.action = TRADE_ACTION_DEAL;
            request.symbol = Symbol();
            request.volume = Lots;
            request.type = ORDER_TYPE_BUY;
            request.price = ask;
            request.sl = sl;
            request.tp = tp;
            request.deviation = Slippage;
            request.magic = MagicNumber;
            request.comment = "Buy Order";
      
            //--- Send the buy order
            if (!OrderSend(request, result))
              {
               Print("Error opening buy order: ", result.retcode);
              }
            else
              {
               Print("Buy order opened successfully! Ticket: ", result.order);
              }
           }
      
         //--- Check for a sell signal
         if (Buffer6[0] != EMPTY_VALUE)
           {
            double bid = SymbolInfoDouble(Symbol(), SYMBOL_BID);
            double sl = NormalizeDouble(bid + StopLoss * _Point, _Digits);
            double tp = NormalizeDouble(bid - TakeProfit * _Point, _Digits);
      
            //--- Prepare the sell order request
            MqlTradeRequest request;
            MqlTradeResult result;
            ZeroMemory(request);
            ZeroMemory(result);
      
            request.action = TRADE_ACTION_DEAL;
            request.symbol = Symbol();
            request.volume = Lots;
            request.type = ORDER_TYPE_SELL;
            request.price = bid;
            request.sl = sl;
            request.tp = tp;
            request.deviation = Slippage;
            request.magic = MagicNumber;
            request.comment = "Sell Order";
      
            //--- Send the sell order
            if (!OrderSend(request, result))
              {
               Print("Error opening sell order: ", result.retcode);
              }
            else
              {
               Print("Sell order opened successfully! Ticket: ", result.order);
              }
           }
        }
      
      //+------------------------------------------------------------------+
      //| Trade function                                                   |
      //+------------------------------------------------------------------+
      void OnTrade()
        {
         //--- Handle trade events if necessary
        }
      
      //+------------------------------------------------------------------+
      //| Tester function                                                  |
      //+------------------------------------------------------------------+
      double OnTester()
        {
         double ret = 0.0;
         //--- Custom calculations for strategy tester
         return (ret);
        }
      
      //+------------------------------------------------------------------+
      //| TesterInit function                                              |
      //+------------------------------------------------------------------+
      void OnTesterInit()
        {
         //--- Initialization for the strategy tester
        }
      
      //+------------------------------------------------------------------+
      //| TesterPass function                                              |
      //+------------------------------------------------------------------+
      void OnTesterPass()
        {
         //--- Code executed after each pass in optimization
        }
      
      //+------------------------------------------------------------------+
      //| TesterDeinit function                                            |
      //+------------------------------------------------------------------+
      void OnTesterDeinit()
        {
         //--- Cleanup after tester runs
        }
      
      //+------------------------------------------------------------------+
      //| ChartEvent function                                              |
      //+------------------------------------------------------------------+
      void OnChartEvent(const int id,
                        const long &lparam,
                        const double &dparam,
                        const string &sparam)
        {
         //--- Handle chart events here
        }
      //+------------------------------------------------------------------+
      

      Im nächsten Abschnitt werden wir nach erfolgreicher Kompilierung unser Programm testen.


      Tests:

      In MetaEditor können wir die Schaltfläche „Kompilieren“ oder „Ausführen“ verwenden, um unser Programm für den Test vorzubereiten. In diesem Fall war die Kompilierung erfolgreich, und wir starteten den Test auf dem Boom 500 Index mit dem Strategy Tester.

      Start des Strategietesters

      Starten des Testers über den Navigator

      Es öffnet sich ein Panel des Strategie-Testers, in dem Sie einige Einstellungen vornehmen können, bevor Sie auf die Schaltfläche „Start“ in der unteren rechten Ecke klicken. Zum Beispiel ist die Standard-Lotgröße im EA auf 0,1 Lots eingestellt, aber für den Boom 500 Index musste ich sie in diesem Fall auf ein Minimum von 0,2 Lots erhöhen.

      Strategie Tester

      Panel des Strategie Testers

      Erstaunlicherweise schnitt unser System im Strategietester gut ab, wie die folgende Abbildung zeigt:

      MetaTester Visualisierung

      Strategie-Tester Visualisierung: Der Experte Trend-Constraint


      Schlussfolgerung

      Die zusätzlichen Risiko- und Ertragsrechtecke bieten Händlern eine klare, grafische Darstellung ihrer Geschäfte, was die Überwachung und Verwaltung offener Positionen erleichtert. Dieses visuelle Hilfsmittel ist besonders nützlich auf schnelllebigen Märkten, wo schnelle Entscheidungen erforderlich sind. Die Rechtecke dienen als ständige Erinnerung an die möglichen Ergebnisse des Handels und helfen den Händlern, sich an ihren ursprünglichen Handelsplan zu halten.

      Die erfolgreiche Zusammenarbeit zwischen dem Indikator „Trend Constraint V1.09“ und dem Expert Advisor zeigt, wie wichtig die Synergie zwischen den Instrumenten in einer Handelsstrategie ist. Der Indikator identifiziert potenzielle Trends und Umkehrungen, während der EA auf der Grundlage dieser Informationen Trades ausführt und das Risiko verwaltet. Dieser integrierte Ansatz führt zu einer kohärenteren und effektiveren Handelsstrategie.

      Im Anhang finden Sie den verwendeten Indikator, das Skript und den EA. Es gibt noch Raum für Verbesserungen und Änderungen. Ich hoffe, Sie haben diese Informationen als nützlich empfunden. Sie sind herzlich eingeladen, Ihre Gedanken im Kommentarbereich mitzuteilen. Viel Spaß beim Handeln!

      Angehängte Datei Beschreibung
      (Trend_Constraint V1.09.mq5) Quellcode für den Indikator zur Verwendung mit EA.
      (Trend Constraint R-R.mq5) Quellcode für das Risk-Reward Rectangle Script.
      (Trend Constraint Expert.mq5) Quellcode für den Expert Advisor, der ausschließlich mit (Trend Constraint V1.09) arbeitet

      Zurück zum Inhalt

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

      MQL5-Integration: Python MQL5-Integration: Python
      Python ist eine bekannte und beliebte Programmiersprache mit vielen Funktionen, insbesondere in den Bereichen Finanzen, Datenwissenschaft, künstliche Intelligenz und maschinelles Lernen. Python ist ein leistungsfähiges Werkzeug, das auch beim Handel nützlich sein kann. MQL5 ermöglicht es uns, diese leistungsstarke Sprache als Integration zu nutzen, um unsere Ziele effektiv zu erreichen. In diesem Artikel erfahren Sie, wie Sie Python in MQL5 integrieren und verwenden können, nachdem Sie einige grundlegende Informationen über Python gelernt haben.
      Сode Lock Algorithmus (CLA) Сode Lock Algorithmus (CLA)
      In diesem Artikel werden wir Zahlenschlösser (Code Locks) neu überdenken und sie von Sicherheitsmechanismen in Werkzeuge zur Lösung komplexer Optimierungsprobleme verwandeln. Entdecken Sie die Welt der Zahlenschlösser, die nicht als einfache Sicherheitsvorrichtungen betrachtet werden, sondern als Inspiration für einen neuen Ansatz zur Optimierung. Wir werden eine ganze Population von Zahlenschlössern (Locks) erstellen, wobei jedes Schloss eine einzigartige Lösung für das Problem darstellt. Wir werden dann einen Algorithmus entwickeln, der diese Schlösser „knackt“ und optimale Lösungen in einer Vielzahl von Bereichen findet, vom maschinellen Lernen bis zur Entwicklung von Handelssystemen.
      Integration von MQL5 in Datenverarbeitungspakete (Teil 2): Maschinelles Lernen und prädiktive Analytik Integration von MQL5 in Datenverarbeitungspakete (Teil 2): Maschinelles Lernen und prädiktive Analytik
      In unserer Serie über die Integration von MQL5 mit Datenverarbeitungspaketen befassen wir uns mit der leistungsstarken Kombination aus maschinellem Lernen und prädiktiver Analyse. Wir werden untersuchen, wie MQL5 nahtlos mit gängigen Bibliotheken für maschinelles Lernen verbunden werden kann, um anspruchsvolle Vorhersagemodelle für Finanzmärkte zu ermöglichen.
      Wie man jede Art von Trailing-Stop entwickelt und mit einem EA verbindet Wie man jede Art von Trailing-Stop entwickelt und mit einem EA verbindet
      In diesem Artikel werden wir uns Klassen für die bequeme Erstellung verschiedener Trailing-Stops ansehen und lernen, wie man sie mit einem beliebigen EA verbindet.