vendredi 17 janvier 2014

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

Suite de la gestion de l'historique d'un indicateur. Nous allons maintenant voir comment gérer ça. Dans le post où je vous fais faire un premier indicateur, la gestion est mauvaise et il se produit des artéfacts dans le traçage de la courbe de l'indicateur de temps en temps. Cela signifie qu'il y a un problème dans la continuité des valeurs calculées. Premièrement nous devons remplir l'historique de l'indicateur simplement parce que notre indicateur doit être valide pour toutes les périodes dans l'historique géré par MetaTrader. Il faut que notre indicateur propose une valeur pour chaque période, comme ça, il y a continuité temporelle pour pouvoir analyser le passé et tenter d'anticiper le futur des cours. Un problème supplémentaire vient se mêler à tout ça: la mémoire de l'ordinateur est limitée. Or Metatrader, doit gérer l'historique des cours en remontant loin dans le passé, en général plus de 50 000 périodes du passé chargées en mémoire. Et cela pour cinq valeurs enregistrées par période pour une seule paire de devises: time, volume, high, low, open et close. Cela fait 5 valeurs de 8 octets chacune plus le temps sur 4 octets: 5*8+4=44 octets par période, à multiplier par un nombre de périodes entre 50 000 et 100 000, nous obtenons une mémoire nécessaire entre 2 200 000 et 4 400 00 octets pour une seule paire de devises. Et c'est sans compter les éventuels indicateurs à ajouter sur la paire. Chaque signal (ou index) d'indicateur doit enregistrer pour ce même nombre de périodes des nombres sur 8 octets, soit entre 400 000 et 800 000 octets par index d'indicateur (un indicateur pouvant aller jusqu'à huit index, on comprend pourquoi c'est limité). MetaTrader a donc besoin pour une paire de devises avec un indicateur mono-index de 2 600 000 octets à 5 200 000 octets, soit entre 2,5 et 5Mo. Si le logiciel MetaTrader fonctionne jour, et nuit sans discontinuer (et il est sensé le faire, il est la pour ça, parce qu'un trader manuel doit quand même dormir un peu !) le nombre de périodes de l'historique va gonfler et faire monter la note en coût d'utilisation mémoire. Alors MetaTrader fait des recyclages de mémoire tampon, et des réallocations des tampons, bref, il fait sa tambouille pour gérer la mémoire dont il dispose. Nous avons donc des recyclages de mémoire, et de temps en temps, et, si nous ne remplissons pas automatiquement l'historique, nous voyons apparaitre des vielles valeurs du passé dans le présent. Sur le graphique nous avons de grands sauts brutaux de la courbe. Ce sont les artéfacts qui apparaissent avec le premier indicateur que je vous ai proposé.

La variable prédéfinie Bars:


La taille de l'historique en nombre de périodes est donnée par la variable Bars. Il faut donc parcourir tout l'historique du cours, faire les calculs et mettre un résultat dans le buffer de l'indicateur pour chaque période. Le buffer a une taille gérée par MetaTrader et donc il a la même que celle des tableaux de l'historique du cours. Donc Bars nous donne sa taille. Alors la dernière case du tableau possède le numéro Bars-1 (la numérotation commence à zéro), et doit contenir la toute première valeur de l'historique à calculer par notre indicateur évidemment. C'est la valeur la plus ancienne que l'indicateur calculera et donnera.

L'historique du cours:


Je l'ai déjà précisé: l'historique du cours de la paire de devise est stocké dans des tableaux gérés par MetaTrader. Ces tableaux sont mis à notre disposition pour pouvoir lire dedans et obtenir les cours pour la paire de devise sur laquelle nous avons lancé l'indicateur (ou l'expert). Nous disposons grâce à MetaTrader, des plus-hauts des périodes (tableau High[]), des plus-bas (tableau Low[]), des cours d'ouverture des périodes (tableau Open[]), des cours de fermeture des périodes (tableau Close[]), des horodatages d'ouvertures des périodes (tableau Time[]) et des volumes d'échanges des périodes (tableau Volume[]). On lit donc les valeurs de la période en cours avec l'indice 0, celle qui vient de se terminer avec l'indice 1 et celle d'avant avec l'indice 2. Etc, jusqu'à l'indice Bars-1. A chaque changement de période, il y a une nouvelle période en cours n°0, celle qui était en cours (donc ex n°0) devient la n°1 , l'ex n°1 devient la n°2 etc, de manière automatique et géré par MetaTrader.

La fonction IndicatorCounted():


Puisque qu'il faut remplir l'historique, MetaTrader nous indique à chaque fois que la fonction start() de l'indicateur est exécutée, le nombre de périodes qui n'ont pas changées depuis la dernière exécution. Cette fonction nous donne le nombre de périodes déjà calculées, qui sont restées inchangées, et donc qui n'ont pas besoin d'être recalculées. C'est-à-dire que la première fois que l'indicateur est exécuté, IndicatorCounted() donne un nombre de périodes déjà calculées de zéro: il faut tout calculer, tout l'historique. La taille de l'historique en nombre de périodes est donnée par la variable Bars. Il faut donc parcourir tout l'historique du cours, faire les calculs et mettre un résultat dans le buffer de l'indicateur pour chaque période.

Respecter la demande de calcul de MetaTrader:


Par l'intermédiaire de la fonction IndicatorCounted() MetaTrader nous demande de calculer certaines périodes. La première fois, la fonction nous donne la valeur de 0, ce qui signifie qu'il y a 0 valeurs déjà calculées. On devra alors parcourir tout l'historique du cours et effectuer les calculs et écritures des résultats dans le buffer de l'indicateur pour toutes les périodes de la plus ancienne n°Bars-1 à la plus récente n°1, ou même n°0 si notre calcul nous permet d'utiliser une période qui n'est pas finie. La fois suivante, si la période en cours n'est pas une nouvelle, IndicatorCounted() nous donnera la valeur de Bars, c'est-à-dire Bars valeurs qui n'ont pas besoin d'être recalculées, car toutes les valeurs auront été calculées et n'auront pas changé, sauf la période n°0, celle en cours, car on vient de recevoir un tick, qui fait partie de cette période, et donc les high, low, open, close et volume de cette période sont susceptibles de changer et il faut alors la recalculer. Il vient un moment ou la période en cours est nouvelle et du coup IndicatorCounted() donne la valeur Bars-1 car pour éviter des problèmes de transition d'une période à l'autre, MetaTrader demande de recalculer une période de plus en remontant dans l'historique: celle qui vient de finir.
De temps en temps MetaTrader opère des recyclages et réallocations de mémoire tampon, et demande alors de recalculer des valeurs en remontant plus dans l'historique car suite au recyclage, un certain nombre de périodes ne sont plus bonnes (toujours les plus récentes). La fonction IndicatorCounted() donnera la valeur nécessaire. C'est à nous de parcourir l'historique en conséquence et de faire les calculs demandés.

L'algorithme nécessaire pour chaque indicateur programmé:


Le logiciel MetaTrader nous indique donc le nombre de périodes dans l'historique avec la variable prédéfinie Bars, et le nombre de périodes les plus anciennes à ne pas recalculer avec la fonction IndicatorCounted(). l'algorithme de base de chaque indicateur devra donc être une boucle de parcours de l'historique de la période à calculer la plus ancienne à la période la plus récente. Des deux valeurs précédentes fournies par MetaTrader on peut en déduire une nouvelle qui est le nombre de période à traiter par le calcul suivant (différence entre le nombre total de périodes et celles qu'on ne doit pas recalculer):
nbPeriodesATraiter = Bars - IndicatorCounted();
Les numéros des cases des tableaux étant inférieurs d'une unité on parcourra les tableaux d'historique de nbPeriodesATraiter-1 jusqu'à 1 ou 0 selon l'indicateur avec une boucle for ou une boucle while:

int noPeriode;
for(noPeriode=nbPeriodesATraiter-1 ; noPeriode>0 ; noPeriode--) {
   instructions de lecture de l'historique,
   de calculs et d'écriture dans le buffer de l'indicateur
}
Dans cette boucle for: le numéro de période commence comme prévu à nbPeriodesATraiter-1, est décrémenté de 1 à chaque passage de boucle par noPeriode-- et la boucle continue tant que noPeriode>0. Ou bien utiliser la boucle while sans oublier qu'il faut gérer manuellement les changements de numéro de période:

int noPeriode = nbPeriodesATraiter - 1 ;
while(noPeriode>0) {
   instructions de lecture de l'historique,
   de calculs et d'écriture dans le buffer de l'indicateur
   noPeriode--;
}
Voilà pour l'algorithme principal d'un indicateur. Un exemple au prochain post, histoire de vous laissez digérer un peu ce qu'on vient de voir.

Aucun commentaire:

Enregistrer un commentaire