mercredi 31 juillet 2013

L'instruction switch en MQL4: initiation à la programmation des experts advisors

Aujourd'hui, ce sera l'instruction ' switch ' qui permet plus de choix que l'instruction ' if - else '. Nous restons dans le comptage des mouvements, mais cette fois-ci, nous comptons en plus des hausses et des baisses, les stagnations. Nous avons donc trois possibilités, et l'instruction ' if - else ' ne permet que de traiter deux cas seulement: quand la condition est remplie et quand elle ne l'est pas. L'instruction ' switch ' quant à elle, permet d'exécuter des instructions en fonction de la valeur que peut prendre une variable, ou une expression. (Une expression est un calcul ou un test ou... tout ce qui peut donner un résultat). Sa syntaxe est la suivante:

switch(expression ou variable)  {
   case résultat 1:
      instruction a;
      instruction b;
      ...
      break;
   case résultat 2:
      instruction c;
      instruction d;
      ...
      break;
   case résultat n:
      instruction x;
      instruction y;
      ...
      break;
   default:
      instruction z
      ...
}

L'instruction ' break ' est optionnelle, mais elle est très utile ici, voire même indispensable selon le cas: elle dit de s'arrêter sur elle et de sortir du bloc. Sans elle, les instructions du cas suivant seraient exécutées, et même les suivants s'il n'y a pas de break non plus. La syntaxe est donc simple: après ' switch ' on place l'expression à calculer ou la variable dans les parenthèses, puis on ouvre un bloc avec une accolade qu'il faut refermer. Dans ce bloc les différents cas signalés par ' case ' et la valeur sous forme de constante (les variables ne sont pas acceptées) et le caractère deux-points pour commencer les instructions. On fini la séquence d'instructions du cas par un ' break ' de préférence, à moins qu'on ne veuille que la séquence continue sur les instructions du cas suivant. Un cas peut être vide d'instructions, et dans ce cas, sans le ' break ' il est regroupé avec le cas suivant. On termine généralement, c'est optionnel mais très vivement conseillé, par le cas ' default ' qui est comme son nom l'indique un cas par défaut, pour toutes les autres valeurs. Ces autres valeurs peuvent avoir été prévues ou non. Dans cette éventualité, c'est une gestion des erreurs qu'il faut faire dans le cas ' default ', parce que la valeur n'aura pas été prévue. La gestion d'erreur est simplement une suite d'instructions à exécuter dans un cas qu'on aurait pas prévu.

Pour appliquer cette instruction, nous allons rester dans le même principe de comptabilisation des mouvements, et on va en ajouter un ! Eh oui ! Dans l'expert réalisé précédemment, celui qui compte les hausses et les baisses, on avait le total des ticks reçus car certains ne faisaient pas varier le cours (ni hausse, ni baisse). Ce sont ceux-là qu'on veut maintenant explicitement comptabiliser. Mais alors il y a trois cas différents, et l'instruction if est alors dépassée puisqu'elle ne propose que deux possibilités, à moins d'imbriquer les ' if ' les uns dans les autres. Mais là, le code serait vraiment pas beau et illisible. Plus on imbrique et moins c'est propre et clair. Il ne faut pas non plus faire trop de cas dans le ' switch '. On a donc les variables nbHausses, nbBaisses et nbStagnations pour compter les trois possibilités. Je profite de ce tutoriel pour vous présenter un concept mathématique fondamental que nous utiliserons souvent et une astuce arithmétique.

Les variations

L'instruction switch ne pourra pas faire de tests de comparaisons, mais seulement vérifier l'égalité avec la valeur précisée dans chaque cas. On utilisera pas de valeur logique à tester car il n'y aurait que deux possibilités et l'instruction if suffirait. Nous allons devoir placer dans une seule valeur à comparer, les trois possibilités avec trois valeurs différentes. Pour commencer, ce qui nous intéresse, ce sont les variations du cours et surtout son sens de variation. Si les valeurs grandissent, diminuent ou stagnent. On va donc déterminer la variation entre deux valeurs du cours: l'actuelle et la précédente. Si on veut l'écart entre les deux, c'est à dire la distance qui les sépare, il faut faire la différence, et en général il vaut mieux prendre l'arrivée moins le départ. Si les valeurs grandissent, l'arrivée sera plus grande et donc on fait la soustraction d'une valeur moins une valeur plus petite, et donc le résultat est positif. Inversement si les valeurs diminuent, l'arrivée aura une valeur plus petite, on fera alors la soustraction d'une valeur moins une autre plus grande, et on aura donc un résultat négatif. Et s'il les valeurs stagnent, la différence sera nulle car une valeur moins la même valeur donne zéro. Ce qu'il faut surtout retenir c'est que si les valeurs grandissent (croissantes) la différence sera positive, négative dans le cas où elles diminuent et nulle s'il n'y a pas de variations.

La valeur absolue

C'est bien joli tout ça mais on aura plein de valeurs différentes positives et négatives mais, tellement que c'est ingérable avec l'instruction ' switch '. Impossible même car les possibilités sont bien trop nombreuses (plusieurs milliards). Il faut donc réduire leur nombre, d'autant plus que la seule chose qui nous intéresse ici, c'est leur signe: positive, négative ou nulle. On va donc réduire les valeurs à 1, -1 ou 0 par une division avec elles même. En divisant une valeur par elle même, on obtient 1. Petit soucis: on perd le signe car -0.034-0.034=1. Pour conserver le signe on va diviser par la même valeur, sans prendre le signe. Pour cela il y a la valeur absolue: c'est la même valeur, mais sans le signe et on la note en encadrant avec deux barres verticales. On fait donc -0.034|-0.034|=-0.0340.034=-1. Et si on a valeur positive sur valeur absolue de la même valeur, enlever le signe ne change rien et le résultat reste positif. On a donc notre calcul qui nous donnera 1 ou -1. Mais attention, on ne peut pas faire ce calcul si la variation est nulle, car on diviserait par zéro ce qui est mathématiquement interdit, sinon on pourrait prouver n'importe quoi. Pas de soucis dans ce cas, car la valeur de la variation est déjà ce qu'on veut obtenir.

L'algorithme de l'expert advisor

Notre expert advisor devra donc comptabiliser les mouvements de hausses, de baisses et les stagnations (variation nulle). La fonction start() qui se déclenche à chaque tick devra donc
  • copier la valeur du cours actuel (du tick précédent) dans la variable cours précédent.
  • calculer le nouveau cours actuel à partir du bid et du ask.
  • calculer la variation qu'il y a eu entre les deux cours: actuel - précédent.
  • tester si la variation est nulle
    • nulle, alors le sens de la pente vaut 0.
    • non nulle, alors faire la division de la variation par sa valeur absolue pour avoir 1 ou -1.
  • en fonction de la valeur du sens de la pente 1, -1 ou 0 faire les incrémentations des comptes avec une instruction switch.

Voici les étapes à suivre pour créer cet expert advisor:
  • Cliquez sur le bouton au losange jaune dans MetaTrader 4 pour ouvrir l'éditeur de code.
  • Cliquez sur le bouton à la croix verte pour créer un nouveau programme.
  • Sélectionnez Expert Advisor dans l'assistant, puis cliquez sur suivant.
  • Mettez le nom: DetecteurChangementsPente et cliquez sur Terminer.
  • Dans le fichier préparé et ouvert, supprimez les six lignes //---- (pas seulement ce qui y est écrit, il doit y avoir une ligne en moins à chaque fois).
  • Placez-vous sur la ligne vide en dessous de celles commençant par #property et appuyez sur Entrée pour faire une nouvelle ligne vide.
  • Saisissez le code:

double coursActuel, coursPrecedent;
int nbBaisses, nbStagnations, nbHausses;
string messageBilanPentes = "Les baisses, stagnations et hausses sont: ";
string messageErreurValeur = "La valeur du sens de la pente n\'est pas prévue: ";

  • Placez le curseur sur la ligne vide au dessus de return(0); dans la fonction init()
  • Saisissez le code d'initialisation suivant:

   coursActuel = 1.0;
   nbBaisses = 0;
   nbStagnations = 0;
   nbHausses = 0;

  • Placez le curseur sur la ligne vide au dessus de return(0); dans la fonction deinit()
  •  Saisissez le code suivant:

Print(messageBilanPentes, nbBaisses, ", ", nbStagnations, ", ", nbHausses);
  • Placez le curseur sur la ligne vide au dessus de return(0); dans la fonction start()
  •  Saisissez le code suivant:

   int sensPente;
   coursPrecedent = coursActuel;
   coursActuel = (Bid + Ask) / 2;
   double variationCours = coursActuel - coursPrecedent;
   if(variationCours == 0.0) { sensPente = 0; }
   else { sensPente = MathRound(variationCours / MathAbs(variationCours)); }
   switch(sensPente) {
      case -1:
         nbBaisses = nbBaisses + 1;
         break;
      case 0:
         nbStagnations = nbStagnations + 1;
         break;
      case 1:
         nbHausses = nbHausses + 1;
         break;
      default:
         Alert(messageErreurValeur, sensPente);
   }

Voici les explications ligne par ligne:
  • On déclare une variable pour y stocker le sens de la pente (sens de variation). On y stockera -1, 1 ou 0, donc c'est un nombre entier. On le fait à l'intérieur de la fonction, comme ça, cette variable n'existe que dans cette fonction. On n'en a pas besoin ailleurs.
  • on fait la copie de sauvegarde du cours du tick précédent.
  • on calcule la valeur du cours pour le tick actuel par une moyenne du bid et du ask.
  • on déclare une nouvelle variable pour contenir la variation de cours entre les deux ticks, et on fait le calcul et on la stocke dedans en même temps. (la variation du cours, c'est la différence entre l'actuel et le précédent).
  • on teste si la variation est nulle, dans ce cas, le sens de la pente c'est zéro. (il y a bien un double signe égal pour le test d'égalité, car un seul égal, c'est l'affectation d'une valeur à une variable)
  • si elle n'est pas nulle on peut faire le calcul. Ce calcul est la division de la variation par sa valeur absolue. Mais on veut un nombre entier, et pour être sûr de l'avoir, on utilise la fonction MathRound() qui prend un nombre décimal pour en faire un entier en supprimant tout ce qu'il y a après la virgule. Donc dans les parenthèses on y place le calcul qui donnera le sens de la pente 1 ou -1. Pour la valeur absolue, on utilise la fonction MathAbs() qui supprime le signe du nombre qu'on lui met entre les parenthèses. Dans les parenthèses de MathRound() on trouve donc la division de la variation par sa propre valeur absolue. Observez bien, vous devez voir ça clairement. Insistez, il faut le voir. Ne pas laissez quelque chose d'incompris.
  • Ensuite en fonction de la valeur du sens de la pente, on effectue les incrémentations des comptes. Le tout grâce à l'instruction switch. Il y a un dernier cas dans ce bloc: default, qui normalement ne devrait pas servir car la valeur ne devrait pas être autre chose que 1, -1 ou 0. Mais en programmation il faut être prudent et si jamais une valeur différente arrive, il y aura donc une alerte qui nous donnera la valeur avec le message d'erreur. il y a des instructions break dans chaque cas pour ne pas exécuter le code des cas qui suivent. Le break, stoppe et dit de sortir du bloc.
Voilà pour les explications, et pour des questions et éclaircissements supplémentaires, demandez en commentaires. Je vous fais pratiquez avec moi en vidéo, et je vous passe le code complet en modèle.



//+------------------------------------------------------------------+
//|                                    DetecteurChangementsPente.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"

double coursActuel, coursPrecedent;
int nbBaisses, nbStagnations, nbHausses;
string messageBilanPentes = "Les baisses, stagnations et hausses sont: ";
string messageErreurValeur = "La valeur du sens de la pente n\'est pas prévue: ";

//+------------------------------------------------------------------+
//| expert initialization function                                   |
//+------------------------------------------------------------------+
int init()
  {
   coursActuel = 1.0;
   nbBaisses = 0;
   nbStagnations = 0;
   nbHausses = 0;
   return(0);
  }
//+------------------------------------------------------------------+
//| expert deinitialization function                                 |
//+------------------------------------------------------------------+
int deinit()
  {
   Print(messageBilanPentes, nbBaisses, ", ", nbStagnations, ", ", nbHausses);
   return(0);
  }
//+------------------------------------------------------------------+
//| expert start function                                            |
//+------------------------------------------------------------------+
int start()
  {
   int sensPente;
   coursPrecedent = coursActuel;
   coursActuel = (Bid + Ask) / 2;
   double variationCours = coursActuel - coursPrecedent;
   if(variationCours == 0.0) { sensPente = 0; }
   else { sensPente = MathRound(variationCours / MathAbs(variationCours)); }
   switch(sensPente) {
      case -1:
         nbBaisses = nbBaisses + 1;
         break;
      case 0:
         nbStagnations = nbStagnations + 1;
         break;
      case 1:
         nbHausses = nbHausses + 1;
         break;
      default:
         Alert(messageErreurValeur);
   }
   return(0);
  }
//+------------------------------------------------------------------+

lundi 29 juillet 2013

Le IF et la clause ELSE en MQL4: initiation à la programmation des experts advisors

Suite au post précédent, nous allons retravailler l'expert advisor qui contenait un petit artefact, en vous présentant un nouvel élément du langage: la clause ' else '. Je rappelle que l'instruction ' if ' exécute les instructions qui sont dans le bloc qu'elle précède seulement si la condition dans les parenthèses est vraie. Mais si elle est fausse !! On veut pouvoir exécuter des instructions dans ce cas là aussi !! C'est là qu'intervient la clause ' else ' qui signifie littéralement ' autrement ' ou ' sinon ', c'est équivalent.

if(condition) {
    instruction 1 si condition réalisée;
    instruction 2 si condition réalisée;
    ...
    instruction n si condition réalisée;
} else {
    instruction 1 si condition non réalisée;
    instruction 2 si condition non réalisée;
    ...
    instruction k si condition non réalisée;
}

On a donc un ' if ' suivi par une condition entre parenthèses puis on ouvre une accolade pour y placer le bloc d'instructions qui sera exécuté si la condition est remplie. Puis les instructions à exécuter sous condition et une accolade fermante pour terminer ce bloc et après ce bloc un ' else ' et un nouveau bloc d'instructions entre accolades qui contiennent les instructions à exécuter si la condition n'est pas remplie. Je ne vais pas en dire plus, il n'a y pas grand chose à dire. On va modifier de suite l'expert advisor qui compte les mouvements pour qu'il ne compte plus le premier mouvement car il part d'une valeur arbitraire, ce qui donne un mouvement qui n'est pas réel dans le cours et qui est comptabilisé. Pour cela on va utiliser une variable booléenne qui dira si c'est le premier tick reçu. On fait les opérations de copie et de calcul du nouveau cours, puis si c'est le premier tick on met faux dans cette variable car après ce tick, les autres ne seront pas le premier tick, sinon on comptabilise les mouvements. Je récapitule autrement:

  1. on initialise la variable estPremierTick à vrai en plus des autres variables.
  2. le premier tick arrive.
  3. on fait notre copie du coursActuel arbitraire dans le coursPrecedent
  4. on calcule notre nouveau coursActuel
  5. on teste si c'est le premier tick
    • oui c'est le premier passage
    • on met faux dans la variable estPremierTick car ce ne sera plus valable dès le suivant.
    • traitement fini pour le premier tick
  6. second tick qui arrive
  7. on fait notre copie de coursActuel vers coursPrecedent.
  8. on calcule notre nouveau cours
  9. on teste si c'est le premier tick
    • non, la variable ne vaut plus vrai, elle vaut faux.
    • on passe alors à la clause else
    • on teste si l'un est plus grand que l'autre et inversement et les codes adéquats seront exécutés comme l'expert le fait déjà.
    • le second tick est traité
  10. un nouveau tick arrive, et la variable vaut toujours faux, et ça ne changera pas puisque rien ne la remettra à vrai, donc c'est le bloc else qui sera désormais toujours exécuté.

Je vous détaille bien l'algorithme et la séquence qui va se passer car le plus dur pour débuter en programmation, c'est de voir ces choses-là, ce que le programme va faire étape par étape, et par conséquent on a aussi du mal à imaginer un code pour faire quelque chose. Ça vient avec la pratique, ne vous inquiétez pas. Et même les programmeurs expérimentés ne sortent pas un code parfait au premier jet. Un code se travaille pour mieux maîtriser le problème, c'est un art, et malgré les années d'expérience, on ne sort pas un code parfait du premier coup. (Je ne suis pas en train de dire que ce code est parfait, il y a sûrement mieux à faire. Mais sous cette forme il est adapté aux débutants, pour la compréhension). Je vais vous donner les étapes à suivre pour réaliser cet expert à partir du précédent, puis le code complet pour référence. (Pour apprendre et maîtriser, il faut faire toutes les étapes, détailler, être pointilleux et soigneux, ne pas rechigner à faire toutes les étapes. Tout ça pour dire qu'un copier coller du code complet ne vous fera pas apprendre). Je ne vais pas trop mettre de notions dans ce post, et faire simple car dans le code que vous allez réaliser il y a des blocs imbriqués, et c'est pas évident d'y voir clair au début. (deux ' if ' dans un bloc ' else '). Juste un petit point sur le nommage: j'ai nommé la variable estPremierTick et ça peut vous sembler pas bien élégant. Certes, mais dans le nom d'une variable on doit pouvoir savoir le type de donnée qu'elle contient. Et une valeur vrai ou faux doit répondre à une question par oui ou par non, c'est une question sans mot interrogatif, une question qui commence par "est-ce que". Version anglaise, que je trouve plus élégante: "isFirstTick". Ici avec juste "premierTick", on ne sait pas que c'est une valeur booléenne, on la prendrait facilement pour un nombre décimal qui serait la valeur du premier tick. Le nom "premierTick" est ambigu. C'est pour faire un code "clean". Et ça c'est réellement professionnel !

  1. Dans le navigateur arborescent de gauche, déroulez les experts en cliquant sur la croix.
  2. Faites un clic droit sur l'expert CompteMouvements et Modifier dans le menu contextuel.
  3. Une fois l'éditeur ouvert, agrandissez la fenêtre de l'expert.
  4. Placez le curseur à la fin de la deuxième ligne des déclarations:  double coursActuel, coursPrecedent; et appuyez sur le touche Entrée.
  5. Saisissez  bool estPremierTick;  
  6. Allez dans la première section, dans la fonction init(), placez le curseur à la fin de la ligne où on initialise le nombre de ticks reçus à zéro. Appuyez sur Entrée.
  7. Saisissez  estPremierTick = true
  8. Allez directement dans la dernière section dans la fonction start(), et sélectionnez 7 lignes en commençant par le premier if jusqu'à  nbTicksRecus + 1
  9. Une fois les 7 lignes sélectionnées (surlignées en bleu) appuyez sur la touche TAB (tabulation, à gauche du A) pour ajouter une indentation sur ces sept lignes. C'est un niveau d'imbrication supplémentaire où vont se trouver ces lignes.
  10. Cliquez à la fin de la ligne du calcul de la moyenne entre Bid et Ask, après le point-virgule et appuyez sur la touche Entrée pour faire une nouvelle ligne.
  11. Saisissez
    if(estPremierTick){
       estPremierTick = false;
    } else {
  12. Placez le curseur à la fin de la ligne d'incrémentation du nombre de ticks, après le point-virgule de  nbTicksRecus + 1; et appuyez sur la touche Entrée.
  13. Sur la nouvelle ligne vide placez une accolade fermante pour terminer le bloc du ' else '. Ce bloc contient les sept lignes qu'on a décalé vers la droite.
  14. Cliquez sur le bouton disquette bleue pour sauvegarder.
  15. Cliquez sur le bouton Compile pour recréer le fichier exécutable, car on a amélioré l'expert précédent et donc on recrée un fichier exécutable qui écrase le précédent.
  16. Basculez sous MetaTrader 4, et allez à l'arborescence des experts consultants à gauche.
  17. Glissez et déposer l'expert CompteMouvements sur un graphique.
  18. Attendez un moment et faites un clic droit sur le graphique et dans l'option Experts Consultants cliquez sur Retirer pour l'arrêter.
  19. Allez cliquer sur l'onglet Experts dans la fenêtre du bas pour constater les messages.
  20. Constatez le message qui annonce les comptes des hausses et des baisses.

Vous constatez dans le code qu'on a ajouté une variable booléenne pour savoir si c'est le premier tick. Qu'on l'initialise à true (vrai). Quand arrive un tick, après la copie dans le précédent et le calcul du nouveau, on teste si c'est le premier tick. Oui pour la première fois on met alors la variable à faux, pour que ça ne soit plus vrai pour la suite. Les fois suivantes, la condition du premier tick est fausse, on exécute le code du ' else ' qui est de déterminer s'il y a une hausse et s'il y a une baisse. Voici le code complet:

//+------------------------------------------------------------------+
//|                                             CompteMouvements.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 nbHausses, nbBaisses, nbTicksRecus;
double coursActuel, coursPrecedent;
bool estPremierTick;
string debutMessage = "Il y a eu ";
string milieuMessage = " baisses et ";
string finMessage = " hausses sur ";

//+------------------------------------------------------------------+
//| expert initialization function                                   |
//+------------------------------------------------------------------+
int init()
  {
   coursActuel = 1.0;
   nbBaisses = 0;
   nbHausses = 0;
   nbTicksRecus = 0;
   estPremierTick = true;
   return(0);
  }
//+------------------------------------------------------------------+
//| expert deinitialization function                                 |
//+------------------------------------------------------------------+
int deinit()
  {
   Print(debutMessage, nbBaisses, milieuMessage, nbHausses, finMessage, nbTicksRecus);
   return(0);
  }
//+------------------------------------------------------------------+
//| expert start function                                            |
//+------------------------------------------------------------------+
int start()
  {
   coursPrecedent = coursActuel;
   coursActuel = (Bid + Ask) / 2;
   if(estPremierTick){
      estPremierTick = false;
   } else {
      if(coursActuel > coursPrecedent){
         nbHausses = nbHausses + 1;
      }
      if(coursActuel < coursPrecedent){
         nbBaisses = nbBaisses + 1;
      }
      nbTicksRecus = nbTicksRecus + 1;
   }
   return(0);
  }
//+------------------------------------------------------------------+

Voilà pour l'amélioration de cet expert. Vous allez le pratiquer avec moi en vidéo, je vous montre d'abord et on reconmmence et j'irai plus lentement et détaillerai bien pour que vous le fassiez avec moi. Comme je vous disais, nous reviendrons sur cet algorithme (procédé pour faire quelque chose, ou bien autrement dit séquence d'instructions qui permettent de faire quelque chose ou résoudre un problème). Nous y reviendrons et nous en ferons un indicateur intéressant grâce à ces deux comptages qui sont réalisés ici. Nous en tirerons peut-être une avancée dans l'analyse technique des cours, car à ma connaissance, il n'existe pas d'indicateur tel que celui que j'imagine. Ce sera pour plus tard, car notre priorité est un EA qui travaille pour nous. Je laisse le mystère en suspens... Au prochain post l'instruction ' switch ' qui permet d'exécuter du code en fonction de nombreux cas possibles, et non pas seulement deux comme avec le 'if else '. (J'ai oublié à la fin de la vidéo de faire compiler ! Pensez à compiler après la sauvegarde par le clic sur le bouton disquette ).


mercredi 17 juillet 2013

Les conditions en MQL4: initiation à la programmation des experts advisors

L'instruction if

Quelque chose d'important, et sans ça la programmation serait très restreinte: les conditions. Une condition permet de faire un choix, de n'exécuter une série d'instructions (ou une seule) seulement si une condition est remplie. Cette condition est testée avec une instruction ' if '. C'est la traduction anglaise de ' si '. On met après le ' if ' la condition à respecter pour que les instructions qui suivent soient exécutées. ' if ' est considéré dans la documentation de MQL4 comme un opérateur. Peu importe le nom qu'on donne à cette chose, elle réalise le même travail quelque soit le langage de programmation. Elle se présente comme suit:

if(condition) {
    instruction 1;
    instruction 2;
    ...
    instruction n;
}
instruction suivante toujours réalisée;

Le ' if ' suivi par une condition entre parenthèses puis on ouvre une accolade pour y placer le bloc d'instructions qui sera exécuté si la condition est remplie. Puis les instructions à exécuter sous cette condition et une accolade fermante pour terminer ce bloc et après ce bloc les instructions qui continuent le programme et qui seront exécutées de toute façon. Nous allons maintenant détailler un peu la condition.

La condition

Une condition est une expression qui donne une valeur booléenne, c'est-à-dire vrai ou faux. Si la valeur résultante est vrai, alors la condition est remplie, si elle vaut faux, elle n'est pas remplie. Qu'est-ce qui peut donner une valeur booléenne ? Comme on l'a vu avant il y a des variables qui peuvent contenir une telle valeur. J'avais donné comme exemple  bool enMouvementHaussier = false donc si je mets cette variable dans les parenthèses de la condition, c'est suffisant. Par exemple:

if(enMouvementHaussier) {
    instruction d'ordre d'achat;
}
instruction suivante toujours réalisée;

Si la valeur contenue dans la variable enMouvementHaussier est true (vrai) alors l'ordre d'achat dans la paire d'accolades est exécuté. Et on peut même en combiner plusieurs de manière logique avec des connecteurs logiques qui sont les opérateurs NON, OU et ET. L'opérateur NON prend le contraire de ce qui suit (si la valeur est vrai, il la transforme en faux et inversement). L'opérateur OU connecte deux valeurs et il faut que l'une des valeurs au moins soit vraie pour que le résultat soit vrai. (il faut l'une ou l'autre). L'opérateur ET demande à ce que les deux valeurs soient vraies pour que le résultat soit vrai. (il faut l'une et l'autre). En MQL4 ces opérateurs se notent:

  • NON: avec le point d'exclamation !. Par exemple:  !enMouvementHaussier;  signifie le contraire soit "pas en mouvement haussier" et si  enMouvementHaussier = true; , alors  !enMouvementHaussier  vaut  false .
  • OU: avec le double pipe: || (ALT GR+6). Par exemple  enMouvementHaussier || enMouvementBaissier  qui vaudra vrai si l'un ou l'autre vaut vrai, c'est-à-dire s'il y a un mouvement quel qu'il soit.
  • ET: avec le double esperluette: &&. Par exemple:  enMouvementHaussier && enAcceleration  qui vaudra vrai si l'une et l'autre valent vrai. Il faut les deux à la fois, c'est à dire qu'il y a une hausse et une accélération.

Les tests

C'est très bien tout ça, et on peut faire plein de combinaisons, mais ça ne dit toujours pas comment on peut décider de mettre vrai ou faux dans une variable ! Pouvoir calculer un vrai ou un faux, ça se fait avec un test. Il y a dans un langage de programmation, des opérations qui produisent une valeur vrai ou faux. Ces opérations sont des tests logiques. Des comparaisons d'égalité, d'infériorité ou de supériorité. Le compilateur lit ces tests à faire entre des valeurs, il les effectue et il produit un résultat vrai ou faux.

  • test d'égalité: a == b teste si les deux valeurs sont égales et donne la valeur vrai si c'est le cas, faux sinon. (double signe égal pour l'opérateur d'égalité.)
  • test d'inégalité: a != b teste si les deux valeurs sont différentes et renvoie la valeur vrai si c'est le cas et faux sinon.
  • test d'infériorité stricte: a < b teste si a est plus petit que b et renvoie vrai si c'est le cas, faux sinon.
  • test d'infériorité large: a <= b teste si a est inférieur ou égal à b et renvoie vrai si c'est le cas, faux sinon.
  • test de supériorité large a >= b teste si a est plus grand ou égal à b et renvoie vrai si c'est le cas, faux sinon.
  • test de supériorité stricte: a > b teste si a est plus grand que b et renvoie vrai si c'est le cas, faux sinon.

Que faire avec ces tests ? On peut stocker le résultat dans une variable:
  enMouvementHaussier = (coursActuel > coursPrecedent); 
Puis utiliser cette variable dans la condition du if ou alors la combiner avec des connecteurs logiques NON, OU ou ET pour produire un autre résultat logique. On peut aussi mettre directement le test dans la condition du if:


if(coursActuel > coursPrecedent) {
    instructions dans le cas ou le cours actuel
    est plus grand que le précédent;
}
instruction suivante toujours réalisée;

On va tester ces déterminations de mouvement haussiers et baissiers et voir que la détermination d'une tendance n'est pas si simple que juste regarder si le cours actuel est plus grand ou plus petit que le précédent. On va donc faire un expert advisor basé sur ce principe pour compter les mouvements dans un sens et dans l'autre.

Expert Advisor primitif de comptage de mouvements

Première chose: on peut facilement obtenir le bid et le ask par les variables prédéfinies du même nom: Bid et Ask. Des variables prédéfinies sont bel et bien des variables et s'utilisent comme les autres sauf nous ne les avons pas déclarées et ni choisi leur nom et ni affecté une valeur, c'est MetaTrader qui a fait tout ça. Nous avons juste à les utiliser, mais sans leur affecter de valeur car c'est MetaTrader qui doit s'en charger. D'ailleurs on ne saurait pas quoi mettre comme valeur car nous n'avons pas les éléments nécessaires pour le faire. On va donc utiliser ces valeurs d'offre et de demande, mais comme le broker les font varier indépendamment du cours réel, on va tenter de retrouver la valeur du cours réel en faisant la moyenne des deux, car le cours réel se trouve approximativement au milieu, entre les deux. on va donc faire (Bid + Ask) / 2.
Deuxième chose: il faut comparer deux valeurs du cours qui se sont suivies. On va donc copier la valeur qui a été prise au tick précédent dans une variable à part avant de l'écraser par la nouvelle valeur. Comme ça on aura les deux valeurs successives qu'on pourra comparer. On va donc faire:

    coursPrecedent = coursActuel;
    coursActuel = (Bid + Ask) / 2;

A la première ligne le cours actuel est copié dans la variable cours précédent. Le cours actuel était celui du tick précédent qui est resté. Puis à la deuxième ligne le cours actuel prend la nouvelle valeur issue du calcul de la moyenne du bid et du ask. Il faudra initialiser le cours actuel avec une valeur quelconque au début, car au premier tick reçu, on en a besoin avant même de lui affecter sa première valeur. Et donc on aura forcément un mouvement dès le départ entre la valeur d'initialisation et celle du premier tick. Ce mouvement de départ est un artefact de l'algorithme, du programme tel qu'on l'a fait. Il faut coder autrement pour que ce soit parfait. Il faudrait rajouter du code pour l'éviter, mais on s'en fout et on ne va pas se compliquer la vie pour cet exemple. On va alors faire toutes ces étapes:
  1. Démarrer MetaTrader 4 si ce n'est pas déjà le cas.
  2. Cliquez sur le bouton au losange jaune pour démarrer l'éditeur de code.
  3. Une fois l'éditeur de code ouvert, cliquez sur le bouton à la croix verte pour un nouveau programme.
  4. Sélectionnez Expert Advisor et cliquez sur le bouton "suivant".
  5. Mettez le nom "ComptageMouvements" et cliquez sur le bouton "Terminer".
  6. Agrandissez la fenêtre de code et effacez toutes les lignes avec  //----  Il ne doit y avoir qu'une seule ligne vide par section.
  7. Placez-vous en haut du fichier, dessous les lignes  #property  cliquez pour y placer le curseur et appuyez sur la touche Entrée pour faire une nouvelle ligne vide.
  8. Saisissez le code suivant
  9. int nbHausses, nbBaisses, nbTicksRecus;
    double coursActuel, coursPrecedent;
    string debutMessage = "Il y a eu ";
    string milieuMessage = " baisses et ";
    string finMessage = " hausses sur ";
    
  10. Cliquez sur la ligne vide de la première section, pour y placer le curseur et saisissez:
  11. coursActuel = 1.0;
    nbBaisses = 0;
    nbHausses = 0;
    nbTicksRecus = 0;
    
  12. Dans la deuxième section, cliquez sur la ligne vide pour y placer le curseur et saisissez:
  13. prin
    Appuyez sur la touche Entrée pour valider la proposition
    de l'auto-complétion.
    (debutMessage, nbBaisses, milieuMessage, nbHausses, finMessage, nbTicksRecus);
    
  14. Allez dans la troisième section et cliquez dans la ligne vide pour y mettre le curseur et saisissez:
  15. coursPrecedent = coursActuel;
    coursActuel = (Bid + Ask) / 2;
    if(coursActuel > coursPrecedent){
        nbHausses = nbHausses + 1;
    }
    if(coursActuel < coursPrecedent){
        nbBaisses = nbBaisses + 1;
    }
    nbTicksRecus = nbTicksRecus + 1;
    
  16. Cliquez sur le bouton avec la disquette bleue pour sauvegarder;
  17. Cliquez sur le bouton "Compile" pour créer le fichier exécutable.
  18. Vérifiez qu'il n'y a pas d'erreurs, sinon corrigez, puis retournez dans MetaTrader
  19. Déroulez l'arborescence de Experts Consultants de la petite fenêtre de gauche.
  20. Glissez et déposez l'expert ComptageMouvements sur un graphique.
  21. Au bout d'un moment Faites un clic droit sur le graphique
  22. Dans le menu contextuel, à Experts consultants, cliquez sur Retirer.
  23. Allez dans la fenêtre du bas, cliquez sur l'onglet Experts.
  24. Vous pouvez constater les comptages de mouvements de hausses et de baisses, ainsi que le total des ticks reçus.

Je vais un peu commenter ce code. Premièrement, on peut, lorsqu'on déclare des variables de même type, les déclarer ensemble sur la même ligne en les séparant par des virgules. Deuxièmement, à l'initialisation, on fixe le cours actuel à une valeur arbitraire, donc autant choisir celle qui a le plus de sens: j'ai choisis l'équité parfaite entre les devises de la paire, c'est-à-dire qu'elles valent autant l'une que l'autre donc un rapport entre leurs valeurs de 1. Je tape 1.0 parce qu'il faut un nombre à virgule, vu que le cours est stocké dans une variable de type nombre décimal (double). Les comptes de mouvements sont évidemment mis à zéro. Maintenant, dans la fonction start(). On commence par copier la valeur de coursActuel qui est celle du tick précédent (ou 1.0 si c'est le premier tick) pour sauvegarder temporairement dans coursPrecedent, histoire de pouvoir comparer l'évolution. Ensuite on écrase l'ancienne valeur coursActuel par la nouvelle qui est le calcul de la moyenne entre bid et ask. Puis il n'y a qu'à comparer, si le cours actuel est plus grand que le précédent on augmente les hausses de 1, et si le cours actuel est plus petit que le précédent on augmente les baisses de 1. Et pour finir, dans tous les cas on augmente le nombre de ticks reçus de 1. Toutes ces opérations d'augmentation s'appelent dans le jargon informatique et mathématique: incrémenter de 1. A l'arrêt de l'expert, il faut évidemment afficher les comptes. Conservez ce petit expert, j'y reviendrai pour faire un indicateur qui peut être vraiment intéressant et dont je viens d'avoir l'idée. D'ailleurs, tous ceux qui ont des idées d'amélioration, d'utilisation de ce code peuvent les proposer en commentaires, n'hésitez pas, partagez. Maintenant, le code complet pour que vous puissiez avoir une référence et la même chose en vidéo, je vous le montre et je vous fais ensuite pratiquer avec moi plus lentement.

//+------------------------------------------------------------------+
//|                                           ComptageMouvements.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 nbHausses, nbBaisses, nbTicksRecus;
double coursActuel, coursPrecedent;
string debutMessage = "Il y a eu ";
string milieuMessage = " baisses et ";
string finMessage = " hausses sur ";

//+------------------------------------------------------------------+
//| expert initialization function                                   |
//+------------------------------------------------------------------+
int init()
  {
   coursActuel = 1.0;
   nbBaisses = 0;
   nbHausses = 0;
   nbTicksRecus = 0;
   return(0);
  }
//+------------------------------------------------------------------+
//| expert deinitialization function                                 |
//+------------------------------------------------------------------+
int deinit()
  {
   Print(debutMessage, nbBaisses, milieuMessage, nbHausses, finMessage, nbTickRecus);
   return(0);
  }
//+------------------------------------------------------------------+
//| expert start function                                            |
//+------------------------------------------------------------------+
int start()
  {
   coursPrecedent = coursActuel;
   coursActuel = (Bid + Ask) / 2;
   if(coursActuel > coursPrecedent){
      nbHausses = nbHausses + 1;
   }
   if(coursActuel < coursPrecedent){
      nbBaisses = nbBaisses + 1;
   }
   nbTicksRecus = nbTicksRecus + 1;
   return(0);
  }
//+------------------------------------------------------------------+



J'espère que ça vous a été profitable, que vous progressez. Si vous avez besoin d'éclaircissements, demandez-les en commentaires. Pour le prochain post nous allons en remettre une couche sur le ' if ' avec son extension: la clause ' else '. Comme ça vous pratiquerez un peu plus cette notion, vous la comprendrez mieux et la maîtriserez mieux. Il faut battre le fer tant qu'il est chaud !

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.

jeudi 11 juillet 2013

Premier programme Expert Advisor

Alors, aujourd'hui nous allons être technique, mais ne prenez pas peur ! Il s'agit simplement d'une petite manipulation dans l'éditeur de code et de trois lignes de programmes qui vont être soigneusement expliquées. Notre premier programme ne déroge pas à la tradition de la programmation avec un "Hello World !". Il s'agit en fait des tous premiers programmes qu'on fait faire aux débutants pour qu'il sachent comment avec un langage de programmation, communiquer avec l'utilisateur. C'est-à-dire que le programme lorsqu'il fonctionne peut envoyer des messages à l'utilisateur, et c'est très utile. Nous verrons deux manières de le faire en utilisant deux fonctions différentes. Et ces programmes appelés "Hello World !" ne font qu'envoyer ce message de salutations, juste histoire d'afficher un message. Nous aurons en même temps une petite découverte de la structure d'un Expert Advisor. Rassurez-vous, elle est assez simple !

Pour commencer il faut que MetaTrader 4 soit ouvert, puis il faut cliquer dans la barre d'outils, sur le bouton avec l'icône en losange jaune et un point d'exclamation à l'intérieur. Ce bouton démarre l'éditeur de code de MetaTrader. En s'ouvrant vous constaterez une grande place vide, et deux petites fenêtres en bas et à droite. Nous allons demander un nouveau programme en cliquant sur le bouton à gauche dans la barre d'outils; celui avec la feuille blanche écrite et la croix verte, qui signifie "ajout". Il y a alors un assistant qui démarre pour nous demander le type de programme que l'on veut créer. Je rappelle qu'il y en a trois principaux: Expert Advisors (les programmes qui vont analyser et trader), les indicateurs (programmes qui ne font qu'analyser et dessiner sur le graphique pour donner des indications au trader) et les scripts (programmes qui assistent le trader dans son trading manuel pour faire des tâches répétitives, fastidieuses et avec des calculs, et ainsi aller plus vite). L'assistant propose également de faire une "Librairie", un include et de générer d'après un template. Ne vous inquiétez pas ! Voici ce dont il s'agit:

  • Librairie: fichier contenant des fonctions prêtes à être utilisées par d'autres programmes.
  • Include: fichier contenant des définitions de constantes (valeurs à utiliser) ou des déclarations de fonctions externes qu'on va utiliser (elles sont dans une librairie). Ce fichier se met en entête de notre programme pour que le compilateur sache, lorsqu'il va analyser notre programme, à quoi correspond les constantes qu'on utilise ou bien qu'on utilise correctement des fonctions externes.
  • Template: en fait l'assistant ne fait que préparer un fichier avec déjà présent des éléments qui vont nous être nécessaires selon le type de programme qu'on souhaite faire (EA, indicateur, script, librairie, include). Comme ça nous n'avons pas besoin de les saisir au clavier. Ici, avec cette dernière option, il nous propose directement que nous choisissions notre propre modèle. Il ne fera rien d'autre qu'ouvrir ce modèle qui contient déjà des éléments que nous avons préparé.

Nous choisissons donc un Expert Advisor. Cliquez sur cette option si elle n'est pas déjà cochée puis cliquez sur le bouton "suivant". L'assistant nous demande maintenant quelques informations d'auteur. Ainsi que le nom du programme et s'il y a des paramètres dont ce programme aurait besoin. Nous n'avons pas besoin de paramètres pour l'instant. Mettez juste un nom au programme tel que "Hello World". Changez les informations d'auteur si MetaQuotes vous dérange. Mais elles n'influent en rien sur le programme. Vous pourrez même les changer après, car elles apparaissent en clair en haut du code. Cliquez sur "Terminer".

Une fenêtre de code s'est ouverte. On voit en premier lieu un cadre en haut où il y a le nom que nous avons donné plus une extension ".mq4". et les informations de copyright dans le même cadre. On peut changer toutes ces informations cela n'aura aucune influence sur le programme et ne changera pas le nom du fichier qui restera "Hello World.mq4". En fait si les lignes commencent pas un double slash "//", cela signifie que c'est un commentaire, et le compilateur s'en fout, il passe sans regarder. Il y a plusieurs lignes de commentaires dans ce fichier de code. Vous devrez apprendre à les ignorer pour se concentrer sur le code, et s'il y en a trop les supprimer. Agrandissez la fenêtre et nous pouvons constater qu'il y a plusieurs choses, les commentaires mis à part:

  • Les cadres d'intitulés mis en commentaires: Il ne servent qu'à repérer les différentes parties du code. En tant que cadres il ne sont pas gênants.
  • Des petites lignes de commentaires "//----" : supprimez ces lignes elles sont gênantes pour visualiser le code. Elles étaient là pour qu'on puisse y mettre des commentaires, mais contrairement à la plupart des programmeurs, je pratique ce qu'on appelle le "clean code": c'est-à-dire en autres choses que les commentaires sont bannis. Si on a besoin de commentaires pour comprendre ce qu'on a codé, c'est que c'est très mal codé ! Je vous montrerai comment faire un code propre qui est tellement clair et explicite que vous le comprendrez trop facilement et les commentaires deviendront totalement inutiles, et même gênants. Une fois supprimés, vous avez une ligne avec une accolade ouvrante, un retour à la ligne et une ligne vide sauf avec des espaces au début pour que le code s'aligne avec l'accolade ouvrante. Puis un retour à la ligne avec un "return (0);" aussi aligné avec l'accolade. Et enfin un retour à la ligne et une accolade fermante alignée elle aussi. Et ça dans chacune des trois parties.
  • Des #property en dessous du cadre entête principal: ces lignes définissent des valeurs (propriétés) utilisables dans le programme. Ici des informations de copyright. On s'en fout pour l'instant. Je vous expliquerai les properties dans un autre post quand nous en aurons besoin.
  • Trois sections construites sur le même modèle:
    • expert initialization function : la fonction qui va s'exécuter une seule fois, quand le programme va démarrer
    • expert deinitialization function : la fonction qui va s'exécuter une seule fois, quand le programme va être arrêté.
    • expert start function : la fonction qui va être exécutée à chaque fois que MetaTrader reçoit une nouvelle mise à jour des cours en temps réel. C'est-à-dire environ un fois par seconde. Ça peut être plus, ça peut être moins, c'est variable en fonction du moment de la journée.

Chaque section commence par un mot clef du langage car il est en bleu: int. Puis il y a le nom de la fonction: init, deinit ou start. avec une paire de parenthèses. Ensuite l'accolade ouvrante, la ligne vide qu'on a laissé, un return (0); sans oublier le point-virgule puis l'accolade fermante. Le zéro est en vert car l'éditeur le considère comme un nombre dans le programme. les mots-clefs du langage sont en bleu et les mots en noir sont des noms librement choisis, sauf que les noms de ces fonctions-là ne peuvent être changés car MetaTrader va appeler des fonctions qui se nomment ainsi. Si on les change, ça ne fonctionnera plus. Comme vous pouvez le voir avec les property: l'éditeur met en vert-bleuté ce qu'il considère comme du texte dans le programme. C'est-à-dire que le programme prendra pour des données textuelles. Et ces données doivent être encadrées par des guillemets simples ou doubles pour être reconnues comme des données textuelles. Nous allons tout de suite les utiliser. Tout d'abord sachez qu'une fonction est un ensemble d'instructions qui font faire quelque chose au programme. On demande d'exécuter cet ensemble d'instructions en appelant la fonction tout simplement par son nom. La fonction est construite en mettant sur la première ligne le type de la valeur qu'elle donnera à la fin de son exécution, ici int pour integer, soit un nombre entier (O, 1, 2, 3, ... et aussi -1, -2, -3 ...). Puis il y a le nom de la fonction qu'on peut donner librement. (sauf certaines qui ont obligation de porter un nom précis) et des parenthèses (nous verrons plus tard ce qu'on peut y placer). Il y a une accolade ouvrante pour délimiter le début des instructions de la fonction. Puis les instructions. En général ces instructions finissent par une instruction return qui donne la valeur que la fonction doit retourner. En gros le résultat de la fonction. Mais ici, ce résultat est un code d'erreur 0 qui signifie tout s'est bien passé. Donc MetaTrader appelle une fonction nommée init(), et reçoit quand elle est finie, la valeur 0 qui signifie que tout s'est bien passé. Si on détecte que quelque chose s'est mal passé dans la fonction, il faudra retourner un nombre différent de 0. Puis on ferme l'accolade pour dire que c'est la fin de la fonction.

  1. Placez-vous dans la ligne vide de la première section, celle de la fonction nommée init().
  2. Nous allons appeler une fonction déjà toute prête qui nous permet d'informer l'utilisateur: c'est la fonction print(). Cette fonction envoie du texte dans l'onglet "Experts" de la fenêtre du bas de MetaTrader. Donc si on veut recevoir un message ici, c'est la fonction print(). Comme tout appel de fonction il faut mettre des parenthèses. Et dans ces parenthèses, il faut placer ce qu'on veut que le fonction prenne comme données. Ici on veut qu'elle prenne un message.
  3. Tapez "prin", et vous verrez que l'éditeur vous aura proposé une liste de termes pouvant correspondre, et avec les quatre premières lettres il aura trouvé print. Vous pouvez alors appuyer sur "Entrée" pour valider. Je vous conseille d'utiliser cette fonction, comme ça vous éviterez de nombreuses erreurs dues au fautes de frappe. Cette fonctionnalité d'un éditeur de code s'appelle l'auto-complétion. (ça complète automatiquement). Vous constaterez que les fonctions standards de MetaTrader sont colorées en violet et en plus il faut aura mis la majuscule que la fonction réclame. Quand vous tapez donc une fonction standard, soyez donc attentif au fait qu'elle soit en violet et commence par une majuscule. Alors vous ne vous n'aurez pas fait de fautes dans la saisie du nom de cette fonction. (et ça arrive souvent !)
  4. tapez une parenthèse ouvrante.
  5. Tapez le texte "Hello Trading World, l\'Expert Advisor s\'initialise !" entre guillemets doubles. On doit mettre l'antislash devant le guillemet simple qui sert d'apostrophe car sinon il indique la fin de la chaîne et c'est pas ce qu'on veut. l'antislash est un échappement, c'est a dire qu'il permet d'annuler l'effet d'un caractère ou de lui donner une signification précise. Ici le guillemet simple a déjà une signification précise: il indique le début ou la fin d'un texte. Si on le place tel quel, le compilateur va considérer qu'on a fini notre texte, or il y aura le reste du texte qui va suivre et il ne va pas comprendre ce qu'il y a après car ça n'aura aucune signification pour lui. On place donc un échappement pour dire d'annuler l'effet du guillemet simple. Vous pouvez constater que sans l'antislash, la couleur verte s'arrête au guillemet, avec elle continue jusqu'au guillemet double.
  6. tapez la parenthèse fermante.*
  7. Tapez un point-virgule. Chaque instruction doit se terminer par un point-virgule, c'est la règle pour dire au compilateur que l'instruction est complète. Ici notre instruction était d'appeler la fonction Print().
  8. Nous continuons et tapons une ligne similaire pour la deuxième section, celle de la dé-initialisation (lors de l'arrêt du programme): Print("Good-bye Trading World ! L\'Expert Advisor se dé-initialise !");
  9. Cliquez sur le bouton disquette pour enregistrer, ou bien faites la combinaison de touches CTRL+S.
  10. Cliquez sur le bouton Compile pour que le compilateur analyse et transforme ça en un fichier exécutable par l'ordinateur, et surtout exécutable par MétaTrader. Vous vérifiez dans la fenêtre du bas qu'il y ait indiqué "0 error(s), 0 warning(s)", qui signifie que le compilateur a tout analysé et n'a pas trouvé d'erreurs et qu'il a pu transformer ça en fichier exécutable. Les warnings sont des avertissements qui disent qu'on ne doit pas ou plus faire comme ça, mais que ce n'est pas une erreur empêchant la compilation. Si vous avez une erreur ou un warning, vérifiez ce que vous avez tapé.

Maintenant retournez dans MetaTrader, et vous cliquez sur la croix de expert consultant dans la petite fenêtre de gauche pour dérouler tous les experts existants, et oh ! Surprise, le notre s'y trouve déjà ! Pour démarrer cet expert, c'est très simple, il suffit d'une glisser-déposer: vous cliquez sur l'expert voulu, et sans relâcher vous le déplacer jusqu'au graphique du cours désiré, ici EURUSD par exemple et vous relâchez dessus. Une fenêtre apparaît et qui nous permet de faire les réglages de paramètres, mais nous n'en avons pas ici, donc cliquez sur OK. L'expert Advisor a démarré et vous pouvez cliquer sur l'onglet experts de la fenêtre du bas pour constater qu'il y a plusieurs messages. Les messages sont chronologiques et s'empilent les uns sur les autres, donc le plus haut est le plus récent. Il y en a trois, du plus ancien au plus récent (du bas vers le haut):

  • Hello World EURUSD,M1 : loaded successfully. C'est MetaTrader qui nous dit que notre expert "Hello World" a été chargé en mémoire sans problèmes pour le cours EURUSD et timeframe M1.
  • Hello World EURUSD,M1 : Hello Trading World ! L'expert Advisor s'initialise !. C'est notre message quand la fonction init() a été exécutée.
  • Hello World EURUSD,M1 : initialized. C'est MetaTrader qui dit que l'initialisation c'est bien passée, car a la fin de l'exécution de la fonction init(), il a reçu le code d'erreur 0, qui veut dire tout va bien.

Chaque message d'expert advisor, est précédé de son nom, du cours sur lequel il travaille et du timeframe. Car on peut ouvrir plusieurs cours de la même paire de devises sur différents timeframes et avec chacun les experts advisors de notre choix. Nous allons maintenant l'arrêter en faisant un clic droit sur le graphique et dans le menu sur Experts Consultants cliquez sur Retirer dans le sous menu qui s'ouvre. Nous obtenons en bas, quatre messages cette fois-ci. Notre message de Good-bye, puis un message de MetaTrader pour dire que la fonction deinit() s'est bien passée (valeur de retour 0), puis un message "uninit reason 1" donné par MetaTrader pour dire pour quelle raison l'expert a été arrêté parce qu'il y a plusieurs raisons qui peuvent provoquer l'arrêt d'un expert (ici la numéro 1), et enfin un dernier message de MetaTrader "removed" pour dire que notre programme a bien été déchargé de la mémoire.

Nous allons maintenant ajouter une instruction dans la dernière section et voir une autre manière d'afficher des messages. Ce n'est plus la fonction Print(), mais la fonction Alert(). Cette fonction ne met rien dans la fenêtre du bas, elle ouvre sa propre fenêtre à côté du graphique et donne le message, mais aussi tous les messages précédents. Cette fenêtre ne bloque pas l'éxecution de notre fonction car elle est indépendante. La fonction Alert() fait ouvrir cette nouvelle fenêtre, mais continue son exécution tout de suite et donc ne bloque pas notre programme quand cette fenêtre est ouverte. Nous allons la placer dans la fonction start. Je rappelle que c'est la fonction que MetaTrader appelle à chaque fois qu'il y a une mise à jour des cours. Elle va donc nous inonder de messages ! vous arrêterez donc l'expert advisor rapidement par le clic droit sur le graphique, car il y a en plus une sonnerie à chaque message, normal comme l'indique le nom de la fonction c'est une alerte !

  1. Allez dans la section expert start function.
  2. Dans la ligne vide tapez: ale .
  3. L'auto-complétion vous propose déjà la bonne fonction tapez la touche entrée.
  4. Complétez la ligne pour obtenir: Alert("nouveau tick"); On demande donc d'afficher le texte "nouveau tick". N'oubliez pas le point-virgule de la fin d'instruction.
  5. Cliquez sur le bouton disquette, ou faites CTRL+S pour sauvegarder.
  6. Cliquez sur le bouton Compile. Et vérifier que le compilateur vous indique bien 0 erreur et 0 warning. Sinon vérifiez ce que vous avez tapé. Le code complet doit identique à ceci (peu importe les informations d'auteur et copyright) :

//+------------------------------------------------------------------+
//|                                                  Hello World.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"

//+------------------------------------------------------------------+
//| expert initialization function                                   |
//+------------------------------------------------------------------+
int init()
  {
   Print("Hello Trading World ! L\'expert Advisor s\'initialise !");
   return(0);
  }
//+------------------------------------------------------------------+
//| expert deinitialization function                                 |
//+------------------------------------------------------------------+
int deinit()
  {
   Print("Good-bye Trading World ! L\'Expert Advisor se dé-initialise !");
   return(0);
  }
//+------------------------------------------------------------------+
//| expert start function                                            |
//+------------------------------------------------------------------+
int start()
  {
   Alert("Nouveau tick");
   return(0);
  }
//+------------------------------------------------------------------+
  1. Allez dans MetaTrader et glissez et déposer l'expert sur le graphique pour le démarrer.
  2. Cliquez sur Ok dans la fenêtre des paramètres.
  3. Vous êtes désormais bombardé de sonneries et de messages au rythme des actualisations du cours.
  4. Sans fermer la fenêtre des messages d'alerte, faire un clic droit sur le graphique puis dans experts consultants du menu contextuel, cliquer sur retirer.
  5. Vous pouvez donc constater que les mises à jours se font à un rythme soutenu dans la journée, souvent plus d'une fois par seconde. Si vous faites ça le soir ou la nuit, c'est plus calme.
  6. Vous constatez aussi les messages d'initialisation et de dé-initialisation dans le journal des experts advisors en bas.

Voilà pour votre tout premier EA (Expert Advisor), qui ne sert à rien ! Mais vous avez découvert la structure principale d'un EA et manipulé deux fonctions de messages différentes. Je vous fais pratiquer tout ça dans la vidéo ci-dessous. Vous utiliserez la combinaison de touches ALT+TAB pour passer de la vidéo à MetaTrader. Une précision sur cette combinaison de touches: lorsque vous la faites une fois vous revenez sur le logiciel qui était utilisé juste avant, mais si sans relâcher le ALT vous appuyer encore une fois sur TAB vous remonter plus loin dans l'ordre chronologique des logiciels que vous avez utilisés. Vous devrez basculer entre la vidéo de cette page, MetaTrader et l'éditeur de code. Donc les trois plus récents dans l'ordre. Je serai donc là présent, si vous ne coupez pas la vidéo pour vous dire quoi taper dans l'éditeur et quoi faire.


Dans le prochain post je vous initierai à une notion fondamentale en programmation: les variables.