English Русский 中文 Español Deutsch 日本語 Português 한국어 Italiano Türkçe
Le prototype du robot de trading

Le prototype du robot de trading

MetaTrader 5Exemples | 22 décembre 2021, 16:39
260 0
---
---

Introduction

Le cycle de vie de tout système de trading est réduit aux positions d'ouverture et de fermeture. Cela ne fait aucun doute. Mais quand il s'agit de la réalisation de l'algorithme, ici il y a autant d'avis que de programmeurs. Chacun pourra résoudre le même problème à sa manière, mais avec le même résultat final.

Au fil des années, la pratique de la programmation a essayé plusieurs approches pour construire la logique et la structure des experts. À l'heure actuelle, on peut affirmer que l'on a établi un modèle clair qui est utilisé dans tous les codes.

Cette approche n'est pas universelle à 100 %, mais elle peut changer votre méthode de conception de la logique d'expert. Et la question n'est pas de savoir quelles capacités de travail utiliser pour les ordres lorsque l'on veut appeler l’Expert. Le point global - est le principe de la création d'un modèle de trading.


1. Principes de conception des systèmes de négociation et types de sources d'événements

L'approche de base de la conception de l'algorithme, utilisée par la majorité, consiste à tracer une position depuis son ouverture jusqu'à sa fermeture. Il s'agit d'une approche linéaire. Et si vous souhaitez apporter des modifications au code, cela entraîne souvent de grandes complications, car un grand nombre de conditions apparaissent et le code accumule de nouvelles branches d'analyse.

La meilleure solution pour modéliser un robot de trading est de « créer des conditions ». Et le principe fondamental - analyser non pas comment cette condition d'expert et ses positions et ordres sont apparus - mais ce que nous devons en faire maintenant. Ce principe de base change fondamentalement la gestion des trades et simplifie l'élaboration du code.

Considérez-le plus en détail.

1.1. Le principe des « conditions de service »

Comme déjà mentionné, l'expert n'a pas besoin de savoir comment l'état actuel a été atteint. Il doit savoir quoi en faire maintenant en fonction de son environnement (valeurs des paramètres, propriétés des ordres stockés, etc.).

Ce principe est directement lié au fait que l'expert existe de boucle en boucle (en particulier - de trait en trait), et il ne doit pas se soucier de ce qui s'est passé avec les ordres au trait précédent. Par conséquent, vous devez utiliser une approche événementielle de la gestion des ordres. C'est-à-dire que sur le trait actuel, l'expert enregistre son état, qui est le point de départ de la décision concernant le prochain trait.

Par exemple, vous devez supprimer tous les ordres en attente de l’expert et ensuite seulement continuer à analyser les indicateurs et à passer de nouveaux ordres. La plupart des exemples de code que nous avons vus utilisent la boucle « while (true) {try to remove} » ou la boucle légèrement plus douce « while (k < 1000) {try to remove; k++;} ». Nous allons ignorer la variante, où l'appel unique de l’ordre suppression sans analyse d'erreur.

Cette méthode est linéaire, elle « accroche » l'expert pour une durée indéterminée.

Par conséquent, il sera plus correct de ne pas boucler un expert, mais de stocker l’ordre de suppression des ordres, de sorte qu'à chaque nouveau trait, cet ordre soit vérifié lors de la tentative de suppression d’ordres en attente. Dans ce cas, un expert, en lisant les paramètres de statut, sait qu'à ce moment il doit supprimer des ordres. Et il tentera de les supprimer. Si une erreur de trading se produit, un expert bloquera simplement une analyse plus approfondie et travaillera avant la prochaine boucle.

1.2. Le deuxième principe principal de la conception - est l'abstraction maximale possible de la direction de la position considérée (achat/vente), de la devise et du graphique. Toutes les fonctions d’expert doivent être mises en œuvre de telle manière que la direction ou le symbole soient analysés dans de rares cas où cela ne peut vraiment pas être évité (par exemple, lorsque vous considérez la croissance favorable du prix pour la position ouverte, bien qu'il existe différentes options pour éviter les détails). Essayez toujours d'éviter une telle conception « de bas niveau ». Cela réduira le code et le processus d'écriture des fonctions au moins deux fois. Et les rendra « indépendants du trade ». 

La mise en œuvre de ce principe consiste à remplacer l'analyse explicite des types d'ordres, des paramètres symboles et des paramètres calculés liés par des macro-fonctions. Dans l'article suivant, nous couvrons cette mise en œuvre en détail.

1.3. Troisième principe segmentation de l'algorithme en lexèmes logiques (modules indépendants)

En pratique, on peut dire que la meilleure approche est la séparation des opérations d’experts en fonctions individuelles. Je pense que vous conviendrez qu'il est difficile d'écrire tout l'algorithme de l'expert écrit en une seule fonction, et cela complique l'analyse et l'édition ultérieures. Il ne faut donc pas le faire en MQL5, qui offre désormais un contrôle quasi complet sur votre environnement.

Par conséquent, les lexèmes logiques (par exemple ouverture, suivi, clôture des ordres) doivent être mis en œuvre séparément les uns des autres avec une analyse complète des paramètres et événements environnementaux. Grâce à cette approche, l'expert devient flexible dans la conception. Vous pouvez facilement y ajouter de nouveaux modules indépendants sans toucher aux modules existants, ou désactiver les modules existants sans modifier le code principal.

Ces trois principes permettent de créer un prototype unique pour tous les experts, que vous pouvez facilement modifier et adapter pour n'importe quelle tâche donnée.

Les sources d'événements pour le système expert sont :


1. Indicateurs. Un exemple - est l'analyse des valeurs des lignes d’indicateur, leurs intersections, combinaisons, etc. De plus, les indicateurs peuvent être : l'heure du jour, les données obtenues sur Internet, etc. Dans la plupart des cas, les événements indicateurs sont utilisés pour signaler l'ouverture et la clôture des ordres. Moins pour leur ajustement (généralement Trailing Stop Loss ou ordre en attente pour l'indicateur).  

Par exemple, la mise en œuvre pratique de l'indicateur peut être appelée un expert, qui analyse l'intersection de la MA rapide et lente avec l'ouverture supplémentaire de la position dans la direction de l'intersection. 

2. Positions, ordres existants, et leur statut. Par exemple, la taille actuelle de la perte ou du profit, la présence/absence de positions ou d'ordres en attente, le profit de la position fermée, etc. La mise en œuvre pratique de ces événements est beaucoup plus large et plus diversifiée, car il existe plus d'options de leur relation que pour les événements dd l’indicateur.

L'exemple le plus simple d'un expert, basé uniquement sur l'événement de trading, est le remplissage pour faire la moyenne de la position existante et la générer en fonction du profit souhaité. C'est-à-dire que la présence d'une perte sur une position disponible sera un événement permettant de passer un nouvel ordre de moyenne.

Ou, par exemple, Trailing Stop Loss. Cette fonction coche un événement, lorsque le prix évolue vers le profit pour un nombre spécifié de points par rapport au Stop Loss précédent. En conséquence, l'expert tire le Stop Loss au delà du prix.

3. Événements externes. Bien qu'un tel événement ne se produise généralement pas dans un système purement expert, il doit en général être pris en compte pour prendre une décision. Cela inclut l'ajustement des ordres, des positions, le traitement des erreurs de trades, le traitement des événements graphiques (déplacement/création/suppression d'objets, clics sur des boutons, etc.). En général, ce sont les événements, qui ne sont pas vérifiables sur l'historique et ne se produisent que lorsque l'expert travaille.

Un exemple frappant de tels experts sont les systèmes d'information de trades avec contrôle graphique de trades.

Toute variété d'experts est basée sur la combinaison de ces trois sources d'événements

2. La classe de base CExpertAdvisor – constructeur expert

Quel sera le travail de l'expert de trading ? Le schéma général des interactions du programme MQL est présenté sur le graphique ci-dessous.

Figure 1. Schéma général des interactions des éléments du programme MQL

Figure 1. Schéma général des interactions des éléments du programme MQL

Comme vous pouvez le voir sur le schéma, l'entrée de la boucle de travail vient en premier (cela peut être un trait ou un signal de minuteur). A ce stade au niveau du premier bloc, ce trait peut être filtré sans traitement. Cela se produit dans ces cas où l'expert n'a pas besoin de travailler sur chaque trait, mais uniquement sur une nouvelle barre ou si l'expert n'est tout simplement pas autorisé à travailler.

Ensuite, le programme passe dans le deuxième bloc - les modules de travail avec les ordres et les positions, et alors seulement les blocs de traitement des événements sont appelés à partir des modules. Chaque module ne peut interroger que l'événement qui l'intéresse.  

Cette séquence peut être appelée schéma avec logique direct, car elle détermine d'abord CE QUE l'expert fera (quels modules de traitement d'événements sont utilisés), et ensuite seulement elle implémente COMMENT et POURQUOI elle fera cela (la provision des signaux d'événement).

La logique directe est cohérente avec notre perception du monde et la logique universelle. Après tout, un homme pense d'abord à des concepts concrets, puis il les résume, puis classe et identifie leurs interrelations.

Les experts en conception ne font pas exception à cet égard. Tout d'abord, il est déclaré ce qu'un expert doit faire (ouvrir et fermer des positions, tirer le stop de protection), et ce n'est qu'ensuite qu'il est spécifié, dans quels événements et comment il doit le faire. Mais en tout cas pas l'inverse : recevez le signal et réfléchissez où et comment le traiter. C'est la logique inverse, et il vaut mieux ne pas l'utiliser, car en conséquence vous obtiendrez un code lourd avec un grand nombre de branches de condition.

Voici un exemple de logique inverse et directe. Prendre l'ouverture/fermeture via le signal RSI.

  • En logique inverse, l'expert commence par obtenir la valeur de l'indicateur, puis il vérifie la direction du signal et ce que vous avez à faire avec la position : ouvrir l'achat et fermer la vente, ou vice versa - ouvrir la vente et fermer l'achat. C'est-à-dire que le point d'entrée est d'obtenir et d'analyser le signal.
  • En logique inverse, tout est opposé. L’Expert dispose de deux modules de positions d'ouverture et de fermeture, et il vérifie simplement les conditions pour exécuter ces modules. C'est-à-dire qu'après être entré dans le module d'ouverture, l'expert reçoit la valeur de l'indicateur et vérifie s'il s'agit d'un signal d'ouverture. Ensuite, après être entré dans le module de clôture des ordres, l'expert vérifie s'il s'agit d'un signal de clôture de position. C'est-à-dire qu'il n'y a pas de point d'entrée - il existe des modules d'analyse du statut du système fonctionnant indépendamment (le premier principe de conception).

Maintenant, si vous voulez compliquer l'expert, il sera beaucoup plus facile d'utiliser la deuxième variante que la première. Il suffira de créer un nouveau module de traitement des événements.

Et dans la première variante, vous devrez réviser la structure du traitement du signal ou la coller en tant que fonction distincte.

Recommandation : Lorsque vous décrivez le système de trading, ne commencez pas par des mots comme « 1. Obtenez le signal... ouvrez l’ordre », mais plutôt scindez immédiatement en sections : « a) La condition d’ouverture d’ordres, b) conditions de maintien d’ordres, etc.

Pour mieux comprendre cette approche, voici les différents schémas de travail dans le cadre de quatre experts différents.

Figure 2. Exemples de mise en œuvre d'experts

Figure 2. Exemples de mise en œuvre d'experts

a). Expert, basé uniquement sur les signaux de certains indicateurs. Il peut ouvrir et fermer des positions lorsque le signal change. Exemple - un expert MA.
b). Expert avec contrôle graphique de trade.
c). Expert basé sur indicateurs, mais avec en plus le Trailing Stop Loss et le temps d’opération. Exemple - scalpage sur les nouvelles avec position d'ouverture dans la tendance par l'indicateur MA.
d). Expert sans indicateurs, avec positions moyennes. Il vérifie les paramètres de position une seule fois lors de l'ouverture d'une nouvelle barre. Exemple - expert de moyenne.

Comme le montrent les schémas, tout système de trading est très facile à décrire en utilisant la logique directe


3. Mise en œuvre de la classe Expert

Créer une classe en utilisant toutes les règles et exigences mentionnées ci-dessus, qui seront la base de tous les experts futurs.

La fonctionnalité minimale qui devrait être dans la classe CExpertAdvisor est la suivante :

1. Initialisation :

  • Indicateurs de registre
  • Configurer les valeurs initiales des paramètres
  • Ajuster suivant le symbole et le délai requis

2. Fonctions d'obtention de signaux

  • Temps de travail autorisé (intervalles tradés)
  • Déterminer le signal pour ouvrir/fermer des positions ou des ordres
  • Déterminer le filtre (tendance, temps, etc.)  
  • Minuteur de démarrage/arrêt

3. Fonctions de service

  • Calculer le prix d'ouverture, les niveaux SL et TP, le volume des ordres
  • Envoyer des demandes de trades (ouvrir, fermer, modifier)

4. Modules de trade

  • Traiter les signaux et les filtres
  • Contrôler les positions et les ordres
  • Travailler dans des fonctions d’experts : OnTrade(), OnTimer(), OnTester(), OnChartEvent().

5. Dé-initialisation

  • Produire des messages, des rapports
  • Effacer les graphiques, décharger les indicateurs

Toutes les fonctions de la classe sont divisées en trois groupes. Le schéma général des fonctions imbriquées et leurs descriptions sont présentés ci-dessous.

Figure 3. Schéma des fonctions d'imbrication d'un expert

Figure 3. Schéma des fonctions d'imbrication d'un expert

1. Fonctions macro 

Ce petit groupe de fonctions constitue la base pour travailler avec les types d'ordres, les paramètres de symboles et les valeurs de prix pour configurer les ordres (l'ouverture et les arrêts). Ces macros fonctions fournissent le deuxième principe de conception - l'abstraction. Elles travaillent dans le contexte du symbole, qui est utilisé par l'expert.

Les fonctions macro des types de conversion fonctionnent avec la direction du marché - acheter ou vendre. Par conséquent, afin de ne pas créer vos propres constantes, mieux vaut utiliser celles existantes - ORDER_TYPE_BUY et ORDER_TYPE_SELL. Voici quelques exemples d'utilisation de macro et les résultats de leur travail.

   //--- Type conversion macro
   long       BaseType(long dir);        // returns the base type of order for specified direction
   long       ReversType(long dir);      // returns the reverse type of order for specified direction
   long       StopType(long dir);        // returns the stop-order type for specified direction
   long       LimitType(long dir);       // returns the limit-order type for specified direction

   //--- Normalization macro
   double     BasePrice(long dir);       // returns Bid/Ask price for specified direction
   double     ReversPrice(long dir);     // returns Bid/Ask price for reverse direction

   long dir,newdir;
   dir=ORDER_TYPE_BUY;
   newdir=ReversType(dir);               // newdir=ORDER_TYPE_SELL
   newdir=StopType(dir);                 // newdir=ORDER_TYPE_BUY_STOP
   newdir=LimitType(dir);                // newdir=ORDER_TYPE_BUY_LIMIT
   newdir=BaseType(newdir);              // newdir=ORDER_TYPE_BUY

   double price;
   price=BasePrice(dir);                 // price=Ask
   price=ReversPrice(dir);               // price=Bid

Lors du développement d'experts, la fonction macro vous permet de ne pas spécifier la direction du traitement et aide à créer un code plus compact.

2. Fonctions de service

Ces fonctions sont conçues pour fonctionner avec les ordres et les positions. Comme la fonction macro, ils sont également de bas niveau. Pour plus d’aisance, elles peuvent être divisées en deux catégories : les fonctions d'information et les fonctions exécutives. Elles n'effectuent tous qu'un seul type d'action, sans analyser aucun événement. Elles exécutent les ordres des gestionnaires experts seniors.

Exemples de fonctions d'information : recherche du cours maximum d'ouverture des ordres en cours ; découvrir comment la position a été fermée - avec profit ou perte ; obtenir le nombre et la liste des tickets d'ordres d'experts, etc.

Exemples de fonctions exécutives : clôture des ordres spécifiés ; modifier le Stop Loss dans la position spécifiée, etc.

Ce groupe est le plus important. C'est sur ce type de fonctionnalité que repose tout le travail de routine de l'expert. Le grand nombre d'exemples de ces fonctions peut être trouvé sur le forum à l'adresse https://www.mql5.com/ru/forum/107476. Mais en plus de cela, la bibliothèque standard MQL5 contient déjà des classes qui prennent en charge une partie du travail de passation des ordres et des positions, en particulier - la classe CTrade.

Mais n'importe quelle tâche de votre part nécessitera de créer de nouvelles mises en œuvre ou de modifier légèrement celles existantes.

3. Modules de traitement d'événements

Le groupe de ces fonctions est une superstructure de haut niveau sur les deux premiers groupes. Comme mentionné ci-dessus, ce sont des blocs prêts à l'emploi sur lesquels votre expert est construit. En général, ils sont inclus dans la fonction de traitement des événements du programme MQL : OnStart(), OnTick(), OnTimer(), OnTrade(), OnChartEvent(). Ce groupe n'est pas nombreux, et le contenu de ces modules peut être ajusté d'une tâche à l'autre. Mais essentiellement rien ne change.  

Dans les modules, tout doit être abstrait (le deuxième principe de conception) afin que le même module puisse être invoqué à la fois pour l'achat et la vente. Ceci est réalisé, bien sûr, avec l'aide de la macro fonction.

Alors, passez à la mise en œuvre

1. Initialisation, Désinitialisation

class CExpertAdvisor
  {
protected:
   bool              m_bInit;       // flag of correct initialization
   ulong             m_magic;       // magic number of expert
   string              m_smb;       // symbol, on which expert works
   ENUM_TIMEFRAMES      m_tf;       // working timeframe
   CSymbolInfo      m_smbinf;       // symbol parameters
   int               m_timer;       // time for timer

public:
   double              m_pnt;       // consider 5/3 digit quotes for stops
   CTrade            m_trade;       // object to execute trade orders
   string              m_inf;       // comment string for information about expert's work

Il s'agit de l'ensemble de paramètres minimum requis pour que les fonctions de l’expert fonctionnent.

Les paramètres m_smb et m_tf sont spécialement placés dans les propriétés de l'expert pour indiquer facilement à l'expert la devise et la période à utiliser pour votre travail. Par exemple, si vous assignez m_smb = « USDJPY », l'expert travaillera sur ce symbole, quel que soit le symbole sur lequel il a été exécuté. Si vous définissez tf = PERIOD_H1, alors tous les signaux et l'analyse des indicateurs auront lieu sur le graphique H1. 

De plus, il existe des méthodes de classe. Les trois premières méthodes sont l'initialisation et la désinitialisation d'un expert.

public:
   //--- Initialization
   void              CExpertAdvisor();                               // constructor
   void             ~CExpertAdvisor();                               // destructor
   virtual bool      Init(long magic,string smb,ENUM_TIMEFRAMES tf); // initialization

Le constructeur et le destructeur dans la classe de base ne font rien.

La méthode Init() effectue l'initialisation des paramètres de lexpert par le symbole, le délai et le nombre magique.

//------------------------------------------------------------------ CExpertAdvisor
void CExpertAdvisor::CExpertAdvisor()
  {
   m_bInit=false;
  }
//------------------------------------------------------------------ ~CExpertAdvisor
void CExpertAdvisor::~CExpertAdvisor()
  {
  }
//------------------------------------------------------------------ Init
bool CExpertAdvisor::Init(long magic,string smb,ENUM_TIMEFRAMES tf)
  {
   m_magic=magic; m_smb=smb; m_tf=tf;         // set initializing parameters
   m_smbinf.Name(m_smb);                      // initialize symbol
   m_pnt=m_smbinf.Point();                    // calculate multiplier for 5/3 digit quote
   if(m_smbinf.Digits()==5 || m_smbinf.Digits()==3) m_pnt*=10;  
   m_trade.SetExpertMagicNumber(m_magic);     // set magic number for expert

   m_bInit=true; return(true);                // trade allowed
  }

2. Fonctions d'obtention de signaux

Ces fonctions analysent le marché et les indicateurs.

   bool              CheckNewBar();                          // check for new bar
   bool              CheckTime(datetime start,datetime end); // check allowed trade time
   virtual long      CheckSignal(bool bEntry);               // check signal
   virtual bool      CheckFilter(long dir);                  // check filter for direction

Les deux premières fonctions ont une mise en œuvre assez spécifique et peuvent être utilisées dans d'autres enfants de cette classe.

//------------------------------------------------------------------ CheckNewBar
bool CExpertAdvisor::CheckNewBar()          // function of checking new bar
  {
   MqlRates rt[2];
   if(CopyRates(m_smb,m_tf,0,2,rt)!=2)      // copy bar
     { Print("CopyRates of ",m_smb," failed, no history"); return(false); }
   if(rt[1].tick_volume>1) return(false);   // check volume 
   return(true);
  }
//---------------------------------------------------------------   CheckTime
bool CExpertAdvisor::CheckTime(datetime start,datetime end)
  {
   datetime dt=TimeCurrent();                          // current time
   if(start<end) if(dt>=start && dt<end) return(true); // check if we are in the range
   if(start>=end) if(dt>=start|| dt<end) return(true);
   return(false);
  }

Les deux dernières dépendent toujours de ces indicateurs que vous utilisez. Il est tout simplement impossible de configurer ces fonctions pour tous les cas.

L'essentiel - il est important de comprendre que les fonctions de signal CheckSignal() et CheckFilter() peuvent analyser absolument tous les indicateurs et leurs combinaisons ! C'est-à-dire que les modules de trading, dans lesquels ces signaux seront ultérieurement inclus, sont indépendants des sources.

Cela vous permet d'utiliser un expert une fois écrit comme modèle pour d'autres experts qui travaillent sur un principe similaire. Il suffit de modifier les indicateurs analysés ou d'ajouter de nouvelles conditions de filtrage.

3. Fonctions de service

Comme déjà mentionné, ce groupe de fonctions est le plus nombreux. Pour nos tâches pratiques décrites dans l'article, il suffira de mettre en œuvre quatre de ces fonctions :

   double         CountLotByRisk(int dist,double risk,double lot); // calculate lot by size of risk
   ulong          DealOpen(long dir,double lot,int SL,int TP);     // execute deal with specified parameter
   ulong          GetDealByOrder(ulong order);                     // get deal ticket by order ticket
   double         CountProfitByDeal(ulong ticket);                 // calculate profit by deal ticket
//------------------------------------------------------------------ CountLotByRisk
double CExpertAdvisor::CountLotByRisk(int dist,double risk,double lot) // calculate lot by size of risk
  {
   if(dist==0 || risk==0) return(lot);
   m_smbinf.Refresh();
   return(NormalLot(AccountInfoDouble(ACCOUNT_BALANCE)*risk/(dist*10*m_smbinf.TickValue())));
  }
//------------------------------------------------------------------ DealOpen
ulong CExpertAdvisor::DealOpen(long dir,double lot,int SL,int TP)
  {
   double op,sl,tp,apr,StopLvl;
   // determine price parameters
   m_smbinf.RefreshRates(); m_smbinf.Refresh();
   StopLvl = m_smbinf.StopsLevel()*m_smbinf.Point(); // remember stop level
   apr     = ReversPrice(dir); 
   op      = BasePrice(dir);                         // open price
   sl      = NormalSL(dir, op, apr, SL, StopLvl);    // stop loss
   tp      = NormalTP(dir, op, apr, TP, StopLvl);    // take profit

   // open position
   m_trade.PositionOpen(m_smb,(ENUM_ORDER_TYPE)dir,lot,op,sl,tp);
   ulong order = m_trade.ResultOrder(); 
   if(order<=0) return(0);                           // order ticket
   return(GetDealByOrder(order));                    // return deal ticket
  }
//------------------------------------------------------------------ GetDealByOrder
ulong CExpertAdvisor::GetDealByOrder(ulong order) // get deal ticket by order ticket
  {
   PositionSelect(m_smb);
   HistorySelectByPosition(PositionGetInteger(POSITION_IDENTIFIER));
   uint total=HistoryDealsTotal();
   for(uint i=0; i<total; i++)
     {
      ulong deal=HistoryDealGetTicket(i);
      if(order==HistoryDealGetInteger(deal,DEAL_ORDER))
         return(deal);                            // remember deal ticket
     }
   return(0);
  }
//------------------------------------------------------------------ CountProfit
double CExpertAdvisor::CountProfitByDeal(ulong ticket)  // position profit by deal ticket
  {
   CDealInfo deal; deal.Ticket(ticket);                 // deal ticket
   HistorySelect(deal.Time(),TimeCurrent());            // select all deals after this
   uint total  = HistoryDealsTotal();
   long pos_id = deal.PositionId();                     // get position id
   double prof = 0;
   for(uint i=0; i<total; i++)                          // find all deals with this id
     {
      ticket = HistoryDealGetTicket(i);
         if(HistoryDealGetInteger(ticket,DEAL_POSITION_ID)!=pos_id) continue;
      prof += HistoryDealGetDouble(ticket,DEAL_PROFIT); // summarize profit
     }
   return(prof);                                        // return profit
  }

4. Modules de trade

Enfin, ce groupe de fonctions lie l'ensemble du processus de trading, en traitant les signaux et les événements, en utilisant les fonctions de service et la macro. Les lexèmes logiques des opérations de trades sont peu nombreux, ils dépendent de vos objectifs spécifiques. Cependant, nous pouvons distinguer les concepts communs, qui existent presque dans tous les experts.

   virtual bool      Main();                            // main module controlling trade process
   virtual void      OpenPosition(long dir);            // module of opening position
   virtual void      CheckPosition(long dir);           // check position and open additional ones
   virtual void      ClosePosition(long dir);           // close position
   virtual void      BEPosition(long dir,int BE);       // moving Stop Loss to break-even
   virtual void      TrailingPosition(long dir,int TS); // trailing position of Stop Loss
   virtual void      OpenPending(long dir);             // module of opening pending orders
   virtual void      CheckPending(long dir);            // work with current orders and open additional ones
   virtual void      TrailingPending(long dir);         // move pending orders
   virtual void      DeletePending(long dir);           // delete pending orders

Nous considérerons des mises en œuvre spécifiques de ces fonctions dans les exemples ci-dessous.

L'ajout de nouvelles fonctions ne sera pas difficile, puisque nous avons choisi la bonne approche et composé la structure de l’expert. Si vous utilisez exactement ce schéma, vos conceptions nécessiteront un minimum d'efforts et de temps, le code sera lisible même après un an.

Bien entendu, vos experts ne se limitent pas là. Dans la classe CExpertAdvisor, nous n'avons déclaré que les méthodes les plus nécessaires. Vous pouvez ajouter de nouveaux gestionnaires dans les classes des enfants, modifier ceux qui existent déjà, développer vos propres modules, créant ainsi une seule bibliothèque. Disposer d'une telle bibliothèque, développer des experts « clé en main » prend d'une demi-heure à deux jours.


4. Exemples d'utilisation de la classe CExpertAdvisor

4.1. Exemple de travail basé sur les signaux d’indicateur

Comme premier exemple, commençons par la tâche la plus simple - considérons l’Expert Advisor MovingAverage (exemple de base de MetaTrader 5) en utilisant la classe CExpertAdvisor. Compliquons juste un peu.

Algorithme :

a) Condition d'ouverture de position

  • Si le prix traverse la MA de bas en haut, ouvrez la position pour acheter.
  • Si le prix traverse la MA de haut en bas, ouvrez la position pour vendre.
  • Configurez SL (Stop Loss), TP (TakeProfit).
  • Le lot de position est calculé par le paramètre Risk - combien perdront de leur dépôt lorsque le Stop Loss est déclenché.

b) Condition de fermeture de la position

  • Si le prix franchit MA de bas en haut, fermez la position pour vendre.
  • Si le prix franchit la MA de haut en bas, fermez la position pour acheter.

c) Limitation

  • Limiter le travail d'un expert par temps de HourStart jusqu'à HourEnd quotidiennement.
  • L'expert effectue des opérations de trade uniquement sur la nouvelle barre.

d) Prise en charge de position

  • Utilisez un simple trailing stop à une distance de TS.

Pour notre expert nous aurons besoin de sept fonctions de la classe CExpertAdvisor :

  • Fonction de signal - CheckSignal()  
  • Filtre de traits - CheckNewBar()
  • Filtre de temps - CheckTime()
  • Fonction de service des positions d'ouverture - DealOpen()
  • Trois modules de travail - OpenPosition(), ClosePosition(), TrailingPosition()

La fonction CheckSignal() et les modules doivent être définis dans une classe enfant pour résoudre spécifiquement sa tâche. Nous devons également ajouter l'initialisation de l'indicateur.

//+------------------------------------------------------------------+
//|                                              Moving Averages.mq5 |
//|                        Copyright 2010, MetaQuotes Software Corp. |
//|                                              https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2010, MetaQuotes Software Corp."
#property link      "https://www.mql5.com"
#property version   "1.00"

#include "ExpertAdvisor.mqh"

input double Risk      = 0.1; // Risk
input int    SL        = 100; // Stop Loss distance
input int    TP        = 100; // Take Profit distance
input int    TS        =  30; // Trailing Stop distance
input int    pMA       =  12; // Moving Average period
input int    HourStart =   7; // Hour of trade start
input int    HourEnd   =  20// Hour of trade end
//---
class CMyEA : public CExpertAdvisor
  {
protected:
   double            m_risk;          // size of risk
   int               m_sl;            // Stop Loss
   int               m_tp;            // Take Profit
   int               m_ts;            // Trailing Stop
   int               m_pMA;           // MA period
   int               m_hourStart;     // Hour of trade start
   int               m_hourEnd;       // Hour of trade end
   int               m_hma;           // MA indicator
public:
   void              CMyEA();
   void             ~CMyEA();
   virtual bool      Init(string smb,ENUM_TIMEFRAMES tf); // initialization
   virtual bool      Main();                              // main function
   virtual void      OpenPosition(long dir);              // open position on signal
   virtual void      ClosePosition(long dir);             // close position on signal
   virtual long      CheckSignal(bool bEntry);            // check signal
  };
//------------------------------------------------------------------ CMyEA
void CMyEA::CMyEA() { }
//----------------------------------------------------------------- ~CMyEA
void CMyEA::~CMyEA()
  {
   IndicatorRelease(m_hma); // delete MA indicator
  }
//------------------------------------------------------------------ Init
bool CMyEA::Init(string smb,ENUM_TIMEFRAMES tf)
  {
   if(!CExpertAdvisor::Init(0,smb,tf)) return(false);  // initialize parent class

   m_risk=Risk; m_tp=TP; m_sl=SL; m_ts=TS; m_pMA=pMA;  // copy parameters
   m_hourStart=HourStart; m_hourEnd=HourEnd;

   m_hma=iMA(m_smb,m_tf,m_pMA,0,MODE_SMA,PRICE_CLOSE); // create MA indicator
   if(m_hma==INVALID_HANDLE) return(false);            // if there is an error, then exit
   m_bInit=true; return(true);                         // trade allowed
  }
//------------------------------------------------------------------ Main
bool CMyEA::Main()                            // main function
  {
   if(!CExpertAdvisor::Main()) return(false); // call function of parent class

   if(Bars(m_smb,m_tf)<=m_pMA) return(false); // if there are insufficient number of bars
   
   if(!CheckNewBar()) return(true);           // check new bar

   // check each direction
   long dir;
   dir=ORDER_TYPE_BUY;
   OpenPosition(dir); ClosePosition(dir); TrailingPosition(dir,m_ts);
   dir=ORDER_TYPE_SELL;
   OpenPosition(dir); ClosePosition(dir); TrailingPosition(dir,m_ts);

   return(true);
  }
//------------------------------------------------------------------ OpenPos
void CMyEA::OpenPosition(long dir)
  {
   if(PositionSelect(m_smb)) return;     // if there is an order, then exit
   if(!CheckTime(StringToTime(IntegerToString(m_hourStart)+":00"),
                 StringToTime(IntegerToString(m_hourEnd)+":00"))) return;
   if(dir!=CheckSignal(true)) return;    // if there is no signal for current direction
   double lot=CountLotByRisk(m_sl,m_risk,0);
   if(lot<=0) return;                    // if lot is not defined then exit
   DealOpen(dir,lot,m_sl,m_tp);          // open position
  }
//------------------------------------------------------------------ ClosePos
void CMyEA::ClosePosition(long dir)
  {
   if(!PositionSelect(m_smb)) return;                 // if there is no position, then exit
   if(!CheckTime(StringToTime(IntegerToString(m_hourStart)+":00"),
                 StringToTime(IntegerToString(m_hourEnd)+":00")))
     { m_trade.PositionClose(m_smb); return; }        // if it's not time for trade, then close orders
   if(dir!=PositionGetInteger(POSITION_TYPE)) return; // if position of unchecked direction
   if(dir!=CheckSignal(false)) return;                // if the close signal didn't match the current position
   m_trade.PositionClose(m_smb,1);                    // close position
  }
//------------------------------------------------------------------ CheckSignal
long CMyEA::CheckSignal(bool bEntry)
  {
   MqlRates rt[2];
   if(CopyRates(m_smb,m_tf,0,2,rt)!=2)
     { Print("CopyRates ",m_smb," history is not loaded"); return(WRONG_VALUE); }

   double ma[1];
   if(CopyBuffer(m_hma,0,0,1,ma)!=1)
     { Print("CopyBuffer MA - no data"); return(WRONG_VALUE); }

   if(rt[0].open<ma[0] && rt[0].close>ma[0])
      return(bEntry ? ORDER_TYPE_BUY:ORDER_TYPE_SELL); // condition for buy
   if(rt[0].open>ma[0] && rt[0].close<ma[0])
      return(bEntry ? ORDER_TYPE_SELL:ORDER_TYPE_BUY); // condition for sell

   return(WRONG_VALUE);                                // if there is no signal
  }

CMyEA ea; // class instance
//------------------------------------------------------------------ OnInit
int OnInit()
  {
   ea.Init(Symbol(),Period()); // initialize expert
   return(0);
  }
//------------------------------------------------------------------ OnDeinit
void OnDeinit(const int reason) { }
//------------------------------------------------------------------ OnTick
void OnTick()
  {
   ea.Main();                  // process incoming tick
  }

Analysons la structure de la fonction Main(). Par convention, elle est divisée en deux parties.

Dans la première partie, la fonction parent est appelée. Cette fonction traite les éventuels paramètres qui affectent globalement le travail d'un expert. Ceux-ci incluent la vérification de l'autorisation de trader pour un expert et la validation des données historiques.

Dans la deuxième partie, les événements du marché sont directement traités.

Le filtre CheckNewBar() est testé - pour la vérification d'une nouvelle barre. Et les modules pour deux directions de trade sont appelés l'un après l'autre.

Dans les modules, tout est organisé assez abstraitement (le deuxième principe de conception). Il n'y a pas d'adresse directe aux propriétés du symbole. Et trois modules - OpenPosition(), ClosePosition() et TrailingPosition() - ne reposent que sur les paramètres qui leur viennent de l'extérieur. Cela vous permet d'appeler ces modules pour la vérification des ordres à la fois pour l'Achat et pour la Vente. 


4.2. Exemple d'utilisation du CExpertAdvisor - Expert sans indicateurs, analyse du statut de position et de résultat

Pour démontrer, prenons le système qui trade uniquement sur la position inversée avec une augmentation du lot après une perte (ce type d'experts est généralement appelé « Martingale »)

a) Passer l’ordre initial

  •  lorsque l'expert démarre, il ouvre la première position pour Acheter avec le lot initial

b) Ouvrir les positions suivantes

  • si la position précédente a été fermée en profit, alors ouvrez la position dans la même direction avec le lot initial
  • si la position précédente a été fermée avec perte, ouvrez la position dans la direction opposée avec un lot plus important (via un facteur).

Pour notre expert nous aurons besoin de trois fonctions de la classe CExpertAdvisor :

  • position ouverte - DealOpen()
  • obtenir la valeur du profit de la position fermée par ticket de transaction - CountProfitByDeal()
  • modules de travail - OpenPosition(), CheckPosition()

Étant donné que l'expert n'analyse aucun indicateur, mais traite uniquement les résultats, nous utiliserons les événements OnTrade() pour une productivité optimale. C'est-à-dire que l'expert qui a passé une fois le premier ordre initial d'achat, ne passera tous les ordres ultérieurs qu'après la clôture de cette position. Nous passerons donc l’ordre initial dans OnTick() et effectuerons tout le travail ultérieur dans OnTrade().

La fonction Init(), comme d'habitude, initialise simplement les paramètres de la classe avec les paramètres externes de l'expert.

Le module OpenPosition() ouvre la position initiale et est bloqué par le drapeau m_first.

Le module CheckPosition() contrôle les autres inversions de position.

Ces modules sont appelés à partir des fonctions respectives de l'expert : OnTick() et OnTrade().

//+------------------------------------------------------------------+
//|                                                       eMarti.mq5 |
//|              Copyright Copyright 2010, MetaQuotes Software Corp. |
//|                                              https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2010, MetaQuotes Software Corp."
#property link      "https://www.mql5.com"
#property version   "1.00"

#include "ExpertAdvisor.mqh"
#include <Trade\DealInfo.mqh>

input double Lots    = 0.1; // Lot
input double LotKoef = 2;   // lot multiplier for loss
input int    Dist    = 60;  // distance to Stop Loss and Take Profit
//---
class CMartiEA : public CExpertAdvisor
  {
protected:
   double            m_lots;       // Lot
   double            m_lotkoef;    // lot multiplier for loss
   int               m_dist;       // distance to Stop Loss and Take Profit
   CDealInfo         m_deal;       // last deal
   bool              m_first;      // flag of opening the first position
public:
   void              CMartiEA() { }
   void             ~CMartiEA() { }
   virtual bool      Init(string smb,ENUM_TIMEFRAMES tf); // initialization
   virtual void      OpenPosition();
   virtual void      CheckPosition();
  };
//------------------------------------------------------------------ Init
bool CMartiEA::Init(string smb,ENUM_TIMEFRAMES tf)
  {
   if(!CExpertAdvisor::Init(0,smb,tf)) return(false); // initialize parent class
   m_lots=Lots; m_lotkoef=LotKoef; m_dist=Dist;       // copy parameters
   m_deal.Ticket(0); m_first=true;
   m_bInit=true; return(true);                        // trade allowed
  }
//------------------------------------------------------------------ OnTrade
void CMartiEA::OpenPosition()
  {
   if(!CExpertAdvisor::Main()) return;                       // call parent function
   if(!m_first) return;                                      // if already opened initial position
   ulong deal=DealOpen(ORDER_TYPE_BUY,m_lots,m_dist,m_dist); // open initial position
   if(deal>0) { m_deal.Ticket(deal); m_first=false; }        // if position exists
  }
//------------------------------------------------------------------ OnTrade
void CMartiEA::CheckPosition()
  {
   if(!CExpertAdvisor::Main()) return;           // call parent function
   if(m_first) return;                           // if not yet placed initial position 
   if(PositionSelect(m_smb)) return;             // if position exists

   // check profit of previous position
   double lot=m_lots;                            // initial lot
   long dir=m_deal.Type();                       // previous direction
   if(CountProfitByDeal(m_deal.Ticket())<0)      // if there was loss
     {
      lot=NormalLot(m_lotkoef*m_deal.Volume());  // increase lot
      dir=ReversType(m_deal.Type());             // reverse position
     }
   ulong deal=DealOpen(dir,lot,m_dist,m_dist);   // open position
   if(deal>0) m_deal.Ticket(deal);               // remember ticket
  }

CMartiEA ea; // class instance
//------------------------------------------------------------------ OnInit
int OnInit()
  {
   ea.Init(Symbol(),Period()); // initialize expert
   return(0);
  }
//------------------------------------------------------------------ OnDeinit
void OnDeinit(const int reason) { }
//------------------------------------------------------------------ OnTick
void OnTick()
  {
   ea.OpenPosition();          // process tick - open first order
  }
//------------------------------------------------------------------ OnTrade
void OnTrade()
  {
   ea.CheckPosition();         // process trade event
  }


5. Travailler avec des événements

Dans cet article, vous avez rencontré des exemples de traitement de deux événements - NewTick et Trade qui étaient respectivement représentés par les fonctions OnTick() et OnTrade(). Dans la plupart des cas, ces deux événements sont constamment utilisés.

Pour les experts, il existe quatre fonctions de traitement des événements :

  • OnChartEvent traite un grand groupe d'événements : lorsque vous travaillez avec des objets graphiques, clavier, souris et événements personnalisés. Par exemple, la fonction permet de créer des experts interactifs ou des experts, construits sur le principe de la gestion graphique des ordres. Ou simplement pour créer des contrôles actifs des paramètres du programme MQL (à l'aide de boutons et de champs d'édition). En général, cette fonction est utilisée pour traiter l'événement externe d'un expert.
  • OnTimer est appelé lorsque l'événement de minuterie système est traité. Il est utilisé dans les cas où le programme MQL nécessite d'analyser son environnement régulièrement, pour calculer les valeurs des indicateurs, lorsqu'il est nécessaire de se référer en permanence à des sources externes de signaux, etc. En gros, la fonction OnTimer() - est une alternative, voire le meilleur remplacement pour :
    while(true) {  /* perform analysis */; Sleep(1000); }. 
    C'est-à-dire que l'expert n'a pas à travailler dans une boucle sans fin à son démarrage, mais assez pour déplacer les appels de ses fonctions de OnTick() à OnTimer().
  • OnBookEvent traite un événement, qui est généré lorsque la profondeur Market change de statut. Cet événement peut être attribué à l'externe et effectuer son traitement conformément à la tâche.
  • OnTester est appelé après avoir testé l'expert sur une plage de dates donnée, avant la fonction OnDeinit() pour le dépistage éventuel des générations de test, lors de l'utilisation de l'optimisation génétique par le paramètre Custom max.

N'oubliez pas qu'il est toujours conseillé d'utiliser tous les événements et leurs combinaisons pour la résolution de leur tâche spécifique.


Épilogue

Comme vous pouvez le voir, écrire un expert, tout en ayant le bon schéma, ne prend pas beaucoup de temps. En raison des nouvelles possibilités de traitement des événements dans MQL5, nous avons une structure plus flexible de gestion du processus de trading. Mais tout cela ne devient un outil vraiment puissant que si vous avez correctement préparé vos algorithmes de trading.

L'article décrit trois grands principes de leur création - événementiel, abstraction, modularité. Vous faciliterez votre trade, si vous basez vos experts sur ces « trois piliers ».

Traduit du russe par MetaQuotes Ltd.
Article original : https://www.mql5.com/ru/articles/132

Fichiers joints |
emarti.mq5 (3.57 KB)
emyea.mq5 (5.9 KB)
expertadvisor.mqh (17.92 KB)
Comment créer votre propre Trailing Stop Comment créer votre propre Trailing Stop
La règle de base du trader : Laisse que le bénéfice croisse et que les pertes tombent ! Cet article considère l'une des techniques de base, permettant de suivre cette règle - déplacer le niveau d’arrêt protecteur (niveau de stop loss) après avoir augmenté le bénéfice de la position, c'est-à-dire - le niveau Trailing Stop. Vous trouverez la procédure étape par étape pour créer une classe pour le trailing stop sur les indicateurs SAR et NRTR. Chacun pourra insérer ce trailing stop dans ses experts ou l'utiliser de manière autonome pour contrôler les positions dans leurs comptes.
20 signaux de trade en MQL5 20 signaux de trade en MQL5
Cet article vous apprendra comment recevoir les signaux de trade nécessaires au fonctionnement d'un système de trading. Les exemples de formation de 20 signaux de trade sont donnés ici en tant que fonctions personnalisées distinctes qui peuvent être utilisées lors du développement d'Expert Advisors. Pour votre aisance, toutes les fonctions utilisées dans l'article sont regroupées dans un seul fichier d'inclusion mqh qui peut être facilement connecté à un futur Expert Advisor.
Création d'indicateurs multicolores dans MQL5 Création d'indicateurs multicolores dans MQL5
Dans cet article, nous verrons comment créer des indicateurs multicolores ou convertir les indicateurs existants en multicolores. MQL5 permet de représenter les informations sous une forme pratique. Désormais, il n'est plus nécessaire de regarder une douzaine de graphiques avec des indicateurs et d'effectuer des analyses du RSI ou des niveaux stochastiques, il est mieux simplement de peindre les bougies avec des couleurs différentes selon les valeurs des indicateurs.
Comment écrire un indicateur à partir d'un autre indicateur Comment écrire un indicateur à partir d'un autre indicateur
Dans MQL5, vous pouvez écrire un indicateur à la fois à partir de zéro et à partir d'un autre indicateur déjà existant, intégré au terminal client ou personnalisé. Et ici, vous avez également deux manières - d'améliorer un indicateur en lui ajoutant de nouveaux calculs et styles graphiques, ou d'utiliser un indicateur intégré au terminal client ou un indicateur personnalisé via les fonctions iCustom() ou IndicatorCreate().