English Русский 中文 日本語
preview
Wie man ein interaktives MQL5 Dashboard/Panel mit Hilfe der Controls-Klasse erstellt (Teil 2): Reaktionsfähigkeit von Schaltflächen hinzufügen

Wie man ein interaktives MQL5 Dashboard/Panel mit Hilfe der Controls-Klasse erstellt (Teil 2): Reaktionsfähigkeit von Schaltflächen hinzufügen

MetaTrader 5Handel | 20 Januar 2025, 12:38
326 0
Allan Munene Mutiiria
Allan Munene Mutiiria

Einführung

In unserem vorangegangenen Artikel haben wir erfolgreich die Hauptkomponenten unseres Dashboard-Panels für MetaQuotes Language 5 eingerichtet. In diesem Stadium blieben die von uns zusammengestellten Schaltflächen und Beschriftungen statisch und bildeten eine grundlegende, aber inaktive Struktur. Jetzt ist es an der Zeit, über einen rein visuellen Rahmen hinauszugehen. In diesem nächsten Teil werden wir uns darauf konzentrieren, das Panel wirklich interaktiv zu gestalten. Wir werden den Komponenten Leben einhauchen, indem wir die Fähigkeit hinzufügen, auf Nutzereingaben und Klicks zu reagieren, und unser Dashboard in ein dynamisches Tool verwandeln, das für Echtzeit-Handelsinteraktionen bereit ist.

In diesem Artikel, im Wesentlichen Teil 2, werden wir untersuchen, wie wir die Funktionalität der in Teil 1 erstellten Schaltflächen automatisieren und sicherstellen können, dass sie reagieren, wenn sie angeklickt und bearbeitet werden. Wir werden lernen, wie man Ereignisse einrichtet, die bestimmte Aktionen auslösen, sodass der Nutzer auf sinnvolle Weise mit dem Panel interagieren kann. Wir werden wichtige Themen behandeln, unter anderem:

  1. Illustration der zu automatisierenden Elemente: Ein detaillierter Überblick über die Komponenten, die an Funktionalität gewinnen werden.
  2. Automatisieren der GUI-Interaktionen in MQL5: Implementierung des erforderlichen Codes, um sicherzustellen, dass Schaltflächen effektiv auf Nutzereingaben und Klicks reagieren.
  3. Schlussfolgerung: Zusammenfassung der Fortschritte, die bei der Erstellung des interaktiven Dashboard-Panels gemacht wurden.

Lassen Sie uns in diese Themen eintauchen, um unsere Handelsschnittstelle zu verbessern!


Illustration der zu automatisierenden Elemente

Wir werden uns auf die Automatisierung der Schaltflächen konzentrieren, die wir im ersten Teil unseres MQL5-Panels erstellt haben. Jede Schaltfläche hat eine bestimmte Funktion, und wir wollen sicherstellen, dass sie intuitiv auf die Befehle des Nutzers reagiert. Diese Reaktion ist unerlässlich, denn im Gegensatz zu einem Programm, das im Hintergrund läuft, muss ein Handelsforum nutzerfreundlich und zugänglich sein. Zunächst gibt es die Schaltfläche in der oberen rechten Ecke des Fensters, mit der die gesamte Oberfläche geschlossen werden kann. Wenn also die Handelsumgebung auf dem Chart von MetaTrader 5 geöffnet ist, sollte es möglich sein, das Panel auf die gleiche Weise zu schließen, wie man eine Anwendung schließen würde.

Während die Handelsschaltfläche aktiv ist, werden wir Schaltflächen einrichten, die bestimmte Handelsoperationen ausführen. Dazu gehören „Open Buy“, „Sell“, „Sell Stop“, „Sell Limit“, „Buy Stop“ und „Buy Limit“. Diese Schaltflächen ermöglichen eine schnelle Auftragserteilung und erleichtern die sofortige Reaktion auf den sich ständig ändernden Markt. Wir werden auch die Schaltflächen zum Schließen automatisieren, die den Handel praktisch verwalten, wenn diese Schaltfläche aktiv ist. Dazu gehören u. a. „Alle schließen“ und „Alle Handelsgeschäfte im Gewinn schließen“ sowie eine Funktion, bei der es uns fast die Sprache verschlägt: „Alle schwebenden Aufträge schließen“. Wenn Sie auf eine Schaltfläche klicken, wird sie das tun, was sie angibt zu tun.

Schließlich werden wir die Informationstaste automatisieren, die, wenn sie gedrückt wird, eine Schnittstelle mit Schaltflächen aufruft, die Einzelheiten zu den Kontoinformationen und Hintergrundinformationen des Nutzers enthalten. Wir hoffen, dass dies dazu beiträgt, die Händler über relevante Details im Zusammenhang mit ihren Konten auf dem Laufenden zu halten und ihnen so hilft, bessere Entscheidungen zu treffen. Das Ziel von all dem ist es, ein reaktionsfähiges Handels-Panel zu schaffen, das die Art von Operationen, die ein Händler durchführen muss, einfach macht, und das auch in gewisser Weise versucht, den Nutzer mehr einzubeziehen, als es das letzte Panel tat.

Um das Verständnis dieser Automatisierungsprozesse und -komponenten zu erleichtern, finden Sie im Folgenden eine ausführliche Beschreibung dieser Prozesse und Komponenten mit dem vorherigen Meilenstein.

KOMPONENTEN ILLUSTRATION

Da wir nun wissen, was wir tun werden, können wir sofort mit der Automatisierung beginnen. Bitte lesen Sie den vorhergehenden Artikel, in dem wir die statische Anordnung der GUI-Elemente erstellt haben, falls Sie ihn noch nicht durchgearbeitet haben, damit Sie mit uns auf dem richtigen Weg sind. Los geht's.


Automatisieren der GUI-Interaktionen in MQL5

Wir werden von einfachen zu komplexen Prozessen übergehen, sodass unsere Struktur in chronologischer Reihenfolge angeordnet ist. Daher werden wir die Kontoinformationen bei jeder Tick- oder Kursänderung aktualisieren. Um dies zu erreichen, benötigen wir die Ereignisbehandlung von OnTick, eine integrierte MQL5-Funktion, die normalerweise aufgerufen wird, wenn sich die Kursnotierungen ändern. Die Funktion ist ein ungültiger Datentyp, was bedeutet, dass sie direkt ausgeführt wird und keine Ausgabe zurückgeben muss. Ihre Funktion sollte wie folgt aussehen.

//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick(){

  ...
}
//+------------------------------------------------------------------+

Dies ist die Ereignisbehandlung, die für die Preisaktualisierung zuständig ist und somit das Herzstück unserer Logik. Wir fügen die Steuerlogik zu dieser Funktion wie folgt hinzu:

//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick() {
   //--- Start of the OnTick function, called on every price tick

   //--- Check if the background color of the INFO button is yellow
   if (obj_Btn_INFO.ColorBackground() == clrYellow) {
      //--- Update the account equity display on the panel
      obj_Btn_ACC_EQUITY.Text(DoubleToString(AccountInfoDouble(ACCOUNT_EQUITY), 2));

      //--- Update the account balance display on the panel
      obj_Btn_ACC_BALANCE.Text(DoubleToString(AccountInfoDouble(ACCOUNT_BALANCE), 2));

      //--- Update the server trade time display on the panel
      obj_Btn_TIME.Text(TimeToString(TimeTradeServer(), TIME_DATE | TIME_SECONDS));
   }

   //--- End of the OnTick function
}
//+------------------------------------------------------------------+

Die erste Aktion, die wir innerhalb der Funktion OnTick durchführen, ist die Überprüfung der Hintergrundfarbe der Informationsschaltfläche („obj_Btn_INFO“). Wenn der Hintergrund der Schaltfläche gelb ist, bedeutet dies, dass wir den Informationsanzeigemodus aktiviert haben. Wenn diese Bedingung erfüllt ist, werden wir verschiedene kontobezogene Anzeigen auf dem Panel aktualisieren. Konkret aktualisieren wir die Anzeige des Kontokapitals, indem wir das aktuelle Kontokapital mit der Funktion AccountInfoDouble abrufen, die Eigenschaft „ACCOUNT_EQUITY“ als Eingabeparameter übergeben und sie mit der Funktion DoubleToString auf zwei Dezimalstellen formatieren. Diesen Wert ordnen wir dann dem Text der Schaltfläche „obj_Btn_ACC_EQUITY“ zu, sodass wir immer die neuesten Aktieninformationen zur Hand haben.

Als Nächstes aktualisieren wir auf ähnliche Weise die Anzeige des Kontostands, indem wir den Kontostand mithilfe der Parametereigenschaft „ACCOUNT_BALANCE“ abrufen, ihn auf zwei Dezimalstellen formatieren und ihn auf den Text der Schaltfläche „obj_Btn_ACC_BALANCE“ setzen. Schließlich aktualisieren wir die Anzeige der Server-Handelszeit, indem wir die aktuelle Server-Handelszeit mit der Funktion TimeTradeServer abrufen, sie so formatieren, dass sie sowohl Datum als auch Sekunden enthält, und den Text der Schaltfläche „obj_Btn_TIME“ mit diesem Wert aktualisieren. Auf diese Weise ist sichergestellt, dass wir immer über den neuesten Handelszeitpunkt informiert sind, was für eine rechtzeitige Entscheidungsfindung in Handelsszenarien unerlässlich ist. Dies sind die Ergebnisse.

ONTICK AKTUELL

Anhand der Visualisierung können wir sehen, dass das Zeitfeld entsprechend aktualisiert wird, was ein Erfolg ist. Nun ist die erste Automatisierungskomponente fertig. Das war doch einfach, oder? Wir fahren dann mit den anderen Komponenten unseres GUI-Panels fort. Die Automatisierung der übrigen Elemente erfolgt innerhalb von OnChartEvent, weshalb wir uns dessen Eingabeparameter und Funktionen genauer ansehen wollen.

void OnChartEvent(const int id,
                  const long &lparam,
                  const double &dparam,
                  const string &sparam)
{
      ...
}

Der Zweck der Funktion besteht darin, Chart-Änderungen zu verarbeiten, die entweder von einem Nutzer oder einem MQL5-Programm vorgenommen werden. So werden die Interaktionen des Nutzers, wie z. B. das Bewegen der Maus, das Bearbeiten der Schaltflächenfelder und das Anklicken von Beschriftungen und Schaltflächen, von diesem Ereignis-Handler erfasst und verarbeitet. Wir wollen die Argumente aufschlüsseln, um sie besser interpretieren zu können:

  • id: Dieser Parameter steht für die Ereignis-ID und entspricht einem der 11 vordefinierten Ereignistypen. Dazu gehören Ereignisse wie Tastendruck, Mausbewegungen, Objekterstellung, Chart-Änderungen und nutzerdefinierte Ereignisse. Für nutzerdefinierte Ereignisse können wir die IDs von CHARTEVENT_CUSTOM bis CHARTEVENT_CUSTOM_LAST verwenden. Die 11 Ereignistypen sind wie unten dargestellt;

DIAGRAMM-EREIGNISTYPEN

  • lparam: Ein Ereignisparameter vom Typ long. Sein Wert hängt von dem jeweiligen Ereignis ab, das behandelt wird. Es könnte zum Beispiel einen Tastencode während eines Tastendrucks darstellen.
  • dparam: Ein Ereignisparameter vom Typ double. Ähnlich wie bei lparam variiert sein Wert je nach Ereignistyp. Bei einer Mausbewegung kann sie beispielsweise die Position des Mauszeigers übermitteln.
  • sparam: Ein Ereignisparameter vom Typ string. Auch hier hängt die Bedeutung vom jeweiligen Ereignis ab. Bei der Objekterstellung könnte er beispielsweise den Namen des neu erstellten Objekts enthalten.

Um dies verständlicher darzustellen, erstellen wir innerhalb der Funktion einen Ausdruck, der alle vier Argumente für das Journal enthält.

// Print the 4 function parameters    
Print("ID = ",id,", LPARAM = ",lparam,", DPARAM = ",dparam,", SPARAM = ",sparam);

Diese Funktion druckt die ID des Chart-Ereignisses, den Wert des Ereignisses vom langen Typ, den Wert des Ereignisses vom doppelten Typ und den Wert vom Typ String. Schauen wir uns das folgende GIF an, um die Referenzierung zu erleichtern.

ALLGEMEINE CHARTEVENTS

Anhand des mitgelieferten GIFs sollte nun alles klar sein. Wir gehen nun zur Erfassung von Chart-Klickereignissen auf den GUI-Panel-Elementen über. Unsere ID wird also CHARTEVENT_OBJECT_CLICK sein.

   //Print("ID = ",id,", LPARAM = ",lparam,", DPARAM = ",dparam,", SPARAM = ",sparam);

   if (id==CHARTEVENT_OBJECT_CLICK){
        
        //---
   }

Zunächst kommentieren wir die vorherige Codezeile aus, da wir unser Journal nicht mit irrelevanten Informationen zukleistern wollen. Die beiden Schrägstriche (//) werden als einzeilige Kommentare bezeichnet und kommentieren einen Code von ihrem Anfang bis zum Ende der Zeile aus, daher der Name „einzeiliger“ Kommentar. Insbesondere Kommentare werden vom Computer während der Ausführung ignoriert. Wir verwenden die if-Anweisung, um zu prüfen, ob ein Objekt angeklickt wurde. Dies wird erreicht, indem die ID des Chart-Ereignisses mit den Enumeration der Objektklicks gleichgesetzt wird. Wenn wir ein Objekt angeklickt haben, können wir die Argumente ausdrucken und sehen, was wir erhalten. Der folgende Code wird verwendet. 

   if (id==CHARTEVENT_OBJECT_CLICK){
      Print("ID = ",id,", LM = ",lparam,", DM = ",dparam,", SPARAM = ",sparam);

     ...
   }

In der Druckfunktion haben wir „LPARAM“ in „LP“ und „DPARAM“ in „DP“ geändert, sodass wir uns nur auf die ID des Chart-Ereignisses und den Namen des angeklickten Objekts konzentrieren können, von wo aus wir die ID des Objekts abrufen und gegebenenfalls Maßnahmen ergreifen können. Im Folgenden wird die Logik veranschaulicht:

OBJEKT KLICKEN GIF

Die erste Schaltfläche, die wir automatisieren, ist die Schaltfläche Handel. 

      if (sparam==obj_Btn_TRADE.Name()){
         // Print to the log which object was clicked for debugging purposes
         Print("OBJECT CLICKED = ", obj_Btn_TRADE.Name());

         // Reset the pressed states of all buttons to ensure only one button appears pressed at a time
         obj_Btn_TRADE.Pressed(false);
         obj_Btn_CLOSE.Pressed(false);
         obj_Btn_INFO.Pressed(false);

         // Change the background color of the Trade button to yellow to indicate it is active
         obj_Btn_TRADE.ColorBackground(clrYellow);
         // Set the background color of the Close and Info buttons to silver to indicate they are inactive
         obj_Btn_CLOSE.ColorBackground(clrSilver);
         obj_Btn_INFO.ColorBackground(clrSilver);

         // Set the border color of the Trade button to yellow to match its background
         obj_Btn_TRADE.ColorBorder(clrYellow);
         // Set the border color of the Close and Info buttons to silver to indicate they are inactive
         obj_Btn_CLOSE.ColorBorder(clrSilver);
         obj_Btn_INFO.ColorBorder(clrSilver);

         // Call a function to destroy the Close section if it exists
         destroySection_Close();
         // Call a function to destroy the Information section if it exists
         destroySection_Information();

         // Create the Trade section, bringing it to the forefront
         createSection_Trade();
      }

Hier verwalten wir die Interaktion, wenn die Schaltfläche „Handeln“ angeklickt wird, indem wir prüfen, ob der String-Parameter mit dem Namen der Schaltfläche „Handeln“ übereinstimmt, indem wir die Funktion „Name“ verwenden. Zunächst verwenden wir die Funktion Print, um den Namen des angeklickten Objekts zu protokollieren, was bei der Fehlersuche hilft, indem es bestätigt, welche Schaltfläche angeklickt wurde. Als Nächstes setzen wir den gedrückten Zustand aller relevanten Schaltflächen - „obj_Btn_TRADE“, „obj_Btn_CLOSE“ und „obj_Btn_INFO“ - zurück, indem wir die Funktion „Pressed“ für jede Schaltfläche aufrufen und ein false flag übergeben. Dadurch wird sichergestellt, dass jeweils nur eine Taste sichtbar gedrückt wird. Nach dem Zurücksetzen zeigen wir visuell an, dass die Schaltfläche „Trade“ (Handeln) aktiv ist, indem wir ihre Hintergrundfarbe mit der Funktion „ColorBackground“ auf Gelb ändern, während wir die Hintergrundfarben der Schaltflächen „Close“ (Schließen) und „Info“ auf Silber setzen, um anzuzeigen, dass sie inaktiv sind. In ähnlicher Weise aktualisieren wir die Rahmenfarben mit der Funktion „ColorBorder“ - gelb für die Schaltfläche „Trade“ (Handeln) und silber für die anderen.

Anschließend bereinigen wir die Schnittstelle, indem wir die Funktionen „destroySection_Close“ und „destroySection_Information“ aufrufen, um alle für die Abschnitte „Close“ (Schließen) und „Information“ angezeigten Inhalte zu entfernen. Schließlich rufen wir die Funktion „createSection_Trade“ auf, um den Handelsbereich dynamisch zu erstellen und anzuzeigen und sicherzustellen, dass der Nutzer Zugang zur Handelsschnittstelle hat. Ähnlich verfahren wir auch mit der Schaltfläche „Close“ (Schließen).

      // Check if the clicked object is the Close button
      else if (sparam==obj_Btn_CLOSE.Name()){
         // Print to the log which object was clicked for debugging purposes
         Print("OBJECT CLICKED = ", obj_Btn_CLOSE.Name());

         // Reset the pressed states of all buttons
         obj_Btn_TRADE.Pressed(false);
         obj_Btn_CLOSE.Pressed(false);
         obj_Btn_INFO.Pressed(false);

         // Set the background color of the Trade button to silver, indicating it's inactive
         obj_Btn_TRADE.ColorBackground(clrSilver);
         // Change the background color of the Close button to yellow to indicate it is active
         obj_Btn_CLOSE.ColorBackground(clrYellow);
         obj_Btn_INFO.ColorBackground(clrSilver);

         // Set the border color of the Trade button to silver
         obj_Btn_TRADE.ColorBorder(clrSilver);
         // Set the border color of the Close button to yellow
         obj_Btn_CLOSE.ColorBorder(clrYellow);
         obj_Btn_INFO.ColorBorder(clrSilver);

         // Call a function to destroy the Trade section if it exists
         destroySection_Trade();
         // Call a function to destroy the Information section if it exists
         destroySection_Information();

         // Create the Close section, bringing it to the forefront
         createSection_Close();
      }

Hier wird fast genau die gleiche Logik angewandt wie bei der Schaltfläche „Trade“. Wir behandeln das Szenario, in dem die Schaltfläche „Close“ angeklickt wird, indem wir prüfen, ob der String-Parameter mit dem Namen der Schaltfläche „Close“ übereinstimmt, indem wir die Funktion „Name“ verwenden. Wir protokollieren zunächst den Namen der angeklickten Schaltfläche mit der Funktion Print zu Debugging-Zwecken. So können wir sicherstellen, dass die richtige Schaltfläche angeklickt wurde. Als Nächstes setzen wir die gedrückten Zustände der Schaltflächen „obj_Btn_TRADE“, „obj_Btn_CLOSE“ und „obj_Btn_INFO“ mit der Funktion „Pressed“ zurück. Dadurch wird sichergestellt, dass keine der Schaltflächen nach dem Anklicken visuell gedrückt bleibt. Dann aktualisieren wir die Schnittstelle, indem wir die Hintergrundfarbe der Schaltfläche „Close“ mit der Funktion „ColorBackground“ auf Gelb ändern, um anzuzeigen, dass sie aktiv ist, während wir die Hintergrundfarbe der Schaltflächen „Trade“ und „Info“ auf Silber setzen, um anzuzeigen, dass sie inaktiv sind.

In ähnlicher Weise passen wir die Randfarben der Schaltflächen mit der Funktion „ColorBorder“ an, indem wir den Rand der Schaltfläche „Schließen“ gelb färben, damit er zu ihrem Hintergrund passt, während die Ränder der Schaltflächen „Handel“ und „Info“ silberfarben sind, um Inaktivität anzuzeigen. Um aufzuräumen, rufen wir die Funktionen „destroySection_Trade“ und „destroySection_Information“ auf, um alle vorhandenen Inhalte für die Handels- oder Informationsabschnitte zu entfernen. Schließlich rufen wir die Funktion „createSection_Close“ auf, um die Schnittstelle für das Schließen von Positionen oder Aufträgen dynamisch zu generieren und anzuzeigen und sicherzustellen, dass der Nutzer mit den entsprechenden Schließungsoptionen im Panel interagieren kann. Auch die Informationstaste folgt der gleichen Logik. Das Codeschnipsel sieht wie folgt aus:

      // Check if the clicked object is the Information button
      else if (sparam==obj_Btn_INFO.Name()){
         // Print to the log which object was clicked for debugging purposes
         Print("OBJECT CLICKED = ", obj_Btn_INFO.Name());

         // Reset the pressed states of all buttons
         obj_Btn_TRADE.Pressed(false);
         obj_Btn_CLOSE.Pressed(false);
         obj_Btn_INFO.Pressed(false);

         // Set the background color of the Trade and Close buttons to silver, indicating they are inactive
         obj_Btn_TRADE.ColorBackground(clrSilver);
         obj_Btn_CLOSE.ColorBackground(clrSilver);
         // Change the background color of the Info button to yellow to indicate it is active
         obj_Btn_INFO.ColorBackground(clrYellow);

         // Set the border color of the Trade and Close buttons to silver
         obj_Btn_TRADE.ColorBorder(clrSilver);
         obj_Btn_CLOSE.ColorBorder(clrSilver);
         // Set the border color of the Info button to yellow
         obj_Btn_INFO.ColorBorder(clrYellow);

         // Call a function to destroy the Trade section if it exists
         destroySection_Trade();
         // Call a function to destroy the Close section if it exists
         destroySection_Close();

         // Create the Information section, bringing it to the forefront
         createSection_Information();
      }

Wenn wir das Programm kompilieren und ausführen, erhalten wir die folgende Ausgabe für die Interaktion mit der Schaltfläche.

KONTROLLKLICKEN

Wie wir sehen, war das ein Erfolg. Jetzt wollen wir das Panel zerstören, wenn die Schaltfläche Fenster angeklickt wird.

      // Check if the clicked object is the exit button (X button)
      else if (sparam==obj_Btn_X.Name()){
         // Print to the log which object was clicked for debugging purposes
         Print("OBJECT CLICKED = ", obj_Btn_X.Name());

         // Call functions to destroy all sections, effectively closing the entire panel
         destroySection_Trade();
         destroySection_Close();
         destroySection_Information();

         // Call a function to destroy the main panel itself
         destroySection_Main_Panel();
      }

Hier behandeln wir den Fall, dass wir auf die Schaltfläche „Exit“ klicken, indem wir prüfen, ob der String-Parameter mit dem Namen der Schaltfläche „Exit“ übereinstimmt, indem wir die Funktion „Name“ verwenden. Wir protokollieren zunächst den Namen des angeklickten Objekts zu Debugging-Zwecken mit der Funktion Print, was uns hilft, zu bestätigen, dass die Schaltfläche exit angeklickt wurde.

Anschließend wird das gesamte Panel durch den Aufruf mehrerer Funktionen geschlossen. Zunächst rufen wir „destroySection_Trade“, „destroySection_Close“ und „destroySection_Information“ auf, um alle Abschnitte zu entfernen, die mit dem Handel, dem Schließen von Positionen oder der Anzeige von Kontoinformationen zu tun haben, sofern sie derzeit angezeigt werden. Diese Funktionen stellen sicher, dass alle interaktiven Bereiche des Bedienfelds ordnungsgemäß gelöscht werden. Schließlich rufen wir die Funktion „destroySection_Main_Panel“ auf, die für die Zerstörung des Haupt-Panels selbst zuständig ist. Dadurch werden alle Elemente des Bedienfelds aus der Oberfläche entfernt und das Bedienfeld vollständig geschlossen. Hier ist eine Illustration.

PANEL SCHLIESSEN

Nun müssen wir die Handelsoperationen durchführen, wenn die entsprechenden Schaltflächen angeklickt werden. Um dies einfach zu tun, müssen wir eine Klasseninstanz einfügen, die diesen Prozess unterstützt. Wir binden also eine Handelsinstanz ein, indem wir #include am Anfang des Quellcodes verwenden. Dadurch erhalten wir Zugriff auf die Klasse „CTrade“, mit der wir ein Handelsobjekt erstellen können. Dies ist von entscheidender Bedeutung, da wir es für die Handelsgeschäfte benötigen.

#include <Trade/Trade.mqh>
CTrade obj_Trade;

Der Präprozessor wird die Zeile #include <Trade/Trade.mqh> durch den Inhalt der Datei Trade.mqh ersetzen. Die spitzen Klammern zeigen an, dass die Datei Trade.mqh aus dem Standardverzeichnis entnommen wird (normalerweise ist es das Terminal-Installationsverzeichnis\MQL5\Include). Das aktuelle Verzeichnis wird bei der Suche nicht berücksichtigt. Die Zeile kann an beliebiger Stelle im Programm platziert werden, aber in der Regel werden alle Einschlüsse am Anfang des Quellcodes platziert, um den Code besser zu strukturieren und die Referenz zu erleichtern. Die Deklaration des Objekts obj_Trade der Klasse CTrade ermöglicht uns dank der MQL5-Entwickler einen einfachen Zugriff auf die in dieser Klasse enthaltenen Methoden.

Die Klasse CTRADE

Nachdem wir die Handelsbibliothek eingebunden haben, können wir mit der Logik beginnen, um eine Verkaufsposition zu eröffnen, wenn die Schaltfläche Verkaufen angeklickt wird. Das ist die Logik, die wir anwenden müssen.

      else if (sparam==obj_Btn_SELL.Name()){ //--- Check if the Sell button is clicked
         Print("OBJECT CLICKED = ",obj_Btn_SELL.Name()); //--- Log the button click event
         
         double Ask = NormalizeDouble(SymbolInfoDouble(_Symbol,SYMBOL_ASK),_Digits); //--- Get and normalize the ask price
         double Bid = NormalizeDouble(SymbolInfoDouble(_Symbol,SYMBOL_BID),_Digits); //--- Get and normalize the bid price
         
         double lots = StringToDouble(obj_Edit_LOTS.Text()); //--- Get the lot size from input field
         double entry_price = Bid; //--- Set the entry price for selling to the current bid price
         double stopLoss = Ask+StringToDouble(obj_Edit_SL.Text())*_Point; //--- Calculate stop loss based on user input
         double takeprofit = Ask-StringToDouble(obj_Edit_TP.Text())*_Point; //--- Calculate take profit based on user input
         
         Print("Lots = ",lots,", Entry = ",entry_price,", SL = ",stopLoss,", TP = ",takeprofit); //--- Log order details
         obj_Trade.Sell(lots,_Symbol,entry_price,stopLoss,takeprofit); //--- Execute the sell order
      }

Sobald wir bestätigen, dass die Schaltfläche zum Verkaufen angeklickt wurde, protokollieren wir das Schaltflächenklick-Ereignis mit der Funktion Print zu Debugging-Zwecken, die den Namen der angeklickten Schaltfläche ausgibt. Anschließend erfassen wir die wichtigsten Handelsdaten. Zunächst werden die aktuellen Geld- und Briefkurse des Symbols mit der Funktion SymbolInfoDouble abgerufen und normalisiert. Die Funktion NormalizeDouble sorgt dafür, dass diese Preise entsprechend der durch _Digits definierten Anzahl von Dezimalstellen richtig formatiert werden.

Als Nächstes erhalten wir die Losgröße aus dem Eingabefeld, indem wir den Text aus dem Feld „Lots“ (Losgröße) mit der Funktion StringToDouble in eine Zahl umwandeln. Der Einstiegskurs wird auf den aktuellen Geldkurs gesetzt, da ein Verkaufsauftrag diesen Kurs für die Ausführung verwendet. Wir berechnen auch Stop-Loss und Take-Profit auf der Grundlage der Nutzereingaben. Der Stop-Loss wird berechnet, indem der nutzerdefinierte Wert (aus dem Eingabefeld „SL“) zum „Ask“ addiert wird, bereinigt um das minimale Kursinkrement des Symbols, _Point. In ähnlicher Weise wird der Take Profit berechnet, indem der nutzerdefinierte Wert (aus dem Eingabefeld „TP“) vom „Ask“-Kurs abgezogen wird.

Schließlich protokollieren wir die Auftragsdetails wie Losgröße, Einstiegskurs, Stop-Loss und Take-Profit mit der Funktion Print. Der Verkaufsauftrag wird durch den Aufruf der Funktion „Sell“ aus dem Objekt „obj_Trade“ ausgeführt, wobei die erforderlichen Parameter übergeben werden: Losgröße, Symbol, Einstiegskurs, Stop Loss und Take Profit. Für die Kaufposition wird eine ähnliche Logik wie unten beschrieben verwendet.

      else if (sparam==obj_Btn_BUY.Name()){ //--- Check if the Buy button is clicked
         Print("OBJECT CLICKED = ",obj_Btn_BUY.Name()); //--- Log the button click event
         
         double Ask = NormalizeDouble(SymbolInfoDouble(_Symbol,SYMBOL_ASK),_Digits); //--- Get and normalize the ask price
         double Bid = NormalizeDouble(SymbolInfoDouble(_Symbol,SYMBOL_BID),_Digits); //--- Get and normalize the bid price
         
         double lots = StringToDouble(obj_Edit_LOTS.Text()); //--- Get the lot size from input field
         double entry_price = Ask; //--- Set the entry price for buying to the current ask price
         double stopLoss = Bid-StringToDouble(obj_Edit_SL.Text())*_Point; //--- Calculate stop loss based on user input
         double takeprofit = Bid+StringToDouble(obj_Edit_TP.Text())*_Point; //--- Calculate take profit based on user input
         
         Print("Lots = ",lots,", Entry = ",entry_price,", SL = ",stopLoss,", TP = ",takeprofit); //--- Log order details
         obj_Trade.Buy(lots,_Symbol,entry_price,stopLoss,takeprofit); //--- Execute the buy order
      }

Wenn wir den Fortschritt kompilieren und testen, erhalten wir die folgende Ausgabe.

DIE TASTEN SELL & BUY

Für die Eröffnung von Limit- und Stop-Aufträgen wird eine ähnliche Logik verwendet. Da diese jedoch nicht direkt die aktuellen Marktkurse verwenden, müssen wir einen zusätzlichen Algorithmus für Sicherheitsüberprüfungen konfigurieren und einbauen. Beginnen wir mit der Verkaufsstopptaste.

      else if (sparam==obj_Btn_SELLSTOP.Name()){ //--- Check if the Sell Stop button is clicked
         Print("OBJECT CLICKED = ",obj_Btn_SELLSTOP.Name()); //--- Log the button click event
         
         double Ask = NormalizeDouble(SymbolInfoDouble(_Symbol,SYMBOL_ASK),_Digits); //--- Get and normalize the ask price
         double Bid = NormalizeDouble(SymbolInfoDouble(_Symbol,SYMBOL_BID),_Digits); //--- Get and normalize the bid price
         
         double user_price = StringToDouble(obj_Edit_PRICE.Text()); //--- Get the user-defined price from input field
         long stopslevel = SymbolInfoInteger(_Symbol,SYMBOL_TRADE_STOPS_LEVEL); //--- Get the minimum stop level for the symbol
         double valid_price = Bid - stopslevel*_Point; //--- Calculate the valid price for placing a sell stop order
         
         if (user_price > valid_price){ //--- Check if the user-defined price is valid
            Print("ERROR: INVALID STOPS PRICE. ",user_price," > ",valid_price); //--- Log an error message
         }
         else if (user_price <= valid_price){ //--- If the user-defined price is valid
            double lots = StringToDouble(obj_Edit_LOTS.Text()); //--- Get the lot size from input field
            double entry_price = user_price; //--- Set the entry price for the sell stop order
            double stopLoss = user_price+StringToDouble(obj_Edit_SL.Text())*_Point; //--- Calculate stop loss based on user input
            double takeprofit = user_price-StringToDouble(obj_Edit_TP.Text())*_Point; //--- Calculate take profit based on user input
            
            Print("Lots = ",lots,", Entry = ",entry_price,", SL = ",stopLoss,", TP = ",takeprofit); //--- Log order details
            obj_Trade.SellStop(lots,entry_price,_Symbol,stopLoss,takeprofit); //--- Execute the sell stop order
         }
      }

Hier handhaben wir die Automatisierungslogik für das Anklicken der Schaltfläche „Sell Stop", indem wir prüfen, ob der variable String-Parameter mit dem Namen der Schaltfläche „Sell Stop" übereinstimmt, indem wir die Funktion „Name" verwenden. Sobald wir dies bestätigt haben, protokollieren wir das Ereignis wie üblich mit der Funktion Print, um den Namen der angeklickten Schaltfläche zu Debugging-Zwecken auszugeben. Wir beginnen mit dem Abrufen und Normalisieren der Geld- und Briefkurse, indem wir die Funktion SymbolInfoDouble verwenden, um die Marktpreise abzurufen, und NormalizeDouble, um sie entsprechend der durch „_Digits“ definierten Dezimalgenauigkeit des Symbols zu formatieren.

Als Nächstes holen wir uns den „nutzerdefinierten Preis“ aus dem Eingabefeld „Preis“ und wandeln ihn mit der Funktion StringToDouble von Text in einen numerischen Wert um. Darüber hinaus wird der Mindest-Stopp-Level für das Symbol, der für die Validierung von Stop-Order erforderlich ist, über SymbolInfoInteger mit dem Parameter „SYMBOL_TRADE_STOPS_LEVEL“ abgefragt. Wir berechnen den gültigen Preis, indem wir das (in Punkte umgerechnete) Stop-Level vom „Bid“ abziehen.

Wir prüfen dann, ob der „nutzerdefinierte Preis“ gültig ist, indem wir ihn mit dem berechneten gültigen Preis vergleichen. Wenn der Nutzerpreis den gültigen Bereich überschreitet, wird eine Fehlermeldung mit dem Hinweis auf einen „ungültigen Stopp-Preis“ ausgegeben. Wenn der nutzerdefinierte Preis jedoch gültig ist, fahren wir mit den folgenden Schritten fort. Wir rufen die Losgröße aus dem Eingabefeld ab, indem wir StringToDouble verwenden, um den Text in eine Zahl zu konvertieren. Der Einstiegspreis wird auf den nutzerdefinierten Preis gesetzt. Wir berechnen dann den Stop-Loss, indem wir den nutzerdefinierten Stop-Loss-Wert (aus dem Feld „SL“) zum Einstiegskurs addieren und ihn auf der Grundlage des Preisinkrements des Symbols _Point anpassen. In ähnlicher Weise wird der Take-Profit berechnet, indem der nutzerdefinierte Take-Profit (aus dem Feld „TP“) vom Einstiegskurs subtrahiert wird.

Schließlich protokollieren wir die Auftragsdetails, einschließlich der Losgröße, des Einstiegskurses, des Stop-Loss und des Take-Profits, mit der Funktion Print. Die Sell-Stop-Order wird ausgeführt, indem die Funktion „Sell Stop“ vom Objekt „obj_Trade“ aufgerufen wird, wobei die relevanten Parameter wie Losgröße, Einstiegskurs, Symbol, Stop-Loss und Take-Profit übergeben werden und die Sell-Stop-Order gemäß den Anweisungen des Nutzers platziert wird. Für die Ausführung der anderen Aufträge verwenden wir einen ähnlichen Ansatz.

      else if (sparam==obj_Btn_BUYSTOP.Name()){ //--- Check if the Buy Stop button is clicked
         Print("OBJECT CLICKED = ",obj_Btn_BUYSTOP.Name()); //--- Log the button click event
         
         double Ask = NormalizeDouble(SymbolInfoDouble(_Symbol,SYMBOL_ASK),_Digits); //--- Get and normalize the ask price
         double Bid = NormalizeDouble(SymbolInfoDouble(_Symbol,SYMBOL_BID),_Digits); //--- Get and normalize the bid price
         
         double user_price = StringToDouble(obj_Edit_PRICE.Text()); //--- Get the user-defined price from input field
         long stopslevel = SymbolInfoInteger(_Symbol,SYMBOL_TRADE_STOPS_LEVEL); //--- Get the minimum stop level for the symbol
         double valid_price = Ask + stopslevel*_Point; //--- Calculate the valid price for placing a buy stop order
         
         if (user_price < valid_price){ //--- Check if the user-defined price is valid
            Print("ERROR: INVALID STOPS PRICE. ",user_price," < ",valid_price); //--- Log an error message
         }
         else if (user_price >= valid_price){ //--- If the user-defined price is valid
            double lots = StringToDouble(obj_Edit_LOTS.Text()); //--- Get the lot size from input field
            double entry_price = user_price; //--- Set the entry price for the buy stop order
            double stopLoss = user_price-StringToDouble(obj_Edit_SL.Text())*_Point; //--- Calculate stop loss based on user input
            double takeprofit = user_price+StringToDouble(obj_Edit_TP.Text())*_Point; //--- Calculate take profit based on user input
            
            Print("Lots = ",lots,", Entry = ",entry_price,", SL = ",stopLoss,", TP = ",takeprofit); //--- Log order details
            obj_Trade.BuyStop(lots,entry_price,_Symbol,stopLoss,takeprofit); //--- Execute the buy stop order
         }
      }
      else if (sparam==obj_Btn_SELLLIMIT.Name()){ //--- Check if the Sell Limit button is clicked
         Print("OBJECT CLICKED = ",obj_Btn_SELLLIMIT.Name()); //--- Log the button click event
         
         double Ask = NormalizeDouble(SymbolInfoDouble(_Symbol,SYMBOL_ASK),_Digits); //--- Get and normalize the ask price
         double Bid = NormalizeDouble(SymbolInfoDouble(_Symbol,SYMBOL_BID),_Digits); //--- Get and normalize the bid price
         
         double user_price = StringToDouble(obj_Edit_PRICE.Text()); //--- Get the user-defined price from input field
         long stopslevel = SymbolInfoInteger(_Symbol,SYMBOL_TRADE_STOPS_LEVEL); //--- Get the minimum stop level for the symbol
         double valid_price = Bid + stopslevel*_Point; //--- Calculate the valid price for placing a sell limit order
         
         if (user_price < valid_price){ //--- Check if the user-defined price is valid
            Print("ERROR: INVALID STOPS PRICE. ",user_price," < ",valid_price); //--- Log an error message
         }
         else if (user_price >= valid_price){ //--- If the user-defined price is valid
            double lots = StringToDouble(obj_Edit_LOTS.Text()); //--- Get the lot size from input field
            double entry_price = user_price; //--- Set the entry price for the sell limit order
            double stopLoss = user_price+StringToDouble(obj_Edit_SL.Text())*_Point; //--- Calculate stop loss based on user input
            double takeprofit = user_price-StringToDouble(obj_Edit_TP.Text())*_Point; //--- Calculate take profit based on user input
            
            Print("Lots = ",lots,", Entry = ",entry_price,", SL = ",stopLoss,", TP = ",takeprofit); //--- Log order details
            obj_Trade.SellLimit(lots,entry_price,_Symbol,stopLoss,takeprofit); //--- Execute the sell limit order
         }
      }
      else if (sparam==obj_Btn_BUYLIMIT.Name()){ //--- Check if the Buy Limit button is clicked
         Print("OBJECT CLICKED = ",obj_Btn_BUYLIMIT.Name()); //--- Log the button click event
         
         double Ask = NormalizeDouble(SymbolInfoDouble(_Symbol,SYMBOL_ASK),_Digits); //--- Get and normalize the ask price
         double Bid = NormalizeDouble(SymbolInfoDouble(_Symbol,SYMBOL_BID),_Digits); //--- Get and normalize the bid price
         
         double user_price = StringToDouble(obj_Edit_PRICE.Text()); //--- Get the user-defined price from input field
         long stopslevel = SymbolInfoInteger(_Symbol,SYMBOL_TRADE_STOPS_LEVEL); //--- Get the minimum stop level for the symbol
         double valid_price = Ask - stopslevel*_Point; //--- Calculate the valid price for placing a buy limit order
         
         if (user_price > valid_price){ //--- Check if the user-defined price is valid
            Print("ERROR: INVALID STOPS PRICE. ",user_price," > ",valid_price); //--- Log an error message
         }
         else if (user_price <= valid_price){ //--- If the user-defined price is valid
            double lots = StringToDouble(obj_Edit_LOTS.Text()); //--- Get the lot size from input field
            double entry_price = user_price; //--- Set the entry price for the buy limit order
            double stopLoss = user_price-StringToDouble(obj_Edit_SL.Text())*_Point; //--- Calculate stop loss based on user input
            double takeprofit = user_price+StringToDouble(obj_Edit_TP.Text())*_Point; //--- Calculate take profit based on user input
            
            Print("Lots = ",lots,", Entry = ",entry_price,", SL = ",stopLoss,", TP = ",takeprofit); //--- Log order details
            obj_Trade.BuyLimit(lots,entry_price,_Symbol,stopLoss,takeprofit); //--- Execute the buy limit order
         }
      }

Wenn wir die Schaltflächen testen, erhalten wir die folgenden Ausgabedaten.

SCHWEBENDE AUDFTRÄGE

Das war ein Erfolg. Wir gehen nun dazu über, Positionen zu schließen und schwebende Aufträge zu löschen. Beginnen wir mit der Logik, alle offenen Marktpositionen zu schließen.

      else if (sparam==obj_Btn_CLOSE_ALL.Name()){ //--- Check if the Close All button is clicked
         Print("OBJECT CLICKED = ",obj_Btn_CLOSE_ALL.Name()); //--- Log the button click event
         
         for (int i = PositionsTotal() -1; i >= 0; i--){ //--- Loop through all positions
            ulong pos_ticket = PositionGetTicket(i); //--- Get the ticket of the position
            if (pos_ticket > 0){ //--- Check if the position ticket is valid
               if (PositionSelectByTicket(pos_ticket)){ //--- Select the position by ticket
                  if (PositionGetString(POSITION_SYMBOL)==_Symbol){ //--- Check if the position matches the symbol
                     obj_Trade.PositionClose(pos_ticket); //--- Close the position
                  }
               }
            }
         }
      }

Hier wird nur die Logik für die Automatisierung der Schaltfläche „Close All“ (Alle schließen) behandelt, die alle aktiven Positionen schließt, wenn sie angeklickt wird. Zunächst prüfen wir, ob die Schaltfläche angeklickt wurde, und protokollieren das Ereignis, indem wir den Namen der angeklickten Schaltfläche zu Debugging-Zwecken mit der Funktion Print in das Protokoll drucken. Als Nächstes starten wir eine for-Schleife, die alle offenen Positionen des Kontos durchläuft, indem wir die Funktion PositionsTotal verwenden, die die Gesamtzahl der offenen Positionen zurückgibt. Die Schleife beginnt an der letzten Position in der Liste (dargestellt durch „PositionsTotal() - 1“) und arbeitet rückwärts, wobei „i“ nach jeder Iteration dekrementiert wird, um sicherzustellen, dass wir alle Positionen bearbeiten.

Bei jeder Iteration verwenden wir die Funktion PositionGetTicket, um die Ticketnummer der aktuellen Position abzurufen. Die Ticketnummer identifiziert jede Position eindeutig. Wir prüfen dann, ob das Ticket gültig ist, indem wir sicherstellen, dass der Wert größer als 0 ist. Falls gültig, verwenden wir die Funktion PositionSelectByTicket, um die mit diesem Ticket verbundene Position auszuwählen.

Sobald die Position ausgewählt ist, überprüfen wir, ob die Position mit dem Symbol übereinstimmt, für das wir Trades verwalten, indem wir die Funktion PositionGetString mit dem Parameter „POSITION_SYMBOL“ verwenden und ihn mit _Symbol vergleichen, das das aktuelle Handelssymbol darstellt. Wenn die Position mit dem Symbol übereinstimmt, rufen wir die Funktion „PositionClose“ aus dem Objekt „obj_Trade“ auf und übergeben die Ticketnummer als Parameter, um die Position zu schließen. Dieser Vorgang wird so lange fortgesetzt, bis alle Positionen, die dem Handelssymbol entsprechen, geschlossen sind. Um alle Verkaufspositionen zu schließen, verwenden wir dieselbe Funktion, fügen ihr aber eine zusätzliche Kontrolllogik hinzu, und zwar wie folgt

      else if (sparam==obj_Btn_CLOSE_ALL_SELL.Name()){ //--- Check if the Close All Sell button is clicked
         Print("OBJECT CLICKED = ",obj_Btn_CLOSE_ALL_SELL.Name()); //--- Log the button click event
         
         for (int i = PositionsTotal() -1; i >= 0; i--){ //--- Loop through all positions
            ulong pos_ticket = PositionGetTicket(i); //--- Get the ticket of the position
            if (pos_ticket > 0){ //--- Check if the position ticket is valid
               if (PositionSelectByTicket(pos_ticket)){ //--- Select the position by ticket
                  if (PositionGetString(POSITION_SYMBOL)==_Symbol){ //--- Check if the position matches the symbol
                     ENUM_POSITION_TYPE pos_type = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE); //--- Get the position type
                     if (pos_type == POSITION_TYPE_SELL){ //--- Check if the position is a sell position
                        obj_Trade.PositionClose(pos_ticket); //--- Close the sell position
                     }
                  }
               }
            }
         }
      }

Dies ist ein ähnlicher Ansatz wie der vorherige, den wir zur Schließung aller Positionen verwendet haben. Daher haben wir den zusätzlichen Abschnitt hervorgehoben, damit wir uns auf die zusätzliche Logik konzentrieren können, die verwendet wird, um nur Verkaufspositionen zu schließen, wenn die Schaltfläche „Alle Verkäufe schließen“ angeklickt wird. Nach dem Durchlaufen aller Positionen wird der Positionstyp für jede Position mit der Funktion PositionGetInteger und der Konstante POSITION_TYPE abgefragt. Dieser Wert wird dann in die Enumeration ENUM_POSITION_TYPE umgewandelt, um ihn verständlicher zu machen, ein Prozess, der als Typecasting bezeichnet wird. Sobald wir die Positionsart kennen, prüfen wir speziell, ob die aktuelle Position eine Verkaufsposition ist, indem wir sie mit der Eigenschaft POSITION_TYPE_SELL vergleichen. Wenn die Bedingung wahr ist, d.h. die Position tatsächlich verkauft wurde, wird sie mit der Funktion „PositionClose“ des Objekts „obj_Trade“ geschlossen. Dasselbe tun wir, wenn wir die Kaufpositionen schließen.

      else if (sparam==obj_Btn_CLOSE_ALL_BUY.Name()){ //--- Check if the Close All Buy button is clicked
         Print("OBJECT CLICKED = ",obj_Btn_CLOSE_ALL_BUY.Name()); //--- Log the button click event
         
         for (int i = PositionsTotal() -1; i >= 0; i--){ //--- Loop through all positions
            ulong pos_ticket = PositionGetTicket(i); //--- Get the ticket of the position
            if (pos_ticket > 0){ //--- Check if the position ticket is valid
               if (PositionSelectByTicket(pos_ticket)){ //--- Select the position by ticket
                  if (PositionGetString(POSITION_SYMBOL)==_Symbol){ //--- Check if the position matches the symbol
                     ENUM_POSITION_TYPE pos_type = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE); //--- Get the position type
                     if (pos_type == POSITION_TYPE_BUY){ //--- Check if the position is a buy position
                        obj_Trade.PositionClose(pos_ticket); //--- Close the buy position
                     }
                  }
               }
            }
         }
      }

Wenn wir alle Verkaufspositionen schließen, die einen Verlust aufweisen, müssen wir den derzeitigen Ansatz noch erweitern, um den Gewinn der ausgewählten Position zu berücksichtigen, den wir dann zum Vergleich heranziehen können, um festzustellen, ob es sich um Gewinner oder Verlierer handelt. Hier ist die Logik.

      else if (sparam==obj_Btn_CLOSE_LOSS_SELL.Name()){ //--- Check if the Close Loss Sell button is clicked
         Print("OBJECT CLICKED = ",obj_Btn_CLOSE_LOSS_SELL.Name()); //--- Log the button click event
         
         for (int i = PositionsTotal() -1; i >= 0; i--){ //--- Loop through all positions
            ulong pos_ticket = PositionGetTicket(i); //--- Get the ticket of the position
            if (pos_ticket > 0){ //--- Check if the position ticket is valid
               if (PositionSelectByTicket(pos_ticket)){ //--- Select the position by ticket
                  if (PositionGetString(POSITION_SYMBOL)==_Symbol){ //--- Check if the position matches the symbol
                     ENUM_POSITION_TYPE pos_type = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE); //--- Get the position type
                     if (pos_type == POSITION_TYPE_SELL){ //--- Check if the position is a sell position
                        double profit_loss = PositionGetDouble(POSITION_PROFIT); //--- Get the profit/loss of the position
                        if (profit_loss < 0){ //--- Check if the position is at a loss
                           obj_Trade.PositionClose(pos_ticket); //--- Close the losing sell position
                        }
                     }
                  }
               }
            }
         }
      }

Nachdem wir in einer Schleife alle Positionen durchlaufen und sichergestellt haben, dass die aktuelle Position vom Typ Verkauf ist, führen wir eine Überprüfung des Gewinns/Verlusts jeder Position ein. Die Funktion PositionGetDouble ruft den aktuellen Gewinn/Verlust der ausgewählten Position ab, wobei die Konstante POSITION_PROFIT verwendet wird, um festzustellen, ob sich die Position im Gewinn oder Verlust befindet. Wir fügen dann eine bedingte Anweisung hinzu, um zu prüfen, ob der Gewinn/Verlust-Wert kleiner als Null ist, was bedeuten würde, dass die Position im Verlust ist. Wenn diese Bedingung erfüllt ist, schließen wir die Verlustposition, indem wir die Funktion „PositionClose“ aufrufen. Diese zusätzliche Logik, die wir gelb hervorgehoben haben, sorgt dafür, dass nur Verkaufspositionen geschlossen werden, die derzeit einen Verlust aufweisen. Für die anderen Schaltflächen wird ein ähnlicher Ansatz verfolgt.

      else if (sparam==obj_Btn_CLOSE_LOSS_BUY.Name()){ //--- Check if the Close Loss Buy button is clicked
         Print("OBJECT CLICKED = ",obj_Btn_CLOSE_LOSS_BUY.Name()); //--- Log the button click event
         
         for (int i = PositionsTotal() -1; i >= 0; i--){ //--- Loop through all positions
            ulong pos_ticket = PositionGetTicket(i); //--- Get the ticket of the position
            if (pos_ticket > 0){ //--- Check if the position ticket is valid
               if (PositionSelectByTicket(pos_ticket)){ //--- Select the position by ticket
                  if (PositionGetString(POSITION_SYMBOL)==_Symbol){ //--- Check if the position matches the symbol
                     ENUM_POSITION_TYPE pos_type = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE); //--- Get the position type
                     if (pos_type == POSITION_TYPE_BUY){ //--- Check if the position is a buy position
                        double profit_loss = PositionGetDouble(POSITION_PROFIT); //--- Get the profit/loss of the position
                        if (profit_loss < 0){ //--- Check if the position is at a loss
                           obj_Trade.PositionClose(pos_ticket); //--- Close the losing buy position
                        }
                     }
                  }
               }
            }
         }
      }
      
      else if (sparam==obj_Btn_CLOSE_PROFIT_SELL.Name()){ //--- Check if the Close Profit Sell button is clicked
         Print("OBJECT CLICKED = ",obj_Btn_CLOSE_PROFIT_SELL.Name()); //--- Log the button click event
         
         for (int i = PositionsTotal() -1; i >= 0; i--){ //--- Loop through all positions
            ulong pos_ticket = PositionGetTicket(i); //--- Get the ticket of the position
            if (pos_ticket > 0){ //--- Check if the position ticket is valid
               if (PositionSelectByTicket(pos_ticket)){ //--- Select the position by ticket
                  if (PositionGetString(POSITION_SYMBOL)==_Symbol){ //--- Check if the position matches the symbol
                     ENUM_POSITION_TYPE pos_type = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE); //--- Get the position type
                     if (pos_type == POSITION_TYPE_SELL){ //--- Check if the position is a sell position
                        double profit_loss = PositionGetDouble(POSITION_PROFIT); //--- Get the profit/loss of the position
                        if (profit_loss >= 0){ //--- Check if the position is profitable
                           obj_Trade.PositionClose(pos_ticket); //--- Close the profitable sell position
                        }
                     }
                  }
               }
            }
         }
      }

      else if (sparam==obj_Btn_CLOSE_PROFIT_BUY.Name()){ //--- Check if the Close Profit Buy button is clicked
         Print("OBJECT CLICKED = ",obj_Btn_CLOSE_PROFIT_BUY.Name()); //--- Log the button click event
         
         for (int i = PositionsTotal() -1; i >= 0; i--){ //--- Loop through all positions
            ulong pos_ticket = PositionGetTicket(i); //--- Get the ticket of the position
            if (pos_ticket > 0){ //--- Check if the position ticket is valid
               if (PositionSelectByTicket(pos_ticket)){ //--- Select the position by ticket
                  if (PositionGetString(POSITION_SYMBOL)==_Symbol){ //--- Check if the position matches the symbol
                     ENUM_POSITION_TYPE pos_type = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE); //--- Get the position type
                     if (pos_type == POSITION_TYPE_BUY){ //--- Check if the position is a buy position
                        double profit_loss = PositionGetDouble(POSITION_PROFIT); //--- Get the profit/loss of the position
                        if (profit_loss >= 0){ //--- Check if the position is profitable
                           obj_Trade.PositionClose(pos_ticket); //--- Close the profitable buy position
                        }
                     }
                  }
               }
            }
         }
      }      
      else if (sparam==obj_Btn_CLOSE_ALL_LOSS.Name()){ //--- Check if the Close All Loss button is clicked
         Print("OBJECT CLICKED = ",obj_Btn_CLOSE_ALL_LOSS.Name()); //--- Log the button click event
         
         for (int i = PositionsTotal() -1; i >= 0; i--){ //--- Loop through all positions
            ulong pos_ticket = PositionGetTicket(i); //--- Get the ticket of the position
            if (pos_ticket > 0){ //--- Check if the position ticket is valid
               if (PositionSelectByTicket(pos_ticket)){ //--- Select the position by ticket
                  if (PositionGetString(POSITION_SYMBOL)==_Symbol){ //--- Check if the position matches the symbol
                     double profit_loss = PositionGetDouble(POSITION_PROFIT); //--- Get the profit/loss of the position
                     if (profit_loss < 0){ //--- Check if the position is at a loss
                        obj_Trade.PositionClose(pos_ticket); //--- Close the losing position
                     }
                  }
               }
            }
         }
      }            
      else if (sparam==obj_Btn_CLOSE_ALL_PROFIT.Name()){ //--- Check if the Close All Profit button is clicked
         Print("OBJECT CLICKED = ",obj_Btn_CLOSE_ALL_PROFIT.Name()); //--- Log the button click event
         
         for (int i = PositionsTotal() -1; i >= 0; i--){ //--- Loop through all positions
            ulong pos_ticket = PositionGetTicket(i); //--- Get the ticket of the position
            if (pos_ticket > 0){ //--- Check if the position ticket is valid
               if (PositionSelectByTicket(pos_ticket)){ //--- Select the position by ticket
                  if (PositionGetString(POSITION_SYMBOL)==_Symbol){ //--- Check if the position matches the symbol
                     double profit_loss = PositionGetDouble(POSITION_PROFIT); //--- Get the profit/loss of the position
                     if (profit_loss >= 0){ //--- Check if the position is profitable
                        obj_Trade.PositionClose(pos_ticket); //--- Close the profitable position
                     }
                  }
               }
            }
         }
      }            

Nachdem wir mit der Logik der Positionen fertig sind, können wir nun zur Logik der schwebenden Aufträge übergehen. Hier löschen wir alle ausstehenden Aufträge. Dies wird durch die folgende Logik erreicht.

      else if (sparam==obj_Btn_CLOSE_PENDING.Name()){ //--- Check if the Close Pending button is clicked
         Print("OBJECT CLICKED = ",obj_Btn_CLOSE_PENDING.Name()); //--- Log the button click event
         
         for (int i = OrdersTotal() -1; i >= 0; i--){ //--- Loop through all pending orders
            ulong order_ticket = OrderGetTicket(i); //--- Get the ticket of the order
            if (order_ticket > 0){ //--- Check if the order ticket is valid
               if (OrderSelect(order_ticket)){ //--- Select the order by ticket
                  if (OrderGetString(ORDER_SYMBOL)==_Symbol){ //--- Check if the order matches the symbol
                     obj_Trade.OrderDelete(order_ticket); //--- Delete the pending order
                  }
               }
            }
         }
      }

Hier konzentrieren wir uns auf die Funktionalität des Schließens schwebender Aufträge, wenn die Schaltfläche „Close Pending“ (schwebende Aufträge schließen) angeklickt wird. Zunächst wird geprüft, ob der String-Parameter mit dem Namen der Schaltfläche Ausstehend schließen übereinstimmt, was bedeutet, dass die Schaltfläche aktiviert wurde. Wenn der Klick auf die Schaltfläche bestätigt wird, protokollieren wir die Aktion zu Debugging-Zwecken mit der Funktion Print, die den Namen der angeklickten Schaltfläche ausgibt. Als Nächstes wird eine Schleife eingefügt, die alle ausstehenden Aufträge durchläuft, indem die Funktion OrdersTotal verwendet wird, um die Gesamtzahl der Aufträge zu ermitteln, und der Index in umgekehrter Reihenfolge dekrementiert wird, um zu vermeiden, dass beim Löschen Aufträge übersprungen werden. Für jede Order wird die Ticketnummer der Order mit der Funktion OrderGetTicket abgefragt. Anschließend wird geprüft, ob das Ticket gültig ist, indem sichergestellt wird, dass der Wert größer als Null ist. Wenn es gültig ist, wird der Auftrag mit der Funktion OrderSelect ausgewählt.

Sobald der Auftrag ausgewählt ist, wird überprüft, ob er dem in _Symbol angegebenen Symbol entspricht, indem die Funktion OrderGetString mit der Konstante „ORDER_SYMBOL“ aufgerufen wird. Wenn die Bestellung übereinstimmt, rufen wir die Funktion OrderDelete auf, um die mit dem abgerufenen Ticket verbundene ausstehende Bestellung zu löschen. Dieser gesamte Prozess ermöglicht die effiziente Schließung aller ausstehenden Aufträge, die mit dem angegebenen Handelssymbol verbunden sind, sobald die Schaltfläche aktiviert wird. So wird sichergestellt, dass der Nutzer seine Aufträge effektiv verwalten kann. Danach aktualisieren wir das Chart mit der Funktion ChartRedraw (siehe unten), um sicherzustellen, dass die Änderungen im Chart wirksam werden.

   ChartRedraw(0);

Das ist alles, was wir brauchen, um das Panel zu automatisieren. Wenn wir das Programm ausführen und die Schließ-Schnittstelle testen, erhalten wir die folgende Ausgabe.

SCHLIEß-SCHNITTSTELLE

Die Logik der Ereignisbehandlung von OnChartEvent, die wir in das System integriert haben, um Chart-Klickereignisse zu behandeln, ist wie folgt:

//+------------------------------------------------------------------+
//| Handling chart events                                            |
//+------------------------------------------------------------------+
void OnChartEvent(
   const int       id,       // Event ID indicating the type of event (e.g., mouse click, timer, etc.)
   const long&     lparam,   // Long type parameter associated with the event, usually containing data like mouse coordinates or object IDs
   const double&   dparam,   // Double type parameter associated with the event, used for floating-point values related to the event
   const string&   sparam    // String type parameter associated with the event, typically the name of the object that triggered the event
){
   // Print the 4 function parameters    
   //Print("ID = ",id,", LPARAM = ",lparam,", DPARAM = ",dparam,", SPARAM = ",sparam);

   // Check if the event is a click on a chart object
   if (id == CHARTEVENT_OBJECT_CLICK){
      //Print("ID = ",id,", LM = ",lparam,", DM = ",dparam,", SPARAM = ",sparam);
      // Check if the clicked object is the Trade button
      if (sparam==obj_Btn_TRADE.Name()){
         // Print to the log which object was clicked for debugging purposes
         Print("OBJECT CLICKED = ", obj_Btn_TRADE.Name());

         // Reset the pressed states of all buttons to ensure only one button appears pressed at a time
         obj_Btn_TRADE.Pressed(false);
         obj_Btn_CLOSE.Pressed(false);
         obj_Btn_INFO.Pressed(false);

         // Change the background color of the Trade button to yellow to indicate it is active
         obj_Btn_TRADE.ColorBackground(clrYellow);
         // Set the background color of the Close and Info buttons to silver to indicate they are inactive
         obj_Btn_CLOSE.ColorBackground(clrSilver);
         obj_Btn_INFO.ColorBackground(clrSilver);

         // Set the border color of the Trade button to yellow to match its background
         obj_Btn_TRADE.ColorBorder(clrYellow);
         // Set the border color of the Close and Info buttons to silver to indicate they are inactive
         obj_Btn_CLOSE.ColorBorder(clrSilver);
         obj_Btn_INFO.ColorBorder(clrSilver);

         // Call a function to destroy the Close section if it exists
         destroySection_Close();
         // Call a function to destroy the Information section if it exists
         destroySection_Information();

         // Create the Trade section, bringing it to the forefront
         createSection_Trade();
      }
      // Check if the clicked object is the Close button
      else if (sparam==obj_Btn_CLOSE.Name()){
         // Print to the log which object was clicked for debugging purposes
         Print("OBJECT CLICKED = ", obj_Btn_CLOSE.Name());

         // Reset the pressed states of all buttons
         obj_Btn_TRADE.Pressed(false);
         obj_Btn_CLOSE.Pressed(false);
         obj_Btn_INFO.Pressed(false);

         // Set the background color of the Trade button to silver, indicating it's inactive
         obj_Btn_TRADE.ColorBackground(clrSilver);
         // Change the background color of the Close button to yellow to indicate it is active
         obj_Btn_CLOSE.ColorBackground(clrYellow);
         obj_Btn_INFO.ColorBackground(clrSilver);

         // Set the border color of the Trade button to silver
         obj_Btn_TRADE.ColorBorder(clrSilver);
         // Set the border color of the Close button to yellow
         obj_Btn_CLOSE.ColorBorder(clrYellow);
         obj_Btn_INFO.ColorBorder(clrSilver);

         // Call a function to destroy the Trade section if it exists
         destroySection_Trade();
         // Call a function to destroy the Information section if it exists
         destroySection_Information();

         // Create the Close section, bringing it to the forefront
         createSection_Close();
      }
      // Check if the clicked object is the Information button
      else if (sparam==obj_Btn_INFO.Name()){
         // Print to the log which object was clicked for debugging purposes
         Print("OBJECT CLICKED = ", obj_Btn_INFO.Name());

         // Reset the pressed states of all buttons
         obj_Btn_TRADE.Pressed(false);
         obj_Btn_CLOSE.Pressed(false);
         obj_Btn_INFO.Pressed(false);

         // Set the background color of the Trade and Close buttons to silver, indicating they are inactive
         obj_Btn_TRADE.ColorBackground(clrSilver);
         obj_Btn_CLOSE.ColorBackground(clrSilver);
         // Change the background color of the Info button to yellow to indicate it is active
         obj_Btn_INFO.ColorBackground(clrYellow);

         // Set the border color of the Trade and Close buttons to silver
         obj_Btn_TRADE.ColorBorder(clrSilver);
         obj_Btn_CLOSE.ColorBorder(clrSilver);
         // Set the border color of the Info button to yellow
         obj_Btn_INFO.ColorBorder(clrYellow);

         // Call a function to destroy the Trade section if it exists
         destroySection_Trade();
         // Call a function to destroy the Close section if it exists
         destroySection_Close();

         // Create the Information section, bringing it to the forefront
         createSection_Information();
      }
      // Check if the clicked object is the exit button (X button)
      else if (sparam==obj_Btn_X.Name()){
         // Print to the log which object was clicked for debugging purposes
         Print("OBJECT CLICKED = ", obj_Btn_X.Name());

         // Call functions to destroy all sections, effectively closing the entire panel
         destroySection_Trade();
         destroySection_Close();
         destroySection_Information();

         // Call a function to destroy the main panel itself
         destroySection_Main_Panel();
      }
      else if (sparam==obj_Btn_SELL.Name()){ //--- Check if the Sell button is clicked
         Print("OBJECT CLICKED = ",obj_Btn_SELL.Name()); //--- Log the button click event
         
         double Ask = NormalizeDouble(SymbolInfoDouble(_Symbol,SYMBOL_ASK),_Digits); //--- Get and normalize the ask price
         double Bid = NormalizeDouble(SymbolInfoDouble(_Symbol,SYMBOL_BID),_Digits); //--- Get and normalize the bid price
         
         double lots = StringToDouble(obj_Edit_LOTS.Text()); //--- Get the lot size from input field
         double entry_price = Bid; //--- Set the entry price for selling to the current bid price
         double stopLoss = Ask+StringToDouble(obj_Edit_SL.Text())*_Point; //--- Calculate stop loss based on user input
         double takeprofit = Ask-StringToDouble(obj_Edit_TP.Text())*_Point; //--- Calculate take profit based on user input
         
         Print("Lots = ",lots,", Entry = ",entry_price,", SL = ",stopLoss,", TP = ",takeprofit); //--- Log order details
         obj_Trade.Sell(lots,_Symbol,entry_price,stopLoss,takeprofit); //--- Execute the sell order
      }
      else if (sparam==obj_Btn_BUY.Name()){ //--- Check if the Buy button is clicked
         Print("OBJECT CLICKED = ",obj_Btn_BUY.Name()); //--- Log the button click event
         
         double Ask = NormalizeDouble(SymbolInfoDouble(_Symbol,SYMBOL_ASK),_Digits); //--- Get and normalize the ask price
         double Bid = NormalizeDouble(SymbolInfoDouble(_Symbol,SYMBOL_BID),_Digits); //--- Get and normalize the bid price
         
         double lots = StringToDouble(obj_Edit_LOTS.Text()); //--- Get the lot size from input field
         double entry_price = Ask; //--- Set the entry price for buying to the current ask price
         double stopLoss = Bid-StringToDouble(obj_Edit_SL.Text())*_Point; //--- Calculate stop loss based on user input
         double takeprofit = Bid+StringToDouble(obj_Edit_TP.Text())*_Point; //--- Calculate take profit based on user input
         
         Print("Lots = ",lots,", Entry = ",entry_price,", SL = ",stopLoss,", TP = ",takeprofit); //--- Log order details
         obj_Trade.Buy(lots,_Symbol,entry_price,stopLoss,takeprofit); //--- Execute the buy order
      }
      else if (sparam==obj_Btn_SELLSTOP.Name()){ //--- Check if the Sell Stop button is clicked
         Print("OBJECT CLICKED = ",obj_Btn_SELLSTOP.Name()); //--- Log the button click event
         
         double Ask = NormalizeDouble(SymbolInfoDouble(_Symbol,SYMBOL_ASK),_Digits); //--- Get and normalize the ask price
         double Bid = NormalizeDouble(SymbolInfoDouble(_Symbol,SYMBOL_BID),_Digits); //--- Get and normalize the bid price
         
         double user_price = StringToDouble(obj_Edit_PRICE.Text()); //--- Get the user-defined price from input field
         long stopslevel = SymbolInfoInteger(_Symbol,SYMBOL_TRADE_STOPS_LEVEL); //--- Get the minimum stop level for the symbol
         double valid_price = Bid - stopslevel*_Point; //--- Calculate the valid price for placing a sell stop order
         
         if (user_price > valid_price){ //--- Check if the user-defined price is valid
            Print("ERROR: INVALID STOPS PRICE. ",user_price," > ",valid_price); //--- Log an error message
         }
         else if (user_price <= valid_price){ //--- If the user-defined price is valid
            double lots = StringToDouble(obj_Edit_LOTS.Text()); //--- Get the lot size from input field
            double entry_price = user_price; //--- Set the entry price for the sell stop order
            double stopLoss = user_price+StringToDouble(obj_Edit_SL.Text())*_Point; //--- Calculate stop loss based on user input
            double takeprofit = user_price-StringToDouble(obj_Edit_TP.Text())*_Point; //--- Calculate take profit based on user input
            
            Print("Lots = ",lots,", Entry = ",entry_price,", SL = ",stopLoss,", TP = ",takeprofit); //--- Log order details
            obj_Trade.SellStop(lots,entry_price,_Symbol,stopLoss,takeprofit); //--- Execute the sell stop order
         }
      }
      else if (sparam==obj_Btn_BUYSTOP.Name()){ //--- Check if the Buy Stop button is clicked
         Print("OBJECT CLICKED = ",obj_Btn_BUYSTOP.Name()); //--- Log the button click event
         
         double Ask = NormalizeDouble(SymbolInfoDouble(_Symbol,SYMBOL_ASK),_Digits); //--- Get and normalize the ask price
         double Bid = NormalizeDouble(SymbolInfoDouble(_Symbol,SYMBOL_BID),_Digits); //--- Get and normalize the bid price
         
         double user_price = StringToDouble(obj_Edit_PRICE.Text()); //--- Get the user-defined price from input field
         long stopslevel = SymbolInfoInteger(_Symbol,SYMBOL_TRADE_STOPS_LEVEL); //--- Get the minimum stop level for the symbol
         double valid_price = Ask + stopslevel*_Point; //--- Calculate the valid price for placing a buy stop order
         
         if (user_price < valid_price){ //--- Check if the user-defined price is valid
            Print("ERROR: INVALID STOPS PRICE. ",user_price," < ",valid_price); //--- Log an error message
         }
         else if (user_price >= valid_price){ //--- If the user-defined price is valid
            double lots = StringToDouble(obj_Edit_LOTS.Text()); //--- Get the lot size from input field
            double entry_price = user_price; //--- Set the entry price for the buy stop order
            double stopLoss = user_price-StringToDouble(obj_Edit_SL.Text())*_Point; //--- Calculate stop loss based on user input
            double takeprofit = user_price+StringToDouble(obj_Edit_TP.Text())*_Point; //--- Calculate take profit based on user input
            
            Print("Lots = ",lots,", Entry = ",entry_price,", SL = ",stopLoss,", TP = ",takeprofit); //--- Log order details
            obj_Trade.BuyStop(lots,entry_price,_Symbol,stopLoss,takeprofit); //--- Execute the buy stop order
         }
      }
      else if (sparam==obj_Btn_SELLLIMIT.Name()){ //--- Check if the Sell Limit button is clicked
         Print("OBJECT CLICKED = ",obj_Btn_SELLLIMIT.Name()); //--- Log the button click event
         
         double Ask = NormalizeDouble(SymbolInfoDouble(_Symbol,SYMBOL_ASK),_Digits); //--- Get and normalize the ask price
         double Bid = NormalizeDouble(SymbolInfoDouble(_Symbol,SYMBOL_BID),_Digits); //--- Get and normalize the bid price
         
         double user_price = StringToDouble(obj_Edit_PRICE.Text()); //--- Get the user-defined price from input field
         long stopslevel = SymbolInfoInteger(_Symbol,SYMBOL_TRADE_STOPS_LEVEL); //--- Get the minimum stop level for the symbol
         double valid_price = Bid + stopslevel*_Point; //--- Calculate the valid price for placing a sell limit order
         
         if (user_price < valid_price){ //--- Check if the user-defined price is valid
            Print("ERROR: INVALID STOPS PRICE. ",user_price," < ",valid_price); //--- Log an error message
         }
         else if (user_price >= valid_price){ //--- If the user-defined price is valid
            double lots = StringToDouble(obj_Edit_LOTS.Text()); //--- Get the lot size from input field
            double entry_price = user_price; //--- Set the entry price for the sell limit order
            double stopLoss = user_price+StringToDouble(obj_Edit_SL.Text())*_Point; //--- Calculate stop loss based on user input
            double takeprofit = user_price-StringToDouble(obj_Edit_TP.Text())*_Point; //--- Calculate take profit based on user input
            
            Print("Lots = ",lots,", Entry = ",entry_price,", SL = ",stopLoss,", TP = ",takeprofit); //--- Log order details
            obj_Trade.SellLimit(lots,entry_price,_Symbol,stopLoss,takeprofit); //--- Execute the sell limit order
         }
      }
      else if (sparam==obj_Btn_BUYLIMIT.Name()){ //--- Check if the Buy Limit button is clicked
         Print("OBJECT CLICKED = ",obj_Btn_BUYLIMIT.Name()); //--- Log the button click event
         
         double Ask = NormalizeDouble(SymbolInfoDouble(_Symbol,SYMBOL_ASK),_Digits); //--- Get and normalize the ask price
         double Bid = NormalizeDouble(SymbolInfoDouble(_Symbol,SYMBOL_BID),_Digits); //--- Get and normalize the bid price
         
         double user_price = StringToDouble(obj_Edit_PRICE.Text()); //--- Get the user-defined price from input field
         long stopslevel = SymbolInfoInteger(_Symbol,SYMBOL_TRADE_STOPS_LEVEL); //--- Get the minimum stop level for the symbol
         double valid_price = Ask - stopslevel*_Point; //--- Calculate the valid price for placing a buy limit order
         
         if (user_price > valid_price){ //--- Check if the user-defined price is valid
            Print("ERROR: INVALID STOPS PRICE. ",user_price," > ",valid_price); //--- Log an error message
         }
         else if (user_price <= valid_price){ //--- If the user-defined price is valid
            double lots = StringToDouble(obj_Edit_LOTS.Text()); //--- Get the lot size from input field
            double entry_price = user_price; //--- Set the entry price for the buy limit order
            double stopLoss = user_price-StringToDouble(obj_Edit_SL.Text())*_Point; //--- Calculate stop loss based on user input
            double takeprofit = user_price+StringToDouble(obj_Edit_TP.Text())*_Point; //--- Calculate take profit based on user input
            
            Print("Lots = ",lots,", Entry = ",entry_price,", SL = ",stopLoss,", TP = ",takeprofit); //--- Log order details
            obj_Trade.BuyLimit(lots,entry_price,_Symbol,stopLoss,takeprofit); //--- Execute the buy limit order
         }
      }

      
      else if (sparam==obj_Btn_CLOSE_ALL.Name()){ //--- Check if the Close All button is clicked
         Print("OBJECT CLICKED = ",obj_Btn_CLOSE_ALL.Name()); //--- Log the button click event
         
         for (int i = PositionsTotal() -1; i >= 0; i--){ //--- Loop through all positions
            ulong pos_ticket = PositionGetTicket(i); //--- Get the ticket of the position
            if (pos_ticket > 0){ //--- Check if the position ticket is valid
               if (PositionSelectByTicket(pos_ticket)){ //--- Select the position by ticket
                  if (PositionGetString(POSITION_SYMBOL)==_Symbol){ //--- Check if the position matches the symbol
                     obj_Trade.PositionClose(pos_ticket); //--- Close the position
                  }
               }
            }
         }
      }
      else if (sparam==obj_Btn_CLOSE_ALL_SELL.Name()){ //--- Check if the Close All Sell button is clicked
         Print("OBJECT CLICKED = ",obj_Btn_CLOSE_ALL_SELL.Name()); //--- Log the button click event
         
         for (int i = PositionsTotal() -1; i >= 0; i--){ //--- Loop through all positions
            ulong pos_ticket = PositionGetTicket(i); //--- Get the ticket of the position
            if (pos_ticket > 0){ //--- Check if the position ticket is valid
               if (PositionSelectByTicket(pos_ticket)){ //--- Select the position by ticket
                  if (PositionGetString(POSITION_SYMBOL)==_Symbol){ //--- Check if the position matches the symbol
                     ENUM_POSITION_TYPE pos_type = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE); //--- Get the position type
                     if (pos_type == POSITION_TYPE_SELL){ //--- Check if the position is a sell position
                        obj_Trade.PositionClose(pos_ticket); //--- Close the sell position
                     }
                  }
               }
            }
         }
      }
      else if (sparam==obj_Btn_CLOSE_ALL_BUY.Name()){ //--- Check if the Close All Buy button is clicked
         Print("OBJECT CLICKED = ",obj_Btn_CLOSE_ALL_BUY.Name()); //--- Log the button click event
         
         for (int i = PositionsTotal() -1; i >= 0; i--){ //--- Loop through all positions
            ulong pos_ticket = PositionGetTicket(i); //--- Get the ticket of the position
            if (pos_ticket > 0){ //--- Check if the position ticket is valid
               if (PositionSelectByTicket(pos_ticket)){ //--- Select the position by ticket
                  if (PositionGetString(POSITION_SYMBOL)==_Symbol){ //--- Check if the position matches the symbol
                     ENUM_POSITION_TYPE pos_type = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE); //--- Get the position type
                     if (pos_type == POSITION_TYPE_BUY){ //--- Check if the position is a buy position
                        obj_Trade.PositionClose(pos_ticket); //--- Close the buy position
                     }
                  }
               }
            }
         }
      }
      else if (sparam==obj_Btn_CLOSE_LOSS_SELL.Name()){ //--- Check if the Close Loss Sell button is clicked
         Print("OBJECT CLICKED = ",obj_Btn_CLOSE_LOSS_SELL.Name()); //--- Log the button click event
         
         for (int i = PositionsTotal() -1; i >= 0; i--){ //--- Loop through all positions
            ulong pos_ticket = PositionGetTicket(i); //--- Get the ticket of the position
            if (pos_ticket > 0){ //--- Check if the position ticket is valid
               if (PositionSelectByTicket(pos_ticket)){ //--- Select the position by ticket
                  if (PositionGetString(POSITION_SYMBOL)==_Symbol){ //--- Check if the position matches the symbol
                     ENUM_POSITION_TYPE pos_type = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE); //--- Get the position type
                     if (pos_type == POSITION_TYPE_SELL){ //--- Check if the position is a sell position
                        double profit_loss = PositionGetDouble(POSITION_PROFIT); //--- Get the profit/loss of the position
                        if (profit_loss < 0){ //--- Check if the position is at a loss
                           obj_Trade.PositionClose(pos_ticket); //--- Close the losing sell position
                        }
                     }
                  }
               }
            }
         }
      }
      else if (sparam==obj_Btn_CLOSE_LOSS_BUY.Name()){ //--- Check if the Close Loss Buy button is clicked
         Print("OBJECT CLICKED = ",obj_Btn_CLOSE_LOSS_BUY.Name()); //--- Log the button click event
         
         for (int i = PositionsTotal() -1; i >= 0; i--){ //--- Loop through all positions
            ulong pos_ticket = PositionGetTicket(i); //--- Get the ticket of the position
            if (pos_ticket > 0){ //--- Check if the position ticket is valid
               if (PositionSelectByTicket(pos_ticket)){ //--- Select the position by ticket
                  if (PositionGetString(POSITION_SYMBOL)==_Symbol){ //--- Check if the position matches the symbol
                     ENUM_POSITION_TYPE pos_type = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE); //--- Get the position type
                     if (pos_type == POSITION_TYPE_BUY){ //--- Check if the position is a buy position
                        double profit_loss = PositionGetDouble(POSITION_PROFIT); //--- Get the profit/loss of the position
                        if (profit_loss < 0){ //--- Check if the position is at a loss
                           obj_Trade.PositionClose(pos_ticket); //--- Close the losing buy position
                        }
                     }
                  }
               }
            }
         }
      }
      
      else if (sparam==obj_Btn_CLOSE_PROFIT_SELL.Name()){ //--- Check if the Close Profit Sell button is clicked
         Print("OBJECT CLICKED = ",obj_Btn_CLOSE_PROFIT_SELL.Name()); //--- Log the button click event
         
         for (int i = PositionsTotal() -1; i >= 0; i--){ //--- Loop through all positions
            ulong pos_ticket = PositionGetTicket(i); //--- Get the ticket of the position
            if (pos_ticket > 0){ //--- Check if the position ticket is valid
               if (PositionSelectByTicket(pos_ticket)){ //--- Select the position by ticket
                  if (PositionGetString(POSITION_SYMBOL)==_Symbol){ //--- Check if the position matches the symbol
                     ENUM_POSITION_TYPE pos_type = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE); //--- Get the position type
                     if (pos_type == POSITION_TYPE_SELL){ //--- Check if the position is a sell position
                        double profit_loss = PositionGetDouble(POSITION_PROFIT); //--- Get the profit/loss of the position
                        if (profit_loss >= 0){ //--- Check if the position is profitable
                           obj_Trade.PositionClose(pos_ticket); //--- Close the profitable sell position
                        }
                     }
                  }
               }
            }
         }
      }

      else if (sparam==obj_Btn_CLOSE_PROFIT_BUY.Name()){ //--- Check if the Close Profit Buy button is clicked
         Print("OBJECT CLICKED = ",obj_Btn_CLOSE_PROFIT_BUY.Name()); //--- Log the button click event
         
         for (int i = PositionsTotal() -1; i >= 0; i--){ //--- Loop through all positions
            ulong pos_ticket = PositionGetTicket(i); //--- Get the ticket of the position
            if (pos_ticket > 0){ //--- Check if the position ticket is valid
               if (PositionSelectByTicket(pos_ticket)){ //--- Select the position by ticket
                  if (PositionGetString(POSITION_SYMBOL)==_Symbol){ //--- Check if the position matches the symbol
                     ENUM_POSITION_TYPE pos_type = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE); //--- Get the position type
                     if (pos_type == POSITION_TYPE_BUY){ //--- Check if the position is a buy position
                        double profit_loss = PositionGetDouble(POSITION_PROFIT); //--- Get the profit/loss of the position
                        if (profit_loss >= 0){ //--- Check if the position is profitable
                           obj_Trade.PositionClose(pos_ticket); //--- Close the profitable buy position
                        }
                     }
                  }
               }
            }
         }
      }      
      else if (sparam==obj_Btn_CLOSE_ALL_LOSS.Name()){ //--- Check if the Close All Loss button is clicked
         Print("OBJECT CLICKED = ",obj_Btn_CLOSE_ALL_LOSS.Name()); //--- Log the button click event
         
         for (int i = PositionsTotal() -1; i >= 0; i--){ //--- Loop through all positions
            ulong pos_ticket = PositionGetTicket(i); //--- Get the ticket of the position
            if (pos_ticket > 0){ //--- Check if the position ticket is valid
               if (PositionSelectByTicket(pos_ticket)){ //--- Select the position by ticket
                  if (PositionGetString(POSITION_SYMBOL)==_Symbol){ //--- Check if the position matches the symbol
                     double profit_loss = PositionGetDouble(POSITION_PROFIT); //--- Get the profit/loss of the position
                     if (profit_loss < 0){ //--- Check if the position is at a loss
                        obj_Trade.PositionClose(pos_ticket); //--- Close the losing position
                     }
                  }
               }
            }
         }
      }            
      else if (sparam==obj_Btn_CLOSE_ALL_PROFIT.Name()){ //--- Check if the Close All Profit button is clicked
         Print("OBJECT CLICKED = ",obj_Btn_CLOSE_ALL_PROFIT.Name()); //--- Log the button click event
         
         for (int i = PositionsTotal() -1; i >= 0; i--){ //--- Loop through all positions
            ulong pos_ticket = PositionGetTicket(i); //--- Get the ticket of the position
            if (pos_ticket > 0){ //--- Check if the position ticket is valid
               if (PositionSelectByTicket(pos_ticket)){ //--- Select the position by ticket
                  if (PositionGetString(POSITION_SYMBOL)==_Symbol){ //--- Check if the position matches the symbol
                     double profit_loss = PositionGetDouble(POSITION_PROFIT); //--- Get the profit/loss of the position
                     if (profit_loss >= 0){ //--- Check if the position is profitable
                        obj_Trade.PositionClose(pos_ticket); //--- Close the profitable position
                     }
                  }
               }
            }
         }
      }            
      else if (sparam==obj_Btn_CLOSE_PENDING.Name()){ //--- Check if the Close Pending button is clicked
         Print("OBJECT CLICKED = ",obj_Btn_CLOSE_PENDING.Name()); //--- Log the button click event
         
         for (int i = OrdersTotal() -1; i >= 0; i--){ //--- Loop through all pending orders
            ulong order_ticket = OrderGetTicket(i); //--- Get the ticket of the order
            if (order_ticket > 0){ //--- Check if the order ticket is valid
               if (OrderSelect(order_ticket)){ //--- Select the order by ticket
                  if (OrderGetString(ORDER_SYMBOL)==_Symbol){ //--- Check if the order matches the symbol
                     obj_Trade.OrderDelete(order_ticket); //--- Delete the pending order
                  }
               }
            }
         }
      }

      
   }
   
   
   ChartRedraw(0);
   
}

Kurz gesagt, haben wir genau das erreicht.

DARSTELLUNG DES GANZEN PNANELS

Das war fantastisch! Wir haben unser Panel erfolgreich zum Leben erweckt und es vollständig interaktiv und reaktionsfähig gemacht. Sie bietet jetzt Schaltflächenklicks, Live-Datenaktualisierungen und Reaktionsfähigkeit auf aktive Zustände, was die allgemeine Nutzerfreundlichkeit und Funktionalität unserer Handelsoberfläche verbessert.


Schlussfolgerung

Zusammenfassend lässt sich sagen, dass die Verbesserungen, die wir in unser MetaQuotes Language 5 GUI-Panel implementiert haben, dessen Interaktivität und Funktionalität deutlich erhöhen und so ein noch angenehmeres Nutzererlebnis schaffen. Dank der zusätzlichen Live-Datenaktualisierung und der reaktionsschnellen Schaltflächenklicks können wir jetzt nahtlos und intuitiv mit dem Panel interagieren. Diese Funktionen erleichtern nicht nur die Ausführung von Kauf- und Verkaufsaufträgen, sondern stellen auch sicher, dass wir sofortigen Zugang zu Echtzeit-Informationen über unsere Handelskonten haben, sodass wir bei veränderten Marktbedingungen schnelle und fundierte Handelsentscheidungen treffen können.

Darüber hinaus trägt die Automatisierung verschiedener Komponenten, wie z. B. der Positionsverwaltung und der Anzeige von Kontoinformationen, zu einem noch bequemeren und effizienteren Handelsprozess bei. Durch die Möglichkeit, Positionen und Order mit nur einem Klick zu schließen, und die anpassbaren Optionen wird das GUI-Panel zu einem leistungsstarken Werkzeug für moderne Trader. Diese Umgestaltung fördert einen saubereren, besser organisierten Arbeitsplatz, der eine bessere Konzentration und Produktivität ermöglicht. Wir hoffen, dass dieser Artikel wertvolle Einblicke in die Verbesserung der MQL5-GUI-Panels gegeben hat und hoffen, dass Sie die Erklärungen klar und informativ fanden. Viel Spaß beim Handeln!

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

Beigefügte Dateien |
Erstellen eines Handelsadministrator-Panels in MQL5 (Teil V): Zwei-Faktoren-Authentifizierung (2FA) Erstellen eines Handelsadministrator-Panels in MQL5 (Teil V): Zwei-Faktoren-Authentifizierung (2FA)
Heute werden wir uns mit der Verbesserung der Sicherheit für das derzeit in der Entwicklung befindliche Trading Administrator Panel befassen. Wir werden untersuchen, wie MQL5 in eine neue Sicherheitsstrategie implementiert werden kann, indem die Telegram-API für die Zwei-Faktor-Authentifizierung (2FA) verwendet wird. Diese Diskussion wird wertvolle Einblicke in die Anwendung von MQL5 bei der Verstärkung von Sicherheitsmaßnahmen liefern. Darüber hinaus werden wir die Funktion MathRand untersuchen, wobei wir uns auf ihre Funktionalität konzentrieren werden und darauf, wie sie innerhalb unseres Sicherheitsrahmens effektiv genutzt werden kann. Lesen Sie weiter, um mehr zu erfahren!
Connexus-Helfer (Teil 5): HTTP-Methoden und Status Codes Connexus-Helfer (Teil 5): HTTP-Methoden und Status Codes
In diesem Artikel werden wir HTTP-Methoden und Status-Codes erklären, zwei sehr wichtige Elemente der Kommunikation zwischen Client und Server im Internet. Wenn Sie wissen, was die einzelnen Methoden bewirken, können Sie Ihre Anfragen präziser formulieren und dem Server mitteilen, welche Aktion Sie durchführen möchten, um die Effizienz zu steigern.
Anfragen in Connexus (Teil 6): Erstellen einer HTTP-Anfrage und -Antwort Anfragen in Connexus (Teil 6): Erstellen einer HTTP-Anfrage und -Antwort
In diesem sechsten Artikel der Connexus-Bibliotheksreihe befassen wir uns mit einer vollständigen HTTP-Anfrage, wobei jede Komponente, aus der eine Anfrage besteht, behandelt wird. Wir werden eine Klasse erstellen, die den Anfrage als Ganzes repräsentiert, was uns helfen wird, die zuvor erstellten Klassen zusammenzuführen.
Aufbau des Kerzenmodells Trend Constraint (Teil 9): Expert Advisor für mehrere Strategien (II) Aufbau des Kerzenmodells Trend Constraint (Teil 9): Expert Advisor für mehrere Strategien (II)
Die Zahl der Strategien, die in einen Expert Advisor integriert werden können, ist praktisch unbegrenzt. Jede zusätzliche Strategie erhöht jedoch die Komplexität des Algorithmus. Durch die Einbeziehung mehrerer Strategien kann sich ein Expert Advisor besser an unterschiedliche Marktbedingungen anpassen, was seine Rentabilität erhöhen kann. Heute werden wir uns mit der Implementierung von MQL5 für eine der bekannten, von Richard Donchian entwickelten Strategien befassen, da wir die Funktionalität unseres Trend Constraint Expert weiter verbessern wollen.