Непонятное поведение OnCalculate() в индикаторе - страница 3

 
Sergey_Mechanic:

Автор, если вам нужно выполнение кода только при поступлении нового тика, пишите советник, а не индикатор.

Этот вариант мне известен и не подходит. Надо именно в индикаторе.

Sergey_Mechanic:

И не только. Ещё при достижении лимита баров на графике (когда часть старых баров удаляется и значение rates_total меняется), перемотке графика назад с подргузкой истории - короче, при любом изменении ценовых данных. А ещё после каждого события Init, в том числе при переключении таймфреймов (что у автора и происходит).

 Еще раз повторю, что этот эффект происходит на "ровном месте", никаких достижений лимитов, перемоток, переключений графиков и т.п.!!! Время случайное, не равное границе периодов/свечей. К терминалу вообще не прикасаюсь. Да сами можете запустить и попробовать этот код.

 
AlexPORT:

Этот вариант мне известен и не подходит. Надо именно в индикаторе.

 Еще раз повторю, что этот эффект происходит на "ровном месте", никаких достижений лимитов, перемоток, переключений графиков и т.п.!!! Время случайное, не равное границе периодов/свечей. К терминалу вообще не прикасаюсь. Да сами можете запустить и попробовать этот код.

Просто поступил новый тик. Кто-то, или что-то разместил новую заявку. Без разницы в цене, без запаха апельсина,- просто новую.

 
tara:

Просто поступил новый тик. Кто-то, или что-то разместил новую заявку. Без разницы в цене, без запаха апельсина,- просто новую.

И зачем об этом информировать индикатор? Изменения цены же не было.

Ладно, проверил... Добавил к биду и аску, фильтр по тиковому объему - НИЧЕГО не изменилось. Так что мимо.

В принципе не так часто это происходит, терпимо, ~раз в минуту лишний обработчик проблемы не создаст. Но все же хотелось бы разрабов услышать по этому поводу.

 

Меня теперь даже больше волнует 2-й вопрос - почему, когда меняешь период на одном из графиков, событие OnCalculate() возникает и на всех других графиках???!!!

 

Когда Вы меняете таймфрейм у какого-то графика, то это означает запрос серверу на исторические данные этого самого нового таймфрейма. В отличие от пятёрки, в четвёрке исторические данные хранятся по каждому таймфрейму отдельно (а не в виде минуток).

Сервер принял запрос и послал в ответ недостающие данные.

Клиентский терминал принял порцию исторических данных и выставил всем графикам сообщение "эй, графики, тут обновление истории подвалило". Всем графикам.

Все графики идут в историческую базу за обновлением своих данных и (вот оно!) пересчитывают свои индикаторы вне зависимости от того, обновились данные графика или нет.


Чтобы предотвратить "лишний" пересчёт (лишний вызов OnCalculate), нужно сделать очень маленькое изменение в примере индикатора, представленном топикстартером.

В OnCalculate вместо нуля return(0) нужно возвращать rates_total

#property strict
#property indicator_chart_window

double OldBid, OldAsk;

int OnCalculate(const int rates_total,
                const int prev_calculated,
                const datetime &time[],
                const double &open[],
                const double &high[],
                const double &low[],
                const double &close[],
                const long &tick_volume[],
                const long &volume[],
                const int &spread[])
{
   if (Bid != OldBid || Ask != OldAsk)
   {
        OldBid = Bid;
        OldAsk = Ask;
        return(rates_total);
   }
   
   Print("Почему сюда попадает?");
   
   return(rates_total);
}
 
stringo:

Когда Вы меняете таймфрейм у какого-то графика, то это означает запрос серверу на исторические данные этого самого нового таймфрейма. В отличие от пятёрки, в четвёрке исторические данные хранятся по каждому таймфрейму отдельно (а не в виде минуток).

Сервер принял запрос и послал в ответ недостающие данные.

Клиентский терминал принял порцию исторических данных и выставил всем графикам сообщение "эй, графики, тут обновление истории подвалило". Всем графикам.

Все графики идут в историческую базу за обновлением своих данных и (вот оно!) пересчитывают свои индикаторы вне зависимости от того, обновились данные графика или нет.

Но Вы самый важный, ключевой момент почему-то не объяснили - почему сообщение об обновлении исторических данных посылается ВСЕМ графикам, хотя таймфрейм был изменен на ОДНОМ конкретном?

 
stringo:

В OnCalculate вместо нуля return(0) нужно возвращать rates_total

Да уже давно переделал и на rates_total - никакого эффекта! По крайней мере заметного "на глаз".
 
AlexPORT:

Но Вы самый важный, ключевой момент почему-то не объяснили - почему сообщение об обновлении исторических данных посылается ВСЕМ графикам, хотя таймфрейм был изменен на ОДНОМ конкретном?

Как не объяснил?

Клиентский терминал принял порцию исторических данных и выставил всем графикам сообщение "эй, графики, тут обновление истории подвалило". Всем графикам.

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

Графики уже сами разбираются, их история пришла или не их

 
AlexPORT:
Да уже давно переделал и на rates_total - никакого эффекта! По крайней мере заметного "на глаз".

Если Bid==OldBid и Ask==OldAsk, то это может означать, что пока вызывался OnCalculate, цена вернулась на прежнее место

Попробуйте вот такой индикатор

//+------------------------------------------------------------------+
//|                                                TestCalculate.mq4 |
//|                        Copyright 2016, MetaQuotes Software Corp. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2016, MetaQuotes Software Corp."
#property link      "https://www.mql5.com"
#property version   "1.00"
#property strict
#property indicator_chart_window

double OldBid,OldAsk;
long   OldVol;
//+------------------------------------------------------------------+
//| Custom indicator iteration function                              |
//+------------------------------------------------------------------+
int OnCalculate(const int rates_total,
                const int prev_calculated,
                const datetime &time[],
                const double &open[],
                const double &high[],
                const double &low[],
                const double &close[],
                const long &tick_volume[],
                const long &volume[],
                const int &spread[])
  {
//---
   if(Bid!=OldBid || Ask!=OldAsk || OldVol!=tick_volume[0])
     {
      if(Bid==OldBid && Ask==OldAsk)
         PrintFormat("Ask=%.5f  Bid=%.5f  tick_volume=%I64d  OldVol=%I64d",Bid,Ask,tick_volume[0],OldVol);
      OldVol = tick_volume[0];
      OldBid = Bid;
      OldAsk = Ask;
      return(rates_total);
     }

   Print("Почему сюда попадает?");

   return(rates_total);
  }
//+------------------------------------------------------------------+
 
stringo:

Как не объяснил?

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

Графики уже сами разбираются, их история пришла или не их

Что значит МОЖЕТ? Мы говорим о конкретном случае - изменении периода по конкретному инструменту. Т.е. на сервер должен вполне уйти вполне однозначный запрос: "Верни мне свежие данные по инструменту XXXYYY по периоду TT"... и вернуться по идее должно что-то типа: "Вот тебе пачка данных по инструменту XXXYYY по периоду TT"...

Вобщем, правильно ли я понимаю, что никакого бага тут нет и все дело в особенности реализации протокола обмена/архитектуры МТ4?

Хотя все равно не понятно - почему так сделано? Я понимаю, когда сервер принудительно присылает обновленные данные, тики - тут непредсказуемо, неизвестно, что и кому придет в следующий момент. Но когда клиентский терминал сам инициирует вполне конкретный запрос, вроде бы логично и вернуть запрошенные данные четко "по адресу"...

 
stringo:

Если Bid==OldBid и Ask==OldAsk, то это может означать, что пока вызывался OnCalculate, цена вернулась на прежнее место

Хм, интересный момент, понял, спасибо за наводку! :) 

Однако! Это не объясняет проблему иногда подряд идущих одинаковых тиков, где вообще все равно и обе цены и тиковый объем

Вот, доработал код:

double OldBid, OldAsk;
long OldTickVolume;
datetime OldTimeLocal, OldTimeCurrent;
uint OldTickCount, NewTickCount;

int OnCalculate(const int rates_total,
                const int prev_calculated,
                const datetime &time[],
                const double &open[],
                const double &high[],
                const double &low[],
                const double &close[],
                const long &tick_volume[],
                const long &volume[],
                const int &spread[])
{
   NewTickCount   = GetTickCount();
        
   if (Bid != OldBid || Ask != OldAsk || tick_volume[0] != OldTickVolume)
   {
        OldTimeLocal   = TimeLocal();
        OldTimeCurrent = TimeCurrent();
        OldTickCount   = NewTickCount;

        OldBid = Bid;
        OldAsk = Ask;
        OldTickVolume = tick_volume[0];
        
        return(rates_total);
   }
        
   Print("Почему это сработало?  OldTimeLocal = ",OldTimeLocal,"  TimeLocal = ",TimeLocal(),"  OldTimeCurrent = ",OldTimeCurrent,"  TimeCurrent = ",TimeCurrent(),"  lms = ",NewTickCount-OldTickCount);

   OldTimeLocal   = TimeLocal();
   OldTimeCurrent = TimeCurrent();
   OldTickCount   = NewTickCount;
        
   return(rates_total);
}

В большинстве случаев разница по времени в миллисекундах между такими тиками (lms) = 0

Причина обращения: