Die Handelssignale mehrerer Währungen überwachen (Teil 2): Implementierung des visuellen Teils der Anwendung
Inhaltsverzeichnis
- Einführung
- Setup-Schritt 1: Symbole
- Setup-Schritt 2: Zeitrahmen
- Setup-Schritt 3: Signale hinzufügen
- Fenster zur Erstellung und Bearbeitung von Handelssignalen
- Monitor der Handelssignale
- Schlussfolgerung
Einführung
Im vorherigen Artikel entwickelten wir die allgemeine Struktur des Monitors für Handelssignale mehrerer Symbole. In diesem Teil werden wir nacheinander die mit der anfänglichen Anwendungskonfiguration verbundenen Schritt-für-Schritt-Stufen implementieren und die grundlegende Interaktion der Elemente schaffen, aus denen die Schnittstelle besteht.
Setup-Schritt 1: Symbole
Entsprechend der Anwendungsstruktur impliziert der erste Schritt zur Einrichtung der Anwendung bei der ersten Inbetriebnahme die Schaffung einer Schnittstelle zur Auswahl von Symbolen, die weiter zur Suche nach erzeugten Handelssignalen verwendet werden sollen. Am Ende des vorhergehenden Artikels haben wir einen Anwendungsrahmen geschaffen, auf dessen Grundlage wir weiter arbeiten. Fahren wir mit der Anwendungsentwicklung fort. Zunächst werden wir die Hauptgruppen von Elementen definieren, die für die Implementierung dieses Anwendungsteils erforderlich sind:
- Das Anwendungsfenster.
- Schnelle Auswahl der Symbole.
- Eingabefeld der Gruppe.
- Schaltflächen zum Speichern und Laden von Symbolgruppen.
- Die vollständige Liste aller verfügbaren Symbole als Kontrollkästchen mit einer Textbeschriftung für den Symbolnamen.
- Die Schaltfläche Next (Weiter) für den Wechsel zum zweiten Einrichtungsschritt: Auswahl des Zeitrahmens.
Die zuvor erstellte Dateistruktur sollte wie folgt aussehen:
Abb. 1 Struktur der Anwendungsdatei.
Wir öffnen zunächst die Anwendungsdatei SignalMonitor.mq5 und geben die Eingabeparameter ein. Sie werden die Parameter einstellen können, wenn Sie die Anwendung direkt im MetaTrader 5 Terminal ausführen. Wir deklarieren auch die Instanz der zuvor erstellten Klasse CProgramm und initialisieren einige der Variablen. Bearbeiten wir die Datei wie folgt:
//+------------------------------------------------------------------+ //| SignalMonitor.mq5 | //| Copyright 2019, Alexander Fedosov | //| https://www.mql5.com/en/users/alex2356 | //+------------------------------------------------------------------+ #property copyright "Copyright 2019, 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; // Basic font input color Caption = C'0,130,225'; // Caption color input color Background = clrWhiteSmoke; // Background color //--- CProgram program; ulong tick_counter; //+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit(void) { //--- tick_counter=GetTickCount(); //--- Initialize class variables program.OnInitEvent(); program.m_base_font_size=Inp_BaseFont; program.m_background=Background; program.m_caption=Caption; //--- Set up the trading panel if(!program.CreateGUI()) { Print(__FUNCTION__," > Failed to create graphical interface!"); return(INIT_FAILED); } //--- Initialization completed successfully return(INIT_SUCCEEDED); } //+------------------------------------------------------------------+ //| Expert deinitialization function | //+------------------------------------------------------------------+ void OnDeinit(const int reason) { program.OnDeinitEvent(reason); } //+------------------------------------------------------------------+ //| 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"); } } //+------------------------------------------------------------------+
Wie aus dem Code ersichtlich ist, wurden drei Eingabeparameter hinzugefügt:
- Schriftgröße.
- Die Farbe der Kopfzeile für die Anwendungsfenster.
- Die Hintergrundfarbe für die Anwendungsfenster und -elemente.
Als Nächstes deklarieren wir die Klasseninstanz CProgramm mit dem Namen Programm und die Variable tick_counter (sie wird nur für die Anzeige von Informationen über die Startzeit der Anwendung benötigt). Außerdem initialisieren wir in der Methode OnInit() die Klasseninstanzvariablen, indem wir ihnen die Werte der Eingabeparameter der Anwendung zuweisen. Wir rufen auch die grundlegende Methode CreateGUI() auf, die die Anwendung startet.
Wenn wir jedoch versuchen, die geöffnete Datei jetzt zu kompilieren, erhalten wir Kompilierungsfehler, die besagen, dass die Variablen m_base_font_size, m_background, m_caption und die Methode CreateGUI() in der Klasse CProgram nicht gefunden wurden. Wir öffnen daher die Datei Program.mqh, um die Klasse CProgram zu implementieren. Wir fügen zunächst die oben genannten Variablen und die Methode sowie andere Methoden hinzu, die für die korrekte Inbetriebnahme der Anwendung erforderlich sind. Die Datei CProgram wird so aussehen, nachdem wir die erforderlichen Elemente hinzugefügt haben:
//+------------------------------------------------------------------+ //| Class for creating an application | //+------------------------------------------------------------------+ class CProgram : public CWndEvents { public: //--- int m_base_font_size; //--- string m_base_font; //--- color m_background; color m_caption; public: CProgram(void); ~CProgram(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); //--- Create the graphical interface of the program bool CreateGUI(void); };
Die Implementierung der Methode, die die Schnittstelle erzeugt, ist noch leer:
//+------------------------------------------------------------------+ //| Creates the graphical interface of the program | //+------------------------------------------------------------------+ bool CProgram::CreateGUI(void) { //--- //--- Finish the creation of GUI CWndEvents::CompletedGUI(); return(true); } //+------------------------------------------------------------------+
Beachten Sie, dass wir auch die String-Variable m_base_font hinzugefügt haben, die für den Schriftnamen in der Anwendung verantwortlich ist. Sie wird in unserem Klassenkonstruktor initialisiert:
//+------------------------------------------------------------------+ //| Constructor | //+------------------------------------------------------------------+ CProgram::CProgram(void) { m_base_font="Trebuchet MS"; }
Nun wollen wir mit der Erstellung des ersten Fensters der Anwendung fortfahren. Zu diesem Zweck deklarieren Sie in der Klasse die neue Variable m_step_window, die eine Instanz der Klasse CWindow ist. Deklarieren wir auch eine Methode, die das erste Fenster erzeugt, und nennen sie CreateStepWindow(). So sieht der Code in der Klasse aus:
class CProgram : public CWndEvents { public: //--- Application windows CWindow m_step_window; ... protected: //--- forms bool CreateStepWindow(const string caption_text);
Wir haben bereits früher festgelegt, dass sich die Implementierung des Schnittstellenteils, der für die schrittweise Konfiguration des ersten Starts verantwortlich ist, in der Include-Datei StepWindow.mqh befindet. Öffnen wir sie also und beginnen mit der Implementierung der Methode CreateStepWindow():
#include "Program.mqh" //+------------------------------------------------------------------+ //| Creates a form for the selection of symbols | //+------------------------------------------------------------------+ bool CProgram::CreateStepWindow(const string text) { //--- Add the pointer to the window array CWndContainer::AddWindow(m_step_window); //--- Properties m_step_window.XSize(600); m_step_window.YSize(200); //--- Coordinates int x=int(ChartGetInteger(m_chart_id,CHART_WIDTH_IN_PIXELS)-m_step_window.XSize())/2; int y=10; m_step_window.CaptionHeight(22); m_step_window.IsMovable(true); m_step_window.CaptionColor(m_caption); m_step_window.CaptionColorLocked(m_caption); m_step_window.CaptionColorHover(m_caption); m_step_window.BackColor(m_background); m_step_window.FontSize(m_base_font_size); m_step_window.Font(m_base_font); //--- Creating a form if(!m_step_window.CreateWindow(m_chart_id,m_subwin,text,x,y)) return(false); //--- return(true); } //+------------------------------------------------------------------+
Vergessen wir nicht, Folgendes in der Methode CreateGUI() hinzuzufügen:
//+------------------------------------------------------------------+ //| Creates the graphical interface of the program | //+------------------------------------------------------------------+ bool CProgram::CreateGUI(void) { //--- Step 1-3 if(!CreateStepWindow("Signal Monitor Step 1: Choose Symbols")) return(false); //--- Finish the creation of GUI CWndEvents::CompletedGUI(); return(true); } //+------------------------------------------------------------------+
Wenn die Reihenfolge der Aktionen korrekt ist, sehen Sie das neu erstellte Formular, nachdem Sie die Datei SignalMonitor.mq5 kompiliert und im Terminal gestartet haben:
Abb. 2 Das erste Fenster der Anwendung
Zu den ersten Elementen des erstellten Fensters gehört eine Gruppe von Schaltflächen, die die schnelle Auswahl vordefinierter Symbolsätze aus dem Terminal ermöglichen: forex.all, forex.crosses, forex.major. In der Datei Program.mqh fügen wir ein Array der Klasseninstanzen von CButton mit der drei Dimensionen, sowie die universelle Methode CreateSymbolSet() zur Erstellen von Schaltflächen hinzu:
//+------------------------------------------------------------------+ //| Class for creating an application | //+------------------------------------------------------------------+ class CProgram : public CWndEvents { public: //--- Application windows CWindow m_step_window; //--- Simple buttons CButton m_currency_set[3]; ... //--- Buttons bool CreateSymbolSet(CButton &button,string text,const int x_gap,const int y_gap);
Öffnen wir nun die Datei StepWindow.mqh und fügen die obige Methodenimplementierung hinzu.
//+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ bool CProgram::CreateSymbolSet(CButton &button,string text,const int x_gap,const int y_gap) { //--- color baseclr=C'220,225,235'; color pressed=C'55,160,250'; //--- Save the window pointer button.MainPointer(m_step_window); //--- Set properties before creation button.TwoState(true); button.XSize(80); button.YSize(30); button.LabelXGap(19); button.LabelYGap(2); button.Font(m_base_font); button.FontSize(m_base_font_size); button.BackColor(baseclr); button.BackColorHover(baseclr); button.BackColorPressed(pressed); button.BorderColor(baseclr); button.BorderColorHover(baseclr); button.BorderColorPressed(pressed); button.LabelColor(clrBlack); button.LabelColorPressed(clrWhite); button.IsCenterText(true); //--- Create a control if(!button.CreateButton(text,x_gap,y_gap)) return(false); //--- Add a pointer to element to the base CWndContainer::AddToElementsArray(0,button); return(true); } //+------------------------------------------------------------------+
Jetzt brauchen wir nur noch drei Schaltflächen mit unterschiedlichen Koordinatenwerten und Textbeschriftungen mit dieser Methode in der Grundmethode CreateStepWindow() des Fensters nach dem Erstellen des Formulars hinzuzufügen:
... //--- Creating a form if(!m_step_window.CreateWindow(m_chart_id,m_subwin,text,x,y)) return(false); //--- if(!CreateSymbolSet(m_currency_set[0],"ALL",10,30)) return(false); if(!CreateSymbolSet(m_currency_set[1],"Major",10+100,30)) return(false); if(!CreateSymbolSet(m_currency_set[2],"Crosses",10+2*(100),30)) return(false); ...
Nach dem Kompilieren sieht das Ergebnis wie folgt aus:
Abb. 3 Hinzufügen von Schaltflächen zur Schnellauswahl von Symbolgruppen.
Als Nächstes fügen wir ein Eingabefeld für den Namen der ausgewählten Symbolgruppe hinzu, das mit zwei Schaltflächen gespeichert und geladen werden kann: Speichern und Laden. Dafür fügen wir eine Klasseninstanz für die Erstellung des Eingabefeldes CTextEdit und zwei weitere Klasseninstanzen für die Erstellung von Schaltflächen CButton hinzu. Da sich die Schaltflächen zum Speichern und Laden nur in ihren Namen unterscheiden, erzeugen Sie die allgemeine Methode CreateButton1(), und fügen sie dem Eingabefeld CreateEditValue() zur Klasse CProgram: hinzu.
//+------------------------------------------------------------------+ //| Class for creating an application | //+------------------------------------------------------------------+ class CProgram : public CWndEvents { public: //--- Application windows CWindow m_step_window; //--- Simple buttons CButton m_currency_set[3]; CButton m_load_button; CButton m_save_button; //--- Input fields CTextEdit m_text_edit; ... bool CreateButton1(CButton &button,string text,const int x_gap,const int y_gap); //--- Input field bool CreateEditValue(CTextEdit &text_edit,const int x_gap,const int y_gap);
Gehen wir zurück zur Datei StepWindow.mqh und fügen die Implementierung der erstellten Methoden am Ende der Datei hinzu.
//+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ bool CProgram::CreateEditValue(CTextEdit &text_edit,const int x_gap,const int y_gap) { //--- Store the pointer to the main control text_edit.MainPointer(m_step_window); //--- Properties text_edit.XSize(110); text_edit.YSize(24); text_edit.Font(m_base_font); text_edit.FontSize(m_base_font_size); text_edit.GetTextBoxPointer().XGap(1); text_edit.GetTextBoxPointer().XSize(110); text_edit.GetTextBoxPointer().DefaultTextColor(clrSilver); text_edit.GetTextBoxPointer().DefaultText("Template name"); //--- Create a control if(!text_edit.CreateTextEdit("",x_gap,y_gap)) return(false); //--- Add an object to the common array of object groups CWndContainer::AddToElementsArray(0,text_edit); return(true); } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ bool CProgram::CreateButton1(CButton &button,string text,const int x_gap,const int y_gap) { //--- color baseclr=C'70,180,70'; color pressed=C'70,170,70'; //--- Save the window pointer button.MainPointer(m_step_window); //--- Set properties before creation button.XSize(80); button.YSize(30); button.Font(m_base_font); button.FontSize(m_base_font_size); button.BackColor(baseclr); button.BackColorHover(baseclr); button.BackColorPressed(pressed); button.BorderColor(baseclr); button.BorderColorHover(baseclr); button.BorderColorPressed(pressed); button.LabelColor(clrWhite); button.LabelColorPressed(clrWhite); button.LabelColorHover(clrWhite); button.IsCenterText(true); //--- Create a control if(!button.CreateButton(text,x_gap,y_gap)) return(false); //--- Add a pointer to element to the base CWndContainer::AddToElementsArray(0,button); return(true); }
Gehen wir dann zurück zu der Klasse CreateStepWindow(), um zwei Schaltflächen und ein Eingabefeld im Anwendungsfenster hinzuzufügen.
//--- if(!CreateEditValue(m_text_edit,300,m_step_window.CaptionHeight()+10)) return(false); //--- if(!CreateButton1(m_load_button,"Load(L)",m_step_window.XSize()-2*(80+10),m_step_window.CaptionHeight()+10)) return(false); if(!CreateButton1(m_save_button,"Save(S)",m_step_window.XSize()-(80+10),m_step_window.CaptionHeight()+10)) return(false);
Wieder kompilieren wir die Datei SignalMonitor.mq5. Hier ist das Ergebnis:
Abb.4 Das hinzugefügte Eingabefeld für Symbolgruppen und die Schaltflächen Save/Load (Sichern/Laden).
Kommen wir nun zur Visualisierung und der Möglichkeit, alle Symbole auszuwählen, die für das ausgewählte Konto im MetaTrader 5-Terminal zur Verfügung stehen. Beachten Sie, dass die Höhe des Anwendungsfensters nicht ausreicht, um alle verfügbaren Symbole anzuzeigen. Eine gute Lösung ist es, die automatische Anpassung der Fensterhöhe in Abhängigkeit von den Daten zu ermöglichen. Das Hinzufügen aller Symbole ist ähnlich: Wir fügen eine Reihe von Klasseninstanzen für die Erstellung von Kontrollkästchen CCheckBox und universelle Methoden für deren Erstellung hinzu (da sie sich nur im Namen unterscheiden werden).
... //--- Checkboxes CCheckBox m_checkbox[]; ... //--- Checkboxes bool CreateCheckBox(CCheckBox &checkbox,const int x_gap,const int y_gap,const string text);
Die Dimension des Arrays m_checkbox[] wird nicht angegeben, da im Voraus nicht bekannt ist, wie viele Symbole auf dem ausgewählten Konto im Terminal vorhanden sind. Lassen Sie uns deshalb zwei Variablen im 'private' Teil der Klasse Programm erstellen und ihnen die Gesamtzahl der verfügbaren Symbole und die Anzahl der aktuell ausgewählten Symbole in Market Watch zuweisen.
private: //--- int m_symbol_total; int m_all_symbols;
Im Klassenkonstruktor weisen wir die erforderlichen Werte zu und setzen die entsprechende Dimension auf das Array m_checkbox[]:
//+------------------------------------------------------------------+ //| Constructor | //+------------------------------------------------------------------+ CProgram::CProgram(void) { m_base_font="Trebuchet MS"; m_symbol_total=SymbolsTotal(true); m_all_symbols=SymbolsTotal(false); ArrayResize(m_checkbox,m_all_symbols); }
Fügen wir diese Methodenimplementierung am Ende der Datei StepWindow.mqh hinzu:
//+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ bool CProgram::CreateCheckBox(CCheckBox &checkbox,const int x_gap,const int y_gap,const string text) { //--- Store the pointer to the main control checkbox.MainPointer(m_step_window); //--- Properties checkbox.GreenCheckBox(true); checkbox.IsPressed(false); checkbox.Font(m_base_font); checkbox.FontSize(m_base_font_size); checkbox.BackColor(m_background); checkbox.LabelColorHover(C'55,160,250'); //--- Create a control if(!checkbox.CreateCheckBox(text,x_gap,y_gap)) return(false); //--- Add a pointer to element to the base CWndContainer::AddToElementsArray(0,checkbox); return(true); }
Fügen wir auch noch Kontrollkästchen in der Methode CreateStepWindow() hinzu. Im untenstehenden Code hat die gesamte Liste der verfügbaren Symbole 7 Spalten. Zusätzlich wird die Fensterhöhe entsprechend der Anzahl der empfangenen Zeilen geändert.
//--- Checkboxes int k=0; for(int j=0; j<=MathCeil(m_all_symbols/7); j++) { for(int i=0; i<7; i++) { if(k<m_all_symbols) if(!CreateCheckBox(m_checkbox[k],10+80*i,m_step_window.CaptionHeight()+70+j*25,SymbolName(k,false))) return(false); k++; } } m_step_window.ChangeWindowHeight(m_checkbox[m_all_symbols-1].YGap()+30+30);
Kopieren Sie die resultierenden Ergänzungen:
Abb.5 Die Kontrollkästchen für alle verfügbaren Symbolen.
Das letzte Element dieses Anwendungsteils enthält Navigationsschaltflächen zum Umschalten zwischen den Einrichtungsschritten. Sie können leicht hinzugefügt werden: Fügen Sie zwei Instanzen der Klasse CButton mit den Namen m_next_button und m_back_button hinzu, verwenden Sie die Erstellungsmethode aus dem zuvor erstellten CreateButton1(). Fügen wir in der Erstellungsmethode des Fensters CreateStepWindow() Folgendes hinzu:
//--- if(!CreateButton1(m_back_button,"Back",m_step_window.XSize()-2*(80+10),m_step_window.YSize()-(30+10))) return(false); if(!CreateButton1(m_next_button,"Next",m_step_window.XSize()-(80+10),m_step_window.YSize()-(30+10))) return(false);
Jetzt müssen wir nur noch die Bedienung der Schaltflächen konfigurieren, mit denen vordefinierte Symbolsätze ausgewählt werden können. Öffnen wir die Datei Program.mqh und suchen die Methode OnEvent() und fügen den folgenden Code hinzu:
//+------------------------------------------------------------------+ //| Chart event handler | //+------------------------------------------------------------------+ void CProgram::OnEvent(const int id,const long &lparam,const double &dparam,const string &sparam) { //--- Pressing the button event if(id==CHARTEVENT_CUSTOM+ON_CLICK_BUTTON) { //--- All if(lparam==m_currency_set[0].Id() && m_currency_set[0].IsPressed()) { m_currency_set[1].IsPressed(false); m_currency_set[2].IsPressed(false); m_currency_set[1].Update(true); m_currency_set[2].Update(true); //--- for(int i=0; i<m_all_symbols; i++) { m_checkbox[i].IsPressed(true); m_checkbox[i].Update(true); } } //--- Majors else if(lparam==m_currency_set[1].Id() && m_currency_set[1].IsPressed()) { m_currency_set[0].IsPressed(false); m_currency_set[2].IsPressed(false); m_currency_set[0].Update(true); m_currency_set[2].Update(true); //--- string pairs[4]= {"EURUSD","GBPUSD","USDCHF","USDJPY"}; //--- Clear the selection for(int i=0; i<m_all_symbols; i++) { m_checkbox[i].IsPressed(false); m_checkbox[i].Update(true); } //--- for(int i=0; i<m_all_symbols; i++) { for(int j=0; j<4; j++) if(m_checkbox[i].LabelText()==pairs[j]) { m_checkbox[i].IsPressed(true); m_checkbox[i].Update(true); } } } //--- Crosses else if(lparam==m_currency_set[2].Id() && m_currency_set[2].IsPressed()) { m_currency_set[0].IsPressed(false); m_currency_set[1].IsPressed(false); m_currency_set[0].Update(true); m_currency_set[1].Update(true); //--- string pairs[20]= { "EURUSD","GBPUSD","USDCHF","USDJPY","USDCAD","AUDUSD","AUDNZD","AUDCAD","AUDCHF","AUDJPY", "CHFJPY","EURGBP","EURAUD","EURCHF","EURJPY","EURCAD","EURNZD","GBPCHF","GBPJPY","CADCHF" }; //--- Clear the selection for(int i=0; i<m_all_symbols; i++) { m_checkbox[i].IsPressed(false); m_checkbox[i].Update(true); } //--- for(int i=0; i<m_all_symbols; i++) { for(int j=0; j<20; j++) if(m_checkbox[i].LabelText()==pairs[j]) { m_checkbox[i].IsPressed(true); m_checkbox[i].Update(true); } } } //--- if((lparam==m_currency_set[0].Id() && !m_currency_set[0].IsPressed()) || (lparam==m_currency_set[1].Id() && !m_currency_set[1].IsPressed()) || (lparam==m_currency_set[2].Id() && !m_currency_set[2].IsPressed()) ) { //--- Clear the selection for(int i=0; i<m_all_symbols; i++) { m_checkbox[i].IsPressed(false); m_checkbox[i].Update(true); } } } }
Die Idee dieser Umsetzung ist wie folgt:
- Ein Klick auf ALL wählt alle Symbole aus.
- Ein Klick auf Major entfernt die vorherige Auswahl und setzt einen Satz von Symbolen, der forex.major in Terminals entspricht.
- Ein Klick auf Crosses entfernt die vorherige Auswahl und setzt eine Reihe von Symbolen, die crosses.major in Terminals entsprechen.
- Wenn alle drei Knöpfe nicht gedrückt sind, wird jede Auswahl aufgehoben.
Und so sieht das Ganze aus:
Abb.6 Implementierung der grundlegenden Interaktion der Elemente.
Zwei kleine Ergänzungen sind erforderlich, um die visuelle Implementierung zu vervollständigen. In Abbildung 5 sehen wir, dass das Fenster die zuvor erstellte Schaltfläche Back (Zurück) enthält. Dies ist jedoch Schritt 1, so dass es eine solche Schaltfläche nicht geben sollte. Sie sollte ausgeblendet und nur in den Schritten 2 und 3 angezeigt werden. Fügen wir die folgende Zeile der Methode CreateGUI() hinzu:
bool CProgram::CreateGUI(void) { //--- Step 1-3 if(!CreateStepWindow("Signal Monitor Step 1: Choose Symbols")) return(false); //--- Finish the creation of GUI CWndEvents::CompletedGUI(); m_back_button.Hide(); return(true); }
Außerdem müssen wir die Auswahl des Nutzers überwachen. Der Wechsel zu Schritt 2 sollte nicht erlaubt sein, wenn der Nutzer nicht mindestens ein Symbol ausgewählt hat. Das Umschalten zwischen den Schritten wird mit den Schaltflächen Back (Zurück) und Next (Weiter) durchgeführt. Um die Aufgabe zu lösen, fügen wir also drei neue Methoden zum 'private' Abschnitt der Klasse Programm hinzu. Die Methoden verarbeiten die in jedem der drei Schritte ausgewählten Informationen und führen so die anfängliche Einrichtung der Anwendung durch. Wir fügen auch die Variable m_current_step hinzu: Wenn Back/Next angeklickt wird, weiß die Anwendung, in welchem Schritt wir uns gerade befinden.
private: //--- int m_symbol_total; int m_all_symbols; int m_current_step; //--- void ToStep_1(void); void ToStep_2(void); void ToStep_3(void);
Danach setzen wir im Klassenkonstruktor den Wert des ersten Schrittes für die erzeugte Variable, d.h. 1. Um die Navigation zwischen den drei Konfigurationsschritten einzurichten, fügen wir den untenstehenden Code in das Ereignis eines Schaltflächenklicks in OnEvent() ein:
//--- Navigation if(lparam==m_back_button.Id()) { //--- Return to Step 1 if(m_current_step==2) ToStep_1(); //--- Return to Step 2 else if(m_current_step==3) ToStep_2(); } //--- Go to Step 2 if(lparam==m_next_button.Id()) { //--- Go to Step 2 if(m_current_step==1) ToStep_2(); //--- Go to Step 3 else if(m_current_step==2) ToStep_3(); }
Wenn wir versuchen, das Projekt so zu kompilieren, gibt der Compiler den Fehler zurück, dass die drei verwendeten Methoden zwar erstellt wurden, sie aber keine Implementierung haben:
die Funktion 'CProgramm::ToStep_1' muss einen Körper haben Programm.mqh 60 22
Um dies zu beheben, implementieren wir diese Klassen in der Datei Program.mqh. Lassen wir sie jedoch für die Methoden ToStep_1() und ToStep_3() vorerst leer. Sie werden später gefüllt. Nun interessieren wir uns für den Methodenwechsel zum zweiten Schritt ToStep_2(). Fügen wir eine Prüfung hinzu, ob mindestens ein Symbol ausgewählt ist:
//+------------------------------------------------------------------+ //| Go to Step 1 | //+------------------------------------------------------------------+ void CProgram::ToStep_1(void) { //--- } //+------------------------------------------------------------------+ //| Go to Step 2 | //+------------------------------------------------------------------+ void CProgram::ToStep_2(void) { //--- Check whether at least one symbol is selected int cnt=0; for(int i=0; i<m_all_symbols; i++) { if(m_checkbox[i].IsPressed()) cnt++; } if(cnt<1) { MessageBox("No symbols selected!","Warning"); return; } } //+------------------------------------------------------------------+ //| Move to Step 3 3 | //+------------------------------------------------------------------+ void CProgram::ToStep_3(void) { //--- }
Wenn der Nutzer versehentlich Next drückt, ohne ein Symbol auszuwählen, wird eine Warnung angezeigt, dass mindestens ein Symbol ausgewählt werden sollte.
Setup-Schritt 2: Zeitrahmen
Im zweiten Schritt der Anwendungseinrichtung sollte der Nutzer Zeitrahmen auswählen, zu denen Handelssignale gesucht werden sollen. Die erforderlichen UI-Elemente haben wir bereits im ersten Artikel erwähnt:
- Eine Gruppe von Schaltflächen zur schnellen Auswahl von Zeitrahmen.
- Eine Liste von Zeitrahmen in Form von Ankreuzfeldern.
- Die Schaltfläche Back, um zu Schritt 1 zurückzukehren.
Lassen Sie uns vorhandene Objekte aus der visuellen Umsetzung von Schritt 1 verwenden und diese für die Auswahl der Zeitrahmen anpassen. Gehen wir zum Hauptteil der Methode ToStep_2(), die wir vor kurzem bearbeitet haben, und fügen ihr zusätzliche Funktionen hinzu. Erinnern wir uns zunächst an die Auswahl der Symbole in Schritt 1 und zeigen sie in Market Watch im MetaTrader 5:
//--- Set selected symbols in Market Watch for(int i=0; i<m_all_symbols; i++) { if(m_checkbox[i].IsPressed()) SymbolSelect(m_checkbox[i].LabelText(),true); else SymbolSelect(m_checkbox[i].LabelText(),false); }
Dann transformieren wir die Schnittstelle von Schritt 1 in die zweite:
//--- Change header m_step_window.LabelText("Signal Monitor Step 2: Choose Timeframes"); m_step_window.Update(true); //--- Hide elements of Step 1 for(int i=0; i<m_all_symbols; i++) { m_checkbox[i].IsLocked(false); m_checkbox[i].IsPressed(false); m_checkbox[i].Hide(); } string names[3]= {"All","Junior","Senior"}; //--- Change names of selection buttons for(int i=0; i<3; i++) { m_currency_set[i].LabelText(names[i]); m_currency_set[i].IsPressed(false); if(m_current_step==3) m_currency_set[i].Show(); m_currency_set[i].Update(true); } //--- Hide block for working with templates m_text_edit.Hide(); m_load_button.Hide(); m_save_button.Hide(); //--- Show all timeframes string timeframe_names[21]= { "M1","M2","M3","M4","M5","M6","M10","M12","M15","M20","M30", "H1","H2","H3","H4","H6","H8","H12","D1","W1","MN" }; for(int i=0; i<21; i++) { m_checkbox[i].LabelText(timeframe_names[i]); m_checkbox[i].Show(); m_checkbox[i].Update(true); } //--- Show Back button m_back_button.Show(); //--- m_current_step=2;
Die Implementierung ist recht einfach. Am Ende weisen wir der Variablen m_current_step den Wert 2 zu (Schritt 2). Jetzt müssen wir die korrekte Anzeige der ausgewählten Zeitrahmensätze All, Junior, Senior durch die geänderte Schnittstelle bereitstellen. Öffnen wir Programm.mqh und ändern den Code in der Methode OnEvent(). Die Änderung ist im Ereignisabschnitt "button click" erforderlich. Aus der Sicht des Objekts sind die Schnellauswahltasten in Schritt 1 und 2 ähnlich. Deshalb ist es notwendig, den aktuellen Einrichtungsschritt im Ereignis "button click" zu definieren:
//--- Step 1 if(m_current_step==1) { ... } //--- Step 2 else if(m_current_step==2) { //--- All if(lparam==m_currency_set[0].Id() && m_currency_set[0].IsPressed()) { m_currency_set[1].IsPressed(false); m_currency_set[2].IsPressed(false); m_currency_set[1].Update(true); m_currency_set[2].Update(true); //--- for(int i=0; i<21; i++) { m_checkbox[i].IsPressed(true); m_checkbox[i].Update(true); } } //--- Junior Timeframes else if(lparam==m_currency_set[1].Id() && m_currency_set[1].IsPressed()) { m_currency_set[0].IsPressed(false); m_currency_set[2].IsPressed(false); m_currency_set[0].Update(true); m_currency_set[2].Update(true); //--- string pairs[11]= { "M1","M2","M3","M4","M5","M6","M10","M12","M15","M20","M30" }; //--- Clear the selection for(int i=0; i<21; i++) { m_checkbox[i].IsPressed(false); m_checkbox[i].Update(true); } //--- for(int i=0; i<21; i++) { for(int j=0; j<11; j++) if(m_checkbox[i].LabelText()==pairs[j]) { m_checkbox[i].IsPressed(true); m_checkbox[i].Update(true); } } } //--- Senior Timeframes else if(lparam==m_currency_set[2].Id() && m_currency_set[2].IsPressed()) { m_currency_set[0].IsPressed(false); m_currency_set[1].IsPressed(false); m_currency_set[0].Update(true); m_currency_set[1].Update(true); //--- string pairs[10]= { "H1","H2","H3","H4","H6","H8","H12","D1","W1","MN" }; //--- Clear the selection for(int i=0; i<m_all_symbols; i++) { m_checkbox[i].IsPressed(false); m_checkbox[i].Update(true); } //--- for(int i=0; i<m_all_symbols; i++) { for(int j=0; j<10; j++) if(m_checkbox[i].LabelText()==pairs[j]) { m_checkbox[i].IsPressed(true); m_checkbox[i].Update(true); } } }
Das letzte UI-Element, das im zweiten Setup-Schritt implementiert wird, ist die Schaltfläche Back, die zu Schritt 1 zurückkehrt. Dies geschieht durch den angelegten, aber noch leeren Schritt ToStep_1(). Kehren wir zur vorherigen Schnittstelle zurück und setzen den vorherigen Wrapper für die Verarbeitung der Klick-Ereignisse der Auswahltaste.
//+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ void CProgram::ToStep_1(void) { //--- Change header m_step_window.LabelText("Signal Monitor Step 1: Choose Symbols"); m_step_window.Update(true); //--- Hide the Back button m_back_button.Hide(); //--- Clear selection for(int i=0; i<21; i++) { m_checkbox[i].IsPressed(false); m_checkbox[i].Update(true); } //--- Show elements of Step 1 for(int i=0; i<m_all_symbols; i++) { m_checkbox[i].Show(); m_checkbox[i].LabelText(SymbolName(i,false)); m_checkbox[i].Update(true); } string names[3]= {"All","Majors","Crosses"}; //--- Change names of selection buttons for(int i=0; i<3; i++) { m_currency_set[i].IsPressed(false); m_currency_set[i].LabelText(names[i]); m_currency_set[i].Update(true); } //--- Show block for working with templates m_text_edit.Show(); m_load_button.Show(); m_save_button.Show(); //--- Set the current setup step m_current_step=1; }
Kompilieren wir nun das Projekt. Wenn alles korrekt hinzugefügt wurde, wird das Ergebnis wie in Abb.7 aussehen.
Abb.7 Implementierung von Schritt 2 der Anwendung.
Setup-Schritt 3: Signale hinzufügen
Der nächste Schritt ist Schritt 3: Das Interface, um Signale hinzufügen. Es ist recht einfach und besteht aus einer Schaltfläche zum Hinzufügen von Signalen und einer Kopfzeile für die Liste der hinzugefügten Signale. Öffnen wir Programm.mqh und deklarieren zwei neue Variablen in СProgram:
CButton m_add_signal;
//---
CTextLabel m_signal_header;
Methoden, die Variablen zu implementieren:
bool CreateIconButton(CButton &button,string text,const int x_gap,const int y_gap); //--- Text label bool CreateLabel(CTextLabel &text_label,const int x_gap,const int y_gap,string label_text);
Fügen wir ihre Implementierung am Ende der Datei StepWindow.mqh hinzu.
//+------------------------------------------------------------------+ //| Creates a button with an image | //+------------------------------------------------------------------+ #resource "\\Images\\EasyAndFastGUI\\Icons\\bmp16\\plus.bmp" bool CProgram::CreateIconButton(CButton &button,string text,const int x_gap,const int y_gap) { //--- color baseclr=C'70,180,70'; color pressed=C'70,170,70'; //--- Save the window pointer button.MainPointer(m_step_window); //--- Set properties before creation button.XSize(110); button.YSize(30); button.Font(m_base_font); button.FontSize(m_base_font_size); button.IconXGap(3); button.IconYGap(7); button.IconFile("Images\\EasyAndFastGUI\\Icons\\bmp16\\plus.bmp"); button.BackColor(baseclr); button.BackColorHover(baseclr); button.BackColorPressed(pressed); button.BorderColor(baseclr); button.BorderColorHover(baseclr); button.BorderColorPressed(pressed); button.LabelColor(clrWhite); button.LabelColorPressed(clrWhite); button.LabelColorHover(clrWhite); button.IsCenterText(true); //--- Create a control if(!button.CreateButton(text,x_gap,y_gap)) return(false); //--- Add a pointer to element to the base CWndContainer::AddToElementsArray(0,button); return(true); } //+------------------------------------------------------------------+ //| Creates the text label | //+------------------------------------------------------------------+ bool CProgram::CreateLabel(CTextLabel &text_label,const int x_gap,const int y_gap,string label_text) { //--- Save the window pointer text_label.MainPointer(m_step_window); //--- text_label.Font(m_base_font); text_label.FontSize(m_base_font_size); text_label.XSize(120); text_label.BackColor(m_background); //--- Create the button if(!text_label.CreateTextLabel(label_text,x_gap,y_gap)) return(false); //--- Add a pointer to element to the base CWndContainer::AddToElementsArray(0,text_label); return(true); } //+------------------------------------------------------------------+
Fügen wir die folgenden Elemente in das Fenster CreateStepWindow() ein, damit sie beim Start der Anwendung erstellt werden können.
//--- if(!CreateIconButton(m_add_signal,"Add Signal",10,30)) return(false); if(!CreateLabel(m_signal_header,10,30+30+10,"Signal List")) return(false);
Um nun ihre Anzeige beim Start, d.h. im ersten Schritt, unmittelbar nach der Erzeugung der Schnittstelle durch den Aufruf von CreateGUI() zu deaktivieren, fügen wir zwei Zeilen hinzu, die die Elemente vom Ende der Methode verbergen.
//+------------------------------------------------------------------+ //| Creates the graphical interface of the program | //+------------------------------------------------------------------+ bool CProgram::CreateGUI(void) { //--- Step 1-3 if(!CreateStepWindow("Signal Monitor Step 1: Choose Symbols")) return(false); //--- Finish the creation of GUI CWndEvents::CompletedGUI(); m_back_button.Hide(); m_add_signal.Hide(); m_signal_header.Hide(); return(true); }
Implementieren wir nun die zuvor hinzugefügte Methode ToStep_3(), die die Visualisierung im vorherigen Schritt löscht und die von uns erstellten Elemente anzeigt:
//+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ void CProgram::ToStep_3(void) { //--- Check whether at least one timeframe is selected int cnt=0; for(int i=0; i<21; i++) { if(m_checkbox[i].IsPressed()) cnt++; } if(cnt<1) { MessageBox("No timeframes selected!","Warning"); return; } //--- m_step_window.LabelText("Signal Monitor Step 3: Create Signals"); m_step_window.Update(true); m_next_button.LabelText("Create"); m_next_button.Update(true); //--- Hide elements of Step 2 for(int i=0; i<21; i++) { if(i<3) m_currency_set[i].Hide(); m_checkbox[i].Hide(); } //--- m_add_signal.Show(); m_signal_header.Show(); //--- m_current_step=3; }
Kompilieren wir das Projekt erneut und gehen durch einen Doppelklick auf die Schaltfläche Next zu Schritt 3. Vergessen wir nicht, in den ersten beiden Schritten Elemente auszuwählen, sonst lässt uns die Anwendung nicht zum dritten Schritt übergehen.
Abb.8 Implementierung von Schritt 3 der Anwendung.
Fenster zur Erstellung und Bearbeitung von Handelssignalen
Die visuelle Komponente, die mit der Arbeit mit Handelssignalen zusammenhängt, befindet sich in der Datei SetWindow.mqh, also öffnen wir sie. Jetzt hat sie nur noch die Include-Datei Program.mqh, die über #include eingebunden ist. Erstellen wir zunächst ein separates Fenster, das für alle anderen Erstellungs- und Einrichtungselemente grundlegend sein wird. Öffnen wir Programm.mqh und deklarieren in der Klasse die Variable m_set_window, die eine Instanz der Klasse CWindow ist. Fügen wir auch die Methode CreateSetWindow() zur Erzeugung des Fensters hinzu:
CWindow m_set_window; bool CreateSetWindow(const string caption_text);
Danach gehen wir zurück zu SetWindow.mqh und implementieren die erstellte Methode.
//+------------------------------------------------------------------+ //| Creates a window for creating and editing trading signals | //+------------------------------------------------------------------+ bool CProgram::CreateSetWindow(const string text) { //--- Add the pointer to the window array CWndContainer::AddWindow(m_set_window); //--- Properties m_set_window.XSize(568); m_set_window.YSize(555); //--- Coordinates int x=int(ChartGetInteger(m_chart_id,CHART_WIDTH_IN_PIXELS)-m_set_window.XSize())/2; int y=30; //--- m_set_window.CaptionHeight(22); m_set_window.IsMovable(true); m_set_window.CaptionColor(m_caption); m_set_window.CaptionColorLocked(m_caption); m_set_window.CaptionColorHover(m_caption); m_set_window.BackColor(m_background); m_set_window.FontSize(m_base_font_size); m_set_window.Font(m_base_font); m_set_window.WindowType(W_DIALOG); //--- Creating a form if(!m_set_window.CreateWindow(m_chart_id,m_subwin,text,x,y)) return(false); return(true); } //+------------------------------------------------------------------+
Binden wir nun das neu erstellte Fenster mit bereits verfügbaren Elementen. Zuerst fügen wir den Methodenaufruf im Menü für das Erstellen des Interfaces CreateGUI() hinzu. Das Fenster sollte sich bei einem Klick auf die Schaltfläche "Add Signal" in Schritt 3 öffnen.
//+------------------------------------------------------------------+ //| Creates the graphical interface of the program | //+------------------------------------------------------------------+ bool CProgram::CreateGUI(void) { //--- Step 1-3 if(!CreateStepWindow("Signal Monitor Step 1: Choose Symbols")) return(false); //--- Creation and editing window if(!CreateSetWindow("Signal Monitor Edit Signal")) return(false); //--- Finish the creation of GUI CWndEvents::CompletedGUI(); m_back_button.Hide(); m_add_signal.Hide(); m_signal_header.Hide(); return(true); }
Beim Klick-Ereignis in OnEvent():
//--- Click on the "Add Signal" button if(lparam==m_add_signal.Id()) { m_set_window.OpenWindow(); }
Kompilieren wir das Projekt und überprüfen das Ergebnis: Bei einem Klick auf "Add Signal" in Schritt 3 wird ein zusätzliches Erstellungs- und Bearbeitungsfenster geöffnet.
Abb. 9 Implementierung des Fensters zur Erstellung und Bearbeitung von Handelssignalen.
Das erste Fensterelement ist die Auswahl des Indikatortyps, der bei der Erzeugung eines Handelssignals verwendet werden soll. Das Verfahren zum Hinzufügen von Elementen ist das gleiche: Wir erstellen eine Klasseninstanz und eine Methode zur Implementierung der Instanz.
//--- Drop-down menu CComboBox m_indicator_type; //--- Creates a drop-down method bool CreateIndicatorType(const int x_gap,const int y_gap);
Die Methodenimplementierung befindet sich in derselben Datei, in der auch das zuvor erstellte Fenster erstellt wird.
//+------------------------------------------------------------------+ //| Creates a drop-down menu with indicator types | //+------------------------------------------------------------------+ bool CProgram::CreateIndicatorType(const int x_gap,const int y_gap) { //--- Pass the object to the panel m_indicator_type.MainPointer(m_set_window); //--- Array of the item values in the list view string pattern_names[7]= { "ATR","CCI","DeMarker","Force Ind","WPR","RSI","Momentum" }; //--- Set properties before creation m_indicator_type.XSize(200); m_indicator_type.YSize(26); m_indicator_type.LabelYGap(4); m_indicator_type.ItemsTotal(7); m_indicator_type.Font(m_base_font); m_indicator_type.FontSize(m_base_font_size); m_indicator_type.BackColor(m_background); m_indicator_type.GetButtonPointer().Font(m_base_font); m_indicator_type.GetButtonPointer().FontSize(m_base_font_size); m_indicator_type.GetButtonPointer().BackColor(clrWhite); m_indicator_type.GetButtonPointer().XGap(100); m_indicator_type.GetButtonPointer().XSize(100); m_indicator_type.GetListViewPointer().Font(m_base_font); m_indicator_type.GetListViewPointer().FontSize(m_base_font_size); m_indicator_type.GetListViewPointer().ItemYSize(26); //--- Store the item values in the combo box list view for(int i=0; i<7; i++) m_indicator_type.SetValue(i,pattern_names[i]); //--- Get the list pointer CListView *lv=m_indicator_type.GetListViewPointer(); //--- Set the list view properties lv.LightsHover(true); m_indicator_type.SelectItem(5); //--- Create a control if(!m_indicator_type.CreateComboBox("Indicator Type",x_gap,y_gap)) return(false); //--- Add an object to the common array of object groups CWndContainer::AddToElementsArray(1,m_indicator_type); return(true); }
Der einzige Zusatz hier ist, dass wir am Ende der Methode CreateSetWindow() eine Methode zum Anlegen der Auswahlmöglichkeit für den Indikatortyp CreateIndicatorType() aufrufen.
... //--- Creating a form if(!m_set_window.CreateWindow(m_chart_id,m_subwin,text,x,y)) return(false); //--- Indicator type if(!CreateIndicatorType(10,22+10)) return(false);
Das Ergebnis ist das UI-Element, das die Auswahl aus 7 Standardindikatoren des Oszillatortyps ermöglicht.
Abb. 10 Element zur Auswahl des Indikatortyps.
Betrachten wir als Nächstes Gruppen von Elementen, die in zwei Abschnitten gruppiert sind: Indikatoreinstellungen und Signaleinstellungen. Alle ausgewählten Indikatoren aus dem Standardsatz haben gemeinsame Einstellungen, wie z.B. Periode und Angewandter Preis. Daher wird für den ersten Abschnitt Folgendes benötigt: eine Textkennzeichnung, ein Periodeneingabefeld und ein Dropdown-Menü zur Auswahl des für die Indikatorberechnung verwendeten Preises. Fügen wir die erforderliche Variable und ihre Erstellungsmethoden der Klasse Programm hinzu.
//--- Text label CTextLabel m_set_header[5]; //--- Input fields CTextEdit m_period_edit; //--- Drop-down menu CComboBox m_applied_price; ... bool CreateSetLabel(CTextLabel &text_label,const int x_gap,const int y_gap,string label_text); bool CreatePeriodEdit(const int x_gap,const int y_gap); bool CreateAppliedPrice(const int x_gap,const int y_gap);
Implementieren wir die hinzugefügten Methoden und rufen sie am Ende des Methodenrumpfes CreateSetWindow() auf. Fügen wir nun einen Mechanismus hinzu, durch den die erstellten Elemente den Satz der verfügbaren Einstellungen in Abhängigkeit vom gewählten Indikatortyp ändern. Fügen wir dazu in OnEvent() einen Abschnitt mit einem Ereignis des Anklickens des Dropdown-Menüpunkts hinzu und stellen den individuellen Satz von Einstellungen für jeden der Indikatoren ein:
//--- Item selection in the combobox list if(id==CHARTEVENT_CUSTOM+ON_CLICK_COMBOBOX_ITEM) { int index=m_indicator_type.GetListViewPointer().SelectedItemIndex(); switch(index) { case 0: m_period_edit.LabelText("ATR Period"); m_applied_price.Hide(); break; case 1: m_period_edit.LabelText("CCI Period"); m_applied_price.Show(); break; case 2: m_period_edit.LabelText("DeMarker Period"); m_applied_price.Hide(); break; case 3: m_period_edit.LabelText("Force Index Period"); m_applied_price.Show(); break; case 4: m_period_edit.LabelText("WPR Period"); m_applied_price.Hide(); break; case 5: m_period_edit.LabelText("RSI Period"); m_applied_price.Show(); break; case 6: m_period_edit.LabelText("Momentum Period"); m_applied_price.Hide(); break; default: m_period_edit.LabelText("RSI Period"); m_applied_price.Hide(); break; } m_period_edit.Update(true); }
Kompilieren wir das Projekt und sehen uns das Ergebnis an:
Abb. 11 Implementierung der Indikatoreneinstellungen.
Als Nächstes gehen wir zum zweiten Abschnitt der Signalbearbeitung über. Er besteht aus der Kopfzeile und acht Einstellungen:
- Signalregel.
- Textwert des Labels im Signalblock.
- Farbe des Textlabels.
- Hintergrundverwendung und -farbe.
- Kantenverwendung und -farbe.
- Verwendung, Farbe und Wert eines Tooltips über dem Signalblock.
- Die Verwendung von Grafik-Labels und deren Darstellung im Signalblock.
- Auswahl der verfügbaren Zeitrahmen für die Suche nach einem bestimmten Signal.
Um eine Kopfzeile für diesen Abschnitt hinzuzufügen, fügen wir den folgenden Code am Ende des Körpers CreateSetWindow() ein (wir haben zuvor eine Methode zur Visualisierung der Kopfzeile erstellt, die mit verschiedenen Argumentwerten wieder verwendet werden kann):
//--- Signal settings if(!CreateSetLabel(m_set_header[1],10,22+10+4*(25+10),"2.Signal Settings")) return(false);
Die Signalregel besteht aus zwei Elementen: einem Dropdown-Menü und einem Eingabefeld für einen numerischen Wert. Fügen wir die Klasseninstanzen und Implementierungsmethoden der Klasse CProgramm hinzu:
CTextEdit m_rule_value; CComboBox m_rule_type; ... bool CreateRuleValue(const int x_gap,const int y_gap); bool CreateRule(const int x_gap,const int y_gap);
Fügen wir ihre Implementierung zu SetWindow.mqh hinzu und rufen sie in der Methode CreateSetWindow() auf.
//--- Condition settings if(!CreateRuleValue(130,22+10+5*(25+10))) return(false); if(!CreateRule(10,22+10+5*(25+10))) return(false);
Fügen wir jede der Einstellungen auf die gleiche Weise hinzu. So sieht die vollständige Implementierung der Methode CreateSetWindow () aus:
//+------------------------------------------------------------------+ //| Creates a window for creating and editing trading signals | //+------------------------------------------------------------------+ bool CProgram::CreateSetWindow(const string text) { //--- Add the pointer to the window array CWndContainer::AddWindow(m_set_window); //--- Properties m_set_window.XSize(568); m_set_window.YSize(575); //--- Coordinates int x=int(ChartGetInteger(m_chart_id,CHART_WIDTH_IN_PIXELS)-m_set_window.XSize())/2; int y=30; //--- m_set_window.CaptionHeight(22); m_set_window.IsMovable(true); m_set_window.CaptionColor(m_caption); m_set_window.CaptionColorLocked(m_caption); m_set_window.CaptionColorHover(m_caption); m_set_window.BackColor(m_background); m_set_window.FontSize(m_base_font_size); m_set_window.Font(m_base_font); m_set_window.WindowType(W_DIALOG); //--- Creating a form if(!m_set_window.CreateWindow(m_chart_id,m_subwin,text,x,y)) return(false); //--- Indicator type if(!CreateIndicatorType(10,22+10)) return(false); //--- Settings of the selected indicator if(!CreateSetLabel(m_set_header[0],10,22+10+26+10,"1.Indicator Settings")) return(false); if(!CreatePeriodEdit(10,22+10+2*(25+10))) return(false); if(!CreateAppliedPrice(10,22+10+3*(25+10))) return(false); //--- Signal settings if(!CreateSetLabel(m_set_header[1],10,22+10+4*(25+10),"2.Signal Settings")) return(false); //--- Condition settings if(!CreateRuleValue(130,22+10+5*(25+10))) return(false); if(!CreateRule(10,22+10+5*(25+10))) return(false); //--- Label display settings if(!CreateSetLabel(m_set_header[2],10,22+10+6*(25+10),"Label")) return(false); if(!CreateButton2(m_label_button[0],"Value",100,22+7+6*(25+10))) return(false); if(!CreateButton2(m_label_button[1],"Text",100+80,22+7+6*(25+10))) return(false); //--- Label color display settings if(!CreateColorButton(m_color_button[0],10,22+10+7*(25+10),"Label Color")) return(false); if(!CreateTextBox(180+80+10,22+7+6*(25+10))) return(false); //--- if(!CreateColorButton(m_color_button[1],25,22+10+8*(25+10),"")) return(false); if(!CreateSetCheckBox(m_set_param[0],10,22+10+8*(25+10),"Use Background")) return(false); if(!CreateColorButton(m_color_button[2],25,22+10+9*(25+10),"")) return(false); if(!CreateSetCheckBox(m_set_param[1],10,22+10+9*(25+10),"Use Border")) return(false); if(!CreateColorButton(m_color_button[3],25,22+10+10*(25+10),"")) return(false); if(!CreateSetCheckBox(m_set_param[2],10,22+10+10*(25+10),"Use Tooltip")) return(false); if(!CreateTooltipText(240,22+10+10*(25+10))) return(false); if(!CreateSetCheckBox(m_set_param[3],10,22+10+11*(25+10),"Use Image")) return(false); if(!CreateImageSlider(125,22+10+11*(25+10))) return(false); //--- Timeframe selection if(!CreateSetLabel(m_set_header[4],10,22+10+12*(25+10),"Timeframes")) return(false); //--- y=22+10+13*(25+10); int k=0; for(int i=0; i<21; i++) { if(i==11) { y=22+20+14*(25+10); k=0; } if(!CreateTfButton(m_tf_button[i],40*k+10,y)) return(false); k++; } return(true); }
Die vollständige Liste der Ergänzungen und ihrer Umsetzung ist in den Anhängen unten zu finden. Nachdem alle erforderlichen Teile hinzugefügt wurden, sieht das Erstellungs- und Bearbeitungsfenster wie folgt aus:
Abb. 12 Implementierung von UI-Elementen des Signalbearbeitungsfensters.
Wie Sie in der Abbildung sehen können, sind die Schaltflächen zur Auswahl des Zeitrahmens leer. Wir müssen auch die grundlegenden Interaktionen der Elemente konfigurieren:
- Die Schaltflächen für den Zeitrahmen sollten nur die in Schritt 2 ausgewählte Zahl anzeigen.
- Wenn die Schaltfläche für den Wert ausgewählt ist, sollte die Schaltfläche für Text Text nicht gedrückt sein und das Eingabefeld für die Textbeschriftung sollte ausgeblendet sein.
- Ein Klick auf die Farbauswahl-Schaltfläche sollte ein Fenster mit der Farbpalette öffnen.
- Wenn sie nicht angekreuzt ist, sollten die Palettenauswahl, das Tooltip-Eingabefeld und die Auswahl der grafischen Beschriftung inaktiv werden.
Um die Ausgabe von ausgewählten Zeitrahmen zu implementieren, lassen Sie uns die Methode RebulidTimeframes() im 'private' Bereich der Basisklasse erstellen und diese Methode implementieren:
//+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ void CProgram::RebuildTimeframes(void) { //--- Count the number of selected timeframes int cnt=0; for(int i=0; i<21; i++) { if(m_checkbox[i].IsPressed()) cnt++; } ArrayResize(m_timeframes,cnt); cnt=0; //--- Remember the selected timeframe to the array for(int i=0; i<21; i++) { if(m_checkbox[i].IsPressed()) { m_timeframes[cnt]=m_checkbox[i].LabelText(); cnt++; } } //--- for(int i=0; i<cnt; i++) m_tf_button[i].IsLocked(false); //--- for(int i=0; i<cnt; i++) { m_tf_button[i].LabelText(m_timeframes[i]); m_tf_button[i].Update(true); } //--- for(int i=cnt; i<21; i++) m_tf_button[i].IsLocked(true); }
Fügen wir nun Folgendes zum Code hinzu, der das Bearbeitungsfenster aufruft, wenn jemand auf Add Signal (Signal hinzufügen) klickt.
//--- Click on the "Add Signal" button if(lparam==m_add_signal.Id()) { m_set_window.OpenWindow(); if(m_set_window.IsAvailable()) RebuildTimeframes(); }
Kommen wir zum nächsten Moment, der mit der Einrichtung der Interaktion der Schaltflächen Wert und Text zu tun hat. Fügen wir folgenden Code OnEvent() hinzu:
//--- if(lparam==m_label_button[0].Id()) { if(m_label_button[0].IsPressed()) { m_label_button[1].IsPressed(false); m_label_button[1].Update(true); } m_text_box.Hide(); } if(lparam==m_label_button[1].Id()) { if(m_label_button[1].IsPressed()) { m_label_button[0].IsPressed(false); m_label_button[0].Update(true); } m_text_box.Show(); }
Folgende Bedingung ist hier erfüllt: Wenn eine der Tasten gedrückt ist, sollte die andere nicht gedrückt sein. Wenn die Taste Text nicht gedrückt ist, soll das Bearbeitungsfeld ausgeblendet werden. Klicks auf die Schaltflächen für die Farbpaletten sind hier ebenfalls implementiert. Wir haben vier Schaltflächen, ein Array mit vier Elementen wurde deklariert, daher kann der Zugriff auf diese in einer Schleife geschrieben werden.
//--- for(int i=0; i<4; i++) { if(lparam==m_color_button[i].Id()) { m_color_picker.ColorButtonPointer(m_color_button[i]); return; } }
Und die letzte Interaktion besteht darin, Elemente zu blockieren, wenn die Kontrollkästchen nicht gedrückt sind. Fügen wir die Kontrolle der Klicks der Checkboxen OnEvent() hinzu und implementieren die Interaktionen.
//--- Click on the checkbox if(id==CHARTEVENT_CUSTOM+ON_CLICK_CHECKBOX) { //--- for(int i=0; i<3; i++) { if(lparam==m_set_param[i].Id()) { m_color_button[i+1].IsLocked(!m_set_param[i].IsPressed()); if(m_set_param[2].IsPressed()) m_tooltip_text.Show(); else m_tooltip_text.Hide(); } } //--- if(lparam==m_set_param[3].Id()) m_pictures_slider.IsLocked(!m_set_param[3].IsPressed()); }
Kompilieren wir das Projekt erneut und sehen uns das Ergebnis an.
Abb. 13 Implementierungen der Interaktion von UI-Elementen des Signalbearbeitungsfensters.
Monitor der Handelssignale
Der letzte Schritt innerhalb dieser Entwicklungsphase ist die Schaffung eines Fensters für die Überwachung zukünftiger Handelssignale. Dabei sollten wir auch jene Grundeinstellungen berücksichtigen, die in der aktuellen Version bereits implementiert sind. Vor dem Erstellen sollten wir einige Aufgaben festlegen, damit der Leser versteht, zu welchem Zweck die Elemente erstellt werden:
- Erstellen wir die Zeilen mit Textbeschriftungen der im ersten Schritt ausgewählten Signale.
- Erstellen wir Überschriftsspalten mit Textbeschriftungen von Zeitrahmen, die im zweiten Schritt ausgewählt wurden.
- Ändern wir die Größe des Fensters in Übereinstimmung mit den Zeilen und Spalten der erstellten Elemente. Eine Art automatische Größenanpassung.
Um die Erstellung der Text-Labels von Zeitrahmen und Symbolen zu ermöglichen, erstellen wir zwei Arrays mit den Klasseninstanzen CTextLabel und fügen zwei Implementierungsmethoden in der Klasse CProgramm hinzu.
CTextLabel m_timeframe_label[]; CTextLabel m_symbol_label[]; bool CreateTimeframeLabel(CTextLabel &text_label,const int x_gap,const int y_gap,string label_text); bool CreateSymbolLabel(CTextLabel &text_label,const int x_gap,const int y_gap,string label_text);
Implementieren wir nun die erstellten Methoden in der Datei MainWindow.mqh:
//+------------------------------------------------------------------+ //| Creates the text label | //+------------------------------------------------------------------+ bool CProgram::CreateTimeframeLabel(CTextLabel &text_label,const int x_gap,const int y_gap,string label_text) { //--- Save the window pointer text_label.MainPointer(m_step_window); //--- text_label.Font(m_base_font); text_label.FontSize(m_base_font_size); text_label.XSize(40); text_label.BackColor(m_background); //--- Create the button if(!text_label.CreateTextLabel(label_text,x_gap,y_gap)) return(false); //--- Add a pointer to element to the base CWndContainer::AddToElementsArray(0,text_label); return(true); } //+------------------------------------------------------------------+ //| Creates the text label | //+------------------------------------------------------------------+ bool CProgram::CreateSymbolLabel(CTextLabel &text_label,const int x_gap,const int y_gap,string label_text) { //--- Save the window pointer text_label.MainPointer(m_step_window); //--- text_label.Font(m_base_font); text_label.FontSize(m_base_font_size); text_label.XSize(100); text_label.BackColor(m_background); //--- Create the button if(!text_label.CreateTextLabel(label_text,x_gap,y_gap)) return(false); //--- Add a pointer to element to the base CWndContainer::AddToElementsArray(0,text_label); return(true); }
Bevor wir mit der Visualisierung der Fensteroberfläche fortfahren, müssen wir zwei wichtige Variablen im 'private' Bereich sowie zwei Methoden erstellen:
int m_total_signals; string m_symbols[]; void ToMonitor(void); void AutoResize(const int x_size,const int y_size);
Die Variable m_total_signals wird benötigt, um zu überprüfen, ob mindestens ein Handelssignal erzeugt wurde. Diese Prüfung wird vor der Erstellung des Monitorfensters durchgeführt. Das Array m_symbols[] enthält eine Auswahl von Symbolen aus dem ersten Einrichtungsschritt. Die Methode ToMonitor() implementiert die Erstellung der Monitorschnittstelle, während AutoResize() die Fenstergröße entsprechend den erstellten Elementen anpasst. Hier ist die Implementierung der deklarierten Methoden:
//+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ void CProgram::ToMonitor(void) { //--- Check if there is at least one signal if(m_total_signals<1) { MessageBox("No signals created!","Warning"); return; } //--- Hide Step 3 m_add_signal.Hide(); m_signal_header.Hide(); m_back_button.Hide(); m_next_button.Hide(); //--- Change window header m_step_window.LabelText("Signal Monitor"); m_step_window.Update(true); //--- Symbols int sy=ArraySize(m_symbols); ArrayResize(m_symbol_label,sy); for(int i=0; i<sy; i++) { if(!CreateSymbolLabel(m_symbol_label[i],5,m_step_window.CaptionHeight()+25+i*25,m_symbols[i])) return; m_symbol_label[i].Update(true); } //--- Timeframes int tf=ArraySize(m_timeframes); ArrayResize(m_timeframe_label,tf); //--- for(int i=0; i<tf; i++) { if(!CreateTimeframeLabel(m_timeframe_label[i],110+50*i,m_step_window.CaptionHeight()+3,m_timeframes[i])) return; m_timeframe_label[i].Update(true); } //--- Resize window AutoResize(m_timeframe_label[tf-1].XGap()+m_timeframe_label[tf-1].XSize()+5,m_symbol_label[sy-1].YGap()+m_symbol_label[sy-1].YSize()+5); }
Wie aus dem obigen Code ersichtlich ist, werden im Abschnitt Symbole Daten aus m_symbols verwendet. Diese Daten werden jedoch nicht gesammelt oder aufbereitet. Lassen Sie uns das beheben. Gehen wir zur Methode ToStep_2() und nachdem wir überprüft haben, ob mindestens ein Symbol ausgewählt wurde, merken wir uns die im ersten Schritt ausgewählten Symbole in unserem Array:
//--- Count the number of selected symbols ArrayResize(m_symbols,cnt); cnt=0; //--- Remember the selected timeframe to the array for(int i=0; i<m_all_symbols; i++) { if(m_checkbox[i].IsPressed()) { m_symbols[cnt]=m_checkbox[i].LabelText(); cnt++; } }
Erstellen Sie nun die Methode zur automatischen Größenanpassung.
//+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ void CProgram::AutoResize(const int x_size,const int y_size) { m_step_window.ChangeWindowWidth(x_size); m_step_window.ChangeWindowHeight(y_size); }
Bevor wir das Projekt überprüfen, setzen wir die Variable m_total_signals im Konstruktor von CProgramm auf Null. Ein weiterer wichtiger Punkt ist das Hinzufügen zur Methode OnEvent(), im Klick-Ereignis der Schaltfläche.
//--- Navigation if(lparam==m_back_button.Id()) { //--- Go back if(m_current_step==2) ToStep_1(); //--- Return to Step 2 else if(m_current_step==3) ToStep_2(); } //--- Go forward if(lparam==m_next_button.Id()) { //--- Go to Step 2 if(m_current_step==1) ToStep_2(); //--- Go to Step 3 else if(m_current_step==2) ToStep_3(); //--- Go to Monitor else if(m_current_step==3) ToMonitor(); }
Fügen wir hier den Aufruf der erstellten Methode ToMonitor() bei einem Klick auf die Schaltfläche hinzu, die zum nächsten Schritt springt. Diese Schaltfläche wird in Schritt 3 "Create" genannt. Kompilieren wir nun das Projekt und starten die Anwendung:
- Wählen wir im ersten Schritt die Crosses aus.
- Im zweiten Schritt wählen Sie Senior.
- Klicken wir im dritten Schritt auf Add Signal.
- Schließen Sie danach das Fenster zur Signalerzeugung und klicken Sie auf Create.
Abb. 14 Grundlegende Monitoreinstellung
Im nächsten Artikel werden wir uns mit der Implementierung eines Algorithmus befassen, der die konfigurierten Handelssignale unter den Arbeitsbedingungen sucht, die während der anfänglichen Einführung geschaffen wurden.
Schlussfolgerung
Das angehängte Archiv enthält alle besprochenen Dateien, die sich in den entsprechenden Ordnern befinden. Für den ordnungsgemäßen Betrieb müssen Sie lediglich den Ordner MQL5 in den Terminalverzeichnis speichern. Um das Stammverzeichnis des Terminals zu öffnen, in dem sich der Ordner MQL5 befindet, drücken wir die Tastenkombination Strg+Umschalt+D im Terminal MetaTrader 5 oder verwenden das Kontextmenü wie in Abb. 15 unten dargestellt.
Abb. 15. Öffnen des Ordners MQL5 im Stammordner von MetaTrader 5
Übersetzt aus dem Russischen von MetaQuotes Ltd.
Originalartikel: https://www.mql5.com/ru/articles/7528
- 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.