
Création d'un Expert Advisor multi-devises multi-systèmes
Introduction
Je crois qu'il y a pas mal de traders qui tradent plus d'un symbole de trading et utilisent plusieurs stratégies. Cette approche vous permet non seulement d'augmenter potentiellement votre bénéfice, mais également de minimiser le risque de prélèvement substantiel sur une gestion efficace de l'argent. Lors de la création d'un Expert Advisor, la première étape naturelle pour vérifier l'efficacité de la stratégie du programme est l'optimisation afin de déterminer les meilleurs paramètres d'entrée.
Une fois les valeurs des paramètres identifiées, les Expert Advisors seraient techniquement prêts pour le trading. Cependant, cela laisserait une question importante sans réponse. À quoi ressembleraient les résultats des tests si un trader pouvait regrouper toutes ses stratégies dans un seul Expert Advisor ? La prise de conscience que le retrait sur plusieurs symboles ou stratégies peut à un moment donné se chevaucher et donner lieu à un retrait total épouvantable, voire à un appel de marge, peut parfois constituer une mauvaise surprise.
Cet article présente un concept de création d'un Expert Advisor multi-devises multi-systèmes qui nous permettra de trouver une réponse à cette question importante.
1. Structure du conseiller expert
De manière générale, la structure de l'Expert Advisor est la suivante :
Fig. 1. Structure de l'Expert Advisor multi-devises multi-systèmes
Comme vous pouvez le voir, le programme est basé sur une boucle for. Chaque stratégie est organisée en boucle où chaque itération est responsable du trading de chaque symbole séparément. Ici, vous pouvez organiser en boucles un nombre illimité de stratégies. Il est important que votre ordinateur dispose de suffisamment de ressources pour "traiter" un tel programme.
Vous devez garder à l'esprit qu'il ne peut y avoir qu'une seule position pour chaque symbole tradé dans MetaTrader 5. Cette position représente la somme des lots d'achats et de ventes précédemment exécutés. Par conséquent, le résultat des tests multi-stratégies pour un symbole ne sera pas identique à la somme des résultats de tests séparés des mêmes stratégies pour le même symbole.
Pour examiner de plus près la structure de l'Expert Advisor, nous allons prendre 2 stratégies, chacune tradant deux symboles :
Stratégie A :
- Achat : Le prix demandé atteint la bande inférieure de l'indicateur des bandes de Bollinger calculé sur la base du prix bas.
Fermeture : Le prix de l'offre atteint la bande inférieure de l'indicateur des bandes de Bollinger calculé sur la base du prix élevé. - Vente : Le prix de l'offre atteint la bande supérieure de l'indicateur des bandes de Bollinger calculé sur la base du prix élevé.
Fermeture : Le prix demandé atteint la bande supérieure de l'indicateur des bandes de Bollinger calculé sur la base du prix bas. - Restriction : une seule transaction peut être exécutée sur une barre donnée.
Stratégie B :
- Achat : la barre précédente est baissière (fermeture < ouverture) et le prix demandé atteint le plus haut de la barre précédente.
Fermeture : par Stop Loss ou Take Profit. - Vente : la barre précédente est haussière (fermeture > ouverture) et le prix de l'offre atteint le plus bas de la barre précédente.
Fermeture : par Stop Loss ou Take Profit. - Restriction : une seule transaction peut être exécutée sur une barre donnée.
Pour être indépendant des nouveaux ticks d'un symbole sur lequel l'Expert Advisor sera testé ou sur lequel il va trader, il est conseillé d'utiliser la fonction OnTimer() pour trader en mode multi-devises.
À cette fin, lors de l'initialisation de l'Expert Advisor, nous spécifions la fréquence de génération d'un événement pour l'appel de calcul de programme à l'aide de la fonction EventSetTimer(), et lors de la désinitialisation, nous utilisons la fonction EventKillTimer() pour dire au terminal d'arrêter la génération d'événements :
// Include standard libraries // Create external parameters // Create arrays, variables, indicator handles, etc. //--- Initialization of the Expert Advisor int OnInit() { //--- Set event generation frequency EventSetTimer(1); // 1 second // ... return(0); } void OnTimer() { // ... } //--- Deinitialization of the Expert Advisor void OnDeinit(const int reason) { //--- Stop event generation EventKillTimer(); // ... }
Au lieu de EventSetTimer(), vous pouvez également utiliser EventSetMillisecondTimer(), où la fréquence est définie avec précision à la milliseconde, mais vous ne devez pas en abuser par des appels de calcul de programme trop fréquents.
Pour accéder aux paramètres de compte, de position et de symbole, ainsi qu'aux fonctions de trading, nous utiliserons respectivement les classes CAccountInfo, CPositionInfo, CSymbolInfo et CTrade. Incluons-les dans l'Expert Advisor :
//--- Include standard libraries #include <Trade\AccountInfo.mqh> #include <Trade\PositionInfo.mqh> #include <Trade\SymbolInfo.mqh> #include <Trade\Trade.mqh>
Étant donné que l'Expert Advisor est basé sur des boucles for, nous devrons créer des tableaux pour ses paramètres externes. Créons d'abord des constantes égales au nombre de symboles pour chaque stratégie :
//--- Number of traded symbols for each strategy #define Strategy_A 2 #define Strategy_B 2
Nous créons ensuite des paramètres externes. À l'aide de constantes, nous déterminons les tailles des tableaux dans lesquels ils seront copiés. De plus, nous créons des poignées d'indicateur et d'autres variables globales.
Un exemple pour un symbole de stratégie est fourni ci-dessous :
//------------------- External parameters of strategy A input string Data_for_Strategy_A="Strategy A -----------------------"; //--- Symbol 0 input string Symbol_A0 = "EURUSD"; // Symbol input bool IsTrade_A0 = true; // Permission for trading //--- Bollinger Bands (BB) parameters input ENUM_TIMEFRAMES Period_A0 = PERIOD_H1; // ВВ period input uint BBPeriod_A0 = 20; // Period for calculation of the moving average of BB input int BBShift_A0 = 0; // Horizontal shift of ВВ input double BBDeviation_A0 = 2.0; // Number of standard deviations of BB //... //--- General parameters of strategy A input double DealOfFreeMargin_A = 1.0; // Percent of free margin for a deal input uint MagicNumber_A = 555; // Magic number input uint Slippage_A = 100; // Permissible slippage for a deal //... //------------- Set variables of strategy A ----- //--- Arrays for external parameters string Symbol_A[Strategy_A]; bool IsTrade_A[Strategy_A]; ENUM_TIMEFRAMES Period_A[Strategy_A]; int BBPeriod_A[Strategy_A]; int BBShift_A[Strategy_A]; double BBDeviation_A[Strategy_A]; //--- Arrays for global variables double MinLot_A[Strategy_A],MaxLot_A[Strategy_A]; double Point_A[Strategy_A],ContractSize_A[Strategy_A]; uint DealNumber_A[Strategy_A]; datetime Locked_bar_time_A[Strategy_A],time_arr_A[]; //--- Indicator handles int BB_handle_high_A[Strategy_A]; int BB_handle_low_A[Strategy_A]; //--- Arrays for indicator values double BB_upper_band_high[],BB_lower_band_high[]; double BB_upper_band_low[],BB_lower_band_low[]; //--- Class CTrade Trade_A; //... //--- Set global variables for all strategies long Leverage; //--- Classes CAccountInfo AccountInfo; CPositionInfo PositionInfo; CSymbolInfo SymbolInfo;
Pour avoir la possibilité de désactiver le trading pour un certain symbole, nous avons créé une variable booléenne IsTrade_A0 qui sera placée au tout début des boucles for.
2. Initialisation de l'Expert Advisor
Tout d'abord, obtenons les valeurs requises pour toutes les stratégies, par exemple l'effet de levier. Étant donné que l'effet de levier est appliqué au compte de trading et n'a rien à voir avec une stratégie ou un symbole, il n'est pas nécessaire de copier sa valeur dans les tableaux :
//--- Get the leverage for the account
Leverage=AccountInfo.Leverage();
Nous copions ensuite les variables externes dans des tableaux.
//--- Copy external variables to arrays Symbol_A[0] =Symbol_A0; IsTrade_A[0] =IsTrade_A0; Period_A[0] =Period_A0; BBPeriod_A[0] =(int)BBPeriod_A0; BBShift_A[0] =BBShift_A0; BBDeviation_A[0]=BBDeviation_A0;
Si un paramètre externe est défini par le type qui nécessitera une conversion en un autre, cela peut être fait de manière plus pratique lors de la copie dans des tableaux.
Dans ce cas, nous pouvons voir que BBPeriod_A0 a été créé en tant que uint pour empêcher l'utilisateur de définir une valeur négative. Ici, nous le convertissons en int et le copions dans le tableau qui a également été créé en tant que int. Sinon, le compilateur donnera un avertissement si vous essayez d'insérer un paramètre de type uint dans la poignée de l'indicateur.
Voyons ensuite si le symbole tradé est disponible dans le Market Watch et s'il a été utilisé plus d'une fois au sein d'une même stratégie :
//--- Check for the symbol in the Market Watch for(int i=0; i<Strategy_A; i++) { if(IsTrade_A[i]==false) continue; if(IsSymbolInMarketWatch(Symbol_A[i])==false) { Print(Symbol_A[i]," could not be found on the server!"); ExpertRemove(); } } //--- Check whether the symbol is used more than once if(Strategy_A>1) { for(int i=0; i<Strategy_A-1; i++) { if(IsTrade_A[i]==false) continue; for(int j=i+1; j<Strategy_A; j++) { if(IsTrade_A[j]==false) continue; if(Symbol_A[i]==Symbol_A[j]) { Print(Symbol_A[i]," is used more than once!"); ExpertRemove(); } } } } //--- The IsSymbolInMarketWatch() function bool IsSymbolInMarketWatch(string f_Symbol) { for(int s=0; s<SymbolsTotal(false); s++) { if(f_Symbol==SymbolName(s,false)) return(true); } return(false); }
Si les symboles ont été sélectionnés correctement, vérifiez les erreurs dans les paramètres d'entrée pour chacun d'eux, créez des poignées d'indicateur, obtenez les données nécessaires au calcul du lot et, si nécessaire, faites d'autres choses telles que définies par la stratégie donnée.
Nous allons implémenter les actions mentionnées ci-dessus dans une boucle for.
//--- General actions for(int i=0; i<Strategy_A; i++) { if(IsTrade_A[i]==false) continue; //--- Check for errors in input parameters //... //--- Set indicator handles BB_handle_high_A[i]=iBands(Symbol_A[i],Period_A[i],BBPeriod_A[i],BBShift_A[i],BBDeviation_A[i], PRICE_HIGH); if(BB_handle_high_A[i]<0) { Print("Failed to create a handle for Bollinger Bands based on High prices for ",Symbol_A[i]," . Handle=",INVALID_HANDLE, "\n Error=",GetLastError()); ExpertRemove(); } //... //--- Calculate data for the Lot //--- set the name of the symbol for which the information will be obtained SymbolInfo.Name(Symbol_A[i]); //--- minimum and maximum volume size in trading operations MinLot_A[i]=SymbolInfo.LotsMin(); MaxLot_A[i]=SymbolInfo.LotsMax(); //--- point value Point_A[i]=SymbolInfo.Point(); //--- contract size ContractSize_A[i]=SymbolInfo.ContractSize(); //--- Set some additional parameters }
Ensuite, nous définissons les paramètres des opérations de trading de la stratégie A à l'aide de l'objet Trade_A de la classe CTrade.
//--- Set parameters for trading operations //--- set the magic number Trade_A.SetExpertMagicNumber(MagicNumber_A); //--- set the permissible slippage in points upon deal execution Trade_A.SetDeviationInPoints(Slippage_A); //--- order filling mode, use the mode that is allowed by the server Trade_A.SetTypeFilling(ORDER_FILLING_RETURN); //--- logging mode, it is advisable not to call this method as the class will set the optimal mode by itself Trade_A.LogLevel(1); //--- the function to be used for trading: true - OrderSendAsync(), false - OrderSend(). Trade_A.SetAsyncMode(true);
La même procédure est répétée pour chaque stratégie, c'est-à-dire
- Copier les variables externes dans les tableaux ;
- Vérifier si les symboles sont correctement sélectionnés ;
- Vérifier les erreurs, définir les poignées d'indicateur, calculer les données pour le lot et pour tout ce qui est nécessaire pour une stratégie donnée ;
- Définir des paramètres pour les opérations de trading.
Enfin, il serait bon de vérifier si un même symbole est utilisé dans plusieurs stratégies (un exemple pour deux stratégies est fourni ci-dessous) :
//--- Check whether one and the same symbol is used in several strategies for(int i=0; i<Strategy_A; i++) { if(IsTrade_A[i]==false) continue; for(int j=0; j<Strategy_B; j++) { if(IsTrade_B[j]==false) continue; if(Symbol_A[i]==Symbol_B[j]) { Print(Symbol_A[i]," is used in several strategies!"); ExpertRemove(); } } }
3. Échanger des boucles « For »
Le cadre des boucles for à l'intérieur de la fonction OnTimer() est le suivant :
void OnTimer() { //--- Check if the terminal is connected to the trade server if(TerminalInfoInteger(TERMINAL_CONNECTED)==false) return; //--- Section A: Main loop of the FOR operator for strategy A ----------- for(int A=0; A<Strategy_A; A++) { //--- A.1: Check whether the symbol is allowed to be traded if(IsTrade_A[A]==false) continue; // terminate the current FOR iteration } //--- Section В: Main loop of the FOR operator for strategy В ----------- for(int B=0; B<Strategy_B; B++) { //--- B.1: Check whether the symbol is allowed to be traded if(IsTrade_B[B]==false) continue; // terminate the current FOR iteration } }
Si un Expert Advisor à symbole unique basé sur une stratégie unique a une condition selon laquelle tous les calculs ultérieurs doivent être arrêtés, nous utilisons l'opérateur return. Dans notre cas, il suffit de terminer l'itération en cours et de passer à l'itération de symbole suivante. Pour cela, il est préférable d'utiliser l'opérateur continue.
Si vous souhaitez améliorer votre Expert Advisor multi-stratégies en ajoutant une stratégie avec une boucle for qui contient une condition d'arrêt de tous les calculs suivants, vous pouvez utiliser le modèle suivant :
//--- Section N: Main loop of the FOR operator for strategy N ----------- for(int N=0; N<Strategy_N; N++) { //... bool IsInterrupt=false; for(int i=0; i<Number; i++) { if(...) // terminate all calculations { IsInterrupt=true; break; } } if(IsInterrupt=true) continue; // terminate the current FOR iteration //... }
Après avoir créé le cadre des boucles for, nous y insérons simplement les codes d'autres EA, puis nous remplaçons certaines variables par des éléments de tableau.
Par exemple, nous changeons la variable prédéfinie _Symbol en Symbol_A[i] ou _Point en Point_A[i]. Les valeurs de ces variables sont typiques du symbole donné et ont donc été copiées dans des tableaux lors de l'initialisation.
Par exemple, trouvons la valeur de l'indicateur :
//--- A.3: Lower band of BB calculated based on High prices if(CopyBuffer(BB_handle_high_A[A],LOWER_BAND,BBShift_A[A],1,BB_lower_band_high)<=0) continue; // terminate the current FOR iteration ArraySetAsSeries(BB_lower_band_high,true);
Pour implémenter la clôture d'une position d'achat, nous écrirons le code suivant :
//--- A.7.1: Calculate the current Ask and Bid prices SymbolInfo.Name(Symbol_A[A]); SymbolInfo.RefreshRates(); double Ask_price=SymbolInfo.Ask(); double Bid_price=SymbolInfo.Bid(); if(PositionSelect(Symbol_A[A])) { //--- A.7.2: Closing a BUY position if(PositionInfo.PositionType()==POSITION_TYPE_BUY) { if(Bid_price>=BB_lower_band_high[0] || DealNumber_A[A]==0) { if(!Trade_A.PositionClose(Symbol_A[A])) { Print("Failed to close the Buy ",Symbol_A[A]," position. Code=",Trade_A.ResultRetcode(), " (",Trade_A.ResultRetcodeDescription(),")"); continue; // terminate the current FOR iteration } else { Print("The Buy ",Symbol_A[A]," position closed successfully. Code=",Trade_A.ResultRetcode(), " (",Trade_A.ResultRetcodeDescription(),")"); continue; // terminate the current FOR iteration } } } //... }
Ouvrir une position d'achat :
//--- A.9.1: for a Buy if(Ask_price<=BB_lower_band_low[0]) { //... //--- A.9.1.3: Execute a deal if(!Trade_A.Buy(OrderLot,Symbol_A[A])) { Print("The Buy ",Symbol_A[A]," has been unsuccessful. Code=",Trade_A.ResultRetcode(), " (",Trade_A.ResultRetcodeDescription(),")"); continue; // terminate the current FOR iteration } else { Print("The Buy ",Symbol_A[A]," has been successful. Code=",Trade_A.ResultRetcode(), " (",Trade_A.ResultRetcodeDescription(),")"); continue; // terminate the current FOR iteration } }
N'oubliez pas de mettre fin à la génération d'événements de minuterie et de supprimer les poignées d'indicateur lors de la désinitialisation.
4. Résultats de test
Lorsque l'Expert Advisor est prêt, nous testons chaque stratégie et chaque symbole séparément et comparons les résultats du test avec ceux obtenus en mode test lors du trading simultané de toutes les stratégies et symboles.
On suppose que l'utilisateur a déjà identifié les valeurs optimales des paramètres d'entrée.
Vous trouverez ci-dessous les paramètres du Strategy Tester :
Fig. 2. Paramètres du Strategy Tester
Résultats pour la stratégie A, EURUSD :
Fig. 3. Résultats du test pour la stratégie A, EURUSD
Résultats pour la stratégie A, GBPUSD :
Fig. 4. Résultats du test pour la stratégie A, GBPUSD
Résultats pour la stratégie B, AUDUSD :
Fig. 5. Résultats du test pour la stratégie В, AUDUSD
Résultats pour la stratégie B, EURJPY :
Fig. 6. Résultats du test pour la stratégie В, EURJPY
Résultats du test pour toutes les stratégies et tous les symboles :
Fig. 7. Résultats du test pour toutes les stratégies et tous les symboles
Conclusion
En conséquence, nous avons une structure pratique et simple de l'Expert Advisor multi-devises et multi-systèmes dans lequel vous pouvez placer pratiquement n'importe laquelle de vos stratégies.
Un tel Expert Advisor vous permet de mieux évaluer l'efficacité du trading en utilisant toutes vos stratégies. Cela peut également s'avérer utile dans le cas où un seul Expert Advisor est autorisé à travailler sur un compte donné. Le code source de l'Expert Advisor est joint à l'article pour faciliter l'étude des informations ci-dessus.
Traduit du russe par MetaQuotes Ltd.
Article original : https://www.mql5.com/ru/articles/770





- 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