
MQL5 Cookbook : Réduction de l'effet du surajustement et traitement de l'absence de cotations
Introduction
Je pense que de nombreux traders se sont interrogés à plusieurs reprises sur les paramètres optimaux pour leurs systèmes de trading. En effet, un algorithme de trading seul ne suffit pas. Il faut voir comment il peut encore être utilisé. Quelle que soit la stratégie de trading que vous utilisez, qu'elle soit simple ou complexe, avec un ou plusieurs instruments, vous ne pouvez pas éviter la question des paramètres à choisir pour assurer des profits futurs.
Nous avons tendance à vérifier les systèmes de trading avec des paramètres qui ont montré de bons résultats sur la période d'optimisation (backtesting) et sur la période suivante (forward testing ou tests avancés). Les tests avancés ne sont en fait pas si nécessaires. Les résultats pertinents peuvent être obtenus en utilisant des données historiques.
Cette méthode soulève une énorme question à laquelle on ne peut pas répondre avec certitude : quelle quantité de données historiques faut-il utiliser pour optimiser un système de trading ? Le truc, c'est qu'il y a beaucoup d'options. Tout dépend de la gamme de fluctuations de prix sur laquelle vous vous attendez à capitaliser.
Pour en revenir à la quantité d'historique requise pour l'optimisation, nous concluons que les données disponibles pourraient bien être suffisantes pour le trading intra-horaire. Ce n'est pas toujours vrai pour les périodes plus longues. Plus le nombre de répétitions d'un modèle cohérent est élevé, c'est-à-dire plus nous avons des trades, plus les performances du système de trading testé sont véridiques que nous pouvons nous attendre à voir à l'avenir.
Que se passe-t-il si les données de prix d'un certain instrument ne suffisent pas pour obtenir un nombre suffisant de répétitions et se sentir plus sûr ? Réponse : utilisez les données de tous les instruments disponibles.
Exemple dans NeuroShell DayTrader Professional
Avant de passer à la programmation dans MetaTrader 5, examinons un exemple dans NeuroShell DayTrader Professional. Il offre d'excellentes fonctionnalités pour optimiser les paramètres d'un système de trading (compilé avec un constructeur) pour plusieurs symboles. Vous pouvez définir les paramètres requis dans les paramètres du module de trading, optimiser les paramètres pour chaque symbole séparément ou trouver un ensemble de paramètres optimal pour tous les symboles à la fois. Cette option se trouve dans l'onglet Optimisation :
Fig. 1. L'onglet Optimisation dans le module de trading de NeuroShell DayTrader Professional
Dans notre cas, n'importe quel système de trading simple fera l'affaire car nous n'avons qu'à comparer les résultats de deux méthodes d'optimisation, donc le choix du système est actuellement de peu d'importance.
Vous pouvez trouver l'information sur la façon de compiler les stratégies de trading dans NeuroShell DayTrader Professional dans d'autres articles de mon blog (vous pouvez chercher ou utiliser les tags pour trouver l'information pertinente). Je vous recommande également de lire un article intitulé "Comment préparer des cotations MetaTrader 5 pour d'autres applications" qui décrit et montre comment, à l'aide d'un script, vous pouvez télécharger des cotations de MetaTrader 5 dans le format compatible avec NeuroShell DayTrader Professional.
Pour faire ce test, j'ai préparé des données obtenues à partir de barres quotidiennes pour huit symboles de l'année 2000 à janvier 2013 :
Fig. 2. Liste des symboles pour un test dans NeuroShell DayTrader Professional
La figure ci-dessous montre deux résultats d'optimisation. La partie supérieure affiche le résultat de l'optimisation où chaque symbole obtient ses propres paramètres, tandis que la partie inférieure affiche le résultat où les paramètres sont communs à tous les symboles.
Fig. 3. Comparaison des résultats de deux modes d'optimisation des paramètres
Le résultat montrant les paramètres communs n'est pas aussi bon que celui où les paramètres sont différents pour chaque symbole. Pourtant, cela inspire plus de confiance car le système de trading passe par un certain nombre de modèles de comportement de prix différents (volatilité, nombre de tendances/plats) avec les mêmes paramètres pour tous les symboles.
En continuant sur le même sujet, nous pouvons logiquement trouver un autre argument en faveur de l'optimisation utilisant une plus grande quantité de données. Il se peut très bien que le comportement des prix d'une certaine paire de devises, par exemple EURUSD, soit assez différent par la suite (dans deux, cinq ou dix ans). Par exemple, les tendances des prix GBPUSD seront similaires au comportement des prix passés de l'EURUSD et vice versa. Vous devriez être prêt pour cela car cela est vrai pour n'importe quel instrument.
Un exemple dans MetaTrader 5
Voyons maintenant quels modes d'optimisation des paramètres sont proposés dans MetaTrader 5. Ci-dessous, vous pouvez voir tous les symboles sélectionnés dans le mode d'optimisation Market Watch marqués d'une flèche dans la liste déroulante des modes d'optimisation.
Fig. 4. Modes d'optimisation dans le testeur de stratégie MetaTrader 5
Ce mode vous permet de tester uniquement un EA avec les paramètres actuels sur chaque symbole un par un. Les symboles utilisés dans les tests sont ceux qui sont actuellement sélectionnés dans la fenêtre Market Watch. En d'autres termes, l'optimisation des paramètres n'est dans ce cas pas effectuée. Cependant, MetaTrader 5 et MQL5 vous permettent de mettre en œuvre cette idée par vous-même.
Maintenant, nous devons voir comment mettre en œuvre une telle évaluation environnementale. La liste des symboles sera fournie dans un fichier texte (*.txt). De plus, nous mettrons en œuvre la possibilité de stocker plusieurs ensembles de listes de symboles. Chaque ensemble sera dans une section séparée avec son propre en-tête comportant un numéro de section. Les chiffres sont nécessaires pour faciliter la vérification visuelle.
Notez qu'il est important d'avoir # devant le nombre afin de permettre à l'Expert Advisor d'obtenir le bon jeu de données lors du remplissage du tableau de symboles. Généralement, l'en-tête peut contenir n'importe quel symbole, mais il doit toujours avoir #. Le signe dièse peut être remplacé par tout autre symbole selon lequel l'Expert Advisor déterminera/comptera les sections. Dans ce cas, le remplacement devra être reflété dans le code.
Ci-dessous, vous pouvez voir le fichier SymbolsList.txt qui contient trois jeux de symboles à tester. Ce fichier, tel qu'illustré, sera ensuite utilisé lors du test de la méthode.
Fig. 5. Plusieurs jeux de symboles fournis dans un fichier texte pour les tests
Dans les paramètres externes, nous ajouterons un autre paramètre, SectionOfSymbolList, pour indiquer l'ensemble de symboles que l'Expert Advisor doit utiliser dans le test en cours. Ce paramètre prend la valeur (de zéro vers le haut) qui définit le jeu de symboles. Si la valeur dépasse le nombre d'ensembles disponibles, l'Expert Advisor écrira une entrée correspondante dans le journal et le test ne sera effectué que sur le symbole actuel.
SymbolsList.txt doit être situé dans le répertoire du terminal local sous Metatrader 5\MQL5\Files. Il peut également être placé dans le dossier commun mais dans ce cas, il ne sera pas disponible pour l'optimisation des paramètres dans le MQL5 Cloud Network. De plus, pour permettre l'accès au fichier et aux indicateurs personnalisés pertinents pour les tests, nous devons écrire les lignes suivantes au début du fichier :
//--- Allow access to the external file and indicator for optimization in the cloud #property tester_file "SymbolsList.txt" #property tester_indicator "EventsSpy.ex5"
Notre Expert Advisor sera basé sur l'Expert Advisor multi-devises prêt à l'emploi présenté dans l'article "MQL5 Cookbook : Développement d'un Expert Advisor multi-devises avec un nombre illimité de paramètres". Sa stratégie de trading sous-jacente est assez simple mais il suffira de tester l'efficacité de la méthode. Nous allons simplement supprimer les parties inutiles, ajouter ce dont nous avons besoin et corriger le code pertinent existant. Nous allons certainement améliorer notre Expert Advisor avec la fonction d'enregistrement de rapport largement décrite dans l'article précédent de la série "MQL5 Cookbook : Écriture de l'historique des transactions dans un fichier et création de graphiques d'équilibre pour chaque symbole dans Excel". Des tableaux d'équilibre pour tous les symboles seront également nécessaires pour évaluer l'efficacité de la méthode considérée.
Les paramètres externes de l'Expert Advisor doivent être modifiés comme suit :
//--- External parameters of the Expert Advisor sinput int SectionOfSymbolList = 1; // Section number in the symbol lists sinput bool UpdateReport = false; // Report update sinput string delimeter_00=""; // -------------------------------- sinput long MagicNumber = 777; // Magic number sinput int Deviation = 10; // Slippage sinput string delimeter_01=""; // -------------------------------- input int IndicatorPeriod = 5; // Indicator period input double TakeProfit = 100; // Take Profit input double StopLoss = 50; // Stop Loss input double TrailingStop = 10; // Trailing Stop input bool Reverse = true; // Position reversal input double Lot = 0.1; // Lot input double VolumeIncrease = 0.1; // Position volume increase input double VolumeIncreaseStep = 10; // Volume increase step
Tous les tableaux associés aux paramètres externes doivent être supprimés car ils ne seront pas nécessaires et doivent en outre être remplacés par les variables externes dans tout le code. Nous ne devons laisser que le tableau dynamique de symboles, InputSymbols[], dont la taille dépendra du nombre de symboles utilisés dans l'un des ensembles du fichier SymbolsList.txt. Si l'Expert Advisor est utilisé en dehors du Strategy Tester, la taille de ce tableau sera égale à 1 car en mode temps réel, l'Expert Advisor fonctionnera avec un seul symbole.
Les modifications correspondantes doivent également être effectuées dans le fichier d'initialisation du tableau - InitializeArrays.mqh. C'est-à-dire que toutes les fonctions responsables de l'initialisation des tableaux de variables externes doivent être supprimées. La fonction InitializeArraySymbols() ressemble maintenant à ce qui suit :
//+------------------------------------------------------------------+ //| Filling the array of symbols | //+------------------------------------------------------------------+ void InitializeArraySymbols() { int strings_count =0; // Number of strings in the symbol file string checked_symbol =""; // To check the accessibility of the symbol on the trade server //--- Test mode message string message_01="<--- All symbol names in the <- SymbolsList.txt -> file are incorrect ... --->\n" "<--- ... or the value of the \"Section of List Symbols\" parameter is greater, " "than the number of file sections! --->\n" "<--- Therefore we will test only the current symbol. --->"; //--- Real-time mode message string message_02="<--- In real-time mode, we only work with the current symbol. --->"; //--- If in real-time mode if(!IsRealtime()) { //--- Get the number of strings from the specified symbol set in the file and fill the temporary array of symbols strings_count=ReadSymbolsFromFile("SymbolsList.txt"); //--- Iterate over all symbols from the specified set for(int s=0; s<strings_count; s++) { //--- If the correct string is returned following the symbol check if((checked_symbol=GetSymbolByName(temporary_symbols[s]))!="") { //--- increase the counter SYMBOLS_COUNT++; //--- set/increase the array size ArrayResize(InputSymbols,SYMBOLS_COUNT); //--- index with the symbol name InputSymbols[SYMBOLS_COUNT-1]=checked_symbol; } } } //--- If all symbol names were not input correctly or if currently working in real-time mode if(SYMBOLS_COUNT==0) { //--- Real-time mode message if(IsRealtime()) Print(message_02); //--- Test mode message if(!IsRealtime()) Print(message_01); //--- We will work with the current symbol only SYMBOLS_COUNT=1; //--- set the array size and ArrayResize(InputSymbols,SYMBOLS_COUNT); //--- index with the current symbol name InputSymbols[0]=_Symbol; } }
Le code de la fonction ReadSymbolsFromFile() doit également être modifié. Auparavant, il lisait toute la liste des symboles alors que maintenant nous voulons qu'il ne lise que le jeu de symboles spécifié. Ci-dessous le code de fonction modifié :
//+------------------------------------------------------------------+ //| Returning the number of strings (symbols) from the specified | //| set in the file and filling the temporary array of symbols | //+------------------------------------------------------------------+ //--- When preparing the file, symbols in the list should be separated with a line break int ReadSymbolsFromFile(string file_name) { ulong offset =0; // Offset for determining the position of the file pointer string delimeter ="#"; // Identifier of the section start string read_line =""; // For the check of the read string int limit_count =0; // Counter limiting the number of the possibly open charts int strings_count =0; // String counter int sections_count =-1; // Section counter //--- Message 01 string message_01="<--- The <- "+file_name+" -> file has not been prepared appropriately! --->\n" "<--- The first string does not contain the section number identifier ("+delimeter+")! --->"; //--- Message 02 string message_02="<--- The <- "+file_name+" -> file has not been prepared appropriately! --->\n" "<--- There is no line break identifier in the last string, --->\n" "<--- so only the current symbol will be involved in testing. --->"; //--- Message 03 string message_03="<--- The <- "+file_name+" -> file could not be found! --->" "<--- Only the current symbol will be involved in testing. --->"; //--- Open the file (get the handle) for reading in the local directory of the terminal int file_handle=FileOpen(file_name,FILE_READ|FILE_ANSI,'\n'); //--- If the file handle has been obtained if(file_handle!=INVALID_HANDLE) { //--- Read until the current position of the file pointer // reaches the end of the file or until the program is deleted while(!FileIsEnding(file_handle) || !IsStopped()) { //--- Read until the end of the string or until the program is deleted while(!FileIsLineEnding(file_handle) || !IsStopped()) { //--- Read the whole string read_line=FileReadString(file_handle); //--- If the section number identifier has been found if(StringFind(read_line,delimeter,0)>-1) //--- Increase the section counter sections_count++; //--- If the section has been read, exit the function if(sections_count>SectionOfSymbolList) { FileClose(file_handle); // Close the file return(strings_count); // Return the number of strings in the file } //--- If this is the first iteration and the first string does not contain the section number identifier if(limit_count==0 && sections_count==-1) { PrepareArrayForOneSymbol(strings_count,message_01); //--- Close the file FileClose(file_handle); //--- Return the number of strings in the file return(strings_count); } //--- Increase the counter limiting the number of the possibly open charts limit_count++; //--- If the limit has been reached if(limit_count>=CHARTS_MAX) { PrepareArrayForOneSymbol(strings_count,message_02); //--- Close the file FileClose(file_handle); //--- Return the number of strings in the file return(strings_count); } //--- Get the position of the pointer offset=FileTell(file_handle); //--- If this is the end of the string if(FileIsLineEnding(file_handle)) { //--- Go to the next string if this is not the end of the file // For this purpose, increase the offset of the file pointer if(!FileIsEnding(file_handle)) offset++; //--- move it to the next string FileSeek(file_handle,offset,SEEK_SET); //--- If we are not in the specified section of the file, exit the loop if(sections_count!=SectionOfSymbolList) break; //--- Otherwise, else { //--- if the string is not empty if(read_line!="") { //--- increase the string counter strings_count++; //--- increase the size of the array of strings, ArrayResize(temporary_symbols,strings_count); //--- write the string to the current index temporary_symbols[strings_count-1]=read_line; } } //--- Exit the loop break; } } //--- If this is the end of the file, terminate the entire loop if(FileIsEnding(file_handle)) break; } //--- Close the file FileClose(file_handle); } else PrepareArrayForOneSymbol(strings_count,message_03); //--- Return the number of strings in the file return(strings_count); }
Vous pouvez voir que certaines chaînes du code ci-dessus sont mises en évidence. Ces chaînes contiennent la fonction PrepareArrayForOneSymbol() qui prépare simplement un tableau pour un symbole (actuel) en cas d'erreur.
//+------------------------------------------------------------------+ //| Preparing an array for one symbol | //+------------------------------------------------------------------+ void PrepareArrayForOneSymbol(int &strings_count,string message) { //--- Print the message to the log Print(message); //--- Array size strings_count=1; //--- Set the size of the array of symbols ArrayResize(temporary_symbols,strings_count); //--- Write the string with the current symbol name to the current index temporary_symbols[0]=_Symbol; }
Tout est maintenant prêt pour tester la méthode d'optimisation des paramètres. Mais avant de procéder aux tests, ajoutons une autre série de données au rapport. Auparavant, en plus des soldes de tous les symboles, le fichier de rapport contenait tous les retraits des maxima locaux exprimés en pourcentage. Désormais, le rapport couvrira également tous les retraits en termes monétaires. Dans le même temps, nous allons modifier la fonction CreateSymbolBalanceReport() où le rapport est généré.
Le code de la fonction CreateSymbolBalanceReport() est fourni ci-dessous :
//+------------------------------------------------------------------+ //| Creating test report on deals in .csv format | //+------------------------------------------------------------------+ void CreateSymbolBalanceReport() { int file_handle =INVALID_HANDLE; // File handle string path =""; // File path //--- If an error occurred when creating/getting the folder, exit if((path=CreateInputParametersFolder())=="") return; //--- Create a file to write data in the common folder of the terminal file_handle=FileOpen(path+"\\LastTest.csv",FILE_CSV|FILE_WRITE|FILE_ANSI|FILE_COMMON); //--- If the handle is valid (file created/opened) if(file_handle>0) { int digits =0; // Number of decimal places in the price int deals_total =0; // Number of deals in the specified history ulong ticket =0; // Deal ticket double drawdown_max =0.0; // Drawdown double balance =0.0; // Balance string delimeter =","; // Delimiter string string_to_write =""; // To generate the string for writing static double percent_drawdown =0.0; // Drawdown expressed as percentage static double money_drawdown =0.0; // Drawdown in monetary terms //--- Generate the header string string headers="TIME,SYMBOL,DEAL TYPE,ENTRY TYPE,VOLUME," "PRICE,SWAP($),PROFIT($),DRAWDOWN(%),DRAWDOWN($),BALANCE"; //--- If more than one symbol is involved, modify the header string if(SYMBOLS_COUNT>1) { for(int s=0; s<SYMBOLS_COUNT; s++) StringAdd(headers,","+InputSymbols[s]); } //--- Write the report headers FileWrite(file_handle,headers); //--- Get the complete history HistorySelect(0,TimeCurrent()); //--- Get the number of deals deals_total=HistoryDealsTotal(); //--- Resize the array of balances according to the number of symbols ArrayResize(symbol_balance,SYMBOLS_COUNT); //--- Resize the array of deals for each symbol for(int s=0; s<SYMBOLS_COUNT; s++) ArrayResize(symbol_balance[s].balance,deals_total); //--- Iterate in a loop and write the data for(int i=0; i<deals_total; i++) { //--- Get the deal ticket ticket=HistoryDealGetTicket(i); //--- Get all the deal properties GetHistoryDealProperties(ticket,D_ALL); //--- Get the number of digits in the price digits=(int)SymbolInfoInteger(deal.symbol,SYMBOL_DIGITS); //--- Calculate the overall balance balance+=deal.profit+deal.swap+deal.commission; //--- Calculate the max drawdown from the local maximum TesterDrawdownMaximum(i,balance,percent_drawdown,money_drawdown); //--- Generate a string for writing using concatenation StringConcatenate(string_to_write, deal.time,delimeter, DealSymbolToString(deal.symbol),delimeter, DealTypeToString(deal.type),delimeter, DealEntryToString(deal.entry),delimeter, DealVolumeToString(deal.volume),delimeter, DealPriceToString(deal.price,digits),delimeter, DealSwapToString(deal.swap),delimeter, DealProfitToString(deal.symbol,deal.profit),delimeter, DrawdownToString(percent_drawdown),delimeter, DrawdownToString(money_drawdown),delimeter, DoubleToString(balance,2)); //--- If more than one symbol is involved, write their balance values if(SYMBOLS_COUNT>1) { //--- Iterate over all symbols for(int s=0; s<SYMBOLS_COUNT; s++) { //--- If the symbols are equal and the deal result is non-zero if(deal.symbol==InputSymbols[s] && deal.profit!=0) { //--- Display the deal in the balance for the corresponding symbol // Take into consideration swap and commission symbol_balance[s].balance[i]=symbol_balance[s].balance[i-1]+ deal.profit+ deal.swap+ deal.commission; //--- Add to the string StringAdd(string_to_write,","+DoubleToString(symbol_balance[s].balance[i],2)); } //--- Otherwise write the previous value else { //--- If the deal type is "Balance" (the first deal) if(deal.type==DEAL_TYPE_BALANCE) { //--- the balance is the same for all symbols symbol_balance[s].balance[i]=balance; StringAdd(string_to_write,","+DoubleToString(symbol_balance[s].balance[i],2)); } //--- Otherwise write the previous value to the current index else { symbol_balance[s].balance[i]=symbol_balance[s].balance[i-1]; StringAdd(string_to_write,","+DoubleToString(symbol_balance[s].balance[i],2)); } } } } //--- Write the generated string FileWrite(file_handle,string_to_write); //--- Mandatory zeroing out of the variable for the next string string_to_write=""; } //--- Close the file FileClose(file_handle); } //--- If the file could not be created/opened, print the appropriate message else Print("Error creating the file! Error: "+IntegerToString(GetLastError())+""); }
Nous avions l'habitude de calculer les retraits dans la fonction DrawdownMaximumToString(). Ceci est maintenant effectué par la fonction TesterDrawdownMaximum(), tandis que la valeur de retrait est convertie en une chaîne de caractères à l'aide de la fonction de base DrawdownToString().
Le code de la fonction TesterDrawdownMaximum() est le suivant :
//+------------------------------------------------------------------+ //| Returning the max drawdown from the local maximum | //+------------------------------------------------------------------+ void TesterDrawdownMaximum(int deal_number, double balance, double &percent_drawdown, double &money_drawdown) { ulong ticket =0; // Deal ticket string str =""; // The string to be displayed in the report //--- To calculate the local maximum and drawdown static double max =0.0; static double min =0.0; //--- If this is the first deal if(deal_number==0) { //--- There is no drawdown yet percent_drawdown =0.0; money_drawdown =0.0; //--- Set the initial point as the local maximum max=balance; min=balance; } else { //--- If the current balance is greater than in the memory, then... if(balance>max) { //--- Calculate the drawdown using the previous values: // in monetary terms money_drawdown=max-min; // expressed as percentage percent_drawdown=100-((min/max)*100); //--- Update the local maximum max=balance; min=balance; } //--- Otherwise else { //--- Return zero value of the drawdown money_drawdown=0.0; percent_drawdown=0.0; //--- Update the minimum min=fmin(min,balance); //--- If the deal ticket by its position in the list has been obtained, then... if((ticket=HistoryDealGetTicket(deal_number))>0) { //--- ...get the deal comment GetHistoryDealProperties(ticket,D_COMMENT); //--- Flag of the last deal static bool last_deal=false; //--- The last deal in the test can be identified by the "end of test" comment if(deal.comment=="end of test" && !last_deal) { //--- Set the flag last_deal=true; //--- Update the drawdown values: // in monetary terms money_drawdown=max-min; // expressed as percentage percent_drawdown+=100-((min/max)*100); } } } } }
Le code de la fonction DrawdownToString() est fourni ci-dessous :
//+------------------------------------------------------------------+ //| Converting drawdown to a string | //+------------------------------------------------------------------+ string DrawdownToString(double drawdown) { return((drawdown<=0) ? "" : DoubleToString(drawdown,2)); }
Maintenant, tout est prêt pour le test de l'Expert Advisor et l'analyse des résultats. Au début de l'article, nous avons vu un exemple de fichier prêt à l'emploi. Procédons comme suit : optimisons les paramètres des symboles du deuxième ensemble (il y a trois symboles : EURUSD, AUDUSD et USDCHF) et après l'optimisation, exécutez le test en utilisant tous les symboles du troisième ensemble (sept symboles au total) pour voir les résultats des symboles dont les données n'étaient pas impliquées dans l'optimisation des paramètres.
Optimisation des paramètres et test Expert Advisor
Le testeur de stratégie doit être configuré comme indiqué ci-dessous :
Fig. 6. Les paramètres de testeur de stratégie pour l'optimisation
Les paramètres de l'Expert Advisor pour l'optimisation des paramètres sont fournis ci-dessous :
Fig. 7. Les paramètres de l'Expert Advisor pour l'optimisation des paramètres
Étant donné que l'optimisation implique trois symboles et que l'augmentation du volume de la position est activée pour chacun d'eux, nous définissons le lot minimum aux fins d'ouvrir une position et d'augmenter le volume de la position. Dans notre cas, la valeur est de 0,01.
Après l'optimisation, nous sélectionnons le meilleur résultat par le facteur de récupération maximal et définissons le paramètre VolumeIncrease à 0,1 pour le lot. Le résultat est présenté ci-dessous :
Fig. 8. Le résultat du test dans MetaTrader 5
Ci-dessous, vous pouvez voir le résultat tel qu'il apparaît dans Excel 2010 :
Fig. 9. Le résultat du test pour trois symboles comme indiqué dans Excel 2010
Le tirage en termes monétaires est affiché sous forme de marques vertes dans le graphique inférieur en termes de deuxième échelle (auxiliaire).
Vous devez également connaître les limites des graphiques dans Excel 2010 (la liste complète des spécifications et des limites est disponible sur page des spécifications et limites Excel du site Web de Microsoft Office).
Fig. 10. Spécifications et limites des graphiques dans Excel 2010
Le tableau montre que nous pouvons exécuter le test pour 255 symboles en même temps et afficher tous les résultats dans le graphique ! Nous ne sommes limités que par les ressources informatiques.
Exécutons maintenant le test pour sept symboles du troisième ensemble avec les paramètres actuels et vérifions le résultat :
Fig. 11. Le résultat du test pour sept symboles comme indiqué dans Excel 2010
Avec sept symboles à l'étude, nous avons 6901 transactions. Les données du graphique sont mises à jour assez rapidement dans Excel 2010.
Conclusion
Je pense que la méthode introduite est remarquable car même une stratégie de trading simple comme celle que nous avons utilisée a donné de bons résultats. Ici, il faut garder à l'esprit que l'optimisation n'a été effectuée que pour trois symboles sur sept. Nous pouvons essayer d'améliorer le résultat en optimisant les paramètres pour tous les symboles à la fois. Cependant, nous devrions avant tout viser à améliorer le système commercial, ou mieux encore, à avoir un portefeuille de différents systèmes de trading. Nous reviendrons sur cette idée plus tard.
C'est à peu près ça. Nous avons un outil assez utile pour étudier les résultats des stratégies de trading multi-devises. Vous trouverez ci-dessous le fichier zip téléchargeable avec les fichiers de l'Expert Advisor pour votre considération.
Après avoir extrait les fichiers, placez le dossier ReduceOverfittingEA dans le répertoire MetaTrader 5\MQL5\Experts. De plus, l'indicateur EventsSpy.mq5 doit être placé dans MetaTrader 5\MQL5\Indicators. SymbolsList.txt doit être situé sous MetaTrader 5\MQL5\Files.
Traduit du russe par MetaQuotes Ltd.
Article original : https://www.mql5.com/ru/articles/652





- Applications de trading gratuites
- VPS Forex gratuit pendant 24 heures
- 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