Удаление объектов

Для удаления объектов MQL5 API предлагает две функции. Для массового удаления объектов, удовлетворяющих условиям по префиксу в имени, типу или номеру подокна, следует применять ObjectsDeleteAll. Если удаляемые объекты требуется выбирать по каким-то другим признакам (например, по устаревшей координате даты и времени) или это единичный объект, используйте функцию ObjectDelete.

Функция ObjectsDeleteAll имеет две формы: с параметром для префикса имени и без него.

int ObjectsDeleteAll(long chartId, int window = -1, int type = -1)

int ObjectsDeleteAll(long chartId, const string prefix, int window = -1, int type = -1)

Функция удаляет все объекты на графике с идентификатором chartId, с учетом подокна, типа и начальной подстроки в названии.

Значение 0 в chartId обозначает, как обычно, текущий график.

Значения по умолчанию (-1) в параметрах window и type задают, соответственно, все подокна и все типы объектов.

Если префикс пустой, будут удаляться объекты с любым именем.

Функция выполняется синхронно, то есть блокирует вызвавшую MQL-программу до своего завершения, и возвращает количество удаленных объектов. Поскольку функция дожидается выполнения всех команд, которые были в очереди графика перед её вызовом, действие может занять некоторое время.

bool ObjectDelete(long chartId, const string name)

Функция удаляет объект с указанным именем на графике с chartId.

В отличие от ObjectsDeleteAll, ObjectDelete выполняется асинхронно, то есть отправляет графику команду на удаление объекта и сразу возвращает управление MQL-программе. Результат true означает успешное размещение команды в очереди. Для проверки результата выполнения можно использовать функцию ObjectFind или любые ObjectGet-функции, запрашивающие свойства объекта.

В качестве примера рассмотрим скрипт ObjectCleanup1.mq5. Его задача заключается в удалении объектов с "нашим" префиксом, которые генерируются скриптом ObjectSimpleShowcase.mq5 из предыдущего раздела.

В простейшем случае мы могли бы написать так:

#include "ObjectPrefix.mqh"
   
void OnStart()
{
   const int n = ObjectsDeleteAll(0ObjNamePrefix);
   PrintFormat("%d objects deleted"n);
}

Но мы добавим для разнообразия возможность удаления собственными средствами с помощью многократного вызова ObjectDelete. Разумеется, такой подход не имеет смысла, когда ObjectsDeleteAll отвечает всем требованиям. Однако так бывает не всегда: когда объекты нужно отбирать по специальным условиям, то есть не только по префиксу и типу, ObjectsDeleteAll уже не поможет.

Позднее, когда мы познакомимся с функциями чтения свойств объектов, мы дополним пример. А пока введем лишь входную переменную для переключения в "расширенный" режим удаления (UseCustomDeleteAll).

#property script_show_inputs
input bool UseCustomDeleteAll = false;

В функции OnStart будем в зависимости от выбранного режима вызывать стандартную ObjectsDeleteAll или свою собственную реализацию CustomDeleteAllObjects.

void OnStart()
{
   const int n = UseCustomDeleteAll ?
      CustomDeleteAllObjects(0ObjNamePrefix) :
      ObjectsDeleteAll(0ObjNamePrefix);
      
   PrintFormat("%d objects deleted"n);
}

Сначала набросаем эскиз этой функции, а затем усовершенствуем её.

int CustomDeleteAllObjects(const long chartconst string prefix,
   const int window = -1const int type = -1)
{
   int count = 0;
   const int n = ObjectsTotal(chartwindowtype);
   
   // NB: цикл по объектам в обратном порядке внутреннего списка графика
   // чтобы сохранить нумерацию по мере удаления с хвоста
   for(int i = n - 1i >= 0; --i)
   {
      const string name = ObjectName(chartiwindowtype);
      if(StringLen(prefix) == 0 || StringFind(nameprefix) == 0)
      // дополнительные проверки, которые не обеспечивает ObjectsDeleteAll,
      // например, по координатам, цвету или точке привязки
      ...
      {
         // отдаем команду удалить конкретный объект
         count += ObjectDelete(chartname);
      }
   }
   return count;
}

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

Стоит также отметить, что цикл по объектам идет по убыванию индексов. Если бы мы делали это привычным образом, то удаление объектов в начале списка привело бы к нарушению нумерации. Строго говоря, даже текущий цикл не гарантирует полностью удаления, если предположить, что другая MQL-программа начнет добавлять объекты параллельно с нашим удалением. Действительно, новый "чужой" объект может добавиться в начало списка (он формируется по алфавитному порядку имен объектов) и увеличит остальные индексы, вытолкнув "наш" очередной, подлежащий удалению, объект за текущий индекс i. Чем больше "чужих" объектов добавится в начало, тем больше вероятность упустить удаление собственных.

Поэтому для повышения надежности можно было бы после цикла проверить, что количество оставшихся объектов равно разнице между начальным количеством и количеством удаленных объектов. Хотя и это не дает 100% гарантии, так как другие программы могли параллельно удалять объекты. Мы оставим эти нюансы для самостоятельной проработки.

В текущей реализации наш скрипт должен удалить все объекты с "нашим" префиксом вне зависимости от переключения режима UseCustomDeleteAll. В журнале должны появиться примерно такие записи:

ObjectSimpleShowcase (XAUUSD,H1) 14 objects of various types created 
ObjectCleanup1 (XAUUSD,H1) 14 objects deleted

Давайте познакомимся с функциями ObjectsTotal и ObjectName, которые уже были только что использованы, а потом вернемся к версии скрипта ObjectCleanup2.mq5.