Untersuchung von Techniken zur Analyse der Kerzen (Teil IV): Aktualisierungen und Ergänzungen des Pattern Analyzers
Inhaltsverzeichnis
- Einführung
- Update Übersicht
- Implementierung des Updates
- Struktur der Anwendungsfenster. Erstellungsmethode des Hauptfensters
- Struktur der Anwendungsfenster. Struktur der Anwendungsfenster.
- Berechnungsteil. Überarbeitete Verfahren zum Finden von Kerzen und Muster
- Schlussfolgerung
Einführung
In früheren Artikeln dieser Serie haben wir eine MetaTrader 5 App erstellt, die die Relevanz bestehender Kerzenmuster testet. Eine neuere Version bot die Möglichkeit, eigene Muster auf der Grundlage einfacher Kerzentypen wie kurze und lange Kerzen, Doji, Spinning Top usw. zu erstellen. Im letzten Teil haben wir eine Bibliothek zur Erstellung von Indikatoren und Expert Advisors auf Basis von Kerzenmustern entwickelt.
Dieser Artikel präsentiert eine neue Version des Pattern Analyzers. Diese Version enthält Bugfixes und neue Funktionen sowie eine überarbeitete Benutzeroberfläche. Kommentare und Anregungen aus dem vorherigen Artikel wurden bei der Entwicklung der neuen Version berücksichtigt. Die resultierende Anwendung wird in diesem Artikel beschrieben.
Update Übersicht
Die Benutzeroberfläche ist ein wichtiger Bestandteil jeder Anwendung: Eine gut vorbereitete Oberfläche macht die Nutzung der Anwendung effizienter. Wir werden das neue Aussehen der Anwendung mit dem vorherigen vergleichen. Beginnen wir mit der Registerkarte Analyse: Warum brauchte sie Verbesserungen?
Abb.1 Aussehen der Registerkarte Analyze in der vorherigen Version
Punkt 1. Anordnung und Größen der Registerkarten.
In Abb.1 befinden sich die mit 1 gekennzeichneten Registerkarten im oberen Teil des Fensters. Der obere rechte Teil hier ist leer und wird nicht verwendet, aber der Teil reicht nicht aus, um weitere Registerkarten hinzuzufügen. Die Schriftart ist zu klein. Diese drei Registerkarten wurden in den linken Teil des Fensters verschoben: Sie sind nun vertikal angeordnet und besser sichtbar. Außerdem gibt es zusätzlichen Platz, um weitere Abschnitte hinzuzufügen.
Punkt 2. Tabellen mit den Ergebnissen der Mustersuche.
Die visuelle Datenpräsentation ist nicht sehr effizient. Daher wurden zur besseren Lesbarkeit die Schriftart, die Zeilenhöhe und die Tabellengröße erhöht.
Punkt 3. Auswahl des aktuellen Zeitrahmens.
Die Auswahlstruktur 'Timeframe -> Result' für alle Muster schränkt die visuelle Darstellung der Testergebnisse ein. Um dies zu verbessern, werden wir eine Auswahlmöglichkeit für mehrere Zeitrahmen sowie eine individuelle Auswahl der analysierten Muster entwickeln. Dies ermöglicht eine flexiblere Anpassung der Bedienung mit Mustern.
Punkt 4. Die Zeitspanne.
Die in der Vorgängerversion implementierte Idee war es, im Bereich der aktuellen Daten bis hin zu einer bestimmten Anzahl von Kerzen in der Historie zu testen. Eine spezifischere Auswahl von einem Datum bis zu einem anderen war nicht möglich. Daher wird die Methode der Auswahl der Zeitspanne überarbeitet. Abb.2 unten zeigt die Lösung aller oben genannten Probleme und mögliche Verbesserungen.
Abb. 2. Aktualisierte Schnittstelle der Registerkarte Analyze.
Hier sind die Lösungen zu den oben genannten Punkten.
- Vertikale Anordnung der Registerkarten im linken Teil des Fensters.
- Manuelle Auswahl der zu analysierenden Muster.
- Manuelle Auswahl der aktuellen Zeitrahmen.
- Ein neues Hilfsmittel 'Date range' für den Testbereich, anstatt die Anzahl der Kerzen zu verwenden.
Das Anwendungsfenster wurde größer, um neue Elemente anzuzeigen. Eine weitere wichtige Neuerung ist, dass der Parameter "Trend threshold (points)" (Abb.3) von der Registerkarte Settings (Einstellungen) auf die Registerkarten Analyze und AutoSearch (autom. Suche) verschoben wurde. Die Einstellung ist für jede der Registerkarten individuell.
Abb.3 Neue Position des Parameters "Trend threshold (points)"
Das letzte Element, das geändert wurde, ist die Struktur der Ergebnistabelle. Die Spalte Occurrence wurde entfernt und stattdessen der wichtigere Parameter Timeframe hinzugefügt.
Abb. 4. Die neue Struktur der Ergebnistabelle
Betrachten wir nun die Verbesserungen der zweiten Registerkarte, der AutoSearch (autom. Suche), die mit erstellten Mustern arbeitet.
Punkt 1. Einstellungen in verschiedenen Registerkarten.
Einstellungen, die sich direkt auf den Abschnitt AutoSearch beziehen, befinden sich auf der Registerkarte Einstellungen, so dass man ständig zwischen den Registerkarten AutoSearch und Einstellung wechseln musste, um die Einstellungen zu ändern. Deshalb wurden fast alle Einstellungen auf die Registerkarte AutoSearch verschoben. Weitere Verbesserungen wurden in Bezug auf den Schwellentrend, die Auswahl der aktuellen Zeiträume und den Datumsbereich vorgenommen. Das Ergebnis der Aktualisierung der Registerkarte AutoSearch ist in Abb.5 dargestellt.
Abb.5 Aktualisierte Funktionen in der Registerkarte AutoSearch
Dies ermöglicht ein komfortableres Arbeiten mit den Mustern. Der Datumsbereich auf dieser Registerkarte ist ebenfalls individuell.
Implementierung des Updates
Lassen Sie uns die Umsetzung der oben genannten Aktualisierungen sowie Änderungen in den Berechnungen genauer betrachten.
Struktur der Anwendungsfenster. Struktur des Anwendungsfensters.
Die Methode CProgram::CreateGUI(), die für die Erstellung der grafischen Oberfläche zuständig ist, wurde ergänzt:
//+------------------------------------------------------------------+ //| Erstellen der grafischen Schnittstelle des Programms | //+------------------------------------------------------------------+ bool CProgram::CreateGUI(void) { //--- Panel erstelle if(!CreateWindow("Pattern Analyzer")) return(false); //--- Erstellen des Dialogfensters if(!CreateWindowSetting1("Settings")) return(false); //--- Erstellen des Dialogfensters if(!CreateWindowSetting2("Date range settings")) return(false); //--- Erstellen des Dialogfensters if(!CreateWindowSetting3("Date range settings")) return(false); //--- Komplettes Erstellen der GUI CWndEvents::CompletedGUI(); return(true); } //+-----------------------------------------------------------------
CreateWindowSetting2() und CreateWindowSetting3() sind für die Anzeige der neuen Auswahl der Zeitspanne in Abb.1 verantwortlich. Die Erstellungsmethode für das Hauptapplikationsfenster CreateWindow() wurde ebenfalls neu gestaltet. Es wurde in drei Blöcke unterteilt, die den Oberflächenelementen der einzelnen Registerkarten entsprechen: Analyze, AutoSearch und Setting.
//+------------------------------------------------------------------+ //| Registerkarte Analyze | //+------------------------------------------------------------------+ //--- Erstellen der Schaltflächen für eine Reihe von Mustern if(!CreatePatternSet(m_patterns,10,10)) return(false); //--- Kopfzeile der Zeitrahmen if(!CreateTFLabel(m_text_labels[1],10,100,0)) return(false); //--- Erstellen der Schaltflächen der Zeitrahmen if(!CreateTimeframeSet(m_timeframes,10,125,0)) return(false); //--- Suchfenster für die Symbole if(!CreateSymbolsFilter(m_symb_filter1,m_request1,10,180,0)) return(false); //--- Erstellen einer Schaltfläche zur Auswahl der Zeitspanne if(!CreateDateRange(m_request3,280,180,0)) return(false); //--- Erstellen eines Eingabefelds für den Schwellenwert des Gewinns if(!CreateThresholdValue(m_threshold1,400,180,100,0)) return(false); //--- Erstellen einer Symboltabelle if(!CreateSymbTable(m_symb_table1,10,225,0)) return(false); //--- Erstellen einer Ergebnistabelle if(!CreateTable1(m_table1,120,225,0)) return(false);
In der ersten Registerkarte wurden die Methoden zur Anzeige neuer Oberflächenelemente hinzugefügt.
- CreatePatternSet(). Eine neue Methode, die eine Reihe von schaltbaren Musterauswahltasten anzeigt.
- CreateTFLabel(). Kennzeichnung für eine Reihe von Zeitrahmen.
- CreateTimeframeSet(). Eine Reihe von Schaltflächen der Zeitrahmen.
- CreateDateRange(). Eine neue Schaltfläche, die beim Anklicken das Dialogfenster zur Auswahl des Datumsbereichs für die Analyse öffnet.
- CreateThresholdValue(). Ein überarbeitetes Verfahren, das den Trendschwellenwert in Punkten anzeigt (Abb.3).
//+------------------------------------------------------------------+ //| Registerkarte AutoSearch | //+------------------------------------------------------------------+ if(!CreateTFLabel(m_text_labels[4],10,10,1)) return(false); //--- Tasten if(!CreateDualButton(m_buttons[6],m_buttons[7],200,50)) return(false); if(!CreateTripleButton(m_buttons[8],m_buttons[9],m_buttons[10],10,50)) return(false); //--- Kopfzeile der Zeitrahmen if(!CreateTFLabel(m_text_labels[5],10,100,1)) return(false); //--- Erstellen der Schaltflächen der Zeitrahmen if(!CreateTimeframeSet(m_timeframes1,10,125,1)) return(false); //--- Eingabefelder if(!CreateSymbolsFilter(m_symb_filter2,m_request2,10,180,1)) return(false); //--- Erstellen einer Schaltfläche zur Auswahl der Zeitspanne if(!CreateDateRange(m_request4,280,180,1)) return(false); //--- Erstellen eines Eingabefelds für den Schwellenwert des Gewinns if(!CreateThresholdValue(m_threshold2,400,180,100,1)) return(false); //--- Erstellen einer Symboltabelle if(!CreateSymbTable(m_symb_table2,10,225,1)) return(false); //--- Erstellen einer Ergebnistabelle if(!CreateTable2(m_table2,120,225,1)) return(false);
Auch die folgenden Methoden wurden von der Registerkarte Setting in die zweite Registerkarte AutoSearch verschoben (Abb.5): Methoden, die für die Anzeige von Elementen verantwortlich sind, die sich auf die Auswahl der erzeugten Mustergrößen beziehen CreateTripleButton() und die schaltbare Option Repeat/No repeat (mit/ohne Wiederholung) mit der Methode CreateDualButton(). Neue Methoden wurden hinzugefügt: Methoden, die für den Spaltenkopf des Zeitrahmens und die Auswahl verantwortlich sind.
//+------------------------------------------------------------------+ //| Registerkarte Settings | //+------------------------------------------------------------------+ //--- Erstellen der Einstellungen der Kerzen if(!CreateCandle(m_pictures[0],m_buttons[0],m_candle_names[0],"Long",10,10,"Images\\EasyAndFastGUI\\Candles\\long.bmp")) return(false); if(!CreateCandle(m_pictures[1],m_buttons[1],m_candle_names[1],"Short",104,10,"Images\\EasyAndFastGUI\\Candles\\short.bmp")) return(false); if(!CreateCandle(m_pictures[2],m_buttons[2],m_candle_names[2],"Spinning top",198,10,"Images\\EasyAndFastGUI\\Candles\\spin.bmp")) return(false); if(!CreateCandle(m_pictures[3],m_buttons[3],m_candle_names[3],"Doji",292,10,"Images\\EasyAndFastGUI\\Candles\\doji.bmp")) return(false); if(!CreateCandle(m_pictures[4],m_buttons[4],m_candle_names[4],"Marubozu",386,10,"Images\\EasyAndFastGUI\\Candles\\maribozu.bmp")) return(false); if(!CreateCandle(m_pictures[5],m_buttons[5],m_candle_names[5],"Hammer",480,10,"Images\\EasyAndFastGUI\\Candles\\hammer.bmp")) return(false); //--- Textbezeichner if(!CreateTextLabel(m_text_labels[0],10,140)) return(false); if(!CreateTextLabel(m_text_labels[3],300,140)) return(false); //--- Eingabefelder if(!CreateCoef(m_coef1,10,180,"K1",1)) return(false); if(!CreateCoef(m_coef2,100,180,"K2",0.5)) return(false); if(!CreateCoef(m_coef3,200,180,"K3",0.25)) return(false); if(!CreateLanguageSetting(m_lang_setting,10,240,2)) return(false); //--- Listenansicht if(!CreateListView(300,180)) return(false); //--- if(!CreateCheckBox(m_checkbox1,300+8,160,"All candlesticks")) return(false); //--- Statusleiste if(!CreateStatusBar(1,26)) return(false);
Der Abschnitt Setting enthält nun weniger Elemente. Er enthält Einstellungen für individuelle Kerzen, Einstellungen der Verhältnisse für die Berechnung von Wahrscheinlichkeits- und Effizienzkoeffizienten, Auswahl der Schnittstellensprache und Kerzenauswahl für die Mustergenerierung in der Registerkarte AutoSearch Als Nächstes betrachten wir die neuen Methoden etwas genauer.
Verfahren zum Erzeugen einer Musterauswahl CreatePatternSet(). Es handelt sich um eine Reihe von Schaltflächen zur Auswahl vorhandener Muster für die Analyse.
Abb.6 Das Prinzip der Musterauswahl für die Analyse
Die Implementierung wird im Folgenden dargestellt:
//+------------------------------------------------------------------+ //| Erstellen einer Reihe von Schaltflächen für Muster | //+------------------------------------------------------------------+ bool CProgram::CreatePatternSet(CButton &button[],int x_gap,int y_gap) { ArrayResize(button,15); string pattern_names[15]= { "Hummer", "Invert Hummer", "Handing Man", "Shooting Star", "Engulfing Bull", "Engulfing Bear", "Harami Cross Bull", "Harami Cross Bear", "Harami Bull", "Harami Bear", "Doji Star Bull", "Doji Star Bear", "Piercing Line", "Dark Cloud Cover", "All Patterns" }; int k1=x_gap,k2=x_gap,k3=x_gap; for(int i=0;i<=14;i++) { if(i<5) { CreatePatternButton(button[i],pattern_names[i],k1,y_gap); k1+=150; } else if(i>=5 && i<10) { CreatePatternButton(button[i],pattern_names[i],k2,y_gap+30); k2+=150; } else if(i>=10 && i<14) { CreatePatternButton(button[i],pattern_names[i],k3,y_gap+60); k3+=150; } else if(i==14) { CreatePatternButton(button[i],pattern_names[i],k3,y_gap+60); } } return(true); } //+------------------------------------------------------------------+ //| Erstellen einer Schaltfläche zur Musterauswahl für die Analyse | //+------------------------------------------------------------------+ #resource "\\Images\\EasyAndFastGUI\\Candles\\passive.bmp" #resource "\\Images\\EasyAndFastGUI\\Candles\\pressed.bmp" //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ bool CProgram::CreatePatternButton(CButton &button,const string candlename,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(120); button.YSize(20); button.Font("Trebuchet"); button.FontSize(9); button.LabelColor(clrWhite); button.LabelColorHover(clrWhite); button.LabelColorPressed(clrWhite); button.IsCenterText(true); button.TwoState(true); button.IconFile("Images\\EasyAndFastGUI\\Candles\\passive.bmp"); button.IconFilePressed("Images\\EasyAndFastGUI\\Candles\\pressed.bmp"); //--- Erstellen des Kontrollelements if(!button.CreateButton(candlename,x_gap,y_gap)) return(false); //--- Hinzufügen eines Pointers auf das Element auf die Datenbasis CWndContainer::AddToElementsArray(0,button); return(true); }
Achten Sie auf die letzte Schaltfläche 'All Patterns', die alle Muster aus-/abwählt. Der Klick wird durch einen Zusatzcode im Abschnitt Ereignisübergabe eines Klicks auf die Schaltfläche verarbeitet:
if(id==CHARTEVENT_CUSTOM+ON_CLICK_BUTTON) { //--- Alle Mustertasten aus-/abwählen if(lparam==m_patterns[14].Id()) { if(m_patterns[14].IsPressed()) { for(int i=0;i<14;i++) m_patterns[i].IsPressed(true); } else if(!m_patterns[14].IsPressed()) { for(int i=0;i<14;i++) m_patterns[i].IsPressed(false); } for(int i=0;i<14;i++) m_patterns[i].Update(true); } ... }
Die aktuelle Methode zu Auswahl der Zeitrahmen CreateTimeframeSet() ist der vorherigen sehr ähnlich. Sie verfügt auch über eine Reihe von schaltbaren Schaltflächen, die Zeitrahmen für die Analyse auswählen.
Abb.7 Das Prinzip der Schaltfläche der Zeitrahmenauswahl für die Analyse
Die Implementierung ist im folgenden Code dargestellt:
//+------------------------------------------------------------------+ //| Erstellen einer Reihe von Schaltflächen für die Zeitrahmen | //+------------------------------------------------------------------+ bool CProgram::CreateTimeframeSet(CButton &button[],int x_gap,int y_gap,const int tab) { ArrayResize(button,22); string timeframe_names[22]= {"M1","M2","M3","M4","M5","M6","M10","M12","M15","M20","M30","H1","H2","H3","H4","H6","H8","H12","D1","W1","MN","ALL"}; int k1=x_gap,k2=x_gap; for(int i=0;i<22;i++) { CreateTimeframeButton(button[i],timeframe_names[i],k1,y_gap,tab); k1+=33; } return(true); } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ bool CProgram::CreateTimeframeButton(CButton &button,const string candlename,const int x_gap,const int y_gap,const int tab) { //--- Sichern des Zeigers auf das Hauptsteuerelement button.MainPointer(m_tabs1); //--- Hinzufügen zur Registerkarte m_tabs1.AddToElementsArray(tab,button); //--- Eigenschaften button.XSize(30); button.YSize(30); button.Font("Trebuchet"); button.FontSize(10); button.LabelColor(clrWhite); button.LabelColorHover(clrWhite); button.LabelColorPressed(clrWhite); button.BackColor(C'200,200,200'); button.BackColorHover(C'200,200,200'); button.BackColorPressed(C'50,180,75'); button.BorderColor(C'200,200,200'); button.BorderColorHover(C'200,200,200'); button.BorderColorPressed(C'50,180,75'); button.IsCenterText(true); button.TwoState(true); //--- Erstellen des Kontrollelements if(!button.CreateButton(candlename,x_gap,y_gap)) return(false); //--- Hinzufügen eines Pointers auf das Element auf die Datenbasis CWndContainer::AddToElementsArray(0,button); return(true); }
Sie verfügt auch über eine Schaltfläche zum Aus-/Abwahl aller Zeitrahmen und wird im Abschnitt Handhabung von Tastenkicks bearbeitet:
//--- Alle Mustertasten aus-/abwählen if(lparam==m_timeframes[21].Id()) { if(m_timeframes[21].IsPressed()) { for(int i=0;i<21;i++) m_timeframes[i].IsPressed(true); } else if(!m_timeframes[21].IsPressed()) { for(int i=0;i<21;i++) m_timeframes[i].IsPressed(false); } for(int i=0;i<21;i++) m_timeframes[i].Update(true); }
Das nächste, neue Element ist die Schaltfläche 'Date Range'. Es ist Teil des neuen Tools zur Einstellung der Zeitspanne. Es wird mit der Methode CreateDateRange() implementiert.
Abb.8 Das Prinzip der Auswahl des Datumsbereichs für die Analyse
Die Implementierung wird im Folgenden dargestellt:
//+------------------------------------------------------------------+ //| Die Schaltfläche, das Fenster der Zeitspannenauswahl anzuzeigen | //+------------------------------------------------------------------+ bool CProgram::CreateDateRange(CButton &button,const int x_gap,const int y_gap,const int tab) { //--- Sichern des Zeigers auf das Hauptsteuerelement button.MainPointer(m_tabs1); //--- Hinzufügen zur Registerkarte m_tabs1.AddToElementsArray(tab,button); //--- Eigenschaften button.XSize(100); button.YSize(25); button.Font("Trebuchet"); button.FontSize(10); button.IsHighlighted(false); button.IsCenterText(true); button.BorderColor(C'0,100,255'); button.BackColor(clrAliceBlue); //--- Erstellen des Kontrollelements if(!button.CreateButton("",x_gap,y_gap)) return(false); //--- Hinzufügen eines Pointers auf das Element auf die Datenbasis CWndContainer::AddToElementsArray(0,button); return(true); }
Die Ereignisbehandlung eines Tastenklicks beinhaltet auch einen Code, der für die Anzeige der Dialogbox mit dem Datumsbereich verantwortlich ist:
//--- if(lparam==m_request3.Id()) { int x=m_request3.X(); int y=m_request3.Y()+m_request3.YSize(); m_window[2].X(x); m_window[2].Y(y); m_window[2].OpenWindow(); val=(m_lang_index==0)?"Настройки диапазона дат":"Date Range Settings"; m_window[2].LabelText(val); }
Es ist nicht erforderlich, neue Elemente zu beschreiben, die auf der Registerkarte hinzugefügt wurden, da sie der Implementierung der Elemente der Registerkarte Analyze ähnlich sind, mit Ausnahme der Koordinatenparameter. Betrachten wir daher Methoden, die für die Anzeige anderer neuer Fenster verantwortlich sind.
Struktur der Anwendungsfenster. Eine Methode zum Erstellen des Anwendungsdialogfensters.
Die Methoden, die Dialogfelder für die Registerkarten Analyze und AutoSearch anzeigen, sind ähnlich und daher werden wir eine davon berücksichtigen.
//+------------------------------------------------------------------+ //| Erstellt einen Dialog zur Auswahl eines Datumsbereichs in Analyze| //+------------------------------------------------------------------+ bool CProgram::CreateWindowSetting2(const string caption_text) { //--- Hinzufügen eines Zeigers auf das Array des Fensters CWndContainer::AddWindow(m_window[2]); //--- Koordinaten int x=m_request3.X(); int y=m_request3.Y()+m_request3.YSize(); //--- Eigenschaften m_window[2].XSize(372); m_window[2].YSize(300); m_window[2].WindowType(W_DIALOG); //--- Erstellen der Formulars if(!m_window[2].CreateWindow(m_chart_id,m_subwin,caption_text,x,y)) return(false); //--- if(!CreateCalendar(m_calendar1,m_window[2],10,25,D'01.01.2018',2)) return(false); if(!CreateCalendar(m_calendar2,m_window[2],201,25,m_calendar2.Today(),2)) return(false); //--- if(!CreateTimeEdit(m_time_edit1,m_window[2],10,200,"Time",2)) return(false); if(!CreateTimeEdit(m_time_edit2,m_window[2],200,200,"Time",2)) return(false); //--- return(true); }
Berechnungsteil. Neu gestaltete Verfahren, Kerzen und Muster zu finden.
Aufgrund gravierender Änderungen in der Struktur der Benutzeroberfläche sowie durch Hinzufügen neuer Elemente und Löschen einiger alter Elemente haben sich auch die Berechnungsmethoden geändert. In der aktuellen Anwendung gibt es zwei Verfahren: Das erste wird für bestehende Muster und das zweite für generierte Muster verwendet.
Die Berechnung wird nach einem Klick auf eines der verfügbaren Handelsinstrumente in der Tabelle Symbole gestartet. Diese Regel gilt für die beiden Registerkarten Analyze und AutoSearch. Eine der beiden Methoden wird abhängig von der Registerkarte aufgerufen.
//+------------------------------------------------------------------+ //| Symbol in der Registerkarte Analyze ändern | //+------------------------------------------------------------------+ bool CProgram::ChangeSymbol1(const long id) { //--- Prüfen der Elemente-ID if(id!=m_symb_table1.Id()) return(false); //--- Verlassen, wenn die Zeile nicht ausgewählt wurde if(m_symb_table1.SelectedItem()==WRONG_VALUE) { //--- Anzeige der gesamten Symbolbeschreibung in der Statusleiste m_status_bar.SetValue(0,"No symbol selected for analysis"); m_status_bar.GetItemPointer(0).Update(true); return(false); } //--- Abrufen des Symbols string symbol=m_symb_table1.GetValue(0,m_symb_table1.SelectedItem()); //--- Anzeige der gesamten Symbolbeschreibung in der Statusleiste string val=(m_lang_index==0)?"Выбранный символ: ":"Selected symbol: "; m_status_bar.SetValue(0,val+::SymbolInfoString(symbol,SYMBOL_DESCRIPTION)); m_status_bar.GetItemPointer(0).Update(true); //--- GetPatternType(symbol); return(true); } //+------------------------------------------------------------------+ //| Symbolwechsel in der Registerkarte AutoSearch | //+------------------------------------------------------------------+ bool CProgram::ChangeSymbol2(const long id) { //--- Prüfen der Elemente-ID if(id!=m_symb_table2.Id()) return(false); //--- Verlassen, wenn die Zeile nicht ausgewählt wurde if(m_symb_table2.SelectedItem()==WRONG_VALUE) { //--- Anzeige der gesamten Symbolbeschreibung in der Statusleiste m_status_bar.SetValue(0,"No symbol selected for analysis"); m_status_bar.GetItemPointer(0).Update(true); return(false); } //--- Abrufen des Symbols string symbol=m_symb_table2.GetValue(0,m_symb_table2.SelectedItem()); //--- Anzeige der gesamten Symbolbeschreibung in der Statusleiste string val=(m_lang_index==0)?"Выбранный символ: ":"Selected symbol: "; m_status_bar.SetValue(0,val+::SymbolInfoString(symbol,SYMBOL_DESCRIPTION)); m_status_bar.GetItemPointer(0).Update(true); //--- if(!GetCandleCombitation()) { if(m_lang_index==0) MessageBox("Число выбранных свечей меньше размера исследуемого паттерна!","Ошибка",MB_OK); else if(m_lang_index==1) MessageBox("The number of selected candles is less than the size of the studied pattern!","Error",MB_OK); return(false); } //--- GetPatternType(symbol,m_total_combination); return(true); }
Die Methode GetPattertType() mit zwei verschiedenen Argumenttypen wird am Ende jeder Methode aufgerufen. Dies ist die Schlüsselmethode bei der Suche nach Mustern und bei der Handhabung der erzielten Ergebnisse. Nun lassen Sie uns jede der Methoden im Detail betrachten.
Der erste Methodentyp wird verwendet, um nach vorhandenen Mustern zu suchen.
bool GetPatternType(const string symbol);
Die Methode hat eine recht lange Implementierung, daher wird hier nur das Beispiel für ein Muster angegeben.
//+------------------------------------------------------------------+ //| Mustererkennung | //+------------------------------------------------------------------+ bool CProgram::GetPatternType(const string symbol) { CANDLE_STRUCTURE cand1,cand2; //--- RATING_SET hummer_coef[]; RATING_SET invert_hummer_coef[]; RATING_SET handing_man_coef[]; RATING_SET shooting_star_coef[]; RATING_SET engulfing_bull_coef[]; RATING_SET engulfing_bear_coef[]; RATING_SET harami_cross_bull_coef[]; RATING_SET harami_cross_bear_coef[]; RATING_SET harami_bull_coef[]; RATING_SET harami_bear_coef[]; RATING_SET doji_star_bull_coef[]; RATING_SET doji_star_bear_coef[]; RATING_SET piercing_line_coef[]; RATING_SET dark_cloud_cover_coef[]; //--- Datenabruf für den gewählte Zeitrahmen GetTimeframes(m_timeframes,m_cur_timeframes1); int total=ArraySize(m_cur_timeframes1); //--- Prüfen von zumindest einem ausgewählten Zeitrahmen if(total<1) { if(m_lang_index==0) MessageBox("Вы не выбрали рабочий таймфрейм!","Ошибка",MB_OK); else if(m_lang_index==1) MessageBox("You have not selected a working timeframe!","Error",MB_OK); return(false); } int count=0; m_total_row=0; m_table_number=1; //--- Löschen aller Zeilen m_table1.DeleteAllRows(); //--- Abrufen der Zeitspanne datetime start=StringToTime(TimeToString(m_calendar1.SelectedDate(),TIME_DATE)+" "+(string)m_time_edit1.GetHours()+":"+(string)m_time_edit1.GetMinutes()+":00"); datetime end=StringToTime(TimeToString(m_calendar2.SelectedDate(),TIME_DATE)+" "+(string)m_time_edit2.GetHours()+":"+(string)m_time_edit2.GetMinutes()+":00"); //--- Prüfen des angegebenen Zeitpunkts if(start>end || end>TimeCurrent()) { if(m_lang_index==0) MessageBox("Неправильно выбран диапазон дат!","Ошибка",MB_OK); else if(m_lang_index==1) MessageBox("Incorrect date range selected!","Error",MB_OK); return(false); } //--- Hammer, Aufwärtsmuster if(m_patterns[0].IsPressed()) { ArrayResize(m_hummer_total,total); ArrayResize(hummer_coef,total); ZeroMemory(m_hummer_total); ZeroMemory(hummer_coef); ZeroMemory(cand1); count++; //--- Berechnung je nach Zeitrahmen for(int j=0;j<total;j++) { MqlRates rt[]; ZeroMemory(rt); int copied=CopyRates(symbol,m_cur_timeframes1[j],start,end,rt); for(int i=0;i<copied;i++) { GetCandleType(symbol,cand1,m_cur_timeframes1[j],i); // Aktuelle Kerze if(cand1.trend==DOWN && // Prüfen der Trendrichtung cand1.type==CAND_HAMMER) // Prüfen des "Hammer" { m_hummer_total[j]++; GetCategory(symbol,i+3,hummer_coef[j],m_cur_timeframes1[j],m_threshold_value1); } } AddRow(m_table1,"Hammer",hummer_coef[j],m_hummer_total[j],m_cur_timeframes1[j]); } } ... //--- if(count>0) { //--- m_table1.DeleteRow(m_total_row); //--- Aktualisieren der Tabelle m_table1.Update(true); m_table1.GetScrollVPointer().Update(true); } else { if(m_lang_index==0) MessageBox("Вы не выбрали паттерн!","Ошибка",MB_OK); else if(m_lang_index==1) MessageBox("You have not chosen a pattern!","Error",MB_OK); } return(true); }
Der Algorithmus funktioniert wie folgt:
- Die für die Berechnung erforderlichen Strukturen werden aktualisiert.
- Mit der Methode GetTimeframes() wird aus der Benutzeroberfläche Daten über den ausgewählten Zeitrahmen abgerufen.
- Mögliche Fehler werden bearbeitet, wenn kein Zeitrahmen ausgewählt ist.
- Wenn mindestens ein Zeitrahmen ausgewählt ist, werden die Daten des Datumsbereichs geholt und überprüft, ob die Eingaben korrekt sind. Auch wird auf Eingabefehler geprüft.
- Dann wird geprüft, ob jedes der möglichen Muster für die Berechnung ausgewählt ist, und es wird nach diesem Muster in den früher ausgewählten Zeitrahmen und Zeitspannen gesucht.
- Am Ende wird überprüft, ob mindestens ein ausgewähltes Muster für die Berechnung vorhanden ist. Wenn ein Muster ausgewählt ist, wird die Ergebnistabelle ausgegeben. Wenn kein Muster ausgewählt ist, wird eine entsprechende Meldung ausgedruckt.
Als Nächstes betrachten wir die Methoden, die in früheren Versionen existierten und in der aktualisierten Version geändert wurden, sowie eine neue Methode zur Berechnung der erhaltenen Daten und deren Ausgabe in der Ergebnistabelle:
- Spezifizierte Suchmethode für Kerzentypen GetCandleType().
- Methode für die Bewertung der gefundenen Effizienz des Musters GetCategory().
- Verfahren zum Berechnen der erhaltenen Musterdaten und zum Ausgeben der Ergebnisse in der Tabelle AddRow().
Die Methode GetPatternType() für die Mustersuche hat zwei verschiedene Implementierungen, während diese drei Methoden universell sind. Lassen Sie uns diese im Detail betrachten:
//+------------------------------------------------------------------+ //| Erkennen des Kerzentyps | //+------------------------------------------------------------------+ bool CProgram::GetCandleType(const string symbol,CANDLE_STRUCTURE &res,ENUM_TIMEFRAMES timeframe,const int shift) { MqlRates rt[]; int aver_period=5; double aver=0.0; datetime start=TimeCurrent(); SymbolSelect(symbol,true); //--- Abrufen des Anfangsdatums der Zeitspanne in Abhängigkeit des Mustertyps if(m_table_number==1) start=StringToTime(TimeToString(m_calendar1.SelectedDate(),TIME_DATE)+" "+(string)m_time_edit1.GetHours()+":"+(string)m_time_edit1.GetMinutes()+":00"); else if(m_table_number==2) start=StringToTime(TimeToString(m_calendar3.SelectedDate(),TIME_DATE)+" "+(string)m_time_edit3.GetHours()+":"+(string)m_time_edit3.GetMinutes()+":00"); //--- Zeitversatz start+=PeriodSeconds(timeframe)*shift; int copied=CopyRates(symbol,timeframe,start,aver_period+1,rt); if(copied<6) { Print(start,": Not enough data for calculation — ",GetLastError()); } //--- Abrufen der Details der vorherigen Kerze if(copied<aver_period) return(false); //--- res.open=rt[aver_period].open; res.high=rt[aver_period].high; res.low=rt[aver_period].low; res.close=rt[aver_period].close; //--- Bestimmen der Trendrichtung for(int i=0;i<aver_period;i++) aver+=rt[i].close; aver/=aver_period; if(aver<res.close) res.trend=UPPER; if(aver>res.close) res.trend=DOWN; if(aver==res.close) res.trend=FLAT; //--- Bestimmen, ob es eine Auf- oder Abwärtskerze ist res.bull=res.open<res.close; //--- Ermitteln der absoluten Größe des Kerzenkörpers res.bodysize=MathAbs(res.open-res.close); //--- Ermitteln des Kerzenkörpers double shade_low=res.close-res.low; double shade_high=res.high-res.open; if(res.bull) { shade_low=res.open-res.low; shade_high=res.high-res.close; } double HL=res.high-res.low; //--- Berechnen der durchschnittlichen Körpergröße der vorherigen Kerze double sum=0; for(int i=1; i<=aver_period; i++) sum+=MathAbs(rt[i].open-rt[i].close); sum/=aver_period; //--- Bestimmen des Kerzentyps res.type=CAND_NONE; //--- lang if(res.bodysize>sum*m_long_coef && res.bull) res.type=CAND_LONG_BULL; //--- kurz if(res.bodysize<sum*m_short_coef && res.bull) res.type=CAND_SHORT_BULL; //--- lang & abwärts if(res.bodysize>sum*m_long_coef && !res.bull) res.type=CAND_LONG_BEAR; //--- kurz & abwärts if(res.bodysize<sum*m_short_coef && !res.bull) res.type=CAND_SHORT_BEAR; //--- Doji if(res.bodysize<HL*m_doji_coef) res.type=CAND_DOJI; //--- Marubozu if((shade_low<res.bodysize*m_maribozu_coef && shade_high<res.bodysize*m_maribozu_coef) && res.bodysize>0) res.type=CAND_MARIBOZU; //--- Hammer if(shade_low>res.bodysize*m_hummer_coef2 && shade_high<res.bodysize*m_hummer_coef1) res.type=CAND_HAMMER; //--- invertierter Hammer if(shade_low<res.bodysize*m_hummer_coef1 && shade_high>res.bodysize*m_hummer_coef2) res.type=CAND_INVERT_HAMMER; //--- Spinning Top if((res.type==CAND_SHORT_BULL || res.type==CAND_SHORT_BEAR) && shade_low>res.bodysize*m_spin_coef && shade_high>res.bodysize*m_spin_coef) res.type=CAND_SPIN_TOP; //--- ArrayFree(rt); return(true); }
Der Verfahrensalgorithmus ist wie folgt: Holen Sie sich das Anfangsdatum der ausgewählten Zeitspanne, abhängig von den bestehenden oder erstellten Mustern, die Sie analysieren möchten. Da diese Methode in einem Berechnungszyklus im Datumsbereich verwendet wird, ändern Sie das Anfangsdatum, indem Sie es um eine Kerze des gegebenen Zeitraums von der Vergangenheit in die Zukunft verschieben. Dann werden die Daten, die für die Berechnung einfacher Kerzentypen benötigt werden, kopiert. Wenn diese Daten nicht ausreichen, wird dem Nutzer eine entsprechende Meldung angezeigt.
Wichtiger Hinweis! Es ist notwendig, die Verfügbarkeit von historischen Daten im MetaTrader 5 Terminal zu kontrollieren, da die Anwendung sonst möglicherweise nicht korrekt funktioniert.
Wenn die Daten ausreichen, wird geprüft, ob die aktuelle Kerze zu einem einfachen Kerzentyp gehört.
Wie aus früheren Artikeln bekannt, überprüft die Methode GetCategory() das Preisverhalten nach dem Auftreten des Musters anhand historischer Daten.
//+------------------------------------------------------------------+ //| Festlegen der Gewinnkategorien | //+------------------------------------------------------------------+ bool CProgram::GetCategory(const string symbol,const int shift,RATING_SET &rate,ENUM_TIMEFRAMES timeframe,int threshold) { MqlRates rt[]; datetime start=TimeCurrent(); if(m_table_number==1) start=StringToTime(TimeToString(m_calendar1.SelectedDate(),TIME_DATE)+" "+(string)m_time_edit1.GetHours()+":"+(string)m_time_edit1.GetMinutes()+":00"); else if(m_table_number==2) start=StringToTime(TimeToString(m_calendar3.SelectedDate(),TIME_DATE)+" "+(string)m_time_edit3.GetHours()+":"+(string)m_time_edit3.GetMinutes()+":00"); start+=PeriodSeconds(timeframe)*shift; int copied=CopyRates(symbol,timeframe,start,4,rt); //--- Abrufen der Details der vorherigen Kerze if(copied<4) { return(false); } double high1,high2,high3,low1,low2,low3,close0,point; close0=rt[0].close; high1=rt[1].high; high2=rt[2].high; high3=rt[3].high; low1=rt[1].low; low2=rt[2].low; low3=rt[3].low; if(!SymbolInfoDouble(symbol,SYMBOL_POINT,point)) return(false); //--- Prüfen auf einen Aufwärtstrend if((int)((high1-close0)/point)>=threshold) { rate.a_uptrend++; } else if((int)((high2-close0)/point)>=threshold) { rate.b_uptrend++; } else if((int)((high3-close0)/point)>=threshold) { rate.c_uptrend++; } //--- Prüfen auf einen Abwärtstrend if((int)((close0-low1)/point)>=threshold) { rate.a_dntrend++; } else if((int)((close0-low2)/point)>=threshold) { rate.b_dntrend++; } else if((int)((close0-low3)/point)>=threshold) { rate.c_dntrend++; } return(true); }
Bei diesem Algorithmus hat sich nur die Methode zum Erhalten von Daten der analysierten Kerzen geändert. Dies steht in direktem Zusammenhang mit der neuen Auswahl der Zeitspanne.
Die letzte gängige Methode für beide GetPatternType() ist das Erhalten, Berechnen und Anzeigen von Daten in der Ergebnistabelle.
//+------------------------------------------------------------------+ //| Abrufen, Berechnen und Anzeigen der Daten in der Ergebnistabelle | //+------------------------------------------------------------------+ void CProgram::AddRow(CTable &table,string pattern_name,RATING_SET &rate,int found,ENUM_TIMEFRAMES timeframe) { int row=m_total_row; int total_patterns=ArraySize(m_total_combination); double p1,p2,k1,k2; int sum1=0,sum2=0; sum1=rate.a_uptrend+rate.b_uptrend+rate.c_uptrend; sum2=rate.a_dntrend+rate.b_dntrend+rate.c_dntrend; //--- p1=(found>0)?NormalizeDouble((double)sum1/found*100,2):0; p2=(found>0)?NormalizeDouble((double)sum2/found*100,2):0; k1=(found>0)?NormalizeDouble((m_k1*rate.a_uptrend+m_k2*rate.b_uptrend+m_k3*rate.c_uptrend)/found,3):0; k2=(found>0)?NormalizeDouble((m_k1*rate.a_dntrend+m_k2*rate.b_dntrend+m_k3*rate.c_dntrend)/found,3):0; //--- table.AddRow(row); if(m_table_number==1) table.SetValue(0,row,pattern_name); else if(m_table_number==2) { if(row<total_patterns) table.SetValue(0,row,m_total_combination[row]); else if(row>=total_patterns) { int i=row-int(total_patterns*MathFloor(double(row)/total_patterns)); table.SetValue(0,row,m_total_combination[i]); } } table.SetValue(1,row,(string)found); table.SetValue(2,row,TimeframeToString(timeframe)); table.SetValue(3,row,(string)p1,2); table.SetValue(4,row,(string)p2,2); table.SetValue(5,row,(string)k1,2); table.SetValue(6,row,(string)k2,2); ZeroMemory(rate); m_total_row++; } //+------------------------------------------------------------------+
Das Verfahren erhält alle Daten für die Berechnung über seine Argumente. Der Algorithmus für die Datenverarbeitung ist recht einfach. Zwei Momente sollten hier erwähnt werden. In früheren Anwendungsversionen war die genaue Anzahl der Zeilen im Voraus bekannt. Beispielsweise hatte die Ergebnistabelle bei der Verarbeitung vorhandener Musterdaten immer die gleiche Anzahl von Zeilen, die der Anzahl der voreingestellten Muster entsprach, d.h. 14. Nun kann der Nutzer beliebig viele Muster oder Arbeitszeiten wählen, so dass die Anzahl der Zeilen nicht bekannt ist. Dazu wurde ein einfacher Zeilenzähler m_total_row hinzugefügt. Der Aufruf der Methode AddRow() fügt der Ergebnistabelle eine Zeile hinzu, die u.a. auf zwei Parametern basiert: Muster und Zeitrahmen.
Der zweite Punkt betrifft die Registerkarte AutoSearch. In früheren Versionen war die endliche Anzahl der Zeilen gleich der Anzahl der Kombinationen der erzeugten Muster. Der bisherige Algorithmus ist aus dem gleichen Grund nicht geeignet: Die Anzahl der Zeitrahmen ist unbekannt. Daher musste für jeden der ausgewählten Zeitrahmen die gesamte Palette der generierten Kombinationen neu geschrieben werden.
Betrachten wir die zweite Variante der Methode GetPatternType().
bool GetPatternType(const string Symbol,string &total_combination[]);
Hier ist der zweite Parameter neben dem aktuellen Symbol der Link zum String-Array der generierten Muster.
//+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ bool CProgram::GetPatternType(const string symbol,string &total_combination[]) { CANDLE_STRUCTURE cand1[],cand2[],cand3[],cur_cand,prev_cand,prev_cand2; RATING_SET ratings; int total_patterns,m_pattern_total[]; string elements[]; //--- total_patterns=ArraySize(total_combination); ArrayResize(cand1,total_patterns); ArrayResize(cand2,total_patterns); ArrayResize(cand3,total_patterns); ArrayResize(m_pattern_total,total_patterns); ArrayResize(elements,m_pattern_size); //--- for(int i=0;i<total_patterns;i++) { StringReplace(total_combination[i],"[",""); StringReplace(total_combination[i],"]",""); if(m_pattern_size>1) { ushort sep=StringGetCharacter(",",0); StringSplit(total_combination[i],sep,elements); } m_pattern_total[i]=0; if(m_pattern_size==1) IndexToPatternType(cand1[i],(int)total_combination[i]); else if(m_pattern_size==2) { IndexToPatternType(cand1[i],(int)elements[0]); IndexToPatternType(cand2[i],(int)elements[1]); } else if(m_pattern_size==3) { IndexToPatternType(cand1[i],(int)elements[0]); IndexToPatternType(cand2[i],(int)elements[1]); IndexToPatternType(cand3[i],(int)elements[2]); } } //--- GetTimeframes(m_timeframes1,m_cur_timeframes2); int total=ArraySize(m_cur_timeframes2); if(total<1) { if(m_lang_index==0) MessageBox("Вы не выбрали рабочий таймфрейм!","Ошибка",MB_OK); else if(m_lang_index==1) MessageBox("You have not selected a working timeframe!","Error",MB_OK); return(false); } m_total_row=0; m_table_number=2; //--- Löschen aller Zeilen m_table2.DeleteAllRows(); //--- datetime start=StringToTime(TimeToString(m_calendar3.SelectedDate(),TIME_DATE)+" "+(string)m_time_edit3.GetHours()+":"+(string)m_time_edit3.GetMinutes()+":00"); datetime end=StringToTime(TimeToString(m_calendar4.SelectedDate(),TIME_DATE)+" "+(string)m_time_edit4.GetHours()+":"+(string)m_time_edit4.GetMinutes()+":00"); //--- if(start>end || end>TimeCurrent()) { if(m_lang_index==0) MessageBox("Неправильно выбран диапазон дат!","Ошибка",MB_OK); else if(m_lang_index==1) MessageBox("Incorrect date range selected!","Error",MB_OK); return(false); } //--- if(m_pattern_size==1) { ZeroMemory(cur_cand); //--- Berechnung je nach Zeitrahmen for(int i=0;i<total;i++) { MqlRates rt[]; ZeroMemory(rt); ZeroMemory(ratings); int copied=CopyRates(symbol,m_cur_timeframes2[i],start,end,rt); //--- Berechnungen je nach Muster for(int j=0;j<total_patterns;j++) { //--- Berechnungen je nach Zeitspanne for(int k=0;k<copied;k++) { //--- Abrufen des abrufen Kerzentyps GetCandleType(symbol,cur_cand,m_cur_timeframes2[i],k); // Aktuelle Kerze //--- if(cur_cand.type==cand1[j].type && cur_cand.bull==cand1[j].bull) { m_pattern_total[j]++; GetCategory(symbol,k+3,ratings,m_cur_timeframes2[i],m_threshold_value2); } } AddRow(m_table2,"",ratings,m_pattern_total[j],m_cur_timeframes2[i]); m_pattern_total[j]=0; } } } else if(m_pattern_size==2) { ZeroMemory(cur_cand); ZeroMemory(prev_cand); //--- Berechnung je nach Zeitrahmen for(int i=0;i<total;i++) { MqlRates rt[]; ZeroMemory(rt); ZeroMemory(ratings); int copied=CopyRates(symbol,m_cur_timeframes2[i],start,end,rt); //--- Berechnungen je nach Muster for(int j=0;j<total_patterns;j++) { //--- Berechnungen je nach Zeitspanne for(int k=0;k<copied;k++) { //--- Abrufen des abrufen Kerzentyps GetCandleType(symbol,prev_cand,m_cur_timeframes2[i],k+1); // Vorherige Kerze GetCandleType(symbol,cur_cand,m_cur_timeframes2[i],k); // Aktuelle Kerze //--- if(cur_cand.type==cand1[j].type && cur_cand.bull==cand1[j].bull && prev_cand.type==cand2[j].type && prev_cand.bull==cand2[j].bull) { m_pattern_total[j]++; GetCategory(symbol,k+4,ratings,m_cur_timeframes2[i],m_threshold_value2); } } AddRow(m_table2,"",ratings,m_pattern_total[j],m_cur_timeframes2[i]); m_pattern_total[j]=0; } } } else if(m_pattern_size==3) { ZeroMemory(cur_cand); ZeroMemory(prev_cand); ZeroMemory(prev_cand2); //--- Berechnung je nach Zeitrahmen for(int i=0;i<total;i++) { MqlRates rt[]; ZeroMemory(ratings); int copied=CopyRates(symbol,m_cur_timeframes2[i],start,end,rt); //--- Berechnungen je nach Muster for(int j=0;j<total_patterns;j++) { //--- Berechnungen je nach Zeitspanne for(int k=0;k<copied;k++) { //--- Abrufen des abrufen Kerzentyps GetCandleType(symbol,prev_cand2,m_cur_timeframes2[i],k+2); // Vorherige Kerze GetCandleType(symbol,prev_cand,m_cur_timeframes2[i],k+1); // Vorherige Kerze GetCandleType(symbol,cur_cand,m_cur_timeframes2[i],k); // Aktuelle Kerze //--- if(cur_cand.type==cand1[j].type && cur_cand.bull==cand1[j].bull && prev_cand.type==cand2[j].type && prev_cand.bull==cand2[j].bull && prev_cand2.type==cand3[j].type && prev_cand2.bull==cand3[j].bull) { m_pattern_total[j]++; GetCategory(symbol,k+5,ratings,m_cur_timeframes2[i],m_threshold_value2); } } AddRow(m_table2,"",ratings,m_pattern_total[j],m_cur_timeframes2[i]); m_pattern_total[j]=0; } } } //--- m_table2.DeleteRow(m_total_row); //--- Aktualisieren der Tabelle m_table2.Update(true); m_table2.GetScrollVPointer().Update(true); return(true); }
Es ist wichtig, die Reihenfolge der Berechnungen innerhalb dieses Versionsalgorithmus zu verstehen, basierend auf den Eingangsdaten. Wir werden den Erhalt von Zeitrahmen und Datumsbereichen hier nicht in Betracht ziehen, da dies bereits angesprochen wurde. Dann überprüft der Algorithmus die Größe der Muster, die gerade getestet werden. Betrachten wir ein Muster aus drei Kerzen. Nach der Deklaration einer Struktur zur Speicherung von Preisdaten und der Aufhebung der verwendeten "Rating"-Struktur durchläuft der Algorithmus zum ersten Mal Zeitfenster und erhält für jeden von ihnen die Menge der kopierten Daten. Dies ermöglicht das Bestimmen des Bereichs, in dem die angegebenen Muster weiter durchsucht werden. Geben Sie nach dem Zeitrahmenzyklus für jedes Muster im angegebenen Zeitrahmen in den Berechnungszyklus ein. Dann gehen wir für jedes Muster in einer Schleife durch die Kerzen im angegebenen Zeitspanne.
Zum besseren Verständnis sehen Sie sich das Berechnungsbeispiel und die Reihenfolge der Informationsanzeige in der Ergebnistabelle an.
Abb. 9. Berechnungsbeispiel und die Reihenfolge der Ergebnisausgabe in der Tabelle
Wie in Abb.9 zu sehen ist, wurde der Test mit dem Währungspaar EURUSD mit dem 1-Kerzenleuchter-Muster auf den Zeitfenstern M15, M30, H1 und H2 durchgeführt. Zwei einfache Kerzen wurden zum Testen ausgewählt: mit den Indizes 1 und 2. Die Umsetzung des oben beschriebenen Algorithmus kann in der Ergebnistabelle beobachtet werden. Sie wird wie folgt durchgeführt: Zuerst werden alle generierten Muster einzeln im 15-Minuten-Zeitrahmen analysiert, dann im 30-Minuten-Zeitrahmen usw.
Schlussfolgerung
Das unten angehängte Archiv enthält alle beschriebenen Dateien in den jeweiligen Ordnern. Für den ordnungsgemäßen Betrieb müssen Sie lediglich den Ordner MQL5 in den Terminalverzeichnis speichern. Um das Stammverzeichnis des Terminals zu öffnen, in dem sich der Ordner MQL5 befindet, drücken Sie die Tastenkombination Ctrl+Shift+D im Terminal des MetaTrader 5 oder verwenden Sie das Kontextmenü wie in Abbildung 10 unten gezeigt.
Abb.10 Öffnen des MQL5-Wurzelverzeichnisses im Terminal des MetaTrader 5.
Programme, die im diesem Artikel verwendet werden
# |
Name |
Typ |
Beschreibung |
---|---|---|---|
1 |
PatternAnalyzer.mq5 | Grafische Benutzeroberfläche |
Werkzeugleiste für die Analyse der Kerzenmuster |
2 | MainWindow.mqh | Bibliothek | GUI Bibliothek |
3 | Program.mqh | Bibliothek | Methodenbibliothek zur Erstellung der Schnittstelle und der Berechnungselemente |
Frühere Artikel in dieser Serie:
Untersuchung von Techniken zur Analyse der Kerzen (Teil I): Überprüfen vorhandener MusterUntersuchung von Techniken zur Analyse der Kerzen (Teil II): Automatische Suche nach den Mustern
Untersuchung von Techniken zur Analyse der Kerzen (Teil III): Ein Bibliothek für die Musterbearbeitung
Übersetzt aus dem Russischen von MetaQuotes Ltd.
Originalartikel: https://www.mql5.com/ru/articles/6301
- 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.