OnTesterInit

Se llama en los expertos al suceder el evento TesterInit para el procesamiento de las acciones necesarias antes de comenzar la optimización en el simulador de estrategias. Existen dos variantes de la función.

La versión con retorno del resultado

int  OnTesterInit(void);

Valor retornado

Valor del tipo int, cero indica que el experto iniciado en el gráfico antes del comienzo de la optimización se ha inicializado con éxito.

Es prioritario el uso de la llamada de OnTesterInit() con retorno del resultado de ejecución, ya que este método permite no solo ejecutar la inicialización del programa, sino también retornar el código de error en el caso de que el programa finalice antes de tiempo. Retorna cualquier valor distinto a INIT_SUCCEEDED (0); indica que existe un error y la optimización no será iniciada.

La versión sin retorno de resultado se ha dejado solo por compatibilidad con los códigos antiguos. No se recomienda su uso

void  OnTesterInit(void);

Observación

El evento TesterInit se genera de forma automática antes de comenzar la optimización del experto en el simulador de estrategias. Según este evento, el experto que tenga el manejador OnTesterDeinit() y/o OnTesterPass() se cargará automáticamente en un gráfico del terminal aparte con el símbolo y periodo indicado en el simulador.

Este experto obtiene los eventos TesterInit, TesterDeinit y TesterPass, pero no obtiene los eventos Init, Deinit y NewTick. Por consiguiente, la lógica necesaria para el procesamiento de los resultados de cada pasada en el proceso de optimización se debe implementar en los manejadores OnTesterInit(), OnTesterDeinit() y OnTesterPass().

El resultado de cada pasada única en la optimización de la estrategia se puede transmitir a través de un frame del manejador OnTester() con ayuda de la función FrameAdd().

La función OnTesterInit() se ha pensado para inicializar el experto antes de comenzar la optimización para el posterior procesamiento de los resultados de optimización. Siempre se usa de forma conjunta con el manejador OnTesterDeinit().

A la ejecución de OnTesterInit() se dedica un tiempo limitado; una vez se supera el mismo, el funcionamiento del experto finalizará forzosamente, mientras que la propia optimización será cancelada. En este caso, además, el Diario de registro del simulador mostrará el siguiente mensaje:

Tester        OnTesterInit works too long. Tester cannot be initialized.

El ejemplo se ha tomado de OnTick, el manejador OnTesterInit() ha sido añadido para establecer los parámetros de optimización:

//+------------------------------------------------------------------+
//|                                          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 "Ejemplo de asesor con el manejador OnTesterInit()"
#property description "en el que se establecen los valores y límites "
#property description "de los parámetros de entrada para la optimización"
 
input double lots=0. 1;       // volumen en lotes
input double kATR=3;          // longitud de la vela de señal en ATR
input int    ATRperiod=20;    // periodo del indicador ATR
input int    holdbars=8;      // cuántas barras mantenemos la posición
input int    slippage=10;     // Deslizamiento permitido
input bool   revers=false;    // ¿viramos la señal? 
input ulong  EXPERT_MAGIC=0;  // MagicNumber del experto
//--- para guardar el manejador del indicador ATR
int atr_handle;
//--- aquí vamos a guardar los últimos valores de ATR y del cuerpo de la vela
double last_atr,last_body;
datetime lastbar_timeopen;
double trade_lot;
//--- recordamos la hora de comienzo de la optimización
datetime optimization_start;
//--- para mostrar la duración en un gráfico tras finalizar la optimización
string report;
//+------------------------------------------------------------------+
//| TesterInit function                                              |
//+------------------------------------------------------------------+
void OnTesterInit()
  {
//--- establecemos los valores de los parámetros de entrada para la optimización
   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("Se han establecido los valores iniciales y los límites de los parámetros de optimización");
//--- recordamos el inicio de la optimización
   optimization_start=TimeLocal();
   report=StringFormat("%s: la optimización ha sido iniciada en %s",
                       __FUNCTION__,TimeToString(TimeLocal(),TIME_MINUTES|TIME_SECONDS));
//--- mostramos los mensajes en el gráfico y en el diario de registro del terminal
   Print(report);
   Comment(report);
//---   
  }
//+------------------------------------------------------------------+
//| TesterDeinit function                                            |
//+------------------------------------------------------------------+
void OnTesterDeinit()
  {
//--- duración de la optimización
   string log_message=StringFormat("%s: la optimización ha durado %d segundos",
                                   __FUNCTION__,TimeLocal()-optimization_start);
   PrintFormat(log_message);
   report=report+"\r\n"+log_message;
   Comment(report);
  }
//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- inicializamos las variables globales
   last_atr=0;
   last_body=0;
//--- establecemos el volumen correcto
   double min_lot=SymbolInfoDouble(_Symbol,SYMBOL_VOLUME_MIN);
   trade_lot=lots>min_lot? lots:min_lot;   
//--- creamos el manejador del indicador ATR
   atr_handle=iATR(_Symbol,_Period,ATRperiod);
   if(atr_handle==INVALID_HANDLE)
     {
      PrintFormat("%s: no se ha logrado crear iATR, código de error %d",__FUNCTION__,GetLastError());
      return(INIT_FAILED);
     }
//--- el experto se ha inicializado correctamente
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
  {
//--- señal comercial
   static int signal=0; // +1 indica la señal de compra, -1 indica la señal de venta
//--- comprobamos y cerramos las posiciones antiguas, abiertas hace más de "holdbars" barras
   ClosePositionsByBars(holdbars,slippage,EXPERT_MAGIC);
//--- comprobamos la aparición de una nueva barra
   if(isNewBar())
     {
      //--- comprobamos la presencia de una señal      
      signal=CheckSignal();
     }
//--- si tenemos abierta una posición de compensación, omitimos la señal hasta que la posición se cierre
   if(signal!=0 && PositionsTotal()>0 && (ENUM_ACCOUNT_MARGIN_MODE)AccountInfoInteger(ACCOUNT_MARGIN_MODE)==ACCOUNT_MARGIN_MODE_RETAIL_NETTING)
     {
      signal=0;
      return// salimos del manejador de eventos NewTick y no entramos en el mercado hasta que aparezca una nueva barra
     }
//--- para la cuenta de cobertura, cada posición vive y se cierra por separado
   if(signal!=0)
     {
      //--- señal de compra
      if(signal>0)
        {
         PrintFormat("%s: ¡Hay señal de compra! Revers=%s",__FUNCTION__,string(revers));
         if(Buy(trade_lot,slippage,EXPERT_MAGIC))
            signal=0;
        }
      //--- señal de venta
      if(signal<0)
        {
         PrintFormat("%s: ¡Hay señal de venta! Revers=%s",__FUNCTION__,string(revers));
         if(Sell(trade_lot,slippage,EXPERT_MAGIC))
            signal=0;
        }
     }
//--- final de la función OnTick
  }
//+------------------------------------------------------------------+
//| Comprueba la presencia de una señal comercial                    |
//+------------------------------------------------------------------+
int CheckSignal()
  {
//--- 0 indica la ausencia de señal
   int res=0;
//--- obtenemos el valor de ATR en la penúltima barra finalizada (el índice de la barra es igual a 2)
   double atr_value[1];
   if(CopyBuffer(atr_handle,0,2,1,atr_value)!=-1)
     {
      last_atr=atr_value[0];
      //--- obtenemos los datos de la última barra cerrada en una matriz del tipo MqlRates
      MqlRates bar[1];
      if(CopyRates(_Symbol,_Period,1,1,bar)!=-1)
        {
         //--- calculamos el tamaño del cuerpo de la barra en la última barra cerrada
         last_body=bar[0].close-bar[0].open;
         //--- si el cuerpo de la última barra (con índice 1) supera el anterior valor de ATR (en la barra con índice 2), entonces la señal comercial ha sido recibida
         if(MathAbs(last_body)>kATR*last_atr)
            res=last_body>0?1:-1; // para la vela ascendente el valor es positivo
        }
      else
         PrintFormat("%s: ¡No se ha podido obtener la última barra! Error",__FUNCTION__,GetLastError());
     }
   else
      PrintFormat("%s: ¡No se ha podido obtener el valor del indicador ATR! Error",__FUNCTION__,GetLastError());
//--- si está activado el modo de comercio reverso
   res=revers?-res:res;  // si es necesario, viramos la señal (en lugar de 1, retornamos -1, y en lugar de -1, retornamos +1)
//--- retornamos el valor de la señal comercial
   return (res);
  }
//+------------------------------------------------------------------+
//|  Retorna true al aparecer una nueva barra                        |
//+------------------------------------------------------------------+
bool isNewBar(const bool print_log=true)
  {
   static datetime bartime=0; // guardamos la hora de apertura de la barra actual
//--- obtenemos la hora de apertura de la barra cero
   datetime currbar_time=iTime(_Symbol,_Period,0);
//--- si la hora de apertura ha cambiado, significa que ha aparecido una nueva barra
   if(bartime!=currbar_time)
     {
      bartime=currbar_time;
      lastbar_timeopen=bartime;
      //--- es necesario o no mostrar en el log la información sobre la hora de apertura de una nueva barra      
      if(print_log && !(MQLInfoInteger(MQL_OPTIMIZATION)||MQLInfoInteger(MQL_TESTER)))
        {
         //--- mostramos el mensaje sobre la hora de apertura de una nueva barra
         PrintFormat("%s: new bar on %s %s opened at %s",__FUNCTION__,_Symbol,
                     StringSubstr(EnumToString(_Period),7),
                     TimeToString(TimeCurrent(),TIME_SECONDS));
         //--- obtenemos los datos sobre el último tick
         MqlTick last_tick;
         if(!SymbolInfoTick(Symbol(),last_tick))
            Print("SymbolInfoTick() failed, error = ",GetLastError());
         //--- mostramos la hora del útlimo tick con una precisión de hasta un milisegundo
         PrintFormat("Last tick was at %s.%03d",
                     TimeToString(last_tick.time,TIME_SECONDS),last_tick.time_msc%1000);
        }
      //--- tenemos una nueva barra
      return (true);
     }
//--- no hay barras nuevas
   return (false);
  }
//+------------------------------------------------------------------+
//| Comprar según el mercado con un volumen establecido              |
//+------------------------------------------------------------------+
bool Buy(double volume,ulong deviation=10,ulong  magicnumber=0)
  {
//--- compramos al precio de mercado
   return (MarketOrder(ORDER_TYPE_BUY,volume,deviation,magicnumber));
  }
//+------------------------------------------------------------------+
//| Vender según el mercado con un volumen establecido               |
//+------------------------------------------------------------------+
bool Sell(double volume,ulong deviation=10,ulong  magicnumber=0)
  {
//--- vendemos al precio de mercado
   return (MarketOrder(ORDER_TYPE_SELL,volume,deviation,magicnumber));
  }
//+------------------------------------------------------------------+
//| Cierre de posiciones según el tiempo de mantenimiento en barras  |
//+------------------------------------------------------------------+
void ClosePositionsByBars(int holdtimebars,ulong deviation=10,ulong  magicnumber=0)
  {
   int total=PositionsTotal(); // número de posiciones abiertas   
//--- iterar todas las posiciones abiertas
   for(int i=total-1; i>=0; i--)
     {
      //--- parámetros de la posición
      ulong  position_ticket=PositionGetTicket(i);                                      // ticket de la posición
      string position_symbol=PositionGetString(POSITION_SYMBOL);                        // símbolo 
      ulong  magic=PositionGetInteger(POSITION_MAGIC);                                  // MagicNumber de la posición
      datetime position_open=(datetime)PositionGetInteger(POSITION_TIME);               // hora de apertura de la posición
      int bars=iBarShift(_Symbol,PERIOD_CURRENT,position_open)+1;                       // cuántas barras atrás se ha abierto la posición
 
      //--- si la posición vive durante bastante tiempo, y además el MagicNumber y el símbolo coinciden
      if(bars>holdtimebars && magic==magicnumber && position_symbol==_Symbol)
        {
         int    digits=(int)SymbolInfoInteger(position_symbol,SYMBOL_DIGITS);           // número de dígitos decimales
         double volume=PositionGetDouble(POSITION_VOLUME);                              // volumen de la posición
         ENUM_POSITION_TYPE type=(ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE); // tipo de la posición
         string str_type=StringSubstr(EnumToString(type),14);
         StringToLower(str_type); // ponemos el texto en minúsculas para formatear correctamente el mensaje
         PrintFormat("Cerramos la posición #%I64u %s %s %.2f",
                     position_ticket,position_symbol,str_type,volume);
         //--- estableciendo el tipo de orden y enviando la solicitud comercial
         if(type==POSITION_TYPE_BUY)
            MarketOrder(ORDER_TYPE_SELL,volume,deviation,magicnumber,position_ticket);
         else
            MarketOrder(ORDER_TYPE_BUY,volume,deviation,magicnumber,position_ticket);
        }
     }
  }
//+------------------------------------------------------------------+
//| Preparando y enviando la solicitud comercial                     |
//+------------------------------------------------------------------+
bool MarketOrder(ENUM_ORDER_TYPE type,double volume,ulong slip,ulong magicnumber,ulong pos_ticket=0)
  {
//--- declarando e inicializando las estructuras
   MqlTradeRequest request={};
   MqlTradeResult  result={};
   double price=SymbolInfoDouble(Symbol(),SYMBOL_BID);
   if(type==ORDER_TYPE_BUY)
      price=SymbolInfoDouble(Symbol(),SYMBOL_ASK);
//--- parámetros de la solicitud
   request.action   =TRADE_ACTION_DEAL;                     // tipo de operación comercial
   request.position =pos_ticket;                            // ticket de la posición, si cerramos
   request.symbol   =Symbol();                              // símbolo
   request.volume   =volume;                                // volumen 
   request.type     =type;                                  // tipo de orden
   request.price    =price;                                 // precio de finalización de la transacción
   request.deviation=slip;                                  // desviación permitida respecto al precio
   request.magic    =magicnumber;                           // MagicNumber de la orden
//--- envío de la solicitud
   if(!OrderSend(request,result))
     {
      //--- mostramos la información sobre el fallo
      PrintFormat("OrderSend %s %s %.2f at %.5f error %d",
                  request.symbol,EnumToString(type),volume,request.price,GetLastError());
      return (false);
     }
//--- comunicamos que la operación ha tenido éxito
   PrintFormat("retcode=%u  deal=%I64u  order=%I64u",result.retcode,result.deal,result.order);
   return (true);
  }

Ver también

Simulación de estrategias comerciales, Trabajo con los resultados de la optimización, OnTesterDeinit, OnTesterPass, ParameterGetRange, ParameterSetRange