[SOLVED] Индикаторы неправильно инстанцируются при вызове/создании из индикатора другого рабочего таймфрейма.

 

ОБНОВЛЕНИЕ: см. обходной путь ниже

CopyBuffer() выбрасывает ошибку 4806 (Данные индикатора недоступны) при вызове индикатора с другим тайм-фреймом из кода индикатора. Это происходит при вызове действительного хэндла индикатора с таймфреймом, отличным от текущего рабочего таймфрейма. Ошибка проявляется только во время инициализации и первого вызова OnCalculate() ДО первого тика данных. Для того чтобы изолировать ошибку, были применены следующие методы:

Этот блок кода используется для проверки вывода CopyBuffer() при вызове из скрипта, советника и индикатора.

#include <Indicators\Trend.mqh>


   CiMA ima;
   ima.Create(_Symbol,PERIOD_H1,20,0,MODE_SMA,PRICE_CLOSE);
   ima.Refresh();
  
   CIndicatorBuffer *buff = ima.At(0);
   int total = buff.Total();
   Print(__LINE__," ",__FUNCSIG__," ",buff.Name()," Buffer size = ",total);
   for(int i=0;i<total;i++){
      if(i>2) break;
      else{
         Print(__LINE__," ",__FUNCSIG__," ",ima.PeriodDescription()," iMA(",i,") value = ",DoubleToString(ima.Main(i),_Digits));  
      }
   }

Полный код индикатора:

#property indicator_chart_window

#include <Indicators\Trend.mqh>
#include <errordescription.mqh>

CiMA ima;
int m_bufferSize = -1;
//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int OnInit()
  {
   static int iCnt = 0;
//--- indicator buffers mapping
      Print("-----------------------",TimeCurrent(),"--------------------------");
//---
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//| 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(rates_total != prev_calculated || m_bufferSize < 1 ){
      ResetLastError();
      ima.Create(_Symbol,PERIOD_H1,20,0,MODE_SMA,PRICE_CLOSE);
      ima.Refresh();
      
      CIndicatorBuffer *buff = ima.At(0);
      m_bufferSize = buff.Total();
      Print(__LINE__," ",__FUNCSIG__," ",buff.Name()," Buffer size = ",m_bufferSize);
      if(m_bufferSize < 1){
         Print(ErrorDescription(GetLastError()));
      } else {
         for(int i=0;i<m_bufferSize;i++){
            if(i>2) break;
            else{
               Print(__LINE__," ",__FUNCTION__," ",ima.PeriodDescription()," iMA(",i,") value = ",DoubleToString(ima.Main(i),_Digits));  
            }
         }
      }
   }
//--- return value of prev_calculated for next call
   return(rates_total);
}
//+------------------------------------------------------------------+

Единственный способ не получить ошибку - запустить его на графиках H1 (тот же TF).

Ссылки на сообщение на форуме с той же проблемой:

https://www.mql5.com/en/forum/73274

https://www.mql5.com/en/forum/13676

https://www.mql5.com/en/forum/30958

https://www.mql5.com/en/forum/16614

РЕШЕНИЕ ПРОБЛЕМЫ:

Обходным решением было создание индикатора в OnInit() и установка EventSetMillisecondTimer на 1 мс. Это позволило OnCalculate() вернуться после первого прохода и быстро вызвать OnTimer для второго прохода. Для фиксации потребовался только один вызов события OnTimer, и дальнейшая временная задержка для вычислений не требовалась.

//+------------------------------------------------------------------+
//|                                                    THROWAWAY.mq5 |
//|                                                      nicholishen |
//|                                   www.reddit.com/u/nicholishenFX |
//+------------------------------------------------------------------+
#property copyright "nicholishen"
#property link      "www.reddit.com/u/nicholishenFX"
#property version   "1.00"
#property indicator_chart_window

#include <Indicators\Trend.mqh>
#include <errordescription.mqh>

CiMA ima;
int m_bufferSize = -1;
bool timedEvent = false;
//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int OnInit()
  {
      int waitMS = 1;
      Print("-----------------------",TimeCurrent(),"--------------------------");
  
      ima.Create(_Symbol,PERIOD_H1,20,0,MODE_SMA,PRICE_CLOSE);
      EventSetMillisecondTimer(waitMS);
      Print("OnTimer set to ",waitMS," ms");
      
//---
   return(INIT_SUCCEEDED);
  }

void OnTimer()
  {
//---
   ima.Refresh();
   EventKillTimer();
   timedEvent = true;
  
  }
//+------------------------------------------------------------------+
//| 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[])
  {

   static int tickCnt = 0;
   tickCnt++;
  
   if(!timedEvent)return rates_total;
//---
   if(rates_total != prev_calculated || m_bufferSize < 1 ){
      ResetLastError();
      CIndicatorBuffer *buff = ima.At(0);
      m_bufferSize = buff.Total();
      if(m_bufferSize <=0) ima.Refresh();
      // try wait with looping  
      
      if(m_bufferSize < 1){
         Print(ErrorDescription(GetLastError()));
        
      } else {
         for(int i=0;i<m_bufferSize;i++){
            if(i>2) break;
            else{
               Print(__LINE__," ",__FUNCTION__,buff.Name(),
                     " Buffer size = ",m_bufferSize,
                     " | ",ima.PeriodDescription()," iMA(",i,") value = ",
                     DoubleToString(ima.Main(i),_Digits),
                     " | Tick-count = ",tickCnt
                     );  
            }
         }
      }
   }
//--- return value of prev_calculated for next call
   return(rates_total);
}
//+------------------------------------------------------------------+
Error 4806 while copying buffers
Error 4806 while copying buffers
  • www.mql5.com
com/en/articles/100, but tried to change it to use the CCI indicator only.
 
Кто-нибудь знает обходной путь?
 

Боюсь, что это не ошибка в MT5, а ошибка в вашем коде. Кстати, как и во всех других темах, о которых вы сообщаете. Вы должны иметь дело с платформой/языком так, как он был разработан, а не так, как вы думаете или хотите, чтобы было.

Почему вы используете ima.Create() в OnCalculate()? Вы получаете хэндл, но данные еще не доступны, вы получаете ошибку, затем ваш код больше никогда не вызывается.

P.S: Что значит "Работает без доступа к данным." ?
 
Alain Verleyen:

Боюсь, что это не ошибка в MT5, а ошибка в вашем коде. Кстати, как и во всех других темах, о которых вы сообщаете. Вы должны иметь дело с платформой/языком так, как он был разработан, а не так, как вы думаете или хотели бы.

Почему вы используете ima.Create() в OnCalculate()? Вы получаете хэндл, но данные еще не доступны, вы получаете ошибку, затем ваш код больше никогда не вызывается.

P.S: Что значит "Работает без доступа к данным." ?
Боюсь, что это действительно ошибка в платформе. Я запускаю точно такой же блок кода в OnInit() в эксперте и не получаю никаких ошибок, в то время как OnInit() в индикаторе выбрасывает ошибки. Работа без доступа к данным означает, что он работает в автономном режиме, в тестере или в нерабочее время. Вызов любого индикатора должен инстанцировать его из любого места в любое время, и тот факт, что платформа непоследовательна в этом отношении, означает, что это не особенность, а ошибка. Наличие ima.Create внутри oncalculate - лишь пример, потому что он также не может инстанцировать индикатор на любом другом таймфрейме - из любого места, где вы вызываете его внутри индикатора, до первого тика (обновления данных). Вы можете обновлять его бесконечное количество раз, но он не получит доступ к данным индикатора до тех пор, пока oncalculate не выполнится ровно один раз и не вернется обратно. Он сработает при последующем проходе, когда поступит новый тик. Баг.

Еще раз подчеркну, что он действительно корректно работает из любого места в экспертах и скриптах, просто в индикаторах он почему-то сломан.

 
nicholishen:
Боюсь, что это действительно ошибка в платформе. Я запускаю точно такой же блок кода в OnInit() в эксперте и не получаю никаких ошибок, в то время как OnInit() в индикаторе выбрасывает ошибки. Работа без доступа к данным означает, что он работает в автономном режиме, в тестере или в нерабочее время. Вызов любого индикатора должен инстанцировать его из любого места в любое время, и тот факт, что платформа непоследовательна в этом отношении, означает, что это не особенность, а ошибка. Наличие ima.Create внутри oncalculate - это просто пример, потому что он также не инстанцирует индикатор на любом другом таймфрейме - из любого места, где вы вызываете его внутри индикатора, до первого тика (обновления данных). Еще раз хочу подчеркнуть, что это работает корректно из любого места в экспертах и скриптах, просто в индикаторах это как-то нарушено.

Вы не верите мне, это ваше право, но вы ошибаетесь.

Я могу только посоветовать вам написать в ServiceDesk и, пожалуйста, сообщить об их ответе здесь.

 
Alain Verleyen:

Вы не верите мне, это ваше право, но вы ошибаетесь.

Я могу только предложить вам написать в ServiceDesk, и, пожалуйста, сообщите об их ответе здесь.

Спасибо, я так и сделаю. Если я не прав, то почему именно в экспертах и скриптах это работает, а из индикаторов нет?
 
nicholishen:
Спасибо, так и сделаю. Если я не прав, то почему именно в экспертах и скриптах это работает, а от индикаторов нет?

Потому что все индикаторы для символа работают в одном потоке. Тестер стратегий, советник и скрипт - это разные ситуации.

Хотя посмотрим на ответ ServiceDesk. Может я не прав :-)

 
Alain Verleyen:

Потому что все индикаторы для символа работают в одном потоке. Тестер стратегий, советник и скрипт - это разные ситуации.

Хотя посмотрим на ответ ServiceDesk. Возможно, я ошибаюсь :-)

Давайте считать, что это так...

  • Почему можно инстанцировать индикатор того же таймфрейма и сразу получить доступ к его данным, а другого таймфрейма - нет?
 
nicholishen:
  • Почему вы можете инстанцировать индикатор того же таймфрейма и сразу же получить доступ к его данным?

На самом деле это означает, что вам повезло, что данные уже доступны. Это не гарантировано. Это может и не сработать.
 
Stanislav Korotky:
На самом деле это означает, что вам повезло, что данные уже доступны. Это не гарантировано. Это также может не сработать.

Это означает, что если данные немедленно доступны скрипту или советнику, то они будут также доступны и индикатору (т.е. это не проблема доступности данных). Индикатор просто не успевает инстанцироваться до второго прохода OnCalculate() (он же первый тик).

Я принял это во внимание во время диагностики. Я даже включил циклы и период ожидания, на всякий случай. Как и все до меня, у меня те же проблемы с этой конкретной ошибкой.

 
nicholishen:

Это означает, что если данные немедленно доступны скрипту или советнику, то они будут также доступны и индикатору (т.е. это не проблема доступности данных). Индикатор просто не успевает инстанцироваться до второго прохода OnCalculate() (он же первый тик).

Я принял это во внимание во время диагностики. Я даже включил циклы и период ожидания, на всякий случай. Как и все до меня, у меня те же проблемы с этим конкретным багом.

Вы повторяете "не удается инстанцировать", но это не точно. Индикатор инстанцируется во всех случаях.

Проблема в том, что данные не доступны синхронно, с этим нужно смириться. Это НЕ баг МТ5, это СЛОЖНОСТЬ.

Предлагаю прекратить дискуссию и подождать ответа SD.

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