Обсуждение статьи "Рецепты MQL5 - Создаем кольцевой буфер для быстрого расчета индикаторов в скользящем окне" - страница 3

 
Vasiliy Sokolov:

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


Попробую более развернуто ответить:

 есть достаточно тяжелый индикатор. В советнике он запрашивается достаточно редко, то есть пока не сработают все условия. К примеру через каждые, скажем так, 300-1000 баров(на самом деле это зависит от ряда условий, которые выражены в условных операторах). А если получать значение даже один раз на каждом баре, то это будет больше кол-во раз, чем спрятанный вызов индикатора в тело условных операторов. 

if(условие1)
{
if(условие2)
{
if(условие3)
{
if(условие4)
{
 и тут вызываю индикатор(mql4), такая ситуация встречается совсем редко
}
}
}
}

примерно так.

В принципе limit при таком подходе должен всегда равняться P.
 
forexman77:


Попробую более развернуто ответить:

 есть достаточно тяжелый индикатор. В советнике он запрашивается достаточно редко, то есть пока не сработают все условия. К примеру через каждые, скажем так, 300-1000 баров(на самом деле это зависит от ряда условий, которые выражены в условных операторах). А если получать значение даже один раз на каждом баре, то это будет больше кол-во раз, чем спрятанный вызов индикатора в тело условных операторов. 

Такой подход, кстати, не всегда дает экономию. Потому что пропущенные бары при вызове индикатора все равно просчитываются.
 
Andrey Khatimlianskii:
Такой подход, кстати, не всегда дает экономию. Потому что пропущенные бары при вызове индикатора все равно просчитываются.


Но, если я знаю, что условие будет выполняться редко, то использую этот метод, а если индикатор вызывается часто, то да лучше считать по всей истории.

Ну и конечно же, обсчитывать только новый бар, так как старые уже известны, с этим все и так понятно.

Сейчас подумал, что нужно строго с Р начинать без сравнения

if (limit > P)limit=P;//эту строку вообще убрать и сделать сразу limit=P;

Если в цикле расчет начнется с P, а он равняется к примеру 10, то расчет будет начиная с 10 бара. 

P=Period_+10;
  
limit=P;
for(int i=limit;i>=0;i--)

Но, это не столь важно, можно и по старому оставить. Не стоит оно того.

"пропущенные бары при вызове индикатора все равно просчитываются" можно подробней. 

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

К примеру вот основная часть кода одного из них:

int start()
  {
    double summaP,d,Q;

   summaP=0.0;
   for (int k=0;k<Period_;k++)
   {
   d=MathAbs(Close[k]-Close[k+1]);
   summaP=summaP+d;
   }
   if(summaP==0.0) summaP=0.000000001;
   Q=Close[0]-Close[Period_];  
   ExtMapBuffer1[0]=Q/summaP;

   return(0);
  }

То есть это индикаторы для тестера. Для визуализации они естественно не пойдут, в них ничего не видно)

Но, кольцевой буффер будет нужен, если индикатор состоит из нескольких буфферов, где один массив строится из другого.

 
forexman77:

"пропущенные бары при вызове индикатора все равно просчитываются" можно подробней. 

Допустим, вызвали индикатор, когда баров на графике было 1000, он их посчитал. Следующий вызов — баров 1001, он посчитал один бар.
Но если следующий вызов будет, когда баров уже 1500, он посчитает все новые (499) баров.


forexman77:

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

Тогда какой вообще смысл в индикаторах? Перенесите эти расчеты в виде функции в советника, будет еще быстрее.

 
forexman77:


Попробую более развернуто ответить:

Индикатор рассчитывается на каждом баре, не зависимо от того, вызываете Вы его или нет. Читайте спецификацию.

Если хотите реального ускорения переносите расчеты внутрь советника. Другого пути нет. Точка. Все сообщения о том, как удачно можно вставить if просто смешно читать.

Ваша тема оффтоп для данной ветки. Давайте закончим на этом.

 

Доброго времени суток, Vasiliy Sokolov!

Ваши индикаторы созданные при помощи кольцевого буфера показывают как то не внятно, походу метод ChangeValue не работает, или может я что-то не так понял? 

 
Василий, спасибо за код! Очень выручает.
Если позволите, такие замечания:

1. В классе void CRiMaxMin::OnChangeValue(int index, double del_value, double new_value),в методе  " OnChangeValue" в строке 
if(m_min_ind >= 0 && new_value >= GetValue(m_min_ind))
      m_min_ind = index;

Картинка: опечатка?


опечатка - знак "меньше или равно".

2.При поиске Min и Max элементов массива, если использовать именно как кольцевой буфер (когда новые элементы начинают писаться в начало массива), min и мах определяются неправильно. Минимум больше максимума. В одном массиве. Стандартными методами (ArrayMinimum и ArrayMaximum) все работает.
Картинка: Минимум больше Максимума

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

#property copyright "Copyright 2020, MetaQuotes Software Corp."
#property link      "https://www.mql5.com"
#property version   "1.00"

#include  <RingBuffer\RiMaxMin.mqh>

input group "Проверка через кольцевой буфер - true"
input bool ringBuffer=true;


input group "Размер буфера"
input int pTest=10;

double minValue,maxValue;
int minIndex,maxIndex,lastIndex,indArr;

double arr[];

CRiMaxMin minMaxTest;

int OnInit()
  { 
   if(ringBuffer)minMaxTest.SetMaxTotal(pTest);
   else  
      {
      indArr=-1;
      lastIndex=pTest-1;
      ArraySetAsSeries(arr,true);
      ArrayResize(arr,pTest);
      }
   return(INIT_SUCCEEDED);
  }

void OnDeinit(const int reason)
  {
   
   
  }

void OnTick()
  {
   if(ringBuffer)
      {
      minMaxTest.AddValue(rand());
      minIndex=minMaxTest.MinIndex();
      minValue=minMaxTest.GetValue(minIndex);
      //minValue=minMaxTest.MinValue();
      maxIndex=minMaxTest.MaxIndex();
      maxValue=minMaxTest.GetValue(maxIndex);
      //maxValue=minMaxTest.MaxValue();
      }
   else
      {
      //arr[0]=rand();
      //ArrayCopy(arr,arr,1,0,lastIndex);
      indArr++;
      if(indArr>lastIndex)indArr=0;
      arr[indArr]=rand();
      minIndex=ArrayMinimum(arr,0,pTest);
      minValue=arr[minIndex];
      //minValue=arr[ArrayMinimum(arr,0,pTest)];
      maxIndex=ArrayMaximum(arr,0,pTest);
      maxValue=arr[maxIndex]; 
      //maxValue=arr[ArrayMaximum(arr,0,pTest)];
      }  
   Alert("minValue ",DoubleToString(minValue)," --  maxValue ",DoubleToString(maxValue));
   if(minValue>maxValue)
      {
      Alert("Мин  > Mакс !!!");
      Print("Мин  > Mакс !!!");
      ExpertRemove();
      }
  }
Файлы:
 
Извиняюсь. Min и Max считает правильно. Сам я перемудрил.
Причина обращения: