Le prototype du robot de trading
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.
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.
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
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.
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
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.
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
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’indicateurComme 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
- Applications de trading gratuites
- Plus de 8 000 signaux à copier
- Actualités économiques pour explorer les marchés financiers
Vous acceptez la politique du site Web et les conditions d'utilisation