Une autre classe MQL5 OOP
laplacianlab | 13 janvier, 2022
Introduction
Construire un EA orientée objet complète qui fonctionne réellement est à mon humble avis une tâche difficile qui nécessite de nombreuses compétences réunies : raisonnement logique, pensée divergente, capacité d'analyse et de synthèse, imagination, etc. Disons que si le système de trading automatisé que nous devons résoudre était un jeu d'échecs, son idée de trading serait la stratégie d'échecs. Et l'exécution de la stratégie d'échecs par la tactique serait la programmation du robot à l'aide d'indicateurs techniques, de graphiques, d'idées économiques fondamentales et d'axiomes conceptuels.
Figure 1. Détail de l'École d'Athènes de Raffaello Sanzio. Dans cette image, nous voyons les philosophes Platon et Aristote dans une discussion approfondie.
Platon représente ici le monde conceptuel et Aristote le monde empiriste.
Je suis conscient de la difficulté de cet exercice. Un EA OO n'est pas extrêmement difficile à programmer, mais il est vrai qu'il présente un certain degré de difficulté aux personnes ayant peu d'expérience dans le développement d'applications. Comme dans toute autre discipline, cela est, à son tour, dû au manque d'expérience lui-même, c'est pourquoi j'essaie de vous enseigner ce sujet à travers un exemple précis que je suis sûr que vous comprendrez. Ne vous découragez pas si vous ne vous sentez toujours pas en sécurité dans la gestion des concepts de POO, vous trouverez les choses beaucoup plus faciles une fois que vous aurez implémenté vos cinq premiers EA, disons. Vous n'avez pas besoin de construire quoi que ce soit à partir de zéro pour l'instant, comprenez simplement ce que j'explique ici !
L'ensemble du processus de conception et de mise en œuvre d'un système de trading peut être simplifié à bien des égards lorsqu'il est effectué par plusieurs personnes, bien que ce fait pose le problème de la communication. Je veux dire que la personne qui conçoit la stratégie d'investissement n'est pas obligée de gérer les concepts de programmation qui gère son interlocuteur, le programmeur. Et le développeur MQL5 peut ne pas comprendre au début certains aspects importants de la stratégie de trading de son client.
Il s'agit d'un problème classique en génie logiciel qui a conduit à la création de nombreuses méthodologies de développement logiciel telles que Scrum, Test Driven Development (TDD), Extreme programming (XP), etc. Il est crucial d'être conscient des pièges de la langue. Soit dit en passant, à votre connaissance, Wikipédia déclare : "la méthodologie de développement de logiciel ou la méthodologie de développement de système en génie logiciel est un cadre utilisé pour structurer, planifier et contrôler le processus de développement d'un système d'information".
Nous allons supposer que nous sommes capables à la fois de concevoir et de mettre en œuvre rapidement des idées de trading réussies. On peut aussi supposer que l'on est au terme d'un processus de développement itératif où le système de trading pensé par notre client est déjà bien défini et compris par tous. Comme vous préfèrez. D'ailleurs, je vais dorénavant me référer dans ce texte à quelques articles pédagogiques disponibles dans les articles de programmation MQL5 afin que vous puissiez rapidement vérifier et rappeler quelques idées, lorsque cela est nécessaire, pour réussir cet exercice. Êtes-vous prêts ?
1. Vos premiers pas pour adopter le nouveau paradigme
1.1. Pourquoi la POO est-elle bonne pour programmer vos EA Forex ?
Peut-être que vous vous demandez à ce stade pourquoi vous devez faire quelque chose comme ça. Laissez-moi vous dire tout d'abord que vous n'êtes pas obligé d'être POO. Quoi qu'il en soit, il est fortement recommandé d'être une personne de la POO afin que faire un pas de plus dans votre connaissance de la programmation des systèmes de trading automatisés.
La manière classique de développer des applications, la programmation dite procédurale, présente les inconvénients suivants :
- Cela rend difficile la modélisation des problèmes. Sous ce vieux paradigme, la solution du problème principal est réduite à la division de celui-ci en sous-problèmes plus simples qui sont résolus par des modules fonctionnels, à savoir, des fonctions et des procédures.
- Cela rend difficile la réutilisation du code, ce qui à son tour nuit au coût, à la fiabilité, à la flexibilité et à la maintenance.
La réutilisation du code est plus facile avec le nouveau style orienté objet. C'est très important ! De nombreux experts pensent que la réutilisation du code est la vraie solution à la plupart des problèmes de développement de logiciels.
À ce stade, nous devons mentionner abstract data types (ADT). La POO permette la création d'ADT. Un ADT est une abstraction du concept traditionnel de type de données qui est présent dans tous les langages de programmation. Son utilisation principale est de définir confortablement le domaine de données des applications. Include\Arrays\Array.mqh, Include\Arrays\List.mqh et Include\Arrays\Tree.mqh sont quelques exemples de types de données abstraits MQL5.
En bref, le paradigme de la programmation orientée objet veut que vous conceviez vos applications à un niveau conceptuel afin de vous faire bénéficier de la réutilisation du code, de la fiabilité,de la flexibilité et de la facilité de maintenance.
1.2. Êtes-vous un raisonneur conceptuel ? UML vient à la rescousse
Avez-vous déjà entendu parler de UML ? UML est l'abréviation de Unified Modeling Language (langage de modélisation unifié). C'est un langage graphique pour la conception de systèmes orientés objet. Nous, les humains, sommes censés d'abord penser nos systèmes dans la phase d'analyse, puis les coder avec un langage de programmation. Aller de haut en bas est moins insensé pour les développeurs. Néanmoins, mon expérience d'analyste me dit que parfois ce n'est pas possible à cause de plusieurs choses : l'application doit être faite dans un temps très court, il n'y a personne dans l'équipe qui puisse appliquer rapidement ses connaissances d'UML, ou peut-être certaines personnes dans l'équipe ne connaissent pas certaines parties d'UML.
UML est à mon avis un bon outil d'analyse que vous pouvez utiliser si vous vous sentez à l'aise avec, et si les circonstances entourant le projet sont ok. Veuillez lire Comment développer un Expert Advisor à l'aide des outils UML si vous souhaitez explorer le sujet d'UML. Cet article est peut-être un peu accablant, mais il sert à avoir une vue d'ensemble sur la façon dont les ingénieurs logiciels professionnels travaillent leur analyse. Vous auriez besoin de suivre un cours de plusieurs semaines pour bien comprendre UML ! Pour l'instant, il est normal de savoir ce qu'est UML. D'après mon expérience, cet outil d'analyse n'est pas toujours utilisé dans tous les projets logiciels en raison de plusieurs circonstances présentes dans le monde réel.
Figure 2. Le logo UML.
1.3. Bonjour tout le monde ! Votre première classe OO
Si vous êtes un débutant complet dans la programmation orientée objet, je vous recommande de lire d'abord la documentation officielle sur l’OO disponible dans la référence MQL5, puis de jeter un œil à la rédaction d'un Expert Advisor à l'aide de l'approche de programmation orientée objet MQL5 pour obtenir les bases. S'il vous plaît assurez-vous de compléter ces lectures avec d'autres matériaux. A partir de maintenant je suppose que vous connaissez déjà de la POO, vous comprendrez donc facilement l'exemple classique de la classe Person qui en MQL5 est le suivant :
//+------------------------------------------------------------------+ //| CPerson Class | //+------------------------------------------------------------------+ class CPerson { protected: string m_first_name; string m_surname; datetime m_birth; public: //--- Constructor and destructor methods CPerson(void); ~CPerson(void); //--- Getter methods string GetFirstName(void); string GetSurname(void); datetime GetBirth(void); //--- Setter methods void SetFirstName(string first_name); void SetSurname(string surname); void SetBirth(datetime birth); }; //+------------------------------------------------------------------+ //| Constructor | //+------------------------------------------------------------------+ CPerson::CPerson(void) { Alert("Hello world! I am run when an object of type CPerson is created!"); } //+------------------------------------------------------------------+ //| Destructor | //+------------------------------------------------------------------+ CPerson::~CPerson(void) { Alert("Goodbye world! I am run when the object is destroyed!"); } //+------------------------------------------------------------------+ //| GetFirstName | //+------------------------------------------------------------------+ string CPerson::GetFirstName(void) { return m_first_name; } //+------------------------------------------------------------------+ //| GetSurname | //+------------------------------------------------------------------+ string CPerson::GetSurname(void) { return m_surname; } //+------------------------------------------------------------------+ //| GetBirth | //+------------------------------------------------------------------+ datetime CPerson::GetBirth(void) { return m_birth; } //+------------------------------------------------------------------+ //| SetFirstName | //+------------------------------------------------------------------+ void CPerson::SetFirstName(string first_name) { m_first_name=first_name; } //+------------------------------------------------------------------+ //| SetSurname | //+------------------------------------------------------------------+ void CPerson::SetSurname(string surname) { m_surname=surname; } //+------------------------------------------------------------------+ //| SetBirth | //+------------------------------------------------------------------+ void CPerson::SetBirth(datetime birth) { m_birth=birth; } //+------------------------------------------------------------------+
Ce qui suit est destiné aux développeurs puristes obsédés par l'écriture de code de qualité. Contrairement à certains exemples disponibles dans les articles de programmation MQL5 et à de nombreux autres logiciels disponibles dans Code Base, la classe ci-dessus utilise les mêmes conventions de programmation appliquées par MetaQuotes Software Corp. pour coder son cadre MQL5. Je vous encourage à écrire votre code comme le fait MetaQuotes. Soit dit en passant, le fil intitulé À propos des conventions dans les programmes POO MQL5 couvre ce sujet.
En résumé, certaines conventions importantes appliquées à l'écriture de Person.mqh sont :
- Le nom de classe CPerson commence par la lettre C majuscule.
- Les noms de méthode sont en majuscules et commencent par une majuscule, par exemple, GetFirstName, SetSurname, etc.
- Les noms des propriétés protégées sont précédés du préfixe m_, par exemple, m_first_name, m_surname et m_birth.
- Le mot réservé this n'est pas utilisé pour référencer les membres de la classe à l'intérieur de la classe elle-même.
Veuillez jeter un œil à certains fichiers du cadre MQL5, par exemple, Include\Arrays\Array.mqh, Include\Arrays\List.mqh, Include\Trade\Trade.mqh, et voyez comment le code MQL5 d'origine est écrit.
2. Programmons notre première EA orientée objet
2.1. L'idée du système de trading
Notre idée de trading est simple : "Les tendances courtes des marchés volatils sont presque aléatoires". C'est ça ! Cela a été observé par plusieurs experts dans certaines circonstances. Si cette hypothèse est vraie, notre robot Forex doit fonctionner par nécessité. Étant donné un point aléatoire sur un graphique, le prochain mouvement peut évidemment monter et descendre, nous ne savons pas. Le fait est que si la différence entre les niveaux SL et TP établis est suffisamment petite, alors cette différence n'est pas significative en termes absolus, et nous avons donc atteint l'espérance mathématique. Ce système va juste laisser fonctionner l'espérance mathématique. Une fois que vous aurez obtenu le code de l'EA dans cet article et exécuté le backtest, vous verrez qu'il nécessite une politique de gestion de l'argent très simple.
2.2. Le squelette de la POO du robot
Dans cette section, nous développons la stratégie ci-dessus à travers le raisonnement abstrait requis par la programmation orientée objet. Alors pourquoi ne commençons-nous pas à penser à notre EA comme s'il s'agissait d'une créature vivante ? Selon cette vision, notre machine Forex peut être composée de trois parties principales : un cerveau, quelque chose que nous appellerons évolution et un graphique.
Le cerveau est la partie du robot qui contient les données nécessaires à son fonctionnement, quelque chose comme une mémoire morte (ROM). Le graphique est la pièce d'information émulant le graphique sur lequel le robot opère. Enfin, l'évolution dite est une donnée contenant des informations temporelles telles que l'état du robot à un instant donné, l'historique des opérations effectuées, etc. C'est comme si on concevait un être humain à travers ses organes, quelque chose comme un Frankenstein, car il faut développer une application pour le secteur de la santé. Chaque organe est dans ce contexte un concept sémantique unique associé à d'autres parties de l'ensemble.
Tout d'abord, créons le dossier MQL5\Include\Mine pour stocker nos éléments personnalisés. C'est juste une idée pour organiser votre code. Il est bon de savoir que vous pouvez le faire dans vos développements mais bien sûr vous n'êtes pas obligé de le faire. Nous allons ensuite créer le fichier MQL5\Include\Mine\Enums.mqh afin de stocker les enums créés par nos soins :
//+------------------------------------------------------------------+ //| Status enumeration | //+------------------------------------------------------------------+ enum ENUM_STATUS_EA { BUY, SELL, DO_NOTHING }; //+------------------------------------------------------------------+ //| Lifetime enumeration | //+------------------------------------------------------------------+ enum ENUM_LIFE_EA { HOUR, DAY, WEEK, MONTH, YEAR }; //+------------------------------------------------------------------+
Ensuite, il est temps de créer l'embryon de notre EA qui s'appellera ExpertSimpleRandom.mq5 ! Alors, s'il vous plaît, créez le dossier MQL5\Experts\SimpleRandom puis créez à l'intérieur du fichier ExpertSimpleRandom.mq5 avec ce code :
//+------------------------------------------------------------------+ //| ExpertSimpleRandom.mq5 | //| Copyright © 2013, Jordi Bassagañas | //+------------------------------------------------------------------+ #property copyright "Copyright © 2013, laplacianlab" #property link "https://www.mql5.com/fr/articles" #property version "1.00" #include <Trade\Trade.mqh> #include <Trade\SymbolInfo.mqh> #include <Trade\PositionInfo.mqh> #include <Indicators\Indicators.mqh> #include <Mine\Enums.mqh> #include <..\Experts\SimpleRandom\CSimpleRandom.mqh> input int StopLoss; input int TakeProfit; input double LotSize; input ENUM_LIFE_EA TimeLife; MqlTick tick; CSimpleRandom *SR=new CSimpleRandom(StopLoss,TakeProfit,LotSize,TimeLife); //+------------------------------------------------------------------+ //| Initialization function | //+------------------------------------------------------------------+ int OnInit(void) { SR.Init(); return(0); } //+------------------------------------------------------------------+ //| Deinitialization function | //+------------------------------------------------------------------+ void OnDeinit(const int reason) { SR.Deinit(); delete(SR); } //+------------------------------------------------------------------+ //| OnTick event function | //+------------------------------------------------------------------+ void OnTick() { SymbolInfoTick(_Symbol,tick); SR.Go(tick.ask,tick.bid); } //+------------------------------------------------------------------+
Ce n'est qu'une approche parmi les nombreuses possibles. Tout cela sert essentiellement à illustrer le fonctionnement de la POO dans MQL5. Comme vous le voyez, la classe principale de l'Expert Advisor est nommée CSimpleRandom.mqh, s'il vous plaît, enregistrez-la dans MQL5\Experts\SimpleRandom\CSimpleRandom.mqh :
//+------------------------------------------------------------------+ //| ExpertSimpleRandom.mq5 | //| Copyright © 2013, Jordi Bassagañas | //+------------------------------------------------------------------+ #include <Trade\Trade.mqh> #include <Mine\Enums.mqh> #include <..\Experts\SimpleRandom\CBrain.mqh> #include <..\Experts\SimpleRandom\CEvolution.mqh> #include <..\Experts\SimpleRandom\CGraphic.mqh> //+------------------------------------------------------------------+ //| CSimpleRandom Class | //+------------------------------------------------------------------+ class CSimpleRandom { protected: CBrain *m_brain; CEvolution *m_evolution; CGraphic *m_graphic; CTrade *m_trade; CPositionInfo *m_positionInfo; public: //--- Constructor and destructor methods CSimpleRandom(int stop_loss,int take_profit,double lot_size,ENUM_LIFE_EA time_life); ~CSimpleRandom(void); //--- Getter methods CBrain *GetBrain(void); CEvolution *GetEvolution(void); CGraphic *GetGraphic(void); CTrade *GetTrade(void); CPositionInfo *GetPositionInfo(void); //--- Specific methods of CSimpleRandom bool Init(); void Deinit(void); bool Go(double ask,double bid); }; //+------------------------------------------------------------------+ //| Constructor | //+------------------------------------------------------------------+ CSimpleRandom::CSimpleRandom(int stop_loss,int take_profit,double lot_size,ENUM_LIFE_EA time_life) { int lifeInSeconds; switch(time_life) { case HOUR: lifeInSeconds=3600; break; case DAY: lifeInSeconds=86400; break; case WEEK: lifeInSeconds=604800; break; case MONTH: lifeInSeconds=2592000; break; // One year default: lifeInSeconds=31536000; break; } m_brain=new CBrain(TimeLocal(),TimeLocal()+lifeInSeconds,lot_size,stop_loss,take_profit); m_evolution=new CEvolution(DO_NOTHING); m_graphic=new CGraphic(_Symbol); m_trade=new CTrade(); } //+------------------------------------------------------------------+ //| Destructor | //+------------------------------------------------------------------+ CSimpleRandom::~CSimpleRandom(void) { delete(m_brain); delete(m_evolution); delete(m_graphic); delete(m_trade); } //+------------------------------------------------------------------+ //| GetBrain | //+------------------------------------------------------------------+ CBrain *CSimpleRandom::GetBrain(void) { return m_brain; } //+------------------------------------------------------------------+ //| GetBrain | //+------------------------------------------------------------------+ CEvolution *CSimpleRandom::GetEvolution(void) { return m_evolution; } //+------------------------------------------------------------------+ //| GetGraphic | //+------------------------------------------------------------------+ CGraphic *CSimpleRandom::GetGraphic(void) { return m_graphic; } //+------------------------------------------------------------------+ //| GetTrade | //+------------------------------------------------------------------+ CTrade *CSimpleRandom::GetTrade(void) { return m_trade; } //+------------------------------------------------------------------+ //| GetPositionInfo | //+------------------------------------------------------------------+ CPositionInfo *CSimpleRandom::GetPositionInfo(void) { return m_positionInfo; } //+------------------------------------------------------------------+ //| CSimpleRandom initialization | //+------------------------------------------------------------------+ bool CSimpleRandom::Init(void) { // Initialization logic here... return true; } //+------------------------------------------------------------------+ //| CSimpleRandom deinitialization | //+------------------------------------------------------------------+ void CSimpleRandom::Deinit(void) { // Deinitialization logic here... delete(m_brain); delete(m_evolution); delete(m_graphic); delete(m_trade); } //+------------------------------------------------------------------+ //| CSimpleRandom Go | //+------------------------------------------------------------------+ bool CSimpleRandom::Go(double ask,double bid) { double tp; double sl; int coin=m_brain.GetRandomNumber(0,1); // Is there any open position? if(!m_positionInfo.Select(_Symbol)) { // If not, we open one if(coin==0) { GetEvolution().SetStatus(BUY); } else { GetEvolution().SetStatus(SELL); } } // If so, let it work the mathematical expectation. else GetEvolution().SetStatus(DO_NOTHING); switch(GetEvolution().GetStatus()) { case BUY: tp = ask + m_brain.GetTakeProfit() * _Point; sl = bid - m_brain.GetStopLoss() * _Point; GetTrade().PositionOpen(_Symbol,ORDER_TYPE_BUY,m_brain.GetSize(),ask,sl,tp); break; case SELL: sl = ask + m_brain.GetStopLoss() * _Point; tp = bid - m_brain.GetTakeProfit() * _Point; GetTrade().PositionOpen(_Symbol,ORDER_TYPE_SELL,m_brain.GetSize(),bid,sl,tp); break; case DO_NOTHING: // Nothing... break; } // If there is some error we return false, for now we always return true return(true); } //+------------------------------------------------------------------+
2.3. Liaison de CSimpleRandom à des objets de type complexe
Notez comment les objets personnalisés de type CBrain, CEvolution et CGraphic sont liés à CSimpleRandom.
Nous définissons d'abord les propriétés protégées correspondantes :
protected:
CBrain *m_brain;
CEvolution *m_evolution;
CGraphic *m_graphic;
Et juste après avoir instancié ces objets dans le constructeur :
m_brain=new CBrain(TimeLocal(), TimeLocal() + lifeInSeconds, lot_size, stop_loss, take_profit); m_evolution=new CEvolution(DO_NOTHING); m_graphic=new CGraphic(_Symbol);
Ce que nous faisons, c'est créer dynamiquement des objets de type complexe, comme l'expliquent les documents officiels dans Object Pointers. Avec ce schéma, nous pouvons accéder aux fonctionnalités de CBrain, CEvolution et CGraphic directement depuis CSimpleRandom. Nous pourrions exécuter par exemple le code suivant dans ExpertSimpleRandom.mq5 :
//+------------------------------------------------------------------+ //| OnTick event function | //+------------------------------------------------------------------+ void OnTick() { // ... int randNumber=SR.GetBrain().GetRandomNumber(4, 8); // ... }
J'écris maintenant le code de CBrain, CEvolution et CGraphic pour conclure cette section. Veuillez noter qu'il y a certaines parties qui ne sont pas codées car elles ne sont pas strictement nécessaires pour backtester SimpleRandom. Il vous est laissé en exercice de coder les parties manquantes de ces cours, n'hésitez pas à les développer comme vous le souhaitez. Par exemple, m_death n'est pas réellement utilisé, bien que l'idée sous-jacente soit de savoir dès le début la date à laquelle le robot finira son activité.
//+------------------------------------------------------------------+ //| ExpertSimpleRandom | //| Copyright © 2013, Jordi Bassagaсas | //+------------------------------------------------------------------+ //+------------------------------------------------------------------+ //| CBrain Class | //+------------------------------------------------------------------+ class CBrain { protected: ENUM_TIMEFRAMES m_period; // period must always be initialized to PERIOD_M1 to fit the system's idea datetime m_birth; // The datetime in which the robot is initialized for the first time datetime m_death; // The datetime in which the robot will die double m_size; // The size of the positions int m_stopLoss; // Stop loss int m_takeProfit; // Take profit public: //--- Constructor and destructor methods CBrain(datetime birth,datetime death,double size,int stopLoss,int takeProfit); ~CBrain(void); //--- Getter methods datetime GetBirth(void); datetime GetDeath(void); double GetSize(void); int GetStopLoss(void); int GetTakeProfit(void); //--- Setter methods void SetBirth(datetime birth); void SetDeath(datetime death); void SetSize(double size); void SetStopLoss(int stopLoss); void SetTakeProfit(int takeProfit); //--- Brain specific logic int GetRandomNumber(int a,int b); }; //+------------------------------------------------------------------+ //| Constructor | //+------------------------------------------------------------------+ CBrain::CBrain(datetime birth,datetime death,double size,int stopLoss,int takeProfit) { MathSrand(GetTickCount()); m_period=PERIOD_M1; m_birth=birth; m_death=death; m_size=size; m_stopLoss=stopLoss; m_takeProfit=takeProfit; } //+------------------------------------------------------------------+ //| Destructor | //+------------------------------------------------------------------+ CBrain::~CBrain(void) { } //+------------------------------------------------------------------+ //| GetBirth | //+------------------------------------------------------------------+ datetime CBrain::GetBirth(void) { return m_birth; } //+------------------------------------------------------------------+ //| GetDeath | //+------------------------------------------------------------------+ datetime CBrain::GetDeath(void) { return m_death; } //+------------------------------------------------------------------+ //| GetSize | //+------------------------------------------------------------------+ double CBrain::GetSize(void) { return m_size; } //+------------------------------------------------------------------+ //| GetStopLoss | //+------------------------------------------------------------------+ int CBrain::GetStopLoss(void) { return m_stopLoss; } //+------------------------------------------------------------------+ //| GetTakeProfit | //+------------------------------------------------------------------+ int CBrain::GetTakeProfit(void) { return m_takeProfit; } //+------------------------------------------------------------------+ //| SetBirth | //+------------------------------------------------------------------+ void CBrain::SetBirth(datetime birth) { m_birth=birth; } //+------------------------------------------------------------------+ //| SetDeath | //+------------------------------------------------------------------+ void CBrain::SetDeath(datetime death) { m_death=death; } //+------------------------------------------------------------------+ //| SetSize | //+------------------------------------------------------------------+ void CBrain::SetSize(double size) { m_size=size; } //+------------------------------------------------------------------+ //| SetStopLoss | //+------------------------------------------------------------------+ void CBrain::SetStopLoss(int stopLoss) { m_stopLoss=stopLoss; } //+------------------------------------------------------------------+ //| SetTakeProfit | //+------------------------------------------------------------------+ void CBrain::SetTakeProfit(int takeProfit) { m_takeProfit=takeProfit; } //+------------------------------------------------------------------+ //| GetRandomNumber | //+------------------------------------------------------------------+ int CBrain::GetRandomNumber(int a,int b) { return(a+(MathRand()%(b-a+1))); } //+------------------------------------------------------------------+
//+------------------------------------------------------------------+ //| ExpertSimpleRandom | //| Copyright © 2013, Jordi Bassagaсas | //+------------------------------------------------------------------+ #include <Indicators\Indicators.mqh> #include <Mine\Enums.mqh> //+------------------------------------------------------------------+ //| CEvolution Class | //+------------------------------------------------------------------+ class CEvolution { protected: ENUM_STATUS_EA m_status; // The current EA's status CArrayObj* m_operations; // History of the operations performed by the EA public: //--- Constructor and destructor methods CEvolution(ENUM_STATUS_EA status); ~CEvolution(void); //--- Getter methods ENUM_STATUS_EA GetStatus(void); CArrayObj *GetOperations(void); //--- Setter methods void SetStatus(ENUM_STATUS_EA status); void SetOperation(CObject *operation); }; //+------------------------------------------------------------------+ //| Constructor | //+------------------------------------------------------------------+ CEvolution::CEvolution(ENUM_STATUS_EA status) { m_status=status; m_operations=new CArrayObj; } //+------------------------------------------------------------------+ //| Destructor | //+------------------------------------------------------------------+ CEvolution::~CEvolution(void) { delete(m_operations); } //+------------------------------------------------------------------+ //| GetStatus | //+------------------------------------------------------------------+ ENUM_STATUS_EA CEvolution::GetStatus(void) { return m_status; } //+------------------------------------------------------------------+ //| GetOperations | //+------------------------------------------------------------------+ CArrayObj *CEvolution::GetOperations(void) { return m_operations; } //+------------------------------------------------------------------+ //| SetStatus | //+------------------------------------------------------------------+ void CEvolution::SetStatus(ENUM_STATUS_EA status) { m_status=status; } //+------------------------------------------------------------------+ //| SetOperation | //+------------------------------------------------------------------+ void CEvolution::SetOperation(CObject *operation) { m_operations.Add(operation); } //+------------------------------------------------------------------+
//+------------------------------------------------------------------+ //| ExpertSimpleRandom.mq5 | //| Copyright © 2013, Jordi Bassagaсas | //+------------------------------------------------------------------+ #include <Trade\SymbolInfo.mqh> #include <Arrays\ArrayObj.mqh> //+------------------------------------------------------------------+ //| CGrapic Class | //+------------------------------------------------------------------+ class CGraphic { protected: ENUM_TIMEFRAMES m_period; // Graphic's timeframe string m_pair; // Graphic's pair CSymbolInfo* m_symbol; // CSymbolInfo object CArrayObj* m_bars; // Array of bars public: //--- Constructor and destructor methods CGraphic(string pair); ~CGraphic(void); //--- Getter methods string GetPair(void); CSymbolInfo *GetSymbol(void); CArrayObj *GetBars(void); //--- Setter methods void SetPair(string pair); void SetSymbol(CSymbolInfo *symbol); void SetBar(CObject *bar); }; //+------------------------------------------------------------------+ //| Constuctor | //+------------------------------------------------------------------+ CGraphic::CGraphic(string pair) { m_period=PERIOD_M1; m_pair=pair; } //+------------------------------------------------------------------+ //| Destructor | //+------------------------------------------------------------------+ CGraphic::~CGraphic(void) { } //+------------------------------------------------------------------+ //| GetPair | //+------------------------------------------------------------------+ string CGraphic::GetPair(void) { return m_pair; } //+------------------------------------------------------------------+ //| GetSymbol | //+------------------------------------------------------------------+ CSymbolInfo *CGraphic::GetSymbol(void) { return m_symbol; } //+------------------------------------------------------------------+ //| GetBars | //+------------------------------------------------------------------+ CArrayObj *CGraphic::GetBars(void) { return m_bars; } //+------------------------------------------------------------------+ //| SetPair | //+------------------------------------------------------------------+ void CGraphic::SetPair(string pair) { m_pair=pair; } //+------------------------------------------------------------------+ //| SetSymbol | //+------------------------------------------------------------------+ void CGraphic::SetSymbol(CSymbolInfo *symbol) { m_symbol=symbol; } //+------------------------------------------------------------------+ //| SetBar | //+------------------------------------------------------------------+ void CGraphic::SetBar(CObject *bar) { m_bars.Add(bar); } //+------------------------------------------------------------------+
3. Backtesting ExpertSimpleRandom.mq5
Ce système de trading aléatoire s'est avéré valable uniquement pour certains niveaux de stop loss et take profit, comme prévu. Bien entendu, ces intervalles SL/TP gagnants ne sont pas les mêmes pour tous les symboles. En effet, chaque symbole montre sa propre personnalité à un moment donné, ou en d'autres termes, toutes les paires de devises évoluent différemment par rapport aux autres. Veuillez donc identifier d'abord ces niveaux dans le backtesting avant d'exécuter ExpertSimpleRandom.mq5 dans un environnement réel.
Je partage ici quelques exemples de données pour lesquelles l'idée exposée dans cet article semble être gagnante. Cela peut être déduit après avoir exécuté plusieurs fois ExpertSimpleRandom.mq5 dans MetaTrader 5 Strategy Tester.
Certaines entrées gagnantes pour EURUSD, janvier 2012, sont :
- StopLoss : 400
- TakeProfit : 600
- LotSize : 0.01
- TimeLife : MOIS
Exécution numéro 1 :
Exécution numéro 2 :
Exécution numéro 3 :
Conclusion
Nous avons appris à appliquer la programmation orientée objet dans nos systèmes de trading automatisés. Pour ce faire, nous avons d'abord dû définir une stratégie de trading mécanique. Notre idée de trading a été très simple : "Les tendances courtes des marchés volatils sont presque aléatoires". Cela a été observé par plusieurs experts dans certaines circonstances.
Juste après avoir pensé à notre EA en termes réels comme s'il s'agissait d'une créature vivante. Grâce à cette vision, nous avons vu que notre machine Forex peut être composée de trois parties principales : un cerveau, quelque chose que nous avons appelé évolution et un graphique.
Et enfin, nous avons programmé l'expert advisor du système qui intègre la logique nécessaire pour exécuter le backtest, avons exécuté plusieurs fois le robot en janvier 2012, et nous avons constaté que la plupart du temps, le système est gagnant. L'idée derrière ce système s'est avérée vraie mais son efficacité n'est pas très élevée en raison de sa simplicité.