Графики без "дыр"
Мотивация
Система построения графиков в МТ 4 устроена так, что рисуются только те бары, во временных промежутках которых было хоть одно изменение цены. Если в течение минуты изменения цены не было, то на минутном графике образуется пропуск в один бар.
Разработчики сознательно выбрали такой способ построения графиков, так как большинство пользователей их продукта отдают предпочтение графикам, содержащим только существующие цены. Тем не менее, имеются и поклонники непрерывных графиков. Они считают, что если даже изменения цены не было, то должен быть нарисован бар, у которого цена открытия равна цене закрытия предыдущего бара. Таким образом, на графике не будет пропусков по шкале времени, и 100 баров на минутном графике будут всегда соответствовать периоду 100 минут. При текущей реализации эти цифры могут отличаться. Например, 100 минут могут "вместиться" в 98 баров, если за 100 минут было две минуты без поступления котировок.
К счастью, в MQL 4 есть все необходимые инструменты для самостоятельной реализации таких графиков.
Реализация
Для начала, поделим задачу на два этапа:
-
Обработка исторических данных;
Обновление последнего бара.
На первом этапе мы создадим новый файл истории с приставкой "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", и откроем все три графика в режиме "офф-лайн":
Кажется, у нас всё получилось.
Все три графика синхронно обновляются и при появлении пропусков будут "залатаны".- Бесплатные приложения для трейдинга
- 8 000+ сигналов для копирования
- Экономические новости для анализа финансовых рынков
Вы принимаете политику сайта и условия использования