OnTesterInit

La fonction est appelée dans les EA lorsque l'évènement TesterInit se produit pour effectuer les actions nécessaires avant une optimisation dans le testeur de stratégies. Il y a 2 types de fonctions.

La version qui retourne le résultat

int  OnTesterInit(void);

Valeur de Retour

Une valeur de type int, zéro signifie l'initialisation réussie d'un EA lancé sur un graphique avant l'optimisation.

L'appel OnTesterInit() qui retourne le résultat de l'exécution est recommandé puisqu'il permet non seulement l'initialisation du programme, mais retourne également un code d'erreur dans le cas d'une fin prématurée de l'optimisation. Le retour de n'importe quelle valeur autre que INIT_SUCCEEDED (0) signifie une erreur, aucune optimisation n'est lancée.

La version sans retour de résultat n'est utilisé que pour la compatibilité avec les anciens codes. Il n'est pas recommandé de l'utiliser

void  OnTesterInit(void);

Note

L'évènement TesterInit est généré avant le lancement de l'optimisation de l'EA dans le testeur de stratégie. Au moment de cet évènement, un EA ayant les fonctions OnTesterDeInit() ou OnTesterPass() est téléchargé automatiquement sur un graphique séparé du terminal. Il a le symbole et la période qui ont été spécifiés dans le testeur.

Cet évènement reçoit les évènements TesterInit, TesterDeinit et TesterPass, mais pas Init, Deinit et NewTick ones. En conséquence, toute la logique nécessaire pour le traitement des résultats de chaque passe pendant l'optimisation devrait être implémentée dans les fonctions OnTesterInit (), OnTesterDeinit () et OnTesterPass ().

Le résultat de chaque passe individuelle pendant l'optimisation d'une stratégie peut être passé via un frame depuis la fonction OnTester () en utilisant la fonction FrameAdd ().

La fonction OnTesterInit() est utilisée pour initier un Expert Advisor avant le lancement de l'optimisation pour le traitement ultérieur des résultats de l'optimisation. Elle est toujours utilisée avec la fonction OnTesterDeinit().

Le temps d'exécution de OnTesterInit() est limité. S'il est trop grand, l'EA est stoppé obligatoirement, et l'optimisation est annulée. Un message est affiché dans le journal du testeur :

Tester        OnTesterInit works too long. Le testeur ne peut pas être initialisé.

L'exemple est pris de OnTick. La fonction OnTesterInit() est ajoutée pour définir les paramètres de l'optimisation :

//+------------------------------------------------------------------+
//|                                          OnTesterInit_Sample.mq5 |
//|                        Copyright 2018, MetaQuotes Software Corp. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2000-2024, MetaQuotes Ltd."
#property link      "https://www.mql5.com"
#property version   "1.00"
#property description "EA exemple avec la fonction OnTesterInit(),"
#property description "dans laquelle les valeurs et limites des "
#property description "entrées pendant l'optimisation sont définies"
 
input double lots=0. 1;       // volume en lots
input double kATR=3;          // longueur de la bougie de signal dans l'ATR
input int    ATRperiod=20;    // période de l'ATR
input int    holdbars=8;      // nombre de barre pour tenir la position
input int    slippage=10;     // slippage autorisé
input bool   revers=false;    // renversement de signal ? 
input ulong  EXPERT_MAGIC=0;  // nombre magique de l'EA
//--- pour stocker le handle de l'ATR
int atr_handle;
//--- nous stockerons ici les dernières valeurs de l'ATR et le corps de la bougie
double last_atr,last_body;
datetime lastbar_timeopen;
double trade_lot;
//--- heure de lancement de l'optimisation
datetime optimization_start;
//--- pour l'affichage de la durée sur un graphique à la fin de l'optimisation
string report;
//+------------------------------------------------------------------+
//| Fonction TesterInit                                              |
//+------------------------------------------------------------------+
void OnTesterInit()
  {
//--- set the values of inputs for optimization
   ParameterSetRange("lots",false,0.1,0,0,0);
   ParameterSetRange("kATR",true,3.0,1.0,0.3,7.0);
   ParameterSetRange("ATRperiod",true,10,15,1,30);
   ParameterSetRange("holdbars",true,5,3,1,15);
   ParameterSetRange("slippage",false,10,0,0,0);
   ParameterSetRange("revers",true,false,false,1,true);
   ParameterSetRange("EXPERT_MAGIC",false,123456,0,0,0);
   Print("Les valeurs initiales et les limites des paramètres d'optimisation sont définies");
//--- heure de démarrage de l'optimisation
   optimization_start=TimeLocal();
   report=StringFormat("%s: optimisation lancée à %s",
                       __FUNCTION__,TimeToString(TimeLocal(),TIME_MINUTES|TIME_SECONDS));
//--- affiche les messages sur le graphique et dans le journal du terminal
   Print(report);
   Comment(report);
//---   
  }
//+------------------------------------------------------------------+
//| Fonction TesterDeinit                                            |
//+------------------------------------------------------------------+
void OnTesterDeinit()
  {
//--- durée de l'optimisation
   string log_message=StringFormat("%s: l'optimisation a pris %d secondes",
                                   __FUNCTION__,TimeLocal()-optimization_start);
   PrintFormat(log_message);
   report=report+"\r\n"+log_message;
   Comment(report);
  }
//+------------------------------------------------------------------+
//| Fonction d'initialisation de l'expert                            |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- initialisation des variables globales
   last_atr=0;
   last_body=0;
//--- définit le volume correct
   double min_lot=SymbolInfoDouble(_Symbol,SYMBOL_VOLUME_MIN);
   trade_lot=lots>min_lot? lots:min_lot;   
//--- crée le handle de l'indicateur ATR
   atr_handle=iATR(_Symbol,_Period,ATRperiod);
   if(atr_handle==INVALID_HANDLE)
     {
      PrintFormat("%s: échec de la création de iATR, code d'erreur %d",__FUNCTION__,GetLastError());
      return(INIT_FAILED);
     }
//--- initialisation réussie de l'EA
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//| Fonction de tick de l'Expert                                     |
//+------------------------------------------------------------------+
void OnTick()
  {
//--- signal de trading
   static int signal=0; // +1 signifie un signal d'achat, -1 signifie un signal de vente
//--- vérifie et ferme les anciennes positions ouvertes plus de 'holdbars' barres auparavant
   ClosePositionsByBars(holdbars,slippage,EXPERT_MAGIC);
//--- vérifie si c'est une nouvelle barre
   if(isNewBar())
     {
      //--- vérifie la présence d'un signal 
      signal=CheckSignal();
     }
//--- si une position de type netting est ouverte, ignore le signal - attends jusqu'à sa fermeture
   if(signal!=0 && PositionsTotal()>0 && (ENUM_ACCOUNT_MARGIN_MODE)AccountInfoInteger(ACCOUNT_MARGIN_MODE)==ACCOUNT_MARGIN_MODE_RETAIL_NETTING)
     {
      signal=0;
      return// sors de la fonction NewTick et n'entre pas sur le marché avant qu'une nouvelle barre n'apparaisse
     }
//--- pour un compte de type hedging, chaque position est conservée et fermée séparément
   if(signal!=0)
     {
      //--- signal d'achat
      if(signal>0)
        {
         PrintFormat("%s: Signal d'achat ! Revers=%s",__FUNCTION__,string(revers));
         if(Buy(trade_lot,slippage,EXPERT_MAGIC))
            signal=0;
        }
      //--- signal de vente
      if(signal<0)
        {
         PrintFormat("%s: Signal de vente ! Revers=%s",__FUNCTION__,string(revers));
         if(Sell(trade_lot,slippage,EXPERT_MAGIC))
            signal=0;
        }
     }
//--- fin de la fonction OnTick
  }
//+------------------------------------------------------------------+
//| Vérifie la présence d'un nouveau signal de trading               |
//+------------------------------------------------------------------+
int CheckSignal()
  {
//--- 0 signifie aucun signal
   int res=0;
//--- récupère la valeur de l'ATR sur l'avant dernière barre (l'indice de la barre est 2)
   double atr_value[1];
   if(CopyBuffer(atr_handle,0,2,1,atr_value)!=-1)
     {
      last_atr=atr_value[0];
      //--- récupère les données de la dernière barre fermée dans le tableau d'éléments de type MqlRates
      MqlRates bar[1];
      if(CopyRates(_Symbol,_Period,1,1,bar)!=-1)
        {
         //--- calcule la taille du corps de la dernière barre complétée
         last_body=bar[0].close-bar[0].open;
         //--- si le corps de la dernière barre (avec l'indice 1) est supérieur à la valeur précédente de l'ATR (sur la barre d'indice 2), un signal de trading est reçu
         if(MathAbs(last_body)>kATR*last_atr)
            res=last_body>0?1:-1; // valeur positive pour une bougie haussière
        }
      else
         PrintFormat("%s: Impossible de recevoir la dernière barre ! Erreur",__FUNCTION__,GetLastError());
     }
   else
      PrintFormat("%s: Impossible de recevoir la valeur de l'ATR ! Erreur",__FUNCTION__,GetLastError());
//--- si le mode de trading renversé est activé
   res=revers?-res:res;  // retourne le signal si nécessaire (retourne -1 au lieu de 1 et vice versa)
//--- retourne la valeur du signal de trading
   return (res);
  }
//+------------------------------------------------------------------+
//|  Retourne 'true' lorsqu'une nouvelle barre apparaît              |
//+------------------------------------------------------------------+
bool isNewBar(const bool print_log=true)
  {
   static datetime bartime=0; // stocke l'heure d'ouverture de la barre courante
//--- récupère l'heure d'ouverture de la barre zéro
   datetime currbar_time=iTime(_Symbol,_Period,0);
//--- si l'heure d'ouverture change, une nouvelle barre est arrivée
   if(bartime!=currbar_time)
     {
      bartime=currbar_time;
      lastbar_timeopen=bartime;
      //--- affiche les données de l'heure d'ouverture de la nouvelle barre dans le journal     
      if(print_log && !(MQLInfoInteger(MQL_OPTIMIZATION)||MQLInfoInteger(MQL_TESTER)))
        {
         //--- affiche un message avec l'heure d'ouverture de la nouvelle barre
         PrintFormat("%s: nouvelle barre sur %s %s ouverte à %s",__FUNCTION__,_Symbol,
                     StringSubstr(EnumToString(_Period),7),
                     TimeToString(TimeCurrent(),TIME_SECONDS));
         //--- récupère les données du dernier tick
         MqlTick last_tick;
         if(!SymbolInfoTick(Symbol(),last_tick))
            Print("Echec de SymbolInfoTick(), erreur = ",GetLastError());
         //--- affiche l'heure du dernier tick en millisecondes
         PrintFormat("Le dernier tick était à %s.%03d",
                     TimeToString(last_tick.time,TIME_SECONDS),last_tick.time_msc%1000);
        }
      //--- nous avons une nouvelle barre
      return (true);
     }
//--- aucune nouvelle barre
   return (false);
  }
//+------------------------------------------------------------------+
//| Achète au prix du marché avec le volume spécifié                 |
//+------------------------------------------------------------------+
bool Buy(double volume,ulong deviation=10,ulong  magicnumber=0)
  {
//--- achète au prix du marché
   return (MarketOrder(ORDER_TYPE_BUY,volume,deviation,magicnumber));
  }
//+------------------------------------------------------------------+
//| Vends au prix du marché avec le volume spécifié                  |
//+------------------------------------------------------------------+
bool Sell(double volume,ulong deviation=10,ulong  magicnumber=0)
  {
//--- vends au prix du marché
   return (MarketOrder(ORDER_TYPE_SELL,volume,deviation,magicnumber));
  }
//+------------------------------------------------------------------+
//| Ferme les positions détenues trop longtemps (en barres)          |
//+------------------------------------------------------------------+
void ClosePositionsByBars(int holdtimebars,ulong deviation=10,ulong  magicnumber=0)
  {
   int total=PositionsTotal(); // nombre de positions ouvertes
//--- itère sur les positions ouvertes
   for(int i=total-1; i>=0; i--)
     {
      //--- paramètres de la position
      ulong  position_ticket=PositionGetTicket(i);                                      // ticket de la position
      string position_symbol=PositionGetString(POSITION_SYMBOL);                        // symbole 
      ulong  magic=PositionGetInteger(POSITION_MAGIC);                                  // MagicNumber de la position
      datetime position_open=(datetime)PositionGetInteger(POSITION_TIME);               // heure d'ouverture de la position
      int bars=iBarShift(_Symbol,PERIOD_CURRENT,position_open)+1;                       // depuis combien de barres une position est-elle ouverte
 
      //--- si la position est trop vieille et que le MagicNumber et le symbole correspondent
      if(bars>holdtimebars && magic==magicnumber && position_symbol==_Symbol)
        {
         int    digits=(int)SymbolInfoInteger(position_symbol,SYMBOL_DIGITS);           // nombre de décimales
         double volume=PositionGetDouble(POSITION_VOLUME);                              // volume de la position
         ENUM_POSITION_TYPE type=(ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE); // type de la position
         string str_type=StringSubstr(EnumToString(type),14);
         StringToLower(str_type); // mets le texte en minuscules
         PrintFormat("Ferme la position #%I64u %s %s %.2f",
                     position_ticket,position_symbol,str_type,volume);
         //--- définit le type de l'ordre et envoie une demande de trade
         if(type==POSITION_TYPE_BUY)
            MarketOrder(ORDER_TYPE_SELL,volume,deviation,magicnumber,position_ticket);
         else
            MarketOrder(ORDER_TYPE_BUY,volume,deviation,magicnumber,position_ticket);
        }
     }
  }
//+------------------------------------------------------------------+
//| Prépare et envoie une demande de trade                           |
//+------------------------------------------------------------------+
bool MarketOrder(ENUM_ORDER_TYPE type,double volume,ulong slip,ulong magicnumber,ulong pos_ticket=0)
  {
//--- déclare et initialise les structures
   MqlTradeRequest request={};
   MqlTradeResult  result={};
   double price=SymbolInfoDouble(Symbol(),SYMBOL_BID);
   if(type==ORDER_TYPE_BUY)
      price=SymbolInfoDouble(Symbol(),SYMBOL_ASK);
//--- paramètres de la demande
   request.action   =TRADE_ACTION_DEAL;                     // trading operation type
   request.position =pos_ticket;                            // position ticket if closing
   request.symbol   =Symbol();                              // symbole
   request.volume   =volume;                                // volume 
   request.type     =type;                                  // type de l'ordre
   request.price    =price;                                 // prix du trade
   request.deviation=slip;                                  // déviation du prix autorisée
   request.magic    =magicnumber;                           // MagicNumber de l'ordre
//--- envoie une demande
   if(!OrderSend(request,result))
     {
      //--- affiche les informations de l'échec
      PrintFormat("OrderSend %s %s %.2f à %.5f, erreur %d",
                  request.symbol,EnumToString(type),volume,request.price,GetLastError());
      return (false);
     }
//--- information sur l'opération réussie
   PrintFormat("retcode=%u  deal=%I64u  order=%I64u",result.retcode,result.deal,result.order);
   return (true);
  }

Voir également

Test de stratégies de trading, Travailler avec les résultats d'optimisation, OnTesterDeinit, OnTesterPass, ParameterGetRange, ParameterSetRange