• Registre-se

Ampliando as funcionalidades do Construtor de Estratégia

27 janeiro 2020, 11:32
Alexander Fedosov
0
761

Índice

Introdução

Na primeira parte desta série de artigos, nós analisamos os padrões de Merrill e os aplicamos a diferentes arrays de dados, como o preço e os osciladores baseados no ATR, CCI e WPR, entre outros. O objetivo daquele artigo era de explorar e avaliar as perspectivas de uso dos padrões especificados em forex e outros mercados. A segunda parte foi dedicada à criação de um Construtor de Estratégia para montar estratégias simples usando os padrões discutidos anteriormente. Na terceira parte, nós expandiremos a funcionalidade de criação e teste das estratégias. Adicionaremos a possibilidade de trabalhar com lotes, além de pontos, bem como a funcionalidade para visualizar os resultados dos testes.

Visão geral das Adições

Antes de considerar os novos recursos, vamos relembrar a parte anterior. Os resultados de todos os testes foram exibidos em um relatório resumido, enquanto o resultado foi apresentado em lucro/perda em pontos do instrumento financeiro considerado. No entanto, isso não permitiu a avaliação completa de todos os recursos em potencial de uma estratégia. Portanto, o principal objetivo é expandir os recursos do testador e, em seguida, expandir os parâmetros do relatório de negociação.

Nós seguiremos o plano a seguir ao implementar as melhorias:

  • Depósito inicial na moeda da conta.
  • Opção do cálculo do Lucro: "Em pontos" ou "Na moeda de depósito".
  • Se selecionarmos "Na moeda do depósito", dois outros campos de entrada serão exibidos: "Tipo de lote" e "Tamanho do lote".
  • Tipo do lote pode ser constante ou com base no saldo.
  • Tamanho do Lote. Disponível para o tipo de lote Constante.

As seguintes alterações serão implementadas no relatório de negociação:

  • Lucro Líquido Total. O parâmetro está disponível apenas para o tipo de lucro "Na moeda do depósito".
  • Rebaixamento do Saldo Absoluto. O parâmetro está disponível apenas para o tipo de lucro "Na moeda do depósito".
  • Rebaixamento do Saldo Max. O parâmetro está disponível apenas para o tipo de lucro "Na moeda do depósito".
  • Além do número de negociações vendida e comprada, a porcentagem de negociações vencedoras de ambos os tipos é exibida.
  • Rentabilidade da estratégia. A razão do lucro total para a perda total.
  • Fator de recuperação. O parâmetro está disponível apenas para o tipo de lucro "Na moeda do depósito".

Você pode ler mais sobre os novos parâmetros na seção Relatório de teste da Ajuda do MetaTrader 5. O protótipo dos recursos acima é mostrado na fig.1.

Fig.1 Protótipo de novas ferramentas de teste.

Outro novo recurso é a possibilidade de visualizar o resultado de teste de qualquer estratégia. Isso significa que você pode visualizar o gráfico de resultados dos testes. Nós adicionaremos o botão "Abrir gráfico" (mostrado na fig.1) à seção Relatório do nosso aplicativo.

Fig.2 Aparência do gráfico.

Como visto na figura 2, a natureza do movimento dos depósitos e os resultados das negociações podem ser visualmente avaliados no gráfico. Por conveniência, o título exibe o símbolo testado, seu período gráfico e o intervalo de tempo em que o teste é realizado.

Estágios da implementação dos novos recursos

Vamos definir os principais elementos e métodos de implementação, da mesma forma que fizemos ao desenvolver o Construtor de Estratégia do zero. Dois métodos foram adicionados ao método principal, criando a interface CreateGUI(). Vamos ver esses métodos e as adições ao método existente anteriormente.

//+------------------------------------------------------------------+
//| Creates the graphical interface of the program                   |
//+------------------------------------------------------------------+
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);
}

Vamos ver as alterações no método de criação da janela principal CreateWindow(): novos elementos de interface foram adicionados para implementar novos instrumentos de teste mostrados na fig.1.

//---- 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);
   }
....

As alterações visuais foram implementadas apenas na guia Construtor. O código da implementação da guia completa está disponível nos anexos e no artigo anterior, enquanto aqui serão mostrados apenas os novos métodos. Vamos considerar cada um deles. 

CreateProfitType(). O método cria uma lista suspensa com o tipo de lucro usado no teste: moeda de depósito ou pontos.

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CProgram::CreateProfitType(const int x_gap,const int y_gap)
{
//--- Pass the object to the panel
   m_profit_type.MainPointer(m_tabs1);
//--- Attach to tab
   m_tabs1.AddToElementsArray(0,m_profit_type);
//--- Array of the item values in the list view
   string pattern_names[2]=
   {
      "Pips","Currency"
   };
//--- Set properties before creation
   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);
//--- Create a control
   if(!m_profit_type.CreateComboBox("Profit Type",x_gap,y_gap))
      return(false);
//--- Add the object to the common array of object groups
   CWndContainer::AddToElementsArray(0,m_profit_type);
   return(true);
}

CreateLotType(). O método cria uma lista suspensa com o tipo de lote: dependente da constante ou do saldo.

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CProgram::CreateLotType(const int x_gap,const int y_gap)
{
//--- Pass the object to the panel
   m_lot_type.MainPointer(m_tabs1);
//--- Attach to tab
   m_tabs1.AddToElementsArray(0,m_lot_type);
//--- Array of the item values in the list view
   string pattern_names[2]=
   {
      "Balance","Constant"
   };
//--- Set properties before creation
   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);
//--- Create a control
   if(!m_lot_type.CreateComboBox("Lot Type",x_gap,y_gap))
      return(false);
//--- Add the object to the common array of object groups
   CWndContainer::AddToElementsArray(0,m_lot_type);
   return(true);
}

CreateBaseLotValue(). O método cria o campo de entrada do valor do lote.

/+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CProgram::CreateBaseLotValue(const int x_gap,const int y_gap)
{
//--- Save the pointer to the main control
   m_base_lot.MainPointer(m_tabs1);
//--- Attach to tab
   m_tabs1.AddToElementsArray(0,m_base_lot);
//--- Properties
   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);
//--- Create a control
   if(!m_base_lot.CreateTextEdit("Base Lot Size",x_gap,y_gap))
      return(false);
   m_base_lot.SetValue((string)SymbolInfoDouble(Symbol(),SYMBOL_VOLUME_MIN));
//--- Add the object to the common array of object groups
   CWndContainer::AddToElementsArray(0,m_base_lot);
   return(true);
}

CreateInitialDeposit(). Cria um campo de entrada para o depósito inicial ao testar no modo Lucro — Moeda.

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CProgram::CreateInitialDeposit(const int x_gap,const int y_gap)
{
//--- Save the pointer to the main control
   m_init_deposit.MainPointer(m_tabs1);
//--- Attach to tab
   m_tabs1.AddToElementsArray(0,m_init_deposit);
//--- Properties
   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);
//--- Create a control
   if(!m_init_deposit.CreateTextEdit("Initial Deposit",x_gap,y_gap))
      return(false);
   m_init_deposit.SetValue((string)1000);
//--- Add the object to the common array of object groups
   CWndContainer::AddToElementsArray(0,m_init_deposit);
   return(true);
}

CreateIconButton(). Cria um botão para clique no qual abre a janela do gráfico.

//+------------------------------------------------------------------+
//| 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)
{
//--- Save the pointer to the main control
   m_graph_button.MainPointer(m_tabs1);
//--- Attach to tab
   m_tabs1.AddToElementsArray(0,m_graph_button);
//--- Properties
   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);
//--- Create a control
   if(!m_graph_button.CreateButton("",x_gap,y_gap))
      return(false);
//--- Add the element pointer to the data base
   CWndContainer::AddToElementsArray(0,m_graph_button);
   return(true);
}

A estrutura de saída das características do relatório foi modificada em CreateWindow(). Ela foi complementada e implementada em três colunas, em vez de duas como era antes. Essas são todas as modificações no método de criação da janela principal CreateWindow().

Em seguida, vamos seguir para o método que implementa a janela do gráfico e o gráfico do relatório — CreateGraphWindow().

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CProgram::CreateGraphWindow(void)
{
//--- Add the pointer to the window array
   CWndContainer::AddWindow(m_window[2]);
//--- Properties
   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);
//--- Create the form
   if(!m_window[2].CreateWindow(m_chart_id,m_subwin,"",75,75))
      return(false);
   //--- Charts
   if(!CreateGraph(22,22))
      return(false);
//---
   return(true);
}

O método de criação é bem pequeno. Por favor, preste atenção ao método CreateGraph() contido nele.

//+------------------------------------------------------------------+
//| Create a chart                                                   |
//+------------------------------------------------------------------+
bool CProgram::CreateGraph(const int x_gap,const int y_gap)
{
//--- Save the pointer to the main control
   m_graph1.MainPointer(m_window[2]);
//--- Properties
   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);
//--- Add the element pointer to the data base
   CWndContainer::AddToElementsArray(2,m_graph1);
   return(true);
}

Ele cria um gráfico vazio e define suas características visuais. Os dados do gráfico serão adicionados mais tarde.

Mais um método que forma parte do método CreateGUI() é o CreateLoading(), que cria a janela de carregamento. Ele serve como um indicador para carregamento de aplicativos, alteração das configurações de idioma, bem como para testar e trabalhar com os dados.

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
#resource "\\Images\\EasyAndFastGUI\\Icons\\bmp16\\sandglass.bmp"
bool CProgram::CreateLoading(void)
{
//--- Add the pointer to the window array
   CWndContainer::AddWindow(m_window[3]);
//--- Properties
   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);
//--- Create the form
   if(!m_window[3].CreateWindow(m_chart_id,m_subwin,"Working...",x,y))
      return(false);
   return(true);
}

Nós consideramos os elementos visuais do Construtor de Estratégia. Vamos seguir para o algoritmo de teste e ver como as informações são exibidas nele usando os novos controles visuais.

Como você deve se lembrar do artigo anterior, o lançamento e o processamento dos testes de estratégia são implementados pelo método GetResult(). Nós adicionamos os novos dados e, portanto, nós precisamos revisar esse método.

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void CProgram::GetResult(const string symbol)
{
//--- Get the date range
   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");
//--- Check specified dates
   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();
}

As seguintes modificações foram implementadas:

  • Uma verificação é adicionada para verificar se os padrões de sinais de compra e venda não se correspondem.
  • Adicionado o uso da janela de Download criada anteriormente.
  • Adicionado o método AddDeal(), responsável por gravar os resultados dos testes no array de dados usado pelo gráfico. O valor inicial para o gráfico é definido aqui. Com o tipo de lucro "Moeda", o valor inicial é "Moeda". Para "Pontos" ele é zero.

Agora vamos ver o método de adição de dados AddDeal():

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

Ele tem dois valores nos argumentos:

  1. pontos é usado para o novo valor para o gráfico. O valor recebido é o resultado do fechamento do negócio em pontos. Isso é o Take Profit ou o Stop Loss.
  2. índice é o índice da negociação.

Dependendo do modo de exibição do lucro selecionado, os dados correspondentes são calculados no método e adicionados ao array de dados. Para o modo de lucro em "Pontos", cada novo elemento do array é a soma do valor anterior e o resultado da negociação em pontos. No modo "Moeda", os resultados do negócio recebido em pontos são convertidos na moeda do depósito. Esta operação leva em consideração o tipo do par testado: direto, reverso ou cruzado.

Os outros dois métodos que foram modificados são CalculateBuyDeals() e CalculateSellDeals(). Eles processam o sinal encontrado e abrem uma posição virtualmente, se necessário. Vamos ver um dos métodos (as modificações no segundo método são semelhantes):

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

As mudanças dizem respeito ao acionamento do Take Profit e Stop Loss. Aqui, o método descrito acima AddDeal() processa o acontecimento de um encerramento do negócio. Novos parâmetros, tais como profit_pips e loss_pips também foram adicionados à estrutura REPORT m_report. Esses parâmetros permitem calcular novas características no relatório.

E o último método modificado significativamente é o processamento dos dados recebidos e a saída dos resultados no relatório. Novamente, isso é realizado pelo método PrintReport():

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

Como mencionado na visão geral, alguns parâmetros, por exemplo, Lucro total ou Fator de recuperação, não estão disponíveis para o modo de teste "Pontos".


Teste e Demonstração do Construtor de Estratégia

Após as melhorias e atualizações implementadas no aplicativo, é necessário atualizar o manual do usuário.

Passo 1. Definir o idioma da interface do usuário. 

Esta etapa é opcional e só é necessária se você desejar alterar o idioma.

Passo 2. Definir os parâmetros do indicador.

O aplicativo já possui os parâmetros padrão, portanto, não há necessidade de configurar todos os indicadores. Mudar apenas os parâmetros necessários. Se necessário, você sempre pode alterar as configurações.

Passo 3. Definir a tabela de símbolos.

Por padrão, todos os símbolos do Observador de Mercado são filtrados pela presença de USD no nome. Você pode exibir todos os símbolos disponíveis, basta desmarcar a caixa de seleção.

Passos 4-5. Selecionar o intervalo de tempo e o período gráfico - essas etapas não foram alteradas.

Passo 6. Ativar os sinais de venda/compra e selecionar um padrão para teste.

O procedimento de configuração do sinal não mudou. No entanto, as alterações se aplicam à exibição visual: se um dos tipos de sinal estiver desativado, todas as configurações relevantes serão ocultas, como é mostrado na fig.3.

Fig.3 Desativando os sinais de compra ou venda.

Passo 7. As configurações de Take Profit e Stop Loss não foram alteradas.

Passo 8. Selecionar o tipo de lucro.

  • Ao selecionar Pontos, os resultados dos testes na seção Relatório serão mostrados em pontos do símbolo da moeda selecionada. 
  • Se a Moeda estiver selecionada, as configurações adicionais serão exibidas: Tipo de Lote, Tamanho do Lote e Depósito Inicial. O Tipo de Lote afeta o cálculo do lote que será aplicado no teste. Ele pode ser constante ou com base no Saldo.

Passo 9. Após os passos de 1 a 8, selecione o instrumento de teste clicando com o botão esquerdo na tabela. 

Após a conclusão do teste, os resultados aparecerão na seção Relatório. Depois disso, você pode clicar em Abrir gráfico.

Os testes seguindo o algoritmo acima são mostrados no próximo vídeo.



Recomendações para o teste dos padrões de Merrill:

  • Para permitir que o aplicativo funcione corretamente, nós precisamos dos dados históricos baixados para testar o símbolo de negociação selecionado.
  • Cuidado com os parâmetros personalizados do indicador: número do buffer, o nome ou os parâmetros do buffer devem ser separados por vírgulas. Observe que apenas os valores numéricos são suportados. O nome do indicador personalizado é o caminho relativo ao diretório raiz dos indicadores (MQL5/Indicators/). Se o indicador estiver localizado em um subdiretório, por exemplo, MQL5/Indicators/Examples, o nome deverá ter a seguinte aparência: "Examples\\nome_do_indicador" (sempre use uma barra invertida dupla em vez de uma única como separador).
  • Os cenários mais comuns que podem causar dificuldades são acompanhados de dicas. Eles incluem os mesmos padrões para ambos os sinais, tentam abrir um gráfico sem realizar os testes anteriores, datas de início e término do teste incorretos.
  • No começo do artigo, eu mencionei um link às descrições das características usadas no Relatório. Use o link para ler mais sobre as descrições dos parâmetros e os métodos de cálculo.

Conclusão

O arquivo anexado abaixo contém todos os arquivos descritos corretamente organizados em pastas. Para uma execução correta do aplicativo, coloque a pasta MQL5 para o diretório raiz do terminal. Para abrir o diretório raiz do terminal, onde a pasta MQL5 está localizada, pressione a combinação de teclas Ctrl+Shift+D na plataforma MetaTrader 5 ou use o menu de contexto como é exibido na Fig. 5 abaixo.


Fig. 5. Abrindo a pasta MQL5 no diretório raiz da plataforma MetaTrader 5


Traduzido do russo pela MetaQuotes Software Corp.
Artigo original: https://www.mql5.com/ru/articles/7361

Arquivos anexados |
MQL5.zip (1936.58 KB)
Otimização Walk Forward Contínua (Parte 1): Trabalhando com os Relatórios de Otimização Otimização Walk Forward Contínua (Parte 1): Trabalhando com os Relatórios de Otimização

O primeiro artigo é dedicado à criação de um kit de ferramentas para trabalhar com os relatórios de otimização, importá-los da plataforma e para filtrar e classificar os dados obtidos. A MetaTrader 5 permite baixar os resultados da otimização, no entanto, nosso objetivo é adicionar nossos próprios dados ao relatório de otimização.

Criando um EA gradador multiplataforma (conclusão): diversificação como forma de aumentar a lucratividade Criando um EA gradador multiplataforma (conclusão): diversificação como forma de aumentar a lucratividade

Nos artigos anteriores desta série, tentamos de várias maneiras criar um EA gradador mais ou menos rentável. Agora é a vez de tentarmos aumentar a lucratividade do EA por meio da diversificação. Nosso objetivo é obter o desejado lucro de 100% ao ano, com um rebaixamento máximo de saldo de 20%.

Biblioteca para criação simples e rápida de programas para MetaTrader (Parte XXII): classes de negociação - classe básica de negociação, controle de restrições Biblioteca para criação simples e rápida de programas para MetaTrader (Parte XXII): classes de negociação - classe básica de negociação, controle de restrições

No artigo, começaremos a criar uma classe básica de negociação da biblioteca e dotaremos a primeira versão com uma funcionalidade de verificação de permissões inicial para realizar operações de negociação. Também expandiremos levemente os recursos e o conteúdo da classe básica de negociação.

Explorando os Padrões Sazonais de Séries Temporais Financeiras com o Boxplot Explorando os Padrões Sazonais de Séries Temporais Financeiras com o Boxplot

Neste artigo, nós visualizaremos características sazonais de séries temporais financeiras usando diagramas Boxplot. Cada boxplot separado (ou diagrama de caixa) fornece uma boa visualização de como os valores são distribuídos ao longo do conjunto de dados. Os boxplots não devem ser confundidos com os gráficos de velas, embora possam ser visualmente semelhantes.