dimanche 23 février 2014

Le numéro d'une période (bar) en MQL4 partie 2 / 2

Suite du post précédent, nous allons réaliser une fonction qui donne le numéro d'une période selon le calcul (très réfléchi) que je vous ai présenté précédemment. Comme je l'ai expliqué, nous n'utilisons pas la valeur de la variable prédéfinie Bars car cette valeur qui est sensée augmenter de 1 à chaque nouvelle période, peut ne pas augmenter pour des raisons de gestion de mémoire, voire elle peut même diminuer brutalement. Il y a donc des bugs potentiels, latents dans cette façon de faire (et que beaucoup utilisent). Nous utiliserons donc l'horodatage fourni avec la cotation par le serveur, là, au moins il n'y a pas de risque d'arrêt du temps ou de retour dans le passé 

Une fonction donnant un numéro de période:


Pour rappel, pour obtenir le numéro d'une période depuis l'epoch, nous faisons la division euclidienne, c'est-à-dire, prendre la partie entière du résultat de la division de l'horodatage (nombre de secondes depuis l'epoch) par le nombre de secondes dans une période. Et pour cela on exploite les subtilités du langage MQL4 en effectuant simplement la division entre des nombres entiers, ce qui nous donne un résultat entier. Notre fonction sera construite de la même manière que les deux précédentes, et autour de ce calcul. Nous recevons donc en argument obligatoire un horodatage et en argument optionnel le nombre de secondes dans la période voulue, et ce nombre doit être positif. Nous l'initialisons à zéro avec la constante  NULL . La valeur de l'argument ne doit pas être négative. Dans le cas contraire, on ne fera pas le calcul et nous renvoyons la valeur  ERREUR_INT_POSITIF , constante définie précédemment dans un include. La séquence des instructions est alors comme celle des deux fonctions précédentes:
  1. Initialisation de la variable à retourner à la valeur  ERREUR_INT_POSITIF 
  2. Test si la valeur de l'argument est celle par défaut:  NULL , et dans l'affirmative remplacement par la valeur pour la période courante, à demander à la fonction  obtenirNbSecsDsPeriode()  précédemment définie.
  3. Test si l'argument est positif.
  4. Si l'argument est positif, alors faire le calcul et l'affecter à la variable à retourner.
  5. Sinon, afficher une alerte avec un message explicatif et la valeur erronée.
  6. Retourner la variable.
Ce qu'il faut craindre en priorité lorsqu'on fait une division en programmation, c'est la division par zéro. Ici elle est évitée car le nombre de secondes n'est valide que s'il est strictement supérieur à 0, donc différent de zéro. Pour le message d'erreur, on réutilise le message pour les secondes erronées. Voici le code de la fonction à ajouter au fichier header ArithmetiqueTemporelle.mqh:

int obtenirNoPeriode(datetime horodatage, int nbSecsDansPeriode=NULL) {
   int noPeriode = ERREUR_INT_POSITIF;
   if(nbSecsDansPeriode == NULL) { nbSecsDansPeriode = obtenirNbSecsDsPeriode(); }
   bool nbSecsEstValide = (nbSecsDansPeriode > 0);
   if(nbSecsEstValide) { noPeriode = horodatage / nbSecsDansPeriode; }
   else { Alert(" ObtenirNoPeriode() ", msgErrNbSecsNonValide, nbSecsDansPeriode); }
   return(noPeriode);
}

Code de test de cette fonction:


Comme pour les fonctions précédentes, et comme pour toutes les suivantes, nous créons le code de test de la fonction pour s'assurer qu'elle répond aux spécifications qu'on lui demande au travers de différents cas de tests. Nous devons donc lui fournir différents horodatages ( 0 et un autre horodatage ) et soit aucune valeur soit des valeurs de nombre de secondes dans la période aussi bien positifs que négatifs et aussi nul. Les cas proposés sont donc:

Cas de tests:
n° cashorodatagenbSecsDsPerioderésultat retournécommentaires
10000 divisé par le nombre de secondes dans la période courante
2030000 / 300 = 0
30-10ERREUR_INT_POSITIFle mauvais argument compte en priorité
40ARG_INT_EMPTY00 divisé par le nombre de secondes dans la période courante
5120701207 / (60 * Period())1207 divisé par le nombre de secondes dans la période courante
6120730041207 / 300 = 4 + un reste fractionnaire non pris en compte
71207-10ERREUR_INT_POSITIFmauvais argument
81207ARG_INT_EMPTY1207 / (60 * Period())1207 divisé par le nombre de secondes dans la période courante

Dans le code de test, on ne doit pas oublier de déclarer le nombre de cas de tests en début de code dans la section defines. Et dans la section constantes: le nom de la fonction pour les messages affichés. On créé les deux fonctions: une pour exécuter la fonction testée avec ou sans le paramètre optionnel et l'autre pour définir les cas de tests et les faire exécuter et analyser en boucle. On inscrit une ligne dans la fonction init() pour exécuter les tests de cette fonction nouvellement créée. Voici les lignes à ajouter dans le fichier testsArithmetiqueTemporelle.mq4:

// section defines
#define NB_CAS_NO_PERIOD 8

// section constantes
string nomObtenirNoPeriode = "obtenirNoPeriode()"

// section fonctions
int execObtenirNoPeriode(datetime argumentHorodatage, int argumentNbSecsDsPeriode) {
   int resultat;
   if(argumentNbSecsDsPeriode == ARG_INT_EMPTY) {
      resultat = obtenirNoPeriode(argumentHorodatage); }
   else { resultat = obtenirNoPeriode(argumentHorodatage, argumentNbSecsDsPeriode); }
   return(resultat);
}

int testObtenirNoPeriode() {
   int noCas, nbErreurs = 0;
   int resultat;
   datetime argumentsHorodatages[NB_CAS_NO_PERIOD] = {0,
                                                        0,
                                                        0,
                                                        0,
                                                        1207,
                                                        1207,
                                                        1207,
                                                        1207};
   int argumentsNbSecsDsPeriode[NB_CAS_NO_PERIOD] = {0,
                                                                    300,
                                                                    -10,
                                                                    ARG_INT_EMPTY,
                                                                    0,
                                                                    300,
                                                                    -10,
                                                                    ARG_INT_EMPTY};
   string parametresCas[NB_CAS_NO_PERIOD] = {" 0, 0 => ",
                                                     " 0, 300 => ",
                                                     " 0, -10 => ",
                                                     " 0, => ",
                                                     " 1207, 0 => ",
                                                     " 1207, 300 => ",
                                                     " 1207, -10 => ",
                                                     " 1207, => "};
   int resultatsAttendus[NB_CAS_NO_PERIOD];
   resultatsAttendus[0] = 0;
   resultatsAttendus[1] = 0;
   resultatsAttendus[2] = ERREUR_INT_POSITIF;
   resultatsAttendus[3] = 0;
   resultatsAttendus[4] = (1207 / (60 * Period()));
   resultatsAttendus[5] = 4;
   resultatsAttendus[6] = ERREUR_INT_POSITIF;
   resultatsAttendus[7] = (1207 / (60 * Period()));
   for(noCas=0 ; noCas<NO_CAS_NB_SEC_IN_PERIOD ; noCas++) {
      resultat = execObtenirNoPeriode(argumentsHorodatages[noCas],
                                            argumentsNbSecsDsPeriode[noCas]);
      nbErreurs += analyserResultatIntDuTest(resultat,
                                             resultatsAttendus[noCas],
                                             nomObtenirNoPeriode,
                                             parametresCas[noCas]);
   }
   return(nbErreurs);
}

// ajouter dans la fonction init()
   nbErreurs += testObtenirNoPeriode();


Le tutoriel vidéo et les codes complets de l'include et de l'expert de test:




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

//+------------------------------------------------------------------+
//| defines                                                          |
//+------------------------------------------------------------------+

//+------------------------------------------------------------------+
//| DLL imports                                                      |
//+------------------------------------------------------------------+

//+------------------------------------------------------------------+
//| EX4 imports                                                      |
//+------------------------------------------------------------------+

//+------------------------------------------------------------------+
//| includes                                                         |
//+------------------------------------------------------------------+

//+------------------------------------------------------------------+
//| variables globales                                               |
//+------------------------------------------------------------------+

//+------------------------------------------------------------------+
//| constantes globales                                              |
//+------------------------------------------------------------------+

string msgErrNbMinsNonValide = " a reçu un nombre de minutes non valide: ";
string msgErrNbSecsNonValide = " a reçu un nombre de secondes non valide: ";

//+------------------------------------------------------------------+
//| fonctions comptage                                               |
//+------------------------------------------------------------------+

datetime obtenirNoDerniereSecConnue(string paire="NULL") {
   datetime horodatage;
   if(paire=="NULL") { horodatage = TimeCurrent(); }
   else { horodatage = MarketInfo(paire, MODE_TIME); }
   return(horodatage);
}

int obtenirNbSecsDsPeriode(int nbMinsDansPeriode=NULL) {
   int nbSecsDsPeriode = ERREUR_INT_POSITIF;
   if(nbMinsDansPeriode == NULL) { nbMinsDansPeriode = Period(); }
   bool nbMinsEstValide = (nbMinsDansPeriode > 0);
   if(nbMinsEstValide) { nbSecsDsPeriode = 60 * nbMinsDansPeriode; }
   else { Alert("ObtenirNbSecsDsPeriode() ", msgErrNbMinsNonValide, nbMinsDansPeriode); } 
   return(nbSecsDsPeriode);
}

int obtenirNoSecDsPeriode(datetime horodatageSec, int nbSecsDansPeriode=NULL) {
   int noSecDansPeriode = ERREUR_INT_POSITIF;
   if(nbSecsDansPeriode == NULL) { nbSecsDansPeriode = obtenirNbSecsDsPeriode(); }
   bool nbSecsEstValide = (nbSecsDansPeriode > 0);
   if(nbSecsEstValide) { noSecDansPeriode = horodatageSec % nbSecsDansPeriode; }
   else { Alert("ObtenirNoSecDsPeriode() ", msgErrNbSecsNonValide, nbSecsDansPeriode); }
   return(noSecDansPeriode);
}

int obtenirNoPeriode(datetime horodatage, int nbSecsDansPeriode=NULL) {
   int noPeriode = ERREUR_INT_POSITIF;
   if(nbSecsDansPeriode == NULL) { nbSecsDansPeriode = obtenirNbSecsDsPeriode(); }
   bool nbSecsEstValide = (nbSecsDansPeriode > 0);
   if(nbSecsEstValide) { noPeriode = horodatage / nbSecsDansPeriode; }
   else { Alert("ObtenirNoPeriode() ", msgErrNbSecsNonValide, nbSecsDansPeriode); }
   return(noPeriode);
}

//+------------------------------------------------------------------+



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

#include <Constantes.mqh>
#include <UtilitairesTests.mqh>
#include <ArithmetiqueTemporelle.mqh>

//+------------------------------------------------------------------+
//| defines                                                          |
//+------------------------------------------------------------------+

#define NB_CAS_LAST_KNOWN_SEC_NO 5
#define NB_CAS_NB_SECS_IN_PERIOD 5
#define NB_CAS_NO_SEC_IN_PERIOD 7
#define NB_CAS_NO_PERIOD 8

//+------------------------------------------------------------------+
//| constantes de tests                                              |
//+------------------------------------------------------------------+

string nomObtenirNoDerniereSecConnue = "obtenirNoDerniereSecConnue()";
string nomObtenirNbSecsDsPeriode = "obtenirNbSecsDsPeriode()"
string nomObtenirNoSecDsPeriode = "obtenirNoSecDsPeriode()"
string nomObtenirNoPeriode = "obtenirNoPeriode()"

//+------------------------------------------------------------------+
//| fonctions de tests                                               |
//+------------------------------------------------------------------+

datetime execObtenirNoDerniereSecConnue(string argument) {
   datetime resultat;
   if(argument == ARG_STRING_EMPTY) { resultat = obtenirNoDerniereSecConnue(); }
   else { resultat = obtenirNoDerniereSecConnue(argument); }
   return(resultat);
}

int testObtenirNoDerniereSecConnue() {
   int noCas, nbErreurs = 0;
   datetime resultat;
   string arguments[NB_CAS_LAST_KNOWN_SEC_NO] = {ARG_STRING_EMPTY,
                                                 "EURUSD",
                                                 "GBPJPY",
                                                 "EUR",
                                                 ""};
   string parametresCas[NB_CAS_LAST_KNOWN_SEC_NO] = {" sans paramètre => ",
                                                     " \"EURUSD\" => ",
                                                     " \"GBPJPY\" => ",
                                                     " \"EUR\" => ",
                                                     " \"\" => "};
   datetime resultatsAttendus[NB_CAS_LAST_KNOWN_SEC_NO];
   resultatsAttendus[0] = TimeCurrent();
   resultatsAttendus[1] = MarketInfo("EURUSD", MODE_TIME);
   resultatsAttendus[2] = MarketInfo("GBPJPY", MODE_TIME);
   resultatsAttendus[3] = 0;
   resultatsAttendus[4] = 0;
   for(noCas=0 ; noCas<NB_CAS_LAST_KNOWN_SEC_NO ; noCas++) {
      resultat = execObtenirNoDerniereSecConnue(arguments[noCas]);
      nbErreurs += analyserResultatDatetimeDuTest(resultat,
                                                   resultatsAttendus[noCas],
                                                   nomObtenirNoDerniereSecConnue,
                                                   parametresCas[noCas]);
   }
   return(nbErreurs);
}

int execObtenirNbSecsDsPeriode(int argument) {
   int resultat;
   if(argument == ARG_INT_EMPTY) { resultat = obtenirNbSecsDsPeriode(); }
   else { resultat = obtenirNbSecsDsPeriode(argument); }
   return(resultat);
}

int testObtenirNbSecsDsPeriode() {
   int noCas, nbErreurs = 0;
   int resultat;
   int arguments[NB_CAS_NB_SECS_IN_PERIOD] = {ARG_INT_EMPTY, 5, 240, -2, 0};
   string parametresCas[NB_CAS_NB_SECS_IN_PERIOD] = {" sans paramètres => ",
                                                     " 5 => ",
                                                     " 240 => ",
                                                     " -2 => ",
                                                     " 0 => "};
   int resultatsAttendus[NB_CAS_NB_SECS_IN_PERIOD];
   resultatsAttendus[0] = 60 * Period();
   resultatsAttendus[1] = 300;
   resultatsAttendus[2] = 14400;
   resultatsAttendus[3] = ERREUR_INT_POSITIF;
   resultatsAttendus[4] = 60 * Period();
   for(noCas=0 ; noCas<NB_CAS_NB_SECS_IN_PERIOD ; noCas++) {
      resultat = execObtenirNbSecsDsPeriode(arguments[noCas]);
      nbErreurs += analyserResultatIntDuTest(resultat,
                                             resultatsAttendus[noCas],
                                             nomObtenirNbSecsDsPeriode,
                                             parametresCas[noCas]);
   }
   return(nbErreurs);
}

int execObtenirNoSecDsPeriode(datetime argumentHorodatage, int argumentNbSecsDsPeriode) {
   int resultat;
   if(argumentNbSecsDsPeriode == ARG_INT_EMPTY) {
      resultat = obtenirNoSecDsPeriode(argumentHorodatage); }
   else { resultat = obtenirNoSecDsPeriode(argumentHorodatage, argumentNbSecsDsPeriode); }
   return(resultat);
}

int testObtenirNoSecDsPeriode() {
   int noCas, nbErreurs = 0;
   int resultat;
   datetime argumentsHorodatages[NB_CAS_NO_SEC_IN_PERIOD] = {127,
                                                        123456789,
                                                        123456789,
                                                        123456789,
                                                        127,
                                                        127,
                                                        127};
   int argumentsNbSecsDsPeriode[NB_CAS_NO_SEC_IN_PERIOD] = {ARG_INT_EMPTY,
                                                                 ARG_INT_EMPTY,
                                                                 240,
                                                                 900,
                                                                 60,
                                                                 0,
                                                                 -4};
   string parametresCas[NB_CAS_NO_SEC_IN_PERIOD] = {" 127, EMPTY => ",
                                                     " 123456789, EMPTY => ",
                                                     " 123456789, 240 => ",
                                                     " 123456789, 900 => ",
                                                     " 127, 60 => ",
                                                     " 127, 0 => ",
                                                     " 127, -4 => "};
   int resultatsAttendus[NB_CAS_NO_SEC_IN_PERIOD];
   resultatsAttendus[0] = 127 % (60 * Period());
   resultatsAttendus[1] = 123456789 % (60 * Period());
   resultatsAttendus[2] = 69;
   resultatsAttendus[3] = 189;
   resultatsAttendus[4] = 7;
   resultatsAttendus[5] = 127 % (60 * Period());
   resultatsAttendus[6] = ERREUR_INT_POSITIF;
   for(noCas=0 ; noCas<NO_CAS_NB_SEC_IN_PERIOD ; noCas++) {
      resultat = execObtenirNbSecsDsPeriode(argumentsHorodatages[noCas],
                                            argumentsNbSecsDsPeriode[noCas]);
      nbErreurs += analyserResultatIntDuTest(resultat,
                                             resultatsAttendus[noCas],
                                             nomObtenirNoSecDsPeriode,
                                             parametresCas[noCas]);
   }
   return(nbErreurs);
}

int execObtenirNoPeriode(datetime argumentHorodatage, int argumentNbSecsDsPeriode) {
   int resultat;
   if(argumentNbSecsDsPeriode == ARG_INT_EMPTY) {
      resultat = obtenirNoPeriode(argumentHorodatage); }
   else { resultat = obtenirNoPeriode(argumentHorodatage, argumentNbSecsDsPeriode); }
   return(resultat);
}

int testObtenirNoPeriode() {
   int noCas, nbErreurs = 0;
   int resultat;
   datetime argumentsHorodatages[NB_CAS_NO_PERIOD] = {0,
                                                        0,
                                                        0,
                                                        0,
                                                        1207,
                                                        1207,
                                                        1207,
                                                        1207};
   int argumentsNbSecsDsPeriode[NB_CAS_NO_PERIOD] = {0,
                                                                    300,
                                                                    -10,
                                                                    ARG_INT_EMPTY,
                                                                    0,
                                                                    300,
                                                                    -10,
                                                                    ARG_INT_EMPTY};
   string parametresCas[NB_CAS_NO_PERIOD] = {" 0, 0 => ",
                                                     " 0, 300 => ",
                                                     " 0, -10 => ",
                                                     " 0, => ",
                                                     " 1207, 0 => ",
                                                     " 1207, 300 => ",
                                                     " 1207, -10 => ",
                                                     " 1207, => "};
   int resultatsAttendus[NB_CAS_NO_PERIOD];
   resultatsAttendus[0] = 0;
   resultatsAttendus[1] = 0;
   resultatsAttendus[2] = ERREUR_INT_POSITIF;
   resultatsAttendus[3] = 0;
   resultatsAttendus[4] = (1207 / (60 * Period()));
   resultatsAttendus[5] = 4;
   resultatsAttendus[6] = ERREUR_INT_POSITIF;
   resultatsAttendus[7] = (1207 / (60 * Period()));
   for(noCas=0 ; noCas<NO_CAS_NB_SEC_IN_PERIOD ; noCas++) {
      resultat = execObtenirNoPeriode(argumentsHorodatages[noCas],
                                            argumentsNbSecsDsPeriode[noCas]);
      nbErreurs += analyserResultatIntDuTest(resultat,
                                             resultatsAttendus[noCas],
                                             nomObtenirNoPeriode,
                                             parametresCas[noCas]);
   }
   return(nbErreurs);
}

//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+

int init()
  {
   int nbErreurs = 0;
   nbErreurs += testObtenirNoDerniereSecConnue();
   nbErreurs += testObtenirNbSecsDsPeriode();
   nbErreurs += testObtenirNoSecDsPeriode();
   nbErreurs += testObtenirNoPeriode();
   Print("-------------------------------------------------------------------");
   Print("TESTS DE ArithmetiqueTemporelle.mqh TERMINES AVEC ", nbErreurs, " ERREUR(S).");
   return(0);
  }

//+------------------------------------------------------------------+
//| Custom indicator deinitialization function                       |
//+------------------------------------------------------------------+

int deinit()
  {
   return(0);
  }

//+------------------------------------------------------------------+
//| Custom indicator iteration function                              |
//+------------------------------------------------------------------+

int start()
  {
   return(0);
  }

//+------------------------------------------------------------------+

Aucun commentaire:

Enregistrer un commentaire