English Русский 中文 Deutsch 日本語 Português
Gráficos sin agujeros

Gráficos sin agujeros

MetaTrader 4Ejemplos | 4 febrero 2016, 08:07
770 0
Andrey Khatimlianskii
Andrey Khatimlianskii

1. Motivación

En MT 4 se dibujan barras, dentro de las cuales suceden cambios en el precio. Si no ocurre ningún cambio de precio en un minuto aparece un hueco (gap) en el gráfico.

Los desarrolladores han elegido deliberadamente esta forma de dibujar los gráficos porque la mayoría de traders prefiere los gráficos que contienen precios reales. Sin embargo, también hay usuarios que prefieren los gráficos continuos. Prefieren que las barras se dibujen incluso si el precio de apertura es igual al precio de cierre de la barra anterior. En consecuencia no hay gaps en la escala de tiempo del gráfico, y, por ejemplo, 100 barras siempre se corresponden con los 100 minutos del gráfico de un minuto. Estos datos pueden cambiar en la presente implementación. Por ejemplo, 100 minutos pueden "caber" en 98 barras si hubiera dos minutos entre las mismas con cotizaciones sin ingresos.

Afortunadamente, MQL4 proporciona todas las herramientas necesarias que le ayudan a dibujar tales gráficos de forma independiente.


2. Implementación

En primer lugar vamos a dividir la tarea en dos etapas distintas:

  • procesamiento del historial de los datos

  • actualización de la última barra

En la primera etapa, creamos un nuevo archivo de historial con prefijo "ALL" antes del nombre del símbolo ("ALL" significa "todas las barras") y en el historial escribimos las barras añadidas.

En el script "period_converter", que viene con el terminal cliente MT 4, se soluciona un problema parecido. El script genera un gráfico con un periodo no estándar. Vamos a utilizar este ejemplo para aprender a trabajar con el archivo del historial.

Antes de crear una aplicación MQL4 tenemos que hacer un análisis previo y decidir qué tipo de programa será: script, indicador o experto. Los indicadores se utilizan para visualizar el contenido del array. Sin embargo no los necesitamos aquí. La diferencia entre los scripts y los expertos es que los primeros se borran del gráfico inmediatamente después de realizar sus operaciones. Esto encaja en esta fase, así que este es el script que vamos a codificar ahora.

Como resultado obtenemos el siguiente código (AllMinutes_Step1.mq4):

#property show_inputs

//---- Activar/desactivar el dibujo de las barras en los días festivos
//---- Si == true, los días festivos no se rellenan
//---- Si == false, los días festivos se rellenan con barras O=H=L=C
extern bool  SkipWeekEnd=true;

int start()
  {
   int HistoryHandle=-1,pre_time,now_time,_PeriodSec;
   double  now_close,now_open,now_low,now_high,now_volume,pre_close;

   int    _GetLastError=0,cnt_copy=0,cnt_add=0;
   int    temp[13];

//---- recordamos el símbolo del gráfico y el periodo
   string _Symbol=Symbol();
   int _Period= Period();
   _PeriodSec = _Period * 60;

//---- abrimos el archivo donde escribimos el historial
   string file_name=StringConcatenate("ALL",_Symbol,_Period,".hst");
   HistoryHandle=FileOpenHistory(file_name,FILE_BIN|FILE_WRITE);
   if(HistoryHandle<0)
     {
      _GetLastError=GetLastError();
      Alert("FileOpenHistory( \"",file_name,"\", FILE_BIN | FILE_WRITE )"," - Error #",_GetLastError);
      return(-1);
     }

//---- Escribimos la cabecera del archivo
   FileWriteInteger(HistoryHandle,400,LONG_VALUE);
   FileWriteString(HistoryHandle,"Copyright © 2006, komposter",64);
   FileWriteString(HistoryHandle,"ALL"+_Symbol,12);
   FileWriteInteger(HistoryHandle,_Period,LONG_VALUE);
   FileWriteInteger(HistoryHandle,Digits,LONG_VALUE);
   FileWriteInteger(HistoryHandle,0,LONG_VALUE);       //timesign
   FileWriteInteger(HistoryHandle,0,LONG_VALUE);       //last_sync
   FileWriteArray(HistoryHandle,temp,0,13);

//+-----------------------------------------------------------------+
//| Procesamiento del historial
//+-----------------------------------------------------------------+
   int bars=Bars;
   pre_time=Time[bars-1];
   for(int i=bars-1; i>=0; i--)
     {
      //---- Recordamos los parámetros de la barra
      now_open=Open[i];
      now_high=High[i];
      now_low=Low[i];
      now_close=Close[i];
      now_volume=Volume[i];
      now_time=Time[i]/_PeriodSec;
      now_time*=_PeriodSec;

      //---- si hay barras omitidas,
      while(now_time>pre_time+_PeriodSec)
        {
         pre_time+=_PeriodSec;
         pre_time    /= _PeriodSec;
         pre_time    *= _PeriodSec;

         //---- si no es el fin de semana,
         if(SkipWeekEnd)
           {
            if(TimeDayOfWeek(pre_time)<=0 || 
               TimeDayOfWeek(pre_time)>5)
              {
               continue;
              }
            if(TimeDayOfWeek(pre_time)==5)
              {
               if( TimeHour(pre_time) == 23 ||
                  TimeHour(pre_time + _PeriodSec) == 23 )
                 {
                  continue;
                 }
              }
           }

         //---- escribimos la barra omitida en el archivo
         FileWriteInteger(HistoryHandle,pre_time,LONG_VALUE);
         FileWriteDouble(HistoryHandle,pre_close,DOUBLE_VALUE);
         FileWriteDouble(HistoryHandle,pre_close,DOUBLE_VALUE);
         FileWriteDouble(HistoryHandle,pre_close,DOUBLE_VALUE);
         FileWriteDouble(HistoryHandle,pre_close,DOUBLE_VALUE);
         FileWriteDouble(HistoryHandle,0,DOUBLE_VALUE);
         FileFlush(HistoryHandle);
         cnt_add++;
        }

      //---- escribimos la barra nueva en el archivo
      FileWriteInteger(HistoryHandle,now_time,LONG_VALUE);
      FileWriteDouble(HistoryHandle,now_open,DOUBLE_VALUE);
      FileWriteDouble(HistoryHandle,now_low,DOUBLE_VALUE);
      FileWriteDouble(HistoryHandle,now_high,DOUBLE_VALUE);
      FileWriteDouble(HistoryHandle,now_close,DOUBLE_VALUE);
      FileWriteDouble(HistoryHandle,now_volume,DOUBLE_VALUE);
      FileFlush(HistoryHandle);
      cnt_copy++;

      //---- recordamos la hora y el precio de cierre 
      //---- de la barra registrada
      pre_close=now_close;
      pre_time=now_time/_PeriodSec;
      pre_time*=_PeriodSec;
     }

//---- cerramos el archivo
   FileClose(HistoryHandle);

//---- mostramos las estadísticas
   Print("< - - - ",_Symbol,_Period,": hubo ",cnt_copy,
         " barras añadidas ",cnt_add," barras - - - >");
   Print("< - - - Abra el gráfico para ver los resultados \"ALL",
         _Symbol,_Period,"\" - - - >");
   return(0);
  }

Se recomienda prestar atención a la variable SkipWeekEnd . Si vale false, los fines de semana se llenarán con barras O=H=L=C.

Adjuntemos nuestro script al gráfico GBPUSD de un minuto y comprobemos cómo funciona:

Abramos el gráfico ALLGBPUSD1 en modo sin conexión y comparémoslo con el gráfico inicial:


Como se observa, se han añadido al gráfico algunos minutos omitidos. Están marcados con un círculo de color rojo. Esto es lo que queríamos conseguir.

Ahora que tenemos un gráfico con los huecos rellenados, podemos actualizarlo. Las nuevas cotizaciones se mostrarán en el mismo, pero los gaps permanecerán de nuevo vacíos.

El script "period_converter" se puede utilizar a modo de ejemplo. También resuelve el problema de la actualización de los gráficos. Tan solo haremos este cambio: añadir el bloque que rellena las barras omitidas. Vamos a transferir nuestro código al asesor experto porque el gráfico se tiene que actualizar en cada tick. Se lanzará cada vez que entre una nueva cotización. Vamos a poner el código de la primera parte en la función init() porque esta parte se tiene que ejecutar solo una vez. El nuevo código se pondrá en la función start() porque se ejecutará en cada tick. Además, el lugar más adecuado para cerrar el archivo es la función deinit().

Por lo tanto tenemos el siguiente código (AllMinutes_Step2.mq4):

#include <WinUser32.mqh>

//---- Activamos/desactivamos el dibujo de las barras en los días festivos
//---- Si es == true, los días festivos permanecerán sin rellenar
//---- Si es == false, los días festivos se rellenarán con las barras O=H=L=C
extern bool  SkipWeekEnd=true;

int  HistoryHandle=-1,hwnd=0,last_fpos=0,pre_time,now_time;
int  _Period,_PeriodSec;
double  now_close,now_open,now_low,now_high,now_volume;
double  pre_close,pre_open,pre_low,pre_high,pre_volume;
string  _Symbol;

int init()
  {
   int    _GetLastError=0,cnt_copy=0,cnt_add=0;
   int    temp[13];

//---- recordamos el símbolo del gráfico y el periodo
   _Symbol=Symbol();
   _Period=Period();
   _PeriodSec=_Period*60;
   hwnd=0;

//---- abrimos el archivo para escribir en el historial
   string file_name=StringConcatenate("ALL",_Symbol,_Period,".hst");
   HistoryHandle=FileOpenHistory(file_name,FILE_BIN|FILE_WRITE);
   if(HistoryHandle<0)
     {
      _GetLastError=GetLastError();
      Alert("FileOpenHistory( \"",file_name,"\", FILE_BIN | FILE_WRITE )"," - Error #",_GetLastError);
      return(-1);
     }

//---- Escribimos la cabecera del archivo
   FileWriteInteger(HistoryHandle,400,LONG_VALUE);
   FileWriteString(HistoryHandle,"Copyright © 2006, komposter",64);
   FileWriteString(HistoryHandle,StringConcatenate("ALL",_Symbol),
                   12);
   FileWriteInteger(HistoryHandle,_Period,LONG_VALUE);
   FileWriteInteger(HistoryHandle,Digits,LONG_VALUE);
   FileWriteInteger(HistoryHandle,0,LONG_VALUE);       //timesign
   FileWriteInteger(HistoryHandle,0,LONG_VALUE);       //last_sync
   FileWriteArray(HistoryHandle,temp,0,13);

//+-----------------------------------------------------------------+
//| Procesamiento del historial
//+-----------------------------------------------------------------+
   int bars=Bars;
   pre_time=Time[bars-1];
   for(int i=bars-1; i>=1; i--)
     {
      //---- Recordamos los parámetros de la barra
      now_open=Open[i];
      now_high=High[i];
      now_low=Low[i];
      now_close=Close[i];
      now_volume=Volume[i];
      now_time=Time[i]/_PeriodSec;
      now_time*=_PeriodSec;

      //---- si hay barras omitidas,
      while(now_time>pre_time+_PeriodSec)
        {
         pre_time+=_PeriodSec;
         pre_time    /= _PeriodSec;
         pre_time    *= _PeriodSec;

         //---- si no es un fin de semana,
         if(SkipWeekEnd)
           {
            if(TimeDayOfWeek(pre_time)<=0 || TimeDayOfWeek(pre_time)>5)
              {
               continue;
              }
            if(TimeDayOfWeek(pre_time)==5)
              {
               if( TimeHour(pre_time) == 23 ||
                  TimeHour(pre_time + _PeriodSec) == 23 )
                 {
                  continue;
                 }
              }
           }

         //---- escribimos la barra obtenida en el archivo
         FileWriteInteger(HistoryHandle,pre_time,LONG_VALUE);
         FileWriteDouble(HistoryHandle,pre_close,DOUBLE_VALUE);
         FileWriteDouble(HistoryHandle,pre_close,DOUBLE_VALUE);
         FileWriteDouble(HistoryHandle,pre_close,DOUBLE_VALUE);
         FileWriteDouble(HistoryHandle,pre_close,DOUBLE_VALUE);
         FileWriteDouble(HistoryHandle,0,DOUBLE_VALUE);
         FileFlush(HistoryHandle);
         cnt_add++;
        }

      //---- escribimos la barra nueva en el archivo
      FileWriteInteger(HistoryHandle,now_time,LONG_VALUE);
      FileWriteDouble(HistoryHandle,now_open,DOUBLE_VALUE);
      FileWriteDouble(HistoryHandle,now_low,DOUBLE_VALUE);
      FileWriteDouble(HistoryHandle,now_high,DOUBLE_VALUE);
      FileWriteDouble(HistoryHandle,now_close,DOUBLE_VALUE);
      FileWriteDouble(HistoryHandle,now_volume,DOUBLE_VALUE);
      FileFlush(HistoryHandle);
      cnt_copy++;

      //---- recordamos el valor de la hora y el precio de cierre de la 
      //---- barra registrada
      pre_close=now_close;
      pre_time=now_time/_PeriodSec;
      pre_time*=_PeriodSec;
     }

   last_fpos=FileTell(HistoryHandle);

//---- mostramos las estadísticas
   Print("< - - - ",_Symbol,_Period,": hubo ",cnt_copy," barras, añadidas ",cnt_add," bars - - - >");
   Print("< - - - Abra el gráfico para ver los resultados \"ALL",
         _Symbol,_Period,"\" - - - >");

//---- llamamos a la función start para que la barra 0 se dibuje inmediatamente
   start();

   return(0);
  }
//----
int start()
  {
//+---------------------------------------------------------------+
//| Procesamos los ticks entrantes
//+---------------------------------------------------------------+

//---- ponemos el "cursor" antes de la última barra
//---- (esto se tiene que hacer en todos los lanzamientos a excepción del primero)
   FileSeek(HistoryHandle,last_fpos,SEEK_SET);

//---- Recordamos los parámetros de la barra
   now_open=Open[0];
   now_high=High[0];
   now_low=Low[0];
   now_close=Close[0];
   now_volume=Volume[0];
   now_time=Time[0]/_PeriodSec;
   now_time*=_PeriodSec;

//---- si la barra se ha formado, 
   if(now_time>=pre_time+_PeriodSec)
     {
      //---- escribimos la barra recién formada
      FileWriteInteger(HistoryHandle,pre_time,LONG_VALUE);
      FileWriteDouble(HistoryHandle,pre_open,DOUBLE_VALUE);
      FileWriteDouble(HistoryHandle,pre_low,DOUBLE_VALUE);
      FileWriteDouble(HistoryHandle,pre_high,DOUBLE_VALUE);
      FileWriteDouble(HistoryHandle,pre_close,DOUBLE_VALUE);
      FileWriteDouble(HistoryHandle,pre_volume,DOUBLE_VALUE);
      FileFlush(HistoryHandle);

      //---- antes de escribir la barra 0 recordamos la posición en el archivo
      last_fpos=FileTell(HistoryHandle);
     }

//---- si las barras omitidas han aparecido,
   while(now_time>pre_time+_PeriodSec)
     {
      pre_time+=_PeriodSec;
      pre_time /= _PeriodSec;
      pre_time *= _PeriodSec;

      //---- si no es fin de semana,
      if(SkipWeekEnd)
        {
         if(TimeDayOfWeek(pre_time)<=0 || 
            TimeDayOfWeek(pre_time)>5)
           {
            continue;
           }
         if(TimeDayOfWeek(pre_time)==5)
           {
            if(TimeHour(pre_time)==23 || 
               TimeHour(pre_time+_PeriodSec)==23)
              {
               continue;
              }
           }
        }

      //---- escribimos la barra omitida en el archivo
      FileWriteInteger(HistoryHandle,pre_time,LONG_VALUE);
      FileWriteDouble(HistoryHandle,pre_close,DOUBLE_VALUE);
      FileWriteDouble(HistoryHandle,pre_close,DOUBLE_VALUE);
      FileWriteDouble(HistoryHandle,pre_close,DOUBLE_VALUE);
      FileWriteDouble(HistoryHandle,pre_close,DOUBLE_VALUE);
      FileWriteDouble(HistoryHandle,0,DOUBLE_VALUE);
      FileFlush(HistoryHandle);

      //---- antes de escribir la barra 0 recordamos la posición en el archivo
      last_fpos=FileTell(HistoryHandle);
     }

//---- escribimos la barra actual
   FileWriteInteger(HistoryHandle,now_time,LONG_VALUE);
   FileWriteDouble(HistoryHandle,now_open,DOUBLE_VALUE);
   FileWriteDouble(HistoryHandle,now_low,DOUBLE_VALUE);
   FileWriteDouble(HistoryHandle,now_high,DOUBLE_VALUE);
   FileWriteDouble(HistoryHandle,now_close,DOUBLE_VALUE);
   FileWriteDouble(HistoryHandle,now_volume,DOUBLE_VALUE);
   FileFlush(HistoryHandle);

//---- recordamos los parámetros de la barra registrada
   pre_open=now_open;
   pre_high=now_high;
   pre_low=now_low;
   pre_close=now_close;
   pre_volume=now_volume;
   pre_time=now_time/_PeriodSec;
   pre_time*=_PeriodSec;

//---- encontramos las nuevas cotizaciones a enviar
   if(hwnd==0)
     {
      hwnd = WindowHandle( StringConcatenate( "ALL", _Symbol ), _Period );
      if( hwnd != 0 )
        {
         Print("< - - - Gráfico ",
               "ALL"+_Symbol,_Period," se ha encontrado! - - - >");
        }
     }
//---- si la búsqueda es satisfactoria, actualizamos
   if(hwnd!=0)
     {
      PostMessageA(hwnd,WM_COMMAND,33324,0);
     }
  }
//----
int deinit()
  {
   if(HistoryHandle>=0)
     {
      //---- cerramos el archivo
      FileClose(HistoryHandle);
      HistoryHandle=-1;
     }
   return(0);
  }

Llegados a este punto es importante hacer un apunte. La actualización del gráfico consume bastantes recursos del procesador porque el terminal tiene que cargar todas las barras del archivo. Si hay muchas barras en el archivo el terminal se puede ralentizar. Depende en gran medida del rendimiento del PC donde está instalado el terminal cliente de MT 4. En cualquier caso los recursos no son inagotables. El problema se puede resolver de una forma sencilla, basta con reducir la cantidad de barras que se visualizan en el gráfico a 10,000 ("Herramientas" – "Opciones" – "Gráficos", el parámetro "Max. barras en gráfico"). Ahora hay que reiniciar el terminal y adjuntar el experto:

El experto arregla el historial "inmediatamente" y se mantiene a la espera hasta que aparecen nuevos ticks. Después de 2 minutos, los mismos gráficos aparecen como ilustra esta imagen:

   

Como se ve, se ha añadido una barra de un minuto en el gráfico superior, mientras que la barra omitida se añade al gráfico inferior.

¡Esto significa que hemos conseguido el resultado deseado!


3. Escala

De momento tenemos un gráfico correcto. ¿Pero cómo tenemos que proceder cuando necesitamos abrir, por ejemplo, 10 gráficos sin barras omitidas? Abrir un gráfico adicional "temporal" para cada uno de los gráficos no es la mejor solución. Esto malgastaría recursos y, por otro lado, el trabajo se volvería poco agradable.

Vamos a crear un experto que pueda procesar cualquier número de gráficos. Esta es una solución más conveniente que nos ayudará a ahorrar recursos.

En consecuencia tenemos que modificar nuestro código para que pueda trabajar con varios gráficos simultáneamente:

  • añadir una variable externa que ayude a cambiar la lista de gráficos,
  • cambiar todas las variables con arrays que tengan una cantidad de elementos igual a la cantidad de gráficos que se tienen que procesar,
  • colocar el código entero en un bucle donde estos gráficos serán buscados, y
  • poner el bloque actualizado en el bucle infinito, independientemente de las cotizaciones. Si se listan varios símbolos, la hora de actualización también puede ser diferente.

Como resultado obtenemos este código (AllMinutes.mq4):

#include <WinUser32.mqh>

//---- Lista de gráficos a procesar separados por comas (",")
extern string    ChartList="EURUSD1,GBPUSD1";
//---- Activar/desactivar el dibujo de las barras durante los fines de semana
//---- Si es == true, los fines de semana permanecerán vacíos
//---- Si es == false, los fines de semana se rellenan con las barras O=H=L=C
extern bool     SkipWeekEnd=true;
//---- Frecuencia de actualización de los gráficos, en milisegundos
//---- cuanto más alto es el valor, tanto menos recursos utilizará 
//---- el experto.
extern int   RefreshLuft=1000;

int init()
  {
   start();
   return(0);
  }

int start()
  {
   int   _GetLastError=0,cnt_copy=0,cnt_add=0,temp[13];
   int   Charts=0,pos=0,curchar=0,len=StringLen(ChartList);
   string    cur_symbol="",cur_period="",file_name="";

   string    _Symbol[100]; int _Period[100],_PeriodSec[],_Bars[];
   int HistoryHandle[],hwnd[],last_fpos[],pre_time[],now_time[];
   double   now_close[],now_open[],now_low[],now_high[],now_volume[];
   double   pre_close[],pre_open[],pre_low[],pre_high[],pre_volume[];

//---- contamos la cantidad de gráficos a procesar
   while(pos<=len)
     {
      curchar=StringGetChar(ChartList,pos);
      if(curchar>47 && curchar<58)
        {
         cur_period=cur_period+CharToStr(curchar);
        }
      else
        {
         if(curchar==',' || pos==len)
           {
            MarketInfo(cur_symbol,MODE_BID);
            if(GetLastError()==4106)
              {
               Alert("Símbolo desconocido ",cur_symbol,"!!!");
               return(-1);
              }
            if(iClose(cur_symbol,StrToInteger(cur_period),0)<=0)
              {
               Alert("Periodo desconocido ",cur_period,"!!!");
               return(-1);
              }

            _Symbol[Charts]=cur_symbol;
            _Period[Charts]=StrToInteger(cur_period);
            cur_symbol=""; cur_period="";

            Charts++;
           }
         else
           {
            cur_symbol=cur_symbol+CharToStr(curchar);
           }
        }
      pos++;
     }
   Print("< - - - Encontrados ",Charts," gráficos correctos. - - - >");
   ArrayResize(_Symbol,Charts);
   ArrayResize(_Period,Charts);
   ArrayResize(HistoryHandle,Charts);
   ArrayResize(hwnd,Charts);
   ArrayResize(last_fpos,Charts);
   ArrayResize(pre_time,Charts);
   ArrayResize(now_time,Charts);
   ArrayResize(now_close,Charts);
   ArrayResize(now_open,Charts);
   ArrayResize(now_low,Charts);
   ArrayResize(now_high,Charts);
   ArrayResize(now_volume,Charts);
   ArrayResize(pre_close,Charts);
   ArrayResize(pre_open,Charts);
   ArrayResize(pre_low,Charts);
   ArrayResize(pre_high,Charts);
   ArrayResize(pre_volume,Charts);
   ArrayResize(_PeriodSec,Charts);
   ArrayResize(_Bars,Charts);

   for(int curChart=0; curChart<Charts; curChart++)
     {
      _PeriodSec[curChart]=_Period[curChart] *60;

      //---- abrimos el archivo donde escribimos el historial
      file_name=StringConcatenate("ALL",_Symbol[curChart],
                                  _Period[curChart],".hst");
      HistoryHandle[curChart]=FileOpenHistory(file_name,
                                              FILE_BIN|FILE_WRITE);
      if(HistoryHandle[curChart]<0)
        {
         _GetLastError=GetLastError();
         Alert("FileOpenHistory( \"",file_name,"\", FILE_BIN | FILE_WRITE )"," - Error #",_GetLastError);
         continue;
        }

      //---- Escribimos la cabecera del archivo
      FileWriteInteger(HistoryHandle[curChart],400,LONG_VALUE);
      FileWriteString(HistoryHandle[curChart],
                      "Copyright © 2006, komposter",64);
      FileWriteString(HistoryHandle[curChart],
                      StringConcatenate("ALL",
                      _Symbol[curChart]),12);
      FileWriteInteger(HistoryHandle[curChart],_Period[curChart],
                       LONG_VALUE);
      FileWriteInteger(HistoryHandle[curChart],
                       MarketInfo(_Symbol[curChart],
                       MODE_DIGITS),LONG_VALUE);
      FileWriteInteger(HistoryHandle[curChart],0,
                       LONG_VALUE);       //timesign
      FileWriteInteger(HistoryHandle[curChart],0,
                       LONG_VALUE);       //last_sync
      FileWriteArray(HistoryHandle[curChart],temp,0,13);

      //+-----------------------------------------------------------+
      //| Procesamiento del historial
      //+-----------------------------------------------------------+
      _Bars[curChart]=iBars(_Symbol[curChart],_Period[curChart]);
      pre_time[curChart]=iTime(_Symbol[curChart],
                               _Period[curChart],_Bars[curChart]-1);
      for(int i=_Bars[curChart]-1; i>=1; i--)
        {
         //---- Recordamos los parámetros de la barra
         now_open[curChart]=iOpen(_Symbol[curChart],
                                  _Period[curChart],i);
         now_high[curChart]=iHigh(_Symbol[curChart],
                                  _Period[curChart],i);
         now_low[curChart]=iLow(_Symbol[curChart],
                                _Period[curChart],i);
         now_close[curChart]=iClose(_Symbol[curChart],
                                    _Period[curChart],i);
         now_volume[curChart]=iVolume(_Symbol[curChart],
                                      _Period[curChart],i);
         now_time[curChart]=iTime(_Symbol[curChart],
                                  _Period[curChart],i)
         /_PeriodSec[curChart];
         now_time[curChart]*=_PeriodSec[curChart];

         //---- si hay barras omitidas,
         while(now_time[curChart]>pre_time[curChart]+
               _PeriodSec[curChart])
           {
            pre_time[curChart]+=_PeriodSec[curChart];
            pre_time[curChart] /= _PeriodSec[curChart];
            pre_time[curChart] *= _PeriodSec[curChart];

            //---- si no es un fin de semana,
            if(SkipWeekEnd)
              {
               if(TimeDayOfWeek(pre_time[curChart])<=0 || 
                  TimeDayOfWeek(pre_time[curChart])>5)
                 {
                  continue;
                 }
               if(TimeDayOfWeek(pre_time[curChart])==5)
                 {
                  if(TimeHour(pre_time[curChart])==23 || 
                     TimeHour(pre_time[curChart]+_PeriodSec[curChart])==23)
                    {
                     continue;
                    }
                 }
              }

            //---- escribimos la barra omitida en el archivo
            FileWriteInteger(HistoryHandle[curChart],pre_time[curChart],
                             LONG_VALUE);
            FileWriteDouble(HistoryHandle[curChart],
                            pre_close[curChart],DOUBLE_VALUE);
            FileWriteDouble(HistoryHandle[curChart],
                            pre_close[curChart],DOUBLE_VALUE);
            FileWriteDouble(HistoryHandle[curChart],
                            pre_close[curChart],DOUBLE_VALUE);
            FileWriteDouble(HistoryHandle[curChart],
                            pre_close[curChart],DOUBLE_VALUE);
            FileWriteDouble(HistoryHandle[curChart],0,
                            DOUBLE_VALUE);
            FileFlush(HistoryHandle[curChart]);
            cnt_add++;
           }

         //---- escribimos la barra nueva en el archivo
         FileWriteInteger(HistoryHandle[curChart],now_time[curChart],
                          LONG_VALUE);
         FileWriteDouble(HistoryHandle[curChart],
                         now_open[curChart],DOUBLE_VALUE);
         FileWriteDouble(HistoryHandle[curChart],
                         now_low[curChart],DOUBLE_VALUE);
         FileWriteDouble(HistoryHandle[curChart],
                         now_high[curChart],DOUBLE_VALUE);
         FileWriteDouble(HistoryHandle[curChart],
                         now_close[curChart],DOUBLE_VALUE);
         FileWriteDouble(HistoryHandle[curChart],
                         now_volume[curChart],DOUBLE_VALUE);
         FileFlush(HistoryHandle[curChart]);
         cnt_copy++;

         //---- recordamos la hora y el precio de cierre 
         //---- de la barra registrada
         pre_close[curChart]=now_close[curChart];
         pre_time[curChart]=now_time[curChart]/
                            _PeriodSec[curChart];
         pre_time[curChart]*=_PeriodSec[curChart];
        }

      last_fpos[curChart]=FileTell(HistoryHandle[curChart]);

      //---- mostramos las estadísticas
      Print("< - - - ",_Symbol[curChart],_Period[curChart],": hubo ",
            cnt_copy," barras, añadidas ",cnt_add," barras - - - >");
      Print("< - - - Para ver los resultados, abra el gráfico \"ALL",
            _Symbol[curChart],_Period[curChart],"\" - - - >");

     }

//+---------------------------------------------------------------+
//| Procesamos los ticks entrantes
//+---------------------------------------------------------------+
   while(!IsStopped())
     {
      RefreshRates();
      for(curChart=0; curChart<Charts; curChart++)
        {
         //---- ponemos el "cursor" antes de la última barra
         //---- (esto se tiene que hacer en todos los lanzamientos a excepción del primero)
         FileSeek(HistoryHandle[curChart],last_fpos[curChart],
                  SEEK_SET);

         //---- Recordamos los parámetros de la barra
         now_open[curChart]=iOpen(_Symbol[curChart],
                                  _Period[curChart],0);
         now_high[curChart]=iHigh(_Symbol[curChart],
                                  _Period[curChart],0);
         now_low[curChart]=iLow(_Symbol[curChart],
                                _Period[curChart],0);
         now_close[curChart]=iClose(_Symbol[curChart],
                                    _Period[curChart],0);
         now_volume[curChart]=iVolume(_Symbol[curChart],
                                      _Period[curChart],0);
         now_time[curChart]=iTime(_Symbol[curChart],
                                  _Period[curChart],0)
         /_PeriodSec[curChart];
         now_time[curChart]*=_PeriodSec[curChart];

         //---- si se forma una barra, 
         if(now_time[curChart]>=pre_time[curChart]+
            _PeriodSec[curChart])
           {
            //---- escribimos la barra formada
            FileWriteInteger(HistoryHandle[curChart],pre_time[curChart],
                             LONG_VALUE);
            FileWriteDouble(HistoryHandle[curChart],
                            pre_open[curChart],DOUBLE_VALUE);
            FileWriteDouble(HistoryHandle[curChart],
                            pre_low[curChart],DOUBLE_VALUE);
            FileWriteDouble(HistoryHandle[curChart],
                            pre_high[curChart],DOUBLE_VALUE);
            FileWriteDouble(HistoryHandle[curChart],
                            pre_close[curChart],DOUBLE_VALUE);
            FileWriteDouble(HistoryHandle[curChart],
                            pre_volume[curChart],DOUBLE_VALUE);
            FileFlush(HistoryHandle[curChart]);

            //---- recordamos la posición en el archivo antes de registrar la barra cero
            last_fpos[curChart]=FileTell(HistoryHandle[curChart]);
           }

         //---- si aparecen barras omitidas,
         while(now_time[curChart]>pre_time[curChart]+_PeriodSec[curChart])
           {
            pre_time[curChart] += _PeriodSec[curChart];
            pre_time[curChart] /= _PeriodSec[curChart];
            pre_time[curChart] *= _PeriodSec[curChart];

            //---- si no es un fin de semana,
            if(SkipWeekEnd)
              {
               if(TimeDayOfWeek(pre_time[curChart])<=0 || 
                  TimeDayOfWeek(pre_time[curChart])>5)
                 {
                  continue;
                 }
               if(TimeDayOfWeek(pre_time[curChart])==5)
                 {
                  if(TimeHour(pre_time[curChart])==23 || 
                     TimeHour(pre_time[curChart]+_PeriodSec[curChart])==23)
                    {
                     continue;

                    }
                 }
              }

            //---- escribimos la barra omitida en el archivo
            FileWriteInteger(HistoryHandle[curChart],pre_time[curChart],
                             LONG_VALUE);
            FileWriteDouble(HistoryHandle[curChart],
                            pre_close[curChart],DOUBLE_VALUE);
            FileWriteDouble(HistoryHandle[curChart],
                            pre_close[curChart],DOUBLE_VALUE);
            FileWriteDouble(HistoryHandle[curChart],
                            pre_close[curChart],DOUBLE_VALUE);
            FileWriteDouble(HistoryHandle[curChart],
                            pre_close[curChart],DOUBLE_VALUE);
            FileWriteDouble(HistoryHandle[curChart],0,
                            DOUBLE_VALUE);
            FileFlush(HistoryHandle[curChart]);

            //---- recordamos la posición en el archivo antes de registrar la barra cero
            last_fpos[curChart]=FileTell(HistoryHandle[curChart]);
           }

         //---- escribimos la barra actual
         FileWriteInteger(HistoryHandle[curChart],now_time[curChart],
                          LONG_VALUE);
         FileWriteDouble(HistoryHandle[curChart],
                         now_open[curChart],DOUBLE_VALUE);
         FileWriteDouble(HistoryHandle[curChart],
                         now_low[curChart],DOUBLE_VALUE);
         FileWriteDouble(HistoryHandle[curChart],
                         now_high[curChart],DOUBLE_VALUE);
         FileWriteDouble(HistoryHandle[curChart],
                         now_close[curChart],DOUBLE_VALUE);
         FileWriteDouble(HistoryHandle[curChart],
                         now_volume[curChart],DOUBLE_VALUE);
         FileFlush(HistoryHandle[curChart]);

         //---- recordamos los parámetros de la barra escrita
         pre_open[curChart]        = now_open[curChart];
         pre_high[curChart]        = now_high[curChart];
         pre_low[curChart]=now_low[curChart];
         pre_close[curChart]=now_close[curChart];
         pre_volume[curChart]=now_volume[curChart];
         pre_time[curChart]=now_time[curChart]/
                            _PeriodSec[curChart];
         pre_time[curChart]*=_PeriodSec[curChart];

         //---- encontramos la ventana donde enviar las nuevas cotizaciones
         if(hwnd[curChart]==0)
           {
            hwnd[curChart]=WindowHandle(StringConcatenate("ALL",
                                        _Symbol[curChart]),
                                        _Period[curChart]);
            if(hwnd[curChart]!=0)
              {
               Print("< - - - Gráfico ","ALL"+_Symbol[curChart],
                     _Period[curChart]," encontrado! - - - >"); 
              }
           }
         //---- y si la encontramos la actualizamos
         if(hwnd[curChart]!=0)
           {
            PostMessageA(hwnd[curChart],WM_COMMAND,33324,0); 
           }
        }
      Sleep(RefreshLuft);
     }

   for(curChart=0; curChart<Charts; curChart++)
     {
      if(HistoryHandle[curChart]>=0)
        {
         //---- cerramos el archivo
         FileClose(HistoryHandle[curChart]);
         HistoryHandle[curChart]=-1;
        }
     }
   return(0);
  }

Ahora vamos a lanzar el experto en el gráfico de 5 minutos del EURUSD con el parámetro ChartList igual a "EURUSD1,GBPUSD1,EURGBP1" y abriremos los tres gráficos en modo sin conexión:

 


Todo parece estar bien. Los tres gráficos se actualizan simultáneamente y se arreglan si aparecen gaps.

Traducción del ruso hecha por MetaQuotes Ltd.
Artículo original: https://www.mql5.com/ru/articles/1407

Archivos adjuntos |
AllMinutes.mq4 (12.72 KB)
Las matemáticas de los algoritmos genéticos Las matemáticas de los algoritmos genéticos
Los algoritmos genéticos evolutivos se aplican en tareas de optimización. Por ejemplo en el aprendizaje neuronet, esto es, para seleccionar los valores de peso que permiten obtener el mínimo error. El algoritmo genético se basa en el método de búsqueda aleatoria.
El sistema experto 'Comentador'. Aplicación práctica de indicadores embebidos en programas MQL4 El sistema experto 'Comentador'. Aplicación práctica de indicadores embebidos en programas MQL4
El presente artículo describe el uso de los indicadores técnicos en el lenguaje de programación MQL4.
Experto comercial universal: Los modos comerciales de las estrategias (Parte 1) Experto comercial universal: Los modos comerciales de las estrategias (Parte 1)
Cada escritor de expertos, independientemente de su nivel de preparación, se encuentra todos los días con las mismas tareas comerciales y problemas algorítmicos, que debe resolver de una forma u otra para organizar un proceso comercial fiable. Este artículo describe las capacidades del motor comercial CStrategy, capaz de ocuparse de la resolución de estas tareas y de proporcionar al usuario mecanismos cómodos para describir sus ideas sobre trading.
Almacenamiento y visualización de la información Almacenamiento y visualización de la información
Este artículo expone algunos métodos eficientes de almacenamiento y visualización de la información. Vamos a proponer algunas alternativas tanto para el archivo de log estándar del terminal como para la función Comment().