Организация пакетной обработки задачи по снятию изображений с экрана графика

 

Встала у меня задача доработать скрипт https://www.mql5.com/ru/code/936 , который делает скрины.

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

Самой сложной задачей оказалось определить ширину скриншота, что б туда помещались все бары за указанный диапазон. Методом перебора, я определил, количество баров за шкалой в зависимости от настроек Windows и силы приближения графика, но я не учел, что ширина ценовой шкалы графика может меняться и в зависимости от цен, поэтому метод оказался не универсальным, но я его оставил в коде.

Уважаемый fxsaber предлагает такой вариант для определения ширины ценовой шкалы:

// Ширина вертикальной шкалы графика
int GetWidthScale(const long chartID=0)
  {
   const string Name=__FUNCTION__+(string)MathRand();

   ObjectCreate(chartID,Name,OBJ_CHART,0,0,0);
   ObjectSetInteger(chartID,Name,OBJPROP_XDISTANCE,-1e3);

   const long chart=ObjectGetInteger(chartID,Name,OBJPROP_CHART_ID);

   const int Res=(int)(ChartGetInteger(chart,CHART_WIDTH_IN_PIXELS) -
                  (ChartSetInteger(chart,CHART_SHOW,false) ? ChartGetInteger(chart,CHART_WIDTH_IN_PIXELS) : 0));

   ObjectDelete(chartID,Name);

   return(-Res);
  }

А для получения размера 1 бара в пикселях был предложен такой вариант

int GetBarSize( const ulong Chart = 0 )
{
  return(1 << (int)ChartGetInteger(Chart, CHART_SCALE));
}

Однако, у меня есть подозрение, что эта функция будет искажать действительность из-за округления (да я понимаю, что пиксели - целая величина, но количество баров может быть не целым на графике).

Пробовал разные варианты, и где-то один вариант лучше другого, а где-то нет.

Такой вариант показал себя, как более точный

//---размер скрина
int pp=(double)ChartGetInteger(handle,CHART_WIDTH_IN_PIXELS)/(double)ChartGetInteger(handle,CHART_WIDTH_IN_BARS)*ЧИСЛО_БАРОВ-ШКАЛА+ОТСТУП

Ниже выкладываю черновой код, который делает группу скриншотов, согласно датам из массива, можно настроить коэффициент для отступа цены от верхней/нижней границе скриншота, а так же выбрать вариант расчета в зависимости от настройки Windows и по методу определения ширины шкалы fxsaber

//+------------------------------------------------------------------+
//|                                                     WideShootrer |
//|                                           Copyright 2012,Karlson |
//+------------------------------------------------------------------+
#property copyright   "2012, Karlson."
#property link        "https://login.mql5.com/ru/users/Karlson"
#property description "WideShootrer"
#property version "1.00"
//-------------------------------------------------------------------+
#property script_show_inputs
//#define TOSTRING(A) #A + " = " + (string)(A) + "\n"

//input int Zoom=2;//Zoom на котором будет делатся скрин шот
enum ENUM_ZOOMWINDOWS_NAME
  {
ZoomWindows_100=0,
ZoomWindows_125=1,
Zoom_fxsaber=2,
  };
input ENUM_ZOOMWINDOWS_NAME ZOOM=ENUM_ZOOMWINDOWS_NAME(1);

input double K_Proc=0.001;//Коэффициент расширения экрана для снятия скрина


//input string Data_ScreenStart="10.04.2018 10:00";      // - дата с которой будет делаться скриншот
//input string Data_ScreenStop="10.04.2018 22:00";       // - по какую дату будет делаться скриншот (ширина скриншота, фактически конвертированная в итоге в бары).
input int Data_ScreenStart_Shift=0;    // - смещение области для захвата экрана в барах, относительно указанной даты в правую часть графика
input int Data_ScreenStop_Shift=0;     // - смещение области для захвата экрана в барах, относительно указанной даты в левую часть графика
//-------------------------------------------------------------------+
double High[],Low[];
int bars=0;
string Data_ScreenStart="10.04.2018 10:00";      // - дата с которой будет делаться скриншот
string Data_ScreenStop="10.04.2018 22:00";       // - по какую дату будет делаться скриншот (ширина скриншота, фактически конвертированная в итоге в бары).
int Schkala=0;
//-------------------------------------------------------------------+
void OnStart()
  {
   //if(_Digits==5 || _Digits==3) {KF=10;} else {KF=1;}
   
  for (int x=0; x<17+1; x++)
  {
  Schkala=GetWidthScale();   
  Data_ScreenStart=StartDate(x);
  Data_ScreenStop=StopDate(x);
  
  Screen();
//  Print(TOSTRING(GetWidthScale()));
  }

  }

//+------------------------------------------------------------------+

void Screen()
{
int Shift_Start=iBarShift(Symbol(),PERIOD_CURRENT,StringToTime(Data_ScreenStart),false); 
int Shift_Stop=iBarShift(Symbol(),PERIOD_CURRENT,StringToTime(Data_ScreenStop),false); 
//Print(Shift_Start);
int BarsTotal=Bars(Symbol(),PERIOD_CURRENT);
//Shift_Start=100*(-1);
Shift_Start=Shift_Start*(-1);
Shift_Stop=Shift_Stop*(-1);
//--- получим handle текущего графика 
   long handle=ChartID(); 
   if(handle>0) // если получилось, дополнительно настроим 
     { 
     //Print ("handle=",handle);
      //--- отключим автопрокрутку 
      ChartSetInteger(handle,CHART_AUTOSCROLL,false); 
      
      //---отображение сетки на графике
      ChartSetInteger(handle,CHART_SHOW_GRID,false); 

      //---Отображение значения Bid горизонтальной линией на графике      
      ChartSetInteger(handle,CHART_SHOW_BID_LINE,false);

      //---Отображение значения Ask горизонтальной линией на графике
      ChartSetInteger(handle,CHART_SHOW_ASK_LINE,false);
 
      //---Отображение значения Last горизонтальной линией на графике
      ChartSetInteger(handle,CHART_SHOW_LAST_LINE,false);
      
      //--- установим отступ правого края графика 
//      ChartSetInteger(handle,CHART_SHIFT,true); 
      //--- отобразим в виде свечей 
//      ChartSetInteger(handle,CHART_MODE,CHART_CANDLES); 
      //--- установить режим отображения тиковых объемов 
//      ChartSetInteger(handle,CHART_SHOW_VOLUMES,CHART_VOLUME_TICK);  
      //--- получим номер самого первого видимого на графике бара (нумерация как в таймсерии) 
      //long first_bar=ChartGetInteger(0,CHART_FIRST_VISIBLE_BAR,0); 

int Zoom=(int)ChartGetInteger(handle,CHART_SCALE,0);
//Print(ChartGetInteger(handle,CHART_SCALE,0));

//--принудительный зум
//   ChartSetInteger(handle,CHART_SCALE,Zoom);

// зафиксируем шкалу и установим верх низ
   ChartSetInteger(handle,CHART_SCALEFIX,0,1);
   ChartSetInteger(handle,CHART_AUTOSCROLL,false);

// определяем сколько показывает на графике баров - потребуется для определения ширины скриншота
   int vis_bar=(int)ChartGetInteger(handle,CHART_VISIBLE_BARS);
   //Print("По ширине графика отображено баров=",vis_bar);

// сместим на нужный временной участок график
   ChartNavigate(handle,CHART_END,Shift_Start);
   int _SS=Shift_Start+vis_bar-3;   
   ChartNavigate(handle,CHART_END,_SS);

// определяем первый левый бар.От него будем делать скриншот
   int first_bar=(int)ChartGetInteger(handle,CHART_FIRST_VISIBLE_BAR);

// получаем цены баров в массив за период скрина для установки хай-лоу графика
   bars=Shift_Start*(-1)-Shift_Stop*(-1);
   if(first_bar<bars) {bars=first_bar;} // если график смещен менее чем требуется баров,тогда берем все что есть вправо до текущего времени
   int ch=CopyHigh(_Symbol,_Period,StringToTime(Data_ScreenStart),StringToTime(Data_ScreenStop),High);
   int cl=CopyLow(_Symbol,_Period,StringToTime(Data_ScreenStart),StringToTime(Data_ScreenStop),Low);

// определяем хай лоу на нашем промежутке и устанавливаем верх низ шкалы цены
   double max_price=High[ArrayMaximum(High,0,WHOLE_ARRAY)]*(1.00+K_Proc);
   double min_price=Low[ArrayMinimum(Low,0,WHOLE_ARRAY)]*(1.00-K_Proc);
   
// установим верх низ
   ChartSetDouble(handle,CHART_FIXED_MAX,max_price);
   ChartSetDouble(handle,CHART_FIXED_MIN,min_price);

// получим текущую дату в простом формате для фомирования имени файла
   MqlDateTime day;
   TimeToStruct(StringToTime(Data_ScreenStart),day);
   MqlDateTime dayEnd;
   TimeToStruct(StringToTime(Data_ScreenStop),dayEnd);
   string file=_Symbol+"_"+(string)day.year+"_"+(string)day.mon+"_"+(string)day.day+"_"+(string)day.hour+"_"+(string)day.min+
   "-"+(string)dayEnd.year+"_"+(string)dayEnd.mon+"_"+(string)dayEnd.day+"_"+(string)dayEnd.hour+"_"+(string)dayEnd.min+".png";

// определим высоту и ширину будущего скрина
   int scr_height=(int)ChartGetInteger(0,CHART_HEIGHT_IN_PIXELS);


// Обозначим границы баров вертикальными линиями
   ObjectsDeleteAll(handle,"Shift",-1);
VLineCreate(handle,"Shift_Start",0,StringToTime(Data_ScreenStart),clrYellow,STYLE_SOLID,1,false, 
            false,true,false,0);
VLineCreate(handle,"Shift_Stop",0,StringToTime(Data_ScreenStop),clrYellow,STYLE_SOLID,1,false, 
            false,true,false,0);


// сделаем скриншот
int ZoomX=0;
if (ZOOM==ZoomWindows_100)
   {
      if (Zoom==0)ZoomX=52;
      if (Zoom==1)ZoomX=27;
      if (Zoom==2)ZoomX=15;
      if (Zoom==3)ZoomX=8;
      if (Zoom==4)ZoomX=5;
      if (Zoom==5)ZoomX=3;
   }
if (ZOOM==ZoomWindows_125)
   {
      if (Zoom==0)ZoomX=68;
      if (Zoom==1)ZoomX=35;
      if (Zoom==2)ZoomX=18;
      if (Zoom==3)ZoomX=10;
      if (Zoom==4)ZoomX=6;
      if (Zoom==5)ZoomX=4;
   }

int pp=GetBarSize(0)*((Shift_Start-Shift_Stop)*(-1)+ZoomX);
//int pp=(double)ChartGetInteger(handle,CHART_WIDTH_IN_PIXELS)/(double)ChartGetInteger(handle,CHART_WIDTH_IN_BARS)*((Shift_Start-Shift_Stop)*(-1)+ZoomX);

if (ZOOM==Zoom_fxsaber)
   {
      //pp=GetBarSize(0)*((Shift_Start-Shift_Stop)*(-1))+Schkala*1.3;
      pp=(double)ChartGetInteger(handle,CHART_WIDTH_IN_PIXELS)/(double)ChartGetInteger(handle,CHART_WIDTH_IN_BARS)*((Shift_Start-Shift_Stop)*(-1)+2)+Schkala+3;
   }

//Print((Shift_Start-Shift_Stop));
//ChartSetInteger(0,CHART_SHOW_PRICE_SCALE ,0);//Показывать или нет ценовую шкалу

   ChartScreenShot(handle,file,pp,scr_height,ALIGN_LEFT);
   if(GetLastError()>0) {Print("Error  (",GetLastError(),") ");} ResetLastError();

//---востановим настройки чарта
    ChartSetInteger(handle,CHART_SCALEFIX,0,0);
    ChartSetInteger(handle,CHART_SHOW_PRICE_SCALE ,1);

//---удалим созданные объекты
    ObjectsDeleteAll(handle,"Shift",-1);
     } 
}



//+------------------------------------------------------------------+ 
//| Получим iBarShift для заданного номера бара                      | 
//+------------------------------------------------------------------+    
int iBarShift(const string Symb,const ENUM_TIMEFRAMES TimeFrame,datetime time,const bool Exact=false)
  {
   static int Res=-1;
   static string LastSymb=NULL;
   static ENUM_TIMEFRAMES LastTimeFrame=0;
   static datetime LastTime=0;
   static bool LastExact=false;
   static int PerSec=::PeriodSeconds(LastTimeFrame);
   
   if (LastTimeFrame!=TimeFrame) PerSec=::PeriodSeconds(TimeFrame);
   time-=time%PerSec;

   if((time!=LastTime) || (Symb!=LastSymb) || (TimeFrame!=LastTimeFrame) || (Exact!=LastExact))
     {
      Res=::Bars(Symb,TimeFrame,time,UINT_MAX)-1;
      if(Res<0) Res=0;

      LastTime = time;
      LastSymb = Symb;
      LastTimeFrame=TimeFrame;
      LastExact=Exact;
     }

   return(Res);
  }   
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
datetime iTime(string symbol,int tf,int index)
  {
   if(index < 0) return(-1);
//   ENUM_TIMEFRAMES timeframe=TFMigrate(tf);
   ENUM_TIMEFRAMES timeframe=PERIOD_CURRENT;
   datetime Arr[];
   if(CopyTime(symbol,timeframe,index,1,Arr)>0)
      return(Arr[0]);
   else return(-1);
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+  
int GetBarSize( const ulong Chart = 0 )
{
  return(1 << (int)ChartGetInteger(Chart, CHART_SCALE));
}

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+ 
bool VLineCreate(const long            chart_ID=0,        // ID графика 
                 const string          name="VLine",      // имя линии 
                 const int             sub_window=0,      // номер подокна 
                 datetime              time=0,            // время линии 
                 const color           clr=clrRed,        // цвет линии 
                 const ENUM_LINE_STYLE style=STYLE_SOLID, // стиль линии 
                 const int             width=1,           // толщина линии 
                 const bool            back=false,        // на заднем плане 
                 const bool            selection=true,    // выделить для перемещений 
                 const bool            ray=true,          // продолжение линии вниз 
                 const bool            hidden=true,       // скрыт в списке объектов 
                 const long            z_order=0)         // приоритет на нажатие мышью 
  { 
//--- если время линии не задано, то проводим ее через последний бар 
   if(!time) 
      time=TimeCurrent(); 
//--- сбросим значение ошибки 
   ResetLastError(); 
//--- создадим вертикальную линию 
   if(!ObjectCreate(chart_ID,name,OBJ_VLINE,sub_window,time,0)) 
     { 
      Print(__FUNCTION__, 
            ": не удалось создать вертикальную линию! Код ошибки = ",GetLastError()); 
      return(false); 
     } 
//--- установим цвет линии 
   ObjectSetInteger(chart_ID,name,OBJPROP_COLOR,clr); 
//--- установим стиль отображения линии 
   ObjectSetInteger(chart_ID,name,OBJPROP_STYLE,style); 
//--- установим толщину линии 
   ObjectSetInteger(chart_ID,name,OBJPROP_WIDTH,width); 
//--- отобразим на переднем (false) или заднем (true) плане 
   ObjectSetInteger(chart_ID,name,OBJPROP_BACK,back); 
//--- включим (true) или отключим (false) режим перемещения линии мышью 
//--- при создании графического объекта функцией ObjectCreate, по умолчанию объект 
//--- нельзя выделить и перемещать. Внутри же этого метода параметр selection 
//--- по умолчанию равен true, что позволяет выделять и перемещать этот объект 
   ObjectSetInteger(chart_ID,name,OBJPROP_SELECTABLE,selection); 
   ObjectSetInteger(chart_ID,name,OBJPROP_SELECTED,selection); 
//--- включим (true) или отключим (false) режим отображения линии в подокнах графика 
   ObjectSetInteger(chart_ID,name,OBJPROP_RAY,ray); 
//--- скроем (true) или отобразим (false) имя графического объекта в списке объектов 
   ObjectSetInteger(chart_ID,name,OBJPROP_HIDDEN,hidden); 
//--- установим приоритет на получение события нажатия мыши на графике 
   ObjectSetInteger(chart_ID,name,OBJPROP_ZORDER,z_order); 
//--- успешное выполнение 
   return(true); 
  } 
  
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+   
string StartDate(int i)
  {
   string Date[18]=
     {
      "23.03.2018 10:30",
      "09.02.2018 10:30",
      "15.12.2017 10:30",
      "27.10.2017 10:30",
      "15.09.2017 10:30",
      "28.07.2017 10:30",
      "16.06.2017 10:30",
      "28.04.2017 10:30",
      "24.03.2017 10:30",
      "03.02.2017 10:30",
      "16.12.2016 10:30",
      "28.10.2016 10:30",
      "16.09.2016 10:30",
      "29.07.2016 10:30",
      "10.06.2016 10:30",
      "29.04.2016 10:30",
      "18.03.2016 10:30",
      "29.01.2016 10:30",
     };
   return (Date[i] );
  }
string StopDate(int i)
  {
   string Date[18]=
     {
      "23.03.2018 23:30",
      "09.02.2018 23:30",
      "15.12.2017 23:30",
      "27.10.2017 23:30",
      "15.09.2017 23:30",
      "28.07.2017 23:30",
      "16.06.2017 23:30",
      "28.04.2017 23:30",
      "24.03.2017 23:30",
      "03.02.2017 23:30",
      "16.12.2016 23:30",
      "28.10.2016 23:30",
      "16.09.2016 23:30",
      "29.07.2016 23:30",
      "10.06.2016 23:30",
      "29.04.2016 23:30",
      "18.03.2016 23:30",
      "29.01.2016 23:30",
     };
   return (Date[i] );
  }
//+------------------------------------------------------------------+

// Ширина вертикальной шкалы графика
int GetWidthScale(const long chartID=0)
  {
   const string Name=__FUNCTION__+(string)MathRand();

//   int _Zoom=(int)ChartGetInteger(chartID,CHART_SCALE,0);

   ObjectCreate(chartID,Name,OBJ_CHART,0,0,0);
   ObjectSetInteger(chartID,Name,OBJPROP_XDISTANCE,-1e3);

   const long chart=ObjectGetInteger(chartID,Name,OBJPROP_CHART_ID);
/*
   //--принудительный зум
   ChartSetInteger(chart,CHART_SCALE,_Zoom);
   // зафиксируем шкалу и установим верх низ
   ChartSetInteger(chart,CHART_SCALEFIX,0,1);
   ChartSetInteger(chart,CHART_AUTOSCROLL,false);
*/   
   
   const int Res=(int)(ChartGetInteger(chart,CHART_WIDTH_IN_PIXELS) -
                  (ChartSetInteger(chart,CHART_SHOW,false) ? ChartGetInteger(chart,CHART_WIDTH_IN_PIXELS) : 0));

   ObjectDelete(chartID,Name);

   return(-Res);
  }

Прошу помощи в поиске универсального решения задачи - поиск верной ширины скриншота.

Улучшения и оптимизация текущего кода так же приветствуется!

 

Вот пример неверного поиска ширины по третьему методу

Ok

Возможно это ошибка в моем коде, но найти не удается, так-как большинство скринов корректны.

error

 
Aleksey Vyazmikin:


Однако, у меня есть подозрение, что эта функция будет искажать действительность из-за округления (да я понимаю, что пиксели - целая величина, но количество баров может быть не целым на графике).

Внимательно еще раз прочтите это мое сообщение.

Ваше подозрение необоснованно. Посмотрите увеличенный до пикселей скриншот окна и просто посчитаете ручками. 


Aleksey Vyazmikin:

Мне нужно сделать скрин от даты А до даты Б включительно. При этом отработка функции должна быть корректна на любом зуме.

А Вам дату (время наверное имеется ввиду) нужно округлять до бара или до пикселя?

 
Nikolai Semko:

Внимательно еще раз прочтите это мое сообщение.

Ваше подозрение необоснованно. Посмотрите увеличенный до пикселей скриншот окна и просто посчитаете ручками. 

Что посчитать? На скрине отчетливо видно, что на нём 4 целых бара и 2 урезанных...

Nikolai Semko:

А Вам дату (время наверное имеется ввиду) нужно округлять до бара или до пикселя?

Имеется ввиду точную дату и время. Как можно округлять дату и время до пикселя? Мне нужно округлять до бара. Вообще выше скрины показывают, что нужно, и что иногда происходит.

 

Все таки надо запустить пиксельную линейку, мерять в пикселях на экране.

Потом смотреть что показывает скрипт буквально на каждом шаге, и сверяться с линейкой на экране, и понять где лажа при вашем 125% увеличении в свойсвах экрана. 

(я пользуюсь mySize)

 
Dmytro Zelenskyy:

Все таки надо запустить пиксельную линейку, мерять в пикселях на экране.

Потом смотреть что показывает скрипт буквально на каждом шаге, и сверяться с линейкой на экране, и понять где лажа при вашем 125% увеличении в свойсвах экрана. 

По идеи лажа она есть или её нет :) А тут редкие кадры несовпадения бывают... может что-то ещё надо учесть в самом коде...

При 100% разве там нет такой же лажи?
 

Если добавить выведение без шкалы

if (ZOOM==Zoom_notShkala)
   {
      pp=(double)ChartGetInteger(handle,CHART_WIDTH_IN_PIXELS)/(double)ChartGetInteger(handle,CHART_WIDTH_IN_BARS)*((Shift_Start-Shift_Stop)*(-1)+3);
      ChartSetInteger(0,CHART_SHOW_PRICE_SCALE ,0);//Показывать или нет ценовую шкалу
   }

То пришлось добавить уже 3 бара, вместо двух к дельте дат начала и окончания скриншота (1 - для начала чуть правей, другой 1 - что б включительно скриншотился бар, а вот что за ещё 1?)

И Видим, что так же большинство уместилось за небольшим исключением - та же дата:

значит дело не в шкале... тогда в чём?

 

На графиках число баров и разрешение одинаковое, это так, информация к размышлению....

 
Aleksey Vyazmikin:

Что посчитать? На скрине отчетливо видно, что на нём не 4 целых бара и 2 урезанных...

Имеется ввиду точную дату и время. Как можно округлять дату и время до пикселя? Мне нужно округлять до бара. Вообще выше скрины показывают, что нужно, и что иногда происходит.

Эх, не хотите Вы внимательно читать мое первое сообщение. А я писал:
"Левый бар всегда рисуется от центра, правый как угодно. Поэтому начало координат в нуле (слева)."

Если Вы посчитаете пиксели, то поймете что ширина между барами в данном максимальном масштабе всегда 32, а левый бар начинается от центральной линии, а по сути это и есть временное начало. Т.е. размер тела свечи 23 (центральная линия + по 11 пикселей по бокам). У левого бара 11 пикселей - это как раз от центра, а значит от начала времени бара. Поэтому урезанным может быть только самый правый бар. О чем я и писал. С другими масштабами вся та же логика.
Поэтому всегда необходимо делать начало отсчета с левой свечи. Номер левого бара можно узнать с помощью идентификатора CHART_FIRST_VISIBLE_BAR
Что сложного? 
Ваша задача легко решается средствами MQL5. В чем затык? Можно поконкретней?

 
Nikolai Semko:

Эх, не хотите Вы внимательно читать мое первое сообщение. А я писал:
"Левый бар всегда рисуется от центра, правый как угодно. Поэтому начало координат в нуле (слева)."

Если Вы посчитаете пиксели, то поймете что ширина между барами в данном максимальном масштабе всегда 32, а левый бар начинается от центральной линии, а по сути это и есть временное начало. Т.е. размер тела свечи 23 (центральная линия + по 11 пикселей по бокам). У левого бара 11 пикселей - это как раз от центра, а значит от начала времени бара. Поэтому урезанным может быть только самый правый бар. О чем я и писал. С другими масштабами вся та же логика.
Поэтому всегда необходимо делать начало отсчета с левой свечи. Номер левого бара можно узнать с помощью идентификатора CHART_FIRST_VISIBLE_BAR
Что сложного? 
Ваша задача легко решается средствами MQL5. В чем затык? Можно поконкретней?

Вы смотрели код? Из комментария очевидно - нет.

Затык показал визуально в чём. Есть идея, сейчас проверю.
 

Оказывается дело в этой функции - она врет иногда

//+------------------------------------------------------------------+ 
//| Получим iBarShift для заданного номера бара                      | 
//+------------------------------------------------------------------+    
int iBarShift(const string Symb,const ENUM_TIMEFRAMES TimeFrame,datetime time,const bool Exact=false)
  {
   static int Res=-1;
   static string LastSymb=NULL;
   static ENUM_TIMEFRAMES LastTimeFrame=0;
   static datetime LastTime=0;
   static bool LastExact=false;
   static int PerSec=::PeriodSeconds(LastTimeFrame);
   
   if (LastTimeFrame!=TimeFrame) PerSec=::PeriodSeconds(TimeFrame);
   time-=time%PerSec;

   if((time!=LastTime) || (Symb!=LastSymb) || (TimeFrame!=LastTimeFrame) || (Exact!=LastExact))
     {
      Res=::Bars(Symb,TimeFrame,time,UINT_MAX)-1;
      if(Res<0) Res=0;

      LastTime = time;
      LastSymb = Symb;
      LastTimeFrame=TimeFrame;
      LastExact=Exact;
     }

   return(Res);
  }   

И вот где-где же брать нормальную функцию, которая не врет и во всех условиях работает корректно!?!?!

Поменял на эту и все нормально работает 

int iBarShift(const string Symb,const ENUM_TIMEFRAMES TimeFrame,datetime time,bool exact=false)
  {
   int Res=Bars(Symb,TimeFrame,time+1,UINT_MAX);
   if(exact) if((TimeFrame!=PERIOD_MN1 || time>TimeCurrent()) && Res==Bars(Symb,TimeFrame,time-PeriodSeconds(TimeFrame)+1,UINT_MAX)) return(-1);
   return(Res);
  }
Причина обращения: