Ampliando as funcionalidades do Construtor de Estratégia
Índice
- Introdução
- Visão geral das Adições
- Estágios da implementação dos novos recursos
- Teste e Demonstração do Construtor de Estratégia
- Conclusão
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:
- 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.
- í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 Ltd.
Artigo original: https://www.mql5.com/ru/articles/7361
- Aplicativos de negociação gratuitos
- 8 000+ sinais para cópia
- Notícias econômicas para análise dos mercados financeiros
Você concorda com a política do site e com os termos de uso