
MQL5-Handelswerkzeuge (Teil 2): Verbesserung des interaktiven Handelsassistenten durch dynamisches, visuelles Feedback
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:
- Konzeptionelle Erweiterungen für verbesserte Interaktivität
- Implementation in MQL5
- Backtests
- 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.
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).
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.
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.
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.
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
Warnung: Alle Rechte sind von MetaQuotes Ltd. vorbehalten. Kopieren oder Vervielfältigen untersagt.
Dieser Artikel wurde von einem Nutzer der Website verfasst und gibt dessen persönliche Meinung wieder. MetaQuotes Ltd übernimmt keine Verantwortung für die Richtigkeit der dargestellten Informationen oder für Folgen, die sich aus der Anwendung der beschriebenen Lösungen, Strategien oder Empfehlungen ergeben.





- Freie Handelsapplikationen
- Über 8.000 Signale zum Kopieren
- Wirtschaftsnachrichten für die Lage an den Finanzmärkte
Sie stimmen der Website-Richtlinie und den Nutzungsbedingungen zu.