Borrar instancias de indicadores: IndicatorRelease

Como se ha mencionado en la parte introductoria de este capítulo, el terminal mantiene un contador de referencia para cada indicador creado y lo deja en funcionamiento mientras al menos un programa MQL o gráfico lo utilice. En un programa MQL, una señal de la necesidad de un indicador es un manejador válido. Normalmente, pedimos un manejador durante la inicialización y lo utilizamos en algoritmos hasta el final del programa.

En el momento en que se descarga el programa, todos los manejadores únicos creados se liberan automáticamente, es decir, sus contadores se decrementan en 1 (y si llegan a cero, esos indicadores también se descargan de la memoria). Por lo tanto, no es necesario liberar explícitamente el manejador.

No obstante, hay situaciones en las que un subindicador resulta innecesario durante el funcionamiento del programa. Entonces, el indicador inútil sigue consumiendo recursos. Por lo tanto, usted debe liberar explícitamente el manejador con IndicatorRelease.

bool IndicatorRelease(int handle)

La función borra el manejador del indicador especificado y descarga el propio indicador si nadie más lo utiliza. La descarga se produce con un ligero retraso.

La función devuelve un indicador de éxito (true) o de errores (false).

Tras la llamada a IndicatorRelease, el manejador que se ha pasado deja de ser válido, aunque la propia variable conserva su valor anterior. Un intento de utilizar tal manejador en otras funciones de indicador como CopyBuffer fallará con el error 4807 (ERR_INDICATOR_WRONG_HANDLE). Para evitar malentendidos, es conveniente asignar el valor INVALID_HANDLE a la variable correspondiente inmediatamente después de liberar el manejador.

No obstante, si a continuación el programa solicita un manejador para un nuevo indicador, lo más probable es que ese manejador tenga el mismo valor que el liberado anteriormente, pero ahora estará asociado a los datos del nuevo indicador.

Cuando se trabaja en el comprobador de estrategias, no se realiza la función IndicatorRelease.

Para demostrar la aplicación de IndicatorRelease, vamos a preparar una versión especial de UseDemoAllLoop.mq5, que recreará periódicamente un indicador auxiliar en un ciclo de la lista, que incluirá sólo indicadores para la ventana principal (para mayor claridad).

IndicatorType MainLoop[] =
{
   iCustom_,
   iAlligator_jawP_jawS_teethP_teethS_lipsP_lipsS_method_price,
   iAMA_period_fast_slow_shift_price,
   iBands_period_shift_deviation_price,
   iDEMA_period_shift_price,
   iEnvelopes_period_shift_method_price_deviation,
   iFractals_,
   iFrAMA_period_shift_price,
   iIchimoku_tenkan_kijun_senkou,
   iMA_period_shift_method_price,
   iSAR_step_maximum,
   iTEMA_period_shift_price,
   iVIDyA_momentum_smooth_shift_price,
};
   
const int N = ArraySize(MainLoop);
int Cursor = 0// current position inside the MainLoop array
      
const string IndicatorCustom = "LifeCycle";

El primer elemento del array contiene un indicador personalizado como excepción, LifeCycle de la sección Funciones de inicio y parada de programas de diferentes tipos. Aunque este indicador no muestra ninguna línea, es apropiado aquí porque muestra mensajes en el registro cuando se llama a sus manejadores OnInit/OnDeinit, lo que le permitirá rastrear su ciclo de vida. Los ciclos de vida de otros indicadores son similares.

En las variables de entrada, dejaremos sólo los ajustes de renderización. La salida por defecto de las etiquetas DRAW_ARROW es óptima para mostrar diferentes tipos de indicadores.

input ENUM_DRAW_TYPE DrawType = DRAW_ARROW// Drawing Type
input int DrawLineWidth = 1// Drawing Line Width

Para recrear los indicadores «sobre la marcha» , vamos a ejecutar el temporizador de 5 segundos en OnInit, y toda la inicialización anterior (con algunas modificaciones que se describen a continuación) se trasladará al manejador OnTimer.

int OnInit()
{
   Comment("Wait 5 seconds to start looping through indicator set");
   EventSetTimer(5);
   return INIT_SUCCEEDED;
}
   
IndicatorType IndicatorSelector// currently selected indicator type
   
void OnTimer()
{
   if(Handle != INVALID_HANDLE && ClearHandles)
   {
      IndicatorRelease(Handle);
      /*
      // descriptor is still 10, but is no longer valid
      // if we uncomment the fragment, we get the following error
      double data[1];
      const int n = CopyBuffer(Handle, 0, 0, 1, data);
      Print("Handle=", Handle, " CopyBuffer=", n, " Error=", _LastError);
      // Handle=10 CopyBuffer=-1 Error=4807 (ERR_INDICATOR_WRONG_HANDLE)
      */
   }
   IndicatorSelector = MainLoop[Cursor];
   Cursor = ++Cursor % N;
   
   // create a handle with default parameters
   // (because we pass an empty string in the third argument of the constructor)
   AutoIndicator indicator(IndicatorSelector,
      (IndicatorSelector == iCustom_ ? IndicatorCustom : ""), "");
   Handle = indicator.getHandle();
   if(Handle == INVALID_HANDLE)
   {
      Print(StringFormat("Can't create indicator: %s",
         _LastError ? E2S(_LastError) : "The name or number of parameters is incorrect"));
   }
   else
   {
      Print("Handle="Handle);
   }
   
   buffers.empty(); // clear buffers because a new indicator will be displayed
   ChartSetSymbolPeriod(0,NULL,0); // request a full redraw
   ...
   // further setup of diagrams - similar to the previous one
   ...
   Comment("DemoAll: ", (IndicatorSelector == iCustom_ ? IndicatorCustom : s),
      "(default-params)");
}

La principal diferencia es que el tipo del indicador creado actualmente IndicatorSelector ahora no lo establece el usuario, sino que se selecciona secuencialmente del array MainLoop en el índice Cursor. Cada vez que se llama al temporizador, este índice aumenta cíclicamente, es decir, cuando se llega al final del array, saltamos a su principio.

Para todos los indicadores, la línea con los parámetros está vacía. Esto se hace para unificar su inicialización. Como resultado, cada indicador se creará con sus propios valores por defecto.

Al principio del manejador OnTimer, llamamos a IndicatorRelease para el manejador anterior. Sin embargo, hemos proporcionado una variable de entrada ClearHandles para desactivar la rama de operadores if dada y ver lo que sucede si no limpia los manejadores.

input bool ClearHandles = true;

Por defecto, ClearHandles es igual a true, es decir, los indicadores se borrarán como se espera.

Por último, otro ajuste adicional son las líneas que permiten borrar los búferes y solicitar un redibujado completo del gráfico. Ambas son necesarias, pues hemos sustituido el indicador esclavo que suministra los datos visualizados.

El manejador OnCalculate no ha cambiado.

Vamos a ejecutar UseDemoAllLoop con la configuración por defecto. En el registro aparecerán las siguientes entradas (sólo se muestra el principio):

UseDemoAllLoop (EURUSD,H1) Initializing LifeCycle() EURUSD, PERIOD_H1

UseDemoAllLoop (EURUSD,H1) Handle=10

LifeCycle (EURUSD,H1) Loader::Loader()

LifeCycle (EURUSD,H1) void OnInit() 0 DEINIT_REASON_PROGRAM

UseDemoAllLoop (EURUSD,H1) Initializing iAlligator_jawP_jawS_teethP_teethS_lipsP_lipsS_method_price() EURUSD, PERIOD_H1

UseDemoAllLoop (EURUSD,H1) iAlligator_jawP_jawS_teethP_teethS_lipsP_lipsS_method_price requires 8 parameters, 0 given

UseDemoAllLoop (EURUSD,H1) Handle=10

LifeCycle (EURUSD,H1) void OnDeinit(const int) DEINIT_REASON_REMOVE

LifeCycle (EURUSD,H1) Loader::~Loader()

UseDemoAllLoop (EURUSD,H1) Initializing iAMA_period_fast_slow_shift_price() EURUSD, PERIOD_H1

UseDemoAllLoop (EURUSD,H1) iAMA_period_fast_slow_shift_price requires 5 parameters, 0 given

UseDemoAllLoop (EURUSD,H1) Handle=10

UseDemoAllLoop (EURUSD,H1) Initializing iBands_period_shift_deviation_price() EURUSD, PERIOD_H1

UseDemoAllLoop (EURUSD,H1) iBands_period_shift_deviation_price requires 4 parameters, 0 given

UseDemoAllLoop (EURUSD,H1) Handle=10

...

Fíjese en que obtenemos siempre el mismo «número» de manejador (10) porque lo liberamos antes de crear un nuevo manejador.

También es importante que el indicador LifeCycle se descargue poco después de que lo hayamos liberado (suponiendo que no se haya añadido al mismo gráfico por sí mismo, porque entonces su cuenta de referencia no se pondría a cero).

En la siguiente imagen se muestra el momento en que nuestro indicador muestra los datos de Alligator.

UseDemoAllLoop en el paso de demostración de Alligator

UseDemoAllLoop en el paso de demostración de Alligator

Si cambia el valor de ClearHandles a false, veremos una imagen completamente diferente en el registro. A partir de ahora, los números de los manejadores aumentarán constantemente, lo que indica que los indicadores permanecen en el terminal y siguen trabajando, consumiendo recursos en vano. En concreto, no se recibe ningún mensaje de desinicialización del indicador LifeCycle.

UseDemoAllLoop (EURUSD,H1) Initializing LifeCycle() EURUSD, PERIOD_H1

UseDemoAllLoop (EURUSD,H1) Handle=10

LifeCycle (EURUSD,H1) Loader::Loader()

LifeCycle (EURUSD,H1) void OnInit() 0 DEINIT_REASON_PROGRAM

UseDemoAllLoop (EURUSD,H1) Initializing iAlligator_jawP_jawS_teethP_teethS_lipsP_lipsS_method_price() EURUSD, PERIOD_H1

UseDemoAllLoop (EURUSD,H1) iAlligator_jawP_jawS_teethP_teethS_lipsP_lipsS_method_price requires 8 parameters, 0 given

UseDemoAllLoop (EURUSD,H1) Handle=11

UseDemoAllLoop (EURUSD,H1) Initializing iAMA_period_fast_slow_shift_price() EURUSD, PERIOD_H1

UseDemoAllLoop (EURUSD,H1) iAMA_period_fast_slow_shift_price requires 5 parameters, 0 given

UseDemoAllLoop (EURUSD,H1) Handle=12

UseDemoAllLoop (EURUSD,H1) Initializing iBands_period_shift_deviation_price() EURUSD, PERIOD_H1

UseDemoAllLoop (EURUSD,H1) iBands_period_shift_deviation_price requires 4 parameters, 0 given

UseDemoAllLoop (EURUSD,H1) Handle=13

UseDemoAllLoop (EURUSD,H1) Initializing iDEMA_period_shift_price() EURUSD, PERIOD_H1

UseDemoAllLoop (EURUSD,H1) iDEMA_period_shift_price requires 3 parameters, 0 given

UseDemoAllLoop (EURUSD,H1) Handle=14

UseDemoAllLoop (EURUSD,H1) Initializing iEnvelopes_period_shift_method_price_deviation() EURUSD, PERIOD_H1

UseDemoAllLoop (EURUSD,H1) iEnvelopes_period_shift_method_price_deviation requires 5 parameters, 0 given

UseDemoAllLoop (EURUSD,H1) Handle=15

...

UseDemoAllLoop (EURUSD,H1) Initializing iVIDyA_momentum_smooth_shift_price() EURUSD, PERIOD_H1

UseDemoAllLoop (EURUSD,H1) iVIDyA_momentum_smooth_shift_price requires 4 parameters, 0 given

UseDemoAllLoop (EURUSD,H1) Handle=22

UseDemoAllLoop (EURUSD,H1) Initializing LifeCycle() EURUSD, PERIOD_H1

UseDemoAllLoop (EURUSD,H1) Handle=10

UseDemoAllLoop (EURUSD,H1) Initializing iAlligator_jawP_jawS_teethP_teethS_lipsP_lipsS_method_price() EURUSD, PERIOD_H1

UseDemoAllLoop (EURUSD,H1) iAlligator_jawP_jawS_teethP_teethS_lipsP_lipsS_method_price requires 8 parameters, 0 given

UseDemoAllLoop (EURUSD,H1) Handle=11

UseDemoAllLoop (EURUSD,H1) Initializing iAMA_period_fast_slow_shift_price() EURUSD, PERIOD_H1

UseDemoAllLoop (EURUSD,H1) iAMA_period_fast_slow_shift_price requires 5 parameters, 0 given

UseDemoAllLoop (EURUSD,H1) Handle=12

UseDemoAllLoop (EURUSD,H1) Initializing iBands_period_shift_deviation_price() EURUSD, PERIOD_H1

UseDemoAllLoop (EURUSD,H1) iBands_period_shift_deviation_price requires 4 parameters, 0 given

UseDemoAllLoop (EURUSD,H1) Handle=13

UseDemoAllLoop (EURUSD,H1) Initializing iDEMA_period_shift_price() EURUSD, PERIOD_H1

UseDemoAllLoop (EURUSD,H1) iDEMA_period_shift_price requires 3 parameters, 0 given

UseDemoAllLoop (EURUSD,H1) Handle=14

UseDemoAllLoop (EURUSD,H1) void OnDeinit(const int)

...

Cuando el índice en el bucle sobre el array de tipos de indicadores alcanza el último elemento y dibuja un círculo desde el principio, el terminal empezará a devolver a nuestro código manejadores de indicadores ya existentes (los mismos valores: al manejador 22 le sigue de nuevo el 10).