Помогите разобраться с копированием индикаторного буфера

 

Добрый день!
Не получается наполнить индикаторный буфер с помощью функции CopyBuffer: функция всегда возвращает -1 независимо от вводных данных. Не могу понять, что я делаю не так. Видимо, не понимаю чего-то фундаментального в индикаторах. Не смог найти материал на сайте, где бы подобное разъяснялось, поэтому прошу помощи :(

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

Ниже прикрепляю реализацию и файл моего примера:

#property copyright "2009-2017, MetaQuotes Software Corp."
#property link      "http://www.mql5.com"
//---- include
//#include <GetIndicatorBuffers.mqh>
//---- indicator settings
#property indicator_separate_window
#property indicator_buffers 2
#property indicator_plots   1
#property indicator_type1   DRAW_COLOR_HISTOGRAM
#property indicator_color1  Green,Red
#property indicator_style1  0
#property indicator_width1  3

//--- input data
input string OtherSymbol=""; //Название символа, по которому считать. Если пусто - текущий символ
//---- indicator buffers
double                    DataBuffer[];
double                    ColorsBuffer[];
//---- external indicator handle
int                       CustomHandle=0;
//---- other
int                       n=0;
//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int OnInit()
  {  
   SetIndexBuffer(0,DataBuffer,INDICATOR_DATA);
   SetIndexBuffer(1,ColorsBuffer,INDICATOR_COLOR_INDEX);
   IndicatorSetString(INDICATOR_SHORTNAME,"PercentChangeHistogram");
   IndicatorSetInteger(INDICATOR_DIGITS,2);
//----
   if (OtherSymbol!="") //если есть имя другого символа, рекурсивно вызываем индикатор через iCustom этот же индикатор
      { 
      CustomHandle=iCustom(OtherSymbol,_Period,"PercentChangeHistogram","");
     if (CustomHandle==-1)
         {Alert ("iCustom() failed! Check input Symbol name.");
         return (INIT_FAILED);}
     }
   return (INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//|  OnCalculate                                                     |
//+------------------------------------------------------------------+
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[])
  {
   int start;
   if(rates_total<1)
      return(0);
   if (prev_calculated==0)
      start=0;
     else
      start=prev_calculated-1;   
   //---
   if (OtherSymbol=="") // если input переменная содержит пустую строку, заполняем индикаторный массив через простой расчет функцией CalculateData
      CalculateData(start,rates_total,open,close);
     else //если input переменная содержит НЕ пустую строку, пытаемся заполнить индикаторный массив копированием данных буфера внешнего индикатора
      {
      //bool Copy=CopyBufferAsSeries(CustomHandle,0,0,rates_total-prev_calculated,true,DataBuffer);
      //if (!Copy) Alert ("CopyBuffer() failed!");
      int Copy=CopyBuffer(CustomHandle,0,rates_total-1,rates_total-prev_calculated,DataBuffer); // НА ЭТОМ МЕСТЕ ВСЕГДА ОШИБКА
      if (Copy==-1) Alert ("CopyBuffer() failed!");
      //n++; Alert("Iteration "+n);
      }
   //---
   return(rates_total);
  }
  
void CalculateData (const int nPosition,
                    const int nRatesCount,
                    const double &OpenData[],
                    const double &CloseData[])
  
  {
   for(int i=nPosition;i<nRatesCount && !IsStopped();i++)
      {
       double Data=(CloseData[i]-OpenData[i])/(CloseData[i]/100.0);
       DataBuffer[i]=Data;
       if (Data>0)
         ColorsBuffer[i]=0.0;
        else
         ColorsBuffer[i]=1.0;
      }
  }
Файлы:
 
Все норм. Может перезапуск терминала поможет...
 
Нет :(
Мне бы хоть пример найти, где буфер индикатора заполняется через CopyBuffer(), я бы сам разобрался дальше :(
 
Artem Ipatov:
Нет :(
Мне бы хоть пример найти, где буфер индикатора заполняется через CopyBuffer(), я бы сам разобрался дальше :(

Мне кажется ошибка тут

      CustomHandle=iCustom(OtherSymbol,_Period,"PercentChangeHistogram","");

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

 
Artem Ipatov:
Нет :(
Мне бы хоть пример найти, где буфер индикатора заполняется через CopyBuffer(), я бы сам разобрался дальше :(

В статьях должна быть статья, что-то типа "Индикатор от индикатора". Но у меня-то работает этот код нормально (тот который выше).

 
Я разобрался, там нужно учитывать, что данные не с первой попытки копируются по другому инструменту. Таймеры использовать нельзя в индикаторах. Нужно на каждом тике пытаться копировать данные из буфера другого символа, пока не скопируются. Еще, нужно учесть что в индексация в массиве который мы получаем, будет не как в индикаторном массиве (не тайм-серия).
Ниже не очень оптимальный код основного расчёта в OnCalculate(). Главное, что смысл понятен:
      if (!Init) //флаг первый вызов
         {
         Copy=CopyBuffer(CustomHandle,0,0,OtherSymbolDepth,DataBuffer);
         int Copy2=CopyBuffer(CustomHandle,1,0,OtherSymbolDepth,ColorsBuffer);
         if (Copy!=-1 && Copy2!=-1) {Comment ("Init is OK. Loading current bar data...");Init=true;}
            else Comment ("Loading initial data...");
         }
         else
         {
         for (int bar=start;bar<rates_total;bar++) //основной цикл для расчета непосчитанных баров
            {
            Copy=CopyBuffer(CustomHandle,0,rates_total-bar,1,SomeBuffer);
            if (Copy==-1) Comment ("Loading last bars data...");
               else 
               {
               Comment ("OK!");
               DataBuffer[rates_total-1-bar]=SomeBuffer[0];
               if (DataBuffer[rates_total-1-bar]>0) ColorsBuffer[rates_total-1-bar]=0.0;
                  else ColorsBuffer[rates_total-1-bar]=1.0;
                  
               }
            }
         }
 
Есть очень простой способ, если что-то в OnCalculate пошло не так, то делаем - return(0), от этого на следующем тике будет полный пересчет. Разумеется если start вычисляется с учетом prev_calculated.
 
Artem Ipatov:
Я разобрался, там нужно учитывать, что данные не с первой попытки копируются по другому инструменту. Таймеры использовать нельзя в индикаторах. Нужно на каждом тике пытаться копировать данные из буфера другого символа, пока не скопируются. Еще, нужно учесть что в индексация в массиве который мы получаем, будет не как в индикаторном массиве (не тайм-серия).
Ниже не очень оптимальный код основного расчёта в OnCalculate(). Главное, что смысл понятен:

это где такое написано?

 
Taras Slobodyanik:

это где такое написано?

В справочнике написано так, что предполагается использование их в индикаторах. Получается, что Артём ошибается.
 
Artem Ipatov:
С первого раза данные не копируются.
Как-то так:
     else //если input переменная содержит НЕ пустую строку, пытаемся заполнить индикаторный массив копированием данных буфера внешнего индикатора
      {
      int count=0;
      if(prev_calculated<1)
         {
         count=rates_total;
         int calculated=BarsCalculated(CustomHandle);
         if(count>calculated) count=calculated;
         }
      else count=rates_total-prev_calculated+1;
      
      int Copy=CopyBuffer(CustomHandle,0,int(0),count,DataBuffer); // НА ЭТОМ МЕСТЕ ВСЕГДА ОШИБКА
      if (Copy==-1)
         {
         //Print("CopyBuffer() failed!");
         ChartSetSymbolPeriod(0,_Symbol,PERIOD_CURRENT);
         return(0);
         }
      }
 

Aleksey Lebedev:
С первого раза данные не копируются.
Как-то так:

// НА ЭТОМ МЕСТЕ ВСЕГДА ОШИБКА

может потому что вы всегда, с первого раза, копируете 0 (или -1) баров?