Merrill-Muster

7 Oktober 2019, 09:06
Alexander Fedosov
0
245

Inhalt

Einführung

Der erste Versuch, ein System von Preismustern zu schaffen, wurde 1971 von Robert Levy unternommen. Er verwendete ein Muster aus fünf Punkten und prüfte dann, ob sie von Bedeutung waren. Er erzielte keine nennenswerten Ergebnisse, aber Arthur Merrill setzte seine Arbeit 10 Jahre später fort. 

Er teilte die Muster in zwei Kategorien ein, die den Buchstaben M und W ähneln. Jede Kategorie enthält 16 Muster und eigene Unterkategorien. Merrill hat 6 Unterkategorien hervorgehoben:

  • Aufwärtstrend
  • Abwärtstrend
  • Dreieck
  • Expansion
  • Kopf und Schultern
  • Umgekehrte Kopf und Schultern

Wir werden die aktuelle Relevanz der Merrill-Muster anhand der Anwendung zum Testen definieren. Außerdem wäre es interessant, dieses Modell auf verschiedene Arten von Daten anzuwenden — wie die Schluss-, Hoch- und Tiefstpreise sowie Oszillatorwerte.

Definition und Anwendung

Um zu klären, wie und für welche Daten wir Merrill-Muster anwenden werden, müssen wir verstehen, was sie tatsächlich sind. Die beiden Hauptkategorien sind die Muster, die den Buchstaben M und W ähneln. Sie werden M und W Muster genannt. Jede der Kategorien enthält 16 Muster.

Abb. 1 stellt 16 M Muster dar. Wie wir sehen können, liegt der Unterschied in der gegenseitigen Anordnung der fünf Punkte, die das Muster ausmachen.  


Abb. 1. Visuelle Darstellung von М Mustern

Abb. 2 zeigt 16 W-Muster. Wir werden nach diesen beiden Gruppen anhand von Preisdiagrammen und Indikatoren suchen, sowie mögliche Regeln untersuchen, bewerten und suchen.

Abb. 2. Visuelle Darstellung von W Mustern

Die Idee hinter jedem Muster läuft darauf hinaus, dass wir bei Auftreten einer bestimmten Formation erwarten können, dass sich der Preis in eine bestimmte Richtung bewegt und wir dann davon profitieren.

Um möglichst deutlich zu machen, in welchem Bereich und wie Merrill-Muster untersucht werden sollen, hier einige Beispiele. Abb. 3 zeigt ein übliches lineares USDCAD H1 Preischart. Diese Art der Darstellung wird nicht so häufig verwendet, da Kerzen und Bars beliebter geworden sind.

Abb. 3. Lineares USDCAD H1-Chart basierend auf den Schlusskursen

Hier sehen wir bereits einige der oben beschriebenen Muster. Dies soll der erste Studienschwerpunkt sein — die Anwendung auf das lineare Chart auf Basis der Schlusskurse. Außerdem werden wir lineare Charts überprüfen, die auf Eröffnungs-, Hoch- und Tiefstpreisen basieren. 

Der zweite Studienbereich soll aus Oszillatoren bestehen, wie z.B.:

Die Methode, die ich im Artikel Untersuchung von Techniken zur Analyse der Kerzen (Teil I): Überprüfen vorhandener Muster ist als Methode zur Bewertung von Mustern zu verwenden, die sie sowohl auf den Preis als auch auf die oben genannten Oszillatoren anwendbar ist. Die Idee dahinter ist einfach:

  • Identifizieren des analysierten Musters auf einem bestimmten Probenabschnitt.
  • Analyse der Preisbewegung nach der Identifizierung.
  • Erfassung der Daten und Berechnung der Mustereffizienz.


Entwicklung des Testwerkzeugs

Bevor wir mit der Entwicklung beginnen, müssen wir definieren, welche Einstellungen sie enthalten soll. Das Tool soll aus dem Panel mit den Registerkarten Analysis und Settings. Es sind auch die Parameter aus dem Fenster EA-Einstellungen zu verwenden. Insgesamt sollen wir drei Abschnitte mit Werkzeugen für die Arbeit mit Mustern haben. Lassen Sie uns nun die Einstellungen in den einzelnen Abschnitten beschreiben.

Die Registerkarte Analysis enthält:

  1. Zwei Schaltflächensätze zur Auswahl der Arten von getesteten Mustern. Es gibt auch die Schaltflächen All M und All W zur schnellen Aus-/Abwahl von М- und W-Mustern.
  2. Eine Reihe von Schaltflächen zur Auswahl der getesteten Zeitrahmen und die Schaltfläche ALL zum Aus-/Abwählen der gesamten Schaltflächengruppe.
  3. Das Eingabefeld "Trendschwelle (Punkte)". Dies ist ein Gewinn in Punkten, die der Preis innerhalb von maximal drei Kerzen erreichen sollte, nachdem ein analysiertes Merrill-Muster identifiziert wurde.
  4. Die Schaltfläche öffnet das Dialogfenster zur Auswahl von Start- und Enddatum sowie der Testzeit. 
  5. Das Eingabefeld mit dem Kontrollkästchen und der Schaltfläche ist ein Filter zum Auffinden der notwendigen Handelssymbole. Es hat eine Voreinstellung — Major. Es zeigt die wichtigsten Währungspaare an. Das Kontrollkästchen deaktiviert den Filter und zeigt alle verfügbaren Handelssymbole an.
  6. Handelssymbole, die in der Tabelle mit dem Filter ausgewählt wurden. Nach Auswahl aus der Liste wird die Musteranalyse durchgeführt.
  7. Die Ergebnistabelle besteht aus sieben Spalten: 
    • Figure name. In der Spalte wird ein Name eines analysierten Merrill-Musters angezeigt, z.B. M10 oder W12.
    • Found. Anzahl der gefundenen Muster des angegebenen Typs des Probeabschnitts.
    • Timeframe. Der Zeitrahmen auf dem das angegebene Muster gesucht wird.
    • P, Uptrend. Die Wahrscheinlichkeit, dass der Preis um den Wert "Trend threshold (points)" nach dem Muster steigt.
    • P, Dntrend. Die Wahrscheinlichkeit, dass der Preis um den Wert "Trend threshold (points)" nach dem Muster fällt.
    • K, UpTrend/K, DnTrend. Dies ist ein Verhältnis, das in meinem Artikel Untersuchung von Techniken zur Analyse der Kerzen (Teil I): Überprüfen vorhandener Muster. Es wird bewertet, wie schnell der Preis einen bestimmten Gewinn erreicht, nachdem ein analysiertes Muster in Aufwärts- oder Abwärtsrichtung eines Trends erscheint.

Abb. 4 zeigt eine visuelle Umsetzung aller oben beschriebenen Symbole und Parameter.


Abb. 4. Registerkarte Analyse

Betrachten wir nun die Registerkarte Settings:

  1. Used indicator. Wählen Sie einen Indikator aus, auf den die Suche und Analyse von Merrill-Mustern angewendet werden soll.
  2. Bei der Berechnung der oben beschriebenen Verhältnisse von K, UpTrend/DnTrend werden Gewichtungskoeffizienten verwendet. 
  3. Sprache der Nutzeroberfläche. Die Dropdown-Liste zur Auswahl der Oberflächensprache: Englisch oder Russisch.

Die Darstellung der Registerkarte mit den Einstellungen ist in Abb. 5 unten dargestellt:


Abb. 5. Registerkarte Settings

Im letzten Abschnitt wird das Fenster "EA settings" (Hotkey F7) und die Einstellungen der verwendeten Indikatoren, die unter "Verwendete Indikatoren" aufgeführt sind, angewendet. Abb. 6 zeigt das Fenster des letzten Abschnitts der Einstellungen.


Abb. 6. Einstellungsfenster der verwendeten Indikatoren

Wir sollten die folgenden Nuancen bei der Definition der Einstellungen im Fenster berücksichtigen:

  • Die erste ("Applied price") wendet die Variable ENUM_APPLIED_PRICE Aufzählungstyp mit sieben Werten an: Eröffnungs- und Schlusskurs, Höchst- und Tiefstkurs sowie mittlere, typische und gewichtete Durchschnittspreise. Bei der Durchführung einer Analyse auf der Grundlage eines Chartpreises sollten die ersten vier Werte verwendet werden, da die letzten drei Werte für die Berechnung von Indikatoren bestimmt sind.
  • Wenn Sie Indikatoren zur Analyse von Mustern verwenden möchten, wirkt sich die Einstellung "Applied price" auf die Indikatoren aus, die die Variable vom Typ ENUM_APPLIED_PRICE in ihren Berechnungen verwenden, und zwar: ATR, CCI und RSI.

Betrachten wir nun die Implementierung der Schnittstelle der App sowie die Methoden zur Suche und Analyse der Merrill-Muster.

Zur Entwicklung der GUI verwenden wir die Methode CreateGUI(), bestehend aus den Methoden CreateWindow(), die das Hauptfenster der Schnittstelle erstellen, und dem Dialogfenster CreateWindowSetting1() zur Auswahl eines Zeitraumes für die Untersuchung.

//+------------------------------------------------------------------+
//| Create the program GUI                                           |
//+------------------------------------------------------------------+
bool CProgram::CreateGUI(void)
  {
//--- Create the panel
   if(!CreateWindow("Merrill Patterns"))
      return(false);
//--- Create the dialog window
   if(!CreateWindowSetting1("Setting dates"))
      return(false);
//--- Komplettes Erstellen der GUI
   CWndEvents::CompletedGUI();
   return(true);
  }

Nun lassen Sie uns sehen, woraus jede Methode besteht. Wir werden unsere Aufmerksamkeit zunächst auf das Hauptfenster der Schnittstelle richten. Es besteht aus der Implementierung der Registerkarte Analyse, die aus den in Abb. 4 beschriebenen Elementen besteht.

//+------------------------------------------------------------------+
//| Analyze tab                                                      |
//+------------------------------------------------------------------+
//--- Create the pattern set buttons
   if(!CreatePatternSet(m_patterns,10,10))
      return(false);
//--- Timeframe header
   if(!CreateTFLabel(m_text_labels[1],10,105,0))
      return(false);
//--- Create the timeframe set buttons
   if(!CreateTimeframeSet(m_timeframes,10,125,0))
      return(false);
//--- Field for searching the symbol filter 
   if(!CreateSymbolsFilter(m_symb_filter1,m_request1,10,180,0))
      return(false);
//--- Create the button for selecting a date range
   if(!CreateDateRange(m_request3,280,180,0))
      return(false);
//--- Create the field for entering the profit threshold value
   if(!CreateThresholdValue(m_threshold1,400,180,100,0))
      return(false);
//--- Create the symbol table
   if(!CreateSymbTable(m_symb_table1,10,225,0))
      return(false);
//--- Create the result table
   if(!CreateTable1(m_table1,120,225,0))
      return(false);

Und von der Registerkarte Einstellungen, die in Abb. 5 beschrieben ist.

//+------------------------------------------------------------------+
//| Settings tab                                                     |
//+------------------------------------------------------------------+
//---
   if(!CreateButtonsGroup1(10,50))
      return(false);
//--- Textbezeichner
   if(!CreateTextLabel(m_text_labels[0],10,100))
      return(false);
   if(!CreateTextLabel(m_text_labels[3],10,10))
      return(false);
//--- Eingabefelder
   if(!CreateCoef(m_coef1,10,140,"K1",1))
      return(false);
   if(!CreateCoef(m_coef2,100,140,"K2",0.5))
      return(false);
   if(!CreateCoef(m_coef3,200,140,"K3",0.25))
      return(false);
   if(!CreateLanguageSetting(m_lang_setting,10,180,1))
      return(false);
//--- Statusleiste
   if(!CreateStatusBar(1,26))
      return(false);
//---
   return(true);
  }

Eine detailliertere Implementierung jeder der angewandten Methoden, die Interface-Elemente hinzufügen, finden Sie in den beigefügten Quellcodes.

Das Verfahren, das das Dialogfenster zum Setzen einer temporären Stichprobe implementiert, sieht wie folgt aus:

//+---------------------------------------------------------------------------------+
//| Create the dialog window for selecting the range of dates in the Analysis tab   |
//+---------------------------------------------------------------------------------+
bool CProgram::CreateWindowSetting1(const string caption_text)
  {
//--- Fensterpointer zum Array des Fensters hinzufügen
   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(230);
   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.2019',1))
      return(false);
   if(!CreateCalendar(m_calendar2,m_window[2],201,25,m_calendar2.Today(),1))
      return(false);
//---
   if(!CreateTimeEdit(m_time_edit1,m_window[2],10,200,"Time",1))
      return(false);
   if(!CreateTimeEdit(m_time_edit2,m_window[2],200,200,"Time",1))
      return(false);
//---
   return(true);
  }

Lassen Sie uns nun unsere Aufmerksamkeit auf die Methoden der Suche, des Studiums und der Bewertung der Muster richten. Um dies zu erreichen, müssen wir die gesamte Sequenz der Algorithmus-Aktionen verfolgen. Werfen wir zunächst einen Blick in die Datei MerrillPatterns.mq5, wo dieser Algorithmus beginnt.

//--- Include the application class
#include "Program.mqh"
CProgram program;
//+------------------------------------------------------------------+
//| EA Eingabeparameter                                              |
//+------------------------------------------------------------------+
input ENUM_APPLIED_PRICE   Inp_Price1              =  PRICE_CLOSE;   // Applied price
input int                  Inp_ATR_Peroid          =  5;             // ATR Period
input int                  Inp_CCI_Peroid          =  5;             // CCI Period
input int                  Inp_DeM_Peroid          =  5;             // DeMarker Period
input int                  Inp_ForcePeriod         =  13;            // ForceIndex Period
input ENUM_MA_METHOD       Inp_ForceMAMethod       =  MODE_SMA;      // ForceIndex MA method
input ENUM_APPLIED_PRICE   Inp_ForceAppliedPrice   =  PRICE_CLOSE;   // ForceIndex Applied price
input ENUM_APPLIED_VOLUME  Inp_ForceAppliedVolume  =  VOLUME_TICK;   // ForceIndex Volumes
input int                  Inp_WPR_Period          =  5;             // WPR Period
input int                  Inp_RSI_Period          =  5;             // RSI Period
//+------------------------------------------------------------------+
//| Initialisierungsfunktion des Experten                            |
//+------------------------------------------------------------------+
int OnInit(void)
  {
//---
   program.OnInitEvent();
//--- Set the trading panel
   if(!program.CreateGUI())
     {
      ::Print(__FUNCTION__," > Failed to create GUI!");
      return(INIT_FAILED);
     }
//---
   program.InitializePrice(Inp_Price1);
   program.InitializeATR(Inp_ATR_Peroid);
   program.InitializeCCI(Inp_CCI_Peroid);
   program.InitializeDeM(Inp_DeM_Peroid);
   program.InitializeForce(Inp_ForcePeriod,Inp_ForceMAMethod,Inp_ForceAppliedPrice,Inp_ForceAppliedVolume);
   program.InitializeWPR(Inp_WPR_Period);
   program.InitializeRSI(Inp_RSI_Period);
   return(INIT_SUCCEEDED);
  }

Abgesehen von den Anzeigeneingaben erfolgt die Suche nach einer grafischen Shell im Abschnitt OnInit(), gefolgt von der Initialisierung des Datensatzes im Fenster der Eigenschaften. Alle Methoden übergeben die externen Einstellungen an die internen Variablen.

//---
   void              InitializePrice(ENUM_APPLIED_PRICE price)    { m_applied_price=price;        }
   void              InitializeATR(int period)                    { m_atr_period=period;          }
   void              InitializeCCI(int period)                    { m_cci_period=period;          }
   void              InitializeDeM(int period)                    { m_dem_period=period;          }
   void              InitializeWPR(int period)                    { m_wpr_period=period;          }
   void              InitializeRSI(int period)                    { m_rsi_period=period;          }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void CProgram::InitializeForce(int period,ENUM_MA_METHOD ma_method,ENUM_APPLIED_PRICE price,ENUM_APPLIED_VOLUME volume)
  {
   m_force_period=period;
   m_force_ma_method=ma_method;
   m_force_applied_price=price;
   m_force_applied_volume=volume;
  }
//+-----------------------------------------------------------------

Danach ist die Anwendung einsatzbereit, während die restlichen Einstellungen an die erstellte GUI übergeben werden. Ich habe bereits erwähnt, dass der Start der Berechnung durch die Auswahl eines Währungssymbols aus der Symboltabelle (Pos. 6 in Abb. 4) erfolgt. Sie wird auch nach dem Einstellen des "Trend threshold" (Pos. 3 in Abb. 4) durchgeführt. Beide Ereignisse starten die Methode ChangeSymbol1(), um das Sammeln der erfassten Daten zu starten und sie für die Analyse vorzubereiten.

//+------------------------------------------------------------------+
//| Select a symbol in the Analysis tab                              |
//+------------------------------------------------------------------+
bool CProgram::ChangeSymbol1(const long id)
  {
//--- Prüfen der Elemente-ID
   if(id!=m_symb_table1.Id())
      return(false);
//--- Exit if the string is not highlighted
   if(m_symb_table1.SelectedItem()==WRONG_VALUE)
     {
      //--- Show full description of a symbol in the status bar
      m_status_bar.SetValue(0,"Symbol for analysis not selected");
      m_status_bar.GetItemPointer(0).Update(true);
      return(false);
     }
//--- Get a selected symbol
   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);
//---
   GetResult(symbol);
   return(true);
  }

Der Kern ihrer Arbeit besteht darin, ein ausgewähltes Handelssymbol aus der Symboltabelle zu definieren und seinen Wert an die Statusleiste und die Methode GetResult() zu übergeben. Betrachten wir die Methode im Detail, da alle Hauptarbeiten in ihr stattfinden.

//+------------------------------------------------------------------+
//| Handle pattern search results                                    |
//+------------------------------------------------------------------+
bool CProgram::GetResult(const string symbol)
  {
//--- Structure for evaluating pattern efficiency
   RATING_SET m_coef[];
//--- Figure types
   PATTERN_TYPE pattern_types[];
//---
   ArrayResize(pattern_types,33);
   for(int i=0;i<33;i++)
     {
      if(i==16)
         pattern_types[i]=-1;
      if(i<16)
         pattern_types[i]=PATTERN_TYPE(i);
      if(i>16)
         pattern_types[i]=PATTERN_TYPE(i-1);
     }
//--- Define selected timeframes
   GetTimeframes(m_timeframes,m_cur_timeframes);
   int total=ArraySize(m_cur_timeframes);
//--- Check for at least one selected timeframe
   if(total<1)
     {
      if(m_lang_index==0)
         MessageBox("Вы не выбрали рабочий таймфрейм!","Ошибка",MB_OK);
      else if(m_lang_index==1)
         MessageBox("You have not selected working timeframe!","Error",MB_OK);
      return(false);
     }
   int count=0;
   m_total_row=0;
//--- Remove all strings
   m_table1.DeleteAllRows();
//--- Get date range
   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");
//--- Check selected dates
   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);
     }
//--- 
   for(int k=0;k<33;k++)
     {
      if(k==16)
         continue;
      //--- Get selected patterns for analysis
      if(m_patterns[k].IsPressed())
        {
         ArrayResize(m_m_total,total);
         ArrayResize(m_coef,total);
         ZeroMemory(m_m_total);
         ZeroMemory(m_coef);
         count++;
         //--- Calculate by timeframes
         for(int j=0;j<total;j++)
           {
            double arr[];
            //--- Get data for analysis
            int copied=GetData(m_buttons_group1.SelectedButtonIndex(),symbol,m_cur_timeframes[j],start,end,arr);
            //---
            if(copied<9)
               MessageBox("Insufficient data for analysis","Error",MB_OK);
            for(int i=0;i<copied;i++)
              {
               if(i>copied-9)
                  continue;
               //--- Pattern search condition
               double A=arr[i];
               double B=arr[i+1];
               double C=arr[i+2];
               double D=arr[i+3];
               double E=arr[i+4];
               if(GetPatternType(A,B,C,D,E)==pattern_types[k])
                 {
                  m_m_total[j]++;
                  GetCategory(symbol,i+5,m_coef[j],m_cur_timeframes[j],m_threshold_value1);
                 }
              }
            //--- Add the result to the table
            AddRow(m_table1,m_patterns[k].LabelText(),m_coef[j],m_m_total[j],m_cur_timeframes[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);
  }

Zuerst muss ich die Typen der eingegebenen Variablen gleich zu Beginn der Methode erklären. Die erste ist die Struktur RATING_SET.

struct RATING_SET
  {
   int               a_uptrend;
   int               b_uptrend;
   int               c_uptrend;
   int               a_dntrend;
   int               b_dntrend;
   int               c_dntrend;
  };

Sie enthält 6 Variablen vom Typ int und ist notwendig, um Daten darüber hinzuzufügen, wie oft sich der Preis nach der Identifizierung eines Musters in eine bestimmte Richtung bewegt und wie schnell der Preis es erreicht. Angenommen, wir haben einen Aufwärtstrend und die Trendschwelle liegt bei 100 Punkten auf 5 Stellen, während der Preis diesen Wert innerhalb einer einzigen Kerze abdeckt. In diesem Fall erhält die Variable a_uptrend den Wert eins. Wenn der Preis innerhalb von 2 Kerzen 100 Punkte erreicht, wird der Wert an die Variable b_uptrend übergeben. Wir werden das Struktur-Array m_coef[] in unserer Methode verwenden.

Der zweite Variablentyp ist PATTERN_TYPE. Dies ist eine Enumeration, die alle Arten von Merrill-Mustern abdeckt.

//+------------------------------------------------------------------+
//| Figure type                                                      |
//+------------------------------------------------------------------+
enum PATTERN_TYPE
  {
   M1,M2,M3,M4,M5,M6,M7,M8,
   M9,M10,M11,M12,M13,M14,M15,M16,
   W1,W2,W3,W4,W5,W6,W7,W8,
   W9,W10,W11,W12,W13,W14,W15,W16
  };

Das Enumeration-Array pattern_types[] wird in der Methode verwendet. Danach folgt die Prüfung — welche Zeiträume wurden für die Arbeit in der Anwendung ausgewählt. Diese Daten werden von der Methode GetTimeframes() verarbeitet.

//+------------------------------------------------------------------+
//| Get the array of selected timeframes                             |
//+------------------------------------------------------------------+
void  CProgram::GetTimeframes(CButton &buttons[],ENUM_TIMEFRAMES &timeframe[])
  {
   string tf[22]=
     {
      "M1","M2","M3","M4","M5","M6","M10","M12","M15","M20","M30",
      "H1","H2","H3","H4","H6","H8","H12","D1","W1","MN"
     };
   int j=0;
   ArrayResize(timeframe,22);
   for(int i=0;i<22;i++)
     {
      if(buttons[i].IsPressed())
        {
         timeframe[j]=StringToTimeframe(tf[i]);
         j++;
        }
     }
   ArrayResize(timeframe,j);
  }

Die Methode schreibt dies in das vorläufig gesetzte Zeitrahmen-Array m_cur_timeframes[]. Weiter, holen Sie sich den Zeitraum für die Arbeit.

In der ersten Schleife beginnen wir mit der Überprüfung durch Drücken der ausgewählten Taste, die für die Mustertypen verantwortlich ist, und der Definition einer Reihe von untersuchten Mustern. In der nächsten Schleife wird jedes der Muster auf der Grundlage der zuvor ausgewählten Zeitrahmen untersucht. In diesem Stadium stellt sich die Frage, auf welche Daten die vorläufig eingestellten Muster- und Zeitrahmeneinstellungen angewendet werden sollen. Die Methode GetData() ist dafür verantwortlich, da sie die Einstellungen definiert, die Sie im Eigenschaftsfenster des EAs vorgenommen haben, sowie den verwendeten Indikator (Pos.1 in Abb. 5) in der Registerkarte der Anwendungseinstellungen.

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
int CProgram::GetData(int index,string symb,ENUM_TIMEFRAMES tf,datetime start,datetime end,double &arr[])
  {
//---
   int Handle=INVALID_HANDLE,copied;
//--- Close price
   if(index==0)
     {
      MqlRates rt[];
      ZeroMemory(rt);
      copied=CopyRates(symb,tf,start,end,rt);
      ArrayResize(arr,copied);
      for(int i=0;i<copied;i++)
        {
         arr[i]=rt[i].close;
         if(m_applied_price==PRICE_OPEN)
            arr[i]=rt[i].open;
         else if(m_applied_price==PRICE_CLOSE)
            arr[i]=rt[i].close;
         else if(m_applied_price==PRICE_HIGH)
            arr[i]=rt[i].high;
         else if(m_applied_price==PRICE_LOW)
            arr[i]=rt[i].low;
        }
      return(copied);
     }
//--- ATR
   if(index==1)
      Handle=iATR(symb,tf,m_atr_period,m_applied_price);
//--- CCI
   if(index==2)
      Handle=iCCI(symb,tf,m_cci_period,m_applied_price);
//--- DeMarker
   if(index==3)
      Handle=iDeMarker(symb,tf,m_dem_period);
//--- Force Index
   if(index==4)
      Handle=iForce(symb,tf,m_force_period,m_force_ma_method,m_force_applied_volume);
//--- WPR
   if(index==5)
      Handle=iWPR(symb,tf,m_wpr_period);
//--- RSI
   if(index==6)
      Handle=iRSI(symb,tf,m_rsi_period,m_applied_price);
//---
   if(Handle==INVALID_HANDLE)
     {
      Print("Failed to get indicator handle");
      return(-1);
     }
   copied=CopyBuffer(Handle,0,start,end,arr);
   return(copied);
  }

Nachdem der Algorithmus Daten zur Analyse empfangen hat, fährt er mit der Methode GetPatternType() fort, mit der er nach allen zuvor eingestellten Mustern in ausgewählten Zeiträumen sucht. 

//+------------------------------------------------------------------+
//| Define the patterns                                              |
//+------------------------------------------------------------------+
PATTERN_TYPE CProgram::GetPatternType(double A,double B,double C,double D,double E)
  {
//--- M1
   if(B>A && A>D && D>C && C>E)
      return(M1);
//--- M2
   if(B>A && A>D && D>E && E>C)
      return(M2);
//--- M3
   if(B>D && D>A && A>C && C>E)
      return(M3);
//--- M4
   if(B>D && D>A && A>E && E>C)
      return(M4);
//--- M5
   if(D>B && B>A && A>C && C>E)
      return(M5);
//--- M6
   if(D>B && B>A && A>E && E>C)
      return(M6);
//--- M7
   if(B>D && D>C && C>A && A>E)
      return(M7);
//--- M8
   if(B>D && D>E && E>A && A>C)
      return(M8);
//--- M9
   if(D>B && B>C && C>A && A>E)
      return(M9);
//--- M10
   if(D>B && B>E && E>A && A>C)
      return(M10);
//--- M11
   if(D>E && E>B && B>A && A>C)
      return(M11);
//--- M12
   if(B>D && D>C && C>E && E>A)
      return(M12);
//--- M13
   if(B>D && D>E && E>C && C>A)
      return(M13);
//--- M14
   if(D>B && B>C && C>E && E>A)
      return(M14);
//--- M15
   if(D>B && B>E && E>C && C>A)
      return(M15);
//--- M16
   if(D>E && E>B && B>C && C>A)
      return(M16);
//--- W1
   if(A>C && C>B && B>E && E>D)
      return(W1);
//--- W2
   if(A>C && C>E && E>B && B>D)
      return(W2);
//--- W3
   if(A>E && E>C && C>B && B>D)
      return(W3);
//--- W4
   if(A>C && C>E && E>D && D>B)
      return(W4);
//--- W5
   if(A>E && E>C && C>D && D>B)
      return(W5);
//--- W6
   if(C>A && A>B && B>E && E>D)
      return(W6);
//--- W7
   if(C>A && A>E && E>B && B>D)
      return(W7);
//--- W8
   if(E>A && A>C && C>B && B>D)
      return(W8);
//--- W9
   if(C>A && A>E && E>D && D>B)
      return(W9);
//--- W10
   if(E>A && A>C && C>D && D>B)
      return(W10);
//--- W11
   if(C>E && E>A && A>B && B>D)
      return(W11);
//--- W12
   if(E>C && C>A && A>B && B>D)
      return(W12);
//--- W13
   if(C>E && E>A && A>D && D>B)
      return(W13);
//--- W14
   if(E>C && C>A && A>D && D>B)
      return(W14);
//--- W15
   if(C>E && E>D && D>A && A>B)
      return(W15);
//--- W16
   if(E>C && C>D && D>A && A>B)
      return(W16);
   return(-1);
  }

Beim Erkennen des Musters wird es mit der Methode GetCategory() ausgewertet. Hier wird das zuvor definierte Strukturarray vom Typ RATING_SET verwendet.

//+------------------------------------------------------------------+
//| Define the profit categories                                     |
//+------------------------------------------------------------------+
bool CProgram::GetCategory(const string symbol,const int shift,RATING_SET &rate,ENUM_TIMEFRAMES timeframe,int threshold)
  {
   MqlRates rt[];
   datetime start=StringToTime(TimeToString(m_calendar1.SelectedDate(),TIME_DATE)+" "+(string)m_time_edit1.GetHours()+":"+(string)m_time_edit1.GetMinutes()+":00");
   start+=PeriodSeconds(timeframe)*shift;
   int copied=CopyRates(symbol,timeframe,start,4,rt);
//--- Get the data of previous candles
   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);

//--- Check for Uptrend
   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++;
     }

//--- Check for Downtrend
   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);
  }

Die bearbeiteten Auswertungsdaten werden an die Methode AddRow() übergeben, die Wahrscheinlichkeitswerte und Wirkungsgrade berechnet und in die Ergebnistabelle einfügt.

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void CProgram::AddRow(CTable &table,string pattern_name,RATING_SET &rate,int found,ENUM_TIMEFRAMES timeframe)
  {
   int row=m_total_row;
   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)?(double)sum1/found*100:0;
   p2=(found>0)?(double)sum2/found*100:0;
   k1=(found>0)?(m_k1*rate.a_uptrend+m_k2*rate.b_uptrend+m_k3*rate.c_uptrend)/found:0;
   k2=(found>0)?(m_k1*rate.a_dntrend+m_k2*rate.b_dntrend+m_k3*rate.c_dntrend)/found:0;
//---
   table.AddRow(row);
   table.SetValue(0,row,pattern_name);
   table.SetValue(1,row,(string)found);
   table.SetValue(2,row,TimeframeToString(timeframe));
   table.SetValue(3,row,DoubleToString(p1,2),2);
   table.SetValue(4,row,DoubleToString(p2,2),2);
   table.SetValue(5,row,DoubleToString(k1,2),2);
   table.SetValue(6,row,DoubleToString(k2,2),2);
   ZeroMemory(rate);
   m_total_row++;
  }

Um mögliche Fragen im Zusammenhang mit der Verwendung der Anwendung zu vermeiden, zeigt das folgende Video Beispiele für Berechnungen mit unterschiedlichen Einstellungen.


Empfehlungen für die Prüfung von Merrill-Mustern:

  • Damit die Anwendung korrekt funktioniert, benötigen wir historische Daten zum Testen an einem bestimmten Handelssymbol zum Herunterladen.
  • Es wird nicht empfohlen, alle Muster und alle Zeitrahmen gleichzeitig herunterzuladen, da die Handhabung der Ergebnisse sehr lange dauern kann.
  • Die häufigsten Szenarien, die Schwierigkeiten verursachen können, werden von Tipps begleitet. Solche Szenarien beinhalten das Nichtvorgeben eines Zeitrahmens oder eines Musters sowie ein ungültiges Datum.
  • Seien Sie vorsichtig beim Einstellen der Eigenschaften des EAs (Abb. 6). Wenn die Einstellungen nicht eindeutig sind, lesen Sie den Artikel erneut.
  • Der Artikel hat das Thema einer Methode zur Berechnung der Mustereffizienz zweimal behandelt. Der Link zum Artikel über das Thema wurde bereitgestellt. Beachten Sie, dass Sie ein klares Verständnis dafür benötigen, wie sich die Gewichtungskoeffizienten auf der Registerkarte Einstellungen auf die Musterbewertung auswirken.

Schlussfolgerung

Das unten angehängte Archiv enthält alle beschriebenen Dateien in den jeweiligen Ordnern. Für einen korrekten Betrieb legen Sie den Ordner MQL5 in das Stammverzeichnis des Terminals. Um das Stammverzeichnis des Terminals zu öffnen, in dem sich der Ordner MQL5 befindet, drücken Sie die Tastenkombination Ctrl+Shift+D im MetaTrader 5 Terminal oder verwenden Sie das Kontextmenü wie in Abb. 7 unten gezeigt.


Abb. 7. Öffnen des Ordners MQL5 im Stammordner von MetaTrader 5


Übersetzt aus dem Russischen von MetaQuotes Software Corp.
Originalartikel: https://www.mql5.com/ru/articles/7022

Beigefügte Dateien |
MQL5.zip (1736.93 KB)
Optimierungsmanagement (Teil II): Erstellen der Schlüsselobjekte und der Add-on-Logik Optimierungsmanagement (Teil II): Erstellen der Schlüsselobjekte und der Add-on-Logik

Dieser Artikel ist eine Fortsetzung der vorherigen Veröffentlichung über das Erstellen einer grafischen Oberfläche für das Optimierungsmanagement. Der Artikel berücksichtigt die Logik des Add-ons. Es wird ein Wrapper für das MetaTrader 5 Terminal erstellt, der es ermöglicht, das Add-on als verwalteten Prozess über C# auszuführen. Darüber hinaus wird in diesem Artikel der Betrieb mit Konfigurationsdateien und Setup-Dateien betrachtet. Die Anwendungslogik ist in zwei Teile gegliedert: Der erste Teil beschreibt die Methoden, die nach dem Drücken einer bestimmten Taste aufgerufen werden, während der zweite Teil den Start und die Verwaltung der Optimierung umfasst.

Bibliothek für ein leichtes und schnelles Entwickeln vom Programmen für den MetaTrader (Teil XIV): Das Symbolobjekt Bibliothek für ein leichtes und schnelles Entwickeln vom Programmen für den MetaTrader (Teil XIV): Das Symbolobjekt

In diesem Artikel werden wir die Klasse eines Symbolobjekts anlegen, das das Basisobjekt für die Erstellung der Kollektion der Symbole sein soll. Die Klasse wird es uns ermöglichen, Daten über die benötigten Symbole für ihre weitere Analyse und ihren Vergleich zu erhalten.

Eine neue Schiene: Benutzerdefinierte Indikatoren in MQL5 Eine neue Schiene: Benutzerdefinierte Indikatoren in MQL5

Ich werde nicht alle neuen Möglichkeiten und Funktionen des neuen Terminals und der Sprache aufzählen. Sie sind zahlreich und einige der Neuheiten sind eine Diskussion in einem eigenen Beitrag wert. Auch gibt es hier keinen Code, der mit objektorientierter Programmierung geschrieben wurde. Das Thema ist zu wichtig, um es nur im Kontext zusätzlicher Vorteile für Entwickler zu erwähnen. In diesem Beitrag gehen wir auf Indikatoren, ihre Struktur, Zeichnung, Typen und Programmierdetails im Vergleich zu MQL4 ein. Ich hoffe, dass dieser Beitrag für Einsteiger und erfahrene Entwickler hilfreich sein wird. Vielleicht entdeckt auch jemand etwas Neues.

Hier sind der neue MetaTrader 5 und MQL5 Hier sind der neue MetaTrader 5 und MQL5

Dies ist nur ein kurzer Überblick über MetaTrader 5. Ich kann nicht alle neuen Funktionen des Systems in so kurzer Zeit beschreiben. Die Tests begannen am 09.09.2009. Das ist ein symbolisches Datum und ich bin sicher, dass es eine Glückszahl werden wird. Es sind ein paar Tage vergangen, seit ich die Beta-Version des MetaTrader-5-Terminals und MQL5 bekommen habe. Ich konnte noch nicht alle Funktionen ausprobieren, doch ich bin jetzt schon beeindruckt.