English Русский 中文 Español Deutsch 日本語 Português 한국어 Italiano Türkçe
Échange de Données entre les Indicateurs : C'est facile

Échange de Données entre les Indicateurs : C'est facile

MetaTrader 5Exemples | 15 novembre 2021, 14:18
274 0
Alexey Subbotin
Alexey Subbotin

Introduction

La raison pour laquelle nous apprécions les débutants, c'est parce qu'ils refusent obstinément d'utiliser la recherche, et il y a de nombreux sujets comme "FAQ", "Pour les débutants" ou "Celui qui pose une question de cette liste, brûlera en enfer". Leur véritable mission est de poser des questions, comme "Comment...", "Est-il possible de..." et d'obtenir souvent des réponses comme "Pas question", "C'est impossible".

La phrase éternelle "ne plus jamais dire" a incité les scientifiques, les ingénieurs et les programmeurs à penser et à créer quelque chose de nouveau et d'ancien bien oublié.

1. Définition du Problème

Par exemple, voici une citation de l'un des sujets du forum de la communauté MQL4 (traduit du russe) :

 ...il y a deux indicateurs (appelons-les A et B). L'indicateur A utilise les données directement du tableau des prix comme d'habitude, et B utilise les données de l'indicateur A. Voici la question : Que dois-je faire pour effectuer le calcul de B de manière dynamique, en utilisant les données de l'indicateur A (qui est déjà joint), au lieu d'utiliser iCustom("indicateur A", ...), c'est à dire si je modifie certains paramètres de l'indicateur A , cela devrait se refléter dans les changements de paramètres de l'indicateur B.

ou en d'autres termes :

... Supposons que nous ayons attaché une Moyenne Mobile à un graphique. Maintenant, comment obtenir l'accès direct à son tampon de données ?

et ça:

... Si j'appelle un indicateur à l'aide d'iCustom, il se recharge même s'il a été chargé avant l'appel. Y a-t-il un moyen de l'empêcher?

La liste de ces questions peut être allongée, elles sont posées encore et encore, et pas seulement par les débutants. Généralement, le problème est que MetaTrader n'a aucune technique pour accéder aux données de l'indicateur personnalisé sans l'utilisation d'iCustom (MQL4) ou l'utilisation de la liaison iCustom + CopyBuffer (MQL5). Mais il serait si tentant pour la prochaine élaboration du chef-d'œuvre d'écrire certaines fonctions en code MQL, qui obtiendront des données ou calculeront quelque chose en utilisant les données d'un graphique indiqué.

Il n'est pas pratique de travailler avec les fonctions standard mentionnées ci-dessus : par exemple, dans MQL4, les copies de l'indicateur sont créées pour chaque appelant lorsque iCustom est appelé ; dans MQL5, le problème a été en partie résolu en utilisant des poignées, et maintenant les calculs ne sont effectués qu'une seule fois pour une copie originale Ce n'est pourtant pas une panacée : si l'indicateur auquel nous nous référons consomme beaucoup de ressources de calcul, le l’étranglement (panne) du terminal est pratiquement garanti d'une dizaine de secondes à chaque réinitialisation.

Je me rappelle de plusieurs moyens proposés pour accéder à l'origine, dont les suivants :

  • mappage de fichiers sur un disque physique ou une mémoire ;
  • utilisation de la mémoire partagée via DLL pour l'échange de données ;
  • l'utilisation de variables globales du terminal client pour l'échange de données et leur stockage.

Et quelques autres méthodes, qui sont des variantes de celles mentionnées ci-dessus, ainsi que des méthodes insolites telles que les sockets, les mailslots, etc.( il y a une méthode radicale qui est souvent utilisée en Expert Advisors- le transfert des calculs d’indicateur directement dans le code Expert Advisor mais ça va au delà de la portée de notre article.

Du point de vue de l'auteur, il y a quelque chose dans toutes ces méthodes, mais elles ont toutes un inconvénient commun : les données sont d'abord copiées dans un endroit puis distribuées à d'autres. En premier lieu, cela nécessite quelques ressources CPU, et en second lieu, cela crée un nouveau problème de pertinence des données transmises (nous ne l'examinerons pas ici).

Essayons donc de définir le problème :

Nous souhaitons créer un tel environnement, qui donnerait accès aux données d'indicateurs attachés à un graphique, et aurait les propriétés suivantes :

  • absence de copie des données (et problème en rapport avec sa pertinence) ;
  • modification minimale du code des méthodes disponibles, si nous devons les utiliser ;
  • Le code MQL est préférable (bien sûr, nous devons utiliser des DLL, mais nous n'utiliserons qu'une douzaine de chaînes de code C++).

L'auteur a utilisé C++ Builder pour la création de DLL et les terminaux clients MetaTrader 4 et MetaTrader 5. Les codes sources présentés ci-dessous sont écrits en MQL5, les codes MQL4 sont joints à l'article, les principales différences entre eux seront discutées.

2. Tableaux

Tout d'abord, nous avons besoin d'un peu de théorie, car nous allons travailler avec des tampons d'indicateurs, et nous devons savoir comment leurs données sont situées dans la mémoire. Cette information n'est pas correctement documentée.

Les tableaux dynamiques dans MQL sont des structures de taille variable, il est donc intéressant de voir comment MQL résout le problème de réattribution des données, si la taille du tableau a augmenté et qu'il n'y a pas de mémoire libre juste après le tableau. Il y a deux manières de le résoudre :

  1. pour réattribuer de nouvelles données dans la partie supplémentaire de mémoire disponible (et enregistrer les adresses de toutes les parties de ce tableau, par exemple à l'aide d'une liste référencée), ou
  2. déplacer tout le tableau dans son ensemble vers la nouvelle partie de la mémoire, ce qui est suffisant pour l'affecter.

La première méthode entraîne des problèmes supplémentaires, car dans ce cas, nous devons étudier les structures de données créées par le compilateur MQL. La considération suivante témoigne encore de la deuxième variante (qui est encore plus lente) : Lorsqu'un tableau dynamique est passé à une fonction externe (à la DLL), cette dernière obtient le pointeur vers le premier élément de données, les autres éléments du tableau sont ordonnés et situés juste après le premier élément du tableau.

Lors du passage par référence, les données du tableau peuvent être modifiées, cela signifie donc que dans la première méthode, il y a un problème à copier le tableau entier dans une zone mémoire séparée pour le transférer vers une fonction externe, puis ajouter le résultat au tableau source, c'est-à-dire effectuer les mêmes actions que celles impliquées par la deuxième méthode.

Bien que cette conclusion ne soit pas logiquement objective à 100%, elle peut tout de même être considérée comme plutôt fiable (elle est également prouvée par le bon fonctionnement de notre produit basé sur cette idée).

Nous assumerons donc que les affirmations suivantes sont exactes :

  • A chaque instant, les données des tableaux dynamiques sont localisées séquentiellement en mémoire, les unes après les autres ; et le tableau peut être déplacé dans d'autres parties de la mémoire de l'ordinateur, mais uniquement dans son ensemble.
  • L'adresse du premier élément du tableau est transmise à la bibliothèque DLL, lorsqu'un tableau dynamique est transmis à une fonction externe en tant que paramètre par référence.

Et pourtant, nous prenons d'autres hypothèses : par moments de temps, nous entendons les moments de temps où la fonction OnCalculate() (pour MQL4 - start()) de l'indicateur correspondant est appelée ; de plus, par souci de simplicité, nous supposons que nos tampons de données ont la même dimension et le même type de double [].

3. Sine qua non

MQL ne prend pas en charge les pointeurs (à l'exception des pointeurs d'objets, qui ne sont pas des pointeurs dans son sens commun), comme il l'a déclaré et confirmé à maintes reprises les représentants de MetaQuotes Software. Vérifions donc si cela est vrai.

Qu'est-ce qu'un pointeur ? Ce n'est pas seulement un identificateur avec un astérisque, c'est l'adresse d'une cellule dans la mémoire d'un ordinateur. Et quelle est l'adresse de la cellule ? C'est le numéro de série à partir d'un certain début. Et enfin, qu'est-ce qu'un nombre ? C'est un nombre entier qui correspond à une certaine cellule de la mémoire de l'ordinateur. Et pourquoi ne peut-on pas travailler avec un pointeur comme avec le nombre entier ? Oui, nous pouvons, car les programmes MQL fonctionnent parfaitement avec des nombres entiers !

Mais comment convertir un pointeur en un nombre entier ? La bibliothèque de liens dynamiques nous aidera, nous utiliserons les possibilités de transtypage C++. En raison du fait que les pointeurs C++ sont de type de données à quatre octets, dans notre cas, il est pratique d'utiliser le int en tant que type de données à quatre octets.

Le bit de signe n'a pas d'importance et nous n'en tiendrons pas compte (s'il est égal à 1, cela signifie simplement que l'entier est négatif), l'essentiel est de pouvoir conserver tous les bits du pointeur inchangés. Bien sûr, nous pouvons utiliser des entiers non signés, mais ce sera mieux si les codes pour MQL5 et MQL4 sont similaires, car MQL4 ne fournit pas d'entier non signé.

Donc,

extern "C" __declspec(dllexport) int __stdcall GetPtr(double *a)
{
        return((int)a);
}

Wow, maintenant nous avons la variable de type long avec la valeur de l'adresse de début du tableau ! Nous devons maintenant apprendre à lire la valeur du i-ème élément du tableau :

extern "C" __declspec(dllexport) double __stdcall GetValue(int pointer,int i)
{
        return(((double*) pointer)[i]);
}

... et écrire la valeur (ce n'est pas vraiment nécessaire dans notre cas, quand même...)

extern "C" __declspec(dllexport) void __stdcall SetValue(int pointer,int i,double value)
{
        ((double*) pointer)[i]=value;
}

Alors, c'est tout. Nous pouvons maintenant travailler avec des pointeurs en MQL.

4. Emballage

Nous avons créé le noyau du système, nous devons maintenant le préparer pour une utilisation pratique dans les programmes MQL. Néanmoins, les fans d'esthétique s'il vous plaît ne vous vous fâchez pas, il y aura autre chose.

Il y a un milliard de façons de le faire. Nous choisirons la voie suivante. Rappelons-nous que le terminal client a une fonction spéciale pour l'échange de données entre des programmes MQL séparés - Variables Globales. Le moyen le plus naturel est de les utiliser pour stocker des pointeurs vers des tampons d'indicateurs, auxquels nous accéderons. Nous considérerons ces variables comme un tableau de descripteurs. Chaque descripteur aura le nom représenté par une chaîne comme :

string_identifier#buffer_number#symbol#period#buffer_length#indexing_direction#random_number,

et sa valeur sera égale à la représentation entière du pointeur de tampon correspondant dans la mémoire de l'ordinateur.

Quelques détails sur les champs descripteurs.

  • string_identifier - n'importe quelle chaîne (par exemple, le nom de l'indicateur - la variable short_name peut être utilisée, etc.); il sera utilisé pour la recherche du pointeur nécessaire, on entend par là qu'un indicateur enregistrera les descripteurs avec le même identificateur et les distinguera à l'aide du champ :
  • buffer_number – sera utilisé pour distinguer les tampons ;
  • buffer_length - nous en avons besoin pour contrôler les limites, sinon nous pourrions provoquer l’écrasement du terminal client et de Windows Blue Screen of Death :);
  • symbol, period – symbole et période pour indiquer la fenêtre du graphique ;
  • ordering_direction - il indique le sens de l'ordre des éléments du tableau : 0 – usual, 1 – reversal (AS_SERIES flag is true);
  • nombre_aléatoire – il sera utilisé s'il existe plusieurs copies d'un indicateur mais avec des fenêtres différentes ou avec des ensembles de paramètres différents, attachés à un terminal client (ils peuvent définir les mêmes valeurs des premier et deuxième champs, c'est pourquoi il faut en quelque sorte distinguer entre eux ) - ce n'est peut-être pas la meilleure solution, mais cela fonctionne.

Tout d'abord, nous avons besoin de fonctions pour enregistrer le descripteur et le supprimer. Regardez la première chaîne de la fonction - l'appel de la fonction UnregisterBuffer() est nécessaire pour supprimer les anciens descripteurs dans la liste des variables globales.

À chaque nouvelle barre, la taille du tampon sera augmentée de 1, nous devons donc appeler RegisterBuffer(). Et si la taille du buffer a changé, le nouveau descripteur sera créé dans la tableau (l'information de sa taille est contenue dans son nom), l'ancien restera dans la tableau C'est pourquoi il est utilisé.

void RegisterBuffer(double &Buffer[], string name, int mode) export
{
   UnregisterBuffer(Buffer);                    //first delete the variable just in case
   
   int direction=0;
   if(ArrayGetAsSeries(Buffer)) direction=1;    //set the right ordering_direction

   name=name+"#"+mode+"#"+Symbol()+"#"+Period()+"#"+ArraySize(Buffer)+"#"+direction;
   int ptr=GetPtr(Buffer);                      // get the buffer pointer

   if(ptr==0) return;
   
   MathSrand(ptr);                              //it's convenient to use the pointer value instead
                                                //of the current time for the random numbers base 
   while(true)
   {
      int rnd=MathRand();
      if(!GlobalVariableCheck(name+"#"+rnd))    //check for unique name - we assume that 
      {                                         //nobody will use more RAND_MAX buffers :)
         name=name+"#"+rnd;                     
         GlobalVariableSet(name,ptr);           //and write it to the global variable
         break;
      }
   }   
}
void UnregisterBuffer(double &Buffer[]) export
{
   int ptr=GetPtr(Buffer);                      //we will register by the real address of the buffer
   if(ptr==0) return;
   
   int gt=GlobalVariablesTotal();               
   int i;
   for(i=gt-1;i>=0;i--)                         //just look through all global variables
   {                                            //and delete our buffer from all places
      string name=GlobalVariableName(i);        
      if(GlobalVariableGet(name)==ptr)
         GlobalVariableDel(name);
   }      
}

Les commentaires détaillés sont superflus ici, nous soulignons simplement le fait que la première fonction crée un nouveau descripteur du format ci-dessus dans la liste des variables globales ; le second recherche dans toutes les variables globales et supprime les variables et les descripteurs de suppression avec la valeur égale au pointeur.

Considérons maintenant la deuxième tâche - obtenir des données à partir de l'indicateur. Avant d’implémenter l'accès direct aux données, nous devons d'abord trouver un descripteur correspondant. Il peut être résolu en utilisant la fonction suivante. Son algorithme sera le suivant : nous parcourons toutes les variables globales et vérifions la présence de valeurs de champ indiquées dans le descripteur.

Si nous l'avons trouvé, nous ajoutons son nom au tableau, passé en dernier paramètre. Ainsi, comme résultat, la fonction renvoie toutes les adresses mémoire qui correspondent aux conditions de recherche. La valeur renvoyée est le nombre de descripteurs trouvés.

int FindBuffers(string name, int mode, string symbol, int period, string &buffers[]) export
{
   int count=0;
   int i;
   bool found;
   string name_i;
   string descriptor[];
   int gt=GlobalVariablesTotal();

   StringTrimLeft(name);                                    //trim string from unnecessary spaces
   StringTrimRight(name);
   
   ArrayResize(buffers,count);                              //reset size to 0

   for(i=gt-1;i>=0;i--)
   {
      found=true;
      name_i=GlobalVariableName(i);
      
      StringExplode(name_i,"#",descriptor);                 //split string to fields
      
      if(StringFind(descriptor[0],name)<0&&name!=NULL) found=false; //check each field for the match
                                                                    //condition
      if(descriptor[1]!=mode&&mode>=0) found=false;
      if(descriptor[2]!=symbol&&symbol!=NULL) found=false;
      if(descriptor[3]!=period&&period>0) found=false;
      
      if(found)
      {
         count++;                                           //conditions met, add it to the list
         ArrayResize(buffers,count);
         buffers[count-1]=name_i;
      }
   }
   
   return(count);
}

Comme nous le constatons dans le code de la fonction, certaines conditions de recherche peuvent être omises. Par exemple, si nous passons le NULL comme nom, la vérification du string_identifier sera omise. Il en est de même pour les autres champs : mode<0, symbol :=NULL ou period<=0. Il permet d'étendre les options de recherche dans le tableau des descripteurs.

Par exemple, vous pouvez trouver tous les indicateurs de Moyenne Mobile dans toutes les fenêtres de graphique, ou uniquement dans les graphiques EURUSD avec la période M15, etc. Remarque supplémentaire : la vérification du string_identifier est effectuée par la fonction StringFind() au lieu de la vérification d'égalité stricte. Il est fait pour offrir la possibilité de rechercher par une partie du descripteur (par exemple, lorsque plusieurs indicateurs définissent une chaîne de type "MA(xxx)", la recherche peut être effectuée par la sous-chaîne "MA" - en conséquence, nous trouverons toutes les Moyennes Mobiles enregistrées).

Nous avons également utilisé la fonction StringExplode(string s, string separator, string &result[]). Elle divise la chaîne indiquée s en sous-chaînes, à l'aide du séparateur, et note les résultats dans le tableau de résultats.

void StringExplode(string s, string separator, string &result[])
{
   int i,pos;
   ArrayResize(result,1);
   
   pos=StringFind(s,separator); 
   if(pos<0) {result[0]=s;return;}
   
   for(i=0;;i++)
   {
      pos=StringFind(s,separator); 
      if(pos>=0)
      {
         result[i]=StringSubstr(s,0,pos);
         s=StringSubstr(s,pos+StringLen(separator));
      }
      else break;
      ArrayResize(result,ArraySize(result)+1);
   }
}

Maintenant, lorsque nous avons la liste de tous les descripteurs nécessaires, nous pouvons obtenir des données de l'indicateur :

double GetIndicatorValue(string descriptor, int shift) export
{
   int ptr;
   string fields[];
   int size,direction;
   if(GlobalVariableCheck(descriptor)>0)               //check that the descriptor is valid
   {                
      ptr = GlobalVariableGet(descriptor);             //get the pointer value
      if(ptr!=0)
      {
         StringExplode(descriptor,"#",fields);         //split its name to fields
         size = fields[4];                             //we need the current array size
         direction=fields[5];                                 
         if(direction==1) shift=size-1-shift;          //if the ordering_direction is reverse
         if(shift>=0&&shift<size)                      //check for its validity - to prevent crashes
            return(GetValue(MathAbs(ptr),shift));      //ok, return the value
      }   
   } 
   return(EMPTY_VALUE);                                //overwise return empty value 
}

Comme vous le voyez, il s'agit d'un emballage de la fonction GetValue() de la DLL. Il est nécessaire de vérifier la validité du descripteur, les limites du tableau et de prendre en compte l'ordre des tampons des indicateurs. En cas d'échec, la fonction renvoie EMPTY_VALUE.

La modification de l’indicateur de tampon est similaire :

bool SetIndicatorValue(string descriptor, int shift, double value) export
{
   int ptr;
   string fields[];
   int size,direction;
   if(GlobalVariableCheck(descriptor)>0)               //check for its validity
   {                
      ptr = GlobalVariableGet(descriptor);             //get descriptor value
      if(ptr!=0)
      {
         StringExplode(descriptor,"#",fields);         //split it to fields
         size = fields[4];                             //we need its size
         direction=fields[5];                                 
         if(direction==1) shift=size-1-shift;          //the case of the inverse ordering
         if(shift>=0&&shift<size)                      //check index to prevent the crash of the client terminal
         {
            SetValue(MathAbs(ptr),shift,value);
            return(true);
         }   
      }   
   }
   return(false);
}

Si toutes les valeurs sont correctes, il fait appel à la fonction SetValue() à partir de DLL. La valeur renvoyée correspond au résultat de la modification : vrai en cas de succès et faux en cas d'erreur.

5. Vérifier comment cela fonctionne

Essayons maintenant de le vérifier. Comme cible, nous utiliserons l'Average True Range (ATR) du package standard et montrerons ce que nous devons modifier dans son code pour permettre de copier ses valeurs dans une autre fenêtre d'indicateur. Nous allons également tester la modification des données de ses données tampon.

Ce que nous devons faire en premier est d'ajouter quelques lignes de code à la fonction OnCalculate() :

//+------------------------------------------------------------------+

//| Average True Range                                               |
//+------------------------------------------------------------------+
int OnCalculate(const int rates_total,
                const int prev_calculated,
                const datetime &Time[],
                const double &Open[],
                const double &High[],
                const double &Low[],
                const double &Close[],
                const long &TickVolume[],
                const long &Volume[],
                const int &Spread[])
  {
   if(prev_calculated!=rates_total)
      RegisterBuffer(ExtATRBuffer,"ATR",0);
…

Comme nous le voyons, le nouvel enregistrement du tampon doit être effectué à chaque fois, lorsque nous avons de nouvelles barres - ce cas se produit au fil du temps (normalement) ou en cas de téléchargement de données d'historique supplémentaires.

En plus de cela, nous devons supprimer les descripteurs du tableau une fois le fonctionnement de l'indicateur terminé. C'est pourquoi il est nécessaire d'ajouter du code au gestionnaire d'événements deinit (mais rappelez-vous qu'il doit retourner void et avoir un paramètre d'entrée const int Reason) :

void OnDeinit(const int reason)
{
   UnregisterBuffer(ExtATRBuffer);
}

Voici notre indicateur modifié dans la fenêtre graphique du terminal client (Fig.1) et son descripteur dans la liste des variables globales (Fig.2) :

 

Fig. 1. Average True Range

Fig. 2. Descripteur créé dans la liste des variables globales du terminal client

Fig. 2. Descripteur créé dans la liste des variables globales du terminal client

L'étape suivante consiste à accéder aux données ATR. Créons un nouvel indicateur (appelons-le test) avec le code suivant :

//+------------------------------------------------------------------+
//|                                                         test.mq5 |
//|                                             Copyright 2009, alsu |
//|                                                 alsufx@gmail.com |
//+------------------------------------------------------------------+
#property copyright "2009, alsu"
#property link      "alsufx@gmail.com"
#property version   "1.00"
#property indicator_separate_window
#property indicator_buffers 1
#property indicator_plots   1

#include <exchng.mqh>

//---- plot ATRCopy
#property indicator_label1  "ATRCopy"
#property indicator_type1   DRAW_LINE
#property indicator_color1  Red
#property indicator_style1  STYLE_SOLID
#property indicator_width1  1
//--- indicator buffers
double ATRCopyBuffer[];

string atr_buffer;
string buffers[];

//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+

int OnInit()
  {
//--- indicator buffers mapping
   SetIndexBuffer(0,ATRCopyBuffer,INDICATOR_DATA);
//---
   return(0);
  }

//+------------------------------------------------------------------+
//| Custom indicator iteration function                              |
//+------------------------------------------------------------------+
int OnCalculate(const int rates_total,
                const int prev_calculated,
                const datetime& time[],
                const double& open[],
                const double& high[],
                const double& low[],
                const double& close[],
                const long& tick_volume[],
                const long& volume[],
                const int& spread[])
  {
//---
   int found=FindBuffers("ATR",0,NULL,0,buffers);   //search for descriptors in global variables
   if(found>0) atr_buffer=buffers[0];               //if we found it, save it to the atr_buffer
   else atr_buffer=NULL;
   int i;
   for(i=prev_calculated;i<rates_total;i++)
   {
      if(atr_buffer==NULL) break;
      ATRCopyBuffer[i]=GetIndicatorValue(atr_buffer,i);  //now it easy to get data
      SetIndicatorValue(atr_buffer,i,i);                 //and easy to record them
   }
//--- return value of prev_calculated for next call
   return(rates_total);
  }
//+------------------------------------------------------------------+

Comme on le voit, ce n'est pas difficile. Obtenez la liste des descripteurs par une sous-chaîne et lisez les valeurs du tampon indicateur à l'aide de nos fonctions. Et enfin, écrivons quelles foutaises là-bas (dans notre exemple, nous écrivons les valeurs qui correspondent à l'index de l'élément du tableau).

Exécutons maintenant l'indicateur de test (notre ATR cible doit toujours être attaché au graphique). Comme nous le voyons sur la Fig.3, les valeurs d' ATR sont dans la sous-fenêtre inférieure (dans notre indicateur de test), nous voyons la ligne droite à la place - en fait, elle est remplie des valeurs correspondant aux index du tableau.

 

Fig. 3. Résultat de l'opération Test.mq5

Torsion du poignet - c'est tout :)

6. Rétrocompatibilité

L'auteur a essayé de créer une bibliothèque, écrite en MQL5, qui soit au maximum proche des normes MQ4. Ainsi, elle n'a besoin que de quelques modifications pour être utilisée dans MQL4.

En particulier, vous devez supprimer le mot-clé export, n'apparaissant que dans MQL5, et modifier le code où nous avons utilisé le typecasting indirect (MQL4 n'est pas si flexible). En fait, l'utilisation des fonctions dans les indicateurs est totalement identique, les différences ne concernent que la méthode de la nouvelle barre et la commande de l’ajout de l’historique.

Conclusion

Quelques mots sur les avantages.

Il semble que l'utilisation de la méthode décrite dans cet article ne se limite pas à son objectif initial. En plus de la construction d'indicateurs en cascade à grande vitesse, la bibliothèque peut être appliquée avec succès aux Expert Advisors, y compris ses cas de test d'historique. D'autres applications sont difficiles à imaginer pour l'auteur, mais je pense que les chers utilisateurs seront ravis de travailler dans ce sens.

Je pense que les moments techniques suivants pourraient être nécessaires :

  • amélioration du tableau des descripteurs (notamment indication des fenêtres) ;
  • élaboration du tableau supplémentaire pour l'ordre de création des descripteurs - cela peut être important pour la stabilité des systèmes de trading.

Aussi, je serai heureux de recevoir d'autres suggestions pour améliorer la bibliothèque.

Tous les fichiers nécessaires sont joints à l'article. Les codes pour MQL5, MQL4 et le code source de la bibliothèque exchng.dll sont présentés. Les fichiers ont les mêmes emplacements qu'ils devraient avoir dans les dossiers du terminal client.

Avertissement

La documentation présentée dans cet article décrit des techniques de programmation qui, par une utilisation imprudente, peuvent causer des dommages du logiciel exécuté sur votre ordinateur. L'utilisation des Les fichiers joints ne doivent être effectués qu'à vos risques et périls et peuvent entraîner d’éventuels effets secondaires , bien que non révélés jusqu'à présent.

Remerciements

L'auteur a utilisé les problèmes proposés sur le forum de MQL4.Community https://forum.mql4.com et les idées de ses utilisateurs : igor.senych, satop, bank , StSpirit, TheXpert, jartmailru, ForexTools, marketeer, IlyaA – désolé si la liste est incomplète.


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

Fichiers joints |
cpp.zip (0.36 KB)
mql4.zip (39.29 KB)
mql521x.zip (46.87 KB)
Dessiner les Émissions de l'Indicateur en MQL5 Dessiner les Émissions de l'Indicateur en MQL5
Dans cet article, nous allons traiter l'émission d'indicateurs - une nouvelle approche de l'étude de marché. Le calcul de l'émission est basé sur l'intersection de différents indicateurs : de plus en plus de points de couleurs et de formes différentes apparaissent après chaque tick. Ils forment de nombreux groupes comme des nébuleuses, des nuages, des pistes, des lignes, des arcs, etc. Ces formes contribuent à détecter les ressorts et les forces invisibles qui affectent le mouvement des prix du marché.
Comment Échanger des Données : Une DLL pour MQL5 en 10 minutes Comment Échanger des Données : Une DLL pour MQL5 en 10 minutes
Maintenant, peu de développeurs se rappellent de la façon d'écrire une DLL simple et des caractéristiques spéciales des différentes liaisons système. À l'aide de plusieurs exemples, je vais tenter de montrer l'ensemble du processus de création de la DLL simple en 10 minutes, ainsi que de discuter de certains détails techniques de notre implémentation de liaison. Je vais montrer étape par étape le processus de la création de DLL dans Visual Studio avec des exemples d'échange de différents types de variables (nombres, tableaux, chaînes, etc.). En outre, je vais vous expliquer comment protéger votre terminal client des plantages dans les DLL personnalisées.
Comment exporter des cotations de МetaTrader 5 vers des applications .NET à l'aide des services WCF Comment exporter des cotations de МetaTrader 5 vers des applications .NET à l'aide des services WCF
Vous souhaitez organiser l'exportation des cotations de MetaTrader 5 vers votre propre application ? La jonction MQL5-DLL permet de créer de telles solutions ! Cet article vous indiquera l'un des moyens d'exporter des cotations de MetaTrader 5 vers des applications écrites en .NET. Pour moi, il était plus intéressant, rationnel et facile d'implémenter l'export de cotations en utilisant cette même plateforme. Malheureusement, la version 5 ne prend toujours pas en charge .NET, donc comme autrefois, nous utiliserons la dll win32 avec la prise en charge de .NET comme couche intermédiaire.
L'Histogramme des prix (Profile du Marché) et son implémentation  en MQL5 L'Histogramme des prix (Profile du Marché) et son implémentation en MQL5
Le Profile du Marché a été élaboré par le brillant penseur Peter Steidlmayer. Il a suggéré l’utilisation de la représentation alternative de l'information sur les mouvements de marché « horizontaux » et « verticaux » qui conduit à un ensemble de modèles complètement différent. Il a assumé qu'il existe une impulsion sous-jacente du marché ou un modèle fondamental appelé cycle d'équilibre et de déséquilibre. Dans cet article, j’examinerai l'Histogramme des Prix - un modèle simplifié de profil de marché, et décrirai son implémentation dans MQL5.