Обсуждение статьи "Как перенести расчетную часть любого индикатора в код эксперта" - страница 2

 

Вот здесь вылетает ошибка, если сделать больше одного буфера

bool CIndicator::SetBufferSize(const int bars)
  {
   if(bars>0)
      m_data_len  =  bars;
   else
      m_data_len  =  Bars(m_Symbol,m_Timeframe);
//---
   if(m_data_len<=0)
     {
      for(int i=0;i<m_buffers;i++)
         ar_IndBuffers[i].Shutdown();
      return false;
     }
//---
   if(m_history_len<m_data_len)
      if(!SetHistoryLen(m_data_len))
         return false;
//---
   for(int i=0;i<m_buffers;i++)
     {
      ar_IndBuffers[i].Shutdown();
      if(!ar_IndBuffers[i].Resize(m_data_len))
         return false;
     }
//---
   return true;
  }

array out of range in 'Indicator.mqh' (156,20)


UPD: добавил в начале функции строчку и проблема решилась.

ArrayResize(ar_IndBuffers,m_buffers,m_buffers);

 

Еще раз огромное спасибо за статью, всё получилось! Самое интересное, что в таком исполнении работает быстрее. Мой стрелочный индикатор использует запрос тиков через функции CopyTicksRange и CopyTicks, что вызывало массу глюков и приводило зачастую к неверным данным. Через class всё работает как часы. Связываю это с тем, что CopyTicksRange и CopyTicks в индикаторах работают несколько иначе, система не дает им время на синхронизацию.



ps. кто будет переносить стрелочные индикаторы с несколькими буферами, учтите, требуются правки функций Indicator.mqh

CIndicator::SetHistoryLen(const int bars)

CIndicator::LoadHistory(void)

в зоне protected 

ну и метод записи и получения данных из индикаторных буферов ar_IndBuffers для стрелочника другой 

 

Решил поделиться доработанным под себя Indicator.mqh, может кому сэкономит время при переводе индикатора в класс. 

Файлы:
 

Спасибо за статью! Изучаю для ухода от нестабильных обычных индикаторов.

Но мне важна возможность визуализации индикаторов на графике. Кто-нибудь реализовавал?

 

А зачем вообще переносить расчеты из индикатора в советник?

Очень многие используют индикаторы вовсе без советника.

Можно просто разбить расчеты на этапы.

Например так:

//+------------------------------------------------------------------+
//|                                                      FutData.mq5 |
//|                              Copyright 2020 - 2021, prostotrader |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2020-2021, prostotrader"
#property link      "https://www.mql5.com"
#property version   "1.001"
//---
#property indicator_separate_window
#property indicator_plots   1
#property indicator_buffers 1
//---
enum IND_STAGE
{
  LOAD_TICKS = 0,
  READ_TICKS = 1,
  READ_DEALS = 2,
  FILL_DATA = 3
} stage;
//+------------------------------------------------------------------+
//| Custom indicator OnInit function                                 |
//+------------------------------------------------------------------+
int OnInit()
{
  stage = LOAD_TICKS;  
//---
  return(INIT_SUCCEEDED);
}
//+------------------------------------------------------------------+
//| Custom indicator Load ticks function                             |
//+------------------------------------------------------------------+
bool LoadTicks(const datetime &a_times[])
{
  return(false);
}
//+------------------------------------------------------------------+
//| Custom indicator Read primary ticks function                     |
//+------------------------------------------------------------------+
bool ReadTicks()
{
  return(false);
}
//+------------------------------------------------------------------+
//| Custom indicator Read secondary ticks function                   |
//+------------------------------------------------------------------+
bool ReadDeals()
{
  return(false);
}
//+------------------------------------------------------------------+
//| Custom indicator Fill data function                              |
//+------------------------------------------------------------------+
void FillData()
{
//---
}
//+------------------------------------------------------------------+
//| Custom indicator On Calculate 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(prev_calculated == 0)
  {
      switch (stage)
      {
        case LOAD_TICKS:
          if(LoadTicks(time) == true)
          {
            stage = READ_TICKS;
          }
          return(0);
        break;
        case READ_TICKS:
          if(ReadTicks() == true)
          {
            stage = READ_DEALS;
          }  
          return(0);
        break;
        case READ_DEALS:
          if(ReadDeals() == true)
          {
            stage = FILL_DATA;
          }  
          return(0);
        break;
        case FILL_DATA:
          stage = LOAD_TICKS;
        break;
      }
  }
  else
  {
    //
  }    
  //---
  return(rates_total);
}
//+------------------------------------------------------------------+
 
prostotrader #:

А зачем вообще переносить расчеты из индикатора в советник?

Очень многие используют индикаторы вовсе без советника.

Можно просто разбить расчеты на этапы.

Например так:

Из-за того, что штатный механизм индикаторов работает через пень-колоду, например: https://www.mql5.com/ru/forum/372612 и это обусловлено их реализацией.

С ростом сложности индикаторов мой советник "увяз в болоте". В тестере я словил еще много других багов в работе индикаторов, но не стал их описывать, т.к. бесполезно.

Я не понял вашу идею.

Некорректная инициализация индикаторов в визуальном тестере
Некорректная инициализация индикаторов в визуальном тестере
  • 2021.07.04
  • www.mql5.com
Если делаю инициализацию индикаторов в OnInit() { } эксперта, то в визуальном тестере индикатор обычно не появляется и не отрисовывается...
 
Sunriser #:

Из-за того, что штатный механизм индикаторов работает через пень-колоду, например: https://www.mql5.com/ru/forum/372612 и это обусловлено их реализацией.

С ростом сложности индикаторов мой советник "увяз в болоте". В тестере я словил еще много других багов в работе индикаторов, но не стал их описывать, т.к. бесполезно.

Я не понял вашу идею.

Начнем с того, что у Вас не совсем корректный код.

Я бы написал так:

int OnInit()
  {int  TicksTesterIndicatorHandle = INVALID_HANDLE;
   bool InitComplite=false;
   if(IndicatorInitialization() == false) return(INIT_FAILED);
    return(INIT_SUCCEEDED);
 }
void OnDeinit(const int reason)
{
     if(TicksTesterIndicatorHandle != INVALID_HANDLE) IndicatorRelease(TicksTesterIndicatorHandle);
}
void OnTick()   { //if(!InitComplite)
 //  { //   IndicatorInitialization();
 //  }   } //+------------------------------------------------------------------+
bool IndicatorInitialization()
   { //---Получить хэндл индикатора TicksTesterIndicator
    TicksTesterIndicatorHandle=iCustom(NULL, _Period, "OnInit_TestIndicator");
 //--- Нужно проверить, не были ли возвращены значения Invalid Handle 
  if(TicksTesterIndicatorHandle == INVALID_HANDLE)
      {       Print("Ошибка при создании индикатора TicksTesterIndicator - номер ошибки: ",GetLastError(),"!!!");
      
   }
    else
      { 
      Print("TicksTesterIndicator инициализирован, хэндл: ", TicksTesterIndicatorHandle);
       ArraySetAsSeries(Buf, true);
     InitComplite=true;
     return(true);
   }
    return(false);  
 }

Далее, т.к функции в индикаторах должны выполняться с минимальными задержками, то сложные процессы (загрузка истории, сложные расчеты и т.п)

разбиваются на несколько частей, возвращая в  

OnCalculate

нулевое значение (return(0) ), т.е индикатор находится в начальной стадии, пока мы не выполним все необходимые нам действия с минимальными задержками в каждой стадии.

 
В архитектуре MetaTrader 5 организован асинхронный доступ к значениям индикаторов. Иными словами, при получении хэндла индикатора он прикрепляется к графику. Далее этот индикатор производит свои расчеты вне потока советника. Они взаимодействуют лишь на этапе передачи данных, аналогично получению данных тайм-серий. Поэтому и время на выполнение этих операций сопоставимо.

То есть получается в реале все же icustom будет быстрее?! -Ведь в реальности так и будет. Эксперт в одном треде, индикатор в другом (и может даже на разных ядрах). Это только если из засунуть в последовательную обработку получается медленнее - но это ведь только искуственное ограничение из-за  тестера стратегий

 
Из статьи я не понял, есть ли у класса-индикатора защита от пропущенных баров? К примеру произошел разрыв связи на 5 баров, а потом дальше история загрузилась, класс-индикатор перезаполнит только последнее значение в буере или сделает полный перерасчет?
 
Aleksey Vyazmikin #:
Из статьи я не понял, есть ли у класса-индикатора защита от пропущенных баров? К примеру произошел разрыв связи на 5 баров, а потом дальше история загрузилась, класс-индикатор перезаполнит только последнее значение в буере или сделает полный перерасчет?

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

bool CIndicatorBuffer::Refresh(const int handle,const int num)
  {
//--- check
   if(handle==INVALID_HANDLE)
     {
      SetUserError(ERR_USER_INVALID_HANDLE);
      return(false);
     }
//---
   m_data_total=CopyBuffer(handle,num,-m_offset,m_size,m_data);
//---
   return(m_data_total>0);
  }