English Русский 中文 Español Deutsch 日本語 Português 한국어 Italiano Türkçe
Dr. Tradelove ou comment j'ai arrêté de m'inquiéter et créé un Expert Advisor en autoformation

Dr. Tradelove ou comment j'ai arrêté de m'inquiéter et créé un Expert Advisor en autoformation

MetaTrader 5Trading | 12 janvier 2022, 17:05
151 0
Roman Zamozhnyy
Roman Zamozhnyy

Concept

Après avoir créé le Expert Advisor , nous avons tous recours au Testeur de stratégie intégré pour sélectionner les paramètres optimaux. Après avoir sélectionné ces derniers, nous exécutons l'Expert Advisor et dès qu'un changement significatif se produit, l'Expert Advisor est alors arrêté et optimisé encore et encore à l'aide du testeur de stratégie, et ainsi de suite.

Peut-on confier la prise de décision de réoptimisation et de réoptimisation en tant que processus à l'Expert Advisor sans interrompre naturellement son travail ?

Une des solutions à ce problème a été proposée par Quantum dans son article intitulé « Systèmes de trading adaptatifs et leur utilisation dans le terminal MetaTrader5 » est consacré à l'utilisation d'un système de trading réel aux côtés de quelques stratégies de trading virtuelles (en nombre illimité), parmi lesquelles a été sélectionnée la stratégie qui avait jusqu'à présent apporté le plus grand profit. La décision de changer de stratégie de trading est adoptée après le dépassement d'une certaine valeur de barre fixe.

Je propose d'utiliser un code d'algorithme génétique (AG) défini par joo dans l'article « Algorithmes génétiques - C'est facile ! ». Examinons la mise en œuvre d'un tel Expert Advisor (l'un des exemples ci-dessous est un Expert Advisor proposé pour participer au championnat de trading automatique 2011).


Travail en cours

Nous devons donc définir ce que l'Expert Advisor doit être capable de faire. Tout d'abord, et cela va sans dire, trader en utilisant la stratégie choisie. Deuxièmement, prendre une décision : s'il est temps de réoptimiser (effectuer une nouvelle optimisation des paramètres d'entrée). Et troisièmement, de réoptimiser en utilisant GA. Pour commencer, nous allons examiner la réoptimisation la plus simple - il existe une stratégie et il suffit de sélectionner les nouveaux paramètres. Nous verrons ensuite si nous pouvons, à l'aide de l'AG, choisir une autre stratégie dans un environnement de marché modifié et, le cas échéant, comment y parvenir.

De plus, pour faciliter la simulation dans la fonction de fitness, nous décidons de ne négocier que les barres complètes d'un instrument. Il n'y aura pas d'ajout de positions et de fermetures partielles. Ceux qui préfèrent utiliser des stops et des prises fixes ainsi que des stops suiveurs, veuillez vous référer à l'article « Algorithme de génération de ticks dans le testeur de stratégie MetaTrader5 » afin de mettre en œuvre les contrôles d'ordre Stop Loss et Take Profit dans la fonction fitness. Je vais développer la phrase intelligente ci-dessous :

Dans la fonction fitness, je simule un mode de test connu dans le testeur sous le nom de « Prix ouverts uniquement ». MAIS ! Cela ne signifie pas qu'il s'agit de la seule simulation de processus de test possible dans la fonction fitness. Les personnes plus scrupuleuses pourraient vouloir mettre en œuvre un test de fonction de fitness en utilisant le mode « Every Tick ». Afin de ne pas réinventer la roue ou maquiller « every tick », je voudrais attirer leur attention sur un algorithme existant développé par MetaQuotes. Autrement dit, après avoir lu cet article, on pourra simuler le mode « Every Tick » dans la fonction fitness qui est une condition nécessaire pour une simulation correcte des stops et des prises en FF.

Avant de passer au point principal - la mise en œuvre de la stratégie - passons brièvement en revue les aspects techniques et mettons en œuvre les fonctions auxiliaires définissant l'ouverture d'une nouvelle barre ainsi que l'ouverture et la fermeture de positions :

//+------------------------------------------------------------------+
//| Define whether a new bar has opened                             |
//+------------------------------------------------------------------+
bool isNewBars()
  {
   CopyTime(s,tf,0,1,curBT);
   TimeToStruct(curBT[0],curT);
   if(tf==PERIOD_M1||
      tf==PERIOD_M2||
      tf==PERIOD_M3||
      tf==PERIOD_M4||
      tf==PERIOD_M5||
      tf==PERIOD_M6||
      tf==PERIOD_M10||
      tf==PERIOD_M12||
      tf==PERIOD_M15||
      tf==PERIOD_M20||
      tf==PERIOD_M30)
      if(curT.min!=prevT.min)
        {
         prevBT[0]=curBT[0];
         TimeToStruct(prevBT[0],prevT);
         return(true);
        };
   if(tf==PERIOD_H1||
      tf==PERIOD_H2||
      tf==PERIOD_H3||
      tf==PERIOD_H4||
      tf==PERIOD_H6||
      tf==PERIOD_H8||
      tf==PERIOD_M12)
      if(curT.hour!=prevT.hour)
        {
         prevBT[0]=curBT[0];
         TimeToStruct(prevBT[0],prevT);
         return(true);
        };
   if(tf==PERIOD_D1||
      tf==PERIOD_W1)
      if(curT.day!=prevT.day)
        {
         prevBT[0]=curBT[0];
         TimeToStruct(prevBT[0],prevT);
         return(true);
        };
   if(tf==PERIOD_MN1)
      if(curT.mon!=prevT.mon)
        {
         prevBT[0]=curBT[0];
         TimeToStruct(prevBT[0],prevT);
         return(true);
        };
   return(false);
  }
//+------------------------------------------------------------------+
//|  ClosePosition                                                   |
//+------------------------------------------------------------------+
void ClosePosition()
  {
   request.action=TRADE_ACTION_DEAL;
   request.symbol=PositionGetSymbol(0);
   if(PositionGetInteger(POSITION_TYPE)==POSITION_TYPE_BUY) request.type=ORDER_TYPE_SELL; 
   else request.type=ORDER_TYPE_BUY;
   request.type_filling=ORDER_FILLING_FOK;
   if(SymbolInfoInteger(PositionGetSymbol(0),SYMBOL_TRADE_EXEMODE)==SYMBOL_TRADE_EXECUTION_REQUEST||
      SymbolInfoInteger(PositionGetSymbol(0),SYMBOL_TRADE_EXEMODE)==SYMBOL_TRADE_EXECUTION_INSTANT)
     {
      request.sl=NULL;
      request.tp=NULL;
      request.deviation=100;
     }
   while(PositionsTotal()>0)
     {
      request.volume=NormalizeDouble(MathMin(PositionGetDouble(POSITION_VOLUME),SymbolInfoDouble(PositionGetSymbol(0),SYMBOL_VOLUME_MAX)),2);
      if(SymbolInfoInteger(PositionGetSymbol(0),SYMBOL_TRADE_EXEMODE)==SYMBOL_TRADE_EXECUTION_REQUEST||
         SymbolInfoInteger(PositionGetSymbol(0),SYMBOL_TRADE_EXEMODE)==SYMBOL_TRADE_EXECUTION_INSTANT)
        {
         if(request.type==ORDER_TYPE_SELL) request.price=SymbolInfoDouble(s,SYMBOL_BID);
         else request.price=SymbolInfoDouble(s,SYMBOL_ASK);
        }
      OrderSend(request,result);
      Sleep(10000);
     }
  }
//+------------------------------------------------------------------+
//|  OpenPosition                                                    |
//+------------------------------------------------------------------+
void OpenPosition()
  {
   double vol;
   request.action=TRADE_ACTION_DEAL;
   request.symbol=s;
   request.type_filling=ORDER_FILLING_FOK;
   if(SymbolInfoInteger(s,SYMBOL_TRADE_EXEMODE)==SYMBOL_TRADE_EXECUTION_REQUEST||
      SymbolInfoInteger(s,SYMBOL_TRADE_EXEMODE)==SYMBOL_TRADE_EXECUTION_INSTANT)
     {
      request.sl=NULL;
      request.tp=NULL;
      request.deviation=100;
     }
   vol=MathFloor(AccountInfoDouble(ACCOUNT_FREEMARGIN)*optF*AccountInfoInteger(ACCOUNT_LEVERAGE)
       /(SymbolInfoDouble(s,SYMBOL_TRADE_CONTRACT_SIZE)*SymbolInfoDouble(s,SYMBOL_VOLUME_STEP)))*SymbolInfoDouble(s,SYMBOL_VOLUME_STEP);
   vol=MathMax(vol,SymbolInfoDouble(s,SYMBOL_VOLUME_MIN));
   vol=MathMin(vol,GetPossibleLots()*0.95);
   if(SymbolInfoDouble(s,SYMBOL_VOLUME_LIMIT)!=0) vol=NormalizeDouble(MathMin(vol,SymbolInfoDouble(s,SYMBOL_VOLUME_LIMIT)),2);
   request.volume=NormalizeDouble(MathMin(vol,SymbolInfoDouble(s,SYMBOL_VOLUME_MAX)),2);
   while(PositionSelect(s)==false)
     {
      if(SymbolInfoInteger(s,SYMBOL_TRADE_EXEMODE)==SYMBOL_TRADE_EXECUTION_REQUEST||
         SymbolInfoInteger(s,SYMBOL_TRADE_EXEMODE)==SYMBOL_TRADE_EXECUTION_INSTANT)
        {
         if(request.type==ORDER_TYPE_SELL) request.price=SymbolInfoDouble(s,SYMBOL_BID); 
         else request.price=SymbolInfoDouble(s,SYMBOL_ASK);
        }
      OrderSend(request,result);
      Sleep(10000);
      PositionSelect(s);
     }
   while(PositionGetDouble(POSITION_VOLUME)<vol)
     {
      request.volume=NormalizeDouble(MathMin(vol-PositionGetDouble(POSITION_VOLUME),SymbolInfoDouble(s,SYMBOL_VOLUME_MAX)),2);
      if(SymbolInfoInteger(PositionGetSymbol(0),SYMBOL_TRADE_EXEMODE)==SYMBOL_TRADE_EXECUTION_REQUEST||
         SymbolInfoInteger(PositionGetSymbol(0),SYMBOL_TRADE_EXEMODE)==SYMBOL_TRADE_EXECUTION_INSTANT)
        {
         if(request.type==ORDER_TYPE_SELL) request.price=SymbolInfoDouble(s,SYMBOL_BID);
         else request.price=SymbolInfoDouble(s,SYMBOL_ASK);
        }
      OrderSend(request,result);
      Sleep(10000);
      PositionSelect(s);
     }
  }
//+------------------------------------------------------------------+

Après un examen attentif, vous pouvez remarquer trois paramètres importants dans la fonction d'ouverture de position : les variables s et optF et l'appel de la fonction GetPossibleLots() :

  • s - instrument de trading, une des variables optimisées par GA,
  • optF - partie du dépôt à utiliser pour le trading (autre variable optimisée par GA), 
  • Fonction GetPossibleLots() - renvoie la partie du dépôt à utiliser pour le trading :
//+------------------------------------------------------------------+
//|  GetPossibleLots                                                 |
//+------------------------------------------------------------------+
double GetPossibleLots()
  {
   request.volume=1.0;
   if(request.type==ORDER_TYPE_SELL) request.price=SymbolInfoDouble(s,SYMBOL_BID);
   else request.price=SymbolInfoDouble(s,SYMBOL_ASK);
   OrderCheck(request,check);
   return(NormalizeDouble(AccountInfoDouble(ACCOUNT_FREEMARGIN)/check.margin,2));
  }

En rompant légèrement l'ordre du récit, nous introduisons deux autres fonctions communes à tous les Expert Advisors et essentielles à la deuxième étape :

//+------------------------------------------------------------------+
//|  InitRelDD                                                       |
//+------------------------------------------------------------------+
void InitRelDD()
  {
   ulong DealTicket;
   double curBalance;
   prevBT[0]=D'2000.01.01 00:00:00';
   TimeToStruct(prevBT[0],prevT);
   curBalance=AccountInfoDouble(ACCOUNT_BALANCE);
   maxBalance=curBalance;
   HistorySelect(D'2000.01.01 00:00:00',TimeCurrent());
   for(int i=HistoryDealsTotal();i>0;i--)
     {
      DealTicket=HistoryDealGetTicket(i);
      curBalance=curBalance+HistoryDealGetDouble(DealTicket,DEAL_PROFIT);
      if(curBalance>maxBalance) maxBalance=curBalance;
     }
  }
//+------------------------------------------------------------------+
//|  GetRelDD                                                        |
//+------------------------------------------------------------------+
double GetRelDD()
  {
   if(AccountInfoDouble(ACCOUNT_BALANCE)>maxBalance) maxBalance=AccountInfoDouble(ACCOUNT_BALANCE);
   return((maxBalance-AccountInfoDouble(ACCOUNT_BALANCE))/maxBalance);
  }

Que peut-on voir ici ? La première fonction détermine la valeur maximale du solde du compte, la deuxième fonction calcule le prélèvement relatif actuel du compte. Leurs particularités seront détaillées dans la description de la deuxième étape.

Passons aux Expert Advisors en tant que tels. Puisque nous ne sommes que des débutants, nous n'utiliserons pas un Expert Advisor pour sélectionner une stratégie mais nous mettrons strictement en œuvre deux Expert Advisors avec les stratégies suivantes :
  • on négocie en utilisant les intersections des moyennes mobiles (Golden Cross - on achète un instrument, Death Cross - on vend) ;
  • l'autre est un réseau neuronal simple qui reçoit les variations de prix dans l’intervalle [0..1] sur les cinq dernières sessions de trading.

Algorithmiquement, le travail d'un Expert Advisor auto-optimisé peut être illustré comme suit :

  1. Initialisation des variables utilisées par l'Expert Advisor : définir et initialiser les tampons des indicateurs ou configurer la topologie du réseau neuronal (nombre de couches/neurones dans une couche ; un réseau neuronal simple où le nombre de neurones est le même dans toutes les couches est donné en exemple), définir le délai de travail. Ensuite, l'étape probablement la plus importante - nous appelons la fonction d'optimisation génétique qui, à son tour, s'adresse à la fonction la plus importante - la fonction de fitness (ci-après - FF).

    IMPORTANT ! Il existe une nouvelle FF pour chaque stratégie de trading, c'est-à-dire qu'elle est créée à chaque fois à nouveau, par exemple, la FF pour une moyenne mobile unique est complètement différente de la FF pour deux moyennes mobiles et elle diffère considérablement de la FF du réseau neuronal.

    Le résultat de performance FF dans mes Expert Advisors est un solde maximum à condition que le prélèvement relatif n'ait pas dépassé la valeur critique définie comme variable externe (dans nos exemples - 0,5). En d'autres termes, si la prochaine exécution GA donne le solde de 100 000, alors que le prélèvement du solde relatif est de -0,6, alors FF=0,0. Dans votre cas, cher lecteur, le résultat FF peut faire apparaître des critères complètement différents.

    Rassembler les résultats des performances de l'Algorithme Génétique : pour l'intersection des moyennes mobiles, il s'agira évidemment des périodes de moyennes mobiles, dans le cas d'un réseau neuronal, il s'agira des poids des synapses, et le résultat commun aux deux (et à mes autres Expert Advisors) est un instrument à trader jusqu'à la prochaine réoptimisation et déjà familier pour nous optF, c'est-à-dire une partie du dépôt à utiliser pour le trading. Vous êtes libre d'ajouter des paramètres optimisés à votre FF à votre discrétion, par exemple, vous pouvez également sélectionner une période ou d'autres paramètres...

    La dernière étape de l'initialisation consiste à trouver la valeur maximale du solde du compte. Pourquoi est-ce important ? Parce que c'est un point de départ pour la prise de décision de réoptimisation.

    IMPORTANT ! Comment la décision de réoptimisation est prise : une fois que le prélèvement relatif du SOLDE atteint une certaine valeur critique définie comme une variable externe (dans nos exemples - 0,2), nous devons réoptimiser. Afin de ne pas obliger un Expert Advisor à mettre en œuvre une réoptimisation à chaque barre lorsqu'il atteint le prélèvement critique, la valeur maximale du solde est remplacée par une valeur actuelle.

    Vous, mon cher lecteur, avez peut-être un critère totalement différent pour la mise en œuvre de la réoptimisation.

  2. Trading en cours.

  3. A chaque position fermée, nous vérifions si le prélèvement du solde a atteint la valeur critique. Si la valeur critique est atteinte, nous exécutons GA et recueillons les résultats de ses performances (c'est la réoptimisation !).

  4. Et nous attendons soit un appel du directeur des opérations de change demandant de ne pas mettre le monde en faillite, soit (ce qui est plus fréquent) un Stop Out, un appel de marge, une ambulance d'urgence...

Veuillez trouver ci-dessous une implémentation programmée de ce qui précède pour l'Expert Advisor en utilisant la stratégie des moyennes mobiles (le code source est également disponible) et en utilisant le réseau neuronal - le tout en code source.

Le code est fourni sous les termes et conditions de la licence GPL.

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
   tf=Period();
//---for bar-to-bar test...
   prevBT[0]=D'2001.01.01';
//---... long ago
   TimeToStruct(prevBT[0],prevT);
//--- historical depth (should be set since the optimisation is based on historical data)
   depth=10000;
//--- copies at a time (should be set since the optimisation is based on historical data)
   count=2;
   ArrayResize(LongBuffer,count);
   ArrayResize(ShortBuffer,count);
   ArrayInitialize(LongBuffer,0);
   ArrayInitialize(ShortBuffer,0);
//--- calling the neural network genetic optimisation function
   GA();
//--- getting the optimised neural network parameters and other variables
   GetTrainResults();
//--- getting the account drawdown
   InitRelDD();
   return(0);
  }
//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
  {
   if(isNewBars()==true)
     {
      bool trig=false;
      CopyBuffer(MAshort,0,0,count,ShortBuffer);
      CopyBuffer(MAlong,0,0,count,LongBuffer);
      if(LongBuffer[0]>LongBuffer[1] && ShortBuffer[0]>LongBuffer[0] && ShortBuffer[1]<LongBuffer[1])
        {
         if(PositionsTotal()>0)
           {
            if(PositionGetInteger(POSITION_TYPE)==POSITION_TYPE_BUY)
              {
               ClosePosition();
               trig=true;
              }
           }
        }
      if(LongBuffer[0]<LongBuffer[1] && ShortBuffer[0]<LongBuffer[0] && ShortBuffer[1]>LongBuffer[1])
        {
         if(PositionsTotal()>0)
           {
            if(PositionGetInteger(POSITION_TYPE)==POSITION_TYPE_SELL)
              {
               ClosePosition();
               trig=true;
              }
           }
        }
      if(trig==true)
        {
         //--- if the account drawdown has exceeded the allowable value:
         if(GetRelDD()>maxDD)
           {
            //--- calling the neural network genetic optimisation function
            GA();
            //--- getting the optimised neural network parameters and other variables
            GetTrainResults();
            //--- readings of the drawdown will from now on be based on the current balance instead of the maximum balance
            maxBalance=AccountInfoDouble(ACCOUNT_BALANCE);
           }
        }
      CopyBuffer(MAshort,0,0,count,ShortBuffer);
      CopyBuffer(MAlong,0,0,count,LongBuffer);
      if(LongBuffer[0]>LongBuffer[1] && ShortBuffer[0]>LongBuffer[0] && ShortBuffer[1]<LongBuffer[1])
        {
         request.type=ORDER_TYPE_SELL;
         OpenPosition();
        }
      if(LongBuffer[0]<LongBuffer[1] && ShortBuffer[0]<LongBuffer[0] && ShortBuffer[1]>LongBuffer[1])
        {
         request.type=ORDER_TYPE_BUY;
         OpenPosition();
        }
     };
  }
//+------------------------------------------------------------------+
//| Preparing and calling the genetic optimizer                      |
//+------------------------------------------------------------------+
void GA()
  {
//--- number of genes (equal to the number of optimised variables), 
//--- all of them should be specified in the FitnessFunction())
   GeneCount      =OptParamCount+2;    
//--- number of chromosomes in a colony
   ChromosomeCount=GeneCount*11;
//--- minimum search range
   RangeMinimum   =0.0;
//--- maximum search range
   RangeMaximum   =1.0;
//--- search pitch
   Precision      =0.0001;
//--- 1 is a minimum, anything else is a maximum
   OptimizeMethod =2;                                                 
   ArrayResize(Chromosome,GeneCount+1);
   ArrayInitialize(Chromosome,0);
//--- number of epochs without any improvement
   Epoch          =100;                                               
//--- ratio of replication, natural mutation, artificial mutation, gene borrowing, 
//--- crossingover, interval boundary displacement ratio, every gene mutation probabilty, %
   UGA(100.0,1.0,1.0,1.0,1.0,0.5,1.0);                                
  }
//+------------------------------------------------------------------+
//| Fitness function for neural network genetic optimizer:           | 
//| selecting a pair, optF, synapse weights;                         |
//| anything can be optimised but it is necessary                    |
//| to carefully monitor the number of genes                         |
//+------------------------------------------------------------------+
void FitnessFunction(int chromos)
  {
   int    b;
//--- is there an open position?
   bool   trig=false;
//--- direction of an open position
   string dir="";
//--- opening price
   double OpenPrice=0;
//--- intermediary between a gene colony and optimised parameters
   int    z;
//--- current balance
   double t=cap;
//--- maximum balance
   double maxt=t;
//--- absolute drawdown
   double aDD=0;
//--- relative drawdown
   double rDD=0.000001;
//--- fitness function proper
   double ff=0;
//--- GA is selecting a pair
   z=(int)MathRound(Colony[GeneCount-1][chromos]*12);
   switch(z)
     {
      case  0: {s="AUDUSD"; break;};
      case  1: {s="AUDUSD"; break;};
      case  2: {s="EURAUD"; break;};
      case  3: {s="EURCHF"; break;};
      case  4: {s="EURGBP"; break;};
      case  5: {s="EURJPY"; break;};
      case  6: {s="EURUSD"; break;};
      case  7: {s="GBPCHF"; break;};
      case  8: {s="GBPJPY"; break;};
      case  9: {s="GBPUSD"; break;};
      case 10: {s="USDCAD"; break;};
      case 11: {s="USDCHF"; break;};
      case 12: {s="USDJPY"; break;};
      default: {s="EURUSD"; break;};
     }
   MAshort=iMA(s,tf,(int)MathRound(Colony[1][chromos]*MaxMAPeriod)+1,0,MODE_SMA,PRICE_OPEN);
   MAlong =iMA(s,tf,(int)MathRound(Colony[2][chromos]*MaxMAPeriod)+1,0,MODE_SMA,PRICE_OPEN);
   dig=MathPow(10.0,(double)SymbolInfoInteger(s,SYMBOL_DIGITS));
   
//--- GA is selecting the optimal F
   optF=Colony[GeneCount][chromos];                                   
   
   leverage=AccountInfoInteger(ACCOUNT_LEVERAGE);
   contractSize=SymbolInfoDouble(s,SYMBOL_TRADE_CONTRACT_SIZE);
   b=MathMin(Bars(s,tf)-1-count-MaxMAPeriod,depth);
   
//--- for a neural network using historical data - where the data is copied from
   for(from=b;from>=1;from--) 
     {
      CopyBuffer(MAshort,0,from,count,ShortBuffer);
      CopyBuffer(MAlong,0,from,count,LongBuffer);
      if(LongBuffer[0]>LongBuffer[1] && ShortBuffer[0]>LongBuffer[0] && ShortBuffer[1]<LongBuffer[1])
        {
         if(trig==false)
           {
            CopyOpen(s,tf,from,count,o);
            OpenPrice=o[1];
            dir="SELL";
            trig=true;
           }
         else
           {
            if(dir=="BUY")
              {
               CopyOpen(s,tf,from,count,o);
               if(t>0) t=t+t*optF*leverage*(o[1]-OpenPrice)*dig/contractSize; else t=0;
               if(t>maxt) {maxt=t; aDD=0;} else if((maxt-t)>aDD) aDD=maxt-t;
               if((maxt>0) && (aDD/maxt>rDD)) rDD=aDD/maxt;
               OpenPrice=o[1];
               dir="SELL";
               trig=true;
              }
           }
        }
      if(LongBuffer[0]<LongBuffer[1] && ShortBuffer[0]<LongBuffer[0] && ShortBuffer[1]>LongBuffer[1])
        {
         if(trig==false)
           {
            CopyOpen(s,tf,from,count,o);
            OpenPrice=o[1];
            dir="BUY";
            trig=true;
           }
         else
           {
            if(dir=="SELL")
              {
               CopyOpen(s,tf,from,count,o);
               if(t>0) t=t+t*optF*leverage*(OpenPrice-o[1])*dig/contractSize; else t=0;
               if(t>maxt) {maxt=t; aDD=0;} else if((maxt-t)>aDD) aDD=maxt-t;
               if((maxt>0) && (aDD/maxt>rDD)) rDD=aDD/maxt;
               OpenPrice=o[1];
               dir="BUY";
               trig=true;
              }
           }
        }
     }
   if(rDD<=trainDD) ff=t; else ff=0.0;
   AmountStartsFF++;
   Colony[0][chromos]=ff;
  }

//+---------------------------------------------------------------------+
//| getting the optimized neural network parameters and other variables |
//| should always be equal to the number of genes                       |
//+---------------------------------------------------------------------+
void GetTrainResults()
  {
//---  intermediary between a gene colony and optimised parameters
   int z;                                                             
   MAshort=iMA(s,tf,(int)MathRound(Chromosome[1]*MaxMAPeriod)+1,0,MODE_SMA,PRICE_OPEN);
   MAlong =iMA(s,tf,(int)MathRound(Chromosome[2]*MaxMAPeriod)+1,0,MODE_SMA,PRICE_OPEN);
   CopyBuffer(MAshort,0,from,count,ShortBuffer);
   CopyBuffer(MAlong,0,from,count,LongBuffer);
//--- save the best pair
   z=(int)MathRound(Chromosome[GeneCount-1]*12);                      
   switch(z)
     {
      case  0: {s="AUDUSD"; break;};
      case  1: {s="AUDUSD"; break;};
      case  2: {s="EURAUD"; break;};
      case  3: {s="EURCHF"; break;};
      case  4: {s="EURGBP"; break;};
      case  5: {s="EURJPY"; break;};
      case  6: {s="EURUSD"; break;};
      case  7: {s="GBPCHF"; break;};
      case  8: {s="GBPJPY"; break;};
      case  9: {s="GBPUSD"; break;};
      case 10: {s="USDCAD"; break;};
      case 11: {s="USDCHF"; break;};
      case 12: {s="USDJPY"; break;};
      default: {s="EURUSD"; break;};
     }
//--- saving the best optimal F
   optF=Chromosome[GeneCount];                                        
  }
//+------------------------------------------------------------------+

Examinons la fonction principale de l'algorithme - la fonction de fitness.

Toute l'idée derrière un Expert Advisor auto-optimisé est basée sur la simulation du processus de trading (comme dans le Testeur MetaTrader 5 standard de MetaQuotes) sur une période de temps (disons, un historique de 10000 bars) dans la fonction fitness, qui reçoit une entrée de variables optimisées de l'algorithme génétique (fonction GA()). Dans le cas d'un algorithme basé sur l'intersection de moyennes mobiles, les variables optimisées comprennent :

  • instrument (en forex - une paire de devises) ; oui, il s'agit d'un Expert Advisor multi-devises typique et l'Algorithme Génétique sélectionne un instrument (puisque le code a été pris du Expert Advisor proposé pour participer au Championnat, les paires qu'il a correspondent aux paires de devises du Championnat ; en général, il peut y avoir n'importe quel instrument coté par un courtier).
    Remarque : malheureusement, l'Expert Advisor ne peut pas obtenir la liste des paires depuis la fenêtre MarketWatch en mode test (nous l'avons clarifié ici grâce aux utilisateurs de MetaQuotes - Pas question !). Par conséquent, si vous souhaitez exécuter l'Expert Advisor dans le testeur séparément pour le forex et les actions, il suffit de spécifier vos propres instruments dans la fonction FF et GetTrainResults().
  • une partie du dépôt à utiliser pour le trading ;
  • périodes de deux moyennes mobiles.

Les exemples ci-dessous montrent une variante d'un Expert Advisor POUR TESTER !

Le code REAL TRADE peut être considérablement simplifié en utilisant la liste des instruments de la fenêtre Market Watch.

Pour ce faire, dans la fonction FF et GetTrainResults() avec les commentaires « //--- GA sélectionne une paire » et « //--- sauvegarde la meilleure paire », il suffit d'écrire :

//--- GA is selecting a pair
  z=(int)MathRound(Colony[GeneCount-1][chromos]*(SymbolsTotal(true)-1));
  s=SymbolName(z,true);

Ainsi, au début de FF, nous spécifions et initialisons, si nécessaire, les variables pour la simulation du trading basé sur l'historique. À l'étape suivante, nous recueillons différentes valeurs de variables optimisées à partir de l'algorithme génétique, par exemple à partir de cette ligne « optF=Colonie[GeneCount][chromos] ; la valeur de la partie dépôt est transférée à FF à partir de GA.

Nous vérifions en outre le nombre de barres disponibles dans l'historique et en commençant soit par la 10000e barre, soit par la première barre disponible, nous simulons le processus de réception des cotations en mode « Prix ouverts uniquement » et de prise de décisions de trading :

  • Copiez les valeurs des moyennes mobiles dans les tampons ;
  • Vérifiez s'il s'agit d'une Death Cross.
  • S'il y a une Death Cross et aucune position ouverte (if(trig==false)) - ouvrez une position virtuelle de VENTE (rappelez-vous simplement le prix et la direction d'ouverture);
  • S'il y a une Death Cross et une position ACHAT ouverte (if(dir==« ACHAT »)) - prenez le prix d'ouverture de la barre et faites attention aux trois lignes très importantes comme suit :
  1. Simuler la clôture d'une position et la modification du solde : le solde actuel est augmenté d'une valeur de solde actuel, qui est multipliée par la partie du dépôt à négocier, multipliée par la différence entre les prix d'ouverture et de clôture, et multipliée par le prix du pip (approximatif) ;
  2. Vérifiez si le solde actuel a atteint le maximum sur l'historique de la simulation de trading ; si ce n'est pas le cas, calculez le prélèvement maximal du solde en argent ;
  3. Convertir le prélèvement calculé précédemment en argent en solde de prélèvement relatif ;
  • Ouvrez une position virtuelle de VENTE (rappelez-vous simplement le prix d'ouverture et la direction) ;
  • Faites des vérifications et des calculs similaires pour une Golden Cross.

Après avoir parcouru l'ensemble de l'historique disponible et la simulation de la transaction virtuelle, calculer la valeur finale du FF : si le prélèvement du solde relatif calculé est inférieur à celui fixé pour le test, alors FF=solde, sinon FF=0. L'algorithme génétique vise à maximiser la fonction de fitness !

Après tout, en donnant différentes valeurs aux instruments, aux parties du dépôt et aux périodes des moyennes mobiles, l'algorithme génétique trouvera les valeurs qui maximisent le solde avec le minimum (le minimum est fixé par l'utilisateur) de prélèvement relatif.


Conclusion

Voici une brève conclusion : il est facile de créer un Expert Advisor en autoformation, la partie difficile est de trouver ce qu'il faut saisir (l'important est une idée, la mise en œuvre n'est qu'une question technique).

En prévision de la question des pessimistes - « Est-ce que ça marche ? », j'ai une réponse - ça marche ; un mot aux optimistes - ce n'est pas le Saint Graal.

Quelle est la différence fondamentale entre la méthode proposée et celle de Quantum ? La meilleure façon de l'illustrer est de comparer les Expert Advisors utilisant les MA :

  1. La décision sur les périodes de MA dans le système de trading adaptatif, doit être prise avant la compilation et strictement codée et la sélection n'est possible qu'à partir de ce nombre limité de variantes ; nous ne prenons aucune décision sur les délais avant compilation dans l’Expert Advisor Génétiquement Optimisé, cette décision sera prise par GA et le nombre de variantes n'est limité que par le bon sens.
  2. Le trading virtuel dans le système de trading adaptatif, est de barre à barre ; c'est rarement le cas dans l’Expert Advisor Génétiquement Optimisé - et seulement lorsque les conditions de réoptimisation sont réunies. Les performances de l'ordinateur en fonction du nombre croissant de stratégies, de paramètres et d'instruments peuvent constituer un facteur limitant pour le système de trading adaptatif.


Annexe

Voici ce que nous obtenons si le réseau neuronal est exécuté dans le testeur sans aucune optimisation et sur la base des graphiques quotidiens à partir du 01.01.2010 :

Rapport du testeur de stratégie
MetaQuotes-Démo (Build 523)
Expert Advisor : ANNExemple
Symbole : EURUSD
Période : Journalier (2010.01.01 - 2011.09.30)
Paramètres d'entrée : trainDD=0.9
maxDD=0.1
Courtier : Alpari NZ Limited
Devise : USD
Dépôt initial : 10 000,00
Effet de levier : 1:100
Résultats
Qualité historique : 100 %
Barres : 454 Ticks : 2554879
Bénéfice net total : -9 094,49 Bénéfice brut : 29 401,09 Perte brute : -38 495,58
Facteur de profit : 0,76 Gain attendu : -20,53 Niveau de marge : 732,30 %
Facteur de récupération : -0,76 Ratio de Sharpe , -0,06 Résultat OnTester : 0
Prélèvement du solde :
Abs. prélèvement du solde : 9 102,56 Prélèvement maximal du solde : 11 464,70 (92,74 %) Prélèvement relatif du solde : 92,74 % (11 464,70)
Prélèvement sur fonds propres :
Abs. prélèvement sur fonds propres : 9 176,99 Prélèvement maximal sur fonds propres : 11 904,00 (93,53 %) Prélèvement relative des fonds propres : 93,53 % (11 904,00)
Trading totaux : 443 Trading courts (gagnés, %) : 7 (14.29%) Trading longs (gagnés, %) : 436 (53,44 %)
Nombre total d'offres : 886 Trading bénéficiaires (% du total) : 234 (52,82 %) Trading à perte (% du total) : 209 (47,18 %)
Trading le plus lucratif : 1 095,57 La plus grosse perte de trading : -1 438,85
Profit moyen de trading : 125,65 Perte moyenne de trading : -184,19
Max. victoires consécutives (bénéfice en argent) : 8 (397,45) Max. pertes consécutives (perte en argent) : 8 (-1 431,44)
Max. profit consécutif (nombre de gains) : 1 095,57 (1) Max. perte consécutive (compte des pertes) : -3 433,21 (6)
Moyenne des victoires consécutives : 2 Moyenne des pertes consécutives : 2

et voici trois variantes de réoptimisation parmi lesquelles vous pouvez choisir :

d'abord...

Temps Transaction Symbole Type Direction Volume Prix Ordre Swap Profit Solde
01.01.2010 00:00 1   solde         0,00 10 000,00 10 000,00
04.01.2010 00:00 2 AUDUSD achat entrée 0,90 0,89977 2 0,00 0,00 10 000,00
05.01.2010 00:00 3 AUDUSD Vente Sortie 0,90 0,91188 3 5,67 1 089,90 11 095,57
05.01.2010 00:00 4 AUDUSD achat entrée 0,99 0,91220 4 0,00 0,00 11 095,57
06.012010. 00:00 5 AUDUSD Vente Sortie 0,99 0,91157 5 6,24 -62,37 11 039,44
06.012010. 00:00 6 AUDUSD achat entrée 0,99 0,91190 6 0,00 0,00 11 039,44
07.01.2010 00:00 7 AUDUSD Vente Sortie 0,99 0,91924 7 18,71 726,66 11 784,81


seconde...

Temps Transaction Symbole Type Direction Volume Prix Ordre Commission Swap Profit Solde
19.05.2010 00:00 189 AUDUSD Vente Sortie 0,36 0,86110 189 0,00 2,27 -595,44 4 221,30
19.05.2010 00:00 190 EURAUD Vente entrée 0,30 1,41280 190 0,00 0,00 0,00 4 221,30
20.05.2010 00:00 191 EURAUD achat Sortie 0,30 1,46207 191 0,00 7,43 -1 273,26 2 955,47
20.05.2010 00:00 192 AUDUSD achat entrée 0,21 0,84983 192 0,00 0,00 0,00 2 955,47


troisième

Temps Transaction Symbole Type Direction Volume Prix Ordre Swap Profit Solde
16.06.2010 00:00 230 GBPCHF achat entrée 0,06 1,67872 230 0,00 0,00 2 128,80
17.06.2010 00:00 231 GBPCHF Vente Sortie 0,06 1,66547 231 0,13 -70,25 2 058,68
17.06.2010 00:00 232 GBPCHF achat entrée 0,06 1,66635 232 0,00 0,00 2 058,68
18.06.2010 00:00 233 GBPCHF Vente Sortie 0,06 1,64705 233 0,04 -104,14 1 954,58
18.06.2010 00:00 234 AUDUSD achat entrée 0,09 0,86741 234 0,00 0,00 1 954,58
21.06.2010 00:00 235 AUDUSD Vente sortie 0,09 0,87184 235 0,57 39,87 1 995,02
21.06.2010 00:00 236 AUDUSD achat entrée 0,09 0,88105 236 0,00 0,00 1 995,02
22.06. 201000:00 237 AUDUSD Vente sortie 0,09 0,87606 237 0,57 -44,91 1 950,68
22.06. 201000:00 238 AUDUSD achat entrée 0,09 0,87637 238 0,00 0,00 1 950,68
23.06.2010 00:00 239 AUDUSD Vente sortie 0,09 0,87140 239 0,57 -44,73 1 906,52
23.06.2010 00:00 240 AUDUSD achat entrée 0,08 0,87197 240 0,00 0,00 1 906,52
24.06.2010 00:00 241 AUDUSD Vente sortie 0,08 0,87385 241 1,51 15,04 1 923,07
24.06.2010 00:00 242 AUDUSD achat entrée 0,08 0,87413 242 0,00 0,00 1 923,07
25.06.2010 00:00 243 AUDUSD Vente sortie 0,08 0,86632 243 0,50 -62,48 1 861,09
25.06.2010 00:00 244 AUDUSD achat entrée 0,08 0,86663 244 0,00 0,00 1 861,09
28.06.2010 00:00 245 AUDUSD Vente sortie 0,08 0,87375 245 0,50 56,96 1 918,55
28.06.2010 00:00 246 AUDUSD achat entrée 0,08 0,87415 246 0,00 0,00 1 918,55
29.06.2010 00:00 247 AUDUSD Vente sortie 0,08 0,87140 247 0,50 -22,00 1 897,05
29.06.2010 00:00 248 AUDUSD achat entrée 0,08 0,87173 248 0,00 0,00 1 897,05
01.07.2010 00:00 249 AUDUSD Vente sortie 0,08 0,84053 249 2,01 -249,60 1 649,46
01.07.2010 00:00 250 EURGBP Vente entrée 0,07 0,81841 250 0,00 0,00 1 649,46
02.07.2010 00:00 251 EURGBP achat sortie 0,07 0,82535 251 -0,04 -73,69 1 575,73
02.07.2010 00:00 252 EURGBP Vente entrée 0,07 0,82498 252 0,00 0,00 1 575,73
05.07.2010 00:00 253 EURGBP achat sortie 0,07 0,82676 253 -0,04 -18,93 1 556,76
05.07.2010 00:00 254 EURGBP Vente entrée 0,06 0,82604 254 0,00 0,00 1 556,76
06.07.2010 00:00 255 EURGBP achat sortie 0,06 0,82862 255 -0,04 -23,43 1 533,29


P.S. En guise de devoir : ne pas se contenter de sélectionner les paramètres d'un certain système, mais aussi de sélectionner le système qui correspond le mieux au marché à un moment donné (indice - de la banque de systèmes).


Traduit du russe par MetaQuotes Ltd.
Article original : https://www.mql5.com/ru/articles/334

Fichiers joints |
anntrainlib.mqh (9.76 KB)
matrainlib.mqh (8.94 KB)
ugalib.mqh (33.26 KB)
annexample.mq5 (4.32 KB)
maexample.mq5 (4.22 KB)
musthavelib.mqh (8.14 KB)
La stratégie Forex Tout ou Rien La stratégie Forex Tout ou Rien
Le but de cet article est de créer la stratégie de trading la plus simple qui met en œuvre le principe de jeu « Tout ou Rien ». Nous ne voulons pas créer un Expert Advisor rentable - l'objectif est d'augmenter le dépôt initial plusieurs fois avec la plus grande probabilité possible. Est-il possible de décrocher le jackpot sur le ForEx ou de tout perdre sans rien connaître à l'analyse technique et sans utiliser d'indicateurs ?
Comment ajouter de nouvelles langues à l'interface utilisateur de la plateforme MetaTrader 5 Comment ajouter de nouvelles langues à l'interface utilisateur de la plateforme MetaTrader 5
L'interface utilisateur de la plateforme MetaTrader 5 est traduite en plusieurs langues. Ne vous inquiétez pas si votre langue maternelle ne fait pas partie des langues prises en charge. Vous pouvez facilement effectuer la traduction en utilisant l'utilitaire spécial MetaTrader 5 MultiLanguage Pack, offert gratuitement par MetaQuotes Software Corp. à tous les participants. Dans cet article, nous allons montrer quelques exemples de la façon d'ajouter une nouvelle langue d'interface utilisateur à la plateforme MetaTrader 5.
Création d'Expert Advisors MQL5 en quelques minutes à l'aide d'EA Tree : Partie un Création d'Expert Advisors MQL5 en quelques minutes à l'aide d'EA Tree : Partie un
EA Tree est le premier constructeur de MetaTrader MQL5 Expert Advisor par glisser-déposer. Vous pouvez créer des MQL5 complexes à l'aide d'une interface utilisateur graphique très facile à utiliser. Dans EA Tree, les Expert Advisors sont créés en connectant des cases entre elles. Les cases peuvent contenir des fonctions MQL5, des indicateurs techniques, des indicateurs personnalisés ou des valeurs. A l'aide de « l'arbre des cases », EA Tree génère le code MQL5 de l'Expert Advisor.
Comment développer un Expert Advisor à l'aide des outils UML Comment développer un Expert Advisor à l'aide des outils UML
Cet article traite de la création d'Expert Advisors à l'aide du langage graphique UML, qui est utilisé pour la modélisation visuelle de systèmes logiciels orientés objet. Le principal avantage de cette approche est la visualisation du processus de modélisation. L'article contient un exemple qui montre la modélisation de la structure et des propriétés d'un Expert Advisor à l'aide du logiciel Ideas Modeler.