Создание и удаление пользовательских символов

Первые две функции, которые потребуются для работы с пользовательскими символами, — это CustomSymbolCreate и CustomSymbolDelete.

bool CustomSymbolCreate(const string name, const string path = "", const string origin = NULL)

Функция создает пользовательский символ с указанным именем (name) в указанной группе (path) и, при необходимости, со свойствами образцового символа — его имя можно задать в параметре origin.

Параметр name должен быть простым идентификатором, без иерархии. При необходимости один или несколько требуемых уровней групп (вложенных папок) следует указывать в параметре path, причем символом разделителем является обратная наклонная черта '\' (прямая черта здесь не поддерживается, в отличие от файловой системы). Напомним, что обратную черту нужно задваивать в строках-литералах ("\\").

По умолчанию, если строка path пуста ("" или NULL), символ создается непосредственно в папке Custom — она выделена в общей иерархии символов для пользовательских символов. Если путь заполнен, он создается внутри папки Custom на всю глубину (если соответствующих папок еще не было).

Имя символа, также как и название группы любого уровня, может содержать латинские буквы и цифры, без знаков препинания, пробелов и спецсимволов. Дополнительно допускаются только символы '.', '_', '&' и '#'.

Имя должно быть уникальным во всей иерархии символов, независимо от того, в какой группе предполагается создание символа. Если символ с таким именем уже существует, функция вернёт false и установит код ошибки _LastError в 5300 (ERR_NOT_CUSTOM_SYMBOL) или 5304 (ERR_CUSTOM_SYMBOL_EXIST).  

Следует обратить внимание, что если последний (или даже единственный) элемент иерархии в строке path точно совпадает с именем name (с учетом регистра), то он трактуется именно как имя символа, входящее в состав пути, а не как папка. Например, если имя и путь содержат, соответственно, строки "Example" и "MQL5Book\\Example", то будет создан символ "Example" в папке "Custom\\MQL5Book\\". Вместе с тем, если изменить имя на "example", получим символ "example" в папке "Custom\\MQL5Book\\Example".

Эта особенность имеет еще одно следствие. Свойство SYMBOL_PATH возвращает путь вместе с именем символа на конце. Поэтому если перенести его значение без изменений из некоего образцового символа во вновь создаваемый, получим следующий эффект: будет создана папка с именем старого символа, внутри которой и появится новый символ. Таким образом, если необходимо создать пользовательский символ в той же группе, где и исходный символ, то необходимо отрезать имя исходного символа от строки, полученной из свойства SYMBOL_PATH.

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

Свойство SYMBOL_PATH у пользовательских символов всегда начинается с папки "Custom\\" (этот префикс добавляется автоматически).

Длина имени ограничена 31-м знаком. При превышении лимита CustomSymbolCreate вернёт false и установит код ошибки 5302 (ERR_CUSTOM_SYMBOL_NAME_LONG).

Максимальная длина параметра path 127 знаков с учетом "Custom\\", разделителей групп "\\" и имени символа, если оно указано в конце.

Параметр origin позволяет при желании задать имя символа, из которого будут скопированы свойства создаваемого пользовательского символа. После создания пользовательского символа можно изменить любое его свойство на нужное значение соответствующими функциями (см. CustomSymbolSet-функции).

Если в качестве параметра origin задан несуществующий символ, то пользовательский символ будет создан "пустым", как если бы параметр origin не был указан. При этом будет взведена ошибка 4301 (ERR_MARKET_UNKNOWN_SYMBOL).

В новом символе, созданным "пустым", все свойства установлены в значения по умолчанию. Например, размер контракта равен 100000, количество разрядов в цене — 4, расчет маржи — по правилам Forex, а построение графиков — по ценам Bid.

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

Создание символа не означает его автоматическое добавление в Обзор рынка — это нужно сделать явным образом (вручную или программно). Без котировок окно графика будет пустым.

bool CustomSymbolDelete(const string name)

Функция удаляет пользовательский символ с указанным именем. Удаляются не только настройки, но и все данные по символу (котировки и тики). Правда, история удаляется не сразу, а с некоторой задержкой, что может послужить источником проблем, если предполагается воссоздать символ с таким же именем (мы коснемся этого момента в примере в разделе Добавление, замена и удаление котировок).

Удалить можно только пользовательский символ. Кроме того нельзя удалить символ, выбранный в Обзор рынка или для которого открыт график. Напомним, что символ может быть выбран и неявным образом, без отображения в видимом списке (в таких случаях свойство SYMBOL_VISIBLE равно false, а свойство SYMBOL_SELECT — true). Такой символ требуется предварительно "скрыть" вызовом SymbolSelect("name", false) перед попыткой удаления: в противном случае получим ошибку CUSTOM_SYMBOL_SELECTED (5306).

Если в результате удаления символа остается пустая папка (или иерархия папок), она также удаляется.

Для примера создадим простой скрипт CustomSymbolCreateDelete.mq5. Во входных параметрах можно указать название, путь и образцовый символ.

input string CustomSymbol = "Dummy";         // Custom Symbol Name
input string CustomPath = "MQL5Book\\Part7"// Custom Symbol Folder
input string Origin;

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

void OnStart()
{
   bool custom = false;
   if(!PRTF(SymbolExist(CustomSymbolcustom)))
   {
      if(IDYES == MessageBox("Create new custom symbol?""Please, confirm"MB_YESNO))
      {
         PRTF(CustomSymbolCreate(CustomSymbolCustomPathOrigin));
      }
   }
   else
   {
      if(custom)
      {
         if(IDYES == MessageBox("Delete existing custom symbol?""Please, confirm"MB_YESNO))
         {
            PRTF(CustomSymbolDelete(CustomSymbol));
         }
      }
      else
      {
         Print("Can't delete non-custom symbol");
      }
   }
}

Два последовательных запуска с параметрами по умолчанию должны привести к появлению следующих записей в журнале.

SymbolExist(CustomSymbol,custom)=false / ok
Create new custom symbol?
CustomSymbolCreate(CustomSymbol,CustomPath,Origin)=true / ok
   
SymbolExist(CustomSymbol,custom)=true / ok
Delete existing custom symbol?
CustomSymbolDelete(CustomSymbol)=true / ok

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