English Русский 中文 Español Deutsch 日本語 한국어 Français Italiano Türkçe
Guia Prático MQL5: Processamento de Eventos Personalizados do Gráfico

Guia Prático MQL5: Processamento de Eventos Personalizados do Gráfico

MetaTrader 5Exemplos | 4 novembro 2014, 16:54
1 707 0
Denis Kirichenko
Denis Kirichenko

Introdução

Este artigo é uma continuação lógica do artigo Guia Prático MQL5: Processamento de Eventos Típicos do Gráfico. Ele abrange os métodos de trabalho com os eventos personalizados do gráfico. Aqui o leitor pode encontrar exemplos de desenvolvimento e tratamento de eventos personalizados. Todas as idéias discutidas neste artigo foram implementados com ferramentas orientada a objeto.

Como o tema de eventos personalizados é bastante amplo, é possível que os programadores e desenvolvedores possam apresentar muita criatividade em seus trabalhos.


1. Evento personalizado do Gráfico

É claro que este evento é definido pelo utilizador. Cabe ao programador decidir o que exatamente e qual tarefa ou bloco de programa que poderá ser tomado na forma de um evento. Os desenvolvedores em MQL5 podem criar seus próprios eventos, possibilitando a expansão das capacidades da linguagem em implementar algoritmos complexos.

O evento personalizado é o segundo tipo possível de um evento gráfico. O primeiro é um evento típico. Embora não haja um termo tal como "evento típico do gráfico" na Documentação, eu ainda sugiro usá-lo para os dez primeiros tipos de eventos do gráfico.

Os desenvolvedores sugerem apenas uma enumeração para todos os eventos do gráfico - ENUM_CHART_EVENT.

De acordo com a documentação, existem 65.535 identificadores de eventos personalizados. O primeiro e o último identificador dos eventos personalizados são definidos pelos valores explícitos de CHARTEVENT_CUSTOM e CHARTEVENT_CUSTOM_LAST, sendo numericamente iguais a 1000 e 66.534 respectivamente (Fig.1).

Fig.1 O primeiro e o último identificador de eventos personalizados

Fig.1 O primeiro e o último identificador de eventos personalizados

Cálculos simples, considerando o primeiro e o último identificador irá produzir: 66534-1000 + 1 = 65535.

Antes de usar os eventos personalizados, eles devem ser projetados primeiro. Neste sentido, um desenvolvedor se torna um mentor e autor do conceito de evento, na qual, então, ele será implementado como um algoritmo para o futuro Expert. Seria útil ter uma classificação dos eventos personalizados. Este método cognitivo não permitirá se livrar da ambiguidade, mas irá certamente reduzir o grau dela e irá ajudar a organizar a linha de raciocínio.

Vamos considerar tal critério de um evento personalizado como fonte. Por exemplo, o desenvolvedor sergeev sugeriu a idéia de um protótipo de robô de negociação. Ele divide todos os eventos em três grupos (Fig.2).

Fig.2 Grupos de fontes de eventos personalizados

Fig.2 Grupos de fontes de eventos personalizados

Então, de acordo com esta idéia principal, os eventos personalizados devem ser desenvolvidos com base em sua associação ao grupo.

Vamos tentar fazer algo simples para começar. Primeiramente, devemos tomar o primeiro grupo, que inclui os eventos de indicadores. Os eventos que podem pertencer a este grupo são: a criação e exclusão de um indicador, receber um sinal de abertura e fechamento de uma posição. O segundo grupo inclui os eventos de alteração do estado de ordens e posições. Em nosso exemplo, a abertura e o fechamento de posições estará neste grupo. É tudo muito simples. E, finalmente, o grupo mais complexo para formalização é o grupo de eventos externos.

Tomemos dois eventos: a ativação e desativação da negociação manual.

Fig.3 Fontes de eventos personalizados

Fig.3 Fontes de eventos personalizados

O padrão primário pode ser estabelecido através do método dedutivo (do geral ao específico) (Fig.3). Este é o mesmo padrão que vamos usar mais tarde para a criação de tipos de eventos na classe correspondente (Tabela 1).

Tabela 1 Eventos personalizados

Tabela 1 Eventos personalizados

Esta tabela ainda não pode ser chamada como um "conceito de evento", mas ela será o começo. Aqui está outra abordagem. É do conhecimento de todos que um modelo de um sistema de negociação abstrato consiste de três módulos básicos de subsistemas (Fig.4).

Fig.4 Modelo de um sistema de comércio abstrato

Fig.4 Modelo de um sistema de comércio abstrato

Eventos personalizados com base no critério "fonte" podem ser classificados como eventos gerados em:

  1. no subsistema de sinal;
  2. subsistema de rastreio de posições em aberto;
  3. subsistema de gestão do dinheiro.

Este último, por exemplo, pode incluir eventos como atingir o nível de rebaixamento permitido, aumentar o volume de negociação por um valor definido, aumentar a percentagem do limite de perdas, etc


2. Manipulador e Gerador do ChartEvent

As seguintes linhas serão dedicadas ao manipulador e gerador de eventos de gráfico. O processamento de um evento personalizado do gráfico, a princípio, é semelhante ao tratamento de um evento típico do gráfico.

Um manipulador, a função OnChartEvent(), leva quatro constantes como parâmetros. Aparentemente, os desenvolvedores usaram esse mecanismo para implementar a idéia de identificar um evento e obter informações adicionais sobre isso. Na minha opinião, é um mecanismo do programa muito compacto e prático.

A função EventChartCustom() gera um evento personalizado do gráfico. Notavelmente, um evento personalizado do gráfico pode ser criado para o gráfico "atual" e também para o "exterior". Eu acho que, o artigo mais interessante sobre o significado de gráfico atual e exterior é A Implementação de um Modo Multi-currency (múltiplas moedas) no MetaTrader 5.

Em minha opinião, existe uma discordância no fato do identificador de evento ser do tipo ushort no gerador, enquanto que no manipulador ele é do tipo int. Seria mais lógico usar o tipo de dados ushort no manipulador também.


3. Classe de Evento Personalizado

Como mencionei antes, o conceito de evento depende do desenvolvedor do Expert. Agora vamos trabalhar com os eventos da Tabela 1. Primeiramente, vamos classificar a classe de evento personalizado CEventBase e seus derivados (Fig.5).

Fig.5 hierarquia das classes de eventos

Fig.5 hierarquia das classes de eventos

A classe básica é a seguinte:

//+------------------------------------------------------------------+
//| Class CEventBase.                                                |
//| Purpose: base class for a custom event                           |
//| Derives from class CObject.                                      |
//+------------------------------------------------------------------+
class CEventBase : public CObject
  {
protected:
   ENUM_EVENT_TYPE   m_type;
   ushort            m_id;
   SEventData        m_data;

public:
   void              CEventBase(void)
     {
      this.m_id=0;
      this.m_type=EVENT_TYPE_NULL;
     };
   void             ~CEventBase(void){};
   //--
   bool              Generate(const ushort _event_id,const SEventData &_data,
                              const bool _is_custom=true);
   ushort            GetId(void) {return this.m_id;};

private:
   virtual bool      Validate(void) {return true;};
  };

O tipo de evento é definido pelo enumerador ENUM_EVENT_TYPE:

//+------------------------------------------------------------------+
//| A custom event type enumeration                       |
//+------------------------------------------------------------------+
enum ENUM_EVENT_TYPE
  {
   EVENT_TYPE_NULL=0,      // no event
   //---
   EVENT_TYPE_INDICATOR=1, // indicator event
   EVENT_TYPE_ORDER=2,     // order event
   EVENT_TYPE_EXTERNAL=3,  // external event
  };

Os membros de dados compreendem o identificador do evento e a estrutura de dados.

O método Generate() da classe básica CEventBase lida com a geração de um evento. O método GetId() retorna o ID do evento e o método virtual Validate() verifica o valor do identificador do evento. No começo eu incluí o método de manipulação do evento na classe, mas depois eu me dei conta de que cada evento é único e um método abstrato aqui não é suficiente. Eu acabei delegando essa tarefa para a classe CEventProcessor que lida com eventos personalizados.


4. Classe que Manipula Eventos Personalizados

A classe CEventProcessor supostamente gera e manipula oito eventos apresentados. Os membros de dados da classe seguem abaixo:

//+------------------------------------------------------------------+
//| Class CEventProcessor.                                           |
//| Purpose: base class for an event processor EA                    |
//+------------------------------------------------------------------+
class CEventProcessor
  {
//+----------------------------Data members--------------------------+
protected:
   ulong             m_magic;
   //--- flags
   bool              m_is_init;
   bool              m_is_trade;
   //---
   CEventBase       *m_ptr_event;
   //---
   CTrade            m_trade;
   //---
   CiMA              m_fast_ema;
   CiMA              m_slow_ema;
   //---
   CButton           m_button;
   bool              m_button_state;
//+------------------------------------------------------------------+
  };

Entre a lista de atributos, há flags de inicialização e de negociação. O primeiro não permitirá que o EA negocie, caso ele não for iniciado corretamente. O segundo verifica a permissão para negociação.

Há também o ponteiro para o objeto do tipo CEventBase, que funciona com diferentes tipos de eventos, utilizando polimorfismo. Uma instância da classe CTrade fornece acesso as operações de negociação.

Objetos do tipo CiMA facilitam o manuseio dos dados recebidos dos indicadores. Para simplificar o exemplo, eu tomei duas médias móveis, que irão receber um sinal de negociação. Há também uma instância da classe "CButton" que será usada para a ativação/desativação manual do EA.

Os métodos da classe foram divididos pelo princípio de "módulos - procedimentos - funções - macros":

//+------------------------------------------------------------------+
//| Class CEventProcessor.                                           |
//| Purpose: base class for an event processor EA                    |
//+------------------------------------------------------------------+
class CEventProcessor
  {
//+-------------------------------Methods----------------------------+
public:
   //--- constructor/destructor
   void              CEventProcessor(const ulong _magic);
   void             ~CEventProcessor(void);

   //--- Modules
   //--- event generating
   bool              Start(void);
   void              Finish(void);
   void              Main(void);
   //--- event processing
   void              ProcessEvent(const ushort _event_id,const SEventData &_data);

private:
   //--- Procedures
   void              Close(void);
   void              Open(void);

   //--- Functions
   ENUM_ORDER_TYPE   CheckCloseSignal(const ENUM_ORDER_TYPE _close_sig);
   ENUM_ORDER_TYPE   CheckOpenSignal(const ENUM_ORDER_TYPE _open_sig);
   bool              GetIndicatorData(double &_fast_vals[],double &_slow_vals[]);

   //--- Macros
   void              ResetEvent(void);
   bool              ButtonStop(void);
   bool              ButtonResume(void);
  };

Entre os módulos, há três que só geram eventos: o de início Start(), o de término Finish() e o principal Main(). O quarto módulo ProcessEvent() é ao mesmo tempo um manipulador de eventos e um gerador.


4.1 Módulo de Início

Este módulo foi projetado para ser chamado no manipulador OnInit().

//+------------------------------------------------------------------+
//| Start module                                                     |
//+------------------------------------------------------------------+
bool CEventProcessor::Start(void)
  {
//--- create an indicator event object
   this.m_ptr_event=new CIndicatorEvent();
   if(CheckPointer(this.m_ptr_event)==POINTER_DYNAMIC)
     {
      SEventData data;
      data.lparam=(long)this.m_magic;
      //--- generate CHARTEVENT_CUSTOM+1 event
      if(this.m_ptr_event.Generate(1,data))
         //--- create a button
         if(this.m_button.Create(0,"Start_stop_btn",0,25,25,150,50))
            if(this.ButtonStop())
              {
               this.m_button_state=false;
               return true;
              }
     }

//---
   return false;
  }

Neste módulo é criado um ponteiro para o indicador objeto de evento. Em seguida, o evento "criação do indicador" é gerado. O botão é o último a ser criado. Ele é trocado no modo de "Stop". Isso significa que se o botão for pressionado, o Expert iria parar de trabalhar.

A estrutura SEventData também está relacionada na definição deste método. Ela é um simples recipiente para os parâmetros passados ​do gerador de evento personalizado. Apenas um campo da estrutura será preenchida aqui - será o campo do tipo long. Ela irá armazenar o número mágico do EA.


4.2 Módulo de Término

Este módulo deve ser chamado no manipulador OnDeinit().

//+------------------------------------------------------------------+
//| Finish  module                                                   |
//+------------------------------------------------------------------+
void CEventProcessor::Finish(void)
  {
//--- reset the event object
   this.ResetEvent();
//--- create an indicator event object
   this.m_ptr_event=new CIndicatorEvent();
   if(CheckPointer(this.m_ptr_event)==POINTER_DYNAMIC)
     {
      SEventData data;
      data.lparam=(long)this.m_magic;
      //--- generate CHARTEVENT_CUSTOM+2 event
      bool is_generated=this.m_ptr_event.Generate(2,data,false);
      //--- process CHARTEVENT_CUSTOM+2 event
      if(is_generated)
         this.ProcessEvent(CHARTEVENT_CUSTOM+2,data);
     }
  }

Aqui, o ponteiro do evento anterior é eliminado e o evento "remoção do indicador" é gerado. Devo notar que, se um evento personalizado é gerado no manipulador OnDeinit(), você receberá um erro de execução 4001 (Erro externo inesperado). Portanto, a geração e manipulação de eventos são realizados dentro deste método sem chamar a OnChartEvent().

Mais uma vez, o número mágico do EA será armazenado usando a estrutura SEventData.


4.3 Módulo Principal

Este módulo deve ser chamado no manipulador OnTick().

//+------------------------------------------------------------------+
//| Main  module                                                     |
//+------------------------------------------------------------------+
void CEventProcessor::Main(void)
  {
//--- a new bar object
   static CisNewBar newBar;

//--- if initialized     
   if(this.m_is_init)
      //--- if not paused   
      if(this.m_is_trade)
         //--- if a new bar
         if(newBar.isNewBar())
           {
            //--- close module
            this.Close();
            //--- open module
            this.Open();
           }
  }

Os procedimentos Open() e Close() são chamados neste módulo. O primeiro procedimento pode gerar o evento "Recebendo um sinal para a abertura", e o segundo o evento "Recebendo um sinal para o fechamento". A versão atual do módulo é totalmente funcional na aparência de uma nova barra. Uma classe para a detecção de uma nova barra foi descrito por Konstantin Gruzdev.


4.4 Módulo de Manipulação de Eventos

Este módulo deve ser chamado no manipulador OnChartEvent(). Este módulo é o maior em termos de tamanho e funcionalidade.

//+------------------------------------------------------------------+
//| Process event module                                             |
//+------------------------------------------------------------------+
void CEventProcessor::ProcessEvent(const ushort _event_id,const SEventData &_data)
  {
//--- check event id
   if(_event_id==CHARTEVENT_OBJECT_CLICK)
     {
      //--- button click
      if(StringCompare(_data.sparam,this.m_button.Name())==0)
        {
         //--- button state
         bool button_curr_state=this.m_button.Pressed();
         //--- to stop
         if(button_curr_state && !this.m_button_state)
           {
            if(this.ButtonResume())
              {
               this.m_button_state=true;
               //--- reset the event object
               this.ResetEvent();
               //--- create an external event object
               this.m_ptr_event=new CExternalEvent();
               //---
               if(CheckPointer(this.m_ptr_event)==POINTER_DYNAMIC)
                 {
                  SEventData data;
                  data.lparam=(long)this.m_magic;
                  data.dparam=(double)TimeCurrent();
                  //--- generate CHARTEVENT_CUSTOM+7 event
                  ushort curr_id=7;
                  if(!this.m_ptr_event.Generate(curr_id,data))
                     PrintFormat("Failed to generate an event: %d",curr_id);
                 }
              }
           }
         //--- to resume
         else if(!button_curr_state && this.m_button_state)
           {
            if(this.ButtonStop())
              {
               this.m_button_state=false;
               //--- reset the event object
               this.ResetEvent();
               //--- create an external event object
               this.m_ptr_event=new CExternalEvent();
               //---
               if(CheckPointer(this.m_ptr_event)==POINTER_DYNAMIC)
                 {
                  SEventData data;
                  data.lparam=(long)this.m_magic;
                  data.dparam=(double)TimeCurrent();
                  //--- generate CHARTEVENT_CUSTOM+8 event
                  ushort curr_id=8;
                  if(!this.m_ptr_event.Generate(curr_id,data))
                     PrintFormat("Failed to generate an event: %d",curr_id);
                 }
              }
           }
        }
     }
//--- user event 
   else if(_event_id>CHARTEVENT_CUSTOM)
     {
      long magic=_data.lparam;
      ushort curr_event_id=this.m_ptr_event.GetId();
      //--- check magic
      if(magic==this.m_magic)
         //--- check id
         if(curr_event_id==_event_id)
           {
            //--- process the definite user event 
            switch(_event_id)
              {
               //--- 1) indicator creation
               case CHARTEVENT_CUSTOM+1:
                 {
                  //--- create a fast ema
                  if(this.m_fast_ema.Create(_Symbol,_Period,21,0,MODE_EMA,PRICE_CLOSE))
                     if(this.m_slow_ema.Create(_Symbol,_Period,55,0,MODE_EMA,PRICE_CLOSE))
                        if(this.m_fast_ema.Handle()!=INVALID_HANDLE)
                           if(this.m_slow_ema.Handle()!=INVALID_HANDLE)
                             {
                              this.m_trade.SetExpertMagicNumber(this.m_magic);
                              this.m_trade.SetDeviationInPoints(InpSlippage);
                              //---
                              this.m_is_init=true;
                             }
                  //---
                  break;
                 }
               //--- 2) indicator deletion
               case CHARTEVENT_CUSTOM+2:
                 {
                  //---release indicators
                  bool is_slow_released=IndicatorRelease(this.m_fast_ema.Handle());
                  bool is_fast_released=IndicatorRelease(this.m_slow_ema.Handle());
                  if(!(is_slow_released && is_fast_released))
                    {
                     //--- to log?
                     if(InpIsLogging)
                        Print("Failed to release the indicators!");
                    }
                  //--- reset the event object
                  this.ResetEvent();
                  //---
                  break;
                 }
               //--- 3) check open signal
               case CHARTEVENT_CUSTOM+3:
                 {
                  MqlTick last_tick;
                  if(SymbolInfoTick(_Symbol,last_tick))
                    {
                     //--- signal type
                     ENUM_ORDER_TYPE open_ord_type=(ENUM_ORDER_TYPE)_data.dparam;
                     //---
                     double open_pr,sl_pr,tp_pr,coeff;
                     open_pr=sl_pr=tp_pr=coeff=0.;
                     //---
                     if(open_ord_type==ORDER_TYPE_BUY)
                       {
                        open_pr=last_tick.ask;
                        coeff=1.;
                       }
                     else if(open_ord_type==ORDER_TYPE_SELL)
                       {
                        open_pr=last_tick.bid;
                        coeff=-1.;
                       }
                     sl_pr=open_pr-coeff*InpStopLoss*_Point;
                     tp_pr=open_pr+coeff*InpStopLoss*_Point;

                     //--- to normalize prices
                     open_pr=NormalizeDouble(open_pr,_Digits);
                     sl_pr=NormalizeDouble(sl_pr,_Digits);
                     tp_pr=NormalizeDouble(tp_pr,_Digits);
                     //--- open the position
                     if(!this.m_trade.PositionOpen(_Symbol,open_ord_type,InpTradeLot,open_pr,
                        sl_pr,tp_pr))
                       {
                        //--- to log?
                        if(InpIsLogging)
                           Print("Failed to open the position: "+_Symbol);
                       }
                     else
                       {
                        //--- pause
                        Sleep(InpTradePause);
                        //--- reset the event object
                        this.ResetEvent();
                        //--- create an order event object
                        this.m_ptr_event=new COrderEvent();
                        if(CheckPointer(this.m_ptr_event)==POINTER_DYNAMIC)
                          {
                           SEventData data;
                           data.lparam=(long)this.m_magic;
                           data.dparam=(double)this.m_trade.ResultDeal();
                           //--- generate CHARTEVENT_CUSTOM+5 event
                           ushort curr_id=5;
                           if(!this.m_ptr_event.Generate(curr_id,data))
                              PrintFormat("Failed to generate an event: %d",curr_id);
                          }
                       }
                    }
                  //---
                  break;
                 }
               //--- 4) check close signal
               case CHARTEVENT_CUSTOM+4:
                 {
                  if(!this.m_trade.PositionClose(_Symbol))
                    {
                     //--- to log?
                     if(InpIsLogging)
                        Print("Failed to close the position: "+_Symbol);
                    }
                  else
                    {
                     //--- pause
                     Sleep(InpTradePause);
                     //--- reset the event object
                     this.ResetEvent();
                     //--- create an order event object
                     this.m_ptr_event=new COrderEvent();
                     if(CheckPointer(this.m_ptr_event)==POINTER_DYNAMIC)
                       {
                        SEventData data;
                        data.lparam=(long)this.m_magic;
                        data.dparam=(double)this.m_trade.ResultDeal();
                        //--- generate CHARTEVENT_CUSTOM+6 event
                        ushort curr_id=6;
                        if(!this.m_ptr_event.Generate(curr_id,data))
                           PrintFormat("Failed to generate an event: %d",curr_id);
                       }
                    }
                  //---
                  break;
                 }
               //--- 5) position opening
               case CHARTEVENT_CUSTOM+5:
                 {
                  ulong ticket=(ulong)_data.dparam;
                  ulong deal=(ulong)_data.dparam;
                  //---
                  datetime now=TimeCurrent();
                  //--- check the deals & orders history
                  if(HistorySelect(now-PeriodSeconds(PERIOD_H1),now))
                     if(HistoryDealSelect(deal))
                       {
                        double deal_vol=HistoryDealGetDouble(deal,DEAL_VOLUME);
                        ENUM_DEAL_ENTRY deal_entry=(ENUM_DEAL_ENTRY)HistoryDealGetInteger(deal,DEAL_ENTRY);
                        //---
                        if(deal_entry==DEAL_ENTRY_IN)
                          {
                           //--- to log?
                           if(InpIsLogging)
                             {
                              Print("\nNew position for: "+_Symbol);
                              PrintFormat("Volume: %0.2f",deal_vol);
                             }
                          }
                       }
                  //---
                  break;
                 }
               //--- 6) position closing
               case CHARTEVENT_CUSTOM+6:
                 {
                  ulong ticket=(ulong)_data.dparam;
                  ulong deal=(ulong)_data.dparam;
                  //---
                  datetime now=TimeCurrent();
                  //--- check the deals & orders history
                  if(HistorySelect(now-PeriodSeconds(PERIOD_H1),now))
                     if(HistoryDealSelect(deal))
                       {
                        double deal_vol=HistoryDealGetDouble(deal,DEAL_VOLUME);
                        ENUM_DEAL_ENTRY deal_entry=(ENUM_DEAL_ENTRY)HistoryDealGetInteger(deal,DEAL_ENTRY);
                        //---
                        if(deal_entry==DEAL_ENTRY_OUT)
                          {
                           //--- to log?
                           if(InpIsLogging)
                             {
                              Print("\nClosed position for: "+_Symbol);
                              PrintFormat("Volume: %0.2f",deal_vol);
                             }
                          }
                       }
                  //---
                  break;
                 }
               //--- 7) stop trading
               case CHARTEVENT_CUSTOM+7:
                 {
                  datetime stop_time=(datetime)_data.dparam;
                  //---
                  this.m_is_trade=false;                  
                  //--- to log?                  
                  if(InpIsLogging)
                     PrintFormat("Expert trading is stopped at: %s",
                                 TimeToString(stop_time,TIME_DATE|TIME_MINUTES|TIME_SECONDS));
                  //---
                  break;
                 }
               //--- 8) resume trading 
               case CHARTEVENT_CUSTOM+8:
                 {
                  datetime resume_time=(datetime)_data.dparam;
                  this.m_is_trade=true;                  
                  //--- to log?                  
                  if(InpIsLogging)                     
                     PrintFormat("Expert trading is resumed at: %s",
                                 TimeToString(resume_time,TIME_DATE|TIME_MINUTES|TIME_SECONDS));
                  //---
                  break;
                 }
              }
           }
     }
  }

Ele consiste de duas partes. A primeira é para lidar com eventos relacionados ao clique sobre o objeto "Button". Este clique irá gerar um evento externo personalizado, que será tratado pelo manipulador mais tarde.

A segunda parte é projetada para o processamento de eventos personalizados que foram gerados. Ele contém dois blocos, onde após um evento relevante tem sido tratado, um novo é gerado. O evento "Recebendo um sinal de abertura" é processado no primeiro bloco. Seu manuseio bem sucedido gera uma nova ordem de evento "Abertura de uma posição". O evento "Recebendo um sinal para o fechamento" é processado no segundo bloco. Se o sinal é tratado, então o evento "fechamento de uma posição" acontece.

The Expert CustomEventProcessor.mq5 é um bom exemplo do uso da classe CEventProcessor. A EA foi projetado para a criação de eventos e responder a elas de forma adequada. Com o paradigma da POO, fomos capazes de minimizar o número de linhas do código-fonte. O código fonte do EA pode ser encontrado em anexo neste artigo.

A meu ver, não há necessidade de referir-se toda hora a um mecanismo de evento personalizado. Há vários coisas menores, insignificantes e menos agitadas em termos de estratégia que podem ter uma forma diferente.


Conclusão

Neste artigo eu tentei ilustrar os princípios de trabalhar com os eventos personalizados no ambiente MQL5. Eu espero que as ideias abordadas neste artigo seja de interesse aos programadores com diferentes experiências, não apenas aos novatos.

Eu fico feliz que a linguagem MQL5 esteja se desenvolvendo. Provavelmente, em um futuro próximo, haverá modelos de classe e que possa apontar para as funções. Então seremos capazes de escrever e delegar um ponteiro com direito pleno para um método de um objeto arbitrário.

Os arquivos fonte do arquivo podem ser colocados em uma pasta do projeto. No meu caso, na pasta MQL5\Projects\ChartUserEvent.

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

Arquivos anexados |
Porque a Hospedagem Virtual no MetaTrader 4 e no MetaTrader 5 são Melhores que os VPS Usuais Porque a Hospedagem Virtual no MetaTrader 4 e no MetaTrader 5 são Melhores que os VPS Usuais
A rede de hospedagem virtual em nuvem foi desenvolvida especialmente para o MetaTrader 4 e o MetaTrader 5, possuindo todas as vantagens de uma solução nativa. Obtenha os benefícios de nossa oferta gratuita por 24 horas - teste um servidor virtual agora mesmo.
Como acessar o banco de dados MySQL a partir do MQL5 (MQL4) Como acessar o banco de dados MySQL a partir do MQL5 (MQL4)
Este artigo descreve o desenvolvimento de uma interface entre o banco de dados MySQL e a linguagem MQL. Ele discute soluções práticas existentes e oferece uma maneira mais conveniente de implementar uma biblioteca para trabalhar com o bancos de dados. O artigo contém uma descrição detalhada das funções, a estrutura da interface, exemplos e algumas características específicas de se trabalhar com o MySQL. Quanto às soluções de software, encontramos em anexo no artigo os arquivos de bibliotecas dinâmicas, documentação e exemplos de script para as linguagem MQL4 e MQL5.
Princípios de Programação em MQL5: Variáveis Globais do Terminal Princípios de Programação em MQL5: Variáveis Globais do Terminal
Este artigo destaca as capacidades orientada a objeto da linguagem MQL5 em criar objetos que facilitam o trabalho com as variáveis ​​globais do terminal. Como exemplo prático eu considero um caso em que as variáveis ​​globais são usados ​​como pontos de controle para a implementação das fases de um programa.
Guia Prático MQL5 - Expert Advisor Multi-Moeda e Trabalhando com ordens pendentes em MQL5 Guia Prático MQL5 - Expert Advisor Multi-Moeda e Trabalhando com ordens pendentes em MQL5
Desta vez, vamos criar um Expert Advisor multi-moeda com um algoritmo de negociação baseado no envio de ordens pendentes do tipo Buy Stop e Sell Stop. Neste artigo veremos os seguintes tópicos: a negociação em um intervalo de tempo especificado, colocar/modificar/remover as ordens pendentes, verificar se a última posição foi fechada no Take Profit ou no Stop Loss e controlar o histórico de transações para cada símbolo.