Strategieentwickler auf Basis der Merill-Muster
Inhaltsverzeichnis
- Einführung
- Aufgabenbeschreibung und Anwendungsprototyp
- Implementierung eines Strategieentwicklers zum Testen
- Demonstration und Beispiel für den Betrieb des Strategieentwicklers
- Schlussfolgerung
Einführung
Im vorherigen Artikel haben wir die Anwendung der Merill-Muster auf verschiedene Daten erwogen, wie z.B. auf einen Preiswert auf dem Chart eines Währungssymbols und auf Werte von Standard-MetaTrader-5-Indikatoren: ATR, WPR, CCI, RSI, unter anderem. Um diese Idee zu erforschen, wurde eine grafische Oberfläche entwickelt. Der Hauptzweck der Schnittstelle ist es, effiziente Methoden zur Musterverwendung zu testen und zu finden. Weiterhin sollten die gefundenen erfolgreichen Konfigurationen in Handelsstrategien integriert und getestet werden. In diesem Artikel werden wir ein grundlegendes Toolkit entwickeln, um Handelsstrategien aufzubauen und zu testen.
Aufgabenbeschreibung und Anwendungsprototyp
Bevor Sie mit der Entwicklung der Anwendung beginnen, lassen Sie uns eine Liste der erforderlichen Funktionen und Oberflächenelemente erstellen. Zuerst definieren wir zwei Abschnitte:
- Registerkarte Konstruktor
- Registerkarte Einstellungen
Die Registerkarte Konstrukteur enthält die wichtigsten Oberflächenelemente, die zur Bildung einer Handelsstrategie erforderlich sind. Berücksichtige alle Grundelemente:
- Symboltabelle, die aus einer vollständigen Liste der im Terminal verfügbaren Symbole besteht, unter der Registerkarte Marktübersicht.
- Filteroption für die Symboltabelle, die eine komfortable Suche nach gewünschten Symbolgruppen ermöglicht.
- Zwei identische Sektionen zum Erzeugen von Kauf- und Verkaufssignalen. Die Abschnitte enthalten identische Elemente, so dass im Folgenden nur eine Abschnittsbeschreibung angegeben wird.
- Datumsbereich für die Prüfung.
- Auswahl des aktuellen Zeitrahmens.
- Der letzte Abschnitt unter der Registerkarte Konstruktor ist der Block mit Testergebnissen der festgelegten Handelsstrategie.
Betrachten wir den Abschnitt Signalerzeugung:
- Ein Muster, das aus dem Satz von Merill-Mustern ausgewählt wurde.
- Ein Satz von drei Signalen, auf die die Muster angewendet werden können. Jedes der Signale kann deaktiviert werden. D.h. wenn nur ein Signal ausgewählt wird, erfolgt der Markteintritt auf Basis dieses Signals. Wenn mehr Signale ausgewählt sind, wird die Eingabe von einem dieser Signale durchgeführt. Merill-Muster können sowohl auf Indikatoren als auch auf den Preis angewendet werden.
- Einstellung von Take-Profit und Stop-Loss.
Die Registerkarte Einstellungen wird für standardmäßigen Indikatorparameter verwendet und ermöglicht das Hochladen von nutzerdefinierten Indikatoren.
Basierend auf den oben genannten gewünschten Eigenschaften erstellen wir einen Prototyp unserer Anwendung. Abbildung 1 unten zeigt das Schema und einen Satz von Oberflächenelementen für die Registerkarte Konstruktor:
Abb.1 Prototyp der Registerkarte Konstruktor und der Oberflächenelemente.
Lassen Sie uns auch einen Prototyp für die Registerkarte Einstellungen erstellen und Elemente in dieser Registerkarte anordnen.
Abb.2 Prototyp der Registerkarte Einstellungen und Oberflächenelemente.
Achten Sie auf den zweiten Unterabschnitt, die Einstellungen für benutzerdefinierte Indikatoren und das Eingabefeld. Die Eingabe der korrekten Werte und die Bedienung einer Anzeige eines Drittanbieters entsprechen der Funktionssyntax von iCustom. Zunächst einmal geht es um die korrekte Eingabe des Indikatorpfades:
name
[in] Name des Benutzerindikators mit dem Pfad in Bezug auf Ordnerverzeichnis der Indikators (MQL5/Indicators/). Wenn ein Indikator in einem Untervezeichnis liegt, zB. in MQL5/Indicators/Examples, muss der Name dementsprechend aussehen: "Examples\\Name_des Indikators" (es ist notwendig, doppelte statt nur einfache slash als Begrenzer zu verwenden).
Um einen Indikator eines Drittanbieters auf eines der Signale anzuwenden, wählen Sie in der Dropdown-Liste für eines der Signale die Option Benutzerdefiniert, wie in Abb. 3 unten gezeigt:
Abb.3 Auswahl der Verwendung eines benutzerdefinierten Indikators.
Die vollständige Abfolge der Aktionen, die den Aufbau und die Verwendung des Strategieentwicklers betreffen, wird im Artikel nach der Softwareimplementierung der Anwendung näher beschrieben.
Implementierung eines Strategieentwicklers zum Testen
Bevor wir mit der Erstellung der grafischen Benutzeroberfläche der Anwendung fortfahren, lassen Sie uns ihre Grundelemente bestimmen, auf deren Grundlage die restlichen Elemente erstellt und entwickelt werden.
Die Anwendung hat zwei Fenster: das Hauptanwendungsfenster und ein Dialogfenster für die Einrichtung des Datumsbereichs. Das Hauptfenster hat zwei Registerkarten: Konstruktor und Einstellungen. Um dies zu implementieren, kombiniert die Hauptinterface-Erstellungsmethode CreateGUI() zwei Fenstererstellungsmethoden:
- CreateWindow() erstellt das Hauptfenster.
- CreateDateSetting() erstellt das Fenster für die Einstellungen des Datumsbereichs.
//+------------------------------------------------------------------+ //| Erstellen der grafischen Schnittstelle des Programms | //+------------------------------------------------------------------+ bool CProgram::CreateGUI(void) { //--- Panel erstelle if(!CreateWindow("Merrill Constructor")) return(false); //--- Erstellen des Dialogfensters if(!CreateDateSetting()) return(false); //--- Beenden der Erstellung des GUI CWndEvents::CompletedGUI(); return(true); } //+------------------------------------------------------------------+
Lassen Sie uns den Inhalt von jeder Methode betrachten. Die Methode CreateDateSetting() ist einfacher zu implementieren und enthält einfache Elemente. Das durch dieses Verfahren implementierte Interface-Element ist in Abb.4 separat dargestellt:
Abb.4 Dialogfeld Einstellungen für den Datumsbereich.
Das Fenster besteht aus dem Dialogfenster sowie zwei Kalenderelementen und zwei Elementen zur Einstellung der Start- und Endzeit. Wir haben die Elementinhalte definiert. Nun, lassen Sie es uns innerhalb der Methode CreateDate Setting() implementieren.
//+------------------------------------------------------------------+ //| Creates a date range selection dialog box | //+------------------------------------------------------------------+ bool CProgram::CreateDateSetting(void) { //--- Hinzufügen eines Zeigers auf das Array des Fensters CWndContainer::AddWindow(m_window[1]); //--- Koordinaten int x=m_date_range.X(); int y=m_date_range.Y()+m_date_range.YSize(); //--- Eigenschaften m_window[1].XSize(372); m_window[1].YSize(230); m_window[1].WindowType(W_DIALOG); m_window[1].IsMovable(true); //--- Erstellen des Formulars if(!m_window[1].CreateWindow(m_chart_id,m_subwin,"",x,y)) return(false); //--- if(!CreateCalendar(m_calendar1,m_window[1],10,25,D'01.01.2019',1)) return(false); if(!CreateCalendar(m_calendar2,m_window[1],201,25,m_calendar2.Today(),1)) return(false); //--- if(!CreateTimeEdit(m_time_edit1,m_window[1],10,200,"Time",1)) return(false); if(!CreateTimeEdit(m_time_edit2,m_window[1],200,200,"Time",1)) return(false); //--- return(true); }
Kommen wir nun zur Methode CreateWindow(), die das Hauptanwendungsfenster implementiert. Die Methodenstruktur ist umfangreich, deshalb lassen Sie uns sie in einzelne Schlüsselkomponenten unterteilen. Die erste ist die Erstellung des Fensters selbst und seiner Grundstruktur, d.h. der beiden Registerkarten Konstruktor und Einstellungen.
//+------------------------------------------------------------------+ //| Erstellen des Formulars eines Steuerelements | //+------------------------------------------------------------------+ bool CProgram::CreateWindow(const string caption_text) { #define VERSION " 1.0" color caption=C'0,130,255'; int ygap=30; //--- Hinzufügen eines Zeigers auf das Array des Fensters CWndContainer::AddWindow(m_window[0]); //--- Eigenschaften m_window[0].XSize(900); m_window[0].YSize(600); m_window[0].FontSize(9); m_window[0].CloseButtonIsUsed(true); m_window[0].CollapseButtonIsUsed(true); m_window[0].CaptionColor(caption); m_window[0].CaptionColorHover(caption); m_window[0].CaptionColorLocked(caption); //--- Erstellen des Formulars if(!m_window[0].CreateWindow(m_chart_id,m_subwin,caption_text+VERSION,10,20)) return(false); //--- Registerkarten if(!CreateTabs(150,20)) return(false);
Dieser Teil des Codes erzeugt das Hauptfenster, während die Methode CreateTabs() für das Hinzufügen der beiden oben beschriebenen Registerkarten verantwortlich ist:
//+------------------------------------------------------------------+ //| Erstellen einer Gruppe mit Registerkarten | //+------------------------------------------------------------------+ bool CProgram::CreateTabs(const int x_gap,const int y_gap) { //--- Sichern des Zeigers auf das Hauptsteuerelement m_tabs1.MainPointer(m_window[0]); //--- Eigenschaften m_tabs1.IsCenterText(true); m_tabs1.PositionMode(TABS_LEFT); m_tabs1.AutoXResizeMode(true); m_tabs1.AutoYResizeMode(true); m_tabs1.AutoYResizeBottomOffset(25); m_tabs1.TabsYSize(40); //--- Hinzufügen von Registerkarten mit den angegebenen Eigenschaften for(int i=0; i<ArraySize(m_tabs_names); i++) m_tabs1.AddTab(m_tabs_names[i],150); //--- Erstellen eines Steuerelements if(!m_tabs1.CreateTabs(x_gap,y_gap)) return(false); //--- Hinzufügen eines Objekts zum gemeinsamen Array der Objektgruppen CWndContainer::AddToElementsArray(0,m_tabs1); return(true); }
Im obigen Prototyp haben wir den Inhalt der Elemente für jede der Registerkarten definiert: Konstruktor (Abb.1) und Einstellungen (Abb.2). Betrachten wir nun die Implementierung der in den Registerkarten enthaltenen Elemente. Die Registerkarte Konstruktor enthält viele sich wiederholende Elementtypen und so werden wir nur die wichtigsten Unterabschnitte und die Liste der Methoden betrachten, die für die Implementierung dieser Elemente verwendet werden.
//---- Constructor tab //--- Symbols filter if(!CreateSymbolsFilter(10,10)) return(false); if(!CreateSymbolsTable(10,45)) return(false); //--- Working timeframe if(!CreateTextLabel(m_text_labels1[2],290,10,"Timeframe",0)) return(false); if(!CreateTimeframe1(440,10)) return(false); //--- Date range if(!CreateButton(m_date_range,240,10)) return(false); //--- Textbezeichner if(!CreateTextLabel(m_text_labels1[0],int(0.35*(m_window[0].XSize()-150)-100),10+ygap,"BUY—Signal",0)) return(false); if(!CreateTextLabel(m_text_labels1[1],int(0.75*(m_window[0].XSize()-150)-100),10+ygap,"SELL—Signal",0)) return(false); //--- Pattern selection if(!PatternType1(int(0.35*(m_window[0].XSize()-150)-100),40+ygap,0)) return(false); if(!CreateCheckBox(m_checkbox[0],int(0.35*(m_window[0].XSize()-150)-120),45+ygap,"Pattern")) return(false); if(!PatternType2(int(0.75*(m_window[0].XSize()-150)-100),40+ygap,0)) return(false); if(!CreateCheckBox(m_checkbox[1],int(0.75*(m_window[0].XSize()-150)-120),45+ygap,"Pattern")) return(false); //--- Selecting the application of patterns if(!AppliedType1(int(0.35*(m_window[0].XSize()-150)-100),80+ygap)) return(false); if(!AppliedType2(int(0.35*(m_window[0].XSize()-150)-100),50+33*2+ygap)) return(false); if(!AppliedType3(int(0.35*(m_window[0].XSize()-150)-100),50+33*3+ygap)) return(false); if(!AppliedType4(int(0.75*(m_window[0].XSize()-150)-100),80+ygap)) return(false); if(!AppliedType5(int(0.75*(m_window[0].XSize()-150)-100),50+33*2+ygap)) return(false); if(!AppliedType6(int(0.75*(m_window[0].XSize()-150)-100),50+33*3+ygap)) return(false); //--- Signal checkboxes for(int i=2; i<8; i++) { if(i<5) if(!CreateCheckBox(m_checkbox[i],int(0.35*(m_window[0].XSize()-150)-120),50+35*(i-1)+ygap,"Signal "+string(i-1))) return(false); if(i>=5) if(!CreateCheckBox(m_checkbox[i],int(0.75*(m_window[0].XSize()-150)-120),50+35*(i-4)+ygap,"Signal "+string(i-4))) return(false); } //--- Take Profit and Stop Loss settings if(!CreateEditValue(m_takeprofit1,int(0.35*(m_window[0].XSize()-150)-120),50+35*4+ygap,"Take Profit",500,0)) return(false); if(!CreateEditValue(m_stoploss1,int(0.35*(m_window[0].XSize()-150)-120),50+35*5+ygap,"Stop Loss",500,0)) return(false); if(!CreateEditValue(m_takeprofit2,int(0.75*(m_window[0].XSize()-150)-120),50+35*4+ygap,"Take Profit",500,0)) return(false); if(!CreateEditValue(m_stoploss2,int(0.75*(m_window[0].XSize()-150)-120),50+35*5+ygap,"Stop Loss",500,0)) return(false); //--- Report if(!CreateReportFrame(m_frame[2],"",int(0.35*(m_window[0].XSize()-150)-120),60+35*6+ygap)) return(false); for(int i=0; i<6; i++) { if(i<3) if(!CreateTextLabel(m_report_text[i],int(0.4*(m_window[0].XSize()-150)-120),60+35*(7+i)+ygap,"",0)) return(false); if(i>=3) if(!CreateTextLabel(m_report_text[i],int(0.75*(m_window[0].XSize()-150)-120),60+35*(7+i-3)+ygap,"",0)) return(false); m_report_text[i].IsCenterText(false); }
Lassen Sie uns sehen, was die wichtigsten Interface-Teile implementieren und aus welchen Methoden sie bestehen.
1. Symbolfilter.
Besteht aus den Methoden CreateSymbolsFilter() und CreateSymbolsTable(). Sie implementieren das folgende Element:
Abb.5 Symbolfilter.
CreateSymbolsFilter() implementiert ein Eingabefeld mit einem Kontrollkästchen und einer Schaltfläche für eine Suche.
//+------------------------------------------------------------------+ //| Creates a checkbox with the "Symbols filter" input field | //+------------------------------------------------------------------+ bool CProgram::CreateSymbolsFilter(const int x_gap,const int y_gap) { //--- Sichern des Zeigers auf das Hauptsteuerelement m_symb_filter.MainPointer(m_tabs1); //--- Hinzufügen zur Registerkarte m_tabs1.AddToElementsArray(0,m_symb_filter); //--- Eigenschaften m_symb_filter.CheckBoxMode(true); m_symb_filter.YSize(25); m_symb_filter.FontSize(11); m_symb_filter.XSize(200); m_symb_filter.GetTextBoxPointer().XGap(20); m_symb_filter.GetTextBoxPointer().XSize(100); m_symb_filter.GetTextBoxPointer().YSize(25); m_symb_filter.GetTextBoxPointer().AutoSelectionMode(true); m_symb_filter.SetValue("USD"); // "EUR,USD" "EURUSD,GBPUSD" "EURUSD,GBPUSD,AUDUSD,NZDUSD,USDCHF" //--- Erstellen eines Steuerelements if(!m_symb_filter.CreateTextEdit("",x_gap,y_gap)) return(false); //--- Aktivieren des Ankreuzfelds m_symb_filter.IsPressed(true); //--- Hinzufügen eines Objekts zum gemeinsamen Array der Objektgruppen CWndContainer::AddToElementsArray(0,m_symb_filter); //--- if(!CreateRequest(x_gap+125,y_gap)) return(false); return(true); }
CreateSymbolsTable() implementiert eine Tabelle, die gefilterte Währungssymbole aus dem Fenster der Marktübersicht ausgibt.
//+------------------------------------------------------------------+ //| Creates a symbol table | //+------------------------------------------------------------------+ bool CProgram::CreateSymbolsTable(const int x_gap,const int y_gap) { #define ROWS1_TOTAL 1 //--- Sichern des Zeigers auf das Hauptsteuerelement m_table_symb.MainPointer(m_tabs1); //--- Hinzufügen zur Registerkarte m_tabs1.AddToElementsArray(0,m_table_symb); //--- Array of column widths int width[1]= {119}; //--- Array der Textausrichtungen in Spalten ENUM_ALIGN_MODE align[1]= {ALIGN_CENTER}; //--- Array of text offset along the X axis in the columns int text_x_offset[1]= {5}; //--- Eigenschaften m_table_symb.XSize(120); m_table_symb.TableSize(1,ROWS1_TOTAL); m_table_symb.ColumnsWidth(width); m_table_symb.TextAlign(align); m_table_symb.FontSize(10); m_table_symb.TextXOffset(text_x_offset); m_table_symb.ShowHeaders(true); m_table_symb.SelectableRow(true); m_table_symb.IsWithoutDeselect(true); m_table_symb.IsZebraFormatRows(clrWhiteSmoke); m_table_symb.AutoYResizeMode(true); m_table_symb.AutoYResizeBottomOffset(3); m_table_symb.HeadersColor(C'0,130,255'); m_table_symb.HeadersColorHover(clrCornflowerBlue); m_table_symb.HeadersTextColor(clrWhite); m_table_symb.BorderColor(C'0,100,255'); //--- Erstellen eines Steuerelements if(!m_table_symb.CreateTable(x_gap,y_gap)) return(false); //--- Hinzufügen eines Objekts zum gemeinsamen Array der Objektgruppen CWndContainer::AddToElementsArray(0,m_table_symb); return(true); }
2. Arbeitszeitrahmen und die Schaltfläche "Date range" (Datumsbereich).
Alle Elemente implementieren die Auswahl eines Arbeitszeitrahmens für den Test. Die Schaltfläche Datumsbereich öffnet das entsprechende Dialogfeld, wie oben beschrieben. Die Methode CreateButton() implementiert die Schaltfläche. CreateTextLabel() erzeugt ein entsprechendes Label, CreateTimeframe1() implementiert die Zeitrahmenauswahl. CreateButton() und CreateTextLabel() sind universelle Methoden, die weiter verwendet werden. Ihr Code wird hier nur einmal angegeben. Die Elemente sind in Abb.6 separat dargestellt:
Abb.6 Die Schaltfläche Datumsbereich und die manuelle Zeitrahmenauswahl.
//+------------------------------------------------------------------+ //| Creates a text label in the first tab | //+------------------------------------------------------------------+ bool CProgram::CreateTextLabel(CTextLabel &text_label,const int x_gap,const int y_gap,string label_text,int tab) { //--- Sichern des Fensterpointers text_label.MainPointer(m_tabs1); //--- Hinzufügen zur Registerkarte m_tabs1.AddToElementsArray(tab,text_label); //--- text_label.Font("Trebuchet"); text_label.FontSize(11); text_label.XSize(200); text_label.LabelColor(C'0,100,255'); text_label.IsCenterText(true); //--- Erstellen der Taste if(!text_label.CreateTextLabel(label_text,x_gap,y_gap)) return(false); //--- Hinzufügen eines Pointers auf das Element auf die Datenbasis CWndContainer::AddToElementsArray(0,text_label); return(true); } //+------------------------------------------------------------------+ //| Die Schaltfläche, das Fenster der Zeitspannenauswahl anzuzeigen | //+------------------------------------------------------------------+ bool CProgram::CreateButton(CButton &button,const int x_gap,const int y_gap) { //--- Sichern des Zeigers auf das Hauptsteuerelement button.MainPointer(m_tabs1); //--- Hinzufügen zur Registerkarte m_tabs1.AddToElementsArray(0,button); //--- Eigenschaften button.XSize(100); button.YSize(25); button.FontSize(11); button.IsHighlighted(false); button.IsCenterText(true); button.BorderColor(C'0,100,255'); button.BackColor(clrAliceBlue); //--- Erstellen eines Steuerelements if(!button.CreateButton("Date range",x_gap,y_gap)) return(false); //--- Hinzufügen eines Pointers auf das Element auf die Datenbasis CWndContainer::AddToElementsArray(0,button); return(true); }
Die Methode CreateTimeframe1() ist eine Dropdown-Liste mit allen verfügbaren Zeitrahmen.
//+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ bool CProgram::CreateTimeframe1(const int x_gap,const int y_gap) { //--- Übergebe das Objekt dem Bedienungsfeld m_timeframe1.MainPointer(m_tabs1); //--- Hinzufügen zur Registerkarte m_tabs1.AddToElementsArray(0,m_timeframe1); //--- Array of the item values in the list view 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" }; //--- Setze Eigenschaften vor der Erstellung m_timeframe1.XSize(50); m_timeframe1.YSize(25); m_timeframe1.ItemsTotal(21); m_timeframe1.FontSize(12); m_timeframe1.LabelColor(C'0,100,255'); CButton *but=m_timeframe1.GetButtonPointer(); but.FontSize(10); but.XSize(50); but.BackColor(clrAliceBlue); but.XGap(1); m_timeframe1.GetListViewPointer().FontSize(10); //--- Save the item values in the combobox list view for(int i=0; i<21; i++) m_timeframe1.SetValue(i,timeframe_names[i]); //--- Get the list view pointer CListView *lv=m_timeframe1.GetListViewPointer(); //--- Set the list view properties lv.LightsHover(true); m_timeframe1.SelectItem(0); //--- Erstellen eines Steuerelements if(!m_timeframe1.CreateComboBox("",x_gap,y_gap)) return(false); //--- Hinzufügen eines Objekts zum gemeinsamen Array der Objektgruppen CWndContainer::AddToElementsArray(0,m_timeframe1); return(true); }
3. Textbeschriftungen in Abschnitten und Musterauswahlelemente für Kauf- und Verkaufssignale.
Textbeschriftungen werden mit der Methode CreateTextLabel() erstellt, die wir bereits berücksichtigt haben. Die beiden anderen Methoden implementieren die Kontrollkästchen und die Dropdown-Menüs zur Auswahl eines Merill-Musters für den Test.
Abb.7 Textbeschriftungen in Abschnitten und Musterauswahl.
Die Methode CreateCheckBox() erzeugt Muster-Checkboxen.
//+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ bool CProgram::CreateCheckBox(CCheckBox &checkbox,const int x_gap,const int y_gap,const string text) { //--- Sichern des Zeigers auf das Hauptsteuerelement checkbox.MainPointer(m_tabs1); //--- Hinzufügen zur Registerkarte m_tabs1.AddToElementsArray(0,checkbox); //--- Eigenschaften checkbox.YSize(25); checkbox.GreenCheckBox(true); checkbox.IsPressed(true); checkbox.FontSize(12); checkbox.LabelColor(C'0,100,255'); checkbox.LabelColorPressed(C'0,100,255'); //--- Erstellen eines Steuerelements if(!checkbox.CreateCheckBox(text,x_gap,y_gap)) return(false); //--- Hinzufügen eines Pointers auf das Element auf die Datenbasis CWndContainer::AddToElementsArray(0,checkbox); return(true); }
Die Methoden PatternType1() und PatternType2() sind identisch.
//+------------------------------------------------------------------+ //| Creates combobox 1 | //+------------------------------------------------------------------+ bool CProgram::PatternType1(const int x_gap,const int y_gap,const int tab) { //--- Total number of the list items #define ITEMS_TOTAL1 32 //--- Übergebe das Objekt dem Bedienungsfeld m_combobox1.MainPointer(m_tabs1); //--- Hinzufügen zur Registerkarte m_tabs1.AddToElementsArray(tab,m_combobox1); //--- Array of the item values in the list view string pattern_names[ITEMS_TOTAL1]= { "M1","M2","M3","M4","M5","M6","M7","M8", "M9","M10","M11","M12","M13","M14","M15","M16", "W1","W2","W3","W4","W5","W6","W7","W8", "W9","W10","W11","W12","W13","W14","W15","W16" }; //--- Setze Eigenschaften vor der Erstellung m_combobox1.XSize(200); m_combobox1.YSize(25); m_combobox1.ItemsTotal(ITEMS_TOTAL1); m_combobox1.GetButtonPointer().FontSize(10); m_combobox1.GetButtonPointer().BackColor(clrAliceBlue); m_combobox1.GetListViewPointer().FontSize(10); //--- Save the item values in the combobox list view for(int i=0; i<ITEMS_TOTAL1; i++) m_combobox1.SetValue(i,pattern_names[i]); //--- Get the list view pointer CListView *lv=m_combobox1.GetListViewPointer(); //--- Set the list view properties lv.LightsHover(true); m_combobox1.SelectItem(0); //--- Erstellen eines Steuerelements if(!m_combobox1.CreateComboBox("",x_gap,y_gap)) return(false); //--- Hinzufügen eines Objekts zum gemeinsamen Array der Objektgruppen CWndContainer::AddToElementsArray(0,m_combobox1); return(true); }
4. Auswahl der Verwendung von Mustern und Signal-Checkboxen.
Dieser Schnittstellenblock besteht aus einem Satz von Signalen zur Konfiguration von Kauf- und Verkaufssignalen. Jeder Block besteht aus einem optionalen Wahlschalter aus einem bis drei Signalen, die als Eingangsbedingungen verwendet werden können. Bestehend aus den Methoden CreateCheckBox() und AppliedTypeN().
Abb.8 Verwendung von Mustern und Signal-Checkboxen.
Die Strukturen der Methoden AppliedType1()-AppliedType6() sind ähnlich: Sie stellen eine Dropdown-Liste mit einer Auswahl eines Datenarrays zur Suche nach musterbasierten Signalen dar.
//+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ bool CProgram::AppliedType1(const int x_gap,const int y_gap) { //--- Übergebe das Objekt dem Bedienungsfeld m_applied1.MainPointer(m_tabs1); //--- Hinzufügen zur Registerkarte m_tabs1.AddToElementsArray(0,m_applied1); //--- Array of the item values in the list view string pattern_names[9]= { "Price","ATR","CCI","DeMarker","Force Ind","WPR","RSI","Momentum","Custom" }; //--- Setze Eigenschaften vor der Erstellung m_applied1.XSize(200); m_applied1.YSize(25); m_applied1.ItemsTotal(9); m_applied1.GetButtonPointer().FontSize(10); m_applied1.GetButtonPointer().BackColor(clrAliceBlue); m_applied1.GetListViewPointer().FontSize(10); //--- Save the item values in the combobox list view for(int i=0; i<9; i++) m_applied1.SetValue(i,pattern_names[i]); //--- Get the list view pointer CListView *lv=m_applied1.GetListViewPointer(); //--- Set the list view properties lv.LightsHover(true); m_applied1.SelectItem(0); //--- Erstellen eines Steuerelements if(!m_applied1.CreateComboBox("",x_gap,y_gap)) return(false); //--- Hinzufügen eines Objekts zum gemeinsamen Array der Objektgruppen CWndContainer::AddToElementsArray(0,m_applied1); return(true); }
5. Einstellungen von Take-Profit und Stop-Loss.
Der Schnittstellenbereich, der es ermöglicht, Take-Profit und Stop-Loss getrennt für Kaufsignale und Verkaufssignale zu konfigurieren. Die Level werden in Punkten festgelegt.
Abb.9 Die Eingabefelder von Take-Profit und Stop-Loss.
Für die Implementierung dieser Eingabefelder wird die universelle Methode CreateEditValue() verwendet.
//+------------------------------------------------------------------+ //| Creates an input field | //+------------------------------------------------------------------+ bool CProgram::CreateEditValue(CTextEdit &text_edit,const int x_gap,const int y_gap,const string label_text,const int value,const int tab) { //--- Sichern des Zeigers auf das Hauptsteuerelement text_edit.MainPointer(m_tabs1); //--- Hinzufügen zur Registerkarte m_tabs1.AddToElementsArray(tab,text_edit); //--- Eigenschaften text_edit.XSize(210); text_edit.YSize(24); text_edit.LabelColor(C'0,100,255'); text_edit.FontSize(12); text_edit.MaxValue(1000); text_edit.MinValue(10); text_edit.SpinEditMode(true); text_edit.SetValue((string)value); text_edit.GetTextBoxPointer().AutoSelectionMode(true); text_edit.GetTextBoxPointer().XGap(100); //--- Erstellen eines Steuerelements if(!text_edit.CreateTextEdit(label_text,x_gap,y_gap)) return(false); //--- Hinzufügen eines Objekts zum gemeinsamen Array der Objektgruppen CWndContainer::AddToElementsArray(0,text_edit); return(true); }
6. Testergebnisse und Bericht.
Dieser Block besteht aus Testergebnissen. Sie wird mit der oben genannten Methode CreateTextLabel() implementiert.
Abb.10 Berichtsblock.
Wir haben die Implementierung der Registerkarte Konstruktor berücksichtigt. Lassen Sie uns nun mit Einstellungen fortfahren.
1. Standard-Kennzeichenparameter.
Dieser Abschnitt enthält alle Einstellungen der Indikatoren, die für Tests und Analysen angeboten werden.
Abb.11 Block mit Standardeinstellungen des Indikators.
Dieser Block wird mit drei Methoden CreateFrame() implementiert, die einen visuellen Schnitt mit einem Rahmen erzeugen. Wir verwenden hier auch eine universelle Eingabefeldmethode zur Erstellung von Indikatorparametern CreateIndSetting() und einen Satz von IndicatorSetting1()-IndicatorSetting4() Methode für Dropdown-Listen für Ma-Methode, Volumen und Preisparameter.
//+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ bool CProgram::CreateFrame(CFrame &frame,const string text,const int x_gap,const int y_gap) { //--- Sichern des Zeigers auf das Hauptsteuerelement frame.MainPointer(m_tabs1); //--- Hinzufügen zur Registerkarte m_tabs1.AddToElementsArray(1,frame); //--- frame.XSize(350); frame.YSize(500); frame.LabelColor(C'0,100,255'); frame.BorderColor(C'0,100,255'); frame.FontSize(11); frame.AutoYResizeMode(true); frame.AutoYResizeBottomOffset(100); frame.GetTextLabelPointer().XSize(250); //--- Erstellen eines Steuerelements if(!frame.CreateFrame(text,x_gap,y_gap)) return(false); //--- Hinzufügen eines Objekts zum gemeinsamen Array der Objektgruppen CWndContainer::AddToElementsArray(0,frame); return(true); } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ bool CProgram::IndicatorSetting1(const int x_gap,const int y_gap,const string text) { //--- Übergebe das Objekt dem Bedienungsfeld m_ind_set1.MainPointer(m_tabs1); //--- Hinzufügen zur Registerkarte m_tabs1.AddToElementsArray(1,m_ind_set1); //--- Array of the item values in the list view string pattern_names[4]= { "Simple","Exponential","Smoothed","Linear weighted" }; //--- Setze Eigenschaften vor der Erstellung m_ind_set1.XSize(200); m_ind_set1.YSize(25); m_ind_set1.ItemsTotal(4); m_ind_set1.FontSize(12); m_ind_set1.LabelColor(C'0,100,255'); CButton *but=m_ind_set1.GetButtonPointer(); but.FontSize(10); but.XSize(100); but.BackColor(clrAliceBlue); m_ind_set1.GetListViewPointer().FontSize(10); //--- Save the item values in the combobox list view for(int i=0; i<4; i++) m_ind_set1.SetValue(i,pattern_names[i]); //--- Get the list view pointer CListView *lv=m_ind_set1.GetListViewPointer(); //--- Set the list view properties lv.LightsHover(true); m_ind_set1.SelectItem(0); //--- Erstellen eines Steuerelements if(!m_ind_set1.CreateComboBox(text,x_gap,y_gap)) return(false); //--- Hinzufügen eines Objekts zum gemeinsamen Array der Objektgruppen CWndContainer::AddToElementsArray(0,m_ind_set1); return(true); } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ bool CProgram::IndicatorSetting3(const int x_gap,const int y_gap,const string text) { //--- Übergebe das Objekt dem Bedienungsfeld m_ind_set3.MainPointer(m_tabs1); //--- Hinzufügen zur Registerkarte m_tabs1.AddToElementsArray(1,m_ind_set3); //--- Array of the item values in the list view string pattern_names[2]= { "Tick volume","Real Volume" }; //--- Setze Eigenschaften vor der Erstellung m_ind_set3.XSize(200); m_ind_set3.YSize(25); m_ind_set3.ItemsTotal(2); m_ind_set3.FontSize(12); m_ind_set3.LabelColor(C'0,100,255'); CButton *but=m_ind_set3.GetButtonPointer(); but.FontSize(10); but.XSize(100); but.BackColor(clrAliceBlue); m_ind_set3.GetListViewPointer().FontSize(10); //--- Save the item values in the combobox list view for(int i=0; i<2; i++) m_ind_set3.SetValue(i,pattern_names[i]); //--- Get the list view pointer CListView *lv=m_ind_set3.GetListViewPointer(); //--- Set the list view properties lv.LightsHover(true); lv.ItemYSize(20); lv.YSize(42); m_ind_set3.SelectItem(0); //--- Erstellen eines Steuerelements if(!m_ind_set3.CreateComboBox(text,x_gap,y_gap)) return(false); //--- Hinzufügen eines Objekts zum gemeinsamen Array der Objektgruppen CWndContainer::AddToElementsArray(0,m_ind_set3); return(true); } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ bool CProgram::IndicatorSetting4(const int x_gap,const int y_gap,const string text) { //--- Übergebe das Objekt dem Bedienungsfeld m_ind_set4.MainPointer(m_tabs1); //--- Hinzufügen zur Registerkarte m_tabs1.AddToElementsArray(1,m_ind_set4); //--- Array of the item values in the list view string pattern_names[4]= { "Open","Close","High","Low" }; //--- Setze Eigenschaften vor der Erstellung m_ind_set4.XSize(200); m_ind_set4.YSize(25); m_ind_set4.ItemsTotal(4); m_ind_set4.FontSize(12); m_ind_set4.LabelColor(C'0,100,255'); CButton *but=m_ind_set4.GetButtonPointer(); but.FontSize(10); but.XSize(100); but.BackColor(clrAliceBlue); m_ind_set4.GetListViewPointer().FontSize(10); //--- Save the item values in the combobox list view for(int i=0; i<4; i++) m_ind_set4.SetValue(i,pattern_names[i]); //--- Get the list view pointer CListView *lv=m_ind_set4.GetListViewPointer(); //--- Set the list view properties lv.LightsHover(true); lv.ItemYSize(20); lv.YSize(82); m_ind_set4.SelectItem(1); //--- Erstellen eines Steuerelements if(!m_ind_set4.CreateComboBox(text,x_gap,y_gap)) return(false); //--- Hinzufügen eines Objekts zum gemeinsamen Array der Objektgruppen CWndContainer::AddToElementsArray(0,m_ind_set4); return(true); }
2. Sprache der Nutzeroberfläche.
Das Steuerelement "Interface Language" (Schnittstellensprache) ist als Dropdown-Liste mit zwei Optionen implementiert: Englisch und Russisch. Dieses Element wird mit der Methode LanguageSetting() implementiert:
//+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ bool CProgram::LanguageSetting(const int x_gap,const int y_gap,const string text) { //--- Übergebe das Objekt dem Bedienungsfeld m_language_set.MainPointer(m_tabs1); //--- Hinzufügen zur Registerkarte m_tabs1.AddToElementsArray(1,m_language_set); //--- Array of the item values in the list view string pattern_names[2]= { "Русский","English" }; //--- Setze Eigenschaften vor der Erstellung m_language_set.XSize(200); m_language_set.YSize(25); m_language_set.ItemsTotal(2); m_language_set.FontSize(12); m_language_set.LabelColor(C'0,100,255'); CButton *but=m_language_set.GetButtonPointer(); but.FontSize(10); but.XSize(100); but.BackColor(clrAliceBlue); but.XGap(140); m_language_set.GetListViewPointer().FontSize(10); //--- Save the item values in the combobox list view for(int i=0; i<2; i++) m_language_set.SetValue(i,pattern_names[i]); //--- Get the list view pointer CListView *lv=m_language_set.GetListViewPointer(); //--- Set the list view properties lv.LightsHover(true); lv.ItemYSize(20); lv.YSize(42); m_language_set.SelectItem(1); //--- Erstellen eines Steuerelements if(!m_language_set.CreateComboBox(text,x_gap,y_gap)) return(false); //--- Hinzufügen eines Objekts zum gemeinsamen Array der Objektgruppen CWndContainer::AddToElementsArray(0,m_language_set); return(true); }
3. Benutzerdefinierte Indikatorparameter.
Besteht aus dem visuellen Abschnitt mit Kopf und Rahmen, der mit der oben genannten Methode CreateFrame() erstellt wird, und einem Eingabefeld für den mit CreateIndSetting() erzeugten Indikatorwert und einer neuen Methode CreateCustomEdit() zur Eingabe des Indikatornamens und einer kommagetrennten Liste seines Parameters.
Abb.12 Benutzerdefinierte Parameter des Indikators.
//+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ bool CProgram::CreateCustomEdit(CTextEdit &text_edit,const int x_gap,const int y_gap,const string default_text) { //--- Sichern des Zeigers auf das Hauptsteuerelement text_edit.MainPointer(m_tabs1); //--- Hinzufügen zur Registerkarte m_tabs1.AddToElementsArray(1,text_edit); //--- Eigenschaften text_edit.XSize(100); text_edit.YSize(24); text_edit.LabelColor(C'0,100,255'); CTextBox *box=text_edit.GetTextBoxPointer(); box.AutoSelectionMode(true); box.XSize(325); box.XGap(1); box.DefaultTextColor(clrSilver); box.DefaultText(default_text); //--- Erstellen eines Steuerelements if(!text_edit.CreateTextEdit("",x_gap,y_gap)) return(false); //--- Hinzufügen eines Objekts zum gemeinsamen Array der Objektgruppen CWndContainer::AddToElementsArray(0,text_edit); return(true); }
Wir haben den visuellen Teil besprochen. Kommen wir nun zum Algorithmus für das Testen der konfigurierten Handelsstrategien.
Um den Algorithmus des Testens mit dieser Anwendung zu erklären, müssen wir die Reihenfolge der Aktionen festlegen, die es uns ermöglichen, einen Test korrekt durchzuführen und ein Ergebnis zu erhalten. Eine gut durchdachte Abfolge von Aktionen kann das Prinzip jeder Interaktion mit der Anwendungsoberfläche hervorheben.
Schritt 1. Auswahl der Sprache der Benutzeroberfläche
Gemäß unserer Implementierung ist diese Option unter der Registerkarte Einstellungen in einer Dropdown-Liste verfügbar. Lassen Sie mich beschreiben, wie die Oberflächensprache umgestellt wird. Dies geschieht durch ein benutzerdefiniertes Ereignis der Auswahl von Elementen der Combobox, das die Methode ChangeLanguage() aufruft.
//--- Selection of a combo box item if(id==CHARTEVENT_CUSTOM+ON_CLICK_COMBOBOX_ITEM) { //--- Ändern der Schnittstellensprache if(ChangeLanguage(lparam)) Update(true); }
Lassen Sie uns nun die Methode zur Änderung der Oberflächensprache betrachten. Obwohl die Methode etwas länger ist, ist ihre Idee einfach.
//+------------------------------------------------------------------+ //| Ändern der Schnittstellensprache | //+------------------------------------------------------------------+ bool CProgram::ChangeLanguage(const long id) { //--- Prüfen der Elemente-ID if(id!=m_language_set.Id()) return(false); m_lang_index=m_language_set.GetListViewPointer().SelectedItemIndex(); //--- if(m_lang_index==0) { //--- Constructor tab m_tabs1.Text(0,"Конструктор"); m_tabs1.Text(1,"Настройки"); m_table_symb.SetHeaderText(0,"Символ"); m_request.LabelText("Поиск"); m_date_range.LabelText("Диапазон дат"); m_timeframe1.LabelText("Таймфрейм"); for(int i=0; i<8; i++) { if(i<2) m_checkbox[i].LabelText("Паттерн"); else if(i>=2 && i<5) m_checkbox[i].LabelText("Сигнал "+string(i-1)); else if(i>=5) m_checkbox[i].LabelText("Сигнал "+string(i-4)); } m_takeprofit1.LabelText("Тейк Профит"); m_takeprofit2.LabelText("Тейк Профит"); m_stoploss1.LabelText("Стоп Лосс"); m_stoploss2.LabelText("Стоп Лосс"); m_frame[2].GetTextLabelPointer().LabelText("Отчёт"); string report_label[6]= { "Всего трейдов: ","Короткие трейды: ","Прибыльные трейды: ", "Прибыль в пунктах: ","Длинные трейды: ","Убыточные трейды: " }; for(int i=0; i<6; i++) m_report_text[i].LabelText(report_label[i]+"-"); //--- Settings tab m_frame[0].GetTextLabelPointer().LabelText("Настройки стандартных индикаторов"); m_frame[1].GetTextLabelPointer().LabelText("Настройки кастомных индикаторов"); m_custom_buffer.LabelText("Номер буфера"); m_custom_path.GetTextBoxPointer().DefaultText("Введите адрес индикатора"); m_custom_param.GetTextBoxPointer().DefaultText("Введите параметры индикатора через запятую"); m_language_set.LabelText("Язык интерфейса"); //--- Date Range window m_window[1].LabelText("Настройки диапазона дат"); m_time_edit1.LabelText("Время"); m_time_edit2.LabelText("Время"); m_time_edit3.LabelText("Время"); m_time_edit4.LabelText("Время"); m_status_bar.SetValue(0,"Не выбран символ для анализа"); } else { //--- Constructor tab m_tabs1.Text(0,"Constructor"); m_tabs1.Text(1,"Settings"); m_table_symb.SetHeaderText(0,"Symbol"); m_request.LabelText("Search"); m_date_range.LabelText("Date range"); m_timeframe1.LabelText("Timeframe"); for(int i=0; i<8; i++) { if(i<2) m_checkbox[i].LabelText("Pattern"); else if(i>=2 && i<5) m_checkbox[i].LabelText("Signal "+string(i-1)); else if(i>=5) m_checkbox[i].LabelText("Signal "+string(i-4)); } m_takeprofit1.LabelText("Take Profit"); m_takeprofit2.LabelText("Take Profit"); m_stoploss1.LabelText("Stop Loss"); m_stoploss2.LabelText("Stop Loss"); m_frame[2].GetTextLabelPointer().LabelText("Report"); string report_label[6]= { "Total trades: ","Short Trades: ","Profit Trades: ", "Profit in points: ","Long Trades: ","Loss Trades: " }; for(int i=0; i<6; i++) m_report_text[i].LabelText(report_label[i]+"-"); //--- Settings tab m_frame[0].GetTextLabelPointer().LabelText("Standard Indicator Settings"); m_frame[1].GetTextLabelPointer().LabelText("Custom Indicator Settings"); m_custom_buffer.LabelText("Buffer number"); m_custom_path.GetTextBoxPointer().DefaultText("Enter the indicator path"); m_custom_param.GetTextBoxPointer().DefaultText("Enter indicator parameters separated by commas"); m_language_set.LabelText("Interface language"); //--- Date Range window m_window[1].LabelText("Date Range Settings"); m_time_edit1.LabelText("Time"); m_time_edit2.LabelText("Time"); m_time_edit3.LabelText("Time"); m_time_edit4.LabelText("Time"); m_status_bar.SetValue(0,"No symbol selected for analysis"); } return(true); }
Schritt 2. Einstellungen der Parameter des Indikators
Unter der gleichen Registerkarte werden die Werte der Indikatorparameter eingestellt, falls bestimmte Indikatoren getestet werden sollen. Optional werden benutzerdefinierte Indikatorparameter konfiguriert: Puffernummer, Name oder Parameter, die durch Kommas getrennt sind. Bitte beachten Sie, dass für benutzerdefinierte Indikatoren nur numerische Werte unterstützt werden.
Schritt 3. Einstellungen der Symboltabelle.
Konfigurieren Sie im oberen Teil der Registerkarte Konstruktor die erforderlichen Symbole, die im Fenster der Marktübersicht verfügbar sind. Dies geschieht mit der Methode RequestData(). Die Methode wird über den Button "Search" aufgerufen.
//--- Button click event if(id==CHARTEVENT_CUSTOM+ON_CLICK_BUTTON) { //--- Requesting data RequestData(lparam); .... //+------------------------------------------------------------------+ //| Output of symbols to the symbols table | //+------------------------------------------------------------------+ bool CProgram::RequestData(const long id) { //--- Prüfen der Elemente-ID //--- if(id==m_request.Id()) { //--- Tabelle ausblenden m_table_symb.Hide(); //--- Initialisierung der Tabelle GetSymbols(m_symb_filter); RebuildingTables(m_table_symb); //--- Einblenden der Tabelle m_table_symb.Show(); } return(true); }
Schritt 4. Auswahl des Testzeitraums
Dieses Ereignis tritt bei einem Klick auf die Schaltfläche "Date range" ein. Die Logik ist einfach: Es öffnet sich eine Dialogbox zur Einstellung des Datumsbereichs.
//--- Button click event if(id==CHARTEVENT_CUSTOM+ON_CLICK_BUTTON) { ... //--- if(lparam==m_date_range.Id()) { int x=m_date_range.X(); int y=m_date_range.Y()+m_date_range.YSize(); m_window[1].X(x); m_window[1].Y(y); m_window[1].OpenWindow(); string val=(m_lang_index==0)?"Настройки диапазона дат":"Date Range Settings"; m_window[1].LabelText(val); } ...
Seien Sie vorsichtig bei der Auswahl der Daten. Wenn die Daten falsch eingestellt sind, gibt die App Fehlermeldungen zurück. Zu den häufigsten Fehlern gehören folgende: Das Enddatum liegt nach dem aktuellen Datum im Terminal oder das Enddatum liegt vor dem Anfangsdatum.
Schritt 5. Einstellung des Arbeitszeitrahmens.
Der Arbeitszeitrahmen gilt für alle sechs Signale, die im Konstruktor konfiguriert werden können.
Schritt 6. Aktivieren von Verkaufs-/Kaufsignalen und Auswählen eines Musters zum Testen.
Die Tests werden standardmäßig in zwei Richtungen durchgeführt: Kauf und Verkauf. Einer der Modi kann jedoch deaktiviert werden, wie in Abbildung 13 unten gezeigt.
Abb.13 Kauf- oder Verkaufssignale deaktivieren.
Das Merill-Muster für weitere Tests kann links neben dem Muster-Label ausgewählt werden. Die Details der Merill-Muster wurden im vorherigen Artikel beschrieben.
Schritt 7. Auswahl der Signale für den Test und Einstellung Take-Profit und Stop-Loss
Abbildung 13 zeigt, dass für jeden Eröffnungstyp einer Position bis zu drei Signale gleichzeitig eingestellt werden können. Die Signale arbeiten nach dem logischen ODER-Prinzip. Wenn also alle drei Kaufsignale in einem Test gesetzt werden, wird ein Markteintritt registriert, wenn eines der drei Signale auftritt. Gleiches gilt für Verkaufssignale. In der Dropdown-Liste rechts neben den Signaltextbeschriftungen können Sie den Datentyp auswählen, auf den das ausgewählte Muster angewendet werden soll.
Schritt 8. Durchführung des Tests
Nach den Schritten 1-7 wählen Sie das Prüfgerät durch einen Linksklick in der Tabelle aus. Der Testalgorithmus wird durch ein benutzerdefiniertes Ereignis gestartet, bei dem Sie auf ein Listen- oder Tabellenelement klicken.
//--- Event of pressing on a list or table item if(id==CHARTEVENT_CUSTOM+ON_CLICK_LIST_ITEM) { //--- Select a symbol for further work //--- Prüfen der Elemente-ID if(lparam==m_table_symb.Id()) { //--- Verlassen, wenn die Zeile nicht ausgewählt wurde if(m_table_symb.SelectedItem()==WRONG_VALUE) { //--- Anzeige der gesamten Symbolbeschreibung in der Statusleiste m_status_bar.SetValue(0,"Не выбран символ для анализа"); m_status_bar.GetItemPointer(0).Update(true); } //--- Get a selected symbol string symbol=m_table_symb.GetValue(0,m_table_symb.SelectedItem()); //--- Anzeige der gesamten Symbolbeschreibung in der Statusleiste m_status_bar.SetValue(0,"Selected symbol: "+::SymbolInfoString(symbol,SYMBOL_DESCRIPTION)); m_status_bar.GetItemPointer(0).Update(true); GetResult(symbol); } }
Der Test wird mit der Methode GetResult() durchgeführt. Überlegen Sie es sich genauer.
//+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ void CProgram::GetResult(const string symbol) { //--- Abrufen der Zeitspanne m_start_date=StringToTime(TimeToString(m_calendar1.SelectedDate(),TIME_DATE)+" "+(string)m_time_edit1.GetHours()+":"+(string)m_time_edit1.GetMinutes()+":00"); m_end_date=StringToTime(TimeToString(m_calendar2.SelectedDate(),TIME_DATE)+" "+(string)m_time_edit2.GetHours()+":"+(string)m_time_edit2.GetMinutes()+":00"); //--- Prüfen des angegebenen Zeitpunkts if(m_start_date>m_end_date || m_end_date>TimeCurrent()) { if(m_lang_index==0) MessageBox("Неправильно выбран диапазон дат!","Ошибка",MB_OK); else if(m_lang_index==1) MessageBox("Incorrect date range selected!","Error",MB_OK); return; } //--- Проверка выбора паттернов int buy_pat=m_combobox1.GetListViewPointer().SelectedItemIndex(); int sell_pat=m_combobox2.GetListViewPointer().SelectedItemIndex(); if(buy_pat==sell_pat) { if(m_lang_index==0) MessageBox("Паттерн на покупку и продажу не может быть одинаков!","Ошибка",MB_OK); else if(m_lang_index==1) MessageBox("The pattern for buying and selling cannot be the same!","Error",MB_OK); return; } //--- ZeroMemory(m_report); datetime cur_date=m_start_date; string tf=m_timeframe1.GetListViewPointer().SelectedItemText(); int applied1=m_applied1.GetListViewPointer().SelectedItemIndex(); int applied2=m_applied2.GetListViewPointer().SelectedItemIndex(); int applied3=m_applied3.GetListViewPointer().SelectedItemIndex(); int applied4=m_applied4.GetListViewPointer().SelectedItemIndex(); int applied5=m_applied5.GetListViewPointer().SelectedItemIndex(); int applied6=m_applied6.GetListViewPointer().SelectedItemIndex(); //--- while(cur_date<m_end_date) { if( BuySignal(symbol,m_start_date,applied1,1) || BuySignal(symbol,m_start_date,applied2,2) || BuySignal(symbol,m_start_date,applied3,3)) { CalculateBuyDeals(symbol,m_start_date); cur_date=m_start_date; continue; } if( SellSignal(symbol,m_start_date,applied4,1) || SellSignal(symbol,m_start_date,applied5,2) || SellSignal(symbol,m_start_date,applied6,3)) { CalculateSellDeals(symbol,m_start_date); cur_date=m_start_date; continue; } m_start_date+=PeriodSeconds(StringToTimeframe(tf)); cur_date=m_start_date; } //--- Output the report PrintReport(); }
Diese Methode beinhaltet die Prüfung, ob der Datumsbereich korrekt eingestellt ist. Eine weitere Überprüfung wird durchgeführt, um sicherzustellen, dass der Benutzer nicht die gleichen Muster für die Prüfung von Kauf- und Verkaufssignalen festgelegt hat. Die Methode GetResult() beinhaltet drei Methoden zum Arbeiten mit Daten, die in den Einstellungen angegeben sind.
1. Methoden zur Signalsuche: BuySignal() und SellSignal(). Sie sind ähnlich. Betrachten wir einen davon.
//+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ bool CProgram::BuySignal(const string symbol,datetime start,int applied,int signal) { //--- Exit if the buy signal is disabled if(!m_checkbox[0].IsPressed()) return(false); //--- int Handle=INVALID_HANDLE; string tf=m_timeframe1.GetListViewPointer().SelectedItemText(); //--- Preparing data if(m_checkbox[signal+1].IsPressed()) { //--- Price if(applied==0) { MqlRates rt[]; int sl=0,tp=0; POINTS pat; double arr[]; int copied=CopyRates(symbol,StringToTimeframe(tf),m_start_date,5,rt); int app_price=m_ind_set4.GetListViewPointer().SelectedItemIndex(); ArrayResize(arr,copied); //Print(m_start_date+": "+copied); if(copied<5) return(false); //--- for(int i=0; i<copied; i++) { if(app_price==0) arr[i]=rt[i].open; else if(app_price==1) arr[i]=rt[i].close; else if(app_price==2) arr[i]=rt[i].high; else if(app_price==3) arr[i]=rt[i].low; } //--- Pattern search pat.A=arr[0]; pat.B=arr[1]; pat.C=arr[2]; pat.D=arr[3]; pat.E=arr[4]; //--- If the pattern is found, check the signal if(GetPatternType(pat)==m_combobox1.GetListViewPointer().SelectedItemIndex()) { m_start_date=IndexToDate(m_start_date,StringToTimeframe(tf),5); return(true); } return(false); } //--- ATR if(applied==1) Handle=iATR(symbol,StringToTimeframe(tf),int(m_ind_setting[0].GetValue())); //--- CCI if(applied==2) { int app_price; switch(m_ind_set4.GetListViewPointer().SelectedItemIndex()) { case 0: app_price=PRICE_OPEN; break; case 1: app_price=PRICE_CLOSE; break; case 2: app_price=PRICE_HIGH; break; case 3: app_price=PRICE_LOW; break; default: app_price=PRICE_CLOSE; break; } Handle=iCCI(symbol,StringToTimeframe(tf),int(m_ind_setting[1].GetValue()),app_price); } //--- DeMarker if(applied==3) Handle=iDeMarker(symbol,StringToTimeframe(tf),int(m_ind_setting[2].GetValue())); //--- Force Index if(applied==4) { int force_period=int(m_ind_setting[3].GetValue()); ENUM_MA_METHOD force_ma_method; ENUM_APPLIED_VOLUME force_applied_volume; switch(m_ind_set1.GetListViewPointer().SelectedItemIndex()) { case 0: force_ma_method=MODE_SMA; break; case 1: force_ma_method=MODE_EMA; break; case 2: force_ma_method=MODE_SMMA; break; case 3: force_ma_method=MODE_LWMA; break; default: force_ma_method=MODE_SMA; break; } switch(m_ind_set3.GetListViewPointer().SelectedItemIndex()) { case 0: force_applied_volume=VOLUME_TICK; break; case 1: force_applied_volume=VOLUME_REAL; break; default: force_applied_volume=VOLUME_TICK; break; } Handle=iForce(symbol,StringToTimeframe(tf),force_period,force_ma_method,force_applied_volume); } //--- WPR if(applied==5) Handle=iWPR(symbol,StringToTimeframe(tf),int(m_ind_setting[5].GetValue())); //--- RSI if(applied==6) Handle=iRSI(symbol,StringToTimeframe(tf),int(m_ind_setting[4].GetValue()),PRICE_CLOSE); //--- Momentum if(applied==7) Handle=iMomentum(symbol,StringToTimeframe(tf),int(m_ind_setting[6].GetValue()),PRICE_CLOSE); //--- Custom if(applied==8) { string str[]; double arr[]; string parameters=m_custom_param.GetValue(); StringSplit(parameters,',',str); if(ArraySize(str)>20) { if(m_lang_index==0) MessageBox("Количество параметров не должно быть больше 20!","Ошибка",MB_OK); else if(m_lang_index==1) MessageBox("The number of parameters should not be more than 20!","Error",MB_OK); } ArrayResize(arr,ArraySize(str)); for(int i=0; i<ArraySize(str); i++) arr[i]=StringToDouble(str[i]); string name=m_custom_path.GetValue(); Handle=GetCustomValue(StringToTimeframe(tf),name,arr); } //--- if(applied>0) { if(Handle==INVALID_HANDLE) { if(m_lang_index==0) MessageBox("Не удалось получить хендл индикатора!","Ошибка",MB_OK); else if(m_lang_index==1) MessageBox("Failed to get indicator handle!","Error",MB_OK); } double arr[]; int buffer=(applied==8)?int(m_custom_buffer.GetValue()):0; int copied=CopyBuffer(Handle,buffer,m_start_date,5,arr); //--- int sl=0,tp=0; POINTS pat; if(copied<5) return(false); //--- Pattern search condition pat.A=arr[0]; pat.B=arr[1]; pat.C=arr[2]; pat.D=arr[3]; pat.E=arr[4]; //--- If the pattern is found, check the signal if(GetPatternType(pat)==m_combobox1.GetListViewPointer().SelectedItemIndex()) { m_start_date=IndexToDate(m_start_date,StringToTimeframe(tf),5); return(true); } return(false); } return(false); } return(false); }
Die Idee der Methode liegt in der vorgegebenen Reihenfolge der Aktionen:
- Überprüfung, ob ein Kaufsignal erlaubt ist und Überprüfung des spezifischen Signals.
- Überprüfung des Datenarrays, auf das die Muster angewendet werden sollen.
- Vorbereitung der Daten für die Suche und nach dem angegebenen Muster mit der Methode GetPatternType() suchen.
2. Verfahren zur Verarbeitung des gefundenen Signals CalculateBuyDeals() und CalculateSellDeals().
//+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ void CProgram::CalculateBuyDeals(const string symbol,datetime start) { MqlRates rt[]; int TP=int(m_takeprofit1.GetValue()); int SL=int(m_stoploss1.GetValue()); string tf=m_timeframe1.GetListViewPointer().SelectedItemText(); int copied=CopyRates(symbol,StringToTimeframe(tf),m_start_date,m_end_date,rt); double deal_price=iOpen(symbol,StringToTimeframe(tf),copied); for(int j=0; j<copied; j++) { if((iHigh(symbol,StringToTimeframe(tf),copied-j)-deal_price)/SymbolInfoDouble(symbol,SYMBOL_POINT)>=TP) { m_report.profit_trades++; m_report.profit+=TP; m_report.long_trades++; m_report.total_trades++; m_start_date=IndexToDate(m_start_date,StringToTimeframe(tf),j); return; } else if((deal_price-iLow(symbol,StringToTimeframe(tf),copied-j))/SymbolInfoDouble(symbol,SYMBOL_POINT)>=SL) { m_report.loss_trades++; m_report.profit-=SL; m_report.long_trades++; m_report.total_trades++; m_start_date=IndexToDate(m_start_date,StringToTimeframe(tf),j); return; } } m_start_date=m_end_date; } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ void CProgram::CalculateSellDeals(const string symbol,datetime start) { MqlRates rt[]; int TP=int(m_takeprofit2.GetValue()); int SL=int(m_stoploss2.GetValue()); string tf=m_timeframe1.GetListViewPointer().SelectedItemText(); int copied=CopyRates(symbol,StringToTimeframe(tf),m_start_date,m_end_date,rt); double deal_price=iOpen(symbol,StringToTimeframe(tf),copied); for(int j=0; j<copied; j++) { if((deal_price-iLow(symbol,StringToTimeframe(tf),copied-j))/SymbolInfoDouble(symbol,SYMBOL_POINT)>=TP) { m_report.profit_trades++; m_report.profit+=TP; m_report.short_trades++; m_report.total_trades++; m_start_date=IndexToDate(m_start_date,StringToTimeframe(tf),j); return; } else if((iHigh(symbol,StringToTimeframe(tf),copied-j)-deal_price)/SymbolInfoDouble(symbol,SYMBOL_POINT)>=SL) { m_report.loss_trades++; m_report.profit-=SL; m_report.short_trades++; m_report.total_trades++; m_start_date=IndexToDate(m_start_date,StringToTimeframe(tf),j); return; } } m_start_date=m_end_date; }
Ihre Aufgabe ist es, das gefundene Signal zu verarbeiten und Statistiken aufzuzeichnen, anhand derer der Bericht erstellt wird.
3. Die Methode PrintReport(), die Testergebnisse ausgibt.
//+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ void CProgram::PrintReport(void) { if(m_lang_index==0) { string report_label[6]= { "Всего трейдов: ","Короткие трейды: ","Прибыльные трейды: ", "Прибыль в пунктах: ","Длинные трейды: ","Убыточные трейды: " }; //--- m_report_text[0].LabelText(report_label[0]+string(m_report.total_trades)); m_report_text[1].LabelText(report_label[1]+string(m_report.short_trades)); m_report_text[2].LabelText(report_label[2]+string(m_report.profit_trades)); m_report_text[3].LabelText(report_label[3]+string(m_report.profit)); m_report_text[4].LabelText(report_label[4]+string(m_report.long_trades)); m_report_text[5].LabelText(report_label[5]+string(m_report.loss_trades)); } else { string report_label[6]= { "Total trades: ","Short Trades: ","Profit Trades: ", "Profit in points: ","Long Trades: ","Loss Trades: " }; //--- m_report_text[0].LabelText(report_label[0]+string(m_report.total_trades)); m_report_text[1].LabelText(report_label[1]+string(m_report.short_trades)); m_report_text[2].LabelText(report_label[2]+string(m_report.profit_trades)); m_report_text[3].LabelText(report_label[3]+string(m_report.profit)); m_report_text[4].LabelText(report_label[4]+string(m_report.long_trades)); m_report_text[5].LabelText(report_label[5]+string(m_report.loss_trades)); } Update(true); }
Das zeigt die Testdaten in der Anwendung an. Damit ist der Algorithmus vervollständigt.
Demonstration und Beispiel für den Betrieb des Strategieentwicklers
Als Beispiel entschied ich mich, ein kurzes Video aufzunehmen, das die Funktionsweise des Strategieentwicklers zeigt.
Schlussfolgerung
Das unten angehängte Archiv enthält alle beschriebenen Dateien in Ordnern. Für einen korrekten Betrieb speichern Sie den Ordner MQL5 im Stammverzeichnis des Terminals. Um das Terminal-Stammverzeichnis zu öffnen, in dem sich der Ordner MQL5 befindet, drücken Sie die Tastenkombination Ctrl+Shift+D im MetaTrader 5 Terminal oder verwenden Sie das Kontextmenü wie in Abb. 7 unten gezeigt.
Abb. 14. Öffnen des Ordners MQL5 im Stammordner von MetaTrader 5
Übersetzt aus dem Russischen von MetaQuotes Ltd.
Originalartikel: https://www.mql5.com/ru/articles/7218
- 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.
Ich habe das Zip-Archiv entpackt und alle Dateien an ihren Bestimmungsort kopiert.
Nach Compilierung wird der EA nicht geladen und es erscheint im Reiter Experten des Terminals die Meldung:
"CElement::CreateCanvas > Failed to create a canvas for drawing the (CButton) control: 4016"
Wer hat eine Idee den EA zum Laufen zu bekommen?
traderdoc
Ich habe das Zip-Archiv entpackt und alle Dateien an ihren Bestimmungsort kopiert.
Nach Compilierung wird der EA nicht geladen und es erscheint im Reiter Experten des Terminals die Meldung:
"CElement::CreateCanvas > Failed to create a canvas for drawing the (CButton) control: 4016"
Wer hat eine Idee den EA zum Laufen zu bekommen?
traderdoc
Wahrscheinlich nutzt du Build 2280.
In der Canvas.mqh ist ein Bug drin.
Entferne folgenden String "(string)CharId + " aus der Zeile 254.
Danach neu kompilieren und der Fehler sollte nicht mehr auftreten.
Zeile 254 in Canvas.mqh nach der Korrektur:
Gruß
Ja vielen Dank!
Bis zu der Stelle war im inzwischen auch gekommen und hatte die urspünglichen Zeile
m_rcname="::"+name+(string)ChartID()+(string)(GetTickCount()+MathRand());
dann auf
m_rcname="::"+name+(string)ChartID();
verkürzt.
Das funktioniert auch.
traderdoc
Ja vielen Dank!
Bis zu der Stelle war im inzwischen auch gekommen und hatte die urspünglichen Zeile
m_rcname="::"+name+(string)ChartID()+(string)(GetTickCount()+MathRand());
dann auf
m_rcname="::"+name+(string)ChartID();
verkürzt.
Das funktioniert auch.
traderdoc
Im Prinzip ist nur der generierte Name zu lang.
Ob die Zufallskomponente GetTickCount() wichtig ist weiß ich nicht. Probiere oder nutze den ganzen GFX Kram nicht.
Bist du der traderdoc aus bekannten Foren ?
Gruß