vendredi 12 juillet 2013

les variables en MQL4: initiation à la programmation des experts advisors

Nous n'attendrons pas pour en savoir plus sur la programmation en langage MQL 4, car plus tôt nous savons plus vite nous pourrons réaliser des robots de trading. Aujourd'hui une notion fondamentale en programmation: les variables. Mais qu'est-ce que c'est ? Après une explication concise, nous allons pratiquer un peu en les utilisant dans un Expert Advisor. MQL4 signifie MetaQuotes Language version 4

Les variables

Un programme doit manipuler des données, c'est la raison de son existence. Il est fait pour ça, et en informatique tout n'est que donnée: nombre, texte, image, son etc. Et ces données il faut les stocker. Pour cela il y a les fichiers sur le disque dur, mais quand un programme doit réaliser une action avec ces données (calculs, transformation des données, copies etc) il doit les mettre en mémoire vive pour que le processeur puisse travailler dessus (le disque dur est trop lent, la mémoire vive est mille fois plus rapide, et le processeur encore plus). Donc dans un programme il y a des petits emplacements réservés aux données dans la mémoire. Ces petits emplacements sont désignés dans le code du programme par un nom. On donne un nom aux emplacements des données pour y faire référence. Par exemple quand on veut additionner deux nombres, chaque nombre a un emplacement, et si on veut les utiliser à plusieurs moments dans le programme on a tout intérêt à les désigner par un nom plutôt que par l'emplacement précis en mémoire qui est le numéro de la case mémoire là où les données commencent, et c'est un nombre abominable. Donc pour additionner deux nombres qu'on appelle "nb1" et "nb2", dans le programme on écrit "nb1 + nb2" comme ça le compilateur sait qu'il doit prendre le nombre stocké à l'endroit désigné par "nb1" et celui stocké à l'endroit désigné par "nb2". Il fait faire l'addition par le microprocesseur et devra stocker le résultat quelque part. Nous allons voir comment gérer ça. En programmation, nous désignons donc par variable, un emplacement dans la mémoire pour stocker des données, et cet emplacement a un nom, n'importe lequel car c'est nous qui choisissons ce nom.

La déclaration des variables

Comme pour de nombreux langages, avec MQL 4 il faut déclarer les variables, c'est à dire prévenir le compilateur que tel nom sera une variable et de tel type. Oui, il faut préciser le type des données, car selon le type, correspond une place plus ou moins grande en mémoire. Et en plus, la manière de traiter les données et les opérations autorisées dépendent du type de donnée. Par exemple, on peut additionner des nombres, mais pas du texte. Pour un nombre entier il faut une certaine place en mémoire (4 octets) et pour un nombre décimal il en faut plus (8 octets). Pour du texte, la place va dépendre de la longueur du texte, mais le compilateur est averti que c'est du texte et sait comment gérer la place du texte en mémoire. En MQL4 il y a les types:

  •  int:  pour integer. C'est un nombre entier, et il peut aller jusqu'à un peu plus de 2 milliards en positif comme en négatif.
  •  double:  pour un nombre décimal en double précision. C'est un nombre à virgule et les limites sont énormes. (le plus grand et le plus petit) Par contre la précision est limité, mais suffisante. (le nombre de chiffres après la virgule).
  •  bool:  pour booléen. C'est une valeur Vrai ou Faux. (booléenne, qui vient de Mr Boole). Cette valeur est en vérité stockée comme un 1 ou un 0 en mémoire. 0 est assimilé à Faux et toutes les autres valeurs à Vrai.
  •  string:  pour chaine de caractères. C'est du texte.
  •  color:  pour une couleur. Ici, c'est un code de couleur qui est stocké. Rares sont les langages qui proposent le type de donnée couleur. Pour éviter de se prendre le chou avec les codes couleurs en dessinant dans les graphiques.
  •  datetime:  pour un temps ou une date, ou les deux à la fois. C'est le code d'horodatage qui est stocké, heure et date en même temps. Ça désigne un moment précis (date et heure). Là aussi, c'est pas courant, un langage qui possède le type datetime. Très important en trading pour repérer et dater un tick.
  •  void:  pour rien ! Désigne l'absence de donnée, l'absence même de type de données. Il n'y a pas de données, ni de type associé. Si si ! C'est utile ! Pour préciser qu'il n'y a pas de données.

Nous nous préoccuperons que du texte, des valeurs booléennes et des nombres (entier et décimaux) pour l'instant. Le compilateur n'est pas très souple: il faut lui préciser avant de l'utiliser un nom pour désigner une donnée et son type, car il a besoin de savoir quelle quantité de mémoire utiliser et comment gérer cette donnée. On doit donc lui dire le type et le nom qu'on associe à l'emplacement en mémoire, comme ça il peut réserver la place en mémoire et sait désormais que tel nom est à relier à la place qu'il vient de réserver. Et il sait aussi comment gérer les traitements et calculs avec les données qui seront dans cet emplacement. Dans le code cela se fait en écrivant le type suivi du nom qu'on veut utiliser pour la donnée. Sans oublier le point virgule à la fin pour dire que l'instruction est finie.

  • int nbSecondesEcoulees;
  • double dernierTaux; 
  • bool enMouvementHaussier;
  • string messageErreur;

C'est la version courte, nous verrons la longue plus loin. Donc à partir de maintenant le compilateur a réservé quatre emplacements en mémoire: un emplacement de 4 octets pour le nombre entier nommé nbSecondesEcoulees, 8 octets pour un nombre à virgule nommé dernierTaux, 4 octets aussi pour la valeur logique enMouvementHaussier qui vaudra Vrai ou Faux et 4 octets également pour la chaine de caractères messageErreur. Vous me direz, "mais question place, ils prennent presque tous 4 octets !". Oui, mais avant quand la mémoire était moins abondante et chère, on utilisait par exemple juste le nécessaire pour la troisième qui pouvait se contenter de 1 seul octet. Et maintenant, on lui en a mis quatre pour être compatible avec les autres lors d'une conversion. Oui c'est quand même du gaspillage ! (en plus en découpant la mémoire par 4 octets le processeur peut la traiter plus vite, c'est surtout pour ça qu'on gaspille) Et Vous me direz aussi que ça vous étonne qu'un texte entier de taille variable rentre dans 4 octets ! Non, il ne rentre pas, Dans quatre octets on ne met que 4 caractères. Un caractère par octet. Comme on n'a pas encore dit au compilateur quoi mettre comme texte, en réalité il n'a pas réservé la place pour le texte, mais juste la place pour mémoriser l'endroit où il y aura le texte. Et cet endroit est un nombre entier qui tient sur 4 octets. Quand on lui dira qu'il faut y mettre un certain texte, il réservera la place nécessaire (autant d'octets que de caractères dans le texte plus un pour marquer la fin du texte) et il mettra dans la place de 4 octets le numéro de l'endroit du premier caractère du texte. Les noms sont choisis par nos soins, on peut mettre ce qu'on veut en respectant ces règles pour MQL4: que des lettres, des chiffres et le caractère underscore (ou en français 'souligné' _ ) et on ne peut pas commencer par un chiffre. Et surtout ne pas mettre un mot qui est un mot du langage MQL4, sinon le compilateur va confondre et vous dire "mais qu'est-ce que c'est ? C'est n'importe quoi ! Il ne peut pas y avoir ce mot-là ici !" En bref une erreur de compilation. Rassurez-vous les mots du langage sont peu nombreux, et il ne sont pas pertinents en tant que nom de variable.

L'affectation de valeurs aux variables

Maintenant qu'on a la place réservée, il faut mettre quelque chose dedans ! En programmation on dit "affecter une valeur à la variable". Quand on écrit le code, c'est général à tous les langages, on emploie le signe égal: '='. Un exemple:  nbSecondesEcoulees = 0; . Ici on fait mettre la valeur 0 dans l'emplacement désigné par le nom "nbSecondesEcoulees". La chose désignée par ce nom est désormais égale à la valeur 0. C'est aussi simple que ça ! Ne surtout pas oublier le point-virgule qui dit que l'instruction est terminée. Eh oui ! Une affectation de valeur à une variable est une instruction. Normal: on donne l'instruction de faire quelque chose, ici c'est mettre une valeur à un endroit précis (on ne sait pas où mais on en connait le nom, celui qu'on lui a donné). Donc les affectations des variables précédentes peuvent être:

  • nbSecondesEcoulees = 12;
  • dernierTaux = 1.32547;
  • enMouvementHaussier = false;
  • messageErreur = "Pas assez d\'argent pour cet ordre !";

La déclaration des variables avec affectation

Les deux étapes expliquées précédemment: la déclaration puis l'affectation d'une valeur, peuvent se faire en une seule instruction. On déclare le type, puis on donne le nom, on met le signe égal puis la valeur et le point-virgule pour finir l'instruction. Les étapes précédentes se combinent en:

  • int nbSecondesEcoulees = 12;
  • double dernierTaux = 1.32547;
  • bool enMouvementHaussier = false;
  • string messageErreur = "Pas assez d\'argent pour cet ordre !";

L'utilisation des variables

Une fois qu'on a déclaré une variable qui est une place en mémoire réservée, et qu'on a mis une valeur dedans ... qu'est-ce-qu'on en fait ? C'est simple il faut utiliser cette valeur quand on en a besoin. Mais comment ? Simplement en utilisant son nom ! Quand on écrit le nom de la variable dans le code c'est pour utiliser la valeur à la place. Sauf si c'est à gauche d'un signe égal, car comme on l'a vu, cela signifie que on met une valeur dedans: c'est l'affectation. Sinon dans tous les autres cas, écrire le nom, c'est utiliser la valeur à la place du nom. Le compilateur n'utilisera pas le nom, il s'en fout, il sait juste l'endroit où prendre la valeur à utiliser en fonction du nom. Par exemple:   nbSecondesEcoulees + 10 Dans cet exemple, le compilateur va prendre la valeur désignée par le nom à l'endroit où elle se trouve dans la mémoire, et va lui additionner 10. Mais le problème de cet exemple, c'est que le résultat du calcul ... il va disparaitre, car il est stocké temporairement dans le microprocesseur, et on a absolument pas précisé où copier ce résultat en mémoire. Et donc à la prochaine instruction, il sera écrasé par un autre résultat. Alors pour le récupérer il faut le stocker dans une place réservée en mémoire. C'est à dire qu'on réserve un place supplémentaire en déclarant une nouvelle variable qui recevra le résultat. Il faudra donc faire une affectation avec cette nouvelle variable déclarée à gauche du égal parce qu'elle va recevoir la valeur et le calcul à droite.

  • int dureeNecessaire;
  • dureeNecessaire = nbSecondesEcoulees + 10

Le compilateur va travailler de droite à gauche: il fait le calcul à droite du égal, et le résultat va être stocké dans l'emplacement réservé et désigné par le nom de variable à gauche du égal. Notre résultat est sauvé, et pourra être utilisé ailleurs. Nous allons maintenant pratiquer, en créant un petit expert advisor.

Un Expert Advisor qui compte les ticks

L'exercice qu'on va faire aura une petite utilité toute relative. Nous allons compter les ticks reçus tant que l'expert est en fonctionnement. Pour cela on va déclarer une variable qui va contenir le compte des ticks. Ensuite à l'initialisation on va le mettre à zéro, donc on va lui affecter la valeur 0. C'est une initialisation typique. Ensuite à chaque tick, il va falloir prendre le compte actuel de tick, lui ajouter 1 puisque qu'on vient de recevoir un tick car la fonction start() est exécutée. Et on remet cette nouvelle valeur (qui a augmenté de 1) dans le compte (à la même place). Le nombre de ticks reçus va augmenter de 1 à chaque exécution de la fonction start(). Pour finir, quand l'expert advisor est arrêté, on va afficher ce nombre avec un print dans le journal. Pour afficher le nombre de ticks reçus, il faut un message pour l'accompagner. Ce message on va le stocker en deux parties dans deux variables. La première partie est le début de la phrase, puis il faudra afficher le nombre, puis il faut la fin de la phrase. Vous allez voir, c'est pas bien compliqué.

  1. Demarrez MetaTrader 4 si ce n'est pas fait.
  2. Cliquez sur le bouton au losange jaune pour démarrer l'éditeur de code.
  3. Cliquez sur le bouton avec la croix verte dans l'éditeur de code pour faire un nouveau EA.
  4. Sélectionnez Expert Advisor si ce n'est pas le cas, cliquez sur suivant.
  5. Mettez le nom "CompteTicks" pour cet EA et cliquez sur Terminer.
  6. Supprimez toutes les lignes qui contiennent un début de commentaire:  //----  (il ne doit y avoir qu'une seule ligne vide dans les trois sections).
  7. Placez-vous sur la ligne vide en dessous des deux lignes commençant par  #property  et appuyez sur la touche Entrée pour faire une nouvelle ligne vide.
  8. A partir cette nouvelle ligne vide saisissez les lignes:
    • int nbTicksRecus;
    • string messageTicksDebut = "Il y a eu ";
    • string messageTicksFin = " ticks reçus.";
  9. Allez sur la ligne vide de la fonction init() et saisissez:
    • nbTicksRecus = 0;
  10. Allez sur la ligne vide de la fonction start() et saisissez:
    • nbTicksRecus = nbTicksRecus + 1;
  11. Allez sur la ligne vide de la fonction deinit() et saisissez:
    • prin
    • appuyez sur la touche Entrée pour valider la proposition de l'auto-complétion.
    • (messageTicksDebut, nbTicksRecus, messageTicksFin);
  12. Cliquez sur le bouton Disquette pour sauvegarder.
  13. Cliquez sur le bouton Compile pour obtenir le fichier exécutable.
  14. Vérifiez que vous n'avez pas d'erreurs. Si vous en avez vérifiez avec le code complet donné ci-dessous.
  15. Retournez sous MetaTrader 4
  16. Déroulez l'arborescence des Experts Consultants dans la petite fenêtre de gauche.
  17. Glissez et déposer l'expert CompteTicks sur un graphique.
  18. Ouvrez l'onglet Experts dans la fenêtre du bas et constatez les deux messages donnés par MetaTrader 4 à l'initialisation.
  19. Attendez un peu et faites un clic droit sur le graphique, puis dans Experts Consultants Cliquez sur Retirer.
  20. Vérifiez les quatre messages d'arrêt de l'expert, dont notre message avec le nombre de ticks reçus.
Code complet:

//+------------------------------------------------------------------+
//|                                                  CompteTicks.mq4 |
//|                  Copyright 2013, argent-facile-avec-robots-forex |
//|               http://argent-facile-avec-robots-forex.blogspot.fr |
//+------------------------------------------------------------------+
#property copyright "Copyright 2013, argent-facile-avec-robots-forex"
#property link   "http://argent-facile-avec-robots-forex.blogspot.fr"

int nbTicksRecus;
string messageTicksDebut = "Il y a eu ";
string messageTicksFin = " ticks reçus.";

//+------------------------------------------------------------------+
//| expert initialization function                                   |
//+------------------------------------------------------------------+
int init()
  {
   nbTicksRecus = 0;
   return(0);
  }
//+------------------------------------------------------------------+
//| expert deinitialization function                                 |
//+------------------------------------------------------------------+
int deinit()
  {
   Print(messageTicksDebut, nbTicksRecus, messageTicksFin);
   return(0);
  }
//+------------------------------------------------------------------+
//| expert start function                                            |
//+------------------------------------------------------------------+
int start()
  {
   nbTicksRecus = nbTicksRecus + 1;
   return(0);
  }
//+------------------------------------------------------------------+
Quelques remarques: Oui il y a bien un espace à la fin de la première partie du message et un au début de la deuxième partie. La fonction print() reçoit maintenant, non plus un texte directement mais une variable, elle saura s'en débrouiller et aller chercher le texte que la variable désigne au bon endroit. Il y a bien plusieurs éléments fournis à la fonction print(), il y en a trois séparés par des virgules. La fonction print() peut recevoir un nombre quelconque de paramètres (jusqu'à 64), mais au moins un. Elle va les mettre bout à bout pour construire le message à afficher dans le journal. On met donc la première partie du message, le nombre de ticks et la deuxième partie du message. Et vous constatez bien que pour augmenter le nombre de ticks de une unité, on prend la valeur stockée dans nbTicksRecus, on lui ajoute 1 et on stocke le résultat dans ce même emplacement en mémoire, comme ça la valeur du nombre de ticks a augmenté. L'ancienne valeur a bel et bien été écrasée par la nouvelle. Je vais vous faire pratiquer tout ça en vidéo: je vous le montre puis je recommence tout plus lentement pour que vous le fassiez en même temps. Une autre remarque les variables dont on se sert partout dans le programme sont déclarées tout en haut, en dehors des fonctions. On pourra en déclarer à l'intérieur d'une fonction, mais elles ne seront accessibles et n'existeront qu'à l'intérieur de cette fonction. C'est un concept que nous reverrons plus tard.


Au prochain post on travaillera toujours la programmation et on verra l'instruction 'if' qui exécute des instructions seulement si une condition est bien remplie. C'est ce qui nous permet en programmation de faire des choix.

2 commentaires:

  1. Un grand merci pour ce travail énorme et cet esprit de vulgarisation.
    Je me pose la question de comment faire pour avoir la valeur des ticks. Je pense que je trouverai la réponse dans les prochains tutoriaux.
    Bonne continuation et encore merci.

    RépondreSupprimer
    Réponses
    1. Bonjour,
      C'est gentil merci ! J'y tiens à la vulgarisation: tout le monde a le droit d'y arriver, et si tout le monde écoute les médias mainstream, ce n'est même pas la peine d'essayer car selon ces crétins: c'est "compliqué" et une affaire d' "experts" ! Y a pas mieux pour maintenir les moutons loin du succès, et bien dociles devant la télévision.
      oui, effectivement pour avoir la valeurs des ticks, c'est dans les suivants. Et je montrerai tout, il y a plusieurs moyens.

      Supprimer