English Русский 中文 Español Deutsch 日本語 한국어 Français Italiano Türkçe
A Implementação de um Modo Multi-currency (múltiplas moedas) no MetaTrader 5

A Implementação de um Modo Multi-currency (múltiplas moedas) no MetaTrader 5

MetaTrader 5Exemplos | 11 fevereiro 2014, 16:32
2 411 0
Konstantin Gruzdev
Konstantin Gruzdev

Introdução

Atualmente, há um grande número de sistemas desenvolvidos para negociação múltiplas moedas, indicadores e Expert Advisors. No entanto, os desenvolvedores continuam a enfrentar os problemas específicos de desenvolvimento de sistemas de múltiplas moedas.

Com o lançamento do terminal do cliente MetaTrader 5 e da linguagem de programação MQL5, que ganhou uma nova oportunidade de implementar um modo de múltiplas moedas completo e, conseqüentemente, robôs e indicadores múltiplas moedas mais eficientes. Estas novas oportunidades serão o tema deste artigo.


Visão geral da abordagem convencional

No nosso caso, a abordagem convencional é a tentativa de implementar um sistema de múltiplas moedas com base nas funções padrões OnTick() e OnCalculate() que substituíram a função start() do MQL4. Simplificando, com o aparecimento de um novo tick ou barra no gráfico atual, todos os pares de moedas (que participam no sistema múltiplas moedas) são sequencialmente solicitados para análise e tomada de decisão posterior.

Os problemas desta abordagem são:

  1. A dependência de todo o sistema sobre a entrada de ticks em um símbolo de negociação do gráfico atual.

    Para um mercado rápido, quando os ticks para o símbolo do gráfico atual forem frequentes, realmente não há problemas. Mas com um mercado lento, por exemplo, durante a noite, os ticks podem vir muito raramente: uma vez a cada meio minuto ou mais raramente ainda. Durante os intervalos entre a entrada de ticks raros, o sistema inteiro de múltiplas moedas fica "adormecido", embora as mudanças que ocorram em outros símbolos possam ser bastante drásticas.

    Esta deficiência não é muito significativa, se o sistema estiver configurado para trabalhar com intervalos de tempo maiores. Mas quanto menor é intervalo de tempo, mais eficaz ele é. Todos os dias o mundo aumenta a velocidade, os computadores são capazes de processar mais e mais informações por unidade de tempo e, consequentemente, mais pessoas estão hábeis a trabalhar com pequenos períodos e até mesmo com ticks.

  2. A complexidade da sincronização de dados de histórico sobre todos os símbolos utilizados em um sistema de múltiplas moedas.

    "No MetaTrader 4, apenas tais barras são desenhadas, em que pelo menos uma mudança de preço ocorreu. Se nenhuma mudança de preço ocorreu dentro de um minuto, um espaço de uma barra ocorrerá no gráfico com período de um minuto."- determina o início do artigo "Free-of-Holes" Charts (Gráficos "Livres de buracos) .

    Tal abordagem para a construção de um gráfico é mantido no MetaTrader 5. Ou seja, o mesmo número de barras no gráfico para cada símbolo não significa que eles sejam sincronizados com o tempo. Por exemplo, a barra de um centésimo pode ter um tempo de abertura diferente para cada símbolo. Portanto, durante a construção e novo cálculo de um indicador de múltiplas moedas, é importante ter a certeza de que todas as barras sejam compatíveis uma com a outra, para cada símbolo.

    Isto também não é muito significativo, se o sistema estiver configurado para trabalhar em um período de tempo maior, uma vez que, com o aumento do período, a probabilidade de faltar uma barra diminui consideravelmente. Embora, como se diz, você nunca consegue ser assim tão cuidadoso. E você nunca consegue ter certeza, a menos que a pessoa não faça nada.

    Devemos observar separadamente o tempo de sincronização da barra incompleta atual. Quando uma nova barra aparece no gráfico atual, não significa que as novas barras também se formaram em outros símbolos. Portanto, uma tentativa para saber o preço de uma nova barra através de um outro símbolo, usando funções CopyXXXX(), pode conduzir ao fato de que você obterá o preço da barra anterior para o símbolo ou simplesmente um erro de cópia. Por falar nisso, uma nova barra em outro símbolo pode formar muito mais cedo do que na atual. Isso também pode afetar a precisão da avaliação da situação.

    O artigo Criação de um indicador múltiplas moedas utilizando um número de buffers de indicador intermediário descreve as opções que mais ou menos resolveriam a questão da sincronização de dados de histórico.

  3. Outro ponto importante, relevante para a sincronização de dados: como é que vamos descobrir se houve uma atualização de histórico por algum símbolo de negociação?

    Por exemplo, quando nós construímos um indicador de moeda única - não há problemas. Se a variável de entrada prev_calculated da função OnCalculate() zerou, então recalcule o indicador. Mas o que vamos fazer se houve uma atualização no histórico para o símbolo e não no gráfico atual? Isso pode ocorrer a qualquer momento e pode ser necessário recalcular o indicador de múltiplas moedas. A resposta para esta questão é bastante relevante.

Sem nem mesmo entrar em outras origens, podemos ver que estes três exemplos são suficientes para causar tantos problemas que o código do EA ou do indicador de múltiplas moedas crescem para ser tão grandes. Mas a extensão do problema não foi resolvida ...


Nova esperança com a função OnTimer()

A nova capacidade do programa MQL- gerar o evento Timer (temporizador) e o gerenciador de evento padrão OnTimer() , deu esperança para o surgimento de novos tipos de sistemas de múltiplas moedas. Por um lado, isso é devido ao fato de que agora o indicador/Expert Advisor de múltiplas moedas pode tornar-se independente do recebimento dos ticks pelo símbolo do gráfico atual e por outro lado, pelo fato de que poderíamos monitorar o trabalho do EA pelo tempo. Mas ...

Isso resolve parcialmente os problemas descritos no parágrafo 1 da seção anterior e, é claro, fornece algumas vantagens. Mas exatamente quando você recebe o evento NewTick , com o recibo do evento Timer é necessário investigar sequencialmente todos os pares de moedas, em uma tentativa de controlar alterações. Algumas vezes, isso tem que ser feito com bastante frequência, o que pode aumentar significativamente o uso de recursos dos programas MQL5.

As questões identificadas nos parágrafos 2 e 3, permanecem no mesmo nível de solução. Além disso, é necessário resolver as questões que são específicas à função OnTimer() . Por exemplo, os problemas de inicialização/finalização do timer (temporizador), o seu trabalho durante a semana, etc.

Sem desprezar as vantagens óbvias do gerenciador de evento OnTimer() , deve ser observado que ele não permite a implementação de um modo de múltiplas moedas completo.


Novas possibilidades com a função OnChartEvent()

As limitações das funções padrões acima são devido à sua especialização limitada: elas são destinadas a lidar com eventos específicos, pré-definidos, e não se destinam a um sistema de múltiplas moedas.

Um elemento de resgate para desenvolver um sistema de múltiplas moedas pode ser o gerenciador de evento personalizado padrão OnChartEvent() . Ele permite ao programador gerar seus próprios eventos de acordo com a sua necessidade.

Por exemplo, ninguém pode nos impedir de usar esta função para obter ticks para algum símbolo no gráfico atual. Tudo que precisamos é enviar um "spy" ("espião") ao gráfico com o símbolo apropriado.

Pela palavra engraçada "spy", quero dizer o seguinte indicador:

//+------------------------------------------------------------------+
//|                                                         iSpy.mq5 |
//|                                            Copyright 2010, Lizar |
//|                                               lizar-2010@mail.ru |
//+------------------------------------------------------------------+
#define VERSION         "1.00 Build 2 (26 Dec 2010)"

#property copyright   "Copyright 2010, Lizar"
#property link        "https://www.mql5.com/ru/users/Lizar"
#property version     VERSION
#property description "iSpy agent-indicator. If you want to get ticks, attach it to the chart"
#property indicator_chart_window

input long            chart_id=0;        // chart id
input ushort          custom_event_id=0; // event id

//+------------------------------------------------------------------+
//| Custom indicator iteration function                              |
//+------------------------------------------------------------------+
int OnCalculate (const int rates_total,        // size of price[] array
                 const int prev_calculated,  // bars, calculated at the previous call
                 const int begin,            // starting index of data
                 const double& price[]       // array for the calculation
   )
  {
   double price_current=price[rates_total-1];

   //--- Initialization:
   if(prev_calculated==0) 
     { // Generate and send "Initialization" event
      EventChartCustom(chart_id,0,(long)_Period,price_current,_Symbol);
      return(rates_total);
     }
   
   // When the new tick, let's generate the "New tick" custom event
   // that can be processed by Expert Advisor or indicator
   EventChartCustom(chart_id,custom_event_id+1,(long)_Period,price_current,_Symbol);
   
   //--- return value of prev_calculated for next call
   return(rates_total);
  }
    

Podemos lançar este "spy" no gráfico do símbolo desejado e em seguida gerenciar suas mensagens no EA ou no indicador usando a função OnChartEvent() . Para decodificar as mensagens do "spy" corretamente, devemos interpretar os parâmetros desta função da seguinte maneira:

  • id - identificador de evento. Se o id- CHARTEVENT_CUSTOM = 0, nosso "spy" reporta que a variável prev_calculated foi zerada e ações adequadas devem ser realizadas;
  • lparam - neste caso, significa o período do gráfico, em que o "spy" é lançado;
  • dparam - tick price. Por padrão, este é o último preço de fechamento. Embora durante o lançamento do "spy", isso possa ser configurado para qualquer valor da enumeração ENUM_APPLIED_PRICE;
  • sparam - o nome do símbolo de negociação, em que foi recebido o evento.

Para demonstrar o trabalho simultâneo de vários "spies" ("espiões"), vamos escrever uma simples EA exSpy.mq5 (versão completa está disponível no arquivo):

//+------------------------------------------------------------------+
//|                                                        exSpy.mq5 |
//|                                            Copyright 2010, Lizar |
//|                            https://www.mql5.com/ru/users/Lizar |
//+------------------------------------------------------------------+
#define VERSION       "1.00 Build 1 (26 Dec 2010)"

#property copyright   "Copyright 2010, Lizar"
#property link        "https://www.mql5.com/ru/users/Lizar"
#property version     VERSION
#property description "The Expert Advisor shows the work of iSPY indicator"

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {   
   if(iCustom("GBPUSD",PERIOD_M1,"iSpy",ChartID(),0)==INVALID_HANDLE) 
      { Print("Error in setting of spy on GBPUSD"); return(true);}
   if(iCustom("EURUSD",PERIOD_M1,"iSpy",ChartID(),1)==INVALID_HANDLE) 
      { Print("Error in setting of spy on EURUSD"); return(true);}
   if(iCustom("USDJPY",PERIOD_M1,"iSpy",ChartID(),2)==INVALID_HANDLE) 
      { Print("Error in setting of spy on USDJPY"); return(true);}
      
   Print("Spys ok, waiting for events...");
   //---
   return(0);
  }
  
//+------------------------------------------------------------------+
//| The Standard event handler.                                      |
//| See MQL5 Reference for the details.                              |
//|                                                                  |
//| In this case it is used for decoding of spy messages, sent by    |
//| iSPY spy indicator                                               |
//+------------------------------------------------------------------+
void OnChartEvent(const int id,         // event id:
                                     // if id-CHARTEVENT_CUSTOM=0-"initialization" event
                const long&   lparam, // chart period
                const double& dparam, // price
                const string& sparam  // symbol
               )
  {
   if(id>=CHARTEVENT_CUSTOM)      
     {
      Print(TimeToString(TimeCurrent(),TIME_SECONDS)," -> id=",
            id-CHARTEVENT_CUSTOM,":  ",sparam," ",
            EnumToString((ENUM_TIMEFRAMES)lparam)," price=",dparam);
     }
  }
//+------------------------------------------------------------------+
    

Inicie o Expert Advisor no gráfico.

Aqui estão os resultados:


Como pode ser visto a partir do registro, obtém-se todos os ticks para o símbolo desejado, assim como o evento "inicialização", que, em particular, será recebido se houver uma atualização ou um carregamento do histórico.

Vamos resumir os resultados intermediários:

  • Não dependemos de ticks do símbolo em particular, como costumávamos ao usarmos as funções padrões OnTick() e OnCalculate() .
  • Não estamos limitados a intervalos de tempo estritamente definidos como quando utilizamos o OnTimer().
  • Não há necessidade de solicitar todos os símbolos em uma sequência ou em um ciclo para acompanhar suas mudanças. Uma vez que as alterações no símbolo ocorreram, obtemos o evento correspondente. Podemos identificar o símbolo, em que o evento ocorreu, pelo identificador de evento id ou pelo parâmetro sparam.
  • Resolvemos o problema de rastrear a sincronização dos dados históricos com o servidor para cada símbolo individualmente.
  • Uma pequena porção do trabalho é designada a programas adicionais. Isso é relevante para a paralelização de processamento de dados.
  • Nos parâmetros da função OnChartEvent() , obtemos informações adicionais: o período, preço e nome do símbolo. Isto pode simplificar muito o código do EA ou indicador, como não há necessidade de investigar adicionalmente estes dados.

Nesse ponto poderíamos basicamente terminar este artigo, já que implementamos com sucesso o modo por meio do terminal MetaTrader 5 e da linguagem do MQL5. Mas isso é uma implementação "bruta". E por isso, vamos mais adiante.


A implementação de um modo de múltiplas moedas

Se você usar a ideia acima de implementação de um regime de múltiplas moedas em sua forma pura, então, em algum momento você começará a enfrentar dificuldades. O problema é que tal abordagem nos permite obter todos os ticks para cada símbolo de negociação, em que o "spy" esteja em execução.

Em um segundo, com um mercado rápido, pode haver um certo número de ticks em cada símbolo. Isto pode levar a um "blockage" (bloqueio) do pedido de evento. Aqui está um aviso da seção de Ajuda :

O terminal do cliente acrescenta eventos que aparecem para a fila de eventos. Assim, os eventos são processados um após o outro de acordo com a ordem em que foram recebidos. Há uma exceção para o evento NewTick. Se a fila já tiver um evento como esse ou esse evento estiver sendo processado, o novo evento NewTick não será enfileirado.

A fila de eventos é limitada em tamanho. Na fila de excesso, eventos antigos são removidos sem serem processados, a fim de permitir o recebimento de novos eventos. Portanto, recomenda-se escrever gerenciadores de eventos eficientes e não é recomendado o uso de ciclos infinitos (há uma exceção de scripts, que lidam apenas com o evento Start [Início]).

O excesso da fila pode levar à perda de eventos importantes para o indicador ou EA de múltiplas moedas. Isso é por um lado. Por outro lado, nem sempre precisamos de ticks para todos os símbolos. às vezes precisamos obter apenas o "evento de nova barra" a qualquer prazo de tempo. Ou um número de eventos de "new bar" para diferentes prazos. Basicamente, nosso "espião" não é adequado para tais exigências e seu uso não é muito conveniente.

Vamos torná-lo universal, para que nunca mais tenhamos de voltar à questão de como obter um evento baseado no símbolo de um EA ou indicador multi- moeda. Para este propósito, vamos pegar um exemplo, a enumeração de evento ENUM_CHART_EVENT_SYMBOL da descrição do "Painel de controle MCM" para Expert Advisors e indicadores que envolvem múltiplas moedas :

enum ENUM_CHART_EVENT_SYMBOL
  {
   CHARTEVENT_INIT      =0,         // "Initialization" event
   CHARTEVENT_NO        =0,         // No events

   CHARTEVENT_NEWBAR_M1 =0x00000001, // "New bar" event on M1 chart
   CHARTEVENT_NEWBAR_M2 =0x00000002, // "New bar" event on M2 chart
   CHARTEVENT_NEWBAR_M3 =0x00000004, // "New bar" event on M3 chart
   CHARTEVENT_NEWBAR_M4 =0x00000008, // "New bar" event on M4 chart
   
   CHARTEVENT_NEWBAR_M5 =0x00000010, // "New bar" event on M5 chart
   CHARTEVENT_NEWBAR_M6 =0x00000020, // "New bar" event on M6 chart
   CHARTEVENT_NEWBAR_M10=0x00000040, // "New bar" event on M10 chart
   CHARTEVENT_NEWBAR_M12=0x00000080, // "New bar" event on M12 chart
   
   CHARTEVENT_NEWBAR_M15=0x00000100, // "New bar" event on M15 chart
   CHARTEVENT_NEWBAR_M20=0x00000200, // "New bar" event on M20 chart
   CHARTEVENT_NEWBAR_M30=0x00000400, // "New bar" event on M30 chart
   CHARTEVENT_NEWBAR_H1 =0x00000800, // "New bar" event on H1 chart
   
   CHARTEVENT_NEWBAR_H2 =0x00001000, // "New bar" event on H2 chart
   CHARTEVENT_NEWBAR_H3 =0x00002000, // "New bar" event on H3 chart
   CHARTEVENT_NEWBAR_H4 =0x00004000, // "New bar" event on H4 chart
   CHARTEVENT_NEWBAR_H6 =0x00008000, // "New bar" event on H6 chart
   
   CHARTEVENT_NEWBAR_H8 =0x00010000, // "New bar" event on H8 chart
   CHARTEVENT_NEWBAR_H12=0x00020000, // "New bar" event on H12 chart
   CHARTEVENT_NEWBAR_D1 =0x00040000, // "New bar" event on D1 chart
   CHARTEVENT_NEWBAR_W1 =0x00080000, // "New bar" event on W1 chart
     
   CHARTEVENT_NEWBAR_MN1=0x00100000, // "New bar" event on MN1 chart
   CHARTEVENT_TICK      =0x00200000, // "New tick" event
   
   CHARTEVENT_ALL       =0xFFFFFFFF, // All events
  };

Na verdade, esta enumeração são flags de eventos de gráfico personalizados. é o conjunto mínimo, que pode ser necessário para o modo múltiplas moedas. Claro, pode ser complementado. A combinação de flags determinará os eventos que vamos enviar para fora do "espião".

As flags podem ser combinadas usando a operação de lógica binária "OU". Por exemplo, a combinação CHARTEVENT_NEWBAR_M1 | CHARTEVENT_NEWBAR_H1 significará que vamos enviar os eventos "new bar" a partir do prazo de tempo de minuto ou hora com a ajuda do "espião". Estas flags serão o parâmetro de entrada do nosso indicador-espião. Além disso, vamos chamá-lo como "agente-indicador".

O próprio indicador, de acordo com as novas ideias, parece com isto:

//+------------------------------------------------------------------+
//|                                        Spy Control panel MCM.mq5 |
//|                                            Copyright 2010, Lizar |
//|                            https://www.mql5.com/en/users/Lizar |
//+------------------------------------------------------------------+
#define VERSION       "1.00 Build 3 (26 Dec 2010)"

#property copyright   "Copyright 2010, Lizar"
#property link        "https://www.mql5.com/en/users/Lizar"
#property version     VERSION
#property description "This is the MCM Control Panel agent-indicator."
#property description "Is launched on the required symbol on any time-frame"
#property description "and generates the custom NewBar event and/or NewTick"
#property description "for the chart which receives the event"

#property indicator_chart_window
  
input long                    chart_id;                 // identifier of the chart which receives the event
input ushort                  custom_event_id;          // event identifier  
input ENUM_CHART_EVENT_SYMBOL flag_event=CHARTEVENT_NO;// indicator, which determines the event type.

MqlDateTime time, prev_time;

//+------------------------------------------------------------------+
//| Custom indicator iteration function                              |
//+------------------------------------------------------------------+
int OnCalculate (const int rates_total,       // size of the price[] array
                 const int prev_calculated, // bars processed at the previous call
                 const int begin,           // where the data begins
                 const double& price[]      // calculations array
   )
  {  
   double price_current=price[rates_total-1];

   TimeCurrent(time);
   
   if(prev_calculated==0)
     {
      EventCustom(CHARTEVENT_INIT,price_current);
      prev_time=time; 
      return(rates_total);
     }
   
//--- new tick
   if((flag_event & CHARTEVENT_TICK)!=0) EventCustom(CHARTEVENT_TICK,price_current);       

//--- check change time
   if(time.min==prev_time.min && 
      time.hour==prev_time.hour && 
      time.day==prev_time.day &&
      time.mon==prev_time.mon) return(rates_total);

//--- new minute
   if((flag_event & CHARTEVENT_NEWBAR_M1)!=0) EventCustom(CHARTEVENT_NEWBAR_M1,price_current);     
   if(time.min%2 ==0 && (flag_event & CHARTEVENT_NEWBAR_M2)!=0)  EventCustom(CHARTEVENT_NEWBAR_M2,price_current);
   if(time.min%3 ==0 && (flag_event & CHARTEVENT_NEWBAR_M3)!=0)  EventCustom(CHARTEVENT_NEWBAR_M3,price_current); 
   if(time.min%4 ==0 && (flag_event & CHARTEVENT_NEWBAR_M4)!=0)  EventCustom(CHARTEVENT_NEWBAR_M4,price_current);      
   if(time.min%5 ==0 && (flag_event & CHARTEVENT_NEWBAR_M5)!=0)  EventCustom(CHARTEVENT_NEWBAR_M5,price_current);     
   if(time.min%6 ==0 && (flag_event & CHARTEVENT_NEWBAR_M6)!=0)  EventCustom(CHARTEVENT_NEWBAR_M6,price_current);     
   if(time.min%10==0 && (flag_event & CHARTEVENT_NEWBAR_M10)!=0) EventCustom(CHARTEVENT_NEWBAR_M10,price_current);      
   if(time.min%12==0 && (flag_event & CHARTEVENT_NEWBAR_M12)!=0) EventCustom(CHARTEVENT_NEWBAR_M12,price_current);      
   if(time.min%15==0 && (flag_event & CHARTEVENT_NEWBAR_M15)!=0) EventCustom(CHARTEVENT_NEWBAR_M15,price_current);      
   if(time.min%20==0 && (flag_event & CHARTEVENT_NEWBAR_M20)!=0) EventCustom(CHARTEVENT_NEWBAR_M20,price_current);      
   if(time.min%30==0 && (flag_event & CHARTEVENT_NEWBAR_M30)!=0) EventCustom(CHARTEVENT_NEWBAR_M30,price_current);      
   if(time.min!=0) {prev_time=time; return(rates_total);}
//--- new hour
   if((flag_event & CHARTEVENT_NEWBAR_H1)!=0) EventCustom(CHARTEVENT_NEWBAR_H1,price_current);
   if(time.hour%2 ==0 && (flag_event & CHARTEVENT_NEWBAR_H2)!=0)  EventCustom(CHARTEVENT_NEWBAR_H2,price_current);
   if(time.hour%3 ==0 && (flag_event & CHARTEVENT_NEWBAR_H3)!=0)  EventCustom(CHARTEVENT_NEWBAR_H3,price_current);      
   if(time.hour%4 ==0 && (flag_event & CHARTEVENT_NEWBAR_H4)!=0)  EventCustom(CHARTEVENT_NEWBAR_H4,price_current);      
   if(time.hour%6 ==0 && (flag_event & CHARTEVENT_NEWBAR_H6)!=0)  EventCustom(CHARTEVENT_NEWBAR_H6,price_current);      
   if(time.hour%8 ==0 && (flag_event & CHARTEVENT_NEWBAR_H8)!=0)  EventCustom(CHARTEVENT_NEWBAR_H8,price_current);      
   if(time.hour%12==0 && (flag_event & CHARTEVENT_NEWBAR_H12)!=0) EventCustom(CHARTEVENT_NEWBAR_H12,price_current);      
   if(time.hour!=0) {prev_time=time; return(rates_total);}
//--- new day
   if((flag_event & CHARTEVENT_NEWBAR_D1)!=0) EventCustom(CHARTEVENT_NEWBAR_D1,price_current);      
//--- new week
   if(time.day_of_week==1 && (flag_event & CHARTEVENT_NEWBAR_W1)!=0) EventCustom(CHARTEVENT_NEWBAR_W1,price_current);      
//--- new month
   if(time.day==1 && (flag_event & CHARTEVENT_NEWBAR_MN1)!=0) EventCustom(CHARTEVENT_NEWBAR_MN1,price_current);      
   prev_time=time;
//--- return value of prev_calculated for next call
   return(rates_total);
  }
//+------------------------------------------------------------------+

void EventCustom(ENUM_CHART_EVENT_SYMBOL event,double price)
  {
   EventChartCustom(chart_id,custom_event_id,(long)event,price,_Symbol);
   return;
  } 

Este indicador é parte do Painel de controle MCM não o renomeamos, os anexos contêm simplesmente uma versão atualizada do mesmo (consulte "Painel de Controle espião MCM.mq5"). Mas isso não significa que não possa ser utilizado separadamente do painel.

Este agente indicador gera eventos de usuário personalizados e os transmite para o gráfico-recebedor para processamento posterior desses eventos no EA ou no indicador usando a função OnChartEvent() . Agora os parâmetros de entrada desta função devem ser interpretados como a seguir:

  • id - identificador de evento;
  • lparam - indicador de evento, recebido do agente do painel. Os indicadores, correspondentes à enumeração ENUM_CHART_EVENT_SYMBOL;
  • dparam - preço tick ou preço de abertura de uma nova barra para um determinado intervalo de tempo;
  • sparam - o nome do símbolo de negociação, em que ocorreu o evento.

O EA demo parece não mais complicado que o anterior (a versão completa está disponível no arquivo):

//+------------------------------------------------------------------+
//|                                      exSpy Control panel MCM.mq5 |
//|                                            Copyright 2010, Lizar |
//|                            https://www.mql5.com/en/users/Lizar |
//+------------------------------------------------------------------+
#define VERSION       "1.00 Build 1 (28 Dec 2010)"

#property copyright   "Copyright 2010, Lizar"
#property link        "https://www.mql5.com/en/users/Lizar"
#property version     VERSION
#property description "The EA demonstrates the work of the MCM Spy Control panel"

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {   
   if(iCustom("GBPUSD",PERIOD_M1,"Spy Control panel MCM",ChartID(),0,
             CHARTEVENT_NEWBAR_M1|CHARTEVENT_NEWBAR_M5)==INVALID_HANDLE) 
      { Print("Error in setting of spy on GBPUSD"); return(true);}
   if(iCustom("EURUSD",PERIOD_M1,"Spy Control panel MCM",ChartID(),1,
             CHARTEVENT_NEWBAR_M2)==INVALID_HANDLE) 
      { Print("Error in setting of spy on EURUSD"); return(true);}
   if(iCustom("USDJPY",PERIOD_M1,"Spy Control panel MCM",ChartID(),2,
             CHARTEVENT_NEWBAR_M6)==INVALID_HANDLE) 
      { Print("Error in setting of spy on USDJPY"); return(true);}
      
   Print("Spys ok, waiting for events...");
   //---
   return(0);
  }
  
//+------------------------------------------------------------------+
//| The Standard event handler.                                      |
//| See MQL5 Reference for the details.                              |
//|                                                                  |
//| In this case it is used for decoding of spy messages, sent by    |
//| iSPY Control panel MCM indicator.                                |
//+------------------------------------------------------------------+
void OnChartEvent(const int id,           // event identifier
                  const long&   lparam, // the event flag.
                                       // event flag is a bit mask of ENUM_CHART_EVENT_SYMBOL enumeration.
                  const double& dparam, // price
                  const string& sparam  // symbol 
                 )
  {
   if(id>=CHARTEVENT_CUSTOM)      
     {
      Print(TimeToString(TimeCurrent(),TIME_SECONDS)," -> id=",id-CHARTEVENT_CUSTOM,
           ":  ",sparam," ",EnumToString((ENUM_CHART_EVENT_SYMBOL)lparam)," price=",dparam);
     }
  }

Os resultados do trabalho do painel de controle exSpy MCM:


Como você pode ver, recebemos regularmente todos os eventos solicitados.

Vamos resumir mais uma vez o resultado intermediário:

  • Obrigado à nova versão do agente-indicador, mantemos todas as suas conquistas anteriores.
  • Agora, a partir de nossos programas MQL de múltiplas moedas, podemos especificar, pelo menos, 23 eventos, que incluem os eventos "new tick" "new bar" e "initialization" (inicialização).
  • Mesmo mais trabalho a partir do EA/indicador é dado aos agentes, de modo a descarregá-los.

Bem, como foi constatado, não é tão difícil de implementar um modo de múltiplas moedas completo no MetaTrader 5.

Além disso, quero destacar algumas nuances.

Primeira. Todos os eventos, que são gerados pelos "agentes" para nosso EA/indicador de múltiplas moedas são externos. Em conexão a isso, surge a questão: "é necessário executar os agentes diretamente do EA/indicador?". A resposta é: "não."

Segunda. Na função OnChartEvent() o identificador de evento id parece estar sobressalente, visto que podemos descobrir qual símbolo o evento recebeu pelo parâmetro sparam - o nome do símbolo de negociação. Então, talvez possamos usá-lo para algum outro propósito? A resposta é: "sim, podemos."

Tais argumentos nos levaram ao surgimento do "Painel de controle MCM" para Expert Advisors e indicadores que envolvem múltiplas moedas. é um tipo de "interlayer" (camada sobre camada) entre o terminal e o EA/indicador. Este nos forneceu ainda mais benefícios e flexibilidade na configuração de um ambiente de múltiplas moedas:

  • O painel pode ser instalado como um indicador separado no gráfico e em seguida determinar os indicadores múltiplas moedas, compatíveis com o painel.
  • O painel pode incluir indicadores e EAs como unidades de componentes. Ele carregará juntamente com elas.
  • Podemos ativar/desativar o símbolo da janela "Market Watch" para negociações ou análise utilizando o menu "Eventos". Na função OnChartEvent() não podemos descobrir o número de série do símbolo, pelo identificador de evento id, na janela "Market Watch".
  • Podemos configurar o modo de negociação por ticks pelo evento "new bar" para qualquer período e para o símbolo, selecionado no "Market Watch". Tudo isso é feito utilizando o menu regular.
  • Nós podemos mudar todas as configurações acima, sem descarregá-las, pará-las ou ir para a janela de propriedades do EA ou do indicador.
  • Nada disso limita o potencial de criatividade na criação de indicadores e EAs de múltiplas moedas. Além disso, agora não temos que integrar no nosso código as saídas deste painel. O gerenciamento dos agente-indicadores agora é implementado no painel de controle.
  • A estrutura do sistema de múltiplas moedas é ainda mais simples.


O indicador de múltiplas moedas RSI para o índice de dólar USDX

Para experimentar todas as vantagens do método acima, proponho implementar a variante de múltiplas moedas do indicador RSI para o índice de dólar USDx utilizando o Painel de controle MCM.

Para começar, anotarei algumas características especiais. Geralmente, ao tentarmos analisar o índice de dólar, nós simplesmente contamos os indicadores das leituras do índice. Do meu ponto de vista, isso não é inteiramente correto, uma vez que cada símbolo da cesta do índice de pares de moedas, faz suas próprias contribuições. Assim, por exemplo, vamos calcular o RSI para índice de dólar pela fórmula, semelhante à fórmula de cálculo do índice:

Ou seja, primeiro vamos calcular o RSI para um par de moedas em particular, em seguida, ler o RSI para o índice, levando em conta o peso dos coeficientes.

O leitor pode observar que há um problema com a sincronização de dados históricos, todos os símbolos, utilizados no sistema de múltiplas moedas. (consulte o parágrafo 2 da seção "Visão geral da abordagem convencional").

Este problema foi resolvido no indicador, usando as funções de classe para construir os buffers sincronizados RSI (arquivo SynchronizedBufferRSI.mqh). Não há motivo para fornecer o código de classe inteiro, pois há somente momentos relevantes apresentados abaixo.

Primeiro, o buffer de indicador é definido dentro da classe com o modificador de acesso público :

public:
   double   buffer[];   // indicator buffer
    

Em segundo lugar, a inicialização do indicador é feita usando o método da classe:

//--- Initialization methods:
bool Init(int n,string symbol,int rsi_count,int rsi_period);
    

E em terceiro lugar, para cada barra o valor do buffer de indicador é sincronizada com o intervalo de tempo atual, utilizando o método de atualização de classe:

//+------------------------------------------------------------------+
//| The method of receiving/updating indicator data for one bar      |
//| of the indicator buffer.                                         |
//| INPUT:  bar   - bar number                                       |
//| OUTPUT: no.                                                      |
//| REMARK: no.                                                      |
//+------------------------------------------------------------------+
void CSynchronizedBufferRSI::Refresh(int bar=0)
  {
   buffer[bar]=EMPTY_VALUE; // Initialization of the bar of the indicator buffer.
     
   //--- Inquire the time of the bar for the current graph:
   datetime time[1];      
   if(CopyTime(_Symbol,_Period,bar,1,time)!=1) return; // In case of an error, we wait for the next tick/bar...

   //--- Request the value of the indicator for the symbol for the time,
   //--- consistent with that of the bar of the current graph:
   double value[1];
   if(CopyBuffer(m_handle,0,time[0],time[0],value)!=1) return; // In case of an error, wait for the next tick/bar...

   buffer[bar]=value[0];
   return;
  }

Para a sincronização completa de todos os buffers de indicador, precisamos utilizar um intervalo de minuto inteiro sem "buracos", tal como descrito no artigo.a seguir. Mas, para este método de sincronização dos buffers de indicador, selecionamos especialmente o intervalo do gráfico atual, já que o display indicador ocorre nele.

Falando por experiência própria, posso dizer que, para pequenos períodos, não faz sentido usar tal método de sincronização, para qualquer solução de série temporal ou buffer de indicador, se o seu símbolo for diferente do símbolo no gráfico atual.

O gráfico mostra claramente por que vale a pena fazer isso:


Para maiores períodos de tempo, isto não é geralmente observado.

E por último mas não menos importante. Abaixo está o código de um padrão de handler de evento de usuário, que é usado no indicador:

//+------------------------------------------------------------------+
//| The Standard event handler.                                      |
//| See MQL5 Reference for the details.                              |
//+------------------------------------------------------------------+
void OnChartEvent(const int id,         // event identifier or position symbol in the "Market Match"+CHARTEVENT_CUSTOM  
                const long& lparam,   // event indicator
                const double& dparam, // price
                const string& sparam  // symbol
                )
  {
   int custom_id=id-CHARTEVENT_CUSTOM-1;
   
   if(custom_id>=0)      
     {
      if(lparam!=CHARTEVENT_NEWBAR_NO)
        { 
         //--- Recalculation of the last uncompleted bar:
         if(EventToPeriod(lparam)==_Period && sparam==_Symbol)
           { // Recalculation of the indicator, if a new bar on the current chart
            iRSIUSDx_Ind[0]=EMPTY_VALUE;
            //--- Updating the value of the RSI for all of the currency pairs for the new bar
            for(int i=0;i<symbol_total;i++) buffer[i].Refresh();
            iRSIUSDx(symbol_total);   // calculation of the current incomplete bar RSI for the index
            return;
           }
         
         buffer[custom_id].Refresh(); // The value of RSI for the custom_id of the currency pair for the current bar
         iRSIUSDx(symbol_total);      // calculation of the RSI for the current(uncompleted) bar RSIx
         return;
        }
      else 
        { 
         //--- Recalculation of the indicator for the "Initialization" event 
         buffer[custom_id].RefreshBuffer();     // Update of the RSI buffer for the custom_id of the currency pair
         Init_iRSIUSDx(symbol_total,calculate); // Update of the RSI buffer for the index
         return;
        }
     }
  }

Recursos especiais do código:

  • O uso de o evento identificador id para se referir à matriz, que contém ponteiros para as instâncias de classe, os quais são destinados a calcular os buffers de indicador RSI, de forma sincronizada com o intervalo de tempo atual. Esta abordagem simplifica bastante a estrutura do código.
  • O evento "Inicializar" é utilizado para recalcular o buffer de indicador RSI somente para o par de moedas, em que foi recebido, e não para todos os seis símbolos. Como mencionado anteriormente, isto permite que você sincronize o indicador, por exemplo, ao atualizar o histórico para um símbolo.
  • O evento "new bar" é usado para sincronizar todos os buffers de indicador RSI para uma novo barra no gráfico atual.
  • O evento "new tick" é usado de todos os pares de moedas para atualizar os indicadores da última barra incompleta. Além disso, o relato da barra é feito apenas para o par, para a qual "new tick" foi recebido.

Após explorar o código completo de indicador RSI para o índice de dólar USDx, se tornará mais evidente como isso funciona.

Características de instalação:

  • Baixe os indicadores e o "Painel de controle MCM" para Expert Advisors que envolvam múltiplas moedas e compile os arquivos "iControl panel MCM.mq5" e "Spy Control panel MCM.mq5".
  • Especifique a seguinte ordem de símbolos na janela "Market Match":
    1. EURUSD
    2. USDJPY
    3. GBPUSD
    4. USDCAD
    5. USDSEK
    6. USDCHF
    Isto é necessário porque eu não coloquei uma verificação adequada para o indicador e esta sequência é necessária para o cálculo correto do indicador.
  • Descompacte o arquivo na pasta do iRSIUSDx.zip/MQL5 Anexe o iRSIUSDx.ex5 da pasta MQL5/Indicators/iRSIUSDx para o gráfico EURUSD com o período M1.
  • Em sequência, para todos os símbolos no menu "Evento" do painel "Control panel MCM", configure o evento "New tick", como descrito aqui. Você deve obter uma imagem, similar a da foto acima.
  • Além disso, para o símbolo EURUSD, defina o evento "new bar" no gráfico minuto. No indicador, este evento é usado para a sincronização quando a nova barra no intervalo de tempo atual, que é igual a M1.
  • Se você desejar um exemplo mais visual, configure o índice de dólar como descrito aqui.

Conclusão

A implementação discutida de um modo de múltiplas moedas completo no MetaTrader 5 demonstra completamente as vantagens da plataforma e a linguagem de programação MQL5 na resolução deste problema. O que causou anteriormente a maioria das dificuldades, agora está disponível.

Obviamente, isso é só o início de um movimento nessa direção. Certamente, haverá mais opções encontradas de uma forma ainda melhor de sincronização de dados, gerenciamento de modos múltiplas moedas, etc. Mas espero que agora fique claro que temos todas as ferramentas necessárias para isso.

Traduzido do russo pela MetaQuotes Ltd.
Artigo original: https://www.mql5.com/ru/articles/234

Arquivos anexados |
irsiusdx_en.zip (7.54 KB)
Conectando NeuroSolutions Neuronets Conectando NeuroSolutions Neuronets
Além da criação de neuronets, o suite de software NeuroSolutions permite exportá-los como DLLs. Este artigo descreve o processo de criação de um neuronet, gerando um DLL e conectando-o a um Expert Advisor para negociação no MetaTrader 5.
Assistente MQL5: como criar um módulo de rastreamento de posições abertas Assistente MQL5: como criar um módulo de rastreamento de posições abertas
O gerador de estratégias de negociação do Assistente MQL5 simplifica o teste de ideias de negociação. O artigo discute como escrever e conectar ao gerador de estratégias de negociação do Assistente MQL5 a sua própria classe de gerenciamento de posições abertas, movendo o nível de Stop Loss para a zona lossless quando o preço for em direção da posição, permitindo diminuir levantamentos ao negociar. Fala também sobre a estrutura e o formato da descrição da classe criada para o Assistente MQL5.
Moving Mini-Max: um Novo Indicador para a Análise Técnica e sua Implementação no MQL5 Moving Mini-Max: um Novo Indicador para a Análise Técnica e sua Implementação no MQL5
No seguinte artigo, descrevo um processo de implementação do indicador Moving Mini-Max com base em um documento de Z.G.Silagadze 'Moving Mini-max: a new indicator for technical analysis'. A ideia do indicador baseia-se na simulação do fenômeno de tunelamento quântico, proposto por G. Gamov na teoria de desintegração alfa.
Assistente MQL5: como criar um módulo de gerenciamento de risco e dinheiro Assistente MQL5: como criar um módulo de gerenciamento de risco e dinheiro
O gerador de estratégias de negociação do Assistente MQL5 simplifica extremamente o teste de ideias de negociação. O artigo descreve como desenvolver um módulo personalizado de risco e gerenciamento de dinheiro e habilitá-lo no Assistente MQL5. Como exemplo consideramos um algoritmo de gestão de dinheiro, em que o tamanho do volume de negócio é determinado pelos resultados do negócio anterior. A estrutura e o formato da descrição da classe criada para o Assistente MQL5 também são discutidas nesse artigo.