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);
  }
//+------------------------------------------------------------------+

Aucun commentaire:

Enregistrer un commentaire