English Русский 中文 Español Deutsch 日本語 Português 한국어 Italiano Türkçe
Le gestionnaire d'événements "New Bar"

Le gestionnaire d'événements "New Bar"

MetaTrader 5Exemples | 17 novembre 2021, 16:26
491 0
Konstantin Gruzdev
Konstantin Gruzdev

Introduction

Les auteurs d'indicateurs et les experts se sont toujours intéressés à l'écriture du code compact en termes de temps d'exécution. Vous pouvez attaque ce problème sous différents angles. À partir de ce vaste sujet dans cet article, nous couvrirons le problème, qui est apparemment déjà résolu : recherchez une nouvelle barre. C'est un moyen assez répandu pour limiter les boucles de calcul, puisque tous les calculs et opérations de trading sont effectués une seule fois lors de la génération d'une nouvelle barre sur le graphique. Alors, ce qui sera discuté :  

  • Méthodes de détection de nouvelles barres.
  • Lacunes des algorithmes existants de détection de nouvelles barres.
  • Création d'une méthode universelle de détection de nouvelles barres.
  • Subtilités et manières d'appliquer cette méthode.
  • Événement NewBar et gestionnaire de cet événement - OnNewBar().

Façons de détecter de nouvelles barres

Maintenant, il existe une solution acceptable sur la façon de détecter une nouvelle barre. Par exemple, ils peuvent être trouvé dans Limitations et vérifications chez Expert Advisors, Les Principes de Calcul Economique des Indicateurs articles ou ici NewBar. Par ailleurs, je recommande d'étudier ces matières Ce sera plus facile de comprendre ce de quoi je parle.  

Ces matières utilisent le principe de suivi de l'heure d'ouverture de la barre actuellement inachevée. C'est un moyen très simple et fiable. Il existe d'autres méthodes de détecter une nouvelle barre.

Par exemple, dans les indicateurs personnalisés à cet effet, vous pouvez utiliser deux paramètres d'entrée de la fonction OnCalculate() : rates_total et prev_calculated. Limitation de cette méthode - est essentiellement le fait qu'elle ne peut être utilisée que pour détecter une nouvelle barre sur le graphique actuel et uniquement dans les indicateurs. Si vous souhaitez retrouver une nouvelle barre sur une autre période ou symbole, il est nécessaire d'utiliser des techniques supplémentaires.

Ou, par exemple, vous pouvez essayer d'attraper une nouvelle barre sur son premier tick, lorsque Tick Volume = 1 ou lorsque tous les prix des barres sont égaux : Open = High = Low = Close. Ces méthodes peuvent être bien utilisées pour les tests, mais dans le trading réel, elles provoquent souvent des bugs Cela est dû au fait que le moment entre le premier et le deuxième tick n'est parfois pas suffisant pour attraper la barre générée. Ceci est particulièrement visible lors d'un fort mouvement du marché ou lorsque la qualité de la connexion Internet est faible.  

Il existe un moyen de détecter une nouvelle barre basée sur la fonction TimeCurrent(). à propos, c'est un bon moyen si vous devez détecter une nouvelle barre pour le graphique actuel. Nous l'utiliserons au terme de cet article.

Eh bien, vous pouvez même demander à un voisin : "Hé, y a-t-il une nouvelle barre?". Je me demande ce qu'il va répondre ? Bon, d'accord, arrêtons votre choix sur le principe du suivi du temps d'ouverture de la barre inachevée actuelle pour en détecter une nouvelle. Sa simplicité et fiabilité sont véritablement éprouvées et sans faille. 

Point de départ

Dans les matières mentionnées ci-dessus, les choses ne vont pas mal avec la détection d'une nouvelle barre. Mais...  

Pour comprendre ce qu'est ce "mais", comme point de départ (ou un prototype), nous prendrons une fonction de travail simple et efficace pour détecter une nouvelle barre à partir de l'article title Limitations et vérifications dans Expert Advisorstitle Limitations et vérifications dans Expert Advisors. C'est ici:

//+------------------------------------------------------------------+
//| Returns true if a new bar has appeared for a symbol/period pair  |
//+------------------------------------------------------------------+
bool isNewBar()
  {
//--- memorize the time of opening of the last bar in the static variable
   static datetime last_time=0;
//--- current time
   datetime lastbar_time=SeriesInfoInteger(Symbol(),Period(),SERIES_LASTBAR_DATE);

//--- if it is the first call of the function
   if(last_time==0)
     {
      //--- set the time and exit
      last_time=lastbar_time;
      return(false);
     }

//--- if the time differs
   if(last_time!=lastbar_time)
     {
      //--- memorize the time and return true
      last_time=lastbar_time;
      return(true);
     }
//--- if we passed to this line, then the bar is not new; return false
   return(false);
  }

Cette fonction prototype fonctionne réellement et jouit du plein droit à la vie. Mais... 

Analyse de la Fonction Prototype

J'ai copié cette fonction dans le code source de mon (bien sûr) le plus grand et le meilleur des meilleurs Expert Advisor. N'a pas fonctionné. J'ai commencé à enquêter. Voici mes pensées sur cette fonction.

Fonction en-tête. Examinons tout en conséquence. Commençons par l'en-tête de la fonction :

bool isNewBar()

J'aime la fonction en-tête, elle est très simple, intuitive, et n'a pas besoin de gérer les paramètres adoptés.. Ce serait bien de l'utiliser sous cette forme à l'avenir.

Nombre d'appels limités. Après l'en-tête se trouve la première instruction qui initialise la variable statique :

//---  memorize the time of opening of the last bar in the static variable
   static datetime last_time=0;

Tout semble plutôt bien. Mais...

Le problème est que nous utilisons une variable statique. Les rubriques d'aide nous indiquent : Les rubriques d'aide nous indiquent :

Les variables statiques existent dès l'exécution du programme et ne sont initialisées qu'une seule fois avant l'appel de la fonction spécialisée OnInit(). Si les valeurs initiales ne sont pas indiquées, les variables de la classe de stockage statique prennent zéro valeurs initiales. 

Les variables locales, déclarées avec le mot-clé statique conservent leurs valeurs pendant toute la durée de vie de la fonction. A chaque appel de fonction suivant, ces variables locales contiennent les valeurs qu'elles avaient lors de l'appel précédent.

Si vous appelez cette fonction prototype à partir d'un seul endroit, alors nous avons ce dont nous avons besoin. Mais si nous souhaitons utiliser cette fonction, par exemple, à nouveau dans un autre endroit dans la même boucle de calcul, elle retournera toujours faux, ce qui indique qu'il n'y a pas de barre. Et ce ne sera pas toujours vrai. La variable statique dans ce cas impose une limite artificielle au nombre d'appels de fonctions prototypes.

Question d'universalité. L'instruction suivante dans la fonction prototype se présente come ceci :

//--- current time
   datetime lastbar_time=SeriesInfoInteger(Symbol(),Period(),SERIES_LASTBAR_DATE);

Il est logique que pour obtenir l'heure d'ouverture de la dernière barre inachevée, la fonction SeriesInfoInteger() soit utilisée avec le modificateur SERIES_LASTBAR_DATE.

Notre fonction prototype isNewBar() a été conçue à l'origine comme une fonction simple et, par défaut, elle utilise l'instrument de trading et la période du graphique actuel. Ceci est acceptable si vous souhaitez suivre la nouvelle barre uniquement sur le graphique actuel. Mais que faire si j'utilise la période et l'instrument non seulement pour le graphique actuel ? De plus, que se passe-t-il si j'ai un tableau complexe ? Par exemple, et si je décidais de tracer Renko ou Kagi ?

Le manque de peut nous limiter sévèrement. Plus tard, nous verrons comment le réparer.  

La gestion des erreurs. Jetons un coup d'œil à la fonction SeriesInfoInteger(). Que pensez-vous qu'il renverrait, s'il s'exécute alors que le graphique n'a pas encore été formé ? Une telle condition peut se manifester, par exemple, si vous avez attaché votre Expert Advisor ou votre indicateur à un graphique et avez décidé de modifier la période ou le symbole, ou lorsque vous redémarrez le terminal. Et que se passera-t-il pendant la mise à jour de la série temporelle ? Incidemment, le est un tel avertissement dans les rubriques d'aide :

Disponibilité des données

La présence de données au format HCC ou même au format HC prêt à l'emploi ne dénote toujours pas la disponibilité absolue de ces données à afficher sur un graphique ou à utiliser dans les programmes MQL5.

Lorsque vous accédez aux données de prix ou aux valeurs d'indicateur depuis un programme MQL5, rappelez-vous que leur disponibilité à un certain moment ou à partir d'un certain moment n'est pas garantie. Cela est dû au fait qu'afin de sauvegarder les ressources du système, la copie complète des données nécessaires à un programme mql5 n'est pas stockée dans MetaTrader 5 ; seul un accès direct à la base de données du terminal est accordé.

L'historique des prix pour toutes les périodes est conçu à partir de données communes au format HCC, et toute mise à jour des données depuis un serveur entraîne la mise à jour des données pour toutes les périodes et le recalcul des indicateurs. De ce fait, l'accès aux données peut être refusé, même si ces données étaient disponibles il y a un moment.

Alors, qu'est-ce que cette fonction va retourner? Pour éviter cette incertitude, vous devez en quelque sorte commencer à détecter les erreurs de requête de l'heure d'ouverture de la dernière barre inachevée.  

Possibilité d'initialisation. Continuons Tenez compte des déclarations suivantes de notre fonction prototype :

//--- if it is the first call of the function
   if(last_time==0)
     {
      //--- set the time and exit
      last_time=lastbar_time;
      return(false);
     }

Ici, tout va bien. Cependant, il y a une nuance. Avez-vous remarqué la phrase ci-dessus de l'aide : "Les variables statiques existent dès l'exécution du programme et ne sont initialisées qu'une seule fois avant la fonction spécialisée OnInit()" ? Et si nous avons besoin de plus de temps pour initialiser la variable last_time ? Plus précisément, que faire si vous souhaitez créer artificiellement une condition de premier appel ? Ou une autre condition ? Il est facile de poser des questions quand vous avez les réponses. Mais plus là-dessus plus tard.

Nombre de barres. Ensuite, notre fonction prototype aura le code suivant :

//--- if the time differs
   if(last_time!=lastbar_time)
     {
      //--- memorize the time and return true
      last_time=lastbar_time;
      return(true);
     }

Vous voyez, un programmeur comme moi peut faire en sorte que l'opérateur if "surprenne" le terminal client et le Testeur de Stratégie. Le fait est que, logiquement, le temps passé est toujours inférieur au présent. C'est last_time < lastbar_time. En raison d'une erreur accidentelle de programme, j'ai la machine à remonter le temps, ou plus exactement - le contraire s'est produit : lastbar_time < last_time. Quelle surprise! En général, un tel paradoxe temporel est facile à détecter et à afficher un message d'erreur.

Mais chaque nuage a une doublure argentée. En regardant ma "machine à remonter le temps", je me suis rendu compte que parmi les appels isNewBar(), non seulement une nouvelle barre peut apparaître. Plus la période du graphique est petite, plus la probabilité d'occurrence de plusieurs barres entre les appels de fonction est élevée. Il peut y avoir plusieurs raisons à cela : à commencer par le temps de calcul long et se terminer par un manque temporaire de connexion avec le serveur. La possibilité de recevoir non seulement le signal d'une nouvelle barre, mais aussi le nombre de barres, sera certainement utile.

Notre fonction prototype se termine ainsi :

//--- if we passed to this line, then the bar is not new; return false
   return(false);

Oui, si nous sommes passés à cette ligne - la barre n'est pas nouvelle.

Création d'une nouvelle fonction isNewBar() 

Ici commence une chose intéressante. Nous allons résoudre les lacunes détectées. Vous savez, j'étais un peu trop modeste, en appelant la section "Création d'une nouvelle fonction isNewBar()". Nous ferons quelque chose de plus solide.

Nous commencerons par éliminer les restrictions sur le nombre d'appels de fonction.

La première chose qui me vient à l’esprit est que vous pouvez utiliser les fonctions avec le même nom comme IsNewBar() depuis l’article ou d’ici isNewBar. Les Principes de Calcul Economique d’Indicateurs. C'est-à-dire, pour inclure des tableaux stockant plusieurs valeurs last_time dans le corps de la fonction, placez les compteurs des appels de fonction isNewBar() à partir de différents endroits, et ainsi de suite. Bien sûr, ce sont toutes des versions de travail et elles peuvent être implémentées. Mais imaginez, si nous écrivons un Expert Advisor multi-devises pour travailler sur 12 paires de devises. Il y aura tant de nuances nécessaires à prendre en compte et à ne pas confondre ?

Que devrions nous faire? La réponse est ici !

La beauté de la programmation orientée-objet est qu'un objet ou une instance d'une classe peut « vivre leurs propres vies » indépendamment des autres instances de la même classe. Commençons donc par créer une classe CisNewBar, afin que nous puissions produire des instances de cette classe à n'importe quel endroit de notre Expert Advisor ou Indicator un nombre de fois illimité. Et laissez chaque instance "vivre sa propre vie".

C'est ce que nous devons commencer:

class CisNewBar 
  {
   protected:
      datetime          m_lastbar_time;   // Time of opening last bar
      
   public:
      void              CisNewBar();      // CisNewBar constructor
      //--- Methods of detecting new bars:
      bool              isNewBar();       // First type of request for new bar
  };  

bool CisNewBar::isNewBar()
  {
   //--- here is the definition of static variable

   //--- here will be the rest of method's code   
   ...

   //--- if we've reached this line, then the bar is not new; return false
   return(false);
  }

Ce qui était la fonction isNewBar(), est maintenant la méthode. Notez que maintenant il n'y a plus de variable statique last_time - à la place, nous avons maintenant la variable de classe protégée m_lastbar_time. Si nous avions laissé une variable statique dans la méthode isNewBar(), alors tous nos efforts auraient mal tourné, car nous serions confrontés aux mêmes problèmes qu'auparavant avec la fonction isNewBar() - ce sont des caractéristiques des variables statiques.

Et maintenant, l'heure de la dernière barre sera stockée dans la variable protégée m_lastbar_time de la classe, et dans chaque instance de classe, la mémoire sera allouée à cette variable. Ainsi, nous avons pu supprimer la restriction sur le nombre d'appels, qui était dans la fonction prototype. Nous pouvons appeler la méthode isNewBar() à différents endroits de notre programme MQL autant de fois que nous le souhaitons, créant une instance de classe pour chaque endroit.

C'est quelque chose que nous avons réussi. Travaillons maintenant sur l'universalité. Avant d'ajouter quelque chose à notre nouvelle classe, j'aimerais vous diriger à une idée amusante.

Raisonnons. Que souhaitons-nous? Nous souhaitons obtenir un signal sur la nouvelle barre. Comment souhaitons-nous faire cela ? Ainsi, si l'heure d'ouverture de la barre inachevée actuelle sur le dernier tick (ou au dernier moment) est supérieure à l'heure d'ouverture de la barre inachevée actuelle sur le tick précédent (ou au moment précédent), alors une nouvelle barre est formée. Phrase compliquée, mais c'est correct. L'essentiel c’est que nous devons comparer le temps. Par conséquent, j'ai décidé qu'il serait logique de passer l'heure d'ouverture de la barre inachevée actuelle newbar_time dans la méthode isNewBar(). Ensuite, l'en-tête de la méthode sera le suivant :

bool isNewBar(datetime newbar_time)

Ne demandez pas encore où nous prendrons le newbar_time - supposez qu'il est déjà connu. Nous examinerons cela plus tard.  

D'ailleurs, en passant le temps dans la méthode isNewBar(), on obtient un outil très flexible pour détecter une nouvelle barre. Nous pourrons couvrir toutes les périodes graphiques standard avec toutes sortes d'outils de trading. C'est arrivé pour que maintenant nous ne dépendions plus du nom du symbole et de la taille de la période.  

Nous pouvons également utiliser des cartes non standard. Par exemple, si vous tracez des chandeliers en ticks, ou des graphiques Renko ou Kagi, leur heure d'ouverture de la barre ne coïncide pratiquement jamais avec l'heure des périodes graphiques standard. Dans ce cas, notre fonction sera indispensable.

Eh bien, maintenant c'est OK avec la polyvalence. Complétons notre classe CisNewBar conformément à notre idée :

class CisNewBar 
  {
   protected:
      datetime          m_lastbar_time;   // Time of opening last bar
      uint              m_retcode;        // Result code of detecting new bar
      int               m_new_bars;       // Number of new bars
      string            m_comment;        // Comment of execution
      
   public:
      void              CisNewBar();      // CisNewBar constructor
      //--- Methods of detecting new bars:
      bool              isNewBar(datetime new_Time); // First type of request for new bar
  };
   
//+------------------------------------------------------------------+
//| First type of request for new bar                     |
//| INPUT:  newbar_time - time of opening (hypothetically) new bar   |
//| OUTPUT: true   - if new bar(s) has(ve) appeared                  |
//|         false  - if there is no new bar or in case of error      |
//| REMARK: no.                                                      |
//+------------------------------------------------------------------+
bool CisNewBar::isNewBar(datetime newbar_time)
  {
   //--- Initialization of protected variables
   m_new_bars = 0;      // Number of new bars
   m_retcode  = 0;      // Result code of detecting new bar: 0 - no error
   m_comment  =__FUNCTION__+" Successful check for new bar";
   //---
   
   //--- Just to be sure, check: is the time of (hypothetically) new bar m_newbar_time less than time of last bar m_lastbar_time? 
   if(m_lastbar_time>newbar_time)
     { // If new bar is older than last bar, print error message
      m_comment=__FUNCTION__+" Synchronization error: time of previous bar "+TimeToString(m_lastbar_time)+
                                                  ", time of new bar request "+TimeToString(newbar_time);
      m_retcode=-1;     // Result code of detecting new bar: return -1 - synchronization error
      return(false);
     }
   //---
        
   //--- if it's the first call
   if(m_lastbar_time==0)
     {  
      m_lastbar_time=newbar_time; //--- set time of last bar and exit
      m_comment   =__FUNCTION__+" Initialization of lastbar_time = "+TimeToString(m_lastbar_time);
      return(false);
     }   
   //---

   //--- Check for new bar:
   if(m_lastbar_time<newbar_time)       
     { 
      m_new_bars=1;               // Number of new bars
      m_lastbar_time=newbar_time; // remember time of last bar
      return(true);
     }
   //---
   
   //--- if we've reached this line, then the bar is not new; return false
   return(false);
  }

En regardant le code source de notre classe, vous avez probablement remarqué que nous avons pris en compte le suivi des erreurs d'exécution et que nous avons introduit une variable stockant le nombre de nouvelles barres.

Tout va bien, mais notre méthode universelle isNewBar(datetime newbar_time) présente un inconvénient majeur. Cet inconvénient est que nous devons toujours nous soucier de calculer le temps de (hypothétiquement) newbar_time dans le code source de notre expert ou indicateur.  

Heureusement, dans certains cas, nous pouvons vous simplifier la vie en confiant cette fonction à la nouvelle méthode supplémentaire de notre classe. Pour les périodes et les symboles standard dans notre fonction prototype, cela peut être fait en utilisant la deuxième version de la fonction SeriesInfoInteger() avec le modificateur SERIES_LASTBAR_DATE, et dans tous les autres cas - en utilisant la méthode générique. Alors, voici ce que j'ai :

//+------------------------------------------------------------------+
//| Second type of request for new bar                               |
//| INPUT:  no.                                                      |
//| OUTPUT: m_new_bars - Number of new bars                          |
//| REMARK: no.                                                      |
//+------------------------------------------------------------------+
int CisNewBar::isNewBar()
  {
   datetime newbar_time;
   datetime lastbar_time=m_lastbar_time;
      
   //--- Request time of opening last bar:
   ResetLastError(); // Set value of predefined variable _LastError as 0
   if(!SeriesInfoInteger(m_symbol,m_period,SERIES_LASTBAR_DATE,newbar_time))
     { // If request has failed, print error message:
      m_retcode=GetLastError();  // Result code of detecting new bar: write value of variable _LastError
      m_comment=__FUNCTION__+" Error when getting time of last bar opening: "+IntegerToString(m_retcode);
      return(0);
     }
   //---
   
   //---Next use first type of request for new bar, to complete analysis:
   if(!isNewBar(newbar_time)) return(0);
   
   //---Correct number of new bars:
   m_new_bars=Bars(m_symbol,m_period,lastbar_time,newbar_time)-1;
   
   //--- If we've reached this line - then there is(are) new bar(s), return their number:
   return(m_new_bars);
  }

Alors, qu'avons-nous en ce moment ? Maintenant, pour les périodes standard, nous n'avons pas besoin de nous soucier de déterminer l'heure d'ouverture de la dernière barre inachevée. Nous avons abordé notre fonction prototype avec son appel simple et sans les carences qu'elle présentait. Et même obtenu des avantages supplémentaires, y compris des codes d'erreur, des commentaires d'exécution et le nombre de nouvelles barres.   

Reste-t-il quelque chose ? Oui Il y a le dernier moment - l'initialisation. Pour cela, nous utiliserons le constructeur de classe et plusieurs méthodes d’ensemble. Notre constructeur de classe ressemble à ceci :  

//+------------------------------------------------------------------+
//| CisNewBar constructor.                                           |
//| INPUT:  no.                                                      |
//| OUTPUT: no.                                                      |
//| REMARK: no.                                                      |
//+------------------------------------------------------------------+
void CisNewBar::CisNewBar()
  {
   m_retcode=0;         // Result code of detecting new bar
   m_lastbar_time=0;    // Time of opening last bar
   m_new_bars=0;        // Number of new bars
   m_comment="";        // Comment of execution
   m_symbol=Symbol();   // Symbol name, by default - symbol of current chart
   m_period=Period();   // Chart period, by default - period of current chart
  }

Et des méthodes Set comme celle-ci :

//--- Methods of initializing protected data:
void              SetLastBarTime(datetime lastbar_time){m_lastbar_time=lastbar_time;                            }
void              SetSymbol(string symbol)             {m_symbol=(symbol==NULL || symbol=="")?Symbol():symbol;  }
void              SetPeriod(ENUM_TIMEFRAMES period)    {m_period=(period==PERIOD_CURRENT)?Period():period;      }

Grâce au constructeur de classe, nous n'avons pas besoin de faire attention à l'initialisation du symbole et de la période du graphique en cours. Comme dans la fonction prototype, elles seront utilisées par défaut. Mais si nous devons utiliser un autre symbole ou une autre période de graphique, nous pouvons utiliser pour cela nos méthodes Set créées. De plus, en utilisant SetLastBarTime(datetime lastbar_time), vous pouvez recréer la condition du "premier appel".

En conclusion, créons plusieurs Get-methods pour obtenir des données de notre classe en Expert Advisor et Indicateurs : 

      //--- Methods of access to protected data:
uint              GetRetCode()     const  {return(m_retcode);     }  // Result code of detecting new bar 
datetime          GetLastBarTime() const  {return(m_lastbar_time);}  // Time of opening last bar
int               GetNewBars()     const  {return(m_new_bars);    }  // Number of new bars
string            GetComment()     const  {return(m_comment);     }  // Comment of execution
string            GetSymbol()      const  {return(m_symbol);      }  // Symbol name
ENUM_TIMEFRAMES   GetPeriod()      const  {return(m_period);      }  // Chart period

Maintenant, nous pouvons obtenir toutes les informations nécessaires dans nos programmes mql5. Pour l'instant, nous pouvons mettre un point final à la création de la classe CisNewBar.

Le code source complet de notre classe se trouve dans le fichier joint Lib CisNewBar.mqh.

Exemples d'utilisation de la classe CisNewBar

Je vous propose d’examiner les exemples d'utilisation de nos classes afin d'entrer dans toutes les subtilités de ce que nous avons créé. Peut-être qu'il peut y avoir non seulement des avantages mais aussi des inconvénients.

Exemple 1. Pour commencer, créons un Expert Advisor absolument identique pour la fonction isNewBar() à partir de l'article Limitations et vérifications dans les Expert Advisors :

//+------------------------------------------------------------------+
//|                                               Example1NewBar.mq5 |
//|                                            Copyright 2010, Lizar |
//|                                               Lizar-2010@mail.ru |
//+------------------------------------------------------------------+
#property copyright "Copyright 2010, Lizar"
#property link      "Lizar-2010@mail.ru"
#property version   "1.00"

#include <Lib CisNewBar.mqh>

CisNewBar current_chart; // instance of the CisNewBar class: current chart

//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
  {
//---
   if(current_chart.isNewBar()>0)
     {     
      PrintFormat("New bar: %s",TimeToString(TimeCurrent(),TIME_SECONDS));
     }
  }

Exécutons les deux Expert Advisors sur des graphiques avec la même paire et la même période. Voyons ce que nous avons :


Tout d'abord, les deux Expert Advisors signalent de manière synchronisée la nouvelle barre. Puis ils se taisent et seulement quatre minutes plus tard, ils informent qu'il y a uen nouvelle barre (ce temps est marqué comme 1). C'est bon - je viens de me déconnecter d'Internet pendant quelques minutes et j'ai décidé de voir ce qui se passera. Malgré le fait que peu de barres se soient formés, nous n'avons pas reçu cette information. Dans notre nouvel Expert Advisor, nous pouvons corriger cet inconvénient, puisque notre méthode isNewBar() permet de faire une telle chose.

Ensuite, j'ai changé la période du graphique en M2. La réaction des Expert Advisors a été différente. CheckLastBar a commencé à signaler une nouvelle barre toutes les 2 minutes, et Example1NewBar indique les nouvelles barres chaque minute, comme si la période n'avait pas été modifiée (marquée comme 2).

Le fait que notre instance current_chart a été initialisée par le constructeur de classe lorsque Expert Advisor a été attaché au graphique. Lorsque vous modifiez la période d'Expert Advisor, déjà attachée au graphique, le constructeur de classe ne démarre pas et Expert Advisor continue de travailler avec la période M1. Cela nous indique que notre instance de classe vit sa propre vie et qu'elle n'est pas affectée par les changements d'environnement. Cela peut être à la fois pour et contre - tout dépend des tâches.  

Pour que notre Expert Advisor agisse en tant que CheckLastBar, nous devons initialiser les variables de classe protégées m_symbol et m_period dans la fonction OnInit(). Faisons le.

Exemple 2. Présentons quelques ajouts à notre Expert Advisor et comparons à nouveau ses performances avec CheckLastBar. Le code source de l'Expert Advisor est joint en tant que fichier Example2NewBar.mq5. Exécutez Expert Advisors sur des graphiques avec la même paire et la même période. Créons-leur les mêmes obstacles que la dernière fois. Voyons ce que nous avons :


Comme la dernière fois, les Expert Advisors signalent d'abord de manière synchronisée les nouvelles barres. Puis je me déconnecte d'Internet pendant quelques minutes... Ensuite, allumez-le. Notre nouveau conseiller expert a non seulement signalé sur une nouvelle barre, mais également le nombre d'entre elles (marquées 1). Pour la plupart des indicateurs et des experts, ce nombre indiquera le nombre de barres non calculées. Ainsi, nous avons une bonne base pour des algorithmes de recalcul avantageux.  

Ensuite, j'ai changé la période des graphiques en M2. Contrairement à l'exemple 1, Expert Advisor fonctionne de manière synchronisée (marqué comme 2). L'initialisation des variables de classe protégées m_symbol et m_period dans la fonction OnInit() a aidé ! Lors du changement de symbole (marqué comme 3), les Expert Advisors fonctionnent également de la même manière. 

Exemple 3. Dans notre classe CisNewBar, nous avons mis la possibilité de traquer les erreurs. Il peut arriver qu'Expert Advisor soit conçu pour qu'il n'y ait pas besoin de traquer les erreurs. Eh bien, alors n'utilisez pas cette possibilité. Nous allons essayer de créer artificiellement une condition où l'erreur est possible, et essayer de l'attraper. Pour cela nous allons légèrement compléter le code source d'Expert Advisor (le fichier Example3NewBar.mq5).

Que ferai-je? Comme d'habitude, je vais exécuter Example3NewBar sur des graphiques en minutes. Ensuite, je commencerai à modifier les instruments du graphique dans l'espoir qu'une condition se produira où le terminal n'aura pas le temps de construire des séries temporelles avant la demande de l'Expert Advisor. En général, je vais tourmenter le terminal client et voir ce qui va se passer...  

Après plusieurs tentatives, notre Expert Advisor a détecté une erreur :

 

Maintenant, nous pouvons dire avec certitude que nous sommes capables de détecter les erreurs d'exécution. Comment les gérer - est une question de goût. Notez que nous avons traqué cette erreur quatre fois. Lorsque le téléchargement est terminé et que le graphique est formé, l'Expert Advisor a suggéré que nous n'avions raté qu'une seule barre.

d’ailleurs, ceux qui ont regardé le code source d'Expert Advisor ont peut-être remarqué qu'il est logique de vérifier les erreurs uniquement lorsque la méthode isNewBar() renvoie une valeur inférieure ou égale à zéro.

Avertissement Si au cours de cette expérience, vous commencez à modifier la période du graphique, alors lorsque vous modifiez la période du graphique de petite à plus grande, vous obtiendrez une erreur de synchronisation. En effet, l'heure d'ouverture de la barre (par exemple) de H1 est antérieure à M1 dans 59 cas. Pour éviter cette erreur lors de la commutation de période du graphique, vous devez initialiser correctement la variable m_lastbar_time dans la fonction OnInit() avec la méthode SetLastBarTime (datetime lastbar_time).

Exemple 4. Dans cet exemple compliquons la tâche d'Expert Advisor. Prenez trois paires de monnaie : EURUSD sur M1, GBPUSD sur M1 et USDJPY sur M2. Le graphique avec la première paire sera à jour, et sur celui-ci, nous surveillerons simplement une nouvelle barre. Par la deuxième paire, nous calculerons le nombre de barres formées après le démarrage d'Expert Advisor. Nous compterons jusqu'à ce que la première paire signalera qu'il y a une nouvelle barre. Et sur la troisième paire nous allons constamment (quand une barre apparaît sur l'EURUSD) effectuer l'initialisation de la variable de classe protégée m_lastbar_time. Le code source de l'Expert Advisor est joint en tant que fichier Example4NewBar.mq5.

En créant cet exemple, je souhaite découvrir comment notre classe CisNewBar fonctionnera en mode multi-devises. Bon, je le commence... Voici ce que j'ai :


Les résultats soulèvent des questions. Je vais ajouter de l'huile sur le feu et exécuter cet intervalle de temps dans le Testeur de Stratégie Résultats du Testeur de Stratégie :


Ensuite, vous pouvez jouer au jeu "trouver dix différences". En plus des bizarreries du travail d'Expert Advisor sur le compte de démonstration, il est évident qu'il existe des différences entre le compte de démonstration et Testeur de Stratégie - et elles sont clairement visibles. Une comparaison similaire avec la bonne approche révélera non seulement les inconvénients d'Expert Advisor, mais permettra également de les éliminer. Peut-être que je ne vais pas analyser pourquoi cela s'est produit, comment cela s'est produit et ce qui doit être corrigé dans Expert Advisor.  

Exemple 5. Dans les exemples où nous n'avons jamais explicitement utilisé la méthode la plus universelle pour détecter une nouvelle barre - isNewBar(datetime newbar_time). Pour ce faire, je vais prendre le chandelier tick de l'article Créer des indicateurs de tiques dans MQL5 et ajouter un tampon pour stocker l'heure d'ouverture de la barre (fichier TickColorCandles v2.00.mq5). J'écrirai un très court Expert Advisor qui parlera sur l'époque du nouveau chandelier en teck (fichier Example5NewBar.mq5) :

#property copyright "Copyright 2010, Lizar"
#property link      "Lizar-2010@mail.ru"
#property version   "1.00"

#include <Lib CisNewBar.mqh>

CisNewBar newbar_ind; // instance of the CisNewBar class: detect new tick candlestick
int HandleIndicator;  // indicator handle
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- Get indicator handle:
   HandleIndicator=iCustom(_Symbol,_Period,"TickColorCandles v2.00",16,0,""); 
   if(HandleIndicator==INVALID_HANDLE)
     {
      Alert(" Error when creating indicator handle, error code: ",GetLastError());
      Print(" Incorrect initialization of Expert Advisor. Trade is not allowed.");
      return(1);
     }

//--- Attach indicator to chart:  
   if(!ChartIndicatorAdd(ChartID(),1,HandleIndicator))
     {
      Alert(" Error when attaching indicator to chart, error code: ",GetLastError());
      return(1);
     }
//--- If you passed until here, initialization was successful
   Print(" Successful initialization of Expert Advisor. Trade is allowed.");
   return(0);
  }
//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
  {
//---
   double iTime[1];

//--- Get time of opening last unfinished tick candlestick:
   if(CopyBuffer(HandleIndicator,5,0,1,iTime)<=0)
     {
      Print(" Failed to get time value of indicator. "+
            "\nNext attempt to get indicator values will be made on the next tick.",GetLastError());
      return;
     }
//--- Detect the next tick candlestick:
   if(newbar_ind.isNewBar((datetime)iTime[0]))
     {
      PrintFormat("New bar. Opening time: %s  Time of last tick: %s",
                  TimeToString((datetime)iTime[0],TIME_SECONDS),
                  TimeToString(TimeCurrent(),TIME_SECONDS));
     }
  }

Vous avez sûrement remarqué comment nous obtenons le temps d'ouverture du chandelier. Très facile, n'est-ce pas ? Je mets l'indicateur et Expert Advisor dans leurs dossiers, compile et exécute Expert Advisor. Cela fonctionne, et voici les résultats :  

 

Gestionnaire d' Événements "Nouvelle barre"


En approchant de la fin de cet article, je voudrais partager une autre idée. Sur Forum (en russe), il y avait une idée qu'il serait bien d'avoir un gestionnaire d'événements "nouveau bar" standard. Peut-être qu'une fois que les développeurs y arriveront, mais peut-être pas. Mais la beauté de MQL5 c'est qu'il est possible de mettre en œuvre les idées les plus étonnantes avec élégance et simplicité.

Si vous souhaitez avoir un gestionnaire d'événements "new bar" (ou NewBar), créons-le ! D'autant plus que nous pouvons attraper cet événement maintenant avec facilité, en utilisant notre classe. Voici à quoi ressemblera notre expert (avec le gestionnaire d'événements NewBar OnNewBar()) :

#property copyright "Copyright 2010, Lizar"
#property link      "Lizar-2010@mail.ru"
#property version   "1.00"

#include "OnNewBar.mqh" // Here is the secret of launching the "new bar" event handler

//+------------------------------------------------------------------+
//| New bar event handler function                                   |
//+------------------------------------------------------------------+
void OnNewBar()
  {
   PrintFormat("New bar: %s",TimeToString(TimeCurrent(),TIME_SECONDS));
  }

Ça a l'air plutôt bien. Notre Expert Advisor a l'air très simple. Ce gestionnaire imprime une chaîne sur la nouvelle barre. C'est tout ce qu'il fait. Pour comprendre comment suivre l'événement NewBar et comment exécuter le gestionnaire, vous devez consulter le fichier OnNewBar.mqh :

#property copyright "Copyright 2010, Lizar"
#property link      "Lizar@mail.ru"

#include <Lib CisNewBar.mqh>
CisNewBar current_chart; // instance of the CisNewBar class: current chart

//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
  {
//---
   int period_seconds=PeriodSeconds(_Period);                     // Number of seconds in current chart period
   datetime new_time=TimeCurrent()/period_seconds*period_seconds; // Time of bar opening on current chart
   if(current_chart.isNewBar(new_time)) OnNewBar();               // When new bar appears - launch the NewBar event handler
  }

Comme vous pouvez le constater, ici il n'y a rien de compliqué aussi. Mais il y a quelques moments sur lesquels j'aimerais attirer votre attention :

premier. Comme vous l'avez remarqué, j'utilise la fonction TimeCurrent() pour calculer l'heure d'ouverture de la barre et j'utilise la première méthode de vérification de l'événement NewBar de notre classe. C'est un plus sympa. Cela réside dans le fait qu'une telle méthode ne nécessite aucun traitement d'erreur, comme lors de l'utilisation de SeriesInfoInteger() avec le modificateur SERIES_LASTBAR_DATE. Pour nous, c'est important, car notre gestionnaire OnNewBar() doit être aussi fiable que possible.

seconde L'utilisation de la fonction TimeCurrent() pour calculer l'heure d'ouverture de la barre est le moyen le plus rapide. L'utilisation de la fonction SeriesInfoInteger(), même sans contrôle d'erreur, dans le même but est une méthode plus lente.

Le résultat de notre gestionnaire :

   

Conclusion

  Au cours de la présentation des matières, nous avons fait une bonne analyse des moyens de détecter une nouvelle barre. Nous avons exposé les avantages et les inconvénients des méthodes existantes de détection de nouvelles barres. Sur la base de ce que nous avions, nous avons créé la classe CisNewBar, permettant sans frais supplémentaires de programmation pour attraper l'événement "nouvelle barre" dans presque toutes les tâches. En même temps, nous avons éliminé la plupart des inconvénients des solutions précédentes.    

Ces exemples nous ont aidés à comprendre les avantages et les inconvénients des méthodes que nous avons inventées. Une attention particulière en termes de travail correct nécessite le mode multi-devises. Vous devez conduire une analyse approfondie des dysfonctionnements identifiées et élaborer des moyens de les résoudre.

  Le gestionnaire d'événements "nouvelle barre" créé ne convient qu'aux Expert Advisors à devise unique. Mais nous avons appris à utiliser le moyen le plus fiable et le plus rapide à cette fin. Vous pouvez maintenant avancer et créer un gestionnaire d'événements NewBar multi-devises. Mais c'est le sujet d'un autre article.  

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

Gaz neuronal en croissance : Implémentation en MQL5 Gaz neuronal en croissance : Implémentation en MQL5
L'article étale un exemple de comment élaborer un programme MQL5 mettant en implémentant l'algorithme adaptatif de groupement appelé Growing neural gas (GNG). L'article est destiné aux utilisateurs qui ont étudié la documentation du langage et qui ont certaines compétences en programmation et des connaissances de base dans le domaine de la neuro-informatique.
Simulink : un guide pour les développeurs d'Expert Advisors Simulink : un guide pour les développeurs d'Expert Advisors
Je ne suis pas un programmeur professionnel. Et donc, le principe de « passer du simple au complexe » est d’une importance capitale pour moi lorsque je travaille sur l’élaboration d’un système de trading. Qu’est-ce qui est précisément simple pour moi? Tout d’abord, il s’agit de la visualisation du processus de création du système et la logique de son travail. En outre, il s’agit d’un minimum de code manuscrit. Dans cet article, je tenterai de créer et de tester le système de trading, basé sur un package Matlab, puis écrire un Expert Advisor pour MetaTrader 5. Les données historiques de MetaTrader 5 seront utilisées pour le processus de test.
Assistant MQL5 : Création d'Expert Advisors sans programmation Assistant MQL5 : Création d'Expert Advisors sans programmation
Souhaitez-vous tester une stratégie de trading sans perdre de temps en programmation ? Dans l'assistant MQL5, vous pouvez simplement sélectionner le type de signaux de trading, ajouter des modules de positions de suivi et de gestion de l'argent - et votre travail est terminé ! Créez vos propres implémentations de modules ou commandez-les via le service Jobs - et combinez vos nouveaux modules avec ceux existants.
Recherche d'erreurs et journalisation Recherche d'erreurs et journalisation
MetaEditor 5 dispose de la fonctionnalité de débogage. Mais lorsque vous écrivez vos programmes MQL5, vous souhaitez souvent afficher non pas les valeurs individuelles, mais tous les messages qui apparaissent lors des tests et du travail en ligne. Lorsque le contenu du fichier journal est de grande taille, il est évident d'automatiser la récupération rapide et facile du message requis. Dans cet article, nous examinerons les moyens de trouver des erreurs dans les programmes MQL5 et les méthodes de collecte de données. Nous simplifierons également la connexion aux fichiers et apprendrons à connaître un programme simple LogMon pour une visualisation confortable des fichiers journaux