Робот создает много индикаторов при переключении таймфреймов. - страница 5

 
Stanislav Korotky #:

Вы не разобрались в проблеме совершенно. Никто ничего специально не мудрит, а использует штатный способ - на текущем графике размещается эксперт, который под свои нужны создает индикатор - ВСЁ. После этого пользователь переключает таймфрейм и получает задвоенные подокна индикаторов - одно нормальное, второе "зомби" - либо с полным дубликатом параметров первого индикатора (что не может быть в МТ5 по определению - это баг), либо с непроинициализированным именем, то есть без сработавшего OnInit.

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

Что происходит при изменении параметров приатаченного индикатора (ТФ - один из неявных параметров индикатора), доподлинно известно и постоянно - старая копия удаляется, а новая создается, и это видно даже в прилагаемых логах - там выведены все хендлы.

но у меня работает, у вас нет...наверное потому-что я не разбираюсь

 
Stanislav Korotky #:
Да, расчетная часть. Но может что-то изменилось и внешне - книга-то 3 года назад писалась.

Поэкспериментировал с ручным размещением индикаторов. 

В Indicator_01__1 в OnTick() добавил в стоку вывод мэджика createdByEA

Print(EnumToString(_Period), " ", MQLInfoString(MQL_PROGRAM_NAME), " createdByEA: ", createdByEA, " qTick: ", qTick); // демонстрация жизни индикатора

Если кидать вручную и задавать одинаковый мэджик, то имеем несколько дублирующих визуализаций индикатора в подокнах, но расчетная часть принтуется одна (общая),

если кинуть с разными меджиками, то будут принтоваться две разные расчетные части (логично, т.к. параметры индикаторов теперь отличаются из-за меджиков).

А что происходит при работе из эксперта я до конца не понимаю, у меня ощущение, что там все-таки несколько багов:

1. При смене ТФ у индикатора, еще до вызова его OnDeinit, теряется заданный ранее shortname (т.к. моя процедура indicatorsEnumeration из OnDeinit() эксперта выдает имя файла индикатора, хотя перед сменой ТФ она печатала заданное имя)

2. То о чём вы написали в логах - терминал не должен был создавать дубликаты. (Хотя старая расчетная часть не была удалена т.к. индикатор с графика не был удален из-за бага 1.)

Может есть смысл написать куда-то разработчикам?

 
Maxim Kuznetsov #:

но у меня работает, у вас нет...наверное потому-что я не разбираюсь

Если речь про приложенный тесткейс - то ошибка возникает не всегда, а после многократных переключений ТФ. Условия и предполагаемые причины возникновения я описал.

Если у вас свой тесткейс (связка эксперт+пользовательский индикатор), то без исходника обсуждать, что и как там работает - невозможно.

 
Sunriser #:

А что происходит при работе из эксперта я до конца не понимаю, у меня ощущение, что там все-таки несколько багов:

1. При смене ТФ у индикатора, еще до вызова его OnDeinit, теряется заданный ранее shortname (т.к. моя процедура indicatorsEnumeration из OnDeinit() эксперта выдает имя файла индикатора, хотя перед сменой ТФ она печатала заданное имя)

2. То о чём вы написали в логах - терминал не должен был создавать дубликаты. (Хотя старая расчетная часть не была удалена т.к. индикатор с графика не был удален из-за бага 1.)

Может есть смысл написать куда-то разработчикам?


По п.1 возможно следует иметь в виду, что эксперт не перегружается, а остается на графике, хотя для него последовательно вызываются OnDeinit и OnInit, а вот индикаторы - старый удаляется, а новый создается - причем последовательность вызовов OnDeinit у старого и OnInit в новом может быть произвольной.

По п.2 продублировал сообщение в топике про релиз, куда обычно сообщают об ошибках.

 
Maxim Kuznetsov #:

но у меня работает, у вас нет...наверное потому-что я не разбираюсь

100%!

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

Философия об очевидных принципах программирования, вроде конструкторов и деструкторов, мне никак не поможет - эти вещи давно понятны и без вас.

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

 
Sunriser # :

Может есть смысл написать куда-то разработчикам?

Я подтверждаю наличие проблемы с функцией ChartIndicatorDelete() и сообщил о ней в MQ.

 
Alain Verleyen #:

Я подтверждаю наличие проблемы с функцией ChartIndicatorDelete() и сообщил о ней в MQ.

Спасибо!
 

Публикую костыль, который у меня работает надежно! :)

Суть метода:

1. При инициализации пользовательских индикаторов в их input параметр передаем ключевое слово Expert_ID_Name, общее для всех пользовательских индикаторов, создаваемых данным советником. Это слово можно, но не обязательно, использовать для формирования shortname индикатора.

2. В OnDeinit() советника используем общую функцию MyIndicatorsDelete(const string name), где в name передаем Expert_ID_Name.

    Данная функция делает следующее:

    2.1 В обратном порядке перебирает все окна графика.

    2.2 В обратном  порядке перебирает все индикаторы на каждом из окон.

    2.3 Получает структуру параметров каждого индикатора и анализирует строковые параметры на наличие в них ключевого слова name = Expert_ID_Name.

    2.4 При наличии совпадения индикатор считается "своим" и предпринимается попытка его удаления с графика. Если попытка неудачна (из-за багов терминала), то взводится  признак problem = true;

    2.5 После перебора всех индикаторов по п.2.1-2.4 если problem = true, то весь цикл начинается сначала после паузы в 0.1 секунды.

          Максимальное кол-во повторных попыток задано 100 шт., но у меня всё срабатывало без проблем максимум со второй попытки.

3. После функции удаления индикаторов с окон графика вызываем IndicatorHandleRelease(int &handle) последовательно для всех хэндлов индикаторов.


Таким образом я отвязался от shortname индикатора, т.к. он сбрасывается сразу после изменения TF и ключевое слово в нем не обнаруживается какое-то время.

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

Поскольку ChartIndicatorDelete() по тем или иным причинам может не сработать с первого раза - предпринимаются новые попытки после короткой задержки в 100мс.

Функцию MyIndicatorsDelete(const string name) я у себя продублировал и в OnInit() советника, т.к. при нештатном выключении терминала (например при отключении света), OnDeinit() не выполнится и нужно будет "почистить" индикаторы при новом старте советника.

P.S. Перебор окон и индикаторов и их удаление нужно обязательно проводить в обратном порядке, т.к. при удалении индикатора и его окна нумерация изменяется и при прямом порядке обнаружение индикаторов ломается.

string Expert_ID_Name   = "My_Bot";

....

//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
   MyIndicatorsDelete(Expert_ID_Name); // Удаляем индикаторы с графика
   
   // Освобождаем хэндлы и сбрасываем переменные хэндлов
   IndicatorHandleRelease(handle1);
   IndicatorHandleRelease(handle2);
   IndicatorHandleRelease(handle3);
  }


//+------------------------------------------------------------------+
//| Находит индикаторы по ключевому слову name и удаляет из окна     |
//+------------------------------------------------------------------+
void MyIndicatorsDelete(const string name)
  {
   int c = 0;
   Print(__FUNCTION__, " name: ", name);
   int windows_total = (int)ChartGetInteger(0, CHART_WINDOWS_TOTAL);
   bool problem = false;
   do
     {
      c++;
      ulong lasttime = GetMicrosecondCount();
      while(GetMicrosecondCount()-lasttime<=100000)
        {
         // ждём 0.1 секунду ...
        }
      problem = false;
      for(int win = windows_total-1; win >= 0; win--) // Перебор всех окон в обратном порядке
        {
         int total = (int)ChartIndicatorsTotal(0, win); 
         //Print("Анализ окна win: ", win, " кол-во индикаторов в окне: ", total);
         for(int i = total - 1; i >= 0; i--) // Перебор всех индикаторов в окне в обратном порядке
           {
            string temp_name = ChartIndicatorName(0, win, i);
            int temp_handle = ChartIndicatorGet(0, win, temp_name);

            ENUM_INDICATOR   indicator_type;
            MqlParam params[];
            int p = IndicatorParameters(temp_handle, indicator_type, params);

            if(!IndicatorRelease(temp_handle))
               Print("Ошибка IndicatorRelease(temp_handle), где temp_handle: ", temp_handle);

            int size = ArraySize(params);
            //Print(__FUNCTION__, " win: ", win, " i: ", i, " Вижу индикатор: ", temp_name, " p: ", p, " size: ", size);
            if(size>0)
              {
               for(int i=0; i<size; i++) // просмотр структуры параметров анализируемого индикатора
                 {
                  //Print("indicator_type: ", indicator_type);
                  //Print("params[", i, "].integer_value: ", params[i].integer_value);
                  //Print("params[", i, "].double_value: ", params[i].double_value);

                  //Print("params[", i, "].string_value: ", params[i].string_value);
                  int res = StringFind(params[i].string_value, name, 0);
                  //Print("строка из параметров: ", params[i].string_value);
                  //Print("Искомая строка: ", name);

                  if(res!=-1)
                    {
                     //Print("Совпадение найдено");
                     //Print("StringSubstr: ", StringSubstr(params[i].string_value, res, -1));
                     ResetLastError();
                     if(!ChartIndicatorDelete(0, win, temp_name))
                       {
                        Print("Ошибка удаления индикатора ", temp_name, " из окна ", win, " код ошибки: ", _LastError);
                        problem = true;
                       }
                     else
                       {
                        Print("Индикатор ", temp_name, " удален из окна ", win);
                       }
                    }
                 }
              }
            else
               problem = true; // На предыдущих этапах была зафиксирована какая-то проблема
           }
        }
     }
   while(problem && !IsStopped() && c<100); // Повторяем попытки пока проблемы не исчезнут либо не закончится лимит в 100 попыток
  }

//+------------------------------------------------------------------+
//| Освобождает хэндл индикатора и обнуляет переменную хэндла        |
//+------------------------------------------------------------------+
void IndicatorHandleRelease(int &handle)
  {
   if(handle!=INVALID_HANDLE)
     {
      IndicatorRelease(handle);
      handle = INVALID_HANDLE;
     }
  }