English
preview
MQL5-Handelswerkzeuge (Teil 1): Aufbau eines interaktiven visuellen Handelsassistenten für schwebende Aufträge

MQL5-Handelswerkzeuge (Teil 1): Aufbau eines interaktiven visuellen Handelsassistenten für schwebende Aufträge

MetaTrader 5Handel |
17 0
Allan Munene Mutiiria
Allan Munene Mutiiria

Einführung

Die Entwicklung effektiver Handelswerkzeuge ist für die Vereinfachung komplexer Aufgaben im Devisenhandel unerlässlich, doch die Schaffung intuitiver Schnittstellen, die die Entscheidungsfindung verbessern, bleibt eine Herausforderung. Wie wäre es, wenn Sie ein visuelles, interaktives Tool entwerfen könnten, das die Platzierung ausstehender Aufträge innerhalb von MetaTrader 5 rationalisiert? In diesem Artikel stellen wir einen nutzerdefinierten MetaQuotes Language 5 (MQL5) Expert Advisor (EA) vor, der Händlern ein Handelsassistenz-Tool zur Verfügung stellt, das grafische Präzision mit nutzerfreundlichen Steuerelementen kombiniert, um Kauf-/Verkaufsstopps und Limit-Orders effizient zu platzieren. Wir gehen diese Schritte in dieser Reihenfolge durch:

  1. Konzeption und Zielsetzung des Trade Assistant Tools
  2. Implementation in MQL5
  3. Backtests
  4. Schlussfolgerung

Am Ende haben Sie ein klares Verständnis davon, wie man dieses Tool aufbaut und testet, was den Weg für fortgeschrittene Erweiterungen in den vorangehenden Teilen ebnet.


Konzeption und Zielsetzung des Trade Assistant Tools

Unser Ziel ist es, ein Handelsassistententool zu entwickeln, das uns eine nahtlose und effiziente Erfahrung bietet, indem es den Prozess der Platzierung von schwebenden Aufträgen im Devisenhandel vereinfacht. Wir stellen uns das Tool als eine graphische Nutzeroberfläche (GUI) vor, die direkt in den MetaTrader 5 integriert ist und es uns ermöglicht, Aufträge wie Buy Stop, Sell Stop, Buy Limit und Sell Limit über ein intuitives Bedienfeld einzurichten. Unser Entwurf soll Schaltflächen zur Auswahl der gewünschten Auftragsart und ein Eingabefeld zur Angabe der Losgröße enthalten. Wir legen Wert auf visuelle Interaktion und ermöglichen es, Einstiegskurse, Stop-Loss (SL) und Take-Profit (TP) durch Ziehen interaktiver Elemente auf dem Chart zu definieren, was ein unmittelbares Feedback zu den Kursniveaus und den Punktunterschieden zwischen ihnen liefert.

Unser Hauptaugenmerk liegt auf der Zugänglichkeit und Reaktionsfähigkeit des Tools. Wir werden die Schnittstelle so gestalten, dass sie reaktionsschnell ist und es uns ermöglicht, die Preise präzise anzupassen und Bestellungen mit einem einzigen Klick zu bestätigen, sodass der Zeitaufwand für die Einrichtung minimiert wird. Außerdem werden wir die Möglichkeit einbauen, die Schnittstelle zu kündigen oder zu schließen, was uns die Flexibilität gibt, uns schnell an veränderte Marktbedingungen anzupassen. Durch die Entwicklung eines optisch ansprechenden und reaktionsschnellen Tools wollen wir unsere Entscheidungsfindung verbessern, Fehler bei der Auftragsvergabe reduzieren und eine Grundlage für künftige Verbesserungen, wie z. B. erweiterte Risikomanagementfunktionen, schaffen, die wir in späteren Iterationen erforschen werden. Kurz gesagt, hier ist eine Visualisierung dessen, was wir zu schaffen gedenken.

GUI PLAN


Implementation in MQL5

Um das Programm in MQL5 zu erstellen, müssen wir die Programm-Metadaten definieren, dann einige Objektnamenskonstanten festlegen und schließlich einige Bibliotheksdateien einfügen, die es uns ermöglichen, die Handelsaktivitäten durchzuführen.

//+------------------------------------------------------------------+
//|                                         TRADE ASSISTANT GUI TOOL |
//|      Copyright 2025, ALLAN MUNENE MUTIIRIA. #@Forex Algo-Trader. |
//|                           https://youtube.com/@ForexAlgo-Trader? |
//+------------------------------------------------------------------+
#property copyright "Copyright 2025, ALLAN MUNENE MUTIIRIA. #@Forex Algo-Trader"
#property link      "https://youtube.com/@ForexAlgo-Trader?"
#property version   "1.00"

#include <Trade/Trade.mqh> //--- Include the Trade library for trading operations

// Control panel object names
#define PANEL_BG       "PANEL_BG"         //--- Define constant for panel background object name
#define LOT_EDIT       "LOT_EDIT"         //--- Define constant for lot size edit field object name
#define PRICE_LABEL    "PRICE_LABEL"      //--- Define constant for price label object name
#define SL_LABEL       "SL_LABEL"         //--- Define constant for stop-loss label object name
#define TP_LABEL       "TP_LABEL"         //--- Define constant for take-profit label object name
#define BUY_STOP_BTN   "BUY_STOP_BTN"     //--- Define constant for buy stop button object name
#define SELL_STOP_BTN  "SELL_STOP_BTN"    //--- Define constant for sell stop button object name
#define BUY_LIMIT_BTN  "BUY_LIMIT_BTN"    //--- Define constant for buy limit button object name
#define SELL_LIMIT_BTN "SELL_LIMIT_BTN"   //--- Define constant for sell limit button object name
#define PLACE_ORDER_BTN "PLACE_ORDER_BTN" //--- Define constant for place order button object name
#define CANCEL_BTN     "CANCEL_BTN"       //--- Define constant for cancel button object name
#define CLOSE_BTN      "CLOSE_BTN"        //--- Define constant for close button object name

#define REC1 "REC1" //--- Define constant for rectangle 1 (TP) object name
#define REC2 "REC2" //--- Define constant for rectangle 2 object name
#define REC3 "REC3" //--- Define constant for rectangle 3 (Entry) object name
#define REC4 "REC4" //--- Define constant for rectangle 4 object name
#define REC5 "REC5" //--- Define constant for rectangle 5 (SL) object name

#define TP_HL "TP_HL" //--- Define constant for take-profit horizontal line object name
#define SL_HL "SL_HL" //--- Define constant for stop-loss horizontal line object name
#define PR_HL "PR_HL" //--- Define constant for price (entry) horizontal line object name

double Get_Price_d(string name) { return ObjectGetDouble(0, name, OBJPROP_PRICE); }                          //--- Function to get price as double for an object
string Get_Price_s(string name) { return DoubleToString(ObjectGetDouble(0, name, OBJPROP_PRICE), _Digits); } //--- Function to get price as string with proper digits
string update_Text(string name, string val) { return (string)ObjectSetString(0, name, OBJPROP_TEXT, val); }  //--- Function to update text of an object

int
   xd1, yd1, xs1, ys1, //--- Variables for rectangle 1 position and size
   xd2, yd2, xs2, ys2, //--- Variables for rectangle 2 position and size
   xd3, yd3, xs3, ys3, //--- Variables for rectangle 3 position and size
   xd4, yd4, xs4, ys4, //--- Variables for rectangle 4 position and size
   xd5, yd5, xs5, ys5; //--- Variables for rectangle 5 position and size

// Control panel variables
bool tool_visible = false;       //--- Flag to track if trading tool is visible
string selected_order_type = ""; //--- Variable to store selected order type
double lot_size = 0.01;          //--- Default lot size for trades
CTrade obj_Trade;                //--- Trade object for executing trading operations
int panel_x = 10, panel_y = 30;  //--- Panel position coordinates

Hier legen wir das Fundament für unser Trade Assistant Tool, indem wir die wesentlichen Komponenten, Variablen und Funktionen definieren, die die grafischen und handelstechnischen Funktionalitäten des Tools ermöglichen. Wir beginnen mit der Einbindung der Bibliothek „Trade.mqh“, die die Klasse „CTrade“ für die Ausführung von Handelsoperationen, wie z. B. die Platzierung schwebender Aufträge, bereitstellt. Anschließend definieren wir eine Reihe von Konstanten mit #define, um eindeutige Namen für GUI-Elemente zu vergeben, z. B. „PANEL_BG“ für den Hintergrund des Bedienfelds, „LOT_EDIT“ für das Eingabefeld für die Losgröße und Schaltflächen wie „BUY_STOP_BTN“ und „SELL_STOP_BTN“ für die Auswahl der Auftragsart und vieles mehr.

Wir implementieren drei Funktionen zur Verwaltung der Eigenschaften von Chart-Objekten: Die Funktion „Get_Price_d“ ruft den Preis eines Objekts als Double ab, die Funktion „Get_Price_s“ wandelt diesen Preis mit der Funktion DoubleToString in einen String um, der mit der entsprechenden Anzahl von Dezimalstellen formatiert ist, und die Funktion „update_Text“ aktualisiert den Text eines Objekts mit der Funktion ObjectSetString, um Echtzeit-Preisinformationen anzuzeigen.

Um die Positionierung und Größe der interaktiven Rechtecke zu handhaben, werden für jedes Rechteck („REC1“ bis „REC5“) ganzzahlige Variablen wie „xd1“, „yd1“, „xs1“ und „ys1“ deklariert, die den x-Abstand, den y-Abstand, die x-Größe und die y-Größe auf dem Chart darstellen.

Schließlich definieren wir wichtige Variablen für das Bedienfeld: „tool_visible“ als boolescher Wert, um die Sichtbarkeit des Werkzeugs zu verfolgen, „selected_order_type“ als String, um den gewählten Ordertyp zu speichern, „lot_size“ als Double, initialisiert auf 0.01 für das Handelsvolumen, „obj_Trade“ als „CTrade“-Objekt für die Ausführung von Handelsgeschäften und „panel_x“ und „panel_y“ als Ganzzahlen, um die Position des Bedienfelds an den Koordinaten (10, 30) festzulegen.

Diese Elemente bilden zusammen das strukturelle Rückgrat für die interaktive Schnittstelle und die Handelsfunktionen unseres Tools. Jetzt können wir mit der Erstellung des Bedienfelds fortfahren, aber zunächst benötigen wir eine Funktion zur Erstellung einer nutzerdefinierten Schaltfläche.

//+------------------------------------------------------------------+
//| Create button                                                    |
//+------------------------------------------------------------------+
bool createButton(string objName, string text, int xD, int yD, int xS, int yS,
                  color clrTxt, color clrBG, int fontsize = 12,
                  color clrBorder = clrNONE, bool isBack = false, string font = "Calibri") {
   ResetLastError();                                                               //--- Reset last error code
   if(!ObjectCreate(0, objName, OBJ_BUTTON, 0, 0, 0)) {                            //--- Create button object
      Print(__FUNCTION__, ": Failed to create Btn: Error Code: ", GetLastError()); //--- Print error message
      return false;                                                                //--- Return failure
   }
   ObjectSetInteger(0, objName, OBJPROP_XDISTANCE, xD);             //--- Set button x-position
   ObjectSetInteger(0, objName, OBJPROP_YDISTANCE, yD);             //--- Set button y-position
   ObjectSetInteger(0, objName, OBJPROP_XSIZE, xS);                 //--- Set button width
   ObjectSetInteger(0, objName, OBJPROP_YSIZE, yS);                 //--- Set button height
   ObjectSetInteger(0, objName, OBJPROP_CORNER, CORNER_LEFT_UPPER); //--- Set button corner
   ObjectSetString(0, objName, OBJPROP_TEXT, text);                 //--- Set button text
   ObjectSetInteger(0, objName, OBJPROP_FONTSIZE, fontsize);        //--- Set font size
   ObjectSetString(0, objName, OBJPROP_FONT, font);                 //--- Set font
   ObjectSetInteger(0, objName, OBJPROP_COLOR, clrTxt);             //--- Set text color
   ObjectSetInteger(0, objName, OBJPROP_BGCOLOR, clrBG);            //--- Set background color
   ObjectSetInteger(0, objName, OBJPROP_BORDER_COLOR, clrBorder);   //--- Set border color
   ObjectSetInteger(0, objName, OBJPROP_BACK, isBack);              //--- Set background/foreground
   ObjectSetInteger(0, objName, OBJPROP_STATE, false);              //--- Reset button state
   ObjectSetInteger(0, objName, OBJPROP_SELECTABLE, false);         //--- Disable selection
   ObjectSetInteger(0, objName, OBJPROP_SELECTED, false);           //--- Disable selected state

   ChartRedraw(0); //--- Redraw chart
   return true;    //--- Return success
}

Wir definieren die Funktion „createButton“, um anpassbare Schaltflächen für unser Werkzeug zu erstellen. Sie akzeptiert Parameter wie „objName“ für den Namen der Schaltfläche, „text“ für ihre Beschriftung, „xD“ und „yD“ für die Position, „xS“ und „yS“ für die Größe, „clrTxt“ und „clrBG“ für Text- und Hintergrundfarben, „fontsize“ (Standardwert 12), „clrBorder“ (Standardwert „clrNONE“), „isBack“ (Standardwert false) und „font“ (Standardwert „Calibri“).

Wir verwenden die Funktion ResetLastError, um Fehlercodes zu löschen, und dann die Funktion ObjectCreate, um einen OBJ_BUTTON zu erstellen. Wenn es fehlschlägt, rufen wir die Funktion Print mit __FUNCTION__ und GetLastError auf, um den Fehler zu protokollieren und false zurückzugeben.

Bei Erfolg werden Eigenschaften wie Position, Größe und Farben mit den Funktionen ObjectSetInteger und ObjectSetString festgelegt, Status und Auswahl deaktiviert und die Funktion ChartRedraw aufgerufen, um das Chart zu aktualisieren, wobei true zurückgegeben wird. So können wir interaktive Schaltflächen erstellen. Mit dieser Funktion können wir also die Funktion für das Bedienfeld erstellen.

//+------------------------------------------------------------------+
//| Create control panel                                             |
//+------------------------------------------------------------------+
void createControlPanel() {
   // Background rectangle
   ObjectCreate(0, PANEL_BG, OBJ_RECTANGLE_LABEL, 0, 0, 0);        //--- Create panel background rectangle
   ObjectSetInteger(0, PANEL_BG, OBJPROP_XDISTANCE, panel_x);      //--- Set background x-position
   ObjectSetInteger(0, PANEL_BG, OBJPROP_YDISTANCE, panel_y);      //--- Set background y-position
   ObjectSetInteger(0, PANEL_BG, OBJPROP_XSIZE, 250);              //--- Set background width
   ObjectSetInteger(0, PANEL_BG, OBJPROP_YSIZE, 280);              //--- Set background height
   ObjectSetInteger(0, PANEL_BG, OBJPROP_BGCOLOR, C'070,070,070'); //--- Set background color
   ObjectSetInteger(0, PANEL_BG, OBJPROP_BORDER_COLOR, clrWhite);  //--- Set border color
   ObjectSetInteger(0, PANEL_BG, OBJPROP_BACK, false);             //--- Set background to foreground
   
   createButton(CLOSE_BTN, CharToString(203), panel_x + 209, panel_y + 1, 40, 25, clrWhite, clrCrimson, 12, C'070,070,070', false, "Wingdings"); //--- Create close button
   
   // Lot size input
   ObjectCreate(0, LOT_EDIT, OBJ_EDIT, 0, 0, 0); //--- Create lot size edit field
   ObjectSetInteger(0, LOT_EDIT, OBJPROP_XDISTANCE, panel_x + 70); //--- Set edit field x-position
   ObjectSetInteger(0, LOT_EDIT, OBJPROP_YDISTANCE, panel_y + 40); //--- Set edit field y-position
   ObjectSetInteger(0, LOT_EDIT, OBJPROP_XSIZE, 110);              //--- Set edit field width
   ObjectSetInteger(0, LOT_EDIT, OBJPROP_YSIZE, 25);               //--- Set edit field height
   ObjectSetString(0, LOT_EDIT, OBJPROP_TEXT, "0.01");             //--- Set default lot size text
   ObjectSetInteger(0, LOT_EDIT, OBJPROP_COLOR, clrBlack);         //--- Set text color
   ObjectSetInteger(0, LOT_EDIT, OBJPROP_BGCOLOR, clrWhite);       //--- Set background color
   ObjectSetInteger(0, LOT_EDIT, OBJPROP_BORDER_COLOR, clrBlack);  //--- Set border color
   ObjectSetInteger(0, LOT_EDIT, OBJPROP_ALIGN, ALIGN_CENTER);     //--- Center text
   ObjectSetString(0, LOT_EDIT, OBJPROP_FONT, "Arial");            //--- Set font
   ObjectSetInteger(0, LOT_EDIT, OBJPROP_FONTSIZE, 13);            //--- Set font size
   ObjectSetInteger(0, LOT_EDIT, OBJPROP_BACK, false);             //--- Set to foreground
   
   // Entry price label
   ObjectCreate(0, PRICE_LABEL, OBJ_LABEL, 0, 0, 0);                  //--- Create entry price label
   ObjectSetInteger(0, PRICE_LABEL, OBJPROP_XDISTANCE, panel_x + 10); //--- Set label x-position
   ObjectSetInteger(0, PRICE_LABEL, OBJPROP_YDISTANCE, panel_y + 70); //--- Set label y-position
   ObjectSetInteger(0, PRICE_LABEL, OBJPROP_XSIZE, 230);              //--- Set label width
   ObjectSetString(0, PRICE_LABEL, OBJPROP_TEXT, "Entry: -");         //--- Set default text
   ObjectSetString(0, PRICE_LABEL, OBJPROP_FONT, "Arial Bold");       //--- Set font
   ObjectSetInteger(0, PRICE_LABEL, OBJPROP_FONTSIZE, 13);            //--- Set font size
   ObjectSetInteger(0, PRICE_LABEL, OBJPROP_COLOR, clrWhite);         //--- Set text color
   ObjectSetInteger(0, PRICE_LABEL, OBJPROP_ALIGN, ALIGN_CENTER);     //--- Center text
   ObjectSetInteger(0, PRICE_LABEL, OBJPROP_BACK, false); //--- Set to foreground
   
   // SL and TP labels
   ObjectCreate(0, SL_LABEL, OBJ_LABEL, 0, 0, 0);                  //--- Create stop-loss label
   ObjectSetInteger(0, SL_LABEL, OBJPROP_XDISTANCE, panel_x + 10); //--- Set label x-position
   ObjectSetInteger(0, SL_LABEL, OBJPROP_YDISTANCE, panel_y + 95); //--- Set label y-position
   ObjectSetInteger(0, SL_LABEL, OBJPROP_XSIZE, 110);              //--- Set label width
   ObjectSetString(0, SL_LABEL, OBJPROP_TEXT, "SL: -");            //--- Set default text
   ObjectSetString(0, SL_LABEL, OBJPROP_FONT, "Arial Bold");       //--- Set font
   ObjectSetInteger(0, SL_LABEL, OBJPROP_FONTSIZE, 12);            //--- Set font size
   ObjectSetInteger(0, SL_LABEL, OBJPROP_COLOR, clrYellow);        //--- Set text color
   ObjectSetInteger(0, SL_LABEL, OBJPROP_ALIGN, ALIGN_CENTER);     //--- Center text
   ObjectSetInteger(0, SL_LABEL, OBJPROP_BACK, false);             //--- Set to foreground
   
   ObjectCreate(0, TP_LABEL, OBJ_LABEL, 0, 0, 0);                   //--- Create take-profit label
   ObjectSetInteger(0, TP_LABEL, OBJPROP_XDISTANCE, panel_x + 130); //--- Set label x-position
   ObjectSetInteger(0, TP_LABEL, OBJPROP_YDISTANCE, panel_y + 95);  //--- Set label y-position
   ObjectSetInteger(0, TP_LABEL, OBJPROP_XSIZE, 110);               //--- Set label width
   ObjectSetString(0, TP_LABEL, OBJPROP_TEXT, "TP: -");             //--- Set default text
   ObjectSetString(0, TP_LABEL, OBJPROP_FONT, "Arial Bold");        //--- Set font
   ObjectSetInteger(0, TP_LABEL, OBJPROP_FONTSIZE, 12);             //--- Set font size
   ObjectSetInteger(0, TP_LABEL, OBJPROP_COLOR, clrLime);           //--- Set text color
   ObjectSetInteger(0, TP_LABEL, OBJPROP_ALIGN, ALIGN_CENTER);      //--- Center text
   ObjectSetInteger(0, TP_LABEL, OBJPROP_BACK, false);              //--- Set to foreground
   
   // Order type buttons
   createButton(BUY_STOP_BTN, "Buy Stop", panel_x + 10, panel_y + 140, 110, 30, clrWhite, clrForestGreen, 10, clrBlack, false, "Arial");    //--- Create Buy Stop button
   createButton(SELL_STOP_BTN, "Sell Stop", panel_x + 130, panel_y + 140, 110, 30, clrWhite, clrFireBrick, 10, clrBlack, false, "Arial");   //--- Create Sell Stop button
   createButton(BUY_LIMIT_BTN, "Buy Limit", panel_x + 10, panel_y + 180, 110, 30, clrWhite, clrForestGreen, 10, clrBlack, false, "Arial");  //--- Create Buy Limit button
   createButton(SELL_LIMIT_BTN, "Sell Limit", panel_x + 130, panel_y + 180, 110, 30, clrWhite, clrFireBrick, 10, clrBlack, false, "Arial"); //--- Create Sell Limit button
   
   // Place Order and Cancel buttons
   createButton(PLACE_ORDER_BTN, "Place Order", panel_x + 10, panel_y + 240, 110, 30, clrWhite, clrDodgerBlue, 10, clrBlack, false, "Arial"); //--- Create Place Order button
   createButton(CANCEL_BTN, "Cancel", panel_x + 130, panel_y + 240, 110, 30, clrWhite, clrSlateGray, 10, clrBlack, false, "Arial");           //--- Create Cancel button
}

Hier definieren wir die Funktion „createControlPanel“, um die graphisches Nutzerschnittstelle (GUI) für unser Trade Assistant Tool zu erstellen. Wir beginnen mit der Funktion ObjectCreate, um ein Hintergrundrechteck mit dem Namen „PANEL_BG“ vom Typ OBJ_RECTANGLE_LABEL zu erstellen, das bei „panel_x“ und „panel_y“ positioniert ist. (auf 10 und 30 gesetzt), mit einer Größe von 250x280 Pixeln, einem dunkelgrauen Hintergrund („C'070,070,070'“), einem weißen Rand („clrWhite“) und einer Platzierung im Vordergrund (OBJPROP_BACK auf false gesetzt).

Dann rufen wir die Funktion „createButton“ auf, um in der rechten oberen Ecke eine Schaltfläche zum Schließen („CLOSE_BTN“) hinzuzufügen, die ein Kreuzsymbol (Zeichen 203 aus „Wingdings“) in purpurner Farbe anzeigt. Die Eingabe ist in der MQL5-Dokumentation wie unten definiert, aber Sie können eine Eingabe nach Ihrem Geschmack verwenden.

MQL5 WINGDINGS TABELLE

Für die Eingabe der Losgröße verwenden wir die Funktion ObjectCreate, um ein Eingabefeld („LOT_EDIT“) vom Typ OBJ_EDIT an den Positionen „panel_x + 70“ und „panel_y + 40“ zu erstellen. Das Feld hat eine Größe von 110x25 Pixeln, ist mit „0,01“ initialisiert und mit den Funktionen ObjectSetInteger und ObjectSetString mit schwarzem Text, weißem Hintergrund und zentrierter Arial-Schrift gestaltet.

Mit der Funktion „ObjectCreate“ erstellen wir drei Etiketten für die Anzeige von Handelsinformationen: „PRICE_LABEL“ für den Einstiegspreis bei „panel_x + 10“, „panel_y + 70“, über 230 Pixel mit dem Standardtext „Entry: -“; „SL_LABEL“ für den Stop-Loss bei „panel_x + 10“, „panel_y + 95“, mit gelbem Text und dem Standardtext „SL: -“; und „TP_LABEL“ für Take-Profit bei „panel_x + 130“, „panel_y + 95“, mit Text in „lime“ und dem Standardtext „TP: -“, alle in fetter Arial-Schrift und zentrierter Ausrichtung.

Schließlich fügen wir mit der Funktion „createButton“ Schaltflächen für den Auftragstyp - „BUY_STOP_BTN“ und „SELL_STOP_BTN“ bei „panel_y + 140“, „BUY_LIMIT_BTN“ und „SELL_LIMIT_BTN“ bei „panel_y + 180“ in grüner bzw. roter Farbe und die Aktionsschaltflächen „PLACE_ORDER_BTN“ und „CANCEL_BTN“ bei „panel_y + 240“ in blauer und grauer Farbe, alle in der Größe 110x30 Pixel mit Arial-Schrift. Diese Einrichtung bildet das interaktive Bedienfeld für unser Tool. Wir können die Funktion in OnInit aufrufen, um das Panel zu initialisieren.

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit() {
   
// Create control panel
   createControlPanel();   //--- Call function to create the control panel
   ChartRedraw(0);         //--- Redraw chart to display panel
   return(INIT_SUCCEEDED); //--- Return successful initialization
}

In OnInit rufen wir die Funktion „createControlPanel“ auf, um die grafische Nutzeroberfläche zu erstellen und das Bedienfeld mit Schaltflächen, Beschriftungen und Eingabefeldern einzurichten. Als Nächstes verwenden wir die Funktion ChartRedraw, um das Chart zu aktualisieren und sicherzustellen, dass das Panel sofort angezeigt wird. Schließlich geben wir INIT_SUCCEEDED zurück, um die erfolgreiche Initialisierung anzuzeigen. Nach dem Kompilieren erhalten wir die folgende Ausgabe.

DAS BEDIENFELD

Aus dem Bild können wir ersehen, dass wir das Bedienfeld erstellt haben. Was wir jetzt erstellen müssen, ist das Assistenten-Panel, mit dem wir die Chart-Preise dynamisch abrufen und automatisch in das Kontroll-Panel übertragen können, um auf dieser Grundlage zu handeln. Dazu ist es erforderlich, die Skala der Karte und die Skala des Bildschirms zu integrieren, aber dafür haben wir gesorgt. Wir werden alles in einer Funktion unterbringen. Dann fügen wir Ereignis-Listener hinzu, die die entsprechenden Funktionen aufrufen, wenn die Schaltfläche des Steuerelements betätigt wird.

//+------------------------------------------------------------------+
//| Expert onchart event function                                    |
//+------------------------------------------------------------------+
void OnChartEvent(
   const int id,         //--- Event ID
   const long& lparam,   //--- Long parameter (e.g., x-coordinate for mouse)
   const double& dparam, //--- Double parameter (e.g., y-coordinate for mouse)
   const string& sparam  //--- String parameter (e.g., object name)
) {
   if(id == CHARTEVENT_OBJECT_CLICK) {     //--- Handle object click events
      // Handle order type buttons
      if(sparam == BUY_STOP_BTN) {         //--- Check if Buy Stop button clicked
         selected_order_type = "BUY_STOP"; //--- Set order type to Buy Stop
      }
   }
}

Hier implementieren wir die Ereignisbehandlung durch OnChartEvent, um Nutzerinteraktionen mit dem Tool zu verarbeiten. Die Funktion erhält folgende Parameter: „id“ für den Ereignistyp, „lparam“ für Daten wie x-Koordinaten, „dparam“ für Daten wie y-Koordinaten und „sparam“ für String-Daten wie Objektnamen. Wir prüfen, ob „id“ gleich CHARTEVENT_OBJECT_CLICK ist, um Objektklicks zu erkennen, und wenn „sparam“ mit „BUY_STOP_BTN“ übereinstimmt, setzen wir die Variable „selected_order_type“ auf „BUY_STOP“, sodass wir die Auswahl eines Kaufstoppauftrags durch den Nutzer registrieren können. Wenn das der Fall ist, brauchen wir eine Funktion, die das Werkzeug anzeigt.

//+------------------------------------------------------------------+
//| Show main tool                                                   |
//+------------------------------------------------------------------+
void showTool() {
   // Hide panel
   ObjectSetInteger(0, PANEL_BG, OBJPROP_BACK, false);        //--- Hide panel background
   ObjectSetInteger(0, LOT_EDIT, OBJPROP_BACK, false);        //--- Hide lot edit field
   ObjectSetInteger(0, PRICE_LABEL, OBJPROP_BACK, false);     //--- Hide price label
   ObjectSetInteger(0, SL_LABEL, OBJPROP_BACK, false);        //--- Hide SL label
   ObjectSetInteger(0, TP_LABEL, OBJPROP_BACK, false);        //--- Hide TP label
   ObjectSetInteger(0, BUY_STOP_BTN, OBJPROP_BACK, false);    //--- Hide Buy Stop button
   ObjectSetInteger(0, SELL_STOP_BTN, OBJPROP_BACK, false);   //--- Hide Sell Stop button
   ObjectSetInteger(0, BUY_LIMIT_BTN, OBJPROP_BACK, false);   //--- Hide Buy Limit button
   ObjectSetInteger(0, SELL_LIMIT_BTN, OBJPROP_BACK, false);  //--- Hide Sell Limit button
   ObjectSetInteger(0, PLACE_ORDER_BTN, OBJPROP_BACK, false); //--- Hide Place Order button
   ObjectSetInteger(0, CANCEL_BTN, OBJPROP_BACK, false);      //--- Hide Cancel button
   ObjectSetInteger(0, CLOSE_BTN, OBJPROP_BACK, false);       //--- Hide Close button
   
   // Create main tool 150 pixels from the right edge
   int chart_width = (int)ChartGetInteger(0, CHART_WIDTH_IN_PIXELS); //--- Get chart width
   int tool_x = chart_width - 400 - 50;                              //--- Calculate tool x-position (400 is REC1 width, 50 is margin)
   
   if(selected_order_type == "BUY_STOP" || selected_order_type == "BUY_LIMIT") { //--- Check for buy orders
      // Buy orders: TP at top, entry in middle, SL at bottom
      createButton(REC1, "", tool_x, 20, 350, 30, clrWhite, clrGreen, 13, clrBlack, false, "Arial Black"); //--- Create TP rectangle
      
      xd1 = (int)ObjectGetInteger(0, REC1, OBJPROP_XDISTANCE); //--- Get REC1 x-distance
      yd1 = (int)ObjectGetInteger(0, REC1, OBJPROP_YDISTANCE); //--- Get REC1 y-distance
      xs1 = (int)ObjectGetInteger(0, REC1, OBJPROP_XSIZE);     //--- Get REC1 x-size
      ys1 = (int)ObjectGetInteger(0, REC1, OBJPROP_YSIZE);     //--- Get REC1 y-size
      
      xd2 = xd1;       //--- Set REC2 x-distance
      yd2 = yd1 + ys1; //--- Set REC2 y-distance
      xs2 = xs1;       //--- Set REC2 x-size
      ys2 = 100;       //--- Set REC2 y-size

      xd3 = xd2;       //--- Set REC3 x-distance
      yd3 = yd2 + ys2; //--- Set REC3 y-distance
      xs3 = xs2;       //--- Set REC3 x-size
      ys3 = 30;        //--- Set REC3 y-size
      
      xd4 = xd3;       //--- Set REC4 x-distance
      yd4 = yd3 + ys3; //--- Set REC4 y-distance
      xs4 = xs3;       //--- Set REC4 x-size
      ys4 = 100;       //--- Set REC4 y-size

      xd5 = xd4;       //--- Set REC5 x-distance
      yd5 = yd4 + ys4; //--- Set REC5 y-distance
      xs5 = xs4;       //--- Set REC5 x-size
      ys5 = 30;        //--- Set REC5 y-size
   }
   else { //--- Handle sell orders
      // Sell orders: SL at top, entry in middle, TP at bottom
      createButton(REC5, "", tool_x, 20, 350, 30, clrWhite, clrRed, 13, clrBlack, false, "Arial Black"); //--- Create SL rectangle
      
      xd5 = (int)ObjectGetInteger(0, REC5, OBJPROP_XDISTANCE); //--- Get REC5 x-distance
      yd5 = (int)ObjectGetInteger(0, REC5, OBJPROP_YDISTANCE); //--- Get REC5 y-distance
      xs5 = (int)ObjectGetInteger(0, REC5, OBJPROP_XSIZE);     //--- Get REC5 x-size
      ys5 = (int)ObjectGetInteger(0, REC5, OBJPROP_YSIZE);     //--- Get REC5 y-size
      
      xd2 = xd5;       //--- Set REC2 x-distance
      yd2 = yd5 + ys5; //--- Set REC2 y-distance
      xs2 = xs5;       //--- Set REC2 x-size
      ys2 = 100;       //--- Set REC2 y-size

      xd3 = xd2;       //--- Set REC3 x-distance
      yd3 = yd2 + ys2; //--- Set REC3 y-distance
      xs3 = xs2;       //--- Set REC3 x-size
      ys3 = 30;        //--- Set REC3 y-size
      
      xd4 = xd3;       //--- Set REC4 x-distance
      yd4 = yd3 + ys3; //--- Set REC4 y-distance
      xs4 = xs3;       //--- Set REC4 x-size
      ys4 = 100;       //--- Set REC4 y-size

      xd1 = xd4;       //--- Set REC1 x-distance
      yd1 = yd4 + ys4; //--- Set REC1 y-distance
      xs1 = xs4;       //--- Set REC1 x-size
      ys1 = 30;        //--- Set REC1 y-size
   }
   
   datetime dt_tp = 0, dt_sl = 0, dt_prc = 0;        //--- Variables for time
   double price_tp = 0, price_sl = 0, price_prc = 0; //--- Variables for price
   int window = 0;                                   //--- Chart window
   
   ChartXYToTimePrice(0, xd1, yd1 + ys1, window, dt_tp, price_tp);   //--- Convert REC1 coordinates to time and price
   ChartXYToTimePrice(0, xd3, yd3 + ys3, window, dt_prc, price_prc); //--- Convert REC3 coordinates to time and price
   ChartXYToTimePrice(0, xd5, yd5 + ys5, window, dt_sl, price_sl);   //--- Convert REC5 coordinates to time and price

   createHL(TP_HL, dt_tp, price_tp, clrTeal);   //--- Create TP horizontal line
   createHL(PR_HL, dt_prc, price_prc, clrBlue); //--- Create entry horizontal line
   createHL(SL_HL, dt_sl, price_sl, clrRed);    //--- Create SL horizontal line

   if(selected_order_type == "BUY_STOP" || selected_order_type == "BUY_LIMIT") {                              //--- Check for buy orders
      createButton(REC2, "", xd2, yd2, xs2, ys2, clrWhite, clrHoneydew, 12, clrBlack, true);                  //--- Create REC2
      createButton(REC3, "", xd3, yd3, xs3, ys3, clrBlack, clrLightGray, 13, clrBlack, false, "Arial Black"); //--- Create REC3
      createButton(REC4, "", xd4, yd4, xs4, ys4, clrWhite, clrLinen, 12, clrBlack, true);                     //--- Create REC4
      createButton(REC5, "", xd5, yd5, xs5, ys5, clrWhite, clrRed, 13, clrBlack, false, "Arial Black");       //--- Create REC5
   }
   else { //--- Handle sell orders
      createButton(REC2, "", xd2, yd2, xs2, ys2, clrWhite, clrHoneydew, 12, clrBlack, true);                  //--- Create REC2
      createButton(REC3, "", xd3, yd3, xs3, ys3, clrBlack, clrLightGray, 13, clrBlack, false, "Arial Black"); //--- Create REC3
      createButton(REC4, "", xd4, yd4, xs4, ys4, clrWhite, clrLinen, 12, clrBlack, true);                     //--- Create REC4
      createButton(REC1, "", xd1, yd1, xs1, ys1, clrWhite, clrGreen, 13, clrBlack, false, "Arial Black");     //--- Create REC1
   }
   
   update_Text(REC1, "TP: " + DoubleToString(MathAbs((Get_Price_d(TP_HL) - Get_Price_d(PR_HL)) / _Point), 0) + " Points | " + Get_Price_s(TP_HL)); //--- Update REC1 text
   update_Text(REC3, selected_order_type + ": | Lot: " + DoubleToString(lot_size, 2) + " | " + Get_Price_s(PR_HL));                                //--- Update REC3 text
   update_Text(REC5, "SL: " + DoubleToString(MathAbs((Get_Price_d(PR_HL) - Get_Price_d(SL_HL)) / _Point), 0) + " Points | " + Get_Price_s(SL_HL)); //--- Update REC5 text
   update_Text(PRICE_LABEL, "Entry: " + Get_Price_s(PR_HL)); //--- Update entry label text
   update_Text(SL_LABEL, "SL: " + Get_Price_s(SL_HL));       //--- Update SL label text
   update_Text(TP_LABEL, "TP: " + Get_Price_s(TP_HL));       //--- Update TP label text

   tool_visible = true;                              //--- Set tool visibility flag
   ChartSetInteger(0, CHART_EVENT_MOUSE_MOVE, true); //--- Enable mouse move events
   ChartRedraw(0);                                   //--- Redraw chart
}

Um das Preisdiagramm-Tool anzuzeigen, implementieren wir die Funktion „showTool“ und beginnen damit, das Kontrollfeld mit der Funktion ObjectSetInteger auszublenden, indem wir OBJPROP_BACK auf false für Objekte wie „PANEL_BG“, „LOT_EDIT“, „PRICE_LABEL“, „SL_LABEL“, „TP_LABEL“, „BUY_STOP_BTN“, „SELL_STOP_BTN“, „BUY_LIMIT_BTN“, „SELL_LIMIT_BTN“, „PLACE_ORDER_BTN“, „CANCEL_BTN“, und „CLOSE_BTN“.

Wir berechnen die x-Position des Werkzeugs mit der Funktion ChartGetInteger, um CHART_WIDTH_IN_PIXELS zu erhalten und setzen „tool_x“ auf 450 Pixel vom rechten Rand. Für „BUY_STOP“- oder „BUY_LIMIT“-Aufträge erstellen wir mit der Funktion „createButton“ „REC1“ (TP) bei „tool_x“, y=20, Größe 350x30 in grün, und setzen die Variablen „xd1“, „yd1“, „xs1“, „ys1“ über „ObjectGetInteger“, dann positioniere „REC2“ bis „REC5“ vertikal (TP, Eintrag, SL) mit „xd2“ zu „xd5“, „yd2“ zu „yd5“, „xs2“ zu „xs5“, „ys2“ zu „ys5“.

Für Verkaufsaufträge legen wir „REC5“ (SL) in rot an und ordnen „REC2“ bis „REC1“ (SL, Entry, TP).

Wir deklarieren „dt_tp“, „dt_sl“, „dt_prc“ für die Zeit, „price_tp“, „price_sl“ und „price_prc“ für die Preise und „window“ für das Chart, wobei die Funktion ChartXYToTimePrice verwendet wird, um die Koordinaten von „REC1“, „REC3“ und „REC5“ in Preis und Zeit umzuwandeln. Wir rufen die Funktion „createHL“ auf, um „TP_HL“, „PR_HL“ und „SL_HL“ in den Farben Türkis, Blau und Rot zu zeichnen.

Je nach „selected_order_type“ werden mit „createButton“ die restlichen Rechtecke („REC2“, „REC3“, „REC4“, „REC5“ für Kaufen; „REC2“, „REC3“, „REC4“, „REC1“ für Verkaufen) mit entsprechenden Farben. Wir aktualisieren den Text mit der Funktion „update_Text“ für „REC1“, „REC3“, „REC5“, „PRICE_LABEL“, „SL_LABEL“ und „TP_LABEL“ und berechnen die Punktdifferenzen mit „Get_Price_d“, „Get_Price_s“, DoubleToString und MathAbs.

Schließlich setzen wir „tool_visible“ auf true, aktivieren Mausereignisse mit ChartSetInteger und rufen ChartRedraw auf, um das Werkzeug anzuzeigen. Um die horizontalen Linien zu erstellen, verwenden wir die folgende Funktion.

//+------------------------------------------------------------------+
//| Create horizontal line                                           |
//+------------------------------------------------------------------+
bool createHL(string objName, datetime time1, double price1, color clr) {
   ResetLastError();                                                              //--- Reset last error code
   if(!ObjectCreate(0, objName, OBJ_HLINE, 0, time1, price1)) {                   //--- Create horizontal line
      Print(__FUNCTION__, ": Failed to create HL: Error Code: ", GetLastError()); //--- Print error message
      return false; //--- Return failure
   }
   ObjectSetInteger(0, objName, OBJPROP_TIME, time1);             //--- Set line time
   ObjectSetDouble(0, objName, OBJPROP_PRICE, price1);            //--- Set line price
   ObjectSetInteger(0, objName, OBJPROP_COLOR, clr);              //--- Set line color
   ObjectSetInteger(0, objName, OBJPROP_BACK, false);             //--- Set to foreground
   ObjectSetInteger(0, objName, OBJPROP_STYLE, STYLE_DASHDOTDOT); //--- Set line style

   ChartRedraw(0); //--- Redraw chart
   return true;    //--- Return success
}

Hier erstellen wir einfach das Objekt OBJ_HLINE und setzen die notwendigen Objektparameter, wie wir es bereits bei der Funktion zum Erstellen von Schaltflächen getan haben. Wir brauchen auch eine Funktion, um das Panel wie unten dargestellt anzuzeigen.

//+------------------------------------------------------------------+
//| Show control panel                                               |
//+------------------------------------------------------------------+
void showPanel() {
   // Show panel
   ObjectSetInteger(0, PANEL_BG, OBJPROP_BACK, false);        //--- Show panel background
   ObjectSetInteger(0, LOT_EDIT, OBJPROP_BACK, false);        //--- Show lot edit field
   ObjectSetInteger(0, PRICE_LABEL, OBJPROP_BACK, false);     //--- Show price label
   ObjectSetInteger(0, SL_LABEL, OBJPROP_BACK, false);        //--- Show SL label
   ObjectSetInteger(0, TP_LABEL, OBJPROP_BACK, false);        //--- Show TP label
   ObjectSetInteger(0, BUY_STOP_BTN, OBJPROP_BACK, false);    //--- Show Buy Stop button
   ObjectSetInteger(0, SELL_STOP_BTN, OBJPROP_BACK, false);   //--- Show Sell Stop button
   ObjectSetInteger(0, BUY_LIMIT_BTN, OBJPROP_BACK, false);   //--- Show Buy Limit button
   ObjectSetInteger(0, SELL_LIMIT_BTN, OBJPROP_BACK, false);  //--- Show Sell Limit button
   ObjectSetInteger(0, PLACE_ORDER_BTN, OBJPROP_BACK, false); //--- Show Place Order button
   ObjectSetInteger(0, CANCEL_BTN, OBJPROP_BACK, false);      //--- Show Cancel button
   ObjectSetInteger(0, CLOSE_BTN, OBJPROP_BACK, false);       //--- Show Close button
   
   // Reset panel state
   update_Text(PRICE_LABEL, "Entry: -");              //--- Reset entry label text
   update_Text(SL_LABEL, "SL: -");                    //--- Reset SL label text
   update_Text(TP_LABEL, "TP: -");                    //--- Reset TP label text
   update_Text(PLACE_ORDER_BTN, "Place Order");       //--- Reset Place Order button text
   selected_order_type = "";                          //--- Clear selected order type
   tool_visible = false;                              //--- Hide tool
   ChartSetInteger(0, CHART_EVENT_MOUSE_MOVE, false); //--- Disable mouse move events
   ChartRedraw(0);                                    //--- Redraw chart
}

Wir definieren die Funktion „showPanel“, um unser Bedienfeld anzuzeigen. Wir verwenden die Funktion ObjectSetInteger, um „OBJPROP_BACK“ auf false zu setzen für „PANEL_BG“, „LOT_EDIT“, „PRICE_LABEL“, „SL_LABEL“, „TP_LABEL“, „BUY_STOP_BTN“, „SELL_STOP_BTN“, „BUY_LIMIT_BTN“, „SELL_LIMIT_BTN“, „PLACE_ORDER_BTN“, „CANCEL_BTN“ und „CLOSE_BTN“ zu setzen, damit sie sichtbar werden.

Wir setzen den Status mit der Funktion „update_Text“ zurück, setzen „PRICE_LABEL“ auf „Entry: -“, „SL_LABEL“ auf „SL: -“, „TP_LABEL“ auf „TP: -“ und „PLACE_ORDER_BTN“ auf „Place Order“, löschen „selected_order_type“, setzen Sie „tool_visible“ auf false, deaktivieren Mausereignisse über ChartSetInteger und rufen ChartRedraw auf, um den Chart zu aktualisieren.

Um das Werkzeug und das Panel zu löschen, verwenden wir die folgenden Funktionen, indem wir die Funktion ObjectDelete für die jeweiligen Objekte aufrufen.

//+------------------------------------------------------------------+
//| Delete main tool objects                                         |
//+------------------------------------------------------------------+
void deleteObjects() {
   ObjectDelete(0, REC1);  //--- Delete REC1 object
   ObjectDelete(0, REC2);  //--- Delete REC2 object
   ObjectDelete(0, REC3);  //--- Delete REC3 object
   ObjectDelete(0, REC4);  //--- Delete REC4 object
   ObjectDelete(0, REC5);  //--- Delete REC5 object
   ObjectDelete(0, TP_HL); //--- Delete TP horizontal line
   ObjectDelete(0, SL_HL); //--- Delete SL horizontal line
   ObjectDelete(0, PR_HL); //--- Delete entry horizontal line
   ChartRedraw(0);         //--- Redraw chart
}

//+------------------------------------------------------------------+
//| Delete control panel objects                                     |
//+------------------------------------------------------------------+
void deletePanel() {
   ObjectDelete(0, PANEL_BG);        //--- Delete panel background
   ObjectDelete(0, LOT_EDIT);        //--- Delete lot edit field
   ObjectDelete(0, PRICE_LABEL);     //--- Delete price label
   ObjectDelete(0, SL_LABEL);        //--- Delete SL label
   ObjectDelete(0, TP_LABEL);        //--- Delete TP label
   ObjectDelete(0, BUY_STOP_BTN);    //--- Delete Buy Stop button
   ObjectDelete(0, SELL_STOP_BTN);   //--- Delete Sell Stop button
   ObjectDelete(0, BUY_LIMIT_BTN);   //--- Delete Buy Limit button
   ObjectDelete(0, SELL_LIMIT_BTN);  //--- Delete Sell Limit button
   ObjectDelete(0, PLACE_ORDER_BTN); //--- Delete Place Order button
   ObjectDelete(0, CANCEL_BTN);      //--- Delete Cancel button
   ObjectDelete(0, CLOSE_BTN);       //--- Delete Close button
   ChartRedraw(0);                   //--- Redraw chart
}

Um die Aufträge zu erteilen, wenn die entsprechende Schaltfläche angeklickt wird, verwenden wir die folgende Funktion.

//+------------------------------------------------------------------+
//| Place order based on selected type                               |
//+------------------------------------------------------------------+
void placeOrder() {
   double price = Get_Price_d(PR_HL);               //--- Get entry price
   double sl = Get_Price_d(SL_HL);                  //--- Get stop-loss price
   double tp = Get_Price_d(TP_HL);                  //--- Get take-profit price
   string symbol = Symbol();                        //--- Get current symbol
   datetime expiration = TimeCurrent() + 3600 * 24; //--- Set 24-hour order expiration
   
   // Validate lot size
   if(lot_size <= 0) {                       //--- Check if lot size is valid
      Print("Invalid lot size: ", lot_size); //--- Print error message
      return;                                //--- Exit function
   }

   // Validate prices
   if(price <= 0 || sl <= 0 || tp <= 0) {                                                          //--- Check if prices are valid
      Print("Invalid prices: Entry=", price, ", SL=", sl, ", TP=", tp, " (all must be positive)"); //--- Print error message
      return;                                                                                      //--- Exit function
   }

   // Validate price relationships based on order type
   if(selected_order_type == "BUY_STOP" || selected_order_type == "BUY_LIMIT") {                     //--- Check for buy orders
      if(sl >= price) {                                                                              //--- Check if SL is below entry
         Print("Invalid SL for ", selected_order_type, ": SL=", sl, " must be below Entry=", price); //--- Print error message
         return;                                                                                     //--- Exit function
      }
      if(tp <= price) {                                                                              //--- Check if TP is above entry
         Print("Invalid TP for ", selected_order_type, ": TP=", tp, " must be above Entry=", price); //--- Print error message
         return;                                                                                     //--- Exit function
      }
   }
   else if(selected_order_type == "SELL_STOP" || selected_order_type == "SELL_LIMIT") {              //--- Check for sell orders
      if(sl <= price) {                                                                              //--- Check if SL is above entry
         Print("Invalid SL for ", selected_order_type, ": SL=", sl, " must be above Entry=", price); //--- Print error message
         return;                                                                                     //--- Exit function
      }
      if(tp >= price) {                                                                              //--- Check if TP is below entry
         Print("Invalid TP for ", selected_order_type, ": TP=", tp, " must be below Entry=", price); // AMPK--- Print error message
         return;                                                                                     //--- Exit function
      }
   }
   else {                                                 //--- Handle invalid order type
      Print("Invalid order type: ", selected_order_type); //--- Print error message
      return;                                             //--- Exit function
   }

   // Place the order
   if(selected_order_type == "BUY_STOP") {                                                              //--- Handle Buy Stop order
      if(!obj_Trade.BuyStop(lot_size, price, symbol, sl, tp, ORDER_TIME_DAY, expiration)) {             //--- Attempt to place Buy Stop order
         Print("Buy Stop failed: Entry=", price, ", SL=", sl, ", TP=", tp, ", Error=", GetLastError()); //--- Print error message
      }
      else {                                                                //--- Order placed successfully
         Print("Buy Stop placed: Entry=", price, ", SL=", sl, ", TP=", tp); //--- Print success message
      }
   }
   else if(selected_order_type == "SELL_STOP") {                                                         //--- Handle Sell Stop order
      if(!obj_Trade.SellStop(lot_size, price, symbol, sl, tp, ORDER_TIME_DAY, expiration)) {             //--- Attempt to place Sell Stop order
         Print("Sell Stop failed: Entry=", price, ", SL=", sl, ", TP=", tp, ", Error=", GetLastError()); //--- Print error message
      }
      else {                                                                 //--- Order placed successfully
         Print("Sell Stop placed: Entry=", price, ", SL=", sl, ", TP=", tp); //--- Print success message
      }
   }
   else if(selected_order_type == "BUY_LIMIT") {                                                         //--- Handle Buy Limit order
      if(!obj_Trade.BuyLimit(lot_size, price, symbol, sl, tp, ORDER_TIME_DAY, expiration)) {             //--- Attempt to place Buy Limit order
         Print("Buy Limit failed: Entry=", price, ", SL=", sl, ", TP=", tp, ", Error=", GetLastError()); //--- Print error message
      }
      else {                                                                 //--- Order placed successfully
         Print("Buy Limit placed: Entry=", price, ", SL=", sl, ", TP=", tp); //--- Print success message
      }
   }
   else if(selected_order_type == "SELL_LIMIT") {                                                         //--- Handle Sell Limit order
      if(!obj_Trade.SellLimit(lot_size, price, symbol, sl, tp, ORDER_TIME_DAY, expiration)) {             //--- Attempt to place Sell Limit order
         Print("Sell Limit failed: Entry=", price, ", SL=", sl, ", TP=", tp, ", Error=", GetLastError()); //--- Print error message
      }
      else {                                                                  //--- Order placed successfully
         Print("Sell Limit placed: Entry=", price, ", SL=", sl, ", TP=", tp); //--- Print success message
      }
   }
}

Wir implementieren die Funktion „placeOrder“, um schwebende Aufträge für unser Tool auszuführen, und beginnen mit dem Abrufen des Einstiegspreises („price“), des Stop-Loss („sl“) und des Take-Profit („tp“) mit der Funktion „Get_Price_d“ für „PR_HL“, „SL_HL“ und „TP_HL“ abrufen, das aktuelle „Symbol“ mit der Funktion „Symbol“ ermitteln und einen 24-Stunden-“Ablauf“ mit der Funktion „TimeCurrent„ festlegen.

Wir überprüfen „lot_size“ (>0) und vergewissern uns, dass „price“, „sl“ und „tp“ positiv sind und beenden den Vorgang mit der Funktion Print, wenn sie ungültig sind. Bei „BUY_STOP“ oder „BUY_LIMIT“ wird geprüft, ob „sl“ unter „price“ und „tp“ darüber liegt, und bei „SELL_STOP“ oder „SELL_LIMIT“ wird geprüft, ob „sl“ über und „tp“ unter „price“ liegt, wobei „Print“ verwendet wird, um Fehler zu protokollieren und den Vorgang zu beenden, wenn die Bedingungen nicht erfüllt sind. Wenn „selected_order_type“ ungültig ist, wird eine „Print“-Meldung ausgegeben.

Dann verwenden wir die Methoden von „obj_Trade“ - „BuyStop“, „SellStop“, „BuyLimit“ oder „SellLimit“ - um die Order zu platzieren und protokollieren den Erfolg oder bei Misserfolg „Print“ und GetLastError. Mit diesen Funktionen ausgestattet, können wir sie bei entsprechenden Schaltflächenklicks wie folgt aufrufen.

if(id == CHARTEVENT_OBJECT_CLICK) {                    //--- Handle object click events
   // Handle order type buttons
   if(sparam == BUY_STOP_BTN) {                        //--- Check if Buy Stop button clicked
      selected_order_type = "BUY_STOP";                //--- Set order type to Buy Stop
      showTool();                                      //--- Show trading tool
      update_Text(PLACE_ORDER_BTN, "Place Buy Stop");  //--- Update place order button text
   }
   else if(sparam == SELL_STOP_BTN) {                  //--- Check if Sell Stop button clicked
      selected_order_type = "SELL_STOP";               //--- Set order type to Sell Stop
      showTool();                                      //--- Show trading tool
      update_Text(PLACE_ORDER_BTN, "Place Sell Stop"); //--- Update place order button text
   }
   else if(sparam == BUY_LIMIT_BTN) {                  //--- Check if Buy Limit button clicked
      selected_order_type = "BUY_LIMIT";               //--- Set order type to Buy Limit
      showTool();                                      //--- Show trading tool
      update_Text(PLACE_ORDER_BTN, "Place Buy Limit"); //--- Update place order button text
   }
   else if(sparam == SELL_LIMIT_BTN) {                 //--- Check if Sell Limit button clicked
      selected_order_type = "SELL_LIMIT";              //--- Set order type to Sell Limit
      showTool();                                      //--- Show trading tool
      update_Text(PLACE_ORDER_BTN, "Place Sell Limit");//--- Update place order button text
   }
   else if(sparam == PLACE_ORDER_BTN) {                //--- Check if Place Order button clicked
      placeOrder();                                    //--- Execute order placement
      deleteObjects();                                 //--- Delete tool objects
      showPanel();                                     //--- Show control panel
   }
   else if(sparam == CANCEL_BTN) {                     //--- Check if Cancel button clicked
      deleteObjects();                                 //--- Delete tool objects
      showPanel();                                     //--- Show control panel
   }
   else if(sparam == CLOSE_BTN) {                      //--- Check if Close button clicked
      deleteObjects();                                 //--- Delete tool objects
      deletePanel();                                   //--- Delete control panel
   }
   ObjectSetInteger(0, sparam, OBJPROP_STATE, false);  //--- Reset button state
   ChartRedraw(0);                                     //--- Redraw chart
}

Nach der Kompilierung erhalten wir folgendes Ergebnis.

PANEL + PREISDIAGRAMM-TOOL

Aus dem Bild können wir ersehen, dass wir das entsprechende Preisdiagramm-Tool dynamisch erstellen können. Als Nächstes müssen wir das Werkzeug unbeweglich machen, um es auf dem Chart verschieben zu können. Hier ist die Logik, die wir in OnChartEvent verwenden.

//+------------------------------------------------------------------+
//| Chart event handler                                              |
//+------------------------------------------------------------------+
int prevMouseState = 0; //--- Variable to track previous mouse state

int mlbDownX1 = 0, mlbDownY1 = 0, mlbDownXD_R1 = 0, mlbDownYD_R1 = 0; //--- Variables for mouse down coordinates for REC1
int mlbDownX2 = 0, mlbDownY2 = 0, mlbDownXD_R2 = 0, mlbDownYD_R2 = 0; //--- Variables for mouse down coordinates for REC2
int mlbDownX3 = 0, mlbDownY3 = 0, mlbDownXD_R3 = 0, mlbDownYD_R3 = 0; //--- Variables for mouse down coordinates for REC3
int mlbDownX4 = 0, mlbDownY4 = 0, mlbDownXD_R4 = 0, mlbDownYD_R4 = 0; //--- Variables for mouse down coordinates for REC4
int mlbDownX5 = 0, mlbDownY5 = 0, mlbDownXD_R5 = 0, mlbDownYD_R5 = 0; //--- Variables for mouse down coordinates for REC5

bool movingState_R1 = false; //--- Flag for REC1 movement state
bool movingState_R3 = false; //--- Flag for REC3 movement state
bool movingState_R5 = false; //--- Flag for REC5 movement state

Zunächst definieren wir Variablen für die Funktion OnChartEvent, um Drag-and-Drop in unserem Handelsassistenten-Tool zu ermöglichen. „prevMouseState“ verfolgt Änderungen des Mausstatus, während „mlbDownX1“, „mlbDownY1“, „mlbDownXD_R1“, „mlbDownYD_R1“ (und ähnlich für „REC2“ bis „REC5“) die Maus- und Rechteckkoordinaten für „REC1“ (TP), „REC3“ (Eintrag) und „REC5“ (SL) bei Klicks speichern. Die booleschen Flags „movingState_R1“, „movingState_R3“ und „movingState_R5“ zeigen an, ob diese Rechtecke gezogen werden. Anhand dieser Kontrollvariablen können wir dann die Bewegung des Preisinstruments definieren.

if(id == CHARTEVENT_MOUSE_MOVE && tool_visible) { //--- Handle mouse move events when tool is visible
   int MouseD_X = (int)lparam; //--- Get mouse x-coordinate
   int MouseD_Y = (int)dparam; //--- Get mouse y-coordinate
   int MouseState = (int)sparam; //--- Get mouse state
   
   int XD_R1 = (int)ObjectGetInteger(0, REC1, OBJPROP_XDISTANCE); //--- Get REC1 x-distance
   int YD_R1 = (int)ObjectGetInteger(0, REC1, OBJPROP_YDISTANCE); //--- Get REC1 y-distance
   int XS_R1 = (int)ObjectGetInteger(0, REC1, OBJPROP_XSIZE);     //--- Get REC1 x-size
   int YS_R1 = (int)ObjectGetInteger(0, REC1, OBJPROP_YSIZE);     //--- Get REC1 y-size

   int XD_R2 = (int)ObjectGetInteger(0, REC2, OBJPROP_XDISTANCE); //--- Get REC2 x-distance
   int YD_R2 = (int)ObjectGetInteger(0, REC2, OBJPROP_YDISTANCE); //--- Get REC2 y-distance
   int XS_R2 = (int)ObjectGetInteger(0, REC2, OBJPROP_XSIZE);     //--- Get REC2 x-size
   int YS_R2 = (int)ObjectGetInteger(0, REC2, OBJPROP_YSIZE);     //--- Get REC2 y-size

   int XD_R3 = (int)ObjectGetInteger(0, REC3, OBJPROP_XDISTANCE); //--- Get REC3 x-distance
   int YD_R3 = (int)ObjectGetInteger(0, REC3, OBJPROP_YDISTANCE); //--- Get REC3 y-distance
   int XS_R3 = (int)ObjectGetInteger(0, REC3, OBJPROP_XSIZE);     //--- Get REC3 x-size
   int YS_R3 = (int)ObjectGetInteger(0, REC3, OBJPROP_YSIZE);     //--- Get REC3 y-size

   int XD_R4 = (int)ObjectGetInteger(0, REC4, OBJPROP_XDISTANCE); //--- Get REC4 x-distance
   int YD_R4 = (int)ObjectGetInteger(0, REC4, OBJPROP_YDISTANCE); //--- Get REC4 y-distance
   int XS_R4 = (int)ObjectGetInteger(0, REC4, OBJPROP_XSIZE);     //--- Get REC4 x-size
   int YS_R4 = (int)ObjectGetInteger(0, REC4, OBJPROP_YSIZE);     //--- Get REC4 y-size

   int XD_R5 = (int)ObjectGetInteger(0, REC5, OBJPROP_XDISTANCE); //--- Get REC5 x-distance
   int YD_R5 = (int)ObjectGetInteger(0, REC5, OBJPROP_YDISTANCE); //--- Get REC5 y-distance
   int XS_R5 = (int)ObjectGetInteger(0, REC5, OBJPROP_XSIZE);     //--- Get REC5 x-size
   int YS_R5 = (int)ObjectGetInteger(0, REC5, OBJPROP_YSIZE);     //--- Get REC5 y-size

   if(prevMouseState == 0 && MouseState == 1) { //--- Check for mouse button down
      mlbDownX1 = MouseD_X; //--- Store mouse x-coordinate for REC1
      mlbDownY1 = MouseD_Y; //--- Store mouse y-coordinate for REC1
      mlbDownXD_R1 = XD_R1; //--- Store REC1 x-distance
      mlbDownYD_R1 = YD_R1; //--- Store REC1 y-distance
      
      mlbDownX2 = MouseD_X; //--- Store mouse x-coordinate for REC2
      mlbDownY2 = MouseD_Y; //--- Store mouse y-coordinate for REC2
      mlbDownXD_R2 = XD_R2; //--- Store REC2 x-distance
      mlbDownYD_R2 = YD_R2; //--- Store REC2 y-distance

      mlbDownX3 = MouseD_X; //--- Store mouse x-coordinate for REC3
      mlbDownY3 = MouseD_Y; //--- Store mouse y-coordinate for REC3
      mlbDownXD_R3 = XD_R3; //--- Store REC3 x-distance
      mlbDownYD_R3 = YD_R3; //--- Store REC3 y-distance
      
      mlbDownX4 = MouseD_X; //--- Store mouse x-coordinate for REC4
      mlbDownY4 = MouseD_Y; //--- Store mouse y-coordinate for REC4
      mlbDownXD_R4 = XD_R4; //--- Store REC4 x-distance
      mlbDownYD_R4 = YD_R4; //--- Store REC4 y-distance

      mlbDownX5 = MouseD_X; //--- Store mouse x-coordinate for REC5
      mlbDownY5 = MouseD_Y; //--- Store mouse y-coordinate for REC5
      mlbDownXD_R5 = XD_R5; //--- Store REC5 x-distance
      mlbDownYD_R5 = YD_R5; //--- Store REC5 y-distance

      if(MouseD_X >= XD_R1 && MouseD_X <= XD_R1 + XS_R1 && //--- Check if mouse is within REC1 bounds
         MouseD_Y >= YD_R1 && MouseD_Y <= YD_R1 + YS_R1) {
         movingState_R1 = true;                            //--- Enable REC1 movement
      }
      if(MouseD_X >= XD_R3 && MouseD_X <= XD_R3 + XS_R3 && //--- Check if mouse is within REC3 bounds
         MouseD_Y >= YD_R3 && MouseD_Y <= YD_R3 + YS_R3) {
         movingState_R3 = true;                            //--- Enable REC3 movement
      }
      if(MouseD_X >= XD_R5 && MouseD_X <= XD_R5 + XS_R5 && //--- Check if mouse is within REC5 bounds
         MouseD_Y >= YD_R5 && MouseD_Y <= YD_R5 + YS_R5) {
         movingState_R5 = true;                            //--- Enable REC5 movement
      }
   }
   if(movingState_R1) {                                                                        //--- Handle REC1 (TP) movement
      ChartSetInteger(0, CHART_MOUSE_SCROLL, false);                                           //--- Disable chart scrolling
      bool canMove = false;                                                                    //--- Flag to check if movement is valid
      if(selected_order_type == "BUY_STOP" || selected_order_type == "BUY_LIMIT") {            //--- Check for buy orders
         if(YD_R1 + YS_R1 < YD_R3) {                                                           //--- Ensure TP is above entry for buy orders
            canMove = true;                                                                    //--- Allow movement
            ObjectSetInteger(0, REC1, OBJPROP_YDISTANCE, mlbDownYD_R1 + MouseD_Y - mlbDownY1); //--- Update REC1 y-position
            ObjectSetInteger(0, REC2, OBJPROP_YDISTANCE, YD_R1 + YS_R1);                       //--- Update REC2 y-position
            ObjectSetInteger(0, REC2, OBJPROP_YSIZE, YD_R3 - (YD_R1 + YS_R1));                 //--- Update REC2 y-size
         }
      }
      else {                                                                                   //--- Handle sell orders
         if(YD_R1 > YD_R3 + YS_R3) {                                                           //--- Ensure TP is below entry for sell orders
            canMove = true;                                                                    //--- Allow movement
            ObjectSetInteger(0, REC1, OBJPROP_YDISTANCE, mlbDownYD_R1 + MouseD_Y - mlbDownY1); //--- Update REC1 y-position
            ObjectSetInteger(0, REC4, OBJPROP_YDISTANCE, YD_R3 + YS_R3);                       //--- Update REC4 y-position
            ObjectSetInteger(0, REC4, OBJPROP_YSIZE, YD_R1 - (YD_R3 + YS_R3));                 //--- Update REC4 y-size
         }
      }
      
      if(canMove) {           //--- If movement is valid
         datetime dt_TP = 0;  //--- Variable for TP time
         double price_TP = 0; //--- Variable for TP price
         int window = 0;      //--- Chart window
         
         ChartXYToTimePrice(0, XD_R1, YD_R1 + YS_R1, window, dt_TP, price_TP); //--- Convert chart coordinates to time and price
         ObjectSetInteger(0, TP_HL, OBJPROP_TIME, dt_TP);                      //--- Update TP horizontal line time
         ObjectSetDouble(0, TP_HL, OBJPROP_PRICE, price_TP);                   //--- Update TP horizontal line price
         
         update_Text(REC1, "TP: " + DoubleToString(MathAbs((Get_Price_d(TP_HL) - Get_Price_d(PR_HL)) / _Point), 0) + " Points | " + Get_Price_s(TP_HL)); //--- Update REC1 text
         update_Text(TP_LABEL, "TP: " + Get_Price_s(TP_HL));                                                                                             //--- Update TP label text
      }

      ChartRedraw(0); //--- Redraw chart
   }
   
   if(movingState_R5) {                                                                        //--- Handle REC5 (SL) movement
      ChartSetInteger(0, CHART_MOUSE_SCROLL, false);                                           //--- Disable chart scrolling
      bool canMove = false;                                                                    //--- Flag to check if movement is valid
      if(selected_order_type == "BUY_STOP" || selected_order_type == "BUY_LIMIT") {            //--- Check for buy orders
         if(YD_R5 > YD_R4) {                                                                   //--- Ensure SL is below entry for buy orders
            canMove = true;                                                                    //--- Allow movement
            ObjectSetInteger(0, REC5, OBJPROP_YDISTANCE, mlbDownYD_R5 + MouseD_Y - mlbDownY5); //--- Update REC5 y-position
            ObjectSetInteger(0, REC4, OBJPROP_YDISTANCE, YD_R3 + YS_R3);                       //--- Update REC4 y-position
            ObjectSetInteger(0, REC4, OBJPROP_YSIZE, YD_R5 - (YD_R3 + YS_R3));                 //--- Update REC4 y-size
         }
      }
      else {                                                                                   //--- Handle sell orders
         if(YD_R5 + YS_R5 < YD_R3) {                                                           //--- Ensure SL is above entry for sell orders
            canMove = true;                                                                    //--- Allow movement
            ObjectSetInteger(0, REC5, OBJPROP_YDISTANCE, mlbDownYD_R5 + MouseD_Y - mlbDownY5); //--- Update REC5 y-position
            ObjectSetInteger(0, REC2, OBJPROP_YDISTANCE, YD_R5 + YS_R5);                       //--- Update REC2 y-position
            ObjectSetInteger(0, REC2, OBJPROP_YSIZE, YD_R3 - (YD_R5 + YS_R5));                 //--- Update REC2 y-size
         }
      }
      
      if(canMove) {           //--- If movement is valid
         datetime dt_SL = 0;  //--- Variable for SL time
         double price_SL = 0; //--- Variable for SL price
         int window = 0;      //--- Chart window
         
         ChartXYToTimePrice(0, XD_R5, YD_R5 + YS_R5, window, dt_SL, price_SL); //--- Convert chart coordinates to time and price
         ObjectSetInteger(0, SL_HL, OBJPROP_TIME, dt_SL);                      //--- Update SL horizontal line time
         ObjectSetDouble(0, SL_HL, OBJPROP_PRICE, price_SL);                   //--- Update SL horizontal line price
         
         update_Text(REC5, "SL: " + DoubleToString(MathAbs((Get_Price_d(PR_HL) - Get_Price_d(SL_HL)) / _Point), 0) + " Points | " + Get_Price_s(SL_HL)); //--- Update REC5 text
         update_Text(SL_LABEL, "SL: " + Get_Price_s(SL_HL));                                                                                             //--- Update SL label text
      }

      ChartRedraw(0); //--- Redraw chart
   }
   
   if(movingState_R3) {                                                                  //--- Handle REC3 (Entry) movement
      ChartSetInteger(0, CHART_MOUSE_SCROLL, false);                                     //--- Disable chart scrolling
      ObjectSetInteger(0, REC3, OBJPROP_XDISTANCE, mlbDownXD_R3 + MouseD_X - mlbDownX3); //--- Update REC3 x-position
      ObjectSetInteger(0, REC3, OBJPROP_YDISTANCE, mlbDownYD_R3 + MouseD_Y - mlbDownY3); //--- Update REC3 y-position
      
      ObjectSetInteger(0, REC1, OBJPROP_XDISTANCE, mlbDownXD_R1 + MouseD_X - mlbDownX1); //--- Update REC1 x-position
      ObjectSetInteger(0, REC1, OBJPROP_YDISTANCE, mlbDownYD_R1 + MouseD_Y - mlbDownY1); //--- Update REC1 y-position
      
      ObjectSetInteger(0, REC2, OBJPROP_XDISTANCE, mlbDownXD_R2 + MouseD_X - mlbDownX2); //--- Update REC2 x-position
      ObjectSetInteger(0, REC2, OBJPROP_YDISTANCE, mlbDownYD_R2 + MouseD_Y - mlbDownY2); //--- Update REC2 y-position

      ObjectSetInteger(0, REC4, OBJPROP_XDISTANCE, mlbDownXD_R4 + MouseD_X - mlbDownX4); //--- Update REC4 x-position
      ObjectSetInteger(0, REC4, OBJPROP_YDISTANCE, mlbDownYD_R4 + MouseD_Y - mlbDownY4); //--- Update REC4 y-position

      ObjectSetInteger(0, REC5, OBJPROP_XDISTANCE, mlbDownXD_R5 + MouseD_X - mlbDownX5); //--- Update REC5 x-position
      ObjectSetInteger(0, REC5, OBJPROP_YDISTANCE, mlbDownYD_R5 + MouseD_Y - mlbDownY5); //--- Update REC5 y-position

      datetime dt_PRC = 0, dt_SL1 = 0, dt_TP1 = 0;        //--- Variables for time
      double price_PRC = 0, price_SL1 = 0, price_TP1 = 0; //--- Variables for price
      int window = 0;                                     //--- Chart window
      
      ChartXYToTimePrice(0, XD_R3, YD_R3 + YS_R3, window, dt_PRC, price_PRC); //--- Convert REC3 coordinates to time and price
      ChartXYToTimePrice(0, XD_R5, YD_R5 + YS_R5, window, dt_SL1, price_SL1); //--- Convert REC5 coordinates to time and price
      ChartXYToTimePrice(0, XD_R1, YD_R1 + YS_R1, window, dt_TP1, price_TP1); //--- Convert REC1 coordinates to time and price

      ObjectSetInteger(0, PR_HL, OBJPROP_TIME, dt_PRC);    //--- Update entry horizontal line time
      ObjectSetDouble(0, PR_HL, OBJPROP_PRICE, price_PRC); //--- Update entry horizontal line price
      
      ObjectSetInteger(0, TP_HL, OBJPROP_TIME, dt_TP1);    //--- Update TP horizontal line time
      ObjectSetDouble(0, TP_HL, OBJPROP_PRICE, price_TP1); //--- Update TP horizontal line price
      
      ObjectSetInteger(0, SL_HL, OBJPROP_TIME, dt_SL1);    //--- Update SL horizontal line time
      ObjectSetDouble(0, SL_HL, OBJPROP_PRICE, price_SL1); //--- Update SL horizontal line price

      update_Text(REC1, "TP: " + DoubleToString(MathAbs((Get_Price_d(TP_HL) - Get_Price_d(PR_HL)) / _Point), 0) + " Points | " + Get_Price_s(TP_HL)); //--- Update REC1 text
      update_Text(REC3, selected_order_type + ": | Lot: " + DoubleToString(lot_size, 2) + " | " + Get_Price_s(PR_HL));                                //--- Update REC3 text
      update_Text(REC5, "SL: " + DoubleToString(MathAbs((Get_Price_d(PR_HL) - Get_Price_d(SL_HL)) / _Point), 0) + " Points | " + Get_Price_s(SL_HL)); //--- Update REC5 text
      update_Text(PRICE_LABEL, "Entry: " + Get_Price_s(PR_HL));                                                                                       //--- Update entry label text
      update_Text(SL_LABEL, "SL: " + Get_Price_s(SL_HL));                                                                                             //--- Update SL label text
      update_Text(TP_LABEL, "TP: " + Get_Price_s(TP_HL));                                                                                             //--- Update TP label text

      ChartRedraw(0); //--- Redraw chart
   }

   if(MouseState == 0) {                            //--- Check if mouse button is released
      movingState_R1 = false;                       //--- Disable REC1 movement
      movingState_R3 = false;                       //--- Disable REC3 movement
      movingState_R5 = false;                       //--- Disable REC5 movement
      ChartSetInteger(0, CHART_MOUSE_SCROLL, true); //--- Enable chart scrolling
   }
   prevMouseState = MouseState;                     //--- Update previous mouse state
}

Hier erweitern wir die Funktion OnChartEvent, um Mausbewegungen für das Ziehen von Chart-Objekten in unserem Werkzeug zu verarbeiten, wenn „tool_visible“ wahr ist und „id“ CHARTEVENT_MOUSE_MOVE ist. Wir extrahieren „MouseD_X“, „MouseD_Y“, und „MouseState“ aus „lparam“, „dparam“, und „sparam“und verwenden die Funktion ObjectGetInteger, um Position und Größe zu ermitteln („XD_R1“, „YD_R1“, „XS_R1“, „YS_R1“ für „REC1“, und ähnlich für „REC2“ bis „REC5“).

Bei einem Mausklick („prevMouseState“ 0 bis „MouseState“ 1) speichern wir die Mauskoordinaten in „mlbDownX1“, „mlbDownY1“ und die Rechteckpositionen in „mlbDownXD_R1“, „mlbDownYD_R1“ (und für „REC2“ bis „REC5“), wobei „movingState_R1“, „movingState_R3“ oder „movingState_R5“ auf true gesetzt wird, wenn der Klick innerhalb der Grenzen von „REC1“, „REC3“ oder „REC5“ liegt.

Für „movingState_R1“ (TP) deaktivieren wir das Scrollen mit ChartSetInteger, validieren die TP-Position (oberhalb der Eingabe für „BUY_STOP“/“BUY_LIMIT“, unterhalb für „Sell“), aktualisieren „REC1“ und „REC2“/“REC4“ Positionen und Größen mit ObjectSetInteger aktualisieren, Koordinaten mit ChartXYToTimePrice in Preise umwandeln, „TP_HL“ mit ObjectSetDouble aktualisieren und Text mit „update_Text“, „Get_Price_d“, „Get_Price_s“, DoubleToString und MathAbs aktualisieren.

In ähnlicher Weise wird für „movingState_R5“ (SL) „REC5“ und „REC4“/“REC2“ angepasst, „SL_HL“ aktualisiert und der Text aktualisiert. Für „movingState_R3“ (Eintrag) verschieben wir alle Rechtecke und aktualisieren „PR_HL“, „TP_HL“, „SL_HL“ und den Text.

Beim Loslassen der Maus („MouseState“ 0) setzen wir die „movingState“-Flags zurück, aktivieren den Bildlauf und aktualisieren „prevMouseState“, indem wir ChartRedraw aufrufen, um die Änderungen zu berücksichtigen. Schließlich müssen wir die Objekte vollständig löschen, wenn wir das Programm entfernen, und die Losgröße auf dem Häkchen aktualisieren, um die Änderungen widerzuspiegeln, obwohl Sie dies lassen können, da es nicht sehr notwendig ist.

//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason) {
   deleteObjects(); //--- Delete tool objects
   deletePanel();   //--- Delete control panel objects
}

//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick() {
   // Update lot size from edit field
   string lot_text = ObjectGetString(0, LOT_EDIT, OBJPROP_TEXT); //--- Get lot size text from edit field
   double new_lot = StringToDouble(lot_text);                    //--- Convert lot size text to double
   if(new_lot > 0) lot_size = new_lot;                           //--- Update lot size if valid
}

Hier implementieren wir zwei wesentliche Event-Handler für unser Tool: OnDeinit und OnTick. In der Funktion OnDeinit, die ausgelöst wird, wenn der Expert Advisor aus dem Chart entfernt wird, rufen wir die Funktion „deleteObjects“ auf, um Chart-Objekte wie „REC1“ bis „REC5“, „TP_HL“, „SL_HL“ und „PR_HL“ zu entfernen, und die Funktion „deletePanel“, um Kontrollfeldobjekte wie „PANEL_BG“, „LOT_EDIT“ und Schaltflächen wie „BUY_STOP_BTN“ zu löschen und so einen sauberen Ausstieg zu gewährleisten.

In der Funktion OnTick, die bei jedem Preis-Tick ausgeführt wird, verwenden wir die Funktion ObjectGetString, um den Text aus dem Feld „LOT_EDIT“ abzurufen, ihn mit der Funktion StringToDouble in ein Double zu konvertieren und die Variable „lot_size“ zu aktualisieren, wenn der Wert „new_lot“ positiv ist, damit die Losgröße unseres Tools mit den Nutzereingaben synchronisiert wird.

Nach dem Kompilieren erhalten wir die folgende Ausgabe.

ENDGÜLTIGE AUSGABE

Anhand der Visualisierung können wir sehen, dass beim Klicken auf eine der Handelsschaltflächen das entsprechende Handelspreis-Tool generiert wird. Wenn es dann gezogen wird, wird es in Echtzeit aktualisiert und die Preise werden im Handelspanel angezeigt, damit sie für Handelszwecke verwendet werden können, wenn auf die Schaltfläche geklickt wird, die den Handel platziert, werden die entsprechenden Handelsgeschäfte dynamisch platziert. Damit ist sichergestellt, dass wir unser Ziel erreicht haben, und es bleibt nur noch, die Platte zu testen, um sicherzustellen, dass sie perfekt ist, und das wird im nächsten Abschnitt behandelt.


Backtests

Wir haben die Tests durchgeführt und zeigen hier die kompilierte Visualisierung in einem einzigen Graphics Interchange Format (GIF) Bitmap-Bildformat.

BACKTEST-AUSGABE


Schlussfolgerung

Abschließend haben wir in MQL5 ein interaktives Handelsassistententool entwickelt, das visuelle Präzision mit intuitiven Bedienelementen kombiniert und die Platzierung von schwebenden Aufträgen vereinfacht. Wir haben gezeigt, wie man die nutzerfreundliche grafische Nutzeroberfläche (GUI) gestaltet, sie mit Funktionen wie „createControlPanel“ und „placeOrder“ implementiert und ihre Zuverlässigkeit durch strukturierte Implementierung und Validierung sicherstellt. Sie können dieses Tool an Ihren Handelsstil anpassen und so die Effizienz Ihrer Orderplatzierung verbessern. Freuen Sie sich auf die vorangehenden Teile, in denen wir fortgeschrittene Funktionen wie Risikomanagement und verschiebbare Panels vorstellen werden. Bleiben Sie am Ball.

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

Beigefügte Dateien |
Erstellen eines Handelsadministrator-Panels in MQL5 (Teil XI): Modernes Merkmal Kommunikationsschnittstelle (I) Erstellen eines Handelsadministrator-Panels in MQL5 (Teil XI): Modernes Merkmal Kommunikationsschnittstelle (I)
Heute konzentrieren wir uns auf die Verbesserung der Messaging-Schnittstelle des Kommunikationspanels, um sie an die Standards moderner, leistungsstarker Kommunikationsanwendungen anzupassen. Diese Verbesserung wird durch eine Aktualisierung der Klasse CommunicationsDialog erreicht. Begleiten Sie uns in diesem Artikel und in der Diskussion, wenn wir die wichtigsten Erkenntnisse erkunden und die nächsten Schritte bei der Weiterentwicklung der Schnittstellenprogrammierung mit MQL5 skizzieren.
Von der Grundstufe bis zur Mittelstufe: Templates und Typename (II) Von der Grundstufe bis zur Mittelstufe: Templates und Typename (II)
In diesem Artikel wird erklärt, wie man mit einer der schwierigsten Programmiersituationen umgeht, die einem begegnen kann: die Verwendung verschiedener Typen in derselben Funktion oder Prozedur-Template. Obwohl wir uns die meiste Zeit nur auf Funktionen konzentriert haben, ist alles, was hier behandelt wurde, nützlich und kann auf Prozeduren angewendet werden.
MQL5-Assistenten-Techniken, die Sie kennen sollten (Teil 62): Nutzung der Muster von ADX und CCI mit Reinforcement-Learning TRPO MQL5-Assistenten-Techniken, die Sie kennen sollten (Teil 62): Nutzung der Muster von ADX und CCI mit Reinforcement-Learning TRPO
Der ADX-Oszillator und der CCI-Oszillator sind Trendfolge- und Momentum-Indikatoren, die bei der Entwicklung eines Expert Advisors miteinander kombiniert werden können. Wir machen dort weiter, wo wir im letzten Artikel aufgehört haben, indem wir untersuchen, wie das Training in der Praxis und die Aktualisierung unseres entwickelten Modells dank des Verstärkungslernens erfolgen kann. Wir verwenden einen Algorithmus, den wir in dieser Serie noch behandeln werden, die sogenannte Trusted Region Policy Optimization (Optimierung vertrauenswürdiger Regionen). Und wie immer erlaubt uns die Zusammenstellung von Expert Advisors durch den MQL5-Assistenten, unser(e) Modell(e) zum Testen viel schneller und auch so einzurichten, dass es mit verschiedenen Signaltypen verteilt und getestet werden kann.
Population ADAM (Adaptive Moment Estimation) Population ADAM (Adaptive Moment Estimation)
Der Artikel stellt die Umwandlung des bekannten und beliebten ADAM-Gradientenoptimierungsverfahrens in einen Populationsalgorithmus und dessen Modifikation durch die Einführung hybrider Individuen vor. Der neue Ansatz ermöglicht die Schaffung von Agenten, die Elemente erfolgreicher Entscheidungen mit Hilfe von Wahrscheinlichkeitsverteilungen kombinieren. Die wichtigste Innovation ist die Bildung hybrider Populationen, die adaptiv Informationen aus den vielversprechendsten Lösungen sammeln und so die Effizienz der Suche in komplexen mehrdimensionalen Räumen erhöhen.