English Русский 中文 Español Deutsch 日本語 Português 한국어 Italiano Türkçe
Le MQL5 Cookbook : Gestion des événements du graphique personnalisés

Le MQL5 Cookbook : Gestion des événements du graphique personnalisés

MetaTrader 5Exemples | 13 janvier 2022, 15:46
154 0
Denis Kirichenko
Denis Kirichenko

Introduction

Cet article est la suite logique de l'article MQL5 Cookbook : Gestion des événements graphiques typiques. Il couvre les méthodes de travail avec des événements graphiques personnalisés. Ici, le lecteur peut trouver des exemples de développement et de gestion d'événements personnalisés. Toutes les idées discutées dans cet article ont été implémentées avec des outils orientés objet.

Comme le thème des événements personnalisés est assez large, c'est le cas lorsqu'un programmeur et un développeur peuvent introduire de la créativité dans leur travail.


1. Événement de graphique personnalisé

Il est clair que cet événement est défini par l'utilisateur. C'est au programmeur de décider quoi exactement et quelle tâche ou un bloc de programme pourrait prendre la forme d'un événement. Les développeurs MQL5 peuvent créer leurs propres événements, ce qui étend les capacités du langage pour la mise en œuvre d'algorithmes complexes.

Un événement personnalisé est le deuxième type possible d'événement de graphique. Le premier est un événement typique. Bien qu'il n'y ait pas de terme tel que « événement de graphique typique » dans la documentation, je suggère toujours de l'utiliser pour les dix premiers types d'événements de graphique.

Les développeurs suggèrent une seule énumération pour tous les événements de graphique : ENUM_CHART_EVENT.

Selon la documentation, il existe 65535 identifiants d'événements personnalisés. Le premier et le dernier identifiants des événements personnalisés sont définis par les valeurs explicites de CHARTEVENT_CUSTOM et CHARTEVENT_CUSTOM_LAST, qui sont numériquement égales à 1000 et 66534 en conséquence (Fig.1).

Fig.1 Le premier et le dernier identifiants des événements personnalisés

Fig.1 Le premier et le dernier identifiants des événements personnalisés

Des calculs simples prenant en compte le premier et le dernier identifiant produiront : 66534-1000+1=65535.

Avant d'utiliser des événements personnalisés, ils doivent d'abord être conçus. En ce sens, un développeur devient un cerveau et l'auteur du concept de l'événement, qui est ensuite implémenté comme un algorithme pour le futur Expert. Il serait utile d'avoir une classification des événements personnalisés. Cette méthode cognitive ne permettra pas de lever l'ambiguïté mais en réduira certainement le degré et arrangera le raisonnement.

Considérons un tel critère d'événement personnalisé comme source. Par exemple, le développeur Sergeev a suggéré une idée de prototype de robot de trading. Il divise tous les événements en trois groupes (Fig.2).

Fig.2 Groupes de sources d'événements personnalisés

Fig.2 Groupes de sources d'événements personnalisées

Ensuite, selon cette idée principale, les événements personnalisés sont à développer en fonction de leur affiliation à un groupe.

Essayons de faire quelque chose de simple pour commencer. Dans un premier temps, nous prendrons le premier groupe, qui comprend les événements indicateurs. Les événements pouvant appartenir à ce groupe sont : la création et la suppression d'un indicateur, la réception d'un signal d'ouverture et de fermeture d'une position. Le deuxième groupe comprend les événements de changement d'état des ordres et des positions. Dans notre exemple, l'ouverture et la fermeture des positions seront dans ce groupe. Tout est très simple. Et, enfin, le groupe le plus complexe pour la formalisation est un groupe d'événements externes.

Prenons deux événements : activer et désactiver le trading manuel.

Fig.3 Sources d'événements personnalisés

Fig.3 Sources d'événements personnalisés

Le modèle principal peut être établi par la méthode déductive (du général au spécial) (Fig.3). C'est le modèle même que nous allons utiliser plus tard pour créer des types d'événements dans la classe correspondante (tableau 1).

Tableau 1 Événements personnalisés

Tableau 1 Événements personnalisés

Ce tableau ne peut pas encore être qualifié de « concept d'événement », mais c'est un début. Voici une autre approche. Il est de notoriété publique qu'un modèle de système de trading abstrait se compose de trois sous-systèmes : des modules de base (Fig.4).

Fig.4 Modèle d'un système de trading abstrait

Fig.4 Modèle d'un système de trading abstrait

Les événements personnalisés basés sur le critère « source » peuvent être classés comme des événements générés dans :

  1. le sous-système de signalisation ;
  2. sous-système des positions ouvertes de suivi ;
  3. sous-système de gestion de l'argent.

Ce dernier, par exemple, peut inclure des événements tels que l'atteinte du niveau de tirage autorisé, l'augmentation d'un volume de trading d'une valeur définie, l'augmentation du pourcentage d'une limite de perte, etc.


2. Gestionnaire et générateur de ChartEvent

Les quelques lignes suivantes seront consacrées au gestionnaire et au générateur d'événements graphiques. Quant à la gestion d'un événement de graphique personnalisé, son principe est similaire à celui de la gestion d'un événement de graphique typique.

Un gestionnaire, la fonction OnChartEvent(), prend quatre constantes comme paramètres. Apparemment, les développeurs ont utilisé ce mécanisme pour mettre en œuvre l'idée d'identifier un événement et d'obtenir des informations supplémentaires à son sujet. À mon avis, c'est un mécanisme de programme très compact et pratique.

La fonction EventChartCustom() génère un événement de graphique personnalisé. Remarquablement, un événement de graphique personnalisé peut être créé pour un graphique « propre » et pour un graphique « étranger ». Je pense que l'article le plus intéressant sur la signification des graphiques propres et étrangers est La mise en œuvre d'un mode multi-devises dans MetaTrader 5.

À mon avis, il y a une discorde dans le fait que l'identifiant d'événement est de type ushort dans le générateur, alors que dans le gestionnaire, il est de type int. Il serait logique d'utiliser également le type de données ushort dans le gestionnaire.


3. Classe d'événement personnalisé

Comme je l'ai mentionné précédemment, le concept de l'événement appartient au développeur expert. Nous allons maintenant travailler avec les événements du tableau 1. Dans un premier temps, nous allons trier la classe de l'événement personnalisé CEventBase et ses dérivés (Fig.5).

Fig.5 Hiérarchie des classes d'événements

Fig.5 Hiérarchie des classes d'événements

La classe de base se présente comme suit :

//+------------------------------------------------------------------+
//| 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;};
  };

Le type d'événement est défini par l'énumération 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
  };

Les membres de données comprennent l'identifiant d'événement et la structure de données.

La méthode Generate() de la classe de base CEventBase traite de la génération d'un événement. La méthode GetId() renvoie l'identifiant de l'événement et la méthode virtuelle Validate() vérifie la valeur de l'identifiant de l'événement. Au début, j'ai inclus la méthode de gestion des événements dans la classe, mais plus tard, j'ai réalisé que chaque événement est unique et qu'une méthode abstraite n'est pas suffisante ici. J'ai fini par déléguer cette tâche à la classe CEventProcessor qui gère les événements personnalisés.


4. Classe de gestionnaire d'événements personnalisé

La classe CEventProcessor est censée générer et gérer huit événements présentés. Les membres de données de la classe ressemblent à :

//+------------------------------------------------------------------+
//| 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;
//+------------------------------------------------------------------+
  };

Parmi la liste des attributs, il y a des indicateurs d'initialisation et de trade. Le premier ne permettra pas à l'EA de trader s'il ne démarre pas correctement. Le second vérifie l'autorisation de trader.

Il y a aussi le pointeur vers l'objet du type CEventBase, qui fonctionne avec des événements de différents types en utilisant le polymorphisme. Une instance de la classe CTrade permet d'accéder aux opérations de trading.

Les objets de type CiMA facilitent le traitement des données reçues des indicateurs. Pour simplifier l'exemple, j'ai pris deux moyennes mobiles qui vont recevoir un signal de trading. Il existe également une instance de la classe «CButton» qui sera utilisée pour l'activation/la désactivation manuelle de l'EA.

Les méthodes de la classe étaient réparties selon le principe « modules - procédures - fonctions - 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);
  };

Parmi les modules, il y en a trois qui ne génèrent que des événements : celui de départ—Start(), celui de fin—Finish() et le principal—Main(). Le quatrième module ProcessEvent() est à la fois un gestionnaire d'événements et un générateur.


4.1 Module de démarrage

Ce module est conçu pour être appelé dans le gestionnaire 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;
  }

Un pointeur vers l'objet d’événement indicateur est créé dans ce module. Ensuite, l'événement « Création d'indicateur » est généré. Un bouton est le dernier à être créé. Il passe en mode « Stop ». Cela signifie que si le bouton était enfoncé, l'expert cesserait de fonctionner.

La structure SEventData est également impliquée dans cette définition de méthode. Il s'agit d'un simple conteneur de paramètres transmis au générateur de l'événement personnalisé. Un seul champ de la structure sera rempli ici, c'est le champ de type long. Il contiendra le nombre magique d'EA.


4.2 Module de finition

Ce module est censé être appelé dans le gestionnaire 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);
     }
  }

Ici, le pointeur d'événement précédent est effacé et l'événement « Suppression d'indicateur »  est généré. Je dois noter que si un événement personnalisé est généré dans le gestionnaire OnDeinit(), vous obtiendrez une erreur d'exécution 4001 (erreur externe inattendue). Par conséquent, la génération et la gestion des événements sont effectuées dans cette méthode sans appeler OnChartEvent().

Encore une fois, le nombre magique d'EA sera stocké à l'aide de la structure SEventData.


4.3 Module principal

Ce module est censé être appelé dans le gestionnaire 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();
           }
  }

Les procédures Open() et Close() sont appelées dans ce module. La première procédure peut générer l'événement « Réception d'un signal d'ouverture »  et la seconde, l'événement « Réception d'un signal de fermeture ». La version actuelle du module est entièrement fonctionnelle avec une nouvelle apparence de barre. Une classe pour détecter un nouvelle barre a été décrite par Konstantin Gruzdev.


4.4 Module de gestion des événements

Ce module est censé être appelé dans le gestionnaire OnChartEvent(). Ce module est le plus grand en termes de taille et de fonctionnalité.

//+------------------------------------------------------------------+
//| 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;
                 }
              }
           }
     }
  }

Il se compose de deux parties. La première est la gestion des événements liés à un clic sur l'objet « Bouton ». Ce clic générera un événement personnalisé externe, qui sera traité par le gestionnaire ultérieurement.

La deuxième partie est conçue pour le traitement des événements personnalisés générés. Il contient deux blocs, où après qu'un événement pertinent a été traité, un nouveau est généré. L'événement « Réception d'un signal d'ouverture » est traité dans le premier bloc. Son traitement réussi génère un nouvel événement d'ordre « Ouverture d'une position ». L'événement « Réception d'un signal de fermeture » est traité dans le deuxième bloc. Si le signal est traité, alors l'événement « Fermeture d'une position » a lieu.

L'expert CustomEventProcessor.mq5 est un bon exemple d'utilisation de la classe CEventProcessor. L'EA a été conçu pour créer des événements et y répondre de manière appropriée. Avec le paradigme OPP, nous avons pu réduire le code source à un nombre inférieur de lignes. Le code source d'EA se trouve en pièce jointe à cet article.

À mon avis, il n'est pas nécessaire de se référer à chaque fois au mécanisme d'un événement personnalisé. Il y a beaucoup de choses mineures, insignifiantes et sans événement en termes de stratégie qui peuvent avoir une forme différente.


Conclusion

Dans cet article, j'ai essayé d'illustrer les principes de travail avec des événements personnalisés dans l'environnement MQL5. J'espère que les idées couvertes dans cet article intéresseront les programmeurs ayant une expérience différente, pas seulement les novices.

Je suis content que le langage MQL5 se développe. Probablement, dans un avenir proche, il y aura des modèles de classe et peut-être des pointeurs vers des fonctions. Nous pourrons alors écrire un délégué à part entière pointant sur une méthode d'un objet arbitraire.

Les fichiers source de l'archive peuvent être placés dans un dossier de projet. Dans mon cas, il s'agit du dossier MQL5\Projects\ChartUserEvent.

Traduit du russe par MetaQuotes Ltd.
Article original : https://www.mql5.com/ru/articles/1163

Fichiers joints |
Les forêts aléatoires prédisent les tendances Les forêts aléatoires prédisent les tendances
Cet article envisage d'utiliser le package Rattle pour la recherche automatique de modèles afin de prédire les positions longues et courtes des paires de devises sur le Forex. Cet article peut être utile à la fois pour les traders débutants et expérimentés.
Le MQL5 Cookbook : Traitement de l'événement de TradeTransaction Le MQL5 Cookbook : Traitement de l'événement de TradeTransaction
Cet article considère les capacités du langage MQL5 du point de vue de la programmation par évènement. Le plus grand avantage de cette approche est que le programme peut recevoir des informations sur la mise en œuvre progressive d'une opération de trade. L'article contient également un exemple de réception et de traitement d'informations sur les opérations de trade en cours à l'aide du gestionnaire d'événements TradeTransaction. À mon avis, une telle approche peut être utilisée pour copier des transactions d'un terminal à un autre.
Pourquoi l'hébergement virtuel sur MetaTrader 4 et MetaTrader 5 est meilleur que les VPS habituels Pourquoi l'hébergement virtuel sur MetaTrader 4 et MetaTrader 5 est meilleur que les VPS habituels
Le réseau Virtual Hosting Cloud a été développé spécialement pour MetaTrader 4 et MetaTrader 5 et présente tous les avantages d'une solution native. Bénéficiez de notre offre gratuite de 24 heures - testez un serveur virtuel dès maintenant.
Réseaux de neurones de troisième génération : Réseaux profonds Réseaux de neurones de troisième génération : Réseaux profonds
Cet article est consacré à une nouvelle direction en perspective dans l’apprentissage automatique - l’apprentissage profond ou, pour être précis, les réseaux de neurones profonds. Il s’agit d’un bref examen des réseaux de neurones de deuxième génération, de l’architecture de leurs connexions et de leurs principaux types, méthodes et règles d’apprentissage et de leurs principaux inconvénients, suivi de l’histoire du développement des réseaux de neurones de troisième génération, de leurs principaux types, particularités et méthodes d’entraînement. Des expériences pratiques sur la construction et l’entraînement d’un réseau neuronal profond initié par les poids d’un autoencodeur empilé avec des données réelles sont menées. Toutes les étapes, de la sélection des données d’entrée à la dérivation métrique, sont discutées en détail. La dernière partie de l’article contient une implémentation logicielle d’un réseau de neurones profond dans un Expert Advisor avec un indicateur intégré basé sur MQL4/R.