Gráficos "sem buracos"

Andrey Khatimlianskii | 18 fevereiro, 2016


1.Motivação

"No MT 4, apenas tais barras são desenhadas, dentro das quais pelo menos uma mudança de preço ocorreu. Se nenhuma mudança de preço ocorreu dentro de um minuto, um espaço de uma barra ocorrerá no gráfico com período de um minuto".


Os desenvolvedores optaram deliberadamente por essa forma de desenho de gráficos pois a maioria dos traders que usam o produto prefere gráficos que contenham apenas preços existentes. De todo modo, há usuários que gostam de gráficos contínuos. Eles preferem que a barra seja desenhada mesmo que o seu preço seja igual ao preço de fechamento da barra anterior. Portanto, não há lacunas na escala de tempo do gráfico, e 100 barras sempre correspondem a 100 minutos no gráfico de um minuto. Estes dados podem ser diferentes na realização atual. Por exemplo, 100 minutos podem "caber" em 98 barras caso entre eles haja 2 minutos nos quais nenhuma cota chegou.

Por sorte, todas as ferramentas necessárias estão disponíveis no MQL4 para nos auxiliar a desenhar este tipo de gráfico de modo independente.


2.Realização

Primeiramente, vamos dividir esta tarefa em dois estágios:

  • processamento de dados do histórico

  • atualização da barra mais recente

No primeiro estágio, nós criamos um novo arquivo de histórico com o prefixo "ALL" antes do nome do símbolo ("ALL" significa que "todas as barras" estão aqui) e escrevemos nele o histórico com as barras adicionadas.

Um problema semelhante é resolvido no script "period_converter" ("conversor de período") que é fornecido com o terminal do cliente MT4. O script gera um gráfico com um período não padrão. Nós usaremos esse exemplo para aprender a trabalhar com o arquivo de histórico.

Antes da criação de um programa, nós precisamos decidir que forma ele terá: Ele será um script, um indicador ou um expert? Indicadores são usados para exibir os conteúdos de uma matriz. Nós não precisamos disso no momento. Quanto a scripts e experts, a única diferença entre eles é que os scripts são deletados do gráfico imediatamente após a sua operação ser completada. Isso nos é conveniente no momento, então nós vamos produzir um script agora.

Isso é o que vamos obter como resultado (AllMinutes_Step1.mq4):

#property show_inputs

//---- Enable/disable drawing bars on holidays
//---- If == true, holidays will remain unfilled
//---- If == false, holidays will be filled out with bars 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];

//---- remember the chart symbol and period
   string _Symbol=Symbol();
   int _Period= Period();
   _PeriodSec = _Period * 60;

//---- open file, in which we will write the history
   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);
     }

//---- Write the file heading
   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);

//+-----------------------------------------------------------------+
//| Process the history
//+-----------------------------------------------------------------+
   int bars=Bars;
   pre_time=Time[bars-1];
   for(int i=bars-1; i>=0; i--)
     {
      //---- Remember the bar parameters
      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;

      //---- if there are skipped bars,
      while(now_time>pre_time+_PeriodSec)
        {
         pre_time+=_PeriodSec;
         pre_time    /= _PeriodSec;
         pre_time    *= _PeriodSec;

         //---- if it is not the weekend,
         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;
                 }
              }
           }

         //---- write the skipped bar into the file
         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++;
        }

      //---- write the new bar into the file
      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++;

      //---- remember the time value and the close price 
      //---- of the recorded bar
      pre_close=now_close;
      pre_time=now_time/_PeriodSec;
      pre_time*=_PeriodSec;
     }

//---- close the file
   FileClose(HistoryHandle);

//---- display statistics
   Print("< - - - ",_Symbol,_Period,": there were ",cnt_copy,
         " bars, added ",cnt_add," bars - - - >");
   Print("< - - - To view the results, open the chart \"ALL",
         _Symbol,_Period,"\" - - - >");
   return(0);
  }

Recomenda-se prestar atenção à variável SkipWeekEnd. Caso o seu valor seja falso, os fins de semanas serão repletos com barras O=H=L=C (tracejadas).

Vamos verificar como o nosso script funciona. Para isso basta anexá-lo a um gráfico de um minuto GBPUSD:


Agora, vamos abrir o gráfico ALLGBPUSD1 no modo offline e compará-lo ao gráfico inicial:


Como você pode ver, alguns minutos pulados foram adicionados ao gráfico. Eles estão circulados em vermelho. Este é o resultado que nós esperávamos atingir, não é?

Como agora nós temos um gráfico com os buracos preenchidos, nós podemos atualizá-lo. As novas cotas serão mostradas nele, mas os novos buracos vão novamente permanecer não preenchidos.

O script "period_converter" pode novamente ser usado como um exemplo. Ele também pode resolver o problema da atualização dos gráficos. Nós faremos apenas uma mudança: Adicionar o bloco para preencher as barras puladas. Dado que o gráfico precisa ser atualizado a cada tick, vamos transferir o nosso código ao expert. Ele será inicializado sempre que uma nova cota chegar. Vamos colocar o código da primeira parte na função init(), pois esta parte só precisa ser executada uma vez, e a nova parte inteira do código será colocada na função start(), pois ela será usada a cada tick. Além disso, o fechamento de arquivos irá para a função deinit(), pois aquele é o lugar certo para ele.

Desse modo, o código do expert (AllMinutes_Step2.mq4) será o seguinte:

#include <WinUser32.mqh>

//---- Enable/disable drawing of bars on holidays
//---- If it is == true, the holidays will remain unfilled
//---- If it is == false, the holidays will be filled with the O=H=L=C bars
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];

//---- remember the chart symbol and period
   _Symbol=Symbol();
   _Period=Period();
   _PeriodSec=_Period*60;
   hwnd=0;

//---- open the file to write the history in
   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);
     }

//---- Write the file heading
   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);

//+-----------------------------------------------------------------+
//| Process the history
//+-----------------------------------------------------------------+
   int bars=Bars;
   pre_time=Time[bars-1];
   for(int i=bars-1; i>=1; i--)
     {
      //---- Remember the bar parameters
      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;

      //---- if there are skipped bars,
      while(now_time>pre_time+_PeriodSec)
        {
         pre_time+=_PeriodSec;
         pre_time    /= _PeriodSec;
         pre_time    *= _PeriodSec;

         //---- if it isn't a weekend,
         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;
                 }
              }
           }

         //---- write the obtained bar into the file
         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++;
        }

      //---- write the new bar into the file
      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++;

      //---- remember the time value and the close price of the recorded 
      //---- bar
      pre_close=now_close;
      pre_time=now_time/_PeriodSec;
      pre_time*=_PeriodSec;
     }

   last_fpos=FileTell(HistoryHandle);

//---- display statistics
   Print("< - - - ",_Symbol,_Period,": there were ",cnt_copy," bars, added ",cnt_add," bars - - - >");
   Print("< - - - To view the results, open the chart \"ALL",
         _Symbol,_Period,"\" - - - >");

//---- call the start function for the 0th bar to be drawn immediately
   start();

   return(0);
  }
//----
int start()
  {
//+---------------------------------------------------------------+
//| Process the incoming ticks
//+---------------------------------------------------------------+

//---- place the "cursor" before the latest bar
//---- (this must be done at all launches except for the first one)
   FileSeek(HistoryHandle,last_fpos,SEEK_SET);

//---- Remember the bar parameters
   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;

//---- if the bar has been formed, 
   if(now_time>=pre_time+_PeriodSec)
     {
      //---- write the newly formed bar
      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);

      //---- remember the position in the file, before writing of the 0th bar
      last_fpos=FileTell(HistoryHandle);
     }

//---- if the skipped bars have appeared,
   while(now_time>pre_time+_PeriodSec)
     {
      pre_time+=_PeriodSec;
      pre_time /= _PeriodSec;
      pre_time *= _PeriodSec;

      //---- if this is not weekend,
      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;
              }
           }
        }

      //---- write the skipped bar into the file
      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);

      //---- remember the position in the file, before writing of the 0th bar
      last_fpos=FileTell(HistoryHandle);
     }

//---- write the current bar
   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);

//---- remember the parameters of the recorded bar
   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;

//---- find the window the new quotes to be "sent" to
   if(hwnd==0)
     {
      hwnd = WindowHandle( StringConcatenate( "ALL", _Symbol ), _Period );
      if( hwnd != 0 )
        {
         Print("< - - - Chart ",
               "ALL"+_Symbol,_Period," has been found! - - - >");
        }
     }
//---- and, if successfully found, update it
   if(hwnd!=0)
     {
      PostMessageA(hwnd,WM_COMMAND,33324,0);
     }
  }
//----
int deinit()
  {
   if(HistoryHandle>=0)
     {
      //---- close the file
      FileClose(HistoryHandle);
      HistoryHandle=-1;
     }
   return(0);
  }

Uma observação deve ser feita antes: A atualização do gráfico exige bastante do processador, pois o terminal carrega todas as barras escritas no arquivo. Caso haja muitas barras no arquivo, o terminal pode ficar bem mais lento. Isso depende principalmente do desempenho do PC no qual o terminal do cliente MT4 está instalado. De todo modo, os recursos não são inesgotáveis. Nós resolveremos este problema de modo simples: Simplesmente reduza a quantidade de barras exibida no gráfico para 10.000 ("Ferramentas" – "Opções" – "Gráficos", o parâmetro "máximo de barras no gráfico"). Vamos agora reiniciar o terminal e anexar o expert.


O expert "repara" imediatamente o histórico e fica à espera do surgimento do próximo tick. Após 2 minutos, o mesmo gráfico aparece do seguinte modo:

Como você pode ver, uma barra de um minuto foi adicionada ao gráfico superior, e a barra pulada foi adicionada ao gráfico inferior.

Isso significa que nós alcançamos o resultado desejado!


3.Dimensionamento

Um gráfico não representa problemas, é claro, mas o que nós devemos fazer caso surja a necessidade de abrir 10 gráficos sem barras puladas. A abertura de um gráfico adicional "temporário" para cada gráfico normal não seria a melhor solução. Serão consumidos recursos extras e, respectivamente, o trabalho se tornará menos confortável.

Vamos criar um expert que possa processar qualquer quantidade de gráficos. Esta será a solução adequada e econômica.

Portanto, nós teremos que modificar o nosso código para que ele possa trabalhar com vários gráficos simultaneamente:

  • adicione uma variável externa que auxilie a alterar a lista de gráficos,
  • substitua todas as variáveis por matrizes que possuam uma quantidade de elementos igual à quantidade de gráficos a serem processados,
  • coloque todo o código em um ciclo, no qual estes gráficos serão buscados, e
  • coloque o bloco de atualização no ciclo infinito, tornando-o assim independente da chegada de cotas. Caso símbolos diferentes estejam listados, o seu tempo de atualização também pode ser diferente.

É isso que devemos ter desenhado como resultado (AllMinutes.mq4):

#include <WinUser32.mqh>

//---- The list of charts to be processed, separated by commas (",")
extern string    ChartList="EURUSD1,GBPUSD1";
//---- Enable/disable to draw bars at weekends
//---- If it is == true, the weekends will remain unfilled
//---- If it is == false, the weekends will be filled with the bars of O=H=L=C
extern bool     SkipWeekEnd=true;
//---- Frequency of the charts updating, in milliseconds
//---- the higher the value is, the less resources the expert 
//---- will use.
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[];

//---- count the amount of charts to be processed
   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("Unknown symbol ",cur_symbol,"!!!");
               return(-1);
              }
            if(iClose(cur_symbol,StrToInteger(cur_period),0)<=0)
              {
               Alert("Unknown period ",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("< - - - Found ",Charts," correct charts. - - - >");
   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;

      //---- open the file the history to be written in
      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;
        }

      //---- Write the file heading
      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);

      //+-----------------------------------------------------------+
      //| Process the history
      //+-----------------------------------------------------------+
      _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--)
        {
         //---- Remember the bar parameters
         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];

         //---- if there are skipped bars,
         while(now_time[curChart]>pre_time[curChart]+
               _PeriodSec[curChart])
           {
            pre_time[curChart]+=_PeriodSec[curChart];
            pre_time[curChart] /= _PeriodSec[curChart];
            pre_time[curChart] *= _PeriodSec[curChart];

            //---- if this is not the weekend,
            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;
                    }
                 }
              }

            //---- write the skipped bar into the file
            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++;
           }

         //---- write the new bar into the file
         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++;

         //---- remember the time value and the close price 
         //---- of the recorded bar
         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]);

      //---- display statistics
      Print("< - - - ",_Symbol[curChart],_Period[curChart],": there were ",
            cnt_copy," bars, added ",cnt_add," bars - - - >");
      Print("< - - - To view the results, open chart \"ALL",
            _Symbol[curChart],_Period[curChart],"\" - - - >");

     }

//+---------------------------------------------------------------+
//| Process the incoming ticks
//+---------------------------------------------------------------+
   while(!IsStopped())
     {
      RefreshRates();
      for(curChart=0; curChart<Charts; curChart++)
        {
         //---- put the "cursor" before the latest bar
         //---- (this must be done at all launches except for the first one)
         FileSeek(HistoryHandle[curChart],last_fpos[curChart],
                  SEEK_SET);

         //---- Remember the bar parameters
         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];

         //---- if a bar has been formed, 
         if(now_time[curChart]>=pre_time[curChart]+
            _PeriodSec[curChart])
           {
            //---- write the formed bar
            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]);

            //---- remember the position in the file before recording the 0th bar
            last_fpos[curChart]=FileTell(HistoryHandle[curChart]);
           }

         //---- if skipped bars appear,
         while(now_time[curChart]>pre_time[curChart]+_PeriodSec[curChart])
           {
            pre_time[curChart] += _PeriodSec[curChart];
            pre_time[curChart] /= _PeriodSec[curChart];
            pre_time[curChart] *= _PeriodSec[curChart];

            //---- if this is not the weekend,
            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;

                    }
                 }
              }

            //---- write the skipped bar into the file
            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]);

            //---- remember the position in the file before to record the 0th bar
            last_fpos[curChart]=FileTell(HistoryHandle[curChart]);
           }

         //---- write the current bar
         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]);

         //---- remember the parameters of the written bar
         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];

         //---- find the window to "send" the new quotes to
         if(hwnd[curChart]==0)
           {
            hwnd[curChart]=WindowHandle(StringConcatenate("ALL",
                                        _Symbol[curChart]),
                                        _Period[curChart]);
            if(hwnd[curChart]!=0)
              {
               Print("< - - - Chart ","ALL"+_Symbol[curChart],
                     _Period[curChart]," found! - - - >"); 
              }
           }
         //---- and, if found, update it
         if(hwnd[curChart]!=0)
           {
            PostMessageA(hwnd[curChart],WM_COMMAND,33324,0); 
           }
        }
      Sleep(RefreshLuft);
     }

   for(curChart=0; curChart<Charts; curChart++)
     {
      if(HistoryHandle[curChart]>=0)
        {
         //---- close the file
         FileClose(HistoryHandle[curChart]);
         HistoryHandle[curChart]=-1;
        }
     }
   return(0);
  }

Agora vamos inicializar o expert no gráfico de 5 minutos EURUSD com o parâmetro ChartList sendo igual a "EURUSD1,GBPUSD1,EURGBP1", e abrir todos os três gráficos no modo offline:






Tudo parece estar bem: Todos os três gráficos são atualizados simultaneamente e serão "reparados" caso surjam alguns buracos.