
Erstellen eines Handelsadministrator-Panels in MQL5 (Teil IX): Code Organisation (IV): Handelsmanagement-Panel-Klasse
Inhalt
- Einführung
- Überblick über die Diskussion
- Die Klasse des Handelsmanagement-Panels (CTradeManagementPanel)
- Integration mit (New_Admin_Panel)
- Tests
- Schlussfolgerung
Einführung
In unseren vorangegangenen Diskussionen haben wir die Code-Organisation als eine entscheidende Strategie für eine reibungslose und skalierbare Erweiterung des Trading Administrator Panel-Projekts vorgestellt, die dem Prinzip der Trennung von Belangen folgt. Dieser Ansatz hat es mir ermöglicht, mich auf jedes Unter-Panel innerhalb des NewAdminPanel zu konzentrieren und sicherzustellen, dass unser Programm modular und gut strukturiert bleibt. Da wir jede Funktion unabhängig voneinander entwickeln, können wir sie verfeinern, um die bestmögliche Funktionalität zu bieten.
Früher hatte unser Handelspanel nur begrenzte Möglichkeiten, aber jetzt ist es leistungsfähiger als je zuvor. Es bietet die Flexibilität, schnelle Handelsgeschäfte auszuführen und gleichzeitig ein integriertes Risikomanagement einzubauen - und das alles über dieselbe Schnittstelle. Schwebende Aufträge können auch direkt über das Panel eingerichtet werden, was den Handelsprozess vereinfacht.
Mit dieser Aktualisierung werden zwei große Herausforderungen angegangen:
- Aufbau großer, gut organisierter Programme, die einfacher zu entwickeln und zu pflegen sind.
- Sicherstellung eines schnellen Zugriffs auf Handelsoperationen innerhalb eines All-in-One-EAs, der eine Kommunikationsschnittstelle, eine Handelsverwaltungsschnittstelle und in naher Zukunft auch eine Analyseschnittstelle integriert.
Im Folgenden werde ich einen Überblick über diese Diskussion geben und darlegen, wie wir diese Verbesserungen zu Ende führen werden.
Überblick über die Diskussion
Das Hauptziel unserer Artikel ist es, die Verwendung von MQL5 durch Anwendung auf verschiedene Projekte praktisch zu machen. Heute werden wir uns mit der Entwicklung der Klasse für ein Handelsverwaltungs-Panel befassen und dabei berücksichtigen, dass in MQL5 ein Klassenkopf Deklarationen von ähnlichen Variablen enthält. In diesem Zusammenhang werden alle Handelsfunktionen, die wir in unser Panel aufnehmen wollen, von integrierten Klassen wie CTrade, CDialog, CLabel und CEdit erben.
Sobald die Klasse vollständig entwickelt ist, werden wir ihre Methoden in das Hauptprogramm - NewAdminPanel EA - integrieren. Unsere Diskussion wäre nicht vollständig, wenn wir nicht die Testergebnisse mit Ihnen teilen und Ihnen die Quelldateien zur Verfügung stellen würden, damit Sie die Implementierung überprüfen, sich Ideen holen und mit dem Code experimentieren können, um Ihre eigenen Projekte zu verbessern.
In diesem Stadium beschloss ich, die Erstellung des Home-Panels innerhalb des Hauptprogramms zu zentralisieren, da dies die Länge des Codes nicht wesentlich erhöhte. Obwohl der frühere Ansatz seine Vorteile hatte, habe ich mich für diese Struktur entschieden, um die Entwicklung zu vereinfachen und Abhängigkeiten zu reduzieren. Mein Ziel ist es, das Hauptprogramm zu fokussieren und für jede spezifische Funktion eine eigene Klasse zu verwenden. Die wichtigsten Elemente der Nutzeroberfläche werden nun direkt im Hauptprogramm erstellt, was zu einem schlankeren und effizienteren Design führt. Infolgedessen werden die Methoden der Klasse AdminHomeDialog in NewAdminPanel nicht mehr aufgerufen.
Das folgende Bild zeigt, was wir am Ende unserer Diskussion erstellen werden. Aber das ist erst der Anfang - wenn es einmal aufgebaut ist, dient es als solide Grundlage für zukünftige Erweiterungen und Verbesserungen.
Handelsmanagement-Panel (bis zum Ende der Diskussion)
Vorteile dieses Projekts
Die Instrumente, die wir hier entwickeln, bieten mehrere entscheidende Vorteile:
- Schneller Handel - Effizientes Ausführen von Handelsgeschäften.
- Risikomanagement - Legen Sie Stop Loss (S/L) und Take Profit (T/P) fest, bevor Sie Aufträge erteilen, und planen Sie Ihre Handelsgeschäfte im Voraus mit schwebenden Aufträgen.
- Kommunikationsfunktionen - Senden Sie Nachrichten an andere Händler über das Kommunikationspanel, das wir zuvor erstellt haben.
- Lernen Sie MQL5 für Fortgeschrittene - Gewinnen Sie einen tieferen Einblick in die objektorientierte Programmierung in MQL5.
- Engagement der Gemeinschaft - Teilen Sie Ihre Erkenntnisse und Erfahrungen im Kommentarbereich mit.
- Wiederverwendbarkeit - Das CTradeManagementPanel kann in anderen Projekten verwendet werden.
Die Klasse des Handelsmanagement-Panels (CTradeManagementPanel)
1. Einrichten der Basis mit Kopfzeilen und Makros
Bevor wir etwas bauen, müssen wir die richtigen Werkzeuge einsetzen. Der erste Schritt besteht darin, Header-Dateien einzubinden, die die Handelsausführung, die Elemente der Nutzeroberfläche und die Ereignisverwaltung regeln. Anstatt Layout-Werte wie Schaltflächengrößen und -abstände über den gesamten Code zu verstreuen, definieren wir Makros am Anfang. Dies gewährleistet ein einheitliches Design der Nutzeroberfläche und erleichtert die spätere Anpassung der Abmessungen, ohne mehrere Dateien durchsuchen zu müssen.
#include <Trade\Trade.mqh> #include <Controls\Dialog.mqh> #include <Controls\Button.mqh> #include <Controls\Edit.mqh> #include <Controls\Label.mqh> //+------------------------------------------------------------------+ //| Defines for dimensions and spacing | //+------------------------------------------------------------------+ #define BUTTON_WIDTH 100 #define BUTTON_HEIGHT 30 #define DELETE_BUTTON_WIDTH 130 #define EDIT_WIDTH 80 #define EDIT_HEIGHT 20 #define DEFAULT_LABEL_HEIGHT 20 #define SECTION_LABEL_WIDTH 250 #define GAP 10 #define INPUT_LABEL_GAP 3
2. Definition der Kernklasse und ihrer Komponenten
Das Herzstück des Handelspanels ist eine Klasse, die sowohl die Schnittstelle als auch die Handelsausführungslogik verwaltet. Durch die Ableitung von einer Basis-Dialogklasse erhält sie integrierte UI-Management-Funktionen, während wir uns auf die Anpassung des Verhaltens konzentrieren können.
Innerhalb dieser Klasse definieren wir wichtige Mitgliedsvariablen. Dazu gehören ein Verweis auf das Handelsdiagramm, ein Handelsausführungsobjekt und eine strukturierte Sammlung von UI-Steuerelementen. Durch die getrennte Organisation dieser Elemente wird sichergestellt, dass die Nutzeroberfläche sauber von der Logik der Auftragsabwicklung getrennt bleibt.
//+------------------------------------------------------------------+ //| Trade Management Panel class | //+------------------------------------------------------------------+ class CTradeManagementPanel : public CAppDialog { protected: // Store chart and subwindow values for helper functions. long m_chart; int m_subwin; CTrade m_trade; // Trade object for executing trades // Section Labels CLabel m_secQuickLabel; CLabel m_secPendingLabel; CLabel m_secAllOpsLabel; // Section 1: Quick Execution CButton m_buyButton; // Market Buy button CButton m_sellButton; // Market Sell button CEdit m_volumeEdit; // Volume input CLabel m_lotLabel; // "Lot:" label above volume input // TP and SL for market orders—with a label to their right CEdit m_tpEdit; CLabel m_tpRightLabel; // "TP:" label CLabel m_tpErrorLabel; CEdit m_slEdit; CLabel m_slRightLabel; // "SL:" label CLabel m_slErrorLabel; // Section 2: Pending Orders // Column Headers for pending orders CLabel m_pendingPriceHeader; CLabel m_pendingTPHeader; CLabel m_pendingSLHeader; CLabel m_pendingExpHeader; // Buy pending orders CButton m_buyLimitButton; CEdit m_buyLimitPriceEdit; CEdit m_buyLimitTPEdit; CEdit m_buyLimitSLEdit; CEdit m_buyLimitExpEdit; // Expiration input for Buy Limit CButton m_buyStopButton; CEdit m_buyStopPriceEdit; CEdit m_buyStopTPEdit; CEdit m_buyStopSLEdit; CEdit m_buyStopExpEdit; // Expiration input for Buy Stop // Sell pending orders CButton m_sellLimitButton; CEdit m_sellLimitPriceEdit; CEdit m_sellLimitTPEdit; CEdit m_sellLimitSLEdit; CEdit m_sellLimitExpEdit; // Expiration input for Sell Limit CButton m_sellStopButton; CEdit m_sellStopPriceEdit; CEdit m_sellStopTPEdit; CEdit m_sellStopSLEdit; CEdit m_sellStopExpEdit; // Expiration input for Sell Stop // Section 3: All Order Operations CButton m_closeAllButton; CButton m_closeProfitButton; CButton m_closeLossButton; CButton m_closeBuyButton; CButton m_closeSellButton; CButton m_deleteAllOrdersButton; CButton m_deleteLimitOrdersButton; CButton m_deleteStopOrdersButton; CButton m_deleteStopLimitOrdersButton; CButton m_resetButton; //--- Helper: Create a text label using bool CreateLabelEx(CLabel &label, int x, int y, int height, string label_name, string text, color clr) { string unique_name = m_name + label_name; if(!label.Create(m_chart, unique_name, m_subwin, x, y, x, y+height)) return false; if(!Add(label)) return false; if(!label.Text(text)) return false; label.Color(clr); return true; } //--- Helper: Create and add a button control bool CreateButton(CButton &button, string name, int x, int y, int w = BUTTON_WIDTH, int h = BUTTON_HEIGHT, color clr = clrWhite) { if(!button.Create(m_chart, name, m_subwin, x, y, x+w, y+h)) return false; button.Text(name); button.Color(clr); if(!Add(button)) return false; return true; } //--- Helper: Create and add an edit control bool CreateEdit(CEdit &edit, string name, int x, int y, int w = EDIT_WIDTH, int h = EDIT_HEIGHT) { if(!edit.Create(m_chart, name, m_subwin, x, y, x+w, y+h)) return false; if(!Add(edit)) return false; return true; } // Event handler declarations virtual void OnClickBuy(); virtual void OnClickSell(); virtual void OnClickBuyLimit(); virtual void OnClickBuyStop(); virtual void OnClickSellLimit(); virtual void OnClickSellStop(); virtual void OnClickCloseAll(); virtual void OnClickCloseProfit(); virtual void OnClickCloseLoss(); virtual void OnClickCloseBuy(); virtual void OnClickCloseSell(); virtual void OnClickDeleteAllOrders(); virtual void OnClickDeleteLimitOrders(); virtual void OnClickDeleteStopOrders(); virtual void OnClickDeleteStopLimitOrders(); virtual void OnClickReset(); // Validation helpers for market orders bool ValidateBuyParameters(double volume, double tp, double sl, double ask); bool ValidateSellParameters(double volume, double tp, double sl, double bid); public: CTradeManagementPanel(); ~CTradeManagementPanel(); virtual bool Create(const long chart, const string name, const int subwin, const int x1, const int y1, const int x2, const int y2); virtual bool OnEvent(const int id, const long &lparam, const double &dparam, const string &sparam); void Toggle(); };
3. Strukturierung der Nutzeroberfläche mit logischen Abschnitten
Eine gut organisierte Schnittstelle macht den Unterschied aus. Anstatt die Schaltflächen und Eingabefelder wahllos zu verteilen, unterteilen wir das Panel in drei Hauptbereiche.
- Der erste Bereich ist für schnelle Marktaufträge vorgesehen, wo die Nutzer sofort kaufen oder verkaufen, das Handelsvolumen festlegen und die Werte für Stop-Loss und Take-Profit konfigurieren können.
- Der zweite Abschnitt ist den schwebenden Aufträgen gewidmet. Hier können die Nutzer Preise, Verfallszeiten und Auftragsarten wie Buy Limit oder Sell Stop festlegen.
- Der dritte Abschnitt behandelt Massenaktionen wie das Schließen aller Handelsgeschäfte, das Schließen nur profitabler Handelsgeschäfte oder das Löschen der schwebenden Aufträge. Durch die Zusammenfassung dieser Funktionen wird sichergestellt, dass die Nutzeroberfläche einfach zu navigieren ist.
//+------------------------------------------------------------------+ //| Create the trade management panel | //+------------------------------------------------------------------+ bool CTradeManagementPanel::Create(const long chart, const string name, const int subwin, const int x1, const int y1, const int x2, const int y2) { // Save chart and subwin for later use. m_chart = chart; m_subwin = subwin; if(!CAppDialog::Create(chart, name, subwin, x1, y1, x2, y2)) return false; int curX = GAP; int curY = GAP; // Section 1: Quick Order Execution if(!CreateLabelEx(m_secQuickLabel, curX, curY-10, DEFAULT_LABEL_HEIGHT, "SecQuick", "Quick Order Execution:", clrBlack)) return false; curY += DEFAULT_LABEL_HEIGHT + GAP; // Market order row: if(!CreateButton(m_buyButton, "Buy", curX, curY, BUTTON_WIDTH, BUTTON_HEIGHT, clrGreen)) return false; int volX = curX + BUTTON_WIDTH + GAP; if(!CreateLabelEx(m_lotLabel, volX, curY - DEFAULT_LABEL_HEIGHT, DEFAULT_LABEL_HEIGHT, "Lot", "Lot Size:", clrBlack)) return false; if(!CreateEdit(m_volumeEdit, "VolumeEdit", volX, curY, EDIT_WIDTH, EDIT_HEIGHT)) return false; double minVolume = SymbolInfoDouble(Symbol(), SYMBOL_VOLUME_MIN); m_volumeEdit.Text(DoubleToString(minVolume, 2)); int sellBtnX = volX + EDIT_WIDTH + GAP; if(!CreateButton(m_sellButton, "Sell", sellBtnX, curY, BUTTON_WIDTH, BUTTON_HEIGHT, clrRed)) return false; curY += BUTTON_HEIGHT + GAP; // Next row: TP and SL for market orders if(!CreateEdit(m_tpEdit, "TPEdit", 4*curX+ GAP , curY, EDIT_WIDTH, EDIT_HEIGHT)) return false; m_tpEdit.Text("0.00000"); if(!CreateLabelEx(m_tpRightLabel, curX + INPUT_LABEL_GAP, curY, DEFAULT_LABEL_HEIGHT, "TP", "TP:", clrBlack)) return false; if(!CreateLabelEx(m_tpErrorLabel, curX, curY + DEFAULT_LABEL_HEIGHT, DEFAULT_LABEL_HEIGHT, "TPError", "", clrBlack)) return false; int slX = 2*EDIT_WIDTH ; if(!CreateEdit(m_slEdit, "SLEdit", slX + 4*curX, curY, EDIT_WIDTH, EDIT_HEIGHT)) return false; m_slEdit.Text("0.00000"); if(!CreateLabelEx(m_slRightLabel, slX , curY, DEFAULT_LABEL_HEIGHT, "SL", "SL:", clrBlack)) return false; if(!CreateLabelEx(m_slErrorLabel, slX, curY + DEFAULT_LABEL_HEIGHT, DEFAULT_LABEL_HEIGHT, "SLError", "", clrBlack)) return false; curY += EDIT_HEIGHT + GAP*2; // Section 2: Pending Orders if(!CreateLabelEx(m_secPendingLabel, curX, curY, DEFAULT_LABEL_HEIGHT, "SecPend", "Pending Orders:", clrBlack)) return false; curY += DEFAULT_LABEL_HEIGHT + GAP; // Column headers for pending orders (each label includes a colon) int headerX = curX + BUTTON_WIDTH + GAP; if(!CreateLabelEx(m_pendingPriceHeader, headerX, curY, DEFAULT_LABEL_HEIGHT, "PendPrice", "Price:", clrBlack)) return false; if(!CreateLabelEx(m_pendingTPHeader, headerX + EDIT_WIDTH + GAP, curY, DEFAULT_LABEL_HEIGHT, "PendTP", "TP:", clrBlack)) return false; if(!CreateLabelEx(m_pendingSLHeader, headerX + 2*(EDIT_WIDTH + GAP), curY, DEFAULT_LABEL_HEIGHT, "PendSL", "SL:", clrBlack)) return false; if(!CreateLabelEx(m_pendingExpHeader, headerX + 3*(EDIT_WIDTH + GAP), curY, DEFAULT_LABEL_HEIGHT, "PendExp", "Expiration Date:", clrBlack)) return false; curY += DEFAULT_LABEL_HEIGHT + GAP; // Prepare default expiration value as current day end time. datetime now = TimeCurrent(); string exp_default = TimeToString(now, TIME_DATE) + " 23:59:59"; // --- Buy Pending Orders --- // Buy Limit Order row: if(!CreateButton(m_buyLimitButton, "Buy Limit", curX, curY, BUTTON_WIDTH, BUTTON_HEIGHT, clrBlue)) return false; int pendingX = curX + BUTTON_WIDTH + GAP; if(!CreateEdit(m_buyLimitPriceEdit, "BuyLimitPrice", pendingX, curY, EDIT_WIDTH, EDIT_HEIGHT)) return false; string askStr = DoubleToString(SymbolInfoDouble(Symbol(), SYMBOL_ASK), 5); m_buyLimitPriceEdit.Text(askStr); int pending2X = pendingX + EDIT_WIDTH + GAP; if(!CreateEdit(m_buyLimitTPEdit, "BuyLimitTP", pending2X, curY, EDIT_WIDTH, EDIT_HEIGHT)) return false; m_buyLimitTPEdit.Text("0.00000"); int pending3X = pending2X + EDIT_WIDTH + GAP; if(!CreateEdit(m_buyLimitSLEdit, "BuyLimitSL", pending3X, curY, EDIT_WIDTH, EDIT_HEIGHT)) return false; m_buyLimitSLEdit.Text("0.00000"); int pending4X = pending3X + EDIT_WIDTH + GAP; // Double width for expiration input if(!CreateEdit(m_buyLimitExpEdit, "BuyLimitExp", pending4X, curY, 2*EDIT_WIDTH, EDIT_HEIGHT)) return false; m_buyLimitExpEdit.Text(exp_default); curY += BUTTON_HEIGHT + GAP; // Buy Stop Order row: if(!CreateButton(m_buyStopButton, "Buy Stop", curX, curY, BUTTON_WIDTH, BUTTON_HEIGHT, clrBlue)) return false; pendingX = curX + BUTTON_WIDTH + GAP; if(!CreateEdit(m_buyStopPriceEdit, "BuyStopPrice", pendingX, curY, EDIT_WIDTH, EDIT_HEIGHT)) return false; m_buyStopPriceEdit.Text(askStr); pending2X = pendingX + EDIT_WIDTH + GAP; if(!CreateEdit(m_buyStopTPEdit, "BuyStopTP", pending2X, curY, EDIT_WIDTH, EDIT_HEIGHT)) return false; m_buyStopTPEdit.Text("0.00000"); pending3X = pending2X + EDIT_WIDTH + GAP; if(!CreateEdit(m_buyStopSLEdit, "BuyStopSL", pending3X, curY, EDIT_WIDTH, EDIT_HEIGHT)) return false; m_buyStopSLEdit.Text("0.00000"); pending4X = pending3X + EDIT_WIDTH + GAP; if(!CreateEdit(m_buyStopExpEdit, "BuyStopExp", pending4X, curY, 2*EDIT_WIDTH, EDIT_HEIGHT)) return false; m_buyStopExpEdit.Text(exp_default); curY += BUTTON_HEIGHT + GAP; // --- Sell Pending Orders --- // Sell Limit Order row: if(!CreateButton(m_sellLimitButton, "Sell Limit", curX, curY, BUTTON_WIDTH, BUTTON_HEIGHT, clrRed)) return false; pendingX = curX + BUTTON_WIDTH + GAP; if(!CreateEdit(m_sellLimitPriceEdit, "SellLimitPrice", pendingX, curY, EDIT_WIDTH, EDIT_HEIGHT)) return false; string bidStr = DoubleToString(SymbolInfoDouble(Symbol(), SYMBOL_BID), 5); m_sellLimitPriceEdit.Text(bidStr); pending2X = pendingX + EDIT_WIDTH + GAP; if(!CreateEdit(m_sellLimitTPEdit, "SellLimitTP", pending2X, curY, EDIT_WIDTH, EDIT_HEIGHT)) return false; m_sellLimitTPEdit.Text("0.00000"); pending3X = pending2X + EDIT_WIDTH + GAP; if(!CreateEdit(m_sellLimitSLEdit, "SellLimitSL", pending3X, curY, EDIT_WIDTH, EDIT_HEIGHT)) return false; m_sellLimitSLEdit.Text("0.00000"); pending4X = pending3X + EDIT_WIDTH + GAP; if(!CreateEdit(m_sellLimitExpEdit, "SellLimitExp", pending4X, curY, 2*EDIT_WIDTH, EDIT_HEIGHT)) return false; m_sellLimitExpEdit.Text(exp_default); curY += BUTTON_HEIGHT + GAP; // Sell Stop Order row: if(!CreateButton(m_sellStopButton, "Sell Stop", curX, curY, BUTTON_WIDTH, BUTTON_HEIGHT, clrRed)) return false; pendingX = curX + BUTTON_WIDTH + GAP; if(!CreateEdit(m_sellStopPriceEdit, "SellStopPrice", pendingX, curY, EDIT_WIDTH, EDIT_HEIGHT)) return false; m_sellStopPriceEdit.Text(bidStr); pending2X = pendingX + EDIT_WIDTH + GAP; if(!CreateEdit(m_sellStopTPEdit, "SellStopTP", pending2X, curY, EDIT_WIDTH, EDIT_HEIGHT)) return false; m_sellStopTPEdit.Text("0.00000"); pending3X = pending2X + EDIT_WIDTH + GAP; if(!CreateEdit(m_sellStopSLEdit, "SellStopSL", pending3X, curY, EDIT_WIDTH, EDIT_HEIGHT)) return false; m_sellStopSLEdit.Text("0.00000"); pending4X = pending3X + EDIT_WIDTH + GAP; if(!CreateEdit(m_sellStopExpEdit, "SellStopExp", pending4X, curY, 2*EDIT_WIDTH, EDIT_HEIGHT)) return false; m_sellStopExpEdit.Text(exp_default); curY += BUTTON_HEIGHT + GAP; // Section 3: All Order Operations if(!CreateLabelEx(m_secAllOpsLabel, curX, curY, DEFAULT_LABEL_HEIGHT, "SecAllOps", "All Order Operations:", clrBlack)) return false; curY += DEFAULT_LABEL_HEIGHT + GAP; int deleteX = curX + 2*BUTTON_WIDTH + 2*GAP; int deleteY = curY ; CreateButton(m_closeAllButton, "Close All", curX, curY, BUTTON_WIDTH, BUTTON_HEIGHT, clrRed); CreateButton(m_closeProfitButton, "Close Profit", curX + BUTTON_WIDTH + GAP, curY, BUTTON_WIDTH, BUTTON_HEIGHT, clrGreen); CreateButton(m_deleteLimitOrdersButton, "Delete Limits", deleteX, curY, DELETE_BUTTON_WIDTH, BUTTON_HEIGHT, clrRed); curY += BUTTON_HEIGHT + GAP; CreateButton(m_closeLossButton, "Close Loss", curX, curY, BUTTON_WIDTH, BUTTON_HEIGHT, clrRed); CreateButton(m_closeBuyButton, "Close Buy", curX + BUTTON_WIDTH + GAP, curY, BUTTON_WIDTH, BUTTON_HEIGHT, clrGreen); CreateButton(m_deleteStopOrdersButton, "Delete Stops", deleteX, curY , DELETE_BUTTON_WIDTH+10, BUTTON_HEIGHT, clrRed); CreateButton(m_resetButton, "Reset", deleteX+ 2*BUTTON_WIDTH-3*curX, curY, DELETE_BUTTON_WIDTH, 2*BUTTON_HEIGHT, clrRed); curY += BUTTON_HEIGHT + GAP; CreateButton(m_closeSellButton, "Close Sell", curX, curY, BUTTON_WIDTH, BUTTON_HEIGHT, clrBlue); CreateButton(m_deleteAllOrdersButton, "Delete All", curX + BUTTON_WIDTH + GAP, curY, BUTTON_WIDTH, BUTTON_HEIGHT, clrRed); CreateButton(m_deleteStopLimitOrdersButton, "Delete StopLimits", deleteX, curY , DELETE_BUTTON_WIDTH+10, BUTTON_HEIGHT, clrDarkRed); curY += BUTTON_HEIGHT + GAP; return true; }
4. Automatisieren der Erstellung von UI-Controls mit Hilfsfunktionen
Die manuelle Platzierung jeder Schaltfläche und jedes Eingabefeldes würde den Code wiederholen und schwerer zu pflegen machen. Stattdessen schreiben wir Hilfsfunktionen, die das Erstellen, Positionieren und Gestalten von Steuerelementen übernehmen.
Durch die Zentralisierung dieser Logik bleibt der UI-Code übersichtlich. Wenn wir uns entscheiden, das Aussehen von Schaltflächen oder Beschriftungen zu ändern, müssen wir nur eine Funktion aktualisieren, anstatt Dutzende von Zeilen in der gesamten Codebasis zu ändern.
//--- Helper: Create a text label using bool CreateLabelEx(CLabel &label, int x, int y, int height, string label_name, string text, color clr) { string unique_name = m_name + label_name; if(!label.Create(m_chart, unique_name, m_subwin, x, y, x, y+height)) return false; if(!Add(label)) return false; if(!label.Text(text)) return false; label.Color(clr); return true; } //--- Helper: Create and add a button control bool CreateButton(CButton &button, string name, int x, int y, int w = BUTTON_WIDTH, int h = BUTTON_HEIGHT, color clr = clrWhite) { if(!button.Create(m_chart, name, m_subwin, x, y, x+w, y+h)) return false; button.Text(name); button.Color(clr); if(!Add(button)) return false; return true; } //--- Helper: Create and add an edit control bool CreateEdit(CEdit &edit, string name, int x, int y, int w = EDIT_WIDTH, int h = EDIT_HEIGHT) { if(!edit.Create(m_chart, name, m_subwin, x, y, x+w, y+h)) return false; if(!Add(edit)) return false; return true; }
5. Behandlung von Nutzerinteraktionen durch ein zentrales Ereignissystem
Sobald die Nutzeroberfläche eingerichtet ist, brauchen wir einen Weg, um Interaktionen zu handhaben. Anstatt jeder Taste eine eigene Funktion zuzuweisen, verwenden wir einen zentralen Event-Dispatcher.
Dieser Dispatcher lauscht auf Nutzeraktionen, bestimmt, welches Steuerelement das Ereignis ausgelöst hat, und ruft die entsprechende Funktion auf. So bleibt die Ereignisbehandlung sauber und übersichtlich. Wenn Sie auf eine Schaltfläche klicken, um einen Handel zu platzieren, werden die Eingabedaten an die Auftragsausführungslogik gesendet, während ein Klick auf die Schaltfläche „Zurücksetzen“ die Felder löscht.
//+------------------------------------------------------------------+ //| Event handler | //+------------------------------------------------------------------+ bool CTradeManagementPanel::OnEvent(const int id, const long &lparam, const double &dparam, const string &sparam) { if(id == CHARTEVENT_OBJECT_CLICK) { if(sparam == m_buyButton.Name()) OnClickBuy(); else if(sparam == m_sellButton.Name()) OnClickSell(); else if(sparam == m_buyLimitButton.Name()) OnClickBuyLimit(); else if(sparam == m_buyStopButton.Name()) OnClickBuyStop(); else if(sparam == m_sellLimitButton.Name()) OnClickSellLimit(); else if(sparam == m_sellStopButton.Name()) OnClickSellStop(); else if(sparam == m_closeAllButton.Name()) OnClickCloseAll(); else if(sparam == m_closeProfitButton.Name()) OnClickCloseProfit(); else if(sparam == m_closeLossButton.Name()) OnClickCloseLoss(); else if(sparam == m_closeBuyButton.Name()) OnClickCloseBuy(); else if(sparam == m_closeSellButton.Name()) OnClickCloseSell(); else if(sparam == m_deleteAllOrdersButton.Name()) OnClickDeleteAllOrders(); else if(sparam == m_deleteLimitOrdersButton.Name()) OnClickDeleteLimitOrders(); else if(sparam == m_deleteStopOrdersButton.Name()) OnClickDeleteStopOrders(); else if(sparam == m_deleteStopLimitOrdersButton.Name()) OnClickDeleteStopLimitOrders(); else if(sparam == m_resetButton.Name()) OnClickReset(); // Handle reset button click } return true; }
6. Validierung und Ausführung von Market Orders
Bevor ein Handel ausgeführt wird, überprüft das System die Eingaben, um Fehler zu vermeiden. Es wird geprüft, ob das Handelsvolumen innerhalb akzeptabler Grenzen liegt und ob Stop-Loss und Take-Profit im Verhältnis zum Marktpreis richtig gesetzt sind.
Wenn die Eingabe gültig ist, bearbeitet das Handelsausführungsobjekt den Auftrag. Andernfalls warnt das System den Nutzer und verhindert, dass ein fehlerhafter Abschluss getätigt wird. Dieser Validierungsschritt ist entscheidend, um kostspielige Fehler zu vermeiden und sicherzustellen, dass das Panel zuverlässig funktioniert.
//+------------------------------------------------------------------+ //| Validate Buy order parameters (Market orders) | //+------------------------------------------------------------------+ bool CTradeManagementPanel::ValidateBuyParameters(double volume, double tp, double sl, double ask) { bool valid = true; m_tpErrorLabel.Text(""); m_slErrorLabel.Text(""); if(volume <= 0) { m_tpErrorLabel.Text("Invalid volume"); m_tpErrorLabel.Color(clrRed); valid = false; } if(tp != 0 && tp <= ask) { m_tpErrorLabel.Text("Invalid TP"); m_tpErrorLabel.Color(clrRed); valid = false; } if(sl != 0 && sl >= ask) { m_slErrorLabel.Text("Invalid SL"); m_slErrorLabel.Color(clrRed); valid = false; } return valid; } //+------------------------------------------------------------------+ //| Validate Sell order parameters (Market orders) | //+------------------------------------------------------------------+ bool CTradeManagementPanel::ValidateSellParameters(double volume, double tp, double sl, double bid) { bool valid = true; m_tpErrorLabel.Text(""); m_slErrorLabel.Text(""); if(volume <= 0) { m_tpErrorLabel.Text("Invalid volume"); m_tpErrorLabel.Color(clrRed); valid = false; } if(tp != 0 && tp >= bid) { m_tpErrorLabel.Text("Invalid TP"); m_tpErrorLabel.Color(clrRed); valid = false; } if(sl != 0 && sl <= bid) { m_slErrorLabel.Text("Invalid SL"); m_slErrorLabel.Color(clrRed); valid = false; } return valid; }
7. Verwaltung schwebender Aufträge mit zusätzlichen Parametern
Schwebende Aufträge erfordern mehr Einstellungen als Marktaufträge. Die Nutzer müssen einen Einstiegskurs und eine Verfallszeit angeben, weshalb wir unsere Validierungsfunktionen erweitern, um diese Eingaben zu verarbeiten.
Eine Hilfsfunktion interpretiert die Ablaufeinstellungen, ob der Nutzer eine feste Zeit oder eine Option „Good Till Canceled“ wünscht. Diese Struktur stellt sicher, dass anhängige Aufträge vor ihrer Übermittlung den richtigen Regeln folgen.
//+------------------------------------------------------------------+ //| Helper: Parse expiration input | //+------------------------------------------------------------------+ void ParseExpiration(string sExp, ENUM_ORDER_TYPE_TIME &type_time, datetime &expiration) { if(StringCompare(StringToUpper(sExp), "GTC") == 0) { type_time = ORDER_TIME_GTC; expiration = 0; } else { type_time = ORDER_TIME_SPECIFIED; expiration = StringToTime(sExp); } } //+------------------------------------------------------------------+ //| Button click handlers - Pending Orders | //+------------------------------------------------------------------+ void CTradeManagementPanel::OnClickBuyLimit() { double price = StringToDouble(m_buyLimitPriceEdit.Text()); double tp = StringToDouble(m_buyLimitTPEdit.Text()); double sl = StringToDouble(m_buyLimitSLEdit.Text()); double volume = StringToDouble(m_volumeEdit.Text()); string sExp = m_buyLimitExpEdit.Text(); ENUM_ORDER_TYPE_TIME type_time; datetime expiration; ParseExpiration(sExp, type_time, expiration); m_trade.BuyLimit(volume, price, Symbol(), sl, tp, type_time, expiration, ""); } void CTradeManagementPanel::OnClickBuyStop() { double price = StringToDouble(m_buyStopPriceEdit.Text()); double tp = StringToDouble(m_buyStopTPEdit.Text()); double sl = StringToDouble(m_buyStopSLEdit.Text()); double volume = StringToDouble(m_volumeEdit.Text()); string sExp = m_buyStopExpEdit.Text(); ENUM_ORDER_TYPE_TIME type_time; datetime expiration; ParseExpiration(sExp, type_time, expiration); m_trade.BuyStop(volume, price, Symbol(), sl, tp, type_time, expiration, ""); } void CTradeManagementPanel::OnClickSellLimit() { double price = StringToDouble(m_sellLimitPriceEdit.Text()); double tp = StringToDouble(m_sellLimitTPEdit.Text()); double sl = StringToDouble(m_sellLimitSLEdit.Text()); double volume = StringToDouble(m_volumeEdit.Text()); string sExp = m_sellLimitExpEdit.Text(); ENUM_ORDER_TYPE_TIME type_time; datetime expiration; ParseExpiration(sExp, type_time, expiration); m_trade.SellLimit(volume, price, Symbol(), sl, tp, type_time, expiration, ""); } void CTradeManagementPanel::OnClickSellStop() { double price = StringToDouble(m_sellStopPriceEdit.Text()); double tp = StringToDouble(m_sellStopTPEdit.Text()); double sl = StringToDouble(m_sellStopSLEdit.Text()); double volume = StringToDouble(m_volumeEdit.Text()); string sExp = m_sellStopExpEdit.Text(); ENUM_ORDER_TYPE_TIME type_time; datetime expiration; ParseExpiration(sExp, type_time, expiration); m_trade.SellStop(volume, price, Symbol(), sl, tp, type_time, expiration, ""); }
8. Implementierung von Großaufträgen für ein effizientes Handelsmanagement
Die manuelle Verwaltung mehrerer Handelsgeschäfte kann mühsam sein. Deshalb haben wir Funktionen eingebaut, die es den Nutzern ermöglichen, Aktionen auf mehrere Aufträge gleichzeitig anzuwenden.
Die Nutzer können alle Handelsgeschäfte oder nur die mit Gewinn oder die mit Verlust schließen oder die schwebenden Aufträge löschen. Die Ereignisbehandlungsprogramme für diese Aktionen durchlaufen die offenen Positionen und wenden die gewünschte Operation effizient an. Das macht die Verwaltung mehrerer Aufträge viel schneller und einfacher.
//+------------------------------------------------------------------+ //| Button click handlers - All Order Operations | //+------------------------------------------------------------------+ void CTradeManagementPanel::OnClickCloseAll() { for(int i = PositionsTotal()-1; i >= 0; i--) m_trade.PositionClose(PositionGetTicket(i)); } void CTradeManagementPanel::OnClickCloseProfit() { for(int i = PositionsTotal()-1; i >= 0; i--) if(PositionGetDouble(POSITION_PROFIT) > 0) m_trade.PositionClose(PositionGetTicket(i)); } void CTradeManagementPanel::OnClickCloseLoss() { for(int i = PositionsTotal()-1; i >= 0; i--) if(PositionGetDouble(POSITION_PROFIT) < 0) m_trade.PositionClose(PositionGetTicket(i)); } void CTradeManagementPanel::OnClickCloseBuy() { for(int i = PositionsTotal()-1; i >= 0; i--) if(PositionGetInteger(POSITION_TYPE)==POSITION_TYPE_BUY) m_trade.PositionClose(PositionGetTicket(i)); } void CTradeManagementPanel::OnClickCloseSell() { for(int i = PositionsTotal()-1; i >= 0; i--) if(PositionGetInteger(POSITION_TYPE)==POSITION_TYPE_SELL) m_trade.PositionClose(PositionGetTicket(i)); } void CTradeManagementPanel::OnClickDeleteAllOrders() { for(int i = OrdersTotal()-1; i >= 0; i--) m_trade.OrderDelete(OrderGetTicket(i)); } void CTradeManagementPanel::OnClickDeleteLimitOrders() { for(int i = OrdersTotal()-1; i >= 0; i--) if(OrderGetInteger(ORDER_TYPE)==ORDER_TYPE_BUY_LIMIT || OrderGetInteger(ORDER_TYPE)==ORDER_TYPE_SELL_LIMIT) m_trade.OrderDelete(OrderGetTicket(i)); } void CTradeManagementPanel::OnClickDeleteStopOrders() { for(int i = OrdersTotal()-1; i >= 0; i--) if(OrderGetInteger(ORDER_TYPE)==ORDER_TYPE_BUY_STOP || OrderGetInteger(ORDER_TYPE)==ORDER_TYPE_SELL_STOP) m_trade.OrderDelete(OrderGetTicket(i)); } void CTradeManagementPanel::OnClickDeleteStopLimitOrders() { for(int i = OrdersTotal()-1; i >= 0; i--) if(OrderGetInteger(ORDER_TYPE)==ORDER_TYPE_BUY_STOP_LIMIT || OrderGetInteger(ORDER_TYPE)==ORDER_TYPE_SELL_STOP_LIMIT) m_trade.OrderDelete(OrderGetTicket(i)); }
9. Hinzufügen von Reset- und Sichtbarkeitssteuerungen für bessere Nutzerfreundlichkeit
Um die Nutzerfreundlichkeit zu verbessern, haben wir zwei kleine, aber nützliche Funktionen hinzugefügt. Die erste ist eine Reset-Schaltfläche, die alle Eingabefelder löscht und das Panel in den Standardzustand zurückversetzt. Dies ist hilfreich, wenn Nutzer nach Änderungen wieder von vorne beginnen möchten.
Die zweite Funktion ist ein Umschalter für die Sichtbarkeit, mit dem der Nutzer das Panel ein- oder ausblenden kann. Auf diese Weise bleibt der Arbeitsbereich übersichtlich und das Bedienfeld ist dennoch leicht zugänglich, wenn es benötigt wird.
//+------------------------------------------------------------------+ //| Reset all input fields to default values | //+------------------------------------------------------------------+ void CTradeManagementPanel::OnClickReset() { // Reset volume to minimum allowed double minVolume = SymbolInfoDouble(Symbol(), SYMBOL_VOLUME_MIN); m_volumeEdit.Text(DoubleToString(minVolume, 2)); // Reset TP and SL for market orders m_tpEdit.Text("0.00000"); m_slEdit.Text("0.00000"); // Reset pending order prices to current ASK/BID double ask = SymbolInfoDouble(Symbol(), SYMBOL_ASK); double bid = SymbolInfoDouble(Symbol(), SYMBOL_BID); m_buyLimitPriceEdit.Text(DoubleToString(ask, 5)); m_buyStopPriceEdit.Text(DoubleToString(ask, 5)); m_sellLimitPriceEdit.Text(DoubleToString(bid, 5)); m_sellStopPriceEdit.Text(DoubleToString(bid, 5)); // Reset pending order TP/SL to 0 m_buyLimitTPEdit.Text("0.00000"); m_buyLimitSLEdit.Text("0.00000"); m_buyStopTPEdit.Text("0.00000"); m_buyStopSLEdit.Text("0.00000"); m_sellLimitTPEdit.Text("0.00000"); m_sellLimitSLEdit.Text("0.00000"); m_sellStopTPEdit.Text("0.00000"); m_sellStopSLEdit.Text("0.00000"); // Reset expiration dates to current day's end datetime now = TimeCurrent(); string exp_default = TimeToString(now, TIME_DATE) + " 23:59:59"; m_buyLimitExpEdit.Text(exp_default); m_buyStopExpEdit.Text(exp_default); m_sellLimitExpEdit.Text(exp_default); m_sellStopExpEdit.Text(exp_default); // Clear error messages m_tpErrorLabel.Text(""); m_slErrorLabel.Text(""); }
Ich habe den vollständigen Quellcode unter diesem Artikel angehängt, den Sie herunterladen können. Im nächsten Abschnitt werde ich Sie Schritt für Schritt durch die Integration der Klasse „Trade Management Panel“ in das Hauptprogramm führen.
Integration mit (New_Admin_Panel)
1. Einbinden des Trade Management-Kopfes in das New_Admin_Panel-System
Zuerst binden wir das TradeManagementPanel ein, indem wir seine Header-Datei einbeziehen. Damit ist alles, was wir für die Handelsausführung und -verwaltung benötigen, in unserem Verwaltungsbereich verfügbar. Einfach, aber wichtig.
#include <TradeManagementPanel.mqh>
2. Einrichten einer Trade Panel-Instanz
Das Handelsfeld muss im gesamten EA zugänglich sein, daher definieren wir es als globalen Zeiger. Auf diese Weise können wir überprüfen, ob sie bereits erstellt wurde, und bei Bedarf mit ihr interagieren.
CTradeManagementPanel *g_tradePanel = NULL;
3. Hinzufügen einer Schaltfläche für die Handelsverwaltung
Die Nutzer müssen die Möglichkeit haben, das Handelsforum zu öffnen, daher haben wir eine spezielle Schaltfläche in das Admin-Home-Panel eingefügt. Wir verwenden eine Hilfsfunktion, um ein einheitliches Layout und Styling zu gewährleisten. Diese Schaltfläche befindet sich direkt auf der Nutzeroberfläche und ist bereit, das Handelspanel zu starten, wenn sie angeklickt wird.
CButton g_tradeMgmtButton;
4. Das Handelspanel anklickbar machen
Und jetzt wird es interessant. Wir haben eine Funktion geschaffen, die den Lebenszyklus des Handelspanels verwaltet.
- Wenn das Handelspanel nicht existiert, erstellen wir es.
- Wenn sie bereits vorhanden ist, schalten wir sie einfach ein oder aus.
void HandleTradeManagement() { if(g_tradePanel == NULL) { g_tradePanel = new CTradeManagementPanel(); if(!g_tradePanel.Create(g_chart_id, "TradeManagementPanel", g_subwin, 310, 20, 310+565, 20+510)) { delete g_tradePanel; g_tradePanel = NULL; Print("Failed to create Trade Management Panel"); return; } } g_tradePanel.Toggle(); ChartRedraw(); }
Mit dieser Einrichtung erhalten die Nutzer mit einem Mausklick sofortigen Zugang zum Handelsmanagement.
5. Einbindung der Handelsverwaltung in Veranstaltungen
Als Nächstes stellen wir sicher, dass das Trade Panel auf Nutzeraktionen reagiert. Innerhalb der Ereignisbehandlung leiten wir die Interaktionen an das Panel weiter, wenn es geöffnet ist. Dadurch wird sichergestellt, dass das Panel in Echtzeit reagiert, sei es bei der Ausführung von Aufträgen, der Anpassung von Stop-Loss oder der Schließung von Handelsgeschäften.
if(g_tradePanel != NULL && g_tradePanel.IsVisible()) return g_tradePanel.OnEvent(id, lparam, dparam, sparam);
6. Aufräumen, wenn der EA herunterfährt
Wenn der EA entfernt wird, müssen wir Speicher freigeben und Lecks verhindern. Das bedeutet, dass das Handelspanel ordnungsgemäß vernichtet werden muss.
if(g_tradePanel != NULL) { g_tradePanel.Destroy(reason); delete g_tradePanel; g_tradePanel = NULL; }
Keine übrig gebliebenen Instanzen. Keine Verschwendung von Ressourcen. Einfach ein sauberes Herunterfahren. Die vollständige Quelldatei für das New_Admin_Panel ist unten zum Download angehängt. Sie können dasselbe Verfahren mit ein paar Änderungen anwenden, um das Trade Management Panel in Ihre eigenen Projekte zu integrieren. Im nächsten Abschnitt werden wir die Testergebnisse vorstellen.
Tests
Nachdem ich den Code erfolgreich kompiliert hatte, startete ich ihn auf der Plattform MetaTrader 5. Die folgenden Abbildungen zeigen die erfolgreiche Einrichtung des Trade Management Panels über das Admin Home Panel. Über das Home-Panel können wir das Handelsmanagement-Panel ein- und ausschalten, sodass wir bei Bedarf den Chart vollständig anzeigen können. Wenn das Handelsmanagement-Panel aktiv ist, können wir seine Steuerelemente direkt auf dem Chart bedienen, während wir weiterhin die Kursentwicklung beobachten - ein Vorteil für den Scalp-Handel. Siehe das Bild unten.
Verwendung des Trade Management Panels
Schlussfolgerung
Die Entwicklung der Klasse Trade Management Panel ist ein weiterer Meilenstein bei der Anwendung der Modularisierung, um unseren Code effektiv zu strukturieren und große Programmkomponenten projektübergreifend wiederverwendbar zu machen. Wie wir bereits erwähnt haben, bietet dieser Ansatz zahlreiche Vorteile. Wir sind den Entwicklungsprozess Schritt für Schritt durchgegangen und haben gezeigt, wie er abläuft, und ich bin sicher, dass es wertvolle Hinweise gibt, die man mitnehmen kann.
Ich erhebe nicht den Anspruch, in diesem Bereich perfekt zu sein - ich lerne und verbessere mich ständig. Vielleicht gibt es Fehler, die die Erfahreneren erkennen können, und ich freue mich über konstruktives Feedback in den Kommentaren, damit alle davon profitieren. Ich schätze die Vorteile, die dieses Panel denjenigen bietet, die es nützlich finden.
Ich möchte auch Omega J Msigwa für seinen Beitrag zur Codebase mit dem Quellcode des Informative Dashboard danken, der während der Entwicklung hilfreiche Einblicke lieferte. Zögern Sie nicht, den beigefügten Code zu erweitern und zu ändern, um Ihre MQL5-Kenntnisse zu verbessern und noch leistungsfähigere Tools zu erstellen.
Datei | Beschreibung |
---|---|
TradeManagementPanel.mqh | Quellcode des Headers der Klasse, die eine grafische Schnittstelle für die effiziente Ausführung, Verwaltung und Änderung von Handelsgeschäften innerhalb des New_Admin_Panel EA bereitstellt. Er kann auch mit anderen EAs integriert werden. |
Neues_Admin_Panel.mq5 | Der Quellcode des New_Admin_Panel EA dient als zentrale Schnittstelle für die Verwaltung von Handelsausführung, Kommunikation, Analysen und anderen administrativen Funktionen, wenn er für die Ausführung innerhalb der Handelsplattform kompiliert wird. |
Übersetzt aus dem Englischen von MetaQuotes Ltd.
Originalartikel: https://www.mql5.com/en/articles/17396





- 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.