Vous êtes sur la page 1sur 323

Buyer: Evangelos Papageorgiou (vpapnm@yahoo.

gr)
Transaction ID: 28J511988U193831A
Acheteur : Evangelos Papageorgiou (vpapnm@yahoo.gr) ID de
transaction : 28J511988U193831A
Acheteur : Evangelos Papageorgiou (vpapnm@yahoo.gr) ID de
transaction : 28J511988U193831A

Programmation des conseillers


experts
pour MetaTrader 5
Création de systèmes de trading automatisés en langage MQL5

Deuxième édition

Andrew R. Young

Éditions Edgehill
Acheteur : Evangelos Papageorgiou (vpapnm@yahoo.gr) ID de
transaction : 28J511988U193831A

Droits d'auteur © 2018, Andrew R. Young. Tous droits réservés.

Publié par Edgehill Publishing, Nashville, Tennessee.

Exclusion de garantie : Bien que nous nous soyons efforcés de garantir que le contenu de ce livre est exact,
l'éditeur n'assume aucune responsabilité quant à l'exactitude ou à l'exhaustivité de ce livre et décline
spécifiquement toute garantie implicite de qualité marchande ou d'adéquation à un usage particulier. Ni
l'auteur ni l'éditeur ne pourront être tenus responsables de toute perte de profit ou de tout autre dommage
non commercial ou commercial, y compris, mais sans s'y limiter, les dommages consécutifs, accessoires,
spéciaux ou autres.

« MetaTrader 5 », « MQL5 » et « Expert Advisor » sont des marques commerciales de MetaQuotes Software
Corp.

Ce livre et son éditeur ne sont en aucun cas approuvés ou affiliés à MetaQuotes Software Corp.

Pour plus d'informations sur ce livre, y compris les mises à jour, les actualités et les nouvelles éditions,
veuillez visiter notre site Web à l'adresse http://www.expertadvisorbook.com/

ISBN : 978-0-9826459-5-6

Table des matières

Introduction
À propos de ce livre

Téléchargement du code source

Conventions utilisées

Chapitre 1 - Bases de MQL5


Programmes MQL5

Extensions de fichiers

Autres types de fichiers

Emplacements des fichiers

MétaÉditeur

Assistant MQL5

Compilation

Syntaxe

Identifiants

commentaires

Chapitre 2 - Variables et types de données


Acheteur : Evangelos Papageorgiou (vpapnm@yahoo.gr) ID de
transaction : 28J511988U193831A

Variables

Types de données

Types entiers

Types réels

Type de chaîne

Type booléen

Type de couleur

Type de date/heure

Constantes

Tableaux

Tableaux multidimensionnels

Itérer à travers des tableaux

Énumérations

Structures

Typage

Variables d'entrée

Variables locales

Variables globales

Variables statiques

Variables prédéfinies

Chapitre 3 - Opérations 33
Opérations

Addition et multiplication

Soustraction et négation

Division et module

Opérations d'affectation

Opérations relationnelles

Opérations booléennes

Chapitre 4 - Opérateurs conditionnels et de boucle 39


Opérateurs conditionnels
Acheteur : Evangelos Papageorgiou (vpapnm@yahoo.gr) ID de
transaction : 28J511988U193831A

L'opérateur if

L'opérateur else

Opérateur ternaire

Opérateur de commutation

Opérateurs de boucle

L'opérateur while

Les opérateurs à faire pendant

L'opérateur pour

L'opérateur de pause

L'opérateur continuer

Chapitre 5 - Fonctions 49
Les fonctions

Les valeurs par défaut

L'opérateur de retour

Le type vide

Passage de paramètres par référence

Fonctions de surcharge

Chapitre 6 - Programmation orientée objet


Des classes

Modificateurs d'accès

Constructeurs

Classes dérivées

Fonctions virtuelles

Objets

Chapitre 7 - La structure d'un programme MQL5


Directives du préprocesseur

Directive #propriété

#définir la directive

#inclure la directive

Variables d'entrée et globales


Acheteur : Evangelos Papageorgiou (vpapnm@yahoo.gr) ID de
transaction : 28J511988U193831A

Classes et fonctions

Gestionnaires d'événements

Un exemple de programme

Chapitre 8 - Bases de l'Expert Advisor


Systèmes de comptabilisation des positions

Système de position de compensation

Système de position de couverture

Ordres de marché

Gestionnaires d’événements Expert Advisor

surInit()

SurDéinit()

SurTick()

Sur le commerce()

Au chronomètre()

Création d'un conseiller expert dans MetaEditor

Chapitre 9 - Passation de commande


CommandeEnvoyer()

La structure MqlTradeRequest

Ouvrir un ordre au marché

Ajouter un Stop Loss et un Take Profit

Clôturer un ordre au marché

Modification et clôture des ordres de couverture

Fermer par opération

Ouvrir une commande en attente

Modifier une commande en attente

Supprimer une commande en attente

La structure MqlTradeResult

Un simple conseiller expert

Version de couverture

Résumé
Acheteur : Evangelos Papageorgiou (vpapnm@yahoo.gr) ID de
transaction : 28J511988U193831A

Chapitre 10 - Création d'une classe de passation de commandes


Classe commerciale

La fonction OpenPosition()

La gestion des erreurs 101

Réessayer en cas d'erreur 106

Définition de l'écart, du nombre magique et du type de remplissage 109

Utilisation de la fonction OpenPosition()

Classe CTradeHedge 114

Utilisation de la classe CTradeHedge 117

Chapitre 11 – Stop Loss et Take Profit 121


Calculer un prix Stop Loss et/ou Take Profit fixe 121

Arrêter les pertes 121

Tirer profit 124

Création de fonctions de calcul d'arrêt 125

Niveau d'arrêt 127

Création de fonctions de vérification d'arrêt 128

Utilisation des fonctions de calcul et de vérification d'arrêt 130

Utiliser un Stop Loss dynamique 132

Chapitre 12 - Gestion, modification et clôture des positions 135


Fonctions d'information sur la position 135

Comptes de couverture 137

Récupération d'une liste de postes ouverts 137

Fonctions d'information sur les positions pour les comptes de couverture 143

Création d'une fonction de modification de poste 143

Comptes de couverture 146

Positions de clôture 148

Clôture des positions sur les comptes de couverture 151

Utilisation des fonctions d'information de position, de modification et de clôture 152

Comptes de couverture 154

Chapitre 13 - Commandes en attente 157


La fonction OpenPending() 158
Acheteur : Evangelos Papageorgiou (vpapnm@yahoo.gr) ID de
transaction : 28J511988U193831A

Utilisation de la fonction OpenPending() 160

Gestion des commandes en attente 162

Fonctions d'informations sur la commande 166

Modification des commandes en attente

La fonction ModifyPending()

Supprimer les commandes en attente

Création d'un conseiller expert en commandes en attente

Utiliser les ordres en attente pour sortir d'une position

Chapitre 14 - Stops suiveurs


Qu’est-ce qu’un Trailing Stop ?

Faible profit

Instaurer un stop suiveur

La classe CTrailing

Utiliser un stop suiveur dynamique

Arrêt de rentabilité

La fonction BreakEven()

Trailing Stops sur les comptes de couverture

Autres exemples

Chapitre 15 - Gestion de l'argent et dimensionnement des transactions


Vérification du volume des échanges

Gestion de l'argent

Chapitre 16 - Données sur les barres et les prix


Accéder aux prix actuels

La structure MqlTick

Données nues

CopyRates() et la structure MqlRates

Autres fonctions Copier...()

Prix les plus élevés et les plus bas

Signaux de trading basés sur les prix

Modèles de bougies
Acheteur : Evangelos Papageorgiou (vpapnm@yahoo.gr) ID de
transaction : 28J511988U193831A

Chapitre 17 - Utilisation d'indicateurs dans les conseillers experts


Indicateurs de tampon unique

Indicateurs multi-tampon

La classe CIndicator

Classes dérivées

Initialisation d'objet

Indicateurs personnalisés

La fonction iCustom()

Une classe d'indicateurs personnalisés ? 230

Signaux de trading basés sur des indicateurs 231

Allumer et éteindre les indicateurs 234

Chapitre 18 - Travailler avec l'heure et la date 237


Trading sur une nouvelle barre ouverte 237

La classe CNewBar 237

Le type date/heure 240

La structure MqlDateTime 241

Création d'une classe de minuterie d'échange 244

La fonction CreateDateTime() 244

La classe CTimer 245

La fonction DailyTimer() 247

La fonction PrintTimerMessage() 250

La fonction BlockTimer() 251

Récupération de StartTime et EndTime 255

Le gestionnaire d'événements OnTimer() 256

Chapitre 19 – Tout mettre ensemble 259


Créer un modèle 259

Modèle de comptes de couverture 265

Croix de moyenne mobile 266

Moyenne mobile croisée pour les comptes de couverture 268

Système de contre-tendance bandes/RSI 268


Acheteur : Evangelos Papageorgiou (vpapnm@yahoo.gr) ID de
transaction : 28J511988U193831A

Système à contre-tendance pour les comptes de couverture 272

Un modèle de commande en attente 272

Système de trading en petits groupes 274

Chapitre 20 - Trucs et astuces 279


Informations utilisateur et interaction 279

Fonction Alerte() 279

Fonction MessageBox() 280

Fonction SendMail() 281

Envoi de notifications mobiles 282

Jouer du son 282

Fonction Commentaire() 283

Objets du graphique 283

Création et modification d'objets 283

Récupération du temps et du prix à partir des objets de ligne 286

Objets d'étiquette et de flèche

Suppression d'objets

Fonctions de fichiers

Écrire dans un fichier CSV

Lecture à partir d'un fichier CSV

Variables globales

Arrêt de l'exécution

Chapitre 21 - Indicateurs, scripts et bibliothèques


Indicateurs

Styles de dessin

Le gestionnaire d'événements OnCalculate()

Assistant MQL5

Calcul de l'indicateur

Scripts

Bibliothèques

Chapitre 22 - Débogage et tests


Acheteur : Evangelos Papageorgiou (vpapnm@yahoo.gr) ID de
transaction : 28J511988U193831A

les erreurs

Erreurs de compilation

Erreurs d'exécution

Erreurs du serveur de trading

Débogage

Enregistrement

Utilisation du testeur de stratégie

Optimisation

Évaluation des résultats des tests


Acheteur : Evangelos Papageorgiou (vpapnm@yahoo.gr) ID de
transaction : 28J511988U193831A
Acheteur : Evangelos Papageorgiou (vpapnm@yahoo.gr) ID de
transaction : 28J511988U193831A

Introduction

Cela fait près de huit ans depuis l'introduction de MetaTrader 5. Pendant de nombreuses années, MetaTrader 5 a
été éclipsé par la popularité de son prédécesseur, MetaTrader 4. Finalement, MetaQuotes a implémenté
certaines des nouvelles fonctionnalités incluses dans MetaTrader 5 dans MetaTrader 4. plate-forme et porté de
nombreuses nouvelles fonctionnalités linguistiques de MQL5 vers MQL4. (Cela a nécessité une réécriture de
mon livre MQL4 original, sorti en 2015 sous le titreProgrammation Expert Advisor pour MetaTrader 4.)

En 2016, MetaQuotes a ajouté la couverture à la plateforme MetaTrader 5, ce qui a permis plusieurs transactions
simultanées dans le style auquel les utilisateurs de MetaTrader 4 sont habitués depuis longtemps. Bien que les
deux plates-formes aient atteint la parité des fonctionnalités à bien des égards, MetaTrader 5 présente toujours
de nombreux avantages par rapport à son prédécesseur, notamment des outils de test améliorés, la possibilité
de créer des graphiques avec des délais et des instruments personnalisés (y compris les crypto-monnaies), et
bien plus encore.

La deuxième édition de ce livre a été mise à jour pour inclure les nouvelles fonctionnalités de positionnement de
couverture, ainsi que pour corriger les erreurs grammaticales et de code de la première édition.

À propos de ce livre
Le but de ce livre est de donner au lecteur les connaissances et les outils nécessaires pour créer des conseillers
experts dans le langage de programmation MQL5. Nous passerons également en revue la création d'indicateurs,
de scripts et de bibliothèques.

Au cours de ce livre, nous créerons un cadre pour le développement rapide de conseillers experts. En utilisant
les capacités orientées objet de MQL5, nous créerons des classes et des fonctions pour les opérations de
trading courantes, telles que l'ouverture, la clôture et la gestion des ordres. Nous créerons également des
classes et des fonctions pour des fonctionnalités utiles telles que la gestion de l'argent, les stop suiveurs, les
minuteurs de trading, les indicateurs et bien plus encore.

MQL5 est livré avec sa propre bibliothèque standard, qui offre un accès rapide aux fonctions de trading
courantes. La bibliothèque standard est utilisée lors de la génération de conseillers experts avec
MetaEditorAssistant MQL5. Nous n'utiliserons pas la bibliothèque standard MQL5 dans ce livre, bien que le
lecteur soit invité à utiliser les classes de la bibliothèque standard dans ses propres projets.

Le langage MQL5 est très puissant et étendu. Il y a beaucoup de choses que vous pouvez faire dans MQL5 que
nous ne pourrons pas aborder dans ce livre, et ce livre n'est pas destiné à servir de référence linguistique
complète. Une fois que vous avez compris les principes fondamentaux de la programmation dans MQL5, le site
officiel de MQL5 à l'adressehttp://www.mql5.com, aussi bien queRéférence MQL5, sera votre guide pour
apprendre tout ce dont MQL5 est capable.

Ce livre suppose que le lecteur est familier avec MetaTrader 5 et le trading Forex en général. Le lecteur doit être
familier avec l'utilisation des programmes MQL5 et avoir une compréhension fondamentale de l'analyse
technique et des systèmes de trading. Ce livre ne suppose aucune expérience préalable en programmation, bien
Acheteur : Evangelos Papageorgiou (vpapnm@yahoo.gr)
Numéro de transaction : 28J511988U193831A

ETXPERT UNCONSEIL P.ROGRAMMATION POUR METTRADER 5


que le lecteur bénéficiera de toutes les compétences en programmation acquises précédemment dans d'autres
langages.

Même si tous les efforts ont été faits pour garantir que les informations contenues dans ce livre sont correctes, il
est possible que quelques erreurs soient présentes. MetaTrader 5 est toujours en développement actif, de
nouvelles fonctionnalités peuvent donc être implémentées ou modifiées après la publication de ce livre. Si vous
découvrez des erreurs dans le livre, envoyez-moi un email àcontact@expertadvisorbook.com.

Téléchargement du code source


Le code source de ce livre est téléchargeable gratuitement sur le site officiel du livre à l'adresse
http://www.expertadvisorbook.com/. Le téléchargement du code source contient les fichiers d'inclusion pour
le framework Expert Advisor et plusieurs programmes que nous créerons au cours de ce livre. Nous ferons
souvent référence à ces fichiers, il est donc recommandé de télécharger et d'installer ces fichiers dans votre
dossier de données MetaTrader 5.

Le code source est sous licenceCreative Commons Attribution-Pas d'Utilisation Commerciale 3.0 Non Porté
Licence. Cela signifie que vous pouvez utiliser le code source dans vos projets personnels. Vous pouvez même le
modifier pour votre propre usage. Mais vous ne pouvez l'utiliser dans aucun projet commercial, et si vous
partagez le code, il doit être attribué à l'auteur de ce livre.

Conventions utilisées
Police à largeur fixe fait référence au code du programme, aux noms de fichiers et de dossiers, aux URL ou
aux éléments du langage MQL5.

Italique concernent des termes définis ou utilisés pour la première fois dans le texte, des références auRéférence
MQL5, commandes clavier ou éléments d'interface dans MetaEditor ou MetaTrader.

2
Acheteur : Evangelos Papageorgiou (vpapnm@yahoo.gr)
Numéro de transaction : 28J511988U193831A

Bases de MQL5

Chapitre 1 - Bases de MQL5

Programmes MQL5
Il existe trois types de programmes que vous pouvez créer dans MQL5 :

Unconseiller expert est un programme de trading automatisé qui peut ouvrir, modifier et clôturer des ordres.
Vous ne pouvez associer qu’un seul expert à la fois à un graphique. La majorité de ce livre couvrira la création de
conseillers experts en MQL5.

Unindicateur affiche les données d'analyse technique sur un graphique ou dans une fenêtre séparée à l'aide de
lignes, d'histogrammes, de flèches, de barres/bougies ou d'objets graphiques. Vous pouvez attacher plusieurs
indicateurs à un graphique. Le chapitre 21 abordera la création d'indicateurs dans MQL5.

UNscénario est un programme spécialisé qui effectue une tâche spécifique. Lorsqu'un script est attaché à un
graphique, il ne s'exécutera qu'une seule fois. Vous ne pouvez attacher qu'un seul script à la fois à un graphique.
Nous aborderons la création de scripts au chapitre 21.

Extensions de fichiers
Un fichier MQ5 (.mq5) contient le code source d'un programme MQL5, tel qu'un expert, un indicateur, un script
ou une bibliothèque. Il s'agit du fichier créé lorsque nous écrivons un programme MQL5 dans MetaEditor. Ce
fichier peut être ouvert et modifié dans MetaEditor ou n'importe quel éditeur de texte.

Un fichier EX5 (.ex5) est un programme exécutable compilé. Lorsqu'un fichier MQ5 est compilé dans
MetaEditor, un fichier EX5 est produit avec le même nom de fichier. Il s'agit du fichier qui s'exécute lorsque vous
attachez un programme MQL5 à un graphique. Les fichiers EX5 sont des fichiers binaires et ne peuvent pas être
ouverts ou modifiés.

Un fichier MQH (.mqh) est un fichier d'inclusion qui contient le code source à utiliser dans un programme MQL5.
Comme les fichiers MQ5, un fichier MQH peut être ouvert et modifié dans MetaEditor.

Autres types de fichiers


Uninclure le fichier (.mqh) est un fichier de code source qui contient des classes, des fonctions et des variables à
utiliser dans un programme MQL5. Les fichiers d'inclusion contiennent du code utile qui peut être réutilisé
encore et encore. Lorsqu'un programme est compilé, le contenu de tous les fichiers d'inclusion utilisés dans le
programme sera "inclus" dans le programme compilé. Nous créerons de nombreux fichiers d'inclusion au cours
de ce livre.

UNbibliothèque est un fichier exécutable qui contient des fonctions réutilisables, similaires à un fichier
d'inclusion. Les bibliothèques sont au format EX5 ou Windows DLL. Une bibliothèque s'exécute en mémoire en
tant que programme distinct avec votre programme MQL5.

Les bibliothèques sont utiles si vous souhaitez rendre vos fonctions accessibles à d'autres sans rendre le code
source disponible. Nous discuterons des bibliothèques plus en détail au chapitre 21.

3
Acheteur : Evangelos Papageorgiou (vpapnm@yahoo.gr)
Numéro de transaction : 28J511988U193831A

ETXPERT UNCONSEIL P.ROGRAMMATION POUR METTRADER 5


Unparamètres expertsfichier ou fichier prédéfini (.ensemble) contient des paramètres commerciaux pour un
conseiller expert. Les fichiers de paramètres sont chargés ou enregistrés dans l'expert Advisor Propriétés boîte
de dialogue sous leContributions languette. LeCharger Le bouton charge les paramètres d'un.ensemble fichier,
et leSauvegarder Le bouton enregistre les paramètres actuels dans un.ensemble déposer. Vous pouvez
également charger ou enregistrer des paramètres dans leTesteur de stratégie sous leContributions à l'aide du
menu contextuel contextuel.

Emplacements des fichiers


Votre dossier de données MetaTrader 5 contient tous vos programmes et fichiers MQL5. Tous les programmes
MQL5 sont situés sous le\MQL5 dossier de votre dossier de données MetaTrader 5. Pour localiser votre dossier
de données MetaTrader 5, sélectionnezOuvrir le dossier de données duDéposer menu de MetaTrader ou
MetaEditor. Le dossier de données s'ouvrira dans l'Explorateur Windows.

Étant donné que tous les programmes MQL5 utilisent les mêmes extensions de fichiers, les types de
programmes sont organisés en sous-dossiers dans le fichier MetaTrader 5.\MQL5 dossier. Voici le contenu des
sous-dossiers du dossier MQL5 :

• \Experts – Ce dossier contient les fichiers MQ5 et EX5 pour les conseillers experts.

• \Indicateurs – Ce dossier contient les fichiers MQ5 et EX5 pour les indicateurs.

• \Scripts – Ce dossier contient les fichiers MQ5 et EX5 pour les scripts.

• \Inclure – Ce dossier contient les fichiers d’inclusion MQH.

• \Bibliothèques – Ce dossier contient les fichiers MQ5, EX5 et DLL des bibliothèques.

• \Images – Si votre programme utilise des images bitmap, elles doivent être stockées dans ce dossier
sous.bmp format.

• \Des dossiers – Tous les fichiers externes que vous utilisez dans vos programmes, autres que les
fichiers, bibliothèques, images ou autres programmes MQL, doivent être stockés dans ce dossier.

• \Préconfigurations – Il s'agit du dossier par défaut pour.ensemble fichiers chargés ou enregistrés à


partir du conseiller expert Propriétés boîte de dialogue ou depuis leContributions dans le testeur de
stratégie.

• \Journaux – Les journaux de l'Expert Advisor sont enregistrés dans ce dossier. Vous pouvez consulter
ces journaux dans leExperts onglet à l'intérieur duBoîte à outils fenêtre dans l'interface principale de
MetaTrader.

Toute référence aux dossiers ci-dessus dans ce livre suppose qu'ils se trouvent sous le\MQL5 dossier du dossier
de données MetaTrader 5. Donc une référence au\Experts le dossier ferait référence au\MQL5\Experts sous-
dossier du dossier de données MetaTrader 5.

MétaÉditeur
MetaEditor est l'EDI (Environnement de développement intégré) pour MQL5 inclus avec MetaTrader 5. Vous
pouvez ouvrir MetaEditor depuis l'interface MetaTrader en cliquant sur le boutonMétaÉditeur bouton sur

4
Acheteur : Evangelos Papageorgiou (vpapnm@yahoo.gr)
Numéro de transaction : 28J511988U193831A

Bases de MQL5

leStandard barre d'outils, ou en appuyant sur F4 sur votre clavier. Vous pouvez également ouvrir MetaEditor à
partir du menu Démarrer de Windows.

Figure 1.1 – L'interface MétaEditeur. Dans le sens des aiguilles d'une montre, en partant du coin supérieur gauche, se trouve
leNavigateur fenêtre, l'éditeur de code et leBoîte à outils fenêtre.

MetaEditor possède de nombreuses fonctionnalités utiles pour créer des programmes MQL5, notamment la
saisie semi-automatique, des info-bulles d'informations sur les paramètres, des outils de recherche, des outils
de débogage et bien plus encore. La figure 1.2 montre leListe des noms fonctionnalité.

Tapez les premières lettres d'un élément du langage MQL5, d'une variable ou d'un nom de fonction, et une liste
déroulante apparaîtra avec tous les mots-clés correspondants. Faites défiler la liste avec les touches fléchées
haut et bas et sélectionnez le mot-clé à compléter automatiquement en appuyant sur la toucheEntrer clé. Vous
pouvez également sélectionner le mot-clé dans la liste avec le bouton gauche de la souris. Vous pouvez
rappeler la liste déroulante des noms de liste à tout moment en appuyant surCtrl+Espace sur votre clavier, ou en
sélectionnantListe des noms duModifier menu.

5
Acheteur : Evangelos Papageorgiou (vpapnm@yahoo.gr)
Numéro de transaction : 28J511988U193831A

ETXPERT UNCONSEIL P.ROGRAMMATION POUR METTRADER 5


La figure 1.3 montre leInformations sur les paramètres info-bulle. Lorsque
vous renseignez les paramètres d'une fonction, l'info-bulle Parameter Info
apparaît pour vous rappeler les paramètres de la fonction. Le texte en
surbrillance dans l'info-bulle est le paramètre actuel.

Certaines fonctions ont plusieurs variantes – leSymboleInfoDouble() La


fonction de la figure 1.3 a deux variantes, comme le montre le[1 sur 2] texte
qui apparaît dans l'info-bulle. Utilisez les touches fléchées haut et bas pour
faire défiler toutes les variantes de la fonction. Vous pouvez rappeler l'info-
bulle des informations sur les paramètres à tout moment en appuyant
surCtrl+Maj+Espace sur votre clavier, ou en sélectionnantInformations sur le
Figure 1.2 - LeListe des noms dérouler.
paramètreÔ duModifier menu.

Figure 1.3 - LeInformations sur les paramètres info-bulle.

Il y a deux fenêtres supplémentaires dans l'interface MetaEditor. LeNavigateur La fenêtre affiche le contenu du
dossier MQL5 dans une arborescence de dossiers, permettant un accès facile à vos programmes MQL5. LeBoîte
à outils La fenêtre contient plusieurs onglets, dont leles erreurs onglet, qui affiche les erreurs de compilation ;
leRecherche onglet, qui affiche les résultats de la recherche ; et leDes articles,Base de code etProjets publics
onglets, qui répertorient les informations du site Web MQL5.

MetaEditor a un intégréRéférence MQL5, ce qui est utile pour rechercher des fonctions et des éléments de
langage MQL5. Positionnez simplement le curseur sur un mot-clé MQL5 et appuyez surF1 sur votre clavier.
LeMQL5
Référence s'ouvrira sur la page appropriée. Vous pouvez également ouvrir leRéférence MQL5 duAide menu.

Assistant MQL5
LeAssistant MQL5 est utilisé pour créer un nouveau programme MQL5. Pour ouvrir leAssistant MQL5, clique
leNouveau dans la barre d'outils ou sélectionnezNouveau duDéposer menu. Une fenêtre avec les options
suivantes apparaîtra :

6
Acheteur : Evangelos Papageorgiou (vpapnm@yahoo.gr)
Numéro de transaction : 28J511988U193831A

Bases de MQL5

Figure 1.4- LeAssistant MQL5.

• Conseiller expert (modèle) – Cela créera un nouveau fichier de conseiller expert à partir d’un modèle
intégré. Le fichier créé est enregistré dans le\MQL5\Experts dossier ou un sous-dossier spécifié.

• Conseiller expert (générer) – Cela permet à l’utilisateur de créer un conseiller expert sans aucun codage.
Le conseiller expert généré utilise la bibliothèque standard MQL5.

• Indicateur personnalisé – Cela créera un nouveau fichier d’indicateur personnalisé à partir d’un modèle
intégré. Le fichier créé est enregistré dans le\MQL5\Indicateurs dossier ou un sous-dossier spécifié.

• Scénario – Cela créera un fichier de script vierge à partir d’un modèle intégré. Le fichier créé est
enregistré dans le\ MQL5\Scripts dossier ou un sous-dossier spécifié.

• Bibliothèque – Cela créera un fichier de bibliothèque vierge à partir d’un modèle intégré. Le fichier créé
est enregistré dans le\ MQL5\Bibliothèques dossier ou un sous-dossier spécifié.

• Inclure (*.mqh) – Cela créera un fichier d’inclusion vierge à partir d’un modèle intégré avec le.mqh
extension. Le fichier créé est enregistré dans le\MQL5\Inclure dossier ou un sous-dossier spécifié.

• Nouvelle classe – Cela créera un fichier d'inclusion avec une définition de classe à partir d'un modèle
intégré. Le fichier créé est enregistré dans le\MQL5\Experts dossier ou un sous-dossier spécifié.

Nous approfondirons la création de programmes utilisant leAssistant MQL5 tout au long du livre.

7
Acheteur : Evangelos Papageorgiou (vpapnm@yahoo.gr)
Numéro de transaction : 28J511988U193831A

ETXPERT UNCONSEIL P.ROGRAMMATION POUR METTRADER 5


Compilation
Pour compiler un programme MQL5, appuyez simplement sur le boutonCompiler sur la barre d'outils
MetaEditor. Le fichier MQ5 actuel et tous les fichiers inclus seront vérifiés pour détecter les erreurs et un fichier
EX5 sera produit. Toute erreur de compilation ou avertissement apparaîtra dans leles erreurs onglet duBoîte à
outils fenêtre.

Les erreurs devront être corrigées avant qu’un fichier EX5 puisse être généré. Le chapitre 22 traite du débogage
et de la correction des erreurs du programme. Les avertissements doivent être examinés, mais peuvent
généralement être ignorés en toute sécurité. Un programme avec des avertissements sera toujours compilé
avec succès.

Syntaxe
MQL5 est similaire à d'autres langages de programmation modernes tels que C++, C# ou Java. Si vous avez
programmé dans un langage de programmation moderne avec une syntaxe de style C, la syntaxe et la structure
de MQL5 vous seront très familières.

Une expression ou un opérateur dans MQL5 doit se terminer par un point-virgule (;). Une expression peut
s’étendre sur plusieurs lignes, mais il doit y avoir un point-virgule à la fin de l’expression. Ne pas ajouter de
point-virgule à la fin d’une expression est une erreur courante que commettent les nouveaux programmeurs.

// Une expression
simple x = y + z;

// Une expression qui s'étend sur plusieurs


lignes x = (y + z)
/ (q-r);

La seule exception à la règle du point-virgule est l’opérateur composé. UNopérateur composé se compose d'un
opérateur suivi d'une paire de parenthèses ({}). Dans l'exemple ci-dessous, l'opérateur est lesi(x == 0)
expression. Il n'y a pas de point-virgule après la parenthèse fermante. Toutes les expressions entre crochets
doivent être terminées par un point-virgule.

// Un opérateur composé simple


si(x == 0)
{
Print("x est égal à zéro");
retour;
}
Identifiants
Lorsque vous nommez des variables, des fonctions et des classes, vous devez utiliser un nom unique et
descriptif.identifiant. L'identifiant ne doit pas être identique à un autre identifiant du programme, ni à un élément
du langage MQL5.

Vous pouvez utiliser des lettres, des chiffres et le caractère de soulignement (_), même si le premier caractère
d'un identifiant ne doit pas être un chiffre. La longueur maximale d'un identifiant est de 64 caractères. Cela vous
donne beaucoup de marge de créativité, alors utilisez des identifiants clairs et descriptifs.

8
Acheteur : Evangelos Papageorgiou (vpapnm@yahoo.gr)
Numéro de transaction : 28J511988U193831A

Bases de MQL5

Les identifiants sont sensibles à la casse. Cela signifie queMonIdentifiant etmonIdentifiant ne sont pas les
mêmes!
Les programmeurs utilisent la majuscule pour distinguer les différents types de variables, fonctions et classes.
Voici le schéma de capitalisation que nous utiliserons dans ce livre :

• Les variables globales, les objets, les classes et les noms de fonctions mettront en majuscule la première
lettre de chaque mot. Par exemple:MaFonction() ouMaVariable.

• Les variables et objets locaux, déclarés dans une fonction, utiliserontaffaire de chameau. C'est là que la
première lettre est en minuscule et les premières lettres de tous les autres mots en majuscules. Par
exemple:maVariable ouobjet local.

• Les constantes sont toutes en majuscules. Utilisez des traits de soulignement pour séparer les mots, par
exemple :MA_CONSTANT.

commentaires
Les commentaires sont utilisés pour décrire ce que fait une section de code dans un programme. Vous
souhaiterez utiliser des commentaires tout au long de votre programme pour le garder organisé. Vous pouvez
également utiliser des commentaires pour supprimer temporairement des lignes de code de votre programme.
Toute ligne commentée est ignorée par le compilateur.

Pour ajouter un commentaire, les deux premiers caractères doivent être une double barre oblique (//). Cela
commentera une seule ligne de code :
// Ceci est un commentaire

// La ligne de code ci-dessous est commentée


// x = y + z ;

Pour commenter plusieurs lignes de code, utilisez une barre oblique-astérisque (/*) au début de votre
commentaire, et une barre oblique astérisque (*/) à la fin de votre commentaire.

/* Ceci est un commentaire sur plusieurs lignes


Ces lignes seront ignorées par le compilateur
x = y + z ; */
MetaEditor dispose d'un ensemble de commandes de commentaires utiles. Sélectionnez les lignes que vous
souhaitez commenter en les mettant en surbrillance avec votre souris. Dans leModifier menu, sous
lecommentaires sous-menu, leLignes de commentaires l'élément de menu commentera les lignes
sélectionnées, tandis queDécommenter les lignes supprimera les commentaires des lignes sélectionnées. LeEn-
tête de fonction L'élément de menu insérera un en-tête de fonction commenté similaire à ceux des fichiers MQ5
générés automatiquement :

// En-tête de fonction généré par le menu Edition -> Commentaires -> En-tête de fonction

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

9
Acheteur : Evangelos Papageorgiou (vpapnm@yahoo.gr)
Numéro de transaction : 28J511988U193831A

ETXPERT UNCONSEIL P.ROGRAMMATION POUR METTRADER 5

Chapitre 2 - Variables et types de données

Variables
UNvariable est l'unité de stockage de base dans tout langage de programmation. Les variables contiennent des
informations nécessaires au fonctionnement de notre programme, telles que les prix, les valeurs des indicateurs
ou les paramètres commerciaux.

Avant qu'une variable puisse être utilisée, elle doit êtredéclaré. Vous déclarez une variable en spécifiant le type
de données et un identifiant unique. En option, vous pouvezinitialiser la variable avec une valeur. Vous
déclarerez généralement une variable au début d'un programme ou d'une fonction, ou lors de sa première
utilisation. Si vous déclarez une variable plusieurs fois, ou pas du tout, vous obtiendrez une erreur de
compilation.

Voici un exemple de déclaration de variable :

int monNuméro = 1 ;

Dans cet exemple, le type de données estint (entier), l'identifiant estmon numéro, et on l'initialise avec une
valeur de 1. Une fois qu'une variable a été déclarée, vous pouvez modifier sa valeur en lui attribuant une
nouvelle valeur :

monNuméro = 3 ;

La variablemon numéro a désormais la valeur 3. Vous pouvez également affecter la valeur d'une variable à une
autre variable :

int monNuméro ; int


votreNuméro = 2 ;
monNuméro =
votreNuméro ;

La variablemon numéro a maintenant une valeur de 2.

Si vous n'initialisez pas une variable avec une valeur, une valeur vide par défaut lui sera attribuée. Pour les types
numériques, la valeur initiale sera 0, et pour les types chaîne, ce sera une chaîne nulle ou vide (NUL ou"").

Types de données
Lors de la déclaration d'une variable, le type de données détermine le type de données que cette variable peut
contenir. Les types de données dans MQL5 peuvent être organisés en trois types :

• Types entiers sont des nombres entiers. Par exemple : 0, 1 et 10563.

• Types réels sont des nombres fractionnaires avec un point décimal. Par exemple : 1,35635.

10
Acheteur : Evangelos Papageorgiou (vpapnm@yahoo.gr)
Numéro de transaction : 28J511988U193831A

Variables et types de données

• Cordes sont des textes composés de caractères Unicode. Par exemple : « Le renard brun a sauté par-
dessus le chien paresseux.

Types entiers
MQL5 possède de nombreux types entiers contenant diverses plages de nombres entiers. Commençons par
examiner les types entiers signés. UNtype signé peut contenir des nombres positifs ou négatifs :

• carboniser - Lecarboniser le type utilise 1 octet de mémoire. La plage de valeurs va de -128 à 127.

• court - Lecourt le type utilise 2 octets de mémoire. La plage de valeurs va de -32 768 à 32 767.

• int - Leint le type utilise 4 octets de mémoire. La plage de valeurs va de -2 147 483 648 à
2 147 483 647.

• long - Lelong le type utilise 8 octets de mémoire. La plage de valeurs va de -9 223 372 036 854 775
808 à 9 223 372 036 854 775 807.

Alors, quel type entier devriez-vous utiliser ? Vous verrez fréquemment leint oulong type utilisé dans les
fonctions MQL5, ce sont donc les types que vous utiliserez le plus. Vous pouvez utiliser uncarboniser oucourt
tapez vos variables si vous le souhaitez.

Il existe également des types entiers non signés, qui n'autorisent pas les nombres négatifs. Letypes non signés
utilisent la même quantité de mémoire que leurs homologues signés, mais la valeur maximale est le double de
celle du type signé.

• voler - Levoler le type utilise 1 octet de mémoire. La plage de valeurs va de 0 à 255.

• ushort - Leushort le type utilise 2 octets de mémoire. La plage de valeurs va de 0 à 65 535.

• uint - Leuint le type utilise 4 octets de mémoire. La plage de valeurs va de 0 à 4 294 967 295.

• tête - Letête le type utilise 8 octets de mémoire. La plage de valeurs va de 0 à


18 446 744 073 709 551 615.

Les numéros de billets et autres paramètres commerciaux utilisent letête type, Les autres types non signés sont
rarement utilisés, mais vous pouvez les utiliser pour vos variables si vous le souhaitez.

Types réels
Les types de nombres réels sont utilisés pour stocker des valeurs numériques avec une composante
fractionnaire, comme les prix. Il existe deux types de nombres réels dans MQL5. La différence entre les deux
types réside dans le niveau de précision lors de la représentation de valeurs fractionnaires.

• flotter - Leflotter le type utilise 4 octets de mémoire. Il est précis à 7 chiffres significatifs près.

• double - Ledouble le type utilise 8 octets de mémoire. Il est précis à 15 chiffres significatifs près.

Vous utiliserez ledouble tapez fréquemment dans MQL5. Leflotter type peut être utilisé pour économiser de
la mémoire lorsqu'il s'agit de grands tableaux de nombres réels, mais il n'est pas utilisé dans les fonctions MQL5.

11
Acheteur : Evangelos Papageorgiou (vpapnm@yahoo.gr)
Numéro de transaction : 28J511988U193831A

ETXPERT UNCONSEIL P.ROGRAMMATION POUR METTRADER 5


Type de chaîne
Lechaîne le type est utilisé pour stocker du texte. Les chaînes doivent être placées entre guillemets doubles (").
Voici un exemple dechaîne tapez la déclaration de variable :

string maChaîne = "Ceci est une chaîne" ;

Si vous devez utiliser un guillemet simple ou double à l'intérieur d'une chaîne, utilisez une barre oblique inverse
(\) avant le devis. C'est appelés'échapper un caractère.

string myQuote = "Nous sommes en train de \"échapper\" aux guillemets doubles";


Imprimer(monCitation);
// Sortie : Nous "échappons" aux guillemets doubles

Si vous devez utiliser une barre oblique inverse à l'intérieur d'une chaîne, utilisez deux caractères barre oblique
inverse comme celui-ci :

string mySlash = "Ceci est une barre oblique inverse : \\" ;


Imprimer(monSlash);
// Sortie : Il s'agit d'une barre oblique inverse : \

Vous pouvez également ajouter un nouveau caractère de ligne à une chaîne en utilisant la commande\n
caractère d'échappement:

string myNewline = "Cette chaîne comporte \n un caractère de nouvelle ligne";


Imprimer(maNouvelleligne);
// Sortie : Cette chaîne a
// un caractère de nouvelle ligne
Vous pouvez combiner des chaînes ensemble en utilisant leenchaînement opérateur (+). Cela combine plusieurs
chaînes en une seule :

string insert = "concaténé" ;


string myConcat = "Ceci est un exemple de chaîne " + insert + ".";
Imprimer(monConcat); // Sortie : Ceci est un exemple de chaîne
concaténée.

LeStringConcatenate() La fonction peut également être utilisée pour concaténer des chaînes. C'est plus
efficace en mémoire que l'utilisation de l'opérateur de concaténation. Le premier paramètre
duStringConcatenate() la fonction est lachaîne variable vers laquelle copier la chaîne concaténée, et les
paramètres restants sont les chaînes à concaténer :

chaîne nouvelleChaîne ;
string insert = "concaténé" ;
StringConcatenate(newString,"Ceci est un autre exemple de chaîne ", insert, "");
Imprimer(nouvelleChaîne); // Sortie : Ceci est un autre
exemple de chaîne concaténée.

12
Acheteur : Evangelos Papageorgiou (vpapnm@yahoo.gr)
Numéro de transaction : 28J511988U193831A

Variables et types de données

LenouvelleChaîne La variable contient la chaîne concaténée. Notez que les chaînes à concaténer sont séparées
par des virgules à l'intérieur duStringConcatenate() fonction.

Enfin, si vous avez une chaîne très longue, vous pouvez la diviser sur plusieurs lignes. Vous n'avez pas besoin
d'utiliser l'opérateur de concaténation. Chaque ligne doit être entourée de guillemets doubles et il doit y avoir un
point-virgule à la fin de l'expression :

string myMultiline = "Ceci est une chaîne multiligne."


"Ces lignes seront réunies.";
Imprimer(maMultiligne); // Sortie : Il s'agit d'une chaîne multiligne. Ces
lignes seront réunies.

Type booléen
Le booléen (bouffon) le type est utilisé pour stocker les valeurs vraies/fausses. Techniquement, le type booléen
est un type entier, puisqu'il prend la valeur 0 (FAUX) ou 1 (vrai). Voici un exemple de déclaration de variable de
type booléen :

bool monBool = vrai ;


Imprimer(monBool);
// Sortie : vrai

Si une variable booléenne n'est pas explicitement initialisée avec une valeur devrai, la valeur par défaut sera 0,
ouFAUX. Toute valeur non nulle dans une variable booléenne sera évaluée àvrai:

bool monBool ;
Imprimer(monBool);
// Sortie : faux

monBool = 5 ;
if(monBool == true) Print("monBool est
vrai"); // Sortie : myBool est vrai

Dans l'exemple ci-dessus, nous initialisons la variable booléennemaBool sans valeur. Ainsi,maBool est égal à 0
ouFAUX. Quand on attribue une valeur de 5 àmaBool,maBool évalue commevrai dans une opération booléenne.
Nous parlerons davantage des opérations booléennes au chapitre 3.

Type de couleur
Lecouleur le type est utilisé pour stocker des informations sur les couleurs. Les couleurs peuvent être
représentées par des constantes de couleur prédéfinies, des valeurs RVB ou des valeurs hexadécimales.

Vous utiliserez le plus les constantes de couleur. Ce sont les mêmes couleurs que vous utiliserez lors du choix
d’une couleur pour une ligne indicatrice ou un objet graphique. Vous pouvez afficher l'ensemble complet des
constantes de couleur dans leRéférence MQL5 sousConstantes standard... > Constantes d'objets > Couleurs
Web.

Voici un exemple decouleur déclaration de variable utilisant une constante de couleur :

13
Acheteur : Evangelos Papageorgiou (vpapnm@yahoo.gr)
Numéro de transaction : 28J511988U193831A

ETXPERT UNCONSEIL P.ROGRAMMATION POUR METTRADER 5


couleur lineColor = clrRed;

Lecouleur variablecouleur de la ligne est initialisé en utilisant la constante de couleur pour le


rouge,clrRouge. Voici un exemple utilisant la valeur RVB pour le rouge :

couleur lineColor = C'255,0,0';

La valeur RVB pour le rouge est255,0,0. Une constante RVB commence par un C majuscule et la valeur RVB est
placée entre guillemets simples. Enfin, voici un exemple utilisant la valeur hexadécimale du rouge :

couleur lineColor = 0xFF0000;

Une valeur hexadécimale est précédée de '0x', et suivi de la valeur hexadécimale à six caractères, dans ce
casFF0000.

Les valeurs RVB et hexadécimales sont utilisées pour les couleurs personnalisées – celles qui ne sont pas
définies par des constantes de couleur. Si vous êtes à l'aise avec les couleurs RVB ou hexadécimales, vous
pouvez définir vos propres couleurs. Sinon, vous trouverez les constantes de couleur simples et utiles.

Type de date/heure
Ledateheure type est utilisé pour stocker l’heure et la date. L'heure et la date dans undateheure la variable est
stockée dansHeure Unix, qui correspond au nombre de secondes écoulées depuis le 1er janvier 1970. Par
exemple, le 1er janvier 2012 à minuit en heure Unix est 1 325 397 600.

Si vous devez initialiser undateheure variable à une date et une heure spécifiques, utilisez undateheure
constante. UNconstante date/heure commence par un D majuscule, avec la date et l’heure entre guillemets
simples. La date et l'heure sont représentées au formataaaa.mm.jj hh:mm:ss. Voici un exemple :

datetime maDate = D'2012.01.01 00:00:00';

L'exemple ci-dessus initialise la variableMon rendez-vous au 1er janvier 2012 à minuit. Vous pouvez omettre
des parties dudateheure constantes si elles ne sont pas utilisées. Les exemples suivants le démontreront :
datetime maDate = D'2012.01.01 00:00'; // Heure et minute
datetime maDate = D'2012.01.01 00'; // Heure seulement
datetime maDate = D'2012.01.01'; // Date uniquement
Tous ces exemples initialiseront la variableMon rendez-vous à la même heure – le 1er janvier 2012 à minuit.
Depuis lehh:mm:ss partie de ladateheure constante n’est pas utilisée, nous pouvons l’omettre.

Alors, que se passe-t-il si vous omettez la date ? Le compilateur remplacera la date du jour – la date de
compilation. En supposant que nous soyons le 1er janvier 2012, voici ce qui se passe lorsque nous utilisons
undateheure constante sans date :

dateheure maDate = D''; // 01.01.2012 00:00 dateheure


maDate = D'02:00'; // 01.01.2012 02:00

14
Acheteur : Evangelos Papageorgiou (vpapnm@yahoo.gr)
Numéro de transaction : 28J511988U193831A

Variables et types de données

Dans le premier exemple, la variableMon rendez-vous est réglé sur la date d'aujourd'hui à minuit. Dans le
deuxième exemple, nous fournissons une heure dans ledateheure constante. Ceci définitMon rendez-vous à la
date d'aujourd'hui à l'heure spécifiée.

MQL5 possède plusieurs constantes prédéfinies pour la date et l'heure actuelles. Le __DATE__ constante renvoie
la date de compilation. C'est la même chose que d'utiliser un blancdateheure constante. Le __DATEHEURE__
constant renvoie l'heure et la date actuelles lors de la compilation. Notez qu'il existe deux caractères de
soulignement (_) avant et après chaque constante.

Voici un exemple utilisant le__DATE__ constante. Nous supposerons que la date actuelle est le 1er janvier 2012 :

dateheure maDate = __DATE__; // 01.01.2012 00:00 dateheure


maDate = D''; // 01.01.2012 00:00
Et voici un exemple utilisant le__DATETIME__ constante. Nous supposerons que l'heure et la date de compilation
sont le 1er janvier 2012 à 03:15:05 :

dateheure maDate = __DATETIME__; // 01.01.2012 03:15:05

Au chapitre 18, nous examinerons d'autres façons de gérer et de manipulerdateheure valeurs.

Constantes
UNconstante est un identifiant dont la valeur ne change pas. Une constante peut être utilisée partout où une
variable peut être utilisée. Vous ne pouvez pas attribuer une nouvelle valeur à une constante comme vous le
pouvez pour une variable.

Il existe deux manières de définir des constantes dans votre programme. Les constantes globales sont définies
dans votre programme à l'aide du#définir directive du préprocesseur. N'importe lequel#définir les
directives sont placées au tout début de votre programme. Voici un exemple de définition de constante :

#define COMPANY_NAME "Easy Expert Forex"

Le#définir La directive indique au compilateur qu’il s’agit d’une déclaration constante.NOM DE L'ENTREPRISE
est l'identifiant. La chaîne "Forex expert facile" est la valeur constante. La valeur constante peut être de
n'importe quel type de données.

Une constante globale peut être utilisée n'importe où dans votre programme. Voici un exemple de la façon dont
nous pouvons utiliser la constante ci-dessus :

Imprimer("Copyright ©2012 ",COMPANY_NAME);


// Sortie : Copyright ©2012 Easy Expert Forex

L'identifiant constantNOM DE L'ENTREPRISE dans leImprimer() la fonction est remplacée par la valeur
constante"Forex expert facile".

Une autre façon de déclarer une constante consiste à utiliser leconst spécificateur. En plaçant unconst
spécificateur avant une déclaration de variable, vous indiquez que la valeur de la variable ne peut pas être
modifiée :

15
Acheteur : Evangelos Papageorgiou (vpapnm@yahoo.gr)
Numéro de transaction : 28J511988U193831A

ETXPERT UNCONSEIL P.ROGRAMMATION POUR METTRADER 5


const int cVar = 1 ;
cVar = 2 ; // Erreur de compilation

LeVar c La variable est définie comme constante à l'aide duconst spécificateur. Si nous essayons d'attribuer
une nouvelle valeur à cette variable, nous obtiendrons une erreur de compilation.

Tableaux
Untableau est une variable de n'importe quel type pouvant contenir plusieurs valeurs. Considérez un tableau
comme une liste numérique. Chaque numéro de la liste correspond à une valeur différente. Nous pouvons
parcourir cette liste numériquement et accéder à chacune des valeurs par son index numérique.

Voici un exemple de déclaration et d'affectation de tableau :

int monTableau[3];
monTableau[0] = 1;
monTableau[1] = 2;
monTableau[2] = 3;

C'est ce qu'on appelle untableau statique. Un tableau statique a une taille fixe. Lorsqu'un tableau statique est
déclaré, la taille du tableau est spécifiée entre crochets ([]). Dans cet exemple, le tableaumonTableau est
initialisé avec 3 éléments. Les lignes suivantes attribuent une valeur entière à chacun des trois éléments du
tableau.

Nous pouvons également attribuer les valeurs du tableau lors de la première déclaration du tableau. Cette ligne
de code accomplit la même tâche que les quatre lignes de code de l'exemple précédent :

int monTableau[3] = {1,2,3};

exemple, le premier élément,monTableau[0], se voit attribuer la valeur 1, et le troisième Figure


2.1 –
élément,monTableau[2] se voit attribuer la valeur 3.
Indexa
tion
des
tablea
ux.

16
Acheteur : Evangelos Papageorgiou (vpapnm@yahoo.gr)
Numéro de transaction : 28J511988U193831A

Variables et types de données

Les valeurs du tableau sont séparées par des virgules entre parenthèses ({}). Ils sont affectés au
tableau dans l'ordre dans lequel ils apparaissent, de sorte que le premier élément du tableau se voit
attribuer la valeur 1, le deuxième élément du tableau se voit attribuer la valeur 2, et ainsi de suite.
Tous les éléments du tableau auxquels aucune valeur n’est attribuée auront par défaut une valeur
vide – dans ce cas, zéro.

L'indexation du tableau commence à zéro. Ainsi, si un tableau comporte 3 éléments, les éléments
sont numérotés 0, 1 et 2. Voir la figure 2.1 à droite. L'index maximum est toujours inférieur d'un à la
taille du tableau.
Lors de l'accès aux éléments d'un tableau, l'index est spécifié entre crochets. Au dessus
Comme ce tableau ne comporte que 3 éléments, si nous essayons d'accéder à un index supérieur à 2,
une erreur se produira. Par exemple:

int monTableau[3]; monTableau[3] = 4; // Cela provoquera


une erreur de compilation

Étant donné que les index d’un tableau commencent à 0, un index de 3 ferait référence au quatrième élément
d’un tableau. Ce tableau ne comporte que 3 éléments, donc tenter d'accéder à un quatrième élément entraînera
une erreur critique "tableau hors de portée".

Les tableaux statiques ne peuvent pas être redimensionnés, mais si vous devez redimensionner un tableau, vous
pouvez plutôt déclarer un tableau dynamique. UNtableau dynamique est un tableau déclaré sans taille fixe. Les
tableaux dynamiques doivent être dimensionnés avant de pouvoir être utilisés, et un tableau dynamique peut
être redimensionné à tout moment. LeTableauResize() La fonction est utilisée pour définir la taille d’un
tableau dynamique.

Voici un exemple de déclaration, de dimensionnement et d'affectation de tableau dynamique :

double maDynamique[];
ArrayResize(myDynamic,3);
maDynamique[0] = 1,50 ;

Un tableau dynamique est déclaré avec des crochets vides. Cela indique au compilateur qu'il s'agit d'un tableau
dynamique.
LeTableauResize() la fonction prend le nom du tableau (maDynamique) comme premier paramètre et la
nouvelle taille (3) comme deuxième paramètre. Dans ce cas, nous définissons la taille demaDynamique à 3. Une
fois le tableau dimensionné, nous pouvons attribuer des valeurs au tableau.

Les tableaux dynamiques sont utilisés dans MQL5 pour stocker les valeurs des indicateurs et les données de
prix. Vous déclarerez fréquemment des tableaux dynamiques à utiliser dans les fonctions MQL5. Les fonctions
elles-mêmes se chargeront de dimensionner correctement le tableau et de le remplir de données. Lorsque vous
utilisez des tableaux dynamiques dans votre propre code, assurez-vous de les dimensionner en
utilisantTableauResize() d'abord.

Tableaux multidimensionnels
Jusqu'à présent, nous avons déclaré des tableaux unidimensionnels. Jetons un coup d'œil à un tableau à deux
dimensions :

17
Acheteur : Evangelos Papageorgiou (vpapnm@yahoo.gr)
Numéro de transaction : 28J511988U193831A

ETXPERT UNCONSEIL P.ROGRAMMATION POUR METTRADER 5


double maDimension[3][3]; maDimension[0][1] = 1,35;

Dans l'exemple ci-dessus, nous déclarons un tableau à deux dimensions


nommémaDimension, les deux dimensions ayant trois éléments.
Considérez un tableau multidimensionnel comme un « tableau dans un
tableau ». Ce sera plus facile si nous visualisons notre tableau
bidimensionnel sous forme de tableau. Reportez-vous à la figure 2.2 à
droite.

La première dimension sera les lignes horizontales de notre tableau, tandis


que la deuxième dimension sera les colonnes verticales. Donc dans cet
exemple,maDimension[0][0] sera la première ligne, la première
colonne ;maDimension[1]Figure 2.2 – Indexation de tableaux multidimensionnels.
[2] serait la deuxième ligne, la troisième colonne et ainsi de suite. Les tableaux bidimensionnels sont
très utiles si vous disposez d’un ensemble de données pouvant être organisées sous forme de
tableau.

Seule la première dimension d'un tableau multidimensionnel peut être dynamique. Toutes les autres dimensions
doivent avoir une taille statique déclarée. Ceci est utile si vous n'êtes pas sûr du nombre de "lignes" que votre
tableau doit contenir. Regardons un

exemple:

double maDimension[][3];
lignes entières = 5 ;
ArrayResize(maDimension,lignes);

La première dimension est vide, indiquant qu'il s'agit d'un tableau dynamique. La deuxième dimension est
définie sur 3 éléments. La variable entièreLignes est utilisé pour définir la taille de la première dimension de
notre tableau. Nous passerons cette valeur au deuxième paramètre duTableauResize() fonction. Ainsi par
exemple, silignes = 5, alors la première dimension de notre tableau sera définie sur 5 éléments.

Un tableau peut avoir jusqu’à quatre dimensions, mais il est rare que vous en ayez besoin de plus de deux ou
trois. Dans la plupart des cas, une structure constituerait une méthode plus simple pour implémenter une
structure de données complexe. Nous aborderons les structures plus tard dans le chapitre.

Itérer à travers des tableaux


Le principal avantage des tableaux est qu’ils vous permettent de parcourir facilement un ensemble complet de
données. Voici un exemple où nous imprimons la valeur de chaque élément d'un tableau :

string myArray[3] = {"cheese","bread","ale"}; pour


(int index = 0; index < 3; index++)
{

Imprimer(monArray[index]);
}

// Sortie : fromage

18
Acheteur : Evangelos Papageorgiou (vpapnm@yahoo.gr)
Numéro de transaction : 28J511988U193831A

Variables et types de données


bière au
pain

Nous déclarons un tableau de chaînes nommémonTableau, d'une taille de 3. Nous initialisons les trois éléments
du tableau avec respectivement les valeurs "cheese", "bread" et "ale". Ceci est suivi d'unpour boucle. Lepour la
boucle initialise une variable compteur nomméeindice à 0. Cela incrémentera la valeur deindice par 1 à
chaque itération de la boucle. Il continuera à tourner aussi longtemps queindice est inférieur à 3. Leindice La
variable est utilisée comme index du tableau. Lors de la première itération de la boucle,indice sera égal à 0,
donc leImprimer() la fonction imprimera la valeur demonTableau[0] au journal – dans ce cas,"fromage". Lors
de la prochaine itération,indice sera égal à 1, donc la valeur demonTableau[1] sera imprimé dans le journal, et
ainsi de suite.

Comme mentionné plus tôt dans le chapitre, si vous essayez d'accéder à un élément du tableau dont la taille est
supérieure à l'index maximum du tableau, votre programme échouera. Lors d’une boucle dans un tableau, il est
important de connaître la taille du tableau. L'exemple ci-dessus utilise un tableau de taille fixe, nous connaissons
donc à l'avance la taille du tableau. Si vous utilisez un tableau dynamique ou si vous ne savez pas quelle sera la
taille d'un tableau, leTaille du tableau() La fonction peut être utilisée pour déterminer la taille d'un tableau :

int maDynamique[];

ArrayResize(myDynamic,10); int

size = ArraySize(myDynamic);

pour(int i = 0; i < taille; i++)


{ maDynamique[i] =
i;
Imprimer(je); // Sortie : 0, 1, 2... 9
}

Dans cet exemple, le tableaumaDynamique comporte 10 éléments. LeTaille du tableau() La fonction


renvoie le nombre d'éléments dans le tableau et attribue la valeur autaille variable. Letaille La variable est
ensuite utilisée pour définir la condition de fin dupour boucle. Cette boucle attribuera la valeur deje à chaque
élément dumaDynamique

tableau.

Nous discuteronspour boucles au chapitre 4. Gardez simplement à l’esprit qu’une boucle peut être utilisée pour
parcourir tous les éléments d’un tableau, et vous utiliserez fréquemment des tableaux uniquement dans ce but.

Énumérations
Unénumération est un type entier spécial qui définit une liste de constantes représentant des valeurs entières.
Seules les valeurs définies dans une énumération peuvent être utilisées dans des variables de ce type.

Par exemple, disons que nous devons créer une variable entière pour représenter les jours de la semaine. Il n'y a
que sept jours dans une semaine, nous n'avons donc pas besoin de valeurs inférieures à 0 ou supérieures à 7. Et
nous aimerions utiliser des constantes descriptives pour spécifier les jours de la semaine.

19
Acheteur : Evangelos Papageorgiou (vpapnm@yahoo.gr)
Numéro de transaction : 28J511988U193831A

ETXPERT UNCONSEIL P.ROGRAMMATION POUR METTRADER 5


Voici un exemple d'énumération qui permet à l'utilisateur de sélectionner le jour de la semaine :

énumération Jour de la semaine


{
Dimanche,
Lundi,
Mardi,
Mercredi,
Jeudi,
Vendredi,
Samedi,
} ;
Nous utilisons l'identifiant de typeénumération pour définir cela comme une énumération. Le nom de notre
énumération estJour de la semaine. Les sept jours de la semaine sont indiqués entre parenthèses, séparés
par des virgules. Le crochet fermant se termine par un point-virgule.

Les membres d'une énumération sont numérotés consécutivement, en commençant à 0. Donc dimanche = 0,
lundi = 1,
Samedi = 6 heures et ainsi de suite. Ces valeurs entières correspondent aux valeurs que MQL5 utilise dans
leMqlDateHeure structure pour le jour de la semaine. Nous en apprendrons davantage surMqlDateHeure plus
tard dans le livre. LeENUM_DAY_OF_WEEK L'énumération utilise également ces valeurs.

Pour utiliser notre énumération, nous devons définir une variable, en utilisant le nom de notre énumération
comme identifiant de type. Cet exemple crée une variable nomméeJour, et attribue la valeur àLundi à cela :

Jour de la semaine ;
Jour = lundi ;
Imprimer (Jour); // Sortie : 1

Dans la première ligne, nous utilisons le nom de notre énumération,Jour de la semaine, comme le type. C'est
un concept important à noter : lorsque nous créons une énumération, le nom de l'énumération devient un type,
tout comme la manière dontint,double ouchaîne sont des types. Nous déclarons ensuite une variable de ce
type en utilisant le nom de l'énumération comme identifiant de type. Ce concept s'applique également aux
structures et aux classes, dont nous parlerons sous peu.

Nous utilisons les constantes définies dans notre énumération pour attribuer une valeur à notreJour variable.
Dans ce cas, on attribue la valeur de la constanteLundi auJour variable. Si nous imprimons la valeur deJour
variable dans le journal, le résultat est 1, car la valeur entière de la constanteLundi est 1.

Que se passe-t-il si vous souhaitez que l’énumération commence par un nombre autre que zéro ? Peut-être
aimeriez-vous que les membres d'une énumération aient des valeurs non consécutives ? Vous pouvez attribuer
une valeur à chaque constante d'une énumération à l'aide de l'opérateur d'affectation (=). Voici un exemple :

enum annéeIntervalles
{ mois = 1,
deux mois, // 2 quart,
// 3 semestres =
6, année = 12,
} ;

20
Acheteur : Evangelos Papageorgiou (vpapnm@yahoo.gr)
Numéro de transaction : 28J511988U193831A

Variables et types de données

Cet exemple a été adapté duRéférence MQL5. Le nom de notre énumération estAnnéeIntervalles. Le nom du
premier membre estmois, auquel est attribuée la valeur 1. Les deux membres suivants,deux mois etquart, sont
incrémentés de un et reçoivent respectivement les valeurs 2 et 3. Les membres restants se voient attribuer leurs
valeurs respectives.

Un domaine dans lequel les énumérations sont utiles est de fournir à l'utilisateur un ensemble de valeurs parmi
lesquelles choisir. Par exemple, si vous créez une fonctionnalité de minuterie dans votre EA et que vous
souhaitez que l'utilisateur choisisse le jour de la semaine, vous pouvez utiliser l'optionJour de la semaine
énumération définie précédemment comme l’un de vos paramètres d’entrée. L'utilisateur verra une liste
déroulante de constantes dans le conseiller expert Propriétés boîte de dialogue parmi laquelle choisir.

Il existe de nombreuses énumérations standards prédéfinies dans MQL5. Toutes les énumérations standard dans
MQL5 commencent parENUM_ et sont tous en majuscules avec des caractères de soulignement. Vous pouvez
consulter les énumérations standards dans leRéférence MQL5 sousConstantes, énumérations et structures
standard.

Structures
UNstructure est un ensemble de variables liées de différents types. Le concept s'apparente à une énumération,
mais les membres d'une structure peuvent être de n'importe quel type. Il existe plusieurs structures prédéfinies
dans MQL5, et nous les utiliserons souvent. Vous n’aurez probablement pas besoin de créer vos propres
structures très souvent, mais il est important de savoir comment elles fonctionnent.

Regardons un exemple de structure. Cette structure pourrait être utilisée pour stocker les paramètres
commerciaux :

struct tradeSettings
{ glissement ulong ;
double prix ;
double stopPerte ;
double
priseProfit ;
commentaire de
chaîne ;
} ;

L'identifiant du typestructurer définit cela comme une structure. Le nom de la structure estparamètres
commerciaux. Il y a six variables membres définies entre parenthèses. Bien que vous puissiez attribuer une
valeur par défaut à une variable membre à l'aide de l'opérateur d'affectation (=), nous attribuons généralement
des valeurs après l'initialisation d'un objet.

Voici un exemple de la façon dont nous utiliserions notre structure dans le code :

échange de paramètres d'échange ;


trade.slippage = 50 ;
trade.stopLoss = StopLoss *
_Point ;

21
Acheteur : Evangelos Papageorgiou (vpapnm@yahoo.gr)
Numéro de transaction : 28J511988U193831A

ETXPERT UNCONSEIL P.ROGRAMMATION POUR METTRADER 5


Utiliser le nom de notre structureparamètres commerciaux comme type, nous définissons un objet
nommécommerce. Cet objet nous permet d'accéder aux variables membres de la structure. Nous parlerons
davantage des objets au chapitre 6. Pour accéder aux membres de la structure, nous utilisons l'opérateur point
(.) entre le nom de l'objet et le nom du membre. Dans ce cas, nous attribuons la valeur 50 au membre de la
structureglissement, et attribuez une valeur stop loss calculée au membre de la structurearrêter la perte.

Les structures prédéfinies dans MQL5 sont souvent utilisées pour renvoyer des valeurs depuis le serveur
d'échange. Par exemple, leMqlTick La structure stocke l’heure, le prix et le volume les plus récents d’un
instrument. Voici la définition duMqlTick structure:

structure MqlTick
{
dateheure heure ; // Heure de la dernière mise à jour des prix
double offre ; // Prix acheteur actuel double demande ; // Prix
demandé actuel double dernier ; // Prix de la dernière transaction
(Dernier) volume ulong ; // Volume pour le dernier prix actuel };

Pour utiliser leMqlTick structure, on initialise un objet de typeMqlTick. Ensuite, nous utilisons
leSymboleInfoTick()fonction pour remplir l'objet avec les données de temps, de prix et de volume pour le
symbole actuel du serveur de trading :

Prix MqlTick ;
SymbolInfoTick(_Symbol,prix);
Imprimer(prix.offre); // Renvoie le prix actuel de l'offre

Nous définissons un objet nomméprix, en utilisant le nom de la structureMqlTick comme le type. Nous
passons l'objet auSymboleInfoTick() fonction. La fonction renvoie l'objet, rempli avec les informations de prix
actuelles du serveur. Pour accéder à ces informations de prix, nous utilisons l'opérateur point (.) entre le nom de
l'objet et le nom de la variable membre. L'expressionprix.offre renverra le prix acheteur actuel, tandis
queprix.demander renverrait le prix Ask actuel.

Nous couvrirons leMqlTick structure ainsi que d’autres structures couramment utilisées en profondeur plus loin
dans le livre.

Typage
Le processus de conversion d'une valeur d'un type à un autre est appelétranstypage. Lorsque vous copiez le
contenu d'une variable dans une autre variable d'un type différent, le contenu estcoulé dans le type approprié.
Cela peut produire des résultats inattendus si vous n’y faites pas attention.

Lors de la copie de valeurs numériques d'une variable à une autre, il existe un risque de perte de données si
vous copiez d'un type vers un autre type plus petit. Rappelez-vous notre discussion sur les types entiers à la
page 12. Si vous copiez unint valeur dans unlong variable, il n'y a aucune possibilité de perte de données,
puisque lelong le type peut contenir une plus grande plage de valeurs.

Vous pouvez librement copier des types numériques plus petits dans des types plus grands. UNflotter valeur
copiée dans undouble la variable ne subirait aucune perte de données. Cependant, undouble valeur copiée
dans unflotter variable entraînerait ledouble valeur étant tronquée. La même chose peut se produire si vous

22
Acheteur : Evangelos Papageorgiou (vpapnm@yahoo.gr)
Numéro de transaction : 28J511988U193831A

Variables et types de données

copiez unint valeur dans uncourt variable. Cela est particulièrement vrai si vous lancez un type de nombre
flottant (tel qu'undouble) en un type entier. Tout ce qui se trouve après la virgule sera perdu. C'est bien si vous
n'avez pas besoin de la partie fractionnaire du nombre.

Si vous transtypez une valeur d'un type plus grand dans une variable d'un type plus petit, le compilateur vous
avertira avec le message « perte possible de données due à la conversion de type ». Si vous êtes certain que la
valeur du type plus grand ne dépassera pas la plage du type plus petit, vous pouvez alors ignorer le message en
toute sécurité. Sinon, vous pouvezexplicitementtranstypez la nouvelle valeur pour faire disparaître
l'avertissement.

Par exemple, si vous avez undouble valeur que vous devez transmettre à une fonction qui nécessite une valeur
entière, vous obtiendrez l’erreur « perte possible de données due à la conversion de type ». Vous pouvez
convertir la nouvelle valeur sous forme d'entier en faisant précéder la variable de(int). Par exemple:

double différence = (haut – bas) / _Point ; BuyStopLoss(_Symbol,


(int) différence);

La double variabledifférence est calculé en divisant deux valeurs à virgule flottante. LeAcheterStopLoss() La
fonction nécessite une valeur entière pour le deuxième paramètre. Passer ledifférence La variable provoquera
un avertissement du compilateur. En préfaçant ledifférence nom de variable avec(int), nous calculons la
valeur dedifférence à un entier, arrondissant efficacement la valeur et évitant l'erreur du compilateur.

Variables d'entrée
Levariables d'entrée d'un programme MQL5 sont les seules variables pouvant être modifiées par l'utilisateur.
Ces variables comprennent les paramètres de négociation, les paramètres d'indicateurs, les valeurs de stop loss
et de take profit, etc. Ils sont affichés sous leContributions onglet du programmePropriétés fenêtre.

Une variable d'entrée est précédée dusaisir mot-clé. Les variables d'entrée sont placées au début de votre
programme, avant toute fonction ou autre code de programme. Les variables d'entrée peuvent être de n'importe
quel type, y compris des énumérations. Les tableaux et les structures ne peuvent pas être utilisés comme
variables d'entrée. Les identifiants des variables d'entrée doivent être clairs et descriptifs.

Voici un exemple de certaines variables d'entrée que vous pouvez voir dans un conseiller expert :

entrée int MAPeriod = 10 ;


entrée ENUM_MA_METHOD MAMethod = MODE_SMA ;
entrée double StopLoss = 20 ;
chaîne d'entrée Commentaire =
"ea" ;

Ces variables d'entrée définissent la période et la méthode de calcul d'un indicateur de moyenne mobile,
définissent un stop loss pour la commande et ajoutent un commentaire à la commande.

Vous pouvez définir un nom d'affichage convivial dans leContributions en ajoutant une variable d'entrée avec un
commentaire. La chaîne de commentaire apparaîtra dans leVariable colonne duContributions languette. Voici
les variables d'entrée définies ci-dessus avec un commentaire descriptif :

23
Acheteur : Evangelos Papageorgiou (vpapnm@yahoo.gr)
Numéro de transaction : 28J511988U193831A

ETXPERT UNCONSEIL P.ROGRAMMATION POUR METTRADER 5


entrée int MAPeriod = 10 ; // Entrée de période moyenne mobile
ENUM_MA_METHOD MAMethod = MODE_SMA ; // Entrée de la méthode de
moyenne mobile double StopLoss = 20 ; // Chaîne d'entrée Stop loss
(points) Comment = "ea"; // Commentaire commercial

UNstatiqueLa variable d'entrée peut être définie en utilisant lesinput mot-clé. La valeur d'une variable d'entrée
statique peut être modifiée, mais elle ne peut pas être optimisée dans Strategy Tester. Les variables d'entrée
statiques sont utiles pour le regroupement logique des paramètres d'entrée. Déclarez simplement unsinput
variable de type chaîne et incluez un commentaire :

chaîne sinput MASettings ; // Paramètres de moyenne mobile

La figure 2.3 ci-dessous montre comment les variables d'entrée ci-dessus apparaîtront dans leContributions
onglet duPropriétés fenêtre:

Figure 2.3 - LeContributions , affichant des commentaires à la place des noms de variables.

Pour utiliser une énumération que vous avez créée comme type de variable d'entrée, vous devrez définir
l'énumération avant la variable d'entrée elle-même. Nous utiliserons leJour de la semaine énumération que
nous avons définie plus tôt dans le livre :

énumération Jour de la semaine


{
Dimanche,
Lundi,
Mardi,
Mercredi,
Jeudi,
Vendredi,
Samedi,
} ; entrée DayOfWeek Day =

lundi ;

24
Acheteur : Evangelos Papageorgiou (vpapnm@yahoo.gr)
Numéro de transaction : 28J511988U193831A

Variables et types de données

Cela crée une variable d'entrée nomméeJour, en utilisant leJour de la semaine énumération comme type,
avec une valeur par défaut deLundi ou 1. Lorsque l'utilisateur tente de modifier la valeur duJour variable
d'entrée, une liste déroulante apparaîtra, contenant toutes les valeurs de l'énumération.

Variables locales
UNvariable locale est celui qui est déclaré dans une fonction. Les variables locales sont allouées en mémoire
lors de la première exécution de la fonction. Une fois la fonction terminée, la variable est effacée de la mémoire.

Dans cet exemple, nous allons créer une fonction simple. Nous discuterons des fonctions plus en détail au
chapitre 5. Nous déclarerons une variable locale à l'intérieur de notre fonction. Lorsque le code de cette fonction
est exécuté, la variable est déclarée et utilisée. Lorsque la fonction se termine, la variable est effacée de la
mémoire :

annuler maFonction()
{int varInt = 5 ;
Imprimer(varInt); // Sortie : 5
}

Le nom de cette fonction estmaFonction(). Cette fonction serait appelée et exécutée ailleurs dans notre
programme. La variablevarInt est local à cette fonction. C'est ce qu'on appelle leportée variable. Les variables
locales ne peuvent pas être référencées depuis l'extérieur de la fonction ou ailleurs dans le programme. Ils sont
créés lors de l'exécution de la fonction et supprimés à la sortie de la fonction.

Regardons de plus prèsvariable portée. La portée d'une variable locale est limitée au bloc dans lequel elle est
déclarée.bloc est défini comme une fonction ou un opérateur composé au sein d’une fonction. Un bloc est
entouré de crochets ouvrants et fermants ({}). Toutes les variables déclarées à l'intérieur d'un bloc sont locales
uniquement à ce bloc. Jetons un coup d'œil à un exemple modifié de notre fonction :

annuler maFonction()
{ bool varBool = vrai ;

si (varBool == vrai)
{int varInt = 5 ;
Imprimer(varInt); // Sortie : 5
}
}

Nous avons ajouté une nouvelle variable locale, une variable booléenne nomméevarBool. Nous avons
également ajouté unsi bloc opérateur. Lesi L'opérateur, ses parenthèses qui l'accompagnent et le code qu'ils
contiennent sont un opérateur composé. Toutes les variables déclarées entre parenthèses sont locales ausi
bloc opérateur.

Lesi l'expression peut être lue comme suit : "SivarBool contient la valeur devrai, puis exécutez le code entre
parenthèses." Dans ce cas, l'expression est vraie, donc le code entre parenthèses est exécuté.varInt La variable
est déclarée et la valeur est imprimée dans le journal.

25
Acheteur : Evangelos Papageorgiou (vpapnm@yahoo.gr)
Numéro de transaction : 28J511988U193831A

ETXPERT UNCONSEIL P.ROGRAMMATION POUR METTRADER 5


LevarInt la variable est locale ausi bloc opérateur. Cela signifie quevarInt ne peut pas être référencé en
dehors du bloc. Une fois le bloc sorti,varInt est hors de portée. Si nous essayons de le référencer depuis
l'extérieur dusi bloc opérateur, nous obtiendrons une erreur de compilation :

si (varBool == vrai)
{int varInt = 5 ;
Imprimer(varInt); // Sortie : 5
}

varInt = 7 ; // Ces deux expressions Print(varInt); // produira


une erreur

L'expressionvarInt = 7 produira une erreur lors de la compilation. La variablevarInt que nous avons déclaré
à l'intérieur dusi le bloc opérateur est hors de portée. Si l'on veut corriger ce code, on peut simplement déclarer
levarInt variable qui est en dehors dusi bloc opérateur :
si (varBool == vrai)
{int varInt =
5 ;
Imprimer(varInt); // Sortie : 5
}

// C'est une variable


int varInt = 7; différente !
Imprimer(varInt); // Sortie : 7
Nous avons maintenant une variable nomméevarInt dans ce cadre. Notez qu'il s'agit d'une variable différente
de lavarInt variable déclarée à l'intérieur dusi bloc opérateur, même s’ils portent tous les deux le même nom !

Voici un autre exemple : La variablevarInt est déclarée en haut de la fonction, et une autre variable du même
nom et du même type est déclarée à l'intérieur dusi bloc opérateur imbriqué à l’intérieur de la fonction.

annuler maFonction()
{ bool varBool = vrai ;
int varInt = 7;

si (varBool == vrai)
{int varInt = 5 ;
Imprimer(varInt); // Sortie : 5
}

Imprimer(varInt); // Sortie : 7
}

Une variable entière nomméevarInt est déclarée en haut de la fonction et reçoit la valeur 7. Une deuxième
variable entière nomméevarInt est déclaré à l'intérieur dusi bloc opérateur et initialisé avec une valeur de 5.
Lorsque nous imprimons la valeur devarInt à l'intérieur desi bloc opérateur, il imprime une valeur de 5.

Quand lesi le bloc opérateur est quitté et nous imprimons la valeur devarInt encore une fois, cette fois, il
affiche une valeur de 7 – la même valeur qui a été déclarée en haut de la fonction ! En d'autres termes, levarInt
variable qui a été déclarée à l'intérieur dusi Le bloc opérateur remplace celui déclaré dans la portée de la

26
Acheteur : Evangelos Papageorgiou (vpapnm@yahoo.gr)
Numéro de transaction : 28J511988U193831A

Variables et types de données

fonction. À propos, si vous essayez de compiler ceci, vous recevrez un message d'avertissement indiquant "la
déclaration de 'varInt' masque la déclaration locale..." L'exemple ci-dessus n'est pas considéré comme une
bonne pratique, donc renommer le deuxièmevarInt La variable corrigerait l'avertissement.

Juste pour clarifier les choses, voyons ce qui se passerait si nous déclarions levarInt variable une fois en dehors
dusi bloc opérateur, puis l'a référencé à l'intérieur dusi bloc opérateur :

annuler maFonction()
{ bool varBool = vrai ;
int varInt = 5;

si (varBool == vrai)
{
Imprimer(varInt); // Sortie : 5
}
}

Cela fonctionnera comme prévu, car levarInt la variable est déclarée en dehors dusi bloc opérateur, dans la
portée de la fonction. Une variable déclarée dans une portée supérieure est toujours dans la portée de tous les
blocs imbriqués, tant que ces blocs imbriqués n'ont pas de variable du même nom et du même type.

Cela peut sembler arbitraire et déroutant, mais la plupart des langages de programmation modernes traitent la
portée des variables de cette façon. En exigeant que les variables soient locales uniquement à l'intérieur du bloc
dans lequel elles sont déclarées, les erreurs de programmation sont évitées lorsque les variables partagent le
même nom. Notez également que ceci est différent de la façon dont les variables locales étaient initialement
gérées dans MQL4. Dans MQL4, une variable locale déclarée dans une fonction était valide n'importe où dans
cette fonction, même si elle était déclarée dans un opérateur ou un bloc composé.

Variables globales
UNvariable globale est celui qui est déclaré en dehors de toute fonction. Les variables globales sont définies en
haut de votre programme, généralement après les variables d'entrée. La portée d'une variable globale est
l'ensemble du programme.

Comme démontré dans la section précédente, une variable locale déclarée dans un bloc remplacera toute
variable du même nom et du même type dans une portée supérieure. Si vous faites cela avec une variable
globale, vous recevrez un avertissement de compilation : "La déclaration de la variable masque la déclaration
globale." Il n'y a aucune raison pratique de remplacer une variable globale de cette façon, alors faites attention à
cela.

La valeur d'une variable globale peut être modifiée n'importe où dans le programme, et ces modifications seront
disponibles pour toutes les fonctions du programme. Voici un exemple :

// Variable globale
int GlobalVarInt = 5;

fonction videA()
{
GlobalVarInt = 7 ;
}

27
Acheteur : Evangelos Papageorgiou (vpapnm@yahoo.gr)
Numéro de transaction : 28J511988U193831A

ETXPERT UNCONSEIL P.ROGRAMMATION POUR METTRADER 5


fonction videB()
{
Imprimer(GlobalVarInt); // Sortie : 7
}

La variable globaleGlobalVarInt est déclaré en haut du programme et reçoit la valeur 5. Nous supposerons
quefonction() est exécuté en premier. Cela change la valeur deGlobalVarInt à 7. QuandfonctionB()est
exécuté, il imprimera la valeur modifiée deGlobalVarInt, qui est maintenant 7.

Si vous disposez d'une variable à laquelle plusieurs fonctions doivent accéder dans votre programme, déclarez-
la comme variable globale en haut de votre programme. Si vous disposez d'une variable locale dont la valeur
doit être conservée entre les appels de fonction, utilisez plutôt une variable statique.

Variables statiques
UNvariable statique est une variable locale qui reste en mémoire même lorsque le programme a quitté la portée
de la variable. Nous déclarons une variable statique en la faisant précéder dustatique modificateur. Voici un
exemple de variable statique dans une fonction. A chaque appel de la fonction, la variable statique est
incrémentée de 1. La valeur de la variable statique est conservée entre les appels de fonction :

void staticFonction()
{ static int staticVar = 0;
VarVar++ ; // Incrémente staticVar de 1 Print(staticVar); // Sortie : 1,
2, 3, etc.
}

Une variable entière statique nomméeVarState est déclaré avec lestatique modificateur. Au premier appel de
la fonction,VarState est initialisé à 0. La variable est incrémentée de 1 et le résultat est imprimé dans le journal.
La première entrée du journal indiquera 1. Au prochain appel de la fonction,VarState aura la valeur 1. (Notez
qu'elle ne sera pas réinitialisée à zéro !) La variable est ensuite incrémentée de 1, et une valeur de 2 est imprimée
dans le journal, et ainsi de suite.

Variables prédéfinies
MQL5 dispose de plusieurs variables prédéfinies pour accéder aux valeurs couramment utilisées. Les variables
prédéfinies sont précédées du caractère de soulignement (_). Toutes ces variables ont également des fonctions
équivalentes, mais comme elles sont fréquemment utilisées comme paramètres de fonction, les variables
prédéfinies sont plus faciles à lire.

Voici une liste de variables prédéfinies couramment utilisées dans MQL5. Toutes ces variables font référence
aux propriétés du graphique auquel le programme est actuellement attaché :

• _Symbole – Le symbole de la sécurité financière sur le graphique courant.

• _Période – La période, en minutes, du graphique actuel.

• _Indiquer – La valeur en points du symbole actuel. Pour les paires de devises Forex à cinq chiffres, la
valeur en points est
0,00001, et pour les paires de devises à trois chiffres (JPY), la valeur en points est 0,001.

28
Acheteur : Evangelos Papageorgiou (vpapnm@yahoo.gr)
Numéro de transaction : 28J511988U193831A

Variables et types de données

• _Chiffres – Le nombre de chiffres après la virgule pour le symbole actuel. Pour les paires de devises
Forex à cinq chiffres, le nombre de chiffres est 5. Les paires JPY ont 3 chiffres.

29
Acheteur : Evangelos Papageorgiou (vpapnm@yahoo.gr) ID de transaction : 28J511988U193831A
Acheteur : Evangelos Papageorgiou (vpapnm@yahoo.gr)
Numéro de transaction : 28J511988U193831A

Opérations

Chapitre 3 - Opérations

Opérations
Nous pouvons effectuer des opérations mathématiques et attribuer le résultat à une variable. Vous pouvez
même utiliser d'autres variables dans une opération. Montrons comment effectuer des opérations
mathématiques de base dans MQL5.

Addition et multiplication
// Ajout
int varAjouteA = 3 + 5; // Résultat : 8
double varAddB = 2,5 + varAddA ; // Résultat : 10,5

//Multiplication
int varMultA = 5 * 3; // Résultat : 15
double varMultB = 2,5 * varMultA ; // Résultat : 37,5

Dans le premier exemple de chaque opération, nous additionnons ou multiplions deux entiers ensemble et
attribuons le résultat à unint variable (varAjouteA etvarMultA). Dans le deuxième exemple, on additionne ou
multiplie un nombre réel (ou nombre à virgule flottante) par une variable contenant une valeur entière. Le
résultat est stocké dans undouble variable (varAjouteB ouvarMultB).

Si vous savez que deux valeurs seront de type entier, vous pouvez alors les stocker dans une variable entière.
S'il est possible qu'une valeur soit un nombre à virgule flottante, utilisez un type de nombre réel tel quedouble
ouflotter.

Soustraction et négation
// Soustraction
int varSubA = 5 – 3 ; // Résultat : 2
double varSubB = 0,5 – varSubA ; // Résultat : -1,5

// Négation int varNegA = -5; double varNegB = 3,3


– varNegA ; // Résultat : 8.3

Dans l'exemple de soustraction, nous soustrayons d'abord deux entiers et attribuons la valeur à unint
variable,varSubA. Ensuite, nous soustrayons notre variable entière d'un nombre à virgule flottante, 0,5. Le
résultat est un nombre à virgule flottante négatif attribué à undouble variable,varSubB.

Vous pouvez spécifier une constante numérique sous forme de nombre négatif simplement en plaçant un signe
moins (-) directement devant elle. Leint variablevarNegA a une valeur de -5 qui lui est attribuée. Ensuite, nous

31
Acheteur : Evangelos Papageorgiou (vpapnm@yahoo.gr)
Numéro de transaction : 28J511988U193831A

ETXPERT UNCONSEIL P.ROGRAMMATION POUR METTRADER 5


soustrayons -5 de 3,3 et attribuons le résultat àvarNegB. Puisque nous soustrayons un nombre négatif d’un
nombre positif, le résultat est une opération d’addition qui donne une valeur de 8,3.

Division et module
// Division

double varDivA = 5/2 ; // Résultat :


2,5
int varDivB = 5/2 ; // Résultat
// Module : 2

int varMod = 5 % 2 ; // Résultat


: 1
L'exemple de première division,varDivA, divise deux entiers et stocke le résultat dans undouble variable. Noter
que
5/2 ne se divise pas également, il y a donc un reste fractionnaire. En raison de la possibilité de restes
fractionnaires, vous devez toujours utiliser un type de nombre réel lors de la division !

Le deuxième exemple le démontre : nous divisons 5 / 2 et stockons le résultat dans unint variable,varDivB.
Étant donné que les types entiers ne stockent pas de valeurs fractionnaires, la valeur est arrondie au nombre
entier le plus proche.

L'exemple de module divise 5 par 2 et stocke le reste entier dansétaitMod. Vous ne pouvez utiliser l'opérateur
de module (%) que sur deux entiers. Il est donc prudent de stocker le reste dans unint variable.

Opérations d'affectation
Parfois, vous devrez effectuer une opération mathématique à l’aide d’une variable, puis réaffecter le résultat à la
variable d’origine. Voici un exemple utilisant l'addition :

int varAjoute = 5 ;
varAjoute = varAjoute + 3 ;
Imprimer(varAjouter); // Sortie : 8

Nous déclarons la variablevarAjouter et l'initialisons à une valeur de 5. Puis on ajoute 3 àvarAjouter, et


attribuez le résultat àvarAjouter. Le résultat est 8. Voici une manière plus courte de procéder :

int varAjoute = 5 ;
varAjoute += 3; //varAjoute = 8

Ici, nous combinons un opérateur mathématique (+) avec l'opérateur d'affectation (=). Cela peut être lu comme
"Ajouter 3 àvarAjouter, et attribuez le résultat àvarAjouter. " Nous pouvons également faire cela avec
d'autres opérateurs mathématiques :

varSub -= 3 ; // Affectation de
soustraction
varMult *= 3 ; // Affectation de
multiplication

32
Acheteur : Evangelos Papageorgiou (vpapnm@yahoo.gr)
Numéro de transaction : 28J511988U193831A

Opérations
varDiv /= 3; // Affectation de
division
varMod %= 3 ; // Affectation du module
Opérations relationnelles
Vous devrez souvent comparer deux valeurs dans une relation supérieure à, inférieure à, égale ou non égale.
L'opération donne un résultat booléen, soitvrai ouFAUX. Jetons un coup d'œil aux opérations supérieur et
inférieur à :

une > b // Supérieur à a <


b // Moins que

Dans le premier exemple, siun est supérieur àb, le résultat est vrai, sinon faux. Dans le deuxième exemple, siun
est inférieur àb, le résultat est vrai. Vous pouvez également vérifier l’égalité :

une >= b // Supérieur ou égal à a


<= b // Inférieur ou égal à

Dans le premier exemple, siun est supérieur ou égal àb, le résultat est vrai. Dans le deuxième exemple, siun est
inférieur ou égal àb, le résultat est vrai. Regardons les opérations égales et non égales :

une == b // Égal à a
!= b // Pas égal à

Dans le premier exemple, siun est égal àb, le résultat est vrai. Dans le deuxième exemple, siun n'est pas égal àb,
le résultat est vrai. Notez que l'opérateur d'égalité (==) n'est pas le même que l'opérateur d'affectation (=)! C'est
une erreur courante commise par les nouveaux programmeurs.

Lorsque vous utilisez des nombres réels, il est important de normaliser ou d’arrondir les nombres à un nombre
spécifique de décimales. Cela se fait en utilisant leNormaliserDouble() fonction. Le premier argument est
ledouble valeur à normaliser. Le deuxième argument est le nombre de chiffres après la virgule à arrondir. Voici
un

exemple:

double normaleA = 1,35874934 ;


double normaleB = 1,35873692 ;

normalA = NormalizeDouble(normalA,4); // Résultat : 1,3587 normalB =


NormalizeDouble(normalB,4); // Résultat : 1,3587 if(normalA ==
normalB)
{
Imprimer("Égal");
}

33
Acheteur : Evangelos Papageorgiou (vpapnm@yahoo.gr)
Numéro de transaction : 28J511988U193831A

ETXPERT UNCONSEIL P.ROGRAMMATION POUR METTRADER 5


Dans cet exemple, nous avons deuxdouble variables contenant des valeurs fractionnaires à 8 décimales. Nous
utilisons leNormaliserDouble() fonction pour arrondir ces nombres à 4 décimales et attribuer les résultats à
leurs variables d’origine. Nous pouvons alors les comparer dans une déclaration d’égalité. Dans ce cas,normalA
etnormalB sont égaux, donc la chaîne "Égal" est imprimé dans le journal.

Si nous essayions d’effectuer une opération d’égalité sur deux prix sans normaliser les nombres, il est peu
probable que nous obtenions un jour un résultat égal. En interne, les prix et les valeurs des indicateurs sont
calculés selon un grand nombre de chiffres significatifs. En normalisant les nombres, nous pouvons vérifier
l’égalité en utilisant un plus petit nombre de chiffres.

Opérations booléennes
Une opération booléenne compare deux ou plusieurs opérations (mathématiques, booléennes ou relationnelles)
à l'aide d'opérateurs logiques et évalue si l'expression est vraie ou fausse. Il existe trois opérations logiques : ET,
OU

et pas.

Jetons un coup d'œil à un exemple d'opération AND. Une opération AND est vraie si toutes les opérations de
l’expression sont vraies. L'opérateur logique pour AND est&& (deux esperluettes) :

entier a =
1 ; entier b
= 1 ; entier
c = 2 ;

si(a == b && a + b == c)
{
Imprimer(vrai); // Résultat : vrai
}

Tout d’abord, nous déclarons et initialisons trois variables entières :un,b etc. Nous utilisons unsi opérateur pour
évaluer notre opération booléenne. Si l’opération est vraie, le code entre parenthèses est exécuté.

La valeur deun est 1, et la valeur deb vaut 1. L'expressionune == b est vrai, nous passons donc à l’expression
suivante. L'opération d'additiona + b est égal à 2. La valeur dec est également 2, donc cette expression est
vraie. L'opération booléenne AND est évaluée à vrai, donc une valeur devrai est imprimé dans le journal.

Voyons ce qui se passe si nous avons une expression fausse dans notre opération booléenne AND :

entier a =
1 ; entier b
= 1 ; entier
c = 3 ; si(a
== b && a + b
== c)
{
Imprimer(vrai);

34
Acheteur : Evangelos Papageorgiou (vpapnm@yahoo.gr)
Numéro de transaction : 28J511988U193831A

Opérations
}
autre
{
Imprimer(faux); // Résultat : vrai
}

Dans cet exemple, nous initialisons la valeur dec à 3. Depuisa + b n'est pas égal àc, l'expression est évaluée
comme fausse, et donc l'opération booléenne AND est évaluée comme fausse. Dans ce cas, l'exécution passe
auautre opérateur, et une valeur deFAUX est imprimé dans le journal.

Ensuite, nous examinerons l’opération booléenne OR. Une opération OU est vraie si l’une des expressions est
évaluée comme vraie. L'opérateur OU est|| (deux tuyaux) :

entier a =
1 ; entier b
= 1 ; entier
c = 3 ;

si(a == b || a + b == c)
{
Imprimer(vrai); // Résultat : vrai
}

Ce code est presque identique à l'exemple précédent, sauf que nous utilisons un opérateur OR. L'expressionune
== b c'est vrai, maisa + b == c n'est pas. Puisqu’au moins une des expressions est vraie, l’opération
booléenne OU est évaluée comme vraie et une valeur devrai est imprimé dans le journal.

Enfin, nous examinerons l’opération booléenne NOT. L'opérateur NOT (!) est appliqué à une seule expression
booléenne. Si l'expression est vraie, l'opération NOT est évaluée comme fausse, et vice versa. Essentiellement,
l'opérateur NOT inverse la valeur vrai/faux d'une expression booléenne. Voici un exemple :

bool not = faux ;

sinon)
{
Imprimer(vrai); // Résultat : vrai
}

La variablepas est initialisé à false. L'expression booléenne!pas est évalué à vrai. Ainsi, une valeur devrai est
imprimé dans le journal. L'opérateur NOT fonctionne également sur des expressions plus complexes :

entier a = 1 ;
entier b = 2 ;

si(!(a == b))
{
Imprimer(vrai); // Résultat : vrai

35
Acheteur : Evangelos Papageorgiou (vpapnm@yahoo.gr)
Numéro de transaction : 28J511988U193831A

ETXPERT UNCONSEIL P.ROGRAMMATION POUR METTRADER 5


}

L'expressionune == b c'est faux. En mettant l'expression entre parenthèses et en appliquant l'opérateur NOT,
l'expression est évaluée à vrai et une valeur devrai est imprimé dans le journal.

36
Acheteur : Evangelos Papageorgiou (vpapnm@yahoo.gr)
Numéro de transaction : 28J511988U193831A

Opérateurs conditionnels et de boucle

Chapitre 4 - Opérateurs conditionnels et de boucle

Opérateurs conditionnels
L’une des fonctions les plus fondamentales de tout programme est la prise de décisions. Les opérateurs
conditionnels sont utilisés pour prendre des décisions en évaluant une condition vrai/faux. Il existe trois
opérateurs conditionnels dans MQL5 : lesinon l'opérateur, l'opérateur ternaire et l'opérateurboîtier de
commutation opérateur.

L'opérateur if
Vous avez déjà été présenté ausi opérateur. Lesi L'opérateur est l'opérateur conditionnel le plus courant dans
MQL5 et que vous utiliserez souvent. Il s’agit d’un opérateur composé, ce qui signifie qu’il contient généralement
plus d’une expression.

Lesi L’opérateur évalue une condition comme vraie ou fausse. La condition peut être une opération
relationnelle ou booléenne. Si la condition est vraie, la ou les expressions à l’intérieur de l’opérateur composé
sont exécutées. Si la condition est fausse, le contrôle passe à la ligne de code suivante. Regardons un simplesi
expression:

condition booléenne = vrai ;

si (condition == vrai) // Si la condition entre parenthèses est vraie...


{
Imprimer(vrai); // Exécute le code entre parenthèses
}

Nous déclarons une variable booléenne nomméecondition, et définissez sa valeur survrai. Ensuite, nous
évaluons la condition booléenne dans lesi opérateur. Dans ce cas,condition == vrai, nous exécutons donc
le code entre parenthèses, qui imprime une valeur devrai au journal.

Quand unsi L'opérateur composé n'a qu'une seule expression, vous pouvez omettre les crochets et le placer
sur la même ligne que l'opérateur composé.si opérateur. Vous pouvez même placer l'expression sur la ligne
suivante. Notez que cela ne fonctionne qu'avec une seule expression et que cette expression doit être terminée
par un point-virgule :

// Une seule ligne


if(condition == true) Print(true);

// Multiligne
if(condition == true)
Imprimer(vrai);
Vous pouvez en avoir plusieurssi expressions les unes à côté des autres. Chaquesi l’expression sera évaluée
individuellement. Dans l'exemple ci-dessous, deuxsi les opérateurs évaluent une opération relationnelle. Les
deuxImprimer() les fonctions seront exécutées, puisque 2 est supérieur à 1 et inférieur à 3 :

nombre entier = 2 ;

37
Acheteur : Evangelos Papageorgiou (vpapnm@yahoo.gr)
Numéro de transaction : 28J511988U193831A

ETXPERT UNCONSEIL P.ROGRAMMATION POUR METTRADER 5


if(nombre > 1) Print("le nombre est supérieur à 1");
if(nombre < 3) Print("le nombre est inférieur à 3");

// Résultat : le nombre est supérieur à 1


// le nombre est inférieur à 3

L'opérateur else
Leautre l'opérateur est le compagnon dusi opérateur. Leautre l'opérateur est placé après unsi opérateur.
Quand lesi L'opérateur est évalué à false, la ou les expressions à l'intérieur duautre L'opérateur est exécuté à la
place :

condition booléenne = faux ;

if(condition == true) Print(true); // Si la condition est vraie


sinon Imprimer(faux); // Si la condition est fausse
// Résultat : faux

Dans cet exemple, lesi L'opérateur est évalué à false, donc l'expression à l'intérieur duautre l'opérateur est
exécuté et une valeur deFAUX est imprimé dans le journal. Leautre L'opérateur est utile si vous souhaitez
effectuer une action par défaut lorsque toutes les autres conditions sont fausses.

Leautre opérateur peut être combiné avec lesi opérateur, permettant d’évaluer plusieurs conditions. Lorsqu'un
ou plusieurssinon si les opérateurs sont placés dans unsinon bloc, la première condition vraie mettra fin à
l’exécution dusinon bloc. Unsinon si l'opérateur doit être placé après le premiersi opérateur, et avant
toutautre opérateur:

int unOuDeux = 2;

si (unOuDeux == 1)
Print("unOuDeux vaut 1");

sinon si (unOuDeux == 2) // Si précédent si l'opérateur est faux


Print("unOuDeux vaut 2");

autre // Si les deux opérateurs if sont faux


Print("oneOrTwo n'est ni 1 ni 2"); // Résultat : unOuDeux vaut 2
Dans cet exemple, nous déclarons une variable entière,un ou deux, et attribuez une valeur de 2. La condition
pour lesi opérateur,unOuDeux == 1, est faux, nous passons donc ausinon si opérateur. Lesinon si état de
l'opérateur,unOuDeux == 2 est vrai, donc la chaîne "unOuDeux vaut 2" est imprimé dans le journal.

Lesi etsinon si Les opérateurs sont évalués dans l’ordre jusqu’à ce que l’un d’eux soit vrai. Une fois unsi
ousinon si L'opérateur est évalué à vrai, la ou les expressions à l'intérieur de l'opérateur sont exécutées et le
programme reprend son exécution après l'exécution de l'opérateur.sinon bloc. Si aucun dessi ousinon si les
opérateurs sont évalués à vrai, puis la ou les expressions à l'intérieur duautre l’opérateur s’exécutera à la place.

38
Acheteur : Evangelos Papageorgiou (vpapnm@yahoo.gr)
Numéro de transaction : 28J511988U193831A

Opérateurs conditionnels et de boucle

Par exemple, siun ou deux reçoit la valeur 1, l'expression dans le premiersi opérateur,unOuDeux == 1, sera
vrai. Le message"unOuDeux vaut 1" sera imprimé dans le journal. Ce qui suitsinon si etautre Les
opérateurs ne seront pas évalués. Le programme reprendra son exécution après leautre opérateur.

Si tout lesi etsinon si les opérateurs sont faux, l'expression dans leautre L'opérateur est exécuté. Dans ce
cas, le message"oneOrTwo n'est pas 1 ou 2" sera imprimé dans le journal :

int unOuDeux = 3;

si (unOuDeux == 1)
Print("unOuDeux vaut 1");

sinon si (unOuDeux == 2)
Print("unOuDeux vaut 2");

autre // Résultat : oneOrTwo n'est ni 1 ni 2


Print("oneOrTwo n'est ni 1 ni 2");

Notez que leautre l'opérateur n'est requis dans aucunsinon bloc. S'il est présent, il doit venir après toutsi
ousinon si les opérateurs. Vous pouvez en avoir plusieurssinon si opérateurs, voire aucun. Tout dépend de
vos besoins.

Opérateur ternaire
Leternaire L'opérateur est un raccourci sur une seule ligne pour lesinon opérateur. Un opérateur ternaire se
compose de trois parties. La première partie est la condition vrai/faux à évaluer. La deuxième partie est
l'expression à exécuter si la condition est vraie. La troisième partie est l'expression à exécuter si la condition est
fausse. Le résultat de l'expression est affecté à une variable. Voici un exemple :

condition booléenne = vrai ; résultat


booléen = condition ? vrai faux;

Imprimer(résultat); // Sortie : vrai


Nous déclarons une variable booléenne nomméecondition, et définissez la valeur survrai. Cette variable est
utilisée comme condition pour l'opérateur ternaire. Un point d'interrogation (?) sépare la condition des
expressions. La première expression attribue la valeur true à la variable booléennerésultat. La deuxième
expression attribue la valeur false au résultat de la variable. Les expressions sont séparées par deux points (:).

Dans ce cas,condition == vrai, donc la première expression,vrai, est affecté à la variablerésultat. Voici
comment nous exprimerions cela en utilisant l'opérateur if-else :

condition booléenne =
vrai ; résultat booléen ;

si (condition == vrai) résultat = vrai ;


sinon résultat = faux ;

Imprimer(résultat); // Sortie : vrai

39
Acheteur : Evangelos Papageorgiou (vpapnm@yahoo.gr)
Numéro de transaction : 28J511988U193831A

ETXPERT UNCONSEIL P.ROGRAMMATION POUR METTRADER 5


Nous avons enregistré deux lignes de code en utilisant l'opérateur ternaire. Celui que vous préférez utiliser
dépend de vous.

Opérateur de commutation
Lechanger L'opérateur compare une expression à une liste de valeurs constantes à l'aide de l'opérateurcas
opérateur. Lorsqu'une valeur constante correspond, les expressions qui l'accompagnent sont exécutées. Voici
un exemple :

entier x = 1 ;

commutateur
(x) { cas
1 :
Imprimer("x vaut 1"); // Sortie : x vaut 1
casser;

cas 2 :
Imprimer("x vaut 2");
casser;

défaut:
Print("x n'est pas 1 ou 2");
}

On déclare une variable entière,X, et attribuez une valeur de 1. Lechanger L’opérateur contient l’expression à
évaluer. Dans ce cas, l'expression est la variableX. Lecas les opérateurs sont des étiquettes, chacune étant
affectée à une valeur constante différente. DepuisX est égal à 1, la chaîne "x vaut 1" sera imprimé dans le
journal. Lecasser l'opérateur termine l'exécution duchanger opérateur.

Si l'expression à l'intérieur duchanger l'opérateur ne correspond à aucun descas opérateurs, l'optiondéfaut


l’opérateur s’exécutera à la place. Par exemple, siX ne correspond à aucun descas opérateurs, les expressions
après ledéfaut L'opérateur est exécuté à la place. Donc siX ont reçu la valeur 3, la chaîne"x n'est pas 1 ou
2" est imprimé dans le journal.

Contrairement à unsinon bloc, exécution duchanger le bloc opérateur ne s'arrête pas lorsqu'uncas la
constante correspond. À moins qu'uncasser opérateur est rencontré, l'exécution se poursuivra jusqu'à ce que
toutes les expressions restantes dans lechanger l'opérateur a été exécuté. Voici un exemple :

entier x = 1 ;

switch(x)

{ cas 1 :

cas 2 :

cas 3 :
Print("x vaut 1, 2 ou 3"); // Sortie : x vaut 1, 2 ou 3 break ;

défaut:

40
Acheteur : Evangelos Papageorgiou (vpapnm@yahoo.gr)
Numéro de transaction : 28J511988U193831A

Opérateurs conditionnels et de boucle


Print("x n'est pas 1, 2 ou 3");
casser;
}

Notez qu’il n’y a aucune expression après lecas 1 oucas 2 Étiquettes. Si l'une de ces étiquettes correspond, le
programme commencera à exécuter toutes les expressions suivant les étiquettes de cas jusqu'à ce
qu'unecasser opérateur est rencontré, undéfaut opérateur est rencontré, ou lechanger l'opérateur se termine.

Dans cet exemple, la variableX a une valeur de 1. L’expressionX correspond au premiercas étiquette. L'exécution
se poursuit au-delà ducas 3 étiquette, à laImprimer() fonction. La chaîne "x vaut 1, 2 ou 3" est imprimé
dans le journal et lecasser l'opérateur quitte lechanger bloc. SiX ne correspondait à aucun descas étiquettes,
puis les expressions dans ledéfaut l'opérateur s'exécuterait à la place.

Lechanger L'opérateur est utile dans quelques situations spécifiques. Dans la plupart des cas, unsinon le bloc
fonctionnera tout aussi bien, même si unboîtier de commutation Le bloc peut être plus efficace et plus
compact. Voici un exemple utile deboîtier de commutation bloc. Ce code évaluera la période du graphique
en minutes et renverra une chaîne si la période du graphique correspond à plusieurs périodes de graphique
courantes :

int période = _Période ;


chaîne printPeriod ;
switch(period) { case 60 :
printPeriod = "H1" ;

casser;

cas 240 : printPeriod =


"H4" ; casser;

cas 1440 : printPeriod


= "D1" ; casser;

défaut:
printPeriod = "M" + point ;
}

La variable entièrepériode se voit attribuer la période du graphique actuel, en minutes, en utilisant la valeur
prédéfinie_Période variable. Lechanger l'opérateur comparepériode à plusieurs périodes graphiques
communes, dont H1, H4 et D1. Sipériode correspond à l'une des périodes graphiques courantes, la chaîne
appropriée est affectée à la variable chaînepériode d'impression. Dans le cas où aucune de ces périodes de
graphique ne correspond, une chaîne de périodes de graphique est construite en utilisant le préfixe « M » et la
période en minutes.

Par exemple, sipériode == 240, la variablepériode d'impression se voit attribuer la chaîne "H4". Sipériode
== 15, l'expression qui suit ledéfaut l'opérateur exécutera, etpériode d'impression se voit attribuer la chaîne
"M15". La variablepériode d'impression peut être utilisé pour imprimer une période graphique conviviale sur
le journal ou sur l’écran.

Opérateurs de boucle
41
Acheteur : Evangelos Papageorgiou (vpapnm@yahoo.gr)
Numéro de transaction : 28J511988U193831A

ETXPERT UNCONSEIL P.ROGRAMMATION POUR METTRADER 5


Il est parfois nécessaire qu'un programme répète une action encore et encore. Pour cela, nous utilisons des
opérateurs de boucle. Il existe trois opérateurs de boucle dans MQL :alors que,faire pendant etpour.

L'opérateur while
Lealors que La boucle est la boucle la plus simple de MQL. Lealors que L’opérateur vérifie une condition
booléenne ou relationnelle. Si la condition est vraie, le code entre parenthèses s’exécutera. Tant que la condition
reste vraie, le code entre parenthèses continuera à s’exécuter en boucle. Voici un exemple:

boucle booléenne =
vrai ; nombre entier
= 1 ;

while(boucle == vrai)
{
Imprimer (compter); // Sortie : 1, 2, 3, 4, 5
if(count == 5) boucle = false;
compte++;
}

On commence par déclarer une variable booléenne nomméeboucle à utiliser comme condition de boucle.
Nous déclarons également une variable entière nomméecompter et initialisez-le à 1. Lealors que l'opérateur
vérifie si la variableboucle == vrai. Si tel est le cas, le code entre parenthèses est exécuté. Nous imprimons la
valeur ducompter variable au journal. Ensuite, nous vérifions sicompter == 5. Si c'est le cas, nous
définissonsboucle = faux. Finalement on incrémentecompter par 1, et vérifiez à nouveau l'état de la boucle. Le
résultat de cette boucle est que les nombres 1 à 5 sont imprimés dans le journal.

Quandcompter == 5, leboucle la variable est définie surFAUX, et la condition pour lealors que l'opérateur
n'est plus vrai. Ainsi, la boucle cesse de s'exécuter et le contrôle passe à l'expression qui suit le crochet fermant.
Regardons un deuxième exemple, utilisant une condition relationnelle :

nombre entier = 1 ;

pendant que (compte <= 5)


{
Imprimer (compter); // Sortie : 1, 2, 3, 4, 5 points++ ;
}

Ce code produit le même résultat que la boucle ci-dessus. Dans cet exemple, lecompter La variable est utilisée
comme condition de boucle. Sicompter est inférieur ou égal à 5, la boucle s'exécutera. A chaque exécution de la
boucle,compter sera incrémenté de 1 et le résultat imprimé dans le journal. Une foiscompter est supérieur à 5, la
boucle se terminera.

Lealors que la condition de la boucle est vérifiée au début de la boucle. Si la condition est fausse, la boucle
n'est jamais exécutée. Si vous devez vérifier la condition à la fin de la boucle, ou si vous avez besoin que la
boucle s'exécute au moins une fois, utilisez lefaire pendant boucle.

42
Acheteur : Evangelos Papageorgiou (vpapnm@yahoo.gr)
Numéro de transaction : 28J511988U193831A

Opérateurs conditionnels et de boucle

Les opérateurs à faire pendant


Lefaire pendant les opérateurs vérifient la condition de la boucle à la fin de la boucle, au lieu du début. Cela
signifie que la boucle s'exécutera toujours au moins une fois. Voici un exemple :

nombre entier = 1 ;

fai
re
{
Imprimer (compter); // Sortie : 1, 2, 3, 4, 5
points++ ;
}
pendant que (compte < 5)
Encore une fois, c'est identique au précédentalors que exemple de boucle, sauf dans ce cas, la valeur
decompter sera imprimé dans le journal au moins une fois. Par exemple, sicompter == 1, les numéros 1 à 5
seront imprimés dans le journal. Sicompte == 6, le numéro 6 sera imprimé dans le journal.

Dans les deux casalors que etfaire pendant boucles, la condition pour arrêter l’exécution de la boucle doit
se produire à un moment donné pendant la boucle, ou indépendamment de la boucle (comme un événement
externe). Si la condition pour arrêter la boucle ne se produit pas, vous vous retrouverez avec une boucle infinie
et votre programme se figera. Voici un exemple :

boucle booléenne =
vrai ; nombre
entier ;

fai
re
{
Imprimer (compter);
compte++;
} while(boucle ==
vrai)

Cet exemple bouclera à l'infini car la variableboucle est toujours égal à vrai.

Si vous ne savez pas combien de fois une boucle devra être exécutée, ou si vous devez utiliser une condition
booléenne comme condition de boucle, utilisez unalors que oufaire-alors que boucle. Si vous savez
combien de fois une boucle doit être exécutée, ou si vous avez besoin d'une itération plus avancée, utilisez
unpour boucle à la place.

L'opérateur pour
Si vous utilisez une variable entière pour parcourir une boucle (telle quecompter variable dans les exemples
précédents), lepour la boucle est un meilleur choix. Lepour L'opérateur contient trois expressions séparées par
des points-virgules :

• La première expression est une ou plusieurs variables à initialiser au début de la boucle. Cette variable
est généralement utilisée pour parcourir la boucle.

43
Acheteur : Evangelos Papageorgiou (vpapnm@yahoo.gr)
Numéro de transaction : 28J511988U193831A

ETXPERT UNCONSEIL P.ROGRAMMATION POUR METTRADER 5


• La deuxième expression est la condition de boucle. Il s'agit généralement d'une expression relationnelle.
Lorsque cette expression est vraie, la boucle s'exécute. Quand c'est faux, la boucle se termine.

• La troisième expression est exécutée à la fin de chaque boucle. Il s'agit généralement d'une expression
mathématique pour incrémenter la variable itérateur.

Voici un exemple depour boucle:

pour (int count = 1; count <= 5; count++)


{
Imprimer (compter);
}

La première expression dans lepour opérateur,nombre entier = 1, déclare une variable entière
nomméecompter et l'initialise à 1. La deuxième expression,compte <= 5, est la condition de boucle. Si count est
inférieur ou égal à 5, la boucle s'exécutera. La troisième expression,compte++, est exécuté à la fin de chaque
boucle. Cette expression incrémente lecompter variable par 1. Notez qu'il y a des points-virgules après la
première et la deuxième expressions, mais pas après la troisième expression.

Comme les exemples précédents, ce code imprime les nombres 1 à 5 dans le journal. Si vous comparez le code
ci-dessus aualors que exemple de boucle à la page précédente, lepour la boucle nécessite moins de lignes de
code. À peu près tout ce que vous pouvez faire avec unalors que la boucle peut également être réalisée avec
unpour boucle.

Vous pouvez omettre n'importe laquelle des trois expressions dans lepour boucle, mais les points-virgules qui
les séparent doivent rester. Si vous omettez la deuxième expression, la boucle est considérée comme
constamment vraie et devient ainsi une boucle infinie.

Vous pouvez déclarer plusieurs variables dans la première expression d'unpour boucle, ainsi que calculer
plusieurs expressions dans la troisième expression d'unpour boucle. Les expressions supplémentaires doivent
être séparées par des virgules.
Par exemple:

pour(int a = 1, b = 2; a <= 5; a++, b += 2)


{
Imprimer("a=",a," b=",b); // Sortie : "a=1 b=2", "a=2 b=4", etc...
}

Nous déclarons deux variables entières,un etb, et initialisez-les avec les valeurs de 1 et 2 respectivement. La
boucle s'exécutera lorsqueun est inférieur ou égal à 5. A chaque itération de la boucle,un est incrémenté de 1,
tandis queb est incrémenté de 2. Les valeurs deun etb sont imprimés dans le journal à chaque itération.

L'opérateur de pause
Parfois, vous devez quitter une boucle avant la fin de son itération ou lorsqu'une certaine condition est remplie.
Lecasser l'opérateur quitte immédiatement lealors que,faire pendant oupour boucle. Il sort également
duchanger opérateur, comme expliqué à la page 42.

44
Acheteur : Evangelos Papageorgiou (vpapnm@yahoo.gr)
Numéro de transaction : 28J511988U193831A

Opérateurs conditionnels et de boucle

Généralement, lecasser L’opérateur est utilisé pour quitter une boucle lorsqu’une certaine condition est
remplie. Par exemple:

pour (int count = 1; count <= 5; count++)


{ if(count == 3) pause ;
Imprimer (compter); // Sortie : 1, 2
}
Dans cet exemple, lorsquecompter == 3, lecasser l'opérateur est appelé et lepour la boucle est quittée.

L'opérateur continuer
Lecontinuer l'opérateur fonctionne de la même manière que lecasser opérateur. Au lieu de quitter
complètement la boucle, l'opérateur continue quitte l'itération actuelle de la boucle et passe à l'itération
suivante.

nombre entier = 1 ;

pendant que (compte <= 5)


{ if(count == 3) continuer ;
Imprimer (compter); // Sortie : 1, 2, 4, 5 points++
}

Cet exemple imprimera la valeur decompter au journal pour chaque valeur sauf 3.

45
Acheteur : Evangelos Papageorgiou (vpapnm@yahoo.gr)
Numéro de transaction : 28J511988U193831A

ETXPERT UNCONSEIL P.ROGRAMMATION POUR METTRADER 5

Chapitre 5 - Fonctions

Les fonctions
UNfonction est un bloc de code qui effectue une tâche spécifique, comme passer une commande ou ajuster le
stop loss d'une position. Nous allons créer nos propres fonctions pour mener à bien de nombreuses activités
liées au trading dans ce livre. De plus, MQL5 possède des dizaines de fonctions intégrées qui font tout, de la
récupération des informations de commande à l'exécution d'opérations mathématiques complexes.

Les fonctions sont conçues pour être flexibles et réutilisables. Chaque fois que vous devez effectuer une action
spécifique, telle que passer une commande, vous appelez une fonction pour effectuer cette action. La fonction
contient tout le code et la logique nécessaires pour effectuer la tâche. Tout ce que vous avez à faire est de
transmettre les paramètres requis à la fonction, si nécessaire, et de gérer toutes les valeurs renvoyées par la
fonction.

Lorsque vous passez une commande, par exemple, vous appellerez une fonction de passation de commande.
Vous transmettrez des paramètres à la fonction qui lui demanderont de passer une commande sur le symbole
spécifié, au prix spécifié avec le volume de commande spécifié. Une fois l'exécution de la fonction terminée, elle
renverra une valeur telle qu'une confirmation de commande.

Une déclaration de fonction se compose d'un type de retour, d'un identifiant et d'une liste facultative de
paramètres. Voici un exemple de déclaration de fonction :

double BuyStopLoss (chaîne pSymbol, int pStopPoints, double pOpenPrice)


{
// Corps de la fonction
}

Le nom de notre fonction estAcheterStopLoss(). Cette fonction calculera le stop loss pour un ordre d'achat.
Le type de retour estdouble, ce qui signifie que cette fonction calculera une valeur de typedouble, et renvoyons
cette valeur à notre programme.

Cette fonction possède trois paramètres :pSymbole,pPoints d'arrêt etpPrixOuvert. Dans ce livre, nous
ferons précéder tous les identifiants de paramètres de fonction d'un « p » minuscule. Les paramètres sont
séparés par des virgules. Chaque paramètre doit avoir un type et un identifiant. Un paramètre peut également
avoir une valeur par défaut. Nous discuterons prochainement des valeurs par défaut plus en détail.

Les trois paramètres sont obligatoires, ce qui signifie qu'ils doivent être transmis à la fonction lorsque la fonction
est appelée. Le premier paramètre,pSymbole, est une valeur de chaîne représentant le symbole de l'instrument.
Les deuxièmes paramètres,pPoints d'arrêt, est une valeur entière représentant la valeur du stop loss en
points. Le troisième paramètre,pPrixOuvert, est une valeur double représentant le prix d'ouverture de l'ordre.

46
Acheteur : Evangelos Papageorgiou (vpapnm@yahoo.gr)
Numéro de transaction : 28J511988U193831A

Les fonctions

Voici la fonction dans son intégralité. Cette fonction sera placée quelque part dans la portée globale de notre
programme – ce qui signifie qu'elle ne peut pas être dans une autre fonction. Il peut être placé dans un fichier
include, ou même dans un autre programme inclus dans notre programme :

double BuyStopLoss (chaîne pSymbol, int pStopPoints, double pOpenPrice)


{ double stopLoss = pOpenPrice - (pStopPoints * _Point);
stopLoss = NormalizeDouble(stopLoss,_Digits); retour
(stopLoss);
}

Cette fonction calculera le stop loss en multipliantpPoints d'arrêt par la valeur ponctuelle du symbole,
représentée par la variable prédéfinie_Indiquer (généralement 0,00001 ou 0,001), et en soustrayant cette
valeur depPrixOuvert. Le résultat est affecté à la variablearrêter la perte. La valeur dearrêter la perte
est normalisé au nombre de chiffres du prix en utilisant leNormaliserDouble() fonction, et leretour
L'opérateur renvoie la valeur normalisée dearrêter la perte au programme.

Voici un exemple de la façon dont nous appellerions cette fonction dans notre programme :

// Variables d'entrée
input int StopLoss =
500 ;

// Gestionnaire d'événements OnTick()


double orderPrice = SymbolInfoDouble(_Symbol,SYMBOL_ASK);
double useStopLoss = BuyStopLoss(_Symbol,StopLoss,orderPrice);

La variable d'entréeStopPerte est unsaisir variable qui contient la valeur du stop loss en points. Celui-ci sera
situé au début de notre programme et sera défini par l'utilisateur. Ledouble variablePrix de la commande est
une variable locale qui contiendra le prix d'ouverture de notre ordre – dans ce cas, le prix Ask actuel.

Nous appelons leAcheterStopLoss() fonction et transmettez le symbole du graphique actuel (_Symbole),


leStopPerte variable et lePrix de la commande variable comme paramètres de fonction.
LeAcheterStopLoss() La fonction calcule le stop loss et stocke la valeur de retour dans leutiliserStopLoss
variable. Cette variable serait ensuite utilisée pour modifier la position actuellement ouverte et ajouter un stop
loss.

Les valeurs par défaut


Un paramètre de fonction peut se voir attribuer une valeur par défaut lorsque la fonction est déclarée pour la
première fois. Si un paramètre a une valeur par défaut, il doit être placé à la fin de la liste des paramètres.
Ajoutons une valeur par défaut à l'un des paramètres de notreAcheterStopLoss() fonction:

double BuyStopLoss (string pSymbol, int pStopPoints,double pPrixOuvert = 0)


{ double stopLoss = pOpenPrice - (pStopPoints * _Point);
stopLoss = NormalizeDouble(stopLoss,_Digits); retour
(stopLoss);
}

47
Acheteur : Evangelos Papageorgiou (vpapnm@yahoo.gr)
Numéro de transaction : 28J511988U193831A

ETXPERT UNCONSEIL P.ROGRAMMATION POUR METTRADER 5


LepPrixOuvert Le paramètre reçoit la valeur par défaut de zéro. Tout paramètre ayant une valeur par défaut
doit se trouver à la fin de la liste des paramètres. Si vous utilisez la valeur par défaut lors de l'appel de la
fonction, le paramètre peut être omis :

AcheterStopLoss(_Symbol,StopLoss);

Dans l'exemple ci-dessus, la valeur par défaut de 0 sera utilisée pour lepPrixOuvert paramètre.

Vous pouvez avoir plusieurs paramètres avec des valeurs par défaut, mais ils doivent tous se trouver à la fin de
la liste des paramètres. Si une fonction a plusieurs paramètres avec des valeurs par défaut et que vous
transmettez une valeur à un paramètre avec une valeur par défaut, tous les paramètres précédents doivent
également avoir des valeurs transmises. Voici un exemple :

int MaFonction (chaîne pSymbol, int pDefault1 = 0, int pDefault2 = 0)


{
// Corps de la fonction
}

MaFonction() a deux paramètres avec des valeurs par défaut :pDéfaut1 etpDefault2. Si vous devez
transmettre une valeur autre que celle par défaut àpDefault2, mais pas pourpDéfaut1, alors une valeur doit
être passée àpDéfaut1 aussi:

int nonDefault = 5 ;
MaFonction(_Symbol,0,nonDefault);

Le paramètrepDéfaut1 reçoit la valeur par défaut de 0, tandis quepDefault2 reçoit la valeur 5. Vous ne pouvez
pas ignorer des paramètres lors de l'appel d'une fonction, à moins que tous les paramètres restants utilisent
leurs valeurs par défaut. Bien sûr, si vous n'avez pas besoin de transmettre une valeur àpDéfaut1 etpDefault2,
oupDefault2, vous pouvez alors l'omettre de l'appel de fonction :

point entier = 5 ;
MaFonction(_Symbol,point);

Dans cet exemple,pDéfaut1 reçoit une valeur de 5, maispDefault2 utilise sa valeur par défaut de 0. Si vous
utilisez la valeur par défaut pourpDéfaut1 de plus, le seul paramètre qui doit être spécifié estpSymbole.

En résumé, tout paramètre de fonction avec une valeur par défaut doit être à la fin de la liste des paramètres.
Vous ne pouvez pas ignorer les paramètres lors de l'appel de la fonction, donc si un paramètre avec une valeur
par défaut reçoit une valeur différente lors de l'appel de la fonction, alors tous les paramètres qui le précèdent
doivent également avoir une valeur qui lui est transmise.

L'opérateur de retour
Toute fonction qui renvoie une valeur doit en avoir au moins uneretour opérateur. Leretour L’opérateur
contient la variable ou l’expression à renvoyer au programme appelant. Le type de l'expression doit
correspondre au type de retour de la fonction. Généralement, leretour L'opérateur est la dernière ligne de votre

48
Acheteur : Evangelos Papageorgiou (vpapnm@yahoo.gr)
Numéro de transaction : 28J511988U193831A

Les fonctions

fonction, même si vous pouvez en avoir plusieursretour opérateurs dans votre fonction, en fonction de vos
besoins.

Le type de retour d'une fonction peut être de n'importe quel type, y compris les structures et les énumérations.
Vous ne pouvez pas renvoyer un tableau à partir d'une fonction, bien que vous puissiez renvoyer un élément à
partir d'un tableau. Si vous avez besoin d'une fonction pour renvoyer un tableau, vous pouvez transmettre un
tableau à une fonction par référence. Nous discuterons prochainement du passage par référence.

Voici notreAcheterStopLoss() fonctionner à nouveau. Cette fonction renvoie une valeur de typedouble. La
valeur dudouble variablearrêter la perte, qui est local à la fonction, est renvoyé au programme appelant à
l'aide duretour opérateur:

double BuyStopLoss (chaîne pSymbol, int pStopPoints, double pOpenPrice = 0)


{ double stopLoss = pOpenPrice - (pStopPoints * _Point);
stopLoss = NormalizeDouble(stopLoss,_Digits);
retour (stopLoss);
}

Le type vide
Toutes les fonctions n'ont pas besoin de renvoyer une valeur. Il existe un type spécial appelévide, qui spécifie
une fonction qui ne renvoie pas de valeur. UNvide La fonction peut accepter des paramètres, mais n'a pas
besoin d'avoir unretour opérateur. Voici un exemple de fonction devide tapez sans paramètres :

annuler TradeEmail()
{ string subject = "Commerce
placé" ;
string text = "Une transaction a été effectuée sur " + _Symbol ;
SendMail(sujet,texte);
}

Cette fonction enverra un e-mail en utilisant les paramètres de messagerie spécifiés dans leOutils Menu
>Paramètres >E-maillanguette.
Notez qu'il n'y a pasretour opérateur car la fonction est devide taper.

Passage de paramètres par référence


Normalement, lorsque vous transmettez des paramètres à une fonction, les paramètres sont transmis parvaleur.
Cela signifie que la valeur du paramètre est transmise à la fonction et que la variable d'origine reste inchangée.

Vous pouvez également transmettre des paramètres parréférence. Toute modification apportée à une variable à
l'intérieur d'une fonction sera reflétée dans la variable d'origine. Ceci est utile lorsque vous avez besoin d'une
fonction pour modifier un tableau ou une structure. Vous passerez fréquemment des tableaux et des structures
par référence dans les fonctions MQL5.

Voici un exemple de passage d'une structure par référence en utilisant leSymboleInfoTick() fonction. Voici la
définition duSymboleInfoTick() fonction à partir duRéférence MQL5:

49
Acheteur : Evangelos Papageorgiou (vpapnm@yahoo.gr)
Numéro de transaction : 28J511988U193831A

ETXPERT UNCONSEIL P.ROGRAMMATION POUR METTRADER 5


bool SymbolInfoTick(
symbole de chaîne, // nom du symbole
MqlTick& tick // référence à une structure
);

L'esperluette (&) avant lecocher paramètre signifie que ce paramètre est passé par référence. Voici comment
nous utiliserions leSymboleInfoTick() fonction dans le code :

MqlTick maTick ;
SymbolInfoTick(_Symbol,myTick);
Imprimer(myTick.bid);

La première ligne déclare un objet nommémaTique, en utilisant leMqlTick structure que le type. LeMqlTick La
structure stocke les informations de prix actuelles du serveur. LeSymboleInfoTick() la fonction remplit
unMqlTick objet avec les informations de prix actuelles du symbole spécifié. Le deuxième paramètre dans le
SymboleInfoTick() appel de fonction,maTique, est passé par référence. LemaTique La structure est modifiée
par la fonction et remplie avec les informations de prix actuelles du serveur commercial. LeImprimer() La
fonction imprime le prix acheteur actuel dans le journal en faisant référence auoffre variable de lamaTique
objet.

Voici un autre exemple de passage par référence à l'aide d'un tableau. Dans cet exemple, nous passerons un
tableau dynamique à une fonction par référence. On précise qu'un paramètre est passé par référence en
préfixant l'identifiant d'une esperluette (&) :

void FillArray(int &array[])


{
ArrayResize(tableau,
3); tableau[0] = 1;
tableau[1] = 2;
tableau[2] = 3;
}
Le seul paramètre duremplirArray() fonction,&tableau[], est passé par référence. Le tableau dynamique
transmis au&tableau[] Le paramètre sera modifié par la fonction. Voici comment nous appellerions cette
fonction depuis notre programme :

int remplir[];
FillArray(remplir);
Imprimer(fill[0]); // Sortie : 1

Le tableauremplir[] contient désormais les valeurs qui ont été modifiées à l'intérieur duremplirArray()
fonction.

Fonctions de surcharge
Parfois, vous devrez peut-être créer plusieurs fonctions qui effectuent essentiellement la même tâche. Chacune
de ces fonctions aura des paramètres d'entrée différents, mais le résultat final est le même. Auparavant dans

50
Acheteur : Evangelos Papageorgiou (vpapnm@yahoo.gr)
Numéro de transaction : 28J511988U193831A

Les fonctions

MQL4, il aurait fallu donner à ces fonctions des identifiants différents. MQL5 présentesurcharge de fonctions, ce
qui permet d'avoir plusieurs fonctions portant le même nom.

Chaque fonction portant un nom identique doit avoir des paramètres différents, soit en nombre, soit en type.
Montrons-le en utilisant deux fonctions trailing stop que nous créerons plus tard dans ce livre. Les deux
fonctions portent le même nom et font fondamentalement la même chose. La différence est que la première
fonction a un paramètre entier nommépTrailPoints, et le second a un double paramètre nommépPrixTrail:

bool TrailingStop(chaîne pSymbol,int pTrailPoints, int pMinProfit = 0, int pStep = 10);


bool TrailingStop(chaîne pSymbol,double pTrailPrix, int pMinProfit = 0, int pStep = 10);

Leint paramètre dans la première fonction,pTrailPoints, accepte une valeur de stop suiveur en points. Ceci
est utilisé pour calculer un prix stop suiveur, par rapport au prix acheteur ou vendeur actuel. Ledouble
paramètre dans la deuxième fonction,pPrixTrail, accepte un prix à utiliser comme prix stop suiveur.

En ayant deux fonctions portant le même nom et des paramètres différents, nous disposons d'une certaine
flexibilité quant à la manière d'administrer le trailing stop, en fonction du système de trading. Puisque les deux
fonctions partagent le même nom, le programmeur n’a pas besoin de mémoriser deux (ou plus) noms de
fonctions différents. En fin de compte, cela facilite simplement la vie du programmeur.

Le compilateur saura quelle fonction utiliser en fonction de sa signature de paramètre unique. La première
fonction a un paramètre chaîne, suivi de trois paramètres entiers. Le second a un paramètre chaîne, suivi d’un
paramètre double et de deux paramètres entiers. Voici comment nous appellerions la première variante de la
fonction :

// Variables d'entrée
entrée int TrailingPoints = 500 ;
// Gestionnaire d'événements OnTick()
TrailingStop(_Symbol,TrailingPoints);

Unint variable d'entrée nomméePoints de fin permet à l'utilisateur de définir un stop suiveur en points.
Cette valeur est utilisée comme deuxième paramètre dans leArrêtTrail() appel de fonction. Parce que
lePoints de fin la variable est de typeint, le compilateur sait utiliser la première variante de la fonction.
Puisque nous utilisons les valeurs par défaut pour lepMinProfit etpÉtape paramètres, nous les avons omis de
l’appel de fonction.

Et voici comment nous appellerions la deuxième variante de la fonction :

// Variables d'entrée
entrée int TrailingPoints = 500 ;

// Gestionnaire d'événements OnTick()


double trailingPrice = SymbolInfoDouble(_Symbol,SYMBOL_ASK) – (TrailingPoints * _Point);
TrailingStop(_Symbol,trailingPrice);

51
Acheteur : Evangelos Papageorgiou (vpapnm@yahoo.gr)
Numéro de transaction : 28J511988U193831A

ETXPERT UNCONSEIL P.ROGRAMMATION POUR METTRADER 5


Le localdouble variablePrix de fuite contiendra un prix à utiliser comme stop suiveur. Puisque le deuxième
paramètre duArrêtTrail() l'appel de fonction est de typedouble, le compilateur sait utiliser la deuxième
variante de la fonction.

52
Acheteur : Evangelos Papageorgiou (vpapnm@yahoo.gr) ID de transaction : 28J511988U193831A
Acheteur : Evangelos Papageorgiou (vpapnm@yahoo.gr)
Numéro de transaction : 28J511988U193831A

ETXPERT UNCONSEIL P.ROGRAMMATION POUR METTRADER 5

Chapitre 6 - Programmation orientée objet

L'une des nouvelles fonctionnalités les plus intéressantes de MQL5 est l'ajout de la programmation orientée
objet.Programmation orientée objet (POO en abrégé) encourage la réutilisation du code et cache les détails
d'implémentation inutiles à l'utilisateur. Cela permet un style de programmation beaucoup plus flexible et
compact.

Les concepts de programmation orientée objet sont de nature abstraite et souvent confondus par le jargon
technique. Ils peuvent être difficiles à comprendre pour le nouveau programmeur. Mais une fois que vous aurez
appris les principes fondamentaux de la POO, vous les trouverez incroyablement utiles.

La programmation orientée objet s'appuie sur les concepts de classes et d'objets. UNclasse est un ensemble de
variables et de fonctions qui exécutent un ensemble de tâches liées. Les variables et fonctions contenues dans
une classe sont appeléesmembres d'une classe.

Une classe est comme un modèle pour un objet. Prenez une voiture, par exemple. Une voiture a un volant, un
levier de vitesses, un clignotant, des phares, etc. Une classe pour un objet voiture contiendrait toutes les
variables décrivant l'état de la voiture (vitesse, rapport, si les phares sont allumés et éteints) et toutes les
fonctions pour effectuer une tâche spécifique (accélérer ou décélérer, changer de vitesse, tourner le volant).
phares allumés et éteints, etc.)

Unobjet est créé en utilisant la classe comme modèle. La classe décrit la voiture, tandis que l'objet est la voiture
elle-même. Chaque objet a un nom unique, de la même manière que chaque voiture a un numéro
d'identification de véhicule unique. Vous pouvez créer autant d’objets que nécessaire, tout comme un
constructeur peut construire plusieurs voitures différentes d’un même modèle. Les variables d’un objet sont
distinctes des variables d’autres objets, tout comme la façon dont les différentes voitures roulent à des vitesses
différentes sur l’autoroute.

Par exemple, chez un expert-conseil, vous pouvez avoir plusieurs indicateurs. Pour un croisement de moyenne
mobile, vous aurez au moins deux indicateurs de moyenne mobile. Chaque moyenne mobile aura un paramètre
de période différent et peut avoir des modes de calcul et des paramètres de prix différents.

Un indicateur de moyenne mobile peut être représenté par une classe. La classe d'indicateur de moyenne
mobile contient toutes les variables et fonctions nécessaires pour créer l'indicateur et récupérer la valeur
actuelle de l'indicateur pendant l'exécution du programme. À l'aide de cette classe, nous créons un ou plusieurs
objets, chacun ayant son propre identifiant, ses paramètres et ses valeurs d'indicateur.

Nous n'avons pas à nous soucier de créer un tableau de séries dynamiques pour contenir les valeurs de
l'indicateur, ni à initialiser l'indicateur, à stocker le handle de l'indicateur et à copier les valeurs de l'indicateur des
tampons vers le tableau. Tous ces détails sont gérés dans l’implémentation de la classe. Tout ce que nous avons
à faire est de créer un objet et d'utiliser les fonctions de classe pour effectuer ces tâches.

54
Acheteur : Evangelos Papageorgiou (vpapnm@yahoo.gr)
Numéro de transaction : 28J511988U193831A

Programmation orientée objet


Des classes
Les classes sont déclarées sur la portée globale, tout comme les fonctions. Une classe peut être placée dans
votre programme ou dans un fichier d'inclusion. Une déclaration de classe utilise leclasse mot-clé, suivi d’un
identifiant unique. Les membres de la classe sont placés entre parenthèses, triés paraccéder aux mots-clés. Le
crochet fermant d'une déclaration de classe se termine par un point-virgule.

Voici un exemple de déclaration de classe pour un objet indicateur :

classe
CIndicator
{ protégé :
poignée int ;
double
principal[];

publique:
double Principal(int pShift=0);
Annuler la version ();
CIndicateur();
} ;

Le nom de la classe estCIndicateur. Il en a troispublique membres et deuxprotégé membres. Notez que


chaque déclaration de fonction et de variable à l'intérieur de la déclaration de classe se termine par un point-
virgule. Le crochet fermant de la déclaration de classe elle-même se termine également par un point-virgule.
Les membres publics duCIndicateur la classe comprend lePrincipal() fonction, leLibérer() fonction et un
constructeur par défaut avec le même nom que notre classe. Les membres protégés comprennent lespoignée
variable et leprincipal[] tableau.

Nous discuterons deCIndicateur cours plus en détail au chapitre 17, alors ne vous inquiétez pas si vous ne
comprenez pas encore comment cela fonctionne. Dans ce chapitre, nous utiliserons leCIndicateur classe
comme exemple pour expliquer les concepts de programmation orientée objet.

Modificateurs d'accès
Les étiquettespublique,privé etprotégé sontaccéder aux mots-clés. Ils déterminent si une variable ou une
fonction peut être utilisée en dehors d'une classe. Voici les descriptions des mots-clés d’accès :

• Publique les membres d'une classe sont disponibles pour une utilisation en dehors de la classe. C'est la
méthode par laquelle le programme interagit avec un objet. Les membres publics sont généralement
des fonctions qui accomplissent des tâches importantes. Les fonctions publiques peuvent accéder et
modifier les membres privés et protégés d'une classe.

• Privé les membres d'une classe ne peuvent être utilisés que par les fonctions à l'intérieur de la classe.
Un membre privé n’est pas accessible en dehors de la classe. Les classes dérivées de cette classe
n’hériteront pas de ces membres. (Nous discuterons de l'héritage sous peu.) Les membres privés sont

55
Acheteur : Evangelos Papageorgiou (vpapnm@yahoo.gr)
Numéro de transaction : 28J511988U193831A

ETXPERT UNCONSEIL P.ROGRAMMATION POUR METTRADER 5


généralement des fonctions et des variables internes auxquelles accèdent les membres publics d'une
classe.

• Protégé les membres d'une classe sont essentiellement des membres privés qui seront hérités par une
classe dérivée. Utilisez leprotégé mot-clé sauf si vous êtes certain que vous ne dériverez aucune classe
de la classe actuelle.

Ce concept consistant à cacher les membres de la classe du reste du programme est un concept de POO
appeléencapsulation. En masquant les membres de la classe, nous garantissons qu'ils ne seront pas utilisés ou
modifiés inutilement.

LeCIndicateur La classe est destinée à être utilisée comme classe parent pour d’autres classes d’indicateurs,
telles qu’une classe d’indicateurs de moyenne mobile. Nous avons créé cette classe pour implémenter des
fonctionnalités que chaque indicateur utilisera. Par exemple, chaque indicateur nécessite une variable pour
stocker le handle de l'indicateur, nous avons donc créé une variable entière nomméepoignée. Nous aurons
également besoin d'au moins un tableau dynamique pour contenir les valeurs des indicateurs. Le premier (et
parfois le seul) tampon d’un indicateur est appelé letampon principal, nous avons donc déclaré un double
tableau nomméprincipal[]. Ces deux variables sont protégées, ce qui signifie qu'elles ne sont pas accessibles
en dehors de notre classe et ne sont accessibles qu'aux membres publics de la classe.

Pour les membres publics de notre classe, nous avons créé une fonction nomméePrincipal() pour mettre à
jour les valeurs des indicateurs et accéder aux valeurs dans leprincipal[] tableau, et unLibérer() fonction
pour libérer l’indicateur de la mémoire. Examinons de plus près les fonctions publiques de notre classe. Nous
commencerons par lePrincipal() fonction. Cette fonction copie les données de l'indicateur dans
leprincipal[] tableau en utilisant lepoignée variable pour identifier l’indicateur et renvoie la valeur de la barre
spécifiée. Voici la déclaration de fonction pour lePrincipal() fonction:

double CIndicator :: Main (int pShift = 0)


{
CopyBuffer(handle,0,0,MAX_COUNT,main);
valeur double = NormalizeDouble(main[pShift],_Digits); retour
(valeur);
}

Cette déclaration de fonction est placée sur la portée globale, après leCIndicateur déclaration de classe. Noter
laCIndicateur : juste avant l'identifiant de la fonction. L'opérateur double-deux-points (::) est lerésolution
de la portée opérateur. Il identifie une fonction comme appartenant à un périmètre spécifique – dans ce cas, le
périmètre est leCIndicateur classe.

Notez également que notre protégépoignée etprincipal[] les variables sont utilisées comme paramètres
pour leCopieBuffer() fonction. La seule façon dont nous pouvons accéder aux membres protégés et privés de
notre classe est via une fonction de classe publique. Si vous tentez d'accéder à ces membres depuis un autre
endroit de votre programme, vous obtiendrez une erreur de compilation.

56
Acheteur : Evangelos Papageorgiou (vpapnm@yahoo.gr)
Numéro de transaction : 28J511988U193831A

Programmation orientée objet


La méthode préférée pour déclarer les fonctions de classe est la méthode utilisée ci-dessus. Mais vous pouvez
également déclarer une fonction en ligne, dans la déclaration de classe elle-même. Ceci est utile pour les
fonctions très courtes composées d’une ou deux lignes. Voici un exemple utilisant notreLibérer() fonction:

class CIndicator { public: void Release()


{ IndicatorRelease (handle); }

//...
} ;

LeLibérer() La fonction est déclarée sur une seule ligne dans notre déclaration de classe. Il s'agit d'un appel
auIndicateurRelease() fonction. Le contenu de la fonction est contenu entre parenthèses. Il n'est pas
obligatoire d'avoir un point-virgule après le crochet fermant, bien que le compilateur ne se plaindra pas si vous
le faites.

Constructeurs
Lorsqu'un objet est créé à partir d'une classe, une fonction appeléeconstructeur est exécuté automatiquement.
Le constructeur est utilisé pour initialiser les variables à l'intérieur de notre objet. Si aucun constructeur n'est
explicitement défini, alors le compilateur crée un constructeur par défaut pour initialiser les variables. Ce
constructeur par défaut n'est pas visible pour le programmeur.

Dans notreCIndicateur fonction, nous avons déclaré notre propre constructeur par défaut, également
appeléCIndicateur(). Le nom d'un constructeur doit correspondre à celui de l'identifiant de classe. Il n'est pas
nécessaire de spécifier un type de retour pour un constructeur par défaut, car le type est toujoursvide. Le
niveau d'accès d'un constructeur doit êtrepublique.

Voici laCIndicateur() déclaration du constructeur à l'intérieur duCIndicateur déclaration de classe :

classe
CIndicator
{ public :
CIndicateur();

//...
} ;

Et voici notre constructeur de classe explicitement défini. Le seul but de notre constructeur est de définir
notreprincipal[] tableau en tant que tableau en série. Nous parlerons davantage des tableaux en série plus
loin dans le livre :

CIndicator :: CIndicator (vide)


{
ArraySetAsSeries(main,true);
}

57
Acheteur : Evangelos Papageorgiou (vpapnm@yahoo.gr)
Numéro de transaction : 28J511988U193831A

ETXPERT UNCONSEIL P.ROGRAMMATION POUR METTRADER 5


N'oubliez pas que vous n'avez pas besoin de déclarer explicitement un constructeur par défaut si vous n'en avez
pas besoin. Si vous souhaitez effectuer des actions automatiquement lors de la création d'un objet, créez un
constructeur par défaut pour ce faire.

Il existe des choses plus avancées que vous pouvez faire avec les constructeurs, telles que les constructeurs
paramétriques et les listes d'initialisation. Il existe également le destructeur, qui fait appel à la destruction d'un
objet. Puisque nous n’utiliserons pas ces fonctionnalités dans ce livre, ce sera au lecteur d’en apprendre
davantage. Vous pouvez en apprendre davantage sur les constructeurs et les destructeurs dans leRéférence
MQL5 sousBases du langage > Programmation orientée objet.

Classes dérivées
L'une des fonctionnalités les plus utiles de la POO est le concept dehéritage. En POO, vous pouvez créer une
classe en utilisant une autre classe comme modèle. La nouvelle classe hérite de toutes les fonctions et variables
de la classe parent (sauf celles qui utilisent leprivé mot-clé d'accès). Vous pouvez ensuite étendre cette classe
en ajoutant de nouvelles fonctions et variables.

C'est exactement ce que nous ferons avec notreCIndicateur classe. Rappelez-vous que leCIndicateur class
est censé être une classe parent pour d’autres classes d’indicateurs. Les spécificités de la mise en œuvre d'un
indicateur particulier sont gérées dans la classe dérivée, tandis que les variables et fonctions de base sont déjà
définies dans la classe parent.

Voici un exemple de classe dérivée pour un indicateur de moyenne mobile. Cette classe contient une
fonction,chaleur(), qui initialise l'indicateur de moyenne mobile :

class CiMA : public CIndicator


{ public :
int Init (string pSymbol, ENUM_TIMEFRAMES pTimeframe, int pMAPeriod, int
pMAShift, ENUM_MA_METHOD pMAMethod, ENUM_APPLIED_PRICE pMAPrice);
} ;

Le nom de notre classe dérivée estCiMA. Notez les deux points (:), suivi deIndicateur public dans la
déclaration de classe. Ceci précise que leCiMA la classe est dérivée duCIndicateur classe. L'ensemble du
public et des membres protégés de laCIndicateur la classe fait désormais partie de laCiMA classe.

Lepublique Le mot clé spécifie que tous les membres publics et protégés de la classe de base resteront publics
ou protégés dans toutes les classes dérivées de celle-ci. Vous pouvez également spécifier leprotégé ouprivé
mots-clés, qui modifient le niveau d'accès de tous les membres publics et protégés de la classe parent en
protégé ou privé dans toutes les classes dérivées. Cependant, vous n’aurez pas besoin de le faire très souvent,
voire pas du tout.

Voici la déclaration de fonction pour notrechaleur() fonction. Cette fonction transmet les paramètres de
l'indicateur de moyenne mobile auavoir() fonction, qui renvoie une poignée d’indicateur. Notez l'utilisation
dupoignée variable de laCIndicateur classe:

int CiMA::Init (string pSymbol, ENUM_TIMEFRAMES pTimeframe, int pMAPeriod,


int pMAShift, ENUM_MA_METHOD pMAMethod, ENUM_APPLIED_PRICE pMAPrice)

58
Acheteur : Evangelos Papageorgiou (vpapnm@yahoo.gr)
Numéro de transaction : 28J511988U193831A

Programmation orientée objet


{ handle = iMA(pSymbol,pTimeframe,pMAPeriod,pMAShift,pMAMethod,pMAPrice);
retour (poignée);
}

Fonctions virtuelles
Parfois, vous devrez modifier la façon dont une fonction fonctionne dans une classe dérivée. Ou vous
souhaiterez peut-être définir une fonction dans une classe parent, mais prendre soin des détails
d'implémentation dans la classe dérivée. Vous pouvez y parvenir en utilisantfonctions virtuelles.

Reprenons l'exemple d'une voiture : une classe de voiture aurait une fonction pour changer de vitesse.
Cependant, le processus de changement de vitesse dans une voiture à transmission manuelle est différent de
celui de changement de vitesse avec une transmission automatique. Par conséquent, nous déclarons une
fonction de changement de vitesse virtuelle dans notre classe parent, puis écrivons la fonction réelle dans nos
classes dérivées.

Voici à quoi ressemblerait une classe de voiture dans le code :

classe
Voiture
{ public :
virtuel int ShiftGears (int gear) { return (engrenage); }
} ;

Le nom de notre classe estVoiture. Nous avons déclaré une seule fonction – une fonction virtuelle
nomméeChanger de vitesse(). Nous avons ajouté un corps de fonction vide auChanger de vitesse()
déclaration, ne contenant qu'un seulretour opérateur. LeChanger de vitesse() la fonction est déclarée avec
levirtuel mot-clé. Cela signifie que la fonction sera définie dans les classes dérivées.

Créons une classe dérivée pour une voiture à transmission manuelle :

classe ManualCar : voiture


publique { public :
virtuel int ShiftGears (int gear);
} ;
Cette classe est nomméeManuelVoiture, et est destiné à un véhicule à transmission manuelle. C'est ici que
nous définissons le
Changer de vitesse() fonction. La fonction est déclarée avec le même type et les mêmes paramètres que la
fonction dans leVoiture classe. Levirtuel Le mot-clé est facultatif et peut être omis si vous ne prévoyez pas
de dériver de nouvelles classes deManuelVoiture. Le corps de la fonction est défini ailleurs et contient la
logique de changement de vitesse à l'aide d'une transmission manuelle. D'autres classes dérivées duVoiture la
classe définiraitChanger de vitesse() d'une manière similaire.

59
Acheteur : Evangelos Papageorgiou (vpapnm@yahoo.gr)
Numéro de transaction : 28J511988U193831A

ETXPERT UNCONSEIL P.ROGRAMMATION POUR METTRADER 5


Si une classe dérivée a une fonction avec le même nom et les mêmes paramètres qu'une fonction de la classe
parent, la fonction de la classe dérivée remplacera la fonction de la classe parent. Ce processus de redéfinition
des fonctions dans les classes dérivées est un concept POO connu sous le nom depolymorphisme.

Lechaleur() fonctionner dans notreCiMA la classe peut être déclarée comme une fonction virtuelle dans
notreCIndicateur classe. Cela garantit que toute classe dérivée deCIndicateur doit mettre en œuvre
lechaleur() classe:

class CIndicator {
protégé : int
handle ;

publique:
virtual int Init() { return(poignée); }
} ;

Lechaleur() la fonction est déclarée comme membre public de la classe. Levirtuel Le mot clé spécifie que
l'implémentation de la fonction sera effectuée dans toutes les classes dérivées. Le corps de la fonction est
déclaré sur la même ligne. Dans cet exemple, la fonction renvoie simplement la valeur dupoignée variable.
Lorsque nous créons une classe dérivée basée surCIndicateur, lechaleur() La fonction doit être
implémentée dans la nouvelle classe.

Objets
Maintenant que nous avons créé une classe pour un indicateur de moyenne mobile, créons un objet. Vous créez
un objet de la même manière que vous créez une variable, une énumération ou une structure : le nom de la
classe est utilisé comme type et l'objet reçoit un identifiant unique :

Volume CiMA ;

Cela crée un objet nomméobjMa, basé sur la classeCiMA. Lorsqu'un objet est créé, le constructeur de cet objet
est exécuté automatiquement. Depuis leCiMA la classe est dérivée duCIndicateur classe, le constructeur de
laCIndicateur la classe est mise à la disposition desCiMA classe, et s'exécutera automatiquement lors de la
création d'un objet basé sur laCiMA classe. Ainsi, leCIndicateur() le constructeur est appelé, et
leprincipal[] array est défini comme un tableau série lors de la création de l'objet :

CIndicator :: CIndicator (vide)


{
ArraySetAsSeries(main,true);
}

Une fois l'objet déclaré, nous pouvons accéder à tous les membres publics en utilisant l'opérateur point (.). La
première chose que nous devrons faire est d'initialiser notre indicateur avec lechaleur() fonction:

objMa.Init(_Symbol,0,MAPeriod,0,MAMethod,MAPrice);

60
Acheteur : Evangelos Papageorgiou (vpapnm@yahoo.gr)
Numéro de transaction : 28J511988U193831A

Programmation orientée objet


Rappelez-vous que lechaleur() la fonction a été déclarée comme fonction virtuelle dans leCIndicateur
classe, et défini dans laCiMA classe. Lechaleur() La fonction crée une poignée d’indicateur pour l’indicateur de
moyenne mobile en utilisant les paramètres spécifiés. La poignée de l'indicateur est stockée dans la variable
protégéepoignée, qui est défini dans leCIndicateur classe.

Ensuite, nous utiliserons lePrincipal() fonction pour remplir leprincipal[] tableau avec les valeurs des
indicateurs et récupérez la valeur de l’indicateur pour une barre spécifique. Depuis leprincipal[] array est un
membre de classe protégé, nous ne pouvons y accéder que via une fonction publique telle quePrincipal()
fonction. Cette ligne de code imprime la valeur moyenne mobile de la barre actuelle dans le journal :

Print(objMa.Main());

Vous pouvez créer autant d'objets que nécessaire pour votre programme. Si vous avez besoin d'un deuxième
indicateur de moyenne mobile, déclarez-le en utilisant un identifiant unique différent et accédez aux membres
publics de l'objet comme indiqué ci-dessus. En créant des classes pour effectuer des tâches courantes, vous
gagnez du temps et réduisez les erreurs, ainsi que la quantité de code dans votre programme. Le reste du livre
sur les conseillers experts se concentrera sur la création de classes pour effectuer des tâches de trading
courantes.

61
Acheteur : Evangelos Papageorgiou (vpapnm@yahoo.gr)
Numéro de transaction : 28J511988U193831A

ETXPERT UNCONSEIL P.ROGRAMMATION POUR METTRADER 5

Chapitre 7 - La structure d'un programme MQL5

Avant de commencer à développer des programmes MQL5, prenons une minute pour aborder la structure d'un
programme MQL5. Tous les programmes MQL5 partagent la même structure de base. En haut du fichier se
trouveront les directives du préprocesseur. Viennent ensuite les variables d'entrée et globales. Enfin, les
fonctions, classes et gestionnaires d'événements du programme sont définis.

Directives du préprocesseur
LeDirectives du préprocesseur sont utilisés pour définir les propriétés du programme, définir des constantes,
inclure des fichiers et importer des fonctions. Les directives du préprocesseur sont généralement déclarées tout
en haut du fichier programme. Commençons par le#propriété directive de préprocesseur, que vous verrez
dans presque tous les programmes MQL5.

Directive #propriété
Le#propriété La directive définit les propriétés du programme, telles que les informations descriptives, les
indicateurs, les scripts et les propriétés de la bibliothèque. Nous discuterons des propriétés des indicateurs, des
scripts et des bibliothèques dans les chapitres appropriés.

Lorsque vous créez un programme à l'aide deAssistant MQL5, leauteur,lien etversion les propriétés seront
insérées automatiquement. Vous pouvez également ajouter ledescription propriété manuellement. Ceux-ci
seront affichés sur leCommun onglet dans le conseiller expertPropriétés dialogue. Ceci est utile si vous décidez
de distribuer votre programme.

Le#propriété les directives seront placées tout en haut de votre programme. Ils doivent être définis dans votre
fichier programme principal, car toutes les directives de propriété dans les fichiers d'inclusion seront ignorées.
Voici un exemple du descriptif#propriété directives :

#property copyright "Andrew R. Young"


#lien de propriété "http://www.expertadvisorbook.com"
#version de la propriété "1.02"
#description de la propriété "Double croisement de moyenne mobile avec stop suiveur"

Et voici comment ces#propriété les directives ci-dessus s'afficheront dans leCommun onglet du conseiller
expertPropriétés fenêtre:

62
Acheteur : Evangelos Papageorgiou (vpapnm@yahoo.gr)
Numéro de transaction : 28J511988U193831A

La structure d'un programme MQL5

Figure 7.1- LeCommun onglet du conseiller expertPropriétés fenêtre, affichant le#propriété directives
définies dans le fichier de code source.

Ledroits d'auteur la propriété ci-dessus (« Andrew R. Young ») sert également de lien hypertexte. En plaçant
la souris dessus et en cliquant, l'utilisateur sera dirigé vers le site Web défini dans lelien propriété.

#définir la directive
Le#définir La directive est utilisée pour définir des constantes à utiliser tout au long du programme. Nous
avons abordé les constantes plus tôt à la page 17. Pour résumer, le#définir La directive spécifie un identifiant
avec une valeur constante. La convention est d'utiliser toutes les lettres majuscules pour le nom de l'identifiant.
Voici quelques exemples de constantes utilisant le#définir directif:

#définir PI 3.14159265
#définir MAX_BARS 100
#define COMPANY_NAME "Easy Expert Forex"

Pour utiliser une constante, vous remplacez le nom de l'identifiant par la valeur de la constante. Si vous vouliez
utiliser la valeur de Pi dans votre programme, l'identifiant PI serait interprété comme 3.14159265 par le
compilateur :

double diamètre = 5 ;
double circonférence = PI * diamètre ;
Imprimer (circonférence); // Sortie : 15.70796325
L'exemple ci-dessus calcule la circonférence d'un cercle en multipliant la valeur de Pi par le diamètre d'un
cercle.

Il existe une deuxième variante du#définir directive appelée forme paramétrique. La forme paramétrique
du#définir La directive accepte les paramètres, tout comme le ferait une fonction. Vous pouvez avoir jusqu'à

63
Acheteur : Evangelos Papageorgiou (vpapnm@yahoo.gr)
Numéro de transaction : 28J511988U193831A

ETXPERT UNCONSEIL P.ROGRAMMATION POUR METTRADER 5


huit paramètres dans un paramètre#définir directif. Le résultat est une expression qui est calculée et
substituée dans le programme :

#définir PI 3.14159265
#define CIRC(jour) PI * jour

La directive de définitionCIRC(jour) prend un seul argument,est, qui est le diamètre d'un cercle. La valeur
deest sera multiplié par la constantePI, et le résultat est renvoyé au programme.

double diamètre = 5 ;
double circonférence = CIRC(diamètre);
Imprimer (circonférence); // Sortie : 15.70796325

L'exemple ci-dessus calcule la circonférence d'un cercle en utilisant laCIRC(jour) constante paramétrique. La
valeur dudiamètre la variable est transmise auCIRC(jour) constante comme paramètre. Leest Le paramètre
est multiplié par lePI constante, et le résultat est placé dans lecirconférence variable.

Si vous disposez d’une expression mathématique simple fréquemment utilisée, alors la forme paramétrique de
la#définirLa directive peut être un bon substitut à l’écriture d’une fonction dédiée. Cependant, une fonction
permettrait des manipulations plus avancées, comme la normalisation d'un résultat sur un nombre spécifié de
chiffres.

#inclure la directive
Le#inclure La directive spécifie un fichier d'inclusion à inclure dans le programme. Un fichier d'inclusion
contient des variables, des fonctions et des classes à utiliser dans le programme principal. Il existe deux
variantes du#inclure directif:

#include <Trade.mqh>
#include "Trade.mqh"

La première variante du#inclure La directive entoure le nom du fichier d’inclusion entre crochets angulaires
(<>). Cela indique que le compilateur recherchera le fichier include dans le répertoire include par défaut, qui est
le\MQL5\Inclure sous-dossier de votre dossier d'installation MetaTrader 5. Il s’agit de la méthode privilégiée
pour inclure des fichiers, et celle que nous utiliserons dans ce livre.

La deuxième variante du#inclure La directive inclut le nom du fichier d'inclusion entre guillemets doubles (").
Cela indique au compilateur de rechercher le fichier d'inclusion dans le même répertoire que le fichier du
programme. Si, pour une raison quelconque, vous avez stocké le fichier d'inclusion dans le même répertoire que
votre programme , puis utilisez des guillemets doubles dans votre#inclure directif.

Il existe une directive de préprocesseur supplémentaire, la#importer directive, qui est utilisée pour importer
des fonctions à partir de bibliothèques et de DLL. Nous aborderons l'utilisation du#importer directive au
chapitre 21.

Variables d'entrée et globales

64
Acheteur : Evangelos Papageorgiou (vpapnm@yahoo.gr)
Numéro de transaction : 28J511988U193831A

La structure d'un programme MQL5

Après les directives du préprocesseur, la section suivante de votre programme MQL5 sera les déclarations
d'entrée et de variables globales. Ce qui vient en premier n'a pas nécessairement d'importance, mais la
convention est de placer les déclarations de variables d'entrée en premier. Comme vous vous en souviendrez,
les variables d'entrée sont les paramètres réglables par l'utilisateur pour votre programme.

Toutes les variables globales que vous utilisez doivent être déclarées en dehors de toute fonction ou
gestionnaire d'événements. La convention est de les placer en haut du fichier, après les variables d'entrée. Cela
garantit qu'ils ne seront appelés par aucune fonction avant d'avoir été déclarés.

Classes et fonctions
Des classes ou fonctions personnalisées peuvent être définies n'importe où dans votre programme, en
particulier dans les fichiers d'inclusion inclus à l'aide de l'option#inclure directif. Généralement, toutes les
classes ou fonctions présentes dans le fichier programme principal peuvent être placées avant ou après les
gestionnaires d'événements, mais doivent être placées sous toute variable d'entrée ou globale dans le fichier.

Gestionnaires d'événements
Ungestionnaire d'événements est une fonction qui est exécutée chaque fois qu'un certain événement se produit.
Les gestionnaires d'événements sont la méthode par laquelle un programme MQL5 s'exécute. Par exemple,
lorsqu'un devis entrant est reçu par un conseiller expert, leNouveauTick l'événement se produit. Cela provoque
leSurTick() gestionnaire d’événements à exécuter. LeSurTick() Le gestionnaire d'événements contient du
code qui s'exécute à chaque fois qu'un changement de prix se produit.

Chaque type de programme possède ses propres gestionnaires d'événements. Les conseillers experts et les
indicateurs utilisent leChaleur événement pour exécuter lesurInit() gestionnaire d'événements, qui s'exécute
une fois au début du programme. Les scripts utilisent leCommencer événement, qui est géré par leAu
démarrage() gestionnaire d'événements. Les indicateurs utilisent leCalculer l'événement et leSurCalculer()
gestionnaire d’événements pour exécuter les calculs d’indicateurs. Nous entrerons plus en détail sur les
gestionnaires d'événements pour chaque type de programme dans les chapitres correspondants.

Un exemple de programme
Voici un bref exemple montrant tous les éléments décrits ci-dessus et comment ils s'intégreraient dans un
programme MQL5. Tous les éléments ne seront pas présents dans chaque programme – par exemple,
un#inclure La directive n'est pas nécessaire si vous n'incluez pas de fonctions et de variables à partir d'un
fichier externe. Le#propriété les directives sont généralement facultatives. La plupart des programmes auront
des variables d'entrée, mais les variables globales sont facultatives. Et vous devrez peut-être ou non créer vos
propres classes ou fonctions.

Les gestionnaires d'événements varient en fonction du type de programme. Cet exemple montre un programme
de conseillers experts avec lesurInit() etSurTick() gestionnaires d'événements :

// Directives du préprocesseur
#property copyright "Andrew R. Young"
#lien de propriété "http://www.expertadvisorbook.com"
#property description "Un exemple de structure de programme MQL5"

65
Acheteur : Evangelos Papageorgiou (vpapnm@yahoo.gr)
Numéro de transaction : 28J511988U193831A

ETXPERT UNCONSEIL P.ROGRAMMATION POUR METTRADER 5


#définir PI 3.14159265

// Variables d'entrée
entrée double Rayon = 1,5 ;

// Variables globales
doubles RadiusSq ;

// Gestionnaires
d'événements int
OnInit()
{
RadiusSq = MathPow(Rayon,2); retour(0);
}

annuler OnTick()
{ double zone = CalcArea();
Print("L'aire d'un cercle avec un rayon de "+Radius+" est "+area);
}

// Fonctions double
CalcArea()
{ double résultat = PI *
RadiusSq ;
renvoyer le résultat ;
}

Ci-dessus se trouve un programme simple qui calcule l’aire d’un cercle, étant donné le rayon. Le#propriété les
directives viennent en premier, avec quelques informations descriptives sur le programme. UN#définir La
directive définit une constante pour Pi. Une variable d'entrée nomméeRayon permet à l'utilisateur de saisir le
rayon d'un cercle. Enfin, une variable globale nomméeRayonSq est disponible pour toutes les fonctions du
programme.

Ce programme dispose de deux gestionnaires d'événements. LesurInit() Le gestionnaire d'événements


s'exécute une fois au début du programme. La place duRayon la variable est calculée et stockée dans la variable
globaleRayonSq. AprèsonInit() a couru, leSurTick() Le gestionnaire d'événements s'exécutera à chaque
changement de prix entrant.

LeSurTick() le gestionnaire d'événements appelle la fonctionCalcArea(), qui est défini au bas de notre
programme. La fonction calcule l'aire d'un cercle et renvoie le résultat auSurTick() fonction. LeImprimer() La
fonction imprimera la chaîne suivante dans le journal, en supposant que la valeur par défaut deRayon est utilisé:

L'aire d'un cercle de rayon 1,5 est 7,0685834625

C'est ça. Nous n'avons ajouté aucune fonction de trading à ce programme, car nous voulons simplement
démontrer la structure de base d'un programme MQL5. Dans le chapitre suivant, nous commencerons à créer
des conseillers experts.

66
Acheteur : Evangelos Papageorgiou (vpapnm@yahoo.gr)
Numéro de transaction : 28J511988U193831A

Bases du conseiller expert

Chapitre 8 - Bases de l'Expert Advisor

Systèmes de comptabilisation des positions


Il existe deux systèmes de comptabilisation des positions dans MetaTrader 5 :filets etcouverture. Le système de
compensation est le système de comptabilisation des positions d'origine utilisé dans MetaTrader 5. Un trader ne
peut avoir qu'une seule position globale dans un symbole. Les ordres d'achat et de vente ultérieurs s'ajouteront
à la position actuelle et feront la moyenne du prix d'ouverture, ou clôtureront ou inverseront une partie ou la
totalité de la position. Tout prix stop loss ou take profit fixé s’appliquera à l’ensemble de la position. Si des
clôtures partielles de positions sont souhaitées, les ordres stop devront être fixés aux prix souhaités.

Le système de couverture sera familier aux utilisateurs de MetaTrader 4. Un trader peut avoir plusieurs positions
d'achat et de vente ouvertes sur un symbole à la fois, chacune avec son propre prix stop loss ou take profit.
Chaque transaction devra être clôturée indépendamment. La couverture a été ajoutée à MetaTrader 5 en 2016 et
était une fonctionnalité très recherchée par les anciens utilisateurs de MetaTrader 4.

Système de position de compensation


Comme décrit précédemment, le système de comptabilisation des positions nettes était à l'origine le système de
comptabilisation des positions par défaut pour MetaTrader 5. Cela a été fait pour permettre à MetaTrader 5
d'être utilisé avec des instruments autres que le Forex. Dans le système de compensation, il ne peut y avoir
qu’une seule position ouverte à la fois sur un titre. Vous pouvez ajouter des ordres à une position, ou ouvrir des
ordres dans la direction opposée pour réduire ou même changer la direction d'une position. Vous pouvez
également ajuster le stop loss ou prendre profit d’une position à tout moment.

Montrons avec un exemple : un ordre d'achat est ouvert sur EURUSD pour 1,00 lots. Cela se traduit par une
position longue nette de 1 lot sur l'EURUSD. Si nous ouvrons ensuite un ordre de vente pour 0,50 lot, le résultat
est une position longue nette de 0,5 lot. L’ordre de vente a effectivement clôturé une partie de la position longue
existante. Si nous ouvrions un deuxième ordre de vente de 0,5 lot, la position longue restante serait clôturée,
nous laissant sans position.

Voici un autre exemple : nous avons une position longue de 1 lot sur l'EURUSD. Nous ouvrons ensuite un ordre
de vente pour 2,00 lots. Le résultat est une position courte nette de 1 lot. Notre position est passée de longue à
courte en une seule transaction. Ainsi, il est possible de clôturer une position existante et d'ouvrir une nouvelle
position dans la direction opposée en une seule transaction.

Lorsque plusieurs ordres dans la même direction sont ajoutés à une position, le prix de la position est modifié. Le
prix de la nouvelle position est calculé comme une moyenne pondérée. Par exemple, si nous avons une position
longue nette de 1 lot sur
EURUSD à 1,35712, et ajoutez un deuxième ordre d'achat de 0,5 lot à 1,35825, le nouveau prix de position serait
1.357496. Si un ordre est ouvert dans la direction opposée pour clôturer une partie d’une position existante, le
prix de la position ne change pas.

En fonction du type d'ordre et du type d'exécution sur le marché, un stop loss et un take profit peuvent être
définis lorsque l'ordre est passé. Sinon, la position peut être modifiée à tout moment pour ajouter ou modifier le

67
Acheteur : Evangelos Papageorgiou (vpapnm@yahoo.gr)
Numéro de transaction : 28J511988U193831A

ETXPERT UNCONSEIL P.ROGRAMMATION POUR METTRADER 5


stop loss et le take profit. Si un autre ordre est passé avec un stop loss et/ou un take profit différent, le nouveau
prix stop loss et/ou take profit sera appliqué à l'ensemble de la position.

Par exemple, une position d'achat de 0,75 lots est ouverte sur l'EURUSD à 1,35674, avec un stop loss à 1,3517 et
aucun take profit. Un deuxième ordre d'achat de 0,25 lot est passé avec un stop loss de 1,3532 et un take profit
de 1,3632. Le stop loss de l'ensemble de la position est modifié à 1,3532 et le take profit à 1,3632.

Bien qu'il ne soit pas possible d'avoir plusieurs ordres ouverts avec des tailles de lot différentes et des prix stop
loss et/ou take profit en utilisant le système de compensation, vous pouvez utiliser les ordres en attente pour
sortir d'une position à des niveaux de prix prédéterminés. Nous examinerons cela plus en détail au chapitre 13.

Système de position de couverture


À l’inverse, le système de position de couverture permet plusieurs ordres d’achat et de vente à différents prix
d’ouverture, avec une taille de lot individuelle, des prix stop loss et take profit fixés. Les commandes devront
être clôturées indépendamment les unes des autres. Bien qu'il soit possible de clôturer partiellement une
position de couverture, cela n'est pas souhaitable, car on peut échelonner la clôture de plusieurs positions à des
cours de clôture différents. Le processus de gestion des positions couvertes est différent du processus de
gestion des positions de compensation. Dans le reste de cet ouvrage, nous exposerons les différences entre la
gestion des positions de couverture et la gestion des positions de compensation.

Ordres de marché
UNordre du marché est une demande d’ouverture d’une transaction au prix actuel du marché. Le type de
commande est soitacheter ouvendre. Les ordres d'achat sont ouverts au prix Ask actuel, tandis que les ordres
de vente sont ouverts au prix Bid actuel. À l’inverse, lorsqu’une position est fermée, une position d’achat est
fermée au prix acheteur actuel, tandis qu’une position de vente est fermée au prix vendeur actuel.

Le processus par lequel un ordre est exécuté dépend du serveur de trading.type d'exécution. Il existe quatre
types d'exécution dans MetaTrader 5. Le type d'exécution est déterminé par le courtier et est indiqué dans le
champTaper domaine de laNouvel ordre boite de dialogue. La plupart des courtiers Forex utilisent soit
l'exécution de marché, soit l'exécution instantanée. Les courtiers ECN utilisent l'exécution d'échange. Le type
d'exécution de demande est destiné aux instruments non Forex et n'est pas couramment utilisé.

68
Acheteur : Evangelos Papageorgiou (vpapnm@yahoo.gr)
Numéro de transaction : 28J511988U193831A

Bases du conseiller expert

Figure 8.1 - LeNouvel ordre dialogue. Ce courtier utilise l'exécution instantanée.

Exécution instantanée est le mode d'exécution classique familier aux utilisateurs de MetaTrader 4. Le trader
spécifie le type de transaction, le symbole à négocier, le volume de la transaction, un prix stop loss et take profit,
ainsi que l'écart en points. Si le prix actuel du marché s'écarte du dernier prix coté du nombre de points spécifié
dans leDéviation champ, une re-cotation est déclenchée et le trader est invité à accepter ou à rejeter le nouveau
prix. Sinon, la transaction est effectuée au prix actuel du marché.

L'un des avantages de l'exécution instantanée est que le trader peut spécifier un stop loss et prendre des
bénéfices lors du passage de l'ordre, ce qui permet d'économiser une étape lors du trading manuel. En cas
d'évolution rapide des prix et d'écart de prix important (également appeléglissement), le trader a la possibilité
de rejeter l'ordre et d'attendre des conditions de marché plus calmes.

L’inconvénient de l’exécution instantanée est le glissement. Lorsque le glissement dépasse l'écart spécifié, l'ordre
ne sera pas passé, ce qui crée des difficultés lors du trading automatique. Même si l'ordre est passé, le prix du
stop loss et du take profit sera inférieur de quelques points par rapport au prix d'exécution de l'ordre.

La plupart des courtiers utilisent désormais l'exécution sur le marché ou en bourse. Avec leexécution du marché
type, le trader spécifie le volume des échanges et la politique de remplissage. La transaction est exécutée au prix
actuel du marché, sans recotation. Aucun stop loss ou take profit n'est placé avec le type d'exécution sur le
marché. Le trader devra modifier la position pour ajouter un stop loss et prendre des bénéfices une fois l'ordre
exécuté.

Exécution d'échange est utilisé par les courtiers ECN et permet aux traders d'exécuter des transactions sans
pupitre de négociation intermédiaire. À toutes fins utiles, le type d’exécution d’échange fonctionne de la même
manière que le type d’exécution de marché.

69
Acheteur : Evangelos Papageorgiou (vpapnm@yahoo.gr)
Numéro de transaction : 28J511988U193831A

ETXPERT UNCONSEIL P.ROGRAMMATION POUR METTRADER 5


Pour leexécution de la demande type, le trader doit d'abord spécifier le volume des échanges, stopper les
pertes et prendre des bénéfices. Ensuite, le terminal demande un prix de marché actuel au serveur commercial.

Lepolitique de remplissage détermine les mesures à prendre en cas de liquidité insuffisante sur le
marché.Remplir ou tuer signifie que si le serveur de trading ne peut pas passer la commande pour le volume
total des transactions au prix spécifié, la commande est annulée.Remplir ou Annulerl (également
appeléImmédiat ou Annuler) signifie que le serveur de trading tentera d'exécuter partiellement une commande.
Tout volume restant non rempli sera annulé.Retour signifie que le serveur d'échange tentera toujours de remplir
tout volume non rempli.

En règle générale, vous n'aurez pas à vous soucier de la définition de la politique de remplissage, car le serveur
d'échange utilisera par défaut le paramètre approprié. Vous pouvez définir la politique de remplissage lors du
trading automatique si nécessaire, et nous permettra au trader de l'ajuster si nécessaire.

Gestionnaires d’événements Expert Advisor


À la fin du dernier chapitre, nous avons discuté de la structure d'un programme MQL5 et présenté au lecteur les
gestionnaires d'événements. Discutons des gestionnaires d'événements utilisés dans les programmes Expert
Advisor :

surInit()
LesurInit() le gestionnaire d'événements s'exécute lorsque leChaleur l'événement se produit. L'événement
Init se produit lorsque le programme est initialisé. Normalement, lesurInit() Le gestionnaire d'événements
s'exécute une fois au début d'un programme. S'il y a des changements dans les propriétés du conseiller expert,
ou si le symbole ou la période du graphique actuel est modifié, le conseiller expert sera réinitialisé et
lesurInit() la fonction s'exécutera à nouveau.

S'il y a des actions que vous souhaitez exécuter une seule fois au début du programme, placez-les dans
lesurInit() gestionnaire d'événements. LesurInit() Le gestionnaire d'événements n'est pas requis dans
votre programme, mais il est recommandé. Nous utiliseronssurInit() pour initialiser certaines variables et
effectuer des actions ponctuelles.

SurDéinit()
LeSurDéinit() le gestionnaire d'événements s'exécute lorsque leDéinitialisation l'événement se produit.
L'événement Deinit se produit lorsque le programme est désinitialisé. Si les propriétés de l'Expert Advisor sont
modifiées, le symbole ou la période du graphique actuel est modifié, ou si vous quittez le programme,
leSurDéinit() l'événement se déroulera.

Si vous souhaitez exécuter des actions à la fin du programme, placez-les dans leSurDéinit() gestionnaire
d'événements. LeSurDéinit() le gestionnaire d’événements n’est pas requis dans votre programme. Une
utilisation duSurDéinit() Le gestionnaire d'événements consiste à supprimer des objets du graphique lors de
la suppression d'un indicateur ou d'un programme qui les a placés.

SurTick()
LeSurTick() le gestionnaire d'événements s'exécute lorsque leNouveauTick l'événement se produit.
L'événement NewTick se produit lorsqu'un changement de prix est reçu du serveur. En fonction de l'activité

70
Acheteur : Evangelos Papageorgiou (vpapnm@yahoo.gr)
Numéro de transaction : 28J511988U193831A

Bases du conseiller expert

actuelle du marché, les changements de prix peuvent se produire plusieurs fois par minute, voire plusieurs fois
par seconde. Chaque fois qu'un changement de prix se produit, leSurTick() le gestionnaire d’événements
s’exécutera.

LeSurTick() Le gestionnaire d'événements est le gestionnaire d'événements le plus important de votre


programme et est requis dans votre conseiller expert. Presque toute la logique de votre système commercial se
produira dans leSurTick() gestionnaire d'événements, et la plupart des exemples de code de ce livre iront à
l'intérieur duSurTick() gestionnaire d'événements.

Sur le commerce()
LeSur le commerce() le gestionnaire d'événements s'exécute lorsque leCommerce l'événement se produit.
L'événement Trade se produit chaque fois qu'il y a un changement dans le pool de commandes. Cela inclut le
placement et la modification d'ordres, la clôture et la modification de positions et le déclenchement d'ordres en
attente. Si vous souhaitez exécuter des actions lorsque des changements dans le statut de la commande se
produisent, placez-les dans leSur le commerce() gestionnaire d'événements. LeSur le commerce() le
gestionnaire d’événements est facultatif.

Au chronomètre()
LeAu chronomètre() le gestionnaire d'événements s'exécute lorsque leMinuteur l'événement se produit.
L'événement Timer se produit lorsqu'un timer défini par leEventSetTimer() la fonction est activée. Cela vous
permet d'exécuter des actions à des intervalles spécifiés. LeAu chronomètre() le gestionnaire d’événements
est facultatif. Nous discuterons de l'utilisation duAu chronomètre() gestionnaire d'événements au chapitre 18.

Création d'un conseiller expert dans MetaEditor


Le moyen le plus simple de créer un fichier de conseiller expert dans MetaEditor est d'utiliser leAssistant MQL5.
Si vous disposez d'un modèle que vous souhaitez utiliser pour créer vos conseillers experts (comme celui inclus
dans le téléchargement du code source), vous pouvez ouvrir le fichier et l'enregistrer sous un nom différent dans
le fichier\MQL5\Experts annuaire. Malheureusement, MetaEditor pour MQL5 ne permet pas l'utilisation de
fichiers modèles prédéfinis.

Clique leNouveau sur la barre d'outils MetaEditor pour ouvrir leAssistant MQL5. Veiller à ce queConseiller
expert (modèle) est sélectionné, puis cliquez surSuivant. LePropriétés La boîte de dialogue vous permet de saisir
un nom de fichier et des informations descriptives sur votre programme. Vous pouvez éventuellement ajouter
des variables d'entrée dans leParamètres fenêtre. Le chemin vers le\MQL5\Experts Le dossier est déjà inclus
dans le nom du fichier. Tapez le nom de votre conseiller expert en conservant le "Experts\" chemin qui le
précède. La figure 8.2 montre lePropriétés dialogue.

71
Acheteur : Evangelos Papageorgiou (vpapnm@yahoo.gr)
Numéro de transaction : 28J511988U193831A

ETXPERT UNCONSEIL P.ROGRAMMATION POUR METTRADER 5

Figure 8.2 – La boîte de dialogue des propriétés d'Expert Advisor duAssistant MQL5.

Cliquez surSuivant, et vous serez invité à insérer des gestionnaires d'événements supplémentaires. LeSur le
commerce() Le gestionnaire d'événements est coché par défaut. Si vous devez ajouter un gestionnaire
d'événements à votre programme, cochez-le et cliquez surFinition au fond. La figure 8.3 montre la boîte de
dialogue du gestionnaire d'événements. Après avoir cliquéFinition, un nouveau fichier Expert Advisor MQ5 est
créé dans le\MQL5\Experts dossier et le fichier est ouvert dans MetaEditor.

72
Acheteur : Evangelos Papageorgiou (vpapnm@yahoo.gr)
Numéro de transaction : 28J511988U193831A

Bases du conseiller expert

Figure 8.3 – La boîte de dialogue des gestionnaires d'événements duAssistant MQL5.

Voici à quoi ressemble un modèle de conseiller expert vide avec les gestionnaires d'événements de base
ajoutés :

//+------------------------------------------------------------- -------------------+
//| Conseiller expert simple.mq5 |
//| Andrew Jeune |
//| http://www.easyexpertforex.com |
//+------------------------------------------------------------- -------------------+
#propriété copyright "Andrew Young"
#lien de propriété "http://www.easyexpertforex.com"
#version de la propriété "1.00"
//+------------------------------------------------------------- -------------------+
//| Fonction d'initialisation experte |
//+------------------------------------------------------------- -------------------+
int OnInit()
{
//---

//-- return(0); }

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

73
Acheteur : Evangelos Papageorgiou (vpapnm@yahoo.gr)
Numéro de transaction : 28J511988U193831A

ETXPERT UNCONSEIL P.ROGRAMMATION POUR METTRADER 5


//| Fonction de désinitialisation experte |
//+-------------------------------------------------------------
-------------------+ void OnDeinit (const int raison)
{
//---

}
//+------------------------------------------------------------- -------------------+
//| Fonction de tick expert |
//+-------------------------------------------------------------
-------------------+ annuler OnTick()
{
//---

}
//+------------------------------------------------------------- -------------------+

Le fichier d'expert-conseil généré par l'assistant MQL5 comprend trois descriptifs#propriété les directives et
lessurInit(),SurDéinit() etSurTick() fonctions par défaut. Si vous avez spécifié des gestionnaires
d'événements ou des paramètres d'entrée supplémentaires dans l'assistant MQL5, ils apparaîtront également ici.

74
Acheteur : Evangelos Papageorgiou (vpapnm@yahoo.gr)
Numéro de transaction : 28J511988U193831A

Passation de commande

Chapitre 9 - Passation de commande

CommandeEnvoyer()
LeCommandeEnvoyer() La fonction est utilisée pour passer, modifier et clôturer les commandes. L'utilisation de
la fonction a changé par rapport à MQL4. Voici la définition de la fonction pourCommandeEnvoyer():

bool OrderSend (demande MqlTradeRequest&, résultat MqlTradeResult&)

LeCommandeEnvoyer() la fonction a deux paramètres : unMqlTradeRequest objet qui contient les paramètres
de la commande, et unMqlTradeRésultat objet qui renvoie les résultats de la demande de commande. Les
esperluettes (&) dans la définition de la fonction indiquent que les deux objets sont passés par référence.

La structure MqlTradeRequest
Examinons leMqlTradeRequest structure. Nous avons précédemment abordé les structures page 23. Nous
déclarerons un objet duMqlTradeRequest tapez et attribuez les paramètres commerciaux aux variables
membres de l’objet. Voici laMqlTradeRequest définition de la structure à partir duRéférence MQL5:
struct
MqlTradeRequest {
Action ENUM_TRADE_REQUEST_ACTIONS ; // Type d'opération commerciale
magie de la tête; // Nombre magique
ordre de tête ; // Ticket de commande (pour modifier les
commandes en attente)
symbole de chaîne ; // Symbole
double volume; // Volume en lots
double prix ; // Prix d'ouverture de la commande
double stop-limite ; // Prix stop limite (pour les ordres stop
limit)
double sl; // Prix stop-loss
double tp; // Prendre le prix du bénéfice
déviation de la tête ; // Écart en points
Type ENUM_ORDER_TYPE ; // Type de commande
ENUM_ORDER_TYPE_FILLING type_filling ; //Type d'exécution
ENUM_ORDER_TYPE_TIME type_heure ; //Type d'expiration
expiration de la date et de l'heure ; // Date d'expiration
commentaire de chaîne ; // Commentaire de commande
position de la tête ; // Ticket de position (pour modifier les
ordres de couverture)
position de la tête_by; // Ticket de position opposée (pour proximité)
}
Pour déclarer unMqlTradeRequest objet, déclarez simplement un objet en utilisantMqlTradeRequest comme
type :

Requête MqlTradeRequest ;

75
Acheteur : Evangelos Papageorgiou (vpapnm@yahoo.gr)
Numéro de transaction : 28J511988U193831A

ETXPERT UNCONSEIL P.ROGRAMMATION POUR METTRADER 5


Cela crée un objet nommédemande. UnMqlTradeRequest l'objet est généralement déclaré au début de
laSurTick() gestionnaire d'événements de votre programme ou en tant que membre de la classe. Nous
pouvons maintenant affecter les paramètres de trading aux variables de l'objet à l'aide de l'opérateur point (.) :

request.symbol = _Symbol;
requête.volume = 0,5 ;
request.type = ORDER_TYPE_BUY ;

Regardons de plus près les membres duMqlTradeRequest structure:

• action – Le type d’opération commerciale. Requis. Cette variable accepte une valeur
deENUM_TRADE_REQUEST_ACTIONS énumération:

◦TRADE_ACTION_DEAL - LeCommandeEnvoyer() La fonction passera un ordre au marché.

◦TRADE_ACTION_PENDING - LeCommandeEnvoyer() La fonction passera une commande en attente.

◦TRADE_ACTION_SLTP - LeCommandeEnvoyer() La fonction modifiera le stop loss et/ou prendra profit


de la position actuelle.

◦TRADE_ACTION_MODIFY - LeCommandeEnvoyer() La fonction modifiera une commande en attente


précédemment passée.

◦TRADE_ACTION_REMOVE - LeCommandeEnvoyer() La fonction annulera une commande en attente


précédemment passée.

◦TRADE_ACTION_CLOSE_BY – Une nouvelle fonctionnalité ajoutée à MetaTrader 5.


LeCommandeEnvoyer() La fonction fermera deux positions à la fois, économisant ainsi un spread
qui serait autrement dépensé en fermant deux positions séparément.

• la magie – Le « nombre magique ». Ce numéro identifie de manière unique les commandes comme
étant passées par un certain expert-conseil. Facultatif.

• commande – Le ticket de commande d’une commande en attente préalablement passée. Obligatoire si


leaction la variable est définie surTRADE_ACTION_MODIFY ouTRADE_ACTION_REMOVE.

• symbole – Le symbole de la sécurité financière pour trader, par exemple,"EURUSD" ou_Symbole.


Requis.

• volume – Le volume des échanges en lots. Requis.

• prix – Le cours d’ouverture de l’ordre. Pour un ordre en attente, le prix peut être n’importe quel prix
valide supérieur ou inférieur au prix actuel du marché. Pour un ordre au marché, le prix doit être le prix
Ask actuel (pour les ordres d’achat) ou le prix Bid actuel (pour les ordres de vente). Les courtiers qui
utilisent l'exécution sur le marché ou en bourse n'ont pas besoin de ce paramètre lorsqu'ils passent des
ordres au marché. Sinon, obligatoire si l'action est définie
surTRADE_ACTION_DEAL,TRADE_ACTION_PENDING ouTRADE_ACTION_MODIFY.

76
Acheteur : Evangelos Papageorgiou (vpapnm@yahoo.gr)
Numéro de transaction : 28J511988U193831A

Passation de commande

• limite d'arrêt – Il s’agit du prix de l’ordre limite pour les ordres stop limite. Leaction la variable doit être
définie surTRADE_ACTION_PENDING, et letaper La variable ci-dessous doit être définie
surORDER_TYPE_BUY_STOP_LIMIT ouORDER_TYPE_SELL_STOP_LIMIT. Obligatoire pour les ordres stop
limit.

• sl – Le prix stop loss. Obligatoire pour les ordres au marché si le courtier utilise les types de demande
ou d'exécution instantanée. Également requis pour les commandes en attente. Si le courtier utilise
l'exécution sur le marché ou en bourse, le stop loss n'est pas placé.

• ville – Le prix du take profit. Obligatoire pour les ordres au marché si le courtier utilise les types de
demande ou d'exécution instantanée. Également requis pour les commandes en attente. Si le courtier
utilise l'exécution sur le marché ou en bourse, le take profit n'est pas placé.

• déviation – L’écart maximum en points. Requis pour la demande ou l’exécution instantanée d’ordres de
marché.

• taper – Le type de commande. Cette variable accepte une valeur deENUM_ORDER_TYPE énumération.
Requis.

◦ORDER_TYPE_BUY – Un ordre d’achat au marché.

◦ORDER_TYPE_SELL – Un ordre de vente au marché.

◦ORDER_TYPE_BUY_STOP – Un ordre d’achat stop en attente.

◦ORDER_TYPE_SELL_STOP – Un ordre stop de vente en attente.

◦ORDER_TYPE_BUY_LIMIT – Un ordre d’achat à limite en attente.

◦ORDER_TYPE_BUY_STOP_LIMIT – Un ordre stop limit d’achat en attente.

◦ORDER_TYPE_SELL_STOP_LIMIT – Un ordre stop de vente en attente.

• type_remplissage – La politique de remplissage de la commande. Cette variable accepte une valeur de


ENUM_ORDER_TYPE_FILLING énumération. S’il n’est pas spécifié, la valeur par défaut du serveur d’échange
sera utilisée.

◦ORDER_FILLING_FOK –Remplir ou tuer. Si la commande ne peut pas être exécutée au volume et au prix
demandés, la commande ne sera pas passée.

◦ORDER_FILLING_IOC –Immédiat ou Annuler (également connu sous le nom de Remplir ou Annuler). Si


l’ordre ne peut pas être exécuté au volume et au prix demandés, un ordre partiel sera exécuté.

◦ORDER_FILLING_RETURN –Retour. Si un ordre ne peut pas être exécuté au volume et au prix d'échange
demandés, le serveur d'échange passera un ordre supplémentaire pour le volume non exécuté.

• type_heure – Le type d’expiration pour une commande en attente. Cette variable accepte une valeur
deENUM_ORDER_TYPE_TIME énumération. Facultatif. S'il n'est pas spécifié, la valeur par défaut du
serveur d'échange (généralementBon jusqu'à annulation) sera utilisé.

77
Acheteur : Evangelos Papageorgiou (vpapnm@yahoo.gr)
Numéro de transaction : 28J511988U193831A

ETXPERT UNCONSEIL P.ROGRAMMATION POUR METTRADER 5


◦ORDER_TIME_CGV – Bon jusqu’à annulation. La commande en attente n’expirera pas.

◦ORDER_TIME_DAY – L’ordre en attente expirera à la fin de la journée de bourse.

◦ORDER_TIME_SPECIFIED – La commande en attente expirera à la date et à l’heure spécifiées dans la


variable d’expiration ci-dessous.

• expiration – Le délai d’expiration de la commande en attente. Obligatoire si letype_heure la variable


est définie surORDER_TIME_SPECIFIED. La valeur doit être dedateheure type et doit être défini
ultérieurement.

• commentaire – Une chaîne de texte à utiliser comme commentaire de commande.

• position – Le ticket d’ordre d’une position de couverture. Utilisé uniquement lors de la modification ou
de la clôture d'un ordre de couverture.

• position_par – Le ticket d’ordre d’une position de couverture opposée à clôturer. Nous aborderons sous
peu l’opération de commande Close By.

Ouvrir un ordre au marché


Montrons comment leMqlTradeRequest La structure peut être utilisée pour passer un ordre au marché. La
première chose que nous devons faire est de déclarerMqlTradeRequest etMqlTradeRésultat objets. Nous
couvrirons leMqlTradeRésultat structure dans la section suivante :

Requête MqlTradeRequest ;
Résultat MqlTradeResult ;

Cet exemple placera un ordre d'achat au marché de 1,00 lot sur le symbole graphique actuel sans stop loss ni
take profit :

requête.action = TRADE_ACTION_DEAL ;
request.type = ORDER_TYPE_BUY ;
request.symbol = _Symbol;
requête.volume = 1 ;
request.type_filling = ORDER_FILLING_FOK ;
request.price = SymbolInfoDouble(_Symbol,SYMBOL_ASK);
requête.sl = 0 ; demande.tp = 0 ; demande.écart = 50 ;
requête.magic = 43 ; OrderSend (demande, résultat);

Leaction la variable est définie surTRADE_ACTION_DEAL, ce qui indique qu'il s'agit d'un ordre de marché.
Letaper la variable est définie surORDER_TYPE_BUY, ce qui indique qu'il s'agit d'un ordre d'achat. Nous
attribuons le prédéfini_Symbole variable à lasymbole variable pour indiquer que nous allons passer une
commande sur le symbole graphique actuel. Levolumela variable est définie sur 1 lot. Letype_remplissage La
variable définit le type de remplissage surRemplir ou tuer. Tous les types d'exécution nécessitent que les cinq
variables ci-dessus soient spécifiées. (Sitype_remplissage n'est pas spécifié, la politique de remplissage par
défaut du serveur sera appliquée par défaut.)

78
Acheteur : Evangelos Papageorgiou (vpapnm@yahoo.gr)
Numéro de transaction : 28J511988U193831A

Passation de commande

Nous utilisons leSymboleInfoDouble() fonction pour renvoyer le prix vendeur actuel pour le symbole
graphique actuel et attribuer cette valeur auprix variable. N'oubliez pas qu'un ordre d'achat au marché est
passé au prix Ask actuel. Lesl etville les variables sont toutes deux mises à zéro. Ledéviation La variable
définit l’écart à 50 points.

Les courtiers utilisant les types d'exécution de marché ou d'échange ignoreront lesprix,sl,ville etdéviation
variables. Cependant, comme nous souhaitons que notre code fonctionne sur tous les courtiers, quel que soit le
type d'exécution, nous attribuerons des valeurs à ces variables pour les courtiers qui utilisent les types
d'exécution instantané ou de demande.

Vous souhaiterez peut-être spécifier un nombre magique, surtout si vous négociez sur un compte de
couverture. Un nombre magique est un identifiant défini par l'utilisateur qui identifie les commandes définies par
cet expert. Dans l’exemple ci-dessus, nous fixons le nombre magique à 43.

Notez que nous n’avons pas défini de stop loss ou de take profit. Étant donné que les courtiers d'exécution du
marché et des échanges ignoreront le stop loss et prendront des bénéfices, nous ajouterons un stop loss et
prendrons des bénéfices à la position une fois l'ordre passé. Cela garantit également un placement précis du
stop loss et du take profit par rapport au prix d’ouverture de la position.

Enfin, nous appelons leCommandeEnvoyer() fonction, en passant notredemande etrésultat objets comme
paramètres. Le serveur commercial tentera de passer la commande et renverra le résultat dans lerésultat
variables d'objet.

Ajouter un Stop Loss et un Take Profit


Après avoir passé un ordre au marché, nous ajouterons un stop loss et un take profit, s'il y en a un qui a été
spécifié. Par souci de simplicité, nous attribuerons simplement un stop loss valide et prendrons le prix du profit
ausl etville variables dans cet exemple. L'exemple ci-dessous modifie un ordre pour une position de
compensation :

requête.action =
TRADE_ACTION_SLTP ; request.symbol
= _Symbol; requête.sl = 1,3500 ;
demande.tp = 1,3650 ; OrderSend (demande,

résultat);

Leaction la variable est définie surTRADE_ACTION_SLTP, ce qui indique que nous allons modifier le stop loss et
prendre du profit sur la position actuelle. Lesymbole La variable précise que nous modifierons la position
ouverte sur le symbole graphique actuel. Nous avons fixé un prix stop loss et take profit qui serait valable sur
une position d'achat, en supposant que le prix d'ouverture de la position est compris entre 1,3500 et 1,3650.

Toutes les variables ci-dessus doivent être spécifiées lors de la modification d'une position, que la valeur du
stop loss ou du take profit reste inchangée. Si vous ne souhaitez définir ni la valeur stop loss ni la valeur take
profit, définissez la variable appropriée sur zéro.

Lors de la modification du prix stop loss ou take profit d'une position de couverture, vous devrez préciser le
numéro de ticket de la position ouverte à modifier à l'aide dudemande.position paramètre. Vous souhaiterez
peut-être également spécifier un nombre magique, qui est un identifiant unique utilisé pour déterminer quelles

79
Acheteur : Evangelos Papageorgiou (vpapnm@yahoo.gr)
Numéro de transaction : 28J511988U193831A

ETXPERT UNCONSEIL P.ROGRAMMATION POUR METTRADER 5


commandes ont été passées par votre conseiller expert. Dans cet exemple, nous utiliserons un numéro de ticket
arbitraire que nous supposerons être un numéro de ticket valide pour une commande ouverte, et définirons un
nombre magique de 43 :

requête.action =
TRADE_ACTION_SLTP ; request.symbol
= _Symbol; requête.sl = 1,3500 ;
demande.tp = 1,3650 ;
demande.position = 452365 ;
requête.magic = 43 ; OrderSend
(demande, résultat);

Clôturer un ordre au marché


Pour clôturer un ordre au marché, vous placerez simplement une demande de transaction opposée sur le même
symbole. Pour les positions de compensation, vous pouvez clôturer une partie ou la totalité de la position, ou
même inverser la position en spécifiant un volume plus grand que la position actuellement ouverte. Cet exemple
clôturera la position d'achat que nous avons placée plus tôt :

requête.action = TRADE_ACTION_DEAL ;
demande.type = ORDER_TYPE_SELL ;
request.symbol = _Symbol;
requête.volume = 1 ;
request.type_filling = ORDER_FILLING_FOK ;
request.price = SymbolInfoDouble(_Symbol,SYMBOL_BID);
demande.écart = 50 ;

OrderSend (demande, résultat);


Pour les positions de couverture, vous devrez également préciser le numéro de ticket de l'ordre en l'attribuant
audemande.position variable:

requête.action = TRADE_ACTION_DEAL ;
demande.type = ORDER_TYPE_SELL ;
request.symbol = _Symbol;
requête.volume = 1 ;
request.type_filling = ORDER_FILLING_FOK ;
request.price = SymbolInfoDouble(_Symbol,SYMBOL_BID);
demande.écart = 50 ; demande.position =

452365 ; OrderSend (demande, résultat);

Modification et clôture des ordres de couverture


Lors de l’ajout d’un stop loss ou d’un take profit, ou de la clôture d’un ordre au marché sur un compte de
couverture, vous devrez spécifier une variable supplémentaire. Ledemande.position La variable prend le
numéro du ticket d'ordre de l'ordre au marché à modifier ou à clôturer. L'exemple ci-dessous clôturera l'ordre
d'achat au marché correspondant au numéro de ticket attribué audemande.position variable:

requête.action = TRADE_ACTION_DEAL ;
demande.type = ORDER_TYPE_SELL ;

80
Acheteur : Evangelos Papageorgiou (vpapnm@yahoo.gr)
Numéro de transaction : 28J511988U193831A

Passation de commande
request.symbol = _Symbol;demande.position
= 5211547 ;requête.volume = 1 ;
request.type_filling = ORDER_FILLING_FOK ;

request.price = SymbolInfoDouble(_Symbol,SYMBOL_BID);

demande.écart = 50 ; OrderSend (demande, résultat);

Fermer par opération


La nouveauté de MetaTrader 5 est l'opération de clôture, qui vous permet de fermer deux positions couvertes
opposées à la fois, économisant ainsi un spread. Vous devrez spécifier le deuxième numéro de ticket à l’aide
durequête.position_by variable:

request.action = TRADE_ACTION_CLOSE_BY ;
demande.position = 452365 ;
requête.position_by = 451256 ;

OrderSend (demande, résultat);


Ouvrir une commande en attente
Montrons ensuite le placement d'une commande en attente. Nous ajouterons un stop loss, un take profit et un
délai d'expiration à cet ordre. Toutes les variables ci-dessous doivent être spécifiées lors du passage d’une
commande en attente :

request.action =
TRADE_ACTION_PENDING ; request.type =
ORDER_TYPE_BUY_STOP ; request.symbol
= _Symbol; requête.volume = 1 ;
demande.prix = 1,3550 ; requête.sl =
1,3500 ; demande.tp = 1,3650 ;
request.type_time = ORDER_TIME_SPECIFIED ;
request.expiration = D'2018.01.10 15:00';
request.type_filling = ORDER_FILLING_FOK ;
requête.stoplimit = 0 ; OrderSend (demande,
résultat);

Leaction la variable est définie surTRADE_ACTION_PENDING, indiquant que nous passons une commande en
attente. Letaper la variable est définie surORDER_TYPE_BUY_STOP, indiquant un ordre stop d’achat.
Lesymbole,volume, prix, sl etville les variables se voient toutes attribuer des valeurs appropriées.

Letype_heure la variable est définie surORDER_TIME_SPECIFIED, ce qui indique que la commande aura un
délai d'expiration. UNdateheure constante du 10 janvier 2018 à 15h00 est affectée auexpiration variable.
Letype_remplissage La variable définit le type de remplissage sur Fill ou Kill. Lelimite d'arrêt La variable
n'est nécessaire que lors du passage d'un ordre stop limite, mais nous attribuerons une valeur de 0.

Modifier une commande en attente


Montrons comment modifier une commande en attente. Vous aurez besoin du ticket de commande d'une
commande en attente pour pouvoir la modifier. Pour récupérer le ticket de commande, utilisez
leCommandeObtenirTicket() fonction pour parcourir le pool de commandes et sélectionner la commande en

81
Acheteur : Evangelos Papageorgiou (vpapnm@yahoo.gr)
Numéro de transaction : 28J511988U193831A

ETXPERT UNCONSEIL P.ROGRAMMATION POUR METTRADER 5


attente appropriée. Nous aborderons la gestion des commandes en attente au chapitre 13. Pour l'instant, nous
supposerons que lebillet La variable contient le numéro de ticket de commande correct :

request.action =
TRADE_ACTION_MODIFY ; request.order
= billet ; demande.prix = 1,3600 ;
requête.sl = 1,3550 ; demande.tp =
1,3700 ;
request.type_time = ORDER_TIME_SPECIFIED ;
request.expiration = D'2018.01.10 18:00';

OrderSend (demande, résultat);


La variable d'action est définie surTRADE_ACTION_MODIFY, indiquant que nous modifions une commande en
cours. Lecommande à la variable est attribué le numéro de ticket de la commande que l'on souhaite modifier, qui
est stocké dans lebillet variable. Leprix,sl,ville etexpiration les variables se voient attribuer des valeurs
appropriées et l’ordre sera modifié pour refléter ces changements.

Supprimer une commande en attente


Enfin, nous montrerons comment supprimer une commande en attente. Les seules variables nécessaires sont
lesaction etcommande variables :

request.action = TRADE_ACTION_REMOVE ;

request.order = billet ; OrderSend (demande,

résultat);

Leaction la variable est définie surTRADE_ACTION_REMOVE, indiquant que nous souhaitons supprimer une
commande en attente. Comme auparavant, le numéro du ticket est stocké dans lebillet variable, et affecté à
lacommande variable de lademande objet. Appeler leCommandeEnvoyer() La fonction avec les paramètres ci-
dessus supprimera la commande en attente qui correspond au numéro de ticket dans lecommande variable.

La structure MqlTradeResult
Une fois que nous avons passé une commande en utilisant leCommandeEnvoyer() fonction, nous allons vérifier
leMqlTradeRésultat objecter pour voir si la commande a réussi. Les variables duMqlTradeRésultat L'objet
contient le code retour du serveur d'échange, le numéro de ticket de l'échange, le volume et le prix de l'échange,
ainsi que les cours acheteur et vendeur actuels.

Voici la définition duMqlTradeRésultat structure de laRéférence MQL5:

structure MqlTradeResult
{
uint recoder ; // Code retour ulong
deal ; // Distribuer le ticket dans
l'ordre long ; // Commande de ticket
double volume ;// Volume de transaction
double prix ; // Offre double sur le prix
de la transaction ; // Prix acheteur
actuel double demande ; // Prix demandé
actuel

82
Acheteur : Evangelos Papageorgiou (vpapnm@yahoo.gr)
Numéro de transaction : 28J511988U193831A

Passation de commande
commentaire de chaîne ; // Commentaire du courtier
sur l'opération }
Comme démontré plus tôt dans le chapitre, nous déclarons unMqlTradeRésultat objet nommérésultat, et
transmettez l'objet comme deuxième paramètre duCommandeEnvoyer() fonction. Le serveur d'échange remplit
l'objet avec les résultats de la demande d'échange. Après leCommandeEnvoyer() fonction est appelée, on vérifie
les variables de laMqlTradeRésultat objet de vérifier que la transaction a été effectuée correctement. Selon le
type d'opération commerciale, tous lesMqlTradeRésultat les variables seront remplies.

La variable la plus importante duMqlTradeRésultat la structure estrecoder, qui est leCode de retour depuis le
serveur d'échange. Le code retour indique si la requête a abouti ou non. Si l'échange n'a pas été effectué, le code
retour indique la condition d'erreur.

Chaque code retour est représenté par une constante. Les codes retour les plus courants
sontTRADE_RETCODE_PLACED (10008) etTRADE_RETCODE_DONE (10009), qui indiquent tous deux que la
transaction a été effectuée avec succès. Presque tous les autres codes de retour indiquent un problème lors de
la transaction. La description du code retour indiquera la nature du problème.

Vous pouvez trouver une liste complète des codes de retour dans la référence MQL5 sousConstantes standard,
énumérations et structures > Codes d'erreurs et d'avertissements > Codes de retour du serveur commercial.

En fonction du code retour, vous souhaiterez peut-être entreprendre diverses actions, telles que l'envoi par
courrier électronique d'une confirmation d'échange ou l'impression d'un message d'erreur à l'écran. Voici un
exemple simple de la façon de gérer le code retour du serveur :

OrderSend (demande, résultat);

si (result.retcode == TRADE_RETCODE_DONE || result.retcode == TRADE_RETCODE_PLACED)


{
Print("Échange placé");
}
autre
{
Print("Échange non effectué. Code d'erreur ",result.retcode);
}

Après leCommandeEnvoyer() La fonction envoie la demande d'échange au serveur et remplit les variables
durésultat objet, on vérifie ledemande.retcode variable pour le code retour. Si le code retour est égal à
TRADE_RETCODE_DONE ou TRADE_RETCODE_PLACED, nous imprimons un message dans le journal indiquant que
la transaction a été effectuée avec succès. Sinon, nous imprimons un message indiquant que la transaction n'a
pas été effectuée, ainsi que le code retour indiquant un état d'erreur.

Leaccord etcommande Les variables contiennent les numéros de transaction et de ticket d'ordre de la
transaction. L'ordre est la demande initiale d'effectuer une transaction, tandis que la transaction est la
confirmation que la transaction est effectuée. Pour la compensation des positions, nous ne nous soucierons
généralement pas des numéros de ticket, car nous traiterons les positions par le nom du symbole. À l’inverse, les
numéros de tickets d’ordre sont très importants lors du traitement des positions de couverture, et nous
consacrerons beaucoup d’efforts à récupérer et à stocker les numéros de tickets d’ordre lors des transactions
sur des comptes de couverture.

83
Acheteur : Evangelos Papageorgiou (vpapnm@yahoo.gr)
Numéro de transaction : 28J511988U193831A

ETXPERT UNCONSEIL P.ROGRAMMATION POUR METTRADER 5


Les autres variables durésultat L'objet peut être utilisé pour confirmer le placement d'une commande ou pour
résoudre un échec de placement de commande. L'exemple ci-dessous imprime un message dans le journal
contenant levolume,prix,offre etdemander valeurs renvoyées par le serveur d'échange :

OrderSend (demande, résultat);

si (result.retcode == TRADE_RETCODE_DONE || result.retcode == TRADE_RETCODE_PLACED)


{
Print("Échange placé");
}
autre
{
Print("Échange non effectué. Code d'erreur ",result.retcode);
}

Print("Code retour :",result.retcode,", Volume : ",result.volume,


", Prix : ",result.price,", Enchère : ",result.bid,", Demander : ",result.ask);

Notez que les valeurs de retour duMqlTradeRésultat les objets dépendent du courtier – certains courtiers ne
renverront pas de valeurs pour leoffre oudemander, Par exemple.

Dans le chapitre suivant, nous ajouterons du code à nos fonctions de passation de commandes pour nous aider
à dépanner les erreurs, ainsi qu'à réessayer de passer des commandes en cas d'erreurs récupérables.

Un simple conseiller expert


Montrons comment leCommandeEnvoyer() la fonction fonctionne dans un simple conseiller expert. Cette
stratégie de trading ouvrira un ordre d'achat au marché lorsque le cours de clôture actuel est supérieur à la
moyenne mobile, ou ouvrira un ordre de vente au marché lorsque le cours de clôture est inférieur à la moyenne
mobile. L'utilisateur a la possibilité de spécifier un stop loss et un take profit en points, ainsi qu'un volume
d'échanges.

Si vous avez téléchargé le code source, le nom de fichier de cet expert-conseil estExpert simple
Conseiller.mq5, et vous pouvez voir le code dans le\MQL5\Experts\Mql5Book dossier. Ce code suppose que
le compte utilise le système de position nette. Une version de cet expert-conseil pour les comptes de
couverture sera expliquée plus loin dans ce chapitre.

Parcourons ce programme une section à la fois :

// Variables d'entrée entrée


double TradeVolume=0.1;
entrée int StopLoss=1000 ;
entrée int TakeProfit=1000 ;
entrée int MAPeriod=10 ;

Ce sont les variables d'entrée visibles par l'utilisateur final. Ceux-ci permettent à l'utilisateur d'ajuster le volume
des échanges, le stop loss et le take profit en points, ainsi que la période de la moyenne mobile.

84
Acheteur : Evangelos Papageorgiou (vpapnm@yahoo.gr)
Numéro de transaction : 28J511988U193831A

Passation de commande
// Variables globales bool
glBuyPlaced, glSellPlaced;

Ce sont des variables globales visibles par l’ensemble de notre programme. Les variables globales auront ungl
préfixe tout au long de ce livre. LeglAcheterPlacé etglVendrePlaced les variables détermineront si une
position d’achat ou de vente a été précédemment ouverte. Cela empêche une autre position de s'ouvrir dans la
même direction après la clôture de la position actuelle à un prix stop loss ou take profit.

// Gestionnaire d'événements OnTick()


annuler OnTick()
{
// Structures commerciales
Requête MqlTradeRequest ;
Résultat MqlTradeResult ;
ZeroMemory (demande);

Le début duSurTick() Le gestionnaire d'événements contient les déclarations dudemande etrésultat objets.
LeZéroMémoire() La fonction garantit que les variables membres dudemande l'objet est mis à zéro.

// Moyenne mobile double


ma[];
ArraySetAsSeries(ma,true);

int maHandle=iMA(_Symbol,0,MAPeriod,MODE_SMA,0,PRICE_CLOSE);
CopieBuffer(maHandle,0,0,1,ma);

// Prix de clôture
double clôture[];
ArraySetAsSeries(fermer,true);
CopierFermer(_Symbol,0,0,1,close);

Le code ci-dessus initialise les tableaux contenant les valeurs moyennes mobiles et les prix de clôture. Nous
discuterons des données sur les prix et les indicateurs dans les chapitres 16 et 17.et[] Le tableau contient les
valeurs de l'indicateur de moyenne mobile, tandis que lefermer[] array contient le prix de clôture de chaque
barre.

// Informations sur la position actuelle


bool openPosition = PositionSelect(_Symbol);
positionType longue = PositionGetInteger(POSITION_TYPE);
double courantVolume = 0 ; if(openPosition == true) currentVolume =
PositionGetDouble(POSITION_VOLUME);

Avant d'ouvrir une commande, nous devrons vérifier s'il y a une position déjà ouverte. Le
PositionSélection() la fonction renvoie une valeur devrai si une position est ouverte sur le symbole actuel.
Le résultat est attribué auposte libre variable. LePositionGetInteger() fonctionner avec leTYPE DE
POSITION Le paramètre renvoie le type de la position actuelle (achat ou vente) et l'attribue autype de
position variable. Si un poste est actuellement ouvert, le PositionGetDouble() fonctionner avec
lePOSITION_VOLUME Le paramètre attribue le volume de la position actuelle auVolume actuel variable. Nous
discuterons des informations sur le poste au chapitre 12.

85
Acheteur : Evangelos Papageorgiou (vpapnm@yahoo.gr)
Numéro de transaction : 28J511988U193831A

ETXPERT UNCONSEIL P.ROGRAMMATION POUR METTRADER 5


// Ordre d'achat ouvert au marché
if(close[0] > ma[0] && glBuyPlaced == false
&& (positionType != POSITION_TYPE_BUY || openPosition == false))
{ requête.action =
TRADE_ACTION_DEAL ; request.type =
ORDER_TYPE_BUY ; request.symbol = _Symbol;
request.volume = TradeVolume + currentVolume ;
request.price = SymbolInfoDouble(_Symbol,SYMBOL_ASK);
requête.sl = 0 ; demande.tp = 0 ; demande.écart =
50 ; bool sent = OrderSend (demande, résultat);

Il s’agit du code qui vérifie la condition de l’ordre d’achat au marché. Lefermer[0] array fait référence au prix de
clôture de la barre actuelle, tandis queet[0] est la valeur moyenne mobile actuelle. Si la clôture de la barre
actuelle est supérieure à la moyenne mobile, cela indique un signal d'achat. Mais nous devons d’abord vérifier
l’existence d’une position ouverte et déterminer si l’ordre précédent était un ordre d’achat.

Si laglAcheterPlacé la variable globale estvrai, cela indique que la dernière commande passée était un ordre
d'achat. Notre système de trading passera un ordre lorsqu'un signal de trading se produit. Lorsque cet ordre est
clôturé à un prix stop loss ou take profit, nous attendrons un nouveau signal commercial dans la direction
opposée avant d'ouvrir un autre ordre. Un signal de vente définira leglAcheterPlacé variable àFAUX.

Nous devons également vérifier le type de poste ouvert. Si latype de position la variable n'est pas égale à la
valeur dePOSITION_TYPE_BUY constante, nous pouvons alors supposer qu’une position d’achat n’est pas
actuellement ouverte. Nous devons également vérifier leposte libre variable pour voir si un poste est
actuellement ouvert. Nous faisons cela parce que letype de position La variable contiendra une valeur de 0
si une position n’est pas ouverte. La valeur entière pourPOSITION_TYPE_BUY il se trouve que c'est 0. Donc
leposte libre une variable est nécessaire pour confirmer l’existence d’une position d’achat.

Pour résumer les conditions de l'ordre d'achat - si le prix de clôture est supérieur à la moyenne mobile, que le
dernier ordre ouvert n'était pas un ordre d'achat et qu'une position n'est pas actuellement ouverte (ou que la
position actuellement ouverte est une position de vente), alors nous ouvrons un acheter un ordre de marché.
Toute position de vente existante sera clôturée. L’inverse est vrai pour passer des ordres de vente.

Ledemande variables suivies duCommandeEnvoyer() cette fonction devrait déjà vous être familière. Noter
lademande.volume affectation. On ajoute la valeur duVolume des échanges variable d'entrée à la valeur
duVolume actuel variable locale. Si une position de vente est actuellement ouverte, nous souhaitons ouvrir un
ordre d'achat plus important afin que la position de vente soit clôturée, et nous nous retrouvons avec une
position longue nette égale au montant saisi dansVolume des échanges.

Si les paramètres de la requête envoyée àCommandeEnvoyer() sont valides, les booléensenvoyé la variable est
définie survrai. Nous devrons toujours vérifier le code retour de l’objet résultat, ce que nous ferons ensuite. La
raison pour laquelle nous attribuons la valeur de retour deCommandeEnvoyer() auenvoyé variable est d'éviter
un avertissement du compilateur.

// Modifier SL/TP
si (result.retcode == TRADE_RETCODE_PLACED || result.retcode ==
TRADE_RETCODE_DONE)
{ requête.action = TRADE_ACTION_SLTP ;

86
Acheteur : Evangelos Papageorgiou (vpapnm@yahoo.gr)
Numéro de transaction : 28J511988U193831A

Passation de commande
PositionSelect(_Symbol);
double positionOpenPrice = PositionGetDouble(POSITION_PRICE_OPEN);

if(StopLoss > 0) request.sl = positionOpenPrice - (StopLoss * _Point);

if(TakeProfit > 0) request.tp = positionOpenPrice + (TakeProfit * _Point);

if(request.sl > 0 && request.tp > 0) OrderSend(request,result);

glBuyPlaced = vrai ;
glSellPlaced = faux ;
}
}

Une fois l’ordre d’achat au marché passé, nous vérifions si la transaction a réussi. Si tel est le cas, nous modifions
la position pour ajouter le stop loss et prendre des bénéfices. La première chose à faire est de vérifier la valeur
durésultat.retcode variable pour voir si la commande a été passée avec succès. Selon le type d'exécution,
une valeur de code retour deTRADE_RETCODE_PLACED ouTRADE_RETCODE_DONE indique une commande réussie.
Si larecoder La variable correspond à l'une ou l'autre de ces valeurs, puis nous calculons les prix stop loss et
take profit et modifions l'ordre.

Nous attribuons leTRADE_ACTION_SLTP valeur pour ledemande.action variable pour indiquer que nous
modifions le stop loss et le take profit. Ensuite, nous devrons récupérer le prix d’ouverture de la position que
nous venons d’ouvrir. LePositionSélection() La fonction sélectionnera la position actuellement ouverte sur
le symbole actuel afin que nous puissions récupérer des informations sur la position. Nous récupérons ensuite le
prix d'ouverture de la position en utilisantPositionGetDouble() avec lePOSITION_PRICE_OPEN paramètre, en
attribuant le résultat aupositionOuvertPrix variable.

Si laStopPerte et/ouTirer profit les variables d'entrée sont supérieures à zéro, nous calculons le prix du
stop loss et/ou du take profit en ajoutant ou en soustrayant la valeur pertinente dupositionOuvertPrix
variable, et en attribuant le résultat àdemande.sl oudemande.tp. Si soitdemande.sl oudemande.tp contient
une valeur supérieure à zéro, nous appelons leCommandeEnvoyer() fonction pour modifier notre position avec
les nouveaux prix stop loss et/ou take profit.

Enfin, nous devons définir leglAcheterPlacé etglVendrePlaced variables. Puisque notre ordre d'achat a été
passé avec succès, nous définironsglAcheterPlacé à vrai. Cela empêchera l'ouverture d'un deuxième ordre
d'achat si notre position d'achat est fermée par stop loss ou take profit. Nous avons également
fixéglVendrePlaced à faux, ce qui permet à une position de vente de s'ouvrir si le prix de clôture croise la
moyenne mobile dans la direction opposée.

Version de couverture
La version de couverture de notre simple expert-conseil comporte des modifications substantielles qui sont
nécessaires pour gérer et placer des opérations de couverture. Le programme suit la même structure que celui
ci-dessus, mais les informations de position, l'ouverture, la clôture et la modification des ordres doivent être
traitées différemment.

Le fichier est nomméConseiller expert simple (couverture).mq5, et est situé dans le\MQL5\Experts\
Mql5Book dossier. Les changements pertinents à l'intérieur duSurTick() Les gestionnaires d'événements sont
mis en évidence en gras :

87
Acheteur : Evangelos Papageorgiou (vpapnm@yahoo.gr)
Numéro de transaction : 28J511988U193831A

ETXPERT UNCONSEIL P.ROGRAMMATION POUR METTRADER 5


// Gestionnaire d'événements OnTick()
annuler OnTick()
{
// Structures commerciales
Requête MqlTradeRequest ;
Résultat MqlTradeResult ;
ZeroMemory (demande);

// Moyenne mobile double


ma[];
ArraySetAsSeries(ma,true)
;

int maHandle=iMA(_Symbol,0,MAPeriod,MODE_LWMA,0,PRICE_CLOSE);
CopieBuffer(maHandle,0,0,1,ma);

// Prix de clôture
double clôture[];
ArraySetAsSeries(fe
rmer,true);
CopierFermer(_Symbol,0,0,1,close);

// Récupère les ordres de marché actuels


ulong buyTicket = 0, sellTicket = 0 ;
pour(int i = 0; i < PositionsTotal(); i+
+)
{
ticket principal = PositionGetTicket(i);
PositionSelectByTicket(billet);

si (PositionGetInteger (POSITION_TYPE) == POSITION_TYPE_BUY)


{
glBuyTicket = billet ;
glBuyPlaced = vrai ;
}
sinon if(PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_SELL)
{
glSellTicket = billet ;
glSellPlaced = vrai ;
}
}

Pour récupérer la position actuellement ouverte, nous devrons parcourir le pool de commandes et récupérer le
numéro du ticket de commande. Lepour la boucle itère de 0 jusqu'au nombre de positions ouvertes indiqué par
le
PositionsTotal() fonction, moins un. LePositionGetTicket() La fonction récupère le ticket de commande
pour l’index spécifié dans le pool de commandes. La commande la plus ancienne du pool de commandes a un
indice de 0, tandis que la commande la plus récente a un indice dePositionsTotal() - 1.

88
Acheteur : Evangelos Papageorgiou (vpapnm@yahoo.gr)
Numéro de transaction : 28J511988U193831A

Passation de commande

Une fois le numéro du ticket récupéré, nous utilisons lePositionSelectByTicket()fonction pour sélectionner
la commande, ce qui nous permet ensuite de récupérer des informations sur cette commande.
LePositionGetInteger() fonctionner avec le
TYPE DE POSITION Le paramètre récupère le type de commande, soitPOSITION_TYPE_BUY
ouPOSITION_TYPE_SELL. En fonction du type de commande, nous attribuons le numéro de billet soit à
notreAcheter un billet ouvendre un billet variable locale. Nous fixons également la valeur
deglAcheterPlacé ouglVendrePlaced àvrai, ce qui empêche l'ouverture d'un autre ordre dans le même
sens,

À l’heure actuelle, nous nous attendons à n’avoir qu’un seul ordre d’achat ou de vente ouvert. Plus tard, nous
créerons une méthode qui renverra le nombre total d’ordres d’achat et de vente, ainsi que leurs numéros de
ticket.

// Ordre d'achat ouvert au marché


if(close[0] > ma[0] && glBuyPlaced == false)
{
// Clôture de l'ordre de vente
si (vente de billets > 0)
{
PositionSelectByTicket(sellTicket);

requête.action = TRADE_ACTION_DEAL ;
request.type = ORDER_TYPE_BUY ;
request.symbol = _Symbol;
request.position = vendreTicket ;
request.volume = PositionGetDouble(POSITION_VOLUME);
request.price = SymbolInfoDouble(_Symbol,SYMBOL_ASK);
demande.écart = 50 ;

bool sent = OrderSend (demande, résultat);


}

Pour déterminer si un ordre d'achat au marché a été récemment ouvert, nous vérifions la valeur de
l'ordreglAcheterPlacé variable. S'il est réglé surFAUX, et que le prix de clôture est supérieur à la valeur
moyenne mobile, nous passerons un ordre d'achat au marché.

Avant d’ouvrir un nouvel ordre d’achat au marché, nous devrons clôturer la position de vente existante.
N'oubliez pas que dans notre conseiller expert en compensation, nous pouvons inverser une position en ouvrant
simplement un ordre dans la direction opposée qui est supérieur à notre position actuelle. Cela ne fonctionnera
pas pour les comptes de couverture. Pour clôturer un ordre au marché sur un compte de couverture, nous
devrons passer un ordre dans le sens opposé, avec un volume égal ou inférieur au volume de l'ordre ouvert. Le
numéro de ticket de la commande que nous souhaitons clôturer devra être précisé.

Ledemande.position Ce paramètre permet d'indiquer le numéro de ticket de l'ordre au marché à clôturer. Pour
clôturer un ordre de vente au marché, nous attribuons la valeur du numéro de ticket dans levendre un billet
variable àdemande.position, et attribuez le volume de la commande àdemande.volume. Les paramètres
restants sont les mêmes que ceux que nous utilisons pour passer un ordre d’achat au marché.

// Ouvrir l'ordre d'achat


requête.action = TRADE_ACTION_DEAL ;
request.type = ORDER_TYPE_BUY ;

89
Acheteur : Evangelos Papageorgiou (vpapnm@yahoo.gr)
Numéro de transaction : 28J511988U193831A

ETXPERT UNCONSEIL P.ROGRAMMATION POUR METTRADER 5


request.symbol = _Symbol;demande.position
= 0 ; request.volume = TradeVolume ;
request.price = SymbolInfoDouble(_Symbol,SYMBOL_ASK);
requête.sl = 0 ; demande.tp = 0 ; demande.écart =
50 ; bool sent = OrderSend (demande, résultat);

// Modifier SL/TP
si (result.retcode == TRADE_RETCODE_PLACED || result.retcode ==
TRADE_RETCODE_DONE)
{ requête.action =
TRADE_ACTION_SLTP ;request.position
= result.order;
PositionSelectByTicket(result.order
);
double positionOpenPrice = PositionGetDouble(POSITION_PRICE_OPEN);

if(StopLoss > 0) request.sl = positionOpenPrice - (StopLoss * _Point);

if(TakeProfit > 0) request.tp = positionOpenPrice + (TakeProfit * _Point);

if(request.sl > 0 && request.tp > 0) sent = OrderSend(request,result);

glSellPlaced = faux ;
}
}

Lorsque nous passons un ordre d'achat au marché, nous effaçons ledemande.position variable et définissez
lademande.volume variable par rapport au volume réel des échanges. Ensuite, lorsque nous modifions le prix
stop loss et take profit de l'ordre d'achat nouvellement passé, nous définissonsdemande.position au numéro
de billet de cette commande, qui est stocké dansrésultat.ordre.

Chaque fois que vous travaillez directement avec un ordre de couverture, vous devez définirdemande.position
au numéro du ticket, ou définissez-le sur 0 si vous ouvrez une nouvelle commande.

Résumé
Les deux conseillers experts présentés dans ce chapitre représentent un système de trading simple, utilisant des
données de prix et d'indicateurs, qui alterne entre ordres d'achat et de vente en fonction de la tendance. Un prix
stop loss et take profit peut être défini, et le volume des commandes et la période indicatrice peuvent être
ajustés.

Si tout cela vous semble un peu insurmontable en ce moment, ne vous inquiétez pas. L'exemple d'expert-
conseil ci-dessus montre le processus d'ouverture, de clôture et de modification des ordres dans MQL5 à l'aide
de signaux de trading simples, mais la mise en œuvre réelle de ce code sera cachée dans les classes et fonctions
que nous créerons dans les prochains chapitres. Votre travail en tant que programmeur consistera à vous
concentrer sur la logique de votre système commercial et à laisser les bibliothèques gérer les détails de
passation et de gestion des commandes pour vous.

90
Acheteur : Evangelos Papageorgiou (vpapnm@yahoo.gr)
Numéro de transaction : 28J511988U193831A

Création d'une classe de passation de commande

Chapitre 10 - Création d'une classe de passation de


commandes

Vous avez appris à passer, modifier et clôturer des commandes de tous types à l'aide duCommandeEnvoyer()
fonction. Il est maintenant temps de créer des classes réutilisables pour passer, modifier et clôturer les
commandes. Notre classe de passation de commandes se chargera de remplir leMqlTradeRequest variables,
passer la commande et vérifier lesMqlTradeRésultat variables pour vérifier le résultat de la commande. Tout
ce que vous avez à faire est de déclarer un objet basé sur la classe et de transmettre les paramètres pertinents à
la fonction de placement de commande de l'objet.

Tout d’abord, nous devrons créer un fichier d’inclusion pour contenir nos cours de passation de commandes.
Les fichiers d'inclusion sont stockés dans le\ MQL5\Inclure répertoire par défaut. Nous stockerons nos fichiers
d'inclusion dans un sous-dossier nomméMql5Book. Si vous téléchargez le code source
depuishttp://www.expertadvisorbook.com, les fichiers d'inclusion utilisés dans ce livre seront dans le\
MQL5\Include\Mql5Book dossier.

Le#inclure les directives dans nos fichiers de programme feront référence auMql5Book chemin. Le premier
fichier d'inclusion que nous allons créer est nommécommerce.mqh. Alors le#inclure La directive pour inclure
ce dossier dans nos programmes d’experts-conseils sera :

#include <Mql5Book\Trade.mqh>

En supposant qu'il existe un fichier d'inclusion vide nommécommerce.mqh dans le\MQL5\Include\Mql5Book


dossier, créons notre classe commerciale. La convention pour les noms de classe que nous utiliserons dans ce
livre est un « C » majuscule suivi d'un nom de classe en majuscule. Le nom de notre classe de métier
seraCCommerce.

Notez que certains de nos noms de classes seront identiques aux noms de classes de la bibliothèque standard
MQL5. Nous n'utiliserons pas la bibliothèque standard MQL5 dans ce livre, alors gardez à l'esprit que tout nom
de classe identique trouvé dans leRéférence MQL5 faites référence à la bibliothèque standard, et non aux
classes trouvées dans ce livre.

Notez que leCCommerce la classe que nous créons ci-dessous sera utilisée pour passer des ordres de marché
sur les comptes de position de compensation. Plus loin dans ce chapitre, nous créerons une classe dérivée qui
sera utilisée pour passer des ordres de marché sur des comptes de couverture. Le processus de passation des
commandes en attente est le même quel que soit le type de compte. Nous créerons des classes de placement
de commandes en attente au chapitre 13.

Classe commerciale
Commençons par créer une déclaration de classe pour notre classe commerciale. Nous ajouterons
leMqlTradeRequest etMqlTradeRésultat objets à notre déclaration, car ils seront utilisés dans la plupart de
nos fonctions de classe :

91
Acheteur : Evangelos Papageorgiou (vpapnm@yahoo.gr)
Numéro de transaction : 28J511988U193831A

ETXPERT UNCONSEIL P.ROGRAMMATION POUR METTRADER 5


classe CTrade
{ protégé :
Requête MqlTradeRequest ;

publique:
Résultat MqlTradeResult ;
} ;

Nous avons fait ledemande objetprotégé, puisqu'il n'y a aucune raison pour que le programmeur puisse accéder
audemande membres d’objet en dehors de la classe. Cependant, nous voudrons pouvoir hériter dudemande
objet dans toutes les classes dérivées. Nous le ferons plus tard lorsque nous créerons une sous-classe pour
passer des ordres de couverture. Lerésultat l'objet estpublique, car le programmeur voudra peut-être
accéder aurésultat Objet des membres en dehors de la classe pour déterminer le résultat du dernier échange.

La fonction OpenPosition()
Notre fonction de première classe passera des ordres de marché. Nous nommerons la fonctionPoste libre(),
puisqu'il ouvre une position de marché par opposition à un ordre en attente. Nous allons faire lePoste libre()
fonctionprotégé au lieu depublique. Plus loin dans ce chapitre, nous créerons des fonctions d'assistance
publique pour passer des types spécifiques de commandes.

Voici laPoste libre() déclaration de fonction dans notre déclaration de classe :

classe CTrade
{ protégé :
Requête MqlTradeRequest ;
bool OpenPosition (string pSymbol, ENUM_ORDER_TYPE pType, double pVolume,
double pStop = 0, double pProfit = 0, string pComment = NULL);

publique:
Résultat MqlTradeResult ;
} ;

Le type de retour duPoste libre() la fonction estbouffon. La fonction reviendravrai si la commande a été
passée avec succès, etFAUX si ce n'était pas le cas. Cette fonction comporte six paramètres. Vous les
reconnaîtrez comme les paramètres de requête du chapitre précédent. Toutes nos variables de paramètre de
fonction sont précédées d'un « p » minuscule (abréviation de « paramètre »). Cela indique que la variable est un
paramètre de fonction local à la fonction.

Voici les descriptions desPoste libre() paramètres de fonction :

• pSymbole – Le symbole de la sécurité financière sur laquelle ouvrir l’ordre de marché.

• pType – Le type de commande. Accepte une valeur deENUM_ORDER_TYPE énumération.

• pVolume – Le volume en lots de l’ordre du marché.

• pArrêter – Le prix stop loss. Facultatif.

92
Acheteur : Evangelos Papageorgiou (vpapnm@yahoo.gr)
Numéro de transaction : 28J511988U193831A

Création d'une classe de passation de commande

• pBénéfice – Le prix du take profit. Facultatif.•pCommentaire – Un commentaire de commande.

Facultatif.

Les seuls paramètres requis sont le symbole, le type d'ordre et le volume des échanges. Bien que les paramètres
stop loss et take profit soient fournis, nous ajouterons le stop loss et le take profit à la position une fois la
commande passée. Nous aborderons cela dans le prochain chapitre.

Ensuite, nous définirons la fonction elle-même. Cela se fait sous la déclaration de classe :

bool CTrade::OpenPosition (chaîne pSymbol, ENUM_ORDER_TYPE pType, double pVolume,


double pStop = 0, double pProfit = 0, chaîne pComment = NULL)
{

Remarquez leCCommerce : avantPoste libre(). Le double côlon (::) est leopérateur de résolution de
portée, qui identifie lePoste libre() fonctionner comme faisant partie duCCommerce classe. Le nom de classe
et l’opérateur de résolution de portée doivent être présents avant l’identifiant de la fonction de classe.

Commençons par ajouter du code à notre fonction vide. Nous commencerons par remplir ledemande variables
d'objet :

ZeroMemory (demande);
ZeroMemory (résultat);

requête.action = TRADE_ACTION_DEAL ;
request.symbol = pSymbol;
request.type = pType; request.sl
= pStop ; request.tp = pProfit ;
request.comment = pComment;
request.deviation = déviation ;
request.type_filling = fillType;
request.magic = magicNumber;

Cela devrait vous être familier grâce au chapitre précédent.TRADE_ACTION_DEAL définit l'opération commerciale
comme un ordre de marché. Les paramètres de fonction sont affectés à leursdemande variables.
Ledéviation,type_remplissage etla magie Les variables seront abordées plus loin dans ce chapitre.

Nous devrons ensuite déterminer le volume des échanges. Le but de cette fonction est d'ouvrir une position
nette du type (achat ou vente) et du volume indiqués. S'il y a une position actuellement ouverte dans la direction
opposée, nous devrons la clôturer lors de l'ouverture de notre ordre. Par exemple, si nous ouvrons une position
d'achat de 1 lot et qu'une position de vente de 1 lot est actuellement ouverte, nous devrons ouvrir une position
d'achat de 2 lots pour aboutir à une position d'achat nette de 1 lot.

Pour ce faire, nous devons déterminer s'il existe une position actuellement ouverte sur le symbole sélectionné,
et dans quelle direction. Nous utiliserons lePositionSélection() fonction pour sélectionner la position, si elle
existe. Si une position existe, nous récupérerons le type d'ordre et le volume de la position actuelle :

93
Acheteur : Evangelos Papageorgiou (vpapnm@yahoo.gr)
Numéro de transaction : 28J511988U193831A

ETXPERT UNCONSEIL P.ROGRAMMATION POUR METTRADER 5


double positionVol = 0 ;
position longueType =
WRONG_VALUE ;

si (PositionSelect (pSymbol) == vrai)


{ positionVol = PositionGetDouble(POSITION_VOLUME);
positionType = PositionGetInteger(POSITION_TYPE);
}

Premièrement, nous déclarons undouble variable nomméepositionVol pour maintenir le volume de la


position actuelle. Ensuite, nous déclarons unlong variable nomméetype de position pour conserver le type
de position. Ceci est initialisé à la constanteWRONG_VALUE. La valeur entière deWRONG_VALUE la constante est-1.
Cela place la variable dans un état neutre. La raison pour laquelle nous n'initialisons pastype de position à0
c'est parce que0 est une valeur de type de position valide.

LePositionSélection() La fonction demande s'il y a une position ouverte sur le symbole contenu dans
lepSymbole variable. S'il y a un poste ouvert, lePositionSélection() la fonction renvoie une valeur devrai et
sélectionne la position actuelle pour le traitement. SiPositionSélection() Retourvrai, nous exécutons les
expressions entre parenthèses.

LePositionGetDouble() fonctionner avec lePOSITION_VOLUME Le paramètre renvoie le volume du courant


poste et l'attribue aupositionVol variable. LePositionGetInteger() fonctionner avec leTYPE DE POSITION
Le paramètre renvoie le type de position et l'affecte autype de position variable.

Le type de position est une valeur duENUM_POSITION_TYPE l'énumération, soitPOSITION_TYPE_BUY


ouPOSITION_TYPE_SELL. DepuisPOSITION_TYPE_BUY a une valeur entière de0, c'est pourquoi nous avons
initialisé letype de position variable ci-dessus àWRONG_VALUE, ou-1.

Maintenant que nous disposons d’informations sur la position actuelle, nous devons calculer le volume
d’échanges approprié. Si la position actuelle est une position d'achat et que le type d'ordre au marché tel
qu'identifié par lepType La variable est un ordre de vente (ou vice versa), alors nous devons ajouter le volume
de la position actuelle à la valeur dupVolume variable pour obtenir le volume total des échanges :

if((pType == ORDER_TYPE_BUY && positionType == POSITION_TYPE_SELL)


|| (pType == ORDER_TYPE_SELL && positionType == POSITION_TYPE_BUY))
{ request.volume = pVolume + positionVol;
} sinon request.volume =
pVolume;

Il existe deux opérations booléennes AND (&&) à l'intérieur desi opérateur, séparé par une opération OU (||). Si
le type d'ordre au marché (lepType variable) est un ordre d'achat (ORDER_TYPE_BUY) et le type de poste (letype
de position variable) est une position de vente (POSITION_TYPE_SELL), puis on ajoute la valeur depVolume
paramètre à la valeur dupositionVol variable pour obtenir le volume total des échanges. Celui-ci est attribué
audemande.volume variable. Si le type d’ordre au marché est un ordre de vente et que le type de position est
une position d’achat, il en va de même.

94
Acheteur : Evangelos Papageorgiou (vpapnm@yahoo.gr)
Numéro de transaction : 28J511988U193831A

Création d'une classe de passation de commande

Dans les deux cas, la position existante sera clôturée et le résultat sera une position nette du type indiqué par
lepType variable. S'il n'y a pas de position ouverte, ou si la position actuellement ouverte est du même type que
l'ordre au marché, alors nous attribuons simplement la valeur depVolume audemande.volume variable.

La dernière chose que nous devons faire avant de passer l'ordre est d'attribuer le prix actuel du marché
audemande.prix variable. Cela dépend si le type d’ordre est d’achat ou de vente. S'il s'agit d'un ordre d'achat, le
prix d'ouverture de l'ordre sera le prix Ask actuel. S'il s'agit d'un ordre de vente, le prix d'ouverture de l'ordre
sera le prix Bid actuel :

if(pType == ORDER_TYPE_BUY) request.price = SymbolInfoDouble(pSymbol,SYMBOL_ASK); sinon


if(pType == ORDER_TYPE_SELL) request.price = SymbolInfoDouble(pSymbol,SYMBOL_BID);

Si lapType variable indique un ordre d'achat, leSymboleInfoDouble() La fonction récupère le prix Ask actuel
pour le symbole indiqué par lepSymbole variable. SipType indique un ordre de vente,
alorsSymboleInfoDouble() récupère le prix acheteur actuel.

Il ne nous reste plus qu'à appeler leCommandeEnvoyer() fonction pour passer l’ordre au marché.

OrderSend (demande, résultat);

La gestion des erreurs


Lorsqu'une erreur commerciale se produit, nous devons en informer l'utilisateur afin qu'il puisse prendre les
mesures appropriées. Nous enregistrerons également les informations commerciales pertinentes pour vous
aider au dépannage. Certaines conditions d'erreur (telles que les requotes, les délais d'attente et les erreurs de
connexion) se corrigent automatiquement si nous réessayons simplement de passer la commande.

La première chose à faire est de vérifier le code retour du serveur. Comme vous vous en souviendrez,
leMqlTradeRésultat objet que nous passons auCommandeEnvoyer() La fonction contient le code retour du
serveur dans lerecoder variable.

Codes de retour10008 (TRADE_RETCODE_PLACED) et10009 (TRADE_RETCODE_DONE) indiquent que la transaction


a été effectuée avec succès. Si un autre code est renvoyé par le serveur, cela indique une condition d'erreur
probable. Nous allons créer une fonction pour vérifier le code de retour et évaluer si le code de retour indique
une condition d'erreur ou une passation de commande réussie.

Tout d’abord, nous allons créer une énumération qui sera utilisée comme valeur de retour pour déterminer si
une commande a été passée avec succès. Cette énumération sera définie dans notrecommerce.mqh déposer:

énumération ENUM_CHECK_RETCODE
{
CHECK_RETCODE_OK,
CHECK_RETCODE_ERROR,
} ;

La constanteCHECK_RETCODE_OK indique que l'opération commerciale a réussi, tandis que la


constanteCHECK_RETCODE_ERROR indique que l’opération commerciale a échoué.

95
Acheteur : Evangelos Papageorgiou (vpapnm@yahoo.gr)
Numéro de transaction : 28J511988U193831A

ETXPERT UNCONSEIL P.ROGRAMMATION POUR METTRADER 5


Ensuite, nous allons créer une fonction pour vérifier le code de retour. LeCheckReturnCode() la fonction
utilisera leboîtier de commande opérateurs pour évaluer une liste de constantes de code de retour. En
fonction du code retour passé à la fonction, lechanger l'opérateur renverra l'une des constantes
duENUM_CHECK_RETCODE énumération ci-dessus.

Voici laCheckReturnCode() fonction. Celui-ci sera placé dans lecommerce.mqh déposer:

int CheckReturnCode (uint pRetCode)


{statut int ; switch(pRetCode)
{cas TRADE_RETCODE_DONE :
cas TRADE_RETCODE_DONE_PARTIAL :
cas TRADE_RETCODE_PLACED : cas
TRADE_RETCODE_NO_CHANGES :

statut = CHECK_RETCODE_OK ;
casser;

par défaut : statut = CHECK_RETCODE_ERROR ;


}
retour (statut);
}
LeCheckReturnCode() la fonction accepte un paramètre, unuint variable nomméepRetCode. Celui-ci contient
le code retour duMqlTradeRésultat objet.

Nous déclarons une variable entière nomméestatut, qui détiendra une valeur de notreENUM_CHECK_RETCODE
énumération. Lechanger l'opérateur compare la valeur depRetCode par rapport à une liste de constantes de
code retour indiquées par lecas les opérateurs. Si la valeur depRetCode correspond à l'un descas opérateurs,
l'exécution du programme se poursuivra jusqu'à ce qu'uncasser l'opérateur est joint, ou lechanger l'opérateur
se termine.

Tout d’abord, nous vérifions la réussite de la commande. Nous avons ajouté plusieurs codes de retour qui
indiquent une commande réussie ou une condition de non-erreur. Si lapRetCode la valeur correspond à l'un des
codes de retour de cette liste, lestatut la variable se voit attribuer leCHECK_RETCODE_OK constante. Sinon,
ledéfaut l'étiquette est exécutée et lestatut la variable se voit attribuer leCHECK_RETCODE_ERROR constante.
La valeur destatut est renvoyé au programme et la fonction se termine.

Revenons à notre fonction de placement d'ordres au marché et ajoutons leCheckReturnCode() fonction:

int checkCode = 0; OrderSend (demande,

résultat); checkCode =

CheckReturnCode(result.retcode);

Nous déclarons une variable entière nomméecode de contrôle. Cela contiendra la valeur de retour
duCheckReturnCode() fonction. Nous appelons leCommandeEnvoyer() fonction pour passer la commande.
Puis on passe lerésultat.retcode variable à laCheckReturnCode() fonction et stockez la valeur de retour
danscode de contrôle.

96
Acheteur : Evangelos Papageorgiou (vpapnm@yahoo.gr)
Numéro de transaction : 28J511988U193831A

Création d'une classe de passation de commande

Ensuite, nous vérifierons la valeur decode de contrôle variable pour déterminer si le code retour indique une
transaction réussie ou une condition d'erreur. En cas d'erreur, une alerte sera affichée à l'utilisateur :

si (checkCode == CHECK_RETCODE_ERROR)
{ string errDesc = TradeServerReturnCodeDescription (result.retcode);
Alert("Ordre d'ouverture : Erreur ",result.retcode," -
",errDesc);
LogTradeRequest();
casser;
}

Sicode de contrôle contient la valeur deCHECK_RETCODE_ERROR, nous obtenons une description de l'erreur
du
TradeServerReturnCodeDescription() fonction. Cette fonction est contenue dans
ledescriptiondel'erreur.mqh fichier, qui est installé avec certaines versions de MetaTrader 5, mais est inclus
dans le téléchargement du code source pour plus de commodité. Pour ajouter ce fichier à notre projet, nous
plaçons ceci#inclure directive en haut de notrecommerce.mqh déposer:

#include <errordescription.mqh>
LeTradeServerReturnCodeDescription() La fonction renvoie une chaîne avec une description du code
d'erreur. Ceci est stocké dans leerrDesc variable. LeAlerte() La fonction affiche la fenêtre contextuelle d'alerte
avec le code retour et la description :

Figure 10.1 - LeAlerte boîte de dialogue, affichant un message d'erreur de notrePoste libre() fonction.

LeLogTradeRequest() La fonction imprime simplement le contenu dudemande etrésultat objets au journal à


des fins de dépannage. Si une erreur de trading se produit lors du développement de vos propres systèmes de
trading, examiner attentivement le résultat de cette fonction peut aider à identifier les erreurs de codage :

annuler CTrade :: LogTradeRequest ()


{
Print("MqlTradeRequest - action:",request.action,", comment:",request.comment,
", déviation :",request.déviation,", expiration :",request.expiration,
", magic:",request.magic,", order:",request.order,", position:",request.position,
", position_by:",request.position_by,", price:",request.price,", ls:",request.sl,
", stoplimit:",request.stoplimit,", symbole:",request.symbol,", tp:",request.tp,
", tapez :",request.type,", type_filling:",request.type_filling,

97
Acheteur : Evangelos Papageorgiou (vpapnm@yahoo.gr)
Numéro de transaction : 28J511988U193831A

ETXPERT UNCONSEIL P.ROGRAMMATION POUR METTRADER 5


", type_time:",request.type_time,", volume:",request.volume);
Print("MqlTradeResult - demander :",result.ask,", bid:",result.bid, ",
comment:",result.comment,", deal:",result.deal,",
order:",result .commande,
", prix :",result.price,", request_id :",result.request_id,
", retcode:",result.retcode,", retcode_external:",result.retcode_external,
", volume :", résultat.volume);
}

Ensuite, nous imprimerons les informations de commande dans le journal à des fins de dépannage. Si une erreur
s'est produite, ces informations peuvent être utiles. Nous imprimerons le type de commande, le numéro de
billet, le code de retour et la description, ainsi que le prix de la commande et les prix Bid et Ask actuels.

Nous avons créé une fonction pour vérifier la constante du type de commande et renvoyer une chaîne
descriptive décrivant le type de commande :

chaîne CheckOrderType (ENUM_ORDER_TYPE pType)


{ chaîne type de
commande ;
if(pType == ORDER_TYPE_BUY) orderType = "acheter"; sinon if(pType ==
ORDER_TYPE_SELL) orderType = "vendre"; sinon if(pType ==
ORDER_TYPE_BUY_STOP) orderType = "buy stop"; sinon if(pType ==
ORDER_TYPE_BUY_LIMIT) orderType = "limite d'achat" ; sinon if(pType ==
ORDER_TYPE_SELL_STOP) orderType = "sell stop"; sinon if(pType ==
ORDER_TYPE_SELL_LIMIT) orderType = "limite de vente" ; sinon if(pType ==
ORDER_TYPE_BUY_STOP_LIMIT) orderType = "limite d'arrêt d'achat" ; sinon
if(pType == ORDER_TYPE_SELL_STOP_LIMIT) orderType = "limite d'arrêt de
vente" ; else orderType = "type de commande invalide" ;
return (type de commande);
}

Cette fonction est définie dans lecommerce.mqh déposer. C'est une fonction publique qui peut être utilisée
n'importe où dans notre programme. LepType Le paramètre contient une valeur deENUM_ORDER_TYPE
énumération. Nous utilisonssinon opérateurs pour évaluer la valeur depType et attribuez la valeur de chaîne
appropriée auType de commande variable de chaîne. La chaîne est ensuite renvoyée au programme. Notez que
nous aurions pu utiliser leboîtier de commutation opérateurs ici, mais nous voulions garder cette fonction
simple et elle ne nécessite pas les fonctionnalités avancées duchanger opérateur qui était nécessaire dans
notreCheckReturnCode() fonction.

Nous utiliserons leCheckOrderType() etTradeServerReturnCodeDescription() fonctions pour récupérer


des chaînes décrivant notre type de commande et notre code retour. Ensuite, nous appellerons leImprimer()
fonction pour imprimer les informations commerciales dans le journal :

chaîne orderType = CheckOrderType(pType); chaîne errDesc =

TradeServerReturnCodeDescription (result.retcode);

Print("Ouvrir ",orderType," numéro de commande",result.order,": ",result.retcode," -


",errDesc, ", Volume : ",result.volume,", Prix : ",result.price ,", Enchère :
",result.bid,

98
Acheteur : Evangelos Papageorgiou (vpapnm@yahoo.gr)
Numéro de transaction : 28J511988U193831A

Création d'une classe de passation de commande


", Demandez : ",result.ask);

Si nous examinons le journal des experts, voici ce queImprimer() Le résultat de la fonction ressemblera à un
ordre d'achat au marché réussi :

Ordre d'achat ouvert n° 105624 : 10009 - La demande est terminée, Volume : 0,1,
Prix : 1,31095,
Offre : 1,31076, demande : 1,31095

Enfin, nous évaluerons le code retour pour déterminer s'il faut renvoyer unvrai ouFAUX valeur au programme.
Si la commande a été passée avec succès, nous imprimerons un commentaire sur le graphique :
si (checkCode == CHECK_RETCODE_OK)
{
Commentaire(orderType," position ouverte à ", result.price," sur ",pSymbol);
revenir (vrai);
} sinon return(false);

Si lacode de contrôle la variable contient la valeur deCHECK_RETCODE_OK, nous imprimerons un commentaire


sur le graphique et renverrons une valeur devrai, indiquant que l'opération commerciale a été

Figure 10.2 – Commentaire graphique placé par leCommentaire() fonction.


réussi. Sinon, nous renverrons une valeur deFAUX. La figure 10.2 montre
un commentaire graphique placé sur la réussite de la commande.

Réessayer en cas d'erreur


Nous pouvons rendre notre conseiller expert plus fiable et robuste en réessayant l'opération de trading en
fonction du code retour. Certaines conditions d'erreur telles que les recotations, les hors cotations et les
problèmes de connexion au serveur peuvent se corriger automatiquement si nous réessayons simplement
l'opération de trading.

Pour déterminer s'il faut réessayer l'opération d'échange, nous modifierons notreCheckReturnCode() fonction
pour renvoyer une troisième valeur. Nous définirons une troisième constante dans notreENUM_CHECK_RETCODE
énumération nommée
CHECK_RETCODE_RETRY, ce qui indique que la condition d'erreur définie par le code retour est potentiellement
récupérable :

99
Acheteur : Evangelos Papageorgiou (vpapnm@yahoo.gr)
Numéro de transaction : 28J511988U193831A

ETXPERT UNCONSEIL P.ROGRAMMATION POUR METTRADER 5


énumération ENUM_CHECK_RETCODE
{
CHECK_RETCODE_OK,
CHECK_RETCODE_ERROR,
CHECK_RETCODE_RETRY
} ;

Ajoutons cette constante à notreCheckReturnCode() fonction:

int CheckReturnCode (uint pRetCode)


{statut int ; commutateur (pRetCode)
{cas TRADE_RETCODE_REQUOTE : cas
TRADE_RETCODE_CONNECTION : cas
TRADE_RETCODE_PRICE_CHANGED : cas
TRADE_RETCODE_TIMEOUT :
cas TRADE_RETCODE_PRICE_OFF : cas
TRADE_RETCODE_REJECT :
cas TRADE_RETCODE_ERROR :

statut = CHECK_RETCODE_RETRY ;
casser;

cas TRADE_RETCODE_DONE :
cas TRADE_RETCODE_DONE_PARTIAL :
cas TRADE_RETCODE_PLACED : cas
TRADE_RETCODE_NO_CHANGES :

statut = CHECK_RETCODE_OK ;
casser;

par défaut : statut = CHECK_RETCODE_ERROR ;


}

retour (statut);
}

Nous avons sept constantes de code retour attribuées aux opérateurs de cas surlignés en gras. Nous avons
déterminé que ces erreurs sont susceptibles d'être auto-corrigées. Si lapRetCode paramètre correspond à l’une
de ces constantes, leCHECK_RETCODE_RETRY constante est assignée austatut variable.

Nous devrons modifier notre fonction de placement d’ordres au marché pour ajouter la logique de nouvelle
tentative d’ordre. Nous utiliserons unfaire pendant boucle pour gérer la fonctionnalité de nouvelle tentative
de commande. Le code mis à jour est mis en évidence en gras :

int retryCount = 0 ;int


checkCode = 0;

100
Acheteur : Evangelos Papageorgiou (vpapnm@yahoo.gr)
Numéro de transaction : 28J511988U193831A

Création d'une classe de passation de commande


faire {if(pType == ORDER_TYPE_BUY) request.price = SymbolInfoDouble(pSymbol,SYMBOL_ASK);

sinon if(pType == ORDER_TYPE_SELL) request.price = SymbolInfoDouble(pSymbol,SYMBOL_BID);

OrderSend (demande, résultat); checkCode = CheckReturnCode(result.retcode);

if(checkCode == CHECK_RETCODE_OK) pause ;


autresi (checkCode == CHECK_RETCODE_ERROR)
{ string errDesc = TradeServerReturnCodeDescription (result.retcode);
Alert("Ordre d'ouverture : Erreur ",result.retcode," - ",errDesc);
LogTradeRequest();
casser;
}
autre
{ Print("Erreur de serveur détectée, nouvelle
tentative...");
Sommeil (RETRY_DELAY);
retryCount++;
}
} while(retryCount <
MAX_RETRIES);

Tout d’abord, nous déclarons une variable entière nomméenombre de nouvelles tentatives. Ce sera le
compteur qui gardera une trace du nombre de fois que nous aurons réessayé de passer la commande.
L'intégralité du code de passation de commande et de gestion des erreurs de la section précédente est placé
dans unfaire pendant boucle.

La première chose que nous faisons à l'intérieur dufaire La boucle consiste à récupérer le prix acheteur ou
vendeur actuel. Étant donné que ces prix peuvent changer entre les opérations commerciales, nous
récupérerons toujours le prix le plus actuel avant d'appeler leCommandeEnvoyer() fonction.

Après avoir passé la commande et appelé leCheckReturnCode() fonction, nous examinons lacode de
contrôle variable pour déterminer l’action à entreprendre. Si l'échange réussit, etcode de contrôle contient
leCHECK_RETCODE_OK constante, alors nouscasser hors defaire boucle. Sinon, sicode de contrôle contient
leCHECK_RETCODE_ERROR constante, nous affichons une alerte et sortons de la boucle.

Si lacode de contrôle la variable contient leCHECK_RETCODE_RETRY constante, le code dans leautre


L'opérateur est exécuté. Nous imprimons un message dans le journal indiquant qu'une erreur a été détectée et le
programme réessayera l'opération d'échange. LeDormir() La fonction suspendra temporairement l'exécution
du programme, en utilisant la valeur en millisecondes indiquée par leRETRY_DELAY constante. Puis lenombre de
nouvelles tentatives La variable est incrémentée de 1.

Lealors que l'opérateur à la fin de la boucle vérifie lenombre de nouvelles tentatives variable et compare
la valeur à une constante nomméeMAX_RETRIES. LeMAX_RETRIES constant est le nombre maximum de fois où il
faut réessayer de passer la commande. Sinombre de nouvelles tentatives est inférieur àMAX_RETRIES,
lefaire pendant la boucle s'exécutera à nouveau. Sinon, la boucle se terminera.

LeRETRY_DELAY etMAX_RETRIES les constantes sont définies au début ducommerce.mqh inclure le fichier. Nous
les définissons sur une valeur par défaut raisonnable. Vous pouvez les modifier si vous le souhaitez ou les
remplacer par des variables d'entrée pouvant être modifiées par l'utilisateur.

101
Acheteur : Evangelos Papageorgiou (vpapnm@yahoo.gr)
Numéro de transaction : 28J511988U193831A

ETXPERT UNCONSEIL P.ROGRAMMATION POUR METTRADER 5


#définir MAX_RETRIES 5
#définir RETRY_DELAY 3000

LeMAX_RETRIES constant est défini pour réessayer l’opération d’échange jusqu’à 5 fois. LeRETRY_DELAY est de 3
000 millisecondes, soit 3 secondes. Vous pouvez les modifier si vous le souhaitez et recompiler tous les experts
qui utilisent ce fichier.

Si l’opération commerciale n’a pas abouti même après plusieurs tentatives, nous devons alerter l’utilisateur. Ce
code va juste après notrefaire pendant boucle, avant d'imprimer les informations de commande dans le
journal :

si (retryCount >= MAX_RETRIES)


{ string errDesc = TradeServerReturnCodeDescription (result.retcode);
Alert("Nombre maximal de tentatives dépassé : Erreur
",result.retcode," - ",errDesc);
}

Sinombre de nouvelles tentatives est supérieur ou égal àMAX_RETRIES, nous envoyons une alerte à l'écran
de l'utilisateur indiquant que la commande a échoué, avec le dernier code d'erreur connu et sa description.

Ceci complète lePoste libre() fonction pour leCCommerce classe. Vous pouvez voir le code complet de la
fonction dans le\MQL5\Include\Mql5Book\Trade.mqh déposer.

Définition de l'écart, du nombre magique et du type de remplissage


Si votre courtier utilise le type d'exécution instantanée, vous pouvez spécifier undéviation en points. L'écart est
le glissement maximum autorisé lors de la passation d'une commande. Si le prix actuel du marché est supérieur
au nombre de points spécifié par rapport au prix demandé, la commande ne sera pas passée.

Si vous négociez avec un compte de couverture ou si votre système de trading de compte de compensation
sera utilisé pour échanger plusieurs symboles et que vous avez besoin d'un moyen de distinguer les transactions
effectuées par différents conseillers experts, vous souhaiterez pouvoir définir unnombre magique. Le nombre
magique est un nombre arbitraire, fixé par l'utilisateur, qui est attribué à toutes les commandes passées par un
expert-conseil. Cela sera important lorsque nous gérerons les ordres sur des comptes de couverture.

Ajoutons quelques variables et fonctions de classe à notreCCommerce classe qui sera utilisée pour définir les
valeurs d’écart et de nombre magique. Nous ajouterons également une fonction pour définir le type de
remplissage, au cas où le programmeur souhaite le modifier :

classe CTrade
{ protégé :
tête numéro magique ; déviation
de la tête ;
ENUM_ORDER_TYPE_FILLING
fillType ;

publique:
void MagicNumber(tête pMagic);
void Déviation (tête pDeviation);

102
Acheteur : Evangelos Papageorgiou (vpapnm@yahoo.gr)
Numéro de transaction : 28J511988U193831A

Création d'une classe de passation de commande


void FillType (ENUM_ORDER_TYPE_FILLING pFill);
} ;
Notrenombre magique,déviation etType de remplissage les variables sont définies commeprotégé,
puisque nous n'avons pas besoin de pouvoir accéder à ces valeurs en dehors de notreCCommerce classe.
LeNombre magique(),Déviation() etType de remplissage() les fonctions sontpublique fonctions qui
permettent à l'utilisateur de définir ces valeurs lors de l'initialisation du programme.

Commençons par la fonction qui permet à l'utilisateur de définir l'écart en points. LeCTrade::Déviation() La
fonction définit la valeur dudéviation variable de classe à utiliser dans les fonctions de passation de
commandes :

void CTrade :: Déviation (ulong pDeviation)


{ écart = pDéviation ;
}

LeCTrade::MagicNumber() La fonction définit la valeur dunombre magique variable de classe à utiliser dans
nos fonctions de passation de commandes :

void CTrade :: MagicNumber (tête pMagic)


{numéromagique =
pMagique ;
}

Le type de remplissage peut également être défini si vous souhaitez utiliser un type de remplissage différent de
celui par défaut du serveur :

void CTrade::FillType(ENUM_ORDER_TYPE_FILLING pFill)


{ fillType = pFill ;
}

Le type de remplissage doit être spécifié à l'aide d'une constante de typeENUM_ORDER_TYPE_FILLING


énumération.

Voici comment nous pouvons définir l'écart, le nombre magique et le type de remplissage à l'aide des fonctions
ci-dessus dans nos conseillers experts. Comme les valeurs ne doivent être définies qu'une seule fois,surInit()
Le gestionnaire d'événements est l'endroit idéal pour les définir :

#include <Mql5Book\Trade.mqh>
CCommerce Commerce ;

// Variables d'entrée tête


d'entrée Écart = 300 ; tête
d'entrée MagicNumber = 12345 ;

// Gestionnaire d'événement OnInit()


int OnInit()
{
Commerce.Déviation(Déviation);
Trade.MagicNumber(MagicNumber);

103
Acheteur : Evangelos Papageorgiou (vpapnm@yahoo.gr)
Numéro de transaction : 28J511988U193831A

ETXPERT UNCONSEIL P.ROGRAMMATION POUR METTRADER 5


Trade.FillType(ORDER_FILLING_RETURN);
retour(0);
}

Nous avons deuxsaisir variables permettant à l'utilisateur de définir l'écart et le nombre magique. À l'intérieur
desurInit() gestionnaire d'événements, nous appelons leTrade.Déviation() etTrade.MagicNumber()
fonctions et transmettre les valeurs définies par nos variables d'entrée,Déviation etNombre magique. Dans cet
exemple ci-dessus, nous définissons également le type d’exécution de la commande sur Return.

Utilisation de la fonction OpenPosition()


Simplifions le processus de passation des ordres en créant quelques fonctions d'aide pour placer des positions
d'achat et de vente :

classe
CTrade
{ public :
bool Acheter (chaîne pSymbol, double pVolume, double pStop
= 0, double pProfit = 0, chaîne pComment = NULL); bool Sell
(chaîne pSymbol, double pVolume, double pStop = 0, double
pProfit = 0, chaîne pComment = NULL);
} ;

Nous avons déclaré deux fonctions publiques dans notreCCommerce classe:Acheter() etVendre(). Nous
utiliserons ces classes pour appeler notre protégéPoste libre() fonction. De cette façon, nous n'avons pas
besoin de mémoriser les constantes d'énumération pour les types de commande. Voici les définitions des
fonctions :

bool CTrade::Buy (chaîne pSymbol, double pVolume, double pStop = 0, double pProfit = 0,
chaîne pComment = NULL)
{ bool success = OpenPosition(pSymbol,ORDER_TYPE_BUY,pVolume,pStop,pProfit,pComment);
retour(succès);
}

bool CTrade::Sell (chaîne pSymbol, double pVolume, double pStop = 0, double pProfit = 0,
chaîne pComment = NULL)
{ bool success = OpenPosition(pSymbol,ORDER_TYPE_SELL,pVolume,pStop,pProfit,pComment);
retour(succès);
}

Les deux fonctions appellent lePoste libre() fonction avec la constante de type de commande appropriée.
LeAcheter() la fonction utilise leORDER_TYPE_BUY constante pour le deuxième paramètre duPoste libre()
fonction, tandis que
Vendre() les usagesORDER_TYPE_SELL. Le reste des paramètres d'entrée est affecté aux paramètres
appropriés.Poste libre() paramètres de fonction. Nous utiliserons leAcheter() etVendre() fonctions pour
ouvrir les ordres de marché chez nos conseillers experts.

Revenons au simple conseiller expert que nous avons créé dans le chapitre précédent. Nous remplacerons les
variables de requête et lesCommandeEnvoyer() fonctionner avec leAcheter() etVendre() fonctions que nous
venons de créer :

104
Acheteur : Evangelos Papageorgiou (vpapnm@yahoo.gr)
Numéro de transaction : 28J511988U193831A

Création d'une classe de passation de commande


#include <Mql5Book\Trade.mqh>
CCommerce Commerce ;

// Variables d'entrée entrée


double TradeVolume=0.1;
entrée int StopLoss=1000 ;
entrée int TakeProfit=1000 ;
entrée int MAPeriod=10 ;

// Variables globales bool


glBuyPlaced, glSellPlaced;

// Le gestionnaire
d'événements OnTick() annule
OnTick()
{
// Structures commerciales
Requête MqlTradeRequest ;
Résultat MqlTradeResult ;
ZeroMemory (demande);

// Moyenne mobile double


ma[];
ArraySetAsSeries(ma,true);

int maHandle=iMA(_Symbol,0,MAPeriod,MODE_LWMA,0,PRICE_CLOSE);
CopieBuffer(maHandle,0,0,1,ma);

// Prix de clôture
double clôture[];
ArraySetAsSeries(fermer,true);
CopierFermer(_Symbol,0,0,1,close);

// Informations sur la position actuelle


bool openPosition = PositionSelect(_Symbol);
positionType longue = PositionGetInteger(POSITION_TYPE);

// Ordre d'achat ouvert au marché


if(close[0] > ma[0] && glBuyPlaced == false
&& (positionType != POSITION_TYPE_BUY || openPosition == false))
{
glBuyPlaced = Trade.Buy(_Symbol,TradeVolume);
// Modifier SL/TP
si(glBuyPlaced == vrai)
{ requête.action = TRADE_ACTION_SLTP ;

faire dormir (100); while(PositionSelect(_Symbol) == false);


double positionOpenPrice = PositionGetDouble(POSITION_PRICE_OPEN);

if(StopLoss > 0) request.sl = positionOpenPrice - (StopLoss * _Point);

if(TakeProfit > 0) request.tp = positionOpenPrice + (TakeProfit * _Point);

105
Acheteur : Evangelos Papageorgiou (vpapnm@yahoo.gr)
Numéro de transaction : 28J511988U193831A

ETXPERT UNCONSEIL P.ROGRAMMATION POUR METTRADER 5


if(request.sl > 0 && request.tp > 0) OrderSend(request,result);

glSellPlaced = faux ;
}
}

// Ouvrir un ordre de vente au marché sinon if(close[0] < ma[0] && glSellPlaced ==
false && positionType != POSITION_TYPE_SELL)
{
glSellPlaced = Trade.Sell(_Symbol,TradeVolume);

// Modifier SL/TP
si(glSellPlaced == vrai)
{ requête.action = TRADE_ACTION_SLTP ;

faire dormir (100); while(PositionSelect(_Symbol) == false);


double positionOpenPrice = PositionGetDouble(POSITION_PRICE_OPEN);

if(StopLoss > 0) request.sl = positionOpenPrice + (StopLoss * _Point);

if(TakeProfit > 0) request.tp = positionOpenPrice - (TakeProfit * _Point);

if(request.sl > 0 && request.tp > 0) OrderSend(request,result);

glBuyPlaced = faux ;
}
}

Les changements sont mis en évidence en gras. En haut du programme, nous ajoutons un#inclure directive
visant à inclure lecommerce.mqh fichier qui contient nos cours de métier. Ensuite, nous créons un objet basé sur
leCCommerce classe. Nous nommerons cet objetCommerce.

Passant au code de passation de commande, nous avons remplacé ledemande les variables et
lesCommandeEnvoyer() fonctionner avec notreCommerce.Acheter() etCommerce.Vente() fonctions de
classe. LeglAcheterPlacé etglVendrePlaced les variables booléennes globales contiendront le résultat de
l’opération commerciale. Si la transaction a été effectuée avec succès, la valeur de ces variables seravrai.

Une fois la commande passée, nous vérifions la valeur duglAcheterPlacé ouglVendrePlaced variable. Si la
valeur estvrai, nous calculons le prix du stop loss et du take profit, et les ajoutons à la position. Finalement,
nous définissons le contraireglAcheterPlacé ouglVendrePlaced variable à faux.

A noter que le code de modification de position utilise toujours ledemande etrésultat objets définis plus tôt
dans le programme. Nous n'avons pas besoin d'utiliser ces objets lors de nos commandes, puisque nous
utilisons désormais leCommerce.Acheter() etCommerce.Vente() fonctions, qui contiennent leurs
propresdemande etrésultat objets. Dans le prochain chapitre, nous créerons des fonctions qui simplifieront le
processus de modification de la position et élimineront complètement la nécessité de déclarer ces objets.

Ces modifications se trouvent dans le fichier\MQL5\Experts\Mql5Book\Simple Expert Advisor avec


Functions.mq5. Nous apporterons des modifications à ce programme plusieurs fois dans ce livre.

106
Acheteur : Evangelos Papageorgiou (vpapnm@yahoo.gr)
Numéro de transaction : 28J511988U193831A

Création d'une classe de passation de commande

Classe CTradeHedge
Comme indiqué plus haut dans le chapitre, nous devrons créer une classe distincte pour gérer les ordres de
marché pour les comptes de couverture. Cette classe héritera de certaines méthodes duCCommerce classe que
nous créons, ainsi que surcharger les autres méthodes de cette classe. L'utilisation des deux classes sera
similaire – la seule différence résidera dans les types de retour de ces méthodes. Les méthodes dans
leCCommerce la classe (utilisée pour les comptes de compensation) renverra unbouffon, indiquant si l'échange a
réussi. Les méthodes dans leCTradeHedge la classe, en revanche, renverra untête contenant le numéro du
ticket de commande de la transaction qui vient d'être passée. Nous pouvons stocker ce numéro de ticket pour
pouvoir modifier ultérieurement l'échange.

NotreCTradeHedge la classe comprendra lecommerce.mqh classe que nous avons créée plus tôt, car nous
aurons besoin de la
MqlTradeRequest etMqlTradeRésultat les objets que nous avons déclarés dans cette classe, ainsi que
d'autres fonctions que nous déclarerons plus tard. Commençons par notre déclaration de classe :

#include <Mql5Book\Trade.mqh>

classe CTradeHedge : public


CTrade { protégé :
ulong OpenPosition (chaîne pSymbol, ENUM_ORDER_TYPE pType, double pVolume,
double pStop = 0, double pProfit = 0, chaîne pComment = NULL);
} ;

Lecommerce public qui apparaît après les deux points (:) indique que leCTradeHedge la classe hérite des
fonctions, objets et variables de laCCommerce classe. Dans notreCCommerce classe que nous avons créée
précédemment, nous avons également créé une méthode nomméePoste libre(), avec les mêmes
paramètres auPoste libre() méthode ci-dessus. La différence est que cette méthode renvoie untête au lieu
d'unbouffon. LePoste libre() La méthode de cette classe masque efficacement lePoste libre()
fonctionner dans leCCommerce classe. Ceci est fait pour plus de commodité, afin que le programmeur n'ait pas
besoin d'appeler une méthode différente juste pour passer des ordres de couverture.

Entrons dans les détails duCTradeHedge :: OpenPosition () fonction. Une grande partie des fonctionnalités
est très similaire à celle dePoste libre() fonction que nous avons déclarée plus tôt dans ce chapitre :

ulong CTradeHedge::OpenPosition (chaîne pSymbol, ENUM_ORDER_TYPE pType, double pVolume,


double pStop = 0, double pProfit = 0, chaîne pComment = NULL)
{
ZeroMemory (demande);
ZeroMemory (résultat);

requête.action =
TRADE_ACTION_DEAL ; request.symbol
= pSymbol; request.type = pType;
request.sl = pStop ; request.tp =
pProfit ; request.comment =
pComment;requête.volume =
pVolume ;request.deviation =
déviation ; request.type_filling =

107
Acheteur : Evangelos Papageorgiou (vpapnm@yahoo.gr)
Numéro de transaction : 28J511988U193831A

ETXPERT UNCONSEIL P.ROGRAMMATION POUR METTRADER 5


fillType; request.magic =
magicNumber;

Ceci est similaire audemande champs que nous avons remplis plus tôt dans leCTrade :: OpenPosition ()
fonction, à la différence que nous définissons lademande.volume variable directement. Nous n’avons pas à
craindre d’inverser la position actuelle.

// Boucle de
commande int
retryCount = 0; int
checkCode = 0;

faire { if(pType == ORDER_TYPE_BUY) request.price =


SymbolInfoDouble(pSymbol,SYMBOL_ASK);
sinon if(pType == ORDER_TYPE_SELL) request.price =

SymbolInfoDouble(pSymbol,SYMBOL_BID); bool sent =

OrderSend (demande, résultat); checkCode =

CheckReturnCode(result.retcode);

if(checkCode == CHECK_RETCODE_OK) pause ;


sinon si (checkCode == CHECK_RETCODE_ERROR)
{ string errDesc = TradeServerReturnCodeDescription (result.retcode);
Alert("Ordre d'ouverture : Erreur ",result.retcode," - ",errDesc);
LogTradeRequest();
casser;
}
autr
e
{ Print("Erreur de serveur détectée, nouvelle
tentative...");
Sommeil (RETRY_DELAY);
retryCount++;
}
} while(retryCount <
MAX_RETRIES);

Nous entrerons directement dans la boucle de passation des commandes, puisque nous n’avons pas à nous
soucier de l’état de la position actuelle. Ce code est identique au code de passation de commande précédent.
Nous obtenons soit le prix acheteur ou vendeur actuel, en fonction du type d'ordre. Nous passons la
commande, vérifions lerésultat, et soit consigner une erreur si elle se produit, soit réessayer la commande si
nous déterminons qu'il s'agit d'une erreur récupérable.

si (retryCount >= MAX_RETRIES)


{ string errDesc = TradeServerReturnCodeDescription (result.retcode);
Alert("Nombre maximal de tentatives dépassé : Erreur
",result.retcode," - ",errDesc);
} chaîne orderType =

CheckOrderType(pType);

108
Acheteur : Evangelos Papageorgiou (vpapnm@yahoo.gr)
Numéro de transaction : 28J511988U193831A

Création d'une classe de passation de commande


chaîne errDesc = TradeServerReturnCodeDescription (result.retcode);
Print("Ouvrir ",orderType," order #",result.order,": ",result.retcode," - ",errDesc,",
Volume : ",result.volume,", Prix : ",result.price,", Enchère : ",result.bid,",
Demandez : ",result.ask);

si (checkCode == CHECK_RETCODE_OK)
{ Comment(orderType," position #",result.order," ouvert à ",result.price,
" sur
",pSymbole);return(result.order);
}
sinon return(0);
}

Encore une fois, cela est similaire à notre précédent journal des erreurs et à notre code de valeur de retour. La
seule différence est que nous renvoyons la valeur derésultat.ordre si l'échange a été effectué avec succès.
Lerésultat.ordre La variable contient le numéro du ticket de commande de la position que nous venons de
placer. Si la commande n’a pas été passée avec succès, nous renvoyons une valeur nulle.

Comme précédemment, nous déclarerons deux fonctions d'assistance publique,Acheter() etVendre(), que
nous utiliserons pour passer des ordres d'achat et de vente au marché en utilisant notrePoste libre()
fonction:

classe CTradeHedge : public


CTrade { protégé :
ulong OpenPosition (chaîne pSymbol, ENUM_ORDER_TYPE pType, double pVolume,
double pStop = 0, double pProfit = 0, chaîne pComment = NULL);

publique:
ulong Buy (chaîne pSymbol, double pVolume, double pStop = 0, double pProfit =
0, chaîne pComment = NULL); ulong Sell (chaîne pSymbol, double pVolume, double
pStop = 0, double pProfit = 0, chaîne pComment = NULL);
} ;

Et voici les définitions des fonctions pour leCTradeHedge Acheter() etVendre() les fonctions:

ulong CTradeHedge :: Buy (chaîne pSymbol, double pVolume, double pStop = 0,000000,
double pProfit = 0,000000, chaîne pComment = NULL)
{ ticket ulong = OpenPosition (pSymbol, ORDER_TYPE_BUY, pVolume, pStop, pProfit, pComment);
billet de retour);
}

ulong CTradeHedge :: Sell (chaîne pSymbol, double pVolume, double pStop = 0,000000,
double pProfit = 0,000000, chaîne pComment = NULL)
{ ticket ulong = OpenPosition (pSymbol, ORDER_TYPE_SELL, pVolume, pStop, pProfit, pComment);
billet de retour);
}

109
Acheteur : Evangelos Papageorgiou (vpapnm@yahoo.gr)
Numéro de transaction : 28J511988U193831A

ETXPERT UNCONSEIL P.ROGRAMMATION POUR METTRADER 5


Ceux-ci fonctionnent de la même manière que lesAcheter() etVendre() fonctions que nous avons déclarées
dans notreCCommerce classe plus tôt, à l'exception du fait que ces fonctions renvoient le numéro de ticket de
notre nouvel ordre d'achat ou de vente au marché (ou zéro, si aucun ordre n'a été passé.). Comme pour
notrePoste libre() fonction ci-dessus, ces méthodes surchargent les fonctions portant le même nom dans
notreCCommerce classe.

Utilisation de la classe CTradeHedge


Modifions la version de couverture du simple expert-conseil que nous avons créé dans le dernier chapitre. Nous
devrons d’abord ajouter une référence d’inclusion àTradeHedge.mqh:

#include <Mql5Book\TradeHedge.mqh>
CTradeHedge Trade ;

Nous avons créé unCTradeHedge objet nomméCommerce qui contiendra toutes les fonctions de classe de
CTradeHedge, ainsi que toutes les fonctions et objets accessibles au public à partir duCCommerce classe.
Passons au code de placement d'ordre d'achat dans leSurTick() gestionnaire d'événements pour voir nos
modifications :

// Ouvrir un ordre d'achat au marché


if(close[0] > ma[0] && glBuyPlaced == false)
{
// Clôture de l'ordre de vente
si (vente de billets > 0)
{
PositionSelectByTicket(sellTicket);
requête.action =
TRADE_ACTION_DEAL ; request.type =
ORDER_TYPE_BUY ; request.symbol =
_Symbol; request.position = vendreTicket ;
request.volume = PositionGetDouble(POSITION_VOLUME); request.price
= SymbolInfoDouble(_Symbol,SYMBOL_ASK); demande.écart = 50 ;

bool sent = OrderSend (demande, résultat);


}

// Ouvrir l'ordre d'achat


buyTicket = Trade.Buy(_Symbol,TradeVolume);

// Modifier SL/TP
si(acheter un billet > 0)
{ requête.action = TRADE_ACTION_SLTP ;
demande.position =Acheter un
billet;

PositionSelectByTicket(Acheter un billet);
double positionOpenPrice = PositionGetDouble(POSITION_PRICE_OPEN);

if(StopLoss > 0) request.sl = positionOpenPrice - (StopLoss * _Point);

if(TakeProfit > 0) request.tp = positionOpenPrice + (TakeProfit * _Point);

110
Acheteur : Evangelos Papageorgiou (vpapnm@yahoo.gr)
Numéro de transaction : 28J511988U193831A

Création d'une classe de passation de commande


if(request.sl > 0 && request.tp > 0) sent = OrderSend(request,result);

glSellPlaced = faux ;
}
}

Depuis leCommerce.Acheter() vérifie maintenant la réussite de l'exécution de l'ordre, nous pouvons


simplement utiliser la valeur de retour dansAcheter un billet pour déterminer si une commande a été
passée. SiAcheter un billet est supérieur à zéro, nous procéderons à la modification du prix stop loss et take
profit sur l'ordre. Lors de la modification du stop loss et du take profit
prix, nous fixonsdemande.position à la valeur du ticket de commande enAcheter un billet, et sélectionnez
la commande à l'aide dePositionSelectByTicket() avec la valeur deAcheter un billet pour récupérer le
cours d'ouverture de l'ordre.

Dans les prochains chapitres, nous remplacerons les informations de commande, la modification et le code de
clôture par des fonctions similaires. Vous pouvez visualiser les modifications apportées à ce programme dans
le fichier\MQL5\Experts\Mql5Book\Simple Expert Advisor avec fonctions (Hedging).mq5.

111
Acheteur : Evangelos Papageorgiou (vpapnm@yahoo.gr) ID de transaction : 28J511988U193831A
Acheteur : Evangelos Papageorgiou (vpapnm@yahoo.gr)
Numéro de transaction : 28J511988U193831A

Stopper les pertes et prendre des bénéfices

Chapitre 11 – Stop Loss et Take Profit

Calculer un prix Stop Loss et/ou Take Profit fixe


De nombreuses stratégies de trading placent le stop loss et réalisent des bénéfices à une distance fixe du prix
d'ouverture de l'ordre. Le trader précise le nombre de points pour le stop loss et le take profit dans les
propriétés de l'expert Advisor. Les prix stop loss et take profit sont ensuite calculés par rapport au prix
d'ouverture de l'ordre ou de la position.

Pour un ordre au marché, le prix d'ouverture est le prix acheteur ou vendeur actuel au moment où l'ordre est
exécuté. Une fois la commande passée, nous récupérerons le prix d'ouverture de la position en sélectionnant la
position avec lePositionSélection() ouPositionSelectByTicket() fonctions, puis utilisez
lePositionGetDouble() fonctionner avec lePOSITION_PRICE_OPEN paramètre pour récupérer le prix
d’ouverture. Nous calculons ensuite le stop loss et prenons des bénéfices par rapport à ce prix.

Pour une commande en attente, le cours d'ouverture est le prix que nous précisons lors de l'ouverture de la
commande. Nous calculerons le prix stop loss et take profit par rapport au prix d’ouverture de la commande
avant de passer la commande.

Arrêter les pertes


Pour un ordre d'achat, le stop loss est calculé en soustrayant la valeur du stop loss en points du prix d'ouverture.
Pour un ordre de vente, le stop loss est calculé en ajoutant la valeur du stop loss en points au prix d'ouverture.

Tout d’abord, nous multiplions la valeur du stop loss par la valeur en points du symbole. Par exemple, un
symbole Forex avec cinq chiffres après la virgule aura une valeur en points de 0,00001. Si nous avons spécifié
un stop loss de 500 points, nous multiplions 500 par 0,00001 pour obtenir une valeur de 0,005. Ensuite, nous
ajoutons ou soustrayons cette valeur du prix d'ouverture pour trouver le prix stop loss.

Les exemples ci-dessous supposent que nous négocions sur un compte de compensation. Lorsque vous utilisez
un compte de couverture, remplacezPositionSélectionner(_Symbol) avecPositionSelectByTicket(), en
passant le numéro du ticket de commande comme paramètre. Vous devrez également attribuer le numéro du
ticket de commande audemande.position variable.

Voici un exemple d'ajout d'un stop loss à une position d'achat :

// Variables d'entrée
input int StopLoss =
500 ;

// Gestionnaire d'événements OnTick()


Requête MqlTradeRequest ;
Résultat MqlTradeResult ;

113
Acheteur : Evangelos Papageorgiou (vpapnm@yahoo.gr)
Numéro de transaction : 28J511988U193831A

ETXPERT UNCONSEIL P.ROGRAMMATION POUR METTRADER 5


ZeroMemory (demande);

requête.action =

TRADE_ACTION_SLTP ; request.symbol

= _Symbol;

// Calculer le stop loss pour une position d'achat


PositionSelect(_Symbol);
double positionPrice = PositionGetDouble(POSITION_PRICE_OPEN);

double stopLossPoint = StopLoss * _Point ;

request.sl = positionPrice – stopLossPoint ;

OrderSend (demande, résultat);

LeStopPerte La variable d'entrée est définie au début du programme. Le code qui suit est placé dans
leSurTick() gestionnaire d'événements. Nous supposerons qu'une position d'achat est actuellement ouverte.
Ledemande.action variable identifie cette opération de trading comme une modification de position
(TRADE_ACTION_SLTP), et ledemande.symbole La variable identifie la position à modifier (le symbole actuel du
graphique).

Pour calculer le prix stop loss, nous récupérons d’abord le prix d’ouverture de la position actuelle. Le
PositionSélection() La fonction sélectionne la position ouverte sur le symbole graphique actuel pour un
traitement ultérieur.
Puis lePositionGetDouble() fonctionner avec lePOSITION_PRICE_OPEN Le paramètre renvoie le prix
d'ouverture de la position et le stocke dans la variablepositionPrix.

Ensuite, nous multiplions leStopPerte variable d'entrée par la valeur ponctuelle du symbole (_Indiquer). Le
résultat est stocké dans la variablestopLossPoint. Enfin, on soustraitstopLossPoint depuispositionPrix
pour calculer le prix stop loss et attribuer le résultat aurésultat.sl variable. LeCommandeEnvoyer() La
fonction modifiera alors le stop loss de la position.

Le calcul du stop loss pour une position de vente est presque identique. Nous inversons simplement l’opération
de la soustraction à l’addition :

// Calculer le stop loss pour une position de vente


PositionSelect(_Symbol);
double positionPrice = PositionGetDouble(POSITION_PRICE_OPEN);

double stopLossPoint = StopLoss * _Point ;


demande.sl =positionPrix + stopLossPoint;

114
Acheteur : Evangelos Papageorgiou (vpapnm@yahoo.gr)
Numéro de transaction : 28J511988U193831A

Stopper les pertes et prendre des bénéfices


Pour une commande en attente, le stop loss est calculé avant de passer la commande. Dans cet exemple, nous
fixerons le prix de l'ordre stop d'achat à 100 points au-dessus du prix Ask actuel :

// Variables d'entrée input


int StopLoss = 500 ; entrée
int PendingPrice = 100 ; //
Gestionnaire d'événements
OnTick()
Requête MqlTradeRequest ;
Résultat MqlTradeResult ;
ZeroMemory (demande);

request.action = TRADE_ACTION_PENDING ;
request.type = ORDER_TYPE_BUY_STOP ;
request.symbol = _Symbol;
requête.volume = 1 ;
request.type_time = ORDER_TIME_GTC ;

// Calculer le stop loss pour un ordre d'achat en attente


request.price = SymbolInfoDouble(_Symbol,SYMBOL_ASK) + (PendingPrice * _Point);
double stopLossPoint = StopLoss * _Point ;

request.sl = request.price - stopLossPoint ;

OrderSend (demande, résultat);

Nous avons défini unEn attentePrix variable d'entrée pour calculer le prix de l'ordre en attente par rapport au
prix vendeur actuel. Ledemande.action la variable est définie surTRADE_ACTION_PENDING, indiquant qu'il s'agit
d'une opération de commande en attente. Letype de demande La variable indique qu'il s'agit d'un ordre stop
d'achat. Nous spécifions le symbole, un volume d'échange et le type d'expiration (ORDER_TIME_CGV), indiquant
que la commande en attente n'expire pas.

Le prix de la commande en attente (demande.prix) est calculé en multipliant leEn attentePrix variable
d'entrée par la valeur du point du symbole actuel (_Indiquer), et en l'ajoutant au prix Ask actuel, qui est
récupéré à l'aide duSymboleInfoDouble() fonction. Cela fixe le prix de l’ordre en attente à 100 points au-
dessus du prix vendeur actuel.

LestopLossPoint la valeur est calculée comme avant, et le prix stop loss est calculé en
soustrayantstopLossPoint du prix de la commande en cours (demande.prix). Le résultat est attribué
audemande.sl variable, et la demande d'ordre en attente est ensuite envoyée au serveur commercial.

Regardons le calcul du stop loss pour un ordre de vente en attente :

// Calculer le stop loss pour un ordre de vente en attente


request.price = SymbolInfoDouble(_Symbol,SYMBOL_BID) - (PendingPrice *
_Point);double stopLossPoint = StopLoss * _Point ;request.sl =
request.price + stopLossPoint ;

115
Acheteur : Evangelos Papageorgiou (vpapnm@yahoo.gr)
Numéro de transaction : 28J511988U193831A

ETXPERT UNCONSEIL P.ROGRAMMATION POUR METTRADER 5


Le prix de la commande en attente est calculé en soustrayant(Prix en attente * _Point) à partir du prix
acheteur actuel, et le prix stop loss est calculé en ajoutantstopLossPoint au prix de la commande en cours.

La méthode de calcul d’un stop loss fixe est la même quel que soit le type d’ordre en attente. Pour les ordres
d'achat, le stop loss est placé en dessous du prix de l'ordre en attente, et pour les ordres de vente, le stop loss
est placé au-dessus du prix de l'ordre en attente.

Tirer profit
Le prix de take profit est calculé de la même manière qu’un prix stop loss – mais à l’envers. Le prix take profit
d’un ordre d’achat est placé au-dessus du prix d’ouverture, tandis que le prix take profit d’un ordre de vente est
placé en dessous du prix d’ouverture.

Les exemples ci-dessous supposent que nous négocions sur un compte de compensation. Lorsque vous utilisez
un compte de couverture, remplacezPositionSélectionner(_Symbol) avecPositionSelectByTicket(), en
passant le numéro du ticket de commande comme paramètre. Vous devrez également attribuer le numéro du
ticket de commande audemande.position variable.

Voici le calcul du prix de take profit pour une position d’achat :

// Variables d'entrée input


int TakeProfit = 1000 ;

// Gestionnaire d'événements OnTick()


Requête MqlTradeRequest ;
Résultat MqlTradeResult ;
ZeroMemory (demande);

requête.action = TRADE_ACTION_SLTP ;
request.symbol = _Symbol;

// Calculer le take profit pour une position d'achat


PositionSelect(_Symbol);
double positionPrice = PositionGetDouble(POSITION_PRICE_OPEN);

double takeProfitPoint = TakeProfit * _Point ;

request.tp = positionPrice + takeProfitPoint ;

OrderSend (résultat, demande);

Nous déclarons une variable d'entrée nomméeTirer profit, avec une valeur de profit par défaut de 1 000
points. Le code suivant est similaire à la modification stop loss précédente. Nous sélectionnons la position
actuelle et récupérons d'abord le prix d'ouverture de la position actuelle. Ensuite, nous calculons le prix du take
profit en multipliantTirer profit par
_Indiquer et stocker le résultat dans leprendreProfitPoint variable. Puis on ajouteprendreProfitPoint
àpositionPrix pour calculer le prix du take profit. Le résultat est attribué audemande.tp variable.

116
Acheteur : Evangelos Papageorgiou (vpapnm@yahoo.gr)
Numéro de transaction : 28J511988U193831A

Stopper les pertes et prendre des bénéfices


Et voici le code pour calculer le prix du take profit pour une position de vente :

// Calculer le take profit pour une position de vente


double takeProfitPoint = TakeProfit * _Point;
PositionSelect(_Symbol);
double positionPrice = PositionGetDouble(POSITION_PRICE_OPEN);
demande.tp =positionPrix – takeProfitPoint;
La valeur deprendreProfitPoint est soustrait depositionPrix pour obtenir le prix de take profit.

Pour les commandes en attente, la même logique s’applique. Ajoutez la valeur du take profit au prix de l'ordre en
attente pour un ordre d'achat en attente et soustrayez-la pour un ordre de vente en attente.

Création de fonctions de calcul d'arrêt


Nous allons créer un ensemble de fonctions qui nous permettront de calculer un prix stop loss ou take profit
pour un ordre d'achat ou de vente. De cette façon, il ne sera pas nécessaire de mémoriser le calcul d'un stop
loss ou d'un take profit. Tout ce que vous devez spécifier est le symbole, la valeur du stop loss ou du take profit
en points et éventuellement un prix d'ouverture de l'ordre ou de la position. Ces fonctions fonctionneront à la
fois pour les comptes de couverture et de compensation, nous n'aurons donc pas besoin d'examiner les
différences entre les deux types de comptes.

Commençons par les fonctions stop loss. La fonction permettant de calculer le stop loss pour un ordre d'achat
s'appelleAcheterStopLoss(). Cette fonction se trouve dans lecommerce.mqh inclure le fichier :

double BuyStopLoss (chaîne pSymbol, int pStopPoints, double pOpenPrice = 0)


{if(pStopPoints <= 0) return(0);

double prix d'ouverture ;


if(pOpenPrice > 0) openPrice = pOpenPrice ; sinon
openPrice = SymbolInfoDouble(pSymbol,SYMBOL_ASK);

point double = SymbolInfoDouble(pSymbol,SYMBOL_POINT);


double stopLoss = openPrice - (pStopPoints * point);

chiffres longs = SymbolInfoInteger(pSymbol,SYMBOL_DIGITS);


stopLoss = NormalizeDouble(stopLoss,(int)chiffres);

retour (stopLoss);
}

LeAcheterStopLoss() La fonction accepte trois paramètres : le nom du symbole (pSymbole), la valeur du stop
loss en points (pPoints d'arrêt), et un prix d'ouverture optionnel (pPrixOuvert).

La première ligne de la fonction quittera immédiatement la fonction et renverra une valeur de 0 sipPoints
d'arrêt est inférieur ou égal à 0. Ensuite, nous déclarons undouble variable nomméeprix ouvert, qui
maintiendra le prix d'ouverture de l'ordre. Si le prix d'ouverture de l'ordre est précisé avecpPrixOuvert,

117
Acheteur : Evangelos Papageorgiou (vpapnm@yahoo.gr)
Numéro de transaction : 28J511988U193831A

ETXPERT UNCONSEIL P.ROGRAMMATION POUR METTRADER 5


alorspPrixOuvert sera affecté àprix ouvert. Sinon, le prix Ask actuel pour le symbole spécifié parpSymbole
sera utilisé.

Nous utilisons leSymboleInfoDouble() fonctionner avec leSYMBOL_POINT paramètre pour récupérer la valeur
en points du symbole spécifié par lepSymbole paramètre. Celui-ci est attribué auindiquer variable. Ensuite,
nous calculons le prix stop loss en multipliantpPoints d'arrêt parindiquer, et en soustrayant cette valeur
deprix ouvert. Le résultat est enregistré dans learrêter la perte variable.

La dernière étape consiste à normaliser la valeur du stop loss au nombre de chiffres du prix du symbole. Les
symboles Forex ont trois ou cinq chiffres après la virgule, nous arrondirons donc le stop loss au nombre de
chiffres approprié. LeSymbolInfoInteger() fonctionner avec leSYMBOL_DIGITS Le paramètre récupérera la
valeur des chiffres pourpSymbole, et attribuez-le aulong variablechiffres.

LeNormaliserDouble() la fonction arrondit learrêter la perte valeur au nombre de chiffres spécifié par
lechiffresvariable. Le(int) avant lechiffres variable dans leNormaliserDouble() la fonction convertit
explicitement la valeur dechiffres dans leint taper. Ce n'est pas absolument nécessaire, mais nous l'insérons
pour éviter l'avertissement « perte possible de données due à la conversion de type ». Enfin, nous renvoyons la
valeur normalisée dearrêter la perte au programme.

Examinons la fonction permettant de calculer le prix de take profit pour un ordre d'achat. Le code est presque
identique. Dans ce cas, nous ajoutons simplement la valeur du take profit au prix d’ouverture :

double BuyTakeProfit (chaîne pSymbol, int pProfitPoints, double pOpenPrice = 0)


{ si (pProfitPoints <= 0) return
(0);

double prix d'ouverture ;


if(pOpenPrice > 0) openPrice = pOpenPrice ; sinon
openPrice = SymbolInfoDouble(pSymbol,SYMBOL_ASK);

point double = SymbolInfoDouble(pSymbol,SYMBOL_POINT);double


takeProfit = openPrice + (pProfitPoints * point);

chiffres longs = SymbolInfoInteger(pSymbol,SYMBOL_DIGITS);


takeProfit = NormalizeDouble(takeProfit,(int)digits);

retourner(takeProfit);
}

Le code pour un calcul de stop loss de vente et de take profit est similaire. Dans le cas où aucun prix d’ouverture
n’est spécifié, nous utilisons le prix acheteur actuel. Les calculs du stop loss et du take profit sont inversés par
rapport aux calculs de l'ordre d'achat :

double SellTakeProfit (chaîne pSymbol, int pProfitPoints, double pOpenPrice = 0)


{ si (pProfitPoints <= 0) return
(0);

118
Acheteur : Evangelos Papageorgiou (vpapnm@yahoo.gr)
Numéro de transaction : 28J511988U193831A

Stopper les pertes et prendre des bénéfices


double prix d'ouverture ;
if(pOpenPrice > 0) openPrice = pOpenPrice ;
autreopenPrice =
SymbolInfoDouble(pSymbol,SYMBOL_BID);point double =
SymbolInfoDouble(pSymbol,SYMBOL_POINT);double
takeProfit = openPrice - (pProfitPoints * point) ;

chiffres longs = SymbolInfoInteger(pSymbol,SYMBOL_DIGITS);


takeProfit = NormalizeDouble(takeProfit,(int)digits);

retourner(takeProfit);
}

LeVendreStopLoss() la fonction est similaire – nous ajoutons simplementpProfitPoints au prix d'ouverture.


Vous pouvez visualiser ces fonctions dans lecommerce.mqh inclure le fichier.

Niveau d'arrêt
L’une des erreurs les plus courantes commises par les programmeurs et les traders experts est l’invalidité des
prix stop. Un prix stop loss, take profit ou ordre en attente doit être à une distance minimale des prix acheteur et
vendeur actuels. Cette distance minimale est appelée laniveau d'arrêt. (En pratique, la plupart des courtiers
n'utilisent plus de niveaux d'arrêt, même si vous devriez vérifier s'ils le font.)

Le niveau d'arrêt est récupéré du serveur à l'aide duSymbolInfoInteger() fonctionner avec le


SYMBOL_TRADE_STOPS_LEVEL paramètre. Nous devrons d’abord multiplier cette valeur par la valeur en points
du symbole. Pour calculer le prix stop minimum pour les prix buy take profit, sell stop loss, buy stop et sell limit,
ajoutez simplement le niveau stop au prix Ask actuel :

double stopLevel = SymbolInfoInteger(_Symbol,SYMBOL_TRADE_STOPS_LEVEL) * _Point ;


double minStopLevel = SymbolInfoDouble(_Symbol,SYMBOL_ASK) + stopLevel ;

LeSymbolInfoInteger() fonctionner avec leSYMBOL_TRADE_STOPS_LEVEL Le paramètre renvoie le niveau


d’arrêt du serveur. Le niveau stop est une valeur entière, il doit donc être converti en prix en le multipliant
par_Indiquer. Cette valeur est stockée dans leniveau d'arrêt variable.

Ensuite, leSymboleInfoDouble() fonctionner avec leSYMBOL_ASK Le paramètre renvoie le prix vendeur actuel.
On ajoute la valeur duniveau d'arrêt variable au prix Ask pour obtenir le niveau de stop minimum. Tout
achat, take profit, vente stop loss, achat stop ou prix limite de vente doit être supérieur à cette valeur. Sinon,
unTRADE_RETCODE_INVALID_STOPS le code de retour en résultera.

Pour calculer le prix stop maximum pour les prix de vente, de take profit, d'achat de stop loss, de vente stop ou
d'achat limite, soustrayez simplement le niveau de stop du prix acheteur actuel :

double stopLevel = SymbolInfoInteger(_Symbol,SYMBOL_TRADE_STOPS_LEVEL) * _Point ;


doublemaxStopLevel = SymboleInfoDouble(_Symbol,SYMBOL_BID)– stopLevel ;

119
Acheteur : Evangelos Papageorgiou (vpapnm@yahoo.gr)
Numéro de transaction : 28J511988U193831A

ETXPERT UNCONSEIL P.ROGRAMMATION POUR METTRADER 5


Une fois que vous avez calculé le niveau de stop, vous pouvez le comparer au prix de votre stop loss, de votre
take profit ou de votre ordre en attente pour voir s'il est valide :

double stopLevel = SymbolInfoInteger(_Symbol,SYMBOL_TRADE_STOPS_LEVEL) * _Point ;


double minStopLevel = SymbolInfoDouble(_Symbol,SYMBOL_ASK) + stopLevel ;

if(request.price <= minStopLevel)


{
Print("Prix stop invalide");
}

Cet exemple compare le prix d'un ordre en attente au niveau stop minimum au-dessus du prix Ask actuel. Si
lademande.prix la variable est inférieure ou égale àmonNiveauArrêt, un message s'imprimera dans le journal
indiquant que le prix n'est pas valide.

Une fois que vous connaissez le prix du niveau de stop minimum ou maximum, vous pouvez automatiquement
ajuster un prix invalide en un prix valide, simplement en le rendant supérieur ou inférieur au niveau de stop
concerné.

Création de fonctions de vérification d'arrêt


Avant d'essayer de passer une commande avec notre prix stop loss ou take profit nouvellement calculé, nous
devons vérifier le prix pour nous assurer qu'il n'est pas à l'intérieur du niveau stop ou qu'il n'est pas invalide.
Nous allons créer un ensemble de fonctions pour vérifier les prix des ordres stop et en attente au-dessus du prix
Ask et en dessous du prix Bid. La fonction à utiliser dépend du type de commande et du type de prix que nous
vérifions.

Créons une fonction pour vérifier les prix supérieurs au prix Ask. Ceci sera utilisé pour vérifier les prix des ordres
d'achat, de prise de profit, de vente stop loss, d'achat stop et de vente à cours limité. Si le prix est inférieur au
prix du niveau stop, il sera ajusté de manière à ce qu'il soit supérieur au niveau stop, plus ou moins un nombre
de points spécifié.

double AdjustAboveStopLevel (chaîne pSymbol, double pPrice, int pPoints = 10)


{
double currPrice = SymbolInfoDouble(pSymbol,SYMBOL_ASK);
point double = SymbolInfoDouble(pSymbol,SYMBOL_POINT);
double stopLevel = SymbolInfoInteger(pSymbol,SYMBOL_TRADE_STOPS_LEVEL) *
point ; double stopPrice = currPrice + stopLevel ; double addPoints = pPoints *
point ;

if(pPrice > stopPrice + addPoints) return(pPrice);


autre
{ double newPrice = stopPrice + addPoints ;
Print("Prix ajusté au-dessus du niveau d'arrêt à "+DoubleToString(newPrice));
return(nouveauPrix);
}
}

120
Acheteur : Evangelos Papageorgiou (vpapnm@yahoo.gr)
Numéro de transaction : 28J511988U193831A

Stopper les pertes et prendre des bénéfices


LeAjusterAboveStopLevel() la fonction a trois paramètres :pSymbole,pPrix, qui est le prix stop à vérifier,
etpPoints, un paramètre facultatif qui ajoute un nombre supplémentaire de points au niveau d'arrêt. Cela
garantit que le glissement n’invalidera pas un prix très proche du prix du niveau stop.

Tout d'abord, nous déterminons le prix actuel en récupérant le prix Ask pourpSymbole, et le stocker dans
leCurrPrixvariable. Ensuite, nous récupérons la valeur en points du symbole et la stockons dans leindiquer
variable. Nous récupérons le niveau d'arrêt du symbole et multiplions cette valeur par leindiquer variable. Le
résultat est stocké dans leniveau d'arrêt variable. Nous calculons le prix du niveau stop en ajoutantCurrPrix
àniveau d'arrêt, et stocker cette valeur dans learrêterPrix variable. Ensuite, nous convertissons lepPoints
paramètre à une valeur de prix en le multipliant par la valeur en points du symbole et en stockant le résultat
danspoints supplémentaires.

Si notre prix stop (pPrix), est supérieur au prix du niveau stop (stopPrix + points supplémentaires), nous
rendons l'originalpPrix valeur au programme. Sinon, nous devons calculer un nouveau prix stop. Nous
déclarons une variable nomméenouveau prix, et réglez-le surstopPrice + addPoints. Cela garantit que le
prix stop ajusté n’est pas trop proche du prix du niveau stop. Nous imprimons un message dans le journal
indiquant que le prix a été ajusté. Enfin, nous renvoyons le prix ajusté au programme.

Ensuite, nous allons créer une fonction pour vérifier les prix stop inférieurs au prix Bid. Ceci sera utilisé pour
vérifier les prix du stop de vente, de la limite d'achat, du stop loss d'achat et du take profit de vente. Le code est
similaire auAjusterAboveStopLevel() fonction ci-dessus :

double AdjustBelowStopLevel (chaîne pSymbol, double pPrice, int pPoints = 10)


{
double currPrice =
SymbolInfoDouble(pSymbol,SYMBOL_BID);point double =
SymbolInfoDouble(pSymbol,SYMBOL_POINT);
double stopLevel = SymbolInfoInteger(pSymbol,SYMBOL_TRADE_STOPS_LEVEL) * point ;
double stopPrice = currPrice -
stopLevel ;double addPoints = pPoints *
point ;

if(pPrice < stopPrice - addPoints) return(pPrice);


autre
{double newPrice = stopPrice - addPoints ;
Print("Prix ajusté en dessous du niveau d'arrêt à "+DoubleToString(newPrice));
return(nouveauPrix);
}
}

Les changements sont mis en évidence en gras. Nous attribuons le prix acheteur actuel auCurrPrix variable.
LearrêterPrix la variable est calculée en soustrayantniveau d'arrêt depuisCurrPrix. Si lapPrix le
paramètre est inférieur àarrêterPrix -points supplémentaires, l'originalpPrix la valeur est renvoyée au
programme. Sinon, on calcule un nouveau prix en attribuant le résultat dearrêterPrix -points
supplémentaires aunouveau prix variable. Un message est imprimé dans le journal et lenouveau prix La
variable est renvoyée au programme.

121
Acheteur : Evangelos Papageorgiou (vpapnm@yahoo.gr)
Numéro de transaction : 28J511988U193831A

ETXPERT UNCONSEIL P.ROGRAMMATION POUR METTRADER 5


Les fonctions ci-dessus ajusteront automatiquement un prix invalide. Mais que se passe-t-il si vous souhaitez
simplement vérifier si un prix est valide sans l’ajuster ? Nous utiliserons leCheckAboveStopLevel()
etCheckBelowStopLevel() les fonctions. Ces fonctions renvoient une valeur booléenne devrai ouFAUX, selon
que le prix est valable ou non.

Voici le code duCheckAboveStopLevel() fonction:

bool CheckAboveStopLevel (chaîne pSymbol, double pPrice, int pPoints = 10)


{ double currPrice = SymbolInfoDouble(pSymbol,SYMBOL_ASK);
point double = SymbolInfoDouble(pSymbol,SYMBOL_POINT);
double stopLevel = SymbolInfoInteger(pSymbol,SYMBOL_TRADE_STOPS_LEVEL) *
point ; double stopPrice = currPrice + stopLevel ; double addPoints = pPoints
* point ;

if(pPrice >= stopPrice + addPoints) return(true);


sinon return (faux);
}

La fonction est donc très similaire à celle duAjusterAboveStopLevel() fonction. Les différences sont mises en
évidence en gras. Au lieu d'ajuster lepPrix variable par rapport au prix du niveau stop, nous renvoyons
simplement une valeur devrai si le prix est valide, ouFAUX si ce n'est pas le cas. Cela permet au programmeur
de spécifier d'autres actions si un prix n'est pas valide.

LeCheckBelowStopLevel() La fonction est similaire et toutes les fonctions de vérification d'arrêt de ce chapitre
peuvent être consultées dans le\MQL5\Include\Mql5Book\Trade.mqh déposer.

Utilisation des fonctions de calcul et de vérification d'arrêt


Maintenant que nous avons créé des fonctions pour calculer un stop loss fixe et un prix de take profit, ainsi que
des fonctions pour vérifier les prix, montrons comment nous les utiliserons dans nos programmes de conseillers
experts.

Pour un ordre au marché, le stop loss et le take profit seront calculés après le passage de l'ordre. Nous allons
d’abord récupérer le prix d’ouverture de la position actuelle. Ensuite, le stop loss et le take profit seront calculés
par rapport au prix d'ouverture. Les prix stop loss et take profit calculés seront vérifiés, et enfin l'ordre sera
modifié avec le nouveau prix stop loss et/ou take profit.

Revenons à notre simple programme de conseillers experts pour la compensation des comptes. Nous
remplacerons le calcul du stop loss et du take profit par nos fonctions de calcul du stop. Les prix résultants
seront vérifiés par rapport au niveau stop et ajustés en conséquence :

// Ordre d'achat ouvert au marché


if(close[0] > ma[0] && glBuyPlaced == false
&& (positionType != POSITION_TYPE_BUY || openPosition == false))
{ glBuyPlaced = Trade.Buy(_Symbol,TradeVolume);
// Modifier SL/TP
if(glBuyPlaced == true)

122
Acheteur : Evangelos Papageorgiou (vpapnm@yahoo.gr)
Numéro de transaction : 28J511988U193831A

Stopper les pertes et prendre des bénéfices


{ requête.action = TRADE_ACTION_SLTP ;

PositionSelect(_Symbol);
double positionOpenPrice = PositionGetDouble(POSITION_PRICE_OPEN);

double buyStopLoss = BuyStopLoss(_Symbol,StopLoss,positionOpenPrice);


if(buyStopLoss > 0) request.sl = AdjustBelowStopLevel(_Symbol,buyStopLoss);

double buyTakeProfit = BuyTakeProfit(_Symbol,TakeProfit,positionOpenPrice);

if(buyTakeProfit > 0) request.tp =

AdjustAboveStopLevel(_Symbol,buyTakeProfit);if(request.sl > 0 && request.tp > 0)

OrderSend(request,result);

glSellPlaced = faux ;
}
}

// Ordre de vente ouvert au marché


sinon if(close[0] < ma[0] && glSellPlaced == false && positionType != POSITION_TYPE_SELL)
{ glSellPlaced = Trade.Sell(_Symbol,TradeVolume);

// Modifier SL/TP
if(glSellPlaced == true)
{ requête.action = TRADE_ACTION_SLTP ;

PositionSelect(_Symbol);
double positionOpenPrice = PositionGetDouble(POSITION_PRICE_OPEN);

double sellStopLoss = SellStopLoss(_Symbol,StopLoss,positionOpenPrice);


if(sellStopLoss > 0) request.sl = AdjustAboveStopLevel(_Symbol,sellStopLoss);

double sellTakeProfit = SellTakeProfit(_Symbol,TakeProfit,positionOpenPrice);


if(sellTakeProfit > 0) request.tp =

AdjustBelowStopLevel(_Symbol,sellTakeProfit);if(request.sl > 0 && request.tp > 0)

OrderSend(request,result);

glBuyPlaced = faux ;
}
}

Les changements sont mis en évidence en gras. Tout d’abord, nous calculons les prix du stop loss et du take
profit à l’aide de nos fonctions de calcul du stop. Ensuite, nous utilisons nos fonctions de vérification d'arrêt pour
ajuster le prix si nécessaire. Le prix vérifié est attribué audemande variable.

Examinons de plus près les calculs du stop loss d'achat et du take profit :

123
Acheteur : Evangelos Papageorgiou (vpapnm@yahoo.gr)
Numéro de transaction : 28J511988U193831A

ETXPERT UNCONSEIL P.ROGRAMMATION POUR METTRADER 5


double buyStopLoss = BuyStopLoss(_Symbol,StopLoss,positionOpenPrice);
if(buyStopLoss > 0) request.sl = AdjustBelowStopLevel(_Symbol,buyStopLoss);

double buyTakeProfit = BuyTakeProfit(_Symbol,TakeProfit,positionOpenPrice);


if(buyTakeProfit > 0) request.tp = AdjustAboveStopLevel(_Symbol,buyTakeProfit);

Nous utilisons leAcheterStopLoss() fonction pour calculer le stop loss par rapport àpositionOuvertPrix. Le
résultat est attribué auacheterStopLoss variable. Si la valeur deacheterStopLoss est supérieur à zéro, on
passe leacheterStopLoss valeur pour leAjuster au-dessous du niveau d'arrêt() fonction. Le résultat
est attribué aurésultat.sl variable. On fait la même chose pour le take profit, en utilisant
leAcheterTakeProfit() etAjusterAboveStopLevel() les fonctions.

Les modifications apportées à la version de couverture de notre simple expert-conseil sont similaires.
Remplacez simplement le code de calcul du stop loss et du take profit par le code mis en évidence en gras ci-
dessus. Vous pouvez visualiser les modifications dans les fichiers
Conseiller expert simple avec Functions.mq5 etConseiller expert simple avec fonctions
(couverture).mq5 dans le\MQL5\Experts\Mql5Book dossier.

Les exemples ci-dessus ajusteront automatiquement le prix du stop loss ou du take profit s'il n'est pas valide.
Mais que se passe-t-il si vous souhaitez simplement vérifier si le prix est valable ou non ? L'exemple ci-dessous
vérifie le prix du stop loss sans le modifier :

si (StopLoss > 0)
{ double buyStopLoss = BuyStopLoss(_Symbol,StopLoss,positionOpenPrice);

if(CheckBelowStopLevel(_Symbol,buyStopLoss) == false)
{
Alert("Le prix d'achat stop loss n'est pas valide!");
}
sinon if (buyStopLoss > 0) request.sl = buyStopLoss ;
}

LeCheckBelowStopLevel() La fonction est utilisée pour vérifier si leacheterStopLoss le prix est valide. Si la
fonction renvoieFAUX, une alerte est affichée à l'utilisateur et aucune autre action n'est entreprise. Sinon,
siacheterStopLoss est supérieur à zéro, la valeur deacheterStopLoss est assigné àdemande.sl.

Utiliser un Stop Loss dynamique


Les exemples de ce chapitre utilisent une valeur stop loss fixe. Il existe d'autres moyens de déterminer un prix
stop loss, comme l'utilisation d'une valeur indicatrice ou d'un niveau de support/résistance technique. Lorsqu’on
utilise un prix absolu pour un stop loss ou un take profit, il est nécessaire de le vérifier avant de l’ajouter à la
position actuelle. Nous pouvons facilement le faire en utilisant nos fonctions de vérification d'arrêt.

Dans l'exemple ci-dessous, la variablestopPertePrix maintiendra le prix provisoire que nous souhaitons
utiliser comme stop loss pour une position d'achat. Nous le vérifierons à l'aide duAjuster au-dessous du
niveau d'arrêt() fonction, en s'assurant qu'il se trouve à une distance minimale du prix d'ouverture de

124
Acheteur : Evangelos Papageorgiou (vpapnm@yahoo.gr)
Numéro de transaction : 28J511988U193831A

Stopper les pertes et prendre des bénéfices


l'ordre. LeMinStopPerte La variable d'entrée permet à l'utilisateur d'imposer une distance minimale de stop
loss :

// Variables d'entrée input


int MinStopLoss = 100 ;

// Gestionnaire d'événements OnTick()


double stopLossPrice = LowestLow(_Symbol,_Period,8); request.sl =
AdjustBelowStopLevel(_Symbol,stopLossPrice,MinStopLoss);

Le LowestLow() La fonction récupère le plus bas des 8 dernières barres et attribue le résultat à
stopPertePrix. Nous aborderons cette fonction au chapitre 16. Nous utilisons leAjuster au-dessous du
niveau d'arrêt() fonction pour vérifier la valeur destopPertePrix, et assurez-vous que la distance entre le
prix actuel et le prix stop loss est d'au moins 100 points. Le prix vérifié est enregistré dans lerésultat.sl
variable.

En résumé, si vous devez ajouter un prix stop loss ou take profit à une position, ou si vous devez ouvrir un ordre
en attente, assurez-vous d'abord de vérifier le prix. Des paramètres non valides ou des prix trop proches du prix
acheteur ou vendeur actuel peuvent entraîner des erreurs de « stop invalide ». Ceux-ci peuvent être facilement
évités en intégrant la vérification des prix à vos conseillers experts.

125
Acheteur : Evangelos Papageorgiou (vpapnm@yahoo.gr) ID de transaction : 28J511988U193831A
Acheteur : Evangelos Papageorgiou (vpapnm@yahoo.gr)
Numéro de transaction : 28J511988U193831A

Gestion, modification et clôture de positions

Chapitre 12 - Gestion, modification et clôture des positions

Fonctions d'information sur la position


Nous aurons souvent besoin de connaître le statut du poste actuellement ouvert. Nous aurons peut-être besoin
de savoir quel est le type de position (achat ou vente), si la position est actuellement bénéficiaire ou non, et quel
est le stop loss, le take profit ou le volume actuel.

Pour les comptes de compensation, nous utilisons lePositionSélection() fonction, avec


lePositionGetDouble(),PositionGetInteger() etPositionGetString() fonctions pour récupérer des
informations sur la position actuelle. Voici un exemple du code que nous avons utilisé pour récupérer le type de
position actuel :

bool openPosition = PositionSelect(_Symbol);


positionType longue =
PositionGetInteger(POSITION_TYPE);

LePositionSélection() La fonction sélectionne la position du symbole graphique actuel. Une fois le poste
sélectionné, nous pouvons utiliser lePositionObtenir...() fonctions pour récupérer des informations sur la
position. L'exemple ci-dessus utilisePositionGetInteger() avec leTYPE DE POSITION paramètre pour
renvoyer le type de position. Vous pouvez afficher toutes les constantes de propriété de position pour les
troisPositionObtenir...() fonctions dans leRéférence MQL5 sousConstantes standard... > Constantes de
trading > Propriétés de position.

Le problème avec la récupération des informations de position dans MQL5 est de se rappeler
quelPositionObtenir..() fonction à utiliser avec la constante de propriété de position correcte. Nous allons
créer un ensemble de fonctions faciles à retenir qui récupéreront des informations sur la position actuelle avec
un seul appel de fonction.

Montrons avec une fonction pour récupérer le type d'un poste. LeType de position() La fonction sélectionne
la position actuelle du symbole spécifié. Il renverra ensuite la constante de type de position :

longue PositionType (chaîne pSymbol = NULL)


{ if(pSymbol == NULL) pSymbol = _Symbol;
bool select = PositionSelect(pSymbol);
if(select == true) return(PositionGetInteger(POSITION_TYPE));
sinon return (WRONG_VALUE);
}

LepSymbole Le paramètre est facultatif – s’il n’est pas spécifié, la fonction utilisera le symbole du graphique
actuel. LePositionSélection() La fonction sélectionne la position du symbole spécifié et renvoie une valeur
booléenne ausélectionner variable. Sisélectionner est vrai, lePositionGetInteger() La fonction récupère

127
Acheteur : Evangelos Papageorgiou (vpapnm@yahoo.gr)
Numéro de transaction : 28J511988U193831A

ETXPERT UNCONSEIL P.ROGRAMMATION POUR METTRADER 5


le type de position et le renvoie au programme. Si aucune position n'est ouverte, la fonction renvoie
leWRONG_VALUE constante, ou -1.

Voici une autre fonction qui renvoie le prix d'ouverture de la position. Cette fonction est très similaire à celle ci-
dessus, sauf qu'elle utilise lePositionGetDouble() fonctionner avec lePOSITION_PRICE_OPEN paramètre:

double PositionOpenPrice (chaîne pSymbol = NULL)


{ if(pSymbol == NULL) pSymbol = _Symbol;
bool select = PositionSelect(pSymbol);
if(select == true) return(PositionGetDouble(POSITION_PRICE_OPEN));
sinon return (WRONG_VALUE);
}

Au total, nous disposons de dix fonctions qui renvoient des informations sur la position actuelle, notamment le
volume, l'heure d'ouverture, le stop loss, le take profit, le commentaire, le profit actuel et bien plus encore. Ces
fonctions peuvent être visualisées dans le\MQL5\ Inclure\Mql5Book\Trade.mqh déposer.

Modifions notre simple conseiller expert avec nos fonctions d'information de position :

// Informations sur la position


actuellepositionType longue =
TypeType();

// Ordre d'achat ouvert au marché


if(close[0] > ma[0] && glBuyPlaced == false &&positionType != POSITION_TYPE_BUY)
{ glBuyPlaced = Trade.Buy(_Symbol,TradeVolume);

// Modifier SL/TP
if(glBuyPlaced == true)
{ requête.action = TRADE_ACTION_SLTP ;

faire dormir (100); while(PositionSelect(_Symbol) ==


false);double positionOpenPrice = PositionOpenPrice();

double buyStopLoss = BuyStopLoss(_Symbol,StopLoss,positionOpenPrice);


if(buyStopLoss > 0) request.sl = AdjustBelowStopLevel(_Symbol,buyStopLoss);

double buyTakeProfit = BuyTakeProfit(_Symbol,TakeProfit,positionOpenPrice);

if(buyTakeProfit > 0) request.tp = AdjustAboveStopLevel(_Symbol,buyTakeProfit);

if(request.sl > 0 && request.tp > 0) OrderSend(request,result);

glSellPlaced = faux ;
}
}

128
Acheteur : Evangelos Papageorgiou (vpapnm@yahoo.gr)
Numéro de transaction : 28J511988U193831A

Gestion, modification et clôture de positions


Nous utilisons une seule fonction,Type de position(), pour renvoyer le type de position actuel. Si aucune
position n'est ouverte, la fonction reviendraWRONG_VALUE. Pour cette raison, nous n'avons plus besoin de vérifier
la sortie duPositionSélection() fonction, nous avons donc modifié la condition d'ouverture de la commande.

Une fois la commande passée, nous vérifions la valeur duglAcheterPlacé variable. SiglAcheterPlacé
estvrai, nous procédons à la modification de la commande. LePositionPrixOuvert() la fonction a remplacé
la
PositionGetDouble() fonction de récupération du prix d'ouverture de la position. Puisque nous récupérons
des informations pour le symbole graphique actuel, il n’est pas nécessaire de transmettre un paramètre à l’une
de ces fonctions.

Comptes de couverture
Pour les comptes de couverture, nous devons récupérer les informations de position à l'aide d'un numéro de
ticket. Le
PositionSelectByTicket() La fonction est utilisée pour sélectionner les informations de position pour un
ordre de marché ouvert sur un compte de couverture. Voici un exemple de la façon de récupérer le type de
position pour un ordre de couverture. Nous supposerons que lebillet La variable contient un numéro de ticket
de commande valide :

bool openPosition = PositionSelectByTicket(ticket);


positionType longue =
PositionGetInteger(POSITION_TYPE);

LePositionSelectByTicket() La fonction prend un ticket de commande valide comme paramètre. Si une


position ouverte correspondant au numéro de ticket est trouvée avec succès,
alorsPositionSelectByTicket() Retourvrai. À ce stade, lePositionObtenir...() les fonctions peuvent
être utilisées pour récupérer des informations sur la position de couverture.

Rappelons que dans notre simple expert-conseil en comptes de couverture, nous avons utilisé unpour boucle
pour parcourir le pool de commandes pour récupérer nos tickets de commande. Nous allons créer une classe
qui renverra le nombre de tous les ordres d'achat et de vente ouverts, ainsi que des tableaux contenant leurs
numéros de ticket.

Récupération d'une liste de postes ouverts


Nous allons créer une classe nomméeCPpositions cela nous permettra de récupérer le nombre d'ordres de
marché ouverts d'achat et de vente, ainsi que des tableaux qui contiendront nos numéros de tickets.
LeCPpositions la classe sera située dans leTradeHedge.mqh inclure le fichier.

Commençons par déclarer plusieursprotégé les membres de notre classe qui détiennent nos décomptes de
types de commandes et nos numéros de billets :

classe
CPositions
{ protégé :
head Acheter des
billets[]; tête Vendre des
billets[]; tête Billets[];

129
Acheteur : Evangelos Papageorgiou (vpapnm@yahoo.gr)
Numéro de transaction : 28J511988U193831A

ETXPERT UNCONSEIL P.ROGRAMMATION POUR METTRADER 5


int BuyCount;
int SellCount; int
TotalCount;

void GetOpenPositions(head pMagicNumber = 0); int


ResizeArray(tête &array[]);
} ;

Les tableaux de typetête conservera nos numéros de billets, tandis que leint les variables contiendront notre
nombre de commandes. LeGetOpenPositions() La fonction est une fonction interne qui parcourra le pool de
commandes et sélectionnera les commandes qui correspondent au nombre magique fourni.ResizeArray() est
une autre fonction interne qui simplifiera le processus de redimensionnement de nos tableaux de tickets de
commande.

Déclarons notrepublique fonctions qui seront utilisées pour récupérer nos décomptes de commandes et nos
tickets :

class CPositions
{ protégé : head
BuyTickets[] ; tête Vendre
des billets[]; tête
Billets[];

int BuyCount;
int SellCount; int
TotalCount;

void GetOpenPositions (ulong pMagicNumber = 0);


int ResizeArray(tête &array[]);

publique:
int Acheter(head pMagicNumber);
int Vendre (tête pMagicNumber);
int TotalPositions (ulong pMagicNumber);

void GetBuyTickets(head pMagicNumber, head &pTickets[]);


void GetSellTickets(head pMagicNumber, head &pTickets[]); void
GetTickets(head pMagicNumber, head &pTickets[]);} ;

LeAcheter() etVendre() les fonctions renvoient le nombre total d'ordres d'achat ou de vente ouverts au
marché, tandis que
TotalPositions() renvoie un décompte de tous les ordres de marché ouvert. LepNuméroMagique Le
paramètre est le nombre magique qui a été défini pour cet expert-conseil. Nous avons abordé l'utilisation des
nombres magiques au chapitre 10. Si, pour une raison quelconque, vous n'utilisez pas de nombre magique dans
votre conseiller expert, vous pouvez définir ce paramètre sur zéro.

Voici les organes fonctionnels de notreAcheter(),Vendre() etTotalPositions() les fonctions:

130
Acheteur : Evangelos Papageorgiou (vpapnm@yahoo.gr)
Numéro de transaction : 28J511988U193831A

Gestion, modification et clôture de positions


int CPositions :: Acheter (tête pMagicNumber)
{
GetOpenPositions(pMagicNumber);
retourner
(BuyCount); }

int CPositions :: Vendre (tête pMagicNumber)


{
GetOpenPositions(pMagicNumber);
revenir
(VenteCount); }

int CPositions :: TotalPositions (ulong pMagicNumber)


{
GetOpenPositions(pMagicNumber);
retourner
(CompteTotal); }

Les trois fonctions passent simplement lepNuméroMagique valeur pour leGetOpenPositions() fonction et
renvoie le champ approprié dans le champCPpositions classe. Nous passerons en revue
leGetOpenPositions() fonctionner sous peu.

LeObtenir des billets(),GetSellTickets() etProcurez-vous des billets() les fonctions renvoient un


tableau (passé par référence) qui contient les numéros de ticket d'achat, de vente ou de tous les ordres de
marché :

void CPositions::GetBuyTickets(head pMagicNumber, head &pTickets[])


{
GetOpenPositions(pMagicNumber);
ArrayCopy(pTickets, AcheterTickets);

retour; }

void CPositions::GetSellTickets(ulong pMagicNumber, ulong &pTickets[])


{
GetOpenPositions(pMagicNumber);
ArrayCopy(pTickets, SellTickets);
retour; }

void CPositions::GetTickets(head pMagicNumber, head &pTickets[])


{
GetOpenPositions(pMagicNumber);
ArrayCopy(pTickets, Billets);
retour;
}
Les trois fonctions appellent leGetOpenPositions() fonction et copie le tableau de classe approprié dans
lepBillets[] tableau qui est transmis par référence. En appelant leGetOpenPositions() fonctionne chaque

131
Acheteur : Evangelos Papageorgiou (vpapnm@yahoo.gr)
Numéro de transaction : 28J511988U193831A

ETXPERT UNCONSEIL P.ROGRAMMATION POUR METTRADER 5


fois que nous demandons un nombre de commandes mis à jour ou une liste de numéros de billets, nous veillons
à toujours renvoyer les informations les plus récentes.

Regardons de plus près leGetOpenPositions() fonction:

void CPositions :: GetOpenPositions (ulong pMagicNumber = 0)


{
AcheterCount = 0 ;
Nombre de ventes = 0 ;
CompteTotal = 0 ;

ArrayResize (Acheter des billets, 1);


ArrayInitialize (Acheter des billets, 0);

ArrayResize (Vendre des billets, 1);


ArrayInitialize(SellTickets, 0);

ArrayResize (Billets, 1);


ArrayInitialize (Billets, 0);

pour(int i = 0; i < PositionsTotal(); i++)


{ ticket principal =
PositionGetTicket(i);
PositionSelectByTicket(billet);

if(PositionGetInteger(POSITION_MAGIC) != pMagicNumber && pMagicNumber > 0) continuer ;

si (PositionGetInteger (POSITION_TYPE) == POSITION_TYPE_BUY)


{
AcheterCount++ ;
int arrayIndex = ResizeArray (Acheter des billets);
BuyTickets[arrayIndex] = PositionGetInteger(POSITION_TICKET);
}
sinon if(PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_SELL)
{
SellCount++ ;
int arrayIndex = ResizeArray(SellTickets);
SellTickets[arrayIndex] = PositionGetInteger(POSITION_TICKET);
}

TotalCount++ ; int arrayIndex =


ResizeArray (Billets);
Billets[arrayIndex] = PositionGetInteger(POSITION_TICKET);
}
}
Tout d’abord, nous initialisons les valeurs deAcheterCount,Nombre de ventes etLe compte total à zéro et
redimensionnez leAcheter des billets[],Vendre des tickets[] etDes billets[] tableaux à un seul
élément. Ces tableaux sonttableaux dynamiques, qui sont les plus couramment utilisés dans MQL5 pour

132
Acheteur : Evangelos Papageorgiou (vpapnm@yahoo.gr)
Numéro de transaction : 28J511988U193831A

Gestion, modification et clôture de positions


conserver les données de prix. Les tableaux dynamiques peuvent être redimensionnés et nous devrons les
redimensionner et les initialiser avant utilisation pour éviter les erreurs.

Comme décrit précédemment au chapitre 10, nous devrons parcourir le pool de commandes et récupérer le
ticket de commande pour chaque commande ouverte. Nous utilisonsPositionGetTicket() avec une variable
d'index de base zéro pour récupérer chaque numéro de ticket. Le que nous
utilisonsPositionSelectByTicket() pour sélectionner l’ordre de recherche des informations.
LePositionGetInteger() fonctionner avec lePOSITION_MAGIQUE Le paramètre récupère le numéro magique
de la commande. S'il ne correspond pas au nombre magique que nous avons transmis à la fonction (en utilisant
lepNuméroMagique paramètre), nous le sautons et passons à la commande suivante.

Ensuite, nous vérifions le type de position. Selon le type (achat ou vente), nous incrémentons leAcheterCount
ouNombre de ventes variable et ajoutez le numéro de ticket au tableau. Nous devrons redimensionner le
tableau (si nécessaire – nous l'avons déjà redimensionné une fois au début de la fonction), puis définir le plus
grand index du tableau sur le numéro du ticket. À la fin de la boucle, chaque commande traitée est également
ajoutée auLe compte total domaine ainsi que leDes billets[] tableau.

LeResizeArray() function est une fonction interne qui prend un tableau par référence, ajoute un nouvel
élément du tableau à la fin et renvoie le tableau redimensionné, ainsi que l'index de son plus grand élément :

int CPositions::ResizeArray(ulong &array[])


{
int arrayIndex = 0; si (Taille du
tableau (tableau) > 1)
{
int newSize = ArrayResize(array, ArraySize(array) + 1);
arrayIndex = newSize - 1 ;
}

retourner
arrayIndex ; }

Quand leResizeArray() renvoie la fonction, nous attribuons simplement le numéro de ticket de la commande
actuellement sélectionnée au plus grand élément du tableau redimensionné.

Montrons comment nous pouvons utiliser leCPpositions classe pour accéder aux informations sur les
commandes ouvertes. Le code ci-dessous provient du fichier/Mql5/Experts/Mql5Book/Conseiller expert
simple avec fonctions (couverture).mq5. Tout d’abord, nous devrons créer un objet basé sur
leCPpositions classe dans la section déclarations en haut du programme :

#include <Mql5Book\TradeHedge.mqh>

CTradeHedge Trade ;
CPositions Postes ;

Ensuite, nous ajouterons une variable d'entrée permettant à l'utilisateur de définir un nombre magique :

133
Acheteur : Evangelos Papageorgiou (vpapnm@yahoo.gr)
Numéro de transaction : 28J511988U193831A

ETXPERT UNCONSEIL P.ROGRAMMATION POUR METTRADER 5


entrez int MagicNumber = 12345 ;

Nous devrons définir le nombre magique dans lesurInit() gestionnaire d'événements:

// Gestionnaire d'événement OnInit()


annuler OnInit()
{
Trade.MagicNumber(MagicNumber);
}

Le code ci-dessous se situe en haut de notreSurTick() gestionnaire d'événements, avant le code d'ordre
d'achat et de vente :

tête acheterTicket = 0, vendreTicket = 0 ;

si (Positions.Acheter (MagicNumber) == 1)
{
tête acheter des billets[];
Positions.GetBuyTickets(MagicNumber,
buyTickets); buyTicket = buyTickets[0] glBuyPlaced
= true; }

si (Positions. Vendre (MagicNumber) == 1)


{
tête vendre des billets[];
Positions.GetSellTickets(MagicNumber,
sellTickets); vendreTicket = vendreTickets[0];
glSellPlaced = vrai ; }

Dans cet exemple, nous appelons lePositions.Acheter() ouPositions.Vente() fonctions utilisant


notreNombre magique variable d’entrée que nous avons définie ci-dessus. Si ces fonctions indiquent qu'un
ordre d'achat ou de vente est ouvert, nous déclarons un tableau de typetête et transmettez-le
auPositions.GetBuyTickets() ouPositions.GetSellTickets() fonction. Le tableau renvoyé doit contenir
un ticket de commande valide dans le premier élément du tableau, que nous attribuons auAcheter un billet
ouvendre un billet variable.

Maintenant que les numéros de ticket de toutes les positions ouvertes sont disponibles, vous pouvez obtenir
plus d'informations sur vos positions ouvertes en utilisant les fonctions d'informations sur les positions que nous
allons créer dansTradeHedge.mqh. Vous vous souviendrez peut-être plus tôt dans ce chapitre que nous avons
créé des fonctions d'informations de position dans lecommerce.mqh fichier à utiliser avec les comptes de
compensation. Nous créerons des fonctions surchargées à utiliser avec les comptes de couverture dans la
section suivante.

134
Acheteur : Evangelos Papageorgiou (vpapnm@yahoo.gr)
Numéro de transaction : 28J511988U193831A

Gestion, modification et clôture de positions


Fonctions d'information sur les positions pour les comptes de couverture
Une fois que nous avons un numéro de billet, nous pouvons obtenir des informations sur la commande en
utilisantPositionSelectByTicket(), puis en utilisant lePositionGetDouble(),PositionGetInteger()
etPositionGetString() fonctionne avec les paramètres appropriés. Tout comme nous l'avons fait pour les
comptes de compensation, nous avons créé des fonctions d'information sur les positions à utiliser avec les
comptes de couverture.

Par exemple, cette fonction prend un numéro de ticket en entrée et renvoie le type de commande :

Type de position long (tête pTicket = 0)


{
bool select = faux ;
if(pTicket > 0) select = PositionSelectByTicket(pTicket);

if(select == true) return(PositionGetInteger(POSITION_TYPE));


sinon return (WRONG_VALUE);
}

Nous vérifions la valeur de retour dePositionSelectByTicket(). Si c'estFAUX, on retourne leWRONG_VALUE


constante (-1); sinon, nous renvoyons la valeur dePositionGetInteger() avec leTYPE DE POSITION
paramètre.

Les fonctions d'information sur les positions pour les comptes de couverture se trouvent dansTradeHedge.mqh
et surchargez les fonctions portant le même nom dans le fichiercommerce.mqh déposer.

Création d'une fonction de modification de poste


Dans le chapitre précédent, nous avons utilisé leCommandeEnvoyer() fonction pour ajouter un stop loss et
prendre des bénéfices sur une position. Nous pouvons facilement créer une fonction pour modifier une position,
avec les mêmes fonctionnalités de gestion des erreurs et de nouvelle tentative que nos autres fonctions.
LeModifierPosition() la fonction sera une fonction publique dans notreCCommerce classe. Il comporte trois
paramètres :pSymbole est le symbole de la position à modifier,pArrêter est le prix stop loss, etpBénéfice est
le prix du take profit :

classe
CTrade
{ public :
bool ModifyPosition (string pSymbol, double pStop, double pProfit = 0);
}

LepBénéfice Le paramètre est facultatif. Très probablement, vous modifierez le stop loss d’un ordre, mais pas
toujours le take profit. Voici le corps de notreModifierPosition() fonction. Presque tout le code de cette
fonction devrait déjà vous être familier :

bool CTrade::ModifyPosition (string pSymbol, double pStop, double pProfit=0)

135
Acheteur : Evangelos Papageorgiou (vpapnm@yahoo.gr)
Numéro de transaction : 28J511988U193831A

ETXPERT UNCONSEIL P.ROGRAMMATION POUR METTRADER 5


{
ZeroMemory (demande);
ZeroMemory (résultat);

requête.action =
TRADE_ACTION_SLTP ; request.symbol
= pSymbol; request.sl = pStop ;
request.tp = pProfit ;

// Boucle de
commande int
retryCount = 0; int
checkCode = 0;

faire { bool sent = OrderSend (requête,


résultat); checkCode =
CheckReturnCode(result.retcode);

if(checkCode == CHECK_RETCODE_OK) pause ;


sinon si (checkCode == CHECK_RETCODE_ERROR)
{ string errDesc = TradeServerReturnCodeDescription (result.retcode);
Alert("Modifier la position : Erreur ",result.retcode," - ",errDesc);
LogTradeRequest();
casser;
}
autre
{ Print("Erreur de serveur détectée, nouvelle
tentative...");
Sommeil (RETRY_DELAY);
retryCount++;
}
}
while(retryCount < MAX_RETRIES);

si (retryCount >= MAX_RETRIES)


{ string errDesc = TradeServerReturnCodeDescription (result.retcode);
Alert("Nombre maximal de tentatives dépassé : Erreur
",result.retcode," - ",errDesc);
} chaîne errDesc = TradeServerReturnCodeDescription(result.retcode);

Print("Modifier la position : ",result.retcode," - ",errDesc,", SL : ",request.sl,",


TP:", request.tp,",Bid:",SymbolInfoDouble(pSymbol,SYMBOL_BID) ,", Demander: ",
SymbolInfoDouble(pSymbol,SYMBOL_ASK),", Niveau d'arrêt : ",
SymbolInfoInteger(pSymbol,SYMBOL_TRADE_STOPS_LEVEL));

si (checkCode == CHECK_RETCODE_OK)
{
Comment("Position modifiée sur ",pSymbol,", SL : ",request.sl,", TP : ",request.tp);
revenir (vrai);

136
Acheteur : Evangelos Papageorgiou (vpapnm@yahoo.gr)
Numéro de transaction : 28J511988U193831A

Gestion, modification et clôture de positions


}
sinon return (faux);
}

Voici comment nous utilisons notreModifierPosition() fonctionner dans un programme de conseillers


experts. Après avoir passé un ordre au marché, nous calculons et vérifions les prix du stop loss et du take profit.
Ensuite, nous appelons leModifierPosition() fonction pour modifier la position ouverte :

// Ordre d'achat ouvert au marché


if(close[0] > ma[0] && glBuyPlaced == false && positionType != POSITION_TYPE_BUY)
{ glBuyPlaced = Trade.Buy(_Symbol,TradeVolume);

// Modifier SL/TP if(glBuyPlaced


== true)
{
PositionSelect(_Symbol);
double positionOpenPrice = PositionOpenPrice();

double buyStopLoss = BuyStopLoss(_Symbol,StopLoss,positionOpenPrice); si


(acheterStopLoss > 0)acheterStopLoss = AjusterBelowStopLevel(_Symbol,buyStopLoss);

double buyTakeProfit = BuyTakeProfit(_Symbol,TakeProfit,positionOpenPrice); si


(acheterTakeProfit > 0)acheterTakeProfit =
AjusterAboveStopLevel(_Symbol,buyTakeProfit);

si (acheterStopLoss > 0 || acheterTakeProfit > 0)


Trade.ModifyPosition(_Symbol,buyStopLoss,buyTakeProfit);

glSellPlaced = faux ;
}
}
Nous avons remplacé ledemande.sl etdemande.tp variables avec leacheterStopLoss etacheterTakeProfit
variables. Avant de modifier la position, on vérifie si le acheterStopLoss ouacheterTakeProfit les variables
sont supérieures à zéro. Si c'est le cas, leModifierPosition() fonction de notreCCommerce la classe sera
appelée pour modifier le stop loss et prendre profit de la position actuelle. Les modifications ci-dessus peuvent
être consultées dans le fichierConseiller expert simple avec Functions.mq5 dans leDossier \Experts\
Mql5Book.

Comptes de couverture
LeModifierPosition() la fonction est surchargée dans leTradeHedge.mqh déposer. Il faut un numéro de ticket
au lieu d'un symbole :

bool CTradeHedge::ModifyPosition(tête pTicket, double pStop, double pProfit=0,000000)


{
ZeroMemory (demande);
ZeroMemory (résultat);

137
Acheteur : Evangelos Papageorgiou (vpapnm@yahoo.gr)
Numéro de transaction : 28J511988U193831A

ETXPERT UNCONSEIL P.ROGRAMMATION POUR METTRADER 5


bool select = PositionSelectByTicket(pTicket);
symbole de chaîne =
PositionGetString(POSITION_SYMBOL);

requête.action = TRADE_ACTION_SLTP ;
request.sl = pStop ;
request.tp =
pProfit ;request.position =
pTicket; request.symbol =
symbole ;

// Boucle de
commande int
retryCount = 0; int
checkCode = 0;

faire { bool sent = OrderSend (requête,

résultat); checkCode =

CheckReturnCode(result.retcode);

if(checkCode == CHECK_RETCODE_OK) pause ;


sinon si (checkCode == CHECK_RETCODE_ERROR)
{ string errDesc = TradeServerReturnCodeDescription (result.retcode);
Alert("Modifier la position : Erreur ",result.retcode," - ",errDesc);
LogTradeRequest();
casser;
}
autre
{ Print("Erreur de serveur détectée, nouvelle
tentative...");
Sommeil (RETRY_DELAY);
retryCount++;
}
}
while(retryCount < MAX_RETRIES);

si (retryCount >= MAX_RETRIES)


{ string errDesc = TradeServerReturnCodeDescription (result.retcode);
Alert("Nombre maximal de tentatives dépassé : Erreur
",result.retcode," - ",errDesc);
}

chaîne errDesc = TradeServerReturnCodeDescription (result.retcode);


Imprimer("Modifier la position#",pBillet,": ",result.retcode," - ",errDesc,", SL :
",request.sl,", TP : ",request.tp,", Bid : ",SymbolInfoDouble(symbole,SYMBOL_BID),",
Demandez : ",
SymboleInfoDouble(symbole,SYMBOL_ASK),", Niveau d'arrêt : ",
SymbolInfoInteger(symbole,SYMBOL_TRADE_STOPS_LEVEL));

si (checkCode == CHECK_RETCODE_OK)

138
Acheteur : Evangelos Papageorgiou (vpapnm@yahoo.gr)
Numéro de transaction : 28J511988U193831A

Gestion, modification et clôture de positions


{ Commentaire("Position#",pBillet," modifié sur ",symbole,", SL :
",request.sl,
", TP : ",request.tp); revenir
(vrai);
}
sinon return (faux);
}

LepBillet Le paramètre est le numéro de ticket de l’ordre au marché à modifier. Tout d'abord, nous
sélectionnons la commande à l'aide du
PositionSelectByTicket() fonction. Ensuite, nous obtenons le symbole de commande en
utilisantPositionGetString() avec lePOSITION_SYMBOL paramètre et affectez-le ausymbole variable.
Lesymbole variable permet de définir le symbole de la commande à modifier (à l'aide dedemande.symbole)
ainsi que pour obtenir des informations sur les prix actuels à des fins de journalisation. Ledemande.position La
variable est également définie sur la valeur depBillet.

L'utilisation deCTradeHedge :: ModifierPosition() est presque identique à la fonction du même nom dans
le
CCommerce classe. Voici un exemple de modification d'un ordre d'achat au marché, extrait du fichier \MQL5\
Experts\ Mql5Book\Simple Expert Advisor avec fonctions (Hedging).mq5 . Les différences sont
soulignées en gras :

// Modifier SL/TP
si(acheter un billet > 0)
{
PositionSelectByTicket(acheterTicket);
double positionOpenPrice =PositionOpenPrice(acheterTicket);

double buyStopLoss = BuyStopLoss(_Symbol,StopLoss,positionOpenPrice);

if(buyStopLoss > 0) buyStopLoss = AdjustBelowStopLevel(_Symbol,buyStopLoss);

double buyTakeProfit = BuyTakeProfit(_Symbol,TakeProfit,positionOpenPrice);

if(buyTakeProfit > 0) buyTakeProfit =

AdjustAboveStopLevel(_Symbol,buyTakeProfit);

si (acheterStopLoss > 0 || acheterTakeProfit > 0)


Trade.ModifyPosition(Acheter un billet,acheterStopLoss,acheterTakeProfit);

glSellPlaced = faux ;
}

Positions de clôture

139
Acheteur : Evangelos Papageorgiou (vpapnm@yahoo.gr)
Numéro de transaction : 28J511988U193831A

ETXPERT UNCONSEIL P.ROGRAMMATION POUR METTRADER 5


Comme vous vous en souvenez peut-être du chapitre 10, lorsqu'un ordre au marché est ouvert sur un compte
de compensation, nous vérifions d'abord s'il existe actuellement une position ouverte dans la direction opposée.
Si tel est le cas, nous augmentons la taille du lot de notre ordre pour clôturer la position actuelle, ce qui entraîne
une position nette dans la direction opposée. Cependant, vous souhaiterez parfois clôturer une position avant
d’ouvrir un ordre dans la direction opposée. Nous allons créer une fonction pour clôturer une position ouverte.
Vous pouvez clôturer une partie ou la totalité d’une position, mais nous n’inverserons pas la direction d’une
position ouverte.

Le processus de clôture d’une position est le même que celui d’ouverture. Nous précisonsTRADE_ACTION_DEAL
comme le commerceaction, et remplissez lesymbole,taper,volume etprix variables d'unMqlTradeRequest
objet. Pour notre fonction de clôture de position, nous devrons déterminer le volume et la direction de la
position actuelle. Nous devrons nous assurer que le volume de clôture demandé n'est pas supérieur au volume
de position actuel. Pour clôturer la position, nous devrons passer un ordre dans la direction opposée à la
position actuelle.

LeFermer() la fonction est une fonction publique dans notreCCommerce classe. Il comporte trois paramètres : le
symbole de la position à clôturer, le volume de clôture (facultatif) et un commentaire d'ordre (facultatif).

classe
CTrade
{ public :
bool Close (chaîne pSymbol, double pVolume = 0, chaîne pComment = NULL);
}

Passons en revue le corps de notreFermer() fonction:

bool CTrade::Close (chaîne pSymbol, double pVolume=0,000000, chaîne pComment=NULL)


{
ZeroMemory (demande);
ZeroMemory (résultat);

requête.action = TRADE_ACTION_DEAL ;
request.symbol = pSymbol;
request.deviation = déviation ;
request.type_filling = fillType;
request.magic = magicNumber; double
fermetureVol = 0 ; long openType =
WRONG_VALUE ;

si (PositionSelect (pSymbol) == vrai)


{ closeVol = PositionGetDouble(POSITION_VOLUME);
openType = PositionGetInteger(POSITION_TYPE);
} sinon
return(false);

140
Acheteur : Evangelos Papageorgiou (vpapnm@yahoo.gr)
Numéro de transaction : 28J511988U193831A

Gestion, modification et clôture de positions


Comme toujours, nous remplissons les champs pertinents dans ledemande objet. Nous sélectionnons la position
actuelle et définissons lefermerVol etType ouvert variables sur le volume de la position et le type de
transaction, respectivement.

Ensuite, nous devons déterminer le volume de clôture de la position. Nous vérifions si une valeur a été spécifiée
pour lepVolume paramètre, et s'il est valide :

if(pVolume > closeVol || pVolume <= 0) request.volume = closeVol; sinon


request.volume = pVolume;

Si la valeur depVolume est supérieur au volume de la position actuelle (fermerVol), ou sipVolume n'a pas été
précisé (inférieur ou égal à zéro), alorsdemande.volume se voit attribuer la valeur defermerVol. Cela clôture
toute la position. Sinon, si un volume proche a été spécifié avecpVolume, alorsdemande.volume se voit attribuer
la valeur depVolume. Cela clôturera partiellement la position.

Nous allons maintenant entrer dans notre boucle de commande. Avant de clôturer la position, nous devons
déterminer le type d’ordre et le cours de clôture :

int retryCount = 0 ;
int checkCode = 0;

faire {si (openType ==


POSITION_TYPE_BUY)
{ request.type = ORDER_TYPE_SELL ;
request.price = SymbolInfoDouble(pSymbol,SYMBOL_BID);
}
sinon si (openType == POSITION_TYPE_SELL)
{ request.type = ORDER_TYPE_BUY ;
request.price = SymbolInfoDouble(pSymbol,SYMBOL_ASK);
}bool sent = OrderSend (demande,

résultat);

Le code surligné en gras détermine le nouveau type de commande et le nouveau prix. Si la position actuellement
ouverte est une position d'achat (POSITION_TYPE_BUY), le type de commande est défini surORDER_TYPE_SELL
et le prix de l'ordre est fixé au prix acheteur actuel. Si la position actuelle est une position de vente, le type est
défini surORDER_TYPE_BUY et le prix est fixé au prix Ask actuel. LeCommandeEnvoyer() La fonction ferme
ensuite une partie ou la totalité de la position actuelle.

Le code restant dans la fonction prend en charge les fonctionnalités de gestion des erreurs et de nouvelle
tentative de commande :

checkCode = CheckReturnCode(result.retcode);

if(checkCode == CHECK_RETCODE_OK) pause ;


sinon si (checkCode == CHECK_RETCODE_ERROR)
{ string errDesc = TradeServerReturnCodeDescription (result.retcode);

141
Acheteur : Evangelos Papageorgiou (vpapnm@yahoo.gr)
Numéro de transaction : 28J511988U193831A

ETXPERT UNCONSEIL P.ROGRAMMATION POUR METTRADER 5


Alert("Fermer la position : Erreur ",result.retcode," - ",errDesc);
LogTradeRequest();
casser;
}
autre
{ Print("Erreur de serveur détectée, nouvelle
tentative...");
Sommeil (RETRY_DELAY);
retryCount++;
}
}
while(retryCount < MAX_RETRIES);

si (retryCount >= MAX_RETRIES)


{ string errDesc = TradeServerReturnCodeDescription (result.retcode);
Alert("Nombre maximal de tentatives dépassé : Erreur
",result.retcode," - ",errDesc);
}

chaîne posType ;
if(openType == POSITION_TYPE_BUY) posType = "Acheter";
sinon if(openType == POSITION_TYPE_SELL) posType =
"Vendre";

chaîne errDesc = TradeServerReturnCodeDescription (result.retcode);


Print("Fermer ",posType," position #",result.order,": ",result.retcode," - ",errDesc,",
Volume : ",result.volume,", Prix : ",result.price ,", Enchère : ",result.bid,",
Demandez : ",result.ask);

si (checkCode == CHECK_RETCODE_OK)
{
Commentaire(posType," position fermée sur ",pSymbol," à ",result.price);
revenir (vrai);
}
sinon return (faux);
Voici comment nous utiliserions notreFermer() fonctionner dans un conseiller expert. Par exemple, dans notre
simple conseiller expert, nous souhaitons clôturer l'ordre si le prix de clôture actuel dépasse la moyenne mobile.
Le code pour clôturer un ordre ira dans notre conseiller expert juste après avoir récupéré le type de position en
cours, mais avant les conditions d'ouverture de l'ordre :

positionType longue = TypeType();

if(close[0] < ma[0] && positionType == POSITION_TYPE_BUY)


{
Trade.Close(_Symbol);
}

sinon if(close[0] > ma[0] && positionType == POSITION_TYPE_SELL)


{

142
Acheteur : Evangelos Papageorgiou (vpapnm@yahoo.gr)
Numéro de transaction : 28J511988U193831A

Gestion, modification et clôture de positions


Trade.Close(_Symbol);
}

Par souci de simplicité, nous avons séparé les conditions de clôture d’achat et de vente. Si le prix de clôture
actuel est inférieur à la valeur moyenne mobile actuelle et qu'une position d'achat est ouverte, nous clôturons la
position actuelle. Il en va de même si le prix de clôture est supérieur à la moyenne mobile et qu'une position de
vente est actuellement ouverte.

Vous pouvez utiliser leFermer() fonction pour clôturer une partie d’une position. Voici un exemple simplifié de
la façon dont nous pouvons clôturer la moitié de la position actuelle :

double posVolume = PositionVolume();

double closeVolume = posVolume / 2 ;

Trade.Close(_Symbol,closeVolume);

LePositionVolume() La fonction récupérera le volume de la position actuelle et attribuera le résultat


àpostVolume. Nous divisonspostVolume par 2 pour calculer la moitié de la position et attribuer le résultat
àfermerVolume. Nous utilisonsfermerVolume comme deuxième paramètre deFermer() fonction, clôturant
efficacement la moitié du poste.

Si vous deviez clôturer une partie de la position en utilisant cette méthode, assurez-vous que vos conditions de
clôture sont bien précises. Sinon, le programme continuera à clôturer la moitié de la position encore et encore.
Plus loin dans le livre, nous examinerons l’utilisation des ordres en attente pour augmenter les positions.

Clôture des positions sur les comptes de couverture


La clôture d'une position sur un compte de couverture nécessite le numéro de ticket de l'ordre au marché.
Comme nous l'avons fait avec les autres fonctions de couverture dans ce chapitre, nous allons surcharger la
fonction dans lecommerce.mqh fichier avec une fonction du même nom dansTradeHedge.mqh qui prend un
numéro de ticket au lieu d'un symbole :

bool CTradeHedge :: Fermer (tête pTicket, double pVolume=0,000000, chaîne pComment=NULL)


{
ZeroMemory (demande);
ZeroMemory (résultat);

requête.action =
TRADE_ACTION_DEAL ;request.position =
pTicket;request.deviation = déviation ;
request.type_filling = fillType;

double fermetureVol = 0 ;
long openType =
WRONG_VALUE ; symbole de
chaîne ;

143
Acheteur : Evangelos Papageorgiou (vpapnm@yahoo.gr)
Numéro de transaction : 28J511988U193831A

ETXPERT UNCONSEIL P.ROGRAMMATION POUR METTRADER 5


si(PositionSelectByTicket(pTicket) == vrai)
{closeVol = PositionGetDouble(POSITION_VOLUME);
openType = PositionGetInteger(POSITION_TYPE);
symbole = PositionGetString(POSITION_SYMBOL);
}
sinon return (faux);

//...
}

Par souci de concision, nous montrons uniquement la partie de la fonction qui a changé. La demande de
commande et le code de journalisation sont identiques. Nous fixonsdemande.position au numéro de billet
contenu danspBillet. Ensuite, nous sélectionnons la position en utilisantPositionSelectByTicket(), et
utilisez les différentsPositionObtenir...() fonctions pour récupérer des informations sur cette position, y
compris le volume de la commande, le type de commande et le symbole.

L'utilisation deCTradeHedge :: Fermer () est similaire àCTrade::Fermer(), à l'exception du fait que vous
transmettez un numéro de ticket au lieu d'un symbole.

Utilisation des fonctions d'information de position, de modification et de


clôture
Revenons au Simple Expert Advisor que nous avons créé dans les chapitres précédents. Maintenant que nous
disposons de fonctions capables de gérer le placement, la modification et la clôture des commandes, nous
pouvons remplacer une grande partie du code de ce fichier par nos nouvelles fonctions. Cela se traduira par des
conseillers experts plus faciles à lire, à écrire et à modifier.

Jetons un coup d'œil à nos modificationsConseiller expert simple avec Functions.mq5 déposer:

annuler OnTick()
{
// Moyenne mobile
double ma[];
ArraySetAsSeries(ma,tru
e);

int maHandle=iMA(_Symbol,0,MAPeriod,MODE_SMA,0,PRICE_CLOSE);
CopieBuffer(maHandle,0,0,1,ma);

// Prix de clôture
double clôture[];
ArraySetAsSeries(fermer,true);
CopierFermer(_Symbol,0,0,1,close);

144
Acheteur : Evangelos Papageorgiou (vpapnm@yahoo.gr)
Numéro de transaction : 28J511988U193831A

Gestion, modification et clôture de positions


// Informations sur la position
actuellepositionType longue =
TypeType();

// Ordre d'achat ouvert au marché


if(close[0] > ma[0] && glBuyPlaced == false && positionType != POSITION_TYPE_BUY)
{
glBuyPlaced = Trade.Buy(_Symbol,TradeVolume);

// Modifier SL/TP
if(glBuyPlaced == true)
{
PositionSelect(_Symbol);
double positionOpenPrice = PositionOpenPrice();

double buyStopLoss = BuyStopLoss(_Symbol,StopLoss,positionOpenPrice);


if(buyStopLoss > 0) buyStopLoss = AdjustBelowStopLevel(_Symbol,buyStopLoss);

double buyTakeProfit = BuyTakeProfit(_Symbol,TakeProfit,positionOpenPrice);


if(buyTakeProfit > 0) buyTakeProfit = AdjustAboveStopLevel(_Symbol,buyTakeProfit);

si (acheterStopLoss > 0 || acheterTakeProfit > 0)


Trade.ModifyPosition(_Symbol,buyStopLoss,buyTakeProfit);

glSellPlaced = faux ;
}
}

// Ordre de vente ouvert au


marché // ...
}

Nous avons remplacé le code qui récupère le type de position ouverte et le prix d'ouverture par nos fonctions
d'information sur la position,Type de position() etPositionPrixOuvert(). Notez que nous n'utilisons pas le
Commerce.Fermer() fonctionner dans cet expert-conseil, car notreCommerce.Acheter()
etCommerce.Vente() les fonctions inverseront la position si nécessaire. LeTrade.ModifyPosition() La
fonction est ensuite utilisée pour ajouter un stop loss et prendre des bénéfices à la commande.

Comptes de couverture
Voici les modifications apportées auConseiller expert simple avec fonctions (couverture).mq5:

#include <Mql5Book\TradeHedge.mqh>
CTradeHedge Trade ;
CPositions Postes ;

// Variables d'entréeentrez int


MagicNumber = 12345 ;

145
Acheteur : Evangelos Papageorgiou (vpapnm@yahoo.gr)
Numéro de transaction : 28J511988U193831A

ETXPERT UNCONSEIL P.ROGRAMMATION POUR METTRADER 5


annuler OnInit()
{
Trade.MagicNumber(MagicNumber);
}

annuler OnTick()
{
// Moyenne mobile double ma[];
ArraySetAsSeries(ma,true);

int maHandle=iMA(_Symbol,0,MAPeriod,MODE_LWMA,0,PRICE_CLOSE);
CopieBuffer(maHandle,0,0,1,ma);

// Prix de clôture double


clôture[];
ArraySetAsSeries(fermer,true);
CopierFermer(_Symbol,0,0,1,close);

// Récupère les ordres de marché actuelstête


acheterTicket = 0, vendreTicket = 0 ;

si (Positions.Acheter (MagicNumber) == 1)
{
tête acheter des billets[];
Positions.GetBuyTickets(MagicNumber, buyTickets); buyTicket =
buyTickets[0] glBuyPlaced = true; }

si (Positions. Vendre (MagicNumber) == 1)


{
tête vendre des billets[];
Positions.GetSellTickets(MagicNumber, sellTickets);
vendreTicket = vendreTickets[0]; glSellPlaced = vrai ;
}

// Ordre d'achat ouvert au marché


if(close[0] > ma[0] && glBuyPlaced == false)
{
// Clôture de l'ordre de vente
si (vente de billets > 0)
{
Trade.Close(sellTicket);
}

// Ouvrir l'ordre d'achat


buyTicket = Trade.Buy(_Symbol,TradeVolume);

// Modifier SL/TP si
(buyTicket > 0)
{

146
Acheteur : Evangelos Papageorgiou (vpapnm@yahoo.gr)
Numéro de transaction : 28J511988U193831A

Gestion, modification et clôture de positions


PositionSelectByTicket(acheterTicket);
double positionOpenPrice = PositionOpenPrice(buyTicket);

double buyStopLoss = BuyStopLoss(_Symbol,StopLoss,positionOpenPrice);


if(buyStopLoss > 0) buyStopLoss = AdjustBelowStopLevel(_Symbol,buyStopLoss);

double buyTakeProfit = BuyTakeProfit(_Symbol,TakeProfit,positionOpenPrice);


if(buyTakeProfit > 0) buyTakeProfit = AdjustAboveStopLevel(_Symbol,buyTakeProfit);

si (acheterStopLoss > 0 || acheterTakeProfit > 0)


Trade.ModifyPosition(buyTicket,buyStopLoss,buyTakeProfit);

glSellPlaced = faux ;
}
}

// Ordre de vente ouvert au


marché // ...
}

Nous avons ajouté unCPpositions objet de classe nomméPostes en haut du fichier. Nous avons également
ajouté une variable d'entrée pour un nombre magique. Le numéro magique est attribué à toutes les nouvelles
commandes dans lesurInit() gestionnaire d'événements utilisant leTrade.MagicNumber() fonction.

À l'intérieur deSurTick() gestionnaire d'événements, nous utilisons leCPpositions fonctions de classe


(représentées par lePostes objet) pour récupérer les tickets d’ordre de toutes les positions actuellement
ouvertes. Nous avons ajouté leCommerce.Fermer() fonction pour fermer toute position opposée existante lors
de l’ouverture d’une nouvelle position. LePositionPrixOuvert() La fonction récupère le prix d’ouverture de la
commande pour le numéro de billet spécifié. Finalement, le
Trade.ModifyPosition() La fonction définit le prix stop loss et take profit pour le numéro de ticket spécifié.

147
Acheteur : Evangelos Papageorgiou (vpapnm@yahoo.gr) ID de transaction : 28J511988U193831A
Acheteur : Evangelos Papageorgiou (vpapnm@yahoo.gr)
Numéro de transaction : 28J511988U193831A

Les ordres en attente

Chapitre 13 - Commandes en attente

Un ordre en attente est une demande d'ouverture d'une transaction à un prix spécifié. Le prix de transaction
demandé doit être à une distance minimale du prix acheteur ou vendeur actuel, tel que déterminé par le niveau stop.
Une fois que le prix d'ouverture de l'ordre en attente est atteint, une transaction est conclue et la transaction fait
partie de la position actuelle.

Outre le prix et le volume de la commande, un prix stop loss facultatif, un prix de take profit et un délai d'expiration
de la commande peuvent être spécifiés. Il existe plusieurs types d'expiration pour les commandes en attente.CGV
(bon jusqu'à annulation) signifie que la commande n'expirera pas.Aujourd'hui signifie que l'ordre expirera à la fin de
la journée de négociation.Spécifié oblige le commerçant à spécifier une heure d’expiration. À la date et à l'heure
spécifiées, la commande expirera automatiquement.

Il existe trois types de commandes en attente :arrêt,limite etlimite d'arrêt. En combinaison avec les types d’ordres
d’achat et de vente, il existe au total six types d’ordres en attente. Les ordres stop et limite diffèrent selon l'endroit où
se situe le prix de l'ordre par rapport au prix actuel. Le type d’ordre stop limite combine les deux.

UNacheter arrêter la commande est passée au-dessus du prix actuel. La transaction est conclue lorsque le prix Ask
est supérieur ou égal au prix d'ouverture de l'ordre. UNarrêt de vente la commande est passée en dessous du prix
actuel. La transaction est conclue lorsque le prix Bid est inférieur ou égal au prix d'ouverture de l'ordre. Un ordre stop
est passé dans l’espoir que le prix continuera d’évoluer dans le sens du profit.

UNlimite d'achat la commande est passée en dessous du prix actuel. La transaction est conclue lorsque le prix Ask
est inférieur ou égal au prix d'ouverture de l'ordre. UNlimite de vente la commande est passée au-dessus du prix
actuel. La transaction est conclue lorsque le prix Bid est supérieur ou égal au prix d'ouverture de l'ordre. Un ordre
limité est passé dans l’espoir que le prix s’inversera près du prix de l’ordre limité et continuera dans le sens du profit.

Un ordre stop à cours limité passe un ordre à cours limité lorsqu'un prix d'ordre stop est atteint. Cela nécessite que
le trader saisisse deux prix, un prix d'ordre stop et un prix d'ordre limite. UNacheter une limite d'arrêt L'ordre place
un ordre d'achat limité en dessous du prix actuel lorsque le prix Ask est supérieur ou égal au prix de l'ordre stop.
UNlimite d'arrêt de vente L'ordre place un ordre de vente limité au-dessus du prix actuel lorsque le prix acheteur est
inférieur ou égal au prix de l'ordre stop.

Puisqu’un ordre de vente peut être utilisé pour clôturer une position d’achat, et vice versa, il est possible d’utiliser les
ordres en attente comme ordres stop loss et take profit. Un prix stop loss ou take profit placé sur une position
clôturera la totalité de la position lorsque le prix sera atteint. Mais un ordre en attente peut être utilisé pour clôturer
une partie d’une position, ou même pour inverser une position, lorsque l’ordre en attente est déclenché.

Par exemple, nous avons une position d'achat ouverte pour 1 lot sur l'EURUSD à 1,34500. Si nous passons un ordre
stop de vente 500 points en dessous à 1,34000, nous pouvons clôturer tout ou partie de la position à ce prix. Si nous
passons un ordre stop de vente pour 2 lots à 1,34000, la position s'inversera effectivement d'une position longue
nette de 1 lot à une position de vente nette de 1 lot.

149
Acheteur : Evangelos Papageorgiou (vpapnm@yahoo.gr)
Numéro de transaction : 28J511988U193831A

ETXPERT UNCONSEIL P.ROGRAMMATION POUR METTRADER 5


Nous pouvons utiliser des ordres limités pour sortir d’une position rentable. En utilisant l'exemple ci-dessus, nous
pourrions passer des ordres de vente limités pour 0,5 lot chacun à 1,35000 et 1,35500, en augmentant notre
position à 500 et 1000 points respectivement. Nous parlerons davantage de la réduction des postes plus loin dans
ce chapitre.

La fonction OpenPending()
Tout comme nous l'avons fait pour les ordres au marché, nous allons créer une fonction permettant de passer des
ordres en attente. Les mêmes principes que nous avons évoqués au chapitre 10 s’appliquent également ici. Voici
laOuverten attente() déclaration de fonction dans leCCommerce déclaration de classe :

classe CTrade
{privé :
Requête MqlTradeRequest ;
bool OpenPending (string pSymbol, ENUM_ORDER_TYPE pType, double pVolume, double
pPrice, double pStop = 0, double pProfit = 0, double pStopLimit = 0, datetime
pExpiration = 0, string pComment = NULL);

publique:
Résultat MqlTradeResult ;
} ;

LeOuverten attente() La fonction a deux paramètres supplémentaires.pStopLimit est le prix limite stop de
l'ordre, etpExpiration est le délai d'expiration de la commande. Les deux paramètres sont facultatifs, avec une
valeur par défaut de 0. Les autres paramètres sont identiques auxPoste libre() paramètres, que nous avons
abordés à la page 98. Nous avons déclaré leOuverten attente() fonctionne commeprivé, car nous allons créer
des fonctions publiques pour y accéder.

Passons en revue le corps duOuverten attente() fonction. Nous allons d'abord remplir ledemande variables
d'objet avec nos paramètres de fonction :

bool CTrade::OpenPending (string pSymbol, ENUM_ORDER_TYPE pType, double pVolume, double


pPrice, double pStop = 0, double pProfit = 0, double pStopLimit – 0,
datetime pExpiration = 0, chaîne pComment = NULL)
{ request.action = TRADE_ACTION_PENDING ;
request.symbol = pSymbol; request.type =
pType; request.sl = pStop ; request.tp =
pProfit ; request.comment = pComment;
request.price = pPrice ; requête.volume =
pVolume ; request.stoplimit = pStopLimit ;
LeTRADE_ACTION_PENDING constante indique que nous passons une commande en attente. Le restedemande les
variables reçoivent les paramètres qui sont transmis à la fonction. Ensuite, nous devrons déterminer le délai
d'expiration, le cas échéant :

si (pExpiration > 0)
{ request.expiration = pExpiration ;
request.type_time = ORDER_TIME_SPECIFIED ;
} sinon request.type_time =
ORDER_TIME_GTC ;

150
Acheteur : Evangelos Papageorgiou (vpapnm@yahoo.gr)
Numéro de transaction : 28J511988U193831A

Les ordres en attente


Si lapExpiration paramètre a été spécifié, la valeur depExpiration est affecté au
demande.expiration variable, et lerequête.type_time la variable est définie surORDER_TIME_SPECIFIED, ce qui
indique qu'un délai d'expiration a été spécifié. SipExpiration n'a pas été spécifié ou a une valeur de 0,
alorsrequête.type_time est réglé surORDER_TIME_CGV, ce qui indique que la commande n'expire pas.

Ensuite, nous passerons la commande en attente. Voici le code de passation de commande, avec le code de gestion
des erreurs et réessayez en cas d'erreur :

// Boucle de commande
int retryCount = 0;
int checkCode = 0;

faire {
OrderSend (demande, résultat);
checkCode = CheckReturnCode(result.retcode);

if(checkCode == CHECK_RETCODE_OK) pause ; sinon


si (checkCode == CHECK_RETCODE_ERROR)
{ string errDesc = TradeServerReturnCodeDescription (result.retcode);
Alert("Ouverture de commande en attente : Erreur ",result.retcode," -
",errDesc); casser;
} autre
{ Print("Erreur de serveur détectée, nouvelle
tentative...");
Sommeil (RETRY_DELAY); retryCount+
+;
}
}
while(retryCount < MAX_RETRIES);
Ceci est similaire au code dans lePoste libre() fonction, sauf que nous n'avons pas besoin de vérifier le prix
acheteur ou vendeur actuel. Voici le code restant dans leOuverten attente() fonction:

si (retryCount >= MAX_RETRIES)


{ string errDesc = TradeServerReturnCodeDescription (result.retcode);
Alert("Nombre maximal de tentatives dépassé : Erreur
",result.retcode," - ",errDesc);
}

chaîne orderType = CheckOrderType(pType); chaîne errDesc =


TradeServerReturnCodeDescription (result.retcode);

Print("Ouvrir ",orderType," order #",result.order,": ",result.retcode," - ",errDesc,


", Volume : ",result.volume,", Prix : ",request.price,
", Enchère : ",SymbolInfoDouble(pSymbol,SYMBOL_BID),
", Demandez : ",SymbolInfoDouble(pSymbol,SYMBOL_ASK),
", SL : ",request.sl,", TP : ",request.tp,", Limite d'arrêt : ",request.stoplimit,
", Expiration : ", request.expiration);

si (checkCode == CHECK_RETCODE_OK)
{

151
Acheteur : Evangelos Papageorgiou (vpapnm@yahoo.gr)
Numéro de transaction : 28J511988U193831A

ETXPERT UNCONSEIL P.ROGRAMMATION POUR METTRADER 5


Commentaire(orderType," commande ouverte à ",request.price," sur ",pSymbol); revenir
(vrai);
} sinon return(false);

Comme auparavant, la fonction affiche une erreur sinombre de nouvelles tentatives a dépasséMAX_RETRIES.
Une description de chaîne du type de commande est récupérée à l'aide duCheckOrderType() fonction, et la
description du code retour est récupérée à l'aide de laTradeServerReturnCodeDescription() fonction.

Ensuite, nous imprimons les informations sur la commande dans le journal. Nous utilisons une combinaison
dedemande etrésultat variables, puisque lerésultat les variables ne contiennent pas beaucoup d’informations sur
les commandes en attente. Le
SymboleInfoDouble() La fonction est utilisée pour récupérer le prix acheteur et vendeur actuel. Enfin, nous
renvoyons une valeur devrai si la commande a réussi, etFAUX si ce n'était pas le cas.

Vous pouvez visualiser l'intégralitéOuverten attente() fonctionner dans le\MQL5\Include\Mql5Book\Trade.mqh


déposer. Notez que les commandes en attente fonctionnent de la même manière quel que soit le type de compte.
Contrairement aux chapitres précédents, nous n'aurons pas à écrire de fonctions distinctes pour passer et gérer les
ordres en attente pour les comptes de couverture.

Utilisation de la fonction OpenPending()


Comme nous l'avons fait pour lePoste libre() fonction, nous allons créer plusieurs fonctions d'assistance pour
accéder à notreOuverten attente() fonction. Chaque fonction ouvrira une commande du type spécifié :

classe CTrade
{ public :
bool BuyStop (chaîne pSymbol, double pVolume, double pPrice, double pStop = 0,
double pProfit = 0, datetime pExpiration = 0, chaîne pComment = NULL); bool
SellStop (chaîne pSymbol, double pVolume, double pPrice, double pStop = 0, double
pProfit = 0, datetime pExpiration = 0, chaîne pComment = NULL); bool BuyLimit
(chaîne pSymbol, double pVolume, double pPrice, double pStop = 0, double pProfit =
0, datetime pExpiration = 0, chaîne pComment = NULL); bool SellLimit (chaîne
pSymbol, double pVolume, double pPrice, double pStop = 0, double pProfit = 0,
datetime pExpiration = 0, chaîne pComment = NULL);

bool BuyStopLimit (string pSymbol, double pVolume, double pPrice, double pStop = 0,
double pProfit = 0, double pStopLimit = 0, datetime pExpiration = 0,
chaîne pComment = NULL); bool SellStopLimit (string pSymbol, double pVolume, double
pPrice, double pStop = 0, double pProfit = 0, double pStopLimit = 0, datetime
pExpiration = 0, string pComment = NULL);
} ;

Notez que les fonctions d'ordre stop limite contiennent lepStopLimit paramètre, alors que les autres fonctions ne le
font pas. Voici le corps duAcheterStop() fonction. La fonction appelle simplement leOuverten attente()
fonction, en passant les paramètres de la fonction àOuverten attente(), et en précisant leORDER_TYPE_BUY_STOP
constante comme deuxième paramètre. La valeur de retour deOuverten attente() est ensuite renvoyé au
programme :

152
Acheteur : Evangelos Papageorgiou (vpapnm@yahoo.gr)
Numéro de transaction : 28J511988U193831A

Les ordres en attente


bool CTrade::BuyStop (chaîne pSymbol, double pVolume, double pPrice, double pStop = 0, double
pProfit = 0, datetime pExpiration = 0, chaîne pComment = NULL)
{ bool success = OpenPending(pSymbol,ORDER_TYPE_BUY_STOP,pVolume,pPrice,pStop,pProfit,0,
pExpiration,pComment);
retour(succès);
}

Les autres fonctions d'ordre stop et limite sont identiques – à l'exception du type d'ordre transmis auOuverten
attente() fonction. Voici la fonction d'ordre stop limite de vente, qui contient lepStopLimit paramètre:

bool CTrade::SellStopLimit (string pSymbol, double pVolume, double pPrice, double


pStop = 0, double pProfit = 0, double pStopLimit = 0,
datetime pExpiration = 0, chaîne pComment = NULL)
{ bool success = OpenPending(pSymbol,ORDER_TYPE_SELL_STOP_LIMIT,pVolume,pPrice,pStop,
pProfit,pStopLimit,pExpiration,pComment);
retour(succès);
}
Vous pouvez visualiser ces fonctions dans le\MQL5\Include\Mql5Book\Trade.mqh déposer. Nous aborderons
l'utilisation de ces fonctions plus loin dans le chapitre.

Gestion des commandes en attente


Lorsque vous travaillez avec des commandes en attente, vous devrez souvent récupérer des informations sur les
commandes en cours. Puisqu’il peut y avoir plusieurs ordres en attente ouverts par symbole, il est nécessaire
d’obtenir un décompte précis du nombre d’ordres en attente actuellement ouverts.

Pour traiter le pool de commandes en attente, nous utilisons unpour boucle avec leCommandeObtenirTicket()
fonction pour sélectionner chaque commande dans le pool de commandes :

pour(int i = 0; i < OrdersTotal(); i++)


{ ticket principal = OrderGetTicket(i);
Imprimer(billet);
}

Lepour la boucle utilise la variable entièreje comme incrémenteur. Il est initialisé à 0, puisque l'index du pool
d'ordres démarre à 0. LeTotal des commandes() La fonction renvoie le nombre de commandes en attente
actuellement ouvertes. Puisque l'index du pool de commandes commence à 0, la valeur maximale deje doit être un
de moins queTotal des commandes(). Un indice de 0 est la commande la plus ancienne du pool. Nous
commencerons à 0 et incrémenterons de un tant queje est inférieur àTotal des commandes().

LeCommandeObtenirTicket() La fonction sélectionne la commande avec l'index du pool de commandes indiqué


par leje variable. On peut alors utiliser leCommandeObtenirDouble(),CommandeObtenirInteger()
ouCommandeGetString() fonctions pour récupérer des informations sur la commande en cours.
LeCommandeObtenirTicket() La fonction renvoie également le numéro de ticket de la commande sélectionnée.
Dans l'exemple ci-dessus, nous attribuons le ticket de commande aubillet variable. Le numéro du ticket est
imprimé dans le journal et la boucle se répète.

153
Acheteur : Evangelos Papageorgiou (vpapnm@yahoo.gr)
Numéro de transaction : 28J511988U193831A

ETXPERT UNCONSEIL P.ROGRAMMATION POUR METTRADER 5


Maintenant que nous savons comment parcourir le pool de commandes actuel, nous pouvons obtenir un décompte
précis du nombre de commandes actuellement ouvertes par type. Nous allons créer une classe pour contenir notre
fonction de comptage de commandes, ainsi que les variables qui contiennent nos décomptes de commandes et nos
numéros de billets. Nous appellerons cette classeCPEn attente. LeCPEn attente la classe et toutes les fonctions
associées dans ce chapitre seront stockées dans le\MQL5\Include\Mql5Book\ En attente.mqh déposer:

classe CPending
{privé :
void OrderCount (chaîne pSymbol);
int BuyLimitCount, SellLimitCount, BuyStopCount, SellStopCount,
BuyStopLimitCount, SellStopLimitCount, TotalPendingCount ;
head Billets en attente[];
} ;

LeNombre de commandes() fonction de notreCPEn attente la classe parcourra le pool de commandes et


comptera le nombre de commandes en cours. Les variables entièresAcheterLimitCount,VendreLimitCount, etc.
stockera le nombre de commandes, et leBillets en attente[] array contiendra les numéros de ticket des
commandes en cours. Tous ces membres sont privés – nous créerons des fonctions publiques pour accéder à ces
informations.

Voici le corps duNombre de commandes() fonction:

void CPending :: OrderCount (string pSymbol)


{
AcheterLimitCount = 0 ; VenteLimitCount = 0 ; AcheterStopCount = 0 ; VenteStopCount = 0 ;
AcheterStopLimitCount = 0 ; SellStopLimitCount = 0 ; TotalPendingCount = 0 ;
ArrayFree (Tickets en attente);

pour(int i = 0; i < OrdersTotal(); i++)


{ ticket principal = OrderGetTicket(i);
si (OrderGetString (ORDER_SYMBOL) == pSymbol)
{ type long =
CommandeObtenirInteger(ORDER_TYPE);

switch((int)type) { case
ORDER_TYPE_BUY_STOP :
BuyStopCount++ ; casser;

cas ORDER_TYPE_SELL_STOP : SellStopCount++ ;


casser;

cas ORDER_TYPE_BUY_LIMIT : BuyLimitCount++ ;


casser;

cas ORDER_TYPE_SELL_LIMIT : SellLimitCount++ ;


casser;

154
Acheteur : Evangelos Papageorgiou (vpapnm@yahoo.gr)
Numéro de transaction : 28J511988U193831A

Les ordres en attente


cas ORDER_TYPE_BUY_STOP_LIMIT : BuyStopLimitCount++ ;
casser;

cas ORDER_TYPE_SELL_STOP_LIMIT :
SellStopLimitCount++ ; casser;
}

TotalPendingCount++ ;

ArrayResize (PendingTickets, TotalPendingCount);


PendingTickets[ArraySize(PendingTickets)-1] = ticket ;
}
}
}

La première chose que nous faisons dans leNombre de commandes() La fonction consiste à initialiser toutes les
variables du nombre de commandes à zéro. Vous pouvez placer plusieurs expressions par ligne, à condition que
chaque expression se termine par un point-virgule. LeTableauLibre() fonction réinitialise leBillets en
attente[] tableau et efface son contenu.

Lepour l'initialisation de la boucle et leCommandeObtenirTicket() la fonction a déjà été expliquée. Le


CommandeGetString() fonctionner avec leORDER_SYMBOL Le paramètre récupère le symbole de l’ordre
actuellement sélectionné et le compare aupSymbole paramètre. Si le symbole ne correspond pas, nous passons à la
commande suivante dans le pool. Sinon, nous traitons la commande actuellement sélectionnée.

LeCommandeObtenirInteger() fonctionner avec leORDER_TYPE Le paramètre récupère le type de commande pour


la commande actuellement sélectionnée et le stocke dans letaper variable. Lechanger l'opérateur compare
lestaper variable à une liste de types de commandes spécifiées aveccas les opérateurs. Noter la(int) à l'intérieur
de l'opérateur de commutation - cela convertit la valeur dutaper variable en un entier pour éviter un avertissement
de conversion de type. Lorsque le type de commande correct correspond, la variable de comptage correspondante
est incrémentée et lecasser l'opérateur quitte lechanger bloc.

LeNombre total d'attentes La variable est incrémentée pour chaque commande du symbole actuel, quel que
soit son type. LeTableauResize() la fonction redimensionne leBillets en attente[] tableau pour correspondre
au nombre actuel de commandes indiqué parNombre total d'attentes. Enfin, le ticket de commande
actuellement sélectionné est stocké dans l'index le plus élevé du tableau.

Le résultat duNombre de commandes() La fonction est que les variables de comptage pertinentes sont remplies
avec le nombre d'ordres ouverts en attente de chaque type pour le symbole sélectionné. LeNombre total
d'attentes La variable compte toutes les commandes ouvertes pour le symbole sélectionné. Finalement,
leBillets en attente[] Le tableau contient les numéros de ticket de toutes les commandes ouvertes en attente
pour le symbole sélectionné.

Nous devrons maintenant créer des fonctions publiques pour accéder à nos variables de compteur et auxBillets
en attente[] tableau. Voici les membres publics de notreCPEn attente classe:

155
Acheteur : Evangelos Papageorgiou (vpapnm@yahoo.gr)
Numéro de transaction : 28J511988U193831A

ETXPERT UNCONSEIL P.ROGRAMMATION POUR METTRADER 5


classe CPending
{ public :
int BuyLimit (chaîne pSymbol); int
SellLimit (chaîne pSymbol); int
BuyStop (chaîne pSymbol); int
SellStop (chaîne pSymbol); int
BuyStopLimit(string pSymbol); int
SellStopLimit (chaîne pSymbol); int
TotalPending (chaîne pSymbol); void
GetTickets(string pSymbol, ulong
&pTickets[]);
} ;

Les sept fonctions avec leint return type renvoie le nombre pour le type de commande spécifié. LeProcurez-vous
des billets() fonction copie leBillets en attente[] tableau dans un deuxième tableau que le programmeur
traitera ensuite.

Jetons un coup d'oeil au corps de la fonction pour leLimite d'achat() fonction. Ces sept fonctions appellent le
privéNombre de commandes() fonction. Une fois la fonction exécutée, la variable de comptage appropriée est
renvoyée au programme :

int CPending :: BuyLimit (string pSymbol)


{
OrderCount(pSymbol);
return(AcheterLimitCount);
}

LeLimite d'achat() La fonction renvoie le nombre d'ordres d'achat limités actuellement ouverts sur le symbole
spécifié parpSymbole. Les six fonctions de comptage restantes sont similaires. La seule différence est la variable
count renvoyée par la fonction. Vous pouvez visualiser ces fonctions dans le\MQL5\Include\Mql5Book\En
attente.mqh déposer.

Examinons maintenant leProcurez-vous des billets() fonction. Cela appellera leNombre de commandes()
fonction, puis copiez leBillets en attente[] tableau en un deuxième tableau passé à la fonction par référence :

void CPending::GetTickets(string pSymbol,ulong &pTickets[])


{
OrderCount(pSymbol);
ArrayCopy(pTickets,PendingTickets);
retour;
}

L'esperluette (&) avant lepBillets[] Le paramètre indique qu’il s’agit d’un tableau passé par référence. Le tableau
sera modifié par la fonction, et ces modifications seront présentes dans le reste du programme. LeTableauCopie()
fonction copie leBillets en attente[] tableau au tableau spécifié par lepBillets paramètre. Voici comment
nous utiliserions leProcurez-vous des billets() fonction dans notre programme :

156
Acheteur : Evangelos Papageorgiou (vpapnm@yahoo.gr)
Numéro de transaction : 28J511988U193831A

Les ordres en attente


// Variables globales
CPEn attente En attente ;

// Gestionnaire d'événements
OnTick() ulong tickets[];

En attente.GetTickets(_Symbol,tickets);
pour(int i = 0; i < ArraySize(tickets); i++)
{ billet ulong = billets[i];
OrderSelect (billet);

long orderType = OrderGetInteger(ORDER_TYPE);


double orderVolume = OrderGetDouble (ORDER_VOLUME_CURRENT);
double commandeOpenPrice = OrderGetDouble(ORDER_PRICE_OPEN);
double commandeSL = OrderGetDouble(ORDER_SL); double
commandeTP = OrderGetDouble(ORDER_TP);

Imprimer("Ticket :"+ticket+", Type :"+orderType+", Volume :"+orderVolume+",


Prix : "+orderOpenPrice+", SL : "+orderSL+", TP : "+orderTP );
}

Tout d’abord, nous déclarons un objet nomméEn attente basé surCPEn attente classe en haut du programme. Le
reste du code appartient auSurTick() gestionnaire d'événements ou une fonction appelée à partir de celui-ci.

Ledes billets[] Le tableau contiendra les numéros de tickets en attente. LeProcurez-vous des billets() la
fonction remplit ledes billets[] tableau avec les numéros de ticket de toutes les commandes en attente non
exécutées pour le symbole graphique actuel. UNpour La boucle est utilisée pour parcourir les numéros de ticket
dans ledes billets[] tableau. La variable itérateurje commence à 0. La valeur maximale deje est déterminé par
leTaille du tableau() fonction, qui renvoie le nombre d'éléments dans ledes billets[] tableau.

À l'intérieur depour boucle, nous attribuons la valeur debillets[i] aubillet variable. Lebillet la variable est
transmise auCommandeSélection() fonction, qui sélectionne l’ordre de traitement ultérieur.
LeCommandeObtenirInteger(),CommandeObtenirDouble() etCommandeGetString() les fonctions peuvent
désormais être utilisées pour récupérer des informations sur la commande.

Nous utilisons leCommandeObtenirInteger() etCommandeObtenirDouble() fonctions pour récupérer des


informations sur l'ordre actuellement sélectionné, y compris le type d'ordre, le volume, le prix d'ouverture, le stop
loss et le take profit. Enfin, nous imprimons ces informations dans le journal.

En parcourant le pool de commandes en attente de cette manière, vous récupérerez des informations sur chaque
commande et utiliserez ces informations pour prendre des décisions, par exemple s'il convient de modifier ou de
clôturer une commande.

Fonctions d'informations sur la commande


Tout comme les fonctions d'informations de position du chapitre précédent, nous allons créer un ensemble de
fonctions faciles à mémoriser pour récupérer les informations de commande. Au lieu d'utiliser

157
Acheteur : Evangelos Papageorgiou (vpapnm@yahoo.gr)
Numéro de transaction : 28J511988U193831A

ETXPERT UNCONSEIL P.ROGRAMMATION POUR METTRADER 5


leCommandeObtenir...() fonctions, nous allons créer des fonctions courtes pour récupérer ces informations en
utilisant uniquement un numéro de ticket.

Commençons par le type de commande. LeCommandeObtenirInteger() fonctionner avec leORDER_TYPE Le


paramètre renvoie une constante de type de commande duENUM_ORDER_TYPE énumération. La commande doit
d'abord être sélectionnée à l'aide duCommandeSélection() fonction. La fonction ci-dessous récupérera le type de
commande en utilisant uniquement le numéro de ticket :

Type de commande long (ulong pTicket)


{ bool select = CommandeSelect(pTicket);
if(select == true) return(OrderGetInteger(ORDER_TYPE)); sinon
return (WRONG_VALUE);
}

LeCommandeSélection() La fonction sélectionne l'ordre indiqué par le numéro de ticket transmis dans lepBillet
paramètre. Si la commande n'existe pas, la fonction renvoieWRONG_VALUE, ou -1. Sinon, le type de commande est
récupéré avec leCommandeObtenirInteger() fonction et retourné au programme.

Regardons une autre fonction qui utilise leCommandeObtenirDouble() fonction. Cette fonction renverra le volume
actuel de la commande :

double volume de commande (ulong pTicket)


{ bool select = CommandeSelect(pTicket);
if(select == true) return(OrderGetDouble(ORDER_VOLUME_CURRENT));
sinon return (WRONG_VALUE);
}

LeCommandeObtenirDouble() La fonction renvoie le volume actuel de la commande si le numéro de ticket


sélectionné parCommandeSélection() est valable. Enfin, regardons une fonction qui utiliseCommandeGetString():

chaîne OrderComment (tête pTicket)


{ bool select = CommandeSelect(pTicket);
if(select == true) return(OrderGetString(ORDER_COMMENT));
sinon return (NULL);
}

Cette fonction renvoie le commentaire de la commande. Si le ticket de commande est transmis


auCommandeSélection() la fonction n'est pas valide, alors une valeur deNUL est retourné.

Il existe plusieurs autres fonctions qui utilisent leCommandeSélection() etCommandeObtenir...() les fonctions.
Vous pouvez les consulter dans\MQL5\Include\Mql5Book\En attente.mqh déposer. Les paramètres pour
leCommandeObtenir...() les fonctions peuvent être visualisées dans leRéférence MQL5 sousConstantes
standard... > Constantes commerciales > Propriétés de l'ordre.

Utilisons les fonctions d'informations de commande que nous avons définies dans leEn attente.mqh fichier dans
l'exemple de code de la page 165 :

158
Acheteur : Evangelos Papageorgiou (vpapnm@yahoo.gr)
Numéro de transaction : 28J511988U193831A

Les ordres en attente


billets ulong[]; En attente.GetTickets(_Symbol,tickets);

pour(int i = 0; i < ArraySize(tickets); i++)


{ billets de tête = billets[i];

type de commande long =Type de commande (billet);


double commandeVolume =Volume de commande
(billet); commande doubleOpenPrice
=CommandeOuvrePrix(billet); double commandeSL
=CommandeStopLoss (billet); commande doubleTP
=CommandeTakeProfit (billet);

Imprimer("Ticket :"+ticket+", Type :"+orderType+", Volume :"+orderVolume+",


Prix : "+orderOpenPrice+", SL : "+orderSL+", TP : "+orderTP );
}

Nous avons remplacé leCommandeObtenir...() fonctions avec les fonctions d'informations de commande que
nous avons définies. Nous avons également éliminé leCommandeSélection() fonctionner, car il n’est plus
nécessaire.

Pour résumer, nous avons créé leCPEn attente classe avec sept fonctions de comptage d'ordre public (Limite
d'achat(),Limite de vente(), etc.) qui renverra le nombre d'ordres ouverts en attente de ce type pour le
symbole spécifié. LeProcurez-vous des billets() La fonction renverra un tableau contenant les numéros de
ticket de toutes les commandes en attente actuellement ouvertes sur le symbole spécifié. En utilisant le tableau
renvoyé par leProcurez-vous des billets() fonction, nous pouvons récupérer les numéros de ticket des
commandes ouvertes en attente. Avec le numéro de ticket, nous pouvons récupérer des informations sur la
commande en utilisant les fonctions d'informations de commande telles queType de
commande(),CommandePrixOuvert() ouCommandeStopLoss().

Modification des commandes en attente


Il peut être nécessaire de modifier le cours d'ouverture, le stop loss, le take profit ou l'heure d'expiration d'un ordre
en attente.
Pour ce faire, nous utilisons leTRADE_ACTION_MODIFY action avec leCommandeEnvoyer() fonction. Nous devrons
spécifier le numéro du ticket, le prix d'ouverture de l'ordre, le stop loss, le take profit, ainsi que l'heure et le type
d'expiration.

Lors de la modification d'un ordre en cours, il est nécessaire de préciser la valeur actuelle de tout prix qui ne sera pas
modifié. Par exemple, si vous modifiez le prix d'ouverture d'un ordre en attente et que l'ordre a actuellement un prix
stop loss et un prix take profit, vous devrez alors récupérer les prix stop loss et take profit actuels et les transmettre
auCommandeEnvoyer() fonction.

Montrons une simple modification de commande en attente. Dans cet exemple, nous allons modifier le prix
d'ouverture d'un ordre en attente. Le stop loss et le take profit ne seront pas modifiés. Aucun délai d'expiration n'a
été indiqué avec la commande. Nous supposerons que la variablenouveau prix détient le nouveau prix de la
commande en attente :

double nouveauPrix ;

159
Acheteur : Evangelos Papageorgiou (vpapnm@yahoo.gr)
Numéro de transaction : 28J511988U193831A

ETXPERT UNCONSEIL P.ROGRAMMATION POUR METTRADER 5


billets ulong[]; En
attente.GetTickets(_Symbol,tickets);

pour(int i = 0; i < ArraySize(tickets); i++)


{ billets de tête = billets[i];

request.action = TRADE_ACTION_MODIFY ;
request.order = billet ; request.price =
newPrice ; request.sl = OrderStopLoss
(ticket); request.tp = OrderTakeProfit
(ticket); request.expiration = 0 ;
request.type_time = ORDER_TIME_GTC ;
OrderSend (demande, résultat);
}

Vous reconnaîtrez leProcurez-vous des billets() fonction et lepour boucle de la section précédente. Nous
supposerons qu'il y a un seul ordre en attente ouvert sur le symbole graphique actuel, et ce code modifiera le prix
d'ouverture à la valeur stockée dansnouveau prix.

LeTRADE_ACTION_MODIFY constant identifie cela comme une modification de commande en attente. Lors de la
modification d'une commande, nous devons préciser un ticket de commande à l'aide dudemande.commande
variable. Toute modification du prix, du stop loss, du take profit ou de l'expiration est affectée aux variables
appropriées. S'il n'y a aucun changement, nous devons alors attribuer les valeurs actuelles à ces variables.

Puisque le prix d'ouverture de l'ordre est modifié, nous attribuons la valeur dunouveau prix variable
àdemande.prix. Nous utilisons notreCommandeStopLoss() etCommandeTakeProfit() fonctions pour récupérer les
prix stop loss et take profit actuels, et attribuer ces prix audemande.sl etdemande.tp variables. Cela garantit que les
prix du stop loss et du take profit ne seront pas modifiés. Par souci d’exhaustivité, nous spécifions une valeur de 0
pourdemande.expiration, Et mettrerequête.type_time àORDER_TIME_CGV, indiquant que la commande n'expire
pas. LeCommandeEnvoyer() La fonction envoie le prix de la commande en attente mis à jour au serveur pour
modification. Le stop loss, le take profit et le délai d'expiration restent inchangés.

Voici un autre exemple dans lequel nous modifierons les prix stop loss et take profit d'un ordre en attente. Le prix
d’ouverture de l’ordre restera inchangé. Le stop loss sera ajusté 500 points en dessous du prix d'ouverture de
l'ordre, et le take profit sera mis à zéro :

// Variables globalesentrée
int StopLoss = 500 ;

// Gestionnaire d'événements OnTick()


ulong tickets[]; En
attente.GetTickets(_Symbol,tickets);

pour(int i = 0; i < ArraySize(tickets); i++)


{ billets de tête = billets[i];
request.action = TRADE_ACTION_MODIFY ;
request.order = billet ;

160
Acheteur : Evangelos Papageorgiou (vpapnm@yahoo.gr)
Numéro de transaction : 28J511988U193831A

Les ordres en attente


request.price = OrderOpenPrice (billet);
request.sl = OrderOpenPrice(ticket) - (StopLoss * _Point); demande.tp =
0 ;
request.expiration = 0 ;
request.type_time = ORDER_TIME_GTC ;
OrderSend (demande, résultat);
}

Les lignes en gras indiquent les modifications par rapport à l'exemple précédent. Ledemande.prix La variable est
définie sur le prix d'ouverture de l'ordre actuel. Ledemande.sl La variable est fixée au prix d'ouverture de l'ordre,
moins le nombre de points indiqué dans leStopPerte variable. Lerésultat.tp La variable est définie sur 0,
indiquant qu'il n'y aura pas de profit sur l'ordre modifié. S'il existe actuellement un prix take profit sur l'ordre, il sera
réinitialisé à 0.

La fonction ModifyPending()
LeModifierEn attente() La fonction modifiera le prix d'ouverture, le stop loss, le take profit ou le délai
d'expiration d'un ordre. Le ticket de commande, le prix, le stop loss et le take profit sont les paramètres requis. Si le
prix ne sera pas modifié, une valeur de 0 sera transmise aupPrix paramètre. Si le stop loss, le take profit ou l'heure
d'expiration sont fixés et ne seront pas modifiés, alors il sera nécessaire de transmettre les valeurs actuelles
auModifierEn attente() fonction:

Voici laModifierEn attente() déclaration de fonction dans leCCommerce déclaration de classe :

classe CTrade
{ public :
bool ModifyPending (ulong pTicket, double pPrice, double pStop, double pProfit,
datetime pExpiration = 0);
} ;
La fonction estpublique, ce qui signifie que nous l'appellerons directement. Voici la première partie duModifierEn
attente() fonction, où nous définissons les variables de requête :

bool CTrade::ModifyPending (ulong pTicket, double pPrice, double pStop, double pProfit,
datetime pExpiration = 0)
{ request.action = TRADE_ACTION_MODIFY ;
request.order = pTicket;

request.sl = pStop ; request.tp =

pProfit ;

CommandeSélectionner(pTicket);

if(pPrice > 0) request.price = pPrice ;


sinon request.price = OrderGetDouble(ORDER_PRICE_OPEN);

si (pExpiration > 0)
{ request.expiration = pExpiration ;
request.type_time = ORDER_TIME_SPECIFIED ;
} sinon request.type_time = ORDER_TIME_GTC ;

161
Acheteur : Evangelos Papageorgiou (vpapnm@yahoo.gr)
Numéro de transaction : 28J511988U193831A

ETXPERT UNCONSEIL P.ROGRAMMATION POUR METTRADER 5


L'action commerciale devraitTRADE_ACTION_MODIFY, et lepBillet,pArrêter etpBénéfice les valeurs des
paramètres sont affectées aux variables de requête appropriées. Ensuite, nous utilisons leCommandeSélection()
fonction pour sélectionner le ticket de commande indiqué parpBillet. Si lapPrix paramètre est supérieur à zéro,
nous attribuons la valeur depPrix àdemande.prix. Sinon, nous récupérons le prix d'ouverture actuel et attribuons
cette valeur àdemande.prix.

Si la valeur depExpiration est supérieur à zéro, nous attribuons le nouveau délai d'expiration de la commande
àdemande.expiration, Et mettrerequête.type_time àORDER_TIME_SPECIFIED. Sinon, on
poserequête.type_time àORDER_TIME_CGV, ce qui indique que la commande n'aura pas de délai d'expiration.

Le reste de la fonction contient notre code de modification de commande et de gestion des erreurs :

int retryCount = 0 ; int


checkCode = 0;

faire {
OrderSend (demande, résultat); checkCode =

CheckReturnCode(result.retcode); if(checkCode ==

CHECK_RETCODE_OK) pause ; sinon si (checkCode ==

CHECK_RETCODE_ERROR)

{ string errDesc = TradeServerReturnCodeDescription (result.retcode);


Alert("Modifier la commande en attente : Erreur ",result.retcode," -
",errDesc); casser;
} autre
{ Print("Erreur de serveur détectée, nouvelle
tentative...");
Sommeil (RETRY_DELAY); retryCount++;
}
}
while(retryCount < MAX_RETRIES);

si (retryCount >= MAX_RETRIES)


{ string errDesc = TradeServerReturnCodeDescription (result.retcode);
Alert("Nombre maximal de tentatives dépassé : Erreur ",result.retcode," -
",errDesc);
}

CommandeSélectionner(pTicket); chaîne errDesc =


TradeServerReturnCodeDescription (result.retcode);

Print("Modifier la commande en attente #",pTicket,": ",result.retcode," - ",errDesc,


", Prix : ",OrderGetDouble(ORDER_PRICE_OPEN),", SL : ",request.sl,
", TP : ",request.tp,", Expiration : ",request.expiration);

162
Acheteur : Evangelos Papageorgiou (vpapnm@yahoo.gr)
Numéro de transaction : 28J511988U193831A

Les ordres en attente


si (checkCode == CHECK_RETCODE_OK)
{
Comment("Commande en attente ",pTicket," modifié,", " Prix :
",OrderGetDouble(ORDER_PRICE_OPEN),", SL : ",request.sl,
", TP : ",request.tp); revenir (vrai);
}
sinon return (faux);
}

Voici comment nous utiliserions notreModifierEn attente() fonctionner dans un programme de conseillers
experts. Cet exemple modifiera le prix d'ouverture de l'ordre, mais laissera le stop loss et le take profit seul :

double nouveauPrix ; billet


de tête ;

double curSL = OrderStopLoss (ticket); double curTP


= OrderTakeProfit (ticket);

Trade.ModifyPending(ticket,newPrice,curSL,curTP);
Lenouveau prix La variable conservera le prix d'ouverture de la nouvelle commande, tandis que lebillet La
variable contient le numéro du ticket de commande. Nous supposerons qu'ils contiennent tous deux des valeurs
valides. Nous utilisons leCommandeStopLoss() et
CommandeTakeProfit() fonctions pour récupérer le stop loss actuel et les valeurs de profit pour l'ordre spécifié
ticket et stockez ces valeurs danscurSL etcurTP respectivement. Les paramètres sont transmis auModifierEn
attente() fonction, et la commande est mise à jour avec le nouveau prix de la commande en attente.

Voici un deuxième exemple où nous modifions le stop loss et le take profit, mais laissons le prix de l'ordre tranquille :

double nouveauSL, nouveauTP ;


billet de tête ;

Trade.ModifyPending(ticket,0,newSL,newTP);

Nous supposerons que lenouveauSL etnouveauTP les variables détiennent des prix stop loss et take profit valides.
Dans le
ModifierEn attente() appel de fonction, nous utilisons un 0 pour lepPrix paramètre pour indiquer que le prix
d’ouverture de l’ordre ne sera pas modifié.

Dans les deux exemples ci-dessus, lepExpiration Le paramètre utilise la valeur par défaut de 0. Si un délai
d'expiration a été spécifié avec la commande, vous devrez transmettre le délai d'expiration auModifierEn
attente() fonction lors de la modification de la commande.

Supprimer les commandes en attente


Un ordre en attente non exécuté peut être supprimé en attribuant leTRADE_ACTION_REMOVE constante à
lademande.action variable. Le seul paramètre obligatoire est le numéro de ticket de la commande à supprimer :

163
Acheteur : Evangelos Papageorgiou (vpapnm@yahoo.gr)
Numéro de transaction : 28J511988U193831A

ETXPERT UNCONSEIL P.ROGRAMMATION POUR METTRADER 5


request.action = TRADE_ACTION_MODIFY ;
request.order = billet ;

OrderSend (demande, résultat);

Le code ci-dessus supprimera la commande en attente qui correspond aubillet variable. Notez qu'une fois qu'un
ordre en attente est exécuté, il devient une partie de la position et doit être fermé en utilisant les méthodes de
clôture de position du chapitre précédent.

Nous allons créer une fonction pour supprimer une commande en attente, avec le même code de gestion des
erreurs et de nouvelle tentative que la fonction précédente. Voici la déclaration de fonction dans leCCommerce
déclaration de classe :

classe CTrade
{ public :
bool Supprimer(head pTicket);
}

Et voici la fonction elle-même. LeSupprimer() La fonction ne nécessite qu'un seul paramètre : le numéro de ticket
de la commande en attente à supprimer :

bool CTrade :: Supprimer (tête pTicket)


{ request.action = TRADE_ACTION_REMOVE ;
request.order = pTicket;

// Boucle de commande
int retryCount = 0; int
checkCode = 0;

faire {
OrderSend (demande, résultat);
checkCode = CheckReturnCode(result.retcode);

if(checkCode == CHECK_RETCODE_OK) pause ; sinon


si (checkCode == CHECK_RETCODE_ERROR)
{ string errDesc = TradeServerReturnCodeDescription (result.retcode);
Alert("Supprimer la commande : Erreur ",result.retcode," -
",errDesc); casser;
} autre
{ Print("Erreur de serveur détectée, nouvelle
tentative...");
Sommeil (RETRY_DELAY); retryCount++;
}
}
while(retryCount < MAX_RETRIES);

si (retryCount >= MAX_RETRIES)

164
Acheteur : Evangelos Papageorgiou (vpapnm@yahoo.gr)
Numéro de transaction : 28J511988U193831A

Les ordres en attente


{ string errDesc = TradeServerReturnCodeDescription (result.retcode);
Alert("Nombre maximal de tentatives dépassé : Erreur ",result.retcode,"
- ",errDesc);
}

chaîne errDesc = TradeServerReturnCodeDescription (result.retcode);


Print("Supprimer la commande #",pTicket,": ",result.retcode," - ",errDesc);

si (checkCode == CHECK_RETCODE_OK)
{
Comment("Commande en attente ",pTicket," supprimée");
revenir (vrai);
}
sinon return (faux);
}

Pour utiliser leSupprimer() fonction, il suffit de transmettre le numéro de ticket de la commande à supprimer
aupBillet paramètre:

billets ulong[]; En
attente.GetTickets(_Symbol,tickets);

pour(int i = 0; i < ArraySize(tickets); i++)


{ billets de tête = billets[i];
Trade.Delete (billet);
}

Cet exemple supprimera chaque commande en attente ouverte sur le graphique actuel.

Création d'un conseiller expert en commandes en attente


Nous allons créer un simple conseiller expert qui passe les commandes en attente. Cette stratégie est destinée à être
utilisée sur un graphique H4 ou supérieur. À l'ouverture d'une nouvelle barre, deux ordres stop en attente sont
placés un nombre spécifié de points au-dessus et en dessous du haut et du bas de la barre précédente. À l'ouverture
de la barre suivante, toutes les commandes en attente non exécutées sont supprimées et de nouvelles commandes
en attente sont passées. Toute position ouverte se poursuivra jusqu'à ce qu'elle atteigne un prix stop loss ou take
profit.

#include <Mql5Book\Trade.mqh>
CCommerce Commerce ;

#include <Mql5Book\Pending.mqh>
CPEn attente En attente ;

// Variables d'entrée input int


AddPoints = 100 ; entrée double
TradeVolume = 0,1 ; entrée int

165
Acheteur : Evangelos Papageorgiou (vpapnm@yahoo.gr)
Numéro de transaction : 28J511988U193831A

ETXPERT UNCONSEIL P.ROGRAMMATION POUR METTRADER 5


StopLoss=1000 ; entrée int
TakeProfit=1000 ;

// Variables globales bool


glBuyPlaced, glSellPlaced;
dateheure glLastBarTime ;

// Le gestionnaire d'événements
OnTick() annule OnTick()
{
// Données de temps et de prix
Taux MqlRates[] ;
ArraySetAsSeries(taux,true);
int copy = CopyRates(_Symbol,_Period,0,3,rates);

// Recherche une nouvelle barre bool newBar = false;


if(glLastBarTime != taux[0].time)
{ if(glLastBarTime > 0) newBar = true ; glLastBarTime = taux[0].time ;
}

// Placer une commande en attente à l'ouverture d'une nouvelle barre if(newBar


== true)
{
// Récupère les tickets de commande en attente ulong tickets[];
En attente.GetTickets(_Symbol,tickets); int numTickets =
ArraySize(tickets);

// Ferme toutes les commandes ouvertes en attente if(Pending.TotalPending(_Symbol) > 0)


{ pour (int i = 0; i < numTickets; i++)
{
Trade.Delete(tickets[i]);
}
}

// Ouvrir un ordre stop d'achat en attente


double orderPrice = taux[1].high + (AddPoints * _Point); orderPrice =
AjusterAboveStopLevel(_Symbol,orderPrice);

double stopLoss = BuyStopLoss(_Symbol,StopLoss,orderPrice); double takeProfit =

BuyTakeProfit(_Symbol,TakeProfit,orderPrice);

Trade.BuyStop(_Symbol,TradeVolume,orderPrice,stopLoss,takeProfit);

// Ouvrir un ordre stop de vente en attente


orderPrice = taux[1].low - (AddPoints * _Point); orderPrice =
AdjustBelowStopLevel(_Symbol,orderPrice);

166
Acheteur : Evangelos Papageorgiou (vpapnm@yahoo.gr)
Numéro de transaction : 28J511988U193831A

Les ordres en attente


stopLoss = SellStopLoss(_Symbol,StopLoss,orderPrice); takeProfit =
SellTakeProfit(_Symbol,TakeProfit,orderPrice);

Trade.SellStop(_Symbol,TradeVolume,orderPrice,stopLoss,takeProfit);
}
}
Passons en revue le programme :

#include <Mql5Book\Trade.mqh>
CCommerce Commerce ;

#include <Mql5Book\Pending.mqh>
CPEn attente En attente ;

// Variables d'entrée input int


AddPoints = 100 ; entrée double
TradeVolume = 0,1 ; entrée int
StopLoss=1000 ; entrée int
TakeProfit=1000 ;

// Variables globales bool


glBuyPlaced, glSellPlaced;
dateheure glLastBarTime ;

Tout d'abord, nous incluons notrecommerce.mqh etEn attente.mqh fichiers du\MQL5\Include\Mql5Book dossier.
Nous créons leCommerce etEn attente objets basés sur notreCCommerce etCPEn attente Des classes. Nous
ajoutons une variable d'entrée nomméeAjouter des points, qui ajoute ou soustrait le nombre de points spécifié
au prix haut ou bas de la barre précédente. Des variables d'entrée pour définir le volume des échanges, le stop loss
et le take profit sont également incluses. Le Globaldateheure variableglLastBarTime contient l'horodatage de la
barre la plus récente.

annuler OnTick()
{
// Données de temps et de prix
Taux MqlRates[] ;
ArraySetAsSeries(taux,true);
int copy = CopyRates(_Symbol,_Period,0,3,rates);

// Recherche une nouvelle


barre bool newBar = false;
if(glLastBarTime != taux[0].time)
{ if(glLastBarTime > 0) newBar = true ;
glLastBarTime = taux[0].time ;
}

Nous utilisons leles taux[] tableau desTarifs Mql type de structure pour contenir les valeurs hautes, basses et
temporelles de chaque barre. Nous couvrirons leTarifs Mql structure au chapitre 15. LanouveauBar La variable

167
Acheteur : Evangelos Papageorgiou (vpapnm@yahoo.gr)
Numéro de transaction : 28J511988U193831A

ETXPERT UNCONSEIL P.ROGRAMMATION POUR METTRADER 5


contient une valeur booléenne déterminant si une nouvelle barre s'est ouverte (c'est à dire. un changement dans
l'horodatage de la barre actuelle s'est produit). La nouvelle fonctionnalité de barre sera abordée au chapitre 18.

si (nouvelleBarre == vrai)
{
// Récupère les tickets de commande en attente
billets de tête[];
En attente.GetTickets(_Symbol,tickets);
int numTickets = ArraySize(tickets);

// Ferme toutes les commandes ouvertes en attente


if(Pending.TotalPending(_Symbol) > 0)
{ pour (int i = 0; i < numTickets; i++)
{
Trade.Delete(tickets[i]);
}
}

Si la valeur dunouveauBar la variable estvrai, indiquant qu'un nouveau bar est ouvert, nous commencerons par le
code de passation de commande. Dans un premier temps, nous utilisons leProcurez-vous des billets()
fonction de notreCPEn attente classe pour récupérer les tickets de commande ouverts. Ceux-ci sont stockés dans
ledes billets[] tableau. LeTaille du tableau() la fonction renvoie le nombre d'éléments dans ledes
billets[] tableau et stocke cette valeur dans lenombre de billets variable. Si le nombre de commandes
ouvertes est supérieur à zéro, tel que déterminé par lenombre de billets variable, nous parcourons le pool de
commandes en utilisant unpour bouclez et supprimez toutes les commandes ouvertes à l'aide
duCommerce.Supprimer() fonction.

// Ouvrir un ordre stop d'achat en attente


double orderPrice = taux[1].high + (AddPoints * _Point); orderPrice =
AjusterAboveStopLevel(_Symbol,orderPrice);

double stopLoss = BuyStopLoss(_Symbol,StopLoss,orderPrice); double

takeProfit = BuyTakeProfit(_Symbol,TakeProfit,orderPrice);

Trade.BuyStop(_Symbol,TradeVolume,orderPrice,stopLoss,takeProfit);

// Ouvrir un ordre stop de vente en attente


orderPrice = taux[1].low - (AddPoints * _Point); orderPrice =
AdjustBelowStopLevel(_Symbol,orderPrice);

stopLoss = SellStopLoss(_Symbol,StopLoss,orderPrice); takeProfit =

SellTakeProfit(_Symbol,TakeProfit,orderPrice);

Trade.SellStop(_Symbol,TradeVolume,orderPrice,stopLoss,takeProfit);

168
Acheteur : Evangelos Papageorgiou (vpapnm@yahoo.gr)
Numéro de transaction : 28J511988U193831A

Les ordres en attente


LePrix de la commande La variable contient le prix d'ouverture de l'ordre en attente. Nous ajoutons ou
soustrayons leAjouter des points valeur (multipliée d'abord par la valeur du symbole_Indiquer valeur) par
rapport au prix haut ou bas de la barre précédente (taux[1].élevé ettarifs[1].bas). Nous vérifions et ajustons le
prix de la commande si nécessaire à l'aide duAjusterAboveStopLevel() etAjuster au-dessous du niveau
d'arrêt() les fonctions. Ensuite, nous calculons le stop loss et prenons le profit par rapport au prix d'ouverture de
l'ordre. Enfin, nous passons les ordres stop en attente en utilisant leAcheterStop() etVenteStop() fonctions de
notreCCommerce classe.

Vous pouvez consulter le code de cet expert-conseil dans la\Experts\Mql5Book\En attente d'Expert
Advisor.mq5 déposer.

Utiliser les ordres en attente pour sortir d'une position


Si vous souhaitez augmenter votre position à des niveaux de profit ou de perte fixes, la meilleure méthode consiste à
utiliser des ordres en attente pour clôturer une partie d'une position à un prix spécifié. Une position peut être
clôturée en profit en utilisant un ordre limité, tandis qu'une position en perte peut être fermée en utilisant un ordre
stop. Pour clôturer une position d'achat avec profit, utilisez un ordre de vente limité. Pour le clôturer en perte, utilisez
un ordre stop de vente. Pour clôturer une position de vente avec profit, utilisez un ordre d’achat limité. Pour le
clôturer en perte, utilisez un ordre stop d’achat.

Par exemple, si vous souhaitez clôturer la moitié d'une position d'achat à 500 points de profit et le reste à 1 000
points, passez un ordre de vente limité 500 points au-dessus du prix d'ouverture de la position pour la moitié de la
taille du lot de la position. Le prix take profit de la position fera le reste. Voici un exemple :

// Variables d'entrée entrée


double TradeVolume=0.2; entrée
int StopLoss=500 ;entrée int
TakeProfit1=500 ; entrée int
TakeProfit2=1000 ;

// Gestionnaire d'événements OnTick()


// Ordre d'achat ouvert au marché
glBuyPlaced = Trade.Buy(_Symbol,TradeVolume);

// Modifier SL/TP if(glBuyPlaced


== true)
{ faire dormir (100); while(PositionSelect(_Symbol) == false);
double positionOpenPrice = PositionOpenPrice();

double buyStopLoss = BuyStopLoss(_Symbol,StopLoss,positionOpenPrice); if(buyStopLoss >


0) buyStopLoss = AdjustBelowStopLevel(_Symbol,buyStopLoss);

double buyTakeProfit = BuyTakeProfit(_Symbol,PrendreProfit2,positionPrixOuvert);


if(buyTakeProfit > 0) buyTakeProfit = AdjustAboveStopLevel(_Symbol,buyTakeProfit);

si (acheterStopLoss > 0 || acheterTakeProfit > 0)

169
Acheteur : Evangelos Papageorgiou (vpapnm@yahoo.gr)
Numéro de transaction : 28J511988U193831A

ETXPERT UNCONSEIL P.ROGRAMMATION POUR METTRADER 5


Trade.ModifyPosition(_Symbol,buyStopLoss,buyTakeProfit); glSellPlaced =

faux ;

// Ouvrir l'ordre de fermeture partielle


double partialClose = positionOpenPrice + (TakeProfit1 * _Point); double
Volume partiel = TradeVolume / 2 ;

Trade.SellLimit(_Symbol,partialVolume,partialClose);
}

L'exemple ci-dessus ouvre une position d'achat, définit un stop loss et un take profit de 1 000 points, puis passe un
ordre de vente limité – égal à la moitié de la taille du lot de la position d'achat – 500 points au-dessus du prix
d'ouverture de la position d'achat. Nous utilisons deux variables d'entrée pour les valeurs de take
profit.PrendreProfit1 détermine le prix de l'ordre de vente limité, etPrendreProfit2 est le prix de profit de la
position d'achat. LePrendreProfit2 la variable est transmise auAcheterTakeProfit() fonction pour calculer le
prix de profit de la position d'achat.

Pour calculer le prix de l'ordre de vente limité, nous ajoutons la valeur dePrendreProfit1 (multiplié par
le_Indiquer variable) au prix d'ouverture de la position (positionOuvertPrix), et stocke le résultat dans
lepartielFermer variable. Nous calculons le volume de l'ordre de vente limité en divisant leVolume des échanges
variable d'entrée par 2, et stocker le résultat dans leVolume partiel variable. Nous utilisons leLimite de vente()
fonction pour ouvrir un ordre de vente limité de 0,1 lot, 500 points au-dessus du prix d'ouverture de la position.

Lorsque l’ordre de vente limité est déclenché, la moitié de la position actuelle sera fermée. Le reste de la position
sera clôturé au prix de profit de la position. Cette méthode peut être utilisée pour ajouter n'importe quel nombre de
niveaux de clôture partielle à une position. Assurez-vous de ne pas définir le volume de vos ordres en attente pour
qu'il soit supérieur au volume de la position, sinon vous ouvrirez une position dans la direction opposée !

170
Acheteur : Evangelos Papageorgiou (vpapnm@yahoo.gr)
Numéro de transaction : 28J511988U193831A

Stops suiveurs

Chapitre 14 - Stops suiveurs

Qu’est-ce qu’un Trailing Stop ?


Un stop suiveur est un stop loss qui évolue à mesure qu'une position augmente en termes de profit. Pour un ordre
d'achat, le prix du stop suiveur augmente à mesure que la position gagne en profit, et pour un ordre de vente, le prix
du stop suiveur diminue à mesure que la position gagne en profit. Un stop suiveur ne recule jamais.

Le stop suiveur suit généralement le prix actuel d'un nombre de points spécifié. Par exemple, si un stop suiveur est
fixé à 500 points, le stop loss commence à bouger une fois que le prix actuel est à au moins 500 points du prix du
stop loss. Nous pouvons retarder un stop suiveur en exigeant qu'un niveau minimum de profit soit d'abord atteint. Et
même si un stop suiveur suit généralement le prix point par point, nous pouvons suivre le stop par incréments plus
importants.

Examinons comment implémenter un simple stop suiveur. Pour calculer le prix du stop suiveur, nous ajoutons ou
soustrayons le stop suiveur en points du prix acheteur ou vendeur actuel. Si la distance entre le stop loss actuel de la
position et le prix actuel est supérieure au stop suiveur en points, nous modifions le stop loss de la position pour qu'il
corresponde au prix du stop suiveur.

Le code ci-dessous ajoutera un simple stop suiveur à un conseiller expert. Ce code serait placé sous le code de
passation de commande, vers la fin duSurTick() gestionnaire d'événements:

// Variables d'entrée input int


TrailingStop = 500 ;

// Gestionnaire d'événements OnTick()


if(PositionSelect(_Symbol) == true && TrailingStop > 0)
{ requête.action = TRADE_ACTION_SLTP ;
request.symbol = _Symbol;

long posType = PositionGetInteger(POSITION_TYPE); double


currentStop = PositionGetDouble(POSITION_SL);

double trailStop = TrailingStop * _Point; double


trailStopPrice ;

si (posType == POSITION_TYPE_BUY)
{ trailStopPrice = SymbolInfoDouble(_Symbol,SYMBOL_BID) - trailStop ; si
(trailStopPrice > currentStop)
{ request.sl = trailStopPrice ;
OrderSend (demande, résultat);
}
}
sinon si (posType == POSITION_TYPE_SELL)
{ trailStopPrice = SymbolInfoDouble(_Symbol,SYMBOL_ASK) + trailStop ;
si (trailStopPrice < currentStop)

171
Acheteur : Evangelos Papageorgiou (vpapnm@yahoo.gr)
Numéro de transaction : 28J511988U193831A

ETXPERT UNCONSEIL P.ROGRAMMATION POUR METTRADER 5


{ request.sl = trailStopPrice ;
OrderSend (demande, résultat);
}
}
}

Une variable d'entrée nomméeArrêt de fuite est utilisé pour définir le stop suiveur en points. La valeur par défaut
de 500 définit un stop suiveur de 500 points (0,00500 ou 0,500). LePositionSélection() fonction à l'intérieur
dusi L'opérateur détermine s'il existe une position ouverte sur le symbole graphique actuel et sélectionne cette
position pour un traitement ultérieur. Si laPositionSélection() la fonction renvoievrai, et leArrêt de fuite est
supérieur à zéro, nous procédons à la vérification du stop suiveur.

Ledemande.action la variable est définie surTRADE_ACTION_SLTP, puisque nous effectuons une modification de
position. Ledemande.symbole La variable définit le symbole du graphique actuel comme position à modifier. Nous
récupérons le type de position à l'aide duPositionGetInteger() fonctionner avec leTYPE DE POSITION
paramètre et stocker le résultat dans leposType variable. Le stop loss actuel est récupéré à l'aide
duPositionGetDouble() fonctionner avec lePOSITION_SL paramètre et stocké dansarrêt actuel. Nous
convertissons leArrêt de fuite variable d'entrée à une valeur de prix en la multipliant par la valeur en points du
symbole. Nous stockons cette valeur dans leTrailStop variable. Nous déclarons également une variable
nomméetrailStopPrix. Nous calculerons prochainement la valeur de cette variable.

Unsinon block vérifie le type de position. Si la position est une position d'achat, nous calculons le prix du stop
suiveur en soustrayantTrailStop du prix acheteur actuel, et stocker le résultat dans letrailStopPrix variable.
Ensuite, nous comparonstrailStopPrix au stop loss actuel, stocké dans learrêt actuel variable. Si le prix du
stop suiveur est supérieur au prix du stop loss actuel, cela indique que nous devons déplacer le stop vers le prix du
stop suiveur. Ledemande.sl la variable est définie surtrailStopPrix, et leCommandeEnvoyer() La fonction modifie
le stop loss.

Pour une position de vente, le prix stop suiveur (trailStopPrix) est calculé en additionnant leTrailStop valeur au
prix Ask actuel. Si le prix du stop suiveur est inférieur au prix du stop loss actuel, nous modifions le stop loss de la
position au prix du stop suiveur.

Faible profit
Ajoutons quelques modifications à notre simple stop suiveur. Par exemple, vous souhaitez peut-être que le stop
suiveur n'intervienne qu'après qu'un montant minimum de profit ait été atteint. Pour ce faire, nous ajouterons un
paramètre de profit minimum à notre conseiller expert. Nous déterminons d’abord le profit de la position actuelle en
points. Ensuite, nous comparons cela au paramètre de profit minimum. Si le profit actuel en points de la position est
supérieur au profit minimum, alors le stop suiveur s'activera. Les changements sont mis en évidence en gras :

// Variables d'entrée input int


TrailingStop = 500 ;entrée int
MinimumProfit = 200 ;

// Gestionnaire d'événements OnTick()


if(PositionSelect(_Symbol) == true && TrailingStop > 0)
{ requête.action = TRADE_ACTION_SLTP ;
request.symbol = _Symbol;

172
Acheteur : Evangelos Papageorgiou (vpapnm@yahoo.gr)
Numéro de transaction : 28J511988U193831A

Stops suiveurs
long posType = PositionGetInteger(POSITION_TYPE); double
currentStop = PositionGetDouble(POSITION_SL);double openPrice
= PositionGetDouble(POSITION_PRICE_OPEN);

double minProfit = MinimumProfit * _Point ;double


trailStop = TrailingStop * _Point;

double trailStopPrice ;double


bénéfice actuel ;

si (posType == POSITION_TYPE_BUY)
{ trailStopPrice = SymbolInfoDouble(_Symbol,SYMBOL_BID) -
trailStop ;currentProfit = SymbolInfoDouble(_Symbol,SYMBOL_BID) -
openPrice ;si (trailStopPrice > currentStop&& Bénéfice actuel >=
Bénéfice min)
{ request.sl = trailStopPrice ;
OrderSend (demande, résultat);
}
}

sinon si (posType == POSITION_TYPE_SELL)


{ trailStopPrice = SymbolInfoDouble(_Symbol,SYMBOL_ASK) +
trailStop ;currentProfit = openPrice – SymbolInfoDouble
(_Symbol,SYMBOL_ASK);

si (trailStopPrice < currentStop&& Bénéfice actuel >= Bénéfice min)


{ request.sl = trailStopPrice ;
OrderSend (demande, résultat);
}
}
}

Nous avons ajouté une variable d'entrée nomméeBénéfice minimum. La valeur par défaut de 200 signifie que le
profit minimum de la position actuelle doit être d'au moins 200 points (0,00200 ou 0,200). Nous récupérons le prix
d'ouverture de la position à l'aide duPositionGetDouble() fonctionner avec lePOSITION_PRICE_OPEN paramètre
et stockez le résultat dans leprix ouvert variable. Nous convertissons leBénéfice minimum réglage d'une valeur
de prix en la multipliant par le symbole actuel_Indiquer valeur, et stocker le résultat dans leminProfit variable.
Une variable nomméeBénéfice actuel est déclaré et sera calculé prochainement.

Si la position actuelle est une position d'achat, nous calculons le profit actuel en soustrayant le prix d'ouverture de
l'ordre.
(prix ouvert) à partir du prix acheteur actuel et en stockant le résultat dans leBénéfice actuel variable.
SiBénéfice actuel est supérieur àminProfit, et que le prix stop loss actuel est inférieur au prix stop suiveur, nous
modifions le stop loss et le fixons au prix stop suiveur.

Pour une position de vente, nous calculons le profit actuel en soustrayant le prix Ask actuel du prix d'ouverture de
l'ordre. Si le profit actuel est supérieur au profit minimum et que le prix stop loss actuel est supérieur au prix stop
suiveur, nous modifions le stop loss.

Instaurer un stop suiveur

173
Acheteur : Evangelos Papageorgiou (vpapnm@yahoo.gr)
Numéro de transaction : 28J511988U193831A

ETXPERT UNCONSEIL P.ROGRAMMATION POUR METTRADER 5


Une dernière modification du stop suiveur consiste à ajouter une valeur de pas. Le code ci-dessus modifiera le
trailing stop à chaque changement mineur de prix dans le sens du profit. Cela peut être un peu écrasant pour le
serveur d'échange, nous allons donc appliquer un pas minimum de 10 points et permettre à l'utilisateur de spécifier
une valeur de pas plus grande :

// Variables d'entrée input int


TrailingStop = 500 ; entrée int
MinimumProfit = 200 ;entrée int
Étape = 10 ;

// Gestionnaire d'événements OnTick()


if(PositionSelect(_Symbol) == true && TrailingStop > 0)
{ requête.action = TRADE_ACTION_SLTP ;
request.symbol = _Symbol;

long posType = PositionGetInteger(POSITION_TYPE); double


currentStop = PositionGetDouble(POSITION_SL); double
openPrice = PositionGetDouble(POSITION_PRICE_OPEN);

double minProfit = MinimumProfit * _Point ;


if(Étape < 10) Étape = 10 ;
double pas = Pas * _Point ;
double trailStop = TrailingStop * _Point;

double trailStopPrice ; double


bénéfice actuel ;

si (posType == POSITION_TYPE_BUY)
{
trailStopPrice = SymbolInfoDouble(_Symbol,SYMBOL_BID) - trailStop ;
currentProfit = SymbolInfoDouble(_Symbol,SYMBOL_BID) - openPrice ; si
(trailStopPrice > currentStop+ étape && currentProfit >= minProfit)
{ request.sl = trailStopPrice ;
OrderSend (demande, résultat);
}
}
sinon si (posType == POSITION_TYPE_SELL)
{ trailStopPrice = SymbolInfoDouble(_Symbol,SYMBOL_ASK) + trailStop ;
currentProfit = SymbolInfoDouble(_Symbol,SYMBOL_ASK) + openPrice ; si
(trailStopPrice < currentStop- étape && currentProfit >= minProfit)
{ request.sl = trailStopPrice ;
OrderSend (demande, résultat);
}
}
}

Nous avons ajouté une variable d'entrée nomméeÉtape, avec une valeur par défaut de 10 points. SiÉtape est réglé
sur une valeur inférieure à 10, nous définirons la valeur deÉtape à 10. Nous convertissonsÉtape à une valeur en
points en la multipliant par le symbole_Indiquer valeur, et stocker le résultat dans la variableétape. Lors de la
vérification de la condition du stop suiveur, nous ajoutons ou soustrayons leétape valeur de laarrêt actuel
variable. Cela garantit que le stop suiveur se déplace par incréments de 10 points.

174
Acheteur : Evangelos Papageorgiou (vpapnm@yahoo.gr)
Numéro de transaction : 28J511988U193831A

Stops suiveurs

La classe CTrailing
Nous allons créer une classe de stop suiveur réutilisable, suffisamment robuste et flexible pour toutes les situations
dont un trader peut avoir besoin. Nous allons créer un nouveau fichier d'inclusion pour notre classe de stop suiveur.
Le fichier est nomméTrailingStops.mqh, et sera situé dans le\MQL5\Include\Mql5Book dossier.

Voici les directives d'inclusion et la déclaration de classe en haut duTrailingStops.mqh déposer:

#include <errordescription.mqh>
#include "Trade.mqh"
classe CTrailing
{ protégé :
Requête MqlTradeRequest ;

publique:
Résultat MqlTradeResult ;
bool TrailingStop (string pSymbol, int pTrailPoints, int pMinProfit = 0, int
pStep = 10);
} ;

Nous incluons ledescriptiondel'erreur.mqh fichier du\MQL5\Inclure dossier et lecommerce.mqh fichier du


dossier actuel. LeCTrailing la déclaration de classe contient ledemande etrésultat objets que nous avons abordés
dans les chapitres précédents. Jetons un coup d'œil à nos paramètres de fonction trailing stop :

bool TrailingStop (string pSymbol, int pTrailPoints, int pMinProfit = 0, int pStep = 10);

LepSymbole Le paramètre est le symbole de la position pour laquelle nous suivons le stop.pTrailPoints est le stop
final en points.pMinProfit est le gain minimum en points, avec une valeur par défaut de 0. Enfin,pÉtape est la
valeur du pas en points. Une valeur par défaut de 10 points garantit que le stop suiveur ne sera mis à jour que sur les
mouvements de prix significatifs (c'est à dire. plus d'une fraction de pip).

Le code pour leCTrailing ::TrailingStop() la fonction peut être visualisée dans leTrailingStops.mqh
déposer. Si vous avez étudié la section précédente, elle devrait vous paraître très familière. Voici comment nous
utiliserions notreCTrailing cours chez un conseiller expert :

// #include et initialisation de l'objet


#include <Mql5Book\TrailingStops.mqh>
Sentier CTrailing ;

// Variables d'entrée
input bool UseTrailingStop = false;
entrée int TrailingStop = 0 ; entrée int
MinimumProfit = 0 ; entrée int Étape =
0 ;

// Gestionnaire d'événements OnTick()


// Placer après le code de placement de commande
if(UseTrailingStop == true && PositionType(_Symbol) != -1)
{

175
Acheteur : Evangelos Papageorgiou (vpapnm@yahoo.gr)
Numéro de transaction : 28J511988U193831A

ETXPERT UNCONSEIL P.ROGRAMMATION POUR METTRADER 5


Trail.TrailingStop(_Symbol,TrailingStop,MinimumProfit,Step);
}
Nous incluons notreTrailingStops.mqh fichier et initialiser lePiste objet basé sur notreCTrailing classe. La
section des variables d'entrée contient les variables nécessaires à notre fonction de stop suiveur. Nous avons
unbouffon variable nomméeUtiliserTrailingStop pour activer et désactiver le stop suiveur.Arrêt de fuite
définit le stop suiveur en points.Bénéfice minimum est le profit minimum de la position en points, etÉtape déplace
le stop suiveur par incréments.

Notre code stop suiveur est placé vers la fin duSurTick() gestionnaire d'événements. Si laUtiliserTrailingStop
la variable d'entrée est vraie, et notreType de position() la fonction renvoie un type de position valide,
leArrêtTrail() fonction de notreCTrailing la classe sera appelée et le stop suiveur de la position actuelle sera
modifié si nécessaire.

Utiliser un stop suiveur dynamique


Les exemples précédents utilisaient un stop suiveur fixe. Que se passe-t-il si vous souhaitez suivre un indicateur, tel
qu'une moyenne mobile ou un PSAR ? Ou peut-être souhaitez-vous suivre le prix haut ou bas d’une barre
précédente ?

Nous allons créer une autre fonction qui suivra le stop loss en fonction d'un prix spécifié. Si le prix est supérieur (ou
inférieur) au stop loss actuel, le stop loss sera suivi jusqu'à ce prix. Nous utiliserons le même nom de fonction
queArrêtTrail() fonction que nous avons définie dans la section précédente. La seule différence résidera dans les
paramètres de la fonction. (Comme vous vous en souvenez peut-être au chapitre 4, cela s'appelle la surcharge de
fonctions.)

Voici laCTrailing déclaration de classe contenant nos deux fonctions de stop final. Notez la différence entre les
paramètres des deux fonctions. La premièreArrêtTrail() fonction, décrite dans la section précédente, a unint
paramètre nommépTrailPoints. La deuxièmeArrêtTrail() la fonction remplace cela par undouble paramètre
nommépPrixTrail:

classe CTrailing
{ protégé :
Requête MqlTradeRequest ;

publique:
Résultat MqlTradeResult ;

bool TrailingStop (string pSymbol, int pTrailPoints, int pMinProfit = 0, int


pStep = 10);

bool TrailingStop(chaîne pSymbol,double pTrailPrix, int pMinProfit = 0, int pStep


= 10);
} ;

LepPrixTrail Le paramètre est le prix auquel nous suivrons le stop. Si ce prix répond à nos conditions de profit
minimum et de pas, et qu'il est plus proche du prix actuel que du stop loss actuel, alors le stop sera déplacé vers ce
prix.

176
Acheteur : Evangelos Papageorgiou (vpapnm@yahoo.gr)
Numéro de transaction : 28J511988U193831A

Stops suiveurs

Voici un exemple utilisant leArrêt et marche arrière paraboliques indicateur. Le PSAR est utilisé pour définir un stop
loss qui se rapproche progressivement du haut ou du bas de la barre actuelle. Lorsque la valeur du PSAR se
rapproche trop ou que la tendance s'inverse, le PSAR inverse la direction. Si le prix du PSAR est inférieur à la barre
actuelle, nous suivrons le stop pour une position d'achat ouverte. Si le prix du PSAR est au-dessus de la barre la plus
actuelle, alors nous suivrons le stop pour une position de vente ouverte :

// Variables d'entrée
input bool UseTrailingStop = false;
entrée int MinimumProfit = 0 ; entrée
int Étape = 0 ;

entrée double SARStep = 0,2 ; entrée


double SARMaximum = 0,02 ;

// Gestionnaire d'événements
OnTick() double close[];
ArraySetAsSeries(fermer,true);
CopierFermer(_Symbol,0,1,2,close);

// Indicateur PSAR double


sar[];
ArraySetAsSeries(sar,true);

int sarHandle = iSAR(_Symbol,0,SARStep,SARMaximum);


CopyBuffer(sarHandle,0,1,2,sar);

// Vérifiez le prix SAR par rapport au


prix de clôture bool sarSignal = false;

if((PositionType(_Symbol) == POSITION_TYPE_BUY && sar[1] < close[1])


|| (PositionType(_Symbol) == POSITION_TYPE_SELL && sar[1] > close[1]))
{ sarSignal = vrai ;
}

// Arrêt suiveur
si (UseTrailingStop == vrai && sarSignal == vrai)
{
Trail.TrailingStop(_Symbol,sar[1],MinimumProfit,Step);
}

Les variables d'entrée incluent leUtiliserTrailingStop,Bénéfice minimum etÉtape paramètres pour le stop
suiveur. (Notez que nous avons omis leArrêt de fuite variable, car le prix du PSAR déterminera la distance du
stop suiveur). LeÉtape SAR etSARMaximum les variables d’entrée sont les paramètres de l’indicateur PSAR. Le code
restant appartient auSurTick() gestionnaire d'événements ou une fonction appelée à partir de celui-ci.

Lefermer[] array contiendra le prix de clôture de chaque barre, tandis que lesar[] Le tableau contient les valeurs
de l’indicateur PSAR. Avant de modifier le trailing stop, nous devons vérifier le prix du PSAR par rapport au prix de
clôture. Si le PSAR est inférieur au prix de clôture et que la position actuelle est un achat – ou si le PSAR est supérieur
au prix de clôture et que la position actuelle est une vente – alors lesarSignal la variable est définie sur vrai.

177
Acheteur : Evangelos Papageorgiou (vpapnm@yahoo.gr)
Numéro de transaction : 28J511988U193831A

ETXPERT UNCONSEIL P.ROGRAMMATION POUR METTRADER 5


SiUtiliserTrailingStop etsarSignal sont tous les deuxvrai, nous transmettons la valeur PSAR de la barre la
plus récemment fermée
(sar[1]) auArrêtTrail() fonction, et le stop loss sera ajusté au prix du PSAR si leBénéfice minimum etÉtape les
conditions sont remplies. Le deuxième paramètre duArrêtTrail() la fonction doit être de typedouble. Si le
deuxième paramètre est une valeur entière, la compilation supposera que nous transmettons une valeur de stop
suiveur fixe et utilisera l'autreArrêtTrail() fonction à la place.

Figure 14.1 – L’indicateur Parabolic Stop and Reverse (PSAR).

Tant que le PSAR est correctement positionné par rapport au prix de clôture actuel et que les conditions de profit
minimum et/ou de palier sont remplies, le stop loss sera mis à jour pour correspondre au prix du PSAR. Vous pouvez
également le faire avec d’autres indicateurs. Assurez-vous simplement de vérifier que la valeur de l'indicateur n'est
pas supérieure au prix si vous suivez une position d'achat, ou vice versa pour une position de vente.

Arrêt de rentabilité
Parfois, un trader préfère déplacer le stop loss vers le prix d'ouverture de l'ordre lorsqu'un montant de profit spécifié
est atteint. Le stop loss n'est déplacé qu'une seule fois, contrairement à un stop suiveur, qui continue de déplacer le
stop tant que le profit de la transaction augmente. C'est ce qu'on appelle unarrêt de rentabilité.

Le principe d’un stop à seuil de rentabilité est similaire à celui d’un stop suiveur. Tout d’abord, vérifiez si la
transaction a atteint un profit minimum spécifié. Si tel est le cas, vérifiez si le stop loss est inférieur (ou supérieur) au
prix d’ouverture de la position. Si tel est le cas, le stop loss est déplacé vers le prix d’ouverture de l’ordre. Vous
pouvez utiliser un stop de rentabilité à côté d’un stop suiveur. Assurez-vous simplement d'ajuster le paramètre de
profit minimum pour les deux types de stop afin que le trailing stop ne s'active pas avant le break even stop.

Voici un exemple de la façon d'implémenter un seuil de rentabilité dans le code :

// Variables d'entrée
input bool UseBreakEven = false;
entrée int BreakEven = 0 ; entrée int
LockProfit = 0 ;

// Gestionnaire d'événements OnTick()

178
Acheteur : Evangelos Papageorgiou (vpapnm@yahoo.gr)
Numéro de transaction : 28J511988U193831A

Stops suiveurs
Requête MqlTradeRequest ;
Résultat MqlTradeResult ;
ZeroMemory (demande);

// Arrêt au seuil de rentabilité


if(UseBreakEven == true && PositionSelect(_Symbol) == true && BreakEven > 0)
{ requête.action = TRADE_ACTION_SLTP ;
request.symbol = _Symbol; demande.tp =
0 ;

long posType = PositionGetInteger(POSITION_TYPE); double


currentStop = PositionGetDouble(POSITION_SL); double openPrice
= PositionGetDouble(POSITION_PRICE_OPEN);

si (posType == POSITION_TYPE_BUY)
{ double currentPrice = SymbolInfoDouble(_Symbol,SYMBOL_BID); double
breakEvenPrice = openPrice + (LockProfit * _Point); double
currentProfit = currentPrice - openPrice ;

if(currentStop < breakEvenPrice && currentProfit >= BreakEven * _Point)


{ request.sl = breakEvenPrice ;
OrderSend (demande, résultat);
}
}
sinon si (posType == POSITION_TYPE_SELL)
{ double currentPrice = SymbolInfoDouble(_Symbol,SYMBOL_ASK); double
breakEvenPrice = openPrice - (LockProfit * _Point); double
currentProfit = openPrice - currentPrice ;

if (currentStop > breakEvenPrice && currentProfit >= BreakEven * _Point)


{ request.sl = breakEvenPrice ;
OrderSend (demande, résultat);
}
}
}

LeSeuil de rentabilité Le paramètre d'entrée est le nombre de points de profit requis pour que le stop soit
déplacé vers le prix d'équilibre. LeVerrouillerProfit Le paramètre ajoute ou soustrait le nombre spécifié de
points au prix d’équilibre. Ainsi, par exemple, si vous souhaitez déplacer le stop loss jusqu'au seuil de rentabilité + 50
points, définissez simplementVerrouillerProfit à 50.

Le code d'arrêt du seuil de rentabilité se rapproche de la fin duSurTick() gestionnaire d'événements, après le code
de placement de commande.
Si laUtiliser BreakEven le paramètre d'entrée est vrai,Seuil de rentabilité est supérieur à zéro et qu'il y a
une position actuellement ouverte, nous vérifierons la condition d'arrêt de rentabilité.

Nous récupérons le type de position actuelle, le stop loss et le prix d'ouverture, et stockons ces valeurs dans les
variablesposType,arrêt actuel etprix ouvert respectivement. Si la position actuelle est une position d'achat,
nous récupérons le prix acheteur actuel et stockons la valeur dansprix actuel. Ensuite, nous calculons le prix
d'équilibre en ajoutantVerrouillerProfit (multiplié par_Indiquer) auprix ouvert valeur et stocker le résultat

179
Acheteur : Evangelos Papageorgiou (vpapnm@yahoo.gr)
Numéro de transaction : 28J511988U193831A

ETXPERT UNCONSEIL P.ROGRAMMATION POUR METTRADER 5


dansprix d'équilibre. Ensuite, nous calculons le profit de la position actuelle en soustrayant le prix d'ouverture
de la position (prix ouvert) de l'offre (prix actuel), et stocker le résultat dansBénéfice actuel.

Enfin, nous vérifions la condition d’équilibre. Si le stop loss actuel (arrêt actuel) est inférieur au prix d'équilibre
(prix d'équilibre), et le profit de la position actuelle (Bénéfice actuel) est supérieur àSeuil de rentabilité
(multiplié par_Indiquer), puis nous déplaçons le stop loss jusqu'au prix d'équilibre.

La fonction BreakEven()
Nous avons ajouté unSeuil de rentabilité() fonction à notreCTrailing classe. Voici la déclaration de la
fonction :

bool BreakEven (string pSymbol, int pBreakEven, int pLockProfit = 0);

LepSymbole etpPauseMême des paramètres sont requis. LepLockProfit Le paramètre est facultatif. Voici comment
nous utiliserions notreSeuil de rentabilité() fonction dans un conseiller expert:

#include <Mql5Book/TrailingStops.mqh>
Sentier CTrailing ;

// Variables d'entrée
input bool UseBreakEven = false;
entrée int BreakEven = 0 ; entrée
int LockProfit = 0 ;
// Gestionnaire d'événements OnTick() if(UseBreakEven ==
true && PositionType(_Symbol) != -1)
{
Trail.BreakEven(_Symbol,BreakEven,LockProfit);
}

Le code du seuil de rentabilité se situe vers la fin duSurTick() gestionnaire d'événements. Tout ce que nous devons
faire, c'est vérifier que
Utiliser BreakEven est défini sur true et qu'un poste est actuellement ouvert. Nous utilisons notreType de
position() fonction pour vérifier une position ouverte. LeSeuil de rentabilité() la fonction vérifie le reste.

Vous pouvez consulter le code duSeuil de rentabilité() fonctionner dans le\MQL5\Include\Mql5Book\


TrailingStops.mqh déposer.

Trailing Stops sur les comptes de couverture


Comme prévu, les trailing stop doivent être traités différemment pour les comptes de couverture. Le processus de
calcul et de modification d’un ordre de trailing stop est le même – il suffit d’apporter quelques modifications pour les
ordres de couverture.

Nous avons ajouté des surcharges pour leArrêtTrail() etSeuil de rentabilité() fonctions dans leCTrailing
classe qui accepte un numéro de ticket comme premier paramètre. Voici les déclarations de fonction pour
notreArrêtTrail() etSeuil de rentabilité() fonctions pour les comptes de couverture :

classe CTrailing {
protégé :

180
Acheteur : Evangelos Papageorgiou (vpapnm@yahoo.gr)
Numéro de transaction : 28J511988U193831A

Stops suiveurs
Requête MqlTradeRequest ;

publique:
Résultat MqlTradeResult ;

bool TrailingStop (string pSymbol, int pTrailPoints, int pMinProfit = 0, int


pStep = 10); bool TrailingStop (string pSymbol, double pTrailPrice, int
pMinProfit = 0, int pStep = 10);

bool TrailingStop (ulong pTicket, int pTrailPoints, int pMinProfit = 0, int


pStep = 10); bool TrailingStop (ulong pTicket, double pTrailPrice, int
pMinProfit = 0, int pStep = 10);

bool BreakEven (string pSymbol, int pBreakEven, int pLockProfit=0);bool


BreakEven(ulong pTicket, int pBreakEven, int pLockProfit=0);
} ;
Examinons les modifications à l'aide de la fonction d'arrêt par points. Les modifications dans les fonctions restantes
sont presque identiques :

bool CTrailing::TrailingStop(tête pTicket,int pTrailPoints,int pMinProfit=0,int pStep=10)


{ si(PositionSelectByTicket(pTicket) == vrai && pTrailPoints > 0)
{ requête.action =
TRADE_ACTION_SLTP ;request.position =
pTicket;

long posType = PositionGetInteger(POSITION_TYPE); double


currentStop = PositionGetDouble(POSITION_SL); double openPrice
= PositionGetDouble(POSITION_PRICE_OPEN);symbole de chaîne =
PositionGetString(POSITION_SYMBOL);

point double = SymbolInfoDouble (symbole,SYMBOL_POINT); int


chiffres = (int)SymbolInfoInteger(symbole,SYMBOL_DIGITS);

si (pStep < 10) pStep = 10 ; double


pas = pStep * point ;

double minProfit = pMinProfit * point ; double


trailStop = pTrailPoints * point ; currentStop =
NormalizeDouble (currentStop, chiffres);

double trailStopPrice ; double


bénéfice actuel ;

// Boucle de commande
int retryCount = 0; int
checkRes = 0;

faire {if(posType == POSITION_TYPE_BUY)


{ trailStopPrice = SymbolInfoDouble (symbole,SYMBOL_BID) - trailStop ;
trailStopPrice = NormalizeDouble(trailStopPrice,digits);
currentProfit = SymbolInfoDouble (symbole,SYMBOL_BID) - prixouvert ;

if(trailStopPrice > currentStop + step && currentProfit >= minProfit)

181
Acheteur : Evangelos Papageorgiou (vpapnm@yahoo.gr)
Numéro de transaction : 28J511988U193831A

ETXPERT UNCONSEIL P.ROGRAMMATION POUR METTRADER 5


{ request.sl = trailStopPrice ;
bool sent = OrderSend (demande, résultat);
}
sinon return (faux);
}
sinon si (posType == POSITION_TYPE_SELL)
{ trailStopPrice = SymbolInfoDouble (symbole,SYMBOL_ASK) + trailStop ; trailStopPrice =
NormalizeDouble(trailStopPrice,digits); currentProfit = openPrice - SymbolInfoDouble
(symbole,SYMBOL_ASK);

if((trailStopPrice < currentStop - step || currentStop == 0)


&& currentProfit >= minProfit)
{ request.sl = trailStopPrice ; bool sent = OrderSend
(demande, résultat);
}
sinon return (faux);
} checkRes = CheckReturnCode(result.retcode);

if(checkRes == CHECK_RETCODE_OK) pause ; sinon si (checkRes ==


CHECK_RETCODE_ERROR)
{ string errDesc = TradeServerReturnCodeDescription (result.retcode);
Alert("Trailing Stop : Erreur ",result.retcode," - ",errDesc); casser;
} else { Print("Erreur de serveur détectée, nouvelle tentative...");
Sommeil (RETRY_DELAY); retryCount++;
}
}
while(retryCount < MAX_RETRIES);

si (retryCount >= MAX_RETRIES)


{ string errDesc = TradeServerReturnCodeDescription (result.retcode); Alert("Nombre maximal
de tentatives dépassé : Erreur ",result.retcode," - ",errDesc);
}

chaîne errDesc = TradeServerReturnCodeDescription (result.retcode);


Print("Trailing stop : ",result.retcode," - ",errDesc,", #",pBillet,", Ancienne SL : ",
currentStop,", Nouvelle SL : ",request.sl,", Bid : ", SymbolInfoDouble(symbol,SYMBOL_BID),",
Ask : ",
SymbolInfoDouble(symbole,SYMBOL_ASK),", Niveau d'arrêt : ",
SymbolInfoInteger(symbole,SYMBOL_TRADE_STOPS_LEVEL));

if(checkRes == CHECK_RETCODE_OK) return(true);


sinon return (faux);
}
sinon return (faux);
}

Le premier paramètre des fonctions trailing stop pour les comptes de couverture,pBillet, accepte un numéro de
ticket pour un ordre au marché ouvert. Nous utilisons lePositionSelectByTicket() fonction pour sélectionner la
commande pour récupérer les informations de commande. Puisque nous devons récupérer des informations sur le
symbole commercial actuel, nous utilisons lePositionGetString() fonctionner avec lePOSITION_SYMBOL

182
Acheteur : Evangelos Papageorgiou (vpapnm@yahoo.gr)
Numéro de transaction : 28J511988U193831A

Stops suiveurs

paramètre pour obtenir le symbole de la commande sélectionnée. Le seul autre changement est d'enregistrer le
numéro de ticket de la commande en cas d'erreur.

Nous pouvons combiner les fonctions de stop suiveur avec les fonctions d'information sur les ordres dans
leCPpositions classe pour ajuster le stop suiveur pour tous les ordres de marché ouvert :

// Déclarations d'objet
#include <Mql5Book\TradeHedge.mqh>
#include <Mql5Book\TrailingStops.mqh>

CPositions Postes ;
Sentier CTrailing ;

// Variables d'entrée
input bool UseTrailingStop = false;
entrée int TrailingStop = 0 ; entrée
int MinimumProfit = 0 ; entrée int
Étape = 0 ;

// Fin du gestionnaire d'événements OnTick()


if(UseTrailingStop == true)
{ billets de tête[];
Positions.GetTickets(MagicNumber, billets); int
numTickets = ArraySize(tickets);

pour(int i = 0; i < numTickets; i++)


{
Trail.TrailingStop(tickets[i], TrailingStop, MinimumProfit, Step);
}
}

Dans cet exemple, nous utilisons lePositions.GetTickets() fonction pour remplir ledes billets[] tableau avec
tous les tickets d’ordres du marché libre. Ensuite, nous utilisons leTaille du tableau() fonction dans MQL5 pour
renvoyer le nombre d'éléments dans ledes billets[] tableau. Pour chaque élément du tableau, nous appelons
leTrail.TrailingStop() fonction pour vérifier et modifier le stop suiveur de la commande.

Les autres fonctions de stop suiveur et d'équilibre décrites dans cette section sont similaires dans leur utilisation à
celle ci-dessus. Vous pouvez consulter le code de ces fonctions dans le\MQL5\Include\Mql5Book\
TrailingStops.mqh déposer.

Autres exemples
Au chapitre 19, nous créerons plusieurs conseillers experts qui utilisent les fonctions de stop suiveur et d'équilibre
décrites dans ce chapitre. Si vous souhaitez les voir utilisés dans leur contexte dans un programme complet de
conseillers experts, jetez un œil auBandes RSI Contre-tendance etCroix de moyenne mobile programmes dans le\
MQL5\Experts\Mql5Book dossier.

183
Acheteur : Evangelos Papageorgiou (vpapnm@yahoo.gr)
Numéro de transaction : 28J511988U193831A

ETXPERT UNCONSEIL P.ROGRAMMATION POUR METTRADER 5

Chapitre 15 - Gestion de l'argent et dimensionnement des


transactions

Jusqu'à présent, nous avons utilisé une variable d'entrée nomméeVolume des échanges pour spécifier le
volume de la commande.
Programmer un conseiller expert offre la possibilité d'utiliser des techniques automatisées de gestion financière
qui peuvent gérer votre risque et augmenter ou réduire la taille de vos transactions à mesure que le solde de
votre compte change. Nous examinerons les techniques de gestion automatique de la taille des transactions,
ainsi que la vérification de l'exactitude des volumes de transactions.

Vérification du volume des échanges


Avant de transmettre un volume d'échanges auCommandeEnvoyer() fonction, nous devrions d’abord vérifier si
elle est valide. La plupart des courtiers Forex autorisent des tailles de transaction aussi petites que 0,01 lot, mais
certains courtiers ont une taille de transaction minimale plus élevée ou n'utilisent pas de micro-lots.

Nous vérifions la taille de la transaction en la comparant aux volumes de transactions minimum et maximum
autorisés par le courtier, et en veillant également à ce qu'elle soit conforme à la valeur de pas du courtier. Voici
un exemple de la façon dont nous vérifierions l’exactitude du volume des échanges :

// Variables d'entrée
entrée double TradeVolume = 0,12 ;

// Gestionnaire d'événements OnTick()


double minVolume = SymbolInfoDouble(_Symbol,SYMBOL_VOLUME_MIN);
double maxVolume = SymbolInfoDouble(_Symbol,SYMBOL_VOLUME_MAX);
double stepVolume = SymbolInfoDouble(_Symbol,SYMBOL_VOLUME_STEP);

double taille d'échange ;


si (TradeVolume < minVolume) tradeSize = minVolume ; sinon if
(TradeVolume > maxVolume) tradeSize = maxVolume ; sinon tradeSize
= MathRound(TradeVolume / stepVolume) * stepVolume ;

if(stepVolume >= 0.1) tradeSize = NormalizeDouble(tradeSize,1);


sinon tradeSize = NormalizeDouble(tradeSize,2);

LeVolume des échanges La variable d'entrée contient la taille de notre transaction. Le code ci-dessous va dans
leSurTick() gestionnaire d’événements avant toute commande. Il sera généralement placé dans une fonction,
afin de pouvoir être réutilisé encore et encore.

LeSymboleInfoDouble() La fonction récupère des informations sur le symbole spécifié à partir du serveur
commercial. Dans l'exemple ci-dessus, nous utilisonsSymboleInfoDouble() pour récupérer le volume
d'échange minimum, le volume d'échange maximum et la taille du pas. Pour les courtiers Forex, la taille du pas

184
Acheteur : Evangelos Papageorgiou (vpapnm@yahoo.gr)
Numéro de transaction : 28J511988U193831A

Gestion de l'argent et dimensionnement des transactions

est généralement de 0,01 ou 0,1. Vous pouvez visualiser leSymboleInfo...() constantes de paramètre dans
leRéférence MQL5 sousConstantes standard... > État de l'environnement > Propriétés des symboles.

Ensuite, nous déclarons une variable nomméetaille du commerce, qui détiendra notre volume d'échanges
vérifié. Nous vérifions
Volume des échanges contre le volume minimum. SiVolume des échanges est inférieur au volume minimum,
nous l'ajustons à la taille minimale du volume. On fait de même pour le volume maximum. Puis on vérifieVolume
des échanges par rapport à la taille du pas.

Nous divisonsVolume des échanges par la taille du pas (étapeVolume), arrondissez le résultat à l’entier le plus
proche en utilisant
MathRound(), puis multipliez à nouveau par la taille du pas. Si le volume des échanges d'origine est conforme à
la taille du pas, ce calcul n'aura aucun effet. Sinon, le volume des échanges sera arrondi au pas valide le plus
proche. Dans l'exemple ci-dessus, siétapeVolume est 0,1 etVolume des échanges est 0,12, la valeur deVolume
des échanges serait arrondi à 0,1. Enfin, nous normaliserons le volume des échanges en fonction du nombre de
chiffres de la taille du pas.

Créons une fonction que nous pouvons utiliser pour vérifier et ajuster automatiquement le volume des échanges
si nécessaire. Nous allons créer un nouveau fichier d'inclusion pour contenir toutes les fonctions liées au volume
des échanges et à la gestion de l'argent que nous allons créer dans ce chapitre. Le fichier d'inclusion est
nomméMoneyManagement.mqh, et sera situé dans le\MQL5\Include\Mql5Book dossier.

Voici notre fonction de vérification du volume des échanges, située dans leMoneyManagement.mqh inclure le
fichier :

double VerifyVolume (chaîne pSymbol, double pVolume)


{ double minVolume = SymbolInfoDouble(pSymbol,SYMBOL_VOLUME_MIN);
double maxVolume = SymbolInfoDouble(pSymbol,SYMBOL_VOLUME_MAX);
double stepVolume = SymbolInfoDouble(pSymbol,SYMBOL_VOLUME_STEP);

double taille d'échange ;


if(pVolume < minVolume) tradeSize = minVolume ; sinon
if(pVolume > maxVolume) tradeSize = maxVolume; sinon tradeSize
= MathRound(pVolume / stepVolume) * stepVolume ;

if(stepVolume >= 0.1) tradeSize = NormalizeDouble(tradeSize,1);


sinon tradeSize = NormalizeDouble(tradeSize,2);

return(tradeSize);
}

LepSymbole le paramètre est le symbole commercial, etpVolume est le volume des échanges à vérifier. Cette
fonction renverra le volume des échanges ajusté au programme.

Gestion de l'argent
Gestion de l'argent est une méthode permettant d’ajuster de manière optimale la taille des positions en fonction
du risque. La plupart des traders utilisent le même volume d'échanges fixe pour chaque transaction. Cela peut
entraîner des transactions trop importantes ou trop petites pour le montant risqué.

185
Acheteur : Evangelos Papageorgiou (vpapnm@yahoo.gr)
Numéro de transaction : 28J511988U193831A

ETXPERT UNCONSEIL P.ROGRAMMATION POUR METTRADER 5


Pour calculer une taille de transaction optimale, nous utiliserons la distance entre le prix stop loss et le prix
d'entrée de la transaction ainsi qu'un pourcentage du solde actuel pour déterminer le risque maximum par
transaction. Une bonne ligne directrice est de limiter votre risque par transaction à 2 à 3 % de votre solde actuel.
Si, pour une raison quelconque, nous ne pouvons pas calculer un volume d'échanges (c'est à dire. un stop loss
ou un pourcentage n'a pas été spécifié), nous reviendrons à un volume de transactions fixe spécifié.

Créons une fonction pour notre routine de gestion financière. La fonction ira dans
leMoneyManagement.mqhinclure le fichier, et sera nomméGestion de l'argent():

#définir MAX_PERCENT 10

double MoneyManagement (chaîne pSymbol, double pFixedVol, double pPercent, int


pStopPoints)
{ double taille de
commerce ;

si (pPourcentage > 0 && pStopPoints > 0)


{ if(pPourcent > MAX_PERCENT) pPourcent =
MAX_PERCENT ;

double marge = AccountInfoDouble(ACCOUNT_BALANCE) * (pPercent / 100) ;


double tickSize = SymbolInfoDouble(pSymbol,SYMBOL_TRADE_TICK_VALUE);

tradeSize = (marge / pStopPoints) / tickSize ;


tradeSize = VerifyVolume(pSymbol,tradeSize);

return(tradeSize);
}
autr
e
{ tradeSize = pFixedVol;
tradeSize = VerifyVolume(pSymbol,tradeSize);

return(tradeSize);
}
}

LepSymbole le paramètre est le symbole commercial,pVolFixe est le volume des échanges par
défaut,pPourcentage est le pourcentage du solde actuel à utiliser, etpPoints d'arrêt est la distance du stop
loss en points. Letaille du commerceLa variable contiendra notre volume d’échanges calculé. Tout d’abord,
nous vérifions sipPourcentage etpPoints d'arrêt sont tous deux supérieurs à zéro. Dans le cas contraire,
nous ne pouvons pas calculer le volume des échanges et nous nous appuierons sur le volume des échanges par
défaut spécifié parpVolFixe.

SipPourcentage etpPoints d'arrêt sont valides, nous procéderons alors au calcul du volume des échanges.
Tout d’abord, nous comparonspPourcentage au volume maximum. Comme mentionné précédemment, il est
recommandé de limiter votre risque à 2 à 3 % maximum de votre solde. LeMAX_PERCENT constante, définie en
haut de notreMoneyManagement.mqhfichier, spécifie un risque maximum de 10 pour cent. SipPourcentage
dépasse ce chiffre, il sera alors ajusté à un maximum de 10 pour cent.

186
Acheteur : Evangelos Papageorgiou (vpapnm@yahoo.gr)
Numéro de transaction : 28J511988U193831A

Gestion de l'argent et dimensionnement des transactions

Ensuite, nous calculons le montant de la marge de risque. Nous récupérons le solde du compte à l'aide du
CompteInfoDouble() fonctionner avec leACCOUNT_BALANCE paramètre. Nous multiplions cela
parpPourcentage (divisé par 100 pour obtenir une valeur fractionnaire), et stockez le résultat dans lemarge
variable. Ensuite, nous récupérons la taille du tick du symbole depuis le serveur de trading en utilisant
leSymboleInfoDouble() fonctionner avec le
SYMBOL_TRADE_TICK_VALUE paramètre et stockez le résultat dans letickSize variable. Letaille de coche est le
montant du profit ou de la perte représenté par un mouvement d'un seul point.

Pour calculer notre volume d'échanges, nous divisons la marge de risque (marge) par le stop loss en points
(pStopPerte), et divisez ce résultat partickSize. Ensuite, nous transmettons notre volume d'échanges calculé
auVérifierVolume() fonction. Le résultat vérifié est renvoyé au programme.

Clarifions cela avec un exemple : nous souhaitons passer une commande en ne risquant pas plus de 2 % du
solde de notre compte de 5 000 $. Le stop loss initial sera placé à 500 points du prix d'ouverture de la
commande. Le symbole est EURUSD et nous utilisons des mini-lots, la taille du tick sera donc de 1 $ par point.
2 % de 5 000 $ équivaut à 100 $, cette valeur sera donc enregistrée dans lemarge variable. La valeur
dutickSize la variable sera de 1 $.

100 $ divisé par 500 points équivaut à 0,2. Chaque point de mouvement équivaudra à environ 0,20 $ de profit
ou de perte. 0,2 divisé par 1 $ équivaut à 0,2, notre volume d'échanges sera donc de 0,2 lot. Si cette transaction
de 0,2 lot atteint son stop loss initial à 500 points, la perte sera d'environ 100 $. Si la distance du stop loss est à
200 points, le volume des échanges sera de 0,5 lot, mais la perte maximale est toujours de 100 $.

Voici un exemple de la façon dont nous pouvons utiliser notre fonction de gestion financière chez un conseiller
expert :

//Inclure la directive
#include <Mql5Book/MoneyManagement.mqh>

// Variables d'entrée entrée


double RiskPercent = 2 ; entrée
double Volume Fixe = 0,1 ;
entrée int StopLoss = 500 ;

// Gestionnaire d'événements OnTick() double tradeSize =


MoneyManagement(_Symbol,FixedVolume,RiskPercent,StopLoss);

Pour utiliser notre fonction de gestion de l'argent, nous devons inclure leMoneyManagement.mqh déposer auprès
de notre conseiller expert. LePourcentage de risque La variable d'entrée est notre risque commercial en
pourcentage de la balance commerciale actuelle. Si vous ne souhaitez pas utiliser la gestion financière, définissez
cette valeur sur zéro.Volume Fixe est le volume d'échange fixe à utiliser siPourcentage de risque
ouStopPerte est zéro.

Letaille du commerce La variable contiendra notre volume d’échanges calculé. LeGestion de l'argent()
La fonction prendra les variables d'entrée spécifiées et renverra le volume des échanges calculé et vérifié. On
peut alors passer letaille du commercevariable à l’une de nos fonctions de passation de commandes telles
que définies dans les chapitres précédents.

187
Acheteur : Evangelos Papageorgiou (vpapnm@yahoo.gr)
Numéro de transaction : 28J511988U193831A

ETXPERT UNCONSEIL P.ROGRAMMATION POUR METTRADER 5


Les exemples ci-dessus supposent un stop loss fixe spécifié par une variable d'entrée. Que se passe-t-il si vous
souhaitez utiliser un prix stop loss dynamique, tel qu'une valeur indicatrice ou un prix de support/résistance ?
Nous devrons calculer la distance entre le prix d’ouverture de l’ordre souhaité (soit un prix d’ordre en attente,
soit le prix acheteur ou vendeur actuel) et le prix stop loss souhaité.

Par exemple, nous souhaitons ouvrir une position d’achat. Le prix Ask actuel est de 1,39426 et le prix stop loss
que nous souhaitons utiliser est de 1,38600. La différence entre les deux est de 826 points. Nous allons créer une
fonction qui déterminera la différence en points entre le prix d'ouverture souhaité et le prix stop loss :

double StopPriceToPoints (chaîne pSymbol, double pStopPrice, double pOrderPrice)


{ double stopDiff = MathAbs(pStopPrice - pOrderPrice);
double getPoint = SymbolInfoDouble(pSymbol,SYMBOL_POINT);
double priceToPoint = stopDiff / getPoint ;
return(prixVersPoint);
}

LepStopPrix Le paramètre est notre prix stop loss souhaité, etpPrixCommande est le prix d'ouverture de
commande souhaité. Tout d'abord, la fonction calcule la différence entrepStopPrix etpPrixCommande, et
renvoie la valeur absolue en utilisant leMathAbs() fonction. Nous stockons le résultat dans learrêterDiff
variable. Ensuite, nous récupérons la valeur en points du symbole et la stockons dans la variableobtenirPoint.
Enfin, nous divisonsarrêterDiff parobtenirPoint pour trouver la distance de stop loss en points.

Voici un exemple de la façon dont nous pouvons procéder dans le code. Nous supposerons que
lestopPertePrix variable est égale à 1,38600, et que le prix Ask actuel est de 1,39426 :

double stopLossPrice = 1,38600 ;


double currentPrice = SymbolInfoDouble(_Symbol,SYMBOL_ASK);
double stopLossDistance = StopPriceToPoints(_Symbol,stopLossPrice,currentPrice)
double tradeSize = MoneyManagement(_Symbol,FixedVolume,RiskPercent,stopLossDistance);

LestopPertePrix La variable contient notre prix stop loss souhaité de 1,38600, tandis que leprix actuel La
variable maintient le prix vendeur actuel de 1,39426. LestopLossDistance La variable contiendra la valeur de
retour duArrêterPrixVersPoints() fonction, qui vaut 826. On passe ensuitestopLossDistance comme
dernier paramètre duGestion de l'argent() fonction. En supposant unPourcentage de risque valeur de
2% et un solde de 5 000 $ en utilisant des lots standard, le volume des échanges sera de 0,08 lot.

Tant que vous avez spécifié un stop loss initial, leGestion de l'argent() La fonction peut être utilisée pour
limiter votre risque commercial à un pourcentage spécifié du solde de votre compte. Vous pouvez même utiliser
un stop loss dynamique en utilisant leArrêterPrixVersPoints() fonction pour calculer le stop loss en points.
À mesure que le solde de votre compte augmente ou diminue, la taille de la transaction augmentera ou
diminuera en conséquence.

188
Acheteur : Evangelos Papageorgiou (vpapnm@yahoo.gr)
Numéro de transaction : 28J511988U193831A

Données sur les barres et les prix

Chapitre 16 - Données sur les barres et les prix

Dans MQL4, nous utilisons des tableaux prédéfinis tels queFermer[] etTemps[] pour les données en barres et
les variables prédéfiniesOffre etDemander pour accéder aux tarifs actuels. MQL5 ne dispose pas de ces
tableaux et variables prédéfinis, mais nous pouvons créer des méthodes simples pour accéder à ces données.
Nous examinerons les différentes manières d'accéder aux données de barres et de prix dans MQL5, et créerons
des classes et des fonctions pour faciliter l'utilisation des prix.

Accéder aux prix actuels


Plus tôt dans ce livre, nous avons utilisé leSymboleInfoDouble() fonction pour récupérer les prix Bid et Ask
actuels. LeSymboleInfo...() les fonctions nécessitent que vous spécifiiez le symbole pour lequel récupérer
les informations, ainsi qu'une valeur de l'un desENUM_SYMBOL_INFO_... des énumérations. Voici comment
récupérer le prix acheteur et vendeur actuel en utilisantSymboleInfoDouble():

double demande = SymbolInfoDouble(_Symbol,SYMBOL_ASK);


double enchère = SymbolInfoDouble(_Symbol,SYMBOL_BID);

Ledemander etoffre les variables contiennent les prix acheteur et vendeur actuels pour le symbole graphique
actuel.

Mais en me rappelant lequelSymboleInfo...() fonction à utiliser, ainsi que la bonneENUM_SYMBOL_INFO_...


l'identifiant peut être gênant. Nous allons créer deux fonctions rapides pour récupérer les prix Bid et Ask
actuels :

double demande (chaîne pSymbol = NULL)


{ if(pSymbol == NULL) pSymbol = _Symbol;
return(SymbolInfoDouble(pSymbol,SYMBOL_ASK))
;
}

double offre (chaîne pSymbol=NULL)


{ if(pSymbol == NULL) pSymbol = _Symbol;
return(SymbolInfoDouble(pSymbol,SYMBOL_BID))
;
}

Les deux fonctions contiennent un paramètre,pSymbole, avec une valeur par défaut deNUL. La constanteNUL fait
référence à une chaîne vide. Si aucun symbole n'est spécifié pour lepSymbole paramètre, la fonction utilisera le
symbole du graphique actuel (_Symbole). Le symbole spécifié est transmis auSymboleInfoDouble() fonction,
et le résultat est renvoyé au programme.

Réécrivons notre exemple de code précédent avec nos nouvelles fonctions :

double demande =
Demander (); double
enchère = Enchère();

189
Acheteur : Evangelos Papageorgiou (vpapnm@yahoo.gr)
Numéro de transaction : 28J511988U193831A

ETXPERT UNCONSEIL P.ROGRAMMATION POUR METTRADER 5


Les deux fonctions renvoient le prix acheteur ou vendeur actuel pour le symbole graphique actuel.

La structure MqlTick
Une autre méthode pour récupérer les prix actuels consiste à utiliser leSymboleInfoTick() fonction et un objet
duMqlTick type de structure.MqlTick est une structure contenant des variables pour contenir les prix actuels
de l'offre, de la demande, de l'heure, du volume et de la dernière transaction :

structure MqlTick
{
dateheure heure ; // Heure de la dernière mise à jour des prix
double bid ; // Prix acheteur actuel double demande ; // Prix
demandé actuel double dernier ; // Prix de la dernière transaction
(Dernier) volume ulong ; // Volume pour le dernier prix actuel };

Les informations ci-dessus proviennent duRéférence MQL5. Pour utiliser leMqlTick structure pour récupérer
les prix actuels, créez d'abord un objet en utilisantMqlTick comme le type. Ensuite, passez l'objet
auSymboleInfoTick() fonction. Voici la définition de la fonction pourSymboleInfoTick() duRéférence
MQL5:

bool SymbolInfoTick (symbole de chaîne, MqlTick& tick);

Lecocher le paramètre est unMqlTick objet passé par référence. Les variables membres de l'objet sont
remplies avec les données de prix les plus récentes. Voici un exemple de la façon dont nous pouvons
utiliserSymboleInfoTick() etMqlTick pour récupérer les prix actuels :

Prix MqlTick ;
SymbolInfoTick(_Symbol,prix);

double demande =
prix.demande ; double offre
= prix.offre ; dateheure
heure = prix.heure ;

Nous déclarons un objet nomméprix, en utilisantMqlTick comme le type. Nous passons leprix s'opposer à la
SymboleInfoTick() fonction, spécifiant le symbole du graphique actuel pour le premier paramètre. La
fonction renvoie leprix objet avec les variables membres remplies avec les informations de prix et de temps
actuelles. Les exemples ci-dessous qui montrent les prix Ask et Bid actuels, ainsi que l'heure du serveur la plus
récente, récupérés à l'aide des variables membres duprix objet.

C'est à vous de décider si vous souhaitez utiliserSymboleInfoTick() pour récupérer les informations de prix
actuelles. LeOffre() etDemander() les fonctions que nous avons définies plus tôt dans le chapitre sont
cependant beaucoup plus simples.

Données nues
Comme mentionné au début du chapitre, MQL4 dispose de tableaux prédéfinis qui contiennent les données
d'ouverture, de clôture, de haut, de bas, de temps et de volume pour chaque barre du graphique. Dans MQL5,

190
Acheteur : Evangelos Papageorgiou (vpapnm@yahoo.gr)
Numéro de transaction : 28J511988U193831A

Données sur les barres et les prix

nous devrons créer et remplir ces tableaux nous-mêmes. Il y a plusieursCopie...() fonctions que nous
pouvons utiliser pour récupérer des données de barre. Commençons parTaux de copie().

CopyRates() et la structure MqlRates


Tarifs Mql est une structure qui contient des variables contenant des informations sur le prix, le temps, le
volume et le spread pour chaque barre. Il est utilisé comme type de tableau. Voici la définition de la structure
pourTarifs Mql duRéférence MQL5 :
struct
MqlRates {
dateheure heure ; // Heure de début de période
double ouverture; // Prix ouvert
double hauteur; // Le prix le plus élevé de la
période
double bas; // Le prix le plus bas de la
période
double fermeture; // Clôture du prix
long tick_volume ; // Volume des ticks
propagation int; // Propagé
long volume_réel ; } ; // Volume des échanges

Pour utiliser leTarifs Mql structure, nous déclarons un objet tableau de typeTarifs Mql et passez l'objet
auTaux de copie() fonction pour remplir le tableau avec des données. Il existe plusieurs variantes duTaux de
copie() fonction, mais nous ne nous occuperons que de la première, qui nous oblige à préciser une position de
départ et le nombre de barres de données à copier :

int CopyRates (string nom_symbole, // nom du symbole


Période ENUM_TIMEFRAMES, // période int start_pos, //
position de départ int count, // nombre de données à
copier MqlRates rate_array[] // tableau cible à copier
);

Lenom_symbole le paramètre est le symbole graphique à utiliser,laps de temps est la période du graphique à
utiliser,start_pos est l'index de la barre de départ (0 est la barre la plus récente),compter est le nombre de
barres à copier dans le tableau, ettaux_array[] est notreTarifs Mql objet.

Montrons comment utiliserTarifs Mql etTaux de copie() pour copier les données de la barre dans un objet
tableau. Nous n’aurons généralement besoin que des données pour les barres les plus récentes – trois est un
bon nombre.

Barre MqlRates[];
ArraySetAsSeries(barre,true);
CopyRates(_Symbol,_Period,0,3,bar);

double ouverture = bar[1].open;


double fermeture =
barre[1].close;

Nous déclarons d’abord l’objet tableaubar[], en utilisantTarifs Mql comme le type. Ensuite, nous utilisons
leArraySetAsSeries() fonction pour indexer lebar[] tableau pour une série de prix. Pour un tableau en série,

191
Acheteur : Evangelos Papageorgiou (vpapnm@yahoo.gr)
Numéro de transaction : 28J511988U193831A

ETXPERT UNCONSEIL P.ROGRAMMATION POUR METTRADER 5


l'élément le plus récent est indexé à 0. À mesure que nous augmentons l'index du tableau, nous remontons
dans le temps. Un tableau non série serait le contraire de cela. Il est important de toujours utiliser
leArraySetAsSeries() fonction pour définir un tableau en tant que série avant d’y copier des données de prix
ou d’indicateur.

Ensuite, nous appelons leTaux de copie() fonction. Nous utilisons le symbole du graphique actuel (_Symbole)
et la période (_Période). Nous commençons la copie à l'index 0, ou à la barre la plus récente. Nous copierons 3
barres de données. Le dernier paramètre est le nom de notrebar[] tableau, qui contiendra nos données de
barre.

Pour accéder aux données de la barre, nous référençons lebar[] tableau avec l’index approprié entre
parenthèses. Nous utilisons ensuite l'opérateur point (.) pour accéder aux variables membres de l'objet tableau.
Par exemple, pour récupérer le cours de clôture de la dernière barre, on utilisebarre[1].fermer.

Nous pouvons simplifier un peu cela en créant une classe qui récupérera les données de la barre pour nous.
Nous nommerons la classeBarres CB. Cette classe, et les autres fonctions que nous allons créer dans ce
chapitre, iront dans un nouveau fichier d'inclusion nomméPrix.mqh, situé dans la\MQL5\Include\Mql5Book\
dossier. Voici la déclaration de classe pourBarres CB:

classe
CBars
{ public :
Barre MqlRates[];
Barres CB (vide);
void Update (chaîne pSymbol, ENUM_TIMEFRAMES pPeriod);
} ;

Tous les membres duBarres CB la classe serapublique. Lebar[] array contiendra nos données de prix.
LeBarres CB (vide) function est notre constructeur de classe, une fonction qui s'exécute automatiquement à
chaque fois qu'un objet est créé. Voici le constructeur duBarres CB classe:

CBars ::CBars (vide)


{
ArraySetAsSeries(barre,true);
}

LeBarres CB le constructeur de classe définit lebar[] tableau en tant que tableau en série. Cela se fait
automatiquement chaque fois que nous créons un objet basé sur la classe. LeMise à jour() la fonction
appelle leTaux de copie() fonction et remplit lebar[]tableau avec des données. Voici la déclaration de
fonction pourCBars :: Mise à jour ():

#définir MAX_BARS 100

void CBars :: Update (string pSymbol, ENUM_TIMEFRAMES pPeriod)


{
CopyRates(pSymbol,pPeriod,0,MAX_BARS,bar);
}

192
Acheteur : Evangelos Papageorgiou (vpapnm@yahoo.gr)
Numéro de transaction : 28J511988U193831A

Données sur les barres et les prix

Au moins une fois dansSurTick() gestionnaire d'événements de notre programme, nous appellerons leMise à
jour() fonction pour remplir lebar[] tableau avec les données les plus récentes. LeMAX_BARS constante définit
le nombre de barres de données à copier chaque fois que nous appelons leMise à jour() fonction. Nous
l'avons réglé à 100 bars.

Nous devons maintenant ajouter quelques fonctions conviviales pour récupérer les prix. Voici une fonction qui
récupérera le prix de clôture de la barre indiquée :

double CBars :: Fermer (int pShift = 0)


{ return(bar[pShift].close);
}

La fonction récupère simplement la valeur debarre[].fermer pour la valeur d'index spécifiée. La valeur par
défaut pour lepShift Le paramètre est 0, donc si aucun paramètre n'est transmis à la fonction, elle récupérera
le prix de la barre actuelle.

Nous disposons de plusieurs autres fonctions pour récupérer les informations de haut, de bas, d'ouverture, de
temps et de volume. Voici le completBarres CB déclaration de fonction avec toutes les fonctions nécessaires :

classe
CBars
{ public :
Barre MqlRates[];
Barres CB (vide);

void Update (chaîne pSymbol, ENUM_TIMEFRAMES pPeriod);


double Fermer (int pShift);
double High(int pShift);
double bas (int pShift);
double ouverture (int
pShift); dateheure Heure
(int pShift); long
TickVolume(int pShift); long
Volume (int pShift);
} ;

Notez que nos noms de fonctions correspondent à chacune des variables duTarifs Mql structure. Montrons
comment nous pouvons utiliser leBarres CB cours chez un conseiller expert. Nous devrons d'abord créer un
objet basé sur leBarres CB classe. Puis leMise à jour() La fonction devra être appelée avant que nous
puissions accéder aux données de prix. Enfin, nous utilisons soit les fonctions de récupération de prix, soit
lesbar[] tableau pour récupérer les prix :

// Inclure les directives


#include <Mql5Book/Price.mqh>
Prix des barres ;

// Gestionnaire d'événements OnTick()


Prix.Update(_Symbol,_Period);

193
Acheteur : Evangelos Papageorgiou (vpapnm@yahoo.gr)
Numéro de transaction : 28J511988U193831A

ETXPERT UNCONSEIL P.ROGRAMMATION POUR METTRADER 5


double fermeture = Prix.Close(); double aussiClose = Price.bar[0].close;
// Même valeur que la variable de fermeture

Nous incluons lePrix.mqh fichier et créez un objet nomméPrix, basé sur notreBarres CB classe. Dans
leSurTick() gestionnaire d'événements, avant d'accéder à des données de prix, nous appelons lePrix.Mise à
jour() fonction pour récupérer les données de barre pour le symbole et la période actuels du graphique.

Ensuite, nous illustrons deux manières d’accéder aux données sur les prix. Le moyen le plus simple est d'utiliser
les fonctions de récupération de prix que nous avons définies dans leBarres CB classe. Lefermer La variable
contient le prix de clôture de la barre actuelle et est récupérée à l'aide de laPrix.Fermer() fonction.
LeaussiFermer La variable contient également le prix de clôture de la barre actuelle et est récupérée à l'aide de
labar[] tableau avec lefermer variable membre.

Le fichier\MQL5\Experts\Mql5Book\Simple Expert Advisor avec Functions.mq5 a été mis à jour pour


utiliser leBarres CB fonctions pour accéder au cours de clôture :

// Inclure les directives


#include <Mql5Book\Price.mqh>
Prix des barres ;

// Le gestionnaire
d'événements OnTick() annule
OnTick()
{
// Moyenne mobile double
ma[];
ArraySetAsSeries(ma,true)
;

int maHandle=iMA(_Symbol,0,MAPeriod,MODE_LWMA,0,PRICE_CLOSE);
CopieBuffer(maHandle,0,0,1,ma);

// Mettre à jour les prix


Prix.Update(_Symbol,_Period);

// Informations sur la position


actuelle long positionType =
PositionType();

// Ordre d'achat ouvert au marché


si(Prix.Fermer() > ma[0] && glBuyPlaced == false && positionType != POSITION_TYPE_BUY) {
//...
}

Autres fonctions Copier...()


Si vous avez simplement besoin de copier une seule série de données dans un tableau, vous pouvez utiliser
l'une des fonctions suivantes :

• double CopieFermer() - Copie uniquement les prix de clôture.

194
Acheteur : Evangelos Papageorgiou (vpapnm@yahoo.gr)
Numéro de transaction : 28J511988U193831A

Données sur les barres et les prix

• double CopieOuverte() - Copie uniquement les prix d'ouverture.

• double CopieHaute() - Copie uniquement les prix élevés.

• double CopieLow() - Copie uniquement les prix bas.

• dateheure CopyTime() - Copie l'horodatage de chaque barre.

• longue CopieTickVolume() - Copie le volume de tick de chaque barre.

• longue CopieRealVolume() - Copie le volume des échanges de chaque barre.

• int CopieSpread() - Copie les valeurs de répartition de chaque barre.

Ceux-ci fonctionnent de manière similaire àTaux de copie() fonction. Tout d’abord, déclarez un tableau du
type approprié. Le type requis pour chaque fonction est répertorié ci-dessus avant le nom de la fonction.
Ensuite, définissez-le comme tableau de série en utilisantArraySetAsSeries(). Utilisez ensuite leCopie..()
fonction pour copier les données dans le tableau.

Nous allons démontrer en utilisant leCopierFermer() fonction. Voici la définition de la fonction


pourCopierFermer():

int CopyClose( string symbol_name, // nom du symbole


ENUM_TIMEFRAMES timeframe, // période int start_pos, // position
de départ int count, // nombre de données à copier double
close_array[] // tableau cible à copier );
Les paramètres sont presque identiques àTaux de copie(). Il existe plusieurs variantes de la fonction qui vous
permettent également de spécifier des dates de début et de fin. Vous pouvez les consulter dans leRéférence
MQL5 sousAccès aux séries temporelles et aux indicateurs. Voici un exemple d'utilisation duCopierFermer()
fonction:

double fermeture[];

ArraySetAsSeries(fermer,true);

CopierFerme(_Symbol,_Period,0,3,close);

double courantFermer = fermer[0];

Nous déclarons un tableau de typedouble nomméfermer[] cela maintiendra nos prix de clôture. Ensuite, nous
le définissons comme un tableau série en utilisantArraySetAsSeries(). Finalement, leCopierFermer() La
fonction copie trois barres de données dans lefermer[] tableau. Pour récupérer la valeur de la barre actuelle,
nous référençons l'indice zéro de lafermer[] tableau.

L'autreCopie...() les fonctions fonctionnent de la même manière, sauf que le type de tableau sera différent.
Se référer auRéférence MQL5 pour plus de détails. Si vous n’avez besoin que d’une seule série de prix, ces
fonctions pourraient mieux vous convenir. Nous utiliserons leBarres CB classe que nous avons créée dans la
section précédente pour le reste de ce livre.

Prix les plus élevés et les plus bas

195
Acheteur : Evangelos Papageorgiou (vpapnm@yahoo.gr)
Numéro de transaction : 28J511988U193831A

ETXPERT UNCONSEIL P.ROGRAMMATION POUR METTRADER 5


Parfois, vous devrez peut-être déterminer la valeur la plus élevée ou la plus basse d’une série de prix. Par
exemple, vous souhaiterez peut-être placer un stop loss pour un ordre d'achat au plus bas du dernierX barres.
Dans MQL4, les fonctionsiLe plus haut() etiLe plus bas() accompli ces tâches pour nous. Dans MQL5,
nous devrons faire un peu plus de travail.

En supposant que vos données de prix soient déjà copiées dans un tableau, leTableauMaximum() et
TableauMinimum() les fonctions peuvent être utilisées pour trouver la valeur la plus élevée ou la plus basse
d’une série de prix. Voici comment trouver le plus bas des 10 dernières barres :

double bas[]; ArraySetAsSeries(faible,

vrai);

CopierLow(_Symbol,_Period,0,100,low);

int minIdx = ArrayMinimum(low,0,10);


double le plus bas = faible[minIdx] ;

Nous déclarons d'abord lefaible[] tableau qui contiendra nos données de prix. Nous définissons le tableau
comme une série et utilisons leCopierBas() fonctions pour copier 100 barres de données de prix bas pour le
symbole et la période du graphique actuel dans notrefaible[] tableau.

LeTableauMinimum() la fonction prend comme premier paramètre le tableau à rechercher. Le deuxième


paramètre est la position de départ et le troisième est le nombre de mesures à rechercher. En commençant par
la barre actuelle, nous regardons 10 barres en arrière pour trouver le plus bas. LeTableauMinimum() La
fonction renvoie la valeur d'index de la barre avec le plus bas. Nous sauvegardons cette valeur dans leminIdx
variable. Un appel àfaible[minIdx] renverra le prix le plus bas trouvé par leTableauMinimum() fonction.

Vous pouvez utiliserTableauMinimum() ouTableauMaximum() sur n’importe quel tableau numérique


unidimensionnel. Le plus souvent, nous rechercherons le plus haut et le plus bas du dernierX barres. Nous
pouvons créer quelques fonctions simples qui renverront le plus haut ou le plus bas d'une plage de barres
spécifiée :

double HighestHigh (chaîne pSymbol, ENUM_TIMEFRAMES pPeriod, int pBars, int pStart = 0)
{ double hauteur[];
ArraySetAsSeries (élevé,
vrai);

int copié = CopyHigh(pSymbol,pPeriod,pStart,pBars,high); if(copié


== -1) return(copié);

int maxIdx = ArrayMaximum (élevé);


double le plus élevé =
élevé[maxIdx] ;

retour (le plus élevé);


}

Cette fonction renvoie le prix le plus élevé pour la plage de barres spécifiée. LepSymbole le paramètre est le
symbole à utiliser, etpPériode est la période du graphique à utiliser. LepBarres le paramètre est le nombre de

196
Acheteur : Evangelos Papageorgiou (vpapnm@yahoo.gr)
Numéro de transaction : 28J511988U193831A

Données sur les barres et les prix

mesures à rechercher, etpDémarrer est le lieu de départ. La valeur par défaut depDémarrer est 0, qui est la
barre actuelle.

Nous déclarons lehaut[] tableau et définissez-le comme série, puis utilisez leCopieHaute() fonction pour
copier les prix élevés dans notre tableau. LepDémarrer Le paramètre détermine d'où nous commençons à
copier les données, tandis que le paramètrepBarres Le paramètre détermine la quantité de données dans notre
tableau. Notez la ligne après leCopieHaute() fonction – si la copie échoue pour une raison quelconque, nous
renvoyons un-1 au programme appelant pour indiquer une erreur.

Quand nous appelons leTableauMaximum() fonction, il nous suffit de spécifier le nom du tableau, car il contient
déjà toutes les données que nous devons rechercher. L'index du tableau contenant le plus haut niveau est
enregistré dans lemaxIdx variable. On utilise alorsélevé[maxIdx] pour récupérer le plus haut et le renvoyer au
programme appelant.

Il y a aussiLe plus bas () fonction qui fonctionne de manière identique à laLe plus élevé() fonction ci-
dessus. Les deux fonctions peuvent être visualisées dans le\MQL5\Include\Mql5Book\Price.mqh inclure le
fichier.

Signaux de trading basés sur les prix


Vous utiliserez fréquemment le prix de clôture, haut ou bas d'une barre dans le cadre d'une condition
d'ouverture d'ordre, ou pour calculer un stop loss, un take profit ou le prix d'un ordre en attente. Généralement,
nous travaillons avec les prix des deux barres les plus récentes.

Par exemple, si vous disposez d'un système de trading dans lequel vous devez vérifier le prix de clôture par
rapport à une ligne indicatrice, vous récupérerez le prix de la barre fermée la plus récemment et le comparerez
à la valeur de l'indicateur de la même barre. Notre simple conseiller expert que nous avons créé plus tôt dans le
livre a comparé le prix de clôture de la barre actuelle à la valeur moyenne mobile actuelle :

si(fermer[0] > ma[0] && glBuyPlaced == faux


&& (positionType != POSITION_TYPE_BUY || openPosition == false))
{
// Position d'achat ouverte
}

Mais peut-être souhaitez-vous attendre la fermeture du bar avant d’ouvrir la commande. Dans ce cas, nous
devrons vérifier la moyenne mobile et les valeurs de clôture de la barre précédente. L'index du tableau de la
barre actuelle est 0, donc l'index de la barre précédente serait 1 :

si(fermer[1] > ma[1] && glBuyPlaced == faux


&& (positionType != POSITION_TYPE_BUY || openPosition == false))
{
// Position d'achat ouverte
}

Nous pourrions définir notre condition d'ouverture de commande encore plus explicitement en nous assurant
que le prix de clôture dépasse la moyenne mobile dans la dernière barre. Une façon de procéder consiste à
vérifier si le prix de clôture de la barre précédente était inférieur à la valeur moyenne mobile :

197
Acheteur : Evangelos Papageorgiou (vpapnm@yahoo.gr)
Numéro de transaction : 28J511988U193831A

ETXPERT UNCONSEIL P.ROGRAMMATION POUR METTRADER 5


si(fermer[1] > ma[1] && fermer[2] <= ma[2]
&& (positionType != POSITION_TYPE_BUY || openPosition == false))
{
// Position d'achat ouverte
}

Étant donné qu'une position d'achat ne s'ouvrira que si le prix de clôture est supérieur à la moyenne mobile de
la barre la plus actuelle et que le prix de clôture était inférieur à la moyenne mobile de la barre précédente, nous
n'avons plus besoin de vérifier la valeur de la position d'achat.glAcheterPlacé variable. Cela fonctionne mieux
si vous avez une condition de trading qui indique toujours un signal de trading haussier ou baissier à tout
moment.

Modèles de bougies
Une autre utilisation des données sur les prix consiste à détecter les modèles de bougies. De
nombreux traders utilisent des modèles de chandeliers japonais tels que le doji,
l'engloutissement, le harami, les marteaux/étoiles filantes, etc. Nous utilisons les données de
prix pour identifier ces modèles sur un graphique.

Prenons par exemple le modèle engloutissant. Un modèle engloutissant est un modèle


d’inversion dans lequel le corps d’une bougie est plus long que le corps de la bougie
précédente et la direction est inversée. Par exemple, vous pouvez voir une bougie
engloutissante au bas d'un
tendance à la baisse. La bougie précédente sera baissière, tandis que la bougie la plus récente
sera baissière. Figure 16.1 –haussier. L'ouverture de la bougie la plus récente sera à peu près égale à la clôture de
la Engloutissement haussier
bougie.
bougie précédente, et la clôture de la bougie la plus récente sera supérieure à l'ouverture de la
bougie précédente.

Lorsque nous négocions une configuration de bougie, nous rechercherons une bougie de confirmation dans la
direction de la tendance anticipée. Dans le cas de notre modèle de retournement haussier engloutissant, nous
recherchons une bougie haussière qui suit notre bougie engloutissante haussière.

Exprimons cela comme une condition commerciale. En supposant que nous ayons deux tableaux,fermer[]
etouvrir[], rempli de données sur les prix, voici comment nous localiserions une tendance haussière
engloutissante chez un conseiller expert :

if(close[3] < open[3] && close[2] > open[3] && close[1] > open[1])
{
Print("Modèle d'inversion engloutissant haussier détecté");
}

Nous recommençons trois bougies en arrière avec la bougie baissière. La conditionfermer[3] < ouvrir[3]
signifie que la bougie est baissière – en d’autres termes, la clôture était inférieure à l’ouverture. Ensuite, nous
vérifions la bougie engloutissante. Le prix d'ouverture d'une bougie est généralement identique (ou très proche)
au prix de clôture de la bougie précédente, nous ne prendrons donc pas la peine de vérifier l'ouverture de la

198
Acheteur : Evangelos Papageorgiou (vpapnm@yahoo.gr)
Numéro de transaction : 28J511988U193831A

Données sur les barres et les prix

bougie engloutissante. La chose importante à vérifier est si la clôture est supérieure à l'ouverture de la barre
précédente, ou sifermer[2] > ouvrir[3]. Si tel est le cas, nous avons alors une bougie engloutissante.

Enfin, sifermer[1] > ouvrir[1], alors la bougie la plus récente est haussière. Si les trois bougies
correspondent à la condition, un message s'imprime dans le journal indiquant qu'une tendance haussière
engloutissante a été détectée sur le graphique. Bien sûr, vous pouvez également ouvrir une position d’achat.

En comparant les valeurs d'ouverture et de clôture d'une barre, vous pouvez déterminer si une bougie est
haussière ou baissière.
Vous pouvez également détecter des modèles de bougies en examinant les valeurs d'ouverture, haute, basse et
fermée de bougies consécutives.

199
Acheteur : Evangelos Papageorgiou (vpapnm@yahoo.gr) ID de transaction : 28J511988U193831A
Acheteur : Evangelos Papageorgiou (vpapnm@yahoo.gr)
Numéro de transaction : 28J511988U193831A

Utiliser des indicateurs dans les conseillers experts

Chapitre 17 - Utilisation d'indicateurs dans les conseillers


experts

Presque tous les conseillers experts utilisent des indicateurs pour générer des signaux de trading. Dans ce chapitre,
nous examinerons comment utiliser les indicateurs intégrés de MetaTrader dans vos conseillers experts, ainsi que
les indicateurs personnalisés que vous pouvez trouver en ligne ou dans les outils de MetaTrader 5.\MQL5\
Indicateurs\Exemples dossier. Nous créerons des classes pour simplifier l'utilisation des indicateurs et
examinerons certains signaux de trading courants basés sur des indicateurs.

Indicateurs de tampon unique


Plus tôt dans le livre, nous avons utilisé un indicateur de moyenne mobile dans notre simple conseiller expert. Voici
le code pour ajouter l'indicateur de moyenne mobile à un conseiller expert :

// Variables d'entrée input


int MAPeriod = 10; entrée
int MAShift = 0 ;
entrée ENUM_MA_METHOD MAMethod = MODE_EMA ;
saisissez ENUM_APPLIED_PRICE MAPrice =
PRICE_CLOSE ;

// Gestionnaire d'événements OnTick()


double ma[]; ArraySetAsSeries(ma,true);

int maHandle = iMA(_Symbol,_Period,MAPeriod,MAShift,MAMethod,MAPrice);


CopieBuffer(maHandle,0,0,3,ma);
double courantMA = ma[0];

Les variables d'entrée incluent quatre paramètres pour ajuster les paramètres de moyenne mobile :Période
MAPajuste la période de l’indicateur.MAShift déplace l’indicateur vers l’avant ou vers l’arrière sur le
graphique.Méthode MAmodifie la méthode de calcul de l'indicateur (simple, exponentiel, etc.), etMAPrix sélectionne
la série de prix utilisée pour calculer la moyenne mobile (clôture, ouverture, etc.).

201
Acheteur : Evangelos Papageorgiou (vpapnm@yahoo.gr)
Numéro de transaction : 28J511988U193831A

ETXPERT UNCONSEIL P.ROGRAMMATION POUR METTRADER 5

Figure 17.1 – Une moyenne mobile exponentielle sur 30 périodes sur un graphique journalier.
Dans leSurTick() gestionnaire d'événements, nous initialisons leet[] tableau, qui contiendra nos données de
moyenne mobile. LeArraySetAsSeries() la fonction définit leet[] tableau en tant que tableau en série.
Leavoir() La fonction prend les variables d'entrée et calcule l'indicateur pour le symbole et la période de graphique
spécifiés. Chaque indicateur MetaTrader intégré a une fonction similaire. Vous pouvez les consulter dansRéférence
MQL5 sousIndicateurs techniques.

Voici la définition de la fonction pour leavoir() indicateur:

int iMA(
symbole de chaîne, // nom du symbole
ENUM_TIMEFRAMES période, // période int ma_période, // période
de moyenne int ma_shift, // décalage horizontal ENUM_MA_METHOD
ma_method, // type de lissage
ENUM_APPLIED_PRICE apply_price // type de prix ou descripteur
);

Chaque fonction d'indicateur technique commence par lesymbole etpériode paramètres. Les paramètres restants
dépendent de l'indicateur. L'indicateur de moyenne mobile comporte quatre paramètres qui peuvent être ajustés.
Certains indicateurs n’ont qu’un ou deux paramètres, et quelques-uns n’en ont aucun. Voici à nouveau les variables
d'entrée pour l'indicateur de moyenne mobile :

// Variables d'entrée input


int MAPeriod = 10; entrée
int MAShift = 0 ;
entrée ENUM_MA_METHOD MAMethod = MODE_EMA ;
saisissez ENUM_APPLIED_PRICE MAPrice =
PRICE_CLOSE ;

Notez le type de chaque paramètre. Par exemple, lema_méthode paramètre duavoir() la fonction utilise
leENUM_MA_METHOD taper. Par conséquent, le paramètre d’entrée correspondant doit être du même type. Dans ce

202
Acheteur : Evangelos Papageorgiou (vpapnm@yahoo.gr)
Numéro de transaction : 28J511988U193831A

Utiliser des indicateurs dans les conseillers experts


cas, leMéthode MA la variable d'entrée utilise leENUM_MA_METHOD type, c'est donc la valeur que nous passerons
auma_méthode paramètre.

En examinant la déclaration de fonction de la fonction d'indicateur technique spécifique que vous devez utiliser,
vous pouvez déterminer les paramètres d'entrée que vous devrez ajouter à votre conseiller expert. Notez que les
quatre variables d'entrée de l'indicateur de moyenne mobile correspondent aux paramètres de l'indicateur de
moyenne mobile.avoir() fonction.

Leavoir() La fonction, ainsi que toutes les autres fonctions d'indicateur, renvoie un indicateurpoignée, qui est un
identifiant unique pour l’indicateur. Ce handle est utilisé pour toutes les actions liées à l’indicateur, y compris la copie
de données et la mise hors service de l’indicateur.

LeCopieBuffer() La fonction est utilisée pour copier les données du tampon indicateur spécifié dans un tableau. Le
tableau peut ensuite être utilisé pour accéder aux données de l'indicateur.CopieBuffer() fonctionne de manière
similaire àCopie...() fonctions du chapitre précédent. Voici la définition de la fonction pour leCopieBuffer()
fonction:

int CopieBuffer(
int indicateur_handle, // descripteur d'indicateur int
buffer_num, // numéro de tampon d'indicateur int start_pos, //
position de départ int count, // nombre de barres à copier
double tampon // tableau cible vers lequel copier
);

Leindicateur_handle Le paramètre accepte le handle d’indicateur renvoyé par leavoir() ou autre fonction
d'indicateur. Lenuméro_tampon Le paramètre est le numéro du tampon d'indicateur à partir duquel vous souhaitez
copier les données. La plupart des indicateurs n’ont qu’un seul tampon, même si certains en ont deux, trois ou plus.
Plus d'informations sur cela dans une minute. Les paramètres restants devraient vous être familiers. Lestart_pos Le
paramètre est la position de départ à partir de laquelle commencer la copie,compter est le nombre de mesures à
copier, ettampon est le nom du tableau vers lequel copier.

Reprenons notre exemple d'indicateur de moyenne mobile :

int maHandle = iMA(_Symbol,_Period,MAPeriod,MAShift,MAMethod,MAPrice);

CopieBuffer(maHandle,0,0,3,ma); double courantMA = ma[0];

Leavoir() La fonction renvoie un handle d'indicateur et l'enregistre dans lemaPoignée variable. LeCopieBuffer()
la fonction prend lemaPoignée valeur comme premier paramètre. Le numéro du tampon est zéro, puisque la
moyenne mobile n'a qu'une seule ligne. Nous commençons par la barre la plus récente (indice de 0) et copions 3
barres de données dans leet[] tableau. Leet[] Le tableau est maintenant rempli de données et prêt à être utilisé.
Un appel àet[0] renverra la valeur moyenne mobile pour la barre actuelle.

Jetons un coup d'œil à un deuxième indicateur qui n'utilise qu'un seul tampon. L'indicateur RSI est très populaire et
est souvent utilisé pour localiser les prix extrêmes. Voici la définition de la fonction de l'indicateur RSI :

203
Acheteur : Evangelos Papageorgiou (vpapnm@yahoo.gr)
Numéro de transaction : 28J511988U193831A

ETXPERT UNCONSEIL P.ROGRAMMATION POUR METTRADER 5


int iRSI(
symbole de chaîne, // nom du symbole
Période ENUM_TIMEFRAMES, // période
int ma_period, // période de moyenne
ENUM_APPLIED_PRICE apply_price // type de prix ou descripteur
);

Tout comme leavoir() fonction, les deux premiers paramètres sontsymbole etpériode. Le RSI prend deux
paramètres d'entrée, un pour la période de moyenne (ma_période) et un autre pour la série de prix
(prix_appliqué). Soit dit en passant, toute fonction d'indicateur ayant leprix_appliqué Le paramètre peut
recevoir un handle d'indicateur à la place, ce qui signifie que vous pouvez utiliser une série de prix dérivée d'un
autre indicateur.

Figure 17.2 – L’indicateur RSI.

Voici comment nous ajouterions l’indicateur RSI à un conseiller expert :

// Paramètres d'entrée input


int RSIPeriod = 10;
entrée ENUM_APPLIED_PRICE RSIPrice = PRICE_CLOSE ;

// Gestionnaire d'événements OnTick()


doublersi[]; ArraySetAsSeries(rsi,true);

int handle = iRSI(_Symbol,_Period,RSIPeriod,RSIPrice); CopieBuffer(poignée,0,0,3,rsi);

doublersi = rsi[0]; double


dernierRsi = rsi[1];

Nous déclarons les variables d'entréePériode RSIP etRSIPPrix avec les types appropriés au début de notre
programme. À l'intérieur deSurTick() gestionnaire d'événements, nous déclarons lersi[] tableau et définissez-le
comme un tableau en série. Nous transmettons nos paramètres d'entrée auiRSI() fonction et enregistrez la
poignée de l'indicateur dans lepoignée variable. Enfin, nous passons lepoignée variable à laCopieBuffer()
fonction et trois barres de données sont copiées dans lersi[]

204
Acheteur : Evangelos Papageorgiou (vpapnm@yahoo.gr)
Numéro de transaction : 28J511988U193831A

Utiliser des indicateurs dans les conseillers experts


tableau.

Lersi etposséderRsi les variables reçoivent les valeurs RSI pour la barre actuelle et la barre précédente. Vous
pouvez comparer ces variables pour déterminer si le RSI augmente ou diminue, par exemple.

Indicateurs multi-tampon
Lorsque nous utilisons des indicateurs avec plusieurs lignes (et plusieurs tampons), nous devrons utiliser plusieurs
tableaux etCopieBuffer() fonctions pour obtenir toutes les données dans notre programme. Commençons par
l'indicateur stochastique. Le stochastique est un oscillateur similaire au RSI. Outre la ligne stochastique principale,
une deuxième ligne de signal est également présente.

Figure 17.3 – L’indicateur stochastique. La ligne pointillée rouge est la ligne de signal.

Voici comment nous ajouterions l’indicateur stochastique à un conseiller expert :

// Paramètres d'entrée
input int KPeriod = 10;
entrée int DPériode = 3 ;
entrée int Ralentissement =
3 ;
entrée ENUM_MA_METHOD StochMethod = MODE_SMA ;
entrée ENUM_STO_PRICE StochPrice = STO_LOWHIGH ;

// Gestionnaire d'événements
OnTick() double main[], signal[];
ArraySetAsSeries(main,true);
ArraySetAsSeries(signal,true);

int handle = iStochastic(_Symbol,_Period,KPeriod,DPeriod,Slowing,StochMethod,StochPrice);


CopyBuffer(handle,0,0,3,main);
CopyBuffer(poignée,1,0,3,signal);

double courantStoch = main[0]; double


courantSignal = signal[0];

205
Acheteur : Evangelos Papageorgiou (vpapnm@yahoo.gr)
Numéro de transaction : 28J511988U193831A

ETXPERT UNCONSEIL P.ROGRAMMATION POUR METTRADER 5


L'indicateur stochastique comporte cinq paramètres d'entrée et deux tampons. Nous déclarons les
tableauxprincipal[] et
signal[] pour contenir les données de l’indicateur stochastique. Les deux tableaux sont définis comme des
tableaux en série à l'aide du
ArraySetAsSeries() fonction. LeiStochastique() La fonction initialise l'indicateur et ramène la poignée de
l'indicateur à la positionpoignée variable. Voici la définition de la fonction pouriStochastique():

int iStochastique(
symbole de chaîne, // nom du symbole
ENUM_TIMEFRAMES période, // période int Kpériode, // K-
période int Dpériode, // D-période int ralentissement, //
lissage final ENUM_MA_METHOD ma_method, // type de lissage
ENUM_STO_PRICE price_field // méthode de calcul stochastique
);
Comparez les variables d'entrée ci-dessus aux paramètres duiStochastique() fonction et notez les types
dema_méthode etchamp_prix paramètres. LeENUM_STO_PRICE L'énumération permet de définir la méthode de
calcul de l'indicateur stochastique (soitbas/haut oufermer/fermer).

Si vous regardez leiStochastique() entrée dans leRéférence MQL5, vous verrez cette note dans la description :

Note
Les numéros de tampon : 0 - MAIN_LINE, 1 – SIGNAL_LINE.

Ce sont les numéros tampons pour les deux lignes indicatrices stochastiques. Nous transmettons le numéro du
tampon aunuméro_tampon paramètre duCopieBuffer() fonction. LeCopieBuffer() La fonction copiera les
données de ce tampon indicateur dans le tableau spécifié. Nous devrons faire ceci pour chaque tampon de
l'indicateur :

CopyBuffer(poignée,0,0,3,principal);
CopyBuffer(poignée,1,0,3,signal);

Les numéros de tampon sont mis en évidence en gras. Le contenu du tampon 0 (MAIN_LINE) sont copiés dans
leprincipal[] tableau et le contenu du tampon 1 (SIGNAL_LINE) sont copiés dans lesignal[] tableau.
Leprincipal[] Le tableau contient les valeurs de la ligne stochastique principale (également appeléeLigne %K),
tandis que lesignal[] Le tableau contient les valeurs de la ligne de signal (également appeléeLigne %D).

La procédure pour ajouter un indicateur multi-tampon est la même. Notez les numéros de tampon dans leRéférence
MQL5 entrée pour la fonction d'indicateur correspondante. Déclarez ensuite le nombre approprié de tableaux,
définissez-les en série, initialisez l'indicateur et copiez chacun des tampons dans le tableau correspondant.

La classe CIndicator
Nous allons créer une classe pour simplifier le processus d'ajout d'indicateurs à un programme. Nous
commencerons par créer une classe de base contenant toutes les variables et fonctions partagées par chaque

206
Acheteur : Evangelos Papageorgiou (vpapnm@yahoo.gr)
Numéro de transaction : 28J511988U193831A

Utiliser des indicateurs dans les conseillers experts


indicateur. Nous créerons ensuite de nouvelles classes dérivées de cette classe de base qui répondent aux besoins
spécifiques de chaque indicateur.

Tous les indicateurs intégrés que nous avons abordés dans la section précédente ont quelques points communs.
Chaque indicateur utilise au moins un tableau pour contenir les données du tampon. Ce tableau doit être défini
comme un tableau en série. Nous avons besoin d'une variable pour maintenir la poignée de l'indicateur. Et nous
avons besoin d'un moyen de copier les données du tampon et d'accéder au tableau. Enfin, nous devons pouvoir
supprimer l'indicateur de notre programme si nécessaire.

Notre classe de base d’indicateurs accomplira toutes ces tâches. Nous allons nommer notre classe de
baseCIndicateur. Il sera placé dans le\MQL5\Include\Mql5Book\Indicators.mqh inclure le fichier. Ce fichier
sera utilisé pour contenir toutes nos classes et fonctions liées aux indicateurs.

Voici la déclaration de classe pour leCIndicateur classe:

classe CIndicator {
protégé :
poignée int ; double
principal[];

publique:
CIndicateur (vide); double
Principal(int pShift=0);
Annuler la version ();
virtual int Init() { return(poignée); }
} ;

LeCIndicateur La classe a deux variables protégées et quatre fonctions publiques. Leprotégé mot-clé signifie que
le contenu de la variable n'est accessible qu'à l'intérieur duCIndicateur classe et dans les classes dérivées.
Lepoignée La variable contiendra la poignée de l'indicateur et laprincipal[] Le tableau contiendra les données du
tampon.

Il existe quatre fonctions publiques. LeCIndicateur() function est le constructeur de classe. Lorsqu'un objet basé
sur cette classe est créé, le constructeur est exécuté automatiquement. Jetons un coup d'oeil àCIndicateur
constructeur de classe :

CIndicator :: CIndicator (vide)


{
ArraySetAsSeries(main,true);
}

Le constructeur de classe définit simplement leprincipal[] tableau sous forme de série en utilisant
leArraySetAsSeries() fonction. Chaque fois que nous créons un objet basé sur leCIndicateur classe,
leprincipal[] array est déclaré et défini comme un tableau série.

207
Acheteur : Evangelos Papageorgiou (vpapnm@yahoo.gr)
Numéro de transaction : 28J511988U193831A

ETXPERT UNCONSEIL P.ROGRAMMATION POUR METTRADER 5


Vient ensuite lePrincipal() fonction:

double CIndicator :: Main (int pShift = 0)


{
CopyBuffer(handle,0,0,MAX_COUNT,main);
valeur double = NormalizeDouble(main[pShift],_Digits); retour
(valeur);
}

En supposant que nous disposons déjà d’un descripteur d’indicateur valide, lePrincipal() La fonction copie les
données du tampon dans leprincipal[] tableau et accède aux données de la barre spécifiée par lepShift
paramètre. Avant de renvoyer la valeur, nous utilisons leNormaliserDouble() fonction pour arrondir la valeur au
nombre de chiffres significatifs du prix du symbole. Nous allons écrire une fonction qui créera le handle de
l'indicateur lorsque nous commencerons à créer des classes dérivées.

Ensuite, nous avons leLibérer() fonction. Cette fonction libère simplement l'indicateur de la mémoire si nous n'en
avons plus besoin :

void CIndicator :: Release (void)


{
IndicateurRelease (poignée);
}

Et enfin, lechaleur() function est une fonction virtuelle dont la fonctionnalité sera définie dans nos classes dérivées.
En déclarant la fonction commevirtuel, nous disons au compilateur que le programmeur doit définir lechaleur()
fonctionner dans toutes les classes dérivées. Nous aborderons les fonctions virtuelles plus loin dans le chapitre :

virtuel int Init()


{ return(poignée);
}

Classes dérivées
LeCIndicateur La classe sera la base des classes dérivées qui seront utilisées pour initialiser et récupérer les
données des indicateurs intégrés de MetaTrader. Étant donné que la mise en œuvre de chaque indicateur sera
différente, nous devrons créer une classe distincte pour chaque indicateur que nous souhaitons utiliser.

Commençons par l'indicateur de moyenne mobile. La moyenne mobile n'a qu'un seul tampon, nous pouvons donc
créer une classe dérivée avec un minimum d'effort. Voici la déclaration de classe pour leCiMA classe:

class CiMA : public CIndicator


{ public :
int Init (string pSymbol, ENUM_TIMEFRAMES pTimeframe, int pMAPeriod, int
pMAShift, ENUM_MA_METHOD pMAMethod, ENUM_APPLIED_PRICE pMAPrice);
} ;

208
Acheteur : Evangelos Papageorgiou (vpapnm@yahoo.gr)
Numéro de transaction : 28J511988U193831A

Utiliser des indicateurs dans les conseillers experts


LeCiMA la classe a une fonction publique,chaleur(), qui est utilisé pour initialiser l’indicateur. Noter la: publique
CIndicateur après le nom de la classe. Cela indique que leCiMA la classe est dérivée deCIndicateur. LeCiMA La
classe hérite de toutes les fonctions et variables publiques et protégées de laCIndicateur classe.

Rappelez-vous que lechaleur() la fonction est déclarée comme fonction virtuelle dansCIndicateur. Cela signifie
que toutes les classes dérivées deCIndicateur doit mettre en œuvre lechaleur() fonction. Si le programmeur
tente de compiler le programme sans implémenter lechaleur() fonction, une erreur se produira.

Voici la déclaration de fonction pour leCiMA::Init() fonction:

int CiMA::Init (string pSymbol, ENUM_TIMEFRAMES pTimeframe, int pMAPeriod, int


pMAShift, ENUM_MA_METHOD pMAMethod, ENUM_APPLIED_PRICE pMAPrice)
{ handle = iMA(pSymbol,pTimeframe,pMAPeriod,pMAShift,pMAMethod,pMAPrice);
retour (poignée);
}

Lechaleur() la classe appelle simplement leavoir() fonction et enregistre la poignée de l'indicateur dans
lepoignée variable qui a été déclarée dans leCIndicateur classe. Il ne nous reste plus qu'à accéder aux données
de moyenne mobile à l'aide duPrincipal() fonction qui a été déclarée dansCIndicateur.

Voici comment nous utiliserions leCiMA classe (et sa classe parent,CIndicateur) chez un conseiller expert :

// Créer un objet basé sur la classe CiMA


#include <Mql5Book\Indicators.mqh>
MA CiMA ;

// Variables d'entrée input int


MAPeriod = 10; entrée ENUM_MA_METHOD
MAMethod = 0 ; entrée int MAShift =
0 ;
saisissez ENUM_APPLIED_PRICE MAPrice = 0 ;

// Gestionnaire d'événement OnInit()


MA.Init(_Symbol,_Period,MAPeriod,MAShift,MAMethod,MAPrice);
// Gestionnaire d'événements
OnTick() double currentMA =
MA.Main();

En haut du fichier de code source de notre conseiller expert, nous créons un objet basé sur leCiMA classe. Nous
nommons cet objetET. Les variables d'entrée nécessaires à l'ajustement des paramètres de moyenne mobile sont
également déclarées en haut du fichier.

Lechaleur() la fonction est appelée depuis lesurInit() gestionnaire d'événements. Rappelez-vous


quesurInit() s'exécute une fois, lors de la première initialisation de l'Expert Advisor. Il suffit d'initialiser l'indicateur

209
Acheteur : Evangelos Papageorgiou (vpapnm@yahoo.gr)
Numéro de transaction : 28J511988U193831A

ETXPERT UNCONSEIL P.ROGRAMMATION POUR METTRADER 5


une seule fois, donc de le placer danssurInit() fonctionnera très bien. Vous pouvez également le placer dans
leSurTick() gestionnaire d'événements sans effets néfastes.

Pour récupérer la valeur moyenne mobile de la barre actuelle, il suffit d'appeler lePrincipal() fonction avec l’index
à barres comme paramètre facultatif. Depuis que nous avons appeléPrincipal() sans paramètre, il renverra la
valeur de la barre actuelle. Pour récupérer la valeur de la barre précédente, par exemple, on appelleraitMA.Main(1).
Le\MQL5\ Experts\Mql5Book\Simple Expert Advisor avec Functions.mq5 le fichier a été mis à jour avec
leCiMA classe pour accéder aux données de moyenne mobile.

Mis à part les variables d'entrée, nous pouvons initialiser un indicateur en seulement deux lignes de code : la
déclaration d'objet au début du fichier et lechaleur() fonction pour initialiser l’indicateur. Nous pouvons ensuite
accéder aux valeurs des indicateurs à l’aide de fonctions faciles à retenir.

Créons une deuxième classe d'indicateurs, en utilisant un indicateur avec plusieurs tampons. Nous avons discuté de
l'indicateur stochastique plus tôt dans le chapitre, qui comporte deux tampons. Nous devrons déclarer tous les
tableaux de tampons supplémentaires dans notre déclaration de classe. Le constructeur de classe définira les
tableaux supplémentaires comme des tableaux en série. Enfin, nous devrons ajouter des fonctions pour accéder aux
tableaux supplémentaires. Voici la déclaration de classe pour leCiStochastique classe:

class CiStochastic : public CIndicator


{privé : double signal[] ;

publique:
int Init (string pSymbol, ENUM_TIMEFRAMES pTimeframe, int pKPeriod, int pDPeriod,
int pSlowing, ENUM_MA_METHOD pMAMethod, ENUM_STO_PRICE pPrice); double signal (int
pShift=0);
CiStochastic (vide);
} ;

Avec lechaleur() fonction qui a été déclarée commevirtuel fonctionner dans leCIndicateur classe,
leCiStochastique la classe compte quelques membres supplémentaires. Un tableau privé nommésignal[]
conservera les valeurs de l'indicateur pour le signal ou%D doubler. Le publicSignal() La fonction sera utilisée pour
accéder à ces données. Nous avons également ajouté un constructeur de classe :

CiStochastic :: CiStochastic (vide)


{
ArraySetAsSeries(signal,true);
}

Tout comme le constructeur duCIndicateur classe, leCiStochastique le constructeur définit simplement


lesignal[] tableau en tant que tableau en série. Nous devrons faire cela pour chaque indicateur comportant plus
d’un tampon.

Voici les déclarations de fonction pour lechaleur() etSignal() les fonctions:

210
Acheteur : Evangelos Papageorgiou (vpapnm@yahoo.gr)
Numéro de transaction : 28J511988U193831A

Utiliser des indicateurs dans les conseillers experts


int CiStochastic :: Init (string pSymbol, ENUM_TIMEFRAMES pTimeframe, int pKPeriod, int
pDPeriod, int pSlowing, ENUM_MA_METHOD pMAMethod, ENUM_STO_PRICE pPrice)
{ handle = iStochastic (pSymbol, pTimeframe, pKPeriod, pDPeriod, pSlowing, pMAMethod,
pPrice); retour (poignée);
}
double CiStochastic :: Signal (int pShift = 0)
{
CopyBuffer(handle,1,0,MAX_COUNT,signal);
valeur double = NormalizeDouble(signal[pShift],_Digits); retour
(valeur);
}

Lechaleur() La fonction transmet les paramètres d'entrée auiStochastique() fonction et enregistre la poignée
de l'indicateur dans lepoignée variable. LeSignal() La fonction copie les données du tampon 1 de l'indicateur
stochastique vers lesignal[] tableau et renvoie la valeur de la barre spécifiée. Nous devrons ajouter une fonction
similaire pour chaque tampon d'indicateur supplémentaire dans nos classes d'indicateurs multi-tampon.

Nous ajoutons l'indicateur stochastique à notre conseiller expert de la même manière que nous avons ajouté la
moyenne mobile : déclarez un objet, initialisez l'indicateur et accédez aux données de l'indicateur :

// Crée un objet basé sur la classe CiStochastic


#include <Mql5Book\Indicators.mqh>
CiStochastique Stoch ;

// Variables d'entrée input


int KPeriod = 10; entrée
int DPériode = 3 ; entrée
int Ralentissement = 3 ;
entrée ENUM_MA_METHOD StochMethod = MODE_SMA ;
entrée ENUM_STO_PRICE StochPrice = STO_LOWHIGH ;

// Gestionnaire d'événement OnInit()


Stoch.Init(_Symbol,_Period,KPeriod,DPeriod,Slowing,StochMethod,StochPrice);

// Gestionnaire d'événements OnTick()


double currentStoch = Stoch.Main();
double courantSignal = Stoch.Signal();

Nous avons ajouté l'indicateur stochastique à notre conseiller expert en utilisant seulement quelques lignes de code.
Nous avons déclaré un objet basé sur leCiStochastique classe nomméeStoch. Nous avons initialisé l'indicateur
dans lesurInit() gestionnaire d'événements utilisantchaleur(). Et nous avons deux fonctions,Principal()
etSignal(), pour récupérer les données de l'indicateur. LecourantStoch etsignal actuel les variables
contiennent le%K et%D valeurs de ligne pour la barre actuelle.

211
Acheteur : Evangelos Papageorgiou (vpapnm@yahoo.gr)
Numéro de transaction : 28J511988U193831A

ETXPERT UNCONSEIL P.ROGRAMMATION POUR METTRADER 5


Initialisation d'objet
Pour affiner votre compréhension du fonctionnement des classes dérivées, passons en revue la création de
notreCiStochastique objet. Tout d’abord, nous initialisons l’objet :

CiStochastique Stoch ;

Le programme initialise les membres protégés et publics duCIndicateur classe. Les variables et objets répertoriés
ci-dessous feront partie duCiStochastique classe, et les membres du public sont accessibles via leStoch objet:

classe CIndicator {
protégé :
poignée int ; double
principal[];

publique:
CIndicateur (vide); double
Principal(int pShift=0);
Annuler la version ();
} ;

Ensuite, leCIndicateur le constructeur de classe s'exécute. Leprincipal[] array est défini comme un tableau
série :

CIndicator :: CIndicator (vide)


{
ArraySetAsSeries(main,true);
}

LeCiStochastique les membres de la classe sont initialisés ensuite :

class CiStochastic : public CIndicator


{privé : double signal[] ;

publique:
int Init (string pSymbol, ENUM_TIMEFRAMES pTimeframe, int pKPeriod, int pDPeriod,
int pSlowing, ENUM_MA_METHOD pMAMethod, ENUM_STO_PRICE pPrice); double signal (int
pShift=0);
CiStochastic (vide);
} ;

Ensuite, le constructeur de classe pour leCiStochastique la classe s'exécute. Lesignal[] array est défini comme
un tableau série :

CiStochastic :: CiStochastic (vide)

212
Acheteur : Evangelos Papageorgiou (vpapnm@yahoo.gr)
Numéro de transaction : 28J511988U193831A

Utiliser des indicateurs dans les conseillers experts


{
ArraySetAsSeries(signal,true);
}

LeStoch l'objet est prêt à l'emploi. Lepoignée variable, ainsi que laprincipal[] etsignal[] Les tableaux sont des
membres protégés ou privés qui ne sont pas accessibles au programmeur. Nous interagissons avec ces membres en
utilisant nos fonctions publiques,chaleur(),Principal(),Signal() etLibérer().

Le\MQL5\Include\Mql5Book\Indicators.mqh contient des classes pour les indicateurs intégrés les plus
populaires pour MetaTrader 5. Si vous devez créer une classe pour un indicateur qui n'est pas répertorié, vous
pouvez le faire en utilisant les techniques répertoriées dans ce chapitre.

Indicateurs personnalisés
Vous pouvez également utiliser des indicateurs personnalisés dans vos conseillers experts. Pour utiliser un
indicateur personnalisé, vous devrez déterminer le nombre de tampons dans l'indicateur, ainsi que leur utilisation.
Vous devrez également déterminer les noms et les types des paramètres de l'indicateur.

Vous pouvez télécharger des indicateurs personnalisés en ligne via la base de code MQL5, le marché MQL5 ou à
partir des forums et sites Web liés à MetaTrader. Il sera beaucoup plus facile de travailler avec un indicateur
personnalisé si vous disposez du fichier MQ5 correspondant. Même si vous ne possédez que l'EX5, il est toujours
possible d'utiliser l'indicateur dans votre projet, même si cela demandera un peu plus de travail de détective.

Utilisons l'un des indicateurs personnalisés fournis en standard avec MetaTrader 5. LeIndicateurs personnalisés
arbre dans leNavigateur la fenêtre a unExemples sous-arbre contenant des exemples d'indicateurs personnalisés
de nombreux indicateurs MetaTrader intégrés, ainsi que quelques extras. Nous utiliserons l'indicateur personnalisé
BB, qui trace les bandes de Bollinger sur le graphique.

Lors de l'examen d'un indicateur personnalisé,


ouvrez la fenêtre Données (Ctrl+D) pour voir
combien de lignes sont tracées sur le
graphique, ainsi que les étiquettes qui leur
sont attribuées, le cas échéant. L'indicateur BB
comporte trois lignes, qui sont
étiquetéesMilieu,Supérieur etInférieur.

Ouvrez le\Indicateurs\Exemples\
BB.mq5fichier dans MetaEditor. Nous devons
savoir combien de tampons ou de tracés
l'indicateur utilise, comme Figure 17.4 –
L’indicateur personnalisé BB.
ainsi que les numéros de tampon correspondants. Regarder dans
lesurInit() gestionnaire d'événements pour leSetIndexBuffer() les fonctions. Cela attribue les tableaux définis
dans le programme à des numéros de tampon d'indicateur spécifiques :

SetIndexBuffer(0,ExtMLBuffer);

213
Acheteur : Evangelos Papageorgiou (vpapnm@yahoo.gr)
Numéro de transaction : 28J511988U193831A

ETXPERT UNCONSEIL P.ROGRAMMATION POUR METTRADER 5


SetIndexBuffer(1,ExtTLBuffer);
SetIndexBuffer(2,ExtBLBuffer);
SetIndexBuffer(3,ExtStdDevBuffer,INDICATOR_CALCULATIONS);

Le premier paramètre duSetIndexBuffer() la fonction est le numéro du tampon. L'indicateur comporte trois
lignes, mais le code affiche quatre tampons ! Noter laINDICATOR_CALCULATIONS paramètre pour le tampon 3. Cela
indique que ce tampon est utilisé uniquement pour les calculs internes. Nous devrons faire un peu plus de
recherche pour comprendre ce que fait chaque tampon.

Regardez juste en dessous dans le fichier et vous en verrez troisPlotIndexSetString() les fonctions. Ceux-ci sont
utilisés pour définir les étiquettes dans la fenêtre de données :

PlotIndexSetString(0,PLOT_LABEL,"Bands("+string(ExtBandsPeriod)+") Milieu");
PlotIndexSetString(1,PLOT_LABEL,"Bands("+string(ExtBandsPeriod)+") Upper");
PlotIndexSetString(2,PLOT_LABEL,"Bands("+string(ExtBandsPeriod)+") Lower");

À partir de ceux-ci, nous pouvons en déduire que le tampon 0 est la ligne médiane, le tampon 1 est la bande
supérieure et le tampon 2 est la bande inférieure. Maintenant que nous connaissons nos numéros de tampon,
déterminons les paramètres d'entrée de l'indicateur des bandes de Bollinger. Nous pouvons trouver les variables
d’entrée juste en haut du fichier :

entrée int InpBandsPeriod=20 ; // Entrée de période int


InpBandsShift=0; // Décalage de l'entrée double
InpBandsDeviations=2.0; // Déviation

Nous avons trois paramètres d'entrée - deuxint variables pour la période et l'équipe, et undouble variable pour
l’écart. N'hésitez pas à copier et coller les variables d'entrée du fichier d'indicateurs personnalisés vers votre fichier
d'expert-conseil. Vous pouvez modifier les noms des variables d'entrée dans votre programme si vous le souhaitez.

La fonction iCustom()
LeiPersonnalisé() Cette fonction fonctionne de la même manière que les fonctions d'indicateur intégrées
examinées précédemment dans ce chapitre. Voici la définition de la fonction duRéférence MQL5:

int iPersonnalisé(
symbole de chaîne, // nom du symbole
Période ENUM_TIMEFRAMES, // période
nom de chaîne // dossier/nom_indicateur personnalisé
... // liste des paramètres d'entrée de l'indicateur
);
Tout comme les autres fonctions d'indicateur technique, leiPersonnalisé() les paramètres commencent
parsymbole etpériode. Lenom Le paramètre correspond au nom de l'indicateur, moins l'extension du fichier. Le
fichier de l'indicateur doit se trouver dans le dossier\MQL5\Indicateurs dossier. Si le fichier se trouve dans un
sous-dossier, alors le nom du sous-dossier doit précéder le nom de l'indicateur, séparé par une double barre
oblique (\\). Par exemple, l'indicateur BB est situé dans le\MQL5\ Indicateurs\Exemples dossier. Donc la valeur

214
Acheteur : Evangelos Papageorgiou (vpapnm@yahoo.gr)
Numéro de transaction : 28J511988U193831A

Utiliser des indicateurs dans les conseillers experts


pour lenom le paramètre serait "Exemples\\BB". (N'oubliez pas que la double barre oblique (\\) est le caractère
d'échappement pour une barre oblique inverse !)

Le . . . dans leiPersonnalisé() La définition de la fonction est l'endroit où vont les paramètres d'entrée de
l'indicateur personnalisé. L'indicateur personnalisé BB a trois paramètres d'entrée. Nous devons transmettre ces
paramètres auiPersonnalisé() fonctionnent dans l’ordre dans lequel ils apparaissent dans le fichier des
indicateurs :

entrée int InpBandsPeriod=20 ; // Entrée de période int


InpBandsShift=0; // Décalage de l'entrée double
InpBandsDeviations=2.0; // Déviation

Il faudrait passer trois paramètres de typeint,int etdouble auiPersonnalisé() fonction. Voici un exemple de la
façon dont nous utiliserions leiPersonnalisé() fonction pour ajouter l'indicateur BB à un expert-conseil :

// Variables d'entrée
entrée int BandsPeriod = 20 ; // Entrée de période
int BandsShift = 0; // Décalage de l'entrée double
BandsDeviation = 2 ; // Déviation

// Gestionnaire d'événements OnTick()


double upper[], lower[], middle[];
ArraySetAsSeries (supérieur, vrai);
ArraySetAsSeries (inférieur, vrai);
ArraySetAsSeries(milieu,true); int bbHandle = iCustom(_Symbol,_Period,"Examples\\

BB",BandsPeriod,BandsShift,BandsDeviation);

CopyBuffer(bbHandle,0,0,3,milieu);
CopieBuffer(bbHandle,1,0,3,upper);
CopyBuffer(bbHandle,2,0,3,inférieur);

double bbMid = milieu[0]; double


bbUp = supérieur[0]; double
bbLow = inférieur[0];

Les variables d'entrée sont présentées dans l'ordre dans lequel elles apparaissent dans le fichier des indicateurs BB.
Nous les avons renommés enBandePériode,BandesShift, etBandesDéviation. Ensuite, nous déclarons les trois
tableaux que nous utiliserons pour contenir les données de l'indicateur (supérieur[],inférieur[] etmilieu[]), et
définissez-les sous forme de tableaux en série. LeiPersonnalisé() La fonction initialise l'indicateur BB et renvoie
notre poignée d'indicateur, en l'enregistrant dans lebbPoignée variable. Notez que nous utilisons"Exemples\\BB"
pour lenom paramètre dans leiPersonnalisé() fonction, puisque l'indicateur BB est situé dans le\Indicateurs\
Exemples sous-dossier.

215
Acheteur : Evangelos Papageorgiou (vpapnm@yahoo.gr)
Numéro de transaction : 28J511988U193831A

ETXPERT UNCONSEIL P.ROGRAMMATION POUR METTRADER 5


Noter laBandePériode,BandesShift, etBandesDéviation paramètres dans leiPersonnalisé() appel de
fonction. Vous devez transmettre tous les paramètres d'entrée de l'indicateur personnalisé dans l'ordre dans lequel
ils apparaissent auiPersonnalisé() fonction. Les paramètres doivent également être du type de données
approprié – la majorité des indicateurs utilisent soitint,double ouchaîne les types. Si vous ne souhaitez pas ajouter
de variable d'entrée pour un paramètre d'indicateur personnalisé particulier, transmettez simplement une valeur par
défaut. Par exemple, si vous ne prévoyez pas de changer leBandesShift paramètre pour l'indicateur BB, puis
remplacez simplement 0 parBandesShift dans leiPersonnalisé() appel de fonction.

Enfin, nous utilisons leCopieBuffer() fonction pour copier les données des tampons indicateurs vers nos tableaux.
Si vous ne prévoyez pas d'utiliser une ligne indicatrice, vous n'avez pas besoin de copier les données
correspondantes. Par exemple, si vous ne prévoyez pas d'utiliser la ligne médiane de l'indicateur BB, omettez
simplement la ligne médiane de l'indicateur BB.milieu[] tableau et leArraySetAsSeries() etCopieBuffer()
fonctions qui y font référence.

Vous pouvez ajouter n'importe quel indicateur personnalisé à votre conseiller expert en utilisant la procédure ci-
dessus. Tout d’abord, notez les numéros de tampon pour les lignes/tracés correspondants dans l’indicateur
personnalisé que vous souhaitez utiliser. Ensuite, notez les noms des paramètres d’entrée et leurs types. Ajoutez les
paramètres d'entrée à votre conseiller expert, en les renommant ou en les omettant à votre guise. Transmettez les
paramètres d'entrée (ou les valeurs par défaut appropriées) auiPersonnalisé() fonction ainsi que le nom de
l’indicateur. Enfin, utilisez leCopieBuffer() fonction pour copier les données du tampon dans le(s) tableau(s) série
que vous avez préparé.

Une classe d'indicateurs personnalisés ?


Pour les indicateurs intégrés, nous avons créé des classes qui facilitent grandement leur ajout à vos conseillers
experts. Malheureusement, la mise en œuvre d'indicateurs personnalisés dans MQL5 est difficile à faire de même
pour les indicateurs personnalisés. Comme vous l'avez vu ci-dessus, leiPersonnalisé() La fonction permet un
nombre flexible de paramètres d’entrée. Nous ne pouvons pas faire de même lors de la création de classes dans
MQL5.

Il existe une deuxième méthode pour ajouter des indicateurs à un programme MQL5, laIndicateurCréer()
fonction, qui utilise un nombre fixe de paramètres et un objet duMqlParam type de structure pour transmettre les
paramètres d’entrée. Mais l’utilisation de cette méthode introduit une plus grande complexité sans réel gain de
simplicité ou de fonctionnalité. Nous n’aborderons donc pas cette méthode dans ce livre.

En résumé, si vous souhaitez ajouter des indicateurs personnalisés à votre conseiller expert, vous devrez utiliser
leiPersonnalisé() fonction pour renvoyer une poignée d’indicateur, puis utilisez laCopieBuffer() fonction pour
copier les données du tampon dans vos tableaux de série comme décrit dans la section précédente.

Signaux de trading basés sur des indicateurs


Examinons quelques-unes des façons dont les indicateurs peuvent être utilisés pour créer des signaux de trading.
Presque tous les signaux de trading basés sur des indicateurs appartiennent à l'une des catégories suivantes :

• La relation entre une ligne indicatrice et le prix.

216
Acheteur : Evangelos Papageorgiou (vpapnm@yahoo.gr)
Numéro de transaction : 28J511988U193831A

Utiliser des indicateurs dans les conseillers experts


• La relation entre deux ou plusieurs lignes d’indicateurs.

• Le changement ou la pente d’une ligne indicatrice entre deux barres.

• La valeur d'un indicateur par rapport à une valeur fixe.

La section surSignaux de trading basés sur les prix Le chapitre précédent traitait de la première catégorie. Un signal
de trading peut être créé lorsque le prix est supérieur ou inférieur à une ligne indicatrice. Dans ce chapitre, nous
avons discuté de l’indicateur des bandes de Bollinger. Nous pouvons utiliser cet indicateur comme exemple de
relation prix/indicateur.

Les bandes de Bollinger peuvent être utilisées pour ouvrir des transactions à des prix extrêmes, par exemple lorsque
le prix sort des bandes. Nous pouvons exprimer ces conditions commerciales comme suit :

si(Prix.Close() > bbUpper && PositionType(_Symbol) != POSITION_TYPE_BUY)


{
// Position d'achat ouverte
}

sinon si(Prix.Close() < bbLower && PositionType(_Symbol) != POSITION_TYPE_SELL)


{
// Position de vente ouverte
}

Nous utilisons leFermer() fonction de notreBarres CB classe créée dans le chapitre précédent pour représenter le
prix de clôture actuel. LebbSupérieur etbbInférieur les variables représentent respectivement les bandes de
Bollinger supérieure et inférieure. Si le prix de clôture actuel est supérieur à la bande supérieure et qu'aucune
position d'achat n'est actuellement ouverte, nous ouvrirons une position d'achat. Il en va de même pour une vente si
la clôture est inférieure à la bande inférieure.

Vous pouvez utiliser deux ou plusieurs lignes indicatrices pour créer un signal de trading. Par exemple, le croisement
de moyenne mobile est l’une des stratégies de trading les plus élémentaires. Il utilise deux moyennes mobiles, une
rapide et une lente. Lorsque la moyenne mobile rapide est supérieure à la moyenne mobile lente, un signal d'achat
est implicite, et vice versa pour la vente.

Par exemple, utilisons une moyenne mobile exponentielle de 10 périodes pour la MA rapide et une moyenne mobile
simple de 30 périodes pour la MA lente :

si(rapideMA > lenteMA && PositionType(_Symbol) != POSITION_TYPE_BUY && glBuyPlaced == false)


{
// Position d'achat ouverte
}

si(fastMA < slowMA && PositionType(_Symbol) != POSITION_TYPE_SELL && glSellPlaced == false)


{
// Position de vente ouverte

217
Acheteur : Evangelos Papageorgiou (vpapnm@yahoo.gr)
Numéro de transaction : 28J511988U193831A

ETXPERT UNCONSEIL P.ROGRAMMATION POUR METTRADER 5


}

Lorsque le 10 EMA (lerapideMA variable) est supérieur au 30 SMA (lelenteMA variable), un signal d’achat se produit.
Lorsque l’inverse est vrai, un signal de vente se produit.

Vous pouvez également utiliser des indicateurs de différents types, à condition qu'ils soient tous dessinés dans la
même fenêtre. Par exemple, vous souhaiterez peut-être utiliser une moyenne mobile avec les bandes de Bollinger
de l'exemple précédent au lieu du prix :

si(ma[1] > bbSupérieur && PositionType(_Symbol) != POSITION_TYPE_BUY)


{
// Position d'achat ouverte
}

Dans cet exemple, nous vérifions si la valeur moyenne mobile de la barre précédente (et 1]) est supérieur à la
bande de Bollinger supérieure (bbSupérieur). Si tel est le cas, un signal d’achat se produit.

Vous pouvez également comparer deux lignes du même indicateur, comme les lignes principales et de signal de
l'indicateur stochastique :

si(toit[1] > signal[1] && PositionType(_Symbol) != POSITION_TYPE_BUY)


{
// Position d'achat ouverte
}

Lestoch[] le tableau représente le principal ou%K ligne de l'indicateur, tandis que lesignal[] le tableau est le%D
doubler. Si la%K la ligne est supérieure à la%D ligne, cela indique une condition haussière.

Pour les indicateurs de tendance, vous souhaiterez peut-être baser vos décisions de trading sur la direction dans
laquelle l'indicateur évolue. Vous pouvez le faire en vérifiant la valeur de l'indicateur d'une barre récente par rapport
à la valeur d'une barre précédente. Par exemple, si nous voulons qu'un signal d'achat se produise lorsqu'une
moyenne mobile est en pente ascendante, nous pouvons comparer la valeur de la barre la plus récemment fermée
à la valeur de la barre précédente :

si(ma[1] > ma[2] && PositionType(_Symbol) != POSITION_TYPE_BUY)


{
// Position d'achat ouverte
}

Cet exemple vérifie si la valeur moyenne mobile de la barre la plus récemment fermée (et 1]) est supérieure à la
valeur de la barre précédente (et[2]). Si tel est le cas, la moyenne mobile a tendance à augmenter et un signal
d’achat se produit. Vous pouvez le faire avec n'importe quel indicateur qui évolue à la hausse ou à la baisse avec le
prix, y compris les oscillateurs tels que RSI et
MACD.

218
Acheteur : Evangelos Papageorgiou (vpapnm@yahoo.gr)
Numéro de transaction : 28J511988U193831A

Utiliser des indicateurs dans les conseillers experts


De nombreux indicateurs, notamment les oscillateurs, apparaissent dans une fenêtre graphique distincte. Ils ne sont
pas tracés en fonction du prix, mais réagissent plutôt aux changements de prix. Le RSI, par exemple, a une valeur
minimale de 0 et une valeur maximale de 100. Lorsque la valeur RSI est inférieure à 30, cela indique une condition
de survente, et lorsqu'elle est supérieure à 70, cela indique une condition de surachat. Nous pouvons vérifier ces
conditions de surachat et de survente simplement en comparant la valeur RSI actuelle à une valeur fixe. L'exemple
ci-dessous vérifie une condition de survente dans le cadre d'un signal d'achat :

si(rsi[1] <= 30 && PositionType(_Symbol) != POSITION_TYPE_BUY)


{
// Position d'achat ouverte
}

Si la valeur RSI de la barre fermée la plus récemment est inférieure ou égale au niveau de survente de 30, cela
indique un signal d'achat.

Les indicateurs d'histogramme tels que le MACD oscillent autour d'une ligne zéro. Les valeurs ascendantes au-
dessus de zéro indiquent une tendance haussière, tandis que les valeurs descendantes en dessous de zéro indiquent
un mouvement de prix baissier. Vous souhaiterez peut-être baser un signal de trading autour de la valeur de
l'indicateur par rapport à zéro :

si(macd[1] > 0 && PositionType(_Symbol) != POSITION_TYPE_BUY)


{
// Position d'achat ouverte
}

Si la valeur MACD de la barre fermée la plus récemment est supérieure à zéro, une condition d'achat est indiquée.

Figure 17.5 – L’indicateur MACD.

Vous pouvez proposer des conditions d’indicateur plus élaborées que celles-ci. Mais presque toutes les conditions
commerciales basées sur des indicateurs sont une combinaison de relations entre les lectures des indicateurs et/ou
les prix. Pour combiner plusieurs conditions de prix et d'indicateurs pour un signal de trading, utilisez simplement les
opérations booléennes AND ou OR entre les expressions :

si(Price.Close() < bbLower && (rsi[1] < 30 || stoch[1] < signal[1])


&& PositionType(_Symbol) != POSITION_TYPE_BUY)
{
// Position d'achat ouverte
}

219
Acheteur : Evangelos Papageorgiou (vpapnm@yahoo.gr)
Numéro de transaction : 28J511988U193831A

ETXPERT UNCONSEIL P.ROGRAMMATION POUR METTRADER 5


Dans l'expression ci-dessus, si le prix de clôture est inférieur à la bande inférieure de Bollinger et que soit le RSI est
inférieur à 30, soit le stochastique est inférieur à sa ligne de signal, nous ouvrons une position d'achat. Lorsque vous
combinez des opérations AND et OR, utilisez des parenthèses pour déterminer quelles expressions seront évaluées
en premier.

Allumer et éteindre les indicateurs


Dans un conseiller expert doté de plusieurs indicateurs, vous souhaiterez peut-être activer et désactiver des
indicateurs individuels. Pour ce faire, il faut ajouter une variable d'entrée booléenne qui permettra à l'utilisateur de
désactiver un indicateur si nécessaire. Il sera également nécessaire de modifier les conditions d'échange pour
permettre à la fois les états activés et désactivés.

Par exemple, créons un conseiller expert qui utilise les bandes de Bollinger et un RSI. Si le prix est inférieur à la
bande inférieure et que le RSI est en territoire de survente (inférieur à 30), alors nous ouvrirons une position d'achat.
Nous ajouterons une variable d'entrée pour activer et désactiver la condition RSI :

// Variables d'entrée input


bool UseRSI = true;

// Gestionnaire d'événements OnTick()


if(close[1] < bande inférieure &&((rsi <= 30 && UseRSI == vrai) || UseRSI == faux)
&& PositionType(_Symbol) != POSITION_TYPE_BUY)
{
// Position d'achat ouverte
}

Dans l'exemple ci-dessus, nous ajoutons une variable d'entrée nomméeVous êtes en RSI. Cela active et
désactive la condition commerciale RSI. À l'intérieur desi opérateur, nous vérifions les états vrai et faux.
SiUtiliserRSI == vrai, nous vérifions si lersi la valeur est inférieure à 30. Si tel est le cas, et si les autres
conditions sont vraies, nous ouvrons une position d'achat. SiUtiliserRSI == false, et les autres conditions sont
vraies, nous ouvrons également une position d'achat.

Les parenthèses établissent l'ordre dans lequel les conditions sont évaluées. L'ensemble intérieur de parenthèses
contient l'opération(rsi <= 30 && UseRSI == vrai). Ceci est évalué en premier. Si cette condition est fausse,
nous évaluons alors la condition à l'intérieur de l'ensemble externe de parenthèses,UtiliserRSI == false. Notez
l'opérateur OU (||) séparant les deux opérations. Si l’une de ces conditions est vraie, alors l’expression entière entre
parenthèses est vraie.

N'oubliez pas que l'état de l'indicateur « activé » se trouve à l'intérieur du jeu de parenthèses le plus à l'intérieur et
qu'il est évalué en premier. L'état "off" de l'indicateur se trouve à l'intérieur du jeu de parenthèses le plus à l'extérieur
et est séparé de l'état "on" par un opérateur OR (||). La combinaison des opérations AND et OR à l'aide de
parenthèses pour établir l'ordre d'évaluation vous permet de créer des conditions de trading complexes. Ce faisant,
veillez à surveiller vos parenthèses ouvrantes et fermantes pour vous assurer de ne pas en omettre une, d'en ajouter
une de trop ou de ne pas les imbriquer de manière incorrecte.

220
Acheteur : Evangelos Papageorgiou (vpapnm@yahoo.gr) ID de transaction : 28J511988U193831A
Acheteur : Evangelos Papageorgiou (vpapnm@yahoo.gr)
Numéro de transaction : 28J511988U193831A

ETXPERT UNCONSEIL P.ROGRAMMATION POUR METTRADER 5

Chapitre 18 - Travailler avec l'heure et la date

Dans ce chapitre, nous apprendrons comment travailler avec l'heure et la date dans MQL5. Nous allons créer
une classe qui nous permettra de passer des commandes uniquement à l'ouverture d'un nouveau bar. Nous
créerons également une classe qui nous permettra d'implémenter un minuteur de trading complet chez nos
conseillers experts. Nous en apprendrons également davantage surMinuteur événement, qui nous permet
d'effectuer des actions à un intervalle prédéfini.

Trading sur une nouvelle barre ouverte


Par défaut, un expert-conseil exécute chaque nouvel entrantcocher, ou changement de prix. Cela signifie que les
ordres seront ouverts intrabar, à moins que vous ne programmiez votre conseiller expert pour qu'il négocie
autrement. Nous pouvons programmer notre conseiller expert pour qu'il négocie uniquement à l'ouverture de
chaque nouvelle barre en enregistrant l'horodatage de la barre actuelle dans une variable statique, globale ou de
classe. Lorsque l'horodatage de la barre actuelle change, nous saurons qu'une nouvelle barre s'est ouverte. A ce
moment, nous pouvons vérifier les conditions d’ouverture et de clôture des commandes.

Lorsque nous négocions à l'ouverture d'une nouvelle barre, nous utiliserons le cours de clôture et les valeurs de
l'indicateur de la barre précédente pour prendre des décisions commerciales. Si vous consultez un graphique
des transactions historiques, gardez à l'esprit que les critères de transaction sont basés sur la barre précédente,
et non sur la barre sur laquelle la transaction a débuté !

Notez que le testeur de stratégie dispose d'unTarifs ouverts uniquement mode d'exécution qui imite ce
comportement. Si votre stratégie ouvre et ferme les ordres uniquement à l'ouverture d'une nouvelle barre, vous
pouvez alors utiliserTarifs ouverts uniquement pour effectuer des tests rapides et précis de votre conseiller
expert.

La classe CNewBar
Créons une classe qui gardera une trace de l'horodatage de la barre actuelle et nous permettra de déterminer
quand une nouvelle barre s'est ouverte. Nous allons créer cette classe dans un nouveau fichier d'inclusion
nommé\MQL5\Include\Mql5Book\ Timer.mqh. Toutes les classes et fonctions de ce chapitre iront dans ce
fichier.

Voici la déclaration de classe pour notreCNouvelleBarre classe:

class CNewBar {privé : datetime


Time[], LastTime ;

publique:
void CNewBar();
bool CheckNewBar (string pSymbol, ENUM_TIMEFRAMES pTimeframe);
} ;

222
Acheteur : Evangelos Papageorgiou (vpapnm@yahoo.gr)
Numéro de transaction : 28J511988U193831A

Travailler avec l'heure et la date


Nous avons deux variables privées dans notre classe. LeTemps[] Le tableau stocke l'horodatage de la barre
actuelle et leDernière fois La variable contient l'horodatage de la barre la plus récemment vérifiée. Notez que
nous avons déclaré les deux variables sur la même ligne, puisqu'elles partagent le même type. Nous
comparerons ces deux valeurs pour déterminer si un nouveau bar s'est ouvert. Notre classe contient également
un constructeur et leVérifierNouvelleBar() fonction. Le constructeur de classe définit simplement
leTemps[] tableau sous forme de tableau série :

annuler CNewBar :: CNewBar (vide)


{
ArraySetAsSeries (Temps, vrai);
}

LeVérifierNouvelleBar() La fonction est utilisée pour vérifier l’ouverture d’une nouvelle barre. Voici la
déclaration de la fonction :

bool CNewBar :: CheckNewBar (string pSymbol, ENUM_TIMEFRAMES pTimeframe)


{ bool firstRun = false, newBar = false ;

CopyTime(pSymbol,pTimeframe,0,2,Time);

if(LastTime == 0) firstRun = true;

si (Heure [0] > Dernière Heure)


{ if(firstRun == false) newBar = true;
DernièreHeure = Heure[0] ;
}

return(nouvelleBarre);
}

La fonction prend deux paramètres, le symbole et la période du graphique que nous utilisons. On initialise deux
variables booléennes,première exécution etnouveauBar, et définissez-les explicitement sur false.
LeCopieTime() la fonction met à jour leTemps[] tableau avec l'horodatage de la barre la plus récente.

Nous vérifions leDernière fois variable de classe pour voir si une valeur lui est attribuée. La première fois que
cette fonction s'exécute,Dernière fois sera égal à zéro, et nous fixerons lepremière exécution variable à
vrai. Cela indique que nous venons d'attacher le conseiller expert au graphique. Puisque nous ne voulons pas
que l'expert-conseil négocie intrabar, il s'agit d'une vérification nécessaire pour garantir que nous n'essayons pas
d'ouvrir un ordre immédiatement.

Ensuite, nous vérifions siTemps[0] >Dernière fois. Si c'est la première fois que nous exécutons cette fonction,
alors cette expression sera vraie. Cette expression sera également évaluée comme vraie à chaque fois qu'une
nouvelle barre s'ouvrira. Si l'horodatage de la barre actuelle a changé, nous vérifions sipremière exécution est
réglé surFAUX. Si c'est le cas, nous définissonsnouveauBar àvrai. Nous mettons à jour leDernière fois
variable avec l'horodatage de la barre actuelle et renvoie la valeur denouveauBar au programme.

223
Acheteur : Evangelos Papageorgiou (vpapnm@yahoo.gr)
Numéro de transaction : 28J511988U193831A

ETXPERT UNCONSEIL P.ROGRAMMATION POUR METTRADER 5


L'exécution deVérifierNouvelleBar() fonctionne comme ceci : lorsque le conseiller expert est attaché pour
la première fois à un graphique, la fonction renvoie une valeur deFAUX parce quepremière exécution sera
réglé survrai. A chaque contrôle ultérieur du fonctionnement,première exécution sera toujoursFAUX.
Lorsque l'horodatage de la barre actuelle (Temps[0]) est supérieur àDernière fois, nous fixonsnouveauBar
àvrai, mettez à jour la valeur deDernière fois, et renvoie une valeur devrai au programme. Cela indique
qu'un nouveau bar a été ouvert et nous pouvons vérifier nos conditions de trading.

Voici comment nous utiliserions leCNouvelleBarre cours chez un conseiller expert :

// Inclut la déclaration de fichier et d'objet


#include <Mql5Book\Timer.mqh>
CNewBar NouvelleBarre ;

// Variables d'entrée
input bool TradeOnNewBar = true ;

// Gestionnaire
d'événements OnTick()
bool newBar = true; int
barShift = 0;

si (TradeOnNewBar == vrai)
{ newBar = NewBar.CheckNewBar(_Symbol,_Period);
Maj gauche = 1 ;
}

si (nouvelleBarre == vrai)
{
// Code de placement de commande
}

Nous déclarons un objet basé sur leCNouvelleBarre classe nomméeNouvelleBarre. LeTradeOnNewBar La


variable d'entrée nous permet de choisir entre le trading sur une nouvelle barre ou le trading à chaque tick. Dans
leSurTick() gestionnaire d'événements, nous déclarons un localbouffon variable nomméenouveauBar et
initialisez-le àvrai, Et unint variable nomméebarreShift.

SiTradeOnNewBar est réglé survrai, nous appelons notreVérifierNouvelleBar() fonction et enregistrez la


valeur de retour dans lenouveauBar
variable. La plupart du temps, cette valeur seraFAUX. Nous fixerons également la valeur debarreShift à 1.
SiTradeOnNewBar est réglé surFAUX, alorsnouveauBar seravrai, etbarreShift sera 0.

SinouveauBar estvrai, nous vérifions nos conditions de passation de commande. Tout code que vous souhaitez
exécuter uniquement à l’ouverture d’une nouvelle barre sera placé entre ces parenthèses. Cela inclut toutes les
conditions d’ouverture de commande et éventuellement vos conditions de clôture.

Lors de l'utilisation duCNouvelleBarre classe pour vérifier l'ouverture d'un nouveau bar, lebarreShift La
variable sera utilisée pour définir l'indice à barres de toute valeur de prix ou d'indicateur. Par exemple, nous

224
Acheteur : Evangelos Papageorgiou (vpapnm@yahoo.gr)
Numéro de transaction : 28J511988U193831A

Travailler avec l'heure et la date


utiliserons les fonctions de moyenne mobile et de prix de clôture que nous avons définies dans les chapitres
précédents :

double fermeture = Prix.Close(barShift);


double ma = MA.Main(barShift);

Lefermer etet les variables se verront attribuer la valeur de la barre actuelle (Maj gauche = 0)
siTradeOnNewBar est réglé surFAUX, ou la valeur de la barre précédente (Maj gauche = 1) siTradeOnNewBar
est réglé survrai.

Le type date/heure
Nous avons abordé ledateheure tapez précédemment à la page 16. Ledateheure le type est utilisé pour
contenir les valeurs de date et d’heure. Dans undateheure variable, la date et l'heure sont représentées comme
le nombre de secondes écoulées depuis le 1er janvier 1970 à minuit. Cela facilite la comparaison de
deuxdateheure valeurs et les manipuler mathématiquement.

Par exemple, disons que nous avons deuxdateheure variables,date1 etdate2. Nous savons quedate1 est égal
au 12 juillet 2012 à 15h00, etdate2 est égal au 14 juillet 2012 à 22h00. Si on voulait savoir quelle date est
antérieure, on peut comparer les deux :

datetime date1 = D'2012.07.12 03:00';


datetime date2 = D'2012.07.14 22:00';

si (date1 < date2)


{
Print("la date1 est plus tôt"); // Vrai
}

sinon si (date1 > date2)


{
Print("la date2 est plus tôt");
}

Le résultat correct est"la date1 est plus tôt". Un autre avantage de travailler avecdateheure valeurs est
que si vous souhaitez ajouter ou soustraire des heures, des jours ou toute période de temps, vous pouvez
simplement ajouter ou soustraire le nombre approprié de secondes vers ou depuis ledateheure valeur. Par
exemple, nous ajouterons 24 heures à ladate1 variable ci-dessus :

datetime addDay = date1 + 86400 ; // Résultat : 13/07/2012 03h00

Il y a 86 400 secondes sur une période de 24 heures. En ajoutant 86400 à undateheure valeur, nous avons
avancé la date d'exactement 24 heures. Ledate1 La variable a désormais une valeur égale au 13 juillet 2012 à
3h00.

225
Acheteur : Evangelos Papageorgiou (vpapnm@yahoo.gr)
Numéro de transaction : 28J511988U193831A

ETXPERT UNCONSEIL P.ROGRAMMATION POUR METTRADER 5


UNdateheure la valeur n'est pas lisible par l'homme, bien que le terminal MetaTrader 5 convertisse
automatiquementdateheure valeurs dans une constante de chaîne lisible lors de leur impression dans le journal.
Si vous devez convertir undateheure valeur à une chaîne à d'autres fins, leTempsVersChaîne() la fonction
convertira undateheure variable en chaîne au formataaaa.mm.jj hh:mm. Dans cet exemple, nous
convertironsdate2 en utilisant le

TempsVersChaîne() fonction:

conversion de chaîne = TimeToString (date2);


Imprimer (convertir); // Résultat : 14/07/2012 22h00

LeTempsVersChaîne() la fonction convertit ledate2 variable en chaîne et enregistre le résultat dans


leconvertir variable. Si nous imprimons la valeur deconvertir, ce sera14/07/2012 22h00.

Nous pouvons créer undateheure valeur en construisant une chaîne au formataaaa.mm.jj hh:mm:ss, et le
convertir en undateheure variable en utilisant leChaîneVersHeure() fonction. Par exemple si nous voulons
convertir la chaîne14/07/2012 22h00 à undateheure valeur, nous passons la chaîne auChaîneVersHeure()
fonction:

chaîne dtConst = "2012.07.14 22:00" ;


dateheure dtDate = StringToTime (dtConst);

LedtDate la variable se voit attribuer undateheure valeur équivalente à14/07/2012 22h00. Nous pouvons
également créer undateheure constante en plaçant la chaîne entre guillemets simples et en la faisant précéder
d'un D majuscule.dateheure constante peut être affectée directement à undateheure variable:

datetime dtDate = D'2012.07.14 22:00';

Cette méthode permet plus de flexibilité lors de la construction dudateheure chaîne constante. Par exemple,
vous pourriez utiliserjj.mm.aaaa comme format de date et laissez l’heure. Ledateheure sujet constant dans
leRéférence MQL5 sousNotions de base du langage > Types de données > Types entiers > Type DateHeure a
plus d'informations sur le formatage d'undateheure constante.

Enfin, nous pouvons utiliser leMqlDateHeure structure. Cette structure nous permet de récupérer des
informations spécifiques à partir d'undateheure variable, comme l'heure ou le jour de la semaine.

La structure MqlDateTime
LeMqlDateHeure La structure contient des variables qui contiennent des informations sur undateheure valeur.
Voici la définition de la structure duRéférence MQL5:

structure MqlDateTime
{
année entière ; // Année int
mon ; // Mois int jour ; // Jour
int heure ; // Heure int min ; //
Minutes int sec ; // Secondes

226
Acheteur : Evangelos Papageorgiou (vpapnm@yahoo.gr)
Numéro de transaction : 28J511988U193831A

Travailler avec l'heure et la date


int jour_de_semaine ; // Jour de la semaine (0-dimanche, 1-lundi, ...
6-samedi) int day_of_year; // Numéro du jour de l'année (1er janvier =
zéro) };

En utilisant leMqlDateHeure structure, nous pouvons récupérer n’importe quel élément de date ou d’heure à
partir d’undateheure valeur. Nous pouvons récupérer l'heure, la minute ou le jour et attribuer cette valeur à une
variable entière. On peut même récupérer le jour de la semaine ou le jour de l'année. Nous pouvons également
attribuer des valeurs à ces variables pour construire undateheure valeur.

LeTempsVersStruct() la fonction est utilisée pour convertir undateheure valeur à un objet duMqlDateHeure
taper. Le premier paramètre de la fonction est ledateheure valeur à convertir. Le deuxième paramètre est
leMqlDateHeure objet dans lequel charger les valeurs. Voici un exemple :

datetime dtTime = D'2012.10.15 16:36:23';

MqlDateTime timeStruct ;
TimeToStruct(dtTime,timeStruct);

int jour = timeStruct.day; int heure =


timeStruct.hour; int dayOfWeek =
timeStruct.day_of_week;

Dans un premier temps, nous construisons undateheure variable,dtHeure, utilisant undateheure constante.
Ensuite, nous déclarons un objet de typeMqlDateHeure nomméstructure du temps. Nous utilisons
leTempsVersStruct() fonction pour convertir ledateheure valeur endtHeure auMqlDateHeure structure.
Enfin, nous montrons comment récupérer le jour, l'heure et le jour de la semaine à partir dustructure du
temps objet. Lejour est 15, leheure a 16 ans, et lejour de la semaine est 1 pour lundi.

Nous pouvons également créer unMqlDateHeure objet, attribuer des valeurs à ses variables membres et
construire undateheure valeur en utilisant leStructToTime() fonction. Lejour de la semaine
etjour_de_l'année les variables ne sont pas utilisées lors de la création d'undateheure valeur à l'aide
d'unMqlDateHeure objet. Voici un exemple de la façon de créer undateheure valeur en utilisant
leStructToTime() fonction:

MqlDateTime timeStruct ;

timeStruct.year = 2012 ; timeStruct.mon =


10 ; timeStruct.day = 20 ; timeStruct.hour
= 2 ; timeStruct.min = 30 ; timeStruct.sec
= 0 ; datetime dtTime =
StructToTime(timeStruct);

Assurez-vous d'attribuer explicitement des valeurs à toutes les variables duMqlDateHeure objet, sinon vous
risquez de ne pas obtenir les résultats escomptés ! LeStructToTime() la fonction convertit lestructure du
temps s'opposer à undateheure valeur et stocke le résultat dans ledtHeure variable. Si nous imprimons
ledtHeure variable, nous pouvons voir les valeurs que nous avons attribuées à lastructure du temps objet:

227
Acheteur : Evangelos Papageorgiou (vpapnm@yahoo.gr)
Numéro de transaction : 28J511988U193831A

ETXPERT UNCONSEIL P.ROGRAMMATION POUR METTRADER 5


Imprimer(dtHeure); // Résultat : 20.10.2012 02:30:00

Que se passe-t-il si vous n'attribuez pas de valeur à unMqlDateHeure variable d'objet, ou si vous attribuez une
valeur invalide ?

MqlDateTime timeStruct ;

// Zéro pour le mois et le jour


timeStruct.year = 2012 ;
timeStruct.mon = 0 ;
timeStruct.day = 0 ;

timeStruct.hour = 0 ; timeStruct.min = 0 ;

timeStruct.sec = 0 ; datetime dtTime =

StructToTime(timeStruct);

Imprimer(TimeToString(dtTime)); // Résultat : 01.01.2012 00:00

// Mois et jour invalides

timeStruct.mon = 13;

timeStruct.day = 32 ; dtTime =

StructToTime(timeStruct);

Imprimer(TimeToString(dtTime)); // Résultat : 31.12.2012 00:00

L'année doit être précisée, sinon leStructToTime() la fonction ne renvoie rien. Si le mois ou le jour est inférieur
à 1, la valeur par défaut est 1. Si le mois est supérieur à la valeur maximale de 12, la valeur par défaut est
décembre (12). Si le jour est supérieur au nombre de jours du mois, soit la valeur par défaut est 31 s'il y a 31 jours
dans le mois, soit la valeur excédentaire déborde sur le mois suivant. Inutile de préciser qu'il ne faut pas
déborderjour variable, sinon votre rendez-vous pourrait se retrouver avec plusieurs jours de congé. Les valeurs
d'heure ou de minute non valides prendront par défaut leur minimum et leur maximum de 0 et 59
respectivement.

Création d'une classe de minuterie d'échange


Maintenant que nous avons appris à créer et convertirdateheure valeurs, nous pouvons utiliser ces
informations pour créer une classe de minuterie commerciale pour notre conseiller expert. Nous devrons
créerdateheure valeurs pour nos heures de début et de fin. Puisque nous travaillerons généralement avec des
heures qui correspondent à la journée ou à la semaine en cours, nous allons créer une fonction qui calculera
undateheure valeur pour une heure spécifiée pour la date actuelle. Nous pouvons ensuite manipuler cette
valeur pour obtenir une date précise si nécessaire.

228
Acheteur : Evangelos Papageorgiou (vpapnm@yahoo.gr)
Numéro de transaction : 28J511988U193831A

Travailler avec l'heure et la date


La fonction CreateDateTime()
Créons une fonction qui créera undateheure valeur pour la date actuelle. Nous spécifierons l'heure et les
minutes, et la fonction renverra une valeur pour cette heure pour la date actuelle. UnMqlDateHeure l'objet sera
utilisé pour créer notredateheure valeur.

Tout le code de ce chapitre ira dans le\MQL5\Include\Mql5Book\Timer.mqh inclure le fichier. Voici le code
duCréerDateTime() fonction:

datetime CreateDateTime (int pHour = 0, int pMinute = 0)


{
MqlDateTime timeStruct ;
TimeToStruct(TimeCurrent(),timeStruct);

timeStruct.hour = pHour; timeStruct.min =

pMinute ; datetime useTime =

StructToTime(timeStruct);

return(useTime);
}

Tout d'abord, nous créons unMqlDateHeure objet nomméstructure du temps. Nous utilisons
leTempsVersStruct() fonction pour remplir les variables membres avec la date et l'heure actuelles à l'aide de
laHeureActuelle() fonction. Ensuite, nous attribuons lepHheure etpMinute valeurs des paramètres auheure
etmin variables dustructure du temps objet. Enfin, nous utilisonsStructToTime() pour construire notre
nouveaudateheure valeur et affecter le résultat auutiliserTime variable. La valeur deutiliserTime est
renvoyé au programme.

Une fois que nous avons undateheure valeur pendant une durée spécifiée, nous pouvons facilement manipuler
cette valeur si nécessaire. Par exemple, nous souhaitons que notre minuterie d'échange démarre à 20h00
chaque jour. Nous arrêterons les échanges pour la journée à 8h00 le lendemain. Dans un premier temps, nous
utiliserons notreCréerDateTime() fonction pour créerdateheure valeurs pour l'heure de début et de fin :

datetime startTime = CreateDateTime (22,0);

datetime endTime = CreateDateTime (8,0);

Print("Début : ",startTime,", Fin : ",endTime);

// Résultat : Début : 2012.10.22 22:00:00, Fin : 2012.10.22 08:00:00

Le problème ici est que notre heure de fin est antérieure à notre heure de début. En supposant que nous
sommes aujourd'hui le 22 octobre et que nous souhaitons que notre minuterie démarre à 20h00, nous devrons
avancer l'heure de fin de 24 heures pour obtenir l'heure correcte. Vous vous souviendrez plus tôt dans le

229
Acheteur : Evangelos Papageorgiou (vpapnm@yahoo.gr)
Numéro de transaction : 28J511988U193831A

ETXPERT UNCONSEIL P.ROGRAMMATION POUR METTRADER 5


chapitre que 24 heures équivaut à 86 400 secondes. Nous pouvons simplement ajouter cette valeur àheure de
fin pour obtenir l'heure exacte.

Au lieu de devoir nous rappeler que 86 400 secondes équivalent à un jour, définissons quelques constantes
auxquelles nous pouvons facilement nous référer si nous devons ajouter ou soustraire du temps à undateheure
variable. Ceux-ci iront en haut de notreMinuterie.mqh inclure le fichier :

#définir TIME_ADD_MINUTE 60
#définir TIME_ADD_HOUR 3600
#définir TIME_ADD_DAY 86400
#définir TIME_ADD_WEEK 604800

En utilisant ces constantes, nous pouvons facilement ajouter ou soustraire la période de temps spécifiée.
Ajoutons 24 heures à notreheure de fin variable:

endTime += TIME_ADD_DAY ; Print("Début :

",startTime," End : ",endTime);

// Résultat : Début : 22.10.2012 22:00:00, Fin : 23.10.2012 08:00:00

Désormais, nos heures de début et de fin sont correctes les unes par rapport aux autres.

La classe CTimer
Examinons le type de minuteur commercial le plus simple. Cette minuterie prendra deuxdateheure valeurs et
déterminer si l’heure actuelle se situe entre ces valeurs. Si tel est le cas, notre minuteur de négociation est actif et
la négociation peut commencer. Si l'heure actuelle est en dehors de ces valeurs, alors notre chronomètre
d'échange est inactif et les transactions ne seront pas effectuées.

LeMinuterie La classe contiendra nos fonctions liées à la minuterie. Nous commencerons par une fonction :
laCheckTimer() fonction, qui vérifiera deuxdateheure variables pour voir si le trading est autorisé. Nous
ajouterons d'autres membres de la classe plus tard :

classe
CTimer
{public :
bool CheckTimer (datetime pStartTime, datetime pEndTime, bool pLocalTime = false);
} ;

Voici le corps duCheckTimer() fonction:

bool CTimer :: CheckTimer (datetime pStartTime, datetime pEndTime, bool pLocalTime =


false)
{ si (pStartTime >= pEndTime)
{

230
Acheteur : Evangelos Papageorgiou (vpapnm@yahoo.gr)
Numéro de transaction : 28J511988U193831A

Travailler avec l'heure et la date


Alert("Erreur : heure de début ou de fin non valide");
retourner(faux);
}

dateheure heure actuelle ;


if(pLocalTime == true) currentTime = TimeLocal();
sinon currentTime = TimeCurrent();

bool timerOn = faux ;


if(currentTime >= pStartTime && currentTime < pEndTime)
{ timerOn = vrai ;
}

retour (minuterieOn);
}

LepHeureDébut Le paramètre contient l'heure de début du trading, tandis que lepHeureFin Le paramètre
contient l’heure de fin de négociation. LepHeureLocale Le paramètre est une valeur booléenne qui détermine si
nous utilisons l’heure locale ou celle du serveur.

Tout d’abord, nous vérifions sipHeureDébut est supérieur ou égal àpHeureFin. Si tel est le cas, nous savons que
nos heures de début et de fin ne sont pas valides, nous affichons donc un message d'erreur et quittons avec une
valeur deFAUX. Sinon, nous continuons à vérifier l'état de notre minuterie.

Nous déclarons undateheure variable nomméeheure actuelle qui tiendra l'heure actuelle. SipHeureLocale
estvrai, nous utiliserons l'heure locale de l'ordinateur. Si c'estFAUX, nous utilisons l'heure du serveur.
LeHeureLocal() etHeureActuelle() Les fonctions récupèrent l'heure actuelle de l'ordinateur local ou du
serveur, respectivement. Normalement, nous utiliserons l'heure du serveur, mais il s'agit d'un simple ajout pour
permettre à l'utilisateur d'utiliser l'heure locale, nous avons donc ajouté l'option.

Une fois laheure actuelle L'heure actuelle a été attribuée à la variable, nous la comparons à nos heures de
début et de fin de trading. Siheure actuelle >= pHeureDébut etheure actuelle < pEndTime, alors le
minuteur d'échange est actif. Nous fixerons leDécompte activé variable àvrai et renvoyez cette valeur au
programme.

Montrons comment créer un minuteur d'échange simple en utilisantCheckTimer(). Nous en utiliserons


deuxdateheure variables d'entrée. L'interface MetaTrader 5 facilite la saisie d'undateheure valeur. Nous
définirons une heure de début et de fin et utiliserons leCheckTimer() fonction pour vérifier une condition
commerciale valide :

#include <Mql5Book/Timer.mqh>
CTimer Minuterie ;

entrée datetime StartTime = D'2012.10.15 08:00';


entrée datetime EndTime = D'2012.10.19 20:00' ;

231
Acheteur : Evangelos Papageorgiou (vpapnm@yahoo.gr)
Numéro de transaction : 28J511988U193831A

ETXPERT UNCONSEIL P.ROGRAMMATION POUR METTRADER 5


// Gestionnaire d'événements OnTick()
bool timerOn = Timer.CheckTimer(StartTime,EndTime,false);

si (timerOn == vrai)
{
// Code de placement de commande
}

Premièrement, nous incluons leMinuterie.mqh déposer et déclarer leMinuteur objet, basé sur notreMinuterie
classe. Les variables d'entréeHeure de début etHeure de fin avoir une heure de début et de fin valide saisie.
Dans leSurTick() gestionnaire d'événements, nous appelons leCheckTimer() fonctionner et passer
notreHeure de début etHeure de fin y entrer des variables. (Nous avons dépassé une valeur deFAUX pour
lepHeureLocale paramètre, ce qui signifie que nous utiliserons l'heure du serveur pour déterminer l'heure
actuelle).

Si l'heure actuelle se situe entre l'heure de début et l'heure de fin, le booléenDécompte activé la variable est
définie survrai. Lorsque nous vérifions nos conditions d'ouverture de commande, nous vérifions siDécompte
activé estvrai. Si tel est le cas, nous procédons à la vérification des conditions de commande.

Les problèmes d'utilisationdateheure Les variables d'entrée pour définir nos heures de début et de fin sont
qu'elles doivent être constamment mises à jour. Et si vous vouliez simplement échanger aux mêmes heures
chaque jour, par exemple ?

La fonction DailyTimer()
La plupart des traders Forex avertis savent que certaines heures de la journée sont plus propices au trading.
L'ouverture de la séance de Londres jusqu'à la fin de la séance de New York constitue la période de négociation
la plus active de la journée. Nous pouvons améliorer les performances de nos conseillers experts en limitant nos
échanges à ces heures.

Nous allons créer une minuterie qui nous permettra d'échanger les mêmes heures chaque jour. Nous définissons
une heure et une minute de début et de fin, et à moins que nous décidions de modifier nos heures de
négociation, nous n'avons pas besoin de les modifier à nouveau. Notre minuterie quotidienne fonctionne selon
les mêmes principes que notre minuterie simple de la dernière section. Nous créons deuxdateheure valeurs en
utilisant notreCréerDateTime() fonction. Nous comparons nos heures de début et de fin, et les incrémentons
ou les décrémentons d'un jour si nécessaire. Si l'heure actuelle se situe entre l'heure de début et l'heure de fin, le
trading est activé.

Ajoutons notre fonction de minuterie quotidienne auMinuterie classe. Nous allons également ajouter
deuxdateheure variable –Heure de début etHeure de fin - auMinuterie classe. Celles-ci seront utilisées
pour sauvegarder nos heures de début et de fin actuelles, juste au cas où nous aurions besoin de les récupérer
ailleurs dans notre programme :

classe CTimer {privé :dateheure


Heure de début, Heure de fin ;

232
Acheteur : Evangelos Papageorgiou (vpapnm@yahoo.gr)
Numéro de transaction : 28J511988U193831A

Travailler avec l'heure et la date


publique:
bool CheckTimer (datetime pStartTime, datetime pEndTime, bool pLocalTime =
false);bool DailyTimer (int pStartHour, int pStartMinute, int pEndHour, int
pEndMinute, bool pLocalTime = false);
} ;

Voici le corps de notreMinuterie quotidienne() fonction:

bool CTimer :: DailyTimer (int pStartHour, int pStartMinute, int pEndHour, int pEndMinute,
bool pLocalTime = false)
{ dateheure heure
actuelle ;
if(pLocalTime == true) currentTime = TimeLocal();
sinon currentTime = TimeCurrent();

StartTime = CreateDateTime (pStartHour,pStartMinute);


EndTime = CreateDateTime(pEndHour,pEndMinute);

si (Heure de fin <= Heure de début)


{
Heure de début -= TIME_ADD_DAY ;

si (heure actuelle > heure de fin)


{
Heure de début += TIME_ADD_DAY ;
Heure de fin += TIME_ADD_DAY ;
}
} bool timerOn =

CheckTimer(StartTime,EndTime,pLocalTime); retour

(minuterieOn);

Pour cette fonction, nous saisissons l'heure et les minutes de début et de fin à l'aide duint
variablespHeureDébut,pDébutMinute,pFinHeure etpFinMinute. Nous incluons également lepHeureLocale
paramètre de sélection entre l’heure du serveur et l’heure locale. Après avoir attribué l'heure actuelle auheure
actuelle variable, nous appelons la
CréerDateTime() fonction et passer lepHeureDébut,pDébutMinute,pFinHeure etpFinMinute variables. La
résultantedateheure les valeurs sont stockées dansHeure de début etHeure de fin respectivement.

Ensuite, nous déterminons si nous devons incrémenter ou décrémenter les valeurs d’heure de début et/ou de
fin. Tout d'abord, nous vérifions si leHeure de fin la valeur est inférieure à laHeure de début valeur. Si c'est le
cas, nous soustrayons un jour à l'heure de début (en utilisant leTIME_ADD_DAY constante) de sorte que la valeur

233
Acheteur : Evangelos Papageorgiou (vpapnm@yahoo.gr)
Numéro de transaction : 28J511988U193831A

ETXPERT UNCONSEIL P.ROGRAMMATION POUR METTRADER 5


deHeure de début est plus tôt que la valeur deHeure de fin. Si la valeur deheure actuelle est supérieur
àHeure de fin, le minuteur a déjà expiré et nous devons le régler pour le lendemain. Nous incrémentons les
deuxHeure de début etHeure de fin d'un jour, de sorte que la minuterie soit réglée pour le jour suivant.
Enfin, nous passons leHeure de début etHeure de fin valeurs à laCheckTimer() fonction, avec
notrepHeureLocale paramètre. Le résultat est enregistré dans leDécompte activé variable, qui est renvoyée
au programme.

Montrons comment nous pouvons utiliser la minuterie quotidienne dans notre programme de conseillers
experts :

#include <Mql5Book/Timer.mqh>
CTimer Minuterie ;

input bool UseTimer = false;


entrée int StartHour = 0 ;
entrée int StartMinute = 0 ;
entrée int EndHour = 0 ; entrée
int EndMinute = 0 ; input bool
UseLocalTime = false;

// Gestionnaire d'événements
OnTick() bool timerOn = true;
si (UseTimer == vrai)
{ timerOn = Timer.DailyTimer(StartHour,StartMinute,EndHour,EndMinute,UseLocalTime);
}

si (timerOn == vrai)
{
// Code de placement de commande
}

Nous avons ajouté une variable d'entrée nomméeUtiliser la minuterie pour allumer et éteindre la
minuterie. Nous avons des entrées pour l'heure et les minutes de début et de fin, ainsi que pour l'heure locale/du
serveur.

Dans leSurTick() gestionnaire d'événements, nous déclarons une variable booléenne nomméeDécompte
activé (à ne pas confondre avec la variable locale du même nom dans notreMinuterie quotidienne()
fonction !) La valeur de cette variable détermine si nous pouvons échanger ou non. Nous allons l'initialiser àvrai.
Si laUtiliser la minuterie la variable d'entrée est définie survrai, nous appelons leMinuterie
quotidienne() fonction. Le résultat de la fonction est enregistré dans leDécompte activé variable.
SiDécompte activé estvrai, les échanges commenceront, et si c'est le casFAUX, nous attendrons le début du
prochain jour de bourse.

La fonction PrintTimerMessage()
Du point de vue de l'utilisateur, un problème est de savoir si le trading est activé ou non par la minuterie. Créons
une courte fonction qui écrit un commentaire dans le graphique ainsi que dans le journal, pour informer
l'utilisateur lorsque le minuteur a démarré et arrêté. La fonction est nomméeImprimerTimerMessage(), et nous

234
Acheteur : Evangelos Papageorgiou (vpapnm@yahoo.gr)
Numéro de transaction : 28J511988U193831A

Travailler avec l'heure et la date


en ferons un membre privé duMinuterie classe. Nous déclarerons également une variable privée
nomméeMinuterie démarrée qui maintiendra notre état marche/arrêt :

classe
CTimer
{privé :
dateheure Heure de début, Heure de fin ;
bool TimerStarted ;
void PrintTimerMessage(bool pTimerOn);

publique:
bool CheckTimer (datetime pStartTime, datetime pEndTime, bool pLocalTime = false);
bool DailyTimer (int pStartHour, int pStartMinute, int pEndHour, int pEndMinute,
bool pLocalTime = false);
} ;

Voici le code duImprimerTimerMessage() fonction:

void CTimer :: PrintTimerMessage (bool pTimerOn)


{ si (pTimerOn == vrai && TimerStarted == faux)
{ string message = "Minuterie
démarrée" ;
Imprimer(message);
Commentaire(message);
TimerStarted = vrai ;
}
sinon si (pTimerOn == false && TimerStarted == true)
{ string message = "Timer arrêté" ;
Imprimer(message);
Commentaire(message);
TimerStarted = faux ;
}
}
Le type de fonction estvide, puisque nous n'avons pas besoin de cette fonction pour renvoyer une valeur.
LepTimerOn Le paramètre prend la valeur de notreDécompte activé variable de laMinuterie quotidienne()
fonction. SipTimerOn estvrai, nous vérifions si la minuterie est active et imprimons un message sur le journal et
sur l'écran.

LeMinuterie démarrée La variable contient l'état d'activation de


la minuterie.
Lorsque la minuterie est activée pour la première fois etpTimerOn
estvrai,Minuterie démarrée seraFAUX. Nous imprimerons le
message « Minuterie démarrée » sur le graphique et dans le
journal, et définironsMinuterie démarrée àvrai. SipTimerOn
estFAUX etMinuterie démarrée estvrai, nous imprimons le
message « Minuterie arrêtée » sur le graphique et le journal, et
définissonsMinuterie démarrée àFAUX.
Il ne nous reste plus qu'à ajouter ceci à la fin de notre Figure 18.1 – Un commentaire graphique généré par le

235
Acheteur : Evangelos Papageorgiou (vpapnm@yahoo.gr)
Numéro de transaction : 28J511988U193831A

ETXPERT UNCONSEIL P.ROGRAMMATION POUR METTRADER 5


ImprimerTimerMessage() fonction.
Minuterie quotidienne() fonction:

bool timerOn =

CheckTimer(StartTime,EndTime,pLocalTime);PrintTimerMessa

ge(timerOn);retour (minuterieOn);

Le résultat est que nous aurons un message imprimé dans le journal lorsque la minuterie s'active, et un autre
lorsque la minuterie se désactive. Le message sera également imprimé dans la zone de commentaire du
graphique et y restera jusqu'à ce qu'il soit écrasé par un autre commentaire.

La fonction BlockTimer()
Les chronomètres d'échange que nous avons créés jusqu'à présent prennent une seule heure de début et de fin.
Le minuteur quotidien est suffisant pour la plupart des traders, mais si vous avez besoin de plus de flexibilité
dans le réglage de votre minuteur de trading, nous avons créé leBloquerTimer() fonction. Nous l'appelons le
« minuterie de bloc » car l'utilisateur définira plusieurs blocs de temps pendant lesquels l'expert-conseil sera
autorisé à négocier chaque semaine. Cela permet à l'utilisateur d'éviter les événements volatils du marché tels
que le rapport sur la paie non agricole.

Nous préciserons les jours de début et d'arrêt du trading en utilisant leENUM_DAY_OF_WEEK énumération.
L'utilisateur sélectionnera le jour de la semaine et spécifiera une heure et une minute de début et de fin dans les
entrées du conseiller expert. Pour chaque bloc de temps, nous avons besoin d'un jour de début, d'une heure de
début, d'une minute de début, d'un jour de fin, d'une heure de fin et d'une minute de fin. Nous aurons également
besoin d'une variable booléenne pour indiquer s'il faut utiliser ce bloc de minuterie.

Un nombre fixe de blocs de minuterie devra être spécifié dans les entrées, en fonction des besoins du trader.
Cinq blocs devraient suffire à la plupart des traders. En raison du nombre de variables requises pour chaque
bloc de minuterie, nous allons créer une structure qui les contiendra toutes. Nous allons créer un tableau basé
sur cette structure qui contiendra toutes les variables de chaque bloc de minuterie. Ce tableau sera transmis
auBloquerTimer() fonction, qui déterminera si le trading doit être activé.

Voici la structure que nous définirons dans leMinuterie.mqh inclure le fichier. Celui-ci sera déclaré sur le scope
global, en haut du fichier :

struct TimerBlock
{
booléen activé ; // Activer ou désactiver le
blocage du timer
int start_day; // Jour de début (1 : lundi... 5 :
vendredi)
int heure_début ; // Heure de début
int start_min; // Minute de début
int fin_jour ; // Fin de journée
int fin_heure ; // Heure de fin
int fin_min; // Minute de fin

236
Acheteur : Evangelos Papageorgiou (vpapnm@yahoo.gr)
Numéro de transaction : 28J511988U193831A

Travailler avec l'heure et la date


} ;

Montrons comment nous utiliserons cette structure pour remplir un objet tableau avec des informations de
minuterie. Tout d’abord, nous déclarerons les variables d’entrée dans notre conseiller expert. Nous ajouterons
deux blocs de minuterie, chacun avec les entrées nécessaires :

chaîne sinput Block1 ; // Bloc de minuterie 1


input bool UseTimer = true;
saisissez ENUM_DAY_OF_WEEK StartDay
= 5 ; entrée int StartHour = 8 ;
entrée int StartMinute = 0 ;
saisissez ENUM_DAY_OF_WEEK EndDay =
5 ;
entrée int EndHour = 12 ;
entrée int EndMinute = 25 ;

chaîne sinput Block2 ; // Bloc de minuterie 2


input bool UseTimer2 = true ;
saisissez ENUM_DAY_OF_WEEK StartDay2
= 5 ;
entrée int StartHour2 = 13 ; entrée
int StartMinute2 = 0 ; saisissez
ENUM_DAY_OF_WEEK EndDay2 = 5 ;
entrée int EndHour2 = 18 ; entrée
int EndMinute2 = 0 ; input bool
UseLocalTime = false;

Chaque bloc timer possède une variable booléenne pour activer et désactiver le bloc timer (Utiliser la
minuterie), unCommence jour et
Jour de fin variable de type ENUM_DAY_OF_WEEK, et laHeure de début,DébutMinute,Heure de fin
etFinMinute variables entières. Noter laBloc1 etBloc2 variables avec lesinput modificateur. Il s'agit de
variables d'entrée statiques utilisées à des fins d'information et de formatage. Le commentaire suivant
l'identifiant de la variable est affiché dans la fenêtre de saisie de MetaTrader :

237
Acheteur : Evangelos Papageorgiou (vpapnm@yahoo.gr)
Numéro de transaction : 28J511988U193831A

ETXPERT UNCONSEIL P.ROGRAMMATION POUR METTRADER 5

Figure 18.2– Les entrées du minuteur de bloc telles qu’elles apparaissent dans l’expert AdvisorContributions languette.

Maintenant que nos variables d'entrée sont déclarées, nous allons déclarer notreMinuterieBloc objet de
structure sur la portée globale :

Bloc TimerBlock[2] ;

Ceci déclare un objet tableau nommébloc, avec deux éléments. Ensuite, nous devons charger nos valeurs
d'entrée dans ce tableau. Cela prendra pas mal de code, mais il n’y a pas de solution simple. Nous copierons les
variables d'entrée dans le tableau à l'intérieur dusurInit() gestionnaire d'événements:

// Gestionnaire d'événements OnInit()


block[0].enabled = UseTimer;
block[0].start_day = StartDay ;
block[0].start_hour = StartHour;
block[0].start_min = StartMinute ;
block[0].end_day = EndDay ;
block[0].end_hour = EndHour;
block[0].end_min = EndMinute ;

block[1].enabled = UseTimer2;
block[1].start_day = StartDay2;
block[1].start_hour = StartHour2;
block[1].start_min = StartMinute2;
block[1].end_day = EndDay2 ;
block[1].end_hour = EndHour2;
block[1].end_min = EndMinute2;

Toutes les variables d'entrée du premier bloc timer sont copiées dans les variables membres dubloc[] tableau
à l'élément 0. La même chose est faite pour le deuxième bloc de minuterie à l'élément 1. Maintenant que les
valeurs d'entrée sont copiées dans notrebloc[] tableau, passons-les à notre fonction timer. C'est la partie la
plus simple, et elle fonctionne exactement comme les autres fonctions de minuterie de ce chapitre :

238
Acheteur : Evangelos Papageorgiou (vpapnm@yahoo.gr)
Numéro de transaction : 28J511988U193831A

Travailler avec l'heure et la date


// Gestionnaire d'événements
OnTick() bool timerOn = true;
si (UseTimer == vrai)
{ timerOn = Timer.BlockTimer(bloc,UseLocalTime);
}

À l'intérieur deSurTick() gestionnaire d'événements, nous vérifions l'état de la minuterie avant de vérifier nos
conditions de commande. Si l'heure actuelle se situe dans l'un de nos blocs de minuterie, la valeur deDécompte
activé sera réglé survrai. Nous pouvons alors vérifier les conditions d'ouverture des commandes et effectuer
toutes les actions qui se produisent lorsque notre timer est actif.

Examinons notreBloquerTimer() fonction:

bool CTimer::BlockTimer(TimerBlock &pBlock[], bool pLocalTime = false)


{
MqlDateTime
aujourd'hui ; bool
timerOn = faux ;
int timerCount = ArraySize(pBlock);

pour (int i = 0; i < timerCount; i++)


{ if(pBlock[i].enabled = false) continuer ;

StartTime = CreateDateTime(pBlock[i].start_hour, pBlock[i].start_min);


EndTime = CreateDateTime(pBlock[i].end_hour, pBlock[i].end_min);

TimeToStruct(StartTime,aujourd'hui);
int dayShift = pBlock[i].start_day - aujourd'hui.day_of_week;
if(dayShift != 0) StartTime += TIME_ADD_DAY * dayShift;

TimeToStruct(EndTime,aujourd'hui);
dayShift = pBlock[i].end_day - aujourd'hui.day_of_week ;
if(dayShift != 0) EndTime += TIME_ADD_DAY * dayShift;

timerOn = CheckTimer(StartTime,EndTime,pLocalTime);
if(timerOn == true) pause;
}

PrintTimerMessage(timerOn);
retour (minuterieOn);
}
LeBloquerTimer() La fonction a deux paramètres : LepBloc[] Le paramètre prend un objet tableau
duMinuterieBloc type de structure passé par référence, et lepHeureLocale Le paramètre est une variable
booléenne qui détermine s’il faut utiliser l’heure du serveur ou l’heure locale.

Tout d’abord, nous déclarons les variables qui seront utilisées dans notre fonction. UnMqlDateHeure objet
nomméaujourd'hui sera utilisé pour récupérer le jour de la semaine pour la date actuelle. LetimerCount La
variable contiendra la taille dupBloc[] tableau, que nous récupérons en utilisant leTaille du tableau()

239
Acheteur : Evangelos Papageorgiou (vpapnm@yahoo.gr)
Numéro de transaction : 28J511988U193831A

ETXPERT UNCONSEIL P.ROGRAMMATION POUR METTRADER 5


fonction. UNpour La boucle sera utilisée pour traiter les blocs de minuterie. Le nombre de blocs de
temporisation est déterminé par letimerCount variable. Nous calculerons les heures de début et de fin pour
chaque bloc de minuterie et déterminerons si l'heure actuelle se situe dans ces heures. Si tel est le cas, nous
sortons de la boucle et renvoyons une valeur devrai au programme appelant.

À l'intérieur depour boucle, la première chose que nous vérifions est laactivé variable du bloc timer actuel. Si
c'estvrai, nous continuons à calculer l'heure de début et de fin. Nous utilisons leCréerDateTime() fonction
pour calculer le
Heure de début etHeure de fin variables en utilisant leheure_début,start_min,fin_heure etque la
mienne variables dupBloc[] objet.

Ensuite, nous calculons la date correcte en fonction du jour de la semaine indiqué pour l'heure de début et de
fin. Nous utilisonsTempsVersStruct() pour convertir leHeure de début etHeure de fin valeurs à
unMqlDateHeure structure. Le résultat est stocké dans leaujourd'hui objet.
Leaujourd'hui.jour_de_semaine La variable contiendra le jour actuel de la semaine. Si le jour de la semaine
en cours diffère ducommence jour oufin_jour valeur de lapBloc[] objet, nous calculons la différence et la
stockons dans lejourShift variable. LejourShift la variable est multipliée par leTIME_ADD_DAY constante, et
ajouté ou soustrait duHeure de début ouHeure de fin variables. Le résultat est queHeure de début
etHeure de fin sont réglés sur l’heure et la date correctes par rapport à la semaine en cours.

Une fois que les heures de début et de fin du bloc horaire actuel ont été déterminées, nous utilisons
leCheckTimer() fonction pour voir si l’heure actuelle se situe entre l’heure de début et l’heure de fin du bloc de
minuterie actuel. Si la fonction renvoievrai, nouscasser hors depour boucle. LeImprimerTimerMessage() La
fonction imprimera un message informatif sur le graphique et le journal, ainsi que la valeur deDécompte activé
est renvoyé au programme.

Programmer un minuteur de trading flexible est la chose la plus complexe que nous ayons eu à faire jusqu'à
présent. Avec les fonctions de minuterie que nous avons définies dans ce chapitre, vous devriez être en mesure
de mettre en œuvre une minuterie de trading suffisamment flexible pour vos besoins.

Récupération de StartTime et EndTime


Un dernier morceau de code à ajouter : si, pour une raison quelconque, vous devez vérifier l'heure de début ou
de fin actuelle utilisée par le minuteur d'échange, utilisez leGetStartTime() etObtenirEndTime() les fonctions.
Ces fonctions récupèrent ledateheure variable et renvoyez-la au programme. Comme ce sont des fonctions
très courtes, nous pouvons déclarer la fonction entière dans la déclaration de classe :

class CTimer {privé : datetime


StartTime, EndTime ;

publique:
datetime GetStartTime() {return(StartTime);};
datetime GetEndTime() {return(EndTime);};
} ;

240
Acheteur : Evangelos Papageorgiou (vpapnm@yahoo.gr)
Numéro de transaction : 28J511988U193831A

Travailler avec l'heure et la date


Pour plus de clarté, nous avons supprimé les autres fonctions et variables duMinuterie classe pour cet
exemple. Noter laretour instruction entre crochets après le nom de la fonction. C'est le corps fonctionnel. Le
seul but deGetStartTime() etObtenirEndTime() est de renvoyer la valeur duHeure de début ouHeure de
fin variable au programme. Si vous disposez d'une fonction courte dont le seul but est de renvoyer une valeur,
vous pouvez alors placer le corps directement dans la déclaration de classe.

Le gestionnaire d'événements OnTimer()


Jusqu'à présent, nous n'avons utilisé que lesurInit() etSurTick() gestionnaires d’événements chez nos
conseillers experts. Il existe plusieurs gestionnaires d'événements facultatifs qui peuvent être utilisés à des fins
spécifiques. L'un d'eux est leAu chronomètre() gestionnaire d'événements, qui s'exécute à un intervalle de
temps prédéfini.

LeSurTick() Le gestionnaire d'événements s'exécute uniquement lorsqu'un changement de prix est reçu du
terminal. LeAu chronomètre() l'événement, en revanche, peut exécuter chaqueX nombre de secondes. Si vous
souhaitez que votre stratégie de trading effectue une action à intervalles réguliers, placez ce code dans le
champAu chronomètre() gestionnaire d'événements.

Pour utiliser leAu chronomètre() gestionnaire d'événements, nous devons définir l'intervalle de minuterie à
l'aide duEventSetTimer() fonction. Cette fonction est généralement déclarée dans lesurInit() gestionnaire
d'événements ou dans un constructeur de classe. Par exemple, si nous voulons que l’expert-conseil effectue une
action toutes les 60 secondes, voici comment régler le minuteur de l’événement :

int OnInit()
{
EventSetTimer(60);
}

Cela exécutera leAu chronomètre() gestionnaire d'événements toutes les 60 secondes, s'il en existe un dans
votre programme. LeAssistant MQL5 peut être utilisé pour ajouter leAu chronomètre() événement à votre
fichier source lors de sa création, mais vous pouvez toujours l'ajouter plus tard. LeAu chronomètre() le
gestionnaire d'événements est de typevide sans paramètres :

annuler OnTimer()
{

Si, pour une raison quelconque, vous devez arrêter le minuteur de votre événement, leEventKillTimer()
supprimera la minuterie d'événement et leAu chronomètre() le gestionnaire d'événements ne fonctionnera
plus. Ceci est généralement déclaré dans leSurDéinit() gestionnaire d'événements ou un destructeur de
classe, bien qu'il devrait fonctionner n'importe où dans le programme :

EventKillTimer();

LeEventKillTimer() la fonction ne prend aucun paramètre et ne renvoie pas de valeur.

241
Acheteur : Evangelos Papageorgiou (vpapnm@yahoo.gr) ID de transaction : 28J511988U193831A
Acheteur : Evangelos Papageorgiou (vpapnm@yahoo.gr)
Numéro de transaction : 28J511988U193831A

Mettre tous ensemble

Chapitre 19 – Tout mettre ensemble

Nous avons passé la majeure partie de ce livre à créer des fonctions et des classes qui passent, clôturent,
modifient et gèrent les commandes. Nous avons ajouté des fonctionnalités utiles telles que les stop suiveurs, la
gestion de l'argent et les minuteurs de transactions. Nous avons également créé des classes qui nous permettent
d'ajouter des indicateurs et de travailler facilement avec les données de prix. Nous allons maintenant vous
montrer comment tout rassembler pour créer un conseiller expert complet et robuste.

Créer un modèle
MetaEditor 5 ne permet pas l'utilisation de modèles personnalisés comme le fait MetaEditor 4. Alors pour gagner
du temps, nous devrons créer notre propre modèle, que nous utiliserons lors de la création de nos conseillers
experts. Lorsque nous créerons un nouveau conseiller expert, nous ouvrirons simplement ce fichier et
l'enregistrerons sous un nouveau nom.

Le modèle que nous utiliserons est nomméModèle de livre MQL.mq5. Pour éviter que ce fichier ne soit
écrasé, accédez au\MQL5\Experts\Mql5Book dossier dansWindows Explorer et localisez leModèle de livre
MQL.mq5 déposer. Faites un clic droit sur le fichier et sélectionnezPropriétés. Mettez une coche dans leLecture
seulement attribut. Lorsque vous tentez d'enregistrer un fichier en lecture seule, MetaEditor vous alertera et
vous invitera à l'enregistrer dans un nouveau fichier.

Voici notre fichier modèle. Nous allons le parcourir section par section. Le premier est notre#inclure directives
et déclarations d'objet :

// Commerce
#include <Mql5Book\Trade.mqh>
CCommerce Commerce ;

// Prix
#include <Mql5Book\Price.mqh>
Prix des barres ;

// Gestion de l'argent
#include <Mql5Book\MoneyManagement.mqh>

// Arrêts suiveurs
#include <Mql5Book\TrailingStops.mqh>
Sentier CTrailing ;

// Minuterie
#include <Mql5Book\Timer.mqh>
CTimer Minuterie ;
CNewBar NouvelleBarre ;

243
Acheteur : Evangelos Papageorgiou (vpapnm@yahoo.gr)
Numéro de transaction : 28J511988U193831A

ETXPERT UNCONSEIL P.ROGRAMMATION POUR METTRADER 5


// Indicateurs
#include <Mql5Book\Indicators.mqh>
Nous incluons six fichiers du\MQL5\Include\Mql5Book annuaire. Nous avons également déclaré plusieurs
objets pour les classes dans ces fichiers. LeCommerce L'objet contient nos fonctions de passation de
commandes. LePrix L'objet est destiné aux fonctions de données de prix.Piste contient les fonctions de stop
suiveur, etMinuteur etNouvelleBarre contient le minuteur d'échange et de nouvelles fonctions de barre. Si
vous avez besoin d'ajouter des indicateurs à votre conseiller expert, vous créerez ici des objets indicateurs,
idéalement sous le#inclure directive pour leIndicateurs.mqh déposer.

Ensuite, nous avons quelques propriétés descriptives et nos variables d'entrée :

//+------------------------------------------------------------- -------------------+
//| Informations d'experts |
//+------------------------------------------------------------- -------------------+

#propriété copyright "Andrew Young"


#version de la propriété "1.00"
#description du bien ""
#lien de propriété "http://www.expertadvisorbook.com"

//+------------------------------------------------------------- -------------------+
//| Variables d'entrée | //+-------------------------------------------------------------
-------------------+

entrée ulong Slippage = 3 ;


input bool TradeOnNewBar =
true ;

chaîne de sinput MM ; // Entrée de


Money Management bool
UseMoneyManagement = true; saisir le
double RiskPercent = 2 ; entrée double
Volume Fixe = 0,1 ;

chaîne d'entrée SL ; // Entrée Stop Loss &


Take Profit int StopLoss = 20 ; entrée int
TakeProfit = 0 ;

chaîne d'entrée TS ; // Entrée


Trailing Stop bool UseTrailingStop =
false; entrée int TrailingStop = 0 ;
entrée int MinimumProfit = 0 ; entrée
int Étape = 0 ;

chaîne sinput BE ; // Entrée du


seuil de rentabilité bool
UseBreakEven = false; entrée int
BreakEvenProfit = 0 ; entrée int
LockProfit = 0 ;

244
Acheteur : Evangelos Papageorgiou (vpapnm@yahoo.gr)
Numéro de transaction : 28J511988U193831A

Mettre tous ensemble


chaîne d'entrée TI ; // Entrée
du minuteur bool UseTimer =
false; entrée int StartHour =
0 ; entrée int StartMinute = 0 ;
entrée int EndHour = 0 ; entrée
int EndMinute = 0 ; input bool
UseLocalTime = false;

Le#propriété les directives décrivent notre programme et apparaîtront dans leCommun onglet dans le
conseiller expertPropriétésdialogue. La section de saisie comprend les paramètres de nos fonctionnalités les
plus couramment utilisées, notamment la gestion de l'argent, le stop suiveur et le seuil de rentabilité, ainsi que le
minuteur de transaction. Les fonctionnalités facultatives ont toutes des variables d'entrée booléennes pour
activer ou désactiver la fonctionnalité. Noter lasinput variables – celles-ci divisent nos variables d’entrée en
sections clairement définies dans leContributions fenêtre.

Ensuite, nous avons nos variables globales et lessurInit() gestionnaire d'événements:

//+------------------------------------------------------------- -------------------+
//| Variables globales |

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

-------------------+ bool glBuyPlaced, glSellPlaced;

//+------------------------------------------------------------- -------------------+
//| Fonction d'initialisation experte |
//+------------------------------------------------------------- -------------------+

int OnInit()
{
Trade.Deviation
(Dérapage); retour(0); }

LeglAcheterPlacé etglVendrePlaced les variables permettent de savoir si une position a été précédemment
ouverte dans la direction spécifiée. Dans la plupart des systèmes de trading, nous souhaitons uniquement ouvrir
une position sur un nouveau signal commercial, et ne pas en ouvrir une autre jusqu'à ce qu'un signal commercial
apparaisse dans la direction opposée, ou qu'une condition se produise où nous définissons l'une de ces
variables surFAUX. LesurInit() Le gestionnaire d'événements contient du code qui s'exécutera une fois au
premier démarrage du programme. Ci-dessus, nous avons défini le dérapage commercial.

Ensuite, nous allons examiner leSurTick() gestionnaire d'événements:

//+------------------------------------------------------------- -------------------+
//| Fonction de tick expert |
//+------------------------------------------------------------- -------------------+

annuler OnTick()

245
Acheteur : Evangelos Papageorgiou (vpapnm@yahoo.gr)
Numéro de transaction : 28J511988U193831A

ETXPERT UNCONSEIL P.ROGRAMMATION POUR METTRADER 5


{
// Recherche une nouvelle barre
bool newBar = vrai ;
int barShift = 0;

si (TradeOnNewBar == vrai)
{ newBar = NewBar.CheckNewBar(_Symbol,_Period);
barreShift = 1 ;
}

// Minuterie bool
timerOn = true ; si
(UseTimer == vrai)
{ timerOn = Timer.DailyTimer(StartHour,StartMinute,EndHour,EndMinute,UseLocalTime);
}

// Mettre à jour les prix


Prix.Update(_Symbol,_Period);

La première section du code vérifie l'ouverture d'un nouveau bar. Nous déclarons une variable booléenne
nomméenouveauBar et initialisez-le àvrai. LebarreShift La variable est initialisée à zéro. Si laTradeOnNewBar
la variable d'entrée est définie survrai, nous appelons leVérifierNouvelleBar() fonction et enregistrez le
résultat dansnouveauBar, et définissez lebarreShift variable à 1. Sinon,nouveauBar reste réglé
survrai,barreShift reste nul et le trading intrabar est activé.

Après cela vient le code de la minuterie. Nous définissons une variable booléenne nomméeDécompte activé et
initialisez-le àvrai. Si laUtiliser la minuterie la variable d'entrée est définie survrai, nous vérifions
leMinuterie quotidienne() fonction et attribuez la valeur de retour àDécompte activé. Sinon, la valeur
deDécompte activé restesvrai. Si nous utilisons des données en barres dans notre conseiller expert,
lePrix.Mise à jour() la fonction met à jour leBar objet avec des données de prix et de temps.

Voici ensuite notre code de passation de commande avec notre fonction de gestion de l'argent :

// Passation de commande
if(newBar == true && timerOn == true)
{
// Gestion de l'argent
double tradeSize ;
if(UseMoneyManagement == true) tradeSize =
MoneyManagement(_Symbol,FixedVolume,RiskPercent,StopLoss);
sinon tradeSize = VerifyVolume(_Symbol,FixedVolume);

// Ouvrir l'ordre d'achat


if(PositionType() != POSITION_TYPE_BUY && glBuyPlaced == false)
{ glBuyPlaced = Trade.Buy(_Symbol,tradeSize);
si (glBuyPlaced == vrai)
{ double openPrice = PositionOpenPrice(_Symbol);

246
Acheteur : Evangelos Papageorgiou (vpapnm@yahoo.gr)
Numéro de transaction : 28J511988U193831A

Mettre tous ensemble


double buyStop = BuyStopLoss(_Symbol,StopLoss,openPrice);
if(buyStop > 0) AdjustBelowStopLevel(_Symbol,buyStop);

double buyProfit = BuyTakeProfit(_Symbol,TakeProfit,openPrice);


if(buyProfit > 0) AdjustAboveStopLevel(_Symbol,buyProfit);

si (acheterStop > 0 || acheterProfit > 0)


Trade.ModifyPosition(_Symbol,buyStop,buyProfit);
glSellPlaced = faux ;
}
}

Nous vérifions la valeur dunouveauBar etDécompte activé variables pour déterminer s’il faut vérifier les
conditions de trading. Si les deux sontvrai, Nous procédons. Le code de gestion de l’argent vient ensuite. Si
laUtiliserMoneyManagement la variable d'entrée estvrai, nous calculons le volume des échanges et le
sauvegardons dans letaille du commerce variable. Sinon, nous utilisons leVérifierVolume() fonction pour
vérifier laVolume Fixe réglage et enregistrez le résultat dans letaille du commerce variable.

Viennent ensuite les conditions des ordres d’achat. Nous avons inséré deux conditions commerciales, le type de
position et la valeur deglAcheterPlacé. LeType de position() la fonction renverra le type de position
actuel, ou-1 s'il n'y a pas de poste ouvert. Puisque nous ne voulons pas ouvrir d’ordre si une position est déjà
ouverte, nous n’ouvrons un ordre que si aucune position d’achat n’est ouverte. LeglAcheterPlacé La variable
détermine si une position d'achat était précédemment ouverte pour ce signal commercial. Nous devrons ajouter
plus de conditions à celasi opérateur, mais c'est le minimum pour l'instant.

Si les conditions commerciales sont vraies, leAcheter() fonction duCommerce L'objet passera notre commande
et définira leglAcheterPlacé variable à true si la commande a été passée avec succès. SiglAcheterPlacé est
vrai, alors nous procédons à la modification de la position.

Nous récupérons le prix d'ouverture de la position et stockons cette valeur dans leprix ouvert variable.
Ensuite, nous appelons le
AcheterStopLoss() fonction et enregistrez la valeur dans leacheterStop variable. LeAjuster au-dessous
du niveau d'arrêt() la fonction vérifie leacheterStop prix et l'ajuste si nécessaire. On fait la même chose
pour leacheterProfit variable. SiacheterStop ouacheterProfit est supérieur à zéro, nous appelons
leModifierPosition() fonction pour ajouter le stop loss et prendre le profit à la position d'achat. Finalement,
leglVendrePlaced la variable est définie surFAUX.

Le code de placement d’ordre de vente est très similaire. Voici pour être complet :

// Ouvrir l'ordre de vente


if(PositionType() != POSITION_TYPE_SELL && glSellPlaced == false)
{ glSellPlaced = Trade.Sell(_Symbol,tradeSize);
si (glSellPlaced == vrai)
{ faire dormir (100); while(PositionSelect(_Symbol) ==
false); double openPrice = PositionOpenPrice(_Symbol);

247
Acheteur : Evangelos Papageorgiou (vpapnm@yahoo.gr)
Numéro de transaction : 28J511988U193831A

ETXPERT UNCONSEIL P.ROGRAMMATION POUR METTRADER 5


double sellStop = SellStopLoss(_Symbol,StopLoss,openPrice);
if(sellStop > 0) sellStop = AdjustAboveStopLevel(_Symbol,sellStop);

double sellProfit = SellTakeProfit(_Symbol,TakeProfit,openPrice); if(sellProfit


> 0) sellProfit = AdjustBelowStopLevel(_Symbol,sellProfit);

si (venteStop > 0 || venteProfit > 0)


Trade.ModifyPosition(_Symbol,sellStop,sellProfit);
glBuyPlaced = faux ;
}
}

} // Fin du placement de la commande

Après le code de placement d’ordre de vente, la dernière tranche de clôture termine notre bloc de placement
d’ordre. N'oubliez pas que tout ce qui se trouve entre ces parenthèses n'est exécuté que si une nouvelle barre
vient de s'ouvrir, si la minuterie est active ou si les deux fonctionnalités sont désactivées.

Enfin, nous avons le code du seuil de rentabilité et du stop suiveur, ainsi que la fin duSurTick() gestionnaire
d'événements:

// Seuil de rentabilité
if(UseBreakEven == true && PositionType(_Symbol) != -1)
{
Trail.BreakEven(_Symbol,BreakEvenProfit,LockProfit);
}

// Arrêt suiveur
if(UseTrailingStop == true && PositionType(_Symbol) != -1)
{
Trail.TrailingStop(_Symbol,TrailingStop,MinimumProfit,Step);
}

} // Fin de OnTick()

Nous venons de créer un modèle de conseiller expert qui comprend la gestion de l'argent, un minuteur de
transaction, un nouveau contrôle de barre, un stop suiveur, un stop au seuil de rentabilité, une gestion des
erreurs, une vérification des prix et bien plus encore. Il ne nous reste plus qu'à ajouter nos indicateurs et nos
conditions d'ouverture et de clôture des commandes.

Nous allons créer deux conseillers experts en utilisant ce modèle. Le premier est un croisement de moyenne
mobile, qui est un système de trading de tendance. L'autre utilisera les bandes de Bollinger et le RSI pour créer
un système commercial à contre-tendance.

248
Acheteur : Evangelos Papageorgiou (vpapnm@yahoo.gr)
Numéro de transaction : 28J511988U193831A

Mettre tous ensemble


Modèle de comptes de couverture
Nous créerons également un modèle pour les comptes de couverture, avec quelques différences mineures. Au
lieu d'utiliser lecommerce.mqh inclure le fichier, nous utiliserons leTradeHedge.mqh fichier et créez des objets
pour leCTradeHedge etCPpositions Des classes:

#include <Mql5Book\TradeHedge.mqh>
CTradeHedge Trade ;
CPositions Postes ;

Puisque nous utiliserons unNombre magique variable d'entrée pour distinguer nos métiers, ajoutons cela en haut
des variables d'entrée :

Glissement de la tête d'entrée =


3 ;tête d'entrée MagicNumber =
123 ;input bool TradeOnNewBar =
true ;

Les conditions d'achat et de vente diffèrent légèrement. Nous vérifierons une commande ouverte en utilisant
leCPpositions fonctions de comptage de commandes et attribuer le ticket de commande auglAcheter un
billet variable. Nous utiliserons ce numéro de ticket pour obtenir le prix d’ouverture de la commande et
modifier la commande :

si(Positions.Acheter (MagicNumber) == 0)
{glAcheter un billet =
Commerce.Acheter(_Symbol,tradeSize);

si(glAcheter un billet > 0)


{ double openPrice = PositionOpenPrice (glAcheter un
billet);

double buyStop = BuyStopLoss(_Symbol,StopLoss,openPrice);


if(buyStop > 0) AdjustBelowStopLevel(_Symbol,buyStop);

double buyProfit = BuyTakeProfit(_Symbol,TakeProfit,openPrice);


if(buyProfit > 0) AdjustAboveStopLevel(_Symbol,buyProfit);

si (buyStop > 0 || buyProfit > 0) Trade.ModifyPosition (glAcheter un


billet,acheterStop,acheterProfit);
glSellTicket = 0 ;
}
}

Pour le stop suiveur et le stop au seuil de rentabilité, nous obtiendrons un tableau de numéros de ticket et
traiterons le stop suiveur ou le stop au seuil de rentabilité pour chaque ordre ouvert :

// Récupère les tickets de


positionbillets de tête[];

249
Acheteur : Evangelos Papageorgiou (vpapnm@yahoo.gr)
Numéro de transaction : 28J511988U193831A

ETXPERT UNCONSEIL P.ROGRAMMATION POUR METTRADER 5


Positions.GetTickets(MagicNumber, billets);
int numTickets = ArraySize(tickets);

// Seuil de rentabilité
si (UseBreakEven == vrai &&numBillets > 0)
{pour(int i = 0; i < numTickets; i++)
{
Trail.BreakEven(billets[i], BreakEvenProfit, LockProfit);
}
}

// Arrêt suiveur
si (UseTrailingStop == vrai &&numBillets > 0)
{pour(int i = 0; i < numTickets; i++)
{
Trail.TrailingStop(billets[i], TrailingStop, MinimumProfit, Étape);
}
}

Vous souhaiterez évidemment modifier les conditions d’ouverture et de clôture des commandes, et vous
souhaiterez peut-être autoriser l’ouverture de plusieurs commandes à la fois. Utilisez leCPpositions fonctions
de classe pour suivre et gérer vos commandes ouvertes.

Croix de moyenne mobile


Le croisement de moyennes mobiles utilise deux
moyennes mobiles, une MA rapide et une MA lente.
Lorsque la MA rapide est supérieure à la MA lente, nous
ouvrons une position d'achat. Lorsque la MA rapide est
inférieure à la MA lente, nous ouvrons une position de
vente. Une seule position est ouverte dans chaque
direction. Lorsque les moyennes mobiles se croisent
dans la direction opposée, nous clôturons toute position
actuellement ouverte et réinitialisons leglAcheterPlacé
etglVendrePlaced variables. Tout d'abord, nous
devons déclarer deux objets basés sur le Figure 19.1 – Le
système croisé de moyenne mobile.CiMA classe – une pour le
MA rapide et une pour le lent
ET:

#include <MqlBook\Indicators.mqh>
CiMA FastMA ;
CiMA SlowMA;

L'objet qui sera utilisé pour la MA rapide est nomméFastMA, et bien sûr, l'objet du MA lent s'appelleSlowMA.
Ensuite, nous devons ajouter les paramètres des moyennes mobiles. Les noms de variables pour les paramètres
MA rapides sont préfixés par "Fast", tandis que ceux pour les paramètres MA lents sont préfixés par "Slow" :

250
Acheteur : Evangelos Papageorgiou (vpapnm@yahoo.gr)
Numéro de transaction : 28J511988U193831A

Mettre tous ensemble


chaîne d'entrée FaMA ; // MA rapide
entrée int FastMAPeriod = 10 ; entrée
ENUM_MA_METHOD FastMAMethod = 0 ; entrée
int FastMAShift = 0 ;
saisissez ENUM_APPLIED_PRICE FastMAPrice = PRICE_CLOSE ;

chaîne d'entrée SlMA ; // MA lente


entrée int SlowMAPeriod = 20 ; entrée ENUM_MA_METHOD
SlowMAMethod = 0 ; entrée int SlowMAShift = 0 ;
entrée ENUM_APPLIED_PRICE SlowMAPrice = PRICE_CLOSE ;

Nous avons un SMA de 10 périodes et un SMA de 20 périodes configurés par défaut. Ensuite, nous devons
initialiser les indicateurs. Nous faisons cela dans lesurInit() gestionnaire d'événements:

int OnInit()
{
FastMA.Init(_Symbol,_Period,FastMAPeriod,FastMAShift,FastMAMethod,FastMAPrice);
SlowMA.Init(_Symbol,_Period,SlowMAPeriod,SlowMAShift,SlowMAMethod,SlowMAPrice);

Trade.Deviation (Dérapage);

retour(0); }

Il ne nous reste plus qu'à utiliser lePrincipal() fonction duFastMA etSlowMA objets pour récupérer les valeurs
moyennes mobiles. Nous faisons cela dans lesi opérateurs qui détiennent les conditions des ordres d’achat et
de vente :

// Ouvrir l'ordre d'achat


si(FastMA.Main (barShift) > SlowMA.Main (barShift) && PositionType() != POSITION_TYPE_BUY
&& glBuyPlaced == false)
{ glBuyPlaced = Trade.Buy(_Symbol,tradeSize);

si (glBuyPlaced == vrai)
{ double openPrice = PositionOpenPrice(_Symbol);
double buyStop =
BuyStopLoss(_Symbol,StopLoss,openPrice);
if(buyStop > 0)
AdjustBelowStopLevel(_Symbol,buyStop);

double buyProfit = BuyTakeProfit(_Symbol,TakeProfit,openPrice);


if(buyProfit > 0) AdjustBelowStopLevel(_Symbol,buyProfit);

if(buyStop > 0 || buyProfit > 0) Trade.ModifyPosition(_Symbol,buyStop,buyProfit);


glSellPlaced = faux ;
}
}

251
Acheteur : Evangelos Papageorgiou (vpapnm@yahoo.gr)
Numéro de transaction : 28J511988U193831A

ETXPERT UNCONSEIL P.ROGRAMMATION POUR METTRADER 5


Si la MA rapide est supérieure à la MA lente, aucune position d'achat n'est actuellement ouverte
etglAcheterPlacé estFAUX, nous ouvrons un ordre d'achat. Notez l'utilisation dubarreShift variable pour
lePrincipal() paramètre de fonction. Si
TradeOnNewBar est réglé survrai,barreShift sera toujours 1. Par conséquent, nous vérifierons la valeur de la
dernière barre, au lieu de la barre actuelle. SiTradeOnNewBar est réglé surFAUX,barreShift sera zéro, et nous
vérifierons la valeur de la barre actuelle.

Et voici la condition pour un ordre de vente :

// Ouvrir l'ordre de vente


si(FastMA.Main (barShift) < SlowMA.Main (barShift) && PositionType() != POSITION_TYPE_SELL
&& glSellPlaced == faux)
{ //...
}

C'est ça! Nous disposons désormais d'un cross à moyenne mobile fonctionnel avec gestion de l'argent, stop
suiveur, stop à l'équilibre et minuteur de trading. Vous pouvez consulter le code dans leMoyenne mobile
Cross.mq5 fichier dans le\MQL5\Experts\ Mql5Book dossier.

Moyenne mobile croisée pour les comptes de couverture


Un conseiller expert croisé en moyenne mobile pour les comptes de couverture est inclus dans le\MQL5\
Experts\Mql5Book dossier. Les changements sont similaires à ceux décrits sous leModèle de comptes de
couverture sujet ci-dessus.

Système de contre-tendance bandes/RSI


Notre deuxième exemple utilisera deux indicateurs : le RSI et les bandes de Bollinger. Il s’agira d’un système à
contre-tendance, ce qui signifie que nous rechercherons les prix extrêmes et tenterons de négocier dans la
direction opposée. Tout d’abord, nous recherchons que le prix s’écarte des bandes extérieures de Bollinger.
Ensuite, nous recherchons une condition de surachat ou de survente sur le RSI. Par exemple, si le prix évolue en
dessous de la bande inférieure de Bollinger et que le RSI est inférieur à 30, nous ouvrirons un ordre d'achat. Si le
prix est supérieur à la bande supérieure et que le RSI est supérieur à 70, nous ouvrirons un ordre de vente.

252
Acheteur : Evangelos Papageorgiou (vpapnm@yahoo.gr)
Numéro de transaction : 28J511988U193831A

Mettre tous ensemble

Figure 19.2 – Un système de contre-tendance utilisant les bandes de Bollinger et le RSI.

Commençons par déclarer nos objets indicateurs :

#include <Mql5Book\Indicators.mqh>
Bandes CiBollinger ;
CiRSI RSI ;

Ensuite, nous ajouterons nos paramètres d'entrée :

chaîne d'entrée BB ; // Bandes de Bollinger


entrée int BandsPeriod = 20 ;
entrée int BandsShift = 0 ;
entrée double BandsDeviation =
2 ;
entrée ENUM_APPLIED_PRICE BandsPrice = PRICE_CLOSE ;

chaîne d'entrée RS ; // Entrée RSI int RSIPeriod


= 8 ; entrée ENUM_APPLIED_PRICE RSIPrice =
PRICE_CLOSE ;

Nous ajouterons l'indicateurchaleur() fonctions à lasurInit() gestionnaire d'événements:

int OnInit()
{Bands.Init (_Symbol,_Period,BandsPeriod,BandsShift,BandsDeviation,BandsPrice);
RSI.Init(_Symbol,_Period,RSIPeriod,RSIPrice);
Trade.Deviation (Dérapage);

retour(0);
}

253
Acheteur : Evangelos Papageorgiou (vpapnm@yahoo.gr)
Numéro de transaction : 28J511988U193831A

ETXPERT UNCONSEIL P.ROGRAMMATION POUR METTRADER 5


Enfin, nous ajouterons nos conditions d’ordre d’achat et de vente :

// Ouvrir l'ordre d'achat


si(Bar.Close(barShift) < Bands.Lower(barShift) && RSI.Main(barShift) < 30
&& PositionType() != POSITION_TYPE_BUY && glBuyPlaced == false)
{ //...
}

// Ouvrir l'ordre de vente


si(Bar.Close(barShift) > Bands.Upper(barShift) && RSI.Main(barShift) > 70
&& PositionType() != POSITION_TYPE_SELL && glSellPlaced == false)
{ //...
}

Si le prix de clôture de la barre actuelle est inférieur au prix inférieur de la bande de Bollinger et que le RSI est
inférieur à 30, nous ouvrons un ordre d'achat. Si la clôture est supérieure à la bande supérieure de Bollinger et
que le RSI est supérieur à 70, nous ouvrons un ordre de vente.

En testant cela dans Strategy Tester, nous pouvons voir qu’il y a place à amélioration. D’une part, les
commandes arrivent généralement trop tôt. Au lieu d'attendre que le prix revienne à l'intérieur des bandes,
l'ordre s'ouvre immédiatement. Deuxièmement, cette stratégie pourrait bénéficier de l'ajout d'une position
ouverte à mesure que de nouveaux signaux commerciaux apparaissent dans la même direction.

Redéfinissons nos conditions de trading : tout d'abord, nous recherchons un mouvement du prix en dehors des
bandes, accompagné d'une condition de surachat ou de survente sur le RSI. Ensuite, nous attendons que le prix
revienne à l'intérieur des bandes, moment auquel nous ouvrons la commande.

Nous devrons vérifier les conditions commerciales en deux étapes. Tout d’abord, nous vérifions si le prix de
clôture est en dehors des bandes et si une condition RSI de surachat ou de survente existe. Ensuite, nous
vérifions si le prix a évolué à l’intérieur des bandes. Le premier ensemble de conditions devra être vérifié en
dehors des conditions normales des ordres d’achat/vente. Créons une variable globale qui contiendra la valeur
du signal :

enum Signal
{
SIGNAL_BUY,
SIGNAL_SELL,
SIGNAL_NONE,
} ;
Signal glSignal ;

Ceci est déclaré sur la portée globale du programme. Nous avons créé une énumération nomméeSignal avec
trois valeurs :SIGNAL_BUY,SIGNAL_SELL etSIGNAL_NONE. LeglSignal La variable globale contiendra une de ces
valeurs à tout moment.

254
Acheteur : Evangelos Papageorgiou (vpapnm@yahoo.gr)
Numéro de transaction : 28J511988U193831A

Mettre tous ensemble


Dans leSurTick() gestionnaire d'événements, avant le code de placement de commande, nous vérifierons le
premier ensemble de conditions.
Ce sont les mêmes conditions que nous avons définies précédemment :

if(Bar.Close(barShift) < Bands.Lower(barShift) && RSI.Main(barShift) < 30) glSignal


= SIGNAL_BUY;

sinon if(Bar.Close(barShift) > Bands.Upper(barShift) && RSI.Main(barShift) > 70)


glSignal = SIGNAL_SELL;

Si le cours de clôture est inférieur à la bande inférieure de Bollinger et que le RSI est inférieur à 30, nous fixons la
valeur deglSignalàSIGNAL_BUY. Cette valeur est conservée même lorsque la condition n'est plus vraie. Il en va
de même pour la condition de vente, auquel cas nous fixons la valeur deglSignal àSIGNAL_SELL.

Ensuite, nous vérifions les conditions d'ouverture de la commande. Nous vérifions la valeur deglSignal, et
déterminez si le cours de clôture a récemment franchi la bande supérieure ou inférieure :

if(glSignal == SIGNAL_BUY && Bar.Close(barShift) > Bands.Lower(barShift)


&& Bar.Close(barShift+1) <= Bands.Lower(barShift+1))
{
// Position d'achat ouverte
}

Tout d'abord, nous vérifions siglSignal contient la valeur deSIGNAL_BUY. Ensuite, nous vérifions si le prix de
clôture de la barre actuelle est supérieur à la bande inférieure. Enfin, nous vérifions si le prix de clôture de la
barre précédente était inférieur à la bande inférieure. Notez que nous ajoutons 1 aubarreShift valeur pour
référencer la barre précédente.

Nous rendons les conditions d'ouverture des ordres très spécifiques pour garantir que notre conseiller expert ne
continuera pas à ouvrir les ordres une fois que le prix évolue à l'intérieur des bandes. Notez que leType de
position() etglAcheterPlacé les conditions ont disparu. Tant que la condition spécifique de l'ordre est
remplie, nous autoriserons notre conseiller expert à ouvrir un autre ordre dans la même direction que notre
position actuelle.

Pour être complet, voici les conditions de vente :

if(glSignal == SIGNAL_SELL && Bar.Close(barShift) < Bands.Upper(barShift)


&& Bar.Close(barShift+1) >= Bandes.Upper(barShift+1))
{
// Position de vente ouverte
}

Encore une chose : après avoir ouvert la commande et confirmé qu'elle a été passée, nous réinitialisons la valeur
deglSignal àSIGNAL_NONE, pour éviter toute commande erronée :

glSignal = SIGNAL_NONE ;

255
Acheteur : Evangelos Papageorgiou (vpapnm@yahoo.gr)
Numéro de transaction : 28J511988U193831A

ETXPERT UNCONSEIL P.ROGRAMMATION POUR METTRADER 5


Nous disposons désormais d'un système commercial à contre-tendance qui se négocie exactement selon nos
souhaits. Cet exemple montre qu'il est nécessaire d'être très précis sur vos conditions de trading lors de la
programmation d'experts-conseils. Votre méthode de trading peut sembler évidente à première vue, mais il
faudra peut-être un peu plus de travail pour que votre conseiller expert la négocie correctement. Vous pouvez
voir le code de ce système commercial dans le fichierBandes RSI Countertrend.mq5, situé dans\MQL5\
Experts\Mql5Book\.

Système à contre-tendance pour les comptes de couverture


Dans le conseiller expert Bands/RSI Counter-trend pour les comptes de couverture, nous montrons comment
utiliser leCPpositions fonctions de classe pour récupérer les numéros de ticket de commande actuels. Cela
peut être utile si l'exécution de l'expert Advisor est interrompue alors que les transactions sont ouvertes :

// Postes ouverts
head buyTickets[], sellTickets[];
Positions.GetBuyTickets(MagicNumber, buyTickets);
glBuyTicket = acheterTickets[0];

Positions.GetSellTickets(MagicNumber, sellTickets);
glSellTicket = sellTickets[0];

LePositions.BuyTickets() etPositions.SellTickets() les fonctions récupèrent un tableau de tickets de


commande qui correspondent auNombre magique valeur définie par l'utilisateur. Le premier numéro de ticket
est attribué auglAcheter un billet ouglVendreTicket variable. Le reste de l'expert-conseil fonctionne de la
même manière que les autres fonctionnalités d'expert-conseil en compte de couverture décrites dans ce
chapitre.

Vous pouvez consulter le code de la version du compte de couverture de ce système de trading dans le
fichierBandes RSI Contre-tendance (Hedging).mq5, situé dans\MQL5\Experts\Mql5Book\.

Un modèle de commande en attente


Le modèle que nous avons créé plus tôt dans ce chapitre concerne les ordres au marché. Parfois, vous
souhaiterez peut-être programmer un conseiller expert qui passe des commandes en attente. Nous allons créer
un modèle qui place des ordres stop en attente au lieu d'ordres de marché.

Ce fichier est nomméModèle de livre MQL en attente.mq5, et est situé dans le\MQL5\Experts\Mql5Book
dossier. Nous noterons les changements par rapport au fichier modèle détaillé plus haut dans le chapitre :

// En attente
#include <MqlBook\Pending.mqh>
CPEn attente En attente ;

Nous incluons leEn attente.mqh inclure un fichier contenant nos fonctions de gestion des commandes en
attente et créer leEn attente objet basé sur leCPEn attente classe.

// Ouvrir un ordre stop d'achat en attente

256
Acheteur : Evangelos Papageorgiou (vpapnm@yahoo.gr)
Numéro de transaction : 28J511988U193831A

Mettre tous ensemble


if(PositionType() != POSITION_TYPE_BUY && glBuyPlaced == false)
{ Prix de commande double
= 0 ;
orderPrice = AjusterAboveStopLevel(_Symbol,orderPrice);

double buyStop = BuyStopLoss(_Symbol,StopLoss,orderPrice); double buyProfit

= BuyTakeProfit(_Symbol,TakeProfit,orderPrice); glBuyPlaced =

Trade.BuyStop(_Symbol,tradeSize,orderPrice,buyStop,buyProfit);

si (glBuyPlaced == vrai)
{ glSellPlaced = faux ;
}
}

// Ouvrir un ordre stop de vente en attente


if(PositionType() != POSITION_TYPE_SELL && glSellPlaced == false)
{ Prix de commande double
= 0 ;
orderPrice = AdjustBelowStopLevel(_Symbol,orderPrice);

double sellStop = SellStopLoss(_Symbol,StopLoss,orderPrice); double sellProfit

= SellTakeProfit(_Symbol,TakeProfit,orderPrice); glSellPlaced =

Trade.SellStop(_Symbol,tradeSize,orderPrice,sellStop,sellProfit);

si (glSellPlaced == vrai)
{ glBuyPlaced = faux ;
}
}

Ci-dessus se trouve le code de placement des ordres d’achat et de vente en attente. LePrix de la commande
La variable doit être remplie avec le prix d'ouverture de l'ordre en attente par le programmeur. L'exactitude du
prix de l'ordre est vérifiée, le stop loss et le take profit sont calculés par rapport à celui-ci et l'ordre stop en
attente est passé. Finalement, leglAcheterPlacé ouglVendrePlaced la variable est définie sur false.

Système de trading en petits groupes


Créons un système de trading d'ordres en attente à l'aide de ce modèle. Nous allons créer un système
commercial qui trouve le prix le plus élevé et le plus bas du dernierX barres. Lorsque le chronomètre de
transaction démarre, un ordre stop d'achat en attente sera placé au plus haut et un ordre stop de vente en
attente sera placé au plus bas. Le stop loss pour les deux ordres sera placé au prix opposé. Une seule
transaction dans chaque direction sera effectuée et toutes les transactions seront clôturées à l'heure de fin du
chronomètre.

257
Acheteur : Evangelos Papageorgiou (vpapnm@yahoo.gr)
Numéro de transaction : 28J511988U193831A

ETXPERT UNCONSEIL P.ROGRAMMATION POUR METTRADER 5


Pour cette stratégie, nous supprimerons le nouveau contrôle de barre, le stop suiveur et les fonctionnalités
d'équilibre. Nous supprimerons également leStopPerte,Glissement etUtiliser la minuterie paramètres.
Commençons par le#inclure directives :

// Commerce
#include <MqlBook\Trade.mqh>
CCommerce Commerce ;

// Prix
#include <MqlBook\Price.mqh>

// Gestion de l'argent
#include <MqlBook\MoneyManagement.mqh>

// Minuterie
#include <MqlBook\Timer.mqh>
CTimer Minuterie ;

// En attente
#include <MqlBook\Pending.mqh>
Figure 19.3 – Un système de trading en petits groupes en attente.
CPEn attente En attente ;

Ce sont les fichiers et objets d’inclusion dont nous aurons besoin pour cette stratégie. Examinons ensuite les
variables d'entrée :

chaîne de sinput MM ; // Entrée de


Money Management bool
UseMoneyManagement = true; saisir le
double RiskPercent = 2 ; entrée
double Volume Fixe = 0,1 ;

chaîne d'entrée TS ; // Entrée des


paramètres de trading int
HighLowBars = 8 ;entrée int
TakeProfit = 0 ;
chaîne d'entrée TI ; // Entrée
de minuterie int StartHour = 8;
entrée int StartMinute = 0 ;
entrée int EndHour = 20 ; entrée
int EndMinute = 0 ; input bool
UseLocalTime = false;

Nous avons ajouté une nouvelle variable d'entrée nomméeBarres hautes et basses. C'est le nombre de
barres que nous allons rechercher (en commençant par la barre actuelle) pour trouver le plus haut et le plus bas.
Nous l'avons regroupé avec leTirer profit variable, ajouté une variable d'entrée statique et étiqueté le groupe
comme "Paramètres commerciaux".

Passons directement au code de passation de commande. Nous reviendrons plus tard sur le code du timer :

258
Acheteur : Evangelos Papageorgiou (vpapnm@yahoo.gr)
Numéro de transaction : 28J511988U193831A

Mettre tous ensemble


// Placement de la commande
if(timerOn == true)
{
// Le plus haut, le plus bas
double hHigh = HighestHigh(_Symbol,_Period,HighLowBars);

double lLow = LowestLow(_Symbol,_Period,HighLowBars);

double diff = (hHigh - lLow) / _Point ;

// Gestion de l'argent
double tradeSize ;
if(UseMoneyManagement == true) tradeSize =
MoneyManagement(_Symbol,FixedVolume,RiskPercent,(int)diff);
sinon tradeSize = VerifyVolume(_Symbol,FixedVolume);

Si le minuteur de trading est actif, nous commencerons par trouver le plus haut et le plus bas. Nous utilisons le
Le plus élevé() etLe plus bas () fonctions que nous avons définies dans notrePrix.mqh inclure le fichier.
LeBarres hautes et basses Le paramètre indique le nombre de barres à rechercher. Les valeurs de retour
sont enregistrées dans lehÉlevé etlFaible variables, respectivement.

Puisque nous utilisons notre code de gestion financière, nous devons déterminer le stop loss en points pour
pouvoir calculer la taille du lot. Nous faisons cela en soustrayant le plus bas (lFaible) du plus haut niveau
(hÉlevé) et en le divisant par le symbole actuel_Indiquer valeur. Le résultat est enregistré dans la
variabledifférence.

Le code de gestion de l'argent est le même que celui des conseillers experts précédents, sauf que nous passons
ledifférence variable comme paramètre final duGestion de l'argent() fonction. Le(int) en face de
ladifférence variable convertit simplement la valeur dedifférence à un nombre entier, en l'arrondissant et en
évitant l'avertissement "perte possible de données due à la conversion de type" qui se produit lorsque nous
modifions implicitement les types de données.

Voici le code de l’ordre stop d’achat et de vente en attente :

// Ouvrir un ordre stop d'achat en attente


if(Pending.BuyStop(_Symbol) == 0 && glBuyPlaced ==
false)
{ double orderPrice = hHigh ;
orderPrice = AjusterAboveStopLevel(_Symbol,orderPrice);

double buyStop = lLow ;


double buyProfit = BuyTakeProfit(_Symbol,TakeProfit,orderPrice);

glBuyPlaced = Trade.BuyStop(_Symbol,tradeSize,orderPrice,buyStop,buyProfit);
}

// Ouvrir un ordre stop de vente en attente


if(Pending.SellStop(_Symbol) == 0 && glSellPlaced == false)

259
Acheteur : Evangelos Papageorgiou (vpapnm@yahoo.gr)
Numéro de transaction : 28J511988U193831A

ETXPERT UNCONSEIL P.ROGRAMMATION POUR METTRADER 5


{ double orderPrice =
lFaible ;
orderPrice = AdjustBelowStopLevel(_Symbol,orderPrice);

double venteStop = hHigh ;


double sellProfit = SellTakeProfit(_Symbol,TakeProfit,orderPrice);

glSellPlaced = Trade.SellStop(_Symbol,tradeSize,orderPrice,sellStop,sellProfit);
}

Examinons d'abord le code de l'ordre stop d'achat. Si laEn attente.BuyStop() La fonction renvoie zéro –
indiquant qu’aucune commande en attente n’est ouverte – et leglAcheterPlacé la variable estFAUX, nous
procédons à la passation de la commande. LePrix de la commande la variable reçoit la valeur dehÉlevé. Il
s'agit de notre prix d'ouverture de commande. Nous le vérifions à l'aide duAjusterAboveStopLevel() fonction
pour s’assurer qu’il n’est pas trop proche du prix actuel. LeacheterStop la variable est définie surlFaible, qui
est notre stop loss. Le take profit est calculé, s'il a été précisé. Le
Trade.BuyStop() La fonction place notre commande en attente, et si elle réussit, laglAcheterPlacé la
variable est définie sur vrai.

Lorsque le minuteur de trading est activé pour la première fois pour la journée, un ordre stop d'achat et de vente
est passé. Aucune commande n'est passée pour la journée et toutes les commandes sont clôturées à l'heure de
fin du minuteur. Jetons un coup d'œil au code de la minuterie. Cela précède le code de passation de commande
ci-dessus :

// Minuterie
bool timerOn = Timer.DailyTimer(StartHour,StartMinute,EndHour,EndMinute,UseLocalTime);

si (timerOn == faux)
{ if (PositionSelect (_Symbol) == true) Trade.Close

(_Symbol); int total = En attente.TotalPending(_Symbol);

si (total > 0)
{ billets ulong[]; En
attente.GetTickets(_Symbol,tickets)
;

pour(int i=0; i<total; i++)


{
Trade.Delete(tickets[i]);
}
}

glBuyPlaced = faux ;
glSellPlaced = faux ;
}

260
Acheteur : Evangelos Papageorgiou (vpapnm@yahoo.gr)
Numéro de transaction : 28J511988U193831A

Mettre tous ensemble


Premièrement, nous déclarons leDécompte activé variable et définissez-la à l'aide de laMinuterie
quotidienne() fonction. Si la valeur deDécompte activé estFAUX, indiquant que le minuteur est éteint, nous
devons fermer toutes les commandes ouvertes et réinitialiser notreglAcheterPlacé etglVendrePlaced
variables. Tout d’abord, nous vérifions la sortie duPositionSélection() fonction. S'il revientvrai, nous
utilisons leCommerce.Fermer() fonction pour fermer la position actuelle.

Ensuite, nous devons vérifier les commandes ouvertes en attente. LeEn attente.TotalPending() La fonction
renvoie le nombre de commandes ouvertes en attente et l'enregistre dans letotal variable. Sitotal est
supérieur à zéro, nous traitons les commandes en attente.

Nous déclarons untête tableau nommédes billets[] qui contient les numéros de ticket de toutes les
commandes ouvertes en attente. LeEn attente.GetTickets() la fonction remplit ledes billets[] tableau
avec les numéros de billets. Nous parcourons ledes billets[] tableau utilisant unpour boucle, et supprimez
chaque commande ouverte à l'aide duCommerce.Supprimer() fonction. Enfin, nous fixonsglAcheterPlacé
etglVendrePlaced à vrai.

Vous pouvez consulter le code de cet expert-conseil dans le\Experts\Mql5Book\Commande en attente


Breakout.mq5 déposer. Nous venons de démontrer comment créer plusieurs stratégies de base en utilisant les
techniques que nous avons examinées dans ce livre. Bien que certaines stratégies avancées nécessitent un peu
plus de modifications dans le fichier modèle, les bases sont là pour que vous puissiez facilement mettre en
œuvre votre stratégie sans avoir à coder une nouvelle stratégie à partir de zéro.

261
Acheteur : Evangelos Papageorgiou (vpapnm@yahoo.gr) ID de transaction : 28J511988U193831A
Acheteur : Evangelos Papageorgiou (vpapnm@yahoo.gr)
Numéro de transaction : 28J511988U193831A

Conseils & Astuces

Chapitre 20 - Trucs et astuces

Ce chapitre couvre les fonctionnalités MQL5 supplémentaires qui peuvent être utiles dans vos projets Expert
Advisor. Nous discuterons des méthodes permettant d'informer les utilisateurs des erreurs et des événements
commerciaux ; travailler avec des objets graphiques dans MetaTrader ; apprendre à lire et à écrire dans des
fichiers CSV ; travaillez avec des variables de terminal et apprenez comment arrêter l'exécution d'un conseiller
expert et fermer le terminal de trading.

Informations utilisateur et interaction


Il est parfois nécessaire d'informer l'utilisateur d'actions commerciales, d'erreurs ou d'autres événements. Cette
section examine les boîtes de dialogue, les notifications par e-mail et mobiles, les alertes sonores et les
commentaires graphiques.

Fonction Alerte()
Si vous devez alerter l'utilisateur d'une erreur ou d'une autre condition défavorable, la boîte de dialogue d'alerte
intégrée est idéale. La fenêtre de dialogue Alerte affiche un journal en cours des messages d'alerte, indiquant
l'heure et le symbole pour chacun. Si les événements sonores sont activés dans lePossibilités onglet duOutils
menu, puis un son d'alerte retentira lorsque la boîte de dialogue apparaîtra.

LeAlerte() La fonction est utilisée pour afficher la boîte de dialogue d'alerte. LeAlerte() la fonction prend
n'importe quel nombre d'arguments, de n'importe quel type. Nous avons utilisé leAlerte() fonctionnent tout au
long de nos fichiers d'inclusion. Par exemple, voici unAlerte() appel de fonction lorsqu'une erreur se produit
dans notreCTrade :: OpenPosition () fonctionner danscommerce.mqh:

Alert("Ordre d'ouverture : Erreur ",result.retcode," - ",errDesc);

Cette fonction possède plusieurs arguments, notamment des chaînes et une variable entière, tous séparés par
des virgules.

Figure 20.1 - LeAlerte dialogue.

263
Acheteur : Evangelos Papageorgiou (vpapnm@yahoo.gr)
Numéro de transaction : 28J511988U193831A

ETXPERT UNCONSEIL P.ROGRAMMATION POUR METTRADER 5


Fonction MessageBox()
Si vous préférez quelque chose d'un peu plus élaboré, ou si vous devez accepter la saisie de l'utilisateur,
leMessagerie() La fonction vous permet d'afficher une boîte de dialogue Windows standard avec du texte,
une légende, des icônes et des boutons définis par l'utilisateur. Voici la définition duMessagerie() fonction:

int MessageBox(
string text, // Texte du message string
caption=NULL, // Légende de la barre de titre int flags=0
// Constantes des boutons et des icônes );

Letexte Le paramètre est le texte à afficher dans la fenêtre de dialogue. Lelégende Le paramètre est le texte à
afficher dans la barre de titre de la fenêtre de dialogue. Ledrapeaux Le paramètre définit les boutons à afficher,
ainsi que les icônes et les boutons par défaut. LeMessagerie() les drapeaux peuvent être vus dans leRéférence
MQL5 sousConstantes standard... > Constantes d'entrée/sortie > MessageBox. Voici quelques-uns des
indicateurs de bouton les plus couramment utilisés :

• MB_OK – Bouton OK

• MB_OKCANCEL – Boutons OK et Annuler

• MB_OUI NON – Boutons Oui et Non

• MB_RETRYCANCEL – Boutons Réessayer et Annuler

Créons une simple boîte de message avec du texte, une légende et des boutons oui/non. Par exemple, si vous
souhaitez inviter l'utilisateur à passer un ordre lorsqu'un signal de trading est reçu :

int place = MessageBox("Passer la commande ?","Confirmation",MB_YESNO);

Cela créera la boîte de dialogue de boîte de message affichée à


droite.

Nous pouvons ajouter du contexte à notre boîte de message en


ajoutant une icône. Puisque nous posons une question à l’utilisateur,
nous utiliserons l’icône de point d’interrogation. Voici les drapeaux
pour les icônes de la boîte de message :

• MB_ICONERROR – Une icône d'erreur rouge.

• MB_ICONQUESTION – Une icône de point d’interrogation.


Figure 20.2 - LeMessagerie dialogue.

• MB_ICONWARNING – Une icône d’exclamation/avertissement.

• MB_ICONINFORMATION – Une icône d’information.

264
Acheteur : Evangelos Papageorgiou (vpapnm@yahoo.gr)
Numéro de transaction : 28J511988U193831A

Conseils & Astuces


Voici comment nous ajouterions l'icône de point d'interrogation à notre boîte de message. Les drapeaux doivent
être séparés par un caractère barre verticale (|) :

int place = MessageBox("Passer la commande ?","Confirmation",MB_YESNO|MB_ICONQUESTION);

La figure 20.3 à droite montre la boîte de dialogue Boîte de message


avec l'icône de point d'interrogation.

Lorsqu'il reçoit la boîte de message, l'utilisateur clique sur un bouton


et la boîte se ferme. Si la boîte de message est une simple
information ou un message d'erreur avec un seul bouton OK, nous
n'avons rien d'autre à faire. Mais si la boîte de message comporte
plusieurs boutons, nous devrons récupérer la valeur du bouton sur
lequel l’utilisateur a appuyé et prendre les mesures appropriées.
Figure 20.3 - LeMessagerie dialogue avec icône.
Dans l'exemple ci-dessous, lelieu La variable entière contiendra la
valeur de retour duMessagerie() fonction. La valeur de retour du
bouton Oui estIDYES, tandis que la valeur de retour du bouton Non estN° d'identification. Si l'utilisateur
a cliqué sur Oui, nous devons alors passer la commande :

int place = MessageBox("Passer la commande ?","Confirmation",MB_YESNO|MB_ICONQUESTION);

si (lieu == IDYES)
{
// Poste libre
}

Vous pouvez afficher des constantes de valeur de retour supplémentaires pourMessagerie() boutons dans
leRéférence MQL5 sousConstantes standard... > Constantes d'entrée/sortie > MessageBox.

Fonction SendMail()
Si vous souhaitez que votre conseiller expert vous envoie un e-mail chaque fois qu'une transaction est
effectuée, ajoutez simplement leEnvoyer un mail() fonction à votre conseiller expert après confirmation de la
passation de la commande. Vous devrez activer les notifications par e-mail dans MetaTrader sous leOutils Menu
>Options > E-mail languette. Sous leE-mail , vous entrerez les informations de votre serveur de messagerie et
votre adresse e-mail.

LeEnvoyer un mail() la fonction prend deux paramètres. Le premier est lesujet du message, et le second est
lemessage texte. Par exemple, si vous souhaitez envoyer un email juste après l’ouverture d’une position d’achat :

// Inclure les déclarations


#include <Mql5Book\Trade.mqh>
si (PositionSelect (_Symbol) == vrai)
{ string subject = "Position d'achat ouverte sur
"+_Symbol ;
string message = "Prix : "+PositionOpenPrice()+", SL : "+PositionStopLoss()+",

265
Acheteur : Evangelos Papageorgiou (vpapnm@yahoo.gr)
Numéro de transaction : 28J511988U193831A

ETXPERT UNCONSEIL P.ROGRAMMATION POUR METTRADER 5


TP : "+PositionTakeProfit();
SendMail (sujet, message);
}

Si laPositionSélection() La fonction renvoie vrai, indiquant qu'une position a été ouverte avec succès, nous
préparerons le sujet et le corps du message de l'e-mail et transmettrons ces variables de chaîne auEnvoyer un
mail() fonction. Un email sera envoyé à l'adresse email définie dans leE-mail onglet duOutilsMenu >
Possibilités

dialogue.

Envoi de notifications mobiles


Une édition mobile de MetaTrader est disponible pour les appareils iPhone et Android. Vos conseillers experts
peuvent envoyer des notifications de transactions au terminal mobile MetaTrader sur votre smartphone en
utilisant leEnvoyerNotification()fonction. Vous devrez configurer MetaTrader pour envoyer des notifications
sous leOutils Menu >Options > Notifications languette. Dans leNotifications , vous entrerez votre identifiant
MetaQuotes, que vous pourrez récupérer à partir de votre version mobile de MetaTrader.

LeEnvoyerNotification() La fonction prend un paramètre, une chaîne indiquant le texte à envoyer. Vous
pouvez utiliser leEnvoyerNotification() fonctionner partout où vous utiliseriez leEnvoyer un mail()
fonction. Voici un exemple :

// Inclure les déclarations


#include <Mql5Book\Trade.mqh>

si (PositionSelect (_Symbol) == vrai)


{ string message = "Position d'achat ouverte sur "+_Symbol+". Prix :
"+PositionOpenPrice()+",
SL : "+PositionStopLoss()+",TP : "+PositionTakeProfit();
EnvoyerNotification(message);
}

Jouer du son
LeJouer son() fonction jouera un fichier son WAV situé dans le\Des sons annuaire. Cela peut être utilisé pour
émettre une alerte sonore lorsqu'une transaction est effectuée ou chaque fois que vous souhaitez attirer
l'attention de l'utilisateur. MetaTrader est livré avec plusieurs fichiers audio, mais vous pouvez en trouver
davantage en ligne.

Si vous souhaitez que l'utilisateur puisse choisir le fichier son, utilisez une variable de chaîne d'entrée pour saisir
le nom du fichier :

chaîne d'entrée SoundFile = "alert.wav" ;


//...

266
Acheteur : Evangelos Papageorgiou (vpapnm@yahoo.gr)
Numéro de transaction : 28J511988U193831A

Conseils & Astuces


JouerSound(SoundFile);

Fonction Commentaire()
LeCommentaire() La fonction affichera le texte dans le coin supérieur gauche du graphique. Ceci est utile pour
informer les utilisateurs des actions entreprises par l'expert-conseil, telles que la modification ou la clôture des
commandes. Nous avons utilisé leCommentaire() travaillez avec nos conseillers experts pour rédiger des
commentaires informatifs sur le graphique. Voici un exemple duCTrade :: OpenPosition () fonctionner
danscommerce.mqh:

si (checkCode == CHECK_RETCODE_OK)
{
Commentaire(orderType," position ouverte à ",result.price," sur ",pSymbol); revenir
(vrai);
}

Le problème avec l’utilisation des commentaires de graphiques est que tous les programmes ont accès à cette
fonction. Donc, si vous disposez d'un indicateur et d'un conseiller expert qui écrivent tous deux des
commentaires sur le graphique en utilisant leCommentaire() fonction, ils s’écraseront mutuellement. Si vous
devez afficher des informations sur le graphique qui sont toujours présentes, envisagez d'utiliser des objets
graphiques.

Objets du graphique
Les objets graphiques sont constitués de lignes, d'outils d'analyse technique, de formes, de flèches, d'étiquettes
et d'autres objets graphiques. Vous pouvez insérer des objets sur un graphique à l'aide de l'outilInsérer Menu
>Objets sous-menu. MQL5 dispose d'une variété de fonctions pour créer, manipuler et récupérer des
informations à partir d'objets graphiques.

Création et modification d'objets


LeObjetCréer() La fonction est utilisée pour créer de nouveaux objets graphiques. Voici la définition de la
fonction pour

ObjetCréer():

bool ObjetCréer(
long chart_id, // nom de chaîne d'identifiant de
graphique, // nom d'objet type ENUM_OBJECT, // type
d'objet sub_window nwin, // index de fenêtre
datetime time1, // heure du premier point d'ancrage double
price1, // prix du premier point d'ancrage
...
datetime timeN=0, // heure du N-ème point d'ancrage
double priceN=0, // prix du N-ème point d'ancrage );

267
Acheteur : Evangelos Papageorgiou (vpapnm@yahoo.gr)
Numéro de transaction : 28J511988U193831A

ETXPERT UNCONSEIL P.ROGRAMMATION POUR METTRADER 5


Lechart_id Le paramètre spécifie le graphique sur lequel créer l’objet. Puisque nous travaillerons toujours avec
le graphique actuel,chart_id sera 0. Lenom Le paramètre est le nom de l'objet à créer. Nous devrons référencer
ce nom chaque fois que nous aurons besoin d’utiliser l’objet. Letaper Le paramètre est le type d’objet à créer. Il
prend une valeur deENUM_OBJECT type d'énumération. Les types d'objets peuvent être visualisés dans
leRéférence MQL5 sousConstantes standard... > Constantes d'objets > Types d'objet.

Lenwin Le paramètre est l'index de la sous-fenêtre du graphique. Les sous-fenêtres sont ouvertes par des
indicateurs tels que RSI et stochastiques qui s'affichent dans une fenêtre séparée. Puisque nous allons créer des
objets dans la fenêtre principale du graphique,nwin sera 0. Leheure1 etprix1 les paramètres sont le temps et le
prix du premier point d’ancrage de l’objet. La plupart des objets possèdent un ou plusieurs points d'ancrage,
même si certains ne les utilisent pas, ou n'en utilisent que l'un ou l'autre. Le premier ensemble de points
d'ancrage doit toujours être spécifié, même s'ils ne sont pas utilisés.

Si l'objet utilise plusieurs jeux de points d'ancrage, ils doivent également être spécifiés. Par exemple, la plupart
des objets de ligne de tendance utilisent deux points d'ancrage. Donc dans leObjetCréer() fonction, un
deuxième ensemble de points d’ancrage serait transmis à la fonction. Vous pouvez afficher le nombre de points
d'ancrage pour chaque type d'objet dans la référence MQL5 sousFonctions d'objet > ObjectCreate.

LeObjetCréer() La fonction créera l’objet à l’emplacement spécifié. Cependant, nous devrons toujours définir
les propriétés de l'objet. LeEnsemble d'objets...() les fonctions sont utilisées pour définir les propriétés de
l'objet. Il ya troisEnsemble d'objets...() les fonctions:ObjectSetInteger(),ObjetSetDouble()
etObjetEnsembleString(). Les trois fonctions partagent les mêmes paramètres. Regardons la définition de la
fonction deObjectSetInteger():

bool ObjectSetInteger(
long chart_id, // nom de la chaîne de
l'identifiant du graphique, // nom de l'objet int
prop_id, // propriété long prop_value //
valeur );

Comme discuté précédemment,chart_id sera 0. Lenom Le paramètre est le nom de l'objet à modifier.
Leprop_id Le paramètre prend une valeur deENUM_OBJECT_PROPERTY_INTEGER énumération, qui indique la
propriété à modifier. Finalement, levaleur_prop Le paramètre est la valeur à définir. Le
ENUM_OBJECT_PROPERTY_INTEGER les constantes peuvent être visualisées dans leRéférence MQL5
sousConstantes standard... > Constantes d'objets > Propriétés d'objet.

À titre d'exemple, créons un objet de ligne de tendance et définissons quelques-unes de ses propriétés. Le type
d'objet pour une ligne de tendance estOBJ_TREND. Puisqu’une ligne de tendance comporte deux points
d’ancrage, nous devrons transmettre deux ensembles de valeurs de temps et de prix.
Nous supposerons que leheure1,heure2,prix1 etprix2 les variables sont remplies avec les valeurs
appropriées :

dateheure heure1, heure2 ; double prix1, prix2 ;

ObjectCreate(0,"Trend",OBJ_TREND,0,time1,price1,time2,price2);

268
Acheteur : Evangelos Papageorgiou (vpapnm@yahoo.gr)
Numéro de transaction : 28J511988U193831A

Conseils & Astuces


Cela crée un objet de ligne de tendance nommé "S'orienter" sur le graphique actuel. Modifions ensuite
quelques propriétés de notre ligne de tendance. Nous allons modifier la couleur, le style et les propriétés du
rayon droit :

ObjectSetInteger(0,"Trend",OBJPROP_COLOR,clrGreen);
ObjectSetInteger(0,"Trend",OBJPROP_STYLE,STYLE_DASH);
ObjectSetInteger(0,"Trend",OBJPROP_RAY_RIGHT,true);

La premièreObjectSetInteger() l'appel de fonction ajuste la propriété color, en utilisant leOBJPROP_COLOR


constante. La valeur utilisée est la constante de couleur du vert,clrVert. Le deuxième appel de la fonction
ajuste le style de ligne, en utilisant leOBJPROP_STYLE constante. La valeur estSTYLE_TIRET, une constante
duENUM_LINE_STYLE taper. Enfin, nous utilisons leOBJPROP_RAY_RIGHT constante pour définir la propriété ray
right survrai. La propriété ray right étend la ligne de tendance vers la droite au-delà du deuxième point
d'ancrage.

Nous avons maintenant une ligne de tendance en pointillés verts qui s’étend vers la droite. Tous les exemples ci-
dessus utilisent des types entiers pour ajuster les propriétés. Le type utilisé pour chaque constante de propriété
d'objet est répertorié dans leRéférence MQL5 sousConstantes standard... > Constantes d'objets > Propriétés
d'objet.

Figure 20.4 – Un objet de ligne de tendance avec les propriétés de couleur, de style et de rayon droit définies.

Si vous devez déplacer un objet, leObjetMove() La fonction permet d'ajuster l'un des points d'ancrage d'un
objet. Voici la définition de la fonction pourObjetMove():

bool DéplacementObjet(
long chart_id, // nom de la chaîne de
l'identifiant du graphique, // nom de l'objet int
point_index, // numéro du point d'ancrage
dateheure heure, // heure
prix double // prix );

Lenom Le paramètre est le nom de l'objet à modifier. Lepoint_index Le paramètre est le point d’ancrage à
modifier. Les numéros de points d'ancrage commencent à 0, donc le premier point d'ancrage serait 0, le second
serait 1, et ainsi de suite. Letemps etprix les paramètres modifient l’heure et le prix pour le point d’ancrage
spécifié.

269
Acheteur : Evangelos Papageorgiou (vpapnm@yahoo.gr)
Numéro de transaction : 28J511988U193831A

ETXPERT UNCONSEIL P.ROGRAMMATION POUR METTRADER 5


Récupération du temps et du prix à partir des objets de ligne
Lorsque vous utilisez des objets de ligne tels que des lignes de tendance ou des canaux, vous devrez peut-être
récupérer la valeur de prix d'une ligne à un moment donné.
barre particulière. En supposant que nous connaissons l’horodatage de la barre, nous pouvons facilement
récupérer le prix. LeObjetGetValueByTime() la fonction récupérera le prix pendant une durée spécifiée :

doubleObjetGetValueByTime(
long chart_id, // nom de la chaîne de
l'identifiant du graphique, // nom de l'objet
datetime time, // time int line_id // numéro
de ligne );

Comme toujours, lechart_id est 0 pour le graphique actuel. Lenom Le paramètre est le nom d’un objet linéaire
sur notre graphique. Letemps Le paramètre est l’horodatage d’une barre qui coupe la ligne. LeId de ligne Le
paramètre est destiné aux objets de canal comportant plusieurs lignes. Pour une ligne de tendance,Id de
ligne serait 0.

En utilisant le"S'orienter" objet de ligne que nous avons créé ci-dessus, voici comment nous récupérerions le
prix de la barre actuelle.
Nous utiliserons leCPrice::Heure() fonction que nous avons créée plus tôt dans le livre :

double TrendPrice = ObjectGetValueByTime(0,"Trend",Price.Time(),0);

LeObjetGetValueByTime() La fonction renverra la valeur de la ligne de tendance pour la barre actuelle et


enregistrera le résultat dans letendancePrix variable.

Maintenant, que se passe-t-il si vous avez un prix et que vous voulez savoir quelle barre se rapproche le plus de
ce prix ? Le
ObjetGetTimeByValue() La fonction renverra l'horodatage de la barre où un objet de ligne croise un prix
donné :

dateheure ObjectGetTimeByValue(
long chart_id, // nom de la chaîne de
l'identifiant du graphique, // nom de
l'objet double valeur, // prix int
line_id // numéro de ligne
);
Levaleur Le paramètre est le prix à rechercher. La fonction renverra l'horodatage de la barre la plus proche où
la ligne coupe le prix :

datetime TrendTime = ObjectGetTimeByValue(0,"Trend",1.265,0);

Objets d'étiquette et de flèche


Plus tôt dans le chapitre, nous avons parlé de l'utilisation duCommentaire() fonction pour écrire des
informations dans le graphique actuel. LeétiquetteL'objet peut également être utilisé à cette fin. Contrairement

270
Acheteur : Evangelos Papageorgiou (vpapnm@yahoo.gr)
Numéro de transaction : 28J511988U193831A

Conseils & Astuces


aux commentaires du graphique, les objets d’étiquette peuvent être placés n’importe où sur le graphique et
peuvent utiliser n’importe quelle couleur ou police de votre choix.

La constante de type d'objet pour l'objet étiquette estOBJ_LABEL. Les objets d'étiquette n'utilisent pas de points
d'ancrage, mais utilisent plutôt lecoin,distance x etdistance y propriétés pour le positionnement. Voici un
exemple de création et de positionnement d'un objet étiquette :

ObjectCreate(0,"Étiquette",OBJ_LABEL,0,0,0);

ObjectSetInteger(0,"Étiquette",OBJPROP_CORNER,1);
ObjectSetInteger(0,"Étiquette",OBJPROP_XDISTANCE,20);
ObjectSetInteger(0,"Étiquette",OBJPROP_YDISTANCE,40);

Nous avons créé un objet étiquette nommé"Étiquette" et réglez la position sur le coin inférieur gauche à l'aide
du boutonObjectSetInteger() fonctionner avec leOBJPROP_CORNER propriété. L'étiquette est à 20 pixels de la
bordure gauche (OBJPROP_XDISTANCE) et 40 pixels au-dessus de la bordure inférieure (OBJPROP_YDISTANCE).

Notez que letemps etprix paramètres pour leObjetCréer() sont définies sur 0, car elles ne sont pas utilisées
lors de la création d'objets d'étiquette. Les coins sont étiquetés de 0 à 3, en commençant dans le sens inverse
des aiguilles d'une montre à partir du coin supérieur gauche. Une valeur de 1 correspond au coin inférieur
gauche, tandis que 3 correspond au coin supérieur droit. La distance x et la distance y sont définies en pixels à
partir du coin spécifié.

Ensuite, nous devrons définir la couleur, la police et le texte de l'objet label :

double prix = Offre();

ObjectSetInteger(0,"Label",OBJPROP_COLOR,clrWhite);
ObjectSetString(0,"Étiquette",OBJPROP_FONT,"Arial");
ObjectSetInteger(0,"Étiquette",OBJPROP_FONTSIZE,10);
ObjectSetString(0,"Étiquette",OBJPROP_TEXT,"Enchère : "+
prix (chaîne)); Figure 20.5– L’objet étiquette.

Nous avons défini la couleur de l'objet étiquette sur blanc, en utilisant la police Arial 10 points. Le texte de
l'étiquette contient le prix actuel de l'offre. Chaque fois que ce code est exécuté, l'objet étiquette sera mis à jour
avec le prix actuel de l'offre.

En résumé, l'objet étiquette est idéal pour imprimer des informations utiles sur le graphique. L'objet est ancré à
la fenêtre graphique elle-même et ne bougera pas si le graphique défile. La police, la couleur, la position et le
texte sont entièrement réglables.

Le dernier type d’objet que nous examinerons est l’objet flèche. Vous souhaiterez peut-être dessiner une flèche
sur le graphique lorsqu'une commande est passée. MQL5 définit deux types d'objets flèches utiles pour marquer
les signaux d'achat et de vente :OBJ_ARROW_BUY etOBJ_ARROW_SELL. Les objets fléchés utilisent un point
d’ancrage – le prix et l’heure où la flèche doit être placée :

271
Acheteur : Evangelos Papageorgiou (vpapnm@yahoo.gr)
Numéro de transaction : 28J511988U193831A

ETXPERT UNCONSEIL P.ROGRAMMATION POUR METTRADER 5


chaîne time = Price.Time(); double prix = Demander();

ObjectCreate(0,"BuyArrow"+time,OBJ_ARROW_BUY,0,time,price);

L'exemple ci-dessus définira un objet flèche d'achat au prix vendeur actuel sur la
barre actuelle. Nous ajoutons l'heure de la barre actuelle au nom de l'objet afin que
chaque flèche que nous dessinons ait un nom unique. Ce code serait appelé après
un Figure 20.6 - LeAcheter Flèche objet.
le commerce a été placé.

Suppression d'objets
Pour supprimer un objet, appelez simplement leObjetSupprimer() fonction et passer lechart_id
(généralement 0) et l'objetnom:

ObjectDelete(0,"Étiquette");

Pour supprimer tous les objets d'un graphique, utilisez l'outilObjetsDeleteAll() fonction. Vous pouvez choisir
de supprimer uniquement les objets d'un certain type ou de certaines sous-fenêtres. Pour supprimer tous les
objets du graphique actuel, utilisez cet appel :

ObjetsDeleteAll(0);

C'est une bonne idée d'appeler cette fonction depuis le fichier de votre programmeSurDéinit() fonction pour
garantir que tous les objets sont supprimés lorsqu’un programme est supprimé du graphique.

Fonctions de fichiers
MQL5 dispose d'un ensemble de fonctions pour lire et écrire dans des fichiers. Vous pouvez, par exemple, créer
un journal des transactions de votre conseiller expert ou importer/exporter des signaux de trading dans un
fichier. Tous les fichiers doivent être dans le\MQL5\Experts\Fichiers dossier de votre installation MetaTrader
5.

Nous examinerons comment lire et écrire dans un fichier CSV. Un CSV (valeur séparée par des virgules) le
fichier contient des données un peu comme une feuille de calcul. Vous pouvez créer et afficher des fichiers CSV
dans des programmes tels que Microsoft Excel ou Google
Feuilles de calcul, ainsi que tout éditeur de texte. Dans cet exemple, notre fichier CSV enregistrera des
informations sur les transactions. Nous écrirons le symbole, le prix d'ouverture, le stop loss, le take profit et
l'heure d'ouverture d'une transaction sur chaque ligne du fichier. Nous lirons ensuite ces informations dans notre
programme.

LeFichierOuvert() la fonction ouvre les fichiers en lecture et en écriture. Si le fichier n'existe pas, il sera créé.
Voici la définition duFichierOuvert() fonction:

int FichierOuvert(
string file_name, // Nom du fichier int
open_flags, // Combinaison de drapeaux

272
Acheteur : Evangelos Papageorgiou (vpapnm@yahoo.gr)
Numéro de transaction : 28J511988U193831A

Conseils & Astuces


short delimiter='\t' // Délimiteur uint
codepage=CP_ACP // Page de codes );

Lenom de fichier Le paramètre est le nom du fichier à ouvrir dans le\MQL5\Experts\Fichiers annuaire.
Leouvrir_flags Le paramètre contient une combinaison d’indicateurs décrivant les opérations sur les fichiers.
Les drapeaux d'ouverture de fichier peuvent être visualisés dans leRéférence MQL5 sousConstantes standard...
> Constantes d'entrée/sortie > Indicateurs d'ouverture de fichier. Ledélimiteur Le paramètre est le délimiteur
de champ pour un fichier CSV. La valeur par défaut est le caractère de tabulation, mais nous utiliserons une
virgule. Lepage de code Le paramètre sera laissé à sa valeur par défaut.

LeFichierOuvert() La fonction renvoie un entier qui servira de descripteur de fichier. Chaque fois que nous
effectuons une opération sur le fichier, nous devrons référencer le descripteur de fichier. Pour les indicateurs
d'ouverture de fichier, nous utiliserons une combinaison deFILE_READ,FILE_WRITE etFILE_CSV.

Écrire dans un fichier CSV


LeFichierEcrire() La fonction est utilisée pour écrire une ligne de données dans un fichier CSV. L'exemple ci-
dessous montre comment ouvrir un fichier, y écrire une ligne de données, puis fermer le fichier :

symbole de chaîne ;

double openPrice, sl,

tp ; dateheure heure

d'ouverture ; //...

int fileHandle = FileOpen("Trades.csv",FILE_READ|FILE_WRITE|FILE_CSV,",");


FileSeek(fileHandle,0,SEEK_END);
FileWrite (fileHandle, symbole, openPrice, sl, tp, openTime);
FichierFerme(fileHandle);
Lesymbole,prix ouvert,sl,ville etheure d'ouverture les variables contiendront les informations à écrire
dans le fichier. Nous supposerons que ces variables sont remplies avec les valeurs appropriées.
LeFichierOuvert() la fonction crée un fichier nommé"Trades.csv". Les indicateurs spécifient les privilèges
de lecture et d'écriture sur un fichier CSV. Le délimiteur sera une virgule. Le descripteur de fichier est enregistré
dans ledescripteur de fichier variable.

Vous devrez ajouter leFILE_READ drapeau lors de l'utilisation duFILE_WRITE drapeau, même si vous ne lisez
pas les informations du fichier, car leRecherche de fichier() la fonction ne fonctionnera pas sans cela.
LeRecherche de fichier() La fonction déplace le pointeur de fichier vers un point spécifié dans le fichier.
Dans cet exemple, le pointeur est déplacé vers la fin du fichier. Le premier paramètre duRecherche de
fichier() La fonction est le descripteur de fichier, le deuxième est le décalage en octets et le troisième
paramètre est l'emplacement de départ. LeSEEK_END constante indique que nous allons déplacer le pointeur de
fichier vers la fin du fichier. Lors de l'ouverture d'un fichier contenant déjà des données, le fait de ne pas
déplacer le pointeur de fichier vers la fin du fichier entraînera l'écrasement des données. C’est pourquoi nous
utilisons toujoursRecherche de fichier() pour localiser la fin du fichier avant d'écrire des données.

273
Acheteur : Evangelos Papageorgiou (vpapnm@yahoo.gr)
Numéro de transaction : 28J511988U193831A

ETXPERT UNCONSEIL P.ROGRAMMATION POUR METTRADER 5


LeFichierEcrire() La fonction écrit une ligne de données dans le fichier CSV. Le premier paramètre est le
descripteur de fichier. Les paramètres restants sont les données à écrire dans le fichier, dans l'ordre dans lequel
elles apparaissent. Jusqu'à 63 paramètres supplémentaires peuvent être spécifiés, et ils peuvent être de
n'importe quel type. Le délimiteur spécifié dans leFichierOuvert() La fonction sera placée entre chaque
champ de données dans le fichier CSV et un nouveau caractère de ligne (\r\n) sera écrit en fin de ligne.

Finalement, leFichierFerme() La fonction fermera le fichier. Assurez-vous de fermer un fichier lorsque vous
avez fini de l'utiliser, sinon vous ne pourrez peut-être pas l'ouvrir dans un autre programme. Si vous prévoyez de
garder un fichier ouvert pendant une période prolongée ou si vous effectuez des opérations de lecture/écriture
ultérieures, utilisez l'optionFichierFlush() fonction pour écrire les données dans le fichier sans le fermer.

Voici ce que notreTrades.csv Le fichier ressemblera à plusieurs lignes de données écrites :

EURUSD,1.2345,1.2325,1.2375,15.11.2012 04:17:41
EURUSD,1.2357,1.2337,1.2397,15.11.2012 04:20:04
EURUSD,1.2412,1.2398,1.2432,15.11.2012 04:21:35

De gauche à droite, chaque ligne contient le symbole commercial, le prix d'ouverture, le stop loss, le take profit
et l'heure d'ouverture, chacun séparé par une virgule. Après que chaque ligne soit écrite dans le fichier, une
nouvelle ligne est démarrée.

Si vous devez écrire plusieurs lignes de données dans un fichier à la fois, placez leFichierEcrire() fonction à
l’intérieur d’une boucle et bouclez-la autant de fois que nécessaire. L'exemple ci-dessous suppose que nous
disposons de plusieurs tableaux, chacun avec le même nombre d'éléments, correctement dimensionnés et
remplis de données. (Vous pouvez également utiliser un tableau de structure pour cela.) Nous utilisons unpour
boucle pour écrire chaque ligne de données dans le fichier :

symbole de chaîne[];
double openPrice[], sl[], tp[];
dateheure

openTime[]; //...

int fileHandle = FileOpen("Trades.csv",FILE_READ|FILE_WRITE|FILE_CSV,",");


FileSeek(fileHandle,0,SEEK_END);

pour (int i = 0; i < ArraySize (symbole); i++)


{
FileWrite(fileHandle,symbole[i],openPrice[i],sl[i],tp[i],openTime[i]);
}

FichierFerme(fileHandle);

Nous utilisons leTaille du tableau() fonction sur lesymbole[] tableau pour déterminer le nombre de fois où
exécuter la boucle. Leje La variable d'incrémentation est l'index du tableau. Si chaque tableau comporte cinq
éléments, par exemple, nous écrivons cinq lignes de données dans le fichier.

274
Acheteur : Evangelos Papageorgiou (vpapnm@yahoo.gr)
Numéro de transaction : 28J511988U193831A

Conseils & Astuces


Lecture à partir d'un fichier CSV
Ensuite, nous examinerons comment lire les données d'un fichier CSV. LeFichierLire...() les fonctions sont
utilisées pour lire les données d'un champ et les convertir en un type approprié. Il existe quatre fonctions
utilisées pour lire les données d'un fichier CSV :

• FichierLireChaîne() - Lit unchaîne à partir d'un fichier CSV.

• FichierLireBool() - Lit une chaîne à partir d'un fichier CSV et la convertit enbouffon taper.

• FichierLireDatetime() - Lit une chaîne à partir d'un fichier CSV au formataaaa.mm.jj hh:mm:ss et le
convertit endateheure taper.

• FichierLireNuméro() - Lit une chaîne à partir d'un fichier CSV et la convertit endouble taper.

Si vous lisez un entier à partir d'un fichier CSV à l'aide de l'optionFichierLireNuméro() fonction, vous devrez le
convertir dans le type approprié si vous devez l'utiliser comme type entier dans votre programme.

Si nous examinons les champs de notreTrades.csv fichier, nous avons unchaîne, troisdouble des valeurs et
undateheure valeur sur chaque ligne. Nous devrons lire chaque champ de données du fichier à l'aide de la
fonction appropriée afin qu'il soit converti dans le type correct. Nous allons lire l'intégralité du contenu du
fichier, ligne par ligne, et enregistrer le résultat dans un tableau de structure :

structurer les métiers


{ symbole de
chaîne ; double
prix
d'ouverture ;
double sl; double
tp;
dateheure heure d'ouverture ;
} ;

Métiers commerce[]; int je; int fileHandle =

FileOpen("Trades.csv",FILE_READ|FILE_CSV,",");

while (FileIsEnding(fileHandle) == false)


{
ArrayResize(commerce,ArraySize(commerce) + 1);

trade[i].symbol = FileReadString(fileHandle);
trade[i].openPrice = FileReadNumber(fileHandle);
trade[i].sl = FileReadNumber(fileHandle); trade[i].tp
= FileReadNumber(fileHandle); trade[i].openTime =
FileReadDatetime(fileHandle);

je++;
}

275
Acheteur : Evangelos Papageorgiou (vpapnm@yahoo.gr)
Numéro de transaction : 28J511988U193831A

ETXPERT UNCONSEIL P.ROGRAMMATION POUR METTRADER 5


FichierFerme(fileHandle);

Tout d’abord, nous créons une structure nomméeMétiers pour contenir les données lues à partir du fichier
CSV. Nous créons un objet tableau nommécommerce[], et initialisez la variable incrémenteurje. Nous ouvrons
leTrades.csv fichier en utilisant leFILE_READ etFILE_CSV drapeaux. Lealors que loop lira chaque ligne de
données du fichier, un champ à la fois.

LeFichierEstFin() la fonction renvoie une valeur devrai si la fin du fichier est atteinte, etFAUX sinon. Tant que
la fin du fichier n'est pas atteinte, on continue la lecture de la ligne suivante. LeTableauResize() la fonction
redimensionne notrecommerce[] tableau, un élément à la fois. Nous appelons leTaille du tableau()
fonction pour obtenir la taille actuelle ducommerce[] tableau et ajoutez-y 1 pour augmenter la taille.

LeFichierLire...() Les fonctions lit chaque champ de données du fichier et le convertit dans le type
approprié. Le résultat est enregistré dans la variable membre appropriée de notremétiers[] tableau. Une fois la
ligne courante lue, nous incrémentons leje variable et vérifiez laFichierEstFin() état à nouveau. Une fois la
boucle terminée, nous fermons le fichier. Nous pouvons désormais accéder aux données lues depuis notre
fichier CSV en utilisant lecommerce[] objet tableau.

Variables globales
MetaTrader a la capacité de sauvegarder des variables sur le terminal, qui restent même si le terminal est arrêté.
Ceux-ci sont appelésVariables globales. Les variables globales enregistrées sur le terminal sont supprimées
après un mois. Vous pouvez afficher les variables globales enregistrées sur votre terminal en cliquant sur le
boutonOutils Menu >Variables globales, ou en appuyant sur la touche F3.

Ne confondez pas les Variables Globales du terminal avec les variables que l'on définit sur la portée globale d'un
programme ! Les variables globales d'un programme ne sont disponibles que pour ce programme, tandis que les
variables globales du terminal sont disponibles pour tous les programmes. Vous pouvez utiliser les variables
globales de MetaTrader pour enregistrer des informations sur l'état de votre programme en cas d'interruption de
l'exécution.

Figure 20.7- LeVariables globales fenêtre.

LeGlobalVariableSet() La fonction est utilisée pour enregistrer une variable globale sur le terminal. Il a deux
paramètres – lenom de la variable, et levaleur à lui attribuer. Assurez-vous d'utiliser des noms uniques pour vos
variables globales. Par exemple, vous pouvez utiliser le nom de votre système de trading, suivi du nom de la
variable et du symbole sur lequel elle est placée :

276
Acheteur : Evangelos Papageorgiou (vpapnm@yahoo.gr)
Numéro de transaction : 28J511988U193831A

Conseils & Astuces


chaîne varName =
"ForexRobot_TradeSize_"+_Symbol ; // Exemple :
ForexRobot_TradeSize_EURUSD

GlobalVariableSet(varName,1.5);

Dans cet exemple, le nom de notre variable globale estForexRobot_TradeSize_EURUSD. Le symbole actuel
estEURUSD, nous pourrons ainsi identifier notre variable globale en fonction du symbole que nous négocions
actuellement. Nous enregistrerions cette variable globale sur le terminal chaque fois que sa valeur est définie ou
modifiée.

Si notre terminal s'arrête de manière inattendue (panne d'ordinateur ou panne de courant), nous pouvons lire le
contenu de la variable globale en utilisantGlobalVariableGet(), et continuez là où nous nous sommes arrêtés.
Nous ferions habituellement cela dans notresurInit() gestionnaire d'événements:

// SurInit()
chaîne varName = "ForexRobot_TradeSize_"+_Symbol ;
double tradeSize = GlobalVariableGet(varName);

Pour empêcher notre programme d'utiliser des variables globales obsolètes, nous devrons les supprimer si
nécessaire. Si nous supprimons manuellement notre conseiller expert du graphique, nous devons alors
supprimer la ou les variables globales actuellement enregistrées. Nous faisons cela dans leSurDéinit()
gestionnaire d'événements utilisant leGlobalVariableDel() fonction:

void OnDeinit (const int raison)


{ string varName = "ForexRobot_TradeSize_"+_Symbol ;
GlobalVariableDel(varName);
}

LeSurDéinit() Le gestionnaire d'événements est appelé pour de nombreuses raisons. Évidemment, il est
appelé si le programme est supprimé du graphique, si le graphique est fermé ou si le terminal est arrêté. Mais il
est également appelé si les paramètres d'entrée sont modifiés, si la période du graphique est modifiée ou si un
modèle est appliqué. Nous ne souhaitons supprimer aucune variable globale lorsque cela se produit. Il est donc
nécessaire de vérifier la raison de la désinitialisation avant de supprimer des variables globales.

Leraison paramètre duSurDéinit() La fonction contient la raison de la désinitialisation. Vous pouvez visualiser
le
Codes de désinitialisation dans leRéférence MQL5 sousConstantes standard... > Constantes nommées >
Désinitialisation
Codes de raison. Les codes qui nous concernent sontREASON_CHARTCHANGE,REASON_PARAMETERS,
etREASON_TEMPLATE. Si laraison Si le paramètre contient l’un de ces codes, nous ne supprimerons pas la
variable globale :

void OnDeinit (const int raison)


{ if(raison != REASON_CHARTCHANGE && raison != REASON_PARAMETERS
&& raison != REASON_TEMPLATE)

277
Acheteur : Evangelos Papageorgiou (vpapnm@yahoo.gr)
Numéro de transaction : 28J511988U193831A

ETXPERT UNCONSEIL P.ROGRAMMATION POUR METTRADER 5


{ string varName = "ForexRobot_TradeSize_"+_Symbol ;
GlobalVariableDel(varName);
}
}

Arrêt de l'exécution
Si vous souhaitez arrêter l'exécution d'un expert Advisor par programmation, utilisez
l'optionExpertSupprimer() fonction. Une fois l'exécution de l'événement en cours terminée, le conseiller
expert arrêtera son fonctionnement et se supprimera du graphique.

Si vous souhaitez fermer le terminal, leTerminalFerme() La fonction fermera MetaTrader. LeTerminalFerme()


la fonction prend un paramètre, un code de désinitialisation qui sera passé auSurDéinit() fonction. En
appelant leTerminalFerme() fonction, elle doit être suivie de l'opérateur de retour :

TerminalFerme(REASON_CLOSE);
retour;

278
Acheteur : Evangelos Papageorgiou (vpapnm@yahoo.gr)
Numéro de transaction : 28J511988U193831A

Indicateurs, scripts et bibliothèques

Chapitre 21 - Indicateurs, scripts et bibliothèques

Indicateurs
Au chapitre 17, nous avons examiné comment ajouter des indicateurs à nos programmes de conseillers experts.
MQL5 vous permet également de créer vos propres indicateurs personnalisés. Dans cette section, nous allons
créer un indicateur personnalisé qui tracera un canal de prix en utilisant le plus haut et le plus bas du dernierX
barres. Ceci est également appelé uncanal Donchian.

Styles de dessin
MQL5 propose plus d'une douzaine de styles de dessin différents pour les lignes indicatrices. Le style de dessin
détermine la façon dont la ligne apparaîtra dans la fenêtre graphique. Voici quelques-uns des styles de dessin
les plus courants :

• DESSINER UNE LIGNE – Il s’agit du style de dessin le plus courant et consiste en une seule ligne de la
couleur spécifiée. La moyenne mobile, le RSI et de nombreux autres indicateurs utilisent ce style de
dessin.

• DESSIN_HISTOGRAMME – Le style de dessin d'histogramme est généralement utilisé par des oscillateurs
tels que le MACD, l'OsMA et les oscillateurs de Bill Williams. Il est constitué de lignes verticales oscillant
autour d’un axe zéro.

• DESSIN_SECTION – Le style de dessin de section relie les prix sur des barres non consécutives avec une
ligne continue. L'indicateur personnalisé ZigZag fourni avec MetaTrader est l'exemple typique de ce
style de dessin.

• DRAW_ARROW – Le style de dessin de flèche dessinera des objets fléchés sur le graphique. L'indicateur
Fractals utilise des objets fléchés pour indiquer les hauts et les bas du swing.

• DRAW_NONE – Utilisé pour les tampons d’indicateurs qui ne seront pas dessinés sur le graphique.

Chaque style de dessin possède également une variante de couleur. LeDESSIN_COLOR_ Les styles de dessin
permettent au programmeur de faire varier la couleur de la ligne indicatrice barre par barre. Vous pouvez
visualiser tous les styles de dessin avec des exemples dans leRéférence MQL5 sousIndicateurs personnalisés >
Styles d'indicateurs dans les exemples.

Le gestionnaire d'événements OnCalculate()


Tous les indicateurs nécessitent leSurCalculer() gestionnaire d'événements. C'est l'équivalent duAu
démarrage() gestionnaire d'événements pour les conseillers experts et s'exécute à chaque tick entrant. Il existe
deux variantes duSurCalculer() gestionnaire d'événements. Le premier concerne les indicateurs qui utilisent
une seule série de prix. Par exemple, un indicateur de moyenne mobile permet à l'utilisateur de sélectionner une
série de prix sur laquelle calculer les données (clôture, haut, bas, etc.). La série de prix sélectionnée est
transmise auSurCalculer() gestionnaire d'événements. Voici la première variante deSurCalculer():

279
Acheteur : Evangelos Papageorgiou (vpapnm@yahoo.gr)
Numéro de transaction : 28J511988U193831A

ETXPERT UNCONSEIL P.ROGRAMMATION POUR METTRADER 5


int OnCalculate (const int rate_total, // taille du tableau price[] const int
prev_calculated, // barres traitées lors d'un appel précédent const int start, //
d'où commencent les données significatives
const double& price[] // tableau à calculer );

Letarifs_total Le paramètre contient le nombre de barres dans l’historique pour le symbole graphique
actuel. Leprev_calculated Le paramètre est le nombre de barres de l'historique qui ont déjà été calculées
précédemment par l'indicateur. La première fois queSurCalculer() court,prev_calculated sera nul. Lors des
courses suivantes,prev_calculated sera égal àtarifs_total. Lorsqu'une nouvelle barre s'ouvre et que la
valeur detarifs_total augmente de un, la différence entretarifs_total etprev_calculated sera 1,
indiquant que nous devons calculer la barre la plus récente.

En comparant la valeur detarifs_total àprev_calculated, on évite de perdre du temps processeur à


recalculer des barres déjà calculées. Lecommencer Le paramètre n’est généralement pas utilisé. Leprix[] Le
tableau contient les données de prix que l'indicateur utilisera dans ses calculs. La série de prix utilisée par
l'indicateur est sélectionnée par l'utilisateur dans leParamètres onglet de l'indicateurPropriétés fenêtre.
LePostuler à La liste déroulante permet à l'utilisateur de sélectionner la série de prix à utiliser :

Figure 21.1 - LePostuler à boîte déroulante sous leParamètres onglet de l'indicateurPropriétés dialogue.

La deuxième variante duSurCalculer() Le gestionnaire d'événements ajoute des tableaux de prix et de séries
chronologiques supplémentaires qui peuvent être utilisés dans les calculs d'indicateurs. Si votre indicateur
nécessite plusieurs séries de prix (comme le haut ET le bas de chaque barre), utilisez la deuxième variante
deSurCalculer():

int OnCalculate (const int rate_total, // taille de la série temporelle


d'entrée const int prev_calculated, // barres gérées lors de l'appel précédent
const datetime& time[], // Heure const
double& open[], // Ouvrir const double& high[], // High
const double& low[], // Low const double& close[], //
Fermer const long& tick_volume[] , // Cochez Volume const
long& volume[], // Volume réel const int& spread[] //
Spread );

Notre indicateur de canal utilisera cette variante duSurCalculer() gestionnaire d'événements, car nous devons
utiliser à la fois les séries de prix élevés et bas.

280
Acheteur : Evangelos Papageorgiou (vpapnm@yahoo.gr)
Numéro de transaction : 28J511988U193831A

Indicateurs, scripts et bibliothèques

Assistant MQL5
LeAssistant MQL5 peut être utilisé pour créer un modèle de départ pour votre indicateur personnalisé. Il est
beaucoup plus pratique d'utiliser l'assistant pour ajouter nos gestionnaires d'événements, nos tampons et nos
lignes que de les ajouter manuellement. Ouvrez leAssistant MQL5 en cliquant sur leNouveau sur la barre d'outils
MetaEditor. SélectionnerIndicateur personnalisé et cliquezSuivant continuer.

Figure 21.2 – La boîte de dialogue des propriétés de l'indicateur personnalisé duAssistant MQL5.

Les indicateurs personnalisés sont enregistrés dans le\MQL5\Indicateurs dossier par défaut. Vous pouvez
ajouter des variables d'entrée sur cet écran si vous le souhaitez. Dans la figure 21.2, nous avons ajouté unint
variable d'entrée nomméeBarres hautes et basses, avec une valeur par défaut de 8.

L'écran suivant vous permet de sélectionner les gestionnaires d'événements à ajouter à votre indicateur. La
première variante duSurCalculer() Le gestionnaire d'événements est sélectionné par défaut. Vous pouvez
ajouter des gestionnaires d'événements supplémentaires si nécessaire. Pour notre indicateur, nous devrons
sélectionner la deuxième variante duSurCalculer() gestionnaire d'événements. La figure 21.3 ci-dessous
montre la boîte de dialogue des gestionnaires d'événements :

281
Acheteur : Evangelos Papageorgiou (vpapnm@yahoo.gr)
Numéro de transaction : 28J511988U193831A

ETXPERT UNCONSEIL P.ROGRAMMATION POUR METTRADER 5

Figure 21.3 – La boîte de dialogue des gestionnaires d'événements duAssistant MQL5 pour les indicateurs personnalisés.

L'écran final est l'endroit où vous définissez les propriétés de dessin de l'indicateur. Si cet indicateur sera affiché
dans une fenêtre séparée (comme un oscillateur), vérifiezIndicateur dans une fenêtre séparée pour ajouter le
pertinent#propriété directive au fichier indicateur. LeParcelles La fenêtre vous permet d'ajouter et de définir
les propriétés des lignes indicatrices :

Figure 21.4 – La boîte de dialogue des propriétés du dessin duAssistant MQL5 pour les indicateurs personnalisés.
LeÉtiquette La colonne est le nom de la ligne indicatrice telle qu'elle apparaîtra dans leFenêtre de données. Il est
également utilisé pour créer le nom du tampon du tableau. Nous avons ajouté deux lignes indicatrices

282
Acheteur : Evangelos Papageorgiou (vpapnm@yahoo.gr)
Numéro de transaction : 28J511988U193831A

Indicateurs, scripts et bibliothèques

nomméesSupérieur etInférieur. Double-cliquez sur leTaper pour afficher une liste déroulante permettant de
sélectionner le type de dessin de la ligne. Nous avons laissé nos lignes réglées sur leDoublertype de dessin.
Finalement, leCouleur La colonne est l'endroit où vous définissez la couleur de la ligne indicatrice. Nous avons
défini les deux lignes sur Rouge.

Cliquez surFinition pour fermer l'assistant et ouvrir le modèle d'indicateur dans MetaEditor. Voici à quoi
ressemble notre modèle d'indicateur. Nous avons laissé de côté leSurCalculer() gestionnaire d'événements
pour l'instant, et formaté le fichier pour plus de clarté :

#propriété indicateur_chart_window
#indicateur de propriété_buffers 2
#property indicateur_plots 2

//--- tracé supérieur


#property Indicator_label1 "Supérieur"
#propriété indicateur_type1 DRAW_LINE
#property Indicator_color1 clrRed
#propriété indicateur_style1 STYLE_SOLID
#propriété indicateur_width1 1

//--- tracé inférieur


#property Indicator_label2 "Inférieur"
#propriété indicateur_type2 DRAW_LINE
#property Indicator_color2 clrRed
#propriété indicateur_style2 STYLE_SOLID
#propriété indicateur_width2 1

//--- paramètres d'entrée


input int HighLowBars=8;

//--- les tampons


d'indicateur doublent
UpperBuffer[]; double
LowerBuffer[];

//+------------------------------------------------------------- -------------------+
//| Fonction d'initialisation d'indicateur personnalisé |
//+-------------------------------------------------------------
-------------------+ int OnInit()
{
//--- mappage des tampons d'indicateur
SetIndexBuffer(0,UpperBuffer,INDICATOR_DATA);
SetIndexBuffer(1,LowerBuffer,INDICATOR_DATA);

retour(0);
}
Le pertinent#propriété des directives ont été insérées pour nos lignes indicatrices. Vous pouvez visualiser tous
les indicateurs
#propriété directives dans leRéférence MQL5 sousNotions de base du langage > Préprocesseur > Propriétés
du programme. Notre

283
Acheteur : Evangelos Papageorgiou (vpapnm@yahoo.gr)
Numéro de transaction : 28J511988U193831A

ETXPERT UNCONSEIL P.ROGRAMMATION POUR METTRADER 5


Barres hautes et basses la variable d'entrée a été insérée, ainsi que deux tableaux pour nos
tampons,Tampon supérieur[] etTampon inférieur[]. LeSetIndexBuffer() fonctions dans lesurInit() Le
gestionnaire d'événements attribue les tableaux aux tampons d'indicateurs appropriés.

Calcul de l'indicateur
Nos calculs d'indicateurs sont effectués dans leSurCalculer() gestionnaire d'événements. LeSurCalculer()
Le gestionnaire d'événements pour notre indicateur est présenté ci-dessous :

//+------------------------------------------------------------- -------------------+
//| Fonction d'itération d'indicateur personnalisé |
//+-------------------------------------------------------------
-------------------+ int OnCalculate(const int rate_total, const int
prev_calculated, const datetime &Time[], const double &Open[], const
double &High[], const double &Low[], const double &Close[], const long
&TickVolume[], const long &Volume[], const int &Spread[])
{
//---

ArraySetAsSeries(UpperBuffer,true);
ArraySetAsSeries(LowerBuffer,true);
ArraySetAsSeries(Élevé, vrai);
ArraySetAsSeries (faible, vrai);

barres int = tarifs_total - 1 ;


if(prev_calculated > 0) bars = rate_total - (prev_calculated - 1) ;

pour(int i = barres; i >= 0; i--)


{
UpperBuffer[i] = High[ArrayMaximum(High,i,HighLowBars)];
LowerBuffer[i] =
Faible[ArrayMinimum(Low,i,HighLowBars)]; }

//--- valeur de retour de prev_calculated pour le prochain appel


return(rates_total);
}
Aucun des tableaux de ce programme n'est défini comme tableau en série par défaut. Nous devrons les définir
sous forme de tableaux en série en utilisant leArraySetAsSeries() fonction. Ceci est généralement facultatif,
mais notre indicateur nous oblige à accéder aux données de prix sous forme de série. Nos deux indicateurs
tampons, ainsi que leHaut[] etFaible[] tableaux passés par leSurCalculer() le gestionnaire d'événements
sera défini comme une série :

ArraySetAsSeries(UpperBuffer,true);
ArraySetAsSeries(LowerBuffer,true);
ArraySetAsSeries(Élevé, vrai);
ArraySetAsSeries (faible, vrai);

Nous utilisons unpour boucle pour calculer les valeurs de l’indicateur pour chacune des barres du graphique
actuel. Nous déterminons le nombre de barres à traiter en utilisant leprev_calculated ettarifs_total
paramètres du

284
Acheteur : Evangelos Papageorgiou (vpapnm@yahoo.gr)
Numéro de transaction : 28J511988U193831A

Indicateurs, scripts et bibliothèques

SurCalculer() gestionnaire d'événements. Comme mentionné précédemment, letarifs_total La variable


contient le nombre total de barres sur le graphique, tandis queprev_calculated contient le nombre de barres
calculé par l'exécution précédente duSurCalculer() gestionnaire d'événements.

Pour les tableaux en série, l'index maximum du tableau esttarifs_total – 1. Il s'agit de la barre la plus
ancienne du graphique. La barre la plus récente a un indice de zéro. QuandSurCalculer() est la première
exécution, la valeur deprev_calculated sera nul. Siprev_calculated est zéro, nous définissons l'index
maximum du tableau surtarifs_total – 1. Lors des exécutions suivantes, nous calculons l'indice maximum
du tableau en soustrayantprev_calculated depuistarifs_total. Cela garantit que seules les barres les plus
récentes seront calculées :

barres int = tarifs_total - 1 ; if(prev_calculated > 0)


barres = taux_total - prev_calculated ;

La variablebarres contiendra l'index du tableau de départ. Dans notrepour boucle ci-dessous, nous attribuons
la valeur debarres à notre variable incrémenteurje. Nous décrémenterons la valeur deje jusqu'àje = 0:

pour(int i = barres; i >= 0; i--)


{
// Calculs des indicateurs
}

Tant que vos tableaux de prix et de tampon sont définis en série, lepour la boucle ci-dessus fonctionnera pour
calculer la plupart des indicateurs. Le code pour calculer l'indicateur et remplir les tampons va à l'intérieur de la
boucle.

Pour calculer les tampons de tableau pour notre indicateur de canal, nous utilisons simplement
leTableauMaximum() et
TableauMinimum() fonctions pour trouver les valeurs les plus élevées et les plus basses pour le nombre de
barres indiqué parBarres hautes et basses, par rapport à l'index du tableau indiqué par leje variable
d'incrémentation. L'index du tableau résultant est utilisé dans leHaut[] etFaible[] tableaux pour renvoyer le
plus haut et le plus bas. Le résultat est enregistré dans leTampon supérieur[] etTampon inférieur[]
tableaux respectivement :

pour(int i = barres; i >= 0; i--)


{
UpperBuffer[i] = High[ArrayMaximum(High,i,HighLowBars)];
LowerBuffer[i] = Faible[ArrayMinimum(Low,i,HighLowBars)];
}

La dernière étape consiste à renvoyer la valeur detarifs_total et quittez leSurCalculer() gestionnaire


d'événements. Celui-ci est inséré automatiquement par l'assistant MQL5 :

//--- valeur de retour de prev_calculated pour le prochain appel


return(taux_total); }

285
Acheteur : Evangelos Papageorgiou (vpapnm@yahoo.gr)
Numéro de transaction : 28J511988U193831A

ETXPERT UNCONSEIL P.ROGRAMMATION POUR METTRADER 5


Nous venons de créer un indicateur simple qui extrait les données de prix duSurCalculer() gestionnaire
d'événements et calcule deux lignes indicatrices. Cet indicateur peut être utilisé pour créer des signaux de
trading ou comme stop loss pour les positions de tendance. Nous n'avons abordé que ce que vous pouvez faire
avec des indicateurs personnalisés dans MQL5. Pour en savoir plus sur la fonction d'indicateur personnalisé,
consultez leRéférence MQL5 sousIndicateurs personnalisés.

Vous pouvez afficher le code source de ce fichier dans\MQL5\Indicateurs\High Low Channel.mq5.

Figure 21.5 – L’indicateur personnalisé High Low Channel.

Scripts
UNscénario est un simple programme MQL5 qui s'exécute une fois lorsqu'il est attaché à un graphique. Il se
compose d'un seul gestionnaire d'événements, leAu démarrage() gestionnaire d'événements. Lorsqu'un script
est attaché à un graphique, leAu démarrage() le gestionnaire d'événements s'exécute. Contrairement à un
expert-conseil ou un indicateur, un script ne répète pas son exécution après leAu démarrage() le gestionnaire
d'événements est terminé. Le script est automatiquement détaché d'un graphique après exécution.

Pour créer un script, utilisez leAssistant MQL5. Tous les scripts sont enregistrés dans le\MQL5\Scripts annuaire.
Votre nouveau fichier de script contiendra un videAu démarrage() gestionnaire d'événements. Il y en a
quelques#propriété directives qui contrôlent le comportement de votre script. Si votre script comporte des
variables d'entrée, vous souhaiterez utiliser lescript_show_inputs propriété pour permettre à l’utilisateur
d’ajuster les entrées et de vérifier l’exécution. Si votre script n'a pas de variables d'entrée,
lescript_show_confirm La propriété affiche une boîte de confirmation demandant à l'utilisateur s'il doit
exécuter le script. Vous voudrez en ajouter un#propriété directives à votre script.

Voici un fichier de script vide contenant plusieurs#propriété les directives et lesAu démarrage() gestionnaire
d'événements:

#propriété copyright "Andrew Young"

286
Acheteur : Evangelos Papageorgiou (vpapnm@yahoo.gr)
Numéro de transaction : 28J511988U193831A

Indicateurs, scripts et bibliothèques


#lien de propriété "http://www.expertadvisorbook.com"
#propriété script_show_confirm

annuler
OnStart()

Nous allons créer un script utile qui peut être utilisé pour clôturer tous les ordres et positions ouverts sur un
graphique. Lorsqu'il est attaché à un graphique, le script demandera d'abord à l'utilisateur s'il doit l'exécuter. Si
tel est le cas, le script fermera d'abord toute position ouverte sur le symbole graphique actuel. Ensuite, il
clôturera toutes les commandes ouvertes en attente.

#propriété copyright "Andrew Young"


#lien de propriété "http://www.expertadvisorbook.com"
#property description "Fermez la position actuelle et tous les ordres pour le symbole
graphique actuel."

#propriété script_show_confirm

#include <Mql5Book\Trade.mqh>
CCommerce Commerce ;

#include <Mql5Book\Pending.mqh>
CPEn attente En attente ;

annuler OnStart()
{
// Ferme la position actuelle
si(PositionType(_Symbol) != WRONG_VALUE)
{bool fermé = Trade.Close(_Symbol); si
(fermé == vrai)
{
Comment("Position clôturée sur "+_Symbol);
}
}

// Ferme toutes les commandes ouvertes en attente


if(Pending.TotalPending(_Symbol) > 0)
{
// Récupère les tickets de
commande en attente ulong
tickets[];
En
attente.GetTickets(_Symbol,tickets);
int numTickets = ArraySize(tickets);

// Clôture des commandes

287
Acheteur : Evangelos Papageorgiou (vpapnm@yahoo.gr)
Numéro de transaction : 28J511988U193831A

ETXPERT UNCONSEIL P.ROGRAMMATION POUR METTRADER 5


pour(int i = 0; i < numTickets; i++)
{
Trade.Delete(tickets[i]);
}

if(Pending.TotalPending(_Symbol) == 0)
{
Comment("Toutes les commandes en attente clôturées le "+_Symbol);
}
}
}

Le#propriété les directives incluentdroits d'auteur,lien etdescription, aussi bien


quescript_show_confirm propriété. Cela invite l'utilisateur avec une boîte de dialogue de confirmation avant
d'exécuter le script. Nous avons également inclus notrecommerce.mqh etEn attente.mqh fichiers du\MQL5\
Include\Mql5Book dossier et objets créés en fonction duCCommerce etCPEn attente Des classes.

Lorsque le script est exécuté, leAu démarrage() le gestionnaire d'événements s'exécute. Nous vérifions
d'abord la sortie du
Type de position() fonction. S'il indique une position ouverte, nous appelons leCommerce.Fermer()
fonction pour fermer la position actuelle. Un commentaire graphique est affiché si la position est fermée
correctement.

Ensuite, nous vérifions leEn attente.TotalPending() fonction pour voir s’il y a des commandes en attente
ouvertes. Si c'est le cas, nous utilisons
leEn attente.GetTickets() fonction pour récupérer les tickets de commande en cours, et les clôturer à
l'aideCommerce.Supprimer(). Si aucune commande en attente n'est ouverte, un commentaire sera imprimé sur
le graphique.

Ce script fermera tous les ordres et positions sur le symbole graphique actuel. Nous pouvons modifier le
programme pour spécifier le symbole pour lequel fermer les positions et les ordres. Nous allons ajouter une
variable d'entrée nomméeFermerSymbole. Si une valeur est spécifiée pourFermerSymbole, le script fermera
toutes les commandes sur le symbole spécifié. Sinon, le script utilisera le symbole du graphique actuel :

#propriété script_show_inputs

#include <Mql5Book\Trade.mqh>
CCommerce Commerce ;

#include <Mql5Book\Pending.mqh>
CPEn attente En

attente ;chaîne d'entrée

CloseSymbol = "";

annuler OnStart()
{

288
Acheteur : Evangelos Papageorgiou (vpapnm@yahoo.gr)
Numéro de transaction : 28J511988U193831A

Indicateurs, scripts et bibliothèques


// Vérifiez la chaîne du nom du symbole
useSymbol = CloseSymbol; if(CloseSymbol ==
"") useSymbol = _Symbol;

// Ferme la position actuelle


if(PositionType(useSymbol) != WRONG_VALUE)
{ bool fermé = Trade.Close (useSymbol);
si (fermé == vrai)
{
Comment("Position fermée le "+useSymbol);
}
}

// Ferme toutes les commandes ouvertes en attente


if(Pending.TotalPending(useSymbol) > 0)
{
// Récupère les tickets de
commande en attente ulong
tickets[];
En attente.GetTickets(useSymbol,tickets);
int numTickets = ArraySize(tickets);

// Clôture des commandes


pour(int i = 0; i < numTickets; i++)
{
Trade.Delete(tickets[i]);
}

if(Pending.TotalPending(useSymbol) == 0)
{
Comment("Toutes les commandes en attente fermées le "+useSymbol);
}
}
}

Nous utilisons lescript_show_inputs directive de propriété pour afficher la fenêtre de saisie à l'utilisateur
avant l'exécution du script. L'utilisateur saisira un nom de symbole pourFermerSymbole et exécutez le script. Si
aucun nom de symbole n'a été spécifié, le programme attribuera le symbole de carte actuel
auutiliserSymbole variable. LeutiliserSymbole La variable est utilisée tout au long du script pour indiquer
le symbole à utiliser.

Vous pouvez consulter le code source duFermer toutes les commandes.mq5 script dans le\MQL5\Scripts
dossier.

Bibliothèques
UNbibliothèque est un fichier exécutable qui contient des fonctions réutilisables pouvant être utilisées par
d'autres programmes. Il est similaire à un fichier d'inclusion, mais avec plusieurs différences importantes.
Contrairement à un fichier d'inclusion, une bibliothèque ne possède pas de classes ou de variables pouvant être

289
Acheteur : Evangelos Papageorgiou (vpapnm@yahoo.gr)
Numéro de transaction : 28J511988U193831A

ETXPERT UNCONSEIL P.ROGRAMMATION POUR METTRADER 5


utilisées par d'autres programmes. Vous pouvez définir des classes, des structures, des énumérations, etc. dans
votre bibliothèque, mais elles ne seront pas utilisables en dehors de la bibliothèque.

Vous pouvez utiliser des DLL Windows natives ou d'autres DLL créées en C++ dans vos programmes MQL5. Le
processus d'importation de fonctions DLL est similaire à l'importation de fonctions à partir d'une bibliothèque
MQL5. Les fonctions contenues dans les bibliothèques ont des limitations quant aux types de paramètres qui
peuvent leur être transmis. Les pointeurs et les objets contenant des tableaux dynamiques ne peuvent pas être
transmis à une fonction de bibliothèque. Si vous importez des fonctions à partir d'une DLL, vous ne pouvez pas
transmettre de chaînes ou de tableaux dynamiques à ces fonctions.

L’avantage d’une bibliothèque est que vous pouvez la distribuer sans rendre disponible le code source. Si vous
utilisez une bibliothèque dans de nombreux experts Advisors, vous pouvez apporter des modifications mineures
à la bibliothèque sans avoir à recompiler tous les programmes qui en dépendent (à condition de ne pas modifier
les paramètres de la fonction, bien sûr).

Vous pouvez créer un fichier de bibliothèque vierge en utilisant leAssistant MQL5. Les bibliothèques sont
enregistrées dans le\MQL5\Bibliothèques dossier. Une bibliothèque doit avoir lebibliothèque directive de
propriété en haut du fichier. S'il n'est pas présent, le fichier ne sera pas compilé. Créons un exemple de
bibliothèque avec deux fonctions exportables. Les fonctions que nous souhaitons exporter auront leexporter
modificateur après les paramètres de la fonction :

#bibliothèque de propriétés

#include <Mql5Book\Indicators.mqh>
CiRSI RSI ;

bool BuySignal (string pSymbol, ENUM_TIMEFRAMES pTimeframe, int pPeriod,


ENUM_APPLIED_PRICE pPrice) exportation
{
RSI.Init(pSymbol,pTimeframe,pPeriod,pPrice);

if(RSI.Main() < 30) return(true);


sinon return (faux);
}
bool SellSignal (string pSymbol, ENUM_TIMEFRAMES pTimeframe, int pPeriod,
ENUM_APPLIED_PRICE pPrice) exportation
{
RSI.Init(pSymbol,pTimeframe,pPeriod,pPrice);

if(RSI.Main() > 70) return(true);


sinon return (faux);
}

Ce fichier est nomméBibliothèque de signaux.mq5, et est situé dans le\MQL5\Bibliothèques dossier.


Le#bibliothèque de propriétés La directive indique qu'il s'agit d'un fichier de bibliothèque. Cette
bibliothèque contient deux fonctions qui seront utilisées pour renvoyer des signaux commerciaux au
programme appelant. Ces fonctions utilisent de simples signaux commerciaux RSI de surachat et de survente,

290
Acheteur : Evangelos Papageorgiou (vpapnm@yahoo.gr)
Numéro de transaction : 28J511988U193831A

Indicateurs, scripts et bibliothèques

mais vous pouvez créer une bibliothèque avec des signaux commerciaux plus élaborés que vous pouvez cacher
aux conseillers experts et aux programmeurs qui les utilisent.

Nous incluons le\MQL5\Include\Mql5Book\Indicators.mqh déposer et déclarer un objet basé sur leCiRSI


classe. Notez que cet objet n'est pas visible en dehors de la bibliothèque. LeAcheterSignal()
etVendreSignal() les fonctions prennent des paramètres qui définissent les paramètres de l’indicateur RSI.
Noter laexporter modificateur après la parenthèse fermante. Toute fonction qui sera importée dans un autre
programme doit avoir leexporter modificateur!

Ce fichier sera compilé comme n'importe quel autre programme MQL5. Pour utiliser les fonctions de notre
bibliothèque dans un autre programme, nous devons les importer dans ce programme. Nous faisons cela en
utilisant le#importer directif. Voici comment nous importerions ces fonctions dans un programme Expert
Advisor :

#importer "SignalLibrary.ex5"
bool BuySignal (string pSymbol, ENUM_TIMEFRAMES pTimeframe, int pPeriod,
ENUM_APPLIED_PRICE pPrix); bool SellSignal (string pSymbol,
ENUM_TIMEFRAMES pTimeframe, int pPeriod,
ENUM_APPLIED_PRICE pPrix);
#importer

Le nom de la bibliothèque est contenu entre guillemets après l'ouverture#importer directif. Noter la.ex5 dans
le nom de la bibliothèque indiquant qu'il s'agit d'un fichier exécutable compilé. Vous ne pouvez pas importer un
fichier de code source. Toutes les bibliothèques doivent être situées dans le\MQL5\Bibliothèques dossier.
Suite à l'ouverture#importer La directive sont les fonctions que nous importons de notre bibliothèque. Une
clôture#importer La directive doit être présente après la dernière fonction importée.

Nos fonctions importées sont utilisées comme toutes les autres fonctions. Voici un exemple de la façon dont
nous pourrions utiliser leAcheterSignal() fonction dans un conseiller expert:

if(PositionType(_Symbol) == WRONG_VALUE && BuySignal(_Symbol,_Period,14,PRICE_CLOSE))


{
// Position d'achat ouverte
}

L'exemple ci-dessus appelle leAcheterSignal() et calcule la valeur RSI pour un RSI sur 14 périodes en utilisant
le prix de clôture du symbole et de la période actuels du graphique. Si le RSI est actuellement survendu, la
fonction renvoievrai.

Si une fonction importée d'une bibliothèque porte le même nom qu'une fonction de votre programme ou une
fonction MQL5 prédéfinie, l'opérateur de résolution de portée (::) doit être utilisé pour identifier la fonction
correcte. Par exemple, supposons que notre programme possède déjà une fonction nomméeAcheterSignal().
Si nous importons leAcheterSignal() fonction de notre fichier de bibliothèque, nous devrons la faire précéder
du nom de la bibliothèque lorsque nous l'appellerons :

SignalLibrary :: BuySignal (_Symbol,_Period,10,PRICE_CLOSE)

291
Acheteur : Evangelos Papageorgiou (vpapnm@yahoo.gr)
Numéro de transaction : 28J511988U193831A

ETXPERT UNCONSEIL P.ROGRAMMATION POUR METTRADER 5

Chapitre 22 - Débogage et tests

Chaque programmeur fait des erreurs et presque tous les programmes contiennent des erreurs. Qu'il s'agisse
d'erreurs de compilation causées par une faute de frappe dans une fonction ou d'une erreur dans la logique de
votre programme, vous devrez apprendre à tester et déboguer vos programmes. Ce chapitre abordera les
erreurs et les procédures de débogage et de test de vos programmes. Vous apprendrez également à utiliser
Strategy Tester pour évaluer les performances de votre programme.

les erreurs
Il existe trois types d'erreurs liées aux programmes MQL5.Erreurs de compilation se produire dans MetaEditor
lorsqu'un code source invalide est compilé.Erreurs d'exécution sont des erreurs logiques ou logicielles qui se
produisent lorsqu'un programme est exécuté dans MetaTrader. Des erreurs de serveur d'échange se produisent
lorsqu'une demande d'échange échoue.

Erreurs de compilation
Il est courant de mal saisir un appel de fonction, d'omettre un point-virgule ou un crochet fermant ou de
commettre une erreur de syntaxe lors du codage. Lorsque vous compilez votre programme, une liste d'erreurs
apparaîtra dans leles erreurs onglet dans MetaEditor. La première fois que cela se produit, cela peut paraître
intimidant. Mais ne vous inquiétez pas, nous allons résoudre certaines des erreurs de syntaxe les plus courantes.

La première chose à retenir face à une liste d’erreurs de compilation est de toujours commencer par la première
erreur de la liste. Le plus souvent, il s’agit d’une seule erreur de syntaxe qui entraîne toute une liste d’erreurs.
Corrigez la première erreur et les erreurs restantes disparaîtront.

Figure 22.1 - Leles erreurs onglet sous leBoîte à outils fenêtre dans MetaEditor. Notez
que toutes ces erreurs sont dues à une seule parenthèse droite manquante.

Double-cliquez sur l'erreur dans leles erreurs L'onglet vous amènera à l'endroit de votre programme où l'erreur
a été déclenchée. Il est plus que probable que l'erreur se trouve juste sous votre curseur ou sur la ligne
précédente. Un point-virgule, des parenthèses ou des crochets manquants peuvent entraîner des erreurs de
compilation trompeuses, alors assurez-vous de vérifier les lignes précédentes lorsque vous êtes confronté à une
erreur sur une ligne qui semble autrement correcte.

292
Acheteur : Evangelos Papageorgiou (vpapnm@yahoo.gr)
Numéro de transaction : 28J511988U193831A

Débogage et tests

Voici une liste des erreurs de syntaxe les plus courantes dans MetaEditor, ainsi que les raisons les plus courantes
de ces erreurs :

• Point-virgule attendu – Il manque un point-virgule ou un crochet gauche dans la ligne précédente.

• Jeton inattendu – Une parenthèse droite est manquante dans la ligne précédente, ou une parenthèse
gauche supplémentaire est présente dans la ligne actuelle.

• Parenthèse droite déséquilibrée – Une parenthèse droite supplémentaire est présente ou une
parenthèse gauche est manquante dans la ligne actuelle.

• Les expressions ne sont pas autorisées sur une portée globale – Un crochet gauche dans un opérateur
composé peut manquer.

• Un opérateur attendu – Il manque un opérateur à l'emplacement spécifié.

• Les mauvais paramètres comptent – Trop ou pas assez de paramètres dans un appel de fonction.

• Identificateur non déclaré – Un nom de variable ou d'objet est utilisé sans être déclaré au préalable.
Vérifiez l'orthographe et la casse du nom de la variable, et déclarez-la si nécessaire.

• Fin inattendue du programme – Il manque une parenthèse fermante dans votre programme. C'est une
question délicate, car l'erreur fait référence à la fin du programme. Examinez le code que vous avez
modifié récemment et recherchez une parenthèse fermante manquante.

Erreurs d'exécution
Une erreur d'exécution est une erreur qui se produit lors de l'exécution d'un programme. Les erreurs d'exécution
sont des erreurs logiques : en d'autres termes, le programme se compilera mais ne fonctionnera pas comme
prévu. Un message d'erreur sera imprimé dans le journal lorsqu'une erreur d'exécution se produit. Le message
d'erreur indiquera la cause de l'erreur, ainsi que la ligne sur laquelle elle s'est produite dans votre code source.

Vous pouvez utiliser leObtenirLastError() fonction pour récupérer le code d'erreur de la dernière erreur
d'exécution, au cas où vous souhaiteriez ajouter une gestion des erreurs pour les erreurs d'exécution à votre
programme. Après avoir accédé au dernier code d'erreur, utilisez leRéinitialiser DernièreErreur ()
fonction pour remettre le code d’erreur à zéro.

Les programmes continueront à s'exécuter après qu'une erreur d'exécution se produise, mais il existe quelques
erreurs critiques qui mettront immédiatement fin à l'exécution d'un programme. UNdiviser par zéro L'erreur se
produit lorsqu'une opération de division utilise un diviseur de zéro. UNtableau hors de portée une erreur se
produit lorsque le programme tente d'accéder à un élément du tableau qui n'existe pas. Cela se produit
généralement en essayant d'accéder à un élément du tableau plus grand que la taille du tableau. Tenter
d'accéder à un pointeur non valide provoquera également une erreur critique.

Une liste complète des erreurs d'exécution peut être trouvée dans leRéférence MQL5 sousConstantes
standard... > Codes d'erreurs et d'avertissements > Erreurs d'exécution.

293
Acheteur : Evangelos Papageorgiou (vpapnm@yahoo.gr)
Numéro de transaction : 28J511988U193831A

ETXPERT UNCONSEIL P.ROGRAMMATION POUR METTRADER 5


Erreurs du serveur de trading
Les erreurs du serveur de trading sont renvoyées par leObjetEnvoyer() fonction lorsque vous tentez
d’effectuer une action commerciale.
Lerecoder variable d'unMqlTradeRésultat objet transmis auObjetEnvoyer() La fonction contient le code
retour du serveur. Nos fonctions de passation et de modification de commandes contiennent déjà du code pour
gérer les erreurs du serveur de trading. Si vous devez écrire une classe ou une fonction qui utilise
leCommandeEnvoyer() fonction, assurez-vous d'ajouter du code qui gérera les erreurs du serveur d'échange.

Débogage
MetaEditor 5 dispose d'un débogueur qui peut être utilisé pour exécuter vos programmes de manière
interactive. Il y a deux boutons sur la barre d'outils MetaEditor pour démarrer le débogage. Le premier bouton
exécutera le conseiller expert dans le testeur de stratégie MetaTrader, en utilisant des données historiques avec
visualisation. Le deuxième bouton lancera le conseiller expert dans une fenêtre graphique dans MetaTrader, en
utilisant des données en temps réel. Vous pouvez arrêter ou suspendre le processus de débogage en cliquant
sur le boutonArrêt ouPause boutons:

Figure 22.2 – Boutons de débogage. De gauche à droite se trouvent lesDébut (Historique),Démarrer (en direct),Pause etArrêt
boutons de débogage.

Le processus de débogage est lancé lorsqu'un point d'arrêt est atteint. UNpoint d'arrêt peut être défini dans
MetaEditor en appuyant sur la touche F9 pour basculer un point d'arrêt sur la ligne actuelle. Vous pouvez
également utiliser leDebugBreak() fonction pour définir un point d’arrêt. Lorsqu'un point d'arrêt est atteint
pendant l'exécution du programme, le programme s'arrête et le contrôle est confié au programmeur. En utilisant
leEntrer dans,Enjamber etSors boutons de la barre d'outils, le programmeur peut observer l'exécution du
programme ligne par ligne.

Figure 22.3 – Un point d'arrêt est défini dans MetaEditor sur la ligne actuelle.

LeEntrer dans Le bouton déplace l'exécution à la ligne suivante du programme.Enjamber ignorera toutes les
fonctions rencontrées lors de l'exécution, etSors quitte la fonction actuelle et rend le contrôle à la fonction qui l'a
appelée (ou quitte l'événement en cours).

Figure 22.4 – Boutons d'étape. De gauche à droite se trouvent lesEntrer dans,Enjamber etSors boutons.

Vous pouvez surveiller la valeur d'une variable pendant le débogage en utilisant l'outilMontre fenêtre à l'intérieur
duDéboguer onglet dans le MetaEditorBoîte à outils fenêtre. LeMontre La fenêtre affiche la valeur actuelle des
variables et expressions surveillées. Pour ajouter une variable ou une expression à la fenêtre de surveillance,

294
Acheteur : Evangelos Papageorgiou (vpapnm@yahoo.gr)
Numéro de transaction : 28J511988U193831A

Débogage et tests

placez le curseur de votre souris sur le nom de la variable ou cliquez et faites glisser pour sélectionner
l'expression que vous souhaitez ajouter à la fenêtre de surveillance. Faites un clic droit et sélectionnezAjouter
une montre dans le menu contextuel, ou appuyez surMaj+F9 sur votre clavier.

Figure 22.5 - LeMontre fenêtre.

Enregistrement
Parfois, des erreurs peuvent survenir lors du trading en direct ou de démonstration lorsqu'un débogueur n'est
pas disponible. Ou vous devrez peut-être tester plusieurs scénarios de trading à la fois dans Strategy Tester.
Vous pouvez utiliser leImprimer() fonction pour enregistrer des informations sur l'état de votre programme
dans les fichiers journaux de MetaTrader. LeAlerte() La fonction imprime automatiquement la chaîne d'alerte
dans le journal, de sorte que toutes les alertes seront également affichées dans le journal.

Nous avons déjà ajoutéImprimer() fonctionne dans toutes nos classes de trading et fonctions qui enregistrent
les opérations commerciales et les états d'erreur. Dans leCCommerce classe, nous avons ajouté une fonction
nomméeLogTradeRequest() qui imprime les valeurs de la requête et des résultats dans le journal pour le
dépannage :

2018.03.01 04:00:00 MqlTradeRequest - action : 10, commentaire :, écart : 0,


expiration : 1970.01.01 00:00:00, magie : 0, ordre : 0, position : 10, position_by : 11,
prix : 0,0, ls : 0,0, stoplimit : 0,0, symbole :, tp : 0,0, type : 0, type_filling :0,
type_time :0, volume :0,0

Vous devez toujours intégrer ce type de fonctionnalité de journalisation dans vos programmes, en particulier
lorsque vous effectuez des opérations commerciales ou testez un nouveau code.

Débogage avecImprimer() Les fonctions, bien que moins interactives que l'utilisation du débogueur,
permettent au programmeur d'examiner le résultat de plusieurs transactions à la fois. Vous pouvez consulter le
journal de Strategy Tester sous l'ongletJournal onglet dans leTesteur de stratégie fenêtre. Faites un clic droit
dans leJournal fenêtre et sélectionnezOuvrir dans le menu contextuel pour ouvrir le dossier des journaux et
afficher les fichiers dans un éditeur de texte. Ou vous pouvez utiliser leVisionneuse de journaux
(sélectionnerTéléspectateur dans le menu contextuel) pour filtrer et afficher les journaux par date.

Figure 22.6- LeVisionneuse de journaux fenêtre.

295
Acheteur : Evangelos Papageorgiou (vpapnm@yahoo.gr)
Numéro de transaction : 28J511988U193831A

ETXPERT UNCONSEIL P.ROGRAMMATION POUR METTRADER 5


Lorsque vous négociez avec un conseiller expert sur un graphique en direct, MetaTrader utilise deux dossiers de
journaux différents pour la sortie. Les journaux du terminal, situés dans le\Journaux dossier, affiche les
informations de base sur le commerce et le terminal. Vous pouvez consulter ce journal sous l'ongletJournal
onglet dans leBoîte à outils fenêtre. Le journal des experts, situé dans\Experts\Journaux, contient la sortie
deImprimer() etAlerte() fonctions, ainsi que des informations commerciales détaillées. Vous pouvez
consulter le journal des experts sousExperts onglet dans leBoîte à outils fenêtre. Vous pouvez ouvrir les dossiers
de journaux en cliquant avec le bouton droit à l'intérieur duJournal ouExperts onglet et sélectionOuvrir dans le
menu contextuel, ou affichez-les dans le Journal Viewer en sélectionnantTéléspectateur dans le menu
contextuel.

Figure 22.7 - LeExperts dans la fenêtre de la boîte à outils. TousImprimer() etAlerte() la sortie est visible ici.

Utilisation du testeur de stratégie


Le Strategy Tester est l’outil le plus important dont vous disposez pour tester et évaluer vos systèmes de trading.
MetaTrader 5 introduit de nouvelles fonctionnalités au testeur de stratégie, notamment des tests multi-devises,
des rapports améliorés et des tests avancés. Pour accéder au Strategy Tester, ouvrez leTesteur de stratégie
fenêtre dans MetaTrader à partir duStandardbarre d'outils ou appuyez surCtrl+R sur votre clavier.

Figure 22.8 - LeParamètres du testeur de stratégie.

LeParamètres onglet dans leTesteur de stratégie La fenêtre est l'endroit où vous entrerez les paramètres de test.
La plupart des paramètres sont explicites et devraient vous être familiers si vous avez utilisé le testeur de
stratégie MetaTrader 4. Il y a quelques nouveaux paramètres qui méritent d'être mentionnés. LeExécution La
liste déroulante vous permet d'incorporer un délai aléatoire lors du test des opérations d'ordre, qui imitent les
retards naturels du trading en direct.

A droite duExécution la liste déroulante est le mode de génération de ticks.Chaque tick tente de modéliser
chaque tick entrant en provenance du serveur. C'est le mode le plus lent, mais le plus précis.1 minute d'OHLC
utilise l'ouverture, le haut, le bas et la clôture de chaque barre M1 au cours de la période de test, fournissant une
approximation approximative des changements de prix intrabar. C'est plus rapide queChaque tick, mais moins
précis.Tarifs ouverts uniquement utilise uniquement l'OHLC de chaque barre de la période graphique

296
Acheteur : Evangelos Papageorgiou (vpapnm@yahoo.gr)
Numéro de transaction : 28J511988U193831A

Débogage et tests

sélectionnée. C'est le mode le plus rapide, mais le moins précis. Ceci est utile si vous souhaitez tester
rapidement un conseiller expert qui n'ouvre les commandes qu'à l'ouverture d'un nouveau bar.

Si vous vérifiez leVisualisation case à cocher, une fenêtre de visualisation distincte apparaîtra lorsque vous
démarrerez le test. La fenêtre de visualisation affiche les tests tick par tick. Tous les indicateurs sont dessinés et
vous pouvez voir l'ouverture et la clôture des ordres sur le graphique. La figure 22.9 montre la fenêtre
Visualisation. La vitesse de visualisation est également réglable. Lorsque vous déboguez un conseiller expert de
MetaEditor sur des données historiques, la fenêtre de visualisation est ce que vous verrez pendant le processus
de débogage.

Les paramètres restants dans leParamètres L'onglet est destiné à l'optimisation. Nous discuterons de
l'optimisation sous peu.

Figure 22.9 – Le testeur de stratégieVisualisation fenêtre.

LeContributions L'onglet est utilisé pour ajuster les paramètres d'entrée de l'expert-conseil. LeValeur La colonne
est utilisée pour définir les valeurs du test en cours. LeCommencer,Étape etArrêt Les colonnes sont destinées à
l'optimisation et seront traitées sous peu. Vous pouvez enregistrer les paramètres actuels dans un fichier,
charger les paramètres à partir d'un fichier ou définir tous les paramètres sur leurs valeurs par défaut à l'aide du
menu contextuel.

297
Acheteur : Evangelos Papageorgiou (vpapnm@yahoo.gr)
Numéro de transaction : 28J511988U193831A

ETXPERT UNCONSEIL P.ROGRAMMATION POUR METTRADER 5

Figure 22.10 - LeContributions du testeur de stratégie.

Une fois vos paramètres de test et vos paramètres d’entrée configurés, appuyez sur le boutonCommencer
bouton dans leParamètres languette. Une fois le test terminé, un graphique s'ouvrira et les résultats apparaîtront
sous leRésultats languette. LeRésultats L'onglet affiche un rapport de test montrant le profit, le prélèvement, le
facteur de profit et d'autres mesures statistiques de performance. Si vous faites défiler vers le bas dansRésultats
vous trouverez des statistiques et des graphiques commerciaux supplémentaires.

Figure 22.11 - LeRésultats dans le testeur de stratégie.

Pour afficher les détails de la transaction, cliquez avec le bouton droit dans leRésultats languette. Vous pouvez
sélectionnerOffres,Ordres ouOrdres &Offres dans le menu contextuel. LeOffres Ce rapport est le plus utile, car il
reflète les résultats commerciaux réels. Noter laDirection colonne dans laOffres rapport. Une direction dedans
signifie qu'un accord s'ajoute à la position actuelle. Une direction dedehors signifie que la transaction clôture une
partie ou la totalité de la position. Unentrée/sortie la direction fait référence à un renversement de position.

Figure 22.12 - LeOffres rapport à l'intérieur duRésultats du testeur de stratégie.

298
Acheteur : Evangelos Papageorgiou (vpapnm@yahoo.gr)
Numéro de transaction : 28J511988U193831A

Débogage et tests

Notez que les rapports ne répertorient pas les stop loss ni les take profits placés sur une position ! Si vous avez
besoin de ces informations, consultez le journal sous leJournal et recherchez le numéro de commande/de
transaction. Si une position se clôture avec un stop loss ou un take profit, cela sera reflété dans leOffres rapport
avec une entrée dans leCommentaire colonne, un fond rouge ou vert dans laPrix colonne, et unDirection
dedehors. Notez que lorsqu'une position est clôturée, elle est répertoriée comme une transaction distincte. Vous
pouvez enregistrer le contenu duRésultats vers un fichier de rapport en cliquant avec le bouton droit à l'intérieur
duRésultats onglet et sélectionOuvrir XML ouHTML duRapport sous-menu.

LeGraphique L'onglet affiche un graphique de rentabilité dans le temps. LeAgents L'onglet vous permet de gérer
des processeurs multicœurs à des fins de test, ainsi que d'utiliser des ordinateurs distants et le réseau cloud
MQL5 pour exécuter des optimisations gourmandes en ressources. Finalement, leJournal affiche le journal des
tests, qui se trouve dans le\ Testeur\Journaux dossier. Les résultats de vos tests, y compris toutes les entrées
et erreurs du journal, apparaîtront dans ce journal.

Optimisation
Exécuter unoptimisation sur un conseiller expert consiste à sélectionner les paramètres d'entrée à optimiser et à
tester toutes les combinaisons possibles d'ensembles de paramètres pour déterminer quels paramètres sont les
plus rentables. Ceci est généralement suivi d'untest avant qui teste les résultats d'optimisation sur des données
hors échantillon. L'optimisation et les tests prospectifs sont le processus par lequel vous évaluerez la rentabilité
de vos conseillers experts.

Commençons sous leContributions languette. LeCommencer,Étape etArrêt Les colonnes sont utilisées pour
définir les paramètres d’optimisation.
Pour optimiser un paramètre, cochez la case à gauche du nom du paramètre dans leVariable colonne.
LeCommencer value est la valeur de départ du paramètre. LeÉtape La valeur incrémente le paramètre du
montant spécifié et la valeurArrêt value est la valeur finale du paramètre. Par exemple, si nous avons
unCommencer valeur de 10, unÉtape valeur de 5, et unArrêt valeur de 50, alors le paramètre sera optimisé à
partir de 10, 15, 20... jusqu'à 50 pour un total de 10 pas.

Sélectionnez chaque paramètre que vous souhaitez optimiser et définissez leCommencer,Étape etArrêt valeurs
pour chacun d’eux. Vous souhaiterez peut-être limiter le nombre de paramètres à tester, ainsi que la valeur de
pas pour chaque paramètre. Plus il y a de paramètres/étapes à tester, plus l’optimisation prendra du temps.

Figure 22.13 - LeContributions dans le Strategy Tester, à l'aide de l'ongletCommencer,Étape etArrêt colonnes pour l'optimisation.

Les paramètres d'optimisation se trouvent en bas de laParamètres languette. Si aucune optimisation n'est
effectuée, leOptimisation la liste déroulante sera définie surDésactivé. Si vous souhaitez effectuer une

299
Acheteur : Evangelos Papageorgiou (vpapnm@yahoo.gr)
Numéro de transaction : 28J511988U193831A

ETXPERT UNCONSEIL P.ROGRAMMATION POUR METTRADER 5


optimisation, sélectionnez l'une des autres options.Algorithme complet lent teste toutes les combinaisons
possibles de paramètres et peut prendre très longtemps s'il y a beaucoup de paramètres à optimiser.Algorithme
génétique rapide utilise un algorithme génétique pour affiner la gamme de paramètres à tester. Les résultats
sont comparables à ceux obtenus avec l’algorithme complet et devraient constituer votre choix par défaut.Tous
les symboles sélectionnés dans MarketWatch testera tous les symboles dans leSurveillance du marché fenêtre.
Vous pouvez ajouter et supprimer des symboles de la fenêtre de surveillance du marché en utilisant le menu
contextuel à l'intérieur duSurveillance du marché fenêtre.

Figure 22.14– Les paramètres d'optimisation dans leParamètres du testeur de stratégie.

La liste déroulante à gauche deOptimisation est le critère d’optimisation de l’algorithme génétique. La valeur par
défaut est de trier par solde maximum, bien que vous puissiez trier par solde maximum combiné avec d'autres
critères, tels que le facteur de profit, le prélèvement ou le gain attendu.

LeAvant La liste déroulante sélectionne la période de test avant. SiAvant est réglé sur autre chose queNon, la
fraction sélectionnée de la période d'optimisation sera réservée aux tests avancés. Vous pouvez sélectionner
votre propre date de début pour la période de test anticipé en sélectionnantCoutume puis en définissant la date
de début dans la zone de saisie de date à droite. Si les tests avancés sont activés, chacun des résultats
d'optimisation sera testé individuellement au cours de la période de tests avancés.

Vous pouvez visualiser les résultats de l'optimisation dans leRésultats d'optimisation languette. Comparez les
résultats d'optimisation aux résultats des tests avancés pour voir dans quelle mesure les résultats d'optimisation
résistent. Si les résultats des tests avancés sont comparables aux résultats de l'optimisation, votre système peut
alors fonctionner correctement dans le trading. Si les deux ensembles de résultats ne sont pas comparables,
vous devrez peut-être ajuster vos paramètres d'optimisation, votre système de trading ou les deux.

Figure 22.15 - LeRésultats d'optimisation dans le testeur de stratégie, affichant les résultats des tests avant et arrière.

Faites un clic droit dans leRésultats d'optimisation onglet pour choisir entreRésultats des tests arrière
etRésultats des tests ultérieurs le cas échéant. Vous pouvez également activer des colonnes supplémentaires
dans le rapport, telles que le facteur de profit, le prélèvement, le gain attendu et les paramètres d'optimisation.
Les résultats de l'optimisation peuvent être enregistrés dans un fichier XML qui peut être ouvert par n'importe
quel tableur en sélectionnantExporter vers XML dans le menu contextuel. LeGraphique d'optimisation L'onglet
affiche un nuage de points des résultats d'optimisation. Vous pouvez sélectionner un graphique 2D ou 3D dans
le menu contextuel et définir les paramètres à utiliser pour l'axe X et l'axe Y.

Évaluation des résultats des tests


LeRésultats etRésultats d'optimisation Les onglets présentent une variété de mesures statistiques pour évaluer
la rentabilité et la stabilité de votre système commercial. MetaTrader 5 a ajouté de nombreuses nouvelles

300
Acheteur : Evangelos Papageorgiou (vpapnm@yahoo.gr)
Numéro de transaction : 28J511988U193831A

Débogage et tests

statistiques au rapport Strategy Trader. Dans cette section, nous examinerons les statistiques les plus
importantes qui apparaissent dans les rapports de trading et d'optimisation :

• Bénéfice net - Lebénéfice net est calculé comme le bénéfice brut moins la perte brute. Il s’agit
probablement de la statistique la plus importante et doit toujours être considérée par rapport à d’autres
statistiques. Évidemment, un bénéfice net plus élevé est préférable.

• Tirage - Leretrait est la perte maximale entre crête et vallée pendant la période de test. Le prélèvement
absolu est le prélèvement maximum du solde ou des capitaux propres en dessous du solde de départ
initial. Le prélèvement maximal et relatif est le prélèvement maximal des capitaux propres du solde du
profit maximum à la perte maximale. Le rabattement relatif est la valeur la plus importante. Les valeurs
inférieures sont meilleures.

• Facteur de profit - Lefacteur de profit est un simple rapport entre le bénéfice brut et la perte brute. Un
système qui ne réalise aucun profit a un facteur de profit de 1. Si le facteur de profit est inférieur à 1,
alors le système a perdu de l’argent. Un facteur de profit plus élevé est préférable.

• Gain attendu - Legain attendu est la moyenne/bénéfice ou perte d’une seule transaction. Des valeurs
plus élevées sont meilleures.

• Facteur de récupération - Lefacteur de récupération détermine le risque d'une stratégie de trading et la


manière dont elle se remet d'une perte. Il s’agit d’un rapport entre profit et prélèvement maximal. Des
valeurs plus élevées sont meilleures.

• Rapport de netteté - LeRapport de netteté détermine également le risque et la stabilité d’un système
commercial. Il utilise un algorithme sophistiqué pour comparer les rendements d’un système par
rapport à une méthode de rendement sans risque (telle qu’un bon du Trésor). Des valeurs plus élevées
sont meilleures.

301
Acheteur : Evangelos Papageorgiou (vpapnm@yahoo.gr) ID de transaction : 28J511988U193831A
Indice C
opérateur de
UN cas ............................................ ...................................................... 42
Barres CB
CompteInfoDouble() .................................................. ................................
classe ................................................. .............................................................
200 Opération
..... 206
d'addition .................. .................................................................. ..................
... 33 Fonction d'inclusion type de
AdjustAboveStopLevel() ........................ .............. 128 caractère ............................................ ............................................................
...... ....... 12
Fonction d'inclusion AdjustBelowStopLevel() .................................. 129
Fonction d'inclusion CheckAboveStopLevel() ......................................
Alerte() ............................................... .............................................................
130
..... .. 104, 279
Fonction d'inclusion CheckBelowStopLevel() ......................................
Fonctionnement
130
ET ................................................ ...................................................... 36
Fonction de classe
ArrayCopy() .................................................. .................................................
CheckNewBar() ............................................ ................ 238
................. 165
Fonction d'inclusion CheckOrderType() ............................................ .....
ArrayFree() .................................................. ...................................................
105 Fonction d'inclusion CheckReturnCode() .....................................
............... .. 164
102, 106 Fonction de classe
ArrayMaximum() .................................................. . ...................................... CheckTimer() ............................................ ...................... 246
211
Classe
ArrayMinimum() .................................................. . ...................................... CiMA ............................................................ ...................................................
211 ............... .222
ArrayResize() .................................................. ............................................... Classe CIndicator ............................................ ......................................
................... .19 220
Tableaux .................................................. ....................................................... Fonction de classe Close()
........... ............ 18 CBars ............................................ ...................... 207
Dynamique ............................................ ...............................................
Fonction de classe Close()
................... .......... 19
CTrade ............................................ ................... 148
Multidimensionnel ............................................................ ..................
Classe
.......... 19
CNewBar ............................................................ ............................................
ArraySetAsSeries() .................. .................................................................. ..
.......... 237
.... 206, 221
type de
couleur ................................................ ...........................................................
Taille du
....... ..... 15
tableau() .................................................. .......................................................
Commentaire() ....................................... .......................................................
........... ..... 21
........... ....... 283
Ask() inclut la Concaténation de
fonction ............................................ ...................................... 203 chaînes ...................................................... .................................. 14
spécificateur
Opérations const ............................................ ...................................................... 17
d'affectation ............................................................ ...................... 34 Constantes .................................................. ...................................................
............... ..... 17 continuer
l'opérateur .......................................... ...................................... 48
B CopyBuffer() . .................................................................. ..............................
Bid() inclut la fonction ............................................ .................................. ........................ 216
203 Fonction de classe CopierFermer() .................................................. ...........................................
BlockTimer() ........ .................................................................. ...... 254 ....................... 209
type CopieHaute() ............................................................ .....................................
booléen.................................................. ......................................................... ............................. .209
......... ...... 14 Arrêt de CopieLow() ................................................. ...................................................
rentabilité .................................................. ..................................................... ............... ... 209
. 189 Taux de
opérateur de copie() ............................................... ..............................................................
pause ................................................ ...................................................... 47 .... 205 propriété du droit
Fonction de classe d'auteur .................................................. ...................................... 65
BreakEven() ............................................ ...................... 191 CopyTime() ........ .................................................................. .........................
BuyStopLoss() inclut la fonction .............................................. .............. ............. 209
125 Cours en
Fonction d'inclusion BuyTakeProfit() ............................................ ........... attente ............................................................ ....................................... 162
126 Fonction d'inclusion CreateDateTime() ............................................ ......
244
Acheteur : Evangelos Papageorgiou (vpapnm@yahoo.gr) ID de
transaction : 28J511988U193831A
Classe Les
CTimer ................................................ ...................................................... énumérations ............................................................ ....................................
245 .............. 21
Opérateur égal
Classe à ..................................................... ....................................... 35
commerciale ...................................................... ........................................... Caractères
....................... .97 d'échappement ................................................ ...................................... 13
Gestionnaires
D d'événements ............................................ ........................................ 74
EventKillTimer() .................................................................. ..........................
Fonction de classe
............ 257
DailyTimer() ............................................ ...................... 248
EventSetTimer() .................................................. ........................................
constantes date-
256 Types
heure ................................................ ...................................... 16
d'exécution ..... .................................................................. ............................
.......... 72
type date/heure ............................................ ..................................... 16,
240
F
DebugBreak() .................................................. .............................................. FichierFerme() .................................................. .............................................
........ 311 ..................... .... 290
FileFlush() .................................................. .....................................................
opérateur par défaut ............................................ ...................................... ............. .... 290
43 FileIsEnding() .................................................. ...............................................
... 292
Fonction de classe FichierOuvert() ................................................. .............................................
Supprimer() ............................................ .................................. 174 ..................... ... 289
FileReadBool() .................................................. .............................................
description de la ......... 291
propriété ............................................ .................................. 65 FileReadDatetime() .................................................. ....................................
.. 291
Fonction de classe FileReadNumber() .................................................. ......................................
Deviation() .................................................. ...................... 110 291
FileReadString() .................................................. ......................................
Fonctionnement des 291
divisions ............................................ ...................................... 34
FileSearch() .................................................. ..................................................
................ ..... 290
opérateur à faire
pendant .............................................. ...................................... 45

type
double .................................................. ..........................................................
........ .13

ET
sinon si opérateur ....................................... ................................................
40 autre
opérateur . .................................................................. ...................................
......... 40 Mots - clés
enum . .................................................................. ........................................
22
ENUM_CHECK_RETCODE ...... .................................................................. .
.... 102, 106
ENUM_DAY_OF_WEEK ....................................... ......................................
251
ENUM_ORDER_TYPE .................................................. .................................
..... 81
ENUM_ORDER_TYPE_FILLING ...................................................... ............
... 81
ENUM_ORDER_TYPE_TIME .................................................................. ......
................ 82
ENUM_TRADE_REQUEST_ACTIONS .........................................................
......... .... 80
Acheteur : Evangelos Papageorgiou (vpapnm@yahoo.gr) ID de
transaction : 28J511988U193831A
FileWrite() ................................................................ .................................................................. .... 289 Remplir
politique .................................................................. .................................................................. ..... 74 types de flotteurs 13
pour opérateur 46
Les fonctions 49
Les valeurs par défaut 50
Surcharge 54
type
entier ................................................. ..............................................................
Paramètres par référence ............................................................ ............ .... .. ........ 12
53 iRSI() ................................... ... ...................................................... ..... ............
.......... 217

g iStochastique() .................................................. ............................................


Fonction de classe ......... ........... 219
GetEndTime() ............................................ ................... 255
GetLastError() .................................................. .............................................. L
........ 310
Fonction de classe GetStartTime() ............................................ ............... Inférieur à
255 l'opérateur ............................................................ ......................................
35
Fonction de classe
GetTickets() ............................................ ....................... 165 Variables Inférieur ou égal à l'opérateur .................................................. ..................
globales (programme) ..................... ................................................ 30 35
Mondial Variables (terminal) ............................................ ...................... Quebrary .................................................................. .....................................
292 ............................. ........ 306 propriété de la
GlobalVariableDel() .................................................. .................................... bibliothèque ....................................... ...........................................................
.. 293 ....... 306
GlobalVariableGet() .................................................. ...................................
... 293 Ordres
limités ............................................................ .................................................
GlobalVariableSet() .................................................. ....................................
..... 157 propriété de
.. 293
lien ............................................................ .......................................................
Supérieur à ........... 65 Variables
l'opérateur ............................................................ ...................................... locales .................................................. .................................................. 27
35 types
Supérieur ou égal à l'opérateur .................................................. ........... longs .................................................................. ..............................................
35 .................... .... 12

H Fonction d'inclusion LowestLow() ............................................ ................


211
Fonction d'inclusion HighestHigh() ............................................ ......

211je M
iPersonnalisé() .................................................. ............................................. Nombre magique ................................................ .......................................
..................... ..... 228 109
si Fonction de classe
opérateur ...................................................... ................................................. Main() .................................................. .................................. 221
................. .... 39 MathAbs() ............................................... . .....................................................
avoir() ............................................... .............................................................. .......... ... 201
.... ............... 216 Messagerie()............................................... ....................................................
Inc.fichier . 280
lude .................................................................. ............................................... Fonction de classe
................... ..... 3 ModifyPending() ............................................ .............. 170
INDICATEUR_CALCULATIONS .................................................. ............... Fonction de classe ModifyPosition() ............................................ ...... 143
228 Fonctionnement du
IndicateurRelease() .................................................. .................................... module .................................. .................................................................. 34
.. 222 Fonction de classe Fonction d'inclusion MoneyManagement() ............................................
Init() ....... .................................................................. ............................ 222 199
mot-clé Assistant
d'entrée .................. .................................................................. ..................... MQL5 ............................................................ ...................................... 75,
... 25 Variables 297
d'entrée ....................... .................................................................. ................ Structure MqlDateTime .................................................. ...................... 241
...... 25 Structure
MqlRates ............................................................ ......................................
205
Acheteur : Evangelos Papageorgiou (vpapnm@yahoo.gr) ID de
transaction : 28J511988U193831A
Structure OnTrade() ............................................................ ...........................................
MqlTick ............................................................ ...................................... 204 ....... .....75
Structure Fonction de classe OpenPending() ............................................ ...... 158,
MqlTradeRequest ......... .................................................................. ......... 160
79 Fonction de classe OpenPosition() ............................................ .........
Structure MqlTradeResult .................................................. ...................... 98, 111
87 Opération
Opération de OU ............................................................ ...................................... 37, 235
multiplication ............................................................ ...................... 33 Fonction de classe
OrderCount() ............................................ ...................... 163
N CommandeObtenirDouble() .................................................. ....................
.................. 167
Opération de
négation ...................................................... ...................................... 33 CommandeGetInteger() .................................................. . ..........................
............ 167
NormaliserDouble() .................................................. ...................................
... 35 OrderGetString() .................................................. . ......................................
167
Différent de
l'opérateur .............................................. ...................................... 35 CommandeGetTicket() .................................................. ..............................
.......... 162
PAS de
CommandeEnvoyer() ...... .................................................................. .........
fonctionnement ................................................. ...........................................
............................. 79
........... 37
CommandesTotal() ... .................................................................. ................
NUL ................................................. ................................................................ ...................... 162
.. .............. 11
P.
Ô
Jouer
Programmation orientée objet .............................................. ............ 57 son() ............................................... .................................................................
Des . 282
classes ................................................. ........................................................... PlotIndexSetString() .................................................. ..................................
....... 58 228
Constructeurs ............................................................ .......................... PositionGetDouble() ............ .................................................................. .....
............ 60 Classes ... 135, 143
dérivées ......... .................................................................. ............ 61, PositionGetInteger() .................................................. ...................... 135,
222 143
Objets ................................................. .................................................. PositionGetString() .................................................. ...................... 135, 143
.... 63 PositionGetTicket() .................................................. .....................................
Fonctions . 94
virtuelles ................................................ ................................ 62 PositionOpenPrice() inclut la fonction ................................... 136, 137
ObjetCréer() .............. .................................................................. ..................
PositionSelect() .................................................. ......................................
.... 283
100
ObjectDelete() .................................................. ............................................
PositionSelectByTicket() ............................................ ...................... 137
.......... 288
PositionType() inclut la fonction ............................................ ...... 135
ObjectGetValueByTime() .................................................. ......................
Directives du
286
préprocesseur .................................. ........................................ 65
ObjetMove() ............................................................ ...................................... Prédéfinir le fichier
............ 285 (.set) .................................................................. ..............................................
ObjetsDeleteAll() .................................................. ...................................... .... 4
288 prev_calculated .. .................................................................. .......................
ObjectSetInteger() .................................................. ...................................... ............... 301
284
OnCalculate() ......... .................................................................. .................... Fonction de classe PrintTimerMessage() ............................................ .....
.................. 295 250 mots-clés d'accès
OnDeinit() ............................................... ................................................ 74, privé .................................. ...................................... 58 mot-clé d'accès
294 À l'initialisation protégé .......... .................................................................. ........... 59
() .................................................. .................................................................. ..
........ 74 mot-clé d'accès
ActivéCommencer() .................................................. .................................. public .................................................. .................................. 58
................................ .... 302
OnTick() ............................................................ ..............................................
R.
.................... .......... 75
Au taux_total ............................................................ ...........................................
chronomètre() ............................................... ................................................ ....................... .301
.................. ...... 75 Gestionnaire d'événements Fonction de classe Release() ............................................ ......................
OnTimer() ....................................... ...................................... 256 222
Acheteur : Evangelos Papageorgiou (vpapnm@yahoo.gr) ID de
transaction : 28J511988U193831A
RéinitialiserLastError() .................................................. ............................... SYMBOL_TRADE_TICK_VALUE .......................................................
....... 310 .. 200
SYMBOL_VOLUME_MAX .................................................. ............
Codes 197
retour ............................................................ .................................................. SYMBOL_VOLUME_MIN .................................................. ..............
.... 88 opérateur de 197
retour ............................................ ............................................ 52 SYMBOL_VOLUME_STEP .................................................. ............
197
S SymbolInfoInteger() .................................................. . .......................
Propriété script_show_confirm .................................................. ...... 303 ................
Propriété script_show_inputs .................................. ...................... 303, SYMBOL_DIGITS .................................................. ...................... 126
305 SYMBOL_TRADE_STOPS_LEVEL ......................................................
SellStopLoss() inclut la fonction ............................................ .............. 127 127
SellTakeProfit() inclut la fonction ............................................ ........... 126 SymbolInfoTick() .................................................. ......................................
SendMail() ..................................... ................................................................. 204
. ............... 281
EnvoyerNotification() ............................................................ ...................... T
................ 282 Tirer
SetIndexBuffer() .................................................. ........................................ profit ................................................ ...............................................................
227 type ... .. 124
court ..... .................................................................. ........................................ TerminalFerme() .................................................. .......................................
.............. 12 294
Fonction de classe Opérateur
Signal() .................................................. ...................................... 225 ternaire ............................................................ ...................................... 41
mot-clé sinput ............................................ ................................... 26, TimeCurrent() .................................................. ..............................................
252emot clé ........ 246
atique .................................................................. ...................................... 30
HeureLocal() .................................................. ................................................
Variables
.................. .246
statiques .. .................................................................. ....................................
.. 30 Niveau Structure du TimerBlock ............................................ ...............................
d'arrêt ....... .................................................................. ................................... 252
................... 127 TimeToString() .................................................. ............................................
Ordres stop à cours .......... 241
limité .................................................. ...................................... 157 TimeToStruct() .................................................. ............................................
Stop- .......... 242
loss ............................................................ ...................................................... TRADE_ACTION_CLOSE_BY ............................................ ......................
............ ..... 121 85
Ordres TRADE_ACTION_DEAL .................................................. ............... 82, 84,
stop .................................................. ............................................................... 85
... 157 Fonction d'inclusion TRADE_ACTION_MODIFY ............................................... ......................
StopPriceToPoints() ............................................ ... 201 86
type de TRADE_ACTION_PENDING .................................................. ......................
chaîne ...................................................... ....................................................... 86
........... .... 13 TRADE_ACTION_REMOVE .................................................. ......................
StringToTime() ............................................ .................................................. 87
................ 241 mot-clé TRADE_ACTION_SLTP .................................................. ...................... 83,
struct .................................................. .................................................. 23 84
StructToTime( ) .................................................. ....................................... TradeServerReturnCodeDescription() ............................................ 103
242 Stop
Structures .................................................. .................................................... suiveur ............................................................ ................................................
.............. .... 23 Opération de 181
soustraction .............................................. ...................................... 33 Dynamique .................................................................. ..................................
opérateur de .................... 187
commutation ........ .................................................................. ...................... Bénéfice minimal ................................................. ................................ 183
......... 42
SymbolInfoDouble() ............... .................................................................. .. Étape ................................................. ..............................................................
.................... .... ... 184
SYMBOL_ASK .......................... .................................................................. ... Fonction de classe
.... 101 TrailingStop() ............................................ ...................... 186
SYMBOL_BID .................................................. ..................................... Typographie ................................................................. .................................
. 101 ................................. .24
SYMBOLE_POINT .................................................. ...................... 125 Transtypage explicite ............................................................ ......................
25
Acheteur : Evangelos Papageorgiou (vpapnm@yahoo.gr) ID de
transaction : 28J511988U193831A
DANS
type
d'uchar ............................................ ................................................................
.. .... 12 types
uint .......................................... .................................................................. .....
....... 12 type
ulong .................................. .................................................................. ..........
........ 12 Fonction de classe
Update() .......................... ...................................................... 207

type
ucourt ............................................................ .................................................
................. .. 12

DANS
Portée variable ................................................. ............................................
27 VérifierVolume() inclure la
fonction ................................................ ......... 198
propriété de
version ...................................................... ...................................... 65
mot-clé
virtuel ............................................................ ................................... 62, 222
type de
vide ................................................ .................................................................
. ....... 52
DANS
pendant que
l'opérateur ............................................ .................................................. 44
VALEUR_FAUX .. .................................................................. .........................
............. 100

AVEC
ZéroMemory() .................................................. .............................................
..... 90

_
_Chiffres ............................................................ .............................................
..................... ........... 31
_Période ................................................. ........................................................
.......... .......... 31
_Indiquer ................................................. .......................................................
........... ...... 31
_Symbole ................................................. ......................................................
............ ........ 31

#
#define
directive ............................................................ ................................... 17,
66 #directive
import ......... .................................................................. ...................... 307
Directive
#include ............................................................ .................................. 67, 97
#directivepropriété ........... .................................................................. ........
...... 65, 303
Acheteur : Evangelos Papageorgiou (vpapnm@yahoo.gr) ID de transaction : 28J511988U193831A
Buyer: Evangelos Papageorgiou (vpapnm@yahoo.gr)
Transaction ID: 28J511988U193831A

Vous aimerez peut-être aussi