Untersuchung von Techniken zur Analyse der Kerzen (Teil IV): Aktualisierungen und Ergänzungen des Pattern Analyzers

21 Mai 2019, 08:45
Alexander Fedosov
0
226

Inhaltsverzeichnis

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 Muster
Untersuchung 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 Software Corp.
Originalartikel: https://www.mql5.com/ru/articles/6301

Beigefügte Dateien |
MQL5.zip (495.94 KB)
Bondrenditen aus dem Web kratzen Bondrenditen aus dem Web kratzen

Automatisiertes Erfassen von Zinsdaten, um die Leistung eines Expert Advisors zu verbessern.

Untersuchung von Techniken zur Analyse der Kerzen (Teil III): Eine Bibliothek für die Musterbearbeitung Untersuchung von Techniken zur Analyse der Kerzen (Teil III): Eine Bibliothek für die Musterbearbeitung

Der Zweck dieses Artikels ist es, ein benutzerdefiniertes Werkzeug zu erstellen, das es den Benutzern ermöglichen würde, die gesamte Bandbreite an Informationen über die zuvor diskutierten Muster zu erhalten und zu nutzen. Wir erstellen eine Bibliothek mit musterbezogenen Funktionen, die Sie in Ihren eigenen Indikatoren, Handelspanels, Expert Advisors usw. verwenden können.

Eine DLL für MQL5 in 10 Minuten (Teil II): Erstellen mit Visual Studio 201 Eine DLL für MQL5 in 10 Minuten (Teil II): Erstellen mit Visual Studio 201

Der ursprüngliche Basisartikel hat seine Relevanz nicht verloren. Daher, wenn Sie an diesem Thema interessiert sind, sollten Sie unbedingt den ersten Artikel lesen. Es ist viel Zeit seitdem vergangen, und Visual Studio 2017 verfügt mittlerweile über eine aktualisierte Oberfläche. Auch der MetaTrader 5 hat neue Funktionen erhalten. Der Artikel enthält eine Beschreibung der Phasen der DLL-Projektentwicklung sowie das Einrichten und Interagieren mit dem MetaTrader 5.

Wie man den Handelsverlauf mehrerer Währungen basierend auf HTML- und CSV-Berichten visualisiert. Wie man den Handelsverlauf mehrerer Währungen basierend auf HTML- und CSV-Berichten visualisiert.

Seit seiner Einführung bietet MetaTrader 5 die Möglichkeit, mehrere Währungen zu testen. Diese Möglichkeit wird von Händlern oft genutzt. Die Funktion ist jedoch nicht universell einsetzbar. Der Artikel stellt mehrere Programme zum Zeichnen von grafischen Objekten in Diagrammen vor, die auf den Berichten der Handelshistorie im HTML- und CSV-Format basieren. Der Mehrwährungshandel kann parallel, in mehreren Unterfenstern sowie in einem Fenster mit dem dynamischen Schaltbefehl analysiert werden.