English
preview
MQL5-Handelswerkzeuge (Teil 2): Verbesserung des interaktiven Handelsassistenten durch dynamisches, visuelles Feedback

MQL5-Handelswerkzeuge (Teil 2): Verbesserung des interaktiven Handelsassistenten durch dynamisches, visuelles Feedback

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

Einführung

In unserem vorherigen Artikel, Teil 1, haben wir ein Trade Assistant Tool in MetaQuotes Language 5 (MQL5) für MetaTrader 5 erstellt, um die Platzierung von schwebenden Aufträgen zu vereinfachen. Jetzt gehen wir noch einen Schritt weiter, indem wir die Interaktivität durch dynamisches visuelles Feedback verbessern. Wir führen Funktionen wie ein verschiebbares Bedienfeld, Hover-Effekte für eine intuitive Navigation und Auftragsvalidierung in Echtzeit ein, um sicherzustellen, dass unsere Handels-Setups präzise und marktrelevant sind. Wir behandeln diese Fortschritte anhand der folgenden Unterthemen:

  1. Konzeptionelle Erweiterungen für verbesserte Interaktivität
  2. Implementation in MQL5
  3. Backtests
  4. Schlussfolgerung

Diese Abschnitte führen uns zu einem reaktionsschnellen, intuitiven und nutzerfreundlichen Handelsinstrument.


Konzeptionelle Erweiterungen für verbesserte Interaktivität

Wir wollen unsern Handelsassistenten verbessern, indem wir es intuitiver und anpassungsfähiger machen. Wir beginnen mit einem verschiebbaren Kontrollfeld, das wir frei auf dem Handels-Chart positionieren können. Dank dieser Flexibilität können wir die Oberfläche an unsere Arbeitsabläufe anpassen, unabhängig davon, ob wir mehrere Charts verwalten oder uns auf ein einzelnes Handels-Setup konzentrieren. Darüber hinaus werden wir Hover-Effekte integrieren, um Schaltflächen und Chart-Elemente hervorzuheben, wenn der Mauszeiger darüber fährt, und so ein sofortiges visuelles Feedback zu geben, das die Navigation vereinfacht und Fehler minimiert.

Die Validierung von Aufträgen in Echtzeit wird eine weitere wichtige Verbesserung sein, die sicherstellt, dass unsere Einstiegs-, Stop-Loss- und Take-Profit-Levels vor der Ausführung logisch mit den aktuellen Marktpreisen abgeglichen werden. Diese Funktion wird unser Vertrauen stärken, indem sie ungültige Handelsvorbereitungen verhindert und die Einfachheit beibehält, während sie die Präzision erhöht. Gemeinsam schaffen diese Verbesserungen ein reaktionsschnelles, nutzerorientiertes Tool, das unsere Handelsentscheidungen unterstützt und die Voraussetzungen für künftige Weiterentwicklungen wie Risikomanagementfunktionen schafft. Im Folgenden finden Sie eine kurze Darstellung dessen, was wir erreichen wollen.

VISUALISIERUNG DES ZIELS


Implementation in MQL5

Um unsere Ziele mit MQL5 zu erreichen, müssen wir zunächst einige zusätzliche Panel-Objekte und Bestätigungsvariablen für Ziehen und dem Hover-Effekt definieren, die wir verwenden werden, um die Interaktionen des Nutzers mit dem Panel oder dem Preis-Tool zu verfolgen (siehe unten).

// Control panel object names
#define PANEL_BG        "PANEL_BG"        //--- Define constant for panel background object name
#define PANEL_HEADER    "PANEL_HEADER"    //--- Define constant for panel header 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

// Variables for dragging panel
bool panel_dragging   = false;            //--- Flag to track if panel is being dragged
int  panel_drag_x     = 0, 
     panel_drag_y     = 0;                //--- Mouse coordinates when drag starts
int  panel_start_x    = 0, 
     panel_start_y    = 0;                //--- Panel coordinates when drag starts

// Button and rectangle hover states
bool buy_stop_hovered    = false;         //--- Buy Stop button hover state
bool sell_stop_hovered   = false;         //--- Sell Stop button hover state
bool buy_limit_hovered   = false;         //--- Buy Limit button hover state
bool sell_limit_hovered  = false;         //--- Sell Limit button hover state
bool place_order_hovered = false;         //--- Place Order button hover state
bool cancel_hovered      = false;         //--- Cancel button hover state
bool close_hovered       = false;         //--- Close button hover state
bool header_hovered      = false;         //--- Header hover state
bool rec1_hovered        = false;         //--- REC1 (TP) hover state
bool rec3_hovered        = false;         //--- REC3 (Entry) hover state
bool rec5_hovered        = false;         //--- REC5 (SL) hover state

Wir beginnen mit der Implementierung der erweiterten Interaktivitätsfunktionen für unser Tool, indem wir Schlüsselvariablen definieren, die das Ziehen von Feldern und Hover-Effekte in der Schnittstelle MetaTrader 5 ermöglichen. Wir verwenden die Direktive #define, um eine Konstante „PANEL_HEADER“ für das Header-Objekt des Panels zu erstellen, das als verschiebbarer Bereich des Bedienfelds dient. Um das Ziehen zu unterstützen, deklarieren wir „panel_dragging“ als boolesches Flag, um zu verfolgen, wann das Panel bewegt wird, und die Integerwerte „panel_drag_x“, „panel_drag_y“, um die Mauskoordinaten zu Beginn des Ziehens zu speichern, und „panel_start_x“, „panel_start_y“, um die Anfangsposition des Panels zu erfassen, damit wir seine neue Position während der Bewegung berechnen können.

Wir führen auch boolesche Variablen ein, um Hover-Zustände für Schaltflächen und Chart-Rechtecke zu verwalten, einschließlich „buy_stop_hovered“, „sell_stop_hovered“, „buy_limit_hovered“, „sell_limit_hovered“, „place_order_hovered“, „cancel_hovered“, „close_hovered“ und „header_hovered“ für die jeweiligen Schaltflächen und die Kopfzeile des Panels sowie „rec1_hovered“, „rec3_hovered“ und „rec5_hovered“ für die Take-Profit-, Entry- und Stop-Loss-Rechtecke. Anhand dieser Variablen können wir erkennen, wenn der Mauszeiger über diesen Elementen verweilt, und visuelle Rückmeldungen wie Farbänderungen auslösen, um die Navigation und Interaktion innerhalb der Nutzeroberfläche des Tools zu verbessern. Als Nächstes müssen wir die Werte des Preistools ermitteln und für den Handel validieren.

//+------------------------------------------------------------------+
//| Check if order setup is valid                                    |
//+------------------------------------------------------------------+
bool isOrderValid() {
   if(!tool_visible) return true;                                     //--- No validation needed if tool is not visible
   double current_price = SymbolInfoDouble(Symbol(), SYMBOL_BID);     //--- Get current bid price
   double entry_price   = Get_Price_d(PR_HL);                         //--- Get entry price
   double sl_price      = Get_Price_d(SL_HL);                         //--- Get stop-loss price
   double tp_price      = Get_Price_d(TP_HL);                         //--- Get take-profit price

   if(selected_order_type == "BUY_STOP") {
      //--- Buy Stop: Entry must be above current price, TP above entry, SL below entry
      if(entry_price <= current_price || tp_price <= entry_price || sl_price >= entry_price) {
         return false;
      }
   }
   else if(selected_order_type == "SELL_STOP") {
      //--- Sell Stop: Entry must be below current price, TP below entry, SL above entry
      if(entry_price >= current_price || tp_price >= entry_price || sl_price <= entry_price) {
         return false;
      }
   }
   else if(selected_order_type == "BUY_LIMIT") {
      //--- Buy Limit: Entry must be below current price, TP above entry, SL below entry
      if(entry_price >= current_price || tp_price <= entry_price || sl_price >= entry_price) {
         return false;
      }
   }
   else if(selected_order_type == "SELL_LIMIT") {
      //--- Sell Limit: Entry must be above current price, TP below entry, SL above entry
      if(entry_price <= current_price || tp_price >= entry_price || sl_price <= entry_price) {
         return false;
      }
   }
   return true;                                                       //--- Order setup is valid
}

Hier implementieren wir die Funktion „isOrderValid“, um unser Tool zu verbessern, indem wir das Order-Setup in Echtzeit validieren und so sicherstellen, dass unsere Trades mit den Marktbedingungen übereinstimmen. Wir beginnen mit der Überprüfung, ob „tool_visible“ falsch ist, und geben „true“ zurück, um die Validierung zu überspringen, wenn das Werkzeug nicht aktiv ist. Wir rufen den aktuellen Marktpreis mit der Funktion SymbolInfoDouble mit SYMBOL_BID ab und ermitteln den Einstiegspreis („entry_price“), den Stop-Loss-Preis („sl_price“) und den Take-Profit-Preis („tp_price“) mit der Funktion „Get_Price_d“ für „PR_HL“, „SL_HL“ und „TP_HL“.

Für „BUY_STOP“ überprüfen wir, ob „entry_price“ über „current_price“, „tp_price“ über „entry_price“ und „sl_price“ unter „entry_price“ liegt; für „SELL_STOP“, „entry_price“ unter „current_price“, „tp_price“ unter „entry_price“ und „sl_price“ über „entry_price“; für „BUY_LIMIT“, „entry_price“ unter „current_price“, „tp_price“ über „entry_price“ und „sl_price“ unter „entry_price“; und für „SELL_LIMIT“, „entry_price“ über „current_price“, „tp_price“ unter „entry_price“ und „sl_price“ über „entry_price“, wobei „false“ zurückgegeben wird, wenn eine Bedingung fehlschlägt, oder „true“, wenn sie gültig sind. Dann können wir die Farben der Rechtecke entsprechend der Gültigkeit der Bestellung aktualisieren.

//+------------------------------------------------------------------+
//| Update rectangle colors based on order validity and hover        |
//+------------------------------------------------------------------+
void updateRectangleColors() {
   if(!tool_visible) return;                                                             //--- Skip if tool is not visible
   bool is_valid = isOrderValid();                                                       //--- Check order validity

   if(!is_valid) {
      //--- Gray out REC1 and REC5 if order is invalid, with hover effect
      ObjectSetInteger(0, REC1, OBJPROP_BGCOLOR, rec1_hovered ? C'100,100,100' : clrGray);
      ObjectSetInteger(0, REC5, OBJPROP_BGCOLOR, rec5_hovered ? C'100,100,100' : clrGray);
   }
   else {
      //--- Restore original colors based on order type and hover state
      if(selected_order_type == "BUY_STOP" || selected_order_type == "BUY_LIMIT") {
         ObjectSetInteger(0, REC1, OBJPROP_BGCOLOR, rec1_hovered ? C'0,100,0'   : clrGreen); //--- TP rectangle (dark green on hover)
         ObjectSetInteger(0, REC5, OBJPROP_BGCOLOR, rec5_hovered ? C'139,0,0'   : clrRed);   //--- SL rectangle (dark red on hover)
      }
      else {
         ObjectSetInteger(0, REC1, OBJPROP_BGCOLOR, rec1_hovered ? C'0,100,0'   : clrGreen); //--- TP rectangle (dark green on hover)
         ObjectSetInteger(0, REC5, OBJPROP_BGCOLOR, rec5_hovered ? C'139,0,0'   : clrRed);   //--- SL rectangle (dark red on hover)
      }
   }

   ObjectSetInteger(0, REC3, OBJPROP_BGCOLOR, rec3_hovered ? C'105,105,105' : clrLightGray); //--- Entry rectangle (darker gray on hover)
   ChartRedraw(0);                                                                          //--- Redraw chart
}

Wir implementieren die Funktion „updateRectangleColors“, um das visuelle Feedback unseres Tools zu verbessern, indem wir die Farben der Chart-Rechtecke basierend auf der Gültigkeit der Reihenfolge und dem Hover-Status aktualisieren. Wir überspringen, wenn „tool_visible“ falsch ist, überprüfen die Gültigkeit mit der Funktion „isOrderValid“ und verwenden die Funktion ObjectSetInteger, um „REC1“ (TP) und „REC5“ (SL) auf Grau („clrGray“ oder „C‘100,100,100‘“, wenn „rec1_hovered“/„rec5_hovered“) zu setzen, wenn sie ungültig sind, oder Grün/Rot („clrGreen“/„clrRed“ oder „C‘0,100,0‘“/„C‘139,0,0‘“ beim Darüberfahren) ist gültig für „BUY_STOP“/„BUY_LIMIT“/Verkaufsaufträge, und „REC3“ (Eintrag) auf Hellgrau („clrLightGray“ oder „C‘105,105,105‘“, wenn „rec3_hovered“), und rufen ChartRedraw auf, um das Diagramm zu aktualisieren.

Danach müssen wir die Hover-Zustände der Schaltflächen wie unten dargestellt abrufen.

//+------------------------------------------------------------------+
//| Update button and header hover state                             |
//+------------------------------------------------------------------+
void updateButtonHoverState(int mouse_x, int mouse_y) {
   // Define button names and their properties
   string buttons[] = {BUY_STOP_BTN, SELL_STOP_BTN, BUY_LIMIT_BTN, SELL_LIMIT_BTN, PLACE_ORDER_BTN, CANCEL_BTN, CLOSE_BTN};
   bool hover_states[] = {buy_stop_hovered, sell_stop_hovered, buy_limit_hovered, sell_limit_hovered, place_order_hovered, cancel_hovered, close_hovered};
   color normal_colors[] = {clrForestGreen, clrFireBrick, clrForestGreen, clrFireBrick, clrDodgerBlue, clrSlateGray, clrCrimson};
   color hover_color = clrDodgerBlue;                      //--- Bluish color for hover
   color hover_border = clrBlue;                           //--- Bluish border for hover

   for(int i = 0; i < ArraySize(buttons); i++) {
      int x = (int)ObjectGetInteger(0, buttons[i], OBJPROP_XDISTANCE);
      int y = (int)ObjectGetInteger(0, buttons[i], OBJPROP_YDISTANCE);
      int width = (int)ObjectGetInteger(0, buttons[i], OBJPROP_XSIZE);
      int height = (int)ObjectGetInteger(0, buttons[i], OBJPROP_YSIZE);

      bool is_hovered = (mouse_x >= x && mouse_x <= x + width && mouse_y >= y && mouse_y <= y + height);

      if(is_hovered && !hover_states[i]) {
         // Mouse entered button
         ObjectSetInteger(0, buttons[i], OBJPROP_BGCOLOR, hover_color);
         ObjectSetInteger(0, buttons[i], OBJPROP_BORDER_COLOR, hover_border);
         hover_states[i] = true;
      }
      else if(!is_hovered && hover_states[i]) {
         // Mouse left button
         ObjectSetInteger(0, buttons[i], OBJPROP_BGCOLOR, normal_colors[i]);
         ObjectSetInteger(0, buttons[i], OBJPROP_BORDER_COLOR, clrBlack);
         hover_states[i] = false;
      }
   }

   // Update header hover state
   int header_x = (int)ObjectGetInteger(0, PANEL_HEADER, OBJPROP_XDISTANCE);
   int header_y = (int)ObjectGetInteger(0, PANEL_HEADER, OBJPROP_YDISTANCE);
   int header_width = (int)ObjectGetInteger(0, PANEL_HEADER, OBJPROP_XSIZE);
   int header_height = (int)ObjectGetInteger(0, PANEL_HEADER, OBJPROP_YSIZE);

   bool is_header_hovered = (mouse_x >= header_x && mouse_x <= header_x + header_width && mouse_y >= header_y && mouse_y <= header_y + header_height);

   if(is_header_hovered && !header_hovered) {
      ObjectSetInteger(0, PANEL_HEADER, OBJPROP_BGCOLOR, C'030,030,030'); //--- Darken header
      header_hovered = true;
   }
   else if(!is_header_hovered && header_hovered) {
      ObjectSetInteger(0, PANEL_HEADER, OBJPROP_BGCOLOR, C'050,050,050'); //--- Restore header color
      header_hovered = false;
   }

   // Update tool rectangle hover states
   if(tool_visible) {
      int x1 = (int)ObjectGetInteger(0, REC1, OBJPROP_XDISTANCE);
      int y1 = (int)ObjectGetInteger(0, REC1, OBJPROP_YDISTANCE);
      int width1 = (int)ObjectGetInteger(0, REC1, OBJPROP_XSIZE);
      int height1 = (int)ObjectGetInteger(0, REC1, OBJPROP_YSIZE);

      int x3 = (int)ObjectGetInteger(0, REC3, OBJPROP_XDISTANCE);
      int y3 = (int)ObjectGetInteger(0, REC3, OBJPROP_YDISTANCE);
      int width3 = (int)ObjectGetInteger(0, REC3, OBJPROP_XSIZE);
      int height3 = (int)ObjectGetInteger(0, REC3, OBJPROP_YSIZE);

      int x5 = (int)ObjectGetInteger(0, REC5, OBJPROP_XDISTANCE);
      int y5 = (int)ObjectGetInteger(0, REC5, OBJPROP_YDISTANCE);
      int width5 = (int)ObjectGetInteger(0, REC5, OBJPROP_XSIZE);
      int height5 = (int)ObjectGetInteger(0, REC5, OBJPROP_YSIZE);

      bool is_rec1_hovered = (mouse_x >= x1 && mouse_x <= x1 + width1 && mouse_y >= y1 && mouse_y <= y1 + height1);
      bool is_rec3_hovered = (mouse_x >= x3 && mouse_x <= x3 + width3 && mouse_y >= y3 && mouse_y <= y3 + height3);
      bool is_rec5_hovered = (mouse_x >= x5 && mouse_x <= x5 + width5 && mouse_y >= y5 && mouse_y <= y5 + height5);

      if(is_rec1_hovered != rec1_hovered || is_rec3_hovered != rec3_hovered || is_rec5_hovered != rec5_hovered) {
         rec1_hovered = is_rec1_hovered;
         rec3_hovered = is_rec3_hovered;
         rec5_hovered = is_rec5_hovered;
         updateRectangleColors();                            //--- Update colors based on hover state
      }
   }

   // Update hover state variables
   buy_stop_hovered = hover_states[0];
   sell_stop_hovered = hover_states[1];
   buy_limit_hovered = hover_states[2];
   sell_limit_hovered = hover_states[3];
   place_order_hovered = hover_states[4];
   cancel_hovered = hover_states[5];
   close_hovered = hover_states[6];

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

Um die Interaktivität unseres Tools durch die Verwaltung von Hover-Effekten für Schaltflächen und Chart-Elemente zu verbessern, implementieren wir die Funktion „updateButtonHoverState“. Wir definieren Arrays „buttons“ für die Namen der Schaltflächen („BUY_STOP_BTN“ bis „CLOSE_BTN“), „hover_states“ für ihre Hover-Flags („buy_stop_hovered“ bis „close_hovered“), und „normal_colors“ für die Standardfarben, mit „hover_color“ (clrDodgerBlue) und „hover_border“ („clrBlue“) für die Hover-Zustände.

Für jede Schaltfläche verwenden wir die Funktion ObjectGetInteger, um Position und Größe zu ermitteln, zu prüfen, ob „mouse_x“ und „mouse_y“ innerhalb der Grenzen liegen, und „OBJPROP_BGCOLOR“ und „OBJPROP_BORDER_COLOR“ auf „hover_color“ oder „normal_colors“ mit ObjectSetInteger, wobei „hover_states“ umgeschaltet wird.

Für den „PANEL_HEADER“ überprüfen wir auf ähnliche Weise den Schwebezustand, indem wir ihn mit ObjectSetInteger auf „C'030,030,030'“ verdunkeln oder „C'050,050,050'“ wiederherstellen. Bei „tool_visible“ werden die Begrenzungen „REC1“, „REC3“ und „REC5“ überprüft, „rec1_hovered“, „rec3_hovered“ und „rec5_hovered“ aktualisiert und bei Änderungen „updateRectangleColors“ aufgerufen. Wir synchronisieren „buy_stop_hovered“ mit „close_hovered“ mit „hover_states“ und rufen ChartRedraw auf, um das Chartzu aktualisieren. Dann können wir diese Funktionen innerhalb des OnChartEvent-Ereignishandlers aufrufen, sodass wir Aktualisierungen in Echtzeit erhalten können.

//+------------------------------------------------------------------+
//| 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
         showTool();                                             //--- Show trading tool
         update_Text(PLACE_ORDER_BTN, "Place Buy Stop");         //--- Update place order button text
         updateRectangleColors();                                //--- Update rectangle colors
      }
      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
         updateRectangleColors();                                //--- Update rectangle colors
      }
      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
         updateRectangleColors();                                //--- Update rectangle colors
      }
      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
         updateRectangleColors();                                //--- Update rectangle colors
      }
      else if(sparam == PLACE_ORDER_BTN) {                       //--- Check if Place Order button clicked
         if(isOrderValid()) {
            placeOrder();                                        //--- Execute order placement
            deleteObjects();                                     //--- Delete tool objects
            showPanel();                                         //--- Show control panel
         }
         else {
            Print("Cannot place order: Invalid price setup for ", selected_order_type);
         }
      }
      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
         ChartSetInteger(0, CHART_EVENT_MOUSE_MOVE, false);      //--- Disable mouse move events
      }
      ObjectSetInteger(0, sparam, OBJPROP_STATE, false);         //--- Reset button state click
      ChartRedraw(0);                                            //--- Redraw chart
   }

   if(id == CHARTEVENT_MOUSE_MOVE) {                             //--- Handle mouse move events
      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

      // Update button and rectangle hover states
      updateButtonHoverState(MouseD_X, MouseD_Y);

      // Handle panel dragging
      int header_xd = (int)ObjectGetInteger(0, PANEL_HEADER, OBJPROP_XDISTANCE);
      int header_yd = (int)ObjectGetInteger(0, PANEL_HEADER, OBJPROP_YDISTANCE);
      int header_xs = (int)ObjectGetInteger(0, PANEL_HEADER, OBJPROP_XSIZE);
      int header_ys = (int)ObjectGetInteger(0, PANEL_HEADER, OBJPROP_YSIZE);

      if(prevMouseState == 0 && MouseState == 1) {               //--- Mouse button down
         if(MouseD_X >= header_xd && MouseD_X <= header_xd + header_xs &&
            MouseD_Y >= header_yd && MouseD_Y <= header_yd + header_ys) {
            panel_dragging = true;                               //--- Start dragging
            panel_drag_x = MouseD_X;                             //--- Store mouse x-coordinate
            panel_drag_y = MouseD_Y;                             //--- Store mouse y-coordinate
            panel_start_x = header_xd;                           //--- Store panel x-coordinate
            panel_start_y = header_yd;                           //--- Store panel y-coordinate
            ChartSetInteger(0, CHART_MOUSE_SCROLL, false);       //--- Disable chart scrolling
         }
      }

      if(panel_dragging && MouseState == 1) {                    //--- Dragging panel
         int dx = MouseD_X - panel_drag_x;                       //--- Calculate x displacement
         int dy = MouseD_Y - panel_drag_y;                       //--- Calculate y displacement
         panel_x = panel_start_x + dx;                           //--- Update panel x-position
         panel_y = panel_start_y + dy;                           //--- Update panel y-position

         // Update all panel objects' positions
         ObjectSetInteger(0, PANEL_BG, OBJPROP_XDISTANCE, panel_x);
         ObjectSetInteger(0, PANEL_BG, OBJPROP_YDISTANCE, panel_y);
         ObjectSetInteger(0, PANEL_HEADER, OBJPROP_XDISTANCE, panel_x);
         ObjectSetInteger(0, PANEL_HEADER, OBJPROP_YDISTANCE, panel_y+2);
         ObjectSetInteger(0, CLOSE_BTN, OBJPROP_XDISTANCE, panel_x + 209);
         ObjectSetInteger(0, CLOSE_BTN, OBJPROP_YDISTANCE, panel_y + 1);
         ObjectSetInteger(0, LOT_EDIT, OBJPROP_XDISTANCE, panel_x + 70);
         ObjectSetInteger(0, LOT_EDIT, OBJPROP_YDISTANCE, panel_y + 40);
         ObjectSetInteger(0, PRICE_LABEL, OBJPROP_XDISTANCE, panel_x + 10);
         ObjectSetInteger(0, PRICE_LABEL, OBJPROP_YDISTANCE, panel_y + 70);
         ObjectSetInteger(0, SL_LABEL, OBJPROP_XDISTANCE, panel_x + 10);
         ObjectSetInteger(0, SL_LABEL, OBJPROP_YDISTANCE, panel_y + 95);
         ObjectSetInteger(0, TP_LABEL, OBJPROP_XDISTANCE, panel_x + 130);
         ObjectSetInteger(0, TP_LABEL, OBJPROP_YDISTANCE, panel_y + 95);
         ObjectSetInteger(0, BUY_STOP_BTN, OBJPROP_XDISTANCE, panel_x + 10);
         ObjectSetInteger(0, BUY_STOP_BTN, OBJPROP_YDISTANCE, panel_y + 140);
         ObjectSetInteger(0, SELL_STOP_BTN, OBJPROP_XDISTANCE, panel_x + 130);
         ObjectSetInteger(0, SELL_STOP_BTN, OBJPROP_YDISTANCE, panel_y + 140);
         ObjectSetInteger(0, BUY_LIMIT_BTN, OBJPROP_XDISTANCE, panel_x + 10);
         ObjectSetInteger(0, BUY_LIMIT_BTN, OBJPROP_YDISTANCE, panel_y + 180);
         ObjectSetInteger(0, SELL_LIMIT_BTN, OBJPROP_XDISTANCE, panel_x + 130);
         ObjectSetInteger(0, SELL_LIMIT_BTN, OBJPROP_YDISTANCE, panel_y + 180);
         ObjectSetInteger(0, PLACE_ORDER_BTN, OBJPROP_XDISTANCE, panel_x + 10);
         ObjectSetInteger(0, PLACE_ORDER_BTN, OBJPROP_YDISTANCE, panel_y + 240);
         ObjectSetInteger(0, CANCEL_BTN, OBJPROP_XDISTANCE, panel_x + 130);
         ObjectSetInteger(0, CANCEL_BTN, OBJPROP_YDISTANCE, panel_y + 240);

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

      if(MouseState == 0) {                                                //--- Mouse button released
         if(panel_dragging) {
            panel_dragging = false;                                        //--- Stop dragging
            ChartSetInteger(0, CHART_MOUSE_SCROLL, true);                  //--- Re-enable chart scrolling
         }
      }

      if(tool_visible) {                                                   //--- Handle tool movement
         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 && !panel_dragging) {   //--- Check for mouse button down, avoid dragging conflict
            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
               ChartSetInteger(0, CHART_MOUSE_SCROLL, false);              //--- Disable chart scrolling
            }
            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
               ChartSetInteger(0, CHART_MOUSE_SCROLL, false);              //--- Disable chart scrolling
            }
            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
               ChartSetInteger(0, CHART_MOUSE_SCROLL, false);              //--- Disable chart scrolling
            }
         }
         if(movingState_R1) {                                                                           //--- Handle REC1 (TP) movement
            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
            }

            updateRectangleColors();                                                                    //--- Update rectangle colors
            ChartRedraw(0);                                                                             //--- Redraw chart
         }
         
         if(movingState_R5) {                                                                           //--- Handle REC5 (SL) movement
            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
            }

            updateRectangleColors();                                                                    //--- Update rectangle colors
            ChartRedraw(0);                                                                             //--- Redraw chart
         }
         
         if(movingState_R3) { //--- Handle REC3 (Entry) movement
            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

            updateRectangleColors(); //--- Update rectangle colors
            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
   }
}

Da wir die Funktion OnChartEvent bereits definiert hatten, konzentrieren wir uns jetzt auf die verbesserte Logik, die wir hinzugefügt haben, um die neuen Interaktivitätsfunktionen, wie das Ziehen von Feldern, die Aktualisierung des Hover-Zustands und die Validierung von Bestellungen, einzubinden. Für CHARTEVENT_OBJECT_CLICK erweitern wir die Behandlung von Schaltflächenklicks für „BUY_STOP_BTN“, „SELL_STOP_BTN“, „BUY_LIMIT_BTN“ und „SELL_LIMIT_BTN“ durch Aufruf der Funktion „updateRectangleColors“ Für „PLACE_ORDER_BTN“ fügen wir eine Prüfung mit der Funktion „isOrderValid“ hinzu, die bei Ungültigkeit einen Fehler über die Funktion „Print“ protokolliert, um fehlerhafte Abschlüsse zu verhindern (siehe unten).

GÜLTIGKEIT DES AUFTRAGS

Wir führen auch die Funktion „updateButtonHoverState“ ein, um Hover-Effekte nach Klicks zu aktualisieren, wobei „lparam“ und „dparam“ für Mauskoordinaten verwendet werden. Für CHARTEVENT_MOUSE_MOVE fügen wir das Ziehen des Panels hinzu, indem wir prüfen, ob der Mausklick innerhalb der Grenzen von „PANEL_HEADER“ liegt (erhalten über ObjectGetInteger), setzen „panel_dragging“ auf „true“, speichern die Koordinaten in „panel_drag_x“, „panel_drag_y“, „panel_start_x“ und „panel_start_y“ und deaktivieren das Scrollen mit ChartSetInteger.

Beim Ziehen („panel_dragging“ und „MouseState“ 1) berechnen wir die Verschiebung („dx“, „dy“), aktualisieren „panel_x“ und „panel_y“, positionieren alle Panel-Objekte („PANEL_BG“, „PANEL_HEADER“, „CLOSE_BTN“, „LOT_EDIT“, etc.) mit ObjectSetInteger und rufen ChartRedraw auf, um das Chartzu aktualisieren. Wenn die Maus losgelassen wird, setzen wir „panel_dragging“ zurück und aktivieren den Bildlauf wieder. Wir stellen sicher, dass das Ziehen von Rechtecken (für „REC1“, „REC3“, „REC5“) Konflikte mit dem Ziehen von Panels vermeidet, indem wir die Option „!panel_dragging“, und aktualisieren die Farben mit „updateRectangleColors“ während „movingState_R1“, „movingState_R5“ und „movingState_R3“, um Schwebe- und Gültigkeitszustände zu reflektieren.

Wir haben einige der wichtigsten Punkte hervorgehoben, die es zu beachten gilt. Hier ist eine Visualisierung.

PANEL ZIEHEN UND SCHWEBEN

Da wir das Header-Panel-Objekt verwendet haben, können wir es folgendermaßen 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

   // Header rectangle (inside panel)
   
   createButton(PANEL_HEADER,"",panel_x+2,panel_y+2,250-4,28-3,clrBlue,C'050,050,050',12,C'050,050,050',false);
   
   createButton(CLOSE_BTN, CharToString(203), panel_x + 209, panel_y + 1, 40, 25, clrWhite, clrCrimson, 12, clrBlack, false, "Wingdings"); //--- Create close button

//---

}

In der Funktion „createControlPanel“ fügen wir mit der Funktion „createButton“ die Schaltfläche „PANEL_HEADER“ zum Bedienfeld unseres Werkzeugs hinzu, platziert bei „panel_x+2“, „panel_y+2“ mit der Größe 246x25, gestylt mit blauem Text (clrBlue), dunkelgrauem Hintergrund/Rahmen („C'050,050,050'“) und ohne Beschriftung, was das Ziehen des Panels im OnChartEvent ermöglicht. Die andere Sache, die wir tun müssen, ist die Zerstörung des Panels wie unten.

//+------------------------------------------------------------------+
//| Delete control panel objects                                     |
//+------------------------------------------------------------------+
void deletePanel() {
   ObjectDelete(0, PANEL_BG);        //--- Delete panel background
   ObjectDelete(0, PANEL_HEADER);    //--- Delete panel header
   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
}

Hier aktualisieren wir die Funktion „deletePanel“, um sicherzustellen, dass das Bedienfeld unseres Tools ordnungsgemäß aufgeräumt wird, indem alle zugehörigen Objekte entfernt werden, einschließlich der neuen Kopfzeile, die wir kürzlich eingeführt haben. Wir verwenden die Funktion ObjectDelete, um den Panel-Hintergrund („PANEL_BG“), die neu hinzugefügte Kopfzeile („PANEL_HEADER“), das Eingabefeld für die Losgröße („LOT_EDIT“), die Beschriftungen („PRICE_LABEL“, „SL_LABEL“, „TP_LABEL“) und Schaltflächen („BUY_STOP_BTN“, „SELL_STOP_BTN“, „BUY_LIMIT_BTN“, „SELL_LIMIT_BTN“, „PLACE_ORDER_BTN“, „CANCEL_BTN“, „CLOSE_BTN“) aus dem MetaTrader 5 Chart zu entfernen.

Schließlich rufen wir die Funktion ChartRedraw auf, um das Chart zu aktualisieren und eine saubere Oberfläche nach dem Löschen zu gewährleisten. Außerdem müssen wir bei der Anzeige des Werkzeugs die Hover-Effekte berücksichtigen, um sicherzustellen, dass sie wie unten dargestellt hervorstechen.

//+------------------------------------------------------------------+
//| Show control panel                                               |
//+------------------------------------------------------------------+
void showPanel() {
   // Ensure panel is in foreground
   ObjectSetInteger(0, PANEL_BG,        OBJPROP_BACK, false); //--- Show panel background
   ObjectSetInteger(0, PANEL_HEADER,    OBJPROP_BACK, false); //--- Show panel header
   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 button hover states
   buy_stop_hovered     = false;
   sell_stop_hovered    = false;
   buy_limit_hovered    = false;
   sell_limit_hovered   = false;
   place_order_hovered  = false;
   cancel_hovered       = false;
   close_hovered        = false;
   header_hovered       = false;

   // Reset button colors
   ObjectSetInteger(0, BUY_STOP_BTN,    OBJPROP_BGCOLOR,       clrForestGreen);
   ObjectSetInteger(0, BUY_STOP_BTN,    OBJPROP_BORDER_COLOR,  clrBlack);
   ObjectSetInteger(0, SELL_STOP_BTN,   OBJPROP_BGCOLOR,       clrFireBrick);
   ObjectSetInteger(0, SELL_STOP_BTN,   OBJPROP_BORDER_COLOR,  clrBlack);
   ObjectSetInteger(0, BUY_LIMIT_BTN,   OBJPROP_BGCOLOR,       clrForestGreen);
   ObjectSetInteger(0, BUY_LIMIT_BTN,   OBJPROP_BORDER_COLOR,  clrBlack);
   ObjectSetInteger(0, SELL_LIMIT_BTN,  OBJPROP_BGCOLOR,       clrFireBrick);
   ObjectSetInteger(0, SELL_LIMIT_BTN,  OBJPROP_BORDER_COLOR,  clrBlack);
   ObjectSetInteger(0, PLACE_ORDER_BTN, OBJPROP_BGCOLOR,       clrDodgerBlue);
   ObjectSetInteger(0, PLACE_ORDER_BTN, OBJPROP_BORDER_COLOR,  clrBlack);
   ObjectSetInteger(0, CANCEL_BTN,      OBJPROP_BGCOLOR,       clrSlateGray);
   ObjectSetInteger(0, CANCEL_BTN,      OBJPROP_BORDER_COLOR,  clrBlack);
   ObjectSetInteger(0, CLOSE_BTN,       OBJPROP_BGCOLOR,       clrCrimson);
   ObjectSetInteger(0, CLOSE_BTN,       OBJPROP_BORDER_COLOR,  clrBlack);
   ObjectSetInteger(0, PANEL_HEADER,    OBJPROP_BGCOLOR,       C'050,050,050');

   // 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, true); //--- Ensure mouse move events are enabled
   ChartRedraw(0);                                   //--- Redraw chart
}

Wir erweitern die Funktion „showPanel“, um die Anzeige und das Zurücksetzen des Zustands des Bedienfelds unseres Tools zu verwalten, indem wir den neuen „PANEL_HEADER“ und die Verwaltung des Hover-Zustands einbeziehen, die zur Unterstützung der verbesserten Interaktivität des Tools eingeführt wurden. Zunächst stellen wir sicher, dass alle Elemente des Panels sichtbar sind, indem wir die Funktion ObjectSetInteger verwenden, um die Eigenschaft OBJPROP_BACK für den Panel-Hintergrund („PANEL_BG“), die neu hinzugefügte Kopfzeile („PANEL_HEADER“), das Eingabefeld für die Losgröße („LOT_EDIT“), preisbezogene Beschriftungen („PRICE_LABEL“, „SL_LABEL“, „TP_LABEL“), und alle Schaltflächen („BUY_STOP_BTN“, „SELL_STOP_BTN“, „BUY_LIMIT_BTN“, „SELL_LIMIT_BTN“, „PLACE_ORDER_BTN“, „CANCEL_BTN“, „CLOSE_BTN“) festzulegen, um sie in den Vordergrund des Charts zu bringen.

Um eine saubere und vorhersehbare Schnittstelle zu erhalten, setzen wir die Schwebezustände zurück, indem wir die booleschen Variablen „buy_stop_hovered“, „sell_stop_hovered“, „buy_limit_hovered“, „sell_limit_hovered“, „place_order_hovered“, „cancel_hovered“, „close_hovered“ und „header_hovered“ auf „false“ setzen, um sicherzustellen, dass keine Hover-Effekte zurückbleiben, wenn das Panel angezeigt wird.

Anschließend stellen wir das Standardaussehen der Schaltflächen und der Kopfzeile wieder her, indem wir die Funktion „ObjectSetInteger“ verwenden, um OBJPROP_BGCOLOR auf ihre ursprünglichen Farben zu setzen: „clrForestGreen“ für „BUY_STOP_BTN“ und „BUY_LIMIT_BTN“, „clrFireBrick“ für „SELL_STOP_BTN“ und „SELL_LIMIT_BTN“, „clrDodgerBlue“ für „PLACE_ORDER_BTN“, „clrSlateGray“ für „CANCEL_BTN“, „clrCrimson“ für „CLOSE_BTN“, und ein dunkles Grau („C'050,050,050'“) für „PANEL_HEADER“.

Wir haben auch OBJPROP_BORDER_COLOR auf „clrBlack“ für alle Schaltflächen gesetzt, um ein einheitliches, nicht überhöhtes Erscheinungsbild zu gewährleisten.

Um den Funktionszustand des Panels zurückzusetzen, rufen wir die Funktion „update_Text“ auf, um „PRICE_LABEL“ auf „Entry: -“, „SL_LABEL“ auf „SL: -“, „TP_LABEL“ auf „TP: -“ und „PLACE_ORDER_BTN“ auf „Place Order“ zu setzen, wodurch alle vorherigen Handels-Setup-Informationen gelöscht werden. Wir löschen die Variable „selected_order_type“, um sicherzustellen, dass kein Auftragstyp vorausgewählt ist, setzen „tool_visible“ auf false, um das Chart-Werkzeug auszublenden, und verwenden die Funktion ChartSetInteger, um CHART_EVENT_MOUSE_MOVE-Ereignisse zu aktivieren, um sicherzustellen, dass das Panel für Hover- und Drag-Interaktionen bereit ist.

Schließlich rufen wir die Funktion ChartRedraw auf, um das Chartzu aktualisieren und das Panel in seinem Standardzustand darzustellen, der für unsere nächste Interaktion vorbereitet ist. Nach der Zusammenstellung ergibt sich folgendes Bild.

ENDGÜLTIGES ERGEBNIS

Anhand der Visualisierung können wir sehen, dass wir in der Lage sind, die Aufträge über das Preistool dynamisch zu validieren und die Farben zu ändern, um den Nutzer darauf hinzuweisen, dass die Preise außerhalb des Rahmens liegen. Außerdem können wir sehen, dass wir das Panel und das Preistool dynamisch ziehen können, und wenn wir über die Schaltflächen schweben, können wir ihre Bereiche abrufen und die Farben auf der Grundlage der Schaltflächenbereiche dynamisch ändern und so unser Ziel erreichen. Jetzt bleibt nur noch, die Interaktivität des Projekts zu testen, und das wird im vorangegangenen 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 GIF


Schlussfolgerung

Abschließend haben wir unser Handelsassistenten-Tool in MQL5 mit einem dynamischen visuellen Feedback, einem verschiebbaren Panel, Hover-Effekten und einer Auftragsvalidierung in Echtzeit verbessert, um die Platzierung ausstehender Aufträge intuitiver und präziser zu gestalten. Wir haben die Entwicklung und Umsetzung dieser Verbesserungen demonstriert und ihre Zuverlässigkeit durch gründliche Backtests sichergestellt, die auf unsere Handelsanforderungen zugeschnitten sind. Sie können dieses Tool an Ihren Stil anpassen und so die Effizienz Ihrer Orderplatzierung in den Handelscharts erheblich verbessern.

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

Entwicklung des Price Action Analysis Toolkit (Teil 22): Korrelation Dashboard Entwicklung des Price Action Analysis Toolkit (Teil 22): Korrelation Dashboard
Bei diesem Tool handelt es sich um ein Korrelations-Dashboard, das Korrelationskoeffizienten für mehrere Währungspaare in Echtzeit berechnet und anzeigt. Durch die Visualisierung, wie sich Paare im Verhältnis zueinander bewegen, fügt es Ihrer Preisaktionsanalyse wertvollen Kontext hinzu und hilft Ihnen, die Dynamik zwischen den Märkten zu antizipieren. Lesen Sie weiter, um seine Funktionen und Anwendungen kennenzulernen.
Erste Schritte mit MQL5 Algo Forge Erste Schritte mit MQL5 Algo Forge
Wir stellen die MQL5 Algo Forge vor – ein spezielles Portal für Entwickler des algorithmischem Handels. Es kombiniert die Leistungsfähigkeit von Git mit einer intuitiven Oberfläche für die Verwaltung und Organisation von Projekten innerhalb des MQL5-Ökosystems. Hier können Sie interessanten Autoren folgen, Teams bilden und an algorithmischen Handelsprojekten mitarbeiten.
MQL5-Assistenten-Techniken, die Sie kennen sollten (Teil 63): Verwenden von Mustern der Kanäle von DeMarker und Envelope MQL5-Assistenten-Techniken, die Sie kennen sollten (Teil 63): Verwenden von Mustern der Kanäle von DeMarker und Envelope
Der DeMarker-Oszillator und der Envelope-Indikator sind Momentum- und Unterstützungs-/Widerstands-Tools, die bei der Entwicklung eines Expert Advisors kombiniert werden können. Wir prüfen daher Muster für Muster, was von Nutzen sein könnte und was möglicherweise zu vermeiden ist. Wir verwenden, wie immer, einen von einem Assistenten erstellten Expert Advisor zusammen mit den Funktionen der Musterverwendung, die in der Signalklasse des Expert Advisors integriert sind.
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.