Schnelle Werkzeuge für den manuellen Handel: Grundlegende Funktionsweise
Inhalt
- Einführung
- Das Konzept des Toolkits
- Implementation der grundlegenden Funktionsweise
- Schlussfolgerung
Einführung
Heutzutage wechseln viele Händler zu automatisierten Handelssystemen, die eine zusätzliche Einrichtung erfordern oder vollständig automatisiert und einsatzbereit sein können. Es gibt jedoch einen beträchtlichen Teil der Händler, die es vorziehen, auf die altmodische Art und Weise manuell zu handeln. Sie bevorzugen das menschliche Expertenurteil, wenn sie in jeder spezifischen Handelssituation Entscheidungen treffen. Manchmal entwickeln sich solche Situationen schnell, so dass der Händler schnell reagieren muss. Außerdem erfordern einige Handelsstile (z.B. Scalping) Genauigkeit beim Timing des Markteintritts. Hier können sich zusätzliche Instrumente als nützlich erweisen: Sie können die schnellstmögliche Umsetzung gewünschter Aktionen, wie z.B. Markteintritt oder Marktaustritt, ermöglichen. Daher beschloss ich, grundlegende Funktionen für schnelles, manuelles Handeln zu implementieren.
Das Konzept des Toolkits
Zunächst ist es notwendig, eine Reihe von grundlegenden Maßnahmen festzulegen, die beim manuellen Handel erforderlich sein können. Darauf aufbauend werden wir ein Werkzeug entwickeln, das eine effiziente und schnelle Ausführung entsprechender Aktionen ermöglicht. Der manuelle Handel hat ein zugrundeliegendes Handelssystem, was eine der folgenden Markteintrittsmethoden impliziert: Verwendung von Markt- oder Pending-Aufträgen. Daher ist das Hauptkriterium für das Toolkit die Fähigkeit, mit diesen beiden Auftragsarten zu arbeiten. Wir können auch Aufgaben auswählen, die von einem Händler während des Handelsprozesses ausgeführt werden können — Werkzeuge können dabei helfen, die Zeit und die Anzahl der zur Ausführung dieser Aufgaben erforderlichen Aktionen zu reduzieren.
Abb. 1 Das Hauptfenster des Toolkits
Abbildung 1 zeigt zwei Kategorien: Market-Order und Pending-Order. Ich habe auch drei grundlegende Aufgaben ausgewählt, die manchmal eine schnelle Ausführung erfordern, die aber nicht in einer Aktion ausgeführt werden können. Viele Anwendungen, darunter auch das Terminal MetaTrader 5, verfügen über einen Basissatz von Hotkeys, die eine schnelle Ausführung bestimmter Befehle oder Aktionen ermöglichen. Unsere Anwendung wird dieser Tatsache Rechnung tragen. Ein Hotkey ist in Klammern angegeben: Wenn er gedrückt wird, wird entweder die angegebene Aktion ausgeführt, oder — bei der Arbeit mit Aufträgen — öffnet sich ein entsprechendes Fenster. Es wird auch möglich sein, Aktionen mit der Maus auszuführen. Zum Beispiel wird es möglich sein, alle profitablen Positionen durch Drücken der Taste C oder durch Klicken auf "Close all profitable" (Alle profitablen Positionen schließen) zu schließen.
Durch Drücken der Taste M oder durch Klicken auf "Market Order" (Marktorder) wird das Fenster "Settings: Market Order" (Einstellung Marktorder) geöffnet: Markt-Order". Das Fenster enthält Optionen zur Eingabe von Daten für die Platzierung von Marktkauf- oder Verkaufsaufträgen.
Abb. 2 Fenster zum Konfigurieren und Erstellen von Marktorders.
Zusätzlich zur Grundeinstellung, ähnlich der im Terminal verfügbaren, wird es möglich sein, die Losgröße nicht nur als numerischen Wert, sondern auch in Prozent des Kontostandes auszuwählen. Dies betrifft auch Take-Profit und Stop-Loss: nicht nur als Preis, sondern auch in Punkten. Die Aktionen Kaufen und Verkaufen können auch auf zwei Arten ausgeführt werden: durch Anklicken der entsprechenden Schaltfläche oder durch Drücken eines in Klammern angegebenen Hotkeys.
Drücken Sie P, um das Fenster zur Einrichtung der Pending-Order zu öffnen. Es werden vier Pending Order-Typen unterstützt.
Abb. 3 Fenster zum Konfigurieren und Erstellen von Pending-Orders
Ähnlich wie Marktorders unterstützen Pending-Orders die Losgröße als numerischen Wert oder als Prozentsatz des Saldos sowie die Auswahl von Take-Profit und Stop-Loss als Preis oder in Punkten.
Implementierung der Tools
Lassen Sie uns zunächst eine erste Projektstruktur erstellen. Öffnen Sie das Verzeichnis "Experts" und erstellen Sie den Ordner "Simple Trading" mit einigen wenigen Dateien, wie in Abb. 4 unten dargestellt.
Abb. 4 Struktur der Projektdatei
Lassen Sie uns den Zweck der erstellten Dateien betrachten:
- SimpleTrading.mq5 — die Datei des Expert Advisors, in der die GUI deklariert ist und die die anfänglichen Anwendungseinstellungen enthalten wird.
- Program.mqh — die Include-Datei, die mit dem Expert Advisor verbunden werden soll und die die Klasse CFastTrading, ihre Felder und Methoden enthalten wird. Sie wird auch deren teilweise Implementierung enthalten.
- MainWindow.mqh — die Include-Datei, die mit Program.mqh zu verbinden ist und die die Implementierungsmethoden der GUI-Elemente enthält.
- Defines.mqh — die Include-Datei, die mit Program.mqh zu verbinden ist; sie enthält eine Reihe von Makrosubstitutionen für GUI-Elemente, die zur Implementierung der englischen und russischen Versionen verwendet werden.
Öffnen Sie zuerst Program.mqh, binden Sie die Bibliotheken, die für die Implementierung der Schnittstelle und der Handelsfunktionen erforderlich sind, ein und erstellen Sie die Klasse CFastTrading.
//+------------------------------------------------------------------+ //| Program.mqh | //| Alex2356 | //| https://www.mql5.com/en/users/alex2356/seller | //+------------------------------------------------------------------+ #include <EasyAndFastGUI\WndEvents.mqh> #include <DoEasy25\Engine.mqh> #include "Defines.mqh" //+------------------------------------------------------------------+ //| Enumeration for switching the interface language | //+------------------------------------------------------------------+ enum LANG { RUSSIAN, // Russian ENGLISH // English }; //+------------------------------------------------------------------+ //| Class for creating an application | //+------------------------------------------------------------------+ class CFastTrading : public CWndEvents { public: CFastTrading(void); ~CFastTrading(void); //--- Initialization/deinitialization void OnInitEvent(void); void OnDeinitEvent(const int reason); //--- Timer void OnTimerEvent(void); //--- Chart event handler virtual void OnEvent(const int id,const long &lparam,const double &dparam,const string &sparam); }; //+------------------------------------------------------------------+ //| Adding GUI elements | //+------------------------------------------------------------------+ #include "MainWindow.mqh" //+------------------------------------------------------------------+ //| Constructor | //+------------------------------------------------------------------+ CFastTrading::CFastTrading(void) { } //+------------------------------------------------------------------+ //| Destructor | //+------------------------------------------------------------------+ CFastTrading::~CFastTrading(void) { } //+------------------------------------------------------------------+ //| Initialization | //+------------------------------------------------------------------+ void CFastTrading::OnInitEvent(void) { } //+------------------------------------------------------------------+ //| Deinitialization | //+------------------------------------------------------------------+ void CFastTrading::OnDeinitEvent(const int reason) { //--- Remove the interface CWndEvents::Destroy(); } //+------------------------------------------------------------------+ //| Timer | //+------------------------------------------------------------------+ void CFastTrading::OnTimerEvent(void) { CWndEvents::OnTimerEvent(); //--- } //+------------------------------------------------------------------+ //| Chart event handler | //+------------------------------------------------------------------+ void CFastTrading::OnEvent(const int id,const long &lparam,const double &dparam,const string &sparam) { } //+------------------------------------------------------------------+
Öffnen Sie nun die EA-Datei SimpleTrading.mq5, binden Sie sie ein in Program.mqh und erzeugen Sie eine Instanz der neu erstellten Klasse. Legen Sie auch die Eingabeparameter fest, die folgendes umfassen
- Base FontSize — die Basis-Schriftgröße der Anwendung.
- Caption Color — die Schriftfarbe des Hauptanwendungsfensters.
- Back color — Hintergrundfarbe.
- Interface language — Sprache der Nutzeroberfläche.
- Magic Number — eindeutige Nummer für die von diesem Expertenberater erstellten Aufträge.
//+------------------------------------------------------------------+ //| SimpleTrading.mq5 | //| Alex2356 | //| https://www.mql5.com/en/users/alex2356/seller | //+------------------------------------------------------------------+ #property copyright "Copyright 2020, Alexander Fedosov" #property link "https://www.mql5.com/en/users/alex2356" #property version "1.00" //--- Include application class #include "Program.mqh" //+------------------------------------------------------------------+ //| Expert Advisor input parameters | //+------------------------------------------------------------------+ input int Inp_BaseFont = 10; // Base FontSize input color Caption = C'0,130,225'; // Caption Color input color Background = clrWhite; // Back color input LANG Language = ENGLISH; // Interface language input ulong MagicNumber = 1111; // Magic Number //--- CFastTrading program; ulong tick_counter; //+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit() { //--- tick_counter=GetTickCount(); //--- return(INIT_SUCCEEDED); } //+------------------------------------------------------------------+ //| Expert deinitialization function | //+------------------------------------------------------------------+ void OnDeinit(const int reason) { program.OnDeinitEvent(reason); } //+------------------------------------------------------------------+ //| Expert tick function | //+------------------------------------------------------------------+ void OnTick() { //--- } //+------------------------------------------------------------------+ //| Timer function | //+------------------------------------------------------------------+ void OnTimer(void) { program.OnTimerEvent(); } //+------------------------------------------------------------------+ //| ChartEvent function | //+------------------------------------------------------------------+ void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam) { program.ChartEvent(id,lparam,dparam,sparam); //--- if(id==CHARTEVENT_CUSTOM+ON_END_CREATE_GUI) { Print("End in ",GetTickCount()-tick_counter," ms"); } } //+------------------------------------------------------------------+
Damit die EA-Eingabeparameter für diese Klasse zur Verfügung stehen, ist es notwendig, Variablen zu erstellen, denen EA-Einstellungswerte zugewiesen werden, sowie Methoden, die für die Implementierung verwendet werden. Erstellen Sie die Variablen im 'private' Teil der Klasse.
private: //--- color m_caption_color; color m_background_color; //--- int m_base_font_size; int m_m_edit_index; int m_p_edit_index; //--- ulong m_magic_number; //--- string m_base_font; //--- LANG m_language;
Ersatellen wir folgende Methode im Abschnitt 'public':
//--- Caption color void CaptionColor(const color clr); //--- Background color void BackgroundColor(const color clr); //--- Font size void FontSize(const int font_size); //--- Font name void FontName(const string font_name); //--- Setting the interface language void SetLanguage(const LANG lang); //--- Setting the magic number void SetMagicNumber(ulong magic_number);
Hier ist die Implementierung:
//+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ void CFastTrading::CaptionColor(const color clr) { m_caption_color=clr; } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ void CFastTrading::BackgroundColor(const color clr) { m_background_color=clr; } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ void CFastTrading::FontSize(const int font_size) { m_base_font_size=font_size; } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ void CFastTrading::FontName(const string font_name) { m_base_font=font_name; } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ void CFastTrading::SetLanguage(const LANG lang) { m_language=lang; } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ void CFastTrading::SetMagicNumber(const ulong magic_number) { m_magic_number=magic_number; }
Jetzt verwenden wir das bei der Initialisierung des EAs:
//+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit() { //--- tick_counter=GetTickCount(); //--- Initialize class variables program.FontSize(Inp_BaseFont); program.BackgroundColor(Background); program.CaptionColor(Caption); program.SetLanguage(Language); program.SetMagicNumber(MagicNumber); //--- return(INIT_SUCCEEDED); }
Fügen Sie die Methode CreateGUI() hinzu, mit der die gesamte Schnittstelle erstellt wird. Im Moment ist sie noch leer. Sie wird weiter gefüllt werden, wenn wir UI-Elemente erstellen.
//--- Create the graphical interface of the program bool CreateGUI(void);
Die Methode kann der Initialisierung hinzugefügt werden:
//+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit() { //--- tick_counter=GetTickCount(); //--- Initialize class variables program.FontName("Trebuchet MS"); program.FontSize(Inp_BaseFont); program.BackgroundColor(Background); program.CaptionColor(Caption); program.SetLanguage(Language); program.SetMagicNumber(MagicNumber); //--- Set up the trading panel if(!program.CreateGUI()) { Print(__FUNCTION__," > Failed to create graphical interface!"); return(INIT_FAILED); } //--- return(INIT_SUCCEEDED); }
Erstellen wir nun das Hauptanwendungsfenster. Dies kann durch Hinzufügen der Methode CreateMainWindow() in den 'protected' Bereich unserer Klasse erfolgen.
protected: //--- forms bool CreateMainWindow(void);
Fügen Sie seine Implementierung in die Datei MainWindow.mqh ein und rufen Sie sie dann in CreateGUI() auf. In dieser Methodenimplementierung habe ich die Makro-Substitution CAPTION_NAME verwendet, deshalb sollte sie in der speziellen Datei Defines.mqh erstellt werden.
//+------------------------------------------------------------------+ //| Creates a form for orders | //+------------------------------------------------------------------+ bool CFastTrading::CreateMainWindow(void) { //--- Add a window pointer to the window array CWndContainer::AddWindow(m_main_window); //--- Properties m_main_window.XSize(400); m_main_window.YSize(182); //--- Coordinates int x=5; int y=20; m_main_window.CaptionHeight(22); m_main_window.IsMovable(true); m_main_window.CaptionColor(m_caption_color); m_main_window.CaptionColorLocked(m_caption_color); m_main_window.CaptionColorHover(m_caption_color); m_main_window.BackColor(m_background_color); m_main_window.FontSize(m_base_font_size); m_main_window.Font(m_base_font); //--- Create the form if(!m_main_window.CreateWindow(m_chart_id,m_subwin,CAPTION_NAME,x,y)) return(false); //--- return(true); } //+------------------------------------------------------------------+ //| Creates the graphical interface of the program | //+------------------------------------------------------------------+ bool CFastTrading::CreateGUI(void) { //--- Create the main application window if(!CreateMainWindow()) return(false); //--- Complete GUI creation CWndEvents::CompletedGUI(); return(true); }
Jetzt noch die Makro-Substitutionen erstellen.
//+------------------------------------------------------------------+ //| Macro substitutions | //+------------------------------------------------------------------+ #include "Program.mqh" #define CAPTION_NAME (m_language==RUSSIAN ? "Быстрый трейдинг" : "Fast Trading")
Kompilieren Sie das Projekt, um das Hauptanwendungsfenster zu erhalten. Fügen wir nun Schaltflächen (Abb.1) hinzu, die die beschriebenen Aktionen ausführen. Um sie zu implementieren, erstellen Sie die universelle Methode CreateButton() im geschützten Bereich unserer Klasse.
//--- Buttons bool CreateButton(CWindow &window,CButton &button,string text,color baseclr,int x_gap,int y_gap,int w_number);
Implementieren Sie sie in MainWindow.mqh und rufen Sie im Hauptfenster auf, indem Sie den Methodenkörper erstellen.
//+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ bool CFastTrading::CreateButton(CWindow &window,CButton &button,string text,color baseclr,int x_gap,int y_gap,int w_number) { //--- Store the window pointer button.MainPointer(window); //--- Set properties before creation button.XSize(170); button.YSize(40); button.Font(m_base_font); button.FontSize(m_base_font_size); button.BackColor(baseclr); button.BackColorHover(baseclr); button.BackColorPressed(baseclr); button.BorderColor(baseclr); button.BorderColorHover(baseclr); button.BorderColorPressed(baseclr); button.LabelColor(clrWhite); button.LabelColorPressed(clrWhite); button.LabelColorHover(clrWhite); button.IsCenterText(true); //--- Create a control element if(!button.CreateButton(text,x_gap,window.CaptionHeight()+y_gap)) return(false); //--- Add a pointer to the element to the database CWndContainer::AddToElementsArray(w_number,button); return(true); } //+------------------------------------------------------------------+ //| Creates a form for orders | //+------------------------------------------------------------------+ bool CFastTrading::CreateMainWindow(void) { //--- Add a window pointer to the window array CWndContainer::AddWindow(m_main_window); //--- Properties m_main_window.XSize(400); m_main_window.YSize(182); //--- Coordinates int x=5; int y=20; m_main_window.CaptionHeight(22); m_main_window.IsMovable(true); m_main_window.CaptionColor(m_caption_color); m_main_window.CaptionColorLocked(m_caption_color); m_main_window.CaptionColorHover(m_caption_color); m_main_window.BackColor(m_background_color); m_main_window.FontSize(m_base_font_size); m_main_window.Font(m_base_font); m_main_window.TooltipsButtonIsUsed(true); //--- Create the form if(!m_main_window.CreateWindow(m_chart_id,m_subwin,CAPTION_NAME,x,y)) return(false); //--- if(!CreateButton(m_main_window,m_order_button[0],MARKET_ORDER_NAME+"(M)",C'87,128,255',20,10,0)) return(false); if(!CreateButton(m_main_window,m_order_button[1],PENDING_ORDER_NAME+"(P)",C'31,209,111',210,10,0)) return(false); if(!CreateButton(m_main_window,m_order_button[2],MARKET_ORDERS_PROFIT_CLOSE+"(C)",C'87,128,255',20,60,0)) return(false); if(!CreateButton(m_main_window,m_order_button[3],MARKET_ORDERS_LOSS_CLOSE+"(D)",C'87,128,255',20,110,0)) return(false); if(!CreateButton(m_main_window,m_order_button[4],PEND_ORDERS_ALL_CLOSE+"(R)",C'31,209,111',210,60,0)) return(false); return(true); }
Makro-Substitutionen werden auch hier verwendet, darum fügen Sie sie in Definitionen.mqh hinzu.
//+------------------------------------------------------------------+ //| Macro substitutions | //+------------------------------------------------------------------+ #include "Program.mqh" #define CAPTION_NAME (m_language==RUSSIAN ? "Быстрый трейдинг" : "Fast Trading System") #define MARKET_ORDER_NAME (m_language==RUSSIAN ? "Рыночный ордер" : "Marker Order") #define PENDING_ORDER_NAME (m_language==RUSSIAN ? "Отложенный ордер" : "Pending Order") #define MARKET_ORDERS_PROFIT_CLOSE (m_language==RUSSIAN ? "Закрыть все прибыльные" : "Close all profitable") #define MARKET_ORDERS_LOSS_CLOSE (m_language==RUSSIAN ? "Закрыть все убыточные" : "Close all losing") #define PEND_ORDERS_ALL_CLOSE (m_language==RUSSIAN ? "Закрыть все отложенные" : "Close all pending")
Das haben wir, wenn die englische Version ausgewählt wird.
Abb. 5 Das Hauptanwendungsfenster.
Wie bereits erwähnt, sollte die Anwendung über zwei weitere Fenster für die Arbeit mit Markt- und Pending-Aufträgen verfügen. Erstellen Sie diese daher und verknüpfen Sie sie mit den Schaltflächen Market Order und Pending Order: die entsprechenden Operationen können durch Anklicken der Schaltflächen oder durch Drücken der Hotkeys M und P ausgeführt werden. Fügen Sie daher zwei Methoden, MarketOrdersWindow() und CreatePendingOrdersWindow(), im 'protected' Bereich der Klasse hinzu und implementieren Sie diese in MainWindow.mqh.
bool CreateMarketOrdersWindow(void); bool CreatePendingOrdersWindow(void); //+------------------------------------------------------------------+ //| Market order creation and editing window | //+------------------------------------------------------------------+ bool CFastTrading::CreateMarketOrdersWindow(void) { //--- Add a window pointer to the window array CWndContainer::AddWindow(m_orders_windows[0]); //--- Properties m_orders_windows[0].XSize(450); m_orders_windows[0].YSize(242+58); //--- Coordinates int x=m_order_button[0].XGap(); int y=m_order_button[0].YGap()+60; //--- color clrmain=C'87,128,255'; //--- m_orders_windows[0].CaptionHeight(22); m_orders_windows[0].IsMovable(true); m_orders_windows[0].CaptionColor(clrmain); m_orders_windows[0].CaptionColorLocked(clrmain); m_orders_windows[0].CaptionColorHover(clrmain); m_orders_windows[0].BackColor(m_background_color); m_orders_windows[0].BorderColor(clrmain); m_orders_windows[0].FontSize(m_base_font_size); m_orders_windows[0].Font(m_base_font); m_orders_windows[0].WindowType(W_DIALOG); //--- Create the form if(!m_orders_windows[0].CreateWindow(m_chart_id,m_subwin,CAPTION_M_ORD_NAME,x,y)) return(false); return(true); } //+------------------------------------------------------------------+ //| Pending order creation and editing window | //+------------------------------------------------------------------+ bool CFastTrading::CreatePendingOrdersWindow(void) { //--- Add a window pointer to the window array CWndContainer::AddWindow(m_orders_windows[1]); //--- Properties m_orders_windows[1].XSize(600); m_orders_windows[1].YSize(580); //--- Coordinates int x=m_order_button[0].XGap(); int y=m_order_button[0].YGap()+60; //--- color clrmain=C'31,209,111'; //--- m_orders_windows[1].CaptionHeight(22); m_orders_windows[1].IsMovable(true); m_orders_windows[1].CaptionColor(clrmain); m_orders_windows[1].CaptionColorLocked(clrmain); m_orders_windows[1].CaptionColorHover(clrmain); m_orders_windows[1].BackColor(m_background_color); m_orders_windows[1].BorderColor(clrmain); m_orders_windows[1].FontSize(m_base_font_size); m_orders_windows[1].Font(m_base_font); m_orders_windows[1].WindowType(W_DIALOG); //--- Create the form if(!m_orders_windows[1].CreateWindow(m_chart_id,m_subwin,CAPTION_P_ORD_NAME,x,y)) return(false); return(true); }
Makro-Substitutionen werden in zwei Fenstern verwendet, daher fügen wir sie der entsprechenden datei hinzu.
#define CAPTION_M_ORD_NAME (m_language==RUSSIAN ? "Настройка: Рыночный Ордер" : "Setting: Market Order") #define CAPTION_P_ORD_NAME (m_language==RUSSIAN ? "Настройка: Отложенный Ордер" : "Setting: Pending Order")
Rufen Sie nun die neu erstellten Fenster in der Basismethode CreateGUI() auf, mit der die Anwendungsschnittstelle erstellt wurde:
//+------------------------------------------------------------------+ //| Creates the graphical interface of the program | //+------------------------------------------------------------------+ bool CFastTrading::CreateGUI(void) { //--- Create the main application window if(!CreateMainWindow()) return(false); if(!CreateMarketOrdersWindow()) return(false); if(!CreatePendingOrdersWindow()) return(false); //--- Complete GUI creation CWndEvents::CompletedGUI(); return(true); }
Da es sich bei diesen erstellten Fenstern um Dialoge handelt, werden sie beim Start der Anwendung angezeigt. Wir müssen einen Mechanismus schaffen, um sie anzuzeigen. Dieser wird sich durch einen Klick auf die Schaltfläche Marktorder / Pending-Order oder durch das Drücken eines Hotkeys öffnen. Suchen Sie in der Basisklasse den Methodenrumpf OnEvent() und schreiben Sie die erforderlichen Bedingungen.
//+------------------------------------------------------------------+ //| Chart event handler | //+------------------------------------------------------------------+ void CFastTrading::OnEvent(const int id,const long &lparam,const double &dparam,const string &sparam) { //--- Pressing the button event if(id==CHARTEVENT_CUSTOM+ON_CLICK_BUTTON) { //--- if(lparam==m_order_button[0].Id()) m_orders_windows[0].OpenWindow(); //--- if(lparam==m_order_button[1].Id()) m_orders_windows[1].OpenWindow(); } //--- Key press event if(id==CHARTEVENT_KEYDOWN) { //--- Opening a market order window if(lparam==KEY_M) { if(m_orders_windows[0].IsVisible()) { m_orders_windows[0].CloseDialogBox(); } else { if(m_orders_windows[1].IsVisible()) { m_orders_windows[1].CloseDialogBox(); } //--- m_orders_windows[0].OpenWindow(); } } //--- Opening a pending order window if(lparam==KEY_P) { if(m_orders_windows[1].IsVisible()) { m_orders_windows[1].CloseDialogBox(); } else { if(m_orders_windows[0].IsVisible()) { m_orders_windows[0].CloseDialogBox(); } //--- m_orders_windows[1].OpenWindow(); } } } }
Kompilieren Sie nun das Projekt und versuchen Sie, die Dialogfelder mit Hotkeys zu öffnen. Das Ergebnis sollte wie in Abbildung 6 dargestellt sein:
Abb. 6 Öffnen und Wechseln von Fenstern über Hotkeys.
Wie Sie sehen, ist es nicht notwendig, ein Dialogfenster zu schließen, bevor ein anderes geöffnet wird. Es wird einfach zu einem anderen Fenster gewechselt — dies ermöglicht Zeitersparnis und den Wechsel zwischen Markt- und Pending-Aufträgen mit nur einem Klick. Eine weitere praktische Kleinigkeit ist die Möglichkeit, Dialoge mit der Taste Esc zu schließen. Als ich testete, wie sich die Fenster öffnen, wollte ich diese Taste ein paar Mal benutzen, um die Fenster zu schließen. Also, fügen wir den Code zum Abschnitt Ereignis hinzu:
//--- Exiting the order placing window if(lparam==KEY_ESC) { if(m_orders_windows[0].IsVisible()) { m_orders_windows[0].CloseDialogBox(); } else if(m_orders_windows[1].IsVisible()) { m_orders_windows[1].CloseDialogBox(); } }
Nun lassen Sie uns mit den erstellten Fenstern weiterarbeiten, beginnend mit dem Fenster Market-Orders. Gemäß Abbildung 2 müssen wir zwei Blöcke für die Auftragsverwaltung erstellen — Kauf und Verkauf. Zuerst erstellen wir zwei Schnittstellenelemente - Rahmen (frame) - mit Hilfe der neuen Methode CreateFrame().
bool CreateFrame(CWindow &window,CFrame &frame,const int x_gap,const int y_gap,string caption,int w_number);
Ihre Implementierung ist wie folgt:
//+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ bool CFastTrading::CreateFrame(CWindow &window,CFrame &frame,const int x_gap,const int y_gap,string caption,int w_number) { //--- Store the pointer to the main control frame.MainPointer(window); //--- color clrmain=clrNONE; if(caption==BUY_ORDER) clrmain=C'88,212,210'; else if(caption==SELL_ORDER) clrmain=C'236,85,79'; //--- frame.YSize(110); frame.LabelColor(clrmain); frame.BorderColor(clrmain); frame.BackColor(m_background_color); frame.GetTextLabelPointer().BackColor(m_background_color); frame.Font(m_base_font); frame.FontSize(m_base_font_size); frame.AutoXResizeMode(true); frame.AutoXResizeRightOffset(10); //--- Create a control element if(!frame.CreateFrame(caption,x_gap,window.CaptionHeight()+y_gap)) return(false); //--- Add the object to the common array of object groups CWndContainer::AddToElementsArray(w_number,frame); return(true); }
In der Implementierung zur Rahmenerstellung haben wir zwei neue Makro-Substitutionen für den Rahmenkopf, fügen Sie diese also zu Defines.mqh hinzu:
#define BUY_ORDER (m_language==RUSSIAN ? "Buy-ордер" : "Buy-order") #define SELL_ORDER (m_language==RUSSIAN ? "Sell-ордер" : "Sell-order")
Um diese Methode anzuwenden, gehen Sie zum Hauptteil der Methode CreateMarketOrdersWindow(), die Marktorders erstellt, und fügen Sie Folgendes hinzu:
... //--- Create the form if(!m_orders_windows[0].CreateWindow(m_chart_id,m_subwin,CAPTION_M_ORD_NAME,x,y)) return(false); //--- BUY BLOCK if(!CreateFrame(m_orders_windows[0],m_frame[0],10,20,BUY_ORDER,1)) return(false); //--- SELL BLOCK if(!CreateFrame(m_orders_windows[0],m_frame[1],10,160,SELL_ORDER,1)) return(false); return(true); }
Ergebnisprüfung:
Abb. 7 Die Blocks der Marktorders.
Jeder der Blöcke in Abbildung 7 wird 4 Hauptkategorien von UI-Elementen enthalten:
- Text-Überschriften. Diese sind: Lot, Take Profit, Stop Loss.
- Schaltflächen zum Umschalten. Für die Losgröße ist dies Lot/% der Einlage; bei Take-Profit und Stop-Loss — Preis- oder Punktmodus.
- Eingabefelder. Lot, Take-Profit und Stop-Loss.
- Aktion der Schaltfläche. Verkaufen oder Kaufen.
Fahren wir mit der schrittweisen Implementierung jeder Kategorie fort. Erstellen Sie die universelle Methode CreateLabel() für Textüberschriften.
//+------------------------------------------------------------------+ //| Creates the text label | //+------------------------------------------------------------------+ bool CFastTrading::CreateLabel(CWindow &window,CTextLabel &text_label,const int x_gap,const int y_gap,string label_text,int w_number) { //--- Store the window pointer text_label.MainPointer(window); //--- text_label.Font(m_base_font); text_label.FontSize(m_base_font_size); text_label.XSize(80); text_label.BackColor(m_background_color); text_label.IsCenterText(true); //--- Creating a label if(!text_label.CreateTextLabel(label_text,x_gap,window.CaptionHeight()+y_gap)) return(false); //--- Add a pointer to the element to the database CWndContainer::AddToElementsArray(w_number,text_label); return(true); }
Wir haben bereits früher die Methode zur Erstellung von Marktaufträgen ergänzt. Nun fügen Sie unter Berücksichtigung der aktuellen Implementierung Folgendes hinzu.
//--- BUY BLOCK if(!CreateFrame(m_orders_windows[0],m_frame[0],10,20,BUY_ORDER,1)) return(false); //--- Headers if(!CreateLabel(m_orders_windows[0],m_m_text_labels[0],20,30,LOT,1)) return(false); if(!CreateLabel(m_orders_windows[0],m_m_text_labels[1],20+80+20,30,TP,1)) return(false); if(!CreateLabel(m_orders_windows[0],m_m_text_labels[2],20+(80+20)*2,30,SL,1)) return(false); //--- SELL BLOCK if(!CreateFrame(m_orders_windows[0],m_frame[1],10,160,SELL_ORDER,1)) return(false); //--- Headers if(!CreateLabel(m_orders_windows[0],m_m_text_labels[3],20,170,LOT,1)) return(false); if(!CreateLabel(m_orders_windows[0],m_m_text_labels[4],20+80+20,170,TP,1)) return(false); if(!CreateLabel(m_orders_windows[0],m_m_text_labels[5],20+(80+20)*2,170,SL,1)) return(false); return(true); }
Hier sind drei neue Makro-Substitutionen für Lot, Take-Profit und Stop-Loss. Fügen Sie Namen in zwei Sprachen hinzu:
#define LOT (m_language==RUSSIAN ? "Лот" : "Lot") #define TP (m_language==RUSSIAN ? "Тейк профит" : "Take Profit") #define SL (m_language==RUSSIAN ? "Стоп лосс" : "Stop Loss")
Die nächste Kategorie umfasst die Umschalttasten. Für sie wird die speziell erstellte Methode CreateSwitchButton() verwendet.
//+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ bool CFastTrading::CreateSwitchButton(CWindow &window,CButton &button,string text,int x_gap,int y_gap,int w_number) { //--- Store the window pointer button.MainPointer(window); color baseclr=clrSlateBlue; color pressclr=clrIndigo; //--- Set properties before creation button.XSize(80); button.YSize(24); button.Font(m_base_font); button.FontSize(m_base_font_size); button.BackColor(baseclr); button.BackColorHover(baseclr); button.BackColorPressed(pressclr); button.BorderColor(baseclr); button.BorderColorHover(baseclr); button.BorderColorPressed(pressclr); button.LabelColor(clrWhite); button.LabelColorPressed(clrWhite); button.LabelColorHover(clrWhite); button.IsCenterText(true); button.TwoState(true); //--- Create a control element if(!button.CreateButton(text,x_gap,window.CaptionHeight()+y_gap)) return(false); //--- Add a pointer to the element to the database CWndContainer::AddToElementsArray(w_number,button); return(true); }
Wenden Sie es für beide Blöcke in der Methode CreateMarketWindow() an:
... //--- BUY BLOCK if(!CreateFrame(m_orders_windows[0],m_frame[0],10,20,BUY_ORDER,1)) return(false); //--- Headers if(!CreateLabel(m_orders_windows[0],m_m_text_labels[0],20,30,LOT,1)) return(false); if(!CreateLabel(m_orders_windows[0],m_m_text_labels[1],20+80+20,30,TP,1)) return(false); if(!CreateLabel(m_orders_windows[0],m_m_text_labels[2],20+(80+20)*2,30,SL,1)) return(false); //--- Toggle buttons for(int i=0; i<3; i++) if(!CreateSwitchButton(m_orders_windows[0],m_switch_button[i],"-",20+(80+20)*i,60,1)) return(false); //--- SELL BLOCK if(!CreateFrame(m_orders_windows[0],m_frame[1],10,160,SELL_ORDER,1)) return(false); //--- Headers if(!CreateLabel(m_orders_windows[0],m_m_text_labels[3],20,170,LOT,1)) return(false); if(!CreateLabel(m_orders_windows[0],m_m_text_labels[4],20+80+20,170,TP,1)) return(false); if(!CreateLabel(m_orders_windows[0],m_m_text_labels[5],20+(80+20)*2,170,SL,1)) return(false); //--- Toggle buttons for(int i=3; i<6; i++) if(!CreateSwitchButton(m_orders_windows[0],m_switch_button[i],"-",20+(80+20)*(i-3),170+30,1)) return(false); return(true); }
Besondere Aufmerksamkeit sollte der nächsten Kategorie, die sich auf Eingabefelder bezieht, geschenkt werden, da die Elemente bestimmte Nutzungseinschränkungen haben müssen. Beispielsweise müssen die Werte für das Eingabefeld Lot durch die aktuelle Symbolspezifikation sowie durch die Besonderheiten eines Handelskontos begrenzt werden. Bei der Bearbeitung dieses Feldes sollten die folgenden Parameter berücksichtigt werden: minimale und maximale Losgröße, minimaler Änderungsschritt. Erstellen Sie auf dieser Grundlage CreateLotEdit() und setzen Sie sein Eingabefeld entsprechend :
//+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ bool CFastTrading::CreateLotEdit(CWindow &window,CTextEdit &text_edit,const int x_gap,const int y_gap,int w_number) { //--- Store the pointer to the main control text_edit.MainPointer(window); //--- Properties text_edit.XSize(80); text_edit.YSize(24); text_edit.Font(m_base_font); text_edit.FontSize(m_base_font_size); text_edit.MaxValue(SymbolInfoDouble(Symbol(),SYMBOL_VOLUME_MAX)); text_edit.StepValue(SymbolInfoDouble(Symbol(),SYMBOL_VOLUME_STEP)); text_edit.MinValue(SymbolInfoDouble(Symbol(),SYMBOL_VOLUME_MIN)); text_edit.SpinEditMode(true); text_edit.SetDigits(2); text_edit.GetTextBoxPointer().XGap(1); text_edit.GetTextBoxPointer().XSize(80); //--- Create a control element if(!text_edit.CreateTextEdit("",x_gap,window.CaptionHeight()+y_gap)) return(false); text_edit.SetValue(string(SymbolInfoDouble(Symbol(),SYMBOL_VOLUME_MIN))); //--- Add the object to the common array of object groups CWndContainer::AddToElementsArray(w_number,text_edit); return(true); }
Erstellen Sie außerdem Eingabefelder für Stop-Loss und Take-Profit. Alle oben genannten Felder sollten dem Fenster zur Erstellung von Marktaufträgen hinzugefügt werden.
//+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ bool CFastTrading::CreateTakeProfitEdit(CWindow &window,CTextEdit &text_edit,const int x_gap,const int y_gap,int w_number) { //--- Store the pointer to the main control text_edit.MainPointer(window); //--- Properties text_edit.XSize(80); text_edit.YSize(24); text_edit.Font(m_base_font); text_edit.FontSize(m_base_font_size); text_edit.MaxValue(9999); text_edit.StepValue(1); text_edit.MinValue(0); text_edit.SpinEditMode(true); text_edit.GetTextBoxPointer().XGap(1); text_edit.GetTextBoxPointer().XSize(80); //--- Create a control element if(!text_edit.CreateTextEdit("",x_gap,window.CaptionHeight()+y_gap)) return(false); text_edit.SetValue(string(150)); //--- Add the object to the common array of object groups CWndContainer::AddToElementsArray(w_number,text_edit); return(true); } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ bool CFastTrading::CreateStopLossEdit(CWindow &window,CTextEdit &text_edit,const int x_gap,const int y_gap,int w_number) { //--- Store the pointer to the main control text_edit.MainPointer(window); //--- Properties text_edit.XSize(80); text_edit.YSize(24); text_edit.Font(m_base_font); text_edit.FontSize(m_base_font_size); text_edit.MaxValue(9999); text_edit.StepValue(1); text_edit.MinValue(0); text_edit.SpinEditMode(true); text_edit.GetTextBoxPointer().XGap(1); text_edit.GetTextBoxPointer().XSize(80); //--- Create a control element if(!text_edit.CreateTextEdit("",x_gap,window.CaptionHeight()+y_gap)) return(false); text_edit.SetValue(string(150)); //--- Add the object to the common array of object groups CWndContainer::AddToElementsArray(w_number,text_edit); return(true); }
Und die letzte Kategorie von Oberflächenelementen in diesem Fenster enthält zwei Schaltflächen, Kaufen und Verkaufen. Die Methoden zum Hinzufügen von Schaltflächen sind CreateBuyButton() und CreateSellButton().
//+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ bool CFastTrading::CreateBuyButton(CWindow &window,CButton &button,string text,int x_gap,int y_gap,int w_number) { //--- Store the window pointer button.MainPointer(window); color baseclr=C'88,212,210'; //--- Set properties before creation button.XSize(120); button.YSize(40); button.Font(m_base_font); button.FontSize(m_base_font_size); button.BackColor(baseclr); button.BackColorHover(baseclr); button.BackColorPressed(baseclr); button.BorderColor(baseclr); button.BorderColorHover(baseclr); button.BorderColorPressed(baseclr); button.LabelColor(clrWhite); button.LabelColorPressed(clrWhite); button.LabelColorHover(clrWhite); button.IsCenterText(true); //--- Create a control element if(!button.CreateButton(text,x_gap,y_gap)) return(false); //--- Add a pointer to the element to the database CWndContainer::AddToElementsArray(w_number,button); return(true); } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ bool CFastTrading::CreateSellButton(CWindow &window,CButton &button,string text,int x_gap,int y_gap,int w_number) { //--- Store the window pointer button.MainPointer(window); color baseclr=C'236,85,79'; //--- Set properties before creation button.XSize(120); button.YSize(40); button.Font(m_base_font); button.FontSize(m_base_font_size); button.BackColor(baseclr); button.BackColorHover(baseclr); button.BackColorPressed(baseclr); button.BorderColor(baseclr); button.BorderColorHover(baseclr); button.BorderColorPressed(baseclr); button.LabelColor(clrWhite); button.LabelColorPressed(clrWhite); button.LabelColorHover(clrWhite); button.IsCenterText(true); //--- Create a control element if(!button.CreateButton(text,x_gap,y_gap)) return(false); //--- Add a pointer to the element to the database CWndContainer::AddToElementsArray(w_number,button); return(true); }
Durch Hinzufügen von zwei Schaltflächen schließen wir die Implementierung der Methode CreateMarketWindow() ab:
//+------------------------------------------------------------------+ //| Market order creation and editing window | //+------------------------------------------------------------------+ bool CFastTrading::CreateMarketOrdersWindow(void) { //--- Add a window pointer to the window array CWndContainer::AddWindow(m_orders_windows[0]); //--- Properties m_orders_windows[0].XSize(450); m_orders_windows[0].YSize(242+58); //--- Coordinates int x=m_order_button[0].XGap(); int y=m_order_button[0].YGap()+60; //--- color clrmain=C'87,128,255'; //--- m_orders_windows[0].CaptionHeight(22); m_orders_windows[0].IsMovable(true); m_orders_windows[0].CaptionColor(clrmain); m_orders_windows[0].CaptionColorLocked(clrmain); m_orders_windows[0].CaptionColorHover(clrmain); m_orders_windows[0].BackColor(m_background_color); m_orders_windows[0].BorderColor(clrmain); m_orders_windows[0].FontSize(m_base_font_size); m_orders_windows[0].Font(m_base_font); m_orders_windows[0].WindowType(W_DIALOG); //--- Create the form if(!m_orders_windows[0].CreateWindow(m_chart_id,m_subwin,CAPTION_M_ORD_NAME,x,y)) return(false); //--- BUY BLOCK if(!CreateFrame(m_orders_windows[0],m_frame[0],10,20,BUY_ORDER,1)) return(false); //--- Headers if(!CreateLabel(m_orders_windows[0],m_m_text_labels[0],20,30,LOT,1)) return(false); if(!CreateLabel(m_orders_windows[0],m_m_text_labels[1],20+80+20,30,TP,1)) return(false); if(!CreateLabel(m_orders_windows[0],m_m_text_labels[2],20+(80+20)*2,30,SL,1)) return(false); //--- Toggle buttons for(int i=0; i<3; i++) if(!CreateSwitchButton(m_orders_windows[0],m_switch_button[i],"-",20+(80+20)*i,60,1)) return(false); //--- Edits if(!CreateLotEdit(m_orders_windows[0],m_lot_edit[0],20,60+34,1)) return(false); if(!CreateTakeProfitEdit(m_orders_windows[0],m_tp_edit[0],20+(80+20),60+34,1)) return(false); if(!CreateStopLossEdit(m_orders_windows[0],m_sl_edit[0],20+(80+20)*2,60+34,1)) return(false); //--- The Buy button if(!CreateBuyButton(m_orders_windows[0],m_buy_execute,BUY+"(B)",m_orders_windows[0].XSize()-(120+20),103,1)) return(false); //--- SELL BLOCK if(!CreateFrame(m_orders_windows[0],m_frame[1],10,160,SELL_ORDER,1)) return(false); //--- Headers if(!CreateLabel(m_orders_windows[0],m_m_text_labels[3],20,170,LOT,1)) return(false); if(!CreateLabel(m_orders_windows[0],m_m_text_labels[4],20+80+20,170,TP,1)) return(false); if(!CreateLabel(m_orders_windows[0],m_m_text_labels[5],20+(80+20)*2,170,SL,1)) return(false); //--- Toggle buttons for(int i=3; i<6; i++) if(!CreateSwitchButton(m_orders_windows[0],m_switch_button[i],"-",20+(80+20)*(i-3),170+30,1)) return(false); //--- Edits if(!CreateLotEdit(m_orders_windows[0],m_lot_edit[1],20,170+30+35,1)) return(false); if(!CreateTakeProfitEdit(m_orders_windows[0],m_tp_edit[1],20+80+20,170+30+35,1)) return(false); if(!CreateStopLossEdit(m_orders_windows[0],m_sl_edit[1],20+(80+20)*2,170+30+35,1)) return(false); //--- The Sell button if(!CreateSellButton(m_orders_windows[0],m_sell_execute,SELL+"(S)",m_orders_windows[0].XSize()-(120+20),242,1)) return(false); return(true); }
Und vergessen Sie nicht, die beiden neuen Makro-Substitutionen einzufügen:
#define BUY (m_language==RUSSIAN ? "Купить" : "Buy") #define SELL (m_language==RUSSIAN ? "Продать" : "Sell")
Kompilieren Sie das Projekt in dieser Phase, und überprüfen Sie das Ergebnis:
Abb. 8 Die befüllte Oberfläche des Fensters zur Erstellung von Marktorders.
Es ist nur eine Vorlage für den Moment. Für die Weiterentwicklung der Vorlage sollten folgende Aufgaben umgesetzt werden:
- Beleben der Schaltflächen zum Umschalten.
- Änderung der Eigenschaften der Eingabefelder in Abhängigkeit von den Schaltflächenzuständen.
- Umsetzung der Platzierung von Marktaufträgen entsprechend dem Umschaltmodus und den Eingabedatenwerten.
Bevor mit der Implementierung des Umschaltmechanismus für die Schaltflächen begonnen wird, der den Namen ändern wird, ist es notwendig, ihre Standardwerte einzustellen. Abbildung 8 zeigt, dass die Schaltflächen jetzt nur noch Striche haben. Um den Namen einzustellen, fügen Sie die Methode SetButtonParam() hinzu. Dieselbe Methode wird später zum Ändern der Namen verwendet.
//+------------------------------------------------------------------+ //| Setting the button text | //+------------------------------------------------------------------+ void CFastTrading::SetButtonParam(CButton &button,string text) { button.LabelText(text); button.Update(true); }
Gehen Sie zum Event-Handler und fügen Sie nach Abschluss der Schnittstellenerstellung den Abschnitt Event hinzu. Setzen Sie dort die Namen für die Schaltflächen mit der Methode SetButtonParam().
//--- UI creation completion if(id==CHARTEVENT_CUSTOM+ON_END_CREATE_GUI) { //--- SetButtonParam(m_switch_button[0],LOT); SetButtonParam(m_switch_button[1],POINTS); SetButtonParam(m_switch_button[2],POINTS); SetButtonParam(m_switch_button[3],LOT); SetButtonParam(m_switch_button[4],POINTS); SetButtonParam(m_switch_button[5],POINTS); }
Hier haben wir eine weitere Makro-Substitution für die Knopfnamen, fügen Sie sie zu Defines.mqh hinzu:
#define POINTS (m_language==RUSSIAN ? "Пункты" : "Points")
Die Vorbereitungen zur Erstellung des Schaltmechanismus sind abgeschlossen. Die Funktion ButtonSwitch() verfolgt den Zustand der Taste (gedrückt/ungedrückt) und ändert den Tastennamen entsprechend.
//+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ void CFastTrading::ButtonSwitch(CButton &button,long lparam,string state1,string state2) { if(lparam==button.Id()) { if(!button.IsPressed()) SetButtonParam(button,state1); else SetButtonParam(button,state2); } }
Da der Name durch das Ereignis eines Knopfdrucks umgeschaltet wird, rufen Sie die erzeugte Methode im Ereignisbehandlungsabschnitt auf.
//--- Pressing the button event if(id==CHARTEVENT_CUSTOM+ON_CLICK_BUTTON) { //--- if(lparam==m_order_button[0].Id()) m_orders_windows[0].OpenWindow(); //--- if(lparam==m_order_button[1].Id()) m_orders_windows[1].OpenWindow(); //--- ButtonSwitch(m_switch_button[0],lparam,LOT,PERC_DEPO); ButtonSwitch(m_switch_button[1],lparam,POINTS,PRICE); ButtonSwitch(m_switch_button[2],lparam,POINTS,PRICE); ButtonSwitch(m_switch_button[3],lparam,LOT,PERC_DEPO); ButtonSwitch(m_switch_button[4],lparam,POINTS,PRICE); ButtonSwitch(m_switch_button[5],lparam,POINTS,PRICE); }
Beschreiben Sie auch neue Makrosubstitutionen in zwei Sprachen:
#define PERC_DEPO (m_language==RUSSIAN ? "% Депозит" : "% Deposit") #define PRICE (m_language==RUSSIAN ? "Цена" : "Price")
Nach der Neukompilierung: Die Namen der Schaltflächen ändern sich jetzt nach einem Knopfdruck:
Abb. 9 Namensänderung nach einem Knopfdruck.
Machen wir weiter. Die nächste Aufgabe besteht darin, die Eigenschaften der Eingabefelder in Abhängigkeit vom Zustand der entsprechenden Umschalttaste zu ändern. Dazu fügen Sie für jedes Eingabefeld drei neue Methoden hinzu: LotMarketSwitch(), TakeMarketSwitch(), StopMarketSwitch(). Für die erste Methode wird zwischen Lot-Wert und Prozent des Kontosaldos umgeschaltet.
//+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ void CFastTrading::LotMarketSwitch(long lparam) { for(int i=0; i<2; i++) { if(lparam==m_switch_button[i*3].Id()) { if(m_switch_button[i*3].IsPressed()) { m_lot_edit[i].SetDigits(0); m_lot_edit[i].StepValue(1); m_lot_edit[i].MaxValue(100); m_lot_edit[i].MinValue(1); m_lot_edit[i].SetValue(string(2)); m_lot_edit[i].GetTextBoxPointer().Update(true); } else { m_lot_edit[i].SetDigits(2); m_lot_edit[i].StepValue(SymbolInfoDouble(Symbol(),SYMBOL_VOLUME_STEP)); m_lot_edit[i].MinValue(SymbolInfoDouble(Symbol(),SYMBOL_VOLUME_MIN)); m_lot_edit[i].MaxValue(SymbolInfoDouble(Symbol(),SYMBOL_VOLUME_MAX)); m_lot_edit[i].SetValue(string(SymbolInfoDouble(Symbol(),SYMBOL_VOLUME_MIN))); m_lot_edit[i].GetTextBoxPointer().Update(true); } } } }
Bei den beiden anderen Methoden werden die Werte von Punkte auf Preis umgestellt.
//+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ void CFastTrading::TakeMarketSwitch(long lparam) { for(int i=0; i<2; i++) { if(lparam==m_switch_button[3*i+1].Id()) { if(m_switch_button[3*i+1].IsPressed()) { MqlTick tick; if(SymbolInfoTick(Symbol(),tick)) { m_tp_edit[i].SetDigits(_Digits); m_tp_edit[i].StepValue(_Point); m_tp_edit[i].SetValue(string(tick.ask)); m_tp_edit[i].GetTextBoxPointer().Update(true); } } else { m_tp_edit[i].SetDigits(0); m_tp_edit[i].StepValue(1); m_tp_edit[i].SetValue(string(150)); m_tp_edit[i].GetTextBoxPointer().Update(true); } } } } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ void CFastTrading::StopMarketSwitch(long lparam) { for(int i=0; i<2; i++) { if(lparam==m_switch_button[3*i+2].Id()) { if(m_switch_button[3*i+2].IsPressed()) { MqlTick tick; if(SymbolInfoTick(Symbol(),tick)) { m_sl_edit[i].SetDigits(_Digits); m_sl_edit[i].StepValue(_Point); m_sl_edit[i].SetValue(string(tick.bid)); m_sl_edit[i].GetTextBoxPointer().Update(true); } } else { m_sl_edit[i].SetDigits(0); m_sl_edit[i].StepValue(1); m_sl_edit[i].SetValue(string(150)); m_sl_edit[i].GetTextBoxPointer().Update(true); } } } }
Rufen wir nun alle drei Methoden im Event-Handler im Abschnitt "Button Click Event" auf:
// --- Switch Lot/Percent of balance LotMarketSwitch(lparam); //--- Switch Take Profit Points/Price TakeMarketSwitch(lparam); //--- Switch Stop Loss Points/Price StopMarketSwitch(lparam);
Ergebnisprüfung:
Abb. 10 Umschalten der Eingabefelder im Fenster Marktorders.
Die letzte Aufgabe besteht darin, die Platzierung von Bestens-Aufträgen entsprechend den Einstellungen und Eingabefeldwerten mittels einer Schaltfläche oder eines Hotkeys zu ermöglichen. Zu diesem Zweck erstellen Sie drei neue Methoden:
- OnInitTrading() — definiert und setzt die Umgebung des Handelskontos.
- SetBuyOrder() — öffnet eine Markt-Kauforder.
- SetSellOrder() — öffnet eine Markt-Verkaufsorder.
Erstellen Sie sie im 'rivate' Bereich unserer Basisklasse CFastTrading, und implementieren Sie sie in derselben Klasse.
//+------------------------------------------------------------------+ //| Trading Environment Initialization | //+------------------------------------------------------------------+ void CFastTrading::OnInitTrading() { string array_used_symbols[]; //--- Fill in the array of used symbols CreateUsedSymbolsArray(SYMBOLS_MODE_CURRENT,"",array_used_symbols); //--- Set the type of the used symbol list in the symbol collection and fill in the list of symbol timeseries m_trade.SetUsedSymbols(array_used_symbols); //--- Pass all existing collections to the trading class m_trade.TradingOnInit(); m_trade.TradingSetMagic(m_magic_number); m_trade.TradingSetLogLevel(LOG_LEVEL_ERROR_MSG); //--- Set synchronous passing of orders for all used symbols m_trade.TradingSetAsyncMode(false); //--- Set correct order expiration and filling types to all trading objects m_trade.TradingSetCorrectTypeExpiration(); m_trade.TradingSetCorrectTypeFilling(); }
Rufen Sie die Klasse im Konstruktor auf:
//+------------------------------------------------------------------+ //| Constructor | //+------------------------------------------------------------------+ CFastTrading::CFastTrading(void) { OnInitTrading(); }
Bevor Sie Handelsfunktionen implementieren, beachten Sie, dass ihre Ausführung bei zwei verschiedenen Ereignissen durchgeführt wird.
//+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ bool CFastTrading::SetBuyOrder(int id,long lparam) { if((id==CHARTEVENT_CUSTOM+ON_CLICK_BUTTON && lparam==m_buy_execute.Id()) || (id==CHARTEVENT_KEYDOWN && lparam==KEY_B)) { //--- double lot; if(m_switch_button[0].IsPressed()) lot=LotPercent(Symbol(),ORDER_TYPE_BUY,SymbolInfoDouble(Symbol(),SYMBOL_ASK),StringToDouble(m_lot_edit[0].GetValue())); else lot=NormalizeLot(Symbol(),StringToDouble(m_lot_edit[0].GetValue())); if(m_switch_button[1].IsPressed() && m_switch_button[2].IsPressed()) { double tp=double(m_tp_edit[0].GetValue()); double sl=double(m_sl_edit[0].GetValue()); if(m_trade.OpenBuy(lot,Symbol(),m_magic_number,sl,tp)) return(true); } else if(!m_switch_button[1].IsPressed() && !m_switch_button[2].IsPressed()) { int tp=int(m_tp_edit[0].GetValue()); int sl=int(m_sl_edit[0].GetValue()); if(m_trade.OpenBuy(lot,Symbol(),m_magic_number,sl,tp)) return(true); } else if(m_switch_button[1].IsPressed() && !m_switch_button[2].IsPressed()) { double tp=double(m_tp_edit[0].GetValue()); int sl=int(m_sl_edit[0].GetValue()); if(m_trade.OpenBuy(lot,Symbol(),m_magic_number,sl,tp)) return(true); } else if(!m_switch_button[1].IsPressed() && m_switch_button[2].IsPressed()) { int tp=int(m_tp_edit[0].GetValue()); double sl=double(m_sl_edit[0].GetValue()); if(m_trade.OpenBuy(lot,Symbol(),m_magic_number,sl,tp)) return(true); } } return(false); } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ bool CFastTrading::SetSellOrder(int id,long lparam) { if((id==CHARTEVENT_CUSTOM+ON_CLICK_BUTTON && lparam==m_sell_execute.Id()) || (id==CHARTEVENT_KEYDOWN && lparam==KEY_S)) { //--- double lot; if(m_switch_button[3].IsPressed()) lot=LotPercent(Symbol(),ORDER_TYPE_SELL,SymbolInfoDouble(Symbol(),SYMBOL_BID),StringToDouble(m_lot_edit[1].GetValue())); else lot=NormalizeLot(Symbol(),StringToDouble(m_lot_edit[1].GetValue())); //--- if(m_switch_button[4].IsPressed() && m_switch_button[5].IsPressed()) { double tp=double(m_tp_edit[1].GetValue()); double sl=double(m_sl_edit[1].GetValue()); if(m_trade.OpenSell(lot,Symbol(),m_magic_number,sl,tp)) return(true); } else if(!m_switch_button[4].IsPressed() && !m_switch_button[5].IsPressed()) { int tp=int(m_tp_edit[1].GetValue()); int sl=int(m_sl_edit[1].GetValue()); if(m_trade.OpenSell(lot,Symbol(),m_magic_number,sl,tp)) return(true); } else if(!m_switch_button[4].IsPressed() && m_switch_button[5].IsPressed()) { int tp=int(m_tp_edit[1].GetValue()); double sl=double(m_sl_edit[1].GetValue()); if(m_trade.OpenSell(lot,Symbol(),m_magic_number,sl,tp)) return(true); } else if(m_switch_button[4].IsPressed() && !m_switch_button[5].IsPressed()) { double tp=double(m_tp_edit[1].GetValue()); int sl=int(m_sl_edit[1].GetValue()); if(m_trade.OpenSell(lot,Symbol(),m_magic_number,sl,tp)) return(true); } } return(false); }
Obwohl beide Handelsmethoden im Event-Handler aufgerufen werden, befinden sie sich aus dem oben genannten Grund außerhalb der Abschnitte, die Ereignisse identifizieren. Die aktuelle Implementierung der obigen Funktionen verwendet die Methode LotPercent() zur Berechnung der Losgröße in Prozent des Saldos.
//+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ double CFastTrading::LotPercent(string symbol,ENUM_ORDER_TYPE trade_type,double price,double percent) { double margin=0.0; //--- checks if(symbol=="" || price<=0.0 || percent<1 || percent>100) return(0.0); //--- calculate margin requirements for 1 lot if(!OrderCalcMargin(trade_type,symbol,1.0,price,margin) || margin<0.0) return(0.0); //--- if(margin==0.0) return(0.0); //--- calculate maximum volume double volume=NormalizeDouble(AccountInfoDouble(ACCOUNT_BALANCE)*percent/100.0/margin,2); //--- normalize and check limits double stepvol=SymbolInfoDouble(symbol,SYMBOL_VOLUME_STEP); if(stepvol>0.0) volume=stepvol*MathFloor(volume/stepvol); //--- double minvol=SymbolInfoDouble(symbol,SYMBOL_VOLUME_MIN); if(volume<minvol) volume=0.0; //--- double maxvol=SymbolInfoDouble(symbol,SYMBOL_VOLUME_MAX); if(volume>maxvol) volume=maxvol; //--- return volume return(volume); }
Der Teil, der die Operationen mit Marktaufträgen betrifft, ist fertig. Gehen wir nun zum Fenster für die Erstellung von Pending-Orders über. Erstellen wir die Schnittstelle nach den gleichen Schritten, die wir bei den Marktaufträgen durchgeführt haben. Die meisten Schritte sind ähnlich. Ich werde nur bei einigen wenigen Punkten tiefer ins Detail gehen.
//+------------------------------------------------------------------+ //| Pending order creation and editing window | //+------------------------------------------------------------------+ bool CFastTrading::CreatePendingOrdersWindow(void) { //--- Add a window pointer to the window array CWndContainer::AddWindow(m_orders_windows[1]); //--- Properties m_orders_windows[1].XSize(600); m_orders_windows[1].YSize(580); //--- Coordinates int x=m_order_button[0].XGap(); int y=m_order_button[0].YGap()+60; //--- color clrmain=C'31,209,111'; //--- m_orders_windows[1].CaptionHeight(22); m_orders_windows[1].IsMovable(true); m_orders_windows[1].CaptionColor(clrmain); m_orders_windows[1].CaptionColorLocked(clrmain); m_orders_windows[1].CaptionColorHover(clrmain); m_orders_windows[1].BackColor(m_background_color); m_orders_windows[1].BorderColor(clrmain); m_orders_windows[1].FontSize(m_base_font_size); m_orders_windows[1].Font(m_base_font); m_orders_windows[1].WindowType(W_DIALOG); //--- Create the form if(!m_orders_windows[1].CreateWindow(m_chart_id,m_subwin,CAPTION_P_ORD_NAME,x,y)) return(false); //---BUY-STOP BLOCK if(!CreateFrame(m_orders_windows[1],m_frame[2],10,20,BUYSTOP_ORDER,2)) return(false); //--- Headers if(!CreateLabel(m_orders_windows[1],m_p_text_labels[0],20,60,PRICE,2)) return(false); if(!CreateLabel(m_orders_windows[1],m_p_text_labels[1],20+80+20,30,LOT,2)) return(false); if(!CreateLabel(m_orders_windows[1],m_p_text_labels[2],20+(80+20)*2,30,TP,2)) return(false); if(!CreateLabel(m_orders_windows[1],m_p_text_labels[3],20+(80+20)*3,30,SL,2)) return(false); //--- Switches for(int i=0; i<3; i++) if(!CreateSwitchButton(m_orders_windows[1],m_p_switch_button[i],"-",120+(80+20)*i,60,2)) return(false); //--- Edits if(!CreatePriceEdit(m_orders_windows[1],m_pr_edit[0],20,60+35,2)) return(false); if(!CreateLotEdit(m_orders_windows[1],m_lot_edit[2],20+(80+20),60+35,2)) return(false); if(!CreateTakeProfitEdit(m_orders_windows[1],m_tp_edit[2],20+(80+20)*2,60+35,2)) return(false); if(!CreateStopLossEdit(m_orders_windows[1],m_sl_edit[2],20+(80+20)*3,60+35,2)) return(false); //--- Buy Stop placing button if(!CreateBuyButton(m_orders_windows[1],m_buystop_execute,"Buy Stop ( 1 )",m_orders_windows[1].XSize()-(120+20),103,2)) return(false); //---SELL-STOP BLOCK if(!CreateFrame(m_orders_windows[1],m_frame[3],10,160,SELLSTOP_ORDER,2)) return(false); //--- Headers if(!CreateLabel(m_orders_windows[1],m_p_text_labels[4],20,170+30,PRICE,2)) return(false); if(!CreateLabel(m_orders_windows[1],m_p_text_labels[5],20+80+20,170,LOT,2)) return(false); if(!CreateLabel(m_orders_windows[1],m_p_text_labels[6],20+(80+20)*2,170,TP,2)) return(false); if(!CreateLabel(m_orders_windows[1],m_p_text_labels[7],20+(80+20)*3,170,SL,2)) return(false); //--- Switches for(int i=3; i<6; i++) if(!CreateSwitchButton(m_orders_windows[1],m_p_switch_button[i],"-",120+(80+20)*(i-3),170+30,2)) return(false); //--- Edits if(!CreatePriceEdit(m_orders_windows[1],m_pr_edit[1],20,170+30+35,2)) return(false); if(!CreateLotEdit(m_orders_windows[1],m_lot_edit[3],20+(80+20),170+30+35,2)) return(false); if(!CreateTakeProfitEdit(m_orders_windows[1],m_tp_edit[3],20+(80+20)*2,170+30+35,2)) return(false); if(!CreateStopLossEdit(m_orders_windows[1],m_sl_edit[3],20+(80+20)*3,170+30+35,2)) return(false); //--- Sell Stop placing button if(!CreateSellButton(m_orders_windows[1],m_sellstop_execute,"Sell Stop ( 2 )",m_orders_windows[1].XSize()-(120+20),242,2)) return(false); //---BUY-LIMIT BLOCK if(!CreateFrame(m_orders_windows[1],m_frame[4],10,300,BUYLIMIT_ORDER,2)) return(false); //--- Headers if(!CreateLabel(m_orders_windows[1],m_p_text_labels[8],20,330,PRICE,2)) return(false); if(!CreateLabel(m_orders_windows[1],m_p_text_labels[9],20+80+20,310,LOT,2)) return(false); if(!CreateLabel(m_orders_windows[1],m_p_text_labels[10],20+(80+20)*2,310,TP,2)) return(false); if(!CreateLabel(m_orders_windows[1],m_p_text_labels[11],20+(80+20)*3,310,SL,2)) return(false); //--- Switches for(int i=6; i<9; i++) if(!CreateSwitchButton(m_orders_windows[1],m_p_switch_button[i],"-",120+(80+20)*(i-6),330,2)) return(false); //--- Edits if(!CreatePriceEdit(m_orders_windows[1],m_pr_edit[2],20,365,2)) return(false); if(!CreateLotEdit(m_orders_windows[1],m_lot_edit[4],20+(80+20),365,2)) return(false); if(!CreateTakeProfitEdit(m_orders_windows[1],m_tp_edit[4],20+(80+20)*2,365,2)) return(false); if(!CreateStopLossEdit(m_orders_windows[1],m_sl_edit[4],20+(80+20)*3,365,2)) return(false); //--- Buy Limit placing button if(!CreateBuyButton(m_orders_windows[1],m_buylimit_execute,"Buy Limit ( 3 )",m_orders_windows[1].XSize()-(120+20),382,2)) return(false); //---SELL-LIMIT BLOCK if(!CreateFrame(m_orders_windows[1],m_frame[5],10,440,SELLLIMIT_ORDER,2)) return(false); //--- Headers if(!CreateLabel(m_orders_windows[1],m_p_text_labels[12],20,470,PRICE,2)) return(false); if(!CreateLabel(m_orders_windows[1],m_p_text_labels[13],20+80+20,450,LOT,2)) return(false); if(!CreateLabel(m_orders_windows[1],m_p_text_labels[14],20+(80+20)*2,450,TP,2)) return(false); if(!CreateLabel(m_orders_windows[1],m_p_text_labels[15],20+(80+20)*3,450,SL,2)) return(false); //--- Switches for(int i=9; i<12; i++) if(!CreateSwitchButton(m_orders_windows[1],m_p_switch_button[i],"-",120+(80+20)*(i-9),470,2)) return(false); //--- Edits if(!CreatePriceEdit(m_orders_windows[1],m_pr_edit[3],20,505,2)) return(false); if(!CreateLotEdit(m_orders_windows[1],m_lot_edit[5],20+(80+20),505,2)) return(false); if(!CreateTakeProfitEdit(m_orders_windows[1],m_tp_edit[5],20+(80+20)*2,505,2)) return(false); if(!CreateStopLossEdit(m_orders_windows[1],m_sl_edit[5],20+(80+20)*3,505,2)) return(false); //--- Sell Limit placing button if(!CreateSellButton(m_orders_windows[1],m_selllimit_execute,"Sell Limit ( 4 )",m_orders_windows[1].XSize()-(120+20),522,2)) return(false); //--- return(true); }
Wie Sie sehen können, verwendet der obige Code die gleichen Methoden, wie wir sie für das Marktauftragsfenster verwendet haben. Er gibt jedoch eine neue Methode. CreatePriceEdit() sind die Eingabefelder für den Platzierungspreis der Pending-Orders.
//+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ bool CFastTrading::CreatePriceEdit(CWindow &window,CTextEdit &text_edit,const int x_gap,const int y_gap,int w_number) { //--- Store the pointer to the main control text_edit.MainPointer(window); //--- Properties text_edit.XSize(80); text_edit.YSize(24); text_edit.Font(m_base_font); text_edit.FontSize(m_base_font_size); text_edit.StepValue(_Point); text_edit.SpinEditMode(true); text_edit.SetDigits(_Digits); text_edit.GetTextBoxPointer().XGap(1); text_edit.GetTextBoxPointer().XSize(80); //--- Create a control element if(!text_edit.CreateTextEdit("",x_gap,window.CaptionHeight()+y_gap)) return(false); //--- MqlTick tick; if(SymbolInfoTick(Symbol(),tick)) text_edit.SetValue(string(tick.bid)); //--- Add the object to the common array of object groups CWndContainer::AddToElementsArray(w_number,text_edit); return(true); }
Die erste Vorlage ist nach der Implementierung der Methode fertig, die Schnittstelle ist jedoch noch nicht nutzbar. Sie muss konfiguriert werden. Wir werden diesen Teil in der gleichen Reihenfolge implementieren: Schaltflächen konfigurieren, ihren Zustand mit den Eigenschaften der Eingabefelder verbinden und Funktionen für die Platzierung ausstehender Aufträge erstellen.
Gehen Sie zu OnEvent(), Abschnitt Interface Creation Completion, und legen Sie die Namen für die Umschaltflächen fest:
//--- UI creation completion if(id==CHARTEVENT_CUSTOM+ON_END_CREATE_GUI) { //--- SetButtonParam(m_switch_button[0],LOT); SetButtonParam(m_switch_button[1],POINTS); SetButtonParam(m_switch_button[2],POINTS); SetButtonParam(m_switch_button[3],LOT); SetButtonParam(m_switch_button[4],POINTS); SetButtonParam(m_switch_button[5],POINTS); //--- SetButtonParam(m_p_switch_button[0],LOT); SetButtonParam(m_p_switch_button[1],POINTS); SetButtonParam(m_p_switch_button[2],POINTS); SetButtonParam(m_p_switch_button[3],LOT); SetButtonParam(m_p_switch_button[4],POINTS); SetButtonParam(m_p_switch_button[5],POINTS); SetButtonParam(m_p_switch_button[6],LOT); SetButtonParam(m_p_switch_button[7],POINTS); SetButtonParam(m_p_switch_button[8],POINTS); SetButtonParam(m_p_switch_button[9],LOT); SetButtonParam(m_p_switch_button[10],POINTS); SetButtonParam(m_p_switch_button[11],POINTS); }
Gehen Sie zum Abschnitt 'Button Click Event' und implementieren Sie den Mechanismus, der Schaltflächenzustände umschaltet und ihre Namen ändert:
//--- ButtonSwitch(m_p_switch_button[0],lparam,LOT,PERC_DEPO); ButtonSwitch(m_p_switch_button[1],lparam,POINTS,PRICE); ButtonSwitch(m_p_switch_button[2],lparam,POINTS,PRICE); ButtonSwitch(m_p_switch_button[3],lparam,LOT,PERC_DEPO); ButtonSwitch(m_p_switch_button[4],lparam,POINTS,PRICE); ButtonSwitch(m_p_switch_button[5],lparam,POINTS,PRICE); ButtonSwitch(m_p_switch_button[6],lparam,LOT,PERC_DEPO); ButtonSwitch(m_p_switch_button[7],lparam,POINTS,PRICE); ButtonSwitch(m_p_switch_button[8],lparam,POINTS,PRICE); ButtonSwitch(m_p_switch_button[9],lparam,LOT,PERC_DEPO); ButtonSwitch(m_p_switch_button[10],lparam,POINTS,PRICE); ButtonSwitch(m_p_switch_button[11],lparam,POINTS,PRICE);
Um die Umschaltflächen mit den Eingabefeldern und ihren Eigenschaften zu verknüpfen, erstellen Sie drei neue Methoden: LotPendingSwitch(), TakePendingSwitch(), StopPendingSwitch().
//+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ void CFastTrading::LotPendingSwitch(long lparam) { for(int i=0; i<4; i++) { if(lparam==m_p_switch_button[3*i].Id()) { if(m_p_switch_button[3*i].IsPressed()) { m_lot_edit[i+2].SetDigits(0); m_lot_edit[i+2].StepValue(1); m_lot_edit[i+2].MaxValue(100); m_lot_edit[i+2].MinValue(1); m_lot_edit[i+2].SetValue(string(2)); m_lot_edit[i+2].GetTextBoxPointer().Update(true); } else { m_lot_edit[i+2].SetDigits(2); m_lot_edit[i+2].StepValue(SymbolInfoDouble(Symbol(),SYMBOL_VOLUME_STEP)); m_lot_edit[i+2].MinValue(SymbolInfoDouble(Symbol(),SYMBOL_VOLUME_MIN)); m_lot_edit[i+2].MaxValue(SymbolInfoDouble(Symbol(),SYMBOL_VOLUME_MAX)); m_lot_edit[i+2].SetValue(string(SymbolInfoDouble(Symbol(),SYMBOL_VOLUME_MIN))); m_lot_edit[i+2].GetTextBoxPointer().Update(true); } } } } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ void CFastTrading::TakePendingSwitch(long lparam) { for(int i=0; i<4; i++) { if(lparam==m_p_switch_button[3*i+1].Id()) { if(m_p_switch_button[3*i+1].IsPressed()) { MqlTick tick; if(SymbolInfoTick(Symbol(),tick)) { m_tp_edit[i+2].SetDigits(_Digits); m_tp_edit[i+2].StepValue(_Point); m_tp_edit[i+2].SetValue(string(tick.ask)); m_tp_edit[i+2].GetTextBoxPointer().Update(true); } } else { m_tp_edit[i+2].SetDigits(0); m_tp_edit[i+2].StepValue(1); m_tp_edit[i+2].SetValue(string(150)); m_tp_edit[i+2].GetTextBoxPointer().Update(true); } } } } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ void CFastTrading::StopPendingSwitch(long lparam) { for(int i=0; i<4; i++) { if(lparam==m_p_switch_button[3*i+2].Id()) { if(m_p_switch_button[3*i+2].IsPressed()) { MqlTick tick; if(SymbolInfoTick(Symbol(),tick)) { m_sl_edit[i+2].SetDigits(_Digits); m_sl_edit[i+2].StepValue(_Point); m_sl_edit[i+2].SetValue(string(tick.bid)); m_sl_edit[i+2].GetTextBoxPointer().Update(true); } } else { m_sl_edit[i+2].SetDigits(0); m_sl_edit[i+2].StepValue(1); m_sl_edit[i+2].SetValue(string(150)); m_sl_edit[i+2].GetTextBoxPointer().Update(true); } } } }
Nun rufen wir sie in der Ereignisbehandlung eines Knopfdruckes auf.
// --- Switch Lot/Percent of balance LotPendingSwitch(lparam); //--- Switch Take Profit Points/Price TakePendingSwitch(lparam); //--- Switch Stop Loss Points/Price StopPendingSwitch(lparam);
Kompilieren wir das Projekt und schauen uns das Ergebnis an:
Abb. 11 Umschalten der Modi der Eingabefelder für ausstehende Aufträge.
Die Umschaltung der Eingabefeldmodi ist bereit; das Ändern der Eigenschaften funktioniert ebenfalls. Fahren Sie mit der Erstellung von Funktionen fort, die zum Platzieren ausstehender Aufträge erforderlich sind. Der Algorithmus ist auch denen der Marktorders ähnlich. Erstellen wir vier Methoden, eine für jeden Typ.
bool SetBuyStopOrder(int id,long lparam); bool SetSellStopOrder(int id,long lparam); bool SetBuyLimitOrder(int id,long lparam); bool SetSellLimitOrder(int id,long lparam);
Implementieren wir sie Setzen Sie einen Hotkey für jeden der Pending-Order, zusätzlich zum Knopfdruck.
//+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ bool CFastTrading::SetBuyStopOrder(int id,long lparam) { if(!m_orders_windows[1].IsVisible()) return(false); if((id==CHARTEVENT_CUSTOM+ON_CLICK_BUTTON && lparam==m_buystop_execute.Id()) || (id==CHARTEVENT_KEYDOWN && lparam==KEY_1)) { //--- double lot; if(m_p_switch_button[0].IsPressed()) lot=LotPercent(Symbol(),ORDER_TYPE_BUY,SymbolInfoDouble(Symbol(),SYMBOL_ASK),StringToDouble(m_lot_edit[2].GetValue())); else lot=NormalizeLot(Symbol(),StringToDouble(m_lot_edit[2].GetValue())); //--- double pr=double(m_pr_edit[0].GetValue()); //--- if(m_p_switch_button[1].IsPressed() && m_p_switch_button[2].IsPressed()) { double tp=double(m_tp_edit[2].GetValue()); double sl=double(m_sl_edit[2].GetValue()); if(m_trade.PlaceBuyStop(lot,Symbol(),pr,sl,tp,m_magic_number)) return(true); } else if(!m_p_switch_button[1].IsPressed() && !m_p_switch_button[2].IsPressed()) { int tp=int(m_tp_edit[2].GetValue()); int sl=int(m_sl_edit[2].GetValue()); if(m_trade.PlaceBuyStop(lot,Symbol(),pr,sl,tp,m_magic_number)) return(true); } else if(m_p_switch_button[1].IsPressed() && !m_p_switch_button[2].IsPressed()) { double tp=double(m_tp_edit[2].GetValue()); int sl=int(m_sl_edit[2].GetValue()); if(m_trade.PlaceBuyStop(lot,Symbol(),pr,sl,tp,m_magic_number)) return(true); } else if(!m_p_switch_button[1].IsPressed() && m_p_switch_button[2].IsPressed()) { int tp=int(m_tp_edit[2].GetValue()); double sl=double(m_sl_edit[2].GetValue()); if(m_trade.PlaceBuyStop(lot,Symbol(),pr,sl,tp,m_magic_number)) return(true); } } return(false); } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ bool CFastTrading::SetSellStopOrder(int id,long lparam) { if(!m_orders_windows[1].IsVisible()) return(false); if((id==CHARTEVENT_CUSTOM+ON_CLICK_BUTTON && lparam==m_sellstop_execute.Id()) || (id==CHARTEVENT_KEYDOWN && lparam==KEY_2)) { //--- double lot; if(m_p_switch_button[3].IsPressed()) lot=LotPercent(Symbol(),ORDER_TYPE_SELL,SymbolInfoDouble(Symbol(),SYMBOL_BID),StringToDouble(m_lot_edit[3].GetValue())); else lot=NormalizeLot(Symbol(),StringToDouble(m_lot_edit[3].GetValue())); //--- double pr=double(m_pr_edit[1].GetValue()); //--- if(m_p_switch_button[4].IsPressed() && m_p_switch_button[5].IsPressed()) { double tp=double(m_tp_edit[3].GetValue()); double sl=double(m_sl_edit[3].GetValue()); if(m_trade.PlaceSellStop(lot,Symbol(),pr,sl,tp,m_magic_number)) return(true); } else if(!m_p_switch_button[4].IsPressed() && !m_p_switch_button[5].IsPressed()) { int tp=int(m_tp_edit[3].GetValue()); int sl=int(m_sl_edit[3].GetValue()); if(m_trade.PlaceSellStop(lot,Symbol(),pr,sl,tp,m_magic_number)) return(true); } else if(m_p_switch_button[4].IsPressed() && !m_p_switch_button[5].IsPressed()) { double tp=double(m_tp_edit[3].GetValue()); int sl=int(m_sl_edit[3].GetValue()); if(m_trade.PlaceSellStop(lot,Symbol(),pr,sl,tp,m_magic_number)) return(true); } else if(!m_p_switch_button[4].IsPressed() && m_p_switch_button[5].IsPressed()) { int tp=int(m_tp_edit[3].GetValue()); double sl=double(m_sl_edit[3].GetValue()); if(m_trade.PlaceSellStop(lot,Symbol(),pr,sl,tp,m_magic_number)) return(true); } } return(false); } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ bool CFastTrading::SetBuyLimitOrder(int id,long lparam) { if(!m_orders_windows[1].IsVisible()) return(false); if((id==CHARTEVENT_CUSTOM+ON_CLICK_BUTTON && lparam==m_buylimit_execute.Id()) || (id==CHARTEVENT_KEYDOWN && lparam==KEY_3)) { //--- double lot; if(m_p_switch_button[6].IsPressed()) lot=LotPercent(Symbol(),ORDER_TYPE_BUY,SymbolInfoDouble(Symbol(),SYMBOL_ASK),StringToDouble(m_lot_edit[4].GetValue())); else lot=NormalizeLot(Symbol(),StringToDouble(m_lot_edit[4].GetValue())); //--- double pr=double(m_pr_edit[2].GetValue()); //--- if(m_p_switch_button[7].IsPressed() && m_p_switch_button[8].IsPressed()) { double tp=double(m_tp_edit[4].GetValue()); double sl=double(m_sl_edit[4].GetValue()); if(m_trade.PlaceBuyLimit(lot,Symbol(),pr,sl,tp,m_magic_number)) return(true); } else if(!m_p_switch_button[7].IsPressed() && !m_p_switch_button[8].IsPressed()) { int tp=int(m_tp_edit[4].GetValue()); int sl=int(m_sl_edit[4].GetValue()); if(m_trade.PlaceBuyLimit(lot,Symbol(),pr,sl,tp,m_magic_number)) return(true); } else if(m_p_switch_button[7].IsPressed() && !m_p_switch_button[8].IsPressed()) { double tp=double(m_tp_edit[4].GetValue()); int sl=int(m_sl_edit[4].GetValue()); if(m_trade.PlaceBuyLimit(lot,Symbol(),pr,sl,tp,m_magic_number)) return(true); } else if(!m_p_switch_button[7].IsPressed() && m_p_switch_button[8].IsPressed()) { int tp=int(m_tp_edit[4].GetValue()); double sl=double(m_sl_edit[4].GetValue()); if(m_trade.PlaceBuyLimit(lot,Symbol(),pr,sl,tp,m_magic_number)) return(true); } } return(false); } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ bool CFastTrading::SetSellLimitOrder(int id,long lparam) { if(!m_orders_windows[1].IsVisible()) return(false); if((id==CHARTEVENT_CUSTOM+ON_CLICK_BUTTON && lparam==m_selllimit_execute.Id()) || (id==CHARTEVENT_KEYDOWN && lparam==KEY_4)) { //--- double lot; if(m_p_switch_button[9].IsPressed()) lot=LotPercent(Symbol(),ORDER_TYPE_SELL,SymbolInfoDouble(Symbol(),SYMBOL_BID),StringToDouble(m_lot_edit[5].GetValue())); else lot=NormalizeLot(Symbol(),StringToDouble(m_lot_edit[5].GetValue())); //--- double pr=double(m_pr_edit[3].GetValue()); //--- if(m_p_switch_button[10].IsPressed() && m_p_switch_button[11].IsPressed()) { double tp=double(m_tp_edit[5].GetValue()); double sl=double(m_sl_edit[5].GetValue()); if(m_trade.PlaceSellStop(lot,Symbol(),pr,sl,tp,m_magic_number)) return(true); } else if(!m_p_switch_button[10].IsPressed() && !m_p_switch_button[11].IsPressed()) { int tp=int(m_tp_edit[5].GetValue()); int sl=int(m_sl_edit[5].GetValue()); if(m_trade.PlaceSellStop(lot,Symbol(),pr,sl,tp,m_magic_number)) return(true); } else if(m_p_switch_button[10].IsPressed() && !m_p_switch_button[11].IsPressed()) { double tp=double(m_tp_edit[5].GetValue()); int sl=int(m_sl_edit[5].GetValue()); if(m_trade.PlaceSellStop(lot,Symbol(),pr,sl,tp,m_magic_number)) return(true); } else if(!m_p_switch_button[10].IsPressed() && m_p_switch_button[11].IsPressed()) { int tp=int(m_tp_edit[5].GetValue()); double sl=double(m_sl_edit[5].GetValue()); if(m_trade.PlaceSellStop(lot,Symbol(),pr,sl,tp,m_magic_number)) return(true); } } return(false); }
Gehen Sie nun zur Ereignisbehandlung und fügen Sie ihm die Aufrufe der neu erstellten Methoden hinzu:
//+------------------------------------------------------------------+ //| Chart event handler | //+------------------------------------------------------------------+ void CFastTrading::OnEvent(const int id,const long &lparam,const double &dparam,const string &sparam) { //--- SetBuyOrder(id,lparam); SetSellOrder(id,lparam); //--- SetBuyStopOrder(id,lparam); SetSellStopOrder(id,lparam); SetBuyLimitOrder(id,lparam); SetSellLimitOrder(id,lparam);
Die Grundfunktionen für die Arbeit mit allen Auftragstypen ist fertig. Da der letztendliche Zweck dieser Anwendung darin besteht, maximale Bequemlichkeit und Geschwindigkeit beim manuellen Handel zu bieten, habe ich beschlossen, eine kleine Funktion hinzuzufügen, die die Platzierung von Markt- und Pending-Aufträgen beschleunigen wird. Ihr Prinzip ist wie folgt: Bei der Einstellung von Eingabefeldwerten in verschiedenen Modi sollte es die Möglichkeit geben, den Wert mit Hilfe von Auf- und Abwärtspfeilen um einen bestimmten Schritt zu ändern. Bei der Einstellung z.B. des Preises einer Pending-Order würde der EURUSD-Preis jedoch durch Drücken eines Pfeils von 1,08500 auf 1,85001 geändert werden. Das ist langsam und nicht sehr bequem. Wenn das Eingabefeld zur Bearbeitung ausgewählt ist und wir auf die Aufwärts/Abwärts-Hotkeys drücken, lassen Sie daher den Wert um einen Schritt multipliziert mit 10 ändern. Zu diesem Zweck müssen wir die Methode ArrowSwitch() erstellen.
//+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ void CFastTrading::ArrowSwitch(long lparam) { //--- Prices for(int i=0; i<4; i++) { if(m_pr_edit[i].GetTextBoxPointer().TextEditState()) { if(lparam==KEY_UP) { //--- Get the new value double value=StringToDouble(m_pr_edit[i].GetValue())+m_pr_edit[i].StepValue()*10; //--- Increase by one step and check for exceeding the limit m_pr_edit[i].SetValue(DoubleToString(value),false); } else if(lparam==KEY_DOWN) { //--- Get the new value double value=StringToDouble(m_pr_edit[i].GetValue())-m_pr_edit[i].StepValue()*10; //--- Increase by one step and check for exceeding the limit m_pr_edit[i].SetValue(DoubleToString(value),false); } } } //--- Lot for(int i=0; i<6; i++) { if(m_lot_edit[i].GetTextBoxPointer().TextEditState()) { if(lparam==KEY_UP) { //--- Get the new value double value=StringToDouble(m_lot_edit[i].GetValue())+m_lot_edit[i].StepValue()*10; //--- Increase by one step and check for exceeding the limit m_lot_edit[i].SetValue(DoubleToString(value),false); } else if(lparam==KEY_DOWN) { //--- Get the new value double value=StringToDouble(m_lot_edit[i].GetValue())-m_lot_edit[i].StepValue()*10; //--- Increase by one step and check for exceeding the limit m_lot_edit[i].SetValue(DoubleToString(value),false); } } } //--- Take Profit, Stop Loss for(int i=0; i<6; i++) { //--- if(m_tp_edit[i].GetTextBoxPointer().TextEditState()) { if(lparam==KEY_UP) { //--- Get the new value double value=StringToDouble(m_tp_edit[i].GetValue())+m_tp_edit[i].StepValue()*10; //--- Increase by one step and check for exceeding the limit m_tp_edit[i].SetValue(DoubleToString(value),false); } else if(lparam==KEY_DOWN) { //--- Get the new value double value=StringToDouble(m_tp_edit[i].GetValue())-m_tp_edit[i].StepValue()*10; //--- Increase by one step and check for exceeding the limit m_tp_edit[i].SetValue(DoubleToString(value),false); } } //--- if(m_sl_edit[i].GetTextBoxPointer().TextEditState()) { if(lparam==KEY_UP) { //--- Get the new value double value=StringToDouble(m_sl_edit[i].GetValue())+m_sl_edit[i].StepValue()*10; //--- Increase by one step and check for exceeding the limit m_sl_edit[i].SetValue(DoubleToString(value),false); } else if(lparam==KEY_DOWN) { //--- Get the new value double value=StringToDouble(m_sl_edit[i].GetValue())-m_sl_edit[i].StepValue()*10; //--- Increase by one step and check for exceeding the limit m_sl_edit[i].SetValue(DoubleToString(value),false); } } } }
Die Methode sollte in der Ereignisbehandlung innerhalb des Abschnitts Tastendruck aufgerufen werden.
//--- Keypress if(id==CHARTEVENT_KEYDOWN) { //--- ArrowSwitch(lparam); ....
Abbildung 12 zeigt den Unterschied zwischen der Wertänderungsgeschwindigkeit.
Abb. 12 Die beschleunigte Wertänderung in Editierfeldern mittels Hotkeys.
Das Werkzeug für die schnelle Platzierung von Markt- und Pending-Orders steht bereit. Nun gehen wir zum Hauptanwendungsfenster und fügen Aktionen hinzu, die sich auf die bestehenden Aufträge beziehen (wie in der allerersten Abbildung, am Anfang des Artikels, dargestellt). Dazu gehören drei Aktionen: Schließen aller Positionen im Gewinn, Schließen aller Positionen im Verlust und Löschen aller bestehenden Pending-Orders. Diese Aktionen sollten nur für die von unserer Anwendung erteilten Aufträge gelten, die eine bestimmte Magicnummer haben sollten. Es sollte auch daran erinnert werden, dass bei der Platzierung von Orders mit der aktuellen Magicnummer und der anschließenden Änderung der Magicnummer in den EA-Eigenschaften die bestehenden Bestellungen mit der alten Nummer ignoriert werden sollten, da sie nicht mehr mit der aktuellen Anwendung zusammenhängen.
Lassen Sie uns diese Aktionen implementieren. Erstellen Sie drei neue Methoden:
void CloseAllMarketProfit(int id,long lparam); void CloseAllMarketLoss(int id,long lparam); void DeleteAllPending(int id,long lparam);
Implementieren wir jede.
//+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ void CFastTrading::CloseAllMarketProfit(int id,long lparam) { if((id==CHARTEVENT_CUSTOM+ON_CLICK_BUTTON && lparam==m_order_button[2].Id()) || (id==CHARTEVENT_KEYDOWN && lparam==KEY_C)) { //--- declare the request and the result MqlTradeRequest request; MqlTradeResult result; int total=PositionsTotal(); // the number of open positions //--- iterate over open positions for(int i=total-1; i>=0; i--) { //--- order parameters ulong position_ticket=PositionGetTicket(i); // position ticket string position_symbol=PositionGetString(POSITION_SYMBOL); // symbol int digits=(int)SymbolInfoInteger(position_symbol,SYMBOL_DIGITS); // the number of decimal places ulong magic=PositionGetInteger(POSITION_MAGIC); // position MagicNumber double volume=PositionGetDouble(POSITION_VOLUME); // position volume ENUM_POSITION_TYPE type=(ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE); // position type double profit=PositionGetDouble(POSITION_PROFIT); double swap=PositionGetDouble(POSITION_SWAP); //--- if MagicNumber matches if(magic==m_magic_number) if(position_symbol==Symbol()) if(profit+swap>0) { //--- zeroing the request and result values ZeroMemory(request); ZeroMemory(result); //--- set the operation parameters request.action =TRADE_ACTION_DEAL; // trading operation type request.position =position_ticket; // position ticket request.symbol =position_symbol; // symbol request.volume =volume; // position volume request.deviation=5; // allowable price deviation request.magic =m_magic_number; // position MagicNumber //--- Set order price and type depending on the position type if(type==POSITION_TYPE_BUY) { request.price=SymbolInfoDouble(position_symbol,SYMBOL_BID); request.type =ORDER_TYPE_SELL; } else { request.price=SymbolInfoDouble(position_symbol,SYMBOL_ASK); request.type =ORDER_TYPE_BUY; } //--- sending a request if(!OrderSend(request,result)) PrintFormat("OrderSend error %d",GetLastError()); // if unable to send the request, output the error code } } } } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ void CFastTrading::CloseAllMarketLoss(int id,long lparam) { if((id==CHARTEVENT_CUSTOM+ON_CLICK_BUTTON && lparam==m_order_button[3].Id()) || (id==CHARTEVENT_KEYDOWN && lparam==KEY_D)) { //--- declare the request and the result MqlTradeRequest request; MqlTradeResult result; ZeroMemory(request); ZeroMemory(result); int total=PositionsTotal(); // the number of open positions //--- iterate over open positions for(int i=total-1; i>=0; i--) { //--- order parameters ulong position_ticket=PositionGetTicket(i); // position ticket string position_symbol=PositionGetString(POSITION_SYMBOL); // symbol int digits=(int)SymbolInfoInteger(position_symbol,SYMBOL_DIGITS); // the number of decimal places ulong magic=PositionGetInteger(POSITION_MAGIC); // position MagicNumber double volume=PositionGetDouble(POSITION_VOLUME); // position volume ENUM_POSITION_TYPE type=(ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE); // position type double profit=PositionGetDouble(POSITION_PROFIT); double swap=PositionGetDouble(POSITION_SWAP); //--- if MagicNumber matches if(magic==m_magic_number) if(position_symbol==Symbol()) if(profit+swap<0) { //--- zeroing the request and result values ZeroMemory(request); ZeroMemory(result); //--- set the operation parameters request.action =TRADE_ACTION_DEAL; // trading operation type request.position =position_ticket; // position ticket request.symbol =position_symbol; // symbol request.volume =volume; // position volume request.deviation=5; // allowable price deviation request.magic =m_magic_number; // position MagicNumber //--- Set order price and type depending on the position type if(type==POSITION_TYPE_BUY) { request.price=SymbolInfoDouble(position_symbol,SYMBOL_BID); request.type =ORDER_TYPE_SELL; } else { request.price=SymbolInfoDouble(position_symbol,SYMBOL_ASK); request.type =ORDER_TYPE_BUY; } //--- sending a request if(!OrderSend(request,result)) PrintFormat("OrderSend error %d",GetLastError()); // if unable to send the request, output the error code } } } } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ void CFastTrading::DeleteAllPending(int id,long lparam) { if((id==CHARTEVENT_CUSTOM+ON_CLICK_BUTTON && lparam==m_order_button[4].Id()) || (id==CHARTEVENT_KEYDOWN && lparam==KEY_R)) { //--- declare and initialize the trade request and result of trade request MqlTradeRequest request; MqlTradeResult result; //--- iterate over all placed pending orders for(int i=OrdersTotal()-1; i>=0; i--) { ulong order_ticket=OrderGetTicket(i); // order ticket ulong magic=OrderGetInteger(ORDER_MAGIC); // order MagicNumber //--- if MagicNumber matches if(magic==m_magic_number) { //--- zeroing the request and result values ZeroMemory(request); ZeroMemory(result); //--- set the operation parameters request.action= TRADE_ACTION_REMOVE; // trading operation type request.order = order_ticket; // order ticket //--- sending a request if(!OrderSend(request,result)) PrintFormat("OrderSend error %d",GetLastError()); // if unable to send the request, output the error code } } } }
Beachten Sie, dass sich die erstellten Methoden auch an die Regel halten, eine Aktion durch zwei verschiedene Ereignisse auszuführen: durch Klicken auf eine Schaltfläche im Hauptanwendungsfenster und durch Drücken eines Hotkeys. Alle drei Funktionen werden in der Ereignisbehandlung aufgerufen, außerhalb aller Abschnitte.
//+------------------------------------------------------------------+ //| Chart event handler | //+------------------------------------------------------------------+ void CFastTrading::OnEvent(const int id,const long &lparam,const double &dparam,const string &sparam) { //--- SetBuyOrder(id,lparam); SetSellOrder(id,lparam); //--- SetBuyStopOrder(id,lparam); SetSellStopOrder(id,lparam); SetBuyLimitOrder(id,lparam); SetSellLimitOrder(id,lparam); //--- CloseAllMarketProfit(id,lparam); CloseAllMarketLoss(id,lparam); //--- DeleteAllPending(id,lparam);
Damit ist die Entwicklung der Basisfunktionalität für die schnellen Werkzeuge für den manuellen Handel abgeschlossen. Das untenstehende Video demonstriert die erstellte Anwendung.
Schlussfolgerung
Das angehängte Archiv enthält alle besprochenen Dateien, die sich in den entsprechenden Ordnern befinden. Für ihren ordnungsgemäßen Betrieb brauchen Sie nur den MQL5-Ordner im Terminalordner zu speichern. Um das Stammverzeichnis des Terminals, in dem sich der MQL5-Ordner befindet, zu öffnen, drücken Sie die Tastenkombination Strg+Umschalt+D im MetaTrader 5 Terminal oder verwenden Sie das Kontextmenü, wie in Abb. 13 unten dargestellt.
Abb.13 Öffnen des MQL5-Ordners im Terminal-Stamm von MetaTrader 5
Übersetzt aus dem Russischen von MetaQuotes Ltd.
Originalartikel: https://www.mql5.com/ru/articles/7892
- 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.