[SOLVED] Los indicadores no se instancian correctamente cuando se llaman/crean desde un indicador de un marco de tiempo de trabajo diferente.

 

ACTUALIZACIÓN: Vea la solución a continuación

CopyBuffer() lanza un error de 4806 (Datos del indicador no accesibles) cuando se llama a un indicador con un Time-Frame diferente desde el código de un indicador. Ocurre cuando se llama a un indicador válido con un Time-Frame diferente al actual. El error sólo aparece durante la inicialización y la primera llamada a OnCalculate() ANTES de los datos del primer tick. Para aislar el fallo se han aplicado los siguientes métodos:

Este es el bloque de código utilizado para probar la salida de CopyBuffer() cuando se llama desde un script, EA e indicador.

#include <Indicators\Trend.mqh>


   CiMA ima;
   ima.Create(_Symbol,PERIOD_H1,20,0,MODE_SMA,PRICE_CLOSE);
   ima.Refresh();
  
   CIndicatorBuffer *buff = ima.At(0);
   int total = buff.Total();
   Print(__LINE__," ",__FUNCSIG__," ",buff.Name()," Buffer size = ",total);
   for(int i=0;i<total;i++){
      if(i>2) break;
      else{
         Print(__LINE__," ",__FUNCSIG__," ",ima.PeriodDescription()," iMA(",i,") value = ",DoubleToString(ima.Main(i),_Digits));  
      }
   }

Código completo del indicador:

#property indicator_chart_window

#include <Indicators\Trend.mqh>
#include <errordescription.mqh>

CiMA ima;
int m_bufferSize = -1;
//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int OnInit()
  {
   static int iCnt = 0;
//--- indicator buffers mapping
      Print("-----------------------",TimeCurrent(),"--------------------------");
//---
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//| Custom indicator iteration function                              |
//+------------------------------------------------------------------+
int OnCalculate(const int rates_total,
                const int prev_calculated,
                const datetime &time[],
                const double &open[],
                const double &high[],
                const double &low[],
                const double &close[],
                const long &tick_volume[],
                const long &volume[],
                const int &spread[])
  {
//---
   if(rates_total != prev_calculated || m_bufferSize < 1 ){
      ResetLastError();
      ima.Create(_Symbol,PERIOD_H1,20,0,MODE_SMA,PRICE_CLOSE);
      ima.Refresh();
      
      CIndicatorBuffer *buff = ima.At(0);
      m_bufferSize = buff.Total();
      Print(__LINE__," ",__FUNCSIG__," ",buff.Name()," Buffer size = ",m_bufferSize);
      if(m_bufferSize < 1){
         Print(ErrorDescription(GetLastError()));
      } else {
         for(int i=0;i<m_bufferSize;i++){
            if(i>2) break;
            else{
               Print(__LINE__," ",__FUNCTION__," ",ima.PeriodDescription()," iMA(",i,") value = ",DoubleToString(ima.Main(i),_Digits));  
            }
         }
      }
   }
//--- return value of prev_calculated for next call
   return(rates_total);
}
//+------------------------------------------------------------------+

La única forma de no recibir un error es lanzarlo en gráficos H1 (mismo TF).

Enlaces a post del foro con el mismo problema:

https://www.mql5.com/en/forum/73274

https://www.mql5.com/en/forum/13676

https://www.mql5.com/en/forum/30958

https://www.mql5.com/en/forum/16614

SOLUCIÓN:

La solución fue crear el indicador en OnInit() y establecer EventSetMillisecondTimer a 1ms. Esto permitió que OnCalculate() regresara después de su primera pasada y llamara rápidamente a OnTimer para una segunda pasada. Sólo fue necesaria una llamada al evento OnTimer para fijarlo y no se necesitó más tiempo de retraso para los cálculos.

//+------------------------------------------------------------------+
//|                                                    THROWAWAY.mq5 |
//|                                                      nicholishen |
//|                                   www.reddit.com/u/nicholishenFX |
//+------------------------------------------------------------------+
#property copyright "nicholishen"
#property link      "www.reddit.com/u/nicholishenFX"
#property version   "1.00"
#property indicator_chart_window

#include <Indicators\Trend.mqh>
#include <errordescription.mqh>

CiMA ima;
int m_bufferSize = -1;
bool timedEvent = false;
//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int OnInit()
  {
      int waitMS = 1;
      Print("-----------------------",TimeCurrent(),"--------------------------");
  
      ima.Create(_Symbol,PERIOD_H1,20,0,MODE_SMA,PRICE_CLOSE);
      EventSetMillisecondTimer(waitMS);
      Print("OnTimer set to ",waitMS," ms");
      
//---
   return(INIT_SUCCEEDED);
  }

void OnTimer()
  {
//---
   ima.Refresh();
   EventKillTimer();
   timedEvent = true;
  
  }
//+------------------------------------------------------------------+
//| Custom indicator iteration function                              |
//+------------------------------------------------------------------+
int OnCalculate(const int rates_total,
                const int prev_calculated,
                const datetime &time[],
                const double &open[],
                const double &high[],
                const double &low[],
                const double &close[],
                const long &tick_volume[],
                const long &volume[],
                const int &spread[])
  {

   static int tickCnt = 0;
   tickCnt++;
  
   if(!timedEvent)return rates_total;
//---
   if(rates_total != prev_calculated || m_bufferSize < 1 ){
      ResetLastError();
      CIndicatorBuffer *buff = ima.At(0);
      m_bufferSize = buff.Total();
      if(m_bufferSize <=0) ima.Refresh();
      // try wait with looping  
      
      if(m_bufferSize < 1){
         Print(ErrorDescription(GetLastError()));
        
      } else {
         for(int i=0;i<m_bufferSize;i++){
            if(i>2) break;
            else{
               Print(__LINE__," ",__FUNCTION__,buff.Name(),
                     " Buffer size = ",m_bufferSize,
                     " | ",ima.PeriodDescription()," iMA(",i,") value = ",
                     DoubleToString(ima.Main(i),_Digits),
                     " | Tick-count = ",tickCnt
                     );  
            }
         }
      }
   }
//--- return value of prev_calculated for next call
   return(rates_total);
}
//+------------------------------------------------------------------+
Error 4806 while copying buffers
Error 4806 while copying buffers
  • www.mql5.com
com/en/articles/100, but tried to change it to use the CCI indicator only.
 
¿Alguien sabe de una solución?
 

Me temo que esto no es un error en MT5, sino un error en su código. Por cierto, como todos los temas que has reportado. Tienes que lidiar con la plataforma/lenguaje tal y como fue diseñada, no como tú crees que es o te gustaría que fuera.

¿Por qué usas ima.Create() en OnCalculate() ? Obtienes un handle pero los datos aún no están disponibles, obtienes un error y luego tu código no es llamado nunca más.

P.D: ¿Qué significa "funciona sin acceso a los datos"? ?
 
Alain Verleyen:

Me temo que esto no es un error en MT5, sino un error en su código. Por cierto, como todos los temas que has reportado. Tienes que lidiar con la plataforma/lenguaje tal y como fue diseñada, no como tú crees que es o te gustaría que fuera.

¿Por qué usas ima.Create() en OnCalculate() ? Obtienes un handle pero los datos aún no están disponibles, obtienes un error y luego tu código no es llamado nunca más.

P.D: ¿Qué significa "funciona sin acceso a los datos"? ?
Me temo que esto es en realidad un error en la plataforma. Estoy ejecutando el mismo bloque de código exacto en el OnInit() en el experto y no obtengo ningún error, mientras que OnInit() en el indicador arroja errores. Funciona sin acceso a los datos significa que funciona fuera de línea, en el probador, o fuera de las horas de mercado. Una llamada a cualquier indicador debería instanciarlo desde cualquier lugar y en cualquier momento, y el hecho de que la plataforma sea inconsistente en esos aspectos significa que esto no es una característica sino un error. El hecho de tener ima.Create dentro de oncalculate es sólo un ejemplo porque también falla al instanciar el indicador en cualquier marco de tiempo diferente -desde cualquier lugar que lo llames dentro del indicador, antes del primer tick (actualización de datos). Puedes refrescarlo infinitas veces pero no accederá a los datos del indicador hasta después de que oncalculate se ejecute exactamente una vez y luego regrese. Funciona en la siguiente pasada cuando llega un nuevo tick. Error.

Nuevamente me gustaría enfatizar que funciona correctamente desde cualquier lugar en expertos y scripts, sólo roto de alguna manera en los indicadores.

 
nicholishen:
Me temo que esto es en realidad un error en la plataforma. Estoy ejecutando el mismo bloque de código exacto en el OnInit() en experto y no obtener ningún error, mientras que OnInit() en el indicador arroja errores. Funciona sin acceso a los datos significa que funciona fuera de línea, en el probador, o fuera de las horas de mercado. Una llamada a cualquier indicador debería instanciarlo desde cualquier lugar y en cualquier momento, y el hecho de que la plataforma sea inconsistente en esos aspectos significa que esto no es una característica sino un error. El hecho de tener ima.Create dentro de oncalculate es sólo un ejemplo porque también falla al instanciar el indicador en cualquier marco de tiempo diferente -desde cualquier lugar que lo llame dentro del indicador, antes del primer tick (actualización de datos). Nuevamente me gustaría enfatizar que funciona correctamente desde cualquier lugar en los expertos y en los scripts, sólo se rompe de alguna manera en los indicadores.

Vale que no me creas, estás en tu derecho, pero te equivocas

Sólo puedo sugerirle que escriba al ServiceDesk, y por favor informe de su respuesta aquí.

 
Alain Verleyen:

Si no me cree, está en su derecho, pero se equivoca.

Sólo puedo sugerirte que escribas al ServiceDesk, y que por favor informes de su respuesta aquí.

Gracias, lo haré. Si me equivoco, ¿por qué exactamente funciona en los expertos y en los scripts, pero no en los indicadores?
 
nicholishen:
Gracias, lo haré. Si me equivoco, ¿por qué exactamente funciona en los expertos y en los scripts, pero no desde los indicadores?

Porque todos los indicadores de un símbolo se ejecutan en el mismo hilo. El probador de estrategias, el EA y el script son situaciones diferentes.

Aunque vamos a ver la respuesta de ServiceDesk. Tal vez estoy equivocado :-)

 
Alain Verleyen:

Porque todos los indicadores de un símbolo se ejecutan en el mismo hilo. El probador de estrategias, el EA y el script son situaciones diferentes.

Aunque veamos la respuesta de ServiceDesk. Tal vez me equivoque :-)

Consideremos que este es el caso...

  • ¿Por qué se puede instanciar un indicador del mismo time-frame y acceder a sus datos inmediatamente, pero no un time-frame diferente?
 
nicholishen:
  • ¿Por qué se puede instanciar un indicador del mismo marco temporal y acceder a sus datos inmediatamente?

En realidad, esto significa que tiene la suerte de que los datos ya están disponibles. Esto no está garantizado. También puede fallar.
 
Stanislav Korotky:
En realidad, esto significa que tienes la suerte de que los datos ya están disponibles. Esto no está garantizado. También puede fallar.

Esto significa que si los datos están disponibles inmediatamente para un script o EA, entonces estarían igualmente disponibles para el indicador (como en este caso no es un problema de disponibilidad de datos). El indicador simplemente está fallando para instanciar antes de la segunda pasada de OnCalculate() (también conocido como el primer tick)

Tuve en cuenta esto durante el diagnóstico. Incluso incorporé bucles y período de espera, por si acaso. Como todos los que me precedieron, estoy teniendo los mismos problemas con este error en particular.

 
nicholishen:

Esto significa que si los datos están disponibles inmediatamente para un script o EA, entonces estarían igualmente disponibles para el indicador (como en este caso no es un problema de disponibilidad de datos). El indicador simplemente está fallando para instanciar antes de la segunda pasada de OnCalculate() (también conocido como el primer tick)

Tuve en cuenta esto durante el diagnóstico. Incluso incorporé bucles y período de espera, por si acaso. Como todos los que me precedieron, estoy teniendo los mismos problemas con este error en particular.

Estás repitiendo "falla en la instanciación", pero eso no es exacto. El indicador se instanciará en todos los casos.

El problema es que los datos no están disponibles de forma sincrónica, tienes que lidiar con ello. NO es un bug de MT5, es una CARACTERÍSTICA.

Propongo dejar la discusión y esperar la respuesta de SD.