Вы не разобрались в проблеме совершенно. Никто ничего специально не мудрит, а использует штатный способ - на текущем графике размещается эксперт, который под свои нужны создает индикатор - ВСЁ. После этого пользователь переключает таймфрейм и получает задвоенные подокна индикаторов - одно нормальное, второе "зомби" - либо с полным дубликатом параметров первого индикатора (что не может быть в МТ5 по определению - это баг), либо с непроинициализированным именем, то есть без сработавшего OnInit.
В приложенных исходниках MQL5 инициализация и деинициализация производятся по лучшим стандартам. Потрудились бы изучить что ли тестовый пример, прежде чем писать лабуду.
Что происходит при изменении параметров приатаченного индикатора (ТФ - один из неявных параметров индикатора), доподлинно известно и постоянно - старая копия удаляется, а новая создается, и это видно даже в прилагаемых логах - там выведены все хендлы.
но у меня работает, у вас нет...наверное потому-что я не разбираюсь
Если кидать вручную и задавать одинаковый мэджик, то имеем несколько дублирующих визуализаций индикатора в подокнах, но расчетная часть принтуется одна (общая),
если кинуть с разными меджиками, то будут принтоваться две разные расчетные части (логично, т.к. параметры индикаторов теперь отличаются из-за меджиков).
А что происходит при работе из эксперта я до конца не понимаю, у меня ощущение, что там все-таки несколько багов:
1. При смене ТФ у индикатора, еще до вызова его OnDeinit, теряется заданный ранее shortname (т.к. моя процедура indicatorsEnumeration из OnDeinit() эксперта выдает имя файла индикатора, хотя перед сменой ТФ она печатала заданное имя)
2. То о чём вы написали в логах - терминал не должен был создавать дубликаты. (Хотя старая расчетная часть не была удалена т.к. индикатор с графика не был удален из-за бага 1.)
но у меня работает, у вас нет...наверное потому-что я не разбираюсь
Если речь про приложенный тесткейс - то ошибка возникает не всегда, а после многократных переключений ТФ. Условия и предполагаемые причины возникновения я описал.
Если у вас свой тесткейс (связка эксперт+пользовательский индикатор), то без исходника обсуждать, что и как там работает - невозможно.
А что происходит при работе из эксперта я до конца не понимаю, у меня ощущение, что там все-таки несколько багов:
1. При смене ТФ у индикатора, еще до вызова его OnDeinit, теряется заданный ранее shortname (т.к. моя процедура indicatorsEnumeration из OnDeinit() эксперта выдает имя файла индикатора, хотя перед сменой ТФ она печатала заданное имя)
2. То о чём вы написали в логах - терминал не должен был создавать дубликаты. (Хотя старая расчетная часть не была удалена т.к. индикатор с графика не был удален из-за бага 1.)
Может есть смысл написать куда-то разработчикам?
По п.1 возможно следует иметь в виду, что эксперт не перегружается, а остается на графике, хотя для него последовательно вызываются OnDeinit и OnInit, а вот индикаторы - старый удаляется, а новый создается - причем последовательность вызовов OnDeinit у старого и OnInit в новом может быть произвольной.
По п.2 продублировал сообщение в топике про релиз, куда обычно сообщают об ошибках.
Публикую костыль, который у меня работает надежно! :)
Суть метода:
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 |//+------------------------------------------------------------------+voidOnDeinit(constint reason)
{
MyIndicatorsDelete(Expert_ID_Name); // Удаляем индикаторы с графика// Освобождаем хэндлы и сбрасываем переменные хэндлов
IndicatorHandleRelease(handle1);
IndicatorHandleRelease(handle2);
IndicatorHandleRelease(handle3);
}
//+------------------------------------------------------------------+//| Находит индикаторы по ключевому слову name и удаляет из окна |//+------------------------------------------------------------------+void MyIndicatorsDelete(conststring 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;
}
}
Для авторизации и пользования сайтом MQL5.com необходимо разрешить использование файлов Сookie.
Пожалуйста, включите в вашем браузере данную настройку, иначе вы не сможете авторизоваться.
Вы вошли в аккаунт. Двухфакторная аутентификация отключена. Настройте её заново и отсканируйте новый QR-код или продолжите с отключённой двухфакторной аутентификацией.
Вы не разобрались в проблеме совершенно. Никто ничего специально не мудрит, а использует штатный способ - на текущем графике размещается эксперт, который под свои нужны создает индикатор - ВСЁ. После этого пользователь переключает таймфрейм и получает задвоенные подокна индикаторов - одно нормальное, второе "зомби" - либо с полным дубликатом параметров первого индикатора (что не может быть в МТ5 по определению - это баг), либо с непроинициализированным именем, то есть без сработавшего OnInit.
В приложенных исходниках MQL5 инициализация и деинициализация производятся по лучшим стандартам. Потрудились бы изучить что ли тестовый пример, прежде чем писать лабуду.
Что происходит при изменении параметров приатаченного индикатора (ТФ - один из неявных параметров индикатора), доподлинно известно и постоянно - старая копия удаляется, а новая создается, и это видно даже в прилагаемых логах - там выведены все хендлы.
но у меня работает, у вас нет...наверное потому-что я не разбираюсь
Да, расчетная часть. Но может что-то изменилось и внешне - книга-то 3 года назад писалась.
Поэкспериментировал с ручным размещением индикаторов.
В Indicator_01__1 в OnTick() добавил в стоку вывод мэджика createdByEA
Если кидать вручную и задавать одинаковый мэджик, то имеем несколько дублирующих визуализаций индикатора в подокнах, но расчетная часть принтуется одна (общая),
если кинуть с разными меджиками, то будут принтоваться две разные расчетные части (логично, т.к. параметры индикаторов теперь отличаются из-за меджиков).
А что происходит при работе из эксперта я до конца не понимаю, у меня ощущение, что там все-таки несколько багов:
1. При смене ТФ у индикатора, еще до вызова его OnDeinit, теряется заданный ранее shortname (т.к. моя процедура indicatorsEnumeration из OnDeinit() эксперта выдает имя файла индикатора, хотя перед сменой ТФ она печатала заданное имя)
2. То о чём вы написали в логах - терминал не должен был создавать дубликаты. (Хотя старая расчетная часть не была удалена т.к. индикатор с графика не был удален из-за бага 1.)
Может есть смысл написать куда-то разработчикам?
но у меня работает, у вас нет...наверное потому-что я не разбираюсь
Если речь про приложенный тесткейс - то ошибка возникает не всегда, а после многократных переключений ТФ. Условия и предполагаемые причины возникновения я описал.
Если у вас свой тесткейс (связка эксперт+пользовательский индикатор), то без исходника обсуждать, что и как там работает - невозможно.
А что происходит при работе из эксперта я до конца не понимаю, у меня ощущение, что там все-таки несколько багов:
1. При смене ТФ у индикатора, еще до вызова его OnDeinit, теряется заданный ранее shortname (т.к. моя процедура indicatorsEnumeration из OnDeinit() эксперта выдает имя файла индикатора, хотя перед сменой ТФ она печатала заданное имя)
2. То о чём вы написали в логах - терминал не должен был создавать дубликаты. (Хотя старая расчетная часть не была удалена т.к. индикатор с графика не был удален из-за бага 1.)
Может есть смысл написать куда-то разработчикам?
По п.1 возможно следует иметь в виду, что эксперт не перегружается, а остается на графике, хотя для него последовательно вызываются OnDeinit и OnInit, а вот индикаторы - старый удаляется, а новый создается - причем последовательность вызовов OnDeinit у старого и OnInit в новом может быть произвольной.
По п.2 продублировал сообщение в топике про релиз, куда обычно сообщают об ошибках.
но у меня работает, у вас нет...наверное потому-что я не разбираюсь
100%!
Ну чтож, я сделал попытку достучаться до вас. Вы ни один пример у себя не запустили.
Философия об очевидных принципах программирования, вроде конструкторов и деструкторов, мне никак не поможет - эти вещи давно понятны и без вас.
Придется сделать костыль "минимального размера", лаконичный и "стильный" и, возможно, поделиться им с сообществом.
Может есть смысл написать куда-то разработчикам?
Я подтверждаю наличие проблемы с функцией ChartIndicatorDelete() и сообщил о ней в MQ.
Я подтверждаю наличие проблемы с функцией 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. Перебор окон и индикаторов и их удаление нужно обязательно проводить в обратном порядке, т.к. при удалении индикатора и его окна нумерация изменяется и при прямом порядке обнаружение индикаторов ломается.
Я подтверждаю наличие проблемы с функцией ChartIndicatorDelete() и сообщил о ней в MQ.
Проблема не решена.
Build 6180 04.09.2026