![Guia prático do MQL5: Consultor Especialista multi-moeda - Abordagem simples, organizada e rápida"](https://c.mql5.com/i/articles/overlay.png)
Guia prático do MQL5: Consultor Especialista multi-moeda - Abordagem simples, organizada e rápida"
Introdução
Este artigo descreverá uma implementação de uma abordagem simples, adequada para um Consultor Especialista multi-moeda. Isso significa que você será capaz de montar o Consultor Especialista para testes/negócios sob condições idênticas, mas com parâmetros diferentes para cada símbolo. Como um exemplo, criaremos um padrão para dois símbolos, mas de forma a ser capaz de somar símbolos adicionais, se necessário, fazendo pequenas alterações ao código.
Um padrão multi-moeda pode ser implementado no MQL5 de várias formas:
Podemos utilizar um padrão onde o Consultor Especialista seja guiado pelo tempo, sendo capaz de realizar verificações mais precisas nos intervalos de tempo especificados na função OnTimer().
De forma alternativa, como em todos os Consultores Especialistas apresentados nos artigos anteriores da série, a verificação pode ser feita na função OnTick() na qual o Consultor Especialista dependerá de pontos para o símbolo atual que estiver operando. Então, se há uma barra completa em outro símbolo, enquanto ainda não há ponto para o símbolo atual, o Consultor Especialista somente realizará uma verificação assim que houver um novo ponto para o símbolo atual.
Ainda há outra opção interessante sugerida por seu autor Konstantin Gruzdev (Lizar). Ela emprega um modelo de evento: utilizando a função OnChartEvent(), um Consultor Especialista obtêm eventos que são reproduzidos por agentes do indicador localizados nos gráficos de símbolos envolvidos nos testes/negócios. Agentes indicadores podem reproduzir uma nova barra e eventos de ponto dos símbolos aos quais estão anexados. Esse tipo de indicador (EventsSpy.mq5) pode ser obtido no fim do artigo. Precisaremos dele para a operação do Consultor Especialista.
Desenvolvimento do Consultor Especialista
O Consultor Especialista apresentou o artigo "Guia rápido do MQL5: Usando indicadores para definir condições de negócios em Consultores Especialistas" servirá como um modelo. Eu já excluí dele tudo o que tinha relação com o painel de informações e também simplifiquei a condição de abertura da posição conforme implementado no artigo anterior intitulado "Guia rápido do MQL5: Desenvolvendo uma estrutura para um sistema de negócios baseado na estratégia de tela tripla". Já que pretendemos criar um Consultor Especialista para dois símbolos, cada um deles precisará de seu próprio conjunto de parâmetros externos:
//--- External parameters of the Expert Advisor sinput long MagicNumber = 777; // Magic number sinput int Deviation = 10; // Slippage //--- sinput string delimeter_00=""; // -------------------------------- sinput string Symbol_01 = "EURUSD"; // Symbol 1 input int IndicatorPeriod_01 = 5; // | Indicator period input double TakeProfit_01 = 100; // | Take Profit input double StopLoss_01 = 50; // | Stop Loss input double TrailingStop_01 = 10; // | Trailing Stop input bool Reverse_01 = true; // | Position reversal input double Lot_01 = 0.1; // | Lot input double VolumeIncrease_01 = 0.1; // | Position volume increase input double VolumeIncreaseStep_01 = 10; // | Volume increase step //--- sinput string delimeter_01=""; // -------------------------------- sinput string Symbol_02 = "NZDUSD"; // Symbol 2 input int IndicatorPeriod_02 = 5; // | Indicator period input double TakeProfit_02 = 100; // | Take Profit input double StopLoss_02 = 50; // | Stop Loss input double TrailingStop_02 = 10; // | Trailing Stop input bool Reverse_02 = true; // | Position reversal input double Lot_02 = 0.1; // | Lot input double VolumeIncrease_02 = 0.1; // | Position volume increase input double VolumeIncreaseStep_02 = 10; // | Volume increase step
Os parâmetros externos serão colocados em arranjos cujos tamanhos dependerão do número de símbolos utilizado. O número de símbolos utilizado no Consultor Especialista será determinado pelo valor da constante NUMBER_OF_SYMBOLS que precisamos criar no início do arquivo:
//--- Number of traded symbols #define NUMBER_OF_SYMBOLS 2 //--- Name of the Expert Advisor #define EXPERT_NAME MQL5InfoString(MQL5_PROGRAM_NAME)
Vamos criar os arranjos que serão necessários para armazenar os parâmetros externos:
//--- Arrays for storing external parameters string Symbols[NUMBER_OF_SYMBOLS]; // Symbol int IndicatorPeriod[NUMBER_OF_SYMBOLS]; // Indicator period double TakeProfit[NUMBER_OF_SYMBOLS]; // Take Profit double StopLoss[NUMBER_OF_SYMBOLS]; // Stop Loss double TrailingStop[NUMBER_OF_SYMBOLS]; // Trailing Stop bool Reverse[NUMBER_OF_SYMBOLS]; // Position reversal double Lot[NUMBER_OF_SYMBOLS]; // Lot double VolumeIncrease[NUMBER_OF_SYMBOLS]; // Position volume increase double VolumeIncreaseStep[NUMBER_OF_SYMBOLS]; // Volume increase step
As funções de inicialização do arranjo serão colocadas no arquivo InitArrays.mqh incluso. Para inicializar o arranjo Symbols[], criaremos a função GetSymbol(). Ela obterá o nome do símbolo dos parâmetros externos e se o símbolo estiver disponível na lista de símbolos no servidor, ele será escolhido na janela Market Watch (Observação de mercado). Além disso, se o símbolo necessário não puder ser encontrado no servidor, a função retornará com uma cadeia vazia e o Diário do Consultor Especialista será atualizado de acordo.
Abaixo está o código da função GetSymbol():
//+------------------------------------------------------------------+ //| Adding the specified symbol to the Market Watch window | //+------------------------------------------------------------------+ string GetSymbolByName(string symbol) { string symbol_name=""; // Symbol name on the server //--- If an empty string is passed, return the empty string if(symbol=="") return(""); //--- Iterate over the list of all symbols on the server for(int s=0; s<SymbolsTotal(false); s++) { //--- Get the symbol name symbol_name=SymbolName(s,false); //--- If the required symbol is available on the server if(symbol==symbol_name) { //--- Select it in the Market Watch window SymbolSelect(symbol,true); //--- Return the symbol name return(symbol); } } //--- If the required symbol cannot be found, return the empty string Print("The "+symbol+" symbol could not be found on the server!"); return(""); }
O arranjo Symbols[] será inicializado da função GetSymbols():
//+------------------------------------------------------------------+ //| Filling the array of symbols | //+------------------------------------------------------------------+ void GetSymbols() { Symbols[0]=GetSymbolByName(Symbol_01); Symbols[1]=GetSymbolByName(Symbol_02); }
Além disso, o implementaremos de tal forma que um valor vazio nos parâmetros externos de um certo símbolo indicará que o bloco correspondente não será envolvido nos testes/negócios. Isso é necessário a fim de ser capaz de otimizar parâmetros para cada símbolo separadamente, enquanto exclui completamente o resto.
Todos os outros arranjos de parâmetros externos são inicializados da mesma forma. Em outras palavras, precisamos criar uma função diferente para cada arranjo. Os códigos de todas essas funções são fornecidos abaixo:
//+------------------------------------------------------------------+ //| Filling the indicator period array | //+------------------------------------------------------------------+ void GetIndicatorPeriod() { IndicatorPeriod[0]=IndicatorPeriod_01; IndicatorPeriod[1]=IndicatorPeriod_02; } //+------------------------------------------------------------------+ //| Filling the Take Profit array | //+------------------------------------------------------------------+ void GetTakeProfit() { TakeProfit[0]=TakeProfit_01; TakeProfit[1]=TakeProfit_02; } //+------------------------------------------------------------------+ //| Filling the Stop Loss array | //+------------------------------------------------------------------+ void GetStopLoss() { StopLoss[0]=StopLoss_01; StopLoss[1]=StopLoss_02; } //+------------------------------------------------------------------+ //| Filling the Trailing Stop array | //+------------------------------------------------------------------+ void GetTrailingStop() { TrailingStop[0]=TrailingStop_01; TrailingStop[1]=TrailingStop_02; } //+------------------------------------------------------------------+ //| Filling the Reverse array | //+------------------------------------------------------------------+ void GetReverse() { Reverse[0]=Reverse_01; Reverse[1]=Reverse_02; } //+------------------------------------------------------------------+ //| Filling the Lot array | //+------------------------------------------------------------------+ void GetLot() { Lot[0]=Lot_01; Lot[1]=Lot_02; } //+------------------------------------------------------------------+ //| Filling the VolumeIncrease array | //+------------------------------------------------------------------+ void GetVolumeIncrease() { VolumeIncrease[0]=VolumeIncrease_01; VolumeIncrease[1]=VolumeIncrease_02; } //+------------------------------------------------------------------+ //| Filling the VolumeIncreaseStep array | //+------------------------------------------------------------------+ void GetVolumeIncreaseStep() { VolumeIncreaseStep[0]=VolumeIncreaseStep_01; VolumeIncreaseStep[1]=VolumeIncreaseStep_02; }
Vamos criar agora uma função que nos ajudará a inicializar de forma prática todos os arranjos de parâmetros externos de uma vez - a função InitializeInputParameters():
//+------------------------------------------------------------------+ //| Initializing external parameter arrays | //+------------------------------------------------------------------+ void InitializeInputParameters() { GetSymbols(); GetIndicatorPeriod(); GetTakeProfit(); GetStopLoss(); GetTrailingStop(); GetReverse(); GetLot(); GetVolumeIncrease(); GetVolumeIncreaseStep(); }
Seguindo a inicialização dos arranjos de parâmetros externos, podemos seguir para a parte principal. Alguns procedimentos como a obtenção de identificadores de indicador, seus valores e informações de preço, assim como verificar quanto a uma nova barra, etc. serão realizados em ciclos consecutivamente para cada símbolo. Esse é o motivo pelo qual valores de parâmetros externos serem organizados em arranjos. Então, tudo será feito nos ciclos como a seguir:
//--- Iterate over all symbols for(int s=0; s<NUMBER_OF_SYMBOLS; s++) { //--- If trading for this symbol is allowed if(Symbols[s]!="") { //--- The rest of the code } }
Mas antes de começarmos a modificar as funções existentes e criarmos novas, vamos também criar arranjos que serão necessários neste padrão.
Precisaremos de dois arranjos para os identificadores de indicador:
//--- Array of indicator agent handles int spy_indicator_handles[NUMBER_OF_SYMBOLS]; //--- Array of signal indicator handles int signal_indicator_handles[NUMBER_OF_SYMBOLS];
Esses dois arranjos serão primeiramente inicializados em valores inválidos:
//+------------------------------------------------------------------+ //| Initializing arrays of indicator handles | //+------------------------------------------------------------------+ void InitializeArrayHandles() { ArrayInitialize(spy_indicator_handles,INVALID_HANDLE); ArrayInitialize(signal_indicator_handles,INVALID_HANDLE); }
Arranjos de dados de preço e valores de indicador serão agora acessados usando estruturas:
//--- Data arrays for checking trading conditions struct PriceData { double value[]; }; PriceData open[NUMBER_OF_SYMBOLS]; // Opening price of the bar PriceData high[NUMBER_OF_SYMBOLS]; // High price of the bar PriceData low[NUMBER_OF_SYMBOLS]; // Low price of the bar PriceData close[NUMBER_OF_SYMBOLS]; // Closing price of the bar PriceData indicator[NUMBER_OF_SYMBOLS]; // Array of indicator values
Agora, se você precisa obter o valor do indicador na última barra completa do primeiro símbolo na lista, você deve escrever algo como isso:
double indicator_value=indicator[0].value[1];
Também precisamos criar arranjos ao invés das variáveis que foram anteriormente utilizadas na função CheckNewBar():
//--- Arrays for getting the opening time of the current bar struct Datetime { datetime time[]; }; Datetime lastbar_time[NUMBER_OF_SYMBOLS]; //--- Array for checking the new bar for each symbol datetime new_bar[NUMBER_OF_SYMBOLS];
Então organizamos os arranjos. Agora precisamos modificar várias funções de acordo com as mudanças feitas acima. Vamos começar com a função GetIndicatorHandles():
//+------------------------------------------------------------------+ //| Getting indicator handles | //+------------------------------------------------------------------+ void GetIndicatorHandles() { //--- Iterate over all symbols for(int s=0; s<NUMBER_OF_SYMBOLS; s++) { //--- If trading for this symbol is allowed if(Symbols[s]!="") { //--- If the handle is yet to be obtained if(signal_indicator_handles[s]==INVALID_HANDLE) { //--- Get the indicator handle signal_indicator_handles[s]=iMA(Symbols[s],_Period,IndicatorPeriod[s],0,MODE_SMA,PRICE_CLOSE); //--- If the indicator handle could not be obtained if(signal_indicator_handles[s]==INVALID_HANDLE) Print("Failed to get the indicator handle for the symbol "+Symbols[s]+"!"); } } } }
Agora, independente do número de símbolos utilizados nos testes/negócios, o código da função permanecerá o mesmo.
Similarmente, criaremos outra função, GetSpyHandles(), para obter identificadores de agentes do indicador que transmitirão pontos para outros símbolos. Mas, antes disso, adicionaremos mais uma enumeração de todos os eventos por símbolo, ENUM_CHART_EVENT_SYMBOL, organizados como sinalizadores no arquivo Enums.mqh:
//+------------------------------------------------------------------+ //| New bar and tick events from all symbols and time frames | //+------------------------------------------------------------------+ enum ENUM_CHART_EVENT_SYMBOL { CHARTEVENT_NO = 0, // Events are disabled - 0 CHARTEVENT_INIT = 0, // Initialization event - 0 //--- CHARTEVENT_NEWBAR_M1 = 0x00000001, // New bar event on a minute chart (1) CHARTEVENT_NEWBAR_M2 = 0x00000002, // New bar event on a 2-minute chart (2) CHARTEVENT_NEWBAR_M3 = 0x00000004, // New bar event on a 3-minute chart (4) CHARTEVENT_NEWBAR_M4 = 0x00000008, // New bar event on a 4-minute chart (8) //--- CHARTEVENT_NEWBAR_M5 = 0x00000010, // New bar event on a 5-minute chart (16) CHARTEVENT_NEWBAR_M6 = 0x00000020, // New bar event on a 6-minute chart (32) CHARTEVENT_NEWBAR_M10 = 0x00000040, // New bar event on a 10-minute chart (64) CHARTEVENT_NEWBAR_M12 = 0x00000080, // New bar event on a 12-minute chart (128) //--- CHARTEVENT_NEWBAR_M15 = 0x00000100, // New bar event on a 15-minute chart (256) CHARTEVENT_NEWBAR_M20 = 0x00000200, // New bar event on a 20-minute chart (512) CHARTEVENT_NEWBAR_M30 = 0x00000400, // New bar event on a 30-minute chart (1024) CHARTEVENT_NEWBAR_H1 = 0x00000800, // New bar event on an hour chart (2048) //--- CHARTEVENT_NEWBAR_H2 = 0x00001000, // New bar event on a 2-hour chart (4096) CHARTEVENT_NEWBAR_H3 = 0x00002000, // New bar event on a 3-hour chart (8192) CHARTEVENT_NEWBAR_H4 = 0x00004000, // New bar event on a 4-hour chart (16384) CHARTEVENT_NEWBAR_H6 = 0x00008000, // New bar event on a 6-hour chart (32768) //--- CHARTEVENT_NEWBAR_H8 = 0x00010000, // New bar event on a 8-hour chart (65536) CHARTEVENT_NEWBAR_H12 = 0x00020000, // New bar event on a 12-hour chart (131072) CHARTEVENT_NEWBAR_D1 = 0x00040000, // New bar event on a daily chart (262144) CHARTEVENT_NEWBAR_W1 = 0x00080000, // New bar event on a weekly chart (524288) //--- CHARTEVENT_NEWBAR_MN1 = 0x00100000, // New bar event on a monthly chart (1048576) CHARTEVENT_TICK = 0x00200000, // New tick event (2097152) //--- CHARTEVENT_ALL = 0xFFFFFFFF // All events are enabled (-1) };
Essa enumeração é necessária para operar com o indicador personalizado EventsSpy.mq5 (o arquivo está anexo nesse artigo) na função GetSpyHandles() cujo código está fornecido abaixo:
//+------------------------------------------------------------------+ //| Getting agent handles by the specified symbols | //+------------------------------------------------------------------+ void GetSpyHandles() { //--- Iterate over all symbols for(int s=0; s<NUMBER_OF_SYMBOLS; s++) { //--- If trading for this symbol is allowed if(Symbols[s]!="") { //--- If the handle is yet to be obtained if(spy_indicator_handles[s]==INVALID_HANDLE) { //--- Get the indicator handle spy_indicator_handles[s]=iCustom(Symbols[s],_Period,"EventsSpy.ex5",ChartID(),0,CHARTEVENT_TICK); //--- If the indicator handle could not be obtained if(spy_indicator_handles[s]==INVALID_HANDLE) Print("Failed to install the agent on "+Symbols[s]+""); } } } }
Por favor, note o último parâmetro na função iCustom(): nesse caso, o identificador CHARTEVENT_TICK foi utilizado para obter eventos de ponto. Mas, se for necessário, ela pode ser modificada para obter os novos eventos de barra. Por exemplo, se você utilizar a linha conforme mostrado abaixo, o Consultor Especialista obterá novos eventos de barra no intervalo de tempo de um minuto (M1) e uma hora (H1):
handle_event_indicator[s]=iCustom(Symbols[s],_Period,"EventsSpy.ex5",ChartID(),0,CHARTEVENT_NEWBAR_M1|CHARTEVENT_NEWBAR_H1);
Para obter todos os eventos (eventos de ponto e barra em todos os intervalos de tempo), você precisa especificar o identificador CHARTEVENT_ALL.
Todos os arranjos são inicializados na função OnInit():
//+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ void OnInit() { //--- Initialization of arrays of external parameters InitializeInputParameters(); //--- Initialization of arrays of indicator handles InitializeArrayHandles(); //--- Get agent handles GetSpyHandles(); //--- Get indicator handles GetIndicatorHandles(); //--- Initialize the new bar InitializeArrayNewBar(); }
Conforme já mencionado no início do artigo, os eventos dos agentes do indicador são recebidos na função OnChartEvent(). Abaixo está o código que será utilizado nessa função:
//+------------------------------------------------------------------+ //| Chart events handler | //+------------------------------------------------------------------+ void OnChartEvent(const int id, // Event identifier const long &lparam, // Long type event parameter const double &dparam, // Double type event parameter const string &sparam) // String type event parameter { //--- If this is a custom event if(id>=CHARTEVENT_CUSTOM) { //--- Exit if trading is not allowed if(CheckTradingPermission()>0) return; //--- If there was a tick event if(lparam==CHARTEVENT_TICK) { //--- Check signals and trade on them CheckSignalsAndTrade(); return; } } }
Na função CheckSignalAndTrade() (a linha destacada no código acima), teremos um ciclo onde todos os símbolos serão verificados alternativamente quanto ao novo evento da barra e os sinais de negócios conforme implementado anteriormente na função OnTick():
//+------------------------------------------------------------------+ //| Checking signals and trading based on the new bar event | //+------------------------------------------------------------------+ void CheckSignalsAndTrade() { //--- Iterate over all specified symbols for(int s=0; s<NUMBER_OF_SYMBOLS; s++) { //--- If trading for this symbol is allowed if(Symbols[s]!="") { //--- If the bar is not new, proceed to the next symbol if(!CheckNewBar(s)) continue; //--- If there is a new bar else { //--- Get indicator data. If there is no data, proceed to the next symbol if(!GetIndicatorsData(s)) continue; //--- Get bar data GetBarsData(s); //--- Check the conditions and trade TradingBlock(s); //--- Trailing Stop ModifyTrailingStop(s); } } } }
Todas as funções que utilizaram parâmetros externos, assim como dados de indicador e símbolo, precisam ser modificadas em concordância com todas as alterações acima. Para essa finalidade, devemos acrescentar o número do símbolo como o primeiro parâmetro e substituir todas as variáveis e arranjos dentro da função com os novos arranjos descritos acima.
Para ilustrar, os códigos revisados das funções CheckNewBar(), TradingBlock() e OpenPosition() são fornecidos abaixo.
O código da função CheckNewBar():
//+------------------------------------------------------------------+ //| Checking for the new bar | //+------------------------------------------------------------------+ bool CheckNewBar(int number_symbol) { //--- Get the opening time of the current bar // If an error occurred when getting the time, print the relevant message if(CopyTime(Symbols[number_symbol],Period(),0,1,lastbar_time[number_symbol].time)==-1) Print(__FUNCTION__,": Error copying the opening time of the bar: "+IntegerToString(GetLastError())); //--- If this is a first function call if(new_bar[number_symbol]==NULL) { //--- Set the time new_bar[number_symbol]=lastbar_time[number_symbol].time[0]; Print(__FUNCTION__,": Initialization ["+Symbols[number_symbol]+"][TF: "+TimeframeToString(Period())+"][" +TimeToString(lastbar_time[number_symbol].time[0],TIME_DATE|TIME_MINUTES|TIME_SECONDS)+"]"); return(false); } //--- If the time is different if(new_bar[number_symbol]!=lastbar_time[number_symbol].time[0]) { //--- Set the time and exit new_bar[number_symbol]=lastbar_time[number_symbol].time[0]; return(true); } //--- If we have reached this line, then the bar is not new, so return false return(false); }
O código da função TradingBlock():
//+------------------------------------------------------------------+ //| Trading block | //+------------------------------------------------------------------+ void TradingBlock(int symbol_number) { ENUM_ORDER_TYPE signal=WRONG_VALUE; // Variable for getting a signal string comment="hello :)"; // Position comment double tp=0.0; // Take Profit double sl=0.0; // Stop Loss double lot=0.0; // Volume for position calculation in case of position reversal double position_open_price=0.0; // Position opening price ENUM_ORDER_TYPE order_type=WRONG_VALUE; // Order type for opening a position ENUM_POSITION_TYPE opposite_position_type=WRONG_VALUE; // Opposite position type //--- Find out if there is a position pos.exists=PositionSelect(Symbols[symbol_number]); //--- Get the signal signal=GetTradingSignal(symbol_number); //--- If there is no signal, exit if(signal==WRONG_VALUE) return; //--- Get symbol properties GetSymbolProperties(symbol_number,S_ALL); //--- Determine values for trade variables switch(signal) { //--- Assign values to variables for a BUY case ORDER_TYPE_BUY : position_open_price=symb.ask; order_type=ORDER_TYPE_BUY; opposite_position_type=POSITION_TYPE_SELL; break; //--- Assign values to variables for a SELL case ORDER_TYPE_SELL : position_open_price=symb.bid; order_type=ORDER_TYPE_SELL; opposite_position_type=POSITION_TYPE_BUY; break; } //--- Get the Take Profit and Stop Loss levels sl=CalculateStopLoss(symbol_number,order_type); tp=CalculateTakeProfit(symbol_number,order_type); //--- If there is no position if(!pos.exists) { //--- Adjust the volume lot=CalculateLot(symbol_number,Lot[symbol_number]); //--- Open a position OpenPosition(symbol_number,lot,order_type,position_open_price,sl,tp,comment); } //--- If the position exists else { //--- Get the position type GetPositionProperties(symbol_number,P_TYPE); //--- If the position is opposite to the signal and the position reversal is enabled if(pos.type==opposite_position_type && Reverse[symbol_number]) { //--- Get the position volume GetPositionProperties(symbol_number,P_VOLUME); //--- Adjust the volume lot=pos.volume+CalculateLot(symbol_number,Lot[symbol_number]); //--- Reverse the position OpenPosition(symbol_number,lot,order_type,position_open_price,sl,tp,comment); return; } //--- If the signal is in the direction of the position and the volume increase is enabled, increase the position volume if(!(pos.type==opposite_position_type) && VolumeIncrease[symbol_number]>0) { //--- Get the Stop Loss of the current position GetPositionProperties(symbol_number,P_SL); //--- Get the Take Profit of the current position GetPositionProperties(symbol_number,P_TP); //--- Adjust the volume lot=CalculateLot(symbol_number,VolumeIncrease[symbol_number]); //--- Increase the position volume OpenPosition(symbol_number,lot,order_type,position_open_price,pos.sl,pos.tp,comment); return; } } }
O código da função OpenPosition():
//+------------------------------------------------------------------+ //| Opening a position | //+------------------------------------------------------------------+ void OpenPosition(int symbol_number, double lot, ENUM_ORDER_TYPE order_type, double price, double sl, double tp, string comment) { //--- Set the magic number in the trading structure trade.SetExpertMagicNumber(MagicNumber); //--- Set the slippage in points trade.SetDeviationInPoints(CorrectValueBySymbolDigits(Deviation)); //--- Instant Execution and Market Execution mode // *** Starting with build 803, Stop Loss and Take Profit *** // *** can be set upon opening a position in the SYMBOL_TRADE_EXECUTION_MARKET mode *** if(symb.execution_mode==SYMBOL_TRADE_EXECUTION_INSTANT || symb.execution_mode==SYMBOL_TRADE_EXECUTION_MARKET) { //--- If the position failed to open, print the relevant message if(!trade.PositionOpen(Symbols[symbol_number],order_type,lot,price,sl,tp,comment)) Print("Error opening the position: ",GetLastError()," - ",ErrorDescription(GetLastError())); } }
Então, cada função agora recebe o número do símbolo (symbol_number). Por favor, note também a alteração introduzida na compilação 803:
Os códigos revisados das outras funções podem ser encontrados nos arquivos anexos. Tudo o que precisamos fazer agora é otimizar os parâmetros e efetuar testes.
Otimizando parâmetros e testando o Consultor Especialista
Primeiro otimizaremos os parâmetros para o primeiro símbolo e depois para o segundo. Vamos começar com o EURUSD.
Abaixo estão as configurações do Testador de Estratégia:
Fig. 1. Configurações do Testador de Estratégia.
As configurações do Consultor Especialista precisam ser feitas conforme mostrados abaixo (para conveniência, os arquivos .set contendo configurações para cada símbolo estão anexados nesse artigo). Para excluir certo símbolo da otimização, você deve simplesmente deixar o campo do parâmetro do nome do símbolo vazio. A otimização de parâmetros realizada para cada símbolo separadamente também acelerará o processo de otimização.
Fig. 2. Configurações do Consultor Especialista para otimização de parâmetro: EURUSD.
A otimização levará cerca de uma hora em um processador dual-core. Os resultados do teste do fator de recuperação máximo são conforme mostrado abaixo:
Fig. 3. Resultados do teste do fator de recuperação máximo para EURUSD.
Agora defina o NZDUSD como o segundo símbolo. Para a otimização, deixe a linha com o nome do símbolo para o primeiro bloco de parâmetro vazio.
Os resultados para NZDUSD parecem ter sido conforme a seguir:
Fig. 4. Resultados do teste para o fator de recuperação máximo para NZDUSD.
Agora podemos testar dois símbolos juntos. Nas configurações do Testador de Estratégia, você pode ajustar qualquer símbolo no qual o Consultor Especialista seja inicializado já que os resultados serão idênticos. Pode até mesmo ser um símbolo que não esteja envolvido nos negócios/testes.
Abaixo estão os resultados para dois símbolos testados juntos.
Fig. 5. Resultados do teste para dois símbolos: EURUSD e NZDUSD.
Conclusão
Isso é tudo. Os códigos-fonte estão anexos abaixo e podem ser baixados para um estudo mais detalhado do acima mencionado. Para praticar, tente selecionar um ou mais símbolos ou altere as condições de abertura de posição utilizando outros indicadores.
Após extrair os arquivos do arquivo, coloque a pasta MultiSymbolExpert dentro do diretório MetaTrader 5\MQL5\Experts. Além disso, o indicador EventsSpy.mq5 deve ser colocado dentro do diretório MetaTrader 5\MQL5\Indicators.
Traduzido do russo pela MetaQuotes Ltd.
Artigo original: https://www.mql5.com/ru/articles/648
![Guia prático do MQL5: Desenvolvendo um Consultor Especialista multi-moeda com um número ilimitado de parâmetros](https://c.mql5.com/2/0/avatar__5.png)
![Guia prático do MQL5: Desenvolvendo uma estrutura para um sistema de negócios baseado na estratégia de tela tripla](https://c.mql5.com/2/0/avatar__2.png)
![Guia prático do MQL5: Registrando o histórico de negociações em um arquivo e criando gráficos de saldo para cada símbolo no Excel](https://c.mql5.com/2/0/avatar11.png)
![O indicador ZigZag: nova abordagem e novas soluções](https://c.mql5.com/2/0/avatar2.png)
![MQL5 - Linguagem para estratégias de negociação inseridas no terminal do cliente MetaTrader 5](https://c.mql5.com/i/registerlandings/logo-2.png)
- 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