Проблема с открытием дочернего окна инструмента

 

Всем привет!

Наткнулся на вот такое - внутренняя ошибка, код 4024 в MQL4.

Задача: при инициализации советника на ведущей паре открыть график дочерней пары

Код:

input string   Pair="USDCHF_OP"; // инструмент
input string Shablon="Green";    // Шаблон графика

long PairID=-1;

int OnInit()
{
   int err;
   ....
   string SlavePair=ChartSymbol(PairID);
   Print("В окне ",PairID," открыта пара '",SlavePair+"'");
   if (SlavePair != Pair)
   {
      Print("Нет нужной пары в окне ",PairID, " Открываем новое.");
      ResetLastError();
      PairID=ChartOpen(Pair,PERIOD_M5);
      if ((err=GetLastError())>0) Print("Проблема с открытием окна пары "+Pair+" код ",err);
      else
      {
         Print("Новое окно ",PairID, " Грузим шаблон...");
         ResetLastError();
         if (ChartApplyTemplate(PairID,Shablon)) Print("Шаблон загружен");
         else Print("Проблема загрузки шаблона, код ", GetLastError());
      }
   }
  
   return(INIT_SUCCEEDED);
}


В результате исполнения в журнале:
2017.02.16 10:15:51.886 eurusd-usdchf EURUSD,M5: initialized
2017.02.16 10:15:51.886 eurusd-usdchf EURUSD,M5: Проблема с открытием окна пары USDCHF код 4024
2017.02.16 10:15:51.886 eurusd-usdchf EURUSD,M5: Нет нужной пары в окне -1 Открываем новое.
2017.02.16 10:15:51.886 eurusd-usdchf EURUSD,M5: В окне -1 открыта пара ''

При этом окно с USDCHF открывается. Где у меня косяк в коде, уже с ног сбился?
 

 
fromme2you:

Всем привет!

Наткнулся на вот такое - внутренняя ошибка, код 4024 в MQL4.

Задача: при инициализации советника на ведущей паре открыть график дочерней пары

Код:

input string   Pair="USDCHF_OP"; // инструмент
input string Shablon="Green";    // Шаблон графика

long PairID=-1;

int OnInit()
{
   int err;
   ....
   string SlavePair=ChartSymbol(PairID);
   Print("В окне ",PairID," открыта пара '",SlavePair+"'");
   if (SlavePair != Pair)
   {
      Print("Нет нужной пары в окне ",PairID, " Открываем новое.");
      ResetLastError();
      PairID=ChartOpen(Pair,PERIOD_M5);
      if ((err=GetLastError())>0) Print("Проблема с открытием окна пары "+Pair+" код ",err);
      else
      {
         Print("Новое окно ",PairID, " Грузим шаблон...");
         ResetLastError();
         if (ChartApplyTemplate(PairID,Shablon)) Print("Шаблон загружен");
         else Print("Проблема загрузки шаблона, код ", GetLastError());
      }
   }
  
   return(INIT_SUCCEEDED);
}


В результате исполнения в журнале:
2017.02.16 10:15:51.886 eurusd-usdchf EURUSD,M5: initialized
2017.02.16 10:15:51.886 eurusd-usdchf EURUSD,M5: Проблема с открытием окна пары USDCHF код 4024
2017.02.16 10:15:51.886 eurusd-usdchf EURUSD,M5: Нет нужной пары в окне -1 Открываем новое.
2017.02.16 10:15:51.886 eurusd-usdchf EURUSD,M5: В окне -1 открыта пара ''

При этом окно с USDCHF открывается. Где у меня косяк в коде, уже с ног сбился?
 

А зачем такая проверка ошибки? Не лучше-ли проверить что вернула функция ChartOpen()?
if(PairID > 0)
И если не больше нуля, тогда и проверять ошибку.
 
Alexey Viktorov:
А зачем такая проверка ошибки? Не лучше-ли проверить что вернула функция ChartOpen()?
if(PairID > 0)
И если не больше нуля, тогда и проверять ошибку.

Я этим путем ходил. ChartOpen() возвращает  значение 0, т.е. не открылось дочернее окно с USDCHF. Но потом чекунд через 15-25 оно появляется. Во какие дела!?

Что не так в коде? Как нужно понимать код ошибки 4024? И как это лечится? 

 
fromme2you:

Я этим путем ходил. ChartOpen() возвращает  значение 0, т.е. не открылось дочернее окно с USDCHF. Но потом чекунд через 15-25 оно появляется. Во какие дела!?

Что не так в коде? Как нужно понимать код ошибки 4024? И как это лечится? 

Ну попробуй перед созданием графика проверить SymbolSelect() и/или после создания Sleep() поставь.
 
Alexey Viktorov:
Ну попробуй перед созданием графика проверить SymbolSelect() и/или после создания Sleep() поставь.

Это предложение имеет второстепенный смысл к той проблеме что я задал.

Повторю еще раз: что такое "внутренняя ошибка" 4024, что является причиной ее возникновения?

 
Скорее всего, кривая реализация функционала, связанная с асинхронностью функции: она просто ставит команду в очередь событий графика, но не контролирует её выполнение. Для избежания этой ошибки следует сразу же после ChartOpen произвести его принудительную отрисовку с помощью ChartRedraw(a), где a - описатель графика, возвращаемый ChartOpen. У меня после этого выдача ошибок более не наблюдалась.
 

Ещё раз вернусь к теме.

К сожалению, таинственная ошибка 4024 возникает при попытке советника открыть новое окно с парой, отличной от той, на которую он брошен, достаточно часто. Никаких сведений в Справке о том, вследствие чего эта ошибка возникает, в чём её суть и как предотвращать её появление, разрабы привести не соизволили. Поэтому пришлось действовать методом наблюдений, проб и ошибок. Если очень коротко, то при возникновении этой ошибки новое окно всегда открывается, но после существенной задержки и, самое главное, оно получает два ID, один - явный, "странный" ID, который не является увеличенным на единицу числом предыдущего графика (на котором висит советник, открывший новое окно), а представляет собой число из совершенно "другой оперы". Другой ID - теневой, "нормальный". При этом явный "странный" ID на самом деле указывает на главное, первое, вызвавшее окно, и, если к этому ID применить функцию ChartClose(), то закрыто будет именно оно, а новое окно так и останется одиноко "висеть" в терминале. Ну, или, как вариант, при подаче команды на открытие нового окна зачем-то создаётся что-то типа указателя на главное окно, и функция ChartNext() в примере ниже возвращает именно его. Но это не более чем моя догадка, в документации, как я уже сказал, об этом ни слова.

В итоге функция открытия советником окна с другой парой приобрела нижеследующий вид:

void RefreshCharts(string Pair,ushort Period_)//подкачка котировок: Pair - пара, Period_ -  ТФ графика
{
   const ulong i = ChartFirst();              //"засекаем" ID главного графика
   Print("ID главного графика - ",i);
   long j = ChartOpen(Pair,Period_);          //даём команду на открытие графика другой пары
   while (j == 0 || j == -1)                  //обрабатываем проблемы при открытии
   {                                          //единственная ошибка, которая возникала у меня здесь за всё время наблюдений - только 4024
      if (j == 0) Print("Ошибка открытия графика ",Pair," ",_LastError,", ожидание открытия...");
      else Print("График в процессе открытия...");
      Sleep(1000);
      j = ChartNext(i);                       //когда новое окно, наконец, откроется, берём его ID "обходным" путём. Это будет "странный" ID, который будет
   }                                          //выдан в журнал в следующей строке
   Print("ID графика ",Pair," - ",j);
   long T;
   while ((!SeriesInfoInteger(Pair,PERIOD_H1,SERIES_LASTBAR_DATE,T) && _Print("Загрузка данных по " + Pair + ", ошибка " + string(_LastError))) ||
          datetime(T) < Time[0])              //проверяем актуальность котировок по открывшейся паре, если надо, подкачиваем новые
   {
      RefreshRates();
      Sleep(1000);
      ChartRedraw(j);                         //принудительно обновляем открывшийся график
      Print("Данные по графику с ID ",j," обновлены");
   }
   Sleep(1000);                               //теперь, после обновления данных, нам надо закрыть второй график. И вот тут обнаруживается очень неприятная
   Print("Закрываем график с ID ",ChartSymbol(j) == Pair ? j : i + 1); //вещь: если просто попытаться закрыть его функцией ChartClose(j), то... будет закрыт
   ChartClose(ChartSymbol(j) == Pair ? j : i + 1);                     //главный график вместе с советником! Со всеми вытекающими. И привет. Несмотря на то,
} //что аргумент функции ChartClose() - j, а не константа i, в которой хранится ID главного графика! Вот что хотите, то и думайте. Привет Метаквотсам и их
  //неубиваемым "внутренним ошибкам". Мне пришлось несколько раз пройти через это печальное обстоятельство, хорошо, что хоть на демо-счёте. Поэтому напрямую
  //мы закрываем новое окно, если оно было открыто без ошибок, и его пара совпадает с заданной при вызове функции. Если же j указывает на главное окно (его
  //пара не совпадает с обновлённой), то ID закрываемого окна указываем как увеличенный на единицу ID окна главного, хотя, грубо говоря, мы это значение
  //как бы "взяли с потолка". Ну а что делать, если иначе не получается? Можно, конечно, плюнуть, не пытаться закрыть новое окно автоматом и закрыть его
  //позже вручную, но это значит сдаться. А сдаваться без крайней необходимости не следует.
//+------------------------------------------------------------------+
bool _Print(string Text)//отображение сработавшей ветки длинного условия
{
   Print(Text);
   return(true);
}
Причина обращения: