Похоже, iCustom() таки ошибается - страница 2

 

Слава богу, я не вчера на свет народился, и о 2^31-1 (EMPTY_VALUE) при инициализации массивов, равно как и о поиске по форуму, да и еще кое о чем кое-какое представление имею. На мой взгляд, наиболее яркое обсуждение состоялось здесь: 'Помогите исправить ошибку в самопальном индикаторе' , однако суть проблемы скрылась за малозначащими подробностями.

Теперь по существу дела.

В справке читаем:
<<
void SetIndexEmptyValue(int index, double value)

Устанавливает значение пустой величины для линии индикатора.
Пустые значения не рисуются и не показываются в DataWindow.
По умолчанию значение пустой величины - EMPTY_VALUE.
>>

В вышеприведенной цитате речь идет лишь об инициализации буферов пользовательского индикатора неким "пустым" неотображаемым значением, и более ни о чем ином. Проблема же заключается совсем в другом: пользовательский индикатор при вызове его из эксперта может спорадически возвращать это самое "пустое" неотображаемое значение, хотя, на мой непросвещенный взгляд, ни в коем случае не должен этого делать. (Если же такая возможность допускается, потому, например, что ее невозможно устранить полностью, то она обязательно должна быть подробно документирована.) Инициализация индикаторных буферов нулем, как и любым другим числом, проблему не решает. Далее, как выяснилось, скорость генерации тиков при тестировании особой роли не играет, из чего следует, между прочим, что эффект может проявляться и в реальных условиях, однако его спорадический характер делает его обнаружение в этих условиях весьма затруднительным. Тот же самый индикатор, помещенный на тот же самый график в том же самом режиме визуализации при прогоне "пустого" эксперта, как и при помещении на реальный график, подобных странностей поведения не обнаруживает (по крайней мере, я их не наблюдал). Не наблюдается ничего необычного и в том случае, если часть кода индикатора, отвечающую за вычисления, разместить прямо в коде эксперта или оформить в виде процедурыфункции. Возможность размещения функции во внутренней (*.mq4) или внешней (*.dll) библиотеке не проверялась.

Для иллюстрации всего вышесказанного был подготовлен еще один пример, состоящий из эксперта и индикатора, причем 3 рабочих буфера индикатора (всего их 4) хотя и инициализируются нулями с помощью вышеозначенной функции SetIndexEmptyValue(...), но в процессе работы по определению не могут принимать нулевых значений. (Буфер с индексом "0" также инициализируется тем же числом, что и рабочие, но его значения в эксперт не передаются.) Эксперт вызывает индикатор, и если какие-то из возвращаемых индикатором значений оказываются нулевыми, снимает скриншот. Далее, чтобы исключить всякую возможность того, что ноль все же играет какую-то особую роль, инициализируем буферы, например, числом "пи" (3.141592635) с очевидными изменениями в коде - и иногда будем получать вообще какую-то ересь вроде суперпозиции числа "пи" со значением цены с графика. Некоторые примеры полученных скриншотов - в аттаче, вместе с кодом эксперта и индикатора (имена, начинающиеся со знака "_" - примеры для числа "пи").

Режим тестирования - все тот же, визуальный, скорость 30.

Файлы:
ama.zip  77 kb
 
alexjou,
Я посмотрел ссылку, увидел ваше участие в той ветке. Если Вы знали об этом свойстве, то надо было использовать данную функцию. Насколько я понимаю, SetIndexEmptyValue() просто сообщает индикатору, какие значения буфера являются нулевыми (незначащими или пустыми). Индикатор подавляет вывод пустых(неинициализированных) значений буферов, то есть это не совсем инициализация. Просто, там где будет встречено пустое (неинициализированное) значение, то там не будет отрисовки. Я сейчас посмотрел как работает Ваш индикатор HLR, действительно, в реал-тайме он не показывает артефакта, надо сначала разобрать алгоритм Вашего индикатора, и если в нем не будет найдено хитростей - буду думать.

В вашем архиве находятся два эксперта и два индикатора. Я открыл сначала коды экспертов - они отличаются только одним условием, в то время как iCustom() вызывает в обоих экспертах вызывает одну иту же функцию. Одна другую вроде не вызывает, буду смотреть дальше.
 
alexjou, в клиентском терминале реализован метод "calculate on demand", для того чтобы лишний раз не нагружать процессор. Индикатор, прикреплённый к графику, рассчитывается каждый раз, когда приходит новый тик (при этом все наши встроенные и соответствующие им пользовательские индикаторы не пересчитываются заново, а дорассчитывается последний и предпоследний бары, то есть очень экономный алгоритм). Индикатор же, вызываемый из эксперта, рассчитывается только при обращении к нему, если обращения нет, то нет и расчёта. Концептуальная разница.

Теперь по поводу Вашего индикатора HLR_
Он вызывается с параметром LastBarOnly=true, то есть пересчитывается только один, текущий бар. При этом в коде Вашего индикатора есть одно очень интересное место
  m_pr = (High[cnt_bars] + Low[cnt_bars]) / 2.0; 
  if (MathAbs(m_pr - m_pr_old) < Point) 
   { return(0); }
которое, вкупе с тем, что эксперт запускается на выполнение только один раз в начале каждого бара, объясняет "спорадическое" появление EMPTY_VALUE (в данном случае нулевой бар просто остаётся непосчитанным). При этом, индикатор на графике этих дыр не показывает, так как нулевой бар рассчитывается заново с каждым пришедшим тиком.
 
Попробую сформулировать на пальцах:

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

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

То есть, необходимо писать индикаторы так, чтобы они учитывали возможность периодического вызова, а не считали исключительно последние бары.
 
Действительно, похоже, что дело было в этом самом блоке. Закомментировал его, и ошибки прекратились. Т.е., эксперт пытался извлечь из индикаторного буфера несуществующее значение, не находил его там, и заменял значением, заданным по умолчанию. С этим ясно. Насчет последнего бара (и тоже на пальцах): если индикатор считает не только последний бар, то для выдачи торговых сигналов он, скорее всего, не годится, т.к. в этом случае возникает опасность "лакировки истории" (пример - индикатор Laguerre в его "классическом" варианте). Насколько красиво он при этом рисуется, большого значения не имеет. Обработка ошибок от пропусков данных при таком способе расчета также всецело ложится на разработчика индикатора. К слову сказать, в визуальном режиме "лакировка истории" и вызванные ею ошибки при открытии\закрытии позиций очень хорошо обнаруживаются. По этой самой причине я в свое время отказался от применения в торговле индикаторов на основе, например, тех или иных интегральных преобразований. Впрочем, это мое частное мнение, и я на нем никоим образом не настаиваю. Во всяком случае, думаю, вопрос закрыт. Всем спасибо за внимание и всяческих успехов!
Причина обращения: