![Die Handelssignale mehrerer Währungen überwachen (Teil 3): Einführung von Suchalgorithmen](https://c.mql5.com/i/articles/overlay.png)
Die Handelssignale mehrerer Währungen überwachen (Teil 3): Einführung von Suchalgorithmen
Inhalt
- Einführung
- System zum Speichern von Symbolsätzen
- Hinzufügen und Bearbeiten eines Handelssignals
- Suchalgorithmen für Handelssignale
- Schlussfolgerung
Einführung
In früheren Artikeln haben wir die Struktur der Anwendung für die Überwachung von Handelssignalen geschaffen. Wir haben auch die Anwendungsschnittstelle mit grundlegenden Interaktionsmöglichkeiten implementiert. Nun ist es an der Zeit, den visuellen Teil des Algorithmus zur Konfiguration und Suche von Symbolen zu programmieren. Wir werden das Projekt aus dem vorhergehenden Artikel als Grundlage verwenden und es Schritt für Schritt um neue Funktionen erweitern.
System zum Speichern von Symbolsätzen
Letztes Mal haben wir im ersten Schritt der Anwendungseinrichtung Werkzeuge zur Auswahl von Symbolen aus der Market Watch erstellt. Die Symbole konnten auf drei verschiedene Arten ausgewählt werden:
- Manuell. Markieren Sie die gewünschten Symbole und klicken Sie auf Next.
- Ein vordefinierter Gruppe (Set). Ein Klick auf All, Major oder Crosses wählte automatisch eine bestimmte vordefinierte Gruppe von Symbolen aus.
- Gespeicherte Gruppe. Eine zuvor vorbereitete Gruppe von Symbolen, die mit den ersten beiden Methoden konfiguriert und unter einem bestimmten Namen in einer Datei gespeichert wurde.
Abb.1 Anwendungs-Setup Schritt 1 und Einstellungen des Saved Set.
Die ersten beiden Möglichkeiten sind recht einfach, und sie wurden bereits früher erstellt. Der dritte Weg muss neu erstellt werden. Nun wollen wir genauer definieren, was wir tun werden. Wir werden uns eingehender mit der Interaktion der Elemente im roten Rahmen in Abb.1 befassen, was Folgendes impliziert:
- Die gewünschten Symbole sind mit Häkchen markiert, der Nutzer gibt den Namen in das Feld 'Template name' ein und klickt dann auf Speichern oder drückt die Schnelltaste S. Bei erfolgreichem Speichern wird eine entsprechende Meldung angezeigt.
- Um auf eine zuvor konfigurierte und gespeicherte Vorlage zuzugreifen, muss der Vorlagenname in das Feld eingegeben und Laden oder die Tastenkombination L gedrückt werden.
Öffnen Sie das Projekt, suchen Sie die Basisklasse Programm und fügen Sie zwei Methoden in seinem 'private' Abschnitt hinzu. Die Methoden werden für das Laden und Speichern der Symbolvorlage verantwortlich sein.
bool SaveSymbolSet(string file_name); bool LoadSymbolSet(string file_name);
Im Folgenden wird beschrieben, wie diese Methoden umgesetzt werden.
//+------------------------------------------------------------------+ //| Save template to a file | //+------------------------------------------------------------------+ bool CProgram::SaveSymbolSet(string file_name) { if(file_name=="") { MessageBox("Select the template name to record","Signal Monitor"); return(false); } int h=FileOpen("Signal Monitor\\"+file_name+".bin",FILE_WRITE|FILE_BIN); if(h==INVALID_HANDLE) { MessageBox("Failed to create a configuration file","Signal Monitor"); return(false); } else MessageBox("The "+file_name+" configuration has been successfully saved","Signal Monitor"); //--- Save the selection of timeframes and patterns for(int i=0; i<m_all_symbols; i++) m_save.tf[i]=m_checkbox[i].IsPressed(); //--- FileWriteStruct(h,m_save); FileClose(h); //--- return(true); } //+------------------------------------------------------------------+ //| Load data to a panel | //+------------------------------------------------------------------+ bool CProgram::LoadSymbolSet(string file_name) { if(file_name=="") { MessageBox("Select the template name to load","Signal Monitor"); return(false); } int h=FileOpen("Signal Monitor\\"+file_name+".bin",FILE_READ|FILE_BIN); if(h==INVALID_HANDLE) { MessageBox("Configuration "+file_name+" not found","Signal Monitor"); return(false); } ZeroMemory(m_save); FileReadStruct(h,m_save); //--- Loading timeframes for(int i=0; i<m_all_symbols; i++) { m_checkbox[i].IsPressed(m_save.tf[i]); m_checkbox[i].Update(true); } //--- FileClose(h); //--- return(true); }
Wenn Sie jedoch versuchen, das Projekt jetzt zu kompilieren, löst dies einen Fehler in Verbindung mit der Variable m_save aus. Diese Struktur hat einen Parameter vom Typ bool mit dem Namen tf. Sie merkt sich die Wahl des Nutzers für eine Datei. Erstellen wir also diese Struktur in der Anwendungsklasse und fügen Sie ihre Instanz zur Basisklasse hinzu.
//+------------------------------------------------------------------+ //| Class for creating the application | //+------------------------------------------------------------------+ struct SAVE { bool tf[100]; }; class CProgram : public CWndEvents { ... SAVE m_save;
Gehen Sie zu OnEvent(), geben Sie den Abschnitt ein, der sich auf das Button-Klick-Ereignis bezieht, und fügen Sie den folgenden Code in der Schritt 1-Bedingung hinzu:
//--- Save the template if(lparam==m_save_button.Id()) { SaveSymbolSet(m_text_edit.GetValue()); } //--- Load the template if(lparam==m_load_button.Id()) { LoadSymbolSet(m_text_edit.GetValue()); }
Implementieren Sie auch die Verwendung von Tastenkombinationen für die oben genannten Tasten. Fügen Sie in derselben Methode eine Prüfung für das Tastendruckereignis hinzu und fügen Sie den Code für die verwendeten Tasten hinzu.
//--- Key press if(id==CHARTEVENT_KEYDOWN) { if(m_current_step==1) { short sym=TranslateKey((int)lparam); //--- if the entered character is successfully converted to Unicode if(sym>0) { if(ShortToString(sym)=="l" || ShortToString(sym)=="д") LoadSymbolSet(m_text_edit.GetValue()); if(ShortToString(sym)=="s" || ShortToString(sym)=="ы") SaveSymbolSet(m_text_edit.GetValue()); } } }
Kompilieren wir das Projekt. Ein erfolgreiches Kompilieren wird zu folgendem Ergebnis führen.
Abb.2 Speichern und Laden einer Nutzervorlage
Hinzufügen und Bearbeiten eines Handelssignals
Gehen Sie nun zum Hauptanwendungsteil über, der für die Erstellung und Bearbeitung von Handelssignalen sowie für deren weitere Verfolgung im Monitor zuständig ist. So sieht die Erstellung und Bearbeitung der Signale aus.
Abb.3 Fenster zur Erzeugung und Bearbeitung von Signalen.
In der aktuellen Phase erscheint das Fenster als ein Satz von GUI-Elementen, die verschiedene Parameter steuern. Diese Einstellungen werden jedoch nirgendwo verwendet. Beginnen wir mit dem Hinzufügen von zwei Schaltflächen zur Schnittstelle für das Hinzufügen/Speichern eines Handelssignals. Eine weitere Schaltfläche bricht die Erstellung/Bearbeitung ab. Öffnen Sie Program.mqh und fügen Sie der Basisklasse eine Implementierungsmethode für diese beiden Schaltflächen hinzu:
bool CreateButton3(CButton &button,string text,const int x_gap,const int y_gap);
Zwei CButton Schaltflächen-Instanzen:
CButton m_new_signal; CButton m_cancel_button;
Gehen Sie nun zu SetWindow.mqh und implementieren Sie diese Methode.
//+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ bool CProgram::CreateButton3(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_set_window); //--- Set up properties before creation button.XSize(60); 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 the control if(!button.CreateButton(text,x_gap,y_gap)) return(false); //--- Add the element pointer to the base CWndContainer::AddToElementsArray(1,button); return(true); }
Damit diese beiden Schaltflächen in der Oberfläche des Handelsfensters erscheinen und ein Signal hinzufügen, fügen Sie die folgenden Zeilen am Ende des Methodenrumpfes CreateSetWindow() ein:
//--- Add/Cancel Buttons if(!CreateButton3(m_new_signal,"Add",m_set_window.XSize()-2*(60+10),m_set_window.YSize()-(30+10))) return(false); if(!CreateButton3(m_cancel_button,"Cancel",m_set_window.XSize()-(60+10),m_set_window.YSize()-(30+10))) return(false);
Nach dem Kompilieren erscheinen zwei Schaltflächen am unteren Rand des Fensters zur Erstellung von Handelssignalen.
Abb.4 Schaltflächen zum Hinzufügen und Löschen von Signalen.
Jetzt müssen wir die Ereignisse hinzufügen, die bei Klicks auf die Schaltflächen eintreten werden. Die Wirkung der Schaltfläche Cancel (Abbrechen) ist offensichtlich: sie speichert keine Aktionen und Einstellungen im gegebenen Fenster und schließt das Fenster, ohne ein Signal hinzuzufügen. Betrachten wir die Schaltfläche Add (Hinzufügen) genauer.
Bestimmen wir zunächst die Reihenfolge der Aktionen, die ausgeführt werden, wenn auf Hinzufügen geklickt wird.
- Ein Klick auf die Schaltfläche speichert die Parameter, die unter Verwendung der UI-Elemente im Fenster zur Erstellung von Handelssignalen ausgewählt wurden.
- Nach erfolgreicher Speicherung wird das Fenster geschlossen, und der erste Datensatz mit dem Signalnamen erscheint in der Liste der Signale im Hauptfenster.
- Nach einem Klick auf den Datensatz wird der zuvor gespeicherte Satz auf die UI-Elemente der Signaleinrichtung angewendet, und die Schaltfläche Hinzufügen wird in Speichern umgewandelt.
Um das Speichern von Einstellungen in einer Datei zu ermöglichen, müssen wir einen universellen Satz von Einstellungen erstellen, der sowohl für die visuelle Anzeige im Editierfenster als auch für die nachfolgende Signalsuche verwendet wird. Lassen Sie uns daher eine Struktur erstellen und diese SIGNAL nennen. Die Konfiguration der Einstellungen im Erstellungs- und Editierfenster wird in diese Struktur geschrieben.
struct SIGNAL { int ind_type; int ind_period; int app_price; int rule_type; double rule_value; int label_type; uchar label_value[10]; color label_color; color back_color; color border_color; bool tooltip; uchar tooltip_text[100]; bool image; int img_index; bool timeframes[21]; TFNAME tf_name[21]; };
Lassen Sie uns jedes der Elemente in der Struktur betrachten:
- ind_type — enthält den Typ des Indikators, der als Basis für die Signalsuche gewählt wurde. Er wird in der Schnittstelle als Indikatortyp angezeigt.
- ind_period — enthält die Periodenlänge des gewählten Indikators.
- app_price — der für die Indikatorberechnung verwendete Preis. Dieser Wert ist nicht für alle Indikatoren verfügbar und wird daher nur geschrieben, wenn er anwendbar ist. Beispielsweise wird er für RSI verwendet, aber nicht für WPR.
- rule_type — legt den Typ der Regel fest, die bei der Suche nach einem Handelssignal verwendet werden soll. Sie erscheint in der Schnittstelle als Dropdown-Menü, das Zeichen wie ==,>=,<= und andere enthält.
- rule_value — ein Schwellenwert des ausgewählten Indikators, auf den die Suchregel angewendet werden soll.
- label_type — dieses Element speichert den Anzeigetyp des Textlabels. Es handelt sich entweder um den aktuellen Indikatorwert oder ein nutzerdefiniertes Label mit bis zu 3 Zeichen Länge.
- label_value — wenn der zweite Textlabel-Anzeigetyp gewählt wird, speichert dieser Parameter den vom Nutzer angegebenen nutzerdefinierten Labeltext.
- label_color — speichert die Farbe des Textlabels.
- back_color — speichert die Hintergrundfarbe des Signalblocks auf dem Monitor, wenn diese Option gewählt ist.
- Randfarbe — speichert die Randfarbe des Signalblocks im Monitor, wenn diese Option ausgewählt ist.
- tooltip — enthält einen Hinweis darauf, ob ein Tooltip verwendet wird.
- tooltip_text — wenn der Tooltip verwendet wird, enthält dieser Parameter den Text.
- image — enthält einen Hinweis auf die Verwendung eines Bildes.
- img_index — speichert die Sequenznummer des Bildes, wenn es verwendet wird.
- timeframes — Array mit Informationen über die im zweiten Schritt gewählten Einstellungen für den Arbeitszeitraum.
- tf_name — speichert die Zeitrahmen, in denen nach dem Handelssignal gesucht wird.
Nun deklarieren wir in der Basisklasse ein Array von Strukturen zum Speichern der Einstellungen der erzeugten Signale.
SIGNAL m_signal_set[5];
Erstellen Sie außerdem zwei Methoden im 'private' Bereich der Klasse CProgram, um einen Satz von Parametern in einer Datei zu speichern und einen aus der Datei in die Struktur zu laden.
bool SaveSignalSet(int index); bool LoadSignalSet(int index);
Hier ist die Implementierung:
//+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ bool CProgram::SaveSignalSet(int index) { //--- int h=FileOpen("Signal Monitor\\signal_"+string(index)+".bin",FILE_WRITE|FILE_BIN); if(h==INVALID_HANDLE) { MessageBox("Failed to create a configuration file","Signal Monitor"); return(false); } //--- Save the selection //--- Indicator type m_signal_set[index].ind_type=m_indicator_type.GetListViewPointer().SelectedItemIndex(); //--- Indicator period m_signal_set[index].ind_period=(int)m_period_edit.GetValue(); //--- Type of applied price m_signal_set[index].app_price=m_applied_price.GetListViewPointer().SelectedItemIndex(); //--- Rule type m_signal_set[index].rule_type=m_rule_type.GetListViewPointer().SelectedItemIndex(); //--- Rule value m_signal_set[index].rule_value=(double)m_rule_value.GetValue(); //--- Text label display type m_signal_set[index].label_type=m_label_button[0].IsPressed()?0:1; //--- Save the value of the text field for the second type if(m_label_button[1].IsPressed()) StringToCharArray(StringSubstr(m_text_box.GetValue(),0,3),m_signal_set[index].label_value); //--- Color of the text label m_signal_set[index].label_color=m_color_button[0].CurrentColor(); //--- Background color if(m_set_param[0].IsPressed()) m_signal_set[index].back_color=m_color_button[1].CurrentColor(); else m_signal_set[index].back_color=clrNONE; //--- Border color if(m_set_param[1].IsPressed()) m_signal_set[index].border_color=m_color_button[2].CurrentColor(); else m_signal_set[index].border_color=clrNONE; //--- Tooltip value m_signal_set[index].tooltip=m_set_param[2].IsPressed(); if(m_signal_set[index].tooltip) StringToCharArray(m_tooltip_text.GetValue(),m_signal_set[index].tooltip_text); //--- Selected image m_signal_set[index].image=m_set_param[3].IsPressed(); if(m_signal_set[index].image) m_signal_set[index].img_index=m_pictures_slider.GetRadioButtonsPointer().SelectedButtonIndex(); //--- Selected timegrames int tf=0; for(int i=0; i<21; i++) { if(!m_tf_button[i].IsLocked() && m_tf_button[i].IsPressed()) { m_signal_set[index].timeframes[i]=true; StringToCharArray(m_tf_button[i].LabelText(),m_signal_set[index].tf_name[i].tf); tf++; } else m_signal_set[index].timeframes[i]=false; } //--- if(tf<1) { MessageBox("No timeframes selected","Signal Monitor"); FileClose(h); return(false); } //--- FileWriteStruct(h,m_signal_set[index]); FileClose(h); Print("Configuration signal_"+string(index)+" has been successfully saved"); //--- return(true); } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ bool CProgram::LoadSignalSet(int index) { int h=FileOpen("Signal Monitor\\signal_"+string(index)+".bin",FILE_READ|FILE_BIN); if(h==INVALID_HANDLE) { MessageBox("Configuration not found","Signal Monitor"); return(false); } ZeroMemory(m_signal_set[index]); FileReadStruct(h,m_signal_set[index]); //--- Loading indicator type m_indicator_type.SelectItem(m_signal_set[index].ind_type); RebuildParameters(m_signal_set[index].ind_type); m_indicator_type.GetButtonPointer().Update(true); //--- Loading indicator period m_period_edit.SetValue((string)m_signal_set[index].ind_period); m_period_edit.GetTextBoxPointer().Update(true); //--- Loading applied price if(!m_applied_price.IsLocked()) { m_applied_price.SelectItem(m_signal_set[index].app_price); m_applied_price.GetButtonPointer().Update(true); } //--- Loading signal rule m_rule_type.SelectItem(m_signal_set[index].rule_type); m_rule_type.GetButtonPointer().Update(true); m_rule_value.SetValue((string)m_signal_set[index].rule_value); m_rule_value.GetTextBoxPointer().Update(true); //--- Loading a text label if(m_signal_set[index].label_type==0) { m_label_button[0].IsPressed(true); m_label_button[0].Update(true); m_label_button[1].IsPressed(false); m_label_button[1].Update(true); m_text_box.IsLocked(true); } else { m_label_button[0].IsPressed(false); m_label_button[0].Update(true); m_label_button[1].IsPressed(true); m_label_button[1].Update(true); m_text_box.IsLocked(false); m_text_box.ClearTextBox(); m_text_box.AddText(0,CharArrayToString(m_signal_set[index].label_value)); m_text_box.Update(true); } //--- Loading the color of the text label m_color_button[0].CurrentColor(m_signal_set[index].label_color); m_color_button[0].Update(true); //--- Loading the background color if(m_signal_set[index].back_color==clrNONE) { m_set_param[0].IsPressed(false); m_set_param[0].Update(true); m_color_button[1].IsLocked(true); m_color_button[1].GetButtonPointer().Update(true); } else { m_set_param[0].IsPressed(true); m_set_param[0].Update(true); m_color_button[1].IsLocked(false); m_color_button[1].CurrentColor(m_signal_set[index].back_color); m_color_button[1].GetButtonPointer().Update(true); } //--- Loading the border color if(m_signal_set[index].border_color==clrNONE) { m_set_param[1].IsPressed(false); m_set_param[1].Update(true); m_color_button[2].IsLocked(true); m_color_button[2].GetButtonPointer().Update(true); } else { m_set_param[1].IsPressed(true); m_set_param[1].Update(true); m_color_button[2].IsLocked(false); m_color_button[2].CurrentColor(m_signal_set[index].border_color); m_color_button[2].GetButtonPointer().Update(true); } //--- Loading the tooltip value if(!m_signal_set[index].tooltip) { m_set_param[2].IsPressed(false); m_set_param[2].Update(true); m_tooltip_text.IsLocked(true); m_tooltip_text.Update(true); } else { m_set_param[2].IsPressed(true); m_set_param[2].Update(true); m_tooltip_text.IsLocked(false); m_tooltip_text.ClearTextBox(); m_tooltip_text.AddText(0,CharArrayToString(m_signal_set[index].tooltip_text)); m_tooltip_text.Update(true); } //--- Loading the image if(!m_signal_set[index].image) { m_set_param[3].IsPressed(false); m_set_param[3].Update(true); m_pictures_slider.IsLocked(true); m_pictures_slider.GetRadioButtonsPointer().Update(true); } else { m_set_param[3].IsPressed(true); m_set_param[3].Update(true); m_pictures_slider.IsLocked(false); m_pictures_slider.GetRadioButtonsPointer().SelectButton(m_signal_set[index].img_index); m_pictures_slider.GetRadioButtonsPointer().Update(true); } //--- Loading selected timeframes for(int i=0; i<21; i++) { if(!m_tf_button[i].IsLocked()) { m_tf_button[i].IsPressed(m_signal_set[index].timeframes[i]); m_tf_button[i].Update(true); } } //--- FileClose(h); return(true); }
Damit ist die erste Aktion mit dem Algorithmus für das Speichern und Laden abgeschlossen. Jetzt müssen wir das Objekt erstellen, das als Aufzeichnung der erzeugten Signale dienen soll. Wenn wir auf diese Objekte klicken, können wir die Parameter des zuvor erstellten Handelssignals bearbeiten. Um diese Objekte zu implementieren, erstellen Sie ein Array mit Klasseninstanzen von CButton.
CButton m_signal_editor[5];
Fügen Sie außerdem eine Methode zur Erzeugung der Objekte hinzu.
bool CreateSignalEditor(CButton &button,string text,const int x_gap,const int y_gap);
Implementieren Sie diese Methode in der Datei StepWindow.mqh, da diese Objekte zum Hauptfenster gehören.
//+------------------------------------------------------------------+ //| Creates a button with an image | //+------------------------------------------------------------------+ #resource "\\Images\\EasyAndFastGUI\\Icons\\bmp16\\settings_light.bmp" bool CProgram::CreateSignalEditor(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 up 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\\settings_light.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 the control if(!button.CreateButton(text,x_gap,y_gap)) return(false); //--- Add the element pointer to the base CWndContainer::AddToElementsArray(0,button); return(true); }
Fügen Sie mit dieser Methode fünf Objekte zum Körper CreateStepWindow() hinzu, die die Objekte in der Liste der Signale sein werden.
//--- for(int i=0; i<5; i++) { if(!CreateSignalEditor(m_signal_editor[i],"Signal_"+string(i),10,40*i+90)) return(false); }
Um die Darstellung dieser Elemente nach dem Start der Anwendung zu deaktivieren, blenden Sie sie in der Methode CreateGUI() aus.
//+------------------------------------------------------------------+ //| Creates the graphical interface of the program | //+------------------------------------------------------------------+ bool CProgram::CreateGUI(void) { //--- Step 1-3. Symbol selection window. if(!CreateStepWindow("Signal Monitor Step 1: Choose Symbols")) return(false); //--- if(!CreateSetWindow("Signal Monitor Edit Signal")) return(false); //--- Creating form 2 for the color picker if(!CreateColorWindow("Color Picker")) return(false); //--- Finishing the creation of GUI CWndEvents::CompletedGUI(); m_back_button.Hide(); m_add_signal.Hide(); m_signal_header.Hide(); m_label_button[1].IsPressed(true); m_label_button[1].Update(true); for(int i=0; i<5; i++) m_signal_editor[i].Hide(); return(true); }
Als Nächstes muss vor der Projektzusammenstellung eine Methode erstellt werden, die während der anfänglichen Einrichtung alle zuvor gespeicherten Daten löscht. Dazu erstellen Sie die Methode ClearSaves() und rufen sie im Klassenkonstruktor CProgramm auf.
//+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ bool CProgram::ClearSaves(void) { for(int i=0; i<5; i++) FileDelete("Signal Monitor\\signal_"+string(i)+".bin"); m_total_signals=0; return(true); }
Fügen Sie nun Folgendes zum Ereignis des Klicks auf die Schaltfläche Add Signal hinzu:
//--- Add Signal button click event if(lparam==m_add_signal.Id()) { m_set_window.OpenWindow(); m_number_signal=-1; RebuildTimeframes(); m_new_signal.LabelText("Add"); m_new_signal.Update(true); }
Der Mechanismus zum Hinzufügen eines neuen Handelssignals ist nach dem Kompilieren des Projekts bereit. Der nächste Schritt ist die Implementierung der Bearbeitung eines früher erstellten Signals.
Abb.5 Hinzufügen eines neuen Handelssignals.
Lassen Sie uns alles zusammenfassen. Wie in Abbildung 5 dargestellt, haben wir die Möglichkeit implementiert, Signale durch einen Klick auf die Schaltfläche Add Signal hinzuzufügen. Außerdem wird eine Schaltfläche mit dem neuen Signalnamen zu der Schaltfläche Signal List hinzugefügt. Derzeit ist dies ein voreingestellter Wert ohne die Möglichkeit, ihn zu bearbeiten. Bei einem Klick auf Signal_0 passiert jedoch nichts, also lassen Sie uns dies korrigieren. Lassen Sie uns das erneute Öffnen des Einstellungsfensters mit dem Laden genau jener Einstellungen in die Schnittstelle ermöglichen, die zuvor für das gewählte Signal gespeichert wurden. Eine weitere Idee ist die Implementierung der Möglichkeit, die geladenen Einstellungen zu bearbeiten und zu speichern.
Öffnen Sie den Methodenrumpf OnEvent() in der Basisklasse CProgram und suchen Sie das Teil mit dem Schaltflächen-Klick-Ereignis. Fügen Sie den folgenden Code hinzu:
//--- for(int i=0; i<5; i++) { if(lparam==m_signal_editor[i].Id()) { LoadSignalSet(i); m_new_signal.LabelText("Save"); m_new_signal.Update(true); m_set_window.OpenWindow(); m_number_signal=i; } }
Stellen Sie hier fest, welcher erzeugte Signalknopf gedrückt wurde. Dies wissend laden wir die zuvor gespeicherten Daten mit der Methode LoadSignalSet() in die Schnittstelle des Einstellungsfensters, ändern den Schaltflächennamen von Add zu Save und öffnen das Einstellungsfenster.
Suchalgorithmen für Handelssignale
Nun, da die Werkzeuge zur Erstellung und Bearbeitung von Handelssignalen fertig sind, ist es an der Zeit, sie mit dem Anwendungsteil zu verbinden, der für die Suche und Anzeige von Signalen zuständig ist. Wir haben bereits eine Grundlage für die Signalüberwachung geschaffen. Sie verfügt über eine tabellarische Ansicht mit den Namen der Zeilen (im ersten Setup-Schritt ausgewählte Symbole) und Spalten (im zweiten Setup-Schritt ausgewählte Zeitrahmen).
Abb.6 Schaltfläche zum Erstellen eines Monitors für Handelssignale.
Die Abfolge der Aktionen, nachdem mindestens ein Handelssignal erstellt wurde, ist einfach. Ein Klick auf Create (Erstellen) löst die Bildung eines Handelssignalmonitors aus, der auf dem gesamten Bereich der zuvor festgelegten Einstellungen basiert. Bevor wir mit der Programmierung dieses Systems fortfahren, müssen wir die Methode ToMonitor() ergänzen, die nach Drücken von Create aufgerufen wird.
//--- Hide Step 3 m_add_signal.Hide(); m_signal_header.Hide(); m_back_button.Hide(); m_next_button.Hide(); for(int i=0; i<5; i++) m_signal_editor[i].Hide();
Da wir über Schaltflächen-Objekte verfügen, die die Anzeige und Bearbeitung aktuell erzeugter Handelssignale ermöglichen, sollten diese Schaltflächen auch ausgeblendet werden, wenn zum Monitorfenster gesprungen wird, ähnlich wie alle Bedienelemente aus dem vorherigen Schritt 3.
Als wir die Anwendungsstruktur entwickelt haben, war eines der Monitorelemente im ersten Artikel der in Abbildung 5 des ersten Artikels gezeigte Indikationsblock. Ihr Zweck ist es, in Echtzeit das Vorhandensein eines der früher erzeugten Handelssignale anzuzeigen. Daher besteht der erste Schritt darin, ein Objekt zu erstellen, das als Indikationsblock verwendet wird. Dies kann durch die Implementierung der Methode CreateSignalButton() in der Klasse CProgram erfolgen.
bool CreateSignalButton(CButton &button,const int x_gap,const int y_gap);
Fügen wir außerdem ein Array mit Klasseninstanzen von CButton hinzu, die für die Erstellung des vollständigen Satzes von Indikationsblöcken erforderlich sind.
CButton m_signal_button[];
Öffnen Sie nun StepWindow.mqh und fügen Sie am Ende der Datei die Implementierung der erstellten Methode hinzu:
//+------------------------------------------------------------------+ //| Creates an indication block | //+------------------------------------------------------------------+ bool CProgram::CreateSignalButton(CButton &button,const int x_gap,const int y_gap) { //--- color baseclr=C'220,225,235'; //--- Save the window pointer button.MainPointer(m_step_window); //--- Set up properties before creation button.TwoState(false); button.XSize(40); button.YSize(20); button.IconXGap(2); button.IconYGap(button.YSize()/2-8); button.LabelXGap(19); button.LabelYGap(2); 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(clrBlack); button.LabelColorPressed(clrSlateGray); button.IconFile(""); button.IconFileLocked(""); button.IsDoubleBorder(true); //--- Create the control if(!button.CreateButton("",x_gap-button.XSize()/2,y_gap)) return(false); //--- Add the element pointer to the base CWndContainer::AddToElementsArray(0,button); return(true); }
Wenden Sie es nun in der Erstellungsmethode ToMonitor() an. Suchen Sie dazu den Abschnitt Timeframes im Methodenrumpf und fügen Sie Code zur Methode hinzu, wie unten gezeigt:
//--- 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); } //-- Signal blocks int k=0; ArrayResize(m_signal_button,sy*tf); for(int j=0; j<sy; j++) { for(int i=0; i<tf; i++) { if(!CreateSignalButton(m_signal_button[k],m_timeframe_label[i].XGap()+m_timeframe_label[i].XSize()/2,m_step_window.CaptionHeight()+25+j*25)) return; m_signal_button[k].Update(true); k++; } } //--- 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);
Kompilieren Sie das Projekt und Sie erhalten ein fertiges Layout für die zukünftige Darstellung von Handelssignalen.
Abb.7 Ein fertiges Layout von Handelssignalen.
Erinnern wir sich nun, welche Elemente des Indikationsblocks so konfiguriert werden können, dass sie bestimmte Signale anzeigen.
- Hintergrundfarbe.
- Das Vorhandensein und die Farbe der Umrandung des Indikationsblocks.
- Die Farbe und der Wert des Textlabels.
- Das Vorhandensein eines Icons.
- Das Vorhandensein eines Tooltips.
Um diese Eigenschaften zu verwalten, stellen Sie die folgenden Methoden im privaten Bereich von unserer Basisklasse CProgram ein:
void SetBorderColor(int index, color clr); void SetLabel(int index, string text,color clr=clrBlack); void SetIcon(int index,int number); void SetBackground(int index,color clr); void SetTooltip(int index,string text="\n");
Deren Implementation:
//+------------------------------------------------------------------+ //| Set the border color | //+------------------------------------------------------------------+ void CProgram::SetBorderColor(int index, color clr) { m_signal_button[index].BorderColor(clr); m_signal_button[index].BorderColorHover(clr); m_signal_button[index].BorderColorPressed(clr); m_signal_button[index].Update(true); } //+------------------------------------------------------------------+ //| Set the label text | //+------------------------------------------------------------------+ void CProgram::SetLabel(int index, string text,color clr=clrBlack) { m_signal_button[index].LabelColor(clr); m_signal_button[index].LabelColorHover(clr); m_signal_button[index].LabelColorPressed(clr); m_signal_button[index].LabelText(text); m_signal_button[index].Update(true); } //+------------------------------------------------------------------+ //| Set the background | //+------------------------------------------------------------------+ void CProgram::SetBackground(int index,color clr) { m_signal_button[index].BackColor(clr); m_signal_button[index].BackColorHover(clr); m_signal_button[index].Update(true); } //+------------------------------------------------------------------+ //| Set the icon | //+------------------------------------------------------------------+ void CProgram::SetIcon(int index,int number) { //--- string image[]= { "Images\\EasyAndFastGUI\\Icons\\bmp16\\arrow_up.bmp", "Images\\EasyAndFastGUI\\Icons\\bmp16\\arrow_down.bmp" }; string path=(number>=0)?image[number]:""; if(number<0) m_signal_button[index].IsCenterText(true); else m_signal_button[index].IsCenterText(false); m_signal_button[index].IconFile(path); m_signal_button[index].IconFilePressed(path); m_signal_button[index].Update(true); } //+------------------------------------------------------------------+ //| Set the tooltip | //+------------------------------------------------------------------+ void CProgram::SetTooltip(int index,string text="\n") { m_signal_button[index].Tooltip(text); m_signal_button[index].ShowTooltip(true); }
Dann müssen wir einige Hilfsmethoden erstellen, die für die weiteren Berechnungen, die korrekte Darstellung und vor allem für die Übereinstimmung jedes der erstellten Indikationsblöcke mit einer bestimmten Zeile (ausgewähltes Symbol) und Spalte (Zeitrahmen) notwendig sind. Zuerst sind Methoden zur Bestimmung der Zeile und der Spalte des Indikationsblocks auf der Grundlage des Index in der Tabelle zu erstellen.
int GetRow(int index,int row_size); int GetCol(int index,int row_size); //+------------------------------------------------------------------+ //| Determining a row by the index | //+------------------------------------------------------------------+ int CProgram::GetRow(int index,int row_size) { return(int(MathFloor(index/row_size)+1)); } //+------------------------------------------------------------------+ //| Determining a column by the index | //+------------------------------------------------------------------+ int CProgram::GetCol(int index,int row_size) { return(int(MathMod(index,row_size)+1)); }
Wir müssen auch in der Lage sein, die erforderlichen Daten von der Schnittstelle zu erhalten. Tatsächlich müssen wir die Textanzeige der Zeitrahmen in das Enumerationselement des Zeitrahmens umwandeln. Wir müssen auch in der Lage sein, anhand des Indikationsblockindex herauszufinden, welchem Symbol und Zeitrahmen er in der Tabelle entspricht.
//+------------------------------------------------------------------+ //| Return timeframe by row | //+------------------------------------------------------------------+ ENUM_TIMEFRAMES CProgram::StringToTimeframe(const string timeframe) { if(timeframe=="M1") return(PERIOD_M1); if(timeframe=="M2") return(PERIOD_M2); if(timeframe=="M3") return(PERIOD_M3); if(timeframe=="M4") return(PERIOD_M4); if(timeframe=="M5") return(PERIOD_M5); if(timeframe=="M6") return(PERIOD_M6); if(timeframe=="M10") return(PERIOD_M10); if(timeframe=="M12") return(PERIOD_M12); if(timeframe=="M15") return(PERIOD_M15); if(timeframe=="M20") return(PERIOD_M20); if(timeframe=="M30") return(PERIOD_M30); if(timeframe=="H1") return(PERIOD_H1); if(timeframe=="H2") return(PERIOD_H2); if(timeframe=="H3") return(PERIOD_H3); if(timeframe=="H4") return(PERIOD_H4); if(timeframe=="H6") return(PERIOD_H6); if(timeframe=="H8") return(PERIOD_H8); if(timeframe=="H12") return(PERIOD_H12); if(timeframe=="D1") return(PERIOD_D1); if(timeframe=="W1") return(PERIOD_W1); if(timeframe=="MN") return(PERIOD_MN1); //--- The default value return(::Period()); } //+------------------------------------------------------------------+ //| Determine the timeframe | //+------------------------------------------------------------------+ ENUM_TIMEFRAMES CProgram::GetTimeframe(int index) { int tf=ArraySize(m_timeframes); return(StringToTimeframe((m_timeframe_label[GetCol(index,tf)-1].LabelText()))); } //+------------------------------------------------------------------+ //| Determine the symbol | //+------------------------------------------------------------------+ string CProgram::GetSymbol(int index) { int tf=ArraySize(m_timeframes); return(m_symbol_label[GetRow(index,tf)-1].LabelText()); }
Die nächste Methode steht in direktem Zusammenhang mit dem Signalsuchalgorithmus: Sie sucht nach dem Parametersatz der früher erzeugten Signale auf dem angegebenen Symbol und Zeitrahmen.
bool GetSignal(string sy,ENUM_TIMEFRAMES tf,SIGNAL &signal_set);
Die Einstellungen werden durch die Struktur SIGNAL aus einer Menge von Parametern übergeben.
//+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ bool CProgram::GetSignal(string sy,ENUM_TIMEFRAMES tf,SIGNAL &signal_set) { //--- Getting the indicator handle int h=INVALID_HANDLE; ENUM_APPLIED_PRICE app_price; switch(signal_set.app_price) { case 0: app_price=PRICE_CLOSE; break; case 1: app_price=PRICE_OPEN; break; case 2: app_price=PRICE_HIGH; break; case 3: app_price=PRICE_LOW; break; case 4: app_price=PRICE_MEDIAN; break; case 5: app_price=PRICE_TYPICAL; break; case 6: app_price=PRICE_WEIGHTED; break; default: app_price=PRICE_CLOSE; break; } //--- switch(signal_set.ind_type) { case 0: h=iATR(sy,tf,signal_set.ind_period); break; case 1: h=iCCI(sy,tf,signal_set.ind_period,app_price); break; case 2: h=iDeMarker(sy,tf,signal_set.ind_period); break; case 3: h=iForce(sy,tf,signal_set.ind_period,MODE_SMA,VOLUME_TICK); break; case 4: h=iWPR(sy,tf,signal_set.ind_period); break; case 5: h=iRSI(sy,tf,signal_set.ind_period,app_price); break; case 6: h=iMomentum(sy,tf,signal_set.ind_period,app_price); break; default: break; } if(h==INVALID_HANDLE) { Print(sy+". Failed to get handle"); Print("Handle = ",h," error = ",GetLastError()); return(false); } //--- double arr[1]; if(CopyBuffer(h,0, 0,1,arr)!=1) { Print("sy= ",sy,"tf= ",EnumToString(tf)," Failed to get handle data ",GetLastError()); return(false); } IndicatorRelease(h); //--- Check the condition double r_value=signal_set.rule_value; double c_value=arr[0]; m_ind_value=c_value; int s=0; switch(signal_set.rule_type) { case 0: if(c_value>r_value) s=1; break; case 1: if(c_value>=r_value) s=1; break; case 2: if(c_value==r_value) s=1; break; case 3: if(c_value<r_value) s=1; break; case 4: if(c_value<=r_value) s=1; break; default: s=0; break; } //--- if(s>0) return(true); return(false); }
Die Methode GetSignal() erhält von der Struktur SIGNAL die Information, welcher der verfügbaren Indikatoren für die Erzeugung von Handelssignalen ausgewählt wurde, welche Einstellungen für den Indikator gewählt wurden und welche Suchregel gesetzt wurde. Vergessen Sie nicht, dass das Filtern nach Zeitrahmen für jedes Signal zweimal durchgeführt werden kann. Das erste Mal wird es im zweiten Einrichtungsschritt durchgeführt, und dann können wir ausgewählte Werte im Fenster zur Erzeugung von Handelssignalen filtern, wie es in Abbildung 8 unten dargestellt ist.
Abb.8 Auswahl der Zeitrahmen für das erzeugte Signal.
Damit unser Algorithmus diesen Filter berücksichtigt und nicht nach Signalen außerhalb der vorgegebenen Zeitrahmen sucht, sollte die Spezifikation der Zeitrahmen für jedes der erzeugten Handelssignale überprüft werden. Erstellen wir also die Methode CheckTimeframe() in der Basisklasse. Die Methode wird als Filter dienen.
//+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ bool CProgram::CheckTimeframe(ENUM_TIMEFRAMES tf,SIGNAL &signal_set) { for(int i=0; i<21; i++) { if(StringToTimeframe(CharArrayToString(signal_set.tf_name[i].tf))==tf) return(true); } return(false); }
Jetzt ist es an der Zeit, den Suchmechanismus für Handelssignale zu schaffen. Dazu fügen wir eine Methode zum Abschnitt public der Klasse CProgram hinzu: SearchSignal().
bool SearchSignals(void);
Lassen Sie uns ihre schrittweise Implementierung genauer analysieren und den Zweck der früher geschaffenen Hilfsmethoden sehen.
//+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ bool CProgram::SearchSignals(void) { //--- Search for set signals SIGNAL signal_set[]; int cnt=0; for(int i=0; i<5; i++) { if(FileIsExist("Signal Monitor\\signal_"+string(i)+".bin")) cnt++; } //--- ArrayResize(signal_set,cnt); ZeroMemory(signal_set); //--- for(int i=0; i<cnt; i++) { int h=FileOpen("Signal Monitor\\signal_"+string(i)+".bin",FILE_READ|FILE_BIN); if(h==INVALID_HANDLE) { MessageBox("Configuration not found","Signal Monitor"); return(false); } FileReadStruct(h,signal_set[i]); FileClose(h); for(int j=0; j<ArraySize(m_signal_button); j++) { //--- string sy=GetSymbol(j); ENUM_TIMEFRAMES tf=GetTimeframe(j); //--- if(!CheckTimeframe(tf,signal_set[i])) continue; //--- if(GetSignal(sy,tf,signal_set[i])) { //--- if(signal_set[i].label_type==1) SetLabel(j,CharArrayToString(signal_set[i].label_value),signal_set[i].label_color); else SetLabel(j,DoubleToString(m_ind_value,3),signal_set[i].label_color); //--- if(signal_set[i].back_color!=clrNONE) SetBackground(j,signal_set[i].back_color); //--- if(signal_set[i].border_color!=clrNONE) SetBorderColor(j,signal_set[i].border_color); else SetBorderColor(j,signal_set[i].back_color); //--- if(signal_set[i].tooltip) SetTooltip(j,CharArrayToString(signal_set[i].tooltip_text)); //--- if(signal_set[i].image) SetIcon(j,signal_set[i].img_index); else SetIcon(j,-1); } } } return(true); }
Im ersten Operationsschritt sammelt die Suchmethode Daten über die Gesamtzahl der erzeugten und konfigurierten Handelssignale. Dann durchläuft die Methode Dateien, die Informationen über Signaleinstellungen verbinden, und liest diese Daten in eine Struktur ein. Der Mechanismus bestimmt für jeden Indikationsblock das entsprechende Symbol und den Zeitrahmen, dem der Block in tabellarischer Form entspricht. Auf der Grundlage dieser Daten prüft, ob ein Handelssignal in dem gewählten Zeitrahmen gesucht werden muss. Wenn der Zeitrahmen übereinstimmt, suchen wir nach einem Signal. Wenn ein Signal gefunden wird, färben wir den Indikationsblock entsprechend der Signalkonfiguration ein. Nun kann die erstellte Methode angewendet werden. Die Methode sollte am Ende des Körpers der Methode ToMonitor() aufgerufen werden.
... //--- 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); //--- SearchSignals(); }
Versuchen wir nun, nach einem bestimmten Zeitintervall eine wiederholte Suche zu ermöglichen. Öffnen Sie die Datei SignalMonitor.mq5 und erstellen Sie eine Enumeration am Anfang der Datei:
//+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ enum UPDATE { MINUTE, // 1 Minute MINUTE_15, // 15 Minutes MINUTE_30, // 30 Minutes HOUR, // 1 Hour HOUR_4 // 4 Hours };
Eine neue Einstellung kann jetzt einfach zu den Eingaben hinzugefügt werden:
input UPDATE Update = HOUR; // Update interval
Erstellen wir zwei Variablen für Berechnungen.
int cnts=0; datetime update;
Fügen Sie die folgenden Zeilen in der Initialisierung des Experten hinzu:
//--- switch(Update) { case MINUTE: cnts=60; break; case MINUTE_15: cnts=60*15; break; case MINUTE_30: cnts=60*30; break; case HOUR: cnts=3600; break; case HOUR_4: cnts=3600*4; break; default: cnts=1; break; } update=TimeLocal()+cnts;
Damit haben wir das Aktualisierungsintervall bestimmt und den nächsten Aktualisierungszeitpunkt festgelegt. Fügen wir im Funktionskörper von OnTick() eine Zeitprüfung hinzu: Wenn das angegebene Zeitintervall verstrichen ist, suchen wir erneut nach Handelssignalen.
//+------------------------------------------------------------------+ //| Expert tick function | //+------------------------------------------------------------------+ void OnTick() { if(TimeLocal()>update) { program.SearchSignals(); update=TimeLocal()+cnts; } }
Stellen Sie das Projekt zusammen und erstellen Sie einen Satz Ihrer eigenen Symbole. Wir können ein Signal hinzufügen, um die Funktionsweise des Monitors zu demonstrieren.
Im nächsten Teil dieser Artikelserie werden wir mit der Erweiterung der derzeitigen Funktionalität in Richtung einer flexibleren Einrichtung von Handelssignalen fortfahren, und wir werden auch einige der bestehenden Funktionen verbessern.
Schlussfolgerung
Das unten angehängte Archiv enthält alle beschriebenen Dateien in Ordnern. Für einen korrekten Betrieb sollten Sie den Ordner MQL5 im Stammverzeichnis des Terminals speichern. Um das Stammverzeichnis des Terminals zu öffnen, in dem sich der Ordner MQL5 befindet, drücken Sie die Tastenkombination Strg+Umschalt+D im Terminal MetaTrader 5 oder verwenden Sie das Kontextmenü wie in Abb. 9 unten dargestellt.
Abb. 9. Öffnen des Ordners MQL5 im Stammordner von MetaTrader 5
Übersetzt aus dem Russischen von MetaQuotes Ltd.
Originalartikel: https://www.mql5.com/ru/articles/7600
![Zeitreihen in der Bibliothek DoEasy (Teil 38): Kollektion von Zeitreihen - Aktualisierungen in Echtzeit und Datenzugriff aus dem Programm](https://c.mql5.com/2/38/MQL5-avatar-doeasy-library__3.png)
![Zeitreihen in der Bibliothek DoEasy (Teil 37): Kollektion von Zeitreihen - Datenbank der Zeitreihen nach Symbolen und Zeitrahmen](https://c.mql5.com/2/38/MQL5-avatar-doeasy-library__2.png)
![MQL als Darstellungsmittel für graphische Schnittstellen von MQL-Programmen. Teil 1](https://c.mql5.com/2/38/MQL5-avatar-dialog_form__1.png)
![Anwendung von OLAP im Handel (Teil 4): Quantitative und visuelle Analyse der Testberichte](https://c.mql5.com/2/38/OLAP_in_trading.png)
![MQL5 - Sprache von Handelsstrategien, eingebaut ins Kundenterminal MetaTrader 5](https://c.mql5.com/i/registerlandings/logo-2.png)
- 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.