Графики без «дыр»

Автор: lexy Среда, Сентябрь 10th, 2014 Нет комментариев

Рубрика: Разное

Система построения графиков в МТ 4 устроена так, что рисуются только те бары, во временных промежутках которых было хоть одно изменение цены. Если в течение минуты изменения цены не было, то на минутном графике образуется пропуск в один бар.

Разработчики сознательно выбрали такой способ построения графиков, так как  большинство пользователей их продукта отдают предпочтение графикам, содержащим только существующие цены. Тем не менее, имеются и поклонники непрерывных графиков. Они считают, что если даже изменения цены не было, то должен быть нарисован бар, у которого цена открытия равна цене закрытия предыдущего бара. Таким образом, на графике не будет пропусков по шкале времени, и 100 баров на минутном графике будут всегда соответствовать периоду 100 минут. При текущей реализации эти цифры могут отличаться. Например, 100 минут могут «вместиться» в 98 баров, если за 100 минут было две минуты без поступления котировок.
MQL


Реализация

Для начала, поделим задачу на два этапа:

  • Обработка исторических данных;

  • Обновление последнего бара.

На первом этапе мы создадим новый файл истории с приставкой «ALL» к имени символа («ALL» – «все», в нашем контексте – «все бары»)  и запишем в него историю с добавленными барами.
Схожая задача решается в скрипте »period_converter», которым укомплектован клиентский терминал  МТ 4. Скрипт генерирует график с нестандартным периодом. На его примере мы и научимся работать с файлом истории.
Перед тем, как создать программу, нужно решить, в каком виде она должна быть оформлена: в виде скрипта, индикатора или эксперта. Индикатор предназначен для отображения содержимого массивов. Это нам не понадобится. А принципиальное отличие скрипта от эксперта только в одном – скрипт удаляется с графика сразу после выполнения. На данном этапе это нас устраивает, поэтому мы будем делать именно скрипт.

Вот, что у нас получилось (AllMinutes_Step1.mq4):


#property show_inputs

//---- Разрешить/запретить рисовать бары в выходные
//---- Если == true, выходные останутся незаполнеными
//---- Если == false, выходные будут заполнены барами 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];

   //---- запоминаем символ и период графика
   string _Symbol = Symbol();
   int _Period = Period();
   _PeriodSec = _Period * 60;

   //---- открываем файл, в который будем записывать историю
   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);
     }

   //---- Записываем заголовок файла
   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);
   //+-----------------------------------------------------------------+
   //| Обрабатываем историю                                            |
   //+-----------------------------------------------------------------+
   int bars = Bars;
   pre_time = Time[bars-1];
   for(int i = bars - 1; i >= 0; i--)
     {
       //---- Запоминаем параметры бара
       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;
       //---- если есть пропущенные бары,
       while(now_time > pre_time + _PeriodSec)
         {
           pre_time += _PeriodSec;
           pre_time /= _PeriodSec;
           pre_time *= _PeriodSec;

           //---- если это не выходные,
           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;
                     }
                 }
             }

           //---- записываем пропущенный бар в файл
           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, 1, DOUBLE_VALUE);
           FileFlush(HistoryHandle);
           cnt_add ++;
         }

       //---- записываем новый бар в файл
       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 ++;

       //---- запоминаем значение времени и цену закрытия
       //---- записанного бара
       pre_close = now_close;
       pre_time  = now_time / _PeriodSec;
       pre_time  *=_PeriodSec;
     }

   //---- закрываем файл
   FileClose( HistoryHandle );

   //---- выводим статистику
   Print("< - - - ", _Symbol, _Period, ": было ", cnt_copy,
         " баров, добавлено ", cnt_add, " баров - - - >");
   Print("< - - - Для просмотра результатов, откройте график \"ALL",
         _Symbol, _Period, "\" - - - >");
   return(0);
  }


Обратите внимание на переменную SkipWeekEnd. Если её значение false, выходные тоже будут заполнены барами O=H=L=C (черточками).

Давайте проверим, как работает наш скрипт, – просто присоединим его к минутному графику GBPUSD:


  Теперь давайте откроем график ALLGBPUSD1 в режиме «офф-лайн» и сравним его с исходным графиком:



 
Как видите, в график добавилось несколько пропущенных минуток. Они обведены красными кругами. Этого мы и добивались, не правда ли?

 

Теперь, когда у нас есть график с заполненными пропусками, займемся его обновлением. Сейчас на нём не будут отображаться новые котировки и, соответственно, не будут заполняться новые пропуски.

 

За примером опять обратимся к скрипту «period_converter«. Задача обновления графиков в нём тоже решается. Сделаем только одно изменение: добавим блок заполнения пропущенных баров.  Поскольку обновлять график необходимо каждый тик, перенесём весь наш код в эксперта. Он будет запускаться с приходом каждой новой котировки. Код из первой части поместим в функцию init(), так как его необходимо выполнить только один раз, а всю новую часть – в функцию start(), так как она будет востребована каждый тик. Кроме того, закрытие файла перенесём в deinit(), ему там самое место.

 

Итак, код эксперта (AllMinutes_Step2.mq4):


#include <WinUser32.mqh>

//---- Разрешить/запретить рисовать бары в выходные
//---- Если == true, выходные останутся незаполнеными
//---- Если == false, выходные будут заполнены барами 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];

    //---- запоминаем символ и период графика
    _Symbol = Symbol();
    _Period = Period();
    _PeriodSec = _Period * 60;
    hwnd = 0;

    //---- открываем файл, в который будем записывать историю
    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);
      }

    //---- Записываем заголовок файла
    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);

    //+-----------------------------------------------------------------+
    //| Обрабатываем историю                                            |
    //+-----------------------------------------------------------------+
    int bars = Bars;
    pre_time = Time[bars-1];
    for(int i = bars - 1; i >= 1; i--)
      {
        //---- Запоминаем параметры бара
        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;

        //---- если есть пропущенные бары,
        while(now_time > pre_time + _PeriodSec)
          {
            pre_time += _PeriodSec;
            pre_time /= _PeriodSec;
            pre_time *= _PeriodSec;

            //---- если это не выходные,
            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;
                  }
              }

            //---- записываем пропущенный бар в файл
            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, 1, DOUBLE_VALUE);
            FileFlush(HistoryHandle );
            cnt_add ++;
          }

        //---- записываем новый бар в файл
        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 ++;

        //---- запоминаем значение времени и цену закрытия записанного
        //---- бара
        pre_close = now_close;
        pre_time = now_time / _PeriodSec;
        pre_time *=_PeriodSec;
      }

    last_fpos = FileTell( HistoryHandle);

    //---- выводим статистику
    Print("< - - - ", _Symbol, _Period, ": было ", cnt_copy, " баров,
          добавлено ", cnt_add, " баров - - - >");
    Print("< - - - Для просмотра результатов, откройте график \"ALL",
          _Symbol, _Period, "\" - - - >");

    //---- вызываем функцию старт, чтоб сразу наисовался 0-й бар
    start();

    return(0);
   }
//----
int start()
   {
     //+---------------------------------------------------------------+
     //| Обрабатываем поступающие тики                                 |
     //+---------------------------------------------------------------+

     //---- ставим "курсор" перед последним баром
     //---- (это необходимо на всех запусках, кроме первого)
      FileSeek(HistoryHandle, last_fpos, SEEK_SET);

     //---- Запоминаем параметры бара
     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(now_time >= pre_time + _PeriodSec)
       {
         //---- записываем сформировавшийся бар
         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);

         //---- запоминаем место в файле, перед записью 0-го бара
         last_fpos = FileTell(HistoryHandle);
       }

     //---- если появились пропущенные бары,
     while(now_time > pre_time + _PeriodSec)
       {
         pre_time += _PeriodSec;
         pre_time /= _PeriodSec;
         pre_time *= _PeriodSec;

         //---- если это не выходные,
         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;
               }
           }

         //---- записываем пропущенный бар в файл
         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, 1, DOUBLE_VALUE);
         FileFlush(HistoryHandle);

         //---- запоминаем место в файле, перед записью 0-го бара
         last_fpos = FileTell(HistoryHandle);
       }

     //---- записываем текущий бар
     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);

     //---- запоминаем параметры записанного бара
     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;

     //---- находим окно, в которое будем "отправлять" свежие котировки
     if(hwnd == 0)
       {
         hwnd = WindowHandle(StringConcatenate( "ALL", _Symbol ), _Period);
         if(hwnd != 0)
           {
             Print("< - - - График ",
                   "ALL" + _Symbol, _Period, " найден! - - - >");
           }
       }
     //---- и, если нашли, обновляем его
     if(hwnd != 0)
       {
         PostMessageA(hwnd, WM_COMMAND, 33324, 0);
       }
   }
//----
int deinit()
  {
   if(HistoryHandle >= 0)
     {
       //---- закрываем файл
       FileClose(HistoryHandle);
       HistoryHandle = -1;
     }
   return(0);
  }

 
Сразу сделаю оговорку, что процесс обновления графика достаточно ресурсоемкий, потому что терминал загружает все бары, записанные в файл. Если баров в файле достаточно много, терминал может заметно тормозить. Это во многом зависит и от характеристик компьютера, на котором установлен клиентский терминал  МТ 4. В любом случае ресурсы не могут быть бесконечными. Решим мы этот вопрос просто – уменьшим количество отображаемых на графике баров до 10 000 («Сервис» – «Настройки» – «Графики», параметр «Макс. баров в окне»). Теперь перезапустим терминал и подключим наш эксперт:


  Эксперт сразу «залатал» историю и начал ждать появления новых тиков. Спустя две минуты, эти же графики выглядели так:

 

   Как видите, на верхнем графике была добавлена одна «минутка», в то время как на нижнем добавился ещё и пропущенный бар.

Значит, мы достигли желаемого результата!

Масштабирование

Один график — это, конечно, хорошо, но что делать, если надо открыть 10 графиков без пропущенных баров? Открывать для каждого графика ещё один, «рабочий», на котором будет работать эксперт, – не самый лучший выход. Будут потрачены лишние ресурсы, и, соответственно, работать станет менее комфортно.
Давайте сделаем эксперт, умеющего обрабатывать любое количество графиков. Это будет удобное в использовании и экономное решение.

Итак, что нам надо поменять в нашем коде, чтобы он работал с несколькими графиками:

  • добавить внешнюю переменную, с помощью которой можно будет менять список графиков;
  • все переменные заменить массивами с количеством элементов равным количеству обрабатываемых графиков;
  • весь код поместить в цикл, в котором будут перебираться эти самые графики;
  • блок обновления поместить в бесконечный цикл, таким образом отвязавшись от прихода котировок. Если в списке будут разные символы, их время обновления может быть разным.

Вот, что у нас должно получиться (AllMinutes.mq4):

 

#include <WinUser32.mqh>

//---- Список графиков, которые необходимо обрабатывать, разделённый
запято�� (",")
extern string    ChartList = "EURUSD1,GBPUSD1";
//---- Разрешить/запретить рисовать бары в выходные
//---- Если == true, выходные останутся незаполнеными
//---- Если == false, выходные будут заполнены барами O=H=L=C
extern bool     SkipWeekEnd = true;
//---- Частота, с которой будут обновляться графики, в милисекундах
//---- Чем больше значение, тем меньше ресурсов будет использовать
//---- эксперт.
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[];

   //---- считаем количество графиков, которые необходимо обработать
   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("Неизвестный символ ", cur_symbol, "!!!");
                   return(-1);
                 }
               if(iClose(cur_symbol, StrToInteger(cur_period), 0) <= 0)
                 {
                   Alert("Неизвестный период ", 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("< - - - Найдено ", 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;

       //---- открываем файл, в который будем записывать историю
       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;
         }

       //---- Записываем заголовок файла
       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);

       //+-----------------------------------------------------------+
       //| Обрабатываем историю                                      |
       //+-----------------------------------------------------------+
       _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--)
         {
           //---- Запоминаем параметры бара
           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];

           //---- если есть пропущенные бары,
           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(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;
                     }
                 }

               //---- записываем пропущенный бар в файл
               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], 1, DOUBLE_VALUE);
               FileFlush(HistoryHandle[curChart]);
               cnt_add ++;
             }

           //---- записываем новый бар в файл
           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 ++;

           //---- запоминаем значение времени и цену закрытия
           //---- записанного бара
           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]);

       //---- выводим статистику
       Print("< - - - ", _Symbol[curChart], _Period[curChart], ": было ",
             cnt_copy," баров, добавлено ", cnt_add, " баров - - - >");
       Print("< - - - Для просмотра результатов, откройте график \"ALL",
             _Symbol[curChart], _Period[curChart], "\" - - - >" );

     }

//+---------------------------------------------------------------+
//| Обрабатываем поступающие тики                                 |
//+---------------------------------------------------------------+
  while(!IsStopped())
    {
      RefreshRates();
      for(curChart = 0; curChart < Charts; curChart ++)
        {
          //---- ставим "курсор" перед последним баром
          //---- (это необходимо на всех запусках, кроме первого)
          FileSeek(HistoryHandle[curChart], last_fpos[curChart],
                   SEEK_SET );

          //---- Запоминаем параметры бара
          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(now_time[curChart] >= pre_time[curChart] + _PeriodSec[curChart])
            {
              //---- записываем сформировавшийся бар
              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] );

              //---- запоминаем место в файле, перед записью 0-го бара
              last_fpos[curChart] = FileTell(HistoryHandle[curChart]);
            }

          //---- если появились пропущенные бары,
          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(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;
                    }
                }

              //---- записываем пропущенный бар в файл
              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], 1,
                              DOUBLE_VALUE);
              FileFlush(HistoryHandle[curChart]);

              //---- запоминаем место в файле, перед записью 0-го бара
              last_fpos[curChart] = FileTell( HistoryHandle[curChart] );
            }

          //---- записываем текущий бар
          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]);

          //---- запоминаем параметры записанного бара
          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];

          //---- находим окно, в которое будем "отправлять" свежие котировки
          if(hwnd[curChart] == 0)
            {
              hwnd[curChart] = WindowHandle(StringConcatenate( "ALL",
                                            _Symbol[curChart]),
                                            _Period[curChart]);
              if(hwnd[curChart] != 0)
                  Print("< - - - График ", "ALL" + _Symbol[curChart],
                        _Period[curChart], " найден! - - - >");
            }
          //---- и, если нашли, обновляем его
          if(hwnd[curChart] != 0)
              PostMessageA(hwnd[curChart], WM_COMMAND, 33324, 0);
        }
      Sleep(RefreshLuft);
    }
   for(curChart = 0; curChart < Charts; curChart ++)
    {
      if(HistoryHandle[curChart] >= 0)
        {
          //---- закрываем файл
          FileClose(HistoryHandle[curChart]);
          HistoryHandle[curChart] = -1;
        }
    }
   return(0);
  }

 Теперь попробуем запустить эксперт на пятиминутном графике EURUSD с параметром ChartList, равным «EURUSD1,GBPUSD1,EURGBP1″, и откроем все три графика в режиме «офф-лайн»:





    Кажется, у нас всё получилось.
Все три графика синхронно обновляются и при появлении пропусков будут «залатаны».

Прикрепленные файлы:
 AllMinutes.mq4 (15.0 Kb)
 AllMinutes_Step1.mq4 (4.9 Kb)
 AllMinutes_Step2.mq4 (9.2 Kb)
Источник: mql4.com

Оставить комментарий

Чтобы оставлять комментарии Вы должны быть авторизованы.

Похожие посты