Ce cours de programmation va s'articuler autour d'une stratégie de trading toute simple : les points de pivot. Le principe de la stratégie est détaillé ici : Lien vers le forum.
Besoin d'un petit rappel sur les points de pivots ? Le cours se trouve ici
Besoin d'un petit rappel sur les supports et résistances ? Le cours se trouve ici
Le cours va se diviser en 4 grandes parties :
- Les bases de la programmation qui serviront à réaliser un EA ou un indicateur.
- L'explication des fondamentaux de la programmation métatrader : Comment faut-il penser son algorithme ?
- La construction d'un indicateur : ici l'indicateur de calcul des points de pivot qui sera utilisé par l'EA.
- La construction d'un EA : l'EA prendra les positions en fonction de la stratégie décrite sur le topic. On y ajoutera un certain nombre de paramètres et d'options afin de couvrir un large périmètre des fonctions disponibles nativement dans métatrader.
A l'issue de ce cours vous aurez programmé un indicateur ainsi qu'un EA utilisant cet indicateur.
Ce cours est destiné aux débutants. Par conséquent absolument tout sera détaillé. Si vous êtes déjà un peu plus à l'aise en matière de programmation je vous invite à suivre ce cours en niveau intermédiaire ou expert. La finalité est la même mais on va à l'essentiel.
Dans un premier temps je vous invite à télécharger les fichiers qui nous serviront de support de cours. Indicateur PivotEA Pivot
Les bases de la programmation
Le trading c'est principalement de la manipulation de chiffres et de données temporelles. Par conséquent nous allons surtout voir ce qui est utile et nécessaire en programmation pour utiliser, transformer ces chiffres et ces temps. Vous allez apprendre ici ce que sont les "types" et les "opérations".
Ensuite on utilise souvent des indicateurs qui aident notre EA à prendre des décisions. Il faut savoir parcourir les informations possédées par ces indicateurs. Vous allez apprendre ici la notion de "boucle".
Notre EA prend position suivant certaines conditions. Vous allez donc apprendre à traduire ces conditions en langage compréhensible par l'EA.
Enfin nous allons apprendre à faire du code lisible et réutilisable surtout. Si on a déjà fait un travail on aime bien pouvoir le réutiliser sans avoir à réinventer la roue à chaque fois. Vous allez donc apprendre ce que sont les "fonctions".
Les types et les opérations
En programmation lorsque l'on veut manipuler des chiffres, des objets ou autre, il faut les stocker en mémoire. De la même façon que chez soi on range des objets dans des tiroirs précis (le tiroir du haut pour les déclarations d'impôt par exemple), les valeurs doivent être stockées dans des variables et ces variables doivent être typées c'est à dire que l'on doit dire à l'ordinateur ce qu'elle contiendra.
Voici les différents types qui nous intéressent :
- Les entiers relatifs (10, -20): le mot clé associé est "int" . Par exemple int pips;
- Les nombres réels (1.3648, 1.6387): le mot clé associé est "double". Par exemple double support1;
- Les dates : le mot clé associé est "datetime". Par exemple datetime dernierBougie;
D'autres types seront utilisés :
- Les chaînes de caractères : utiles pour stocker du texte. Le mot clé associé est "string". Par exemple string positionCourante;
- Les booléens : utilisés pour tester des conditions. Le mot clé associé est "bool". Par exemple bool isPositif;
- Les couleurs : utilisés pour les indicateurs surtout. Le mot clé associé est "color". Par exemple color MARapide;
- Il existe d'autres types mais qui ont un usage spécifique et sont peu utilisés pour des EAs et indicateurs simples.
Maintenant que l'on sait de quel type sera notre variable il faut lui donner une valeur.
Pour cela on utilise le symbole égal "=".
int a = 10; A la variable a on lui a assigné la valeur 10. N'oubliez pas de finir les instructions par le point virgule ";"
Je vous avais aussi parlé de manipulation. Nous allons donc voir les opérations que l'on peut réaliser.
- Le modulo "%" : c'est le reste d'une division euclidienne. Par exemple 33/2 c'est aussi 2*16+1 donc 33%2 = 1
- L'incrémentation "++" : c'est propre à la programmation. Il s'agit d'ajouter 1 à la valeur présente dans une variable. Par exemple si a = 5 alors a++ donnera a = 6
- La décrémentation "--" : c'est le contraire de l'incrémentation. On retire 1 à la valeur. Par exemple si a = 5 alors a-- donnera a = 4
Il est possible de combiner plusieurs opérations à la fois pour gagner de la place. Mais ceci se fait au détrimement de la lecture du code.
J'encourage les débutants à écrire chaque étape !
Les conditions
Le principe d'une condition est de tester si un ensemble d'événement s'est réalisé. En fonction du résultat on réalisera une action.
Par exemple SI mon eau boue, ALORS je me mets mes pates, SINON j'attends.
Ici on ne teste qu'un seul événement. Bien entendu on peut en tester plusieurs en même temps.
Les opérateurs de test sont :
- L'égalité : "=="
- La différence : "!="
- Strictement supérieur : ">"
- Supérieur ou égale : ">="
- Strictement inférieur : "<"
- Inférieur ou égal : "<="
Les mots clés permettant de tester les conditions sont "if""else if" et "else".
Ils s'articulent de la façon suivante :
if(condition1) {
//Faire des choses
} else if(condition2) {
//Faire des trucs
} else {
//Faire des machins
}
Cette portion de code se lit de la façon suivante en français. Si la condition1 est vraie alors je fais des choses, sinon si la condition2 est vraie alors je fais des trucs sinon je fais des machins.
Il est possible de comparer plusieurs conditions en même temps.
Pour cela on utilise les opérateurs logiques "&&" (lire et) et "||" (lire ou).
if((condition1 && condition2) || condition3) {
//Faire des choses
}
Cette portion se lit en français de la façon suivante. Si la condition1 ET la condition2 sont vérifées OU si la condition3 est vérifiée alors on fait des choses.
Une autre façon de réaliser les conditions est l'utilisation du "switch/case".
Son principe est un peu différent du "if/else" et dans certain cas permet de faire du code plus clair.
switch(maVariable) {
case 1 :
//Mon code n°1
break;
case 2:
case 3:
//Mon code n°2
break;
default:
//Mon code n°3
break;
}
La première contrainte imposée par cette notion est le type de "maVariable". Cette dernière ne peut être qu'un entier comme présenté ci dessus ou bien un caractère comme 'a' ou 'b'.
Comment se lit cette portion code ?
Le switch va lire le contenu de "maVariable" et tester de haut en bas les différents cas.
Si "maVariable" est égal à 1 alors le code n°1 sera exécuté.
Si "maVariable" est égal à 2 ou 3 alors le code n°2 sera exécuté.
Si "maVariable" est égal à autre chose que 1,2 ou 3 alors on exécute le code n°3.
L'instruction "break" est très importante. Elle permet de stopper l'exécution du code.
Si par exemple on retire le premier "break" après le code n°1, et si "maVariable" est égal à 1 alors on exécute le code n°1 et puisqu'il n'y a pas de break on exécute aussi le code N°2 !
Donc attention à la structure de votre switch/case.
Les boucles
Les boucles sont utilisées pour réaliser un certain nombre de fois une action donnée.
Il en existe deux types :
- La boucle "for" qui permet de réaliser un nombre de fois connu d'action
- La boucle "while" qui permet de réaliser un nombre d'action dont on ne connait pas le nombre
for(int i=0 ; i < 10 ; i++) {
//Mon code
}
La boucle "for" se divise en 3 parties.
"int i = 0" : On indique la valeur de départ de la boucle et quelle variable sera utilisée pour stocker les valeurs. Ici on démarre à 0 et "i" sera utilisé.
"i < 10" : C'est la condition qui marque la fin de la boucle. Tant que i sera inférieur à 10 on reste dans la boucle.
i++ : Comment "i" est incrémenté ? Ici de 1 en 1.
int i =0;
while(i < 10) {
//Mon code
i++
}
La boucle "while" fonctionne différemment. Le code est exécuté tant que la condition est vérifiée. Tant que i est inférieur à 10 alors on exécute le code. Ici on teste la valeur d'un entier mais on peut aussi comparer des nombres "while(Ask > Support)" par exemple où Ask est la cotation et support la valeur du support de la parité.
Deux mots peuvent modifier le comportement d'une boucle.
- Lorsque i sera égal à 5, la boucle sera stoppée.
- continue : permet de sauter une étape
int i =0;
while(i < 10) {
i++
if(i == 5) { continue; } //Mon code
}
Lorsque i sera égal à 5 "mon code" ne sera pas exécuté mais la boucle va quand même continuer.
Les fonctions
Les fonctions sont utilisées pour exécuter des taches répétives. Plutôt que de recopier plusieurs fois une portion de code, on va l'écrire dans une fonction et on pourra la réutiliser.
- Un nom (nom_fonction) : il faut donner un nom sur ce que fait la fonction pour plus de clarté. Par exemple : afficherLibelle, supprimerLigne
- Un type de retour (type_retour) : il faut dire à l'ordinateur ce que doit retourner la fonction. Par exemple : int, bool. Elle peut cependant ne rien retourner auquel cas on indique void
- Des paramètres : ce n'est pas obligatoire. Les paramètres ont un nom et un type. Par exemple : string label, int position
- Une valeur de retour (seulement si le type de retour n'est pas void) : Pour ce faire à la fin de la fonction on ajoute le mot clé "return(valeur)" où valeur est ce que l'on veut que la fonction retourne.
Voici un exemple de définition de fonction :
void supprimerLabel(string label) {
// Code qui supprime le label
}
void supprimerLabel(string label) {
// Code qui supprime le label
}
Les bases fondamentales de MetaTrader
Pratiquement toutes les fonctions et les paramètres disponibles sont déjà expliqués et détaillés dans la documentation du MetaEditor. Pour y avoir accès il suffit de cliquer sur l'icone et de taper ce que l'on cherche. Un certain nombre de résulats sera affiché et l'on pourra accéder à l'explication de la fonction avec un exemple simple d'utilisation.
Tout d'abord il faut savoir qu'il n'y a pas de différences entre un EA et un indicateur. La seule chose qui les différencie c'est le dossier dans lequel se trouve le fichier mq4 (ou ex4) : le dossier "experts" pour les EAs et le dossier "experts/indicators" pour les indicateurs. Leur principe de fonctionnement est rigoureusement le même. Toutefois l'EA, lui, a le droit de passer des ordres.
Un fichier mq4 se divise en 8 parties :
- L'entête qui décrit à qui appartient le programme.
La directive "property" est ce que l'on appelle une direction de précompilation. Ces informations seront présentes au niveau du fichier compilé ex4. Elles permettent ici d'identifier le propriétaire du programme de façon plus ou moins sûre.
- La déclaration des buffers et de comment ils se comportent.
Un buffer est un tableau qui contient des valeurs. Ce sont ces valeurs qui sont utilisées pour dessiner des courbes, afficher des flèches. Ils sont limités au nombre de 8.
Dans cet exemple l'indicateur sera affiché dans la fenêtre principale du graphique. Il affichera une seule courbe qui par défaut sera jaune.
Voici la liste des instructions possibles.
copyright
le copyright du code
link
le lien vers le site de l'auteur en général
indicator_buffers
le nombre de buffers utilisé par l'indicateur. Metatrader impose une limite de 8
indicator_colorX
la couleur associée au buffer n°X
indicator_levelX
permet de choisir le niveau par défaut de l'indicateur
indicator_chart_window
si cette instruction est présente, l'indicateur sera sur le même graphique que le cours
indicator_separate_window
si cette instruction est présente, l'indicateur sera dans une fenêtre séparée
indicator_minimum
la valeur minimum de l'indicateur
indicator_maximum
la valeur maximum de l'indicateur
show_confirm
si cette instruction est présente, l'indicateur affiche une popup de confirmation avant l'exécution d'un script
show_inputs
si cette instruction est présente, l'indicateur affiche la popup des paramètres avant l'exécution d'un script
stacksize
la taille de la pile
- L'ajout des includes
Un "include" est un fichier mqh qui regroupe des fontions. C'est un moyen de réutiliser le code sans alourdir son fichier mq4. Par exemple on sait que à chaque fois que l'on va développer un EA on aimerait bien une fonction qui archive dans un fichier tout ce que fait cet EA. Puisque cette fonction sera réutilisée il est intéressant de l'externaliser dans un fichier mqh. De cette façon on ne verra pas tout le code présent dans notre fichier ce qui augmente inutilement sa taille mais on pourra quand même l'utiliser. Nous aborderons cela plus en détail lors de la création de l'EA.
Exemple :
#include <FonctonArchivage.mqh>
- La déclaration des paramètres.
Viennent ensuite l'ajout des paramètres qui permettront de personnaliser l'utilisation de l'indicateur ou de l'EA. Comme par exemple le paramètre du "TakeProfit" ou celui de la période de la moyenne mobile. Une variable est considérée comme paramètre si elle commence par le mot clé "extern".
Exemple :
extern int TakeProfit = 10; //10 pips de take profit
Ici on défini un paramètre "extern" qui sera un entier "int" et qui s'appellera "TakeProfit".
- Les 3 méthodes de base.
Metatrader utilise systématiquement 3 méthodes. Elles sont appelées lorsque vous placez l'EA ou l'indicateur sur le graphique, lorsque vous le retirez du graphique et enfin lorsque une nouvelle cotation est envoyée par le serveur.
La méthode "init" est appelée à l'ajout sur le graphique.
Exemple :
int init() { }
La méthode "deinit" est appelée à la suppression.
Exemple :
int deinit() { }
La méthode "start" est appelée à chaque cotation. Attention !!!
Le fonctionnement de cette méthode start est primordial et il faut bien le comprendre. Elle n'est pas appelée à intervalle de temps régulier toutes les secondes ou milliseconde mais bien à chaque fois que la parité traitée subit une recotation de la part du broker. Ainsi l'appel peut se faire par exemple au bout de 0.1 seconde puis 2 secondes puis 0.5 seconde etc...
Il faut bien avoir cela en tête quand vous programmez votre EA ou indicateur. Il faut penser en terme de recotation et non en terme d'intervalle de temps.
Exemple :
int start() { }
La première partie s'achève. Vous avez acquis des notions de base en programmation et vous avez pu avoir un aperçu du principe de fonctionnement de metatrader du point de vue du développeur.
N'oubliez pas quand vous programmez, pensez à utiliser l'aide fournie par le MetaEditor !
Vous pouvez dans un premier temps télécharger le code source de l'indicateur ici. Je vous invite à le placer sur un graphique afin que vous puissiez faire la relation "code - affichage graphique".
Que va faire notre indicateur ?
1) Pour chaque jour il va calculer le point de pivot journalier et hebdomadaire ainsi que les supports et les résistances.
2) Pour la journée en cours il va afficher les valeurs des différents supports, résistances et pivots afin d'avoir plus de confort.
#property indicator_chart_window
#property indicator_buffers 8
#property indicator_color1 Yellow
#property indicator_color2 Red
#property indicator_color3 Red
#property indicator_color4 Red
#property indicator_color5 Green
#property indicator_color6 Green
#property indicator_color7 Green
#property indicator_color8 Blue
Dans un premier temps on définit le comportement de notre indicateur :
- Il s'affichera dans la fenêtre principale de la parité (#property indicator_chart_window)
- Il contiendra 8 courbes #property indicator_buffers 8
- Les différentes ont leurs couleurs correspondantes #property indicator_color1 Yellow
Notre indicateur contient peu de paramètre pour ce didactitiel. Ici simplement les paramètres concernant l'affichage : la couleur, la taille et le type de la police.
extern color labelColor = Black;
C'est paramètre (extern) qui est du type couleur (color) qui s'appelle labelColor et dont la valeur par défaut est Black. Ce paramètre nous servira à choisir la couleur des labels que l'on voudra afficher sur le graphique.
extern int fontSize = 8;
C'est paramètre (extern) qui est du type entier (int) qui s'appelle fontSize et dont la valeur par défaut est 8. Ce paramètre permettra de choisir la taille de la police du label.
extern string fontType = "Arial";
C'est paramètre (extern) qui est du type chaîne de caractères (string) qui s'appelle fontType et dont la valeur par défaut est Arial. Ce paramètre nous permet de choisir la police d'écriture du label.
Cette partie est importante ! Ici on définit tous les tableaux qui stockeront les valeurs qui serviront à l'affichage des courbes.
En général on défini aussi les variables globales et statiques
Normalement vous devez remarquer une particularité. Les crochets "[]". Ils servent à indiquer que la variable sera un tableau de valeurs.
double pivots[];
Ainsi définit un tableau qui contiendra des nombres réels.
Deuxième particularité : "[][6]"
On sait que les crochets définissent un tableau donc deux crochets de suite permet de définir des tableaux de tableaux soit un tableau à deux dimensions
Visuellement cela donne le résultat suivant pour un tableau à 2 dimensions (les valeurs sont prises au hasard):
[1,2]
[2,3]
etc...
int init() {
La méthode "init" permet de définir le comportement de notre indicateur.
IndicatorBuffers(8);
On va spécifier dans un premier temps combien de courbes (de buffers) l'indicateur doit afficher.
On en veut pour afficher les 3 supports, les 3 résistances, le point de pivot journalier et le point de pivot hebdomadaire.
SetIndexStyle(0,DRAW_LINE);
Ensuite pour chaque buffer on va définir son type. Ici notre indicateur n'affichera que des courbes donc le paramètre utilisé est "DRAW_LINE". Si nous avions voulu afficher des flèches comme pour un indicateur qui marque un changement de tendance, le paramètre utilisé serait "DRAW_ARROW". Pour plus de détail sur les différentes possibilités de la méthode "SetIndexStyle" il suffit d'aller consulter l'aide du MetaEditor.
SetIndexBuffer(0,pivots);
Enfin on fait le lien avec le tableau de valeur qui permettra d'afficher notre courbe. Ici toutes les valeurs du point de pivot sont stockées dans le tableau "pivots". On indique donc à l'indicateur que pour afficher la première courbe il doit utiliser les valeurs calculées dans "pivots".
En résumé notre première courbe sera une ligne jaune qui affichera les valeurs du point de pivot.
On réitère le procédé pour chaque courbe que l'indicateur doit afficher. On n'est pas obligé d'afficher toujours le même type. Par exemple on peut mélanger des courbes, des flèches, des points, des carrés, etc. Faites parler votre imagination et surtout pensez à la clarté et au confort visuel.
return(0);
}
Enfin la méthode init retourne un int par convention donc il faut rajouter le mot clé "return"
int start() {
On démarre le contenu de la méthode start. Ici on voit qu'elle retourne un int comme pour la fonction init. Donc on sait que l'on aura une instruction "return" en fin de méthode.
int countedBars = IndicatorCounted();
On récupère le nombre de bougies pour lesquelles l'indicateur a déjà fait le calcul.
if(countedBars > 0){
Si le nombre de bougie pour lesquelles le calcul a été effectué est strictement supérieur à 0 alors...
countedBars--;
}
...on décrémente de 1 le nombre calculé
int limit = Bars - countedBars - 1;
Dans la variable "limit" on va stocker le nombre de bougies affichées à l'écran (valeur retournée par l'instruction Bars) à laquelle on va soustraire le nombre de bougie pour lesquelles l'indicateur a déjà réalisé le calcul.
La méthode "start" sera appelée à chaque tick de cotation reçu par le serveur par conséquent il faut toujour penser à l'optimisation du code. C'est le but des lignes ci-dessus. L'indicateur compte les bougies sur lesquelles le calcul des points de pivot a déjà été réalisé. On peut alors savoir quelles sont les bougies sur lesquelles le calcul du point de pivot est manquant.
int j = 1;
int k = 1;
double pivotHigh = 0;
double pivotLow = 0;
double pivotClose = 0;
On déclare les variables que l'on va avoir besoin pour effectuer les différents calculs.
Ici "j" permettra de répérer la journée sur laquelle le point de pivot est calculé. "k" permettra de répérer la semaine.
"pivotHigh" stockera la valeur du plus haut de la journée précédente.
"pivotLow" stockera la valeur du plus bas de la journée précédente.
"pivotClose" stockera la valeur de clôture de la journée précédente.
Ces trois dernières valeurs sont utilisées dans la formule de calcul du point de pivot (n'oubliez pas l'aller consulter le cours si vous n'avez plus la formule en mémoire)
int previousDay = TimeDay(Time[j]);
"Time[j]" permet de récupérer la date et l'heure de la bougie "j" donc si j = 0 alors on récupère la date et l'heure de la bougie en cours. "TimeDay(datetime date)" permet de récupérer le jour du mois (de 0 à 31) de la date passée en paramètre.
De cette façon on va pouvoir savoir quand est-ce-que l'on a le droit de passer un nouvel ordre.
if(TimeDayOfWeek(Time[0]) == 1) {
"TimeDayOfWeek(datetime date)" permet de récupérer le jour de la semaine (0 à 6). De cette façon si on est Lundi on sait qu'il faut utiliser la bougie du vendredi et non celle du dimanche.
j++;
On est Lundi on incrémente donc "j" pour utiliser la bougie du vendredi.
previousDay = TimeDay(Time[j]);
}
Il faut aussi mémorisé le jour où a eu lieu le trade.
En résumé, cette portion de code est utilisée pour définir sur quel jour le pivot doit-il être calculé. Pour cet exemple on décide d'utiliser la bougie du vendredi pour calculer le point de pivot du Lundi par conséquent il faut sauter la bougie Dimanche. La fonction "TimeDayOfWeek" retour le jour sous forme d'entier (de 0 à 6 où 0 est le Dimanche). On peut donc savoir si la bougie en cours (Time[0]) est celle du Lundi.
Cette fontion permet de copier rapidement dans un tableau les cotations d'une parité. Pour le cas du point de pivot hebdomaire on utilisera une autre méthode que pour le point de pivot journalier car on ne souhaite pas calculer les supports et résistances hebdomadaires.
On a vu que c'était un tableau a plusieurs dimensions. En effet dans ce tableau seront stockées toutes les valeurs d'ouverture, de clôture, de plus haut, de plus bas. Chacune de ces valeurs représente une dimension et donc un tableau complet. Si l'on veut stocker toutes ces valeurs dans une seule variable il faut donc utiliser un tableau de tableau.
for(int i = 0; i < limit ; i++){
On démarre le calcul du pivot sur les bougies pour lesquelles le calcul n'a pas déjà été fait. La variable "limit" contient le nombre de bougies pour lesquelles le calcul n'a pas été fait.
iClose, iLow et iHigh sont des fonctions natives de metatrader(On peut utiliser Close Low et High aussi). Ici sur la parité sur laquelle l'indicateur est placé on souhaite avoir les valeurs Close, Low et High du timeframe journalier. Ces dernières nous permettrons de calculer le point de pivot qui est la base. Le premier paramètre est la parité. Si l'ont met "NULL" il s'agit alors de la parité du graphique. "PERIOD_D1" représente le timeframe (ici journalier). Ceci permet de dire à l'indicateur que quelque soit le timeframe sur lequel il est placé, il devra toujours aller chercher les valeurs Close, Low et High du timeframe D1. "j" représente la bougie sur laquelle on souhaite récupérer les valeurs.
Calcul classique des supports et résistances que l'on stocke dans leurs tableaux respectifs afin qu'ils soient affichés sur le graphique. Les différents tableaux ont été déclarés en début de programme. Ici la variable "i" représente la bougie sur laquelle on va afficher, calculer la valeur. Pour effectuer certains calculs on utilise la notation "[i]". Ceci signifie "aller chercher la valeur à la position i dans le tableau". Ainsi pivots[5] veut dire récupérer la valeur à la 5ème position dans le tableau de valeur qui stocke le calcul du point de pivot.
Puisque notre indicateur peut se placer sur n'importe quel timeframe il faut donc détecter à quel jour appartient la bougie en cours pour être sur de calculer le point de pivot de la journée précédente.
Le test "previousDay != TimeDay(Time[i])" permet de savoir si la bougie est cours appartient à la journée en cours.
Le test "TimeDayOfWeek(Time[i]) != 0" permet de savoir si la bougie en cours n'est pas dans la journée de Dimanche.
Le test "TimeDayOfWeek(Time[i]) != 6" permet de savoir si la bougie en cours n'est pas dans la journée de Samedi.
Si tous les tests sont vrais alors cela signifie que l'on a changé de jour et qu'il faut utiliser de nouvelles valeurs pour les points de pivots, supports et résistances.
Le test "TimeDayOfWeek(Time[i]) == 1" ermet de savoir si la bougie en cours est dans la journée de Lundi. Si c'est le cas il faut sauter la bougie du Dimanche pour utiliser les valeurs du vendredi.
Ici le calcul du point de pivot hebdomadaire. On a vu en début de méthode que les valeurs de cotation étaient copiées dans un tableau. Il ne nous reste donc plus qu'à faire appel aux valeurs et à calculer le point de pivot de la même manière que pour le pivot journalier. "Weekly_Price[k]" contient toutes les valeurs de la semaine N° k (celle dont la bougie en cours appartient).
[3],[2] et [4] sont déterminés par le fonctionnement de la fonction "ArrayCopyRates" ainsi pour la semaine k, le tableau N°3 contient la valeur "High" de la semaine.
Ensuite on procède comme pour le calcul du pivot journalier.
Pour le confort visuel il est intéressant de voir les valeurs de la journée en cours. C'est utile pour ceux qui font du trading manuel. La méthode est détaillée par la suite.
Afin de ne pas répéter le code on a sorti l'algorithme d'affichage d'un label de la fonction start. Ainsi au lieu d'avoir 8 copier/coller de la même portion de code, on a un simple appel à une fonction avec des paramètres définis.
La méthode "start" s'arrête donc ici. A chaque tick elle sera donc appelée afin de réaliser les différents calculs sur les bougies qui n'ont pas encore été traitées.
Les paramètres sont les informations dont a besoin la fonction pour s'exécuter.
Dans un premier temps elle ne fait qu'afficher donc elle ne revoie pas d'éléments. D'où le "void".
Ensuite nous avons besoin d'un identifiant pour le label défini par "string name". C'est ce qui va permettre de le répérer sur la graphique et de pouvoir le modifier ou le supprimer éventuellement.
Puis il me faut la valeur que je souhaite afficher : "double value".
Enfin j'aimerais bien avoir un préfix pour savoir à quel support ou résistance la valeur affichée fait référence. C'est le but du paramètre "string label".
if(ObjectFind(name) != 0) {
Il existe plusieurs manières d'afficher des éléments sur un graphique. Pour cet indicateur on veut que les valeurs des pivots, supports et résistances soient affichés sur la bougie en cours. Il faut donc prévoir 2 cas. Le premier est lorsque le label n'a pas été créé. Le deuxième : s'il existe déjà il suffit de le déplacer sur la bougie en cours lorsqu'une bougie vient de se clôturer.
La fonction "ObjectFind" permet de recherche un objet sur un graphique. Ici on attend que l'objet se trouve sur le graphique principal donc on attend que la valeur retournée soit 0. Si ce n'est pas le cas alors il faut créer l'objet.
ObjectCreate(name, OBJ_TEXT, 0, Time[0], value);
"ObjectCreate" permet de créer un objet sur un graphique. Cette fonction prend en paramètre dans l'ordre :
- Le nom de l'objet "(name)", attention il doit être unique.
- Son type "(OBJ_TEXT)". Ici du texte. Il est possible de choisir 24 types différents.
- La fenêtre dans laquelle il apparaît "(0)". Ici la fenêtre principale.
- La bougie de départ "(Time[0])". Ici la bougie en cours.
- La valeur où l'afficher "(value)". Ici la valeur de notre support ou résistance ou point de pivot. NB ceci détermine la position vertical et non ce qui sera visible à l'écran. En somme on choisit l'abscisse et l'ordonnée de notre label.
"ObjectSetText" permet de définir le texte qui sera affiché pour notre objet. On paramètre la valeur, la taille, la police et la couleur.
} else {
ObjectMove(name, 0, Time[0], value);
}
}
"ObjectMove" permet de déplacer un objet. Puisque ce dernier a été créé il suffit juste de le déplacer sur la bougie en cours lorsque la précédente viens de se fermer.
On choisit donc le nom de l'object à déplacer sur la bonne fenêtre avec ses nouvelles abscisse et ordonnée.
Je vous invite à consulter l'aide de MetaEditor pour explorer toutes les possibilités de ces fonctions car elles sont nombreuses.
Enfin lorsque l'on retire notre indicateur, on ne veut pas que ce dernier pollue notre graphique. Il faut donc nettoyer les labels que nous avons créés. Ceci se fait grâce à la fonction "ObjectDelete".
Son fonctionnement est très simple. Il suffit de lui passer le nom unique de l'objet que l'on souhaite supprimer. C'est celui que l'on a défini lorsque nous l'avons créé.
C'est terminé ! Vous avez créé votre premier indicateur. Il est simple mais il est permet d'aborder un peu la philosophie de développement de MetaTrader. Pour ceux qui veulent aller plus loin voici quelques pistes d'amélioration :
1) Nous calculons ici les points de pivots classiques. Il serait intéressant de rajouter un paramètre à l'indicateur qui nous permettrait de choisir quels types de points de pivots on voudrait calculer (Fibonacci, Camarilla, Woodie, DeMark).
2) Notre indicateur s'arrête à la bougie en cours. Il serait sympa de tracer des lignes horizontales qui prolongeraient les différents supports et résistances. Ceci vous permettra de manipuler un autre type d'objet.
Passons maintenant à l'EA qui va utiliser notre indicateur.
Nous allons maintenant développer notre EA qui va utiliser l'indicateur que nous venons tout juste d'étudier.
Tout d'abord il nous faut établir les règles que devra respecter notre EA :
- Il ne devra trader uniquement que pendant une plage horaire définie
- Il ne passera qu'un seul trade par jour
- Une fois la plage horaire passée l'EA devra clôturer l'ordre ouvert
- Si le cours est au dessus du point de pivot journalier alors on prend position LONG (et inversement)
- Les ordres sont placés avec un takeprofit et un stoploss
En avant pour le détail du code !
Dans un premier temps les paramètres. Il est important de rendre un EA le plus souple possible. Mettre des valeurs en paramètre permet notamment de réaliser des backtests et trouver quelles sont les valeurs qui généreraient probablement le profit maximal.
extern string NOTE1 = "- Parametres horaires -";
extern int heureDebut = 8;
extern int minuteDebut = 0;
extern int heureFin = 12;
extern int minuteFin = 0;
On défini d'abord les paramètres qui restreindront l'EA à trader durant une plage horaire. On arrête la précision à la minute.
C'est la manière la plus simple de définir des plages horaires pour metatrader puisqu'il existe des fonctions qui nous retourne l'heure et la minute en cours.
extern string NOTE2 = "- Parametres trades -";
extern int TakeProfit = 500;
extern int StopLoss = 250;
extern double Lots = 0.1;
extern int slippage = 3;
Ensuite viennent les paramètres qui concerne le trade à proprement parler. Quelle est la taille du lot, combien de pips je veux gagner en plaçant le Take Profit, combien je peux prendre en plaçant le Stop Loss et combien de pips de recotation j'accepte.
int magicalNumber,ticket;
datetime lastTrade = EMPTY_VALUE;
bool skipClosingTrades = true;
Pour utiliser notre EA nous avons besoin de variable.
magicalNumber : cela va nous permettre d'identifier un trade et de s'assurer que le trade en question a été traité et ouvert par l'EA.
ticket : il s'agit d'un identifiant de trade. Il sert pour la sélection d'un trade.
lastTrade : cette variable va nous permettre de savoir quand a eu lieu le dernier trade. Cela sert à éviter d'ouvrir plusieurs positions par jour.
skipClosingTrades : cette variable sera expliquée plus en détail dans le code.
Notez bien les types des différentes variables : "datetime" pour situer un trade dans le temps, "int" pour définir un numéro de trade, etc...
int init()
{
magicalNumber = 98765;
return(0);
}
De la même manière qu'un indicateur, la méthode "init" est appelée au chargement de l'EA sur le graphique. Ici on défini le magical number qui nous permettra d'identifier un trade.
C'est tout ce dont nous avons besoin à l'initialisation de l'EA.
int deinit()
{
return(0);
}
Pour cet EA en particulier, nous n'avons pas besoin de la fonction "deinit". Nous ne voulons faire rien de spécial si l'EA est retiré du graphique.
int start()
{
On démarre le coeur de l'EA à proprement parler. A chaque tick reçu du serveur nous allons tester les différentes règles que nous avons définies pour l'EA.
Tout d'abord tant que nous ne somme pas dans la plage horaire on ne trade pas. C'est le but des tests réalisés ci-dessus.
La fonction "Hour" retourne l'heure au format 24h. S'il est 13h28 alors la valeur retournée sera "13".
La fonction "Minute" fonctionne de la même manière. Pour 13h28 elle retourne "28".
On peut donc tester si l'on est bien dans la plage horaire en contrôlant bien que l'heure courante est comprise entre l'heure de début et l'heure de fin de la plage horaire.
Notez bien les différents tests. Ils sont différents si l'heure en cours est égale à l'heure de début (il faut alors tester les minutes) et il en va de même pour l'heure de fin.
Une fois que l'on se trouve dans la plage horaire il faut contrôler qu'un trade n'a pas déjà été passé. En effet notre TakeProfit ou StopLoss peut très bien être atteint en l'espace de 10 minutes. Or si l'on défini une plage de 4h l'EA reprendra une nouvelle position chose que l'on ne souhaite pas.
On va donc mémoriser le jour où le trade a été passé. La fonction "iTime" retourne la date de la bougie. De plus on spécifie précisement le timeframe grâce à "PERIOD_D1" de cette façon quelque soit le timeframe sur lequel l'EA est placé on est assuré de ne passer qu'un seul trade par jour
skipClosingTrades = false;
Puisque maintenant nous allons passer un trade il faut penser au fait que si l'on sort de la plage horaire, l'EA doit fermer les ordres s'il y en a d'ouvert.
C'est ce que va permettre cette variable.
Si vous regardez un peu plus loin dans le code, on teste cette variable lorsque l'on sort de la plage horaire. On sait donc que l'on a ouvert un trade et que peut être il n'a pas atteint son TakeProfit ou StopLoss.
Il faudra donc s'en occuper.
C'est ici que l'on va utiliser le travail que nous avons accompli précédemment.
L'indicateur que nous avons créé n'existe pas nativement dans MetaTrader.
Il faut donc l'appeler de façon un peu spéciale contrairement au fonction "iClose" ou "iHigh".
La fonction "iCustom" permet d'appeler un indicateur personnalisé. Elle fonctionne comme les indicateurs préxistants.
On défini la parité concernée, le timeframe du graphique, le nom de l'indicateur à appeler ("SimplePivotIndicator") puis les paramètres de l'indicateur. Si on choisi de ne pas les saisir alors il s'agit de ceux par défaut. Souvent il est intéressant de faire 2 versions pour ses indicateurs. Une version graphique et une version qui servira à l'appel. Là on n'a pas besoin d'afficher les labels sur notre EA. C'est des instructions réalisées inutilement et donc une optimisation que l'on pourrait faire.
Les deux derniers paramètres sont très importants. Ils permettent de dire sur quel buffer on veut récupérer notre valeur et sur quelle bougie.
Ici on veut la bougie en cours sur le buffer 0 qui représente notre point de pivot. Si nous avions voulu récupérer la valeur du support il aurait suffit de choisir le buffer 1 ce qui donne pour la fin de l'indicateur ",1,0);"
if(pivotValue < Ask){
On test si le cours actuel est au dessus du point de pivot. Si c'est le cas on va prendre position LONG.
- L'opération : donc ici un LONG soit OP_BUY. Cette constante n'est en fait une façon plus parlante de définir un entier. En réalité OP_BUY est égal à 0. Mais le texte reste plus parlant que la valeur. Vous pouvez passez les 6 types d'ordres grâce aux constantes suivantes : OP_BUY, OP_SELL, OP_BUYLIMIT, OP_SELLLIMIT, OP_BUYSTOP et OP_SELLSTOP
- La taille du lot : Lots soit notre paramètre d'EA
- La valeur d'achat : soit le Ask ou le Bid
- La recotation que l'on accepte : c'est notre paramètre de l'EA
- Le stoploss
- Le take profit
- Un commentaire : Ici on met que l'on passe un BUY
- Le nombre magic : celui qui a été initialisé par notre EA
- La date d'expiration : surtout utile pour les STOP et LIMIT
- La couleur de la flèche qui s'affichera sur notre graphique
Si l'opération s'est bien passé on reçoit alors un numéro de ticket et l'on va pouvoir placer les StopLoss et TakeProfit.
Pour pouvoir placer les limites il faut sélectionner l'ordre précédemment passé. "OrderSelect" sert à ça.
Ici on lui donne notre numéro de ticket et on lui précise que c'est un numéro de ticket qu'il reçoit.
Il est possible de parcours les trades par position (SELECT_BY_POS) et aussi de choisir si l'on veut les trades de l'historique (MODE_HISTORY) ou ceux en cours (MODE_TRADES).
Puisque notre ordre a été sélectionné on va lui affecté ses stoploss et takeprofit.
Comme ces derniers sont définis en pips, il faut les convertir en Point c'est pourquoi il y a "*Point" après nos paramètres sinon nous aurions par exemple si le takeprofit était à 50 pips sur un eur/usd à 1.34 un TakeProfit à 51.34. "OrderModify" va donc modifier notre ordre avec en paramètres :
- Le ticket : OrderTicket()
- Le prix d'ouverture : OrderOpenPrice()
- Le stoploss : OrderOpenPrice()-StopLoss*Point
- Le takeprofit : OrderOpenPrice()+TakeProfit*Point
- La recotation : donc ici 0 puisqu'on ne fait que modifier
- La couleur affichée sur le graphique : ici Bleu
Pourquoi placer les StopLoss et TakeProfit en dehors de la fonction "OrderSend" ?
Tout simplement à cause de la restrinction imposée par certain broker. Ce dernier refuse qu'un EA passe directement un ordre possédant un StopLoss et/ou un TakeProfit.
Pour assurer la compatibilité avec tous les brokers on utilise donc cette méthode.
Notez bien que le StopLoss et le TakeProfit sont calculés sur la fonction "OrderOpenPrice" et non sur "Ask" ou "Bid" car durant ce cours laps de temps la parité peut avoir bougée de quelques dizièmes de pips.
} else if(pivotValue > Bid){
On test si cours actuel est au dessous du point de pivot. Si c'est le cas on va prendre position SHORT.
Enfin si l'on ne se trouve plus dans la plage horaire et qu'un trade a été passé on le clôture.
"skipClosingTrades" est définie à "true" pour ne pas tester continuellement la fermeture du trade.
La fermeture des trades se fait encore dans une fonction !
C'est du code classique donc réutilisable donc il faut en faire une fonction pour gagner du temps plus tard.
return(0);
}
Fin de la fonction "start". Notre EA ne fait rien de plus.
void closeAllTrades(){
Puisque la fermeture de trade prend un peu de place on externalise la méthode "closeAllTrades".
Ceci permet d'apporter de la clarté au code si jamais on souhaite le modifier.
int total = OrdersTotal(); for(int cnt = total-1; cnt >= 0; cnt--){
Encore une autre particularité de MetaTrader. Il faut parcourir les ordres dans le sens inverse.
La raison est toute simple. Si l'on clôture un ordre tous les ordres en cours sont alors décalé d'un cran.
Du coup si l'on parcourt les ordres dans l'ordre croissant et que l'on en ferme un on réalise un saut !
On peut alors louper un ordre qui aurait dû être traité. Ceci est surtout utile lorsque l'EA traite plusieurs ordres sur une même paire en même temps.
De plus un tableau démarre à la valeur 0 donc le premier trade est à la place 0 et à la place 1 c'est pourquoi on démarre le compte à rebour à "total-1".
Si on a deux trades le premier est à la place 0 et le deuxième est à la place 1. Comme "total" est égal à 2 il faut retirer 1 afin d'être à la bonne position.
Est-ce-que l'ordre sélectionné est de la parité sur laquelle l'EA est en train de trader ?
Est-ce-que l'ordre sélectionné a été passé par l'EA ? On le sait grâce au "magical number".
if(OrderType() == OP_BUY){
Ici on teste simplement de quel type est l'ordre afin de savoir sur valeur du cours on doit le clôturer (Asb ou Bid). "OrderType()" renvoi une des valeurs suivantes : OP_BUY, OP_SELL, OP_BUYLIMIT, OP_SELLLIMIT, OP_BUYSTOP et OP_SELLSTOP.
On fait la même chose pour les opérations de ventes. Seule la valeur du cours change.
Ainsi s'achève notre EA. Il est prêt à trader en suivant la méthode des pivots.
Pour aller plus loin
L'EA présenté réalise les choses simplement et il est possible de l'améliorer grandement.
Ceci peut être une source d'exercice pour ceux qui veulent l'utiliser.
Voici quelques pistes d'amélioration :
- On peut rajouter le test de s'assurer que le cours est aussi au dessus ou au dessous du pivot hebdomadaire
- Quoiqu'il arrive on défini un TakeProfit ou un StopLoss. Il faudrait rajouter le test de si une des valeurs est égal à 0 alors on ne la définie pas
- Il serait intéressant d'assurer les profits. Pourquoi ne pas mettre en place une gestion de trailing stop ?
- Notre EA ne garde aucune trace de ce qu'il fait. Mettre en place un système de log est particulièrement utile pour pouvoir traiter les erreurs
J'espère que ce cours vous a permis de dégrossir et d'appréhender le mode de fonctionnement d'un EA et d'un indicateur.
Votre prochaine étape est maintenant d'imaginer votre propre système et de le développer.
Surtout n'oubliez pas de consulter la documentation si vous êtes coincé ! En général elle se suffit à elle même.
Bon courage !
Faites programmer votre Expert Advisor ou votre indicateur
FOREX est l’abréviation de FOReign EXchange – qui signifie marché des changes ou encore marché des devises. Le FOREX est le marché où les monnaies sont vendues, achetées, sous forme de parités. Sur le marché du Forex, toutes les monnaies sont échangées en temps réel, 24h/24h, 7J/7J. Le Forex est depuis quelques années ouvert aux particuliers, simples investisseurs voulant diversifier leurs placements ou purs spéculateurs. L'acces au marché des changes pour les particuliers est offert grâce aux brokers du forex.
ATTENTION: le FOREX est un marché rendu volatil par l'effet de levier qui vous est proposé. Un risque de pertes financières importantes est toujours présent. Tribuforex fournit à ses internautes des idées de trade ainsi que des analyses, mais ne pourra être tenu responsable en cas de perte.