
Création d’un Expert Advisor, qui trade sur un certain nombre d’instruments
Introduction
L’aspect technique de l’implémentation du code du programme afin qu’un seul Expert Advisor, lancé sur un seul graphique, puisse trader avec différents actifs financiers en même temps. En général, ce n’était pas un problème, même dans MQL4. Mais ce n’est qu’avec l’avènement du terminal client MetaTrader 5 que les traders ont finalement eu la possibilité d’effectuer une analyse complète du travail de tels automates, en utilisant des testeurs de stratégie.
Donc, maintenant, les automates multi-devises deviendront plus populaires que jamais, et nous pouvons prévoir un regain d’intérêt pour l’élaboration de tels systèmes de trading. Mais le principal problème de l’implémentation de tels robots réside dans le fait que leurs dimensions dans le code du programme s’étendent, au mieux, dans une progression arithmétique, ce qui n’est pas facile à accepter pour un programmeur typique.
Dans cet article, nous allons écrire un Expert Advisor multi-devises simple, dans lequel les défauts de structure, s’ils ne sont pas absents, du moins sont minimisés.
1. Implémentation d’un système simple de suivi des tendances
En fait, nous pourrions commencer avec un système de trading extrêmement simple, en suivant la tendance sur la base d’un terminal intégré d’un indicateur technique Triple Moyenne Mobile Exponentielle. Il s’agit d’un algorithme très simple, qui ne nécessite pas de commentaires spéciaux, et que nous allons maintenant incarner dans le code du programme.
Mais avant tout, je voudrais tirer les conclusions les plus générales sur l’Expert Advisor. Il est normal de commencer par le bloc de paramètres Expert Advisor entrants, déclarés au niveau mondial.
Donc, tout d’abord, nous devons choisir les actifs financiers avec lesquels nous allons travailler. Cela peut être fait en utilisant des variables d’entrée de ligne, dans lesquels les symboles de la ressource peuvent être stockés. Maintenant, il serait bien d’avoir un commutateur d’interdiction de trade pour chaque actif financier, ce qui permettrait de désactiver les opérations de trading par l’actif.
Évidemment , chaque actif doit être associé à ses paramètres de trading individuels de Excédents de Perte ,Prise de Bénéfice , le volume de la position ouverte et glissement. Et pour des raisons évidentes, les paramètres d’entrée de l’indicateur Triple Moyenne Mobile Exponentielle pour chaque puce de trading doivent être individuels.
Voici un dernier bloc de variables d’entrée pour une seule puce, exécuté conformément à ces arguments. Les blocs restants ne diffèrent que par les nombres dans les noms des paramètres d’entrée de l’Expert Advisor. Pour cet exemple, je me suis limité à seulement douze actifs financiers, bien qu’idéalement il n’y ait pas de limitations logicielles pour le nombre de tels blocs.
Nous avons seulement besoin de quelque chose pour trader! Et le plus important - notre PC doit disposer de suffisamment de ressources pour résoudre ce problème.
input string Symb0 = "EURUSD"; input bool Trade0 = true; input int Per0 = 15; input ENUM_APPLIED_PRICE ApPrice0 = PRICE_CLOSE; input int StLoss0 = 1000; input int TkProfit0 = 2000; input double Lots0 = 0.1; input int Slippage0 = 30;
Maintenant que nous avons compris les variables au niveau mondial, nous pouvons procéder à l’élaboration du code dans la fonction OnTick(). L’option la plus rationnelle ici serait la division de l’algorithme de réception des signaux de trading et la partie trading réelle de l’Expert Advisor en deux fonctions personnalisées.
Et puisque l’Expert Advisor fonctionne avec douze actifs financiers en même temps, il doit également y avoir douze appels de ces fonctions dans le bloc OnTick().
Évidemment, le premier paramètre d’entrée de ces fonctions devrait être un numéro unique, sous lequel ces actifs de trading seront répertoriés. Pour des raisons évidentes,le deuxième paramètre d’entrée sera le nom de ligne de l’actif financier de trading.
Pour le rôle du troisième paramètre, nous allons définir une variable logique pour résoudre le trade. Ensuite, pour l’algorithme de détermination des signaux de trading, suivez les signaux indicateurs d’entrée, et pour une fonction de trading - la distance aux ordres en attente, le volume de position et le glissement (glissement admissible de la position ouverte du prix).
Afin de transférer les signaux de trading d’une fonction à une autre, les tableaux statiques doivent être définis comme paramètres de la fonction, qui dérivent leurs valeurs par une référence. Il s’agit de la version finale du code proposé pour la fonction OnTick().
void OnTick() { //--- declare variables arrays for trade signals static bool UpSignal[12], DnSignal[12], UpStop[12], DnStop[12]; //--- get trade signals TradeSignalCounter( 0, Symb0, Trade0, Per0, ApPrice0, UpSignal, DnSignal, UpStop, DnStop); TradeSignalCounter( 1, Symb1, Trade1, Per1, ApPrice1, UpSignal, DnSignal, UpStop, DnStop); TradeSignalCounter( 2, Symb2, Trade2, Per2, ApPrice2, UpSignal, DnSignal, UpStop, DnStop); TradeSignalCounter( 3, Symb3, Trade3, Per3, ApPrice3, UpSignal, DnSignal, UpStop, DnStop); TradeSignalCounter( 4, Symb4, Trade4, Per4, ApPrice4, UpSignal, DnSignal, UpStop, DnStop); TradeSignalCounter( 5, Symb5, Trade5, Per5, ApPrice5, UpSignal, DnSignal, UpStop, DnStop); TradeSignalCounter( 6, Symb6, Trade6, Per6, ApPrice6, UpSignal, DnSignal, UpStop, DnStop); TradeSignalCounter( 7, Symb7, Trade7, Per7, ApPrice7, UpSignal, DnSignal, UpStop, DnStop); TradeSignalCounter( 8, Symb8, Trade8, Per8, ApPrice8, UpSignal, DnSignal, UpStop, DnStop); TradeSignalCounter( 9, Symb9, Trade9, Per9, ApPrice9, UpSignal, DnSignal, UpStop, DnStop); TradeSignalCounter(10, Symb10, Trade10, Per10, ApPrice10, UpSignal, DnSignal, UpStop, DnStop); TradeSignalCounter(11, Symb11, Trade11, Per11, ApPrice11, UpSignal, DnSignal, UpStop, DnStop); //--- perform trade operations TradePerformer( 0, Symb0, Trade0, StLoss0, TkProfit0, Lots0, Slippage0, UpSignal, DnSignal, UpStop, DnStop); TradePerformer( 1, Symb1, Trade1, StLoss1, TkProfit1, Lots1, Slippage1, UpSignal, DnSignal, UpStop, DnStop); TradePerformer( 2, Symb2, Trade2, StLoss2, TkProfit2, Lots2, Slippage2, UpSignal, DnSignal, UpStop, DnStop); TradePerformer( 3, Symb3, Trade3, StLoss3, TkProfit3, Lots3, Slippage3, UpSignal, DnSignal, UpStop, DnStop); TradePerformer( 4, Symb4, Trade4, StLoss4, TkProfit4, Lots4, Slippage4, UpSignal, DnSignal, UpStop, DnStop); TradePerformer( 5, Symb5, Trade5, StLoss5, TkProfit5, Lots5, Slippage5, UpSignal, DnSignal, UpStop, DnStop); TradePerformer( 6, Symb6, Trade6, StLoss6, TkProfit6, Lots6, Slippage6, UpSignal, DnSignal, UpStop, DnStop); TradePerformer( 7, Symb7, Trade7, StLoss7, TkProfit7, Lots7, Slippage7, UpSignal, DnSignal, UpStop, DnStop); TradePerformer( 8, Symb8, Trade8, StLoss8, TkProfit8, Lots8, Slippage8, UpSignal, DnSignal, UpStop, DnStop); TradePerformer( 9, Symb9, Trade9, StLoss9, TkProfit9, Lots9, Slippage9, UpSignal, DnSignal, UpStop, DnStop); TradePerformer(10, Symb10, Trade10, StLoss10, TkProfit10, Lots10, Slippage10, UpSignal, DnSignal, UpStop, DnStop); TradePerformer(11, Symb11, Trade11, StLoss11, TkProfit11, Lots11, Slippage11, UpSignal, DnSignal, UpStop, DnStop); //--- }
Dans la fonction TradeSignalCounter(), il suffit d’obtenir la poignée de l’indicateur technique Triple Moyenne Mobile Exponentielle une fois au début de chaque puce, puis à chaque remplacement de barre pour calculer les signaux de trading.
Ce schéma relativement simple avec l’implémentation dans le code commence à déborder de détails mineurs.
bool TradeSignalCounter(int Number, string Symbol_, bool Trade, int period, ENUM_APPLIED_PRICE ApPrice, bool &UpSignal[], bool &DnSignal[], bool &UpStop[], bool &DnStop[]) { //--- check if trade is prohibited if(!Trade)return(true); //--- declare variable to store final size of variables arrays static int Size_=0; //--- declare array to store handles of indicators as static variable static int Handle[]; static int Recount[],MinBars[]; double TEMA[4],dtema1,dtema2; //--- initialization if(Number+1>Size_) // Entering the initialization block only on first start { Size_=Number+1; // For this number entering the block is prohibited //--- change size of variables arrays ArrayResize(Handle,Size_); ArrayResize(Recount,Size_); ArrayResize(MinBars,Size_); //--- determine minimum number of bars, sufficient for calculation MinBars[Number]=3*period; //--- setting array elements to 0 DnSignal[Number] = false; UpSignal[Number] = false; DnStop [Number] = false; UpStop [Number] = false; //--- use array as timeseries ArraySetAsSeries(TEMA,true); //--- get indicator's handle Handle[Number]=iTEMA(Symbol_,0,period,0,ApPrice); } //--- check if number of bars is sufficient for calculation if(Bars(Symbol_,0)<MinBars[Number])return(true); //--- get trade signals if(IsNewBar(Number,Symbol_,0) || Recount[Number]) // Entering the block on bar change or on failed copying of data { DnSignal[Number] = false; UpSignal[Number] = false; DnStop [Number] = false; UpStop [Number] = false; //--- using indicator's handles, copy values of indicator's //--- buffers into static array, specially prepared for this purpose if(CopyBuffer(Handle[Number],0,0,4,TEMA)<0) { Recount[Number]=true; // As data were not received, we should return // into this block (where trade signals are received) on next tick! return(false); // Exiting the TradeSignalCounter() function without receiving trade signals } //--- all copy operations from indicator buffer are successfully completed Recount[Number]=false; // We may not return to this block until next change of bar int Digits_ = int(SymbolInfoInteger(Symbol_,SYMBOL_DIGITS)+4); dtema2 = NormalizeDouble(TEMA[2] - TEMA[3], Digits_); dtema1 = NormalizeDouble(TEMA[1] - TEMA[2], Digits_); //---- determining the input signals if(dtema2 > 0 && dtema1 < 0) DnSignal[Number] = true; if(dtema2 < 0 && dtema1 > 0) UpSignal[Number] = true; //---- determining the output signals if(dtema1 > 0) DnStop[Number] = true; if(dtema1 < 0) UpStop[Number] = true; } //----+ return(true); }
Dans cet aspect, le code de la fonction TradePerformer() s’avère être assez simple:
bool TradePerformer(int Number, string Symbol_, bool Trade, int StLoss, int TkProfit, double Lots, int Slippage, bool &UpSignal[], bool &DnSignal[], bool &UpStop[], bool &DnStop[]) { //--- check if trade is prohibited if(!Trade)return(true); //--- close opened positions if(UpStop[Number])BuyPositionClose(Symbol_,Slippage); if(DnStop[Number])SellPositionClose(Symbol_,Slippage); //--- open new positions if(UpSignal[Number]) if(BuyPositionOpen(Symbol_,Slippage,Lots,StLoss,TkProfit)) UpSignal[Number]=false; //This trade signal will be no more on this bar! //--- if(DnSignal[Number]) if(SellPositionOpen(Symbol_,Slippage,Lots,StLoss,TkProfit)) DnSignal[Number]=false; //This trade signal will be no more on this bar! //--- return(true); }Mais c’est seulement parce que les commandes réelles pour la performance des opérations de trading sont emballées dans quatre fonctions supplémentaires:
BuyPositionClose(); SellPositionClose(); BuyPositionOpen(); SellPositionOpen();
Les quatre fonctions opèrent de manière complètement analogue pour que nous puissions nous limiter à l’examen d’une seule d’entre elles:
bool BuyPositionClose(const string symbol,ulong deviation) { //--- declare structures of trade request and result of trade request MqlTradeRequest request; MqlTradeResult result; ZeroMemory(request); ZeroMemory(result); //--- check if there is BUY position if(PositionSelect(symbol)) { if(PositionGetInteger(POSITION_TYPE)!=POSITION_TYPE_BUY) return(false); } else return(false); //--- initializing structure of the MqlTradeRequest to close BUY position request.type = ORDER_TYPE_SELL; request.price = SymbolInfoDouble(symbol, SYMBOL_BID); request.action = TRADE_ACTION_DEAL; request.symbol = symbol; request.volume = PositionGetDouble(POSITION_VOLUME); request.sl = 0.0; request.tp = 0.0; request.deviation=(deviation==ULONG_MAX) ? deviation : deviation; request.type_filling=ORDER_FILLING_FOK; //--- string word=""; StringConcatenate(word, "<<< ============ BuyPositionClose(): Close Buy position at ", symbol," ============ >>>"); Print(word); //--- send order to close position to trade server if(!OrderSend(request,result)) { Print(ResultRetcodeDescription(result.retcode)); return(false); } //----+ return(true); }
Fondamentalement, c’est à peu près tout l’Expert Advisor multi-devises (Exp_TEMA.mq5)!
Outre les fonctions considérées, il contient deux fonctions utilisateur supplémentaires:
bool IsNewBar(int Number, string symbol, ENUM_TIMEFRAMES timeframe); string ResultRetcodeDescription(int retcode);
La première de ces fonctions renvoie la valeur réelle au moment du changement de barre, en fonction du symbole et de la période sélectionnés, et la seconde renvoie la ligne par le code de résultat de la transaction de trading, dérivé du champ retcode de la structure de demande de transaction MqlTradeResult.
L’Expert Advisor est prêt, il est temps d’entamer les tests ! Il n’y a pas de différences majeures visibles dans le test de l’Expert Advisor multi-devises par rapport à son homologue Expert Advisor à monnaie unique.
Déterminez les configurations dans l’onglet " Paramètres " du Testeur de Stratégie :
Figure 1. Onglet " Paramètres " du testeur de stratégie
Si nécessaire, ajustez les valeurs des paramètres d’entrée dans l’onglet " Paramètres d’entrée:
Figure 2. Onglet " Paramètres" du testeur de stratégie
et puis cliquez sur le bouton " Démarrer " dans le testeur de stratégie sous l’onglet " Paramètres »:
Figure 3. Exécution du test Expert Advisor
Le temps qui passe du premier test de l’Expert Advisor peut s’avérer très important, en raison du chargement de l’historique pour les douze symboles. Après accomplissement du test dans le testeur de stratégie, ouvrez l’onglet " Résultats ":
Figure 4. Résultats des tests
et analyser les données, en utilisant le contenu de l’onglet " Graphique ":
Figure 5. Graphique de la dynamique de l’équilibre et des capitaux propres
et le "Journal":
Figure 6. Journal du testeur de stratégie
Tout naturellement, l’essence même des entrées et des sorties de l’algorithme sur le marché de cet Expert Advisor est trop simple, et il serait naïf de s’attendre à des résultats notables, lors de l’utilisation des premiers paramètres aléatoires. Mais notre objectif ici est de démontrer l’idée fondamentale d’élaborer un Expert Advisor multi-devises de la manière la plus simple possible.
Avec l’optimisation de cet Expert Advisor , certains inconvénients peuvent survenir dus à un trop grand nombre de paramètres d’entrée. L’algorithme génétique d’optimisation nécessite une quantité beaucoup plus petite de ces paramètres, de sorte que l’Expert Advisor doit être optimisé sur chaque puce individuellement, en désactivant les puces restantes des paramètres d’entrée TradeN.
Maintenant, lorsque l’essence même de l’approche a été définie, vous pouvez commencer à travailler avec l’algorithme de prise de décision plus intéressant pour le robot multi-devises.
2. Résonances sur les marchés financiers et leur application dans les systèmes de trading
L’idée de prendre en compte les corrélations entre les différents actifs financiers, en général, n’est pas nouvelle, et il serait intéressant d’implémenter l’algorithme, qui serait basé précisément sur l’analyse de telles tendances. Dans cet article, je vais implémenter un automate multi-devises, basé sur l’article de Vasily Yakimkin " Résonances - une Nouvelle Catégorie d’Indicateurs Techniques " publié dans la revue " Currency Speculator " (en russe) 04, 05, 2001.
L’essence même de cette approche en un mot se présente comme suit. Par exemple, pour la recherche sur EUR / USD, nous utilisons non seulement les résultats de certains indicateurs sur l’actif financier, mais également les résultats du même indicateur sur les actifs EUR / USD - EUR / JPY et USD / JPY. Il est préférable d’utiliser l’indicateur, dont les valeurs sont normalisées dans la même fourchette de changements pour la simplicité et la facilité des mesures et des calculs.
Compte tenu de ces exigences, l’indicateur stochastique convient bien à ce classique. Bien que, en réalité, il n’y ait pas de différence dans l’utilisation d’autres indicateurs. En vue de la direction de la tendance, nous considérerons le signe de différence entre la valeur du Stoh stochastique et son signe de ligne de signal .
Figure 7. Détermination de la direction de la tendance
Pour le symbole variable dStoh, il existe un tableau entier des combinaisons possibles et de leurs interprétations pour la direction de la tendance actuelle:
Figure 8. Combinaisons du symbole variable dStoh et de la direction de la tendance
Dans le cas où deux signaux des actifs EUR / JPY et USD / JPY ont des valeurs opposées, nous devons déterminer leur somme, et si cette somme est supérieure à zéro, considérez les deux signaux comme positifs, sinon - comme négatifs.
Ainsi, pour l’ouverture des Longs, utilisez la situation lorsque la tendance est croissante, et pour sortir, utilisez une tendance à la baisse, ou une tendance lorsque les signaux de l’indicateur de l’actif principal EUR / USD sont négatifs. En outre, quittez le long si l’actif principal n’a pas de signaux et si la somme de la variable dStoh pour les actifs restants est inférieure à zéro. Pour les courts-métrages, tout est absolument analogue, seule la situation est adverse.
La solution la plus rationnelle serait de placer toute la partie analytique de l’Expert Advisor dans l’indicateur multi-devises, et pour l’Expert Advisor à partir des tampons d’indicateurs, de ne prendre que les signaux prêts pour le contrôle des trades. La version de ce type d’indicateur est présentée par l’indicateur MultiStochastic.mq5, offrant une analyse visuelle des conditions du marché.
Figure 9. Indicateur Multi-stochastique
La barre verte signale l’ouverture et la rétention des longs, et les barres rouges - des courts, respectivement. Les points roses et vert clair sur le bord supérieur du graphique représentent les signaux de sortie des positions longues et courtes.
Cet indicateur peut être directement utilisé pour recevoir des signaux dans l’Expert Advisor, mais il serait toujours préférable de faciliter son travail et de supprimer tous les tampons et éléments de visualisation inutiles, ne laissant que ce qui est directement impliqué dans l’approvisionnement de signaux de trading. C’est précisément ce qui a été fait dans l’indicateur MultiStochastic_Exp.mq5.
Dans cet Expert Advisor, j’ai tradé avec seulement trois jetons, si bien que le code de la fonction OnTick() est devenue extrêmement simple:
void OnTick() { //--- declare variables arrays for trade signals static bool UpSignal[], DnSignal[], UpStop[], DnStop[]; //--- get trade signals TradeSignalCounter(0, Trade0, Kperiod0, Dperiod0, slowing0, ma_method0, price_0, SymbolA0, SymbolB0, SymbolC0, UpSignal, DnSignal, UpStop, DnStop); TradeSignalCounter(1, Trade1, Kperiod1, Dperiod1, slowing1, ma_method1, price_1, SymbolA1, SymbolB1, SymbolC1, UpSignal, DnSignal, UpStop, DnStop); TradeSignalCounter(2, Trade2, Kperiod2, Dperiod2, slowing2, ma_method2, price_2, SymbolA2, SymbolB2, SymbolC2, UpSignal, DnSignal, UpStop, DnStop); //--- perform trade operations TradePerformer( 0, SymbolA0, Trade0, StopLoss0, 0, Lots0, Slippage0, UpSignal, DnSignal, UpStop, DnStop); TradePerformer( 1, SymbolA1, Trade1, StopLoss1, 0, Lots1, Slippage1, UpSignal, DnSignal, UpStop, DnStop); TradePerformer( 2, SymbolA2, Trade2, StopLoss2, 0, Lots2, Slippage2, UpSignal, DnSignal, UpStop, DnStop); //--- }
Cependant, le code de la fonction TradeSignalCounter() est un peu plus complexe : Le fait est qu’un indicateur multi-devises fonctionne directement avec trois séries chronologiques d’actifs financiers différents, et par conséquent, nous mettons en œuvre une vérification plus subtile des barres pour l’adéquation du nombre minimum d’entre elles dans l’une des trois séries chronologiques, en utilisant la fonction Rates_Total().
En outre, une vérification supplémentaire de la synchronisation des séries chronologiques est effectuée, à l’aide de la fonction SynchroCheck(), afin de garantir la précision de la détermination du moment où un changement de barre se produit simultanément dans toutes les séries chronologiques.
bool TradeSignalCounter(int Number, bool Trade, int Kperiod, int Dperiod, int slowing, ENUM_MA_METHOD ma_method, ENUM_STO_PRICE price_, string SymbolA, string SymbolB, string SymbolC, bool &UpSignal[], bool &DnSignal[], bool &UpStop[], bool &DnStop[]) { //--- check if trade is prohibited if(!Trade)return(true); //--- declare variable to store sizes of variables arrays static int Size_=0; //--- declare arrays to store handles of indicators as static variables static int Handle[]; static int Recount[],MinBars[]; //--- double dUpSignal_[1],dDnSignal_[1],dUpStop_[1],dDnStop_[1]; //--- change size of variables arrays if(Number+1>Size_) { uint size=Number+1; //---- if(ArrayResize(Handle,size)==-1 || ArrayResize(Recount,size)==-1 || ArrayResize(UpSignal, size) == -1 || ArrayResize(DnSignal, size) == -1 || ArrayResize(UpStop, size) == -1 || ArrayResize(DnStop, size) == -1 || ArrayResize(MinBars,size) == -1) { string word=""; StringConcatenate(word,"TradeSignalCounter( ",Number, " ): Error!!! Unable to change sizes of variables arrays!!!"); int error=GetLastError(); ResetLastError(); //--- if(error>4000) { StringConcatenate(word,"TradeSignalCounter( ",Number," ): Error code ",error); Print(word); } Size_=-2; return(false); } Size_=int(size); Recount[Number] = false; MinBars[Number] = Kperiod + Dperiod + slowing; //--- get indicator's handle Handle[Number]=iCustom(SymbolA,0,"MultiStochastic_Exp", Kperiod,Dperiod,slowing,ma_method,price_, SymbolA,SymbolB,SymbolC); } //--- check if number of bars is sufficient for calculation if(Rates_Total(SymbolA,SymbolB,SymbolC)<MinBars[Number])return(true); //--- check timeseries synchronization if(!SynchroCheck(SymbolA,SymbolB,SymbolC))return(true); //--- get trade signals if(IsNewBar(Number,SymbolA,0) || Recount[Number]) { DnSignal[Number] = false; UpSignal[Number] = false; DnStop [Number] = false; UpStop [Number] = false; //--- using indicators' handles, copy values of indicator's //--- buffers into static arrays, specially prepared for this purpose if(CopyBuffer(Handle[Number], 1, 1, 1, dDnSignal_) < 0){Recount[Number] = true; return(false);} if(CopyBuffer(Handle[Number], 2, 1, 1, dUpSignal_) < 0){Recount[Number] = true; return(false);} if(CopyBuffer(Handle[Number], 3, 1, 1, dDnStop_ ) < 0){Recount[Number] = true; return(false);} if(CopyBuffer(Handle[Number], 4, 1, 1, dUpStop_ ) < 0){Recount[Number] = true; return(false);} //--- convert obtained values into values of logic variables of trade commands if(dDnSignal_[0] == 300)DnSignal[Number] = true; if(dUpSignal_[0] == 300)UpSignal[Number] = true; if(dDnStop_ [0] == 300)DnStop [Number] = true; if(dUpStop_ [0] == 300)UpStop [Number] = true; //--- all copy operations from indicator's buffers completed successfully //--- unnecessary to return into this block until next bar change Recount[Number]=false; } //----+ return(true); }
Il n’y a pas d’autres différences idéologiques radicales du code de cet Expert Advisor (Exp_ResonanceHunter.mq5) en raison du fait qu’il est compilé sur la base des mêmes composants fonctionnels. Par conséquent, je ne pense pas qu’il sera nécessaire de passer plus de temps sur sa structure interne.
Conclusion
À mon avis, le code de l’Expert Advisor multi-devises dans MQL5 est absolument similaire au code d’un Expert Advisor régulier.
Traduit du russe par MetaQuotes Ltd.
Article original : https://www.mql5.com/ru/articles/105





- 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