English Русский 中文 Español Deutsch 日本語 Português 한국어 Italiano Türkçe
L’implémentation d'un mode multi-devises dans MetaTrader 5

L’implémentation d'un mode multi-devises dans MetaTrader 5

MetaTrader 5Exemples | 12 janvier 2022, 14:26
241 0
Konstantin Gruzdev
Konstantin Gruzdev

Introduction

Actuellement, il existe un grand nombre de systèmes de trading multi-devises, d'indicateurs et d’Expert Advisors élaborés Néanmoins, les développeurs continuent de faire face aux problèmes spécifiques du développement de systèmes multi-devises.

Avec la sortie du terminal client MetaTrader 5 et du langage de programmation MQL5, nous avons eu une nouvelle opportunité d’implémenter un mode multi-devises àpart entière et, par conséquent, des robots et des indicateurs multi-devises plus efficaces. Ces nouvelles opportunités seront le sujet de cet article.


Aperçu de l'approche conventionnelle

Dans notre cas, l'approche conventionnelle est la tentative d'implémenter un système multi-devises axé sur les fonctions standardOnTick() and OnCalculate(), qui ont remplacé la fonction start() de MQL4. En termes simples, avec l'apparition d'une nouvelle coche ou d'une nouvelle barre sur le graphique actuel, toutes les paires de devises (participant au système multi-devises) sont successivement demandées pour une analyse et une prise de décision ultérieures.

Les défis de cette approche sont :

  1. La dépendance de l'ensemble du système sur l'arrivée de ticks sur un symbole de trading de l’actuel graphique.

    Dans un marché rapide, lorsque les ticks du symbole de l’actuel graphique sont fréquents, il n'y a pas vraiment de problèmes. Mais dans un marché lent, par exemple, pendant la nuit, les tiques peuvent arriver très rarement : une fois toutes les demi-minutes ou même plus rares. Pendant les intervalles entre l'arrivée de ticks rares, l'ensemble du système multi-devises est "en sommeil", bien que les changements se produisant sur les autres symboles puissent être assez radicaux.

    Cette lacune n'est pas considérable si le système est configuré pour fonctionner sur de grandes échelles de temps. Mais plus le délai est court, plus il a d'effet. Chaque jour, le monde augmente la vitesse, les ordinateurs sont capables de traiter de plus en plus d'informations par unité de temps et, par conséquent, de plus en plus de personnes sont prêtes à travailler sur de courtes périodes et même sur des ticks.

  2. La complexité de synchronisation des données de l’historique sur tous les symboles dans un système multi-devises

    "Dans MetaTrader 4, seules les barres sont tracées, dans lesquelles au moins un changement de prix a eu lieu. Si aucun changement de prix ne s'est produit dans une minute, un écart d'une barre se produira dans le graphique avec une période d'une minute. "- indique le début de l'article"Free-of-Holes" Charts.

    Une telle approche pour créer un graphique est maintenue dans MetaTrader 5. C'est-à-dire que le même nombre de barres sur le graphique pour chaque symbole n’indique pas qu'ils sont synchronisés par l'heure. Par exemple, la centième barre peut avoir une heure d'ouverture différente pour chaque symbole. Par conséquent, lors de la construction et du recalcul d'un indicateur multi-devises, il est important de s'assurer que toutes les barres sont cohérentes entre elles pour chaque symbole.

    Ceci n'est toujours pas considérable si le système est configuré pour fonctionner sur une large période de temps, car avec l'augmentation de la période, la probabilité de manquer une barre diminue considérablement. Bien que, comme ils disent, vous ne pouvez jamais être trop prudent. Et vous ne pouvez jamais le dire, seulement si la personne ne fait rien.

    Il faut séparément noter le temps de synchronisation de l’ actuelle barre incomplète. Lorsqu'une nouvelle barre apparaît sur le graphique actuel, cela n’indique pas que de nouvelles barres se sont également formées sur d'autres symboles. Par conséquent, une tentative de demander le prix d'une nouvelle barre via un autre symbole, en utilisant les fonctions CopyXXXX(), peut conduire au fait que vous obtiendrez le prix de la barre précédente pour le symbole, ou simplement une erreur de copie. Par ailleurs, une nouvelle barre sur un autre symbole peut se former beaucoup plus tôt que sur l'actuelle. Cela peut également affecter la précision de l'évaluation de la situation.

    L'article Création d'un indicateur multi-devises à l'aide d'un nombre de tampons d'indicateurs intermédiaires décrit les options qui règlent plus ou moins le problème de la synchronisation des données historiques.

  3. Autre point important, relatif à la synchronisation des données : comment savoir qu'il y a eu une mise à jour de l'historique pour un symbole de trading ?

    Par exemple, lorsque nous construisons un indicateur de devise unique, il n'y a pas de problèmes. Si la variable d'entrée prev_calculated de la fonction OnCalculate() est mise à zéro, alors nous recalculons l'indicateur. Mais que faisons-nous s'il y a eu une mise à jour dans l'historique du symbole, et non sur le graphique actuel ? Cela peut se produire à tout moment et il peut être nécessaire de recalculer l'indicateur multi-devise. La réponse à cette question est tout à fait pertinente.

Sans même entrer dans les autres caractéristiques, nous pouvons constater que ces trois instances sont suffisantes pour causer tellement de problèmes que le code de l'EA ou de l'indicateur multi-devises devient trop volumineux. Mais toute l'étendue du problème n'a pas été réglée...


Nouvel espoir avec la fonction OnTimer()

La nouvelle capacité du programme MQL à générer l'événement Timer et le gestionnaire d'événements standard OnTimer()ont donné de l'espoir pour l'émergence de nouveaux types de systèmes multi-devises. Ceci est dû, d'une part, au fait que désormais l'Expert Advisor/indicateur multi-devises pourrait devenir indépendant de la réception des ticks par le symbole du graphique en cours, et d'autre part, au fait que l'on pourrait surveiller le travail de l'AE dans le temps. Mais ...

Cela résout en partie les problèmes décrits au paragraphe 1 de la section précédente et, bien entendu, offre certains avantages. Mais tout comme lorsque vous recevez l'événementNewTick , avec la réception de l'événementTimer , il est nécessaire de renseigner successivement sur toutes les paires de devises pour tenter de suivre les changements. Parfois, cela doit être fait assez souvent, ce qui peut augmenter considérablement la consommation des ressources des programmes MQL5.

Les problèmes identifiés aux paragraphes 2 et 3 restent au même niveau de solution. En plus de cela, il est nécessaire de résoudre les problèmes spécifiques à la fonctionOnTimer() . Par exemple, les problèmes d'initialisation/désinitialisation du chronomètre, son fonctionnement durant le week-end, etc.

Sans minimiser les avantages évidents du gestionnaire d'événementsOnTimer() , il faut noter qu'il ne permet toujours pas d'implémenter un mode multi-devises complet.


De nouvelles possibilités avec la fonction OnChartEvent()

Les limitations des fonctions standard ci-dessus sont dues à leur spécialisation étroite : elles sont destinées à gérer des événements spécifiques et prédéfinis, et non à des systèmes multi-devises. 

Un élément de sauvetage pour le développeur d'un système multi-devises peut être le gestionnaire d'événements personnalisé standardOnChartEvent() . Il permet au programmeur de générer ses propres événements à sa discrétion.

Par exemple, personne ne peut nous empêcher d'utiliser cette fonction pour obtenir des ticks pour n'importe quel symbole sur l’actuel graphique.. Tout ce que nous devons faire est d'envoyer un "espion" sur le graphique avec le symbole approprié.

Par le drôle de mot "espion", j'entends l'indicateur suivant :

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

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

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

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

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

Nous pouvons lancer cet "espion" dans le graphique du symbole souhaité, puis gérer ses messages dans l'EA ou l'indicateur à l'aide de la fonctionOnChartEvent() . Pour décoder correctement les messages de "l'espion", il faut interpréter les paramètres de cette fonction de la manière suivante :

  • id - identifiant d'événement. Si l'id-CHARTEVENT_CUSTOM = 0, notre ""espion" signale que la variable prev_calculated a été mise à zéro et que les actions appropriées doivent être prises ;
  • lparam - dans ce cas, il s'agit de la période du graphique, sur lequel l'"espion" est lancé ;
  • dparam - cochez le prix. Par défaut, il s'agit du dernier prix de clôture. Bien que lors du lancement de "l'espion", cela puisse être défini sur n'importe quelle valeur de l'énumération ENUM_APPLIED_PRICE ;
  • sparam - le nom du symbole du trading sur lequel l'événement a été reçu.

Pour démontrer le travail simultané de plusieurs "espions", nous allons écrire un simple EA exSpy.mq5 (la version complète est disponible dans l'archive) :

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

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

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

Lancez l'Expert Advisor sur n'importe quel graphique.

Voici les résultats


 

Comme on peut le lire sur le journal, nous obtenons tous les ticks du symbole souhaité, ainsi que l'événement « initialisation », qui sera notamment reçu s'il y a une mise à jour ou un téléchargement de l'historique.

Résumons les résultats intermédiaires :

  • Nous ne dépendons pas des graduations du symbole particulier, comme nous le faisions lorsque nous utilisions les fonctions standard OnTick() etOnCalculate()
  • Nous ne sommes pas limités à des intervalles de temps strictement définis, comme lors de l'utilisation deOnTimer().
  • Il n'est nul besoin de demander tous les symboles d'une séquence ou d'une boucle pour suivre leurs modifications. Une fois les changements sur le symbole intervenus, nous obtenons l'événement correspondant. Nous pouvons identifier le symbole, sur lequel l'événement s'est produit, par l'identifiant d'événement id ou par le paramètre sparam.
  • Nous avons résolu le problème du suivi de la synchronisation des données de l’historique avec le serveur pour chaque symbole individuellement.
  • Une petite partie du travail est affectée à des programmes supplémentaires. Ceci est pertinent pour la parallélisation du traitement des données.
  • Dans les paramètres de la fonctionOnChartEvent() , nous obtenons des informations supplémentaires : la période, le prix, et le nom du symbole. Cela peut considérablement simplifier le code de l' EA ou de l'indicateur, car il n'est pas nécessaire de se renseigner davantage sur ces données.

À ce stade, nous pourrions essentiellement conclure cet article, car nous avons implémenté avec succès un mode multi-devises au moyen du terminalMetaTrader 5 et du langage de programmation MQL5. Mais il s'agit d'une implémentation "brute". Par conséquent, nous irons plus loin.


L’implémentation d'un mode multi-devises

Si vous appliquez l'idée ci-dessus d’implémentation d’un régime multi-devises dans sa forme pure, et puis à un certain moment vous commencerez à éprouver des difficultés. Le problème est qu'une telle approche nous permet d'obtenir tous les ticks pour chaque symbole de trading, sur lequel "l'espion" s'exécute.

En une seconde, avec un marché rapide, il peut y avoir un certain nombre de ticks sur chaque symbole. Cela peut conduire à un "blocage" de l'ordre de l'événement. Voici un avertissement de la sectionHelp  :

Terminal client ajoute des évènements se manifestant dans la file d'attente des événements. Ainsi, les événements sont traités les uns après les autres conformément à l'ordre de réception. Il y a une exception pour l'événement NewTick. Si la file d'attente a déjà un tel événement ou cet événement est en cours de traitement, le nouvel événement NewTick n'est pas mis en file d'attente.

La file d'attente des événements est limitée dans Taille : En cas de débordement de la file d'attente, les anciens événements sont supprimés sans être traités afin de permettre la réception de nouveaux événements. Il est donc recommandé d'écrire des gestionnaires d'événements efficaces, et il n'est pas recommandé d'utiliser des boucles infinies (il existe une exception pour les scripts, qui gèrent l'événement Début uniquement).

Le débordement de la file d'attente peut entraîner la perte d'événements importants pour l'indicateur multi-devises ou EA. C'est d'une part D'autre part, nous n'avons pas toujours besoin de coches pour tous les symboles. Parfois, nous avons besoin d'obtenir uniquement l'événement "nouvelle barre" sur une période donnée. Ou un certain nombre d'événements de "nouvelle barre" pour différentes périodes. Fondamentalement, notre "espion" n'est pas adapté à de telles exigences et son utilisation n'est pas très pratique.

Rendons-le universel, de sorte que nous n'ayons jamais à revenir à la question de savoir comment obtenir un événement axé sur le symbole d'un EA ou d'un indicateur multi-devises. Pour cela, prenons comme exemple l'énumération des événements ENUM_CHART_EVENT_SYMBOL du " Panneau de configuration MCM " pour la description des Expert Advisors et Indicateurs Multidevises :

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

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

En fait, cette énumération est un indicateur des événements de graphique personnalisés. C'est l'ensemble minimal, qui peut être nécessaire pour le mode multi-devises. Bien sûr, il peut être complété. La combinaison de drapeaux déterminera les événements que nous allons envoyer depuis "l'espion".

Les drapeaux peuvent être combinés à l'aide de l'opération au niveau du bit "OU". Par exemple, le CHARTEVENT_NEWBAR_M1 | La combinaison CHARTEVENT_NEWBAR_H1 indiquera que nous allons envoyer les événements "nouvelle barre" à partir de la minute et de l'heure à l'aide de l'"espion". Ces drapeaux seront le paramètre d'entrée de notre indicateur-espion De plus, nous l'appellerons "l'agent-indicateur".

L'indicateur lui-même, conformément aux nouvelles idées, ressemble à ceci:

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

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

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

MqlDateTime time, prev_time;

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

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

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

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

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

Cet indicateur fait partie du Panneau de contrôle MCM nous ne l'avons pas renommé, les pièces jointes en contiennent simplement une version mise à jour (voir « Panneau de contrôle espion MCM.mq5 »). Mais cela ne veut pas dire qu'il ne peut pas être utilisé séparément du panneau.

Cet indicateur-agent génère des événements utilisateur personnalisés et les transmet au destinataire du graphique pour un traitement ultérieur de ces événements dans l'EA ou l'indicateur, à l'aide de la fonctionOnChartEvent() Maintenant, les paramètres d'entrée de cette fonction doivent être interprétés comme suit :

  • id - identifiant d'événement ;
  • lparam - indicateur de l'événement, reçu de l'agent du panel. Les indicateurs, correspondant à l'énumération ENUM_CHART_EVENT_SYMBOL ;
  • dparam - le prix-tick ou le prix d'ouverture d'une nouvelle barre pour une période de temps spécifique ;
  • sparam - le nom du symbole du trading sur lequel l'événement s'est produit.

 La démo EA n'a pas l'air plus compliquée que la précédente (la version complète est disponible dans l'archive) :

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

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

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

Les résultats du travail du panneau de contrôle exSpy MCM :


 

Comme vous pouvez le constater, nous recevons régulièrement tous les événements demandés.

Résumons encore une fois le résultat intermédiaire :

  • Grâce à la nouvelle version de l'agent-indicateur, nous avons conservé toutes nos réalisations précédentes.
  • Maintenant, à partir de nos programmes MQL multi-devises, nous pouvons spécifier au moins 23 événements, qui incluent les événements "nouveau tick", "nouvelle barre" et "initialisation".
  • Davantage de travail de l'EA/indicateur est également assigné aux agents, afin de les décharger. 

Eh bien, il s'est avéré qu'il n'est pas si difficile d'implémenter un mode multi-devises à part entière dansMetaTrader 5

De plus, je tiens à souligner quelques nuances.

Premier Tous les événements générés par les « agents » de notre EA/indicateur multi-devises sont externes. A ce propos, la question se pose : "Est-il nécessaire d'exécuter les agents directement depuis l'EA/l'indicateur ?". La réponse est non."

seconde Dans la fonctionOnChartEvent() , l'identifiant d'événement id semble être de réserve, car nous pouvons savoir pour quel symbole l'événement a été reçu par le paramètre sparam - le nom du symbole de trading. Par conséquent, nous pouvons peut-être l'utiliser à d'autres fins? La réponse est : "oui, nous pouvons." 

De tels arguments ont conduit à l'émergence du "Panneau de contrôle MCM" pour des Expert Advisors et Indicateurs multi-devises. C'est une sorte d'"intercalaire" entre le terminal et un EA/indicateur. Cela nous a fourni encore plus d'avantages et de flexibilité dans la configuration d'un environnement multi-devises :

  • Le panneau peut être installé en tant qu'indicateur séparé sur le graphique, puis attribuer les indicateurs multi-devises, compatibles avec le panneau.
  • Le panneau peut comporter des indicateurs et des EA en tant qu'unités constitutives. Il sera téléchargé avec eux.  
  • Nous pouvons activer/désactiver le symbole depuis la fenêtre « Veille du Marché » pour le trading ou l'analyse en utilisant le menu « Événements ». Dans la fonction OnChartEvent(), nous ne pouvons pas trouver le numéro de série du symbole, par l'identifiant de l'événement, dans la fenêtre "Veille du Marché".
  • Nous pouvons définir le mode de trading par ticks ou par l'événement "nouvelle barre" pour n'importe quelle période, et pour le symbole, sélectionné dans le "Veille du Marché". Tout cela se fait en utilisant le menu régulier.
  • Nous pouvons modifier toutes les configurations ci-dessus, sans décharger, arrêter ou entrer dans la fenêtre de propriétés de l'EA ou de l'indicateur.
  • Tout cela ne limite pas le potentiel de créativité dans la création d'indicateurs multi-devises et d'EA. De plus, nous n'avons désormais plus à intégrer dans notre code les sorties de ce panneau. La gestion des agents-indicateurs est désormais implémentée dans le panneau de contrôle.
  • La structure du système multi-devises est encore plus simple.


L'indicateur multi-devises RSI pour l'indice USDx dollar

Pour profiter de tous les avantages de la méthode ci-dessus, je propose d'implémenter la variante multi-devises de l'indicateur RSI pour l’index dollar USDx à l'aide duPanneau de contrôle MCM .

Pour commencer, je noterai quelques spécificités. Souvent, lorsque nous essayons d'analyser l’Indice Dollar , nous comptons simplement les indicateurs des lectures de l'indice. De mon point de vue, ce n'est pas tout à fait correct, puisque chaque symbole de l'indice du panier de paires de devises apporte sa propre contribution. Par conséquent, par exemple, calculons le RSI pour l'Indice dollar par la formule, similaire à la formule de calcul de l'indice :

C'est-à-dire que nous allons d'abord calculer le RSI pour une paire de devises particulière, puis lire le RSI pour l'indice, en tenant compte du poids des coefficients.

Le lecteur pourra noter qu'il existe un problème de synchronisation des données historiques de tous les symboles, utilisés dans le système multi-devises. (voir paragraphe 2 de la section « Présentation de l'approche conventionnelle »).

Ce problème a été résolu dans l'indicateur, en utilisant les fonctions de classe pour construire les tampons synchronisés RSI (fichier SynchronizedBufferRSI.mqh). Il ne sert à rien de fournir l'intégralité du code de la classe, il n'y a donc que des moments pertinents présentés ci-dessous. 

Tout d'abord, le tampon d'indicateur est défini dans la classe avec le modificateur d'accès public :

public:
   double   buffer[];   // indicator buffer

Deuxièmement, l'initialisation de l'indicateur se fait à l'aide de la méthode de classe :

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

Et troisièmement, pour chaque barre, la valeur du tampon indicateur est synchronisée avec l’ périodecourante , en utilisant la méthode de rafraîchissement de la classe :

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

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

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

Pour la synchronisation complète de tous les tampons indicateurs, nous devons utiliser une minuterie entière sans "trous", comme décrit dans l'articlesuivant. Mais pour cette méthode de synchronisation des tampons d'indicateurs, nous avons spécialement sélectionné l'intervalle de temps du graphe courant, puisque l'affichage de l'indicateur s'y fait.

Parlant de ma propre expérience, je peux dire que pour de courtes périodes, il est logique d'utiliser une telle méthode de synchronisation, pour toute série chronologique ou tampon d'indicateur, si leur symbole est différent du symbole sur le graphique actuel.

Le graphique affiche clairement pourquoi cela vaut la peine de le faire :


 

Pour des périodes plus longues, cela n'est généralement pas observé.

Et pour couronner le tout. Vous trouverez ci-dessous le code d'un gestionnaire d'événements utilisateur standard, qui est utilisé dans l'indicateur :

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

Particularités du code :

  • L'utilisation de l'identifiant d'événement id pour faire référence au tableau, comportant des pointeurs vers des instances de classe, qui sont destinés à calculer les tampons d'indicateur RSI, synchronisés avec l'intervalle de temps actuel. Cette approche simplifie considérablement la structure du code.
  • L'événement "Initialisation" est utilisé pour recalculer le tampon indicateur RSI uniquement pour la paire de devises, par laquelle il a été reçu, et non pour l'ensemble des six symboles. Comme mentionné précédemment, cela vous permet de synchroniser l'indicateur, par exemple, lors de la mise à jour de l'historique d'un symbole.
  • L'événement "nouvelle barre" permet de synchroniser l'ensemble des tampons indicateurs RSI pour une nouvelle barre sur le graphe courant.
  • L'événement "nouveau tick" est utilisé à partir de toutes les paires de devises pour mettre à jour les indicateurs sur la dernière barre incomplète. De plus, le recomptage de la barre se fait uniquement pour la paire, pour laquelle le « nouveau tick » a été reçu. 

Après avoir exploré le code complet de l'indicateur RSI pour l,Indice dollar USDx il deviendra plus clair comment cela fonctionne.

Caractéristiques d'installation :

  • Téléchargez le « Panneau de configuration MCM » pour les Expert Advisors et indicateurs multi-devises et compilez les fichiers « panneau iControl MCM.mq5 » et « Panneau de contrôle espion MCM.mq5 ».
  • Spécifiez l'ordre suivant des symboles dans la fenêtre "Veille du Marché" :
    1. EURUSD
    2. USDJPY
    3. GBPUSD
    4. USDCAD
    5. USDSEK
    6. USDCHF
    Ceci est nécessaire car je n'ai pas placé de contrôle approprié dans l'indicateur, et cette séquence est nécessaire pour le calcul correct de l'indicateur.
  • Décompressez l'archive iRSIUSDx.zip dans le dossier /MQL5. Attachez le iRSIUSDx.ex5 du dossier /MQL5/Indicators/iRSIUSDx/ au graphique EURUSD avec la période M1.
  • Successivement, pour l'ensemble des six symboles du menu "Evénement" du panneau "Panneau de configuration MCM", définissez l'événement "Nouveau tick", comme décrithere Vous devriez obtenir une image, similaire à celle de l'image ci-dessus.
  • De plus, pour le symbole EURUSD, définissez l'événement "nouvelle barre" sur le graphique des minutes. Dans l'indicateur, cet événement est utilisé pour la synchronisation lorsque la nouvelle barre sur l'intervalle de temps en cours, qui est égal à M1.
  • Si vous souhaitez un exemple plus visuel, définissez l'indice du dollar comme décrithere

Conclusion

L’implémentation discutée d'un mode multi-devises à part entière dans MetaTrader 5 démontre pleinement les avantages de la plate-forme et du langage de programmation MQL5 pour résoudre ce problème. Ce qui causait auparavant la majorité des difficultés, est maintenant disponible.

De toute évidence, ce n'est que le début d'une tendance dans ce sens. Sûrement, il y aura plus d'options trouvées d'un moyen encore meilleur de synchronisation des données, de gestion des modes multi-devises, etc.  Mais j'espère que maintenant il est clair que nous avons tous les outils nécessaires pour cela.


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

Fichiers joints |
irsiusdx_en.zip (7.54 KB)
Connecter les NeuroSolutions Réseaux Neuronaux Connecter les NeuroSolutions Réseaux Neuronaux
En plus de la création de réseaux neuronaux, la suite logicielle NeuroSolutions permet de les exporter sous forme de DLL. Cet article décrit le processus de création d'un réseau neuronal, de génération d'une DLL et de connexion à un Expert Advisor pour le trading dans MetaTrader 5.
Assistant MQL5 : Comment Créer un Module de Suivi des Positions Ouvertes Assistant MQL5 : Comment Créer un Module de Suivi des Positions Ouvertes
Le générateur de stratégies de trading MQL5 Wizard simplifie considérablement le test des idées de trading. L'article explique comment écrire et connecter au générateur de stratégies de trade MQL5 Wizard votre propre classe de gestion des positions ouvertes en déplaçant le niveau Stop Loss vers une zone sans perte lorsque le prix va dans le sens de la position, ce qui permet de protéger vos prélèvement du fléchissement des bénéfices lors de l’activité du trading. Il indique également la structure et le format de la description de la classe créée pour l'assistant MQL5.
Moving Mini-Max : un nouvel indicateur pour l'analyse technique et son implémentation en MQL5 Moving Mini-Max : un nouvel indicateur pour l'analyse technique et son implémentation en MQL5
Dans l'article suivant, je décris un processus d’implémentation de l'indicateur Moving Mini-Max basé sur un article de Z.G.Silagadze « Moving Mini-max : un nouvel indicateur pour l'analyse technique ». L'idée de l'indicateur est basée sur la simulation des phénomènes d'effet tunnel quantique, proposée par G. Gamov dans la théorie de la désintégration alpha.
Assistant MQL5 : Comment Créer un Module de Gestion des Risques et de fonds Assistant MQL5 : Comment Créer un Module de Gestion des Risques et de fonds
Le générateur de stratégies de trading de l'assistant MQL5 simplifie considérablement le test des idées de trading. L'article décrit comment élaborer un module de gestion des risques et de fonds personnalisé et l'activer dans l'assistant MQL5. À titre d'exemple, nous avons examiné un algorithme de gestion de l'argent, dans lequel la taille du volume de trade est déterminée par les résultats du deal précédent. La structure et le format de description de la classe créée pour l'Assistant MQL5 sont également abordés dans l'article.