English Русский 中文 Español Deutsch 日本語 Português 한국어 Italiano Türkçe
Optimisation. Quelques idées simples

Optimisation. Quelques idées simples

MetaTrader 5Exemples | 13 janvier 2022, 15:42
177 0
Jose Miguel Soriano
Jose Miguel Soriano

Introduction

Après avoir trouvé une stratégie cohérente pour que l'EA puisse trader, nous la lançons sur le graphique EURUSD, OK ? La stratégie peut-elle être plus rentable sur d'autres paires de devises ? Existe-t-il d'autres paires de devises sur lesquelles la stratégie donnera de meilleurs résultats sans qu'il soit nécessaire d'augmenter le lot en progression géométrique ?

Que se passera-t-il si nous choisissons l'EURUSD et la période standard H1, puis, si nous ne sommes pas satisfaits du résultat, la changeons pour l'EURJPY sur H4 ?

De plus, si nous avons un système d'exploitation 64 bits, ce qui nous permet de ne plus nous soucier de la vitesse de test du système, oublions-nous les combinaisons ridicules des paramètres d'entrée du système de trading, qui participent à l'énumération complète lors de l'optimisation et quels résultats devons-nous négliger dans les rapports finaux ?

J'ai résolu moi-même ces « problèmes mineurs » et dans cet article, je partage les solutions efficaces. J'apprécie cependant qu'il puisse y avoir d'autres solutions plus optimales.


Optimisation par périodes

MQL5 fournit un ensemble complet de périodes : à partir de M1, M2, M3, M4,... H1, H2,... aux graphiques mensuels. Au total, il y a 21 périodes. Dans le processus d'optimisation, cependant, nous voulons savoir quelles périodes conviennent le plus à notre stratégie - les périodes courts comme М1 et М5, les périodes moyens - comme H2 et H4 ou les périodes longs - D1 et W1.

Au départ, nous n'avons pas besoin de cette diversité d'options. Dans tous les cas, si nous pouvons constater que la stratégie s'avère efficace sur la période М5, alors à la prochaine étape d'optimisation, nous pouvons vérifier si elle va fonctionner sur М3 ou М6.

Si on utilise une variable de type ENUM_TIMEFRAMES comme paramètre d'entrée :

input ENUM_TIMEFRAMES marcoTF= PERIOD_M5; 

alors l'Optimiseur offrira 21 variantes d'optimisation. Avons-nous vraiment besoin de cette quantité ?

 Options standard d'une période

Au départ, nous n’en avons pas besoin. Comment simplifier l'optimisation ? Dans un premier temps, nous pouvons définir l'énumération :

enum mis_MarcosTMP
{
   _M1= PERIOD_M1,
   _M5= PERIOD_M5,
   _M15=PERIOD_M15,
//   _M20=PERIOD_M20,
   _M30=PERIOD_M30,
   _H1= PERIOD_H1,
   _H2= PERIOD_H2,
   _H4= PERIOD_H4,
//   _H8= PERIOD_H8,
   _D1= PERIOD_D1,
   _W1= PERIOD_W1,
   _MN1=PERIOD_MN1
};

où nous pouvons ajouter ou supprimer les périodes d'intérêt. Pour l'optimisation, définissez la variable d'entrée au début du code :

input mis_MarcosTMP timeframe= _H1;

et définissez une nouvelle fonction dans la bibliothèque .mqh :

//----------------------------------------- DEFINE THE TIMEFRAME ----------------------------------------------------------
ENUM_TIMEFRAMES defMarcoTiempo(mi_MARCOTMP_CORTO marco)
{
   ENUM_TIMEFRAMES resp= _Period;
   switch(marco)
   {
      case _M1: resp= PERIOD_M1; break;
      case _M5: resp= PERIOD_M5; break;
      case _M15: resp= PERIOD_M15; break;
      //case _M20: resp= PERIOD_M20; break;
      case _M30: resp= PERIOD_M30; break;
      case _H1: resp= PERIOD_H1; break;
      case _H2: resp= PERIOD_H2; break;
      case _H4: resp= PERIOD_H4; break;
      //case _H8: resp= PERIOD_H8; break;
      case _D1: resp= PERIOD_D1; break;
      case _W1: resp= PERIOD_W1; break;
      case _MN1: resp= PERIOD_MN1;
   }
return(resp);
}

Déclarez une nouvelle variable dans le périmètre des variables globales :

ENUM_TIMEFRAMES marcoTmp= defMarcoTiempo(marcoTiempo);          //timeframe is defined as a global variable

« marcoTmp » est une variable globale, qui va être utilisée par l'EA pour définir un calendrier graphique requis. Dans le tableau des paramètres de l'Optimiseur, l'intervalle de lancement de la variable « marcoTiempo » peut être défini. Cela ne couvrira que les étapes de notre intérêt sans perdre du temps et des ressources à analyser М6 ou М12. De cette façon, nous pouvons analyser les résultats des travaux de l'EA sur différentes périodes.

 Options spécifiques à l'utilisateur d'une période

Certes, on peut se contenter de cela

ENUM_TIMEFRAMES marcoTmp= (ENUM_TIMEFRAMES)marcoTiempo;

Cela devient évident après des mois, voire des années de programmation si vous êtes un perfectionniste et que vous parcourez beaucoup le code en essayant de simplifier cela. Ou si vous utilisez un VPS et essayez de réduire votre facture en optimisant les performances de l'ordinateur.


Optimiser un symbole ou un ensemble de symboles

Dans le testeur de stratégie MetaTrader 5, il existe un mode d'optimisation qui facilite l'exécution de l'EA sur tous les symboles sélectionnés dans la fenêtre MarketWatch. Cette fonction, cependant, ne permet pas d'organiser l'optimisation comme si le symbole sélectionné était un autre paramètre. Donc, s'il y a 15 symboles sélectionnés, le testeur organisera 15 exécutions. Comment pouvons-nous savoir quel symbole est le meilleur pour notre EA ? S'il s'agit d'un EA multidevises, quel groupe de symboles donne le meilleur résultat et avec quel jeu de paramètres ? Les variables de chaîne ne sont pas optimisées dans MQL5. Comment ceci peut être fait ?

Codez un symbole ou quelques symboles par la valeur du paramètre d'entrée de la manière suivante :

input int selecDePar= 0;

string cadParesFX= selecPares(selecDePar);

Le paramètre « selecDePar » est utilisé comme paramètre d'optimisation, qui est converti en une variable de chaîne. Utilisez la variable « cadParesFX » dans l'EA. La/les paire(s) de devises (pour ce code, peu importe que la stratégie soit multi-devises ou non) vont être stockées dans cette variable avec d'autres paramètres d'optimisation réguliers.

//------------------------------------- SELECT THE SET OF PAIRS -------------------------------------
string selecPares(int combina= 0)
{
   string resp="EURUSD";
   switch(combina)               
      {
         case 1: resp= "EURJPY"; break;
         case 2: resp= "USDJPY"; break;
         case 3: resp= "USDCHF"; break;      
         case 4: resp= "GBPJPY"; break;
         case 5: resp= "GBPCHF"; break;      
         case 6: resp= "GBPUSD"; break;
         case 7: resp= "USDCAD"; break;
         case 8: resp= "CADJPY"; break;      
         case 9: resp= "XAUUSD"; break;
       
         case 10: resp= "EURJPY;USDJPY"; break;
         case 11: resp= "EURJPY;GBPJPY"; break;
         case 12: resp= "GBPCHF;GBPJPY"; break;
         case 13: resp= "EURJPY;GBPCHF"; break;
         case 14: resp= "USDJPY;GBPCHF"; break;

         case 15: resp= "EURUSD;EURJPY;GBPJPY"; break;
         case 16: resp= "EURUSD;EURJPY;GBPCHF"; break;
         case 17: resp= "EURUSD;EURJPY;USDJPY"; break;
         case 18: resp= "EURJPY;GBPCHF;USDJPY"; break;
         case 19: resp= "EURJPY;GBPUSD;GBPJPY"; break;
         case 20: resp= "EURJPY;GBPCHF;GBPJPY"; break;
         case 21: resp= "USDJPY;GBPCHF;GBPJPY"; break;
         case 22: resp= "EURUSD;USDJPY;GBPJPY"; break;
       
         case 23: resp= "EURUSD;EURJPY;USDJPY;GBPUSD;USDCHF;USDCAD"; break;
         case 24: resp= "EURUSD;EURJPY;USDJPY;GBPUSD;USDCHF;USDCAD;AUDUSD"; break;
      }
   return(resp);
}

En fonction de notre objectif, définir des combinaisons de paires et informer le Testeur de l'intervalle à analyser. Donnez au Testeur de stratégie une commande pour optimiser le paramètre « selecDePar » sur l'intervalle de 15 à 22 (voir l'image ci-dessous). Que fait-on lorsque l'on veut comparer les résultats pour une seule devise ? Dans ce cas, nous exécutons l'optimisation sur l'intervalle de 0 à 9.

Optimisation d'un ensemble de paires

Par exemple, l'EA reçoit la valeur du paramètre cadParesFX= "EURUSD;EURJPY;GBPCHF". Dans OnInit(), appelez la fonction « cargaPares() », qui remplit le tableau dynamique arrayPares[] avec des chaînes, divisées par le symbole « ; » dans le paramètre cadParesFX. Toutes les variables globales doivent être chargées dans des tableaux dynamiques, qui enregistrent les valeurs de chaque symbole, y compris le contrôle de l'ouverture d'une nouvelle barre sur un symbole si possible. Dans le cas où nous travaillons avec un symbole, les dimensions du tableau seront égales à un.

//-------------------------------- STRING CONVERSION FROM CURRENCY PAIRS INTO AN ARRAY  -----------------------------------------------
int cargaPares(string cadPares, string &arrayPares[])
{            //convierte "EURUSD;GBPUSD;USDJPY" a {"EURUSD", "GBPUSD", "USDJPY"}; devuelve el número de paresFX
   string caract= "";
   int i= 0, k= 0, contPares= 1, longCad= StringLen(cadPares);
   if(cadPares=="")
   {
      ArrayResize(arrayPares, contPares);
      arrayPares[0]= _Symbol;
   }
   else
   {
      for (k= 0; k<longCad; k++) if (StringSubstr(cadPares, k, 1)==";") contPares++;
      ArrayResize(arrayPares, contPares);    
      ZeroMemory(arrayPares);
      for(k=0; k<longCad; k++)
      {
         caract= StringSubstr(cadPares, k, 1);
         if (caract!=";") arrayPares[i]= arrayPares[i]+caract;
         else i++;
      }
    }
   return(contPares);
}

Dans OnInit(), cette fonction est implémentée de la manière suivante :

string ar_ParesFX[];    //array, containing names of the pairs for the EA to work with
int numSimbs= 1;        //variable, containing information about the number of symbols it works with

int OnInit()
{
   
   //...
   numSimbs= cargaPares(cadParesFX, ar_ParesFX);     //returns the ar_ParesFX array with pairs for work in the EA
   //...
   
}

Si numSimbs>1, la fonction OnChartEvent() est appelée. Cela fonctionne avec un système multi-devises. Sinon, la fonction OnTick() est utilisée :

void OnTick()
{
   string simb="";
   bool entrar= (nSimbs==1);
   if(entrar)
   {   
      .../...
      simb= ar_ParesFX[0];
      gestionOrdenes(simb);
      .../...
   }
   return;
}

//+------------------------------------------------------------------+
//| EVENT HANDLER                                                   |
//+-----------------------------------------------------------------+
void OnChartEvent(const int idEvento, const long& lPeriodo, const double& dPrecio, const string &simbTick)
{
   bool entrar= nSimbs>1 && (idEvento>=CHARTEVENT_CUSTOM);
   if(entrar)      
   {
      .../...
      gestionOrdenes(simbTick);
      .../...
   }
}
  

Cela signifie que toutes les fonctions dans le rôle du paramètre d'entrée doivent contenir au moins le symbole recherché. Par exemple, au lieu de la fonction Digits(), nous devons utiliser ce qui suit :

//--------------------------------- SYMBOLS OF A SYMBOL ---------------------------------------
int digitosSimb(string simb= NULL)
{
   int numDig= (int)SymbolInfoInteger(simb, SYMBOL_DIGITS);
   return(numDig);
}

Autrement dit, il faut oublier les fonctions Symbol() ou Point(), ainsi que d'autres variables classiques pour МetaТtarder 4 telles que Ask ou Bid.

//----------------------------------- POINT VALUE in price (Point())---------------------------------
double valorPunto(string simb= NULL) 
{
   double resp= SymbolInfoDouble(simb, SYMBOL_POINT);
   return(resp);
}
//--------------------------- precio ASK-BID  -----------------------------------------
double precioAskBid(string simb= NULL, bool ask= true)
{
   ENUM_SYMBOL_INFO_DOUBLE precioSolic= ask? SYMBOL_ASK: SYMBOL_BID;
   double precio= SymbolInfoDouble(simb, precioSolic);
   return(precio);
}

Nous avons également oublié la fonction de contrôle de l'ouverture de la barre, qui est présente dans de tels codes. Si les ticks reçus en EURUSD indiquent l'ouverture d'une nouvelle barre, alors les ticks USDJPY pourraient ne pas être reçus dans les 2 secondes suivantes. En conséquence, au prochain tick de l'USDJPY, l'EA découvrira qu'une nouvelle barre s'ouvre pour ce symbole même si pour l'EURUSD cet événement a eu lieu il y a 2 secondes.

//------------------------------------- NEW MULTI-CURRENCY CANDLESTICK -------------------------------------
bool nuevaVelaMD(string simb= NULL, int numSimbs= 1, ENUM_TIMEFRAMES marcoTmp= PERIOD_CURRENT)
{
        static datetime arrayHoraNV[];
        static bool primVez= true;
        datetime horaVela= iTime(simb, marcoTmp, 0);    //received opening time of the current candlestick
        bool esNueva= false;
        int codS= buscaCadArray(simb, nombreParesFX);      
        if(primVez)
        {
           ArrayResize(arrayHoraNV, numSimbs);
           ArrayInitialize(arrayHoraNV, 0);     
           primVez= false;
        }
        esNueva= codS>=0? arrayHoraNV[codS]!= horaVela: false;
        if(esNueva) arrayHoraNV[codS]= horaVela;
        return(esNueva); 
}

Cette méthode m'a permis de découvrir lors d'une passe d'optimisation que :

  • l'EA fonctionne bien en EURUSD,
  • fonctionne très mal en EURJPY,
  • et fonctionne de manière satisfaisante en USDJPY
  • Sur les paires EURUSD, GBPCHF, EURJPY cela fonctionne très bien (cas réel).

Ceci est vrai pour la période de M5 et une certaine combinaison d'autres paramètres d'optimisation, mais pas pour Н1 ou Н2.

Il y a un moment gênant ici. J'ai adressé une demande au support technique à ce sujet. Je ne sais pas pourquoi cela se produit, mais le résultat de l'optimisation diffère en fonction du symbole que nous sélectionnons dans le testeur de stratégie. C'est pourquoi, pour vérifier le résultat, je garde cette paire fixe tout au long du développement de la stratégie et je m'assure qu'il s'agit de l'une de ces paires qui peuvent être analysées dans l'Optimiseur.


Optimisation d'une combinaison de paramètres

Parfois, certaines combinaisons illogiques parmi toutes les combinaisons de paramètres qui participent à l'optimisation s'avèrent appropriées. Certaines d'entre elles rendent la stratégie déraisonnable. Par exemple, si la variable de l'entrée « maxSpread » définit la valeur du spread défini pour une opération de trading, nous optimisons cette variable pour différentes paires où le spread moyen du broker est inférieur à 30 et la XAUUSD est de 400. Il est absurde d'analyser ces paires si elles dépassent 50 et que la XAUUSD est inférieur à 200. Après avoir transmis les données à l'optimiseur, définissez « évalua maxSpread entre 0 et 600 avec l'intervalle 20 », mais un tel ensemble avec d'autres paramètres produit de nombreuses combinaisons qui n'ont pas de sens.

En suivant le modèle décrit dans la section précédente, nous avons défini des paires pour l'optimisation dans la fonction « selecPares() ». EURUSD se voit attribuer l'option 0 et XAUUSD se voit attribuer l'option 9. Définissez ensuite une variable globale de type booléen « paramCorrect ».

bool paramCorrect= (selecDePar<9 && maxSpread<50) ||
                   (selecDePar==9 && maxSpread>200);

Effectuez l'action dans OnInit() uniquement si paramCorrect est dans la bonne position vraie.

int OnInit()
{   
   ENUM_INIT_RETCODE resp= paramCorrect? INIT_SUCCEEDED: INIT_PARAMETERS_INCORRECT;
   if (paramCorrect)
   {
      //...
      nSimbs= cargaPares(cadParesFX, nombreParesFX);     //return the array nombreParesFX containing pairs for work in the EA
      //... function of the EA initialization
   }
   return(resp);
}

Si le paramCorrect est dans la position incorrecte faux, alors l'EA n'effectue aucune action dans OnInit() et renvoie le INIT_PARAMETERS_INCORRECT au testeur de stratégie, ce qui indique un ensemble de données d'entrée incorrect. Lorsque le testeur de stratégie reçoit la valeur INIT_PARAMETERS_INCORRECT de OnInit(), cet ensemble de paramètres n'est pas transmis aux autres agents de test pour la mise en œuvre et la ligne du tableau avec les résultats d'optimisation est remplie de zéros et surlignée en rouge (voir l'image ci-dessous) .

Résultats de l'utilisation de paramètres incorrects

La raison de l'arrêt du programme est transmise à OnDeinit() en tant que variable d'entrée et permet de comprendre la raison de la fermeture de l'EA. C'est une autre affaire cependant.

void OnDeinit(const int motivo)
{
   if(paramCorrect)
   {
      
      //functions of the program shutdown
      
   }
   infoDeInit(motivo);
   return;
}

//+-------------------------------------- INFORMATION ABOUT THE PROGRAM SHUTDOWN----------------------------
string infoDeInit(int codDeInit)
{                       //informs of the reason of the program shutdown
   string texto= "program initialization...", text1= "CIERRE por: ";
   switch(codDeInit)
   {
      case REASON_PROGRAM:     texto= text1+"The EA finished its work with the ExpertRemove() function"; break;  //0
      case REASON_ACCOUNT:     texto= text1+"The account was changed"; break;                                    //6
      case REASON_CHARTCHANGE: texto= text1+"Symbol or timeframe change"; break;                                 //3
      case REASON_CHARTCLOSE:  texto= text1+"The chart was closed"; break;                                       //4
      case REASON_PARAMETERS:  texto= text1+"Input parameters changed by the user"; break;                       //5
      case REASON_RECOMPILE:   texto= text1+"The program was recompiled"; break //2
      case REASON_REMOVE:      texto= text1+"The program was deleted from the chart"; break;                     //1
      case REASON_TEMPLATE:    texto= text1+"Another chart template was used"; break;                            //7
      case REASON_CLOSE:       texto= text1+"The terminal was closed"; break;                                    //9
      case REASON_INITFAILED:  texto= text1+"The OnInit() handler returned non-zero value"; break;               //8
      default:                 texto= text1+"Other reason";
   }
   Print(texto);
   return(texto);
}

Le fait est que si le jeu de paramètres reçu par l'optimiseur à l'étape spécifiée définit « paramCorrect » sur faux (par exemple si le spread EURUSD était défini sur 100 points), alors nous n'exécutons pas l'EA et cette étape d'optimisation devient zéro sans utilisation inutile de vos ressources informatiques et dépenses d'agents de location pour votre compte MQL5.сcommunity.

Certes, tout ce qui précède peut être implémenté avec OnTesterInit() et les fonctions ParameterGetRange() et ParameterSetRange(), mais le modèle décrit semble être plus simple. Ceci est garanti pour fonctionner alors que le modèle avec OnTesterInit() n'est pas aussi cohérent.


Conclusion

Nous avons discuté de l'accélération de la recherche de périodes optimales dans МetaТrader 5, en optimisant le paramètre « symbole », lorsque МetaТrader 5 ne permet pas d'optimiser les variables de chaîne et en le rendant indifférent au nombre de symboles que l'EA utilise. Nous avons également vu une illustration de la façon de réduire le nombre d'étapes d'optimisation en supprimant des ensembles illogiques de paramètres d'entrée en maintenant les performances de votre ordinateur et en économisant les fonds.

Les idées ci-dessus ne sont pas nouvelles et elles peuvent être mises en œuvre par un novice ou un programmeur avec une certaine expérience. Ces idées étaient le résultat d'une longue recherche d'informations et d'utilisation du programme de débogage. Ce sont des idées très simples mais efficaces. Vous pourriez me demander pourquoi je les partage si je veux que MQL5 génère des bénéfices ? La réponse est de surmonter la « solitude » d'un programmeur.

Merci pour votre attention. Si vous l'avez lu jusqu'au bout et que vous êtes un programmeur expérimenté, ne me jugez pas trop durement.

Traduit de l’espagnol par MetaQuotes Ltd.
Article original : https://www.mql5.com/es/articles/1052

Aperçu du marché MetaTrader (infographie) Aperçu du marché MetaTrader (infographie)
Il y a quelques semaines, nous avons publié l’infographie sur le service Freelance. Nous avons également promis de révéler quelques statistiques du marché MetaTrader. Maintenant, nous vous invitons à examiner les données que nous avons recueillies.
Fournisseurs de signaux Johnpaul77 : « Notre stratégie demeure rentable depuis plus de trois ans maintenant. Alors pourquoi la changerions-nous ? » Fournisseurs de signaux Johnpaul77 : « Notre stratégie demeure rentable depuis plus de trois ans maintenant. Alors pourquoi la changerions-nous ? »
Laissez-nous vous révéler un petit secret : Les visiteurs du site Web MQL5.com passent la plupart de leur temps sur la page du signal Johnpaul77. C'est un leader de notre évaluation de signal avec environ 900 abonnés et des fonds totaux de 5,7 millions de dollars sur des comptes réels. Nous avons interrogé les fournisseurs de signaux. Il s'est avéré qu'ils sont quatre ! Comment les tâches sont-elles réparties entre les membres de l'équipe ? Quels outils techniques utilisent-ils ? Pourquoi les appelle-t-on John Paul ? Et enfin, comment les joueurs ordinaires d'Indonésie sont-ils devenus des fournisseurs du meilleur signal sur MQL5.com ? Découvrez tout cela dans l'article.
Analyse de régression de l'influence des données macroéconomiques sur la fluctuation des prix des devises Analyse de régression de l'influence des données macroéconomiques sur la fluctuation des prix des devises
Cet article examine l'application de l'analyse de régression multiple aux statistiques macroéconomiques. Il donne également un aperçu de l'évaluation de l'impact des statistiques sur la fluctuation du taux de change sur la base de l'exemple de la paire de devises EURUSD. Une telle évaluation permet d'automatiser l'analyse fondamentale qui devient accessible même aux traders débutants.
Construire une start-up de technologie sociale, Deuxième partie : Programmation d'un client REST MQL5 Construire une start-up de technologie sociale, Deuxième partie : Programmation d'un client REST MQL5
Donnons maintenant une forme à l'idée Twitter basée sur le PHP qui a été introduite dans la première partie de cet article. Nous assemblons les différentes parties du SDSS. Concernant le côté client de l'architecture du système, nous nous appuyons sur la nouvelle fonction MQL5 WebRequest() pour envoyer des signaux de trading via HTTP.