Позволяет ли mql5 написать индикатор, рисующий графики в разных окнах или получать доступ к буферам индикаторов в других окнах? - страница 2

 

Пример мультииндикатора с рациональным подходом к расчетам

#property indicator_chart_window
#property indicator_buffers 1
#property indicator_plots   1
#property indicator_color1  clrRed
#property indicator_type1   DRAW_LINE 

sinput bool IsClone = true;

double Buffer[];

int handle = INVALID_HANDLE;

#define PATH "MQL5\\Indicators\\"

string GetIndicatorName( void )
{
  const string StrName = ::MQLInfoString(MQL_PROGRAM_PATH);
  const int Pos = ::StringFind(StrName, PATH) + ::StringLen(PATH);
  
  return(::StringSubstr(StrName, Pos, ::StringLen(StrName) - Pos - 4));
}

void OnInit()
{
  ::SetIndexBuffer(0, Buffer, INDICATOR_DATA);
  
  if (IsClone)
  {
    handle = ::iCustom(_Symbol, _Period, GetIndicatorName(), false);
    
    if (handle != INVALID_HANDLE)
      ::ChartIndicatorAdd(0, (int)::ChartGetInteger(0, CHART_WINDOWS_TOTAL), handle);
  }  
}

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[] )
{
  const int Prev = prev_calculated - 1; // нулевой бар надо пересчитывать
  
  const int Amount = (IsClone ? ((handle != INVALID_HANDLE) ? ::CopyBuffer(handle, 0, 0, ::BarsCalculated(handle) - Prev, Buffer) : -1) :
                                ::ArrayCopy(Buffer, close, Prev, Prev, rates_total - Prev)); // какие-нибудь тяжелые расчеты

  return((Amount > 0) ? Prev + Amount : prev_calculated);
}
 
fxsaber:

Пример мультииндикатора с рациональным подходом к расчетам

Это решение не идеальное, т.к. идет копирование только последнего значения буфера базового индикатора. И если базовый индикатор пересчитывает несколько значений буфера, то клон их не обновит.

Поэтому такое решение, использующее недокументированные возможности

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 Prev_Calculated = 0; // количество уже посчитанных баров на момент вызова OnCalculate
  
  if (prev_calculated == 0)
    Prev_Calculated = 0;    
  
  const int Tmp1 = ((prev_calculated > 0) ? ::BarsCalculated(handle) : rates_total);
  const int Tmp2 = rates_total - Prev_Calculated;
  
  int Amount = (IsClone ? ((handle != INVALID_HANDLE) ? ::CopyBuffer(handle, 0, 0, ((Tmp1 > Tmp2) ? Tmp1 : Tmp2), Buffer) : -1) :
                           ::ArrayCopy(Buffer, close, Prev_Calculated, Prev_Calculated, rates_total - Prev_Calculated)); // какие-нибудь тяжелые расчеты
                                
  if (Amount <= 0)
    Amount = 1;

  Prev_Calculated += Amount - 1; // нулевой бар надо пересчитывать

  return(Amount); // возвращаем количество обновленных баров, а не посчитанных. Чтобы BarsCalculated(handle) был равен этому значению.
}

 

Однако, и это не идеальный вариант. Идеальная схема видится такой:

  1. При запуске на чарте создается новый чарт и на него вешается (ChartIndicatorAdd) базовый индикатор с очень тяжелыми расчетами. Это позволяет убрать тормоза того чарта, на котором собираемся работать.
  2. Базовый тяжелый индикатор после каждого своего расчета делает широковещательное сообщение через кастомный события графика, в котором сообщает, что он обновился и сколько значений буфера он обновил.
  3. Подчиненные индикаторы ловят это сообщение и через CopyBuffer выцепляют посчитанные буфера.
У меня такой вопрос. Целесообразно ли (с точки зрения производительности) при использовании CopyBuffer дописывать буфера на количество измененных значений (входной параметр count не нулевой) или же всегда делать полную копию буфера?

Уважаемые разработчики, сообщите, пожалуйста.

 

fxsaber:

При запуске на чарте создается новый чарт и на него вешается (ChartIndicatorAdd) базовый индикатор с очень тяжелыми расчетами. Это позволяет убрать тормоза того чарта, на котором собираемся работать.

При таком раскладе получится создаваемому через iCustom (на том же символе и периоде) индикатору выделить отдельный поток выполнения?

Программа

Выполнение

Примечание

Индикатор

Один поток выполнения для всех индикаторов на одном символе. Сколько символов с индикаторами - столько потоков выполнения для них

Бесконечный цикл в одном индикаторе остановит работу всех остальных индикаторов на этом символе

 

Или же поток не зависит от чартов и периодов, а зависит только от символа? 

 
Экспериментальным путем выяснил

Форум по трейдингу, автоматическим торговым системам и тестированию торговых стратегий

Ошибки, баги, вопросы

fxsaber, 2016.09.09 14:30

BlackTomcat:
Вчера обсуждали, страница 1687. :) Но читать лучше вот с этого сообщения на 1686-й странице: https://www.mql5.com/ru/forum/1111/page1686#comment_2790746 Кстати, человек на Ваш же вопрос отвечал. Просто получается, что схема такая. Запрос на вызов индикатора попадает в Терминал. Т.е. советник или скрипт не вызывают индикатор напрямую, терминал выступает посредником. В терминале происходит проверка, существует ли уже такой индикатор с указанными параметрами. Если да, то он возвращает программе, вызвавшей индикатор, хэндл (ссылку) на уже существующий индикатор. А в своих терминальных недрах помечает в счётчике, что был ещё один вызов на данный индикатор. Т.е. он ведёт учёт, сколько хэндлов на данный индикатор вернул. Когда кто-то делает IndicatorRelease, это не значит, что остальным, кто вызвал этот же индикатор с такими же параметрами, этот индикатор больше не нужен. Поэтому Терминал уменьшает счётчик на единицу, но оставляет индикатор активным, потому-что им ещё кто-то пользуется. Так будет продолжаться до тех пор, пока счётчик не обнулится. А вот когда он обнулится, тогда Терминал окончательно и бесповоротно удалит данный индикатор из оперативной памяти.
Если Вы попробуете создать код, который будет содержать IndicatorRelease, но при этом не будет содержать вызова самого индикатора, то я думаю, что Вы его просто-напросто не откомпилируете, потому-что в IndicatorRelease нужно передавать хэндл, а если индикатор не вызывался из программы, то и хэндл вы не узнаете.

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

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


 
fxsaber:
Целесообразно ли (с точки зрения производительности) при использовании CopyBuffer дописывать буфера на количество измененных значений (входной параметр count не нулевой) или же всегда делать полную копию буфера?

Жирное выполняется быстрее.

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

Было б желательно минимальный код для воспроизведения увидеть и описание тех разных мест, откуда вызывать. ;-) Если будете писать в сервис-деск, все равно его первым делом попросят.
 
Stanislav Korotky:
Было б желательно минимальный код для воспроизведения увидеть и описание тех разных мест, откуда вызывать. ;-) Если будете писать в сервис-деск, все равно его первым делом попросят.

Запустить индикатор на двух одинаковых чартах (символ и период)

#property indicator_chart_window
#property indicator_buffers 1
#property indicator_plots   0

sinput bool IsClone = true;

double Buffer[];

int handle = INVALID_HANDLE;

#define PATH "MQL5\\Indicators\\"

string GetIndicatorName( void )
{
  const string StrName = ::MQLInfoString(MQL_PROGRAM_PATH);
  const int Pos = ::StringFind(StrName, PATH) + ::StringLen(PATH);
  
  return(::StringSubstr(StrName, Pos, ::StringLen(StrName) - Pos - 4));
}

void OnInit()
{
  ::SetIndexBuffer(0, Buffer, INDICATOR_CALCULATIONS);
  
  if (IsClone)
    handle = ::iCustom(_Symbol, _Period, GetIndicatorName(), false);
  else
    ::MathSrand((int)::TimeCurrent());
}

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 (!IsClone)
    Buffer[rates_total - 1] = ::MathRand();
  else if (handle != INVALID_HANDLE)
  {
    ::CopyBuffer(handle, 0, 0, 1, Buffer);
    
    ::Comment(Buffer[rates_total - 1]);
  }
  
  return(rates_total);
}

Через коммент чартов можно увидеть различия.

 
fxsaber:

Запустить индикатор на двух одинаковых чартах (символ и период)

Через коммент чартов можно увидеть различия.

С этим в сервис-деск. Потом расскажите, чем дело закончилось. Хэндлы одинаковые, но инициализация производится два раза - по разу в каждом iCustom.
 
Stanislav Korotky:
С этим в сервис-деск. Потом расскажите, чем дело закончилось. Хэндлы одинаковые, но инициализация производится два раза - по разу в каждом iCustom.

Ответ из Сервисдеск

Так и задумано. Два графика - два разные ChartID()

fxsaber

Напишу, когда запущу на одном графике.

Тоже будет два. На вопрос "почему?" ответ "потому что каждый индикатор может работать со своим собственным набором графических объектов"

В качестве иллюстрации. Попробуйте накинуть на график 2 раза наш пример SimplePanel

В терминале кроме пользовательских индикаторов есть ещё 40 стандартных индикаторов. Стандартные индикаторы с графическими объектами не работают, поэтому для них не нужны отдельные копии.

Для кастомных всегда нет

Документацию дополним 

 Так что счетчики - это ТОЛЬКО стандартные индикаторы. Кастомные - никаких счетчиков.

 
fxsaber:

Ответ из Сервисдеск

 Так что счетчики - это ТОЛЬКО стандартные индикаторы. Кастомные - никаких счетчиков.

Понятно. Печально. По-хорошему, работа с объектами должна была бы быть отвязана от расчета буферов, т.е. мне даже не особо понятно при чем тут объекты.
Причина обращения: