OnTick

Se llama en los expertos al suceder el evento NewTick para el procesamiento de una nueva cotización.

void  OnTick(void);

Valor retornado

No hay valor retornado

Observación

El evento NewTick se genera solo para los expertos cuando llega un nuevo tick del símbolo a cuyo gráfico está fijado el experto. Es inútil definir la función OnTick() en un indicador o script personalizado, puesto que el evento NewTick no se genera para ellos.

El evento Tick se genera solo para los expertos, pero esto no significa que los expertos estén obligados a tener la función OnTick(), ya que para los expertos se generan no solo los eventos NewTick, sino también los eventos Timer, BookEvent y ChartEvent.

Todos los eventos se procesan uno tras otro según su orden de llegada. Si en la cola ya hay un evento NewTick o bien este evento está siendo procesado, el nuevo evento NewTick no se pondrá en la cola del programa mql5.

El evento NewTick se genera independientemente de si está permitido o no el comercio automático (botón "Comercio automático"). La prohibición del comercio automático implica solo la prohibición del envío de solicitudes comerciales desde un experto; el funcionamiento del experto, en este caso, no se detendrá.

La prohibición del comercio automático pulsando el botón "Comercio automático" no interrumpe la actual ejecución de la función OnTick().

Ejemplo de experto en el que toda la lógica comercial se encuentra implementada en OnTick()

//+------------------------------------------------------------------+
//|                                                   TradeByATR.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 que comercia en la dirección \"explosiva\" de la vela"
#property description La vela "\"explosiva\" tiene el cuerpo de un tamaño superior a k*ATR"
#property description "El parámetro \"revers\" invierte la dirección de la señal"
 
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;
//+------------------------------------------------------------------+
//| 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 deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
//--- comunicamos el código de finalización del funcionamiento del experto
   Print(__FILE__,": Código del motivo de la desinicialización = ",reason);
  }
//+------------------------------------------------------------------+
//| 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

Funciones de procesamiento de eventos, Ejecución de programas, Eventos del terminal de cliente, OnTimer, OnBookEvent, OnChartEvent