samedi 18 janvier 2014

IndicatorCounted() et le remplissage de l'historique de l'indicateur 3 / 3.

Et voici finalement un exemple simple d'indicateur stable dans le temps, sans artefacts. Cet indicateur reprend ce qui est expliqué dans les deux posts précédents et donnera une valeur moyenne approchée du cours à chaque période. Nous ne pouvons pas réaliser une moyenne exacte du cours pour les périodes avant l'exécution de l'indicateur, car l'historique ne garde pas en mémoire tous les ticks: il n'y a que les high, low, open et close, soit quatre valeurs seulement du cours à chaque période. Et ces valeurs sont celles du bid, le ask n'est pas du tout sauvegardé dans l'historique. On ne peut avoir le ask qu'au moment où on reçoit le tick car MetaTrader ne l'enregistre pas. Six valeurs par période quatre du bid plus le temps et le volume, c'est mieux que rien et nous permet d'avoir une moyenne qui devrait être relativement proche de la moyenne réelle du cours sur la période. D'autant plus qu'on a la plus haute et la plus basse dont on va se servir. Il faut bien comprendre qu'avoir ces deux valeurs ne nous permet pas de savoir ce qu'a fait le cours durant la période. Nous savons seulement qu'il est resté entre les deux. Nous allons juste utiliser ces deux valeurs et faire ce qu'en trading on appelle la valeur médiane. Une moyenne des quatre bid (high, low open et close) sera faite ultérieurement dans un autre post, et elle sera plus proche de la vraie moyenne que la valeur médiane.

La valeur médiane:


Il s'agit de faire un simple calcul de moyenne entre deux valeurs, mais nous n'allons pas le laisser comme ça nu dans le code de l'indicateur: on va l'habiller dans une fonction ce qui nous permettra de le réutiliser dans d'autres indicateurs, voire experts advisors. Nous la placerons même dans un fichier header qui contiendra de nombreuses fonctions de calculs sur les cours, mais ce fichier c'est pour plus tard. Pour l'instant voici comment faire la fonction:
  • il faut savoir sur quelle période de l'historique on doit la faire. Il nous faut donc un paramètre qui est le numéro de période dans l'historique. C'est un nombre entier (int)
  • on fait le calcul (high + low) / 2 qu'on stocke dans une variable nommée coursMedian déclarée en même temps comme une valeur à virgule (double). Pour être sûr d'obtenir un nombre à virgule, on fait la division par un nombre à virgule sûr et certain: 2.0.
  • on retourne cette valeur au code appelant la fonction.
Voici donc le code de la fonction:

double obtenirCoursMedian(int noPeriode) {
   double coursMedian = (High[noPeriode] + Low[noPeriode]) / 2.0;
   return(coursMedian);
}

L'initialisation de l'indicateur:


Nous reprenons le code du tout premier indicateur que je vous ai proposé, en version améliorée: celui de l'initialisation de l'indicateur dans une fonction: factorisation du code d'initialisation de l'indicateur. Il y a deux choses dans cette fonction:
  1. On fixe le nombre de décimales de précision des valeurs données par l'indicateur avec la fonction IndicatorDigits(). On doit lui fournir un paramètre qui est un nombre entier indiquant le nombre de décimale, normalement 4 ou 5. On va prendre le nombre de décimales de la paire sur laquelle a été lancé l'indicateur. C'est la fonction MarketInfo() qui nous donnera ça, à condition de lui indiquer deux choses:
    1. La paire sur laquelle nous travaillons, qui est donnée par la fonction Symbol().
    2. et ce qu'on lui demande sur cette paire de devises: le nombre de chiffres décimaux. Ce qui est indiqué par la constante MQL4 : MODE_DIGITS. Cette constante nommée cache un nombre entier défini par MetaTrader, mais peut importe, ce nombre aura pour la fonction la signification de vouloir le nombre de décimales.
  2. et notre fonction fixe le nom court de l'indicateur avec la fonction IndicatorShortName(). Cette fonction prend évidemment un paramètre qui est le nom qu'on donne à l'indicateur sous forme de chaine de caractères (string). Il faut que ce nom soit passé en paramètre à notre fonction, sinon elle ne saura pas quoi mettre. Ou bien elle peut définir un nom elle même et le donner à l'indicateur, mais ce serait toujours le même, et ça n'aurait pas d'intérêt. MetaTrader a besoin de ce nom pour pouvoir l'afficher sur les graphiques et indiquer que l'indicateur est présent.
Voici le code complet de notre fonction déjà faite précédemment. J'ai changé le nom du paramètre, car avec un nom en français, c'est plus lisible et on fait bien la différence entre un nom que nous avons donné et un nom fixé par MetaTrader. (eh oui ! il faut toujours améliorer un code dès qu'on voit une amélioration possible ! Ne jamais cesser de le faire.)

void initIndicateur(string nomCourt) {
   IndicatorDigits(MarketInfo(Symbol(), MODE_DIGITS));
   IndicatorShortName(nomCourt);
}

L'initialisation d'un index:


Nous reprenons aussi le code du tout premier indicateur que je vous ai proposé en version améliorée pour initialiser un index d'indicateur: factorisation du code d'initialisation de l'indicateur. C'est la fonction InitIndex qui fait pour l'instant quatre choses:
  1. initialiser le style de traçage de l'index avec ce style passé en paramètre de notre fonction. Ce style possède une valeur par défaut car le plus souvent ce sera une ligne. Comme ça, le plus souvent, nous n'auront pas besoin de le préciser.
  2. initialiser le buffer de l'index avec le tableau qu'on aura déclaré au début du code de l'indicateur. Ce tableau se passe en paramètre de notre fonction par référence, et non par copie (voir le post consacré à ce sujet: passer une valeur par référence.
  3. initialiser le décalage de l'index (shift) qui est un décalage du traçage de cet index seulement. Nombre entier positif ou négatif. J'ajoute une valeur par défaut par rapport à la première version car c'est très souvent 0.
  4. initialiser le début de traçage: c'est un nombre qui indique le nombre de périodes à ne pas tracer en partant de la période la plus ancienne. Utile lorsque les premières valeurs anciennes dans l'historique sont entachées de grosses erreurs car les périodes concernées n'ont pas suffisamment de périodes précédentes pour donner une valeur correcte. J'ajoute ici une valeur par défaut, car le plus souvent ce sera zéro.
Pour toutes ces taches il faut évidemment préciser le numéro de l'index concerné (entre 0 et 7) qu'on aura reçu en paramètre. Voici le code complet de la fonction (la première ligne a été coupée car trop longue pour rentrer entière dans la largeur du post):

void initIndex(int noIndex, double &buffer[], int shift=0,
               int begin=0, int style=DRAW_LINE) {
   SetIndexStyle(noIndex,style);
   SetIndexBuffer(noIndex,buffer);
   SetIndexShift(noIndex, shift);
   SetIndexDrawBegin(noIndex, begin);
}

Tutoriel vidéo pour le code complet de l'indicateur:


Nous reprenons la boucle principale étudiée dans le post précédent sur le remplissage de l'historique. Nous déclarons également sous forme de constante au début du code le nom court de l'indicateur, ainsi que le numéro d'index que nous utiliserons (le 0). Nous plaçons les deux fonctions d'initialisation dans la fonction init() et rien dans la fonction deinit(). Dans la fonction start() on déclare un numéro de période qui va parcourir toutes les périodes de l'historique et un nombre décimal double qui va contenir le résultat du calcul de chaque période avant de le stocker dans le buffer de l'index. En résumé, à chaque passage de la boucle de remplissage de l'historique, on demande le calcul de la valeur médiane à la fonction créée pour ça et on le stocke dans notre variable coursMedian. Puis cette valeur, on la copie dans le buffer à la bonne case, celle numérotée par la valeur de noPeriode. La variable de boucle noPeriode prendra toutes les valeurs de numéro des périodes de l'historique, la première fois, puis seulement les valeurs à récentes à recalculer les fois suivantes. Stocker les valeurs dans le buffer de l'index c'est tout ce qu'il y a faire pour qu'elles soient disponibles pour tout expert advisor qui la demande et qu'elle soit tracées automatiquement sur les graphiques par MetaTrader. (J'aurais pu mettre directement le résultat du calcul dans le tampon, plutôt que de passer par une variable intermédiaire. Mais je préfère comme ça pour bien vous montrer l'extraction de la valeur du calcul puis l'affectation dans le tampon en deux étapes séparées, c'est plus visible pour les débutants) Voici le tutoriel vidéo:


Et le code complet:

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

#property indicator_chart_window
#property indicator_buffers 1
#property indicator_color1 Aqua

//--- input parameters
extern int decalage = 0;

//--- buffers
double tamponCoursMedian[];

//--- constantes
string NOM_COURT_INDICATEUR = "Cours Median";
int    NO_INDEX = 0;

//--- fonctions
double obtenirCoursMedian(int noPeriode) {
   double coursMedian = (High[noPeriode] + Low[noPeriode]) / 2.0;
   return(coursMedian);
}

void initIndicateur(string nomCourt) {
   IndicatorDigits(MarketInfo(Symbol(), MODE_DIGITS));
   IndicatorShortName(nomCourt);
}

void initIndex(int noIndex, double &buffer[], int shift=0,
               int begin=0, int style=DRAW_LINE) {
   SetIndexStyle(noIndex,style);
   SetIndexBuffer(noIndex,buffer);
   SetIndexShift(noIndex, shift);
   SetIndexDrawBegin(noIndex, begin);
}

//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int init()
  {
   initIndicateur(NOM_COURT_INDICATEUR);
   initIndex(NO_INDEX, tamponCoursMedian, decalage);
   return(0);
  }
//+------------------------------------------------------------------+
//| Custom indicator deinitialization function                       |
//+------------------------------------------------------------------+
int deinit()
  {
   return(0);
  }
//+------------------------------------------------------------------+
//| Custom indicator iteration function                              |
//+------------------------------------------------------------------+
int start()
  {
   double coursMedian;
   int noPeriode;
   int nbPeriodesATraiter = Bars - IndicatorCounted();
   for(noPeriode=nbPeriodesATraiter-1 ; noPeriode>=0 ; noPeriode--) {
      coursMedian = obtenirCoursMedian(noPeriode);
      tamponCoursMedian[noPeriode] = coursMedian;
   }
   return(0);
  }
//+------------------------------------------------------------------+

Aucun commentaire:

Enregistrer un commentaire