Risikomanagement (Teil 2): Implementierung der Losberechnung in einer grafischen Schnittstelle
- Einführung
- Verbesserungen der Funktionen zum Abrufen von Losen und Stop Loss
- Erforschung der Bibliotheken für Steuerelemente und Panels in MQL5
- Funktionen zum Erstellen von Bedienfeldkomponenten (Beschriftungen, Schaltflächen usw.)
- Erstellen von Objekten im Clientbereich
- Funktionen zur dynamischen Aktualisierung von Comboboxen und anderen Elementen
- Behandlung von Tastaturereignissen: Implementierung von OnCharEvent
- Panel-Initialisierung: Verwendung des OnInit-Ereignisses und allgemeine Einstellungen
- Panelprüfung und Validierung
- Schlussfolgerung
Einführung
Herzlich willkommen! In diesem Artikel, der eine Fortsetzung des vorangegangenen Artikels darstellt, werden wir alles, was wir besprochen haben, in die Praxis umsetzen und darüber hinaus einige der Funktionen in der Datei verbessern. Um unsere Arbeit zu erleichtern, werden wir die leistungsstarken Steuerelementbibliotheken von MQL5 nutzen. Ziel ist es, das Gelernte in der Praxis effizienter anzuwenden und Ihnen zu zeigen, wie Sie grafische Oberflächen mit unseren Risikomanagementfunktionen kombinieren können. Am Ende haben Sie ein solides Werkzeug, mit dem Sie die Losgröße und den Stop Loss (SL) genau und effektiv berechnen können.
Verbesserungen der Funktionen zum Abrufen von Losgrößen und Stop Loss
Wir beginnen diesen Artikel mit der Verbesserung der zuvor erstellten Funktionen und konzentrieren uns dabei auf die Vereinfachung und Optimierung ihrer Funktionsweise. Zu den wichtigsten Änderungen gehören die Hinzufügung von Debugging-Meldungen (PrintFormat und Print), die helfen, Fehler in Echtzeit zu identifizieren, sowie die Schaffung neuer Funktionen zur effizienteren Berechnung der idealen Losgröße und des Stop-Loss-Abstands.
Verbesserungen an der Funktion GetMaxLote
Diese Funktion berechnet die maximale Losgröße, die auf der Grundlage der verfügbaren freien Marge und der Symbolspezifikationen gehandelt werden kann.
//+----------------------------------------------------------------------------------------------+ //| Calculates the maximum lot size that can be traded based on free margin and symbol specifics | //+----------------------------------------------------------------------------------------------+ double GetMaxLote(ENUM_ORDER_TYPE type, double DEVIATION = 100, double STOP_LIMIT = 50) { double VOLUME = 1.0; // Initial volume size ENUM_ORDER_TYPE new_type = MarketOrderByOrderType(type); double price = PriceByOrderType(_Symbol, type, DEVIATION, STOP_LIMIT); // Price for the given order type double volume_step = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_STEP); // Volume step for the symbol double margin = EMPTY_VALUE; // Required margin, initialized as empty ResetLastError(); if(!OrderCalcMargin(new_type, _Symbol, VOLUME, price, margin)) { Print("OrderCalcMargin() failed. Error ", GetLastError()); return 0; // Exit the function if margin calculation fails } if(AccountInfoDouble(ACCOUNT_MARGIN_FREE) <= 0) { PrintFormat("Free margin of %+.2f is invalid, you cannot open trades right now",AccountInfoDouble(ACCOUNT_MARGIN_FREE)); return 0; } double result = MathFloor((AccountInfoDouble(ACCOUNT_MARGIN_FREE) / margin) / volume_step) * volume_step; return result; // Return the calculated maximum lot size }
Durchgeführte Verbesserungen:
- Debug-Meldungen: Der Nutzer wird nun benachrichtigt, wenn die Berechnung der Marge fehlschlägt oder wenn die freie Marge nicht ausreicht.
- Validierung des freien Spielraums: Es ist eine Prüfung vorgesehen, um Berechnungen zu verhindern, wenn der freie Spielraum kleiner oder gleich Null ist.
Die Funktion GetIdealLot
Diese Funktion berechnet die ideale Losgröße auf der Grundlage des maximal zulässigen Risikos pro Handel und der aktuellen Marktbedingungen.
//+---------------------------------------------------------------------+ //| Determine the optimal lot size based on risk and current conditions | //+---------------------------------------------------------------------+ void GetIdealLot(double& nlot, double glot, double max_risk_per_operation, double& new_risk_per_operation, long StopLoss) { if(StopLoss <= 0) { Print("[ERROR SL] Stop Loss distance is less than or equal to zero, now correct the stoploss distance: ", StopLoss); nlot = 0.0; return; } Print("Max Lot: ", glot, " | RiskPerOperation: ", max_risk_per_operation); new_risk_per_operation = 0; long spread = (long)SymbolInfoInteger(_Symbol, SYMBOL_SPREAD); double tick_value = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_VALUE); double step = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_STEP); double rpo = (glot * (spread + 1 + (StopLoss * tick_value))); if(rpo > max_risk_per_operation) { if(max_risk_per_operation <= 0) return; double new_lot = (max_risk_per_operation / rpo) * glot; new_lot = MathFloor(new_lot / step) * step; new_risk_per_operation = new_lot * (spread + 1 + (StopLoss * tick_value)); nlot = new_lot; } else { new_risk_per_operation = rpo; nlot = glot; } if(nlot <= 0) PrintFormat("The lot %.2f is invalid, the risk %.2f increases or the sl %i decreases",nlot,max_risk_per_operation,StopLoss); }
Verbesserungen an dieser Funktion:
- Stop-Loss-Überprüfung: Überprüft, ob der Stop-Loss-Abstand gültig ist, bevor er fortgesetzt wird.
- Klare Botschaften: Informiert darüber, ob die berechnete Losgröße ungültig ist, oder, ob das Risiko angepasst werden muss.
Neue Funktion GetLotByRiskPerOperation
Diese Funktion ermöglicht die Berechnung der idealen Losgröße ausschließlich auf der Grundlage des Risikos pro Operation (in USD) und des Auftragstyps, ohne dass der Stop-Loss-Abstand angegeben werden muss.
//+--------------------------------------------------------------------+ //| Function to obtain the ideal lot based on your risk per operation | //+--------------------------------------------------------------------+ // risk_per_operation in USD, not % double GetLotByRiskPerOperation(double risk_per_operation, const ENUM_ORDER_TYPE order_type, double DEVIATION = 100, double STOP_LIMIT = 50) { double VOLUME = 1.0; // Initial volume size ENUM_ORDER_TYPE new_type = MarketOrderByOrderType(order_type); double price = PriceByOrderType(_Symbol, order_type, DEVIATION, STOP_LIMIT); // Price for the given order type double volume_step = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_STEP); // Volume step for the symbol double margin = EMPTY_VALUE; // Required margin, initialized as empty ResetLastError(); if(!OrderCalcMargin(new_type, _Symbol, VOLUME, price, margin)) { Print("OrderCalcMargin() failed. Error ", GetLastError()); return 0; // Exit the function if margin calculation fails } double result = MathFloor((risk_per_operation / margin) / volume_step) * volume_step; if(result <= 0) { PrintFormat("The lot %.2f is invalid, the risk %.2f increases",result,risk_per_operation); return 0; } PrintFormat("The ideal lot for %.2f risk per trade is %.2f lots",risk_per_operation,result); return result; // Return the calculated maximum lot size } //+------------------------------------------------------------------+
Hauptmerkmale:
- Vereinfachte Berechnung: Verwendet nur das Risiko in USD, wodurch die Abhängigkeit von zusätzlichen Werten entfällt.
- Fehlersuche: Klare Meldungen bei ungültigen Losgrößen oder übermäßigem Risiko.
Verbesserungen an der Funktion CalculateSL
Diese Funktion berechnet den idealen SL-Abstand in Punkten entsprechend dem Risiko pro Handel und der gewählten Losgröße.
//+-----------------------------------------------------------------------+ //| Calculate the stop loss distance in points based on risk and lot size | //+-----------------------------------------------------------------------+ long CalculateSL(const ENUM_ORDER_TYPE type, double risk_per_operation, double &chosen_lot, double DEVIATION = 100, double STOP_LIMIT = 50) { double lot = GetLotByRiskPerOperation(risk_per_operation,type,DEVIATION,STOP_LIMIT); chosen_lot = lot; long spread = (long)SymbolInfoInteger(_Symbol, SYMBOL_SPREAD); double tick_value = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_VALUE); double result = ((risk_per_operation / lot) - spread - 1) / tick_value; long ideal_sl = long(MathRound(result)); if(ideal_sl <= 0) { PrintFormat("Stop loss %i invalid, please increase the risk per trade %.2f",ideal_sl,risk_per_operation); return 0; } return ideal_sl; }
Verbesserungen:
- Integration mit GetLotByRiskPerOperation: Verwendet die berechnete Losgröße zur Bestimmung von Stop Loss.
- Verbesserte Berechnung: Stellt sicher, dass der Stop-Loss immer positiv und gültig ist.
Mit diesen Verbesserungen sind die Funktionen nun robuster, leichter zu debuggen und flexibler. Fehlermeldungen ermöglichen eine schnelle Identifizierung von Problemen, und die neuen Implementierungen vereinfachen den Berechnungsprozess für Losgröße und Stop Loss.
Erforschung der Bibliotheken für Steuerelemente und Panels in MQL5
Bevor wir mit den MQL5-Steuerelementbibliotheken fortfahren, müssen wir einige Grundlagen darüber verstehen, wie Objekte in MQL5 funktionieren.
Grafische Objekte in MQL5:
In MQL5 sind grafische Objekte visuelle Elemente, die Sie in einem Chart platzieren können, um Informationen darzustellen oder mit dem Nutzer zu interagieren. Diese Objekte werden anhand von zwei Hauptkoordinaten positioniert:
- X (horizontal) – definiert die Position des Objekts entlang der horizontalen Achse.
- Y (vertikal) – definiert die Position entlang der vertikalen Achse.
Diese Koordinaten bestimmen genau, wo das Objekt auf dem Chart oder der Tafel erscheint.
Grundlegende Eigenschaften von Grafikobjekten
Zusätzlich zur Position haben grafische Objekte Eigenschaften wie:
- Höhe – die Größe des vertikalen Raums, den das Objekt einnimmt.
- Breite – der horizontale Raum, den es einnimmt.
Diese Eigenschaften sollten richtig eingestellt werden, um sicherzustellen, dass das Objekt nicht aus dem Bereich, in dem es erscheinen soll, überläuft. Wenn Sie z. B. eine Nutzeroberfläche entwerfen, sorgen diese Maße dafür, dass die Elemente geordnet und innerhalb der sichtbaren Grenzen bleiben.
Die ecke für den Anker
Der Ankerpunkt ist der Teil des Objekts, der bei der Verwendung der X- und Y-Koordinaten als Referenz verwendet wird. Dieser Punkt bestimmt, wie die Objektposition interpretiert wird. Es gibt vier Arten von Ankerpunkten:
Identifier | Beschreibung |
|---|---|
CORNER_LEFT_UPPER | Das Koordinatenzentrum befindet sich in der linken oberen Ecke des Charts. |
CORNER_LEFT_LOWER | Das Koordinatenzentrum befindet sich in der linken unteren Ecke des Charts. |
CORNER_RIGHT_LOWER | Das Koordinatenzentrum befindet sich in der unteren rechten Ecke des Charts. |
CORNER_RIGHT_UPPER | Der Koordinatenmittelpunkt befindet sich in der oberen rechten Ecke des Charts. |
Diese 4 Ankertypen können in der Abbildung wie folgt dargestellt werden:

Anmerkung: Im Allgemeinen und standardmäßig ist der am häufigsten verwendete Ankertyp CORNER_LEFT_UPPER. Dies kann je nach Anwendungsfall variieren, je nachdem, was für Sie einfacher ist. In diesem Artikel werden wir nur mit dem oben genannten Anker arbeiten.
Die Bibliothek der Steuerelement von MQL5
Die MQL5-Bibliothek Controls ist eine Klassenbibliothek zur Erstellung von Steuerelementen, Bedienfeldern und mehr. Die Beschreibungen der Steuerelemente entnehmen Sie bitte der Dokumentation.
Öffnen Sie dazu die Dokumentation durch Drücken von F1 in Ihrem MetaEditor. Gehen Sie dort zum Abschnitt Inhalt und klicken Sie auf MQL5 Reference Manual. Scrollen Sie zum Ende der Seite, wo Sie einen Abschnitt mit der Bezeichnung Standardbibliothek finden. Wenn Sie darauf klicken, öffnet sich eine Liste mit verschiedenen Bibliotheken für unterschiedliche Anwendungen. Wir interessieren uns für die Bibliothek Controls.
Wenn Sie diese Bibliothek auswählen, erhalten Sie Zugang zu mehreren Tabellen, in denen die Verwendungszwecke und Funktionen der einzelnen Steuerelemente sowie deren Gruppierung beschrieben sind. Damit Sie die Struktur und die Beziehungen zwischen den Steuerklassen besser verstehen, habe ich ein Diagramm erstellt, das zeigt, wie die Vererbung und die mit diesen Steuerelementen verbundenen Klassen funktionieren.

Wie wir sehen können, ist die Hauptklasse CObject, von der die Klasse CWnd geerbt hat. Von CWnd werden zwei weitere Klassen abgeleitet: CWndContainer, das für komplexe Steuerelemente gedacht ist, und CWndObj, das für einfachere Steuerelemente verwendet wird. In unseren Panels werden wir je nach Einzelfall beide Arten von Kontrollen verwenden. Im Moment konzentrieren wir uns jedoch auf das CAppDialog-Steuerelement, die Klasse, die das Hauptfenster erstellt, wie das, das wir entwickeln.
In diesem Panel fügen wir zunächst grundlegende Steuerelemente hinzu, z. B. Kennzeichnungen zur Anzeige von Text, Kombinationsfelder zur Auswahl von Auftragstypen, Schaltflächen zum Speichern von Variablen und Abrufen von Ergebnissen sowie Bearbeitungsfelder, in die der Nutzer manuell Werte eingeben kann, z. B. das gewünschte Risiko pro Handel.
Entwerfen eines Panels in MQL5: Planung und grafische Elemente
Bevor wir mit der Programmierung eines Panels in MQL5 beginnen, sollten wir sein Design planen. Am besten ist es, eine visuelle Skizze zu erstellen, wie sie in der beigefügten Abbildung zu sehen ist, und dafür einfache Tools wie Paint, Canva usw. zu verwenden. Dieser Schritt stellt sicher, dass alle notwendigen Elemente enthalten und richtig positioniert sind, um Auslassungen zu vermeiden und die Umsetzung zu erleichtern. Ich habe dies auf folgende Weise gemacht:

Wenn man das Bild des Bedienfelds analysiert, sieht man mehrere Steuerelemente, die verschiedene Funktionen erfüllen. Diese Steuerelemente ermöglichen es dem Nutzer, auf klare und funktionale Weise mit der Schnittstelle zu interagieren. Nachfolgend finden Sie eine detaillierte Beschreibung der wichtigsten Elemente und wie sie in unserem Unterricht verwendet werden:
| Controls | Funktion | Beispiel im Panel | Grundlegendes Objekt |
|---|---|---|---|
| CLabel | Dient zur Anzeige von statischem Text auf dem Bedienfeld, z. B. Beschreibungen oder Anweisungen für den Nutzer. | Kennzeichnungen wir „Risk Per Operation %“, „Deviation (Points)“ und „Stop Limit (Points)“. | Abgeleitet vom grafischen Objekt OBJ_LABEL |
| CComboBox | Ermöglicht dem Nutzer die Auswahl einer Option aus einer Dropdown-Liste. | „Get Lot“ und „Get SL“, die die Auswahl der Auftragsart für die entsprechende Lot- und Stop-Loss-Berechnung ermöglichen. | Zusammengesetztes Objekt (nicht von einem einzelnen grafischen Objekt abgeleitet) |
| CButton | Interaktive Schaltflächen, die beim Drücken Aktionen ausführen. | Die Schaltfläche neben „SL-Punkt“, die eine Aktion wie das Berechnen oder Bestätigen eines Wertes ausführt. Enthält auch die Schaltfläche „Get ideal sl“. | Abgeleitet vom grafischen Objekt OBJ_BUTTON |
| CEdit | Ermöglicht es dem Nutzer, Daten einzugeben oder manuell zu bearbeiten. | Felder wie „Risk Per Operation %“, „Deviation (Points)“, and „Stop Limit (Points)“, in die der Nutzer Werte eingibt. | Entspricht dem grafischen Objekt OBJ_EDIT |
Funktionen zum Erstellen von Bedienfeldkomponenten (Beschriftungen, Schaltflächen usw.)
Bevor wir mit der Erstellung der Funktionen beginnen, müssen wir zunächst eine Expert Advisor-Vorlage in MetaEditor erstellen. Diese Datei wird zunächst leer sein, und sobald sie fertig ist, können wir damit beginnen, die notwendigen Bibliotheken einzubinden, um mit den Komponenten unserer grafischen Oberfläche zu arbeiten.
Fügen Sie dazu die folgenden Codezeilen am Anfang Ihrer Datei ein:
#include <Controls\Dialog.mqh> #include <Controls\Edit.mqh> #include <Controls\Label.mqh> #include <Controls\Button.mqh> #include <Risk Management.mqh> #include <Controls\ComboBox.mqh>
Erstellung der Hauptklasse
Wir beginnen mit der Definition der Parameter, mit denen wir die Abmessungen der verschiedenen Steuerelemente festlegen werden. Mit diesen Werten können wir die Größe von Oberflächenkomponenten wie Schaltflächen, Textfeldern und Dropdown-Listen anpassen.
//+------------------------------------------------------------------+ //| defines | //+------------------------------------------------------------------+ //--- for edits #define EDIT_HEIGHT (20) // edit height //--- for buttons #define BUTTON_WIDTH (80) // size by X coordinate #define BUTTON_HEIGHT (20) // size by Y coordinate //--- for combo box #define COMBO_BOX_WIDTH (200) // size by X coordinate #define COMBO_BOX_HEIGHT (20) // size by Y coordinate
Als Nächstes werden wir ein String-Array mit einer Kapazität von 8 Elementen erstellen. In diesem Array werden die verschiedenen Auftragsarten im Textformat gespeichert.
string elements_order_type[8] = { "ORDER_TYPE_BUY", "ORDER_TYPE_SELL", "ORDER_TYPE_BUY_LIMIT", "ORDER_TYPE_SELL_LIMIT", "ORDER_TYPE_BUY_STOP", "ORDER_TYPE_SELL_STOP", "ORDER_TYPE_BUY_STOP_LIMIT", "ORDER_TYPE_SELL_STOP_LIMIT" };
Einrichtung der Klasse für das Gremium für Risikomanagement
Wir werden nun eine neue Klasse erstellen, die den öffentlichen Teil der Klasse CAppDialog erbt, die die Hauptklasse für Panels ist. Dieser Kurs wird als Grundlage für unser Risikomanagement-Panel dienen.
//+-------------------------------------------------------------------+ //| Class CRiskManagementPanel | //| This class inherits from CAppDialog and will define the panel for | //| managing risk parameters. | //+-------------------------------------------------------------------+ class CRiskManagementPanel : public CAppDialog { // Declare private members here. };
Hinzufügen von Methoden und Attributen
Als Nächstes werden wir Methoden und Attribute zu unserer Klasse hinzufügen. Wie bereits erwähnt, benötigen wir mehrere grafische Steuerelemente, und wie in der vorherigen Abbildung gezeigt, werden diese Steuerelemente zum privaten Abschnitt der Klasse hinzugefügt.
private:
CLabel m_label_risk_per_operation;
CEdit m_edit_risk_per_operation;
CLabel m_label_deviaiton;
CEdit m_edit_deviaiton;
CLabel m_label_stop_limit;
CEdit m_edit_stop_limit;
CLabel m_label_get_lote;
CComboBox m_combobox_odertype_get_lot;
CLabel m_label_sl;
CEdit m_edit_sl;
CButton m_buttom_get_lote;
CLabel m_label_result_lote;
CLabel m_label_the_result_lote;
CLabel m_label_get_sl;
CComboBox m_combobox_odertype_get_sl;
CLabel m_label_lote;
CButton m_buttom_get_sl;
CLabel m_label_result_sl;
CLabel m_label_the_result_sl;Private Variablen zum Speichern von Daten
Zusätzlich zu den Steuerelementen benötigen wir private Variablen, um die vom Nutzer über die Steuerelemente Edit und ComboBox eingegebenen Daten zu speichern. Diese Variablen enthalten Werte wie Risiko pro Vorgang, Abweichung, Auftragsart und Stop-Loss-Werte.
// Variables to store the data entered by the user double deviation; // Stores deviation entered by the user double stop_limit; // Stores stop limit entered by the user double risk_per_operation; // Stores risk per operation entered by the user long sl; // Stores stop loss value entered by the user ENUM_ORDER_TYPE order_type_get_sl; // Stores the selected order type for stop loss ENUM_ORDER_TYPE order_type_get_lot; // Stores the selected order type for lot size
Um den privaten Teil unserer Klasse abzuschließen, deklarieren wir alle notwendigen Funktionen für die Handhabung der grafischen Steuerelemente und der Nutzerinteraktion. Zu diesen Funktionen gehören u. a. die Erstellung von Objekten, Funktionen, die ausgelöst werden, wenn der Nutzer das ausgewählte Element in einer ComboBox ändert, und Funktionen, die die Werte der Kennzeichnungen aktualisieren.
Deklaration der privaten Funktionen:
//--- create labels and buttons bool CreateAreaClientPanel(); //--- functions to edit labels dynamically void EditLabelResultSL(string text); void EditLabelResultLote(string text); //--- create controls (buttons, labels, edits, combo boxes) bool CreateEdit(CEdit &m_edit, const string name, const int x1, const int y1, string initial_Text = ""); bool CreateLabel(CLabel &label, const string name, const string text, const int x1, const int y1); bool CreateButton(CButton &button, const string name, const string text, const int x1, const int y1, int x2_ = BUTTON_WIDTH, int y2_ = BUTTON_HEIGHT); bool CreateComboBox(CComboBox &combo_box, const string name, string &elements[], string initial_text, const int x1, const int y1); //--- combo box functions for handling user input void OnChangeComBoxOrderTypeGetLote(); void OnChangeComBoxOrderTypeGetSL();
Deklaration der öffentlichen Methoden:
Im öffentlichen Abschnitt der Klasse fügen wir zwei Funktionen zur Erstellung des Panels und der Steuerelemente im Client-Bereich hinzu. Wir deklarieren auch einen OnEvent-Handler zur Verarbeitung von Chart-Ereignissen und eine Funktion zur Umwandlung einer Zeichenkette in einen ENUM_ORDER_TYPE, zusätzlich zum Konstruktor und Destruktor.
public: CRiskManagementPanel(void); ~CRiskManagementPanel(void); //--- create panel and controls virtual bool Create(const long chart, const string name, const int subwin, const int x1, const int y1, const int x2, const int y2); //--- chart event handler virtual bool OnEvent(const int id, const long &lparam, const double &dparam, const string &sparam); //--- function to convert string to ENUM_ORDER_TYPE static ENUM_ORDER_TYPE StringOrderTypeToEnum(const string OrderType);
Mit diesen Deklarationen haben wir alles, was wir brauchen, um das Panel und die Steuerelemente zu erstellen und die Chart-Ereignisse zu verwalten.
Erstellen von Methoden zum Hinzufügen von Steuerelementen
In diesem Abschnitt werden wir die Methoden zur Erstellung grundlegender Steuerelemente wie Kennzeichnungen (labels), Schaltflächen (buttons), Editierfelder (edits) und Kombinationsfelder (ComboBoxes), die Teil unseres Risikomanagement-Panels sein werden, im Detail betrachten. Außerdem werden wir eine wichtige Methode kennenlernen, die sicherstellt, dass das Panel korrekt funktioniert, indem Steuerelemente an den Client-Bereich gebunden werden. Dadurch wird sichergestellt, dass die Steuerelemente am Bedienfeld ausgerichtet bleiben, wenn es verschoben oder in der Größe verändert wird.
Binden von Steuerelementen an den Client-Bereich
Damit sich die Steuerelemente (z. B. Schaltflächen, Kennzeichnungen usw.) mit dem Panel bewegen, wenn der Nutzer mit ihm interagiert, müssen sie im Client-Bereich des Panels verankert werden. Dazu verwenden wir die Methode Add aus der Klasse CDialog. Diese Methode registriert die Steuerelemente in der Objektliste des Client-Bereichs, damit sie mit dem Panel synchronisiert bleiben. Deklaration
//+------------------------------------------------------------------+ //| Add control to the client area (by pointer) | //+------------------------------------------------------------------+ bool CDialog::Add(CWnd *control) { return(m_client_area.Add(control)); } //+------------------------------------------------------------------+ //| Add control to the client area (by reference) | //+------------------------------------------------------------------+ bool CDialog::Add(CWnd &control) { return(m_client_area.Add(control)); }
Die Add-Methode hat zwei Versionen: eine, die einen Zeiger auf das Steuerelement erhält, und eine andere, die einen Verweis erhält. Beide Versionen fügen das Steuerelement in den Container m_client_area ein, der alle mit dem Panel verbundenen Elemente verwaltet.
Wenn wir diese Verknüpfung weglassen, folgen die Steuerelemente nicht der Bewegung des Panels, was zu visuellen Inkonsistenzen oder Interaktionsproblemen führen kann.
Methoden zur Erstellung von Basic Controls
1. Funktion zum Erstellen von Kennzeichnungen (label)
Kennzeichnungen werden verwendet, um statischen Text im Panel anzuzeigen, z. B. Überschriften oder Anweisungen. Die Funktion zur Erstellung einer Kennzeichnung umfasst:
- Festlegung der Kontrollkoordinaten.
- Erstellen des Kennzeichnungdobjekts mit der Methode Create.
- Einstellen des Beschriftungstextes mit Text.
- Verknüpfung der Kennzeichnung mit dem Kundenbereich durch Hinzufügen.
Code-Beispiel:
//+------------------------------------------------------------------+ //| Function to create labels | //+------------------------------------------------------------------+ bool CRiskManagementPanel::CreateLabel(CLabel &label,const string name, const string text, const int x1, const int y1) { //--- coordinates int x2=x1+50; int y2=y1+20; //--- create if(!label.Create(m_chart_id,name+"Label",m_subwin,x1,y1,x2,y2)) return(false); if(!label.Text(text)) return(false); if(!Add(label)) return(false); //--- succeed return(true); }
- label.Create: Erzeugt das grafische Steuerelement an den angegebenen Koordinaten.
- label.Text: Legt den Text fest, der auf dem Kennzeichen angezeigt werden soll.
- Add: Fügt das Kennzeichen dem Panel hinzu, damit es zusammen mit anderen Steuerelementen verwaltet werden kann.
2. Funktion zum Erstellen von Schaltflächen
Schaltflächen ermöglichen die direkte Interaktion des Nutzers mit dem Panel. Die Funktion enthält Parameter für Text, Abmessungen und Startkoordinaten. Außerdem kann die Größe der Schaltflächen anhand von Standardwerten angepasst werden.
Code-Beispiel:
//+------------------------------------------------------------------+ //| Function to create buttons | //+------------------------------------------------------------------+ bool CRiskManagementPanel::CreateButton(CButton &buttom,const string name, const string text, const int x1, const int y1,int x2_= BUTTON_WIDTH,int y2_ = BUTTON_HEIGHT) { int x2=x1+x2_; int y2=y1+y2_; //--- create if(!buttom.Create(m_chart_id,name,m_subwin,x1,y1,x2,y2)) return(false); if(!buttom.Text(text)) return(false); if(!Add(buttom)) return(false); //--- succeed return(true); }
Abmessungen der Schaltflächen:
- x2 und y2: Optionale Parameter zur Festlegung von Breite und Höhe der Schaltfläche. Wenn nicht angegeben, werden die Standardwerte (BUTTON_WIDTH und BUTTON_HEIGHT) verwendet.
Verwendete Methoden:
- button.Create: Erzeugt die Schaltfläche an den angegebenen Koordinaten.
- button.Text: Weist den Text zu, der auf der Schaltfläche erscheinen soll.
- Add: Registriert die Schaltfläche in der Elementliste des Panels.
3. Funktion zum Erstellen von Textfelder
Mit den Bearbeitungssteuerelementen können die Nutzer Text in das Panel eingeben. Diese Funktion enthält einen optionalen Parameter, um einen anfänglichen Textwert festzulegen.
Code-Beispiel:
//+------------------------------------------------------------------+ //| Function to create edits | //+------------------------------------------------------------------+ bool CRiskManagementPanel::CreateEdit(CEdit &m_edit, const string name, const int x1, const int y1, string initial_Text = "") { //--- coordinates int y2=y1+EDIT_HEIGHT; int x2 =x1 +100; //--- create if(!m_edit.Create(m_chart_id,name+"Edit",m_subwin,x1,y1,x2,y2)) return(false); //--- allow editing the content if(!m_edit.ReadOnly(false)) return(false); if(!Add(m_edit)) return(false); m_edit.Text(initial_Text); //--- succeed return(true); }
Textfeld Abmessungen:
- Eine feste Breite von 100 und eine durch EDIT_HEIGHT definierte Höhe bestimmen die Größe des Feldes.
Verwendete Methoden:
- m_edit.ReadOnly(false): Ermöglicht die Bearbeitung des Feldes.
- m_edit.Text: Legt den anfänglichen Text fest, der in dem Feld angezeigt wird.
4. Funktion zum Erstellen von ComboBoxen
ComboBoxen sind komplexe Steuerelemente, mit denen Nutzer ein Element aus einer Dropdown-Liste auswählen können. Diese Funktion bietet auch die Möglichkeit, einen Anfangstext festzulegen und mehrere Elemente hinzuzufügen.
Code-Beispiel:
//+-------------------------------------------------------------------+ //| Function to create the complex object: combo box | //| This function creates a combo box with multiple selectable items. | //+-------------------------------------------------------------------+ bool CRiskManagementPanel::CreateComboBox(CComboBox &combo_box, const string name, string &elements[], string initial_text, const int x1, const int y1) { //--- calculate coordinates for the combo box int x2 = x1 + COMBO_BOX_WIDTH; int y2 = y1 + COMBO_BOX_HEIGHT; //--- create the combo box control if (!combo_box.Create(m_chart_id, name, m_subwin, x1, y1, x2, y2)) return (false); //--- add items to the combo box for (int i = 0; i < ArraySize(elements); i++) { if (!combo_box.AddItem(elements[i], i)) return (false); } //--- select the initial text combo_box.SelectByText(initial_text); //--- add the combo box to the panel if (!Add(combo_box)) return (false); //--- successfully created the combo box return (true); }
ComboBox-Elemente:
- elements[]: Ein Array von Strings, das die in der Dropdown-Liste angezeigten Elemente enthält.
- combo_box.AddItem: Fügt jedes Element der ComboBox hinzu und ordnet es einem eindeutigen Index zu.
Erste Auswahl:
- combo_box.SelectByText(initial_text): Definiert das sichtbare Element, wenn die ComboBox erstellt wird.
Verwendete Methoden:
- combo_box.Create: Initialisiert die ComboBox an den angegebenen Koordinaten.
- Add: Fügt die ComboBox zum Panel hinzu.
Erstellen von Objekten im Client-Bereich
In diesem Abschnitt beginnen wir mit dem Entwurf und der Erstellung der Elemente des Kundenbereichs in unserer Nutzeroberfläche. Ziel ist es, einen funktionalen Raum zu schaffen, in dem der Nutzer Konfigurationen in Bezug auf Risiko und Handelsaufträge verwalten und anpassen kann. Im Folgenden beschreibe ich, wie der Bereich aufgeteilt ist und wie die einzelnen Komponenten erstellt werden.
Das Kundenpanel ist in drei Hauptbereiche unterteilt:

Allgemeiner Teil: In diesem Abschnitt kann der Nutzer allgemeine Aspekte wie das Risiko pro Vorgang, die Abweichung und die Stop-Limit bearbeiten.
Losgrößenberechnung: Der zweite Teil des Panels ist der Berechnung der Losgröße auf der Grundlage des Risikos pro Operation und des Stop-Loss-Abstands in Punkten gewidmet. Dabei wird automatisch abgeschätzt, wie viel Risiko der Nutzer bereit ist, einzugehen.
Stop-Loss-Berechnung: Der untere Teil des Panels schließlich ermöglicht die Berechnung des Stop-Loss auf der Grundlage des akzeptablen Verlustprozentsatzes pro Vorgang.
bool CRiskManagementPanel::CreateAreaClientPanel(void) { int x1 = 11; // Initial X coordinate int y1 = 15; // Initial Y coordinate // --- General Section: Risk Per Operation Configuration --- if (!CreateLabel(m_label_risk_per_operation, "L-Risk-Per-operation", "Risk per operation %: ", x1, y1)) return false; // Create the label for risk per operation if (!CreateEdit(m_edit_risk_per_operation, "Risk-Per-operation", x1 + 150, y1)) return false; // Create the editable field for risk per operation y1 += 30; // Move the Y coordinate down for the next section if (!CreateLabel(m_label_deviation, "L-Deviation", "Deviation (Points):", x1, y1)) return false; // Create the label for deviation if (!CreateEdit(m_edit_deviation, "Deviation", x1 + 150, y1, "100")) return false; // Create the editable field for deviation this.deviation = 100; // Default value for deviation y1 += 30; if (!CreateLabel(m_label_stop_limit, "L-StopLimit", "Stop Limit (Points):", x1, y1)) return false; // Create the label for stop limit if (!CreateEdit(m_edit_stop_limit, "Stop Limit", x1 + 150, y1, "50")) return false; // Create the editable field for stop limit this.stop_limit = 50; // Default value for stop limit y1 += 50; // --- Lot Calculation Section --- if (!CreateLabel(m_label_get_lote, "L-Get-Lote-Title", "Get Lote", x1, y1)) return false; // Create the label for lot calculation section if (!CreateComboBox(m_combobox_order_type_get_lot, "ORDER_TYPE_LOT", elements_order_type, "ORDER_TYPE_BUY", x1 + 60, y1)) return false; // Create the combo box to select order type for lot calculation this.order_type_get_lot = ORDER_TYPE_BUY; // Default order type y1 += 30; if (!CreateLabel(m_label_sl, "L-SL", "SL Point: ", x1, y1)) return false; // Create the label for SL point if (!CreateEdit(m_edit_sl, "WRITE-SL", x1 + 60, y1)) return false; // Create the editable field for SL if (!CreateButton(m_button_get_lote, "GET-LOTE", "Save", x1 + 160 + 5, y1)) return false; // Create the button to save the lot calculation y1 += 25; if (!CreateLabel(m_label_result_lote, "L-Result-Lote", "Ideal Lot: ", x1, y1)) return false; // Create the label for displaying the ideal lot if (!CreateLabel(m_label_the_result_lote, "L-The-Result-lot", " ", x1 + 65, y1)) return false; // Create a label to display the calculated ideal lot y1 += 50; // --- Stop Loss Calculation Section --- if (!CreateLabel(m_label_get_sl, "L-Get-SL-Title", "Get SL", x1, y1)) return false; // Create the label for stop loss calculation section if (!CreateComboBox(m_combobox_order_type_get_sl, "ORDER_TYPE_SL", elements_order_type, "ORDER_TYPE_BUY", x1 + 50, y1)) return false; // Create the combo box to select order type for stop loss calculation this.order_type_get_sl = ORDER_TYPE_BUY; // Default order type y1 += 30; if (!CreateLabel(m_label_lote, "L-LOTE", "Get ideal sl:", x1, y1)) return false; // Create the label for getting the ideal stop loss if (!CreateButton(m_button_get_sl, "GET-SL", "Get", x1 + 90, y1)) return false; // Create the button to get the stop loss value y1 += 25; if (!CreateLabel(m_label_result_sl, "L-Result-sl", "Ideal SL:", x1, y1)) return false; // Create the label for displaying the ideal stop loss if (!CreateLabel(m_label_the_result_sl, "L-The-result-sl", " ", x1 + 65, y1)) return false; // Create a label to display the calculated ideal stop loss return true; // If all components are successfully created }
Hauptfunktion zur Erstellung des Panels:
Die Funktion Create ist die zentrale Methode zur Initialisierung des grafischen Panels in der Nutzeroberfläche. Diese Funktion fasst die wichtigsten Elemente zusammen, die zum Einrichten und Konfigurieren des Panels erforderlich sind, und stellt sicher, dass es ordnungsgemäß mit dem Chart verbunden ist und dass seine Koordinaten und Abmessungen korrekt zugewiesen sind.
Eingabe Details
chart (long):
- Stellt die ID des Charts dar, in dem das Panel erstellt werden soll.
- In MQL5 hat das aktuelle Chart standardmäßig eine ID von 0.
name (string):
- Der Name, der das Panel identifiziert.
- Dieser Name ist eindeutig und wird verwendet, um in anderen Vorgängen auf das Panel zu verweisen.
subwin (int):
- Der Index des Chart-Unterfensters, in dem das Panel platziert werden soll.
- Befindet sich das Panel im Hauptfenster, ist dieser Wert 0. Für zusätzliche Teilfenster werden inkrementelle Werte verwendet (1, 2, usw.).
x1 und y1 (int):
- Dies sind die Anfangskoordinaten (obere linke Ecke) des Feldes im Chart.
x2 und y2 (int):
- Diese geben die Abmessungen der Panel an:
- x2: Breite
- y2: Höhe
- Diese Abmessungen werden in Pixel angegeben.
- Diese geben die Abmessungen der Panel an:
//+------------------------------------------------------------------+ //| function to create the interface | //+------------------------------------------------------------------+ bool CRiskManagementPanel::Create(const long chart,const string name,const int subwin,const int x1,const int y1,const int x2,const int y2) { if(!CAppDialog::Create(chart,name,subwin,x1,y1,x2,y2)) return(false); if(!CreateAreaClientPanel()) return(false); //--- succeed return(true); }
Funktionen zur dynamischen Aktualisierung von Comboboxen und anderen Elementen
Wir fahren mit der Erstellung von Funktionen fort und schließen nun die Implementierung der dynamischen Funktionen ab, die die Werte von Schnittstellenelementen wie ComboBoxen und Labels ändern, die die berechnete Losgröße und den Stop Loss (SL) anzeigen. Mit diesen Funktionen können die Kennzeichnungen schnell aktualisiert werden, und die Schnittstelle kann auf Änderungen reagieren, die der Nutzer in den Kombinationsfeldern vornimmt.
1. Funktionen zur Bearbeitung von Kennzeichnungen
Diese Funktionen ermöglichen es, den Text der Ergebnisbeschreibungen in der Nutzeroberfläche zu ändern, insbesondere die Beschriftungen, die die berechnete Losgröße und den Stop Loss (SL) anzeigen, basierend auf den berechneten Werten.
//+------------------------------------------------------------------+ //| Function to edit the text of the lot size label | //+------------------------------------------------------------------+ void CRiskManagementPanel::EditLabelResultLote(string text) { // This function updates the text of the label that shows the ideal lot size. this.m_label_the_result_lote.Text(text); // Set the new text to the label }Diese Funktion aktualisiert die Kennzeichnung, das die berechnete Losgröße anzeigt. Sie erhält einen Textwert als Parameter und aktualisiert das entsprechende Kennzeichen mit diesem Wert.
//+------------------------------------------------------------------+ //| Function to edit the text of the stop loss label | //+------------------------------------------------------------------+ void CRiskManagementPanel::EditLabelResultSL(string text) { // This function updates the text of the label that shows the ideal stop loss value. this.m_label_the_result_sl.Text(text); // Set the new text to the stop loss label }
In ähnlicher Weise aktualisiert diese Funktion das Kennzeichen, das den idealen Stop-Loss-Wert anzeigt. Sie empfängt auch Text als Parameter und aktualisiert die entsprechende Kennzeichnung.
2. Funktionen zur Behandlung von Änderungen in Kombinationsfelder
Diese Funktionen werden immer dann ausgelöst, wenn der Nutzer einen neuen Wert in einem Kombinationsfeld (ComboBox) auswählt, der die Auftragsart bestimmt (z. B. Kauf oder Verkauf). Sie aktualisieren die interne Variable, in der die ausgewählte Auftragsart gespeichert ist.
//+------------------------------------------------------------------+ //| Function to update the variable that stores | //| the order type to obtain the ideal sl | //+------------------------------------------------------------------+ void CRiskManagementPanel::OnChangeComBoxOrderTypeGetSL(void) { // Iterate through the order types array to find the selected type for(int i = 0; i < ArraySize(elements_order_type); i++) { // If the selected order type matches the one in the array if(m_combobox_order_type_get_sl.Select() == elements_order_type[i]) { // Update the order type variable for stop loss this.order_type_get_sl = StringOrderTypeToEnum(m_combobox_order_type_get_sl.Select()); Print("New order type for sl: ", EnumToString(this.order_type_get_sl)); // Log the selected order type break; } } }
Diese Funktion wird ausgelöst, wenn der Nutzer den Auftragstyp in dem Kombinationsfeld ändert, die für die Ermittlung des Stop Loss verwendet wird. Sie durchläuft ein Array von Auftragstypen und identifiziert, welcher gerade in dem Kombinationsfeld ausgewählt ist. Anschließend wird die interne Variable order_type_get_sl mit dem entsprechenden ENUM-Wert aktualisiert, indem die Funktion StringOrderTypeToEnum verwendet wird.
//+------------------------------------------------------------------+ //| Function to update the variable that stores | //| the order type to obtain the ideal lot | //+------------------------------------------------------------------+ void CRiskManagementPanel::OnChangeComBoxOrderTypeGetLote(void) { // Iterate through the order types array to find the selected type for(int i = 0; i < ArraySize(elements_order_type); i++) { // If the selected order type matches the one in the array if(m_combobox_order_type_get_lot.Select() == elements_order_type[i]) { // Update the order type variable for lot size this.order_type_get_lot = StringOrderTypeToEnum(m_combobox_order_type_get_lot.Select()); Print("New order type for lot: ", EnumToString(this.order_type_get_lot)); // Log the selected order type break; } } }
Ähnlich wie die vorhergehende Funktion führt diese Funktion die gleiche Aufgabe aus, jedoch für die Berechnung der Losgröße. Wenn der Nutzer die Auftragsart in dem Kombinationsfeld zur Berechnung des idealen Loses ändert, aktualisiert die Funktion die Variable order_type_get_lot mit der ausgewählten Auftragsart.
3. Zusatzfunktion zur Umwandlung eines Textstring in einen ENUM_ORDER_TYPE
Schließlich wandelt diese Funktion einen Textstring, der einen Auftragstyp darstellt (z. B. „ORDER_TYPE_BUY“), in den entsprechenden Aufzählungswert ENUM_ORDER_TYPE um.
//+------------------------------------------------------------------+ //| Function to convert a string into an order type | //+------------------------------------------------------------------+ ENUM_ORDER_TYPE CRiskManagementPanel::StringOrderTypeToEnum(const string OrderType) { // Convert the string order type to its corresponding enum value if(OrderType == "ORDER_TYPE_BUY") return ORDER_TYPE_BUY; if(OrderType == "ORDER_TYPE_SELL") return ORDER_TYPE_SELL; if(OrderType == "ORDER_TYPE_BUY_LIMIT") return ORDER_TYPE_BUY_LIMIT; if(OrderType == "ORDER_TYPE_SELL_LIMIT") return ORDER_TYPE_SELL_LIMIT; if(OrderType == "ORDER_TYPE_BUY_STOP") return ORDER_TYPE_BUY_STOP; if(OrderType == "ORDER_TYPE_SELL_STOP") return ORDER_TYPE_SELL_STOP; if(OrderType == "ORDER_TYPE_BUY_STOP_LIMIT") return ORDER_TYPE_BUY_STOP_LIMIT; if(OrderType == "ORDER_TYPE_SELL_STOP_LIMIT") return ORDER_TYPE_SELL_STOP_LIMIT; // Return WRONG_VALUE if no match is found return WRONG_VALUE; }
Behandlung von Tastaturereignissen: Implementierung von OnCharEvent
Zum Abschluss dieses Artikels werden wir die Methode OnChartEvent definieren, die zuvor in unserer Klasse deklariert wurde. Diese Methode führt die im vorherigen Abschnitt erstellten Funktionen aus, berechnet die Losgröße und den Stop-Loss, wenn die entsprechenden Schaltflächen gedrückt werden, aktualisiert die Bearbeitungsfelder und vieles mehr.
Aber bevor wir die Funktion vervollständigen, müssen wir verstehen, wie Ereignisse in der MQL5-Steuerungsbibliothek funktionieren.
Es ist wichtig, klarzustellen, dass wir anstelle von Standardereignissen wie CHARTEVENT_OBJECT_CLICK (die in vielen Programmierumgebungen üblich sind) nutzerdefinierte Ereignisse verwenden. Diese Ereignisse werden in einer speziellen Datei mit dem Namen „Defines.mqh“ definiert, die sich im Ordner Includes\Controls\Defines.mqh in unserem Projekt befindet.
Die Datei Defines.mqh enthält Enumerationen und Definitionen, die für die Steuerelemente benötigt werden. Neben den Standardwerten, wie z. B. den Farben der Steuerelemente oder der Bedienfelder, ist der für unseren Anwendungsfall wichtigste Teil die Ereignisdefinition am Ende der Datei. Sie sind wie folgt aufgebaut und werden wie folgt verwendet.
Ereignis-Definitionen
In Defines.mqh werden mehrere nutzerdefinierte Ereignisse zur Verwendung in Steuerelementen definiert.
//+------------------------------------------------------------------+ //| Events | //+------------------------------------------------------------------+ #define ON_CLICK (0) // clicking on control event #define ON_DBL_CLICK (1) // double clicking on control event #define ON_SHOW (2) // showing control event #define ON_HIDE (3) // hiding control event #define ON_CHANGE (4) // changing control event #define ON_START_EDIT (5) // start of editing event #define ON_END_EDIT (6) // end of editing event #define ON_SCROLL_INC (7) // increment of scrollbar event #define ON_SCROLL_DEC (8) // decrement of scrollbar event #define ON_MOUSE_FOCUS_SET (9) // the "mouse cursor entered the control" event #define ON_MOUSE_FOCUS_KILL (10) // the "mouse cursor exited the control" event #define ON_DRAG_START (11) // the "control dragging start" event #define ON_DRAG_PROCESS (12) // the "control is being dragged" event #define ON_DRAG_END (13) // the "control dragging end" event #define ON_BRING_TO_TOP (14) // the "mouse events priority increase" event #define ON_APP_CLOSE (100) // "closing the application" event //+------------------------------------------------------------------+
Ereignisse im Panel verwenden
In unserem speziellen Fall brauchen wir nicht jedes Ereignis, sondern konzentrieren uns auf drei Schlüsselereignisse:
- ON_CLICK: Wird ausgelöst, wenn ein Steuerelement, z. B. eine Schaltfläche, angeklickt wird.
- ON_CHANGE: Wird ausgelöst, wenn sich der Wert eines Steuerelements ändert, z. B. einem Kombinationsfelder.
- ON_END_EDIT: Wird ausgelöst, wenn der Nutzer die Bearbeitung eines Textfelder, z. B. das Textfeld eines Steuerelements, beendet.
Beispiel für die Ereignisimplementierung in OnChartEvent
Die in Defines.mqh definierten Ereignisse entsprechen dem Parameter id in der Funktion OnChartEvent. Diese Funktion behandelt Ereignisse, die im Chart oder Panel auftreten. Die ID wird mit CHARTEVENT_CUSTOM kombiniert, um einen eindeutigen Bezeichner für jedes Ereignis zu erstellen.
Das Ereignis ON_CLICK würde zum Beispiel wie folgt verwendet werden:
if(id == ON_CLICK + CHARTEVENT_CUSTOM && lparam == m_button_get_lote.Id()) { // Acción a ejecutar cuando el botón de obtener lote es presionado // Aquí se llamaría a la función correspondiente para gestionar la acción del botón }
Erstellung der Funktion OnEvent
1. Funktionserklärung
bool CRiskManagementPanel::OnEvent(const int id,const long &lparam,const double &dparam,const string &sparam)
Die Funktion OnEvent ist ein Ereignisbehandler, der mehrere Parameter erhält:
- id: Kennung des aufgetretenen Ereignisses.
- lparam: Zusätzlicher Parameter, der im Allgemeinen spezifische Informationen über ein Steuerelement oder eine Komponente enthält.
- dparam: Ein numerischer Parameter vom Typ „double“, der zur Übergabe numerischer Werte verwendet wird.
- sparam: Ein String-Parameter, der zur Übergabe von Textwerten verwendet wird.
Der Zweck dieser Funktion ist die Verwaltung von Schnittstellenereignissen wie Textfeldänderungen oder Schaltflächenklicks.
2. Erkennung von Änderungen in den Kombinationsfeldern des Auftragstyps
Mit den Kombinationsfeldern können Nutzer einen Wert aus einer Dropdown-Liste auswählen. In diesem Schritt wird geprüft, ob der Nutzer den Wert des Kombinationsfelds geändert hat.
Für das Kombinationsfeld Losgrößen-Typ:
if(id == ON_CHANGE + CHARTEVENT_CUSTOM && lparam == m_combobox_order_type_get_lot.Id()) { OnChangeComBoxOrderTypeGetLote(); }
Für das Kombinationsfeld Stop-Loss (SL):
if(id == ON_CHANGE + CHARTEVENT_CUSTOM && lparam == m_combobox_order_type_get_sl.Id()) { OnChangeComBoxOrderTypeGetSL(); }
- ON_CHANGE + CHARTEVENT_CUSTOM: Das Ereignis, das anzeigt, dass sich ein Kontrollwert geändert hat.
- Die Bedingung lparam == m_combobox_order_type_get_lot.Id() stellt sicher, dass das Ereignis dem richtigen Kombinationsfeld entspricht, indem seine ID überprüft wird.
Innerhalb dieser Bedingungen werden die Funktionen OnChangeComBoxOrderTypeGetLote und OnChangeComBoxOrderTypeGetSL ausgeführt, um die interne Variable ENUM_ORDER_TYPE zu aktualisieren, die den ausgewählten Auftragstyp darstellt.
3. Textfelder (Edits)
Mit Hilfe von Textfelder können die Nutzer Werte manuell eingeben. Hier überprüfen und aktualisieren wir die internen Variablen, die mit jedem bearbeiteten Textfeld verbunden sind.
Für das Feld Risiko pro Vorgang:
if(id == ON_END_EDIT + CHARTEVENT_CUSTOM && lparam == m_edit_risk_per_operation.Id()) { this.risk_per_operation = StringToDouble(m_edit_risk_per_operation.Text()); this.risk_per_operation = NormalizeDouble((this.risk_per_operation / 100.0) * AccountInfoDouble(ACCOUNT_BALANCE), 2); Print("Edit Risk Per Operation: ", this.risk_per_operation); }
Für den Bereich SL:
if(id == ON_END_EDIT + CHARTEVENT_CUSTOM && lparam == m_edit_sl.Id()) { this.sl = StringToInteger(m_edit_sl.Text()); Print("Edit SL: ", this.sl); }
Und für andere Felder wie Abweichung und Stop-Limit:
if(id == ON_END_EDIT + CHARTEVENT_CUSTOM && lparam == m_edit_deviation.Id()) { this.deviation = StringToDouble(m_edit_deviation.Text()); Print("Edit Deviation: ", this.deviation); } if(id == ON_END_EDIT + CHARTEVENT_CUSTOM && lparam == m_edit_stop_limit.Id()) { this.stop_limit = StringToDouble(m_edit_stop_limit.Text()); Print("Edit Stop Limit: ", this.stop_limit); }
- ON_END_EDIT + CHARTEVENT_CUSTOM: Wird ausgelöst, wenn der Nutzer die Bearbeitung eines Feldes beendet.
- Jede if-Anweisung prüft, ob lparam mit der ID des entsprechenden Steuerelements übereinstimmt (z. B. mit einem Bearbeitungssteuerelement). Ist dies der Fall, wird die interne Variable mit dem neuen, vom Nutzer eingegebenen Wert aktualisiert. Die Werte werden in die entsprechenden Typen (double, int, usw.) umgewandelt und bei Bedarf normalisiert.
4. Abrufen der idealen Losgröße und des Stop Loss
Hier wird geprüft, ob der Nutzer auf eine der Schaltflächen geklickt hat, um die ideale Losgröße oder den idealen Stop Loss abzurufen. Je nachdem, welche Taste gedrückt wurde, werden die entsprechenden Berechnungen durchgeführt.
Für die Schaltfläche „Get Lot“:
if(id == ON_CLICK + CHARTEVENT_CUSTOM && lparam == m_button_get_lote.Id()) { Print("Risk Per operation: ", this.risk_per_operation); Print("SL in points: ", this.sl); Print("Order type get lot: ", EnumToString(this.order_type_get_lot)); double new_lot; double new_risk_per_operation; GetIdealLot(new_lot, GetMaxLote(this.order_type_get_lot), this.risk_per_operation, new_risk_per_operation, this.sl); PrintFormat("Loss in case the following operation fails, with the parameters: lot %.2f and stop loss of %i points will be %.2f ", new_lot, this.sl, new_risk_per_operation); EditLabelResultLote(DoubleToString(new_lot, 2)); m_button_get_lote.Pressed(false); }
Für die Schaltfläche „Get SL“:
if(id == ON_CLICK + CHARTEVENT_CUSTOM && lparam == m_button_get_sl.Id()) { Print("Risk Per operation: ", this.risk_per_operation); Print("Order type get sl: ", EnumToString(this.order_type_get_lot)); double new_lot; long new_sl = CalculateSL(this.order_type_get_sl, this.risk_per_operation, new_lot, this.deviation, this.stop_limit); PrintFormat("For the risk per operation %.2f the chosen lot is %.2f and the ideal stop loss in points is %i", this.risk_per_operation, new_lot, new_sl); EditLabelResultSL(IntegerToString(new_sl)); m_button_get_sl.Pressed(false); }
- ON_CLICK + CHARTEVENT_CUSTOM: Überprüft, ob das Klick-Ereignis den spezifischen Schaltflächen entspricht (m_button_get_lote oder m_button_get_sl).
- Die Berechnungen werden mit den Funktionen GetIdealLot und CalculateSL durchgeführt, um die ideale Losgröße und den Stop Loss zu bestimmen.
- Die Ergebnisse werden in den Bedienfeldbeschriftungen (EditLabelResultLote und EditLabelResultSL) angezeigt.
- Schließlich wird die Schaltfläche deaktiviert, indem ihr Zustand auf false gesetzt wird (m_button_get_lote. Pressed(false)).
5. Rückgabe des OnChartEvent
Nach der Bearbeitung der Ereignisse gibt die Funktion das Ereignis an die Basisklasse CAppDialog zurück, sodass die Basisfunktion auch alle anderen aufgetretenen Ereignisse bearbeiten kann:
return(CAppDialog::OnEvent(id, lparam, dparam, sparam));
Dieser Schritt ist wichtig, um sicherzustellen, dass andere wichtige Ereignisse, die nicht ausdrücklich in OnEvent behandelt werden, nicht verloren gehen.
Panel-Initialisierung: Verwendung des OnInit-Ereignisses und allgemeine Einstellungen
In diesem Abschnitt wird das Risikomanagement-Panel mithilfe der nutzerdefinierten Klasse CRiskManagementPanel konfiguriert. Dieses Panel verwaltet die Losgrößen- und Stop-Loss-Berechnungen sowie andere wichtige Händlerfunktionen, die über eine grafische Schnittstelle dargestellt werden. Nachfolgend sehen wir, wie dieses Panel im Programm deklariert, initialisiert und verwaltet wird.
1. Globale Objektdeklaration
Zunächst deklarieren wir das CRiskManagementPanel-Objekt im globalen Abschnitt des Programms. Dies ermöglicht den Zugriff auf das Panel über verschiedene Methoden im Code, wie OnInit, OnDeinit und OnChartEvent.
//+------------------------------------------------------------------+ //| Global Variables | //+------------------------------------------------------------------+ CRiskManagementPanel risk_panel; // Declare the panel object globally
2. Panel-Setup im OnInit-Ereignis
Im OnInit-Ereignis konfigurieren wir das Panel mit der Methode Create. Diese Methode initialisiert das grafische Fenster des Panels und definiert seine Position und Größe. Schlägt die Erstellung des Panels fehl, gibt das Programm INIT_FAILED zurück, um einen Initialisierungsfehler anzuzeigen. Anschließend wird das Panel mit der Run-Methode ausgeführt, wodurch die Schnittstelle für die Nutzerinteraktion aktiviert wird.
//+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit() { //--- Create the risk management panel if(!risk_panel.Create(0, "Test Risk Management", 0, 40, 40, 380, 380)) return(INIT_FAILED); // If panel creation fails, return an error code //--- Run the panel risk_panel.Run(); // Start the graphical interface //--- Return success return(INIT_SUCCEEDED); }
- 0 (chart_id): Gibt an, dass das Panel im Hauptfenster des Charts erstellt werden soll.
- „Test Risk Management“ (name): Legt den Titel fest, der im Fenster des Panels erscheint.
- 0 (subwin): Gibt das Unterfenster an, in dem das Panel erscheinen soll. Da wir das Panel in einem Bot erstellen, verwenden wir 0.
- 40, 40 (Koordinaten): Gibt die Anfangsposition des Feldes in Pixeln an (x=40, y=40).
- 380, 380 (Abmessungen): Definiert die Breite und Höhe des Panels in Pixeln.
3. Löschen des Panels in OnDeinit
Wenn das Programm beendet oder geschlossen wird, müssen wir die vom Panel verwendeten Ressourcen freigeben. Hierfür verwenden wir die Destroy-Methode innerhalb des OnDeinit-Ereignisses. Dadurch wird sichergestellt, dass keine grafischen Elemente zurückbleiben, die zukünftige Programme stören könnten.
//+------------------------------------------------------------------+ //| Expert deinitialization function | //+------------------------------------------------------------------+ void OnDeinit(const int reason) { //--- Destroy the risk management panel risk_panel.Destroy(reason); // Free resources and remove the panel }
4. Behandlung von Ereignissen mit OnChartEvent
Die OnChartEvent-Methode ist der Schlüssel zur Erfassung und Interpretation von Nutzerinteraktionen mit dem Panel, wie z. B. Schaltflächenklicks, Textfeldeingaben oder Auswahländerungen in den Kombinationsfeldern. Diese Ereignisse werden über die ChartEvent-Methode der CRiskManagementPanel-Klasse verarbeitet.
//+------------------------------------------------------------------+ //| Expert chart event function | //+------------------------------------------------------------------+ void OnChartEvent(const int id, // Event ID const long& lparam, // Event parameter of type long const double& dparam, // Event parameter of type double const string& sparam) // Event parameter of type string { //--- Pass the chart event to the risk panel for processing risk_panel.ChartEvent(id, lparam, dparam, sparam); }
Panelprüfung und Validierung
Jetzt lassen wir den Expert Advisor auf einem Symbol laufen; in meinem Fall werde ich XAUUSD (Gold) verwenden. Die Tabelle sieht wie folgt aus:
Aus Gründen der Transparenz und zur Fehlersuche ist mein Kontostand wie folgt:
Im Moment habe ich nachweislich keine offenen Stellen.
Hier erfahren Sie, wie Sie das Panel verwenden können:
1. Geben Sie in das Feld „Risiko pro Vorgang“ einen Prozentsatz ein (bezogen auf den aktuellen Kontostand). In diesem Beispiel wähle ich 3 % meines Kontos.
Hinweis: Sie können auch die Texte für die Abweichung und die Stoppgrenze ändern.
2. Wählen Sie aus, was Sie berechnen möchten. Wir beginnen mit der Funktion zur Ermittlung des idealen Lots für meinen Handel auf der Grundlage von Stop-Loss in Punkten, Risiko pro Operation und Auftragsart. Ich wähle einen Stop Loss von 500 Punkten für einen Kaufauftrag (ORDER_TYPE_BUY).
Klicken Sie auf die Schaltfläche „Speichern“ in der Zeile „SL-Punkt“. Das Ergebnis ist:
Das Ergebnis zeigt eine Losgröße von 0,03 auf dem Panel. Darüber hinaus wird auf der Registerkarte Experten Folgendes angezeigt:
Diese Nachricht enthält Informationen wie die maximale Lot-Größe (0,45), das Risiko pro Operation und den potenziellen Verlust, wenn der Handel unter Verwendung des berechneten Lots und Stop-Loss fehlschlägt ($15,45 USD).
3. Als Nächstes testen wir die Methode zur Berechnung der Losgröße auf der Grundlage des Risikos pro Vorgang. Klicken Sie auf die Schaltfläche „get“ in der gleichen Zeile wie das Label „Get ideal SL“:
Das Ergebnis ist ein Stop-Loss von 1856 Punkten, sowie:
Ein ideale Losgröße von 0,01 für ein Risiko von 3,0 %.
Schlussfolgerung
In diesem Artikel haben wir gelernt, wie man einen „Rechner“ für die Losgrößen und Stop-Loss einsetzt, ein äußerst nützliches Werkzeug für jeden Händler. Wir haben auch untersucht, wie man MQL5-Klassen und -Bibliotheken verwendet, um nutzerdefinierte Panels zu erstellen. Darüber hinaus haben wir uns mit den MQL5-Klassenstrukturen beschäftigt, einschließlich der Vererbung und der Verwendung von Objekten wie Textfelder, Kombinationsfelder und Schaltflächen, die es uns ermöglichen, dynamische und funktionale grafische Oberflächen zu erstellen.
Dieser Ansatz optimiert nicht nur den Arbeitsablauf, sondern eröffnet auch eine breite Palette von Möglichkeiten für die Entwicklung nutzerdefinierter Tools in MQL5. Im nächsten Artikel werden wir mit der Arbeit an der Hauptklasse beginnen und die hier vorgestellten Konzepte vertiefen. Ich hoffe, dass dieser Leitfaden hilfreich war und Sie dazu ermutigt, weiter zu forschen und fortschrittliche grafische Oberflächen zu erstellen, um Ihre Handelserfahrung zu verbessern.
Alle in diesem Artikel erstellten und verwendeten Dateien sind in der Tabelle aufgeführt:| Dateiname | Beschreibung |
|---|---|
| „Risk_Management_Panel.mq5“ | Expert Advisor-Datei, die das in diesem Artikel entwickelte Risikomanagement-Panel enthält. |
| „Risk_Management.mqh“ | Datei zur Definition von Funktionen zur Berechnung der Losgröße, des Stop-Loss und anderer Risikomanagement-Funktionen einbeziehen. |
Übersetzt aus dem Spanischen von MetaQuotes Ltd.
Originalartikel: https://www.mql5.com/es/articles/16985
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.
Die Übertragung der Trading-Signale in einem universalen Expert Advisor.
Neuronale Netze im Handel: Multi-Task-Lernen auf der Grundlage des ResNeXt-Modells
Eine alternative Log-datei mit der Verwendung der HTML und CSS
Risikomanagement (Teil 1): Grundlagen für den Aufbau einer Risikomanagement-Klasse
- 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.