English 中文 Español Deutsch 日本語 Português
preview
Торговый инструментарий MQL5 (Часть 2): Расширение и применение EX5-библиотеки для управления позициями

Торговый инструментарий MQL5 (Часть 2): Расширение и применение EX5-библиотеки для управления позициями

MetaTrader 5Примеры |
707 0
Wanateki Solutions LTD
Kelvin Muturi Muigua

Введение

В первой статье мы подробно проанализировали библиотеки кода MQL5. Мы рассмотрели различные типы библиотек, их преимущества, создание EX5-библиотек и компоненты файла исходного кода библиотеки (.mq5). Это дало нам прочную основу для изучения EX5-библиотек и процесса их создания. Затем мы создали практический пример EX5-библиотеки управления позициями, демонстрирующий, как реализовывать экспортируемые функции в MQL5-коде.

В этой статье мы расширим EX5-библиотеку для управления позициями и создадим два базовых советника. Один из этих примеров советников будет использовать графическую торговую и информационную панель, демонстрируя, как импортировать и внедрять EX5-библиотеку для управления позициями на практике. Это послужит реальным примером того, как создать и интегрировать EX5-библиотеку в ваш код MQL5. Для начала разберем процесс импортирования и использования уже разработанной и скомпилированной двоичной библиотеки .ex5.


Как импортировать и использовать EX5-библиотеку

Для импорта и использования EX5-библиотеки в вашем MQL5-коде (советники, пользовательские индикаторы, скрипты или сервисы), необходимо добавить директивы #import под директивами #property в заголовке или верхней части файла исходного кода. Чтобы включить скомпилированную бинарную библиотеку, начните с указания директивы#import, за которой следует путь к файлу, где хранится библиотека. По умолчанию MQL5 ищет библиотеки в двух местах, чтобы сэкономить время на обращение к ним напрямую в коде. Первое место — это папка MQL5/Libraries, которая является предопределенным местом по умолчанию для хранения библиотек. Если библиотека там не найдена, MQL5 выполнит поиск в папке, где находится сама MQL-программа. Если ваша EX5-библиотека хранится непосредственно в папке MQL5/Libraries/ или в той же папке, что и исходный код, просто укажите имя библиотеки, заключенное в двойные кавычки, после директивы #import, не указывая путь к папке.

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

#import "FilePath/LibraryName.ex5" //-- Opening .EX5 Library import directive

   //-- Function definitions/descriptions prototypes
   int  FunctionPrototype1();
   void FunctionPrototype2(bool y);
   bool FunctionPrototype3(double x);

#import //--- Closing .EX5 Library import directive

При объявлении директивы import необходимо указать расширение .ex5 после имени библиотеки. Если не указывать расширение, это будет означать, что вы импортируете библиотеку .DLL по умолчанию.

Вы также можете импортировать и реализовать несколько .ex5-библиотек в одном файле MQL5. Структура кода для импорта нескольких .ex5-библиотек похожа на импорт одной библиотеки. Единственное отличие - расположение закрывающих директив  #import. Для импорта нескольких библиотек за закрывающей директивой #import первой библиотеки должно следовать имя следующей импортируемой .ex5-библиотеки. Это закроет первую директиву import, одновременно инициируя следующую и так далее. При закрытии последней директивы import последней библиотеки убедитесь, что она заканчивается без имени библиотеки.

#import "FilePath/LibraryName.ex5"  //-- Opening .EX5 Library import directive

   //-- Function definitions/descriptions prototypes for the first library here
   int  FunctionPrototype1();
   void FunctionPrototype2(bool y);
   
#import "FilePath/SecondLibraryName.ex5"
   //-- Function definitions/descriptions prototypes for the second library here
   bool  FunctionPrototype();
   string FunctionPrototype2(bool z);

#import //--- Closing .EX5 Library import directive

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

Каждая библиотека создает свою собственную изолированную среду или "пространство имен" (namepsace). Это означает, что функции внутри библиотеки связаны с именем этой библиотеки. Вы можете свободно называть функции внутри библиотеки, не беспокоясь о конфликтах, даже если они совпадают с именами встроенных функций. Однако обычно рекомендуется избегать подобных наименований для большей ясности.

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

Далее в статье я подробно объясняю, как внедрять и использовать EX5-библиотеки на практике. Вы найдете две подробные демонстрации: в одной мы пишем код советника на основе торговой стратегии VIDyA, а в другой — с использованием графического пользовательского интерфейса (GUI). Эти советники будут интегрировать и использовать нашу специально разработанную EX5-библиотеку для управления позициями. Эти практические примеры предоставят ценную информацию о внедрении EX5-библиотек в реальные советники.


Распространенные ошибки выполнения EX5-библиотеки

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

Любой файл исходного кода, содержащий эти ошибки, будет успешно скомпилирован, но при попытке загрузить скомпилированное MQL5-приложение в торговый терминал возникнут ошибки выполнения. Эти ошибки отображаются либо на вкладке "Эксперты" или "Журнал" терминала MetaTrader 5. Вот наиболее распространенные ошибки:

Unresolved Import Function Call(cannot find 'Function_Name' in 'Library_Name.ex5')

    • Описание: Эта ошибка выполнения возникает при попытке загрузить приложение MQL5 на график MetaTrader 5 и отображается на вкладке "Эксперты". Ошибка вызвана неправильными определениями или описаниями прототипов функций, такими как тип, имя или параметры, предоставленными в директив import библиотеки.
    • Решение: Убедитесь, что сегмент кода import библиотеки содержит правильные определения прототипов функций и перекомпилируйте ваш код.

Unresolved import function call EX5 error


Cannot Open File 'Library_Name.ex5'(loading of ExpertAdvisor_Name (GBPJPY,M15) failed [0])

    • Описание: Эта ошибка выполнения возникает при попытке загрузить приложение MQL5 на график MetaTrader 5 и отображается на вкладке "Журнал". Ошибка возникает, когда импортированный файл EX5-библиотеки не может быть найден и загружен.
    • Решение: Убедитесь, что сегмент кода import содержит правильный путь к библиотеке и перекомпилируйте код.
can not open EX5 library file error

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


Как обновлять и повторно разворачивать EX5-библиотеки

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

  1. Скомпилируйте новый EX5-файл: Начните с компиляции обновленного файла исходного кода библиотеки .mq5 для создания нового исполняемого двоичного файла .ex5.
  2. Обновите импортируемые прототипы функций: Во всех проектах MQL5, использующих EX5-библиотеку, обновите все определения import прототипов функций, если они изменились в очередном обновлении .ex5-библиотеки.
  3. Скомпилируйте проекты: Перекомпилируйте все MQL5-проекты, реализующие EX5-библиотеку.
Соблюдая эту последовательность, вы обеспечите интеграцию всех обновлений EX5-библиотеки во все проекты, импортирующие эту библиотеку.


Функция трейлинга уровня стоп-лосс

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

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

Сначала функция проверяет, разрешена ли торговля и действителен ли указанный скользящий стоп-лосс в пипсах (пунктах). Затем она выберет позицию, извлечет и сохранит всю необходимую информацию о символе и рассчитает цену трейлинг-стоп-лосса. Если рассчитанная цена верна, она отправит на торговый сервер приказ на установку стоп-лосса. Если ордер успешно выполнен, функция вернет true, если нет - false.

Начнем с определения функции. Функция трейлинг-стоп-лосса будет иметь тип bool и будет содержать два параметра:

  1. ulong positionTicket - уникальный идентификатор позиции, которую мы изменим.
  2. int trailingStopLoss - желаемый уровень стоп-лосса в пипсах (пунктах) от текущей цены позиции.

Ключевое слово export указывает, что эту библиотечную функцию можно вызвать из любого файла исходного кода MQL5 или проекта, который ее импортирует.

bool SetTrailingStopLoss(ulong positionTicket, int trailingStopLoss) export
  {
    //-- place the function body here
  }

Нам нужно проверить, разрешена ли торговля, а также то, что параметр trailingStopLoss выше нуля. Если какое-либо условие не выполняется, мы выходим из функции и возвращаем false для прекращения операции.

if(!TradingIsAllowed() || trailingStopLoss == 0)
     {
      return(false); //--- algo trading is disabled or trailing stop loss is invalid, exit function
     }

Далее подтверждаем и выбираем позицию, используя предоставленный positionTicket. Если выбрать позицию не удалось, выводим сообщение об ошибке и выходим из функции.

//--- Confirm and select the position using the provided positionTicket
   ResetLastError(); //--- Reset error cache incase of ticket selection errors
   if(!PositionSelectByTicket(positionTicket))
     {
      //---Position selection failed
      Print("\r\n_______________________________________________________________________________________");
      Print(__FUNCTION__, ": Selecting position with ticket:", positionTicket, " failed. ERROR: ", GetLastError());
      return(false); //-- Exit the function
     }

Затем создаем несколько переменных, которые помогут нам хранить и проверять скользящий стоп-лосс. Начнем с создания переменной slPrice для хранения рассчитанной цены трейлинг-стоп-лосса, а затем сохраним свойства позиции, такие как символ, цена входа, объем, текущие цены стоп-лосса и тейк-профита, а также тип позиции.

//-- create variable to store the calculated trailing sl prices to send to the trade server
   double slPrice = 0.0;

//--- Position ticket selected, save the position properties
   string positionSymbol = PositionGetString(POSITION_SYMBOL);
   double entryPrice = PositionGetDouble(POSITION_PRICE_OPEN);
   double volume = PositionGetDouble(POSITION_VOLUME);
   double currentPositionSlPrice = PositionGetDouble(POSITION_SL);
   double currentPositionTpPrice = PositionGetDouble(POSITION_TP);
   ENUM_POSITION_TYPE positionType = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);

Аналогичным образом мы продолжаем сохранять различные свойства символа, связанные с выбранной позицией. Эти свойства также будут использоваться позже для проверки и расчета трейлинг-стоп-лосса.

//-- Get some information about the positions symbol
   int symbolDigits = (int)SymbolInfoInteger(positionSymbol, SYMBOL_DIGITS); //-- Number of symbol decimal places
   int symbolStopLevel = (int)SymbolInfoInteger(positionSymbol, SYMBOL_TRADE_STOPS_LEVEL);
   double symbolPoint = SymbolInfoDouble(positionSymbol, SYMBOL_POINT);
   double positionPriceCurrent = PositionGetDouble(POSITION_PRICE_CURRENT);
   int spread = (int)SymbolInfoInteger(positionSymbol, SYMBOL_SPREAD);

Проверяем меньше ли указанное значение стоп-лосса, чем стоп-уровень символа. Если да, корректируем трейлинг-стоп-лосс так, чтобы он был равен стоп-уровню символа.

//-- Check if the trailing stop loss is less than the symbol trade stop levels
   if(trailingStopLoss < symbolStopLevel)
     {
      //-- Trailing stop loss is less than the allowed level for the current symbol
      trailingStopLoss = symbolStopLevel; //-- Set it to the symbol stop level by default
     }

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

//-- Calculate and store the trailing stop loss price
   if(positionType == POSITION_TYPE_BUY)
     {
      slPrice = positionPriceCurrent - (trailingStopLoss * symbolPoint);

      //-- Check if the proposed slPrice for the trailing stop loss is valid
      if(slPrice < entryPrice || slPrice < currentPositionSlPrice)
        {
         return(false); //-- Exit the function, proposed trailing stop loss price is invalid
        }
     }
   else  //-- SELL POSITION
     {
      slPrice = positionPriceCurrent + (trailingStopLoss * symbolPoint);

      //-- Check if the proposed slPrice for the trailing stop loss is valid
      if(slPrice > entryPrice || slPrice > currentPositionSlPrice)
        {
         return(false); //-- Exit the function, proposed trailing stop loss price is invalid
        }
     }

Перед установкой трейлинг-стоп-лосса давайте выведем данные позиции в журнале MetaTrader 5. Сюда входят символ, тип позиции, объем, цена входа, текущие цены стоп-лосса и тейк-профита, а также другая важная информация.

//-- Print position properties before setting the trailing stop loss
   string positionProperties = "--> "  + positionSymbol + " " + EnumToString(positionType) + " Trailing Stop Loss Modification Details" +
                               " <--\r\n";
   positionProperties += "------------------------------------------------------------\r\n";
   positionProperties += "Ticket: " + (string)positionTicket + "\r\n";
   positionProperties += "Volume: " + StringFormat("%G", volume) + "\r\n";
   positionProperties += "Price Open: " + StringFormat("%G", entryPrice) + "\r\n";
   positionProperties += "Current SL: " + StringFormat("%G", currentPositionSlPrice) + "   -> New Trailing SL: " + (string)slPrice + "\r\n";
   positionProperties += "Current TP: " + StringFormat("%G", currentPositionTpPrice) + "\r\n";
   positionProperties += "Comment: " + PositionGetString(POSITION_COMMENT) + "\r\n";
   positionProperties += "Magic Number: " + (string)PositionGetInteger(POSITION_MAGIC) + "\r\n";
   positionProperties += "---";
   Print(positionProperties);

Сбрасываем структуры tradeRequest и tradeResult до нуля. Затем мы инициализируем параметры, необходимые для установки стоп-лосса и тейк-профита.

//-- reset the the tradeRequest and tradeResult values by zeroing them
   ZeroMemory(tradeRequest);
   ZeroMemory(tradeResult);

//-- initialize the parameters to set the sltp
   tradeRequest.action = TRADE_ACTION_SLTP; //-- Trade operation type for setting sl and tp
   tradeRequest.position = positionTicket;
   tradeRequest.symbol = positionSymbol;
   tradeRequest.sl = slPrice;
   tradeRequest.tp = currentPositionTpPrice;

Наконец, мы сбрасываем кэш ошибок и отправляем ордер на торговый сервер до тех пор, пока он не будет успешно выполнен или пока не будет выполнена 101 повторная попытка. Если ордер успешно выполнен, мы выводим сообщение об этом, возвращаем true и выходим из функции. Если запрос ордера не выполнен, обрабатываем ошибку, возвращаем false и выходим из функции.

ResetLastError(); //--- reset error cache so that we get an accurate runtime error code in the ErrorAdvisor function

   for(int loop = 0; loop <= 100; loop++) //-- try modifying the sl and tp 101 times untill the request is successful
     {
      //--- send order to the trade server
      if(OrderSend(tradeRequest, tradeResult))
        {
         //-- Confirm order execution
         if(tradeResult.retcode == 10008 || tradeResult.retcode == 10009)
           {
            PrintFormat("Successfully set the Trailing SL for #%I64d %s %s", positionTicket, positionSymbol, EnumToString(positionType));
            PrintFormat("retcode=%u  runtime_code=%u", tradeResult.retcode, GetLastError());
            Print("_______________________________________________________________________________________\r\n\r\n");
            return(true); //-- exit function
            break; //--- success - order placed ok. exit for loop
           }
        }
      else  //-- Order request failed
        {
         //-- order not sent or critical error found
         if(!ErrorAdvisor(__FUNCTION__, positionSymbol, tradeResult.retcode) || IsStopped())
           {
            PrintFormat("ERROR setting the Trailing SL for #%I64d %s %s", positionTicket, positionSymbol, EnumToString(positionType));
            Print("_______________________________________________________________________________________\r\n\r\n");
            return(false); //-- exit function
            break; //-- exit for loop
           }
        }
     }
   return(false);
  }

Убедитесь, что сегменты кода функции SetTrailingStopLoss() идут в следующей последовательности:

bool SetTrailingStopLoss(ulong positionTicket, int trailingStopLoss) export
  {
//-- first check if the EA is allowed to trade and the trailing stop loss parameter is more than zero
   if(!TradingIsAllowed() || trailingStopLoss == 0)
     {
      return(false); //--- algo trading is disabled or trailing stop loss is invalid, exit function
     }

//--- Confirm and select the position using the provided positionTicket
   ResetLastError(); //--- Reset error cache incase of ticket selection errors
   if(!PositionSelectByTicket(positionTicket))
     {
      //---Position selection failed
      Print("\r\n_______________________________________________________________________________________");
      Print(__FUNCTION__, ": Selecting position with ticket:", positionTicket, " failed. ERROR: ", GetLastError());
      return(false); //-- Exit the function
     }

//-- create variable to store the calculated trailing sl prices to send to the trade server
   double slPrice = 0.0;

//--- Position ticket selected, save the position properties
   string positionSymbol = PositionGetString(POSITION_SYMBOL);
   double entryPrice = PositionGetDouble(POSITION_PRICE_OPEN);
   double volume = PositionGetDouble(POSITION_VOLUME);
   double currentPositionSlPrice = PositionGetDouble(POSITION_SL);
   double currentPositionTpPrice = PositionGetDouble(POSITION_TP);
   ENUM_POSITION_TYPE positionType = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);

//-- Get some information about the positions symbol
   int symbolDigits = (int)SymbolInfoInteger(positionSymbol, SYMBOL_DIGITS); //-- Number of symbol decimal places
   int symbolStopLevel = (int)SymbolInfoInteger(positionSymbol, SYMBOL_TRADE_STOPS_LEVEL);
   double symbolPoint = SymbolInfoDouble(positionSymbol, SYMBOL_POINT);
   double positionPriceCurrent = PositionGetDouble(POSITION_PRICE_CURRENT);
   int spread = (int)SymbolInfoInteger(positionSymbol, SYMBOL_SPREAD);

//-- Check if the trailing stop loss is less than the symbol trade stop levels
   if(trailingStopLoss < symbolStopLevel)
     {
      //-- Trailing stop loss is less than the allowed level for the current symbol
      trailingStopLoss = symbolStopLevel; //-- Set it to the symbol stop level by default
     }

//-- Calculate and store the trailing stop loss price
   if(positionType == POSITION_TYPE_BUY)
     {
      slPrice = positionPriceCurrent - (trailingStopLoss * symbolPoint);

      //-- Check if the proposed slPrice for the trailing stop loss is valid
      if(slPrice < entryPrice || slPrice < currentPositionSlPrice)
        {
         return(false); //-- Exit the function, proposed trailing stop loss price is invalid
        }
     }
   else  //-- SELL POSITION
     {
      slPrice = positionPriceCurrent + (trailingStopLoss * symbolPoint);

      //-- Check if the proposed slPrice for the trailing stop loss is valid
      if(slPrice > entryPrice || slPrice > currentPositionSlPrice)
        {
         return(false); //-- Exit the function, proposed trailing stop loss price is invalid
        }
     }

//-- Print position properties before setting the trailing stop loss
   string positionProperties = "--> "  + positionSymbol + " " + EnumToString(positionType) + " Trailing Stop Loss Modification Details" +
                               " <--\r\n";
   positionProperties += "------------------------------------------------------------\r\n";
   positionProperties += "Ticket: " + (string)positionTicket + "\r\n";
   positionProperties += "Volume: " + StringFormat("%G", volume) + "\r\n";
   positionProperties += "Price Open: " + StringFormat("%G", entryPrice) + "\r\n";
   positionProperties += "Current SL: " + StringFormat("%G", currentPositionSlPrice) + "   -> New Trailing SL: " + (string)slPrice + "\r\n";
   positionProperties += "Current TP: " + StringFormat("%G", currentPositionTpPrice) + "\r\n";
   positionProperties += "Comment: " + PositionGetString(POSITION_COMMENT) + "\r\n";
   positionProperties += "Magic Number: " + (string)PositionGetInteger(POSITION_MAGIC) + "\r\n";
   positionProperties += "---";
   Print(positionProperties);

//-- reset the the tradeRequest and tradeResult values by zeroing them
   ZeroMemory(tradeRequest);
   ZeroMemory(tradeResult);

//-- initialize the parameters to set the sltp
   tradeRequest.action = TRADE_ACTION_SLTP; //-- Trade operation type for setting sl and tp
   tradeRequest.position = positionTicket;
   tradeRequest.symbol = positionSymbol;
   tradeRequest.sl = slPrice;
   tradeRequest.tp = currentPositionTpPrice;

   ResetLastError(); //--- reset error cache so that we get an accurate runtime error code in the ErrorAdvisor function

   for(int loop = 0; loop <= 100; loop++) //-- try modifying the sl and tp 101 times untill the request is successful
     {
      //--- send order to the trade server
      if(OrderSend(tradeRequest, tradeResult))
        {
         //-- Confirm order execution
         if(tradeResult.retcode == 10008 || tradeResult.retcode == 10009)
           {
            PrintFormat("Successfully set the Trailing SL for #%I64d %s %s", positionTicket, positionSymbol, EnumToString(positionType));
            PrintFormat("retcode=%u  runtime_code=%u", tradeResult.retcode, GetLastError());
            Print("_______________________________________________________________________________________\r\n\r\n");
            return(true); //-- exit function
            break; //--- success - order placed ok. exit for loop
           }
        }
      else  //-- Order request failed
        {
         //-- order not sent or critical error found
         if(!ErrorAdvisor(__FUNCTION__, positionSymbol, tradeResult.retcode) || IsStopped())
           {
            PrintFormat("ERROR setting the Trailing SL for #%I64d %s %s", positionTicket, positionSymbol, EnumToString(positionType));
            Print("_______________________________________________________________________________________\r\n\r\n");
            return(false); //-- exit function
            break; //-- exit for loop
           }
        }
     }
   return(false);
  }



Функция закрытия всех позиций

Эта функция разработана очень гибко и будет отвечать за закрытие всех открытых позиций на основе указанных параметров. Назовем функцию CloseAllPositions(). Она будет сканировать открытые позиции, соответствующие предоставленным аргументам или параметрам символа и магического символа, и пытаться закрыть их все. Если торговля не разрешена, функция прекратит выполнение и немедленно завершит работу. Функция пройдёт по всем открытым позициям, отсортирует их на основе указанных критериев и закроет все соответствующие позиции.

После того, как она завершит попытки закрыть все позиции, она войдет в цикл, чтобы подтвердить, что все целевые позиции закрыты, обработать все ошибки и убедиться, что мы не попали в бесконечный цикл. После успешного закрытия всез позиций, функция выходит, возвращая true. В противном случае она возвращает false.

Начнем с определения функции CloseAllPositions(). Она вернет логическое значение и примет два параметра со значениями по умолчанию:

  1. string symbol: По умолчанию ALL_SYMBOLS.
  2. ulong magicNumber: По умолчанию 0.

bool CloseAllPositions(string symbol = ALL_SYMBOLS, ulong magicNumber = 0) export
  {
//-- Functions body goes here
  }

Нам нужно проверить, разрешена ли торговля. Если торговля не разрешена, выходим из функции и возвращаем false.

if(!TradingIsAllowed())
     {
      return(false); //--- algo trading is disabled, exit function
     }

Создадим новую булеву переменную returnThis для сохранения возвращаемого значения функции и присвоения ему значения по умолчанию.

bool returnThis = false;

Извлечем и сохраним общее количество открытых позиций и используем это значение в цикле for, который позволит нам получить доступ ко всем открытым позициям и обработать их. На каждой итерации мы будем сохранять выбранные свойства позиции и использовать эти данные для сортировки позиций на основе предоставленных символа и магического числа. Если позиция не соответствует критериям, переходим к следующей позиции. Если позиция соответствует критериям, мы закрываем позицию с помощью функции ClosePositionByTicket().

int totalOpenPositions = PositionsTotal();
   for(int x = 0; x < totalOpenPositions; x++)
     {
      //--- Get position properties
      ulong positionTicket = PositionGetTicket(x); //-- Get ticket to select the position
      string selectedSymbol = PositionGetString(POSITION_SYMBOL);
      ulong positionMagicNo = PositionGetInteger(POSITION_MAGIC);

      //-- Filter positions by symbol and magic number
      if(
         (symbol != ALL_SYMBOLS && symbol != selectedSymbol) ||
         (magicNumber != 0 && positionMagicNo != magicNumber)
      )
        {
         continue;
        }

      //-- Close the position
      ClosePositionByTicket(positionTicket);
     }

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

int breakerBreaker = 0; //-- Variable that safeguards and makes sure we are not locked in an infinite loop
   while(SymbolPositionsTotal(symbol, magicNumber) > 0)
     {
      breakerBreaker++;
      CloseAllPositions(symbol, magicNumber); //-- We still have some open positions, do a function callback
      Sleep(100); //-- Micro sleep to pace the execution and give some time to the trade server

      //-- Check for critical errors so that we exit the loop if we run into trouble
      if(!ErrorAdvisor(__FUNCTION__, symbol, GetLastError()) || IsStopped() || breakerBreaker > 101)
        {
         break;
        }
     }

Повторно проверим, что все целевые позиции закрыты, и сохраним этот статус в возвращаемой переменной перед окончательным завершением и выходом из функции.

if(SymbolPositionsTotal(symbol, magicNumber) == 0)
     {
      returnThis = true; //-- Save this status for the function return value
     }

   return(returnThis);

Confirm that all the CloseAllPositions() function code segments are complete in the sequence below:

bool CloseAllPositions(string symbol = ALL_SYMBOLS, ulong magicNumber = 0) export
  {
//-- first check if the EA is allowed to trade
   if(!TradingIsAllowed())
     {
      return(false); //--- algo trading is disabled, exit function
     }

   bool returnThis = false;

//-- Scan for symbol and magic number specific positions and close them
   int totalOpenPositions = PositionsTotal();
   for(int x = 0; x < totalOpenPositions; x++)
     {
      //--- Get position properties
      ulong positionTicket = PositionGetTicket(x); //-- Get ticket to select the position
      string selectedSymbol = PositionGetString(POSITION_SYMBOL);
      ulong positionMagicNo = PositionGetInteger(POSITION_MAGIC);

      //-- Filter positions by symbol and magic number
      if(
         (symbol != ALL_SYMBOLS && symbol != selectedSymbol) ||
         (magicNumber != 0 && positionMagicNo != magicNumber)
      )
        {
         continue;
        }

      //-- Close the position
      ClosePositionByTicket(positionTicket);
     }

//-- Confirm that we have closed all the positions being targeted
   int breakerBreaker = 0; //-- Variable that safeguards and makes sure we are not locked in an infinite loop
   while(SymbolPositionsTotal(symbol, magicNumber) > 0)
     {
      breakerBreaker++;
      CloseAllPositions(symbol, magicNumber); //-- We still have some open positions, do a function callback
      Sleep(100); //-- Micro sleep to pace the execution and give some time to the trade server

      //-- Check for critical errors so that we exit the loop if we run into trouble
      if(!ErrorAdvisor(__FUNCTION__, symbol, GetLastError()) || IsStopped() || breakerBreaker > 101)
        {
         break;
        }
     }

//-- Final confirmations that all targeted positions have been closed
   if(SymbolPositionsTotal(symbol, magicNumber) == 0)
     {
      returnThis = true; //-- Save this status for the function return value
     }

   return(returnThis);
  }


Перегрузка функции для закрытия всех позиций

Для удобства мы перегрузим функцию CloseAllPositions() со второй версией, которая не принимает никаких параметров и закрывает все открытые позиции на счете при вызове. Эту функцию также можно будет экспортировать и использовать в EX5-библиотеке для управления позициями.

//+------------------------------------------------------------------+
//| CloseAllPositions(): Closes all positions in the account         |
//+------------------------------------------------------------------+
bool CloseAllPositions() export
  {
   return(CloseAllPositions(ALL_SYMBOLS, 0));
  }


Сортировка функций закрытия позиций

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

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


Закрытие всех позиций на покупку

Функция CloseAllBuyPositions() отвечает за закрытие всех открытых позиций на покупку, которые соответствуют указанным параметрам или аргументам функции имени символа и магического числа. Функция возвращает логическое значение true при успешном закрытии всех указанных позиций и false в противном случае.

bool CloseAllBuyPositions(string symbol = ALL_SYMBOLS, ulong magicNumber = 0) export
  {
//-- first check if the EA is allowed to trade
   if(!TradingIsAllowed())
     {
      return(false); //--- algo trading is disabled, exit function
     }

   bool returnThis = false;

//-- Scan for symbol and magic number specific buy positions and close them
   int totalOpenPositions = PositionsTotal();
   for(int x = 0; x < totalOpenPositions; x++)
     {
      //--- Get position properties
      ulong positionTicket = PositionGetTicket(x); //-- Get ticket to select the position
      string selectedSymbol = PositionGetString(POSITION_SYMBOL);
      ulong positionMagicNo = PositionGetInteger(POSITION_MAGIC);
      ulong positionType = PositionGetInteger(POSITION_TYPE);

      //-- Filter positions by symbol, type and magic number
      if(
         (symbol != ALL_SYMBOLS && symbol != selectedSymbol) || (positionType != POSITION_TYPE_BUY) ||
         (magicNumber != 0 && positionMagicNo != magicNumber)
      )
        {
         continue;
        }

      //-- Close the position
      ClosePositionByTicket(positionTicket);
     }

//-- Confirm that we have closed all the buy positions being targeted
   int breakerBreaker = 0; //-- Variable that safeguards and makes sure we are not locked in an infinite loop
   while(SymbolBuyPositionsTotal(symbol, magicNumber) > 0)
     {
      breakerBreaker++;
      CloseAllBuyPositions(symbol, magicNumber); //-- We still have some open buy positions, do a function callback
      Sleep(100); //-- Micro sleep to pace the execution and give some time to the trade server

      //-- Check for critical errors so that we exit the loop if we run into trouble
      if(!ErrorAdvisor(__FUNCTION__, symbol, GetLastError()) || IsStopped() || breakerBreaker > 101)
        {
         break;
        }
     }

   if(SymbolBuyPositionsTotal(symbol, magicNumber) == 0)
     {
      returnThis = true;
     }
   return(returnThis);
  }

Закрытие всех позиций на продажу

Функция CloseAllSellPositions() отвечает за закрытие всех открытых позиций на продажу, которые соответствуют указанным параметрам или аргументам функции имени символа и магического числа. Функция возвращает логическое значение true при успешном закрытии всех указанных позиций и false в противном случае.

bool CloseAllSellPositions(string symbol = ALL_SYMBOLS, ulong magicNumber = 0) export
  {
//-- first check if the EA is allowed to trade
   if(!TradingIsAllowed())
     {
      return(false); //--- algo trading is disabled, exit function
     }

   bool returnThis = false;

//-- Scan for symbol and magic number specific sell positions and close them
   int totalOpenPositions = PositionsTotal();
   for(int x = 0; x < totalOpenPositions; x++)
     {
      //--- Get position properties
      ulong positionTicket = PositionGetTicket(x); //-- Get ticket to select the position
      string selectedSymbol = PositionGetString(POSITION_SYMBOL);
      ulong positionMagicNo = PositionGetInteger(POSITION_MAGIC);
      ulong positionType = PositionGetInteger(POSITION_TYPE);

      //-- Filter positions by symbol, type and magic number
      if(
         (symbol != ALL_SYMBOLS && symbol != selectedSymbol) || (positionType != POSITION_TYPE_SELL) ||
         (magicNumber != 0 && positionMagicNo != magicNumber)
      )
        {
         continue;
        }

      //-- Close the position
      ClosePositionByTicket(positionTicket);
     }

//-- Confirm that we have closed all the sell positions being targeted
   int breakerBreaker = 0; //-- Variable that safeguards and makes sure we are not locked in an infinite loop
   while(SymbolSellPositionsTotal(symbol, magicNumber) > 0)
     {
      breakerBreaker++;
      CloseAllSellPositions(symbol, magicNumber); //-- We still have some open sell positions, do a function callback
      Sleep(100); //-- Micro sleep to pace the execution and give some time to the trade server

      //-- Check for critical errors so that we exit the loop if we run into trouble
      if(!ErrorAdvisor(__FUNCTION__, symbol, GetLastError()) || IsStopped() || breakerBreaker > 101)
        {
         break;
        }
     }

   if(SymbolSellPositionsTotal(symbol, magicNumber) == 0)
     {
      returnThis = true;
     }
   return(returnThis);
  }



Закрытие всех позиций по магическому числу

Функция CloseAllMagicPositions() отвечает за закрытие всех открытых позиций, соответствующих указанному параметру или аргументу функции магического числа. Функция возвращает логическое значение true при успешном закрытии всех указанных позиций и false в противном случае.

bool CloseAllMagicPositions(ulong magicNumber) export
  {
//-- first check if the EA is allowed to trade
   if(!TradingIsAllowed())
     {
      return(false); //--- algo trading is disabled, exit function
     }

   bool returnThis = false;

//-- Variables to store the selected positions data
   ulong positionTicket, positionMagicNo;
   string positionSymbol;

//-- Scan for magic number specific positions and close them
   int totalOpenPositions = PositionsTotal();
   for(int x = 0; x < totalOpenPositions; x++)
     {
      //--- Get position properties
      positionTicket = PositionGetTicket(x); //-- Get ticket to select the position
      positionMagicNo = PositionGetInteger(POSITION_MAGIC);
      positionSymbol = PositionGetString(POSITION_SYMBOL);

      //-- Filter positions by magic number
      if(magicNumber == positionMagicNo)
        {
         //-- Close the position
         ClosePositionByTicket(positionTicket);
        }
     }

//-- Confirm that we have closed all the positions being targeted
   int breakerBreaker = 0; //-- Variable that safeguards and makes sure we are not locked in an infinite loop
   while(MagicPositionsTotal(magicNumber) > 0)
     {
      breakerBreaker++;
      CloseAllMagicPositions(magicNumber); //-- We still have some open positions, do a function callback
      Sleep(100); //-- Micro sleep to pace the execution and give some time to the trade server

      //-- Check for critical errors so that we exit the loop if we run into trouble
      if(!ErrorAdvisor(__FUNCTION__, positionSymbol, GetLastError()) || IsStopped() || breakerBreaker > 101)
        {
         break;
        }
     }

   if(MagicPositionsTotal(magicNumber) == 0)
     {
      returnThis = true;
     }
   return(returnThis);
  }



Закрытие всех прибыльных позиций

Функция CloseAllProfitablePositions() закрывает все прибыльные открытые позиции, которые соответствуют указанным параметрам или аргументам функции имени символа и магического числа. Функция возвращает логическое значение true при успешном закрытии всех указанных позиций и false в противном случае.

bool CloseAllProfitablePositions(string symbol = ALL_SYMBOLS, ulong magicNumber = 0) export
  {
//-- first check if the EA is allowed to trade
   if(!TradingIsAllowed())
     {
      return(false); //--- algo trading is disabled, exit function
     }

//-- Scan for profitable positions that match the specified symbol and magic number to close them
   int totalOpenPositions = PositionsTotal();
   for(int x = 0; x < totalOpenPositions; x++)
     {
      //--- Get position properties
      ulong positionTicket = PositionGetTicket(x); //-- Get ticket to select the position
      string selectedSymbol = PositionGetString(POSITION_SYMBOL);
      ulong positionMagicNo = PositionGetInteger(POSITION_MAGIC);
      double positionProfit = PositionGetDouble(POSITION_PROFIT);

      //-- Filter positions by symbol, magic number and profit
      if(
         ((symbol != ALL_SYMBOLS && symbol != selectedSymbol) || (magicNumber != 0 && positionMagicNo != magicNumber)) ||
         positionProfit <= 0
      )
        {
         continue;
        }

      //-- Close the position
      ClosePositionByTicket(positionTicket);
     }
   return(true);
  }

___


Закрытие всех прибыльных позиций на покупку

Функция CloseAllProfitableBuyPositions() закрывает все прибыльные открытые позиции на покупку, которые соответствуют указанным параметрам или аргументам функции имени символа и магического числа. Функция возвращает логическое значение true при успешном закрытии всех указанных позиций и false в противном случае.

bool CloseAllProfitableBuyPositions(string symbol = ALL_SYMBOLS, ulong magicNumber = 0) export
  {
//-- first check if the EA is allowed to trade
   if(!TradingIsAllowed())
     {
      return(false); //--- algo trading is disabled, exit function
     }

//-- Scan for profitable positions that match the specified symbol and magic number to close them
   int totalOpenPositions = PositionsTotal();
   for(int x = 0; x < totalOpenPositions; x++)
     {
      //--- Get position properties
      ulong positionTicket = PositionGetTicket(x); //-- Get ticket to select the position
      string selectedSymbol = PositionGetString(POSITION_SYMBOL);
      ulong positionMagicNo = PositionGetInteger(POSITION_MAGIC);
      double positionProfit = PositionGetDouble(POSITION_PROFIT);

      //-- Filter positions by symbol, magic number, profit and type
      if(
         ((symbol != ALL_SYMBOLS && symbol != selectedSymbol) || (magicNumber != 0 && positionMagicNo != magicNumber)) ||
         positionProfit <= 0 || PositionGetInteger(POSITION_TYPE) != POSITION_TYPE_BUY
      )
        {
         continue;
        }

      //-- Close the position
      ClosePositionByTicket(positionTicket);
     }
   return(true);
  }


Закрытие всех прибыльных позиций на продажу

Функция CloseAllProfitableSellPositions() закрывает все прибыльные открытые позиции на продажу, которые соответствуют указанным параметрам или аргументам функции имени символа и магического числа. Функция возвращает логическое значение true при успешном закрытии всех указанных позиций и false в противном случае.

bool CloseAllProfitableSellPositions(string symbol = ALL_SYMBOLS, ulong magicNumber = 0) export
  {
//-- first check if the EA is allowed to trade
   if(!TradingIsAllowed())
     {
      return(false); //--- algo trading is disabled, exit function
     }

//-- Scan for profitable positions that match the specified symbol and magic number to close them
   int totalOpenPositions = PositionsTotal();
   for(int x = 0; x < totalOpenPositions; x++)
     {
      //--- Get position properties
      ulong positionTicket = PositionGetTicket(x); //-- Get ticket to select the position
      string selectedSymbol = PositionGetString(POSITION_SYMBOL);
      ulong positionMagicNo = PositionGetInteger(POSITION_MAGIC);
      double positionProfit = PositionGetDouble(POSITION_PROFIT);

      //-- Filter positions by symbol, magic number, profit and type
      if(
         ((symbol != ALL_SYMBOLS && symbol != selectedSymbol) || (magicNumber != 0 && positionMagicNo != magicNumber)) ||
         positionProfit <= 0 || PositionGetInteger(POSITION_TYPE) != POSITION_TYPE_SELL
      )
        {
         continue;
        }

      //-- Close the position
      ClosePositionByTicket(positionTicket);
     }
   return(true);
  }


Закрытие всех убыточных позиций

Функция CloseAllLossPositions() закрывает все убыточные открытые позиции, которые соответствуют указанным параметрам или аргументам функции имени символа и магического числа. Функция возвращает логическое значение true при успешном закрытии всех указанных позиций и false в противном случае.

bool CloseAllLossPositions(string symbol = ALL_SYMBOLS, ulong magicNumber = 0) export
  {
//-- first check if the EA is allowed to trade
   if(!TradingIsAllowed())
     {
      return(false); //--- algo trading is disabled, exit function
     }

//-- Scan for loss positions that match the specified symbol and magic number and close them
   int totalOpenPositions = PositionsTotal();
   for(int x = 0; x < totalOpenPositions; x++)
     {
      //--- Get position properties
      ulong positionTicket = PositionGetTicket(x); //-- Get ticket to select the position
      string selectedSymbol = PositionGetString(POSITION_SYMBOL);
      ulong positionMagicNo = PositionGetInteger(POSITION_MAGIC);
      double positionProfit = PositionGetDouble(POSITION_PROFIT);

      //-- Filter positions by symbol, magic number and profit
      if(
         ((symbol != ALL_SYMBOLS && symbol != selectedSymbol) || (magicNumber != 0 && positionMagicNo != magicNumber)) ||
         positionProfit > 0
      )
        {
         continue;
        }

      //-- Close the position
      ClosePositionByTicket(positionTicket);
     }

   return(true);
  }


Закрытие всех убыточных позиций на покупку

Функция CloseAllLossBuyPositions() закрывает все убыточные открытые позиции на покупку, которые соответствуют указанным параметрам или аргументам функции имени символа и магического числа. Функция возвращает логическое значение true при успешном закрытии всех указанных позиций и false в противном случае.

bool CloseAllLossBuyPositions(string symbol = ALL_SYMBOLS, ulong magicNumber = 0) export
  {
//-- first check if the EA is allowed to trade
   if(!TradingIsAllowed())
     {
      return(false); //--- algo trading is disabled, exit function
     }

//-- Scan for loss positions that match the specified symbol and magic number and close them
   int totalOpenPositions = PositionsTotal();
   for(int x = 0; x < totalOpenPositions; x++)
     {
      //--- Get position properties
      ulong positionTicket = PositionGetTicket(x); //-- Get ticket to select the position
      string selectedSymbol = PositionGetString(POSITION_SYMBOL);
      ulong positionMagicNo = PositionGetInteger(POSITION_MAGIC);
      double positionProfit = PositionGetDouble(POSITION_PROFIT);

      //-- Filter positions by symbol, magic number, profit and type
      if(
         ((symbol != ALL_SYMBOLS && symbol != selectedSymbol) || (magicNumber != 0 && positionMagicNo != magicNumber)) ||
         positionProfit > 0 || PositionGetInteger(POSITION_TYPE) != POSITION_TYPE_BUY
      )
        {
         continue;
        }

      //-- Close the position
      ClosePositionByTicket(positionTicket);
     }

   return(true);
  }


Закрытие всех убыточных позиций на продажу

Функция CloseAllLossSellPositions() закрывает все убыточные открытые позиции на продажу, которые соответствуют указанным параметрам или аргументам функции имени символа и магического числа. Функция возвращает логическое значение true при успешном закрытии всех указанных позиций и false в противном случае.

bool CloseAllLossSellPositions(string symbol = ALL_SYMBOLS, ulong magicNumber = 0) export
  {
//-- first check if the EA is allowed to trade
   if(!TradingIsAllowed())
     {
      return(false); //--- algo trading is disabled, exit function
     }

//-- Scan for loss positions that match the specified symbol and magic number and close them
   int totalOpenPositions = PositionsTotal();
   for(int x = 0; x < totalOpenPositions; x++)
     {
      //--- Get position properties
      ulong positionTicket = PositionGetTicket(x); //-- Get ticket to select the position
      string selectedSymbol = PositionGetString(POSITION_SYMBOL);
      ulong positionMagicNo = PositionGetInteger(POSITION_MAGIC);
      double positionProfit = PositionGetDouble(POSITION_PROFIT);

      //-- Filter positions by symbol, magic number, profit and type
      if(
         ((symbol != ALL_SYMBOLS && symbol != selectedSymbol) || (magicNumber != 0 && positionMagicNo != magicNumber)) ||
         positionProfit > 0 || PositionGetInteger(POSITION_TYPE) != POSITION_TYPE_SELL
      )
        {
         continue;
        }

      //-- Close the position
      ClosePositionByTicket(positionTicket);
     }

   return(true);
  }  



Функции статуса позиции

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


Получение данные о позициях

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

Разместим следующие объявления глобальных переменных в верхней части нашей библиотеки под объявлениями глобальных переменных структур данных запроса и результата торговых операций.

string accountCurrency = AccountInfoString(ACCOUNT_CURRENCY);

//-- Position status global variables
//-------------------------------------------------------------------------------------------------------------------
int accountBuyPositionsTotal = 0, accountSellPositionsTotal = 0,
    symbolPositionsTotal = 0, symbolBuyPositionsTotal = 0, symbolSellPositionsTotal = 0,
    magicPositionsTotal = 0, magicBuyPositionsTotal = 0, magicSellPositionsTotal = 0;
double accountPositionsVolumeTotal = 0.0, accountBuyPositionsVolumeTotal = 0.0, accountSellPositionsVolumeTotal = 0.0,
       accountBuyPositionsProfit = 0.0, accountSellPositionsProfit = 0.0,
       symbolPositionsVolumeTotal = 0.0, symbolBuyPositionsVolumeTotal = 0.0,
       symbolSellPositionsVolumeTotal = 0.0, symbolPositionsProfit = 0.0,
       symbolBuyPositionsProfit = 0.0, symbolSellPositionsProfit = 0.0,
       magicPositionsVolumeTotal = 0.0, magicBuyPositionsVolumeTotal = 0.0,
       magicSellPositionsVolumeTotal = 0.0, magicPositionsProfit = 0.0,
       magicBuyPositionsProfit = 0.0, magicSellPositionsProfit = 0.0;

Обновим и сохраним данные о статусе позиции в функции GetPositionsData() ниже.

void GetPositionsData(string symbol, ulong magicNumber)
  {
//-- Reset the acount open positions status
   accountBuyPositionsTotal = 0;
   accountSellPositionsTotal = 0;
   accountPositionsVolumeTotal = 0.0;
   accountBuyPositionsVolumeTotal = 0.0;
   accountSellPositionsVolumeTotal = 0.0;
   accountBuyPositionsProfit = 0.0;
   accountSellPositionsProfit = 0.0;

//-- Reset the EA's magic open positions status
   magicPositionsTotal = 0;
   magicBuyPositionsTotal = 0;
   magicSellPositionsTotal = 0;
   magicPositionsVolumeTotal = 0.0;
   magicBuyPositionsVolumeTotal = 0.0;
   magicSellPositionsVolumeTotal = 0.0;
   magicPositionsProfit = 0.0;
   magicBuyPositionsProfit = 0.0;
   magicSellPositionsProfit = 0.0;

//-- Reset the symbol open positions status
   symbolPositionsTotal = 0;
   symbolBuyPositionsTotal = 0;
   symbolSellPositionsTotal = 0;
   symbolPositionsVolumeTotal = 0.0;
   symbolBuyPositionsVolumeTotal = 0.0;
   symbolSellPositionsVolumeTotal = 0.0;
   symbolPositionsProfit = 0.0;
   symbolBuyPositionsProfit = 0.0;
   symbolSellPositionsProfit = 0.0;

//-- Update and save the open positions status with realtime data
   int totalOpenPositions = PositionsTotal();
   if(totalOpenPositions > 0)
     {
      //-- Scan for symbol and magic number specific positions and save their status
      for(int x = 0; x < totalOpenPositions; x++)
        {
         //--- Get position properties
         ulong  positionTicket = PositionGetTicket(x); //-- Get ticket to select the position
         string selectedSymbol = PositionGetString(POSITION_SYMBOL);
         ulong positionMagicNo = PositionGetInteger(POSITION_MAGIC);

         //-- Filter positions by magic number
         if(magicNumber != 0 && positionMagicNo != magicNumber)
           {
            continue;
           }

         //-- Save the account positions status first
         accountPositionsVolumeTotal += PositionGetDouble(POSITION_VOLUME);

         if(PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY)
           {
            //-- Account properties
            ++accountBuyPositionsTotal;
            accountBuyPositionsVolumeTotal += PositionGetDouble(POSITION_VOLUME);
            accountBuyPositionsProfit += PositionGetDouble(POSITION_PROFIT);
           }
         else //-- POSITION_TYPE_SELL
           {
            //-- Account properties
            ++accountSellPositionsTotal;
            accountSellPositionsVolumeTotal += PositionGetDouble(POSITION_VOLUME);
            accountSellPositionsProfit += PositionGetDouble(POSITION_PROFIT);
           }

         //-- Filter positions openend by EA and save their status
         if(
            PositionGetInteger(POSITION_REASON) == POSITION_REASON_EXPERT &&
            positionMagicNo == magicNumber
         )
           {
            ++magicPositionsTotal;
            magicPositionsProfit += PositionGetDouble(POSITION_PROFIT);
            magicPositionsVolumeTotal += PositionGetDouble(POSITION_VOLUME);
            if(PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY)
              {
               //-- Magic properties
               ++magicBuyPositionsTotal;
               magicBuyPositionsProfit += PositionGetDouble(POSITION_PROFIT);
               magicBuyPositionsVolumeTotal += PositionGetDouble(POSITION_VOLUME);
              }
            else //-- POSITION_TYPE_SELL
              {
               //-- Magic properties
               ++magicSellPositionsTotal;
               magicSellPositionsProfit += PositionGetDouble(POSITION_PROFIT);
               magicSellPositionsVolumeTotal += PositionGetDouble(POSITION_VOLUME);
              }
           }

         //-- Filter positions by symbol
         if(symbol == ALL_SYMBOLS || selectedSymbol == symbol)
           {
            ++symbolPositionsTotal;
            symbolPositionsVolumeTotal += PositionGetDouble(POSITION_VOLUME);
            symbolPositionsProfit += PositionGetDouble(POSITION_PROFIT);
            if(PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY)
              {
               ++symbolBuyPositionsTotal;
               symbolBuyPositionsVolumeTotal += PositionGetDouble(POSITION_VOLUME);
               symbolBuyPositionsProfit += PositionGetDouble(POSITION_PROFIT);
              }
            else //-- POSITION_TYPE_SELL
              {
               ++symbolSellPositionsTotal;
               symbolSellPositionsVolumeTotal += PositionGetDouble(POSITION_VOLUME);
               symbolSellPositionsProfit += PositionGetDouble(POSITION_PROFIT);
              }
           }
        }
     }
  }

Чтобы получить доступ к различным свойствам статуса позиции, которые мы зафиксировали и сохранили в глобальных переменных выше, нам необходимо создать простые экспортируемые функции, которые могут быть доступны из внешней базы кодов. Добавим следующие функции.


Buy Positions Total

Возвращает целое значение общего количества всех открытых позиций на покупку на счете.

int BuyPositionsTotal() export
  {
   GetPositionsData(ALL_SYMBOLS, 0);
   return(accountBuyPositionsTotal);
  }

Sell Positions Total

Возвращает целое значение общего количества всех открытых позиций на продажу на счете.

int SellPositionsTotal() export
  {
   GetPositionsData(ALL_SYMBOLS, 0);
   return(accountSellPositionsTotal);
  }


Positions Total Volume

Возвращает double-значение общего объема/лота/количества всех открытых позиций на счете.

double PositionsTotalVolume() export
  {
   GetPositionsData(ALL_SYMBOLS, 0);
   return(accountPositionsVolumeTotal);
  }


Buy Positions Total Volume

Возвращает double-значение общего объема/лота/количества всех открытых позиций на покупку на счете.

double BuyPositionsTotalVolume() export
  {
   GetPositionsData(ALL_SYMBOLS, 0);
   return(accountBuyPositionsVolumeTotal);
  }


Sell Positions Total Volume

Возвращает double-значение общего объема/лота/количества всех открытых позиций на продажу на счете.

double SellPositionsTotalVolume() export
  {
   GetPositionsData(ALL_SYMBOLS, 0);
   return(accountSellPositionsVolumeTotal);
  }


Buy Positions Profit

Возвращает double-значение общей прибыли всех открытых позиций на покупку на счете.

double BuyPositionsProfit() export
  {
   GetPositionsData(ALL_SYMBOLS, 0);
   return(accountBuyPositionsProfit);
  }


Sell Positions Profit

Возвращает double-значение общей прибыли всех открытых позиций на продажу на счете.

double SellPositionsProfit() export
  {
   GetPositionsData(ALL_SYMBOLS, 0);
   return(accountSellPositionsProfit);
  }


Magic Positions Total

Возвращает целое число общего количества всех открытых позиций для указанного магического числа на счете.

int MagicPositionsTotal(ulong magicNumber) export
  {
   GetPositionsData(ALL_SYMBOLS, magicNumber);
   return(magicPositionsTotal);
  }


Magic Buy Positions Total

Возвращает целое число общего количества всех открытых позиций на покупку для указанного магического числа на счете.

int MagicBuyPositionsTotal(ulong magicNumber) export
  {
   GetPositionsData(ALL_SYMBOLS, magicNumber);
   return(magicBuyPositionsTotal);
  }


Magic Sell Positions Total

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

int MagicSellPositionsTotal(ulong magicNumber) export
  {
   GetPositionsData(ALL_SYMBOLS, magicNumber);
   return(magicSellPositionsTotal);
  }


Magic Positions Total Volume

Возвращает double-значение общего объема/лота/количества всех открытых позиций для указанного магического числа на счете.

double MagicPositionsTotalVolume(ulong magicNumber) export
  {
   GetPositionsData(ALL_SYMBOLS, magicNumber);
   return(magicPositionsVolumeTotal);
  }


Magic Buy Positions Total Volume

Возвращает double-значение общего объема/лота/количества всех открытых позиций на покупку для указанного магического числа на счете.

double MagicBuyPositionsTotalVolume(ulong magicNumber) export
  {
   GetPositionsData(ALL_SYMBOLS, magicNumber);
   return(magicBuyPositionsVolumeTotal);
  }


Magic Sell Positions Total Volume

Возвращает double-значение общего объема/лота/количества всех открытых позиций на продажу для указанного магического числа на счете.

double MagicSellPositionsTotalVolume(ulong magicNumber) export
  {
   GetPositionsData(ALL_SYMBOLS, magicNumber);
   return(magicSellPositionsVolumeTotal);
  }


Magic Positions Profit

Возвращает double-значение общей прибыли всех открытых позиций для указанного магического числа на счете.

double MagicPositionsProfit(ulong magicNumber) export
  {
   GetPositionsData(ALL_SYMBOLS, magicNumber);
   return(magicPositionsProfit);
  }


Magic Buy Positions Profit

Возвращает double-значение общей прибыли всех открытых позиций на покупку для указанного магического числа на счете.

double MagicBuyPositionsProfit(ulong magicNumber) export
  {
   GetPositionsData(ALL_SYMBOLS, magicNumber);
   return(magicBuyPositionsProfit);
  }


Magic Sell Positions Profit

Возвращает double-значение общей прибыли всех открытых позиций на продажу для указанного магического числа на счете.

double MagicSellPositionsProfit(ulong magicNumber) export
  {
   GetPositionsData(ALL_SYMBOLS, magicNumber);
   return(magicSellPositionsProfit);
  }


Symbol Positions Total

Возвращает целое значение общего количества всех открытых позиций по указанному символу на счете.

int SymbolPositionsTotal(string symbol, ulong magicNumber) export
  {
   GetPositionsData(symbol, magicNumber);
   return(symbolPositionsTotal);
  }


Symbol Buy Positions Total

Возвращает целое число общего количества всех открытых позиций на покупку для указанного символа на счете.

int SymbolBuyPositionsTotal(string symbol, ulong magicNumber) export
  {
   GetPositionsData(symbol, magicNumber);
   return(symbolBuyPositionsTotal);
  }


Symbol Sell Positions Total

Возвращает целое число общего количества всех открытых позиций на продажу по указанному символу на счете.

int SymbolSellPositionsTotal(string symbol, ulong magicNumber) export
  {
   GetPositionsData(symbol, magicNumber);
   return(symbolSellPositionsTotal);
  }


Symbol Positions Total Volume

Возвращает double-значение общего объема/лота/количества всех открытых позиций по указанному символу на счете.

double SymbolPositionsTotalVolume(string symbol, ulong magicNumber) export
  {
   GetPositionsData(symbol, magicNumber);
   return(symbolPositionsVolumeTotal);
  }


Symbol Buy Positions Total Volume

Возвращает double-значение общего объема/лота/количества всех открытых позиций на покупку для указанного символа на счете.

double SymbolBuyPositionsTotalVolume(string symbol, ulong magicNumber) export
  {
   GetPositionsData(symbol, magicNumber);
   return(symbolBuyPositionsVolumeTotal);
  }


Symbol Sell Positions Total Volume

Возвращает double-значение общего объема/лота/количества всех открытых позиций на продажу для указанного символа на счете.

double SymbolSellPositionsTotalVolume(string symbol, ulong magicNumber) export
  {
   GetPositionsData(symbol, magicNumber);
   return(symbolSellPositionsVolumeTotal);
  }


Symbol Positions Profit

Возвращает double-значение общей прибыли всех открытых позиций по указанному символу на счете.

double SymbolPositionsProfit(string symbol, ulong magicNumber) export
  {
   GetPositionsData(symbol, magicNumber);
   return(NormalizeDouble(symbolPositionsProfit, 2));
  }


Symbol Buy Positions Profit

Возвращает double-значение общей прибыли всех открытых позиций на покупку по указанному символу на счете.

double SymbolBuyPositionsProfit(string symbol, ulong magicNumber) export
  {
   GetPositionsData(symbol, magicNumber);
   return(NormalizeDouble(symbolBuyPositionsProfit, 2));
  }


Symbol Sell Positions Profit

Возвращает double-значение общей прибыли всех открытых позиций на продажу по указанному символу на счете.

double SymbolSellPositionsProfit(string symbol, ulong magicNumber) export
  {
   GetPositionsData(symbol, magicNumber);
   return(NormalizeDouble(symbolSellPositionsProfit, 2));
  }


Account Positions Status

Возвращает предварительно отформатированную строку, содержащую статус позиций счета, который можно вывести в журнал или отобразить в комментариях к графику. Функция принимает один логический параметр formatForComment. Если formatForComment равен true, функция форматирует данные для отображения в окне графика. При false данные форматируются для отображения на вкладке журналов советника.

string AccountPositionsStatus(bool formatForComment) export
  {
   GetPositionsData(ALL_SYMBOLS, 0); //-- Update the position status variables before we display their data
   string spacer = "";
   if(formatForComment) //-- Add some formating space for the chart comment string
     {
      spacer = "                                        ";
     }
   string accountPositionsStatus = "\r\n" + spacer + "|---------------------------------------------------------------------------\r\n";
   accountPositionsStatus += spacer + "| " + (string)AccountInfoInteger(ACCOUNT_LOGIN) + " - ACCOUNT POSTIONS STATUS \r\n";
   accountPositionsStatus += spacer + "|---------------------------------------------------------------------------\r\n";
   accountPositionsStatus += spacer + "|     Total Open:   " + (string)PositionsTotal() + "\r\n";
   accountPositionsStatus += spacer + "|     Total Volume: " + (string)accountPositionsVolumeTotal + "\r\n";
   accountPositionsStatus += spacer + "|     Total Profit: " +
   (string)(NormalizeDouble(AccountInfoDouble(ACCOUNT_PROFIT), 2)) + accountCurrency + "\r\n";
   accountPositionsStatus += spacer + "|------------------------------------------------------------------\r\n";
   accountPositionsStatus += spacer + "| BUY POSITIONS: \r\n";
   accountPositionsStatus += spacer + "|     Total Open:   " + (string)accountBuyPositionsTotal + "\r\n";
   accountPositionsStatus += spacer + "|     Total Volume: " + (string)accountBuyPositionsVolumeTotal + "\r\n";
   accountPositionsStatus += spacer + "|     Total Profit: " + (string)(NormalizeDouble(accountBuyPositionsProfit, 2)) +
   accountCurrency + "\r\n";
   accountPositionsStatus += spacer + "|------------------------------------------------------------------\r\n";
   accountPositionsStatus += spacer + "| SELL POSITIONS: \r\n";
   accountPositionsStatus += spacer + "|     Total Open:   " + (string)accountSellPositionsTotal + "\r\n";
   accountPositionsStatus += spacer + "|     Total Volume: " + (string)accountSellPositionsVolumeTotal + "\r\n";
   accountPositionsStatus += spacer + "|     Total Profit: " + (string)(NormalizeDouble(accountSellPositionsProfit, 2)) +
   accountCurrency + "\r\n";
   accountPositionsStatus += spacer + "|---------------------------------------------------------------------------\r\n";
   accountPositionsStatus += spacer + "\r\n";
   return(accountPositionsStatus);
  }


Magic Positions Status

Возвращает предварительно отформатированную строку, содержащую статус позиций счета по магическому числу, который можно вывести в журнал или отобразить в комментариях к графику. Функция принимает два аргумента или параметра. Неподписанный long magicNumber и логический formatForComment. Если formatForComment равен true, функция форматирует данные для отображения в окне графика. При false данные форматируются для отображения в логах советниках.

string MagicPositionsStatus(ulong magicNumber, bool formatForComment) export
  {
   GetPositionsData(ALL_SYMBOLS, magicNumber); //-- Update the position status variables before we display their data
   string spacer = "";
   if(formatForComment) //-- Add some formating space for the chart comment string
     {
      spacer = "                                        ";
     }
   string magicPositionsStatus = "\r\n" + spacer + "|---------------------------------------------------------------------------\r\n";
   magicPositionsStatus += spacer + "| " + (string)magicNumber + " - MAGIC POSTIONS STATUS \r\n";
   magicPositionsStatus += spacer + "|---------------------------------------------------------------------------\r\n";
   magicPositionsStatus += spacer + "|     Total Open:   " + (string)magicPositionsTotal + "\r\n";
   magicPositionsStatus += spacer + "|     Total Volume: " + (string)magicPositionsVolumeTotal + "\r\n";
   magicPositionsStatus += spacer + "|     Total Profit: " +
   (string)(NormalizeDouble(magicPositionsProfit, 2)) + accountCurrency + "\r\n";
   magicPositionsStatus += spacer + "|------------------------------------------------------------------\r\n";
   magicPositionsStatus += spacer + "| BUY POSITIONS: \r\n";
   magicPositionsStatus += spacer + "|     Total Open:   " + (string)magicBuyPositionsTotal + "\r\n";
   magicPositionsStatus += spacer + "|     Total Volume: " + (string)magicBuyPositionsVolumeTotal + "\r\n";
   magicPositionsStatus += spacer + "|     Total Profit: " + (string)(NormalizeDouble(magicBuyPositionsProfit, 2)) +
   accountCurrency + "\r\n";
   magicPositionsStatus += spacer + "|------------------------------------------------------------------\r\n";
   magicPositionsStatus += spacer + "| SELL POSITIONS: \r\n";
   magicPositionsStatus += spacer + "|     Total Open:   " + (string)magicSellPositionsTotal + "\r\n";
   magicPositionsStatus += spacer + "|     Total Volume: " + (string)magicSellPositionsVolumeTotal + "\r\n";
   magicPositionsStatus += spacer + "|     Total Profit: " + (string)(NormalizeDouble(magicSellPositionsProfit, 2)) +
   accountCurrency + "\r\n";
   magicPositionsStatus += spacer + "|---------------------------------------------------------------------------\r\n";
   magicPositionsStatus += spacer + "\r\n";
   return(magicPositionsStatus);
  }


Symbol Positions Status

Возвращает предварительно отформатированную строку, содержащую статус позиций символа, который можно вывести в журнал или отобразить в комментариях к графику. Функция принимает три аргумента или параметра. Строковый symbol, неподписанный long magicNumber и логический formatForComment. Если логическое значение formatForComment равно true, функция форматирует данные для отображения в окне графика. При false данные форматируются для отображения в логах советника.

string SymbolPositionsStatus(string symbol, ulong magicNumber, bool formatForComment) export
  {
   GetPositionsData(symbol, magicNumber); //-- Update the position status variables before we display their data
   string spacer = "";
   if(formatForComment) //-- Add some formating space for the chart comment string
     {
      spacer = "                                        ";
     }
   string symbolPositionsStatus = "\r\n" + spacer + "|---------------------------------------------------------------------------\r\n";
   symbolPositionsStatus += spacer + "| " + symbol + " - SYMBOL POSTIONS STATUS \r\n";
   symbolPositionsStatus += spacer + "|---------------------------------------------------------------------------\r\n";
   symbolPositionsStatus += spacer + "|     Total Open:   " + (string)symbolPositionsTotal + "\r\n";
   symbolPositionsStatus += spacer + "|     Total Volume: " + (string)symbolPositionsVolumeTotal + "\r\n";
   symbolPositionsStatus += spacer + "|     Total Profit: " +
   (string)(NormalizeDouble(symbolPositionsProfit, 2)) + accountCurrency + "\r\n";
   symbolPositionsStatus += spacer + "|------------------------------------------------------------------\r\n";
   symbolPositionsStatus += spacer + "| BUY POSITIONS: \r\n";
   symbolPositionsStatus += spacer + "|     Total Open:   " + (string)symbolBuyPositionsTotal + "\r\n";
   symbolPositionsStatus += spacer + "|     Total Volume: " + (string)symbolBuyPositionsVolumeTotal + "\r\n";
   symbolPositionsStatus += spacer + "|     Total Profit: " + (string)(NormalizeDouble(symbolBuyPositionsProfit, 2)) +
   accountCurrency + "\r\n";
   symbolPositionsStatus += spacer + "|------------------------------------------------------------------\r\n";
   symbolPositionsStatus += spacer + "| SELL POSITIONS: \r\n";
   symbolPositionsStatus += spacer + "|     Total Open:   " + (string)symbolSellPositionsTotal + "\r\n";
   symbolPositionsStatus += spacer + "|     Total Volume: " + (string)symbolSellPositionsVolumeTotal + "\r\n";
   symbolPositionsStatus += spacer + "|     Total Profit: " + (string)(NormalizeDouble(symbolSellPositionsProfit, 2)) +
   accountCurrency + "\r\n";
   symbolPositionsStatus += spacer + "|---------------------------------------------------------------------------\r\n";
   symbolPositionsStatus += spacer + "\r\n";
   return(symbolPositionsStatus);
  }



Как импортировать и внедрить EX5-библиотеку для управления позициями

Мы разработали комплексную EX5-библиотеку для управления позициями, содержащую все основные функции для операций с позициями, извлечения статуса и модулей отображения. Теперь пришло время задокументировать и объяснить, как эффективно импортировать и использовать эту библиотеку в любом проекте MQL5.

Чтобы упростить процесс внедрения, давайте начнем с описания всех функций или модулей в библиотеке управления позициями, а также приведем несколько примеров использования реального кода. Это даст некоторое представление о компонентах, включенных в бинарный файл PositionsManager.ex5.

Документация EX5-библиотеки для управления позициями

Описание прототипа функции Описание Пример использования
bool ErrorAdvisor(
   string callingFunc, 
   string symbol, 
   int tradeServerErrorCode
);
Управляет ошибками торгового сервера и времени выполнения при обработке позиций и ордеров. Возвращает true, если ошибка устранима и запрос на торговлю можно отправить повторно, и false, если ошибка критическая и отправку запроса следует прекратить.
ResetLastError(); //-- Reset and clear the last error
//--------------------------------------------------------------
//-- Insert code to send the order request to the trade server
//--------------------------------------------------------------
string symbol = _Symbol; //Symbol being traded
int retcode = tradeResult.retcode;//Trade Request Structure (MqlTradeRequest)
if(!ErrorAdvisor(__FUNCTION__, symbol, retcode)
  {
//Critical error found
//Order can not be executed. Exit function or log this error
  }
bool TradingIsAllowed();
Проверяет, предоставлено ли советнику разрешение на совершение сделок пользователем, торговым сервером и брокером. 
if(!TradingIsAllowed())
  {
   //--- algo trading is disabled, exit function
   return(false);
  }

bool OpenBuyPosition(
   ulong magicNumber,
   string symbol,
   double lotSize,
   int sl, int tp,
   string positionComment
);
Открывает новую позицию на покупку, соответствующую указанным параметрам.
ulong magicNo = 123;
string symbol = _Symbol;
double lotSize = SymbolInfoDouble(symbol, SYMBOL_VOLUME_MIN);
int sl = 500; //-- pips
int tp = 1000; //-- pips
string comment = "Buy position";
OpenBuyPosition(magicNo, symbol, lotSize, sl, tp, comment);
bool OpenSellPosition(
   ulong magicNumber,
   string symbol,
   double lotSize,
   int sl,
   int tp,
   string positionComment
);
Открывает новую позицию на продажу, соответствующую указанным параметрам.
ulong magicNo = 123;
string symbol = _Symbol;
double lotSize = SymbolInfoDouble(symbol, SYMBOL_VOLUME_MIN);
int sl = 500; //-- pips
int tp = 1000; //-- pips
string comment = "Sell position";
OpenSellPosition(magicNo, symbol, lotSize, sl, tp, comment);
bool SetSlTpByTicket(
   ulong positionTicket,
   int sl,
   int tp
);
Устанавливает стоп-лосс для позиции, соответствующей указанному тикету.
int sl = 500, int tp = 1000; //-- pips
int totalOpenPostions = PositionsTotal();
for(int x = 0; x < totalOpenPostions; x++)
{
   ulong positionTicket = PositionGetTicket(x);
   if(positionTicket > 0)
   {
      SetSlTpByTicket(positionTicket, sl, tp);
   }   
}
bool ClosePositionByTicket(
   ulong positionTicket
);
Закрывает позицию, соответствующую указанному тикету.
//-- Example to close all open positions
for(int x = 0; x < totalOpenPostions; x++)
{
   ulong positionTicket = PositionGetTicket(x);
   if(positionTicket > 0)
   {
      ClosePositionByTicket(positionTicket);
   }   
}
bool SetTrailingStopLoss(
   ulong positionTicket,
   int trailingStopLoss
);
Устанавливает трейлинг-стоп-лосс для позиции, соответствующей указанному тикету. Эта функция должна выполняться на каждом тике в функции OnTick() для обновления трейлинг-стоп-лосса в реальном времени.
//-- Execute this on every tick
//-- Example to set 500 pips trailing stop loss for all positions
int trailingStopLoss = 500; //-- 500 pips trailing stop loss
for(int x = 0; x < totalOpenPostions; x++)
{
   ulong positionTicket = PositionGetTicket(x);
   if(positionTicket > 0)
   {
      SetTrailingStopLoss(positionTicket, trailingStopLoss);
   }   
}
bool CloseAllPositions(
   string symbol, 
   ulong magicNumber
);
Универсальная функция, которая закрывает все открытые позиции, соответствующие указанным параметрам.
//Close all positions
CloseAllPositions("", 0);

//Only close all positions matching a magic number value of 1
CloseAllPositions("", 1);

//Only close all current symbol positions
CloseAllPositions(_Symbol, 0);
bool CloseAllPositions();

Закрывает все открытые позиции.
//Close all open positions in the account
CloseAllPositions();
 
bool CloseAllBuyPositions(
   string symbol, 
   ulong magicNumber
);
Закрывает все позиции на покупку, соответствующие указанным параметрам.
//Close all buy positions for the current symbol
CloseAllBuyPositions(_Symbol, 0);

//Close all buy positions matching magic number 1 for all symbols
CloseAllBuyPositions("", 1);

//Close all buy positions in the account
CloseAllBuyPositions("", 0);
 
bool CloseAllSellPositions(
   string symbol,
   ulong magicNumber
);

Закрывает все позиции на продажу, соответствующие указанным параметрам.  
//Close all sell positions for the current symbol
CloseAllSellPositions(_Symbol, 0);

//Close all sell positions matching magic number 1 for all symbols
CloseAllSellPositions("", 1);

//Close all sell positions in the account
CloseAllSellPositions("", 0);
 
bool CloseAllMagicPositions(
   ulong magicNumber
);
Закрывает все позиции, соответствующие указанному магическому числу.  
//Close all positions matching magic number 1
CloseAllMagicPositions(1);

//Close all positions in the account
CloseAllMagicPositions(0);
 
bool CloseAllProfitablePositions(
   string symbol,
   ulong magicNumber
);

Закрывает все прибыльные позиции, соответствующие указанным параметрам.
//Close all profitable positions for the current symbol
CloseAllProfitablePositions(_Symbol, 0);

//Close all profitable positions matching magic number 1 for all symbols
CloseAllProfitablePositions("", 1);

//Close all profitable positions in the account
CloseAllProfitablePositions("", 0);
 
bool CloseAllProfitableBuyPositions(
   string symbol,
   ulong magicNumber
);
Закрывает все прибыльные позиции на покупку, соответствующие указанным параметрам.
//Close all profitable buy positions for the current symbol
CloseAllProfitableBuyPositions(_Symbol, 0);

//Close all profitable buy positions matching magic number 1 for all symbols
CloseAllProfitableBuyPositions("", 1);

//Close all profitable buy positions in the account
CloseAllProfitableBuyPositions("", 0);
 
bool CloseAllProfitableSellPositions(
   string symbol,
   ulong magicNumber
);
Закрывает все прибыльные позиции на продажу, соответствующие указанным параметрам.
//Close all profitable sell positions for the current symbol
CloseAllProfitableSellPositions(_Symbol, 0);

//Close all profitable sell positions matching magic number 1 for all symbols
CloseAllProfitableSellPositions("", 1);

//Close all profitable sell positions in the account
CloseAllProfitableSellPositions("", 0);
 
bool CloseAllLossPositions(
   string symbol,
   ulong magicNumber
);

Закрывает все убыточные позиции, соответствующие указанным параметрам.
//Close all loss positions for the current symbol
CloseAllLossPositions(_Symbol, 0);

//Close all loss positions matching magic number 1 for all symbols
CloseAllLossPositions("", 1);

//Close all loss positions in the account
CloseAllLossPositions("", 0);
 
bool CloseAllLossBuyPositions(
   string symbol,
   ulong magicNumber
);
Закрывает все убыточные позиции на покупку, соответствующие указанным параметрам.
//Close all loss buy positions for the current symbol
CloseAllLossBuyPositions(_Symbol, 0);

//Close all loss buy positions matching magic number 1 for all symbols
CloseAllLossBuyPositions("", 1);

//Close all loss buy positions in the account
CloseAllLossBuyPositions("", 0);

 
bool CloseAllLossSellPositions(
   string symbol,
   ulong magicNumber
);

Закрывает все убыточные позиции на продажу, соответствующие указанным параметрам.
//Close all loss sell positions for the current symbol
CloseAllLossSellPositions(_Symbol, 0);

//Close all loss sell positions matching magic number 1 for all symbols
CloseAllLossSellPositions("", 1);

//Close all loss sell positions in the account
CloseAllLossSellPositions("", 0);
 
int BuyPositionsTotal();

Возвращает количество открытых позиций на покупку.  
//Get the total number of open buy positions in the account
BuyPositionsTotal();

 
int SellPositionsTotal();

Возвращает количество открытых позиций на продажу.
//Get the total number of open sell positions in the account
SellPositionsTotal();

 
double PositionsTotalVolume();

Возвращает общий объем всех открытых позиций.
//Get the total volume of all open positions in the account
PositionsTotalVolume();

 
double BuyPositionsTotalVolume();
Возвращает общий объем всех открытых позиций на покупку.
//Get the total volume of all open buy positions in the account
BuyPositionsTotalVolume();

 
double SellPositionsTotalVolume();

Возвращает общий объем всех открытых позиций на продажу.
//Get the total volume of all open sell positions in the account
SellPositionsTotalVolume();

 
double BuyPositionsProfit();
Возвращает общую прибыль всех открытых позиций на покупку.
//Get the total profit of all open buy positions in the account
BuyPositionsProfit();

 
double SellPositionsProfit();

Возвращает общую прибыль всех открытых позиций на продажу.
//Get the total profit of all open sell positions in the account
SellPositionsProfit();

 
int MagicPositionsTotal(
   ulong magicNumber
);

Возвращает количество открытых позиций, соответствующих указанному магическому числу.
//Get the total number of open positions matching magic number 1
MagicPositionsTotal(1);
 
int MagicBuyPositionsTotal(
   ulong magicNumber
);

Возвращает количество открытых позиций на покупку, соответствующих указанному магическому числу.
//Get the total number of open buy positions matching magic number 1
MagicBuyPositionsTotal(1);
 
int MagicSellPositionsTotal(
   ulong magicNumber
);

Возвращает количество открытых позиций на продажу, соответствующих указанному магическому числу.
//Get the total number of open sell positions matching magic number 1
MagicSellPositionsTotal(1);
 
double MagicPositionsTotalVolume(
   ulong magicNumber
);

Возвращает общий объем всех открытых позиций, соответствующих указанному магическому числу.
//Get the total volume of open positions matching magic number 1
MagicPositionsTotalVolume(1);
 
double MagicBuyPositionsTotalVolume(
   ulong magicNumber
);

Возвращает общий объем всех открытых позиций на покупку, соответствующих указанному магическому числу.
//Get the total volume of open buy positions matching magic number 1
MagicBuyPositionsTotalVolume(1);
 
double MagicSellPositionsTotalVolume(
   ulong magicNumber
);

Возвращает общий объем всех открытых позиций на продажу, соответствующих указанному магическому числу.
 
//Get the total volume of open sell positions matching magic number 1
MagicSellPositionsTotalVolume(1);
 
double MagicPositionsProfit(
   ulong magicNumber
);

Возвращает общую прибыль всех открытых позиций, соответствующих указанному магическому числу.
//Get the total profit of open positions matching magic number 1
MagicPositionsProfit(1);
 
double MagicBuyPositionsProfit(
   ulong magicNumber
);
Возвращает общую прибыль всех открытых позиций на покупку, соответствующих указанному магическому числу.
//Get the total profit of open buy positions matching magic number 1
MagicBuyPositionsProfit(1);
 
double MagicSellPositionsProfit(
   ulong magicNumber
);
Возвращает общую прибыль всех открытых позиций на продажу, соответствующих указанному магическому числу.
//Get total profit of sell positions matching magic number 1
MagicSellPositionsProfit(1);

 
int SymbolPositionsTotal(
   string symbol,
   ulong magicNumber
);

Возвращает общее количество всех открытых позиций, соответствующих указанному символу и магическому числу.
//Get total number of positions matching symbol and magic number 1
MagicPositionsTotal(_Symbol, 1);
 
int SymbolBuyPositionsTotal(
   string symbol,
   ulong magicNumber
);
Возвращает общее количество всех открытых позиций на покупку, соответствующих указанному символу и магическому числу.
//Get total number of buy positions matching symbol and magic number 1
SymbolBuyPositionsTotal(_Symbol, 1);

 
int SymbolSellPositionsTotal(
   string symbol,
   ulong magicNumber
);
Возвращает общее количество всех открытых позиций на продажу, соответствующих указанному символу и магическому числу.
//Get total number of sell positions matching symbol and magic number 1
SymbolSellPositionsTotal(_Symbol, 1);

 
double SymbolPositionsTotalVolume(
   string symbol,
   ulong magicNumber
);

Возвращает общий объем всех открытых позиций, соответствующих указанному символу и магическому числу.
//Get the volume of positions matching symbol and magic number 1
SymbolPositionsTotalVolume(_Symbol, 1);

 
double SymbolBuyPositionsTotalVolume(
   string symbol,
   ulong magicNumber
);

Возвращает общий объем всех открытых позиций на покупку, соответствующих указанному символу и магическому числу.
//Get the volume of buy positions matching symbol and magic number 1
SymbolBuyPositionsTotalVolume(_Symbol, 1);
 
double SymbolSellPositionsTotalVolume(
   string symbol,
   ulong magicNumber
);

Возвращает общий объем всех открытых позиций на продажу, соответствующих указанному символу и магическому числу.
//Get the volume of sell positions matching symbol and magic number 1
SymbolSellPositionsTotalVolume(_Symbol, 1);
 
double SymbolPositionsProfit(
   string symbol,
   ulong magicNumber
);

Возвращает общую прибыль всех открытых позиций, соответствующих указанному символу и магическому числу.
//Get the profit of all positions matching symbol and magic number 1
SymbolPositionsProfit(_Symbol, 1);
 
double SymbolBuyPositionsProfit(
   string symbol,
   ulong magicNumber
);
Возвращает общую прибыль всех открытых позиций на покупку, соответствующих указанному символу и магическому числу.
//Get the profit of all buy positions matching symbol and magic number 1
SymbolBuyPositionsProfit(_Symbol, 1);

 
double SymbolSellPositionsProfit(
   string symbol,
   ulong magicNumber
);
Возвращает общую прибыль всех открытых позиций на продажу, соответствующих указанному символу и магическому числу.
//Get the profit of all sell positions matching symbol and magic number 1
SymbolSellPositionsProfit(_Symbol, 1);
 
string AccountPositionsStatus(
   bool formatForComment
);
Выводит на экран статус всех открытых позиций на графике символов или на вкладке "Эксперты" в MetaTrader 5 в виде строки.  
//Print the status of all open positions formatted for the chart comments
AccountPositionsStatus(true);

//Print the status of all open positions formatted for the Experts tab
AccountPositionsStatus(false);
 
string MagicPositionsStatus(
   ulong magicNumber,
   bool formatForComment
);

Выводит на экран статус всех открытых позиций, соответствующих указанному магическому числу, на графике символов или на вкладке "Эксперты" в MetaTrader 5 в виде строки.
//Print the status of all open positions matching
//the magic number 1 formatted for the chart comments
MagicPositionsStatus(1, true);

//Print the status of all open positions matching
//the magic number 1 formatted for the Experts tab
MagicPositionsStatus(1, false);
 
string SymbolPositionsStatus(
   string symbol,
   ulong magicNumber,
   bool formatForComment
);
Выводит на экран статус всех открытых позиций, соответствующих указанному символу и магическому числу, на графике символов или на вкладке "Эксперты" в MetaTrader 5 в виде строки.
//Print the status of all open positions matching
//the symbol and magic number 1 formatted for the chart comments
SymbolPositionsStatus(_Symbol, 1, true);

//Print the status of all open positions matching
//the symbol and magic number 1 formatted for the Experts tab
SymbolPositionsStatus(_Symbol, 1, false);

Интеграция библиотеки в проекты MQL5 проста. Выполните следующие два шага для импорта PositionsManager.ex5 в ваш MQL5-код:

  • Шаг 1: Скопируйте исполняемый файл библиотеки (PositionsManager.ex5)

Установите файл PositionsManager.ex5 в папку MQL5/Libraries/Toolkit. Убедитесь, что вы загрузили и скопировали файл в указанное место, если его там еще нет. Файл PositionsManager.ex5 приложен в конце статьи.

  • Шаг 2: Импортируйте описания прототипов функций

Добавьте директивы импорта библиотеки для управления позициями и описания прототипов ее функций в раздел заголовка вашего файла исходного кода. Используйте фрагмент кода ниже для эффективного импорта всех функций или модулей из библиотеки PositionsManager.ex5. Я также создал пустой шаблон советника (PositionsManager_Imports_Template.mq5), который включает в себя сегмент кода, указанный ниже. Вы можете выборочно комментировать или удалять любые описания функций, которые не требуются для вашего проекта. Файл PositionsManager_Imports_Template.mq5 также приложен в конце статьи.

//+-------------------------------------------------------------------------------------+
//| PositionsManager.ex5 imports template                                               |
//+-------------------------------------------------------------------------------------+
#import "Toolkit/PositionsManager.ex5" //-- Opening import directive
//-- Function descriptions for the imported function prototypes

//-- Error Handling and Permission Status Functions
bool   ErrorAdvisor(string callingFunc, string symbol, int tradeServerErrorCode);
bool   TradingIsAllowed();

//-- Position Execution and Modification Functions
bool   OpenBuyPosition(ulong magicNumber, string symbol, double lotSize, int sl, int tp, string positionComment);
bool   OpenSellPosition(ulong magicNumber, string symbol, double lotSize, int sl, int tp, string positionComment);
bool   SetSlTpByTicket(ulong positionTicket, int sl, int tp);
bool   ClosePositionByTicket(ulong positionTicket);
bool   SetTrailingStopLoss(ulong positionTicket, int trailingStopLoss);
bool   CloseAllPositions(string symbol, ulong magicNumber);
bool   CloseAllPositions();
bool   CloseAllBuyPositions(string symbol, ulong magicNumber);
bool   CloseAllSellPositions(string symbol, ulong magicNumber);
bool   CloseAllMagicPositions(ulong magicNumber);
bool   CloseAllProfitablePositions(string symbol, ulong magicNumber);
bool   CloseAllProfitableBuyPositions(string symbol, ulong magicNumber);
bool   CloseAllProfitableSellPositions(string symbol, ulong magicNumber);
bool   CloseAllLossPositions(string symbol, ulong magicNumber);
bool   CloseAllLossBuyPositions(string symbol, ulong magicNumber);
bool   CloseAllLossSellPositions(string symbol, ulong magicNumber);

//-- Position Status Monitoring Functions
int    BuyPositionsTotal();
int    SellPositionsTotal();
double PositionsTotalVolume();
double BuyPositionsTotalVolume();
double SellPositionsTotalVolume();
double BuyPositionsProfit();
double SellPositionsProfit();

//-- Positions Filtered By Magic Number Status Monitoring Functions
int    MagicPositionsTotal(ulong magicNumber);
int    MagicBuyPositionsTotal(ulong magicNumber);
int    MagicSellPositionsTotal(ulong magicNumber);
double MagicPositionsTotalVolume(ulong magicNumber);
double MagicBuyPositionsTotalVolume(ulong magicNumber);
double MagicSellPositionsTotalVolume(ulong magicNumber);
double MagicPositionsProfit(ulong magicNumber);
double MagicBuyPositionsProfit(ulong magicNumber);
double MagicSellPositionsProfit(ulong magicNumber);

//-- Positions Filtered By Symbol and/or Magic Number Status Monitoring Functions
int    SymbolPositionsTotal(string symbol, ulong magicNumber);
int    SymbolBuyPositionsTotal(string symbol, ulong magicNumber);
int    SymbolSellPositionsTotal(string symbol, ulong magicNumber);
double SymbolPositionsTotalVolume(string symbol, ulong magicNumber);
double SymbolBuyPositionsTotalVolume(string symbol, ulong magicNumber);
double SymbolSellPositionsTotalVolume(string symbol, ulong magicNumber);
double SymbolPositionsProfit(string symbol, ulong magicNumber);
double SymbolBuyPositionsProfit(string symbol, ulong magicNumber);
double SymbolSellPositionsProfit(string symbol, ulong magicNumber);

//-- Log and Data Display Functions
string AccountPositionsStatus(bool formatForComment);
string MagicPositionsStatus(ulong magicNumber, bool formatForComment);
string SymbolPositionsStatus(string symbol, ulong magicNumber, bool formatForComment);

#import //--- Closing import directive

Импортировав библиотеку, вы теперь можете без труда открывать, закрывать, изменять или извлекать данные о состоянии позиции с помощью простых вызовов функций. Чтобы проиллюстрировать это, создадим три простых советника в следующих разделах.


Разработка советника Dual VIDyA Trailing Stop на основе EX5-библиотеки для управления позициями

В этом разделе мы разработаем советник по стратегии трейлинг-стопа на основе технического индикатора Variable Index Dynamic Average (VIDyA), чтобы проиллюстрировать применение EX5-библиотеки для управления позициями в реальном торговом приложении.

Стратегия трейлинг-стопа VIDyA будет использовать пару динамических скользящих технических индикаторов с переменным индексом для генерации сигналов на покупку и продажу. Поскольку индикатор VIDyA отображается на графике в виде линии, мы будем использовать стратегию пересечения линий для подачи сигнала о новой сделке. Для реализации стратегии пересечения пары индикаторов должны иметь разные настройки. Первый индикатор VIDyA с меньшими входными значениями будет называться fast VIDyA (быстрый VIDyA), поскольку он будет реагировать и генерировать сигналы быстрее. Второй, с более высокими входными значениями, будет называться slow VIDyA (медленный VIDyA), поскольку он медленнее реагирует на изменение цен. Для входа на покупку линия fast VIDyA должна быть выше линии slow VIDyA, а для входа на продажу линия fast VIDyA должна быть ниже линии slow VIDyA.

Стратегия Dual VIDyA


Создадим новый советник в среде MetaEditor, создав новый файл в Мастере MQL Wizard и назвав его DualVidyaTrader.mq5. Поскольку наш советник будет использовать библиотеку PositionsManager.ex5, первым шагом является импорт и вставка описаний прототипов библиотечных функций, как описано ранее. Поместите сегмент import библиотеки под директивами #property. Поскольку библиотека содержит много функций, мы не будем импортировать или использовать их все. Импортируем только прототипы функций, перечисленные в коде ниже.

//--- Import the PositionsManager EX5 Library
#import "Toolkit/PositionsManager.ex5" //-- Open the ex5 import directive
//-- Prototype function descriptions of the EX5 PositionsManager library
bool   OpenBuyPosition(ulong magicNumber, string symbol, double lotSize, int sl, int tp, string positionComment);
bool   OpenSellPosition(ulong magicNumber, string symbol, double lotSize, int sl, int tp, string positionComment);
bool   SetTrailingStopLoss(ulong positionTicket, int trailingStopLoss);
int    MagicPositionsTotal(ulong magicNumber);
int    MagicBuyPositionsTotal(ulong magicNumber);
int    MagicSellPositionsTotal(ulong magicNumber);
bool   CloseAllProfitableBuyPositions(string symbol, ulong magicNumber);
bool   CloseAllProfitableSellPositions(string symbol, ulong magicNumber);
string MagicPositionsStatus(ulong magicNumber, bool formatForComment);
#import //-- Close the ex5 import directive

После сегмента import библиотеки PositionsManager.ex5 добавим введенные пользователем глобальные переменные, как показано далее.

input group ""
input ulong magicNo = 1234;
input ENUM_TIMEFRAMES timeframe = PERIOD_H1;
input ENUM_APPLIED_PRICE  appliedPrice = PRICE_CLOSE; // Applied VIDyA Price

//-- Fast Vidya user inputs
input group "-- FAST VIDyA INPUTS"
input int fast_cmoPeriod = 5; // Fast Chande Momentum Period
input int fast_maPeriod = 10; // Fast MA Smoothing Period
input int fast_emaShift = 0; // Fast Horizontal Shift

//-- Slow Vidya user inputs
input group "-- SLOW VIDyA INPUTS"
input int slow_cmoPeriod = 9; //  Slow Chande Momentum Period
input int slow_maPeriod = 12; // Slow MA Smoothing Period
input int slow_emaShift = 0; // Slow Horizontal Shift

input group "-- PROFIT MANAGEMENT"
input bool liquidateProfitOnCrossover = false; // Liquidate Profit On VIDyA Signal
input bool enableTrailingStops = true; // Use Trailing Stop Losses

Создадим больше глобальных переменных для хранения стоп-лосса, тейк-профита, скользящего стоп-лосса и значений лота или объема.

//-- Get and save the SL, trailingSL and TP values from the spread
int spreadMultiForSl = 1000;
int spreadMultiForTrailingSl = 300;
int spreadMultiForTp = 1000;
int sl = int(SymbolInfoInteger(_Symbol, SYMBOL_SPREAD)) * spreadMultiForSl;
int trailingSl = int(SymbolInfoInteger(_Symbol, SYMBOL_SPREAD)) * spreadMultiForTrailingSl;
int tp = int(SymbolInfoInteger(_Symbol, SYMBOL_SPREAD)) * spreadMultiForTp;

//-- Set the lot or volume to the symbol allowed min value
double lotSize = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MIN);

Создадим переменные технического индикатора VIDyA в глобальной области видимости, чтобы обеспечить к ним доступ из любой части кода нашего советника.

//-- Vidya indicator variables
double fastVidya[], slowVidya[];
int fastVidyaHandle, slowVidyaHandle;
bool buyOk, sellOk, vidyaBuy, vidyaSell;
string vidyaTrend;

После раздела глобальных переменных создадим функцию инициализации советника, который будет вызываться из стандартной функции обработки событий MQL5 OnInit(), которая впервые выполняется при запуске или инициализации советника. Назовем эту функцию GetInit(). В этой функции мы выполним все инициализации данных, включая инициализацию индикатора VIDyA и обработку запуска.

int GetInit()
  {
   int returnVal = 1;

//-- Helps to regulate and prevent openning multiple trades on a single signal trigger
   buyOk = true;
   sellOk = true;

//-- Create the fast iVIDyA indicator handle
   fastVidyaHandle = iVIDyA(_Symbol, timeframe, fast_cmoPeriod, fast_maPeriod, fast_emaShift, appliedPrice);
   if(fastVidyaHandle < 0)
     {
      Print("Error creating fastVidyaHandle = ", INVALID_HANDLE);
      Print("Handle creation: Runtime error = ", GetLastError());
      //-- Close the EA if the handle is not properly loaded
      return(-1);
     }
   ArraySetAsSeries(fastVidya, true); //-- set the vidya array to series access

//-- Create the slow iVIDyA indicator handle
   slowVidyaHandle = iVIDyA(_Symbol, timeframe, slow_cmoPeriod, slow_maPeriod, slow_emaShift, appliedPrice);
   if(slowVidyaHandle < 0)
     {
      Print("Error creating vidyaHandle = ", INVALID_HANDLE);
      Print("Handle creation: Runtime error = ", GetLastError());
      //-- Close the EA if the handle is not properly loaded
      return(-1);
     }
   ArraySetAsSeries(slowVidya, true); //-- set the vidya array to series access

   return(returnVal);
  }

Теперь создадим функцию деинициализации и назовем ее GetDeinit(). Эта функция будет вызываться в стандартной функции обработки событий OnDeinit() для выполнения общесистемной очистки всех ресурсов, используемых советником. Это включает в себя высвобождение индикатора VIDyA, высвобождение всех ресурсов, привязанных к массивам хэндлов индикатора, и удаление любых комментариев или объектов графика.

void GetDeinit()  //-- De-initialize the robot on shutdown and clean everything up
  {
//-- Delete the vidya handles and de-allocate the memory spaces occupied
   IndicatorRelease(fastVidyaHandle);
   ArrayFree(fastVidya);
   IndicatorRelease(slowVidyaHandle);
   ArrayFree(slowVidya);

//-- Delete and clear all chart displayed messages
   Comment("");
  }

Далее нам необходимо создать пользовательскую функцию для обнаружения и извлечения торговых сигналов, генерируемых нашей парой индикаторов VIDyA. Функция будет называться GetVidya(). Она будет выполняться и обновляться каждый раз при поступлении нового тика, чтобы гарантировать точность генерации сигнала и его актуальность.

void GetVidya()
  {
//-- Get vidya line directions
   if(CopyBuffer(fastVidyaHandle, 0, 0, 100, fastVidya) <= 0 || CopyBuffer(slowVidyaHandle, 0, 0, 100, slowVidya) <= 0)
     {
      return;
     }

//-- Reset vidya status variables
   vidyaBuy = false;
   vidyaSell = false;
   vidyaTrend = "FLAT";

//-- Scan for vidya crossover buy signal
   if(fastVidya[1] > slowVidya[1])
     {
      //-- Save the vidya signal
      vidyaTrend = "BUY/LONG";
      vidyaBuy = true;
      vidyaSell = false;
     }

//-- Scan for vidya crossover sell signal
   if(fastVidya[1] < slowVidya[1])
     {
      //-- Save the vidya signal
      vidyaTrend = "SELL/SHORT";
      vidyaSell = true;
      vidyaBuy = false;
     }
  }

Теперь, когда мы создали функцию для получения и обновления сигнала VIDyA, давайте создадим еще одну пользовательскую функцию, которая будет выполняться на каждом новом тике для сканирования и открытия новых позиций на основе текущего сигнала VIDyA. Назовем ее ScanForTradeOpportunities(). В этой функции мы вызовем и выполним прототипы функций открытия и статуса позиции, импортированные из нашей библиотеки PositionsManager.ex5 в этой функции.

void ScanForTradeOpportunities()
  {
//-- Get the VIDyA signal
   GetVidya();

   if(MagicPositionsTotal(magicNo) == 0)
     {
      buyOk = true;
      sellOk = true;
     }

//-- Check for a buy entry when a VIDyA buy signal is found
   if(buyOk && vidyaBuy) //-- Open a new buy position
     {
      if(OpenBuyPosition(magicNo, _Symbol, lotSize, sl, tp, "Vidya_BUY: " + IntegerToString(MagicBuyPositionsTotal(magicNo) + 1)))
        {
         buyOk = false;
         sellOk = true;
        }
      //-- Market has a strong buy trend, close all profitable sell positions
      if(liquidateProfitOnCrossover)
        {
         CloseAllProfitableSellPositions(_Symbol, magicNo);
        }
     }

//-- Check for a sell entry when a VIDyA sell signal is found
   if(sellOk && vidyaSell) //-- Open a new sell position
     {
      if(OpenSellPosition(magicNo, _Symbol, lotSize, sl, tp, "Vidya_SELL: " + IntegerToString(MagicSellPositionsTotal(magicNo) + 1)))
        {
         sellOk = false;
         buyOk = true;
        }
      //-- Market has a strong sell trend, close all profitable buy positions
      if(liquidateProfitOnCrossover)
        {
         CloseAllProfitableBuyPositions(_Symbol, magicNo);
        }
     }
  }

Нам также необходимо проверить и установить трейлинг-стоп-лоссы по всем открытым позициям. Это будет просто, так как наша библиотека PositionsManager.ex5 содержит прототип функции трейлинг-стоп-лосса, который поможет нам в этом. Создадим новую функцию CheckAndSetTrailingSl() для сканирования всех открытых позиций и извлечения их тикетов для использования в качестве параметров в импортируемом прототипе функции SetTrailingStopLoss().

void CheckAndSetTrailingSl()
  {
   int totalOpenPostions = PositionsTotal();
   for(int x = 0; x < totalOpenPostions; x++)
     {
      //--- Get position properties
      ulong  positionTicket = PositionGetTicket(x); //-- Get ticket to select the position
      string selectedSymbol = PositionGetString(POSITION_SYMBOL);
      ulong positionMagicNo = PositionGetInteger(POSITION_MAGIC);
      int positionType = int(PositionGetInteger(POSITION_TYPE));

      //-- modify only the positions we have opened with this EA (magic number)
      if(selectedSymbol != _Symbol && positionMagicNo != magicNo)
        {
         continue;
        }
      //-- Only set the trailing stop loss when the market trend is in the opposing direction of the position type
      if((positionType == POSITION_TYPE_BUY && vidyaBuy) || (positionType == POSITION_TYPE_SELL && vidyaSell))
        {
         continue;
        }
      //--- set the trailing stop loss
      SetTrailingStopLoss(positionTicket, trailingSl); //-- call the imported function from our ex5 library
     }
  }

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

void OnTick()
  {
//-- Scan and open new positions based on the vidya signal
   ScanForTradeOpportunities();

//-- Check and set the trailing stop
   if(enableTrailingStops)
     {
      CheckAndSetTrailingSl();
     }

//-- Display the vidya trend and positions status for the EA's magicNo
   Comment(
      "\nvidyaTrend: ", vidyaTrend,
      MagicPositionsStatus(magicNo, true)
   );
  }

Наконец, не забудьте поместить функции инициализации и деинициализации в соответствующие стандартные функции обработки событий MQL5.

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
//---
   if(GetInit() <= 0)
     {
      return(INIT_FAILED);
     }
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
//---
   GetDeinit();
  }

Файл исходного кода советника DualVidyaTrader.mq5 приложен в конце статьи. Я прикрепил полный файл исходного кода, чтобы у вас были все необходимые компоненты для внедрения и настройки торговой стратегии по мере необходимости.

При компиляции и загрузке нового советника DualVidyaTrader в MetaTrader 5 вы заметите, что на вкладке Dependencies (зависимости) все прототипы библиотечных функций, импортированные из PositionsManager.ex5 перечислены вместе с полным путем к файлу, где сохранена EX5-библиотека. Это гарантирует, что все необходимые зависимости будут правильно указаны до загрузки советника на график. Если будут обнаружены какие-либо ошибки ссылок на библиотеки, подобные тем, которые обсуждались ранее в статье, они будут выведены на вкладке "Эксперты" или "Журнал" в окне "Инструменты" MetaTrader 5.

Dual Vidya Trader, вкладка Dependencies


Торговая панель управления позициями на базе EX5-библиотеки

Во втором примере мы создадим простую торговую панель с графическим пользовательским интерфейсом (GUI) на базе нашей библиотеки PositionsManager.ex5.

Графический интерфейс панели управления позициями


Создадим новый советник в среде MetaEditor, создав новый файл в Мастере MQL и назвав его PositionsManagerPanel.mq5. Под сегментом директив #property импортируйте библиотеку PositionsManager.ex5. В разделе описаний функций import импортируйте только следующие прототипы функций.

//+------------------------------------------------------------------+
//| EX5 PositionsManager imports                                     |
//+------------------------------------------------------------------+
#import "Toolkit/PositionsManager.ex5" //-- Open import directive
//-- Function descriptions for the imported function prototypes

//--Position Execution and Modification Functions
bool   OpenBuyPosition(ulong magicNumber, string symbol, double lotSize, int sl, int tp, string positionComment);
bool   OpenSellPosition(ulong magicNumber, string symbol, double lotSize, int sl, int tp, string positionComment);
bool   SetSlTpByTicket(ulong positionTicket, int sl, int tp);
bool   ClosePositionByTicket(ulong positionTicket);
bool   SetTrailingStopLoss(ulong positionTicket, int trailingStopLoss);
bool   CloseAllPositions(string symbol, ulong magicNumber);
bool   CloseAllBuyPositions(string symbol, ulong magicNumber);
bool   CloseAllSellPositions(string symbol, ulong magicNumber);
bool   CloseAllMagicPositions(ulong magicNumber);
bool   CloseAllProfitablePositions(string symbol, ulong magicNumber);
bool   CloseAllLossPositions(string symbol, ulong magicNumber);

//--Position Status Monitoring Functions
int    BuyPositionsTotal();
int    SellPositionsTotal();
double PositionsTotalVolume();
double BuyPositionsTotalVolume();
double SellPositionsTotalVolume();
double BuyPositionsProfit();
double SellPositionsProfit();
int    MagicPositionsTotal(ulong magicNumber);
int    MagicBuyPositionsTotal(ulong magicNumber);
int    MagicSellPositionsTotal(ulong magicNumber);
double MagicPositionsTotalVolume(ulong magicNumber);
double MagicBuyPositionsTotalVolume(ulong magicNumber);
double MagicSellPositionsTotalVolume(ulong magicNumber);
double MagicPositionsProfit(ulong magicNumber);
double MagicBuyPositionsProfit(ulong magicNumber);
double MagicSellPositionsProfit(ulong magicNumber);
int    SymbolPositionsTotal(string symbol, ulong magicNumber);
int    SymbolBuyPositionsTotal(string symbol, ulong magicNumber);
int    SymbolSellPositionsTotal(string symbol, ulong magicNumber);
double SymbolPositionsTotalVolume(string symbol, ulong magicNumber);
double SymbolBuyPositionsTotalVolume(string symbol, ulong magicNumber);
double SymbolSellPositionsTotalVolume(string symbol, ulong magicNumber);
double SymbolPositionsProfit(string symbol, ulong magicNumber);
double SymbolBuyPositionsProfit(string symbol, ulong magicNumber);
double SymbolSellPositionsProfit(string symbol, ulong magicNumber);
string AccountPositionsStatus(bool formatForComment);
string MagicPositionsStatus(ulong magicNumber, bool formatForComment);
string SymbolPositionsStatus(string symbol, ulong magicNumber, bool formatForComment);
#import //--- Close import directive

PositionsManagerPanel.mq5 будет содержать только один пользовательский ввод (магическое число).

//--User input variables
input ulong magicNo = 101010;

Далее создадим глобальные переменные для хранения volumeLot, sl и tp.

//-- Global variables
//-----------------------
//-- Get the current symbol spread and multiply it by a significant number
//-- to simulate user-input SL and TP values
double volumeLot = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MIN);
int sl = (int)SymbolInfoInteger(_Symbol, SYMBOL_SPREAD) * 50;
int tp = (int)SymbolInfoInteger(_Symbol, SYMBOL_SPREAD) * 100;

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

//+-----------------------------------------------------------------------+
//| CreateRectangleLabel(): Creates a rectangle label on the chart window |
//+-----------------------------------------------------------------------+
void CreateRectangleLabel()
  {
//--- Detect if we have an object named the same as our rectangle label
   if(ObjectFind(0, "mainRectangleLabel") >= 0)
     {
      //--- Delete the specified object if it is not a rectangle label
      if(ObjectGetInteger(0, "mainRectangleLabel", OBJPROP_TYPE) != OBJ_RECTANGLE_LABEL)
        {
         ObjectDelete(0, "mainRectangleLabel");
        }
     }
   else
     {
      //-- Create the mainRectangleLabel
      ObjectCreate(0, "mainRectangleLabel", OBJ_RECTANGLE_LABEL, 0, 0, 0);
     }
//--- Set up the new rectangle label properties
   ObjectSetInteger(0, "mainRectangleLabel", OBJPROP_CORNER, CORNER_LEFT_UPPER);
   ObjectSetInteger(0, "mainRectangleLabel", OBJPROP_XDISTANCE, 240);
   ObjectSetInteger(0, "mainRectangleLabel", OBJPROP_YDISTANCE, 2);
   ObjectSetInteger(0, "mainRectangleLabel", OBJPROP_XSIZE, 460);
   ObjectSetInteger(0, "mainRectangleLabel", OBJPROP_YSIZE, 520);
   ObjectSetInteger(0, "mainRectangleLabel", OBJPROP_BGCOLOR, clrMintCream);
   ObjectSetInteger(0, "mainRectangleLabel", OBJPROP_BACK, false);
   ObjectSetInteger(0, "mainRectangleLabel", OBJPROP_HIDDEN, true);
  }

Нам также нужны метки для отображения текста на нашей торговой панели. Создадим еще одну пользовательскую функцию CreateLabel() для решения этой задачи.

//+---------------------------------------------------------+
//| CreateLabel(): Creates a text label on the chart window |
//+---------------------------------------------------------+
void CreateLabel(
   string labelName, int xDistance, int yDistance, int xSize, int ySize,
   string labelText, color textColor, string fontType, int fontSize
)
  {
//--- Detect if we have an object with the same name as our label
   if(ObjectFind(0, labelName) >= 0)
     {
      //--- Delete the specified object if it is not a label
      if(ObjectGetInteger(0, labelName, OBJPROP_TYPE) != OBJ_LABEL)
        {
         ObjectDelete(0, labelName);
        }
     }
   else
     {
      //-- Create the label
      ObjectCreate(0, labelName, OBJ_LABEL, 0, 0, 0);
     }
//--- Set up the new rectangle label properties
   ObjectSetInteger(0, labelName, OBJPROP_CORNER, CORNER_LEFT_UPPER);
   ObjectSetInteger(0, labelName, OBJPROP_XDISTANCE, xDistance);
   ObjectSetInteger(0, labelName, OBJPROP_YDISTANCE, yDistance);
   ObjectSetInteger(0, labelName, OBJPROP_XSIZE, xSize);
   ObjectSetInteger(0, labelName, OBJPROP_YSIZE, ySize);
   ObjectSetString(0, labelName, OBJPROP_TEXT, labelText);
   ObjectSetInteger(0, labelName, OBJPROP_COLOR, textColor);
   ObjectSetString(0, labelName, OBJPROP_FONT, fontType);
   ObjectSetInteger(0, labelName, OBJPROP_FONTSIZE, fontSize);
   ObjectSetInteger(0, labelName, OBJPROP_BACK, false);
   ObjectSetInteger(0, labelName, OBJPROP_HIDDEN, true);
   ObjectSetInteger(0, labelName, OBJPROP_SELECTABLE, false);
   ObjectSetInteger(0, labelName, OBJPROP_SELECTED, false);
  }

Для выполнения различных торговых операций на нашей торговой панели необходимы кликабельные или адаптивные кнопки. Поэтому нам необходимо создать специальную функцию для управления созданием кнопок. Назовем эту функцию CreateButton().

//+------------------------------------------------------+
//| CreateButton(): Creates buttons on the chart window  |
//+------------------------------------------------------+
void CreateButton(
   string btnName, int xDistance, int yDistance, int xSize, int ySize, string btnText,
   string tooltip, color textColor, string fontType, int fontSize, color bgColor
)
  {
//--- Detect if we have an object named the same as our button
   if(ObjectFind(0, btnName) >= 0)
     {
      //--- Delete the specified object if it is not a button
      if(ObjectGetInteger(0, btnName, OBJPROP_TYPE) != OBJ_BUTTON)
        {
         ObjectDelete(0, btnName);
        }
     }
   else
     {
      //-- Create the button
      ObjectCreate(0, btnName, OBJ_BUTTON, 0, 0, 0);
     }
//--- Set up the new button properties
   ObjectSetInteger(0, btnName, OBJPROP_CORNER, CORNER_LEFT_UPPER);
   ObjectSetInteger(0, btnName, OBJPROP_XDISTANCE, xDistance);
   ObjectSetInteger(0, btnName, OBJPROP_YDISTANCE, yDistance);
   ObjectSetInteger(0, btnName, OBJPROP_XSIZE, xSize);
   ObjectSetInteger(0, btnName, OBJPROP_YSIZE, ySize);
   ObjectSetString(0, btnName, OBJPROP_TEXT, btnText);
   ObjectSetString(0, btnName, OBJPROP_TOOLTIP, tooltip);
   ObjectSetInteger(0, btnName, OBJPROP_COLOR, textColor);
   ObjectSetString(0, btnName, OBJPROP_FONT, fontType);
   ObjectSetInteger(0, btnName, OBJPROP_FONTSIZE, fontSize);
   ObjectSetInteger(0, btnName, OBJPROP_BGCOLOR, bgColor);
  }

Теперь нам нужна еще одна пользовательская функция для загрузки всех различных объектов графика и графических компонентов пользовательского ввода, созданных с помощью функций, созданных выше. Назовем эту функцию LoadChartObjects().

//+--------------------------------------------------------------------+
//| LoadChartObjects(): Create and load the buttons and chart objects  |
//| for demonstrating how the imported library functions work          |
//+--------------------------------------------------------------------+
void LoadChartObjects()
  {
//-- Create the rectangle label first
   CreateRectangleLabel();

//-- Create the heading label
   CreateLabel(
      "headingLabel", 250, 10, 440, 60,
      "PositionsManager ex5 Library Demo Trade Panel",
      clrMidnightBlue, "Calibri", 10
   );

//-- Create the second heading label
   CreateLabel(
      "headingLabel2", 250, 30, 440, 60,
      ("Trading " + _Symbol + " with Magic Number: " + (string)magicNo),
      clrBlack, "Consolas", 11
   );

//-- "BUY": Button to call the imported ex5 OpenBuyPosition() function
   CreateButton(
      "OpenBuyPositionBtn", 250, 50, 215, 35, "BUY",
      "OpenBuyPosition() Function", clrMintCream, "Arial Black", 10, clrDodgerBlue
   );

//-- "SELL": Button to call the imported ex5 OpenSellPosition() function
   CreateButton(
      "OpenSellPositionBtn", 475, 50, 215, 35, "SELL",
      "OpenSellPosition() Function", clrMintCream, "Arial Black", 10, clrCrimson
   );

//-- "SetSlTpByTicket": Button to call the imported ex5 SetSlTpByTicket() function
   CreateButton(
      "SetSlTpBtn", 250, 90, 215, 35, "SetSlTpByTicket",
      "SetSlTpByTicket() Function", clrMintCream, "Arial Black", 10, clrDarkSlateGray
   );

//-- "SetTrailingStopLoss": Button to call the imported ex5 SetTrailingStopLoss() function when clicked
   CreateButton(
      "SetTrailingStopLossBtn", 475, 90, 215, 35, "SetTrailingStopLoss",
      "SetTrailingStopLoss Function", clrMintCream, "Arial Black", 10, clrDarkSlateGray
   );

//-- "ClosePositionsByTicket": Button to call the imported ex5 ClosePositionByTicket() function
   CreateButton(
      "ClosePositionsBtn", 250, 130, 215, 35, "ClosePositionsByTicket",
      "ClosePositionByTicket() Function", clrMintCream, "Arial Black", 10, clrMaroon
   );

//-- "CloseAllSymbolPositions": Button to call the imported ex5 CloseAllSymbolPositions() function
   CreateButton(
      "CloseAllPositionsBtn", 475, 130, 215, 35, "CloseAllPositions",
      "CloseAllPositions() Function", clrMintCream, "Arial Black", 10, clrMaroon
   );

//-- "CloseAllBuySymbolPositions": Button to call the imported ex5 CloseAllBuySymbolPositions() function
   CreateButton(
      "CloseAllBuyPositionsBtn", 250, 170, 215, 35, "CloseAllBuyPositions",
      "CloseAllBuyPositions() Function", clrMintCream, "Arial Black", 10, clrBrown
   );

//-- "CloseAllSellSymbolPositions": Button to call the imported ex5 CloseAllSellSymbolPositions() function
   CreateButton(
      "CloseAllSellPositionsBtn", 475, 170, 215, 35, "CloseAllSellPositions",
      "CloseAllSellPositions() Function", clrMintCream, "Arial Black", 10, clrBrown
   );

//-- "CloseAllMagicPositions": Button to call the imported ex5 CloseAllMagicPositions() function
   CreateButton(
      "CloseAllMagicPositionsBtn", 250, 210, 440, 35, "CloseAllMagicPositions",
      "CloseAllMagicPositions() Function", clrMintCream, "Arial Black", 10, C'203,18,55'
   );

//-- "CloseAllProfitablePositions": Button to call the imported ex5 CloseAllMagicPositions() function
   CreateButton(
      "CloseAllProfitablePositionsBtn", 250, 250, 215, 35, "CloseAllProfitablePositions",
      "CloseAllProfitablePositions() Function", clrMintCream, "Arial Black", 10, clrSeaGreen
   );

//-- "CloseAllLossPositions": Button to call the imported ex5 CloseAllLossPositions() function
   CreateButton(
      "CloseAllLossPositionsBtn", 475, 250, 215, 35, "CloseAllLossPositions",
      "CloseAllLossPositions() Function", clrMintCream, "Arial Black", 10, C'179,45,0'
   );

//-- Create the bottomHeadingLabel
   CreateLabel(
      "bottomHeadingLabel", 250, 310, 440, 60,
      (_Symbol + " - (Magic Number: " + (string)magicNo + ") Positions Status"),
      clrBlack, "Calibri", 12
   );

//-- Create totalOpenPositionsLabel
   CreateLabel(
      "totalOpenPositionsLabel", 250, 340, 440, 60,
      ("  Total Open:   " + (string)MagicPositionsTotal(magicNo)),
      clrNavy, "Consolas", 11
   );

//-- Create totalPositionsVolumeLabel
   CreateLabel(
      "totalPositionsVolumeLabel", 250, 360, 440, 60,
      ("  Total Volume: " + (string)NormalizeDouble(MagicPositionsTotalVolume(magicNo), 2)),
      clrNavy, "Consolas", 11
   );

//-- Create the totalPositionsProfitLabel
   CreateLabel(
      "totalPositionsProfitLabel", 250, 380, 100, 60,
      (
         "  Total Profit: " + (string)(NormalizeDouble(MagicPositionsProfit(magicNo), 2)) +
         " " + AccountInfoString(ACCOUNT_CURRENCY)
      ),
      clrNavy, "Consolas", 11
   );

//-- Create the buyPositionsHeadingLabel
   CreateLabel(
      "buyPositionsHeadingLabel", 250, 410, 440, 60,
      ("BUY POSITIONS:"),
      clrBlack, "Calibri", 12
   );

//-- Create the totalBuyPositionsLabel
   CreateLabel(
      "totalBuyPositionsLabel", 250, 430, 440, 60,
      ("  Total Open:   " + (string)MagicBuyPositionsTotal(magicNo)),
      clrNavy, "Consolas", 11
   );

//-- Create the totalBuyPositionsVolumeLabel
   CreateLabel(
      "totalBuyPositionsVolumeLabel", 250, 450, 440, 60,
      ("  Total Volume: " + (string)NormalizeDouble(MagicBuyPositionsTotalVolume(magicNo), 2)),
      clrNavy, "Consolas", 11
   );

//-- Create the totalBuyPositionsProfitLabel
   CreateLabel(
      "totalBuyPositionsProfitLabel", 250, 470, 440, 60,
      (
         "  Total Profit: " + (string)(NormalizeDouble(MagicBuyPositionsProfit(magicNo), 2)) +
         " " + AccountInfoString(ACCOUNT_CURRENCY)
      ),
      clrNavy, "Consolas", 11
   );

//-- Create the sellPositionsHeadingLabel
   CreateLabel(
      "sellPositionsHeadingLabel", 475, 410, 440, 60,
      ("SELL POSITIONS:"),
      clrBlack, "Calibri", 12
   );

//-- Create the totalSellPositionsLabel
   CreateLabel(
      "totalSellPositionsLabel", 475, 430, 440, 60,
      ("  Total Open:   " + (string)MagicSellPositionsTotal(magicNo)),
      clrNavy, "Consolas", 11
   );

//-- Create the totalSellPositionsVolumeLabel
   CreateLabel(
      "totalSellPositionsVolumeLabel", 475, 450, 440, 60,
      ("  Total Volume: " + (string)NormalizeDouble(MagicSellPositionsTotalVolume(magicNo), 2)),
      clrNavy, "Consolas", 11
   );

//-- Create the totalSellPositionsProfitLabel
   CreateLabel(
      "totalSellPositionsProfitLabel", 475, 470, 100, 60,
      (
         "  Total Profit: " + (string)(NormalizeDouble(MagicSellPositionsProfit(magicNo), 2)) +
         " " + AccountInfoString(ACCOUNT_CURRENCY)
      ),
      clrNavy, "Consolas", 11
   );

//--- Redraw the chart to refresh it so that it loads our new chart objects
   ChartRedraw();
//---
  }

Следующая пользовательская функция будет отвечать за очистку и удаление всех объектов графика и данных при завершении работы или деинициализации советника. Назовем ее DeleteChartObjects(). Она будет помещена и выполнена в стандартной функции обработки событий OnDeinit().

//+------------------------------------------------------------------------------+
//| DeleteChartObjects(): Delete all the chart objects when the EA is terminated |
//| on De-initialization                                                         |
//+------------------------------------------------------------------------------+
void DeleteChartObjects()
  {
//---
//--- Clean up and delete all the buttons or graphical objects
   ObjectDelete(0, "OpenBuyPositionBtn");
   ObjectDelete(0, "OpenSellPositionBtn");
   ObjectDelete(0, "SetSlTpBtn");
   ObjectDelete(0, "SetTrailingStopLossBtn");
   ObjectDelete(0, "ClosePositionsBtn");
   ObjectDelete(0, "CloseAllPositionsBtn");
   ObjectDelete(0, "CloseAllBuyPositionsBtn");
   ObjectDelete(0, "CloseAllSellPositionsBtn");
   ObjectDelete(0, "CloseAllMagicPositionsBtn");
   ObjectDelete(0, "CloseAllProfitablePositionsBtn");
   ObjectDelete(0, "CloseAllLossPositionsBtn");
   ObjectDelete(0, "mainRectangleLabel");

   ObjectDelete(0, "headingLabel");
   ObjectDelete(0, "headingLabel2");

   ObjectDelete(0, "bottomHeadingLabel");
   ObjectDelete(0, "totalOpenPositionsLabel");
   ObjectDelete(0, "totalPositionsVolumeLabel");
   ObjectDelete(0, "totalPositionsProfitLabel");

   ObjectDelete(0, "buyPositionsHeadingLabel");
   ObjectDelete(0, "totalBuyPositionsLabel");
   ObjectDelete(0, "totalBuyPositionsVolumeLabel");
   ObjectDelete(0, "totalBuyPositionsProfitLabel");

   ObjectDelete(0, "sellPositionsHeadingLabel");
   ObjectDelete(0, "totalSellPositionsLabel");
   ObjectDelete(0, "totalSellPositionsVolumeLabel");
   ObjectDelete(0, "totalSellPositionsProfitLabel");
  }

Создадим пользовательские функции, которые будут реализовывать некоторые импортированные функции библиотеки PositionsManager.ex5. Первая функция в этой группе, которую мы назовем ModifySlTp(), будет отвечать за изменение стоп-лосса (sl) и тейк-профита (tp) всех позиций, открытых этим советником, которые соответствуют введенному пользователем магическому числу. Эта функция будет выполняться каждый раз при нажатии кнопки setSLTP на графике.

//+-------------------------------------------------------------------------+
// ModifySlTp(): This function demonstrates how to use the imported ex5     |
// bool SetSlTpByTicket(ulong positionTicket, int sl, int tp);              |
// It runs this function when the setSLTP button on the chart is clicked.   |
//+-------------------------------------------------------------------------+
void ModifySlTp()
  {
//-- Get positions that we have openend with the chart buy and sell buttons to test the imported function with
   int totalOpenPostions = PositionsTotal();
//--- Scan open positions
   for(int x = 0; x < totalOpenPostions; x++)
     {
      //--- Get position properties
      ulong  positionTicket = PositionGetTicket(x); //-- Get ticket to select the position
      string selectedSymbol = PositionGetString(POSITION_SYMBOL);
      ulong positionMagicNo = PositionGetInteger(POSITION_MAGIC);

      //-- modify only the positions we have opened with this EA (magic number) using the BUY and SELL buttons on the chart
      if(selectedSymbol != _Symbol && positionMagicNo != magicNo)
        {
         continue;
        }
      //--- modify the sl and tp of the position
      SetSlTpByTicket(positionTicket, sl, tp);//-- call the imported function from our ex5 library
     }
  }

Следующая функция, SetTrailingSl(), будет отвечать за обновление трейлинг-стоп-лосса. Она будет выполняться и при нажатии на кнопку SetTrailingStopLoss на графике, и на каждом новом входящем тике в функции обработки событий OnTick().

//+-----------------------------------------------------------------------------------+
// SetTrailingSl(): This function demonstrates how to use the imported ex5            |
// bool SetTrailingStopLoss(ulong positionTicket, int trailingStopLoss);              |
// It runs this function when the SetTrailingStopLoss button on the chart is clicked. |
//+-----------------------------------------------------------------------------------+
void SetTrailingSl()
  {
   int trailingSl = sl;

//-- Get positions that we have openend with the chart buy and sell buttons to test the imported function with
   int totalOpenPostions = PositionsTotal();
//--- Scan open positions
   for(int x = 0; x < totalOpenPostions; x++)
     {
      //--- Get position properties
      ulong  positionTicket = PositionGetTicket(x); //-- Get ticket to select the position
      string selectedSymbol = PositionGetString(POSITION_SYMBOL);
      ulong positionMagicNo = PositionGetInteger(POSITION_MAGIC);

      //-- modify only the positions we have opened with this EA (magic number) using the BUY and SELL buttons on the chart
      if(selectedSymbol != _Symbol && positionMagicNo != magicNo)
        {
         continue;
        }
      //--- set the trailing stop loss
      SetTrailingStopLoss(positionTicket, trailingSl); //-- call the imported function from our ex5 library
     }
  }

Функция ClosePositionWithTicket() будет отвечать за закрытие всех открытых позиций и будет выполняться при нажатии на кнопку ClosePositions на графике.

//+-----------------------------------------------------------------------------------+
// ClosePositionWithTicket(): This function demonstrates how to use the imported ex5  |
// bool ClosePositionByTicket(ulong positionTicket)                                   |
// It runs this function when the ClosePositions button on the chart is clicked.      |
//+-----------------------------------------------------------------------------------+
void ClosePositionWithTicket()
  {
//-- Get positions that we have openend with the chart buy and sell buttons to test the imported function with
   int totalOpenPostions = PositionsTotal();
//--- Scan open positions
   for(int x = 0; x < totalOpenPostions; x++)
     {
      //--- Get position properties
      ulong  positionTicket = PositionGetTicket(x); //-- Get ticket to select the position
      string selectedSymbol = PositionGetString(POSITION_SYMBOL);
      ulong positionMagicNo = PositionGetInteger(POSITION_MAGIC);

      //-- close only the positions we have opened with this EA (magic number) using the BUY and SELL buttons on the chart
      if(selectedSymbol != _Symbol && positionMagicNo != magicNo)
        {
         continue;
        }

      //--- Close the position
      ClosePositionByTicket(positionTicket);//-- call the imported function from our ex5 library
     }
  }

Последняя функция — стандартная MQL5-функция OnChartEvent(), которая будет определять нажатие различных кнопок и выполнять соответствующие действия. Почти все импортированные функции управления позициями из библиотеки PositionsManager.ex5 будут вызываться и выполняться из этой функции.

//+------------------------------------------------------------------+
//| ChartEvent function to detect when the buttons are clicked       |
//+------------------------------------------------------------------+
void OnChartEvent(const int id,
                  const long &lparam,
                  const double &dparam,
                  const string &sparam)
  {
//---
//--- Detected a CHARTEVENT_CLICK event
   if(id == CHARTEVENT_OBJECT_CLICK)
     {
      Print(__FUNCTION__, ": ", sparam);

      //--- Buy when OpenBuyPositionBtn button (BUY) is pressed or clicked
      if(sparam == "OpenBuyPositionBtn")
        {
         //-- Call our imported function from the Toolkit/PositionsManager ex5 library
         OpenBuyPosition(magicNo, _Symbol, volumeLot, sl, tp, "ex5 PositionsManager");

         //--- Release and unpress the button
         ObjectSetInteger(0, "OpenBuyPositionBtn", OBJPROP_STATE, false);
        }

      //--- Sell when OpenSellPositionBtn button (SELL) is pressed
      if(sparam == "OpenSellPositionBtn")
        {
         //-- Call our imported function from the Toolkit/PositionsManager ex5 library
         OpenSellPosition(magicNo, _Symbol, volumeLot, sl, tp, "ex5 PositionsManager");
         //OpenSellPosition(magicNo, "NON-EXISTENT-Symbol-Name"/*_Symbol*/, volumeLot, sl, tp, "ex5 PositionsManager");

         //--- Release and unpress the button
         ObjectSetInteger(0, "OpenSellPositionBtn", OBJPROP_STATE, false);
        }

      //--- Modify specified positions SL and TP when SetSlTpBtn button (setSLTP) is pressed
      if(sparam == "SetSlTpBtn")
        {
         ModifySlTp();//-- Modify the SL and TP of the positions generated by the BUY and SELL buttons
         //--- Release and unpress the button
         ObjectSetInteger(0, "SetSlTpBtn", OBJPROP_STATE, false);
        }

      //--- Set the Trailing Stop Loss when SetSlTpBtn button (SetTrailingStopLossBtn) is pressed
      if(sparam == "SetTrailingStopLossBtn")
        {
         SetTrailingSl();//-- Set the Trailing Stop Loss for the positions generated by the BUY and SELL buttons
         //--- Release and unpress the button
         ObjectSetInteger(0, "SetTrailingStopLossBtn", OBJPROP_STATE, false);
        }

      //--- Close specified positions when SetSlTpBtn button (setSLTP) is pressed
      if(sparam == "ClosePositionsBtn")
        {
         ClosePositionWithTicket();//-- Close all the positions generated by the BUY and SELL buttons
         //--- Release and unpress the button
         ObjectSetInteger(0, "ClosePositionsBtn", OBJPROP_STATE, false);
        }

      //--- Close all positions for the current symbol when the CloseAllPositionsBtn button is pressed
      if(sparam == "CloseAllPositionsBtn")
        {
         CloseAllPositions(_Symbol, 0);//-- Close all the open symbol positions
         //--- Release and unpress the button
         ObjectSetInteger(0, "CloseAllPositionsBtn", OBJPROP_STATE, false);
        }

      //--- Close all buy positions for the current symbol when the CloseAllBuyPositionsBtn button is pressed
      if(sparam == "CloseAllBuyPositionsBtn")
        {
         CloseAllBuyPositions(_Symbol, magicNo);//-- Close all the open symbol buy positions
         //--- Release and unpress the button
         ObjectSetInteger(0, "CloseAllBuyPositionsBtn", OBJPROP_STATE, false);
        }

      //--- Close all sell positions for the current symbol when the CloseAllSellPositionsBtn button is pressed
      if(sparam == "CloseAllSellPositionsBtn")
        {
         CloseAllSellPositions(_Symbol, magicNo);//-- Close all the open symbol sell positions
         //--- Release and unpress the button
         ObjectSetInteger(0, "CloseAllSellPositionsBtn", OBJPROP_STATE, false);
        }

      //--- Close all positions with the specified magic number when the CloseAllMagicPositionsBtn button is pressed
      if(sparam == "CloseAllMagicPositionsBtn")
        {
         CloseAllMagicPositions(magicNo);//-- Close all the open positions with the specified magic number
         //--- Release and unpress the button
         ObjectSetInteger(0, "CloseAllMagicPositionsBtn", OBJPROP_STATE, false);
        }

      //--- Close all profitable positions with the specified symbol and magic number when the CloseAllProfitablePositionsBtn button is pressed
      if(sparam == "CloseAllProfitablePositionsBtn")
        {
         CloseAllProfitablePositions(_Symbol, magicNo);//-- Close all the open profitable positions with the specified symbol and magic number
         //--- Release and unpress the button
         ObjectSetInteger(0, "CloseAllProfitablePositionsBtn", OBJPROP_STATE, false);
        }

      //--- Close all loss positions with the specified symbol and magic number when the CloseAllLossPositionsBtn button is pressed
      if(sparam == "CloseAllLossPositionsBtn")
        {
         CloseAllLossPositions(_Symbol, magicNo);//-- Close all the open loss positions with the specified symbol and magic number
         //--- Release and unpress the button
         ObjectSetInteger(0, "CloseAllLossPositionsBtn", OBJPROP_STATE, false);
        }

      //--- Redraw the chart to refresh it
      ChartRedraw();
     }
//---
  }

Перед запуском советника PositionsTradePanel.mq5 нам нужно включить все необходимые компоненты в функции обработки событий OnInit(), OnDeinit() и OnTick(). Начнем с загрузки всех графических объектов во время инициализации советника функцией LoadChartObjects() в функции OnInit().

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
//---
//create buttons to demonstrate how the different ex5 library functions work
   LoadChartObjects();
//---
   return(INIT_SUCCEEDED);
  }

Вызовем и выполним следующие функции в OnTick().

//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
  {
//-- Check for profitable positions and set the trailing stop loss on every new tick
   SetTrailingSl(); //-- Calls the ex5 library function responsible for setting Trailing stops
   LoadChartObjects(); //--- Update chart objects
  }

Добавим функцию деинициализации в обработчик событий OnDeinit() для удаления всех графических объектов при закрытии или завершении работы советника.

//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
//---
//-- Clean up the chart
   DeleteChartObjects();

//-- Clear any chart comments
   Comment("");
  }

Файл исходного кода для советника PositionsManagerPanel.mq5 приложен в конце статьи.

Сохраним, скомпилируем и загрузим советник PositionsManagerPanel.mq5 на любой график в MetaTrader 5. На вкладке Dependencies (зависимости) вы увидите все прототипы библиотечных функций, импортированные из PositionsManager.ex5, а также полный путь к файлу, где сохранена EX5-библиотека. После загрузки на график протестируем его, открыв несколько позиций и проверив записи на вкладке "Эксперты" в панели "Инструменты" торгового терминала. Мы должны увидеть записи от прототипов функций PositionsManager.ex5.

Вкладка Dependencies панели управления позициями


Заключение

Мы всесторонне рассмотрели EX5-библиотеку для MQL5. Мы увидели их создание, интеграцию и внедрение во внешние MQL5-проекты, включая способы отладки различных ошибок EX5-библиотеки, а также способы их обновления и повторного развертывания. Кроме того, мы создали мощную многофункциональную EX5-библиотеку для управления позициями, дополненную подробной документацией и практическими примерами использования. Я также продемонстрировал, как импортировать и реализовать эту библиотеку в двух различных MQL5-советниках, таким образом предоставив реальные примеры эффективного развертывания EX5-библиотеки. В следующей статье мы воспользуемся аналогичным подходом для разработки комплексной EX5-библиотеки для управления отложенными ордерами, призванной упростить задачи обработки отложенных ордеров в ваших MQL5-приложениях. Спасибо за внимание! Желаю вам всяческих успехов в торговле и программировании!


Перевод с английского произведен MetaQuotes Ltd.
Оригинальная статья: https://www.mql5.com/en/articles/15224

Прикрепленные файлы |
DualVidyaTrader.mq5 (10.77 KB)
Создание советника Daily Drawdown Limiter на языке MQL5 Создание советника Daily Drawdown Limiter на языке MQL5
В статье подробно рассматриваются возможности реализации советника на основе торгового алгоритма. Это поможет автоматизировать систему на MQL5 и взять под контроль дневную просадку.
Квантовые вычисления и трейдинг: Новый взгляд на прогнозы цен Квантовые вычисления и трейдинг: Новый взгляд на прогнозы цен
В статье рассматривается инновационный подход к прогнозированию движения цен на финансовых рынках с использованием квантовых вычислений. Основное внимание уделяется применению алгоритма квантовой оценки фазы (QPE) для поиска продобразов ценовых паттернов, что позволяет значительно ускорить процесс анализа рыночных данных.
От начального до среднего уровня: Переменные (II) От начального до среднего уровня: Переменные (II)
Сегодня мы рассмотрим, как работать со статическими переменными. Этот вопрос часто ставит в тупик многих программистов, как начинающих, так и имеющих некоторый опыт, и это связано с тем, что существует несколько советов и рекомендаций, которые необходимо соблюдать при использовании данного механизма. Представленные здесь материалы предназначены исключительно для дидактических целей. Ни в коем случае нельзя рассматривать это как приложение, чьей целью будет что-то иное помимо изучения и освоения представленных концепций.
Нейросети в трейдинге: Многоагентная система с концептуальным подтверждением (Окончание) Нейросети в трейдинге: Многоагентная система с концептуальным подтверждением (Окончание)
Продолжаем реализацию подходов, предложенных авторами фреймворка FinCon. FinCon является многоагентной системой, основанной на больших языковых моделях (LLM). Сегодня мы реализуем необходимые модули и проведем комплексное тестирование модели на реальных исторических данных.