English Русский 中文 Español Deutsch 日本語 Português 한국어 Italiano Türkçe
Utilisation de Pseudo-Modèles comme Alternative aux Modèles C++

Utilisation de Pseudo-Modèles comme Alternative aux Modèles C++

MetaTrader 5Exemples | 12 janvier 2022, 14:37
130 0
Mykola Demko
Mykola Demko


Introduction

La question de l'implémentation des modèles en tant que standard du langage a été soulevée à plusieurs reprises sur le forum mql5.com. Alors que je faisais face au mur de refus des développeurs de MQL5, mon intérêt pour l’implémentation de modèles utilisant des méthodes personnalisées a commencé à grandir. Le résultat de mes études est présenté dans cet article.


Un peu d'histoire du C et du C++

Dès le début, le langage C a été élaboré pour offrir la possibilité d'effectuer des tâches du système. Les créateurs du langage C n'ont pas implémenté un modèle abstrait d'environnement d'exécution du langage ; ils viennent d'implémenter des fonctionnalités pour les besoins des programmeurs de système. Tout d'abord, ce sont les méthodes de travail direct avec la mémoire, les constructions de structures de contrôle et de gestion de modules d'applications.

En fait, rien de plus n'était inclus dans le langage ; toutes les autres choses ont été prises dans la bibliothèque d'exécution. C'est pourquoi certaines personnes malintentionnées se réfèrent parfois au langage C comme à l'assembleur structuré. Mais quoi qu'ils disent, l'approche semble être très réussie. Grâce à cela, un nouveau niveau de rapport entre la simplicité et la puissance du langage a été atteint. 

Ainsi, le C est apparu comme un langage universel pour la programmation du système. Mais il n'est pas resté dans ces limites. À la fin des années 80, écartant Fortran du leadership, C a gagné une grande popularité parmi les programmeurs du monde entier et est devenu largement utilisé dans différentes applications. Une contribution majeure à sa popularité a été apportée par la distribution d'Unix (et donc du langage C) dans les universités, où une nouvelle génération de programmeurs a été formée.

Mais si tout est si clair, alors pourquoi toutes les autres langages sont encore utilisés et qu'est-ce qui soutient leur existence ? Le talon d'Achille du langage C est trop bas pour des problèmes définis dans les années 90. Ce problème présente deux aspects.

D'une part, le langage comporte des moyens de trop bas niveau : c'est d'abord le travail de l'arithmétique mémoire et adresse. Ainsi, un changement dans le nombre de bits du processeur crée beaucoup de problèmes pour de nombreuses applications C. D'un autre côté, il y a un manque de moyens de haut niveau en C - types abstraits de données et d'objets, polymorphisme et gestion des exceptions. Ainsi, dans les applications C, une technique d’implémentation d'une tâche l'emporte souvent sur son côté matériel.

Les premières tentatives pour corriger ces inconvénients ont été menées au début des années 80. A cette époque, Bjarne Stroustrup dans AT&T Bell Labs a commencé à élaborer l'extension du langage C appelée "C avec classes". Le style du développement correspondait à l'esprit de création du langage C lui-même - l'ajout de différentes fonctionnalités pour rendre le travail de certains groupes de personnes plus pratique.

La principale innovation en C++ est le mécanisme des classes qui donne la possibilité d'utiliser de nouveaux types de données. Un programmeur décrit la représentation interne de l'objet de classe et l'ensemble des fonctions-méthodes pour accéder à cette représentation. L'un des principaux objectifs de la création de C++ était d'augmenter la proportion de réutilisation de code déjà écrit.

L'innovation du langage C++ ne consiste pas seulement en l'introduction de classes. Il a implémenté un mécanisme de gestion structurelle des exceptions (l'absence de celui-ci a compliqué le développement d'applications à sécurité intégrée), un mécanisme de modèles et bien d'autres choses. Ainsi, l'axe principal de l’élaboration du langage visait à étendre ses perspectives en introduisant de nouvelles créations de haut niveau tout en conservant la compatibilité totale avecANSI С


Modèle Comme Mécanisme de Substitution Macro

Pour comprendre comment implémenter un modèle en MQL5, vous devez comprendre comment ils fonctionnent en C++.

Voyons ladéfinition.

Les modèles sont une fonctionnalité du langage de programmation C++ qui permettent aux fonctions et aux classes de fonctionner avec des types génériques. Cela permet à une fonction ou à une classe de fonctionner sur de nombreux types de données différents sans être réécrites pour chacun.

MQL5 n'a pas de modèles, mais cela ne signifie pas qu'il est impossible d'utiliser un style de programmation avec des modèles. Le mécanisme des modèles dans le langage C++ est, en fait, un mécanisme sophistiqué de génération de macros profondément ancré dans le langage. En d'autres termes, lorsqu'un programmeur utilise un modèle, le compilateur détermine le type de données où la fonction correspondante est appelée, et non pas où elle est déclarée.

Les modèles ont été introduits en C++ pour réduire la quantité de code écrit par les programmeurs. Mais il ne faut pas oublier qu'un code tapé au clavier par un programmeur n'est pas le même que celui créé par le compilateur. Le mécanisme des modèles lui-même n'a pas entraîné de diminution de la taille des programmes ; cela a juste diminué la taille de leur code source. C'est pourquoi le principal problème résolu en utilisant des modèles est la diminution du code tapé par les programmeurs.

Puisque le code machine est généré lors de la compilation, les programmeurs ordinaires ne voient pas si le code d'une fonction est généré une ou à plusieurs reprises. Lors de la compilation d'un code modèle, le code de la fonction est généré autant de fois qu'il y a de types, où le modèle a été utilisé. En principe, un modèle est prioritaire sur l'étape de compilation.

Le deuxième aspect de l'introduction de modèles en C++ est l'allocation de mémoire. Le problème est que la mémoire dans le langage C est allouée de manière statique. Pour rendre cette allocation plus flexible, un modèle qui définit la taille de la mémoire pour les tableaux est utilisé. Mais cet aspect a déjà été implémenté par les développeurs de MQL4 sous forme de tableaux dynamiques, et il l'a également été dans MQL5 sous forme d'objets dynamiques.

Ainsi, seul le problème de la substitution des types reste sans solution. Les développeurs de MQL5 ont refusé de le résoudre, argumentant que l'utilisation du mécanisme de substitution de modèle permettrait de cracker le compilateur, ce qui conduirait à l'apparition d'un décompilateur.

Eh bien, ils le savent mieux. Et nous n'avons qu'un seul choix : Implémenter ce paradigme de manière personnalisée.

Tout d'abord, permettez-moi de faire une remarque que nous n'allons pas changer le compilateur ou modifier les normes du langage. Je suggère de modifier l'approche des modèles eux-mêmes. Si nous ne pouvons pas créer de modèles au stade de la compilation, cela ne signifie pas que nous ne sommes pas autorisés à écrire le code machine. Je suggère de déplacer l'utilisation des modèles de la partie de génération de code binaire à la partie où le code texte est écrit. Appelons cette approche des "pseudo-modèles".


Pseudo-Templates

Un pseudo-modèle présente ses avantages et ses inconvénients par rapport à un modèle C++. Les inconvénients comprennent des manipulations supplémentaires avec le déplacement de fichiers. Les avantages comprennent des possibilités plus flexibles que celles déterminées par les normes du langage. Passons aux actes.

Pour utiliser des pseudo-modèles, nous avons besoin d'un analogue de préprocesseur. Nous utiliserons le script 'Modèles' à cette fin. Voici les exigences générales du script : il doit lire un fichier indiqué (en conservant la structure des données), trouver un modèle et le remplacer par des types indiqués.

Ici, je dois faire une remarque. Puisque nous allons utiliser le mécanisme de redéfinition à la place des modèles, le code sera réécrit autant de fois qu'il y a de types devant être redéfinis. En d'autres termes, la substitution sera effectuée dans l'ensemble du code donné pour l'analyse. Ensuite, le code sera réécrit plusieurs fois par le script, créant à chaque fois une nouvelle substitution. Ainsi, on peut réaliser le slogan « travail manuel effectué par des machines ».


Élaboration du code de script

Déterminons les variables d'entrée requises :

  1. Nom d'un fichier à traiter.
  2. Une variable pour stocker le type de données à remplacer.
  3. Nom d'un modèle qui sera utilisé à la place des types de données réels.
input string folder="Example templat";//name of file for processing

input string type="long;double;datetime;string"
                 ;//names of custom types, separator ";"
string TEMPLAT="_XXX_";// template name

Pour que le script ne multiplie qu'une partie du code, définissez des noms de marqueurs. Le marqueur d'ouverture est destiné à indiquer le début de la pièce à traiter, et le marqueur de clôture - à indiquer sa fin.

En utilisant le script, j'ai rencontré le problème de la lecture des marqueurs.

Au cours de l'analyse, j'ai découvert que lors du formatage d'un document dansMetaEditor, un espace ou une tabulation (selon la situation) est souvent ajouté aux lignes de commentaires. Le problème a été résolu en supprimant les espaces avant et après un symbole significatif lors de la détermination des marqueurs. Cette fonctionnalité est réalisée comme automatique dans le script, mais il y a une remarque.

Un nom de marqueur ne doit pas commencer ou se terminer par un espace.

Un marqueur de clôture n'est pas obligatoire; s'il est absent, le code sera traité jusqu'à la fin du fichier. Mais il doit y avoir un d’ ouverture. Comme les noms des marqueurs sont constants, j'utilise#define l’instruction de préprocesseur à la place des variables.

#define startread "//start point"
#define endread "//end point"

Pour former un tableau de types, j'ai créé la fonction void ParserInputType(int i,string &type_d[],string text), qui remplit le tableau type_dates[] avec des valeurs à l'aide de la variable 'type'.    

Une fois que le script reçoit le nom d'un fichier et des marqueurs, il commence à lire le fichier. Pour enregistrer le formatage du document, le script lit les informations ligne par ligne, en enregistrant les lignes trouvées dans le tableau.

Bien sûr, vous pouvez tout vider dans une seule variable ; mais dans ce cas, vous perdrez la césure et le texte se transformera en une ligne sans fin. C'est la raison pour laquelle la fonction de lecture du fichier utilise le tableau de chaînes qui change de taille à chaque itération pour obtenir une nouvelle chaîne.

//+------------------------------------------------------------------+
//| downloading file                                                 |
//+------------------------------------------------------------------+
void ReadFile()
  {
   string subfolder="Templates";
   int han=FileOpen(subfolder+"\\"+folder+".mqh",FILE_READ|FILE_SHARE_READ|FILE_TXT|FILE_ANSI,"\r"); 
   if(han!=INVALID_HANDLE)
     {
      string temp="";
      //--- scrolling file to the starting point
      do {temp=FileReadString(han);StringTrimLeft(temp);StringTrimRight(temp);}
      while(startread!=temp);

      string text=""; int size;
      //--- reading the file to the array until a break point or the end of the file
      while(!FileIsEnding(han))
        {
         temp=text=FileReadString(han);
         // deleting symbols of tabulation to check the end
         StringTrimLeft(temp);StringTrimRight(temp);
         if(endread==temp)break;
         // flushing data to the array
         if(text!="")
           {
            size=ArraySize(fdates);
            ArrayResize(fdates,size+1);
            fdates[size]=text;
           }
        }
      FileClose(han);
     }
   else
     {
      Print("File open failed"+subfolder+"\\"+folder+".mqh, error",GetLastError());
      flagnew=true;
     }
  }

Pour plus de commodité d'utilisation, le fichier est ouvert en modeFILE_SHARE_READ Il donne la possibilité de démarrer le script sans fermer le fichier édité. L'extension de fichier est indiquée comme 'mqh'. Ainsi, le script lit directement le texte du code qui est stocké dans le fichier d'inclusion. Le problème est qu'un fichier avec l'extension « mqh » est, en fait, un fichier texte ; vous pouvez vous en assurer en renommant simplement le fichier en 'txt' et en ouvrant le fichier 'mqh' à l'aide de n'importe quel éditeur de texte. 

A la fin de la lecture, la longueur du tableau est égale au nombre de lignes entre les marqueurs de début et de fin.

Le nom du fichier ouvert doit avoir l'extension "template", sinon le fichier initial sera écrasé et toutes les informations seront perdues.

Passons maintenant à l'analyse des informations. La fonction qui analyse et remplace les informations est appelée à partir de la fonction d'écriture dans un fichier void WriteFile(int count). Les commentaires sont fournis au sein de la fonction.

void WriteFile(int count)
  {
   ...
   if(han!=INVALID_HANDLE)
     {
      if(flagnew)// if the file cannot be read
        {
         ...
        }
      else
        {// if the file exists
         ArrayResize(tempfdates,count);
         int count_type=ArraySize(type_dates);
         //--- the cycle rewrites the contents of the file for each type of the type_dates template
         for(int j=0;j<count_type;j++)
           {
            for(int i=0;i<count;i++) // copy data into the temporary array
               tempfdates[i]=fdates[i];
            for(int i=0;i<count;i++) // replace templates with types
               Replace(tempfdates,i,j);

            for(int i=0;i<count;i++)
               FileWrite(han,tempfdates[i]); // flushing array in the file
           }
        }
     ...
  }

Puisque les données sont remises à leur place et que le tableau est modifié après transformation, nous allons travailler avec une copie de celui-ci. Ici, nous définissons la taille du tableau tempfdates[] utilisé pour le stockage temporaire des données et le remplissons conformément à l'exemple fdates[].

Ensuite, la substitution de modèles à l'aide de la fonction Replace() est effectuée. Les paramètres de la fonction sont : le tableau à traiter (où la substitution du modèle est effectuée), le compteur de lignesi(pour se déplacer à l'intérieur du tableau) et le compteur de typesj (pour naviguer dans le tableau de types).

Comme nous avons deux cycles imbriqués, le code source est imprimé autant de fois qu'il y a de types indiqués.

//+------------------------------------------------------------------+
//| replacing templates with types                                   |
//+------------------------------------------------------------------+
void Replace(string &temp_m[],int i,int j)
  {
   if(i>=ArraySize(temp_m))return;
   if(j<ArraySize(type_dates))
      StringReplac(temp_m[i],TEMPLAT,type_dates[j]);// replacing  templat with types   
  }

La fonction Replace() comporte des contrôles (pour éviter d'appeler un index inexistant d'un tableau) et elle appelle la fonction imbriquée StringReplac(). Il y a une raison pour laquelle le nom de la fonction est identique à la fonction standardStringReplace , ils ont également le même nombre de paramètres.

Ainsi, en ajoutant une seule lettre"e" , nous pouvons changer toute la logique de remplacement. LaFonction standard prend la valeur de l'exemple 'find' et la remplace par la chaîne indiquée 'replacement'. Et ma fonction non seulement remplace, mais analyse s'il y a des symboles avant 'trouver' (c'est-à-dire vérifie si 'find' fait partie d'un mot); et s'il y en a, il remplace 'find' par 'remplacement' mais en majuscule, sinon le remplacement est effectué tel quel. Par conséquent, en plus de définir les types, vous pouvez les utiliser dans les noms des données remplacées.


Innovations

Permettez-moi maintenant de parler des innovations qui ont été ajoutées lors de l'utilisation. J'ai déjà mentionné qu'il y avait des problèmes de lecture des marqueurs lors de l'utilisation du script.

Le problème est résolu par le code suivant dans la fonction void ReadFile() :

      string temp="";
      //--- scrolling the file to the start point
      do {temp=FileReadString(han);StringTrimLeft(temp);StringTrimRight(temp);}
      while(startread!=temp);

Le cycle lui-même était implémenté dans la version précédente, mais couper les symboles de tabulation à l'aide des fonctionsStringTrimLeft() et StringTrimRight() n'apparaissait que dans la version améliorée.

De plus, les innovations comprennent la suppression de l'extension "template" du nom du fichier de sortie, de sorte que le fichier de sortie soit prêt à l’utilisation Il est implémenté à l'aide de la fonction de suppression d'un exemple indiqué d'une chaîne indiquée.

Code de la fonction de suppression :

//+------------------------------------------------------------------+
//| Deleting the 'find' template from the 'text' string              |
//+------------------------------------------------------------------+
string StringDel(string text,const string find)
  {
   string str=text;
   StringReplace(str,find,"");
   return(str);
  }

Le code en charge de la coupe d'un nom de fichier se trouve dans la fonction void WriteFile(int count):

   string newfolder;
   if(flagnew)newfolder=folder;// if it is the first start, create an empty file of pre-template
   else newfolder=StringDel(folder," templat");// or create the output file according to the template

En plus de cela, le mode de préparation d'un pré-modèle est introduit. Si le fichier requis n'existe pas dans le répertoire Fichiers/Modèles, il sera formé en tant que fichier de pré-modèle.

Exemple:

//#define _XXX_ long
 
//this is the start point
 _XXX_
//this is the end point

Le code qui crée ces lignes se trouve dans la fonction void WriteFile(int count):

      if(flagnew)// if the file couldn't be read
        {// fill the template file with the pre-template
         FileWrite(han,"#define "+TEMPLAT+" "+type_dates[0]);
         FileWrite(han," ");
         FileWrite(han,startread);
         FileWrite(han," "+TEMPLAT);
         FileWrite(han,endread);
         Print("Creating pre-template "+subfolder+"\\"+folder+".mqh");
        }

L'exécution du code est protégée par la variable globale flagnew, qui prend la valeur 'true' en cas d'erreur de lecture du fichier.

Lors de l'utilisation du script, j'ai ajouté un autre modèle. Le processus de connexion du deuxième modèle est le même. Les fonctions qui nécessitent des modifications sont placées plus près de la fonctionOnStart() pour la connexion d'un modèle supplémentaire. Un chemin pour connecter de nouveaux modèles est battu. Ainsi, nous avons la possibilité de connecter autant de modèles que nécessaire. Vérifions maintenant l’opération


Vérification de l’Opération

Tout d'abord, mettons en marche le script en spécifiant tous les paramètres requis. Dans la fenêtre qui apparaît, indiquez le nom du fichier "Example template".

Remplissez les champs des types de données personnalisés en utilisant le ';' séparateur.

Fenêtre de démarrage du script

Dès que le bouton "OK" est appuyé, le répertoire Templates est créé ; il comporte le fichier de pré-modèle "Example templat.mqh".

Cet événement est affiché dans le journal avec le message :

Messages de journal

Modifions le pré-modèle et redémarrons le script. Cette fois le fichier existe déjà dans le répertoire Templates (ainsi que le répertoire lui-même), c'est pourquoi le message concernant l'erreur d'ouverture du fichier ne s'affichera pas. Le remplacement sera effectué selon le modèle indiqué :

//this_is_the_start_point
 _XXX_ Value_XXX_;
//this_is_the_end_point

Ouvrez à nouveau le fichier créé "Example.mqh".

 long ValueLONG;
 double ValueDOUBLE;
 datetime ValueDATETIME;
 string ValueSTRING;

Comme vous pouvez le constater, 4 lignes sont faites à partir d'une ligne selon le nombre de types que nous avons transmis en paramètre. Écrivez maintenant deux lignes suivantes dans le fichier modèle :

//this_is_the_start_point
 _XXX_ Value_XXX_;
 _XXX_ Type_XXX_;
//this_is_the_end_point

Le résultat démontre clairement la logique de l'opération de script.

Tout d'abord, le code en entier est réécrit avec un type de données, puis le traitement d'un autre type est effectué. Ceci se répète jusqu'à ce que tous les types soient traités.

 long ValueLONG;
 long TypeLONG;
 double ValueDOUBLE;
 double TypeDOUBLE;
 datetime ValueDATETIME;
 datetime TypeDATETIME;
 string ValueSTRING;
 string TypeSTRING;

Incluez maintenant le deuxième modèle dans le texte de l'exemple.

//this_is_the_start_point
 _XXX_ Value_XXX_(_xxx_ ind){return((_XXX_)ind);};
 _XXX_ Type_XXX_(_xxx_ ind){return((_XXX_)ind);};

 //this_is_the_end_button

Résultat :

 long ValueLONG(int ind){return((long)ind);};
 long TypeLONG(int ind){return((long)ind);};
 
 double ValueDOUBLE(float ind){return((double)ind);};
 double TypeDOUBLE(float ind){return((double)ind);};
 
 datetime ValueDATETIME(int ind){return((datetime)ind);};
 datetime TypeDATETIME(int ind){return((datetime)ind);};
 
 string ValueSTRING(string ind){return((string)ind);};
 string TypeSTRING(string ind){return((string)ind);};

Dans le dernier exemple, j'ai volontairement mis un espace après la dernière ligne. Cet espace montre où le script termine le traitement d'un type et commence à en traiter un autre. Concernant le deuxième modèle, nous pouvons noter que le traitement des types est effectué de manière similaire au premier modèle. Si un type correspondant n'est pas trouvé pour un type du premier modèle, rien n'est imprimé.

Maintenant, je veux clarifier la question du débogage du code. Les exemples donnés sont assez simples pour le débogage. Pendant la programmation, vous devrez peut-être déboguer une assez grande partie du code et la multiplier dès que c'est fait. Pour ce faire, il y a une ligne commentée réservée dans le pré-modèle : "//#define _XXX_ long".

Si vous supprimez les commentaires, notre modèle deviendra un vrai type. En d'autres termes, nous dirons au compilateur comment le modèle doit être interprété.

Malheureusement, nous ne pouvons pas déboguer tous les types de cette manière. Mais nous pouvons déboguer un type, puis changer le type du modèle dans 'define' ; afin que nous puissions déboguer tous les types un par un. Bien sûr, pour le débogage, nous devons déplacer le fichier dans le répertoire du fichier appelé ou dans le répertoire Include. C'est l'inconvénient du débogage que j'ai précédemment évoqué en parlant des inconvénients des pseudo-modèles.


Conclusion

En conclusion, je tiens à dire, bien que l'idée d'utiliser des pseudo-modèles soit intéressante et assez productive, ce n'est que l'idée avec un petit début d’implémentation. Bien que le code décrit ci-dessus fonctionne et qu'il m'ait épargné de nombreuses heures d'écriture du code, de nombreuses questions restent ouvertes. C'est d'abord la question de l'élaboration des normes.

Mon script implémente le remplacement de blocs de modèles. Mais cette approche n'est pas obligatoire. Vous pouvez créer un analyseur plus complexe qui interprète certaines règles. Mais voici le début. Espérons une grande discussion. La pensée se nourrit du conflit. Bonne chance!


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

Fichiers joints |
templates.mq5 (9.91 KB)
Le Rôle des Distributions Statistiques dans le Travail des Traders Le Rôle des Distributions Statistiques dans le Travail des Traders
Cet article est la suite logique de mon article Statistical Probability Distributions en MQL5 qui présente les classes pour travailler avec certaines distributions statistiques théoriques. Maintenant que nous disposons d'une base théorique, je suggère que nous procédions directement à des ensembles de données réelles et que nous essayions de faire un usage informatif de cette base.
Utilisation des Indicateurs MetaTrader 5 avec le Cadre d'Apprentissage Automatique ENCOG pour la Prédiction de Séries Chronologiques Utilisation des Indicateurs MetaTrader 5 avec le Cadre d'Apprentissage Automatique ENCOG pour la Prédiction de Séries Chronologiques
Cet article présente la connexion de MetaTrader 5 à ENCOG - Advanced Neural Network and Machine Learning Framework. Il comporte la description et l’implémentation d'un indicateur de réseau neuronal simple axé sur des indicateurs techniques standard et un Expert Advisor axé sur un indicateur neuronal. Tout le code source, les binaires compilés, les DLL et un réseau formé exemplaire sont joints à l'article.
Filtrage des Signaux en Fonction des Données Statistiques de la Corrélation des Prix. Filtrage des Signaux en Fonction des Données Statistiques de la Corrélation des Prix.
Existe-t-il une corrélation entre le comportement passé des prix et ses tendances futures ? Pourquoi le prix répète-t-il aujourd'hui le caractère de son mouvement de la veille ? Les statistiques peuvent-elles être utilisées pour prévoir la dynamique des prix ? Il y a une réponse, et elle est positive. En cas de doute, cet article est fait pour vous. Je vais vous expliquer comment créer un filtre fonctionnel pour un système de trading dans MQL5, révélant une tendance intéressante dans les fluctuations de prix.
Modèle de Régression Universel pour la Prévision des Prix du Marché Modèle de Régression Universel pour la Prévision des Prix du Marché
Le prix du marché est formé à partir d’un équilibre stable entre l’offre et la demande qui, à son tour, dépendent d’une variété de facteurs économiques, politiques et psychologiques. Les différences de nature ainsi que les causes d’influence de ces facteurs rendent difficile la prise en compte directe de tous les composants. Cet article présente une tentative de prédire le prix du marché sur la base d’un modèle de régression élaboré.