Die Funktionen des Strategy Builders erweitern

27 Januar 2020, 12:38
Alexander Fedosov
0
327

Inhaltsverzeichnis

Einführung

In dem ersten Teil der Artikelserie haben wir Merrill-Muster analysiert und auf verschiedene Datenarrays angewendet, wie z.B. auf die Preise, preisbasierten Oszillatoren ATR, CCI, WPR und anderen. Der Zweck des Artikels war es, die Aussichten für die Verwendung der angegebenen Muster auf Devisen- und anderen Märkten zu untersuchen und zu bewerten. Der zweite Teil war der Erstellung eines Strategy Builder gewidmet, um einfache Strategien unter Verwendung der zuvor besprochenen Muster zusammenzustellen. Im dritten Teil werden wir die Funktionen der Strategieerstellung und -prüfung erweitern. Wir werden die Möglichkeit hinzufügen, zusätzlich zu den Punkten mit Losen zu arbeiten, sowie die Funktionalität zur Anzeige der Testergebnisse.

Übersicht über die Ergänzungen

Bevor wir uns mit den Neuerungen befassen, wollen wir uns den vorherigen Teil in Erinnerung rufen. Die Ergebnisse aller Tests wurden in einem kurzen zusammenfassenden Bericht dargestellt, während der Bericht als Gewinn/Verlust in Bezug auf Punkte des spezifisch betrachteten Finanzinstruments präsentiert wurde. Dies ermöglichte jedoch nicht die vollständige Bewertung aller potenziellen Fähigkeiten einer Strategie. Daher ist das Hauptziel die Erweiterung der Testerfunktionen und dann die Erweiterung der Parameter des Handelsberichts.

Bei der Umsetzung der Verbesserungen werden wir uns an den folgenden Plan halten:

  • Ersteinlage in Kontowährung.
  • Gewinn-Berechnungsoptionen: "In points" (Punkten) oder "In deposit currency" (Kontowährung).
  • Wenn "In deposit currency" gewählt wird, erscheinen zwei weitere Eingabefelder: "Lot Type" (Typ der Losgröße) und "Lot Size" (Losgröße).
  • Lot Type kann konstant oder auf dem Saldo basierend sein.
  • Lot Size. Verfügbar für den Lostyp "Constant".

Die folgenden Änderungen werden im Handelsbericht implementiert:

  • Total Net Profit Der Parameter ist nur für die Gewinnart "In deposit currency" verfügbar.
  • Balance Drawdown Absolute. Der Parameter ist nur für die Gewinnart "In deposit currency" verfügbar.
  • Balance Drawdown Max. Der Parameter ist nur für die Gewinnart "In deposit currency" verfügbar.
  • Neben der Anzahl der Kauf- und Verkaufspositionen wird auch der Prozentsatz der gewonnenen Positionen beider Arten angezeigt.
  • Strategy profitability. Das Verhältnis von Gesamtgewinn zu Gesamtverlust.
  • Recovery factor. Der Parameter ist nur für die Gewinnart "In deposit currency" verfügbar.

Sie können mehr über die neuen Parameter im Abschnitt Tester Bericht der MetaTrader 5 Hilfe lesen. Der Prototyp der oben genannten Funktionen ist in Abb.1 dargestellt.

Abb.1 Prototyp der neuen Testwerkzeuge.

Eine weitere Neuerung ist die Möglichkeit, das Testergebnis einer beliebigen Strategie visuell zu betrachten. Das bedeutet, dass Sie das Diagramm der Testergebnisse anzeigen können. Wir werden die Schaltfläche "Open graph" (siehe Abb.1) in den Berichtsabschnitt unserer App einfügen.

Abb.2 Aussehen der Grafik.

Wie in Abb.2 zu sehen ist, können die Art der Saldenveränderungen und die Handelsergebnisse in der Grafik visuell bewertet werden. Der Einfachheit halber wird im Titel das getestete Symbol, sein Zeitrahmen und das Zeitintervall, in dem die Tests durchgeführt werden, angezeigt.

Stufen der Implementierung der neuen Funktionen

Lassen Sie uns die Hauptelemente und Umsetzungsmethoden definieren, ähnlich wie wir es getan haben, als wir den Strategy Builder von Grund auf neu entwickelt haben. Der Hauptmethode wurden zwei Methoden hinzugefügt, die die Schnittstelle CreateGUI() erstellen. Betrachten wir diese Methoden und die Ergänzungen zu einer bereits bestehenden Methode.

//+------------------------------------------------------------------+
//| Erstellen der grafischen Schnittstelle des Programms             |
//+------------------------------------------------------------------+
bool CProgram::CreateGUI(void)
{
//--- Create a panel
   if(!CreateWindow("Merrill Constructor"))
      return(false);
//--- Create a dialog window
   if(!CreateDateSetting())
      return(false);
//--- Create a chart window
   if(!CreateGraphWindow())
      return(false);
//--- Create a load window
   if(!CreateLoading())
      return(false);
//--- Finish the creation of GUI
   CWndEvents::CompletedGUI();
   return(true);
}

Betrachten wir die Änderungen in der Methode zur Erstellung des Hauptfensters CreateWindow(): Es wurden neue Schnittstellenelemente hinzugefügt, um die in Abb. 1 gezeigten neuen Testinstrumente zu implementieren.

//---- CONSTRUCTOR tab
....
//---
   if(!CreateProfitType(int(0.35*(m_window[0].XSize()-150)-120),50+35*6+ygap))
      return(false);
   if(!CreateLotType(int(0.6*(m_window[0].XSize()-150)-120),50+35*6+ygap))
      return(false);
   if(!CreateBaseLotValue(int(0.85*(m_window[0].XSize()-150)-120),50+35*6+ygap))
      return(false);
   if(!CreateInitialDeposit(int(0.35*(m_window[0].XSize()-150)-120),50+35*7+ygap))
      return(false);
//---
   if(!CreateReportFrame(m_frame[2],int(0.35*(m_window[0].XSize()-150)-120),110+35*7+ygap))
      return(false);
//--- Graph opening button
   if(!CreateIconButton(int(0.35*(m_window[0].XSize()-150)-50),100+35*7+ygap))
      return(false);
//--- Report lines
   for(int i=0; i<11; i++)
   {
      if(i<5)
         if(!CreateTextLabel(m_report_text[i],int(0.37*(m_window[0].XSize()-150)-120),380+25*i+ygap,"",0))
            return(false);
      if(i>=5 && i<9)
         if(!CreateTextLabel(m_report_text[i],int(0.63*(m_window[0].XSize()-150)-120),380+25*(i-5)+ygap,"",0))
            return(false);
      if(i>=9)
         if(!CreateTextLabel(m_report_text[i],int(0.89*(m_window[0].XSize()-150)-120),380+25*(i-9)+ygap,"",0))
            return(false);
      m_report_text[i].IsCenterText(false);
      m_report_text[i].FontSize(10);
   }
....

Visuelle Änderungen wurden nur in der Registerkarte Konstruktor implementiert. Der vollständige Registerkarten-Implementierungscode ist in den Anhängen und im vorherigen Artikel verfügbar, während hier nur die neue Methoden gezeigt werden. Betrachten wir jede von ihnen. 

CreateProfitType(). Die Methode erstellt eine Dropdown-Liste mit dem beim Testen verwendeten Profit-Typ: Kontowährung oder Punkte.

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CProgram::CreateProfitType(const int x_gap,const int y_gap)
{
//--- Übergebe das Objekt dem Bedienungsfeld
   m_profit_type.MainPointer(m_tabs1);
//--- Hinzufügen zur Registerkarte
   m_tabs1.AddToElementsArray(0,m_profit_type);
//--- Array of the item values in the list view
   string pattern_names[2]=
   {
      "Pips","Currency"
   };
//--- Setze Eigenschaften vor der Erstellung
   m_profit_type.XSize(200);
   m_profit_type.YSize(25);
   m_profit_type.ItemsTotal(2);
   m_profit_type.FontSize(12);
   m_profit_type.LabelColor(C'0,100,255');
   m_profit_type.GetButtonPointer().FontSize(10);
   m_profit_type.GetButtonPointer().XGap(80);
   m_profit_type.GetButtonPointer().XSize(100);
   m_profit_type.GetButtonPointer().BackColor(clrAliceBlue);
   m_profit_type.GetListViewPointer().FontSize(10);
   m_profit_type.GetListViewPointer().YSize(44);
//--- Save the item values in the combobox list view
   for(int i=0; i<2; i++)
      m_profit_type.SetValue(i,pattern_names[i]);
//--- Get the list view pointer
   CListView *lv=m_profit_type.GetListViewPointer();
//--- Set the list view properties
   lv.LightsHover(true);
   m_profit_type.SelectItem(1);
//--- Erstellen des Kontrollelements
   if(!m_profit_type.CreateComboBox("Profit Type",x_gap,y_gap))
      return(false);
//--- Hinzufügen eines Objekts zum gemeinsamen Array der Objektgruppen
   CWndContainer::AddToElementsArray(0,m_profit_type);
   return(true);
}

CreateLotType(). Die Methode erstellt eine Dropdown-Liste, die mit dem Lostyp verloren geht: Abhängig von Constant oder Balance.

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CProgram::CreateLotType(const int x_gap,const int y_gap)
{
//--- Übergebe das Objekt dem Bedienungsfeld
   m_lot_type.MainPointer(m_tabs1);
//--- Hinzufügen zur Registerkarte
   m_tabs1.AddToElementsArray(0,m_lot_type);
//--- Array of the item values in the list view
   string pattern_names[2]=
   {
      "Balance","Constant"
   };
//--- Setze Eigenschaften vor der Erstellung
   m_lot_type.XSize(200);
   m_lot_type.YSize(25);
   m_lot_type.ItemsTotal(2);
   m_lot_type.FontSize(12);
   m_lot_type.LabelColor(C'0,100,255');
   m_lot_type.GetButtonPointer().FontSize(10);
   m_lot_type.GetButtonPointer().XGap(65);
   m_lot_type.GetButtonPointer().XSize(100);
   m_lot_type.GetButtonPointer().BackColor(clrAliceBlue);
   m_lot_type.GetListViewPointer().FontSize(10);
   m_lot_type.GetListViewPointer().YSize(44);
//--- Save the item values in the combobox list view
   for(int i=0; i<2; i++)
      m_lot_type.SetValue(i,pattern_names[i]);
//--- Get the list view pointer
   CListView *lv=m_lot_type.GetListViewPointer();
//--- Set the list view properties
   lv.LightsHover(true);
   m_lot_type.SelectItem(1);
//--- Erstellen des Kontrollelements
   if(!m_lot_type.CreateComboBox("Lot Type",x_gap,y_gap))
      return(false);
//--- Hinzufügen eines Objekts zum gemeinsamen Array der Objektgruppen
   CWndContainer::AddToElementsArray(0,m_lot_type);
   return(true);
}

CreateBaseLotValue(). Die Methode erstellt das Eingabefeld für die Losgröße.

/+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CProgram::CreateBaseLotValue(const int x_gap,const int y_gap)
{
//--- Sichern des Zeigers auf das Hauptsteuerelement
   m_base_lot.MainPointer(m_tabs1);
//--- Hinzufügen zur Registerkarte
   m_tabs1.AddToElementsArray(0,m_base_lot);
//--- Eigenschaften
   m_base_lot.XSize(210);
   m_base_lot.YSize(24);
   m_base_lot.LabelColor(C'0,100,255');
   m_base_lot.FontSize(12);
   m_base_lot.MaxValue(SymbolInfoDouble(Symbol(),SYMBOL_VOLUME_MAX));
   m_base_lot.MinValue(SymbolInfoDouble(Symbol(),SYMBOL_VOLUME_MIN));
   m_base_lot.StepValue(SymbolInfoDouble(Symbol(),SYMBOL_VOLUME_STEP));
   m_base_lot.SetDigits(2);
   m_base_lot.SpinEditMode(true);
   m_base_lot.GetTextBoxPointer().AutoSelectionMode(true);
   m_base_lot.GetTextBoxPointer().XGap(100);
//--- Erstellen des Kontrollelements
   if(!m_base_lot.CreateTextEdit("Base Lot Size",x_gap,y_gap))
      return(false);
   m_base_lot.SetValue((string)SymbolInfoDouble(Symbol(),SYMBOL_VOLUME_MIN));
//--- Hinzufügen eines Objekts zum gemeinsamen Array der Objektgruppen
   CWndContainer::AddToElementsArray(0,m_base_lot);
   return(true);
}

CreateInitialDeposit(). Erstellt ein Eingabefeld für die Ersteinlage beim Testen im Modus Gewinn — Währungsmodus.

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CProgram::CreateInitialDeposit(const int x_gap,const int y_gap)
{
//--- Sichern des Zeigers auf das Hauptsteuerelement
   m_init_deposit.MainPointer(m_tabs1);
//--- Hinzufügen zur Registerkarte
   m_tabs1.AddToElementsArray(0,m_init_deposit);
//--- Eigenschaften
   m_init_deposit.XSize(210);
   m_init_deposit.YSize(24);
   m_init_deposit.LabelColor(C'0,100,255');
   m_init_deposit.FontSize(12);
   m_init_deposit.MinValue(10);
   m_init_deposit.SetDigits(2);
   m_init_deposit.GetTextBoxPointer().AutoSelectionMode(true);
   m_init_deposit.GetTextBoxPointer().XGap(125);
//--- Erstellen des Kontrollelements
   if(!m_init_deposit.CreateTextEdit("Initial Deposit",x_gap,y_gap))
      return(false);
   m_init_deposit.SetValue((string)1000);
//--- Hinzufügen eines Objekts zum gemeinsamen Array der Objektgruppen
   CWndContainer::AddToElementsArray(0,m_init_deposit);
   return(true);
}

CreateIconButton(). Erstellt eine Schaltfläche, ein Klick auf diese Schaltfläche öffnet das Diagrammfenster.

//+------------------------------------------------------------------+
//| Creates a button with an image                                   |
//+------------------------------------------------------------------+
#resource "\\Images\\EasyAndFastGUI\\Icons\\bmp16\\bar_chart.bmp"
#resource "\\Images\\EasyAndFastGUI\\Icons\\bmp16\\bar_chart_gray.bmp"
//---
bool CProgram::CreateIconButton(const int x_gap,const int y_gap)
{
//--- Sichern des Zeigers auf das Hauptsteuerelement
   m_graph_button.MainPointer(m_tabs1);
//--- Hinzufügen zur Registerkarte
   m_tabs1.AddToElementsArray(0,m_graph_button);
//--- Eigenschaften
   m_graph_button.XSize(150);
   m_graph_button.YSize(22);
   m_graph_button.FontSize(11);
   m_graph_button.IconXGap(3);
   m_graph_button.IconYGap(3);
   m_graph_button.IsHighlighted(false);
   m_graph_button.IsCenterText(true);
   m_graph_button.IconFile("Images\\EasyAndFastGUI\\Icons\\bmp16\\bar_chart.bmp");
   m_graph_button.IconFileLocked("Images\\EasyAndFastGUI\\Icons\\bmp16\\bar_chart_gray.bmp");
   m_graph_button.IconFilePressed("Images\\EasyAndFastGUI\\Icons\\bmp16\\bar_chart.bmp");
   m_graph_button.IconFilePressedLocked("Images\\EasyAndFastGUI\\Icons\\bmp16\\bar_chart_gray.bmp");
   m_graph_button.BorderColor(C'0,100,255');
   m_graph_button.BackColor(clrAliceBlue);
//--- Erstellen des Kontrollelements
   if(!m_graph_button.CreateButton("",x_gap,y_gap))
      return(false);
//--- Hinzufügen eines Pointers auf das Element auf die Datenbasis
   CWndContainer::AddToElementsArray(0,m_graph_button);
   return(true);
}

Die Struktur für die Ausgabe der Berichtsmerkmale wurde in CreateWindow() geändert. Sie wurde ergänzt und es wurden drei Spalten statt bisher zwei implementiert. Dies sind alle Änderungen in der Methode zur Erstellung des Hauptfensters CreateWindow().

Als Nächstes kommen wir zu der Methode, die das Diagrammfenster und das Berichtsdiagramm implementiert — CreateGraphWindow().

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CProgram::CreateGraphWindow(void)
{
//--- Hinzufügen eines Zeigers auf das Array des Fensters
   CWndContainer::AddWindow(m_window[2]);
//--- Eigenschaften
   m_window[2].XSize(750);
   m_window[2].YSize(450);
   m_window[2].FontSize(9);
   m_window[2].WindowType(W_DIALOG);
   m_window[2].IsMovable(true);
//--- Erstellen der Formulars
   if(!m_window[2].CreateWindow(m_chart_id,m_subwin,"",75,75))
      return(false);
   //--- Charts
   if(!CreateGraph(22,22))
      return(false);
//---
   return(true);
}

Die Erstellungsmethode ist recht klein. Bitte beachten Sie die darin enthaltene Methode CreateGraph().

//+------------------------------------------------------------------+
//| Create a chart                                                   |
//+------------------------------------------------------------------+
bool CProgram::CreateGraph(const int x_gap,const int y_gap)
{
//--- Sichern des Zeigers auf das Hauptsteuerelement
   m_graph1.MainPointer(m_window[2]);
//--- Eigenschaften
   m_graph1.AutoXResizeMode(true);
   m_graph1.AutoYResizeMode(true);
   m_graph1.AutoXResizeRightOffset(10);
   m_graph1.AutoYResizeBottomOffset(10);
//--- Create element
   if(!m_graph1.CreateGraph(x_gap,y_gap))
      return(false);
//--- Chart properties
   CGraphic *graph=m_graph1.GetGraphicPointer();
   graph.BackgroundColor(::ColorToARGB(clrWhiteSmoke));
   graph.XAxis().Min(0);
   graph.BackgroundMainSize(20);
   graph.HistoryNameSize(0);
   graph.HistorySymbolSize(0);
   graph.HistoryNameWidth(0);
//--- Hinzufügen eines Pointers auf das Element auf die Datenbasis
   CWndContainer::AddToElementsArray(2,m_graph1);
   return(true);
}

Er erstellt ein leeres Diagramm und legt seine visuellen Eigenschaften fest. Die Daten des Diagramms werden später hinzugefügt.

Eine weitere Methode im Hauptfenster CreateGUI() ist CreateLoading(), die das Ladefenster erzeugt. Sie dient als Indikator für das Laden von Anwendungen, das Ändern von Spracheinstellungen sowie für das Testen und Arbeiten mit Daten.

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
#resource "\\Images\\EasyAndFastGUI\\Icons\\bmp16\\sandglass.bmp"
bool CProgram::CreateLoading(void)
{
//--- Hinzufügen eines Zeigers auf das Array des Fensters
   CWndContainer::AddWindow(m_window[3]);
//--- Eigenschaften
   m_window[3].XSize(100);
   m_window[3].YSize(50);
   m_window[3].LabelYGap(50/2-16/2);
   m_window[3].IconYGap(50/2-16/2);
   m_window[3].FontSize(9);
   m_window[3].WindowType(W_DIALOG);
   m_window[3].IsMovable(false);
   m_window[3].CloseButtonIsUsed(false);
   m_window[3].CaptionColorLocked(C'0,130,225');
   m_window[3].LabelColor(clrWhite);
   m_window[3].LabelColorLocked(clrWhite);
   m_window[3].CaptionHeight(51);
   m_window[3].IconFile("Images\\EasyAndFastGUI\\Icons\\bmp16\\sandglass.bmp");
   m_window[3].IconFileLocked("Images\\EasyAndFastGUI\\Icons\\bmp16\\sandglass.bmp");
   m_window[3].IconFilePressed("Images\\EasyAndFastGUI\\Icons\\bmp16\\sandglass.bmp");
   m_window[3].IconFilePressedLocked("Images\\EasyAndFastGUI\\Icons\\bmp16\\sandglass.bmp");
   int x=int(m_window[0].XSize()/2);
   int y=int(m_window[0].YSize()/2);
//--- Erstellen der Formulars
   if(!m_window[3].CreateWindow(m_chart_id,m_subwin,"Working...",x,y))
      return(false);
   return(true);
}

Wir haben die visuellen Elemente des Strategy Builders besprochen. Gehen wir nun zum Testalgorithmus über und sehen wir uns an, wie die Informationen darin mit den neuen visuellen Bedienelementen angezeigt werden.

Wie Sie sich vielleicht noch aus dem vorherigen Artikel erinnern, wird der Start und die Verarbeitung von Strategietests durch die Methode GetResult() implementiert. Wir haben neue Daten hinzugefügt und müssen diese Methode daher überarbeiten.

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void CProgram::GetResult(const string symbol)
{
//--- Abrufen der Zeitspanne
   m_start_date=StringToTime(TimeToString(m_calendar1.SelectedDate(),TIME_DATE)+" "+(string)m_time_edit1.GetHours()+":"+(string)m_time_edit1.GetMinutes()+":00");
   m_end_date=StringToTime(TimeToString(m_calendar2.SelectedDate(),TIME_DATE)+" "+(string)m_time_edit2.GetHours()+":"+(string)m_time_edit2.GetMinutes()+":00");
//--- Prüfen des angegebenen Zeitpunkts
   if(m_start_date>m_end_date || m_end_date>TimeCurrent())
   {
      if(m_lang_index==0)
         MessageBox("Неправильно выбран диапазон дат!","Ошибка",MB_OK);
      else if(m_lang_index==1)
         MessageBox("Incorrect date range selected!","Error",MB_OK);
      return;
   }
//--- Check if patterns are specified correctly
   if(m_combobox1.GetListViewPointer().SelectedItemIndex()==m_combobox2.GetListViewPointer().SelectedItemIndex())
   {
      if(m_lang_index==0)
         Messagebox("Паттерны не могут быть одинаковыми!","Ошибка",MB_OK);
      else if(m_lang_index==1)
         MessageBox("Patterns cannot be the same!","Error",MB_OK);
      return;
   }
//---
   m_window[3].OpenWindow();
//---
   m_counter=0;
   m_all_losses=0;
   m_all_profit=0;
   AddDeal(0,m_counter);
   ZeroMemory(m_report);
   MqlRates rt[];
   datetime cur_date=m_start_date;
   string tf=m_timeframe1.GetListViewPointer().SelectedItemText();
   int applied1=m_applied1.GetListViewPointer().SelectedItemIndex();
   int applied2=m_applied2.GetListViewPointer().SelectedItemIndex();
   int applied3=m_applied3.GetListViewPointer().SelectedItemIndex();
   int applied4=m_applied4.GetListViewPointer().SelectedItemIndex();
   int applied5=m_applied5.GetListViewPointer().SelectedItemIndex();
   int applied6=m_applied6.GetListViewPointer().SelectedItemIndex();
//---
   while(cur_date<m_end_date)
   {
      //---
      if(
         applied1>7 || applied2>7 || applied3>7 ||
         applied4>7 || applied5>7 || applied6>7)
      {
         if(m_custom_path.GetValue()=="")
         {
            if(m_lang_index==0)
               MessageBox("Не установлен путь к индикатору!","Ошибка",MB_OK);
            else if(m_lang_index==1)
               MessageBox("The indicator path is not set!","Error",MB_OK);
            break;
         }
         if(m_custom_param.GetValue()=="")
         {
            if(m_lang_index==0)
               MessageBox("Не установлены параметры индикатора!","Ошибка",MB_OK);
            else if(m_lang_index==1)
               MessageBox("Indicator parameters not set!","Error",MB_OK);
            break;
         }
      }
      //---
      if(
         BuySignal(symbol,m_start_date,applied1,1) ||
         BuySignal(symbol,m_start_date,applied2,2) ||
         BuySignal(symbol,m_start_date,applied3,3))
      {
         CalculateBuyDeals(symbol,m_start_date);
         cur_date=m_start_date;
         continue;
      }
      if(
         SellSignal(symbol,m_start_date,applied4,1) ||
         SellSignal(symbol,m_start_date,applied5,2) ||
         SellSignal(symbol,m_start_date,applied6,3))
      {

         CalculateSellDeals(symbol,m_start_date);
         cur_date=m_start_date;
         continue;
      }
      m_start_date+=PeriodSeconds(StringToTimeframe(tf));
      cur_date=m_start_date;
   }
//--- Output the report
   PrintReport();
//---
   m_window[3].CloseDialogBox();
}

Die folgenden Modifikationen sind implementiert:

  • Es wird ein Check hinzugefügt, um zu überprüfen, ob die Muster für Kauf- und Verkaufssignale übereinstimmen.
  • Es wurde die Verwendung des zuvor erstellten Download-Fensters hinzugefügt.
  • Es wurde die Methode AddDeal() hinzugefügt, die für das Schreiben von Testergebnissen in das Datenfeld, das vom Diagramm verwendet wird, verantwortlich ist. Der Anfangswert für das Diagramm wird hier festgelegt. Bei der Gewinnart "Currency" ist der Anfangswert "Currency". Für "Points" ist er Null.

Betrachten wir nun die Methode AddDeal() zum Hinzufügen von Daten:

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void CProgram::AddDeal(int points,int index)
{
//--- In points
   if(m_profit_type.GetListViewPointer().SelectedItemIndex()==0)
   {
      if(index==0)
      {
         ArrayResize(data,index+1);
         data[index]=0;
         return;
      }
      ArrayResize(data,index+1);
      data[index]=data[index-1]+points;
   }
//--- In deposit currency
   else if(m_profit_type.GetListViewPointer().SelectedItemIndex()==1)
   {
      if(index==0)
      {
         ArrayResize(data,index+1);
         data[index]=StringToDouble(m_init_deposit.GetValue());
         return;
      }
      ArrayResize(data,index+1);
      //--- Get a selected symbol
      string symbol=m_table_symb.GetValue(0,m_table_symb.SelectedItem());
      string basesymbol=AccountInfoString(ACCOUNT_CURRENCY);
      string tf=m_timeframe1.GetListViewPointer().SelectedItemText();
      double lot=StringToDouble(m_base_lot.GetValue());
      if(m_lot_type.GetListViewPointer().SelectedItemIndex()>0)
      {
         lot*=data[index-1];
         lot=GetLotForOpeningPos(symbol,POSITION_TYPE_BUY,lot);
      }

      double pip_price=1;
      int shift=0;
      // --- Direct pair
      if(StringSubstr(symbol,3,3)==basesymbol)
      {
         pip_price=NormalizeDouble(SymbolInfoDouble(symbol,SYMBOL_TRADE_CONTRACT_SIZE)*SymbolInfoDouble(symbol,SYMBOL_POINT)*lot,2);
      }
      //--- Reverse pair
      else if(StringSubstr(symbol,0,3)==basesymbol)
      {
         shift=iBarShift(symbol,StringToTimeframe(tf),m_start_date,false);
         pip_price=NormalizeDouble(SymbolInfoDouble(symbol,SYMBOL_TRADE_CONTRACT_SIZE)*SymbolInfoDouble(symbol,SYMBOL_POINT)*lot/iOpen(symbol,StringToTimeframe(tf),shift),2);
      }
      else
      {
         //--- Cross pair
         StringConcatenate(symbol,StringSubstr(symbol,3,3),basesymbol);
         if(SymbolInfoDouble(symbol,SYMBOL_BID)!=0)
         {
            shift=iBarShift(symbol,StringToTimeframe(tf),m_start_date,false);
            pip_price=NormalizeDouble(SymbolInfoDouble(symbol,SYMBOL_TRADE_CONTRACT_SIZE)*SymbolInfoDouble(symbol,SYMBOL_POINT)*lot/iOpen(symbol,StringToTimeframe(tf),shift),2);
         }
         //---
         StringConcatenate(symbol,basesymbol,StringSubstr(symbol,0,3));
         if(SymbolInfoDouble(symbol,SYMBOL_BID)!=0)
         {
            shift=iBarShift(symbol,StringToTimeframe(tf),m_start_date,false);
            pip_price=NormalizeDouble(SymbolInfoDouble(symbol,SYMBOL_TRADE_CONTRACT_SIZE)*SymbolInfoDouble(symbol,SYMBOL_POINT)*lot*iOpen(symbol,StringToTimeframe(tf),shift),2);
         }
      }
      //---
      if(points>0)
         m_all_profit+=pip_price*points;
      else
         m_all_losses+=pip_price*-points;
      //---
      data[index]=data[index-1]+pip_price*points;
   }
}

Sie hat zwei Argumente:

  1. points wird für den neuen Wert für das Diagramm verwendet. Der erhaltene Wert ist das Ergebnis des Geschäftsabschlusses in Punkten. Dies ist entweder Take Profit oder Stop Loss.
  2. index ist der Index der Position.

Abhängig vom gewählten Profit-Anzeigemodus werden die entsprechenden Daten in der Methode berechnet und zum Datenarray hinzugefügt. Beim Gewinnmodus "Points" ist jedes neue Element des Arrays die Summe des vorherigen Wertes und des Handelsergebnisses in Punkten. Im Modus "Currency" wird das erhaltene Geschäftsergebnis in Punkten in die Einzahlungswährung umgerechnet. Diese Operation berücksichtigt den Typ des getesteten Paares: direktes, umgekehrtes oder Kreuzpaar.

Die beiden anderen Methoden, die modifiziert wurden, sind CalculateBuyDeals() und CalculateSellDeals(). Sie verarbeiten das gefundene Signal und öffnen gegebenenfalls virtuell eine Position. Betrachten wir eine der Methoden (die Modifikationen der zweiten Methode sind ähnlich):

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void CProgram::CalculateBuyDeals(const string symbol,datetime start)
{
   MqlRates rt[];
   int TP=int(m_takeprofit1.GetValue());
   int SL=int(m_stoploss1.GetValue());
   string tf=m_timeframe1.GetListViewPointer().SelectedItemText();
   int copied=CopyRates(symbol,StringToTimeframe(tf),m_start_date,m_end_date,rt);
   double deal_price=iOpen(symbol,StringToTimeframe(tf),copied);
   for(int j=0; j<copied; j++)
   {
      //--- Take Profit trigger
      if((iHigh(symbol,StringToTimeframe(tf),copied-j)-deal_price)/SymbolInfoDouble(symbol,SYMBOL_POINT)>=TP)
      {
         m_counter++;
         AddDeal(TP,m_counter);
         m_report.profit_trades++;
         m_report.profit+=TP;
         m_report.profit_pips+=TP;
         m_report.long_trades++;
         m_report.profit_long++;
         m_report.total_trades++;
         m_start_date=IndexToDate(m_start_date,StringToTimeframe(tf),j);
         return;
      }
      //--- Stop Loss trigger
      else if((deal_price-iLow(symbol,StringToTimeframe(tf),copied-j))/SymbolInfoDouble(symbol,SYMBOL_POINT)>=SL)
      {
         m_counter++;
         AddDeal(-SL,m_counter);
         m_report.loss_trades++;
         m_report.profit-=SL;
         m_report.loss_pips+=SL;
         m_report.long_trades++;
         m_report.total_trades++;
         m_start_date=IndexToDate(m_start_date,StringToTimeframe(tf),j);
         return;
      }
   }
   m_start_date=m_end_date;
}

Die Änderungen betreffen die Auslösung von Take Profit und Stop Loss. Hier verarbeitet die oben beschriebene Methode AddDeal() die Tatsache eines Geschäftsabschlusses. Auch die neuen Parameter profit_pips und loss_pips wurden der Struktur REPORT m_report hinzugefügt. Diese Parameter ermöglichen die Berechnung neuer Merkmale im Bericht.

Und die letzte Methode, die wesentlich modifiziert wurde, ist die Verarbeitung der empfangenen Daten und die Ausgabe der Ergebnisse in den Bericht. Auch dies wird durch die Methode PrintReport() durchgeführt:

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void CProgram::PrintReport(void)
{
   string report_label[11];
   if(m_lang_index==0)
   {
      report_label[0]="Всего трейдов: ";
      report_label[1]="Чистая прибыль: ";
      report_label[2]="Прибыль в пунктах: ";
      report_label[3]="Абс.просадка баланса: ";
      report_label[4]="Макс.просадка баланса: ";
      report_label[5]="Корот.трейды/% выигр: ";
      report_label[6]="Приб.трейды/% от всех: ";
      report_label[7]="Прибыльность: ";
      report_label[8]="Фактор восстановления: ";
      report_label[9]="Длин.трейды/% выигр: ";
      report_label[10]="Убыт.трейды/% от всех: ";
   }
   else
   {
      report_label[0]="Total trades: ";
      report_label[1]="Total profit: ";
      report_label[2]="Total profit(pips): ";
      report_label[3]="Balance Drawdown Abs: ";
      report_label[4]="Balance Drawdown Max: ";
      report_label[5]="Short trades/won %: ";
      report_label[6]="Profit trades/% of all: ";
      report_label[7]="Profit Factor: ";
      report_label[8]="Recovery Factor: ";
      report_label[9]="Long trades/won %: ";
      report_label[10]="Loss trades/% of all: ";
   }
   //---
   m_report_text[0].LabelText(report_label[0]+string(m_report.total_trades));
   //---
   if(m_report.total_trades==0)
      return;
   //---
   if(m_profit_type.GetListViewPointer().SelectedItemIndex()==1)
   {
      double maxprofit=0.0,maxdd=0.0;
      for(int i=1; i<ArraySize(data); i++)
      {
         if(data[i]>maxprofit)
            maxprofit=data[i];
         if(maxdd<maxprofit-data[i])
            maxdd=maxprofit-data[i];
      }
      m_report_text[1].LabelText(report_label[1]+DoubleToString(data[ArraySize(data)-1],2));
      m_report_text[3].LabelText(report_label[3]+string(data[0]-data[ArrayMinimum(data)]));
      m_report_text[4].LabelText(report_label[4]+DoubleToString(maxdd/maxprofit*100,2)+"%");
      m_report_text[7].LabelText(report_label[7]+DoubleToString(m_all_profit/m_all_losses,2));
      m_report_text[8].LabelText(report_label[8]+DoubleToString((data[ArraySize(data)-1]-data[0])/maxdd,2));
   }
   else
   {
      m_report_text[1].LabelText(report_label[1]+"-");
      m_report_text[3].LabelText(report_label[3]+"-");
      m_report_text[4].LabelText(report_label[4]+"-");
      m_report_text[7].LabelText(report_label[7]+DoubleToString(m_report.profit_pips/(double)m_report.loss_pips,2));
   }
   m_report_text[2].LabelText(report_label[2]+string(m_report.profit));
   m_report_text[5].LabelText(report_label[5]+string(m_report.short_trades)+"/"+DoubleToString(m_report.profit_short/(double)m_report.short_trades*100,1)+"%");
   m_report_text[6].LabelText(report_label[6]+string(m_report.profit_trades)+"/"+DoubleToString(m_report.profit_trades/(double)m_report.total_trades*100,1)+"%");
   m_report_text[9].LabelText(report_label[9]+string(m_report.long_trades)+"/"+DoubleToString(m_report.profit_long/(double)m_report.long_trades*100,1)+"%");
   m_report_text[10].LabelText(report_label[10]+string(m_report.loss_trades)+"/"+DoubleToString(m_report.loss_trades/(double)m_report.total_trades*100,1)+"%");
//---
   for(int i=0; i<11; i++)
      m_report_text[i].Update(true);
   m_reported=true;
}

Wie in der Übersicht erwähnt wurde, sind einige Parameter, z.B. Gesamtgewinn oder Erholungsfaktor, für den Testmodus "Points" nicht verfügbar.


Tests und Demonstration des Strategy Builders

Nach den Verbesserungen und Aktualisierungen, die in der Anwendung implementiert wurden, ist es notwendig, das Nutzerhandbuch zu aktualisieren.

Schritt 1. Stellen Sie die Sprache der Nutzeroberfläche ein. 

Dieser Schritt ist optional und wird nur benötigt, wenn Sie die Sprache ändern möchten.

Schritt 2. Setzen Sie die Parameter des Indikators.

Die Anwendung hat bereits Standardparameter, daher ist es nicht notwendig, alle Kennzeichen zu konfigurieren. Ändern Sie nur die erforderlichen Parameter. Falls erforderlich, können Sie die Einstellungen jederzeit ändern.

Schritt 3. Legen Sie die Tabelle der Symbole fest.

Standardmäßig werden alle Symbole aus der Market Watch nach dem Vorhandensein von USD im Namen gefiltert. Sie können alle verfügbaren Symbole anzeigen, indem Sie einfach die Markierung des Ankreuzfeldes entfernen.

Schritt 4-5. Wählen Sie das Zeitintervall und den Zeitrahmen für die Prüfung aus - diese Schritte haben sich nicht geändert.

Schritt 6. Aktivieren Sie Verkaufs-/Kaufsignale und wählen Sie ein Muster für den Test aus.

Das Verfahren zur Signaleinrichtung hat sich nicht geändert. Die Änderungen gelten jedoch für die visuelle Anzeige: Wenn einer der Signaltypen deaktiviert ist, werden alle relevanten Einstellungen ausgeblendet, wie in Abb. 3 gezeigt.

Abb.3 Deaktivieren von Kauf- oder Verkaufssignalen.

Schritt 7. Die Einstellungen für Take Profit und Stop Loss haben sich nicht geändert.

Schritt 8. Wählen Sie den Gewinntyp.

  • Wenn Points gewählt wird, werden die Testergebnisse im Abschnitt Bericht in Punkten des gewählten Währungssymbols angezeigt. 
  • Wenn Currency ausgewählt ist, erscheinen zusätzliche Einstellungen: Losgrößentyp, Losgröße und Ersteinlage. Der Losgrößentyp wirkt sich auf die Berechnung des Loses aus, das beim Testen angewendet wird. Es kann konstant bleiben oder vom Saldo abgeleitet werden.

Schritt 9. Nach den Schritten 1-8 wählen Sie das Testinstrument durch einen Linksklick in der Tabelle aus. 

Nach Abschluss des Tests werden die Ergebnisse im Abschnitt Bericht angezeigt. Danach können Sie auf Grafik öffnen klicken.

Das nächste Video zeigt das Testen nach dem obigen Algorithmus.



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.
  • Seien Sie vorsichtig mit nutzerdefinierten Indikatorparametern: Puffernummer, Name oder Parameter sollten durch Komma getrennt werden. Beachten Sie, dass nur numerische Werte unterstützt werden. Der Name des benutzerdefinierten Indikators ist der Pfad relativ zum Stammverzeichnis des Indikators (MQL5/Indicators/). Wenn sich der Indikator in einem Unterverzeichnis befindet, z.B. MQL5/Indicators/Examples, sollte der Name entsprechend aussehen: "Beispiele\\Indikator_Name" (verwenden Sie als Trennzeichen immer einen doppelten Backslash anstelle eines einfachen).
  • Die häufigsten Szenarien, die Schwierigkeiten verursachen können, werden von Tipps begleitet. Dazu gehören die gleichen Muster für beide Signale, der Versuch, ein Diagramm ohne vorherige Tests zu öffnen, falsche Testbeginn- und Enddaten.
  • Zu Beginn des Artikels erwähnte ich einen Link zu den Beschreibungen der im Bericht verwendeten Merkmale. Verwenden Sie den Link, um mehr über die Parameterbeschreibungen und Berechnungsmethoden zu lesen.

Schlussfolgerung

Das unten angehängte Archiv enthält alle beschriebenen Dateien in 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 Strg+Umschalt+D im MetaTrader 5 Terminal oder verwenden Sie das Kontextmenü wie in Abb. 5 unten gezeigt.


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


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

Beigefügte Dateien |
MQL5.zip (1936.58 KB)
Bibliothek für ein leichtes und schnelles Entwickeln vom Programmen für den MetaTrader (Teil XXIV): Handelsklassen - automatische Korrektur ungültiger Parametern Bibliothek für ein leichtes und schnelles Entwickeln vom Programmen für den MetaTrader (Teil XXIV): Handelsklassen - automatische Korrektur ungültiger Parametern

In diesem Artikel werden wir einen Blick auf die Behandlung ungültiger Handelsparameter werfen und die Handelsereignisklasse verbessern. Jetzt werden alle Handelsereignisse (sowohl einzelne als auch die gleichzeitig bei einem Tick auftretenden) in Programmen korrekt definiert.

Kontinuierliche Walk-Forward-Optimierung (Teil 1): Arbeiten mit Optimierungsberichten Kontinuierliche Walk-Forward-Optimierung (Teil 1): Arbeiten mit Optimierungsberichten

Der erste Artikel beschäftigt sich mit der Erstellung eines Toolkits für die Arbeit mit Optimierungsberichten, für den Import aus dem Terminal sowie für die Filterung und Sortierung der erhaltenen Daten. MetaTrader 5 ermöglicht das Laden von Optimierungsergebnissen, unser Ziel ist es jedoch, dem Optimierungsbericht unsere eigenen Daten hinzuzufügen.

Bibliothek für ein leichtes und schnelles Entwickeln vom Programmen für den MetaTrader (Teil XXV): Behandlung der Fehlermeldungen von Server Bibliothek für ein leichtes und schnelles Entwickeln vom Programmen für den MetaTrader (Teil XXV): Behandlung der Fehlermeldungen von Server

Nachdem wir einen Handelsauftrag an den Server gesendet haben, müssen wir die Fehlercodes oder das Fehlen von Fehlern überprüfen. In diesem Artikel werden wir die Behandlung von Fehlern, die vom Handelsserver zurückgegeben werden, besprechen und die Erstellung von ausstehenden Handelsanfragen vorbereiten.

Mit Boxplot saisonale Muster von Finanzzeitreihen erforschen Mit Boxplot saisonale Muster von Finanzzeitreihen erforschen

In diesem Artikel werden wir die saisonalen Charakteristika von Finanzzeitreihen mit Hilfe von Boxplot-Diagrammen betrachten. Jedes separate Boxplot (oder Box-and-Whiskey-Diagramm) bietet eine gute Visualisierung der Verteilung von Werten entlang des Datensatzes. Boxplots sollten nicht mit den Kerzencharts verwechselt werden, obwohl sie visuell ähnlich aussehen.