Criando um Expert Advisor multissistema e multimoeda
Introdução
Eu acredito que existam pouquíssimos traders que operem mais que um símbolo e que usem várias estratégias. Essa abordagem não só permite que você, potencialmente, aumente o seu lucro, mas também minimiza o risco de perdas significativas. Ao criar um Expert Advisor, o primeiro passo natural para a verificação da eficiência da estratégia do programa é a otimização, a fim de determinar os melhores parâmetros de entrada.
Com os valores dos parâmetros identificados, os Expert Advisors estariam tecnicamente prontos para negociação. No entanto, isso deixaria uma importante questão sem resposta. Quais seriam os resultados dos testes se um trader pudesse colocar todas as suas estratégias juntas em um único Expert Advisor? A percepção de que os rebaixamentos em vários símbolos ou estratégias podem, em algum ponto, se sobrepor resultado em um rebaixamento geral terrível ou mesmo em uma chamada de margem pode, por vezes, chegar como uma surpresa desagradável.
Este artigo introduz um conceito de criação de Expert Advisor multissistema e multimoeda que nos permitirá encontrar uma resposta a esta importante questão.
1. Estrutura do Expert Advisor
Em termos gerais, a estrutura do Expert Advisor é como a seguir:
Fig. 1. Estrutura do Expert Advisor multissistema e multimoeda
Como você pode ver, o programa é baseado em um ciclo for. Cada estratégia é organizada em um ciclo onde cada iteração é responsável pela negociação de cada símbolo separadamente. Aqui, você pode organizar em ciclos um número ilimitado de estratégias. O importante para o seu computador é ter recursos suficientes para processar tal programa.
Você deve ter em mente que pode haver apenas uma posição para cada símbolo negociado no MetaTrader 5. Essa posição representa a soma de muitas compras e vendas executadas anteriormente. Por conseguinte, o resultado do teste multiestratégia para um símbolo não será idêntico à soma dos resultados de testes separados das mesmas estratégias para o mesmo símbolo.
Para uma análise mais aprofundada da estrutura do Expert Advisor tomaremos duas estratégias cada uma das quais negociará dois símbolos:
Estratégia A:
- Compra: preço Ask atingindo a banda inferior do indicador Bollinger Bands calculado com base no preço Low.
Fechamento: preço Bid atingindo a banda inferior do indicador Bollinger Bands calculado com base no preço High. - Venda: preço Bid atingindo a banda superior do indicador Bollinger Bands calculado com base no preço High.
Fechamento: preço Ask atingindo a banda superior do indicador Bollinger Bands calculado com base no preço Low. - Restrição: em cada barra apenas pode ser executado um negócio.
Estratégia B:
- Compra: barra anterior baixista (close < open) e preço Ask atingindo o High da barra anterior.
Fechamento: por Stop Loss ou Take Profit. - Venda: barra anterior altista (close > open) e preço Bid atingindo o Low da barra anterior.
Fechamento: por Stop Loss ou Take Profit. - Restrição: em cada barra apenas pode ser executado um negócio.
A fim de não depender dos novos ticks do símbolo em que será testado ou que operará o Expert Advisor, é aconselhável usar a função OnTimer() para negociação no modo multimoeda.
Para fazer isso, ao inicializar o Expert Advisor, especificamos - com a ajuda da função EventSetTimer() - a frequência com que será gerado o evento para chamar o cálculo do programa, enquanto, se a função EventKillTimer() for desinicializada, indicamos ao terminal que suspenda a geração de eventos:
// Conectamos as bibliotecas padrão // Criamos parâmetros externos // Criamos matrizes, variáveis, indicadores de indicador, etc. //--- Inicializando o Expert Advisor int OnInit() { //--- Definimos a periodicidade da geração do evento EventSetTimer(1); // 1 segundo // ... return(0); } void OnTimer() { // ... } //--- Desinicializando o Expert Advisor void OnDeinit(const int reason) { //--- Parando a geração de eventos EventKillTimer(); // ... }
Em vez de EventSetTimer(), você também pode usar EventSetMillisecondTimer(), a fim de definir a frequência em milissegundos, mas não abuse muito dela chamando o cálculo do programa frequentemente.
Para acessar as configurações da conta, das posições, dos símbolos, bem como das funções de negociação, usaremos as classes CAccountInfo, CPositionInfo, CSymbolInfo e CTrade, respectivamente. Vamos incluí-las no Expert Advisor:
//--- Conectamos as bibliotecas padrão #include <Trade\AccountInfo.mqh> #include <Trade\PositionInfo.mqh> #include <Trade\SymbolInfo.mqh> #include <Trade\Trade.mqh>
Como o Expert Advisor é baseado em ciclos for, precisaremos criar matrizes para seus parâmetros externos. Vamos primeiro criar constantes iguais ao número de símbolos para cada estratégia:
//--- Número de símbolos negociados para cada estratégia #define Strategy_A 2 #define Strategy_B 2
Nós, então, criamos parâmetros externos. Usando constantes, determinamos o tamanho das matrizes para as quais serão copiados. Além disso, criamos identificadores de indicadores e outras variáveis globais.
Um exemplo de um símbolo de estratégia А é fornecido abaixo:
//------------------- Parâmetros externos da estratégia A input string Data_for_Strategy_A="Strategy A -----------------------"; //--- Símbolo 0 input string Symbol_A0 = "EURUSD"; // Símbolo input bool IsTrade_A0 = true; // Permissão para negociar //--- Parâmetros do Bollinger Bands (BB) input ENUM_TIMEFRAMES Period_A0 = PERIOD_H1; // Período do ВВ input uint BBPeriod_A0 = 20; // Período de cálculo da linha média do BB input int BBShift_A0 = 0; // Deslocamento horizontal do ВВ input double BBDeviation_A0 = 2.0; // Número de desvios padrão do BB //... //--- Parâmetros gerais da estratégia А input double DealOfFreeMargin_A = 1.0; // Porcentagem de fundos disponíveis para o negócio input uint MagicNumber_A = 555; // Número mágico input uint Slippage_A = 100; // Slippage admissível para o negócio //... //------------- Definimos as variáveis da estratégia A ----- //--- Matrizes para parâmetros externos string Symbol_A[Strategy_A]; bool IsTrade_A[Strategy_A]; ENUM_TIMEFRAMES Period_A[Strategy_A]; int BBPeriod_A[Strategy_A]; int BBShift_A[Strategy_A]; double BBDeviation_A[Strategy_A]; //--- Matrizes para variáveis globais double MinLot_A[Strategy_A],MaxLot_A[Strategy_A]; double Point_A[Strategy_A],ContractSize_A[Strategy_A]; uint DealNumber_A[Strategy_A]; datetime Locked_bar_time_A[Strategy_A],time_arr_A[]; //--- Identificadores de indicador int BB_handle_high_A[Strategy_A]; int BB_handle_low_A[Strategy_A]; //--- Matrizes para os valores dos indicadores double BB_upper_band_high[],BB_lower_band_high[]; double BB_upper_band_low[],BB_lower_band_low[]; //--- Classe CTrade Trade_A; //... //--- Definimos variáveis globais para todas as estratégias long Leverage; //--- Classes CAccountInfo AccountInfo; CPositionInfo PositionInfo; CSymbolInfo SymbolInfo;
A fim de ter a possibilidade de desativar negociação para um determinado símbolo, criamos uma variável booliana IsTrade_A0 que será colocada bem no início dos ciclos for.
2. Inicialização do Expert Advisor
Primeiro, vamos obter os valores necessários para todas as estratégias, por exemplo, o tamanho de alavancagem. Uma vez que a alavancagem é aplicada à conta de negociação e não depende de uma estratégia ou de um símbolo, não há necessidade de copiar o seu valor para as matrizes:
//--- Obtemos o tamanho da alavancagem da conta
Leverage=AccountInfo.Leverage();
Copiamos as variáveis externas para matrizes.
//--- Copiamos os parâmetros externos para as matrizes Symbol_A[0] =Symbol_A0; IsTrade_A[0] =IsTrade_A0; Period_A[0] =Period_A0; BBPeriod_A[0] =(int)BBPeriod_A0; BBShift_A[0] =BBShift_A0; BBDeviation_A[0]=BBDeviation_A0;
Se o tipo a ser convertido para um outro definir um parâmetro externo, essa conversão poderá ser feita mais convenientemente durante a cópia para matrizes.
Nesse caso, podemos ver que BBPeriod_A0 foi criado como uint, para impedir que o usuário defina um valor negativo. Neste lugar, nós o convertemos em int e copiamos para matriz que também foi criada como int. Caso contrário, o compilador dará o aviso de que você está tentando inserir parâmetro do tipo uint no identificador de indicador.
Vamos ainda ver se o símbolo negociado está disponível na 'Observação do Mercado' e se ele foi usado mais de uma vez dentro de uma estratégia:
//--- Verificamos a presença de símbolo na 'Observação do mercado' for(int i=0; i<Strategy_A; i++) { if(IsTrade_A[i]==false) continue; if(IsSymbolInMarketWatch(Symbol_A[i])==false) { Print(Symbol_A[i]," não encontrado no servidor!"); ExpertRemove(); } } //--- Verificamos se o símbolo é usado mais de uma vez if(Strategy_A>1) { for(int i=0; i<Strategy_A-1; i++) { if(IsTrade_A[i]==false) continue; for(int j=i+1; j<Strategy_A; j++) { if(IsTrade_A[j]==false) continue; if(Symbol_A[i]==Symbol_A[j]) { Print(Symbol_A[i]," é usado mais de uma vez!"); ExpertRemove(); } } } } //--- Função IsSymbolInMarketWatch() bool IsSymbolInMarketWatch(string f_Symbol) { for(int s=0; s<SymbolsTotal(false); s++) { if(f_Symbol==SymbolName(s,false)) return(true); } return(false); }
Se os símbolos foram selecionados corretamente, verifique se há erros nos parâmetros de entrada para cada um deles, crie identificadores de indicadores, obtenha os dados necessários para o cálculo de lote, e, se necessário, faça outras coisas, conforme estabelecido pela estratégia fornecida.
Implementaremos as ações acima mencionadas dentro de um ciclo for.
//--- Ações gerais for(int i=0; i<Strategy_A; i++) { if(IsTrade_A[i]==false) continue; //--- Verificamos se há erros nos parâmetros de entrada //... //--- Definimos o identificador dos indicadores BB_handle_high_A[i]=iBands(Symbol_A[i],Period_A[i],BBPeriod_A[i],BBShift_A[i],BBDeviation_A[i], PRICE_HIGH); if(BB_handle_high_A[i]<0) { Print("Erro ao criar o identificador do Bollinger Bands baseado em preços High para ",Symbol_A[i],". Identificador=",INVALID_HANDLE, "\n Erro=",GetLastError()); ExpertRemove(); } //... //--- Calculamos os dados para o Lote //--- Definimos o nome do símbolo para o qual será recebida a informação SymbolInfo.Name(Symbol_A[i]); //--- tamanhos mínimo e máximo do volume nas operações de negociação MinLot_A[i]=SymbolInfo.LotsMin(); MaxLot_A[i]=SymbolInfo.LotsMax(); //--- tamanho do ponto Point_A[i]=SymbolInfo.Point(); //--- tamanho do contrato ContractSize_A[i]=SymbolInfo.ContractSize(); //--- Definimos alguns parâmetros adicionais }
Em seguida, definimos os parâmetros para operações de negociação da estratégia A usando o objeto Trade_A da classe CTrade.
//--- Definimos os parâmetros para operações de negociação //--- Definimos o número mágico Trade_A.SetExpertMagicNumber(MagicNumber_A); //--- Definimos a derrapagem admissível em pontos ao fazer um negócio Trade_A.SetDeviationInPoints(Slippage_A); //--- Modo de preenchimento da ordem, é preciso usar o modo que é permitido pelo servidor Trade_A.SetTypeFilling(ORDER_FILLING_RETURN); //--- Modo logging, é melhor não chamar esse método, a própria classe irá definir o modo ideal Trade_A.LogLevel(1); //--- Função a usar para negociação: true - OrderSendAsync(), false - OrderSend(). Trade_A.SetAsyncMode(true);
O mesmo procedimento é repetido para cada uma das estratégias, por exemplo.
- Copie variáveis externas para matrizes;
- Verifique se os símbolos são selecionados corretamente;
- Verifique os erros, defina identificadores de indicadores, calcule dados para o lote e para tudo o que for necessário para uma determinada estratégia;
- Defina parâmetros para as operações de negociação.
Finalmente, seria bom verificar se um e o mesmo símbolo é usado em várias estratégias (um exemplo para duas estratégias é fornecidos abaixo):
//--- Verificamos se o mesmo símbolo é usado em várias estratégias for(int i=0; i<Strategy_A; i++) { if(IsTrade_A[i]==false) continue; for(int j=0; j<Strategy_B; j++) { if(IsTrade_B[j]==false) continue; if(Symbol_A[i]==Symbol_B[j]) { Print(Symbol_A[i]," usado em várias estratégias!"); ExpertRemove(); } } }
3. Ciclos de negociação "for"
A estrutura dos ciclos for dentro da função OnTimer() é como a seguir:
void OnTimer() { //--- Verificamos se o terminal está conectado ao servidor de negociação if(TerminalInfoInteger(TERMINAL_CONNECTED)==false) return; //--- Seção A: Ciclo principal do operador FOR para a estratégia A ----------- for(int A=0; A<Strategy_A; A++) { //--- A.1: Verificamos se a negociação é permitida para o símbolo if(IsTrade_A[A]==false) continue; // suspender a iteração FOR atual } //--- Seção B: Ciclo principal do operador FOR para a estratégia B ----------- for(int B=0; B<Strategy_B; B++) { //--- B.1: Verificamos se a negociação é permitida para o símbolo if(IsTrade_B[B]==false) continue; // suspender a iteração FOR atual } }
Se um Expert Advisor 'monoestratégia' e 'monosímbolo' tiver uma condição em que todos os cálculos posteriores precisam ser interrompidos, usamos o operador return. No nosso caso, só precisamos terminar a iteração atual e prosseguir para a próxima iteração de símbolo. Por esse motivo, é melhor utilizar o operador continue.
Se você deseja melhorar seu Expert Advisor multiestratégia com a adição de uma estratégia com ciclo for contendo uma condição para interromper todos os cálculos posteriores, você pode usar o seguinte esquema:
//--- Seção N: Ciclo principal do operador FOR para a estratégia N ----------- for(int N=0; N<Strategy_N; N++) { //... bool IsInterrupt=false; for(int i=0; i<Number; i++) { if(...) // interromper todos os cálculos { IsInterrupt=true; break; } } if(IsInterrupt=true) continue; // interromper a iteração FOR atual //... }
Depois de criar a estrutura do ciclo for, simplesmente inserimos nele os códigos de outros EAs e, em seguida, substituímos algumas variáveis por elementos de matriz.
Por exemplo, alteramos a variável predefinida _Symbol para Symbol_A[i] ou _Point para Point_A[i]. Os valores dessas variáveis são típicos do símbolo fornecido e, portanto, foram copiados para matrizes na inicialização.
Por exemplo, vamos encontrar o valor do indicador:
//--- A.3: Banda inferior do BB calculada com base no preço High if(CopyBuffer(BB_handle_high_A[A],LOWER_BAND,BBShift_A[A],1,BB_lower_band_high)<=0) continue; // suspendemos a iteração FOR atual ArraySetAsSeries(BB_lower_band_high,true);
Para implementar fechamento de uma posição de compra, escreveremos o seguinte código:
//--- A.7.1: Calculamos os preços Ask e Bid atuais SymbolInfo.Name(Symbol_A[A]); SymbolInfo.RefreshRates(); double Ask_price=SymbolInfo.Ask(); double Bid_price=SymbolInfo.Bid(); if(PositionSelect(Symbol_A[A])) { //--- A.7.2: Fechamos a posição BUY if(PositionInfo.PositionType()==POSITION_TYPE_BUY) { if(Bid_price>=BB_lower_band_high[0] || DealNumber_A[A]==0) { if(!Trade_A.PositionClose(Symbol_A[A])) { Print("Erro ao fechar a posição Buy ",Symbol_A[A],". Código=",Trade_A.ResultRetcode(), " (",Trade_A.ResultRetcodeDescription(),")"); continue; // suspendemos a iteração FOR atual } else { Print("Fechamento da posição Buy ",Symbol_A[A]," bem-sucedido. Código=",Trade_A.ResultRetcode(), " (",Trade_A.ResultRetcodeDescription(),")"); continue; // suspendemos a iteração FOR atual } } } //... }
Abertura de uma posição de Compra:
//--- A.9.1: para Compra if(Ask_price<=BB_lower_band_low[0]) { //... //--- A.9.1.3: Executamos o negócio if(!Trade_A.Buy(OrderLot,Symbol_A[A])) { Print("Compra de ",Symbol_A[A]," falhou. Código=",Trade_A.ResultRetcode(), " (",Trade_A.ResultRetcodeDescription(),")"); continue; // suspendemos a iteração FOR atual } else { Print("Compra de ",Symbol_A[A]," bem-sucedida. Código=",Trade_A.ResultRetcode(), " (",Trade_A.ResultRetcodeDescription(),")"); continue; // suspendemos a iteração FOR atual } }
Durante desinicialização, lembre-se de encerrar a geração de eventos do temporizador e excluir o identificador de indicador.
void OnDeinit(const int reason) { //--- Parando a geração de eventos EventKillTimer(); //--- Removemos os identificadores de indicador for(int i=0; i<Strategy_A; i++) { IndicatorRelease(BB_handle_high_A[i]); IndicatorRelease(BB_handle_low_A[i]); } }
4. Resultados do teste
Quando o Expert Advisor estiver pronto, testamos cada estratégia e cada símbolo separadamente, e comparamos com os resultados de teste no modo de negociação com todas as estratégias e símbolos simultaneamente.
Supõe-se que o usuário já tenha identificado os melhores valores dos parâmetros de entrada.
Abaixo estão as configurações do Testador de Estratégia:
Fig. 2. Configurando o Testador de Estratégia
Resultado para a estratégia A, símbolo EURUSD:
Fig.3. Resultado de teste para a estratégia A, símbolo EURUSD
Resultado para a estratégia A, símbolo GBPUSD:
Fig.4. Resultado de teste para a estratégia A, símbolo GBPUSD
Resultado para a estratégia B, símbolo AUDUSD:
Fig. 5. Resultado de teste para a estratégia B, símbolo AUDUSD
Resultado para a estratégia B, símbolo EURJPY:
Fig. 6. Resultado de teste para a estratégia B, símbolo EURJPY
Resultado de teste para todas as estratégias e símbolos:
Fig. 7. Resultado de teste para todas as estratégias e símbolos
Fim do artigo
Como resultado, temos uma estrutura simples e conveniente de Expert Advisor multissistema e multimoeda que pode ser usada para colocar praticamente qualquer uma de suas estratégias.
Esse EA permitirá que você avalie melhor a eficiência da negociação utilizando todas as suas estratégias. Ele pode também ser útil no caso de apenas um Expert Advisor estar autorizado a trabalhar em uma determinada conta. O código fonte do Expert Advisor está anexo ao artigo para facilitar o estudo do material acima.
Traduzido do russo pela MetaQuotes Ltd.
Artigo original: https://www.mql5.com/ru/articles/770
- 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