Дескрипторы и счетчики владельцев индикаторов

Итак, для программной работы с индикаторами требуется оперировать дескрипторами. Здесь можно провести параллель с дескрипторами файлов (см. раздел Открытие и закрытие файлов): там мы сообщали системе с помощью функции FileOpen название и режимы открытия файла, после чего дескриптор служил "пропуском" во все остальные файловые функции.

Система дескрипторов индикаторов служит нескольким целям.

Она позволяет заранее сообщить терминалу, какой индикатор запустить и какие таймсерии с его помощью рассчитать. Поскольку закачка исходных исторических данных и сам процесс расчета требуют некоторого времени (по крайней мере, при первичном запросе), а также выделения ресурсов (памяти, графики), моменты создания индикатора и его готовности разнесены. Дескриптор является связующим звеном между ними. Это — своего рода ссылка на внутренний объект терминала, хранящий набор свойств, который мы задали при создании индикатора, и актуальное состояние последнего.

Разумеется, терминалу для работы с дескрипторами необходимо поддерживать некую таблицу всех затребованных индикаторов и их свойств. Однако настоящий номер в общей таблице терминал нам не сообщает: вместо этого для каждой программы формируется свой частный список запрошенных из неё индикаторов. Записи в этом списке ссылаются на элементы общей таблицы, и дескриптор — лишь номер в списке.

Отсюда следует, что в разных программах под одними и теми же дескрипторами могут скрываться совершенно разные индикаторы, а кроме того — передавать значения дескрипторов между программами бессмысленно.

Дескрипторы являются частью системы эффективного управления ресурсами терминала за счет исключения дублирования экземпляров индикаторов с одинаковыми характеристиками, когда это возможно. Иными словами, все встроенные и пользовательские индикаторы, созданные программно, вручную или из tpl-шаблонов кэшируются.  

Прежде чем создать новый экземпляр индикатора, терминал проверяет, нет ли идентичного индикатора среди находящихся в кэше. При проверке на копию применяются следующие критерии:

  • Соответствие символа и периода;
  • Соответствие параметров.

Для пользовательских индикаторов должны дополнительно совпадать:

  • Путь на диске (как строка, без нормализации в абсолютный вид);
  • График, к которому индикатор прикреплён (при создании индикатора из MQL-программы, создаваемый индикатор наследует график от создающей его программы).

Кэширование встроенных индикаторов выполняется в разрезе символа, и потому их экземпляры могут выделяться в раздельное пользование на разных графиках (с одинаковыми символом/таймфреймом).

Можно также напомнить, что создать два идентичных индикатора на одном графике вручную нельзя. Разные экземпляры программ могут запросить один и тот же индикатор, и тогда он будет создан в единственном числе и предоставляться обеим программам.

Для каждого уникального сочетания условий терминал ведет счетчик: после первого запроса на создание конкретного индикатора его счетчик равен 1, и при последующих — увеличивается на 1 (копия индикатора при этом не создается). При освобождении индикатора его счетчик уменьшается на 1. Индикатор выгружается только при обнулении счетчика, то есть когда все его владельцы явным образом откажутся от его использования.

При этом следует иметь в виду, что многократное обращение к функции-строителю индикатора с одними и теми же параметрами (включая символ/таймфрейм) в пределах одной MQL-программы не приводит к многократному увеличению счетчика ссылок — счетчик будет увеличен всего один раз. Как следствие этого, для каждого значения дескриптора достаточно одного вызова функции освобождения (IndicatorRelease) — все последующие будут лишними (вернут признак ошибки, т.к. им уже нечего освобождать).

Помимо создания индикаторов с помощью iCustom и IndicatorCreate в MQL5 существует возможность получить дескриптор стороннего (уже существующего) индикатора — предназначенную для этого функцию ChartIndicatorGet мы изучим в главе про графики. Здесь важно отметить, что получение дескриптора таким образом тоже увеличит счетчик ссылок на него и будет препятствовать выгрузке, если дескриптор затем не освободить.

Если программа создавала подчиненные индикаторы, их дескрипторы будут автоматически освобождены (счетчик уменьшен на 1) при выгрузке этой программы, даже если функция IndicatorRelease не вызывалась.