English 中文 Español Deutsch 日本語
preview
Создание торговой панели администратора на MQL5 (Часть VI): Панель управления торговлей (II)

Создание торговой панели администратора на MQL5 (Часть VI): Панель управления торговлей (II)

MetaTrader 5Примеры |
305 0
Clemence Benjamin
Clemence Benjamin

Содержание

Введение

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

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

Вертикальное пространство MetaTrader 5

Вертикальная шкала графика MetaTrader 5 создает пробелы и делает вид диапазона свечей более четким

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

  1. У тех, кто сталкивается с ошибками компиляции, это, скорее всего, связано с запуском кода с файлами библиотеки по умолчанию, которые не были расширены. Чтобы решить эту проблему, обязательно перезапишите содержимое каталога MQL5/Include/controls файлами из папки Extended Header, представленной в предыдущих статьях.
  2. Другая задача заключается в том, чтобы убедиться, что идентификатор чата и токен бота обновлены и соответствуют вашим данным, таким образом чтобы код подтверждения был отправлен на ваш аккаунт.

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

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

Мы уже обсуждали это ранее, и необходимые шаги легко выполнимы.

ШАГ 1:

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

ШАГ 2:

Начните чат с BotFather, чтобы создать двух ботов: одного - для двухфакторной аутентификации (2FA), а другого - для общения. Для каждого бота вы получите уникальный токен, который можно использовать для доступа к API Telegram и получения идентификаторов чатов. У каждого бота есть свой собственный токен, и вы можете называть их по своему усмотрению, что позволит вам легко определить их назначение.

ШАГ 3:

Получите доступ к API по следующей ссылке: https://api.telegram.org/botReplaceMeWithTheTokenFromTheBotFather/getUpdates.

Empty API

Telegram API в браузере Chrome

ШАГ 4:

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

Сообщение Telegram отправлено боту

Чат Telegram: начните разговор с ботом (в этом случае я назвал своего бота Admin Panel)

API-сообщения

Telegram API: Здесь мы можем получить идентификатор чата для 2FA

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

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

Добавление бота в канал в качестве администратора

Добавление бота в качестве администратора в Telegram-канал

Наконец, ниже показан API JSON с идентификатором чата канала.

API JASON

JSON, сгенерированный чатом канала

В этом фрагменте кода вам следует ввести учетные данные бота для доставки кода 2FA:
// Constants for 2FA. Here put the chat ID dedicated for verification code delivery. It must be kept secure always.
const string Hardcoded2FAChatId = "REPLACE WITH YOUR CHAT ID";
const string Hardcoded2FABotToken = "REPLACE WITH YOU BOT TOKEN";  //Obtain your bot token from a telegram bot father.

//If you don't already have a bot you can start the chat with the bot father to create one.

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

input launch

Параметры инициализации


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

На протяжении всех обсуждений мы использовали один и тот же PIN-код - 2024. Вы можете настроить его для собственного использования в исходном коде. Для 2FA алгоритм генератора шестизначных кодов сделает это автоматически. Вам нужно только убедиться, что у вас есть уникальный идентификатор чата для безопасной доставки кода.

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


Улучшение панели управления торговлей

Во-первых, я хотел бы, чтобы вы поняли разницу между кнопками панели коммуникаций (Communication Panel) и панели управления торговлей (Trading Management). Я думаю, вы знакомы с функционалом массовых операций, доступным как в настольной, так и в мобильной версии MetaTrader 5.

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

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

Основные различия представлены на изображениях ниже.

Панель коммуникации

Панель коммуникации: кнопки обмена сообщениями

Почти все кнопки здесь предназначены для коммуникации, за исключением кнопок навигации. Кроме того, отправляемые ими быстрые сообщения можно настроить во время инициализации. Кнопка быстрого сообщения Close All (закрыть все) предназначена исключительно для отправки сообщения указанному клиенту Telegram и не выполняет никаких других действий.

Панель управления торговлей

Панель управления торговлей: (Эти кнопки должны выполнять торговые операции при нажатии)

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


Настройка макета управления торговлей

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

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

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

Во время инициализации наша панель создается с использованием следующего фрагмента кода:

if (!tradeManagementPanel.Create(ChartID(), "Trade Management Panel", 0, 30, 30, 500, 500))
    {
        Print("Failed to create Communictions panel dialog");
        return INIT_FAILED;
    }

В приведенном выше коде:

x1 =30

x2 =500

Следовательно, ширина = X2-X1 = 500-30 = 470.

Если мы увеличим значение X2, новая ширина увеличится в соответствии с дизайном. Например, я увеличу ее на 50% от исходной ширины. Смотрите фрагмент кода ниже.

// Let's increase our panel width by 50% of the former panel and is likely to be 250px but we will add an extra 30px to cover for the initial px on x1 co-ordinate.
// For the y co-ordinate we will reduce so much to 150px
if (!tradeManagementPanel.Create(ChartID(), "Trade Management Panel", 0, 30, 30, 780, 150))
    {
        Print("Failed to create Communictions panel dialog");
        return INIT_FAILED;
    }

Результат после компиляции и тестирования следующий:

новая компоновка панели

Новый макет торговой панели

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

// Create the Trade Management Panel controls
// Here we adjusted our button coordinates to fit well in the new Trade Management Panel
bool CreateTradeManagementControls()
{
    long chart_id = ChartID();
    
    // Buy Button
    if (!buyButton.Create(chart_id, "BuyButton", 0, 130, 5, 210, 40))
    {
        Print("Failed to create Buy button");
        return false;
    }
    buyButton.Text("Buy");
    tradeManagementPanel.Add(buyButton);
    
    // Sell Button
    if (!sellButton.Create(chart_id, "SellButton", 0, 220, 5, 320, 40))
    {
        Print("Failed to create Sell button");
        return false;
    }
    sellButton.Text("Sell");
    tradeManagementPanel.Add(sellButton);

    // Close Position Button
    if (!closePosButton.Create(chart_id, "ClosePosButton", 0, 130, 50, 260, 70))
    {
        Print("Failed to create Close Position button");
        return false;
    }
    closePosButton.Text("Close Position");
    tradeManagementPanel.Add(closePosButton);

    // Modify Position Button
    if (!modifyPosButton.Create(chart_id, "ModifyPosButton", 0, 270, 50, 410, 70))
    {
        Print("Failed to create Modify Position button");
        return false;
    }
    modifyPosButton.Text("Modify Position");
    tradeManagementPanel.Add(modifyPosButton);

    // Set Stop-Loss Button
    if (!setSLButton.Create(chart_id, "SetSLButton", 0, 330, 5, 430, 40))
    {
        Print("Failed to create Set Stop-Loss button");
        return false;
    }
    setSLButton.Text("Set SL");
    tradeManagementPanel.Add(setSLButton);

    // Set Take-Profit Button
    if (!setTPButton.Create(chart_id, "SetTPButton", 0, 440, 5, 540, 40))
    {
        Print("Failed to create Set Take-Profit button");
        return false;
    }
    setTPButton.Text("Set TP");
    tradeManagementPanel.Add(setTPButton);

    return true;
}

Компиляция и запуск кода привели к следующему изображению, на котором все кнопки теперь размещены правильно.

Кнопки теперь расположены удобно.

Панель управления торговлей: кнопки расположены удобно

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

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

Быстрые кнопки перекрывают панель администратора

Чтобы решить эту проблему, мы применим сдвиг к нашим x-координатам, переместив их вправо, чтобы преобразовать положение панели. Во фрагменте кода ниже представлено обновленное значение:

//1
//2
//2
if (!tradeManagementPanel.Create(ChartID(), "Trade Management Panel", 0, 260, 30, 1040, 150))
    {
        Print("Failed to create Communictions panel dialog");
        return INIT_FAILED;
    }
Компиляция прошла успешно, и проблема была решена. Смотрите макет ниже:


После смещения панели по оси X

Кнопки быстрого доступа удобно расположены и не перекрывают друг друга.


Вспомогательная функция и создание новой кнопки

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

Если коротко, вспомогательные функции MQL5 можно сравнить с "функциональными объектами", которые параметризуют поведение, тогда как утилиты повтора C++ больше связаны с применением последовательных преобразований или шаблонов.

Вот некоторые сходства между ними: 

  • Оба направлены на сокращение повторяющегося кода.
  • Оба повышают ясность кода и удобство его поддержки.

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

Одна из функций, которая мне особенно нравится при управлении несколькими сделками, — это возможность оптимизировать процесс, что позволяет мне эффективно обрабатывать несколько сделок одновременно.

Массовые торговые операции

Массовые операции: используются в MetaTrader 5 для управления большим количеством сделок

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

Ниже приведено описание кнопок:

  • Close all Positions (закрыть все позиции)
  • Close Profitable Positions (закрыть прибыльные позиции)
  • Close Losing Positions (закрыть убыточные позиции)
  • Close Buy Positions (закрыть позиции на покупку)
  • Close Sell Positions (закрыть позиции на продажу)
  • Delete All Orders (удалить все ордера)
  • Delete Limit Orders (удалить лимитные ордера)
  • Delete Stop Orders (удалить стоп-ордера)
  • Delete Stop Limit Orders (удалить стоп-лимитные ордера)

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

Сначала мы объявляем наши кнопки как глобальные переменные:

// Button Declarations
CButton buyButton;                 // Button for Buy operations
CButton sellButton;                // Button for Sell operations
CButton closeAllButton;            // Button for closing all positions
CButton closeProfitButton;         // Button for closing profitable positions
CButton closeLossButton;           // Button for closing losing positions
CButton closeBuyButton;            // Button for closing Buy positions
CButton closeSellButton;           // Button for closing Sell positions
CButton deleteAllOrdersButton;     // Button for deleting all orders
CButton deleteLimitOrdersButton;   // Button for deleting limit orders
CButton deleteStopOrdersButton;    // Button for deleting stop orders
CButton deleteStopLimitOrdersButton; // Button for deleting stop limit orders
        

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

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

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

Вот код нашей вспомогательной функции:

//Helper Function For seamless Button creation 
bool CreateButton(CButton &button, const string name, const string text, int x1, int y1, int x2, int y2)
{
    long chart_id = ChartID();
    
    if (!button.Create(chart_id, name, 0, x1, y1, x2, y2))
    {
        Print("Failed to create button: ", name);
        return false;
    }
    
    button.Text(text);
    tradeManagementPanel.Add(button);
    return true;
}

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

Модульная конструкция, реализованная с помощью CreateButton, сохраняет эту высокоуровневую функцию лаконичной и сосредоточенной на ее основной цели: структурировании макета панели. Вместе эти две функции работают согласованно — CreateTradeManagementControls обрабатывает структуру, делегируя повторяющиеся задачи CreateButton, — создавая чистую, эффективную и адаптируемую реализацию панели управления торговлей. Код всех кнопок представлен ниже

//+------------------------------------------------------------------+
//| Create Trade Management Controls                                 |
//+------------------------------------------------------------------+
bool CreateTradeManagementControls()
{
    // Coordinates for buttons (adjust as needed)
    const int Y1_TOP = 5, Y2_TOP = 40;
    const int Y1_MID = 50, Y2_MID = 70;
    const int Y1_BOTTOM = 80, Y2_BOTTOM = 100;

    // Buy Button
    if (!CreateButton(buyButton, "BuyButton", "Buy", 130, Y1_TOP, 210, Y2_TOP)) return false;

    // Sell Button
    if (!CreateButton(sellButton, "SellButton", "Sell", 220, Y1_TOP, 320, Y2_TOP)) return false;
    
    // Close All Positions Button
    if (!CreateButton(closeAllButton, "CloseAllButton", "Close All", 130, Y1_MID, 230, Y2_MID)) return false;

    // Close Profitable Positions Button
    if (!CreateButton(closeProfitButton, "CloseProfitButton", "Close Profitable", 240, Y1_MID, 380, Y2_MID)) return false;

    // Close Losing Positions Button
    if (!CreateButton(closeLossButton, "CloseLossButton", "Close Losing", 390, Y1_MID, 510, Y2_MID)) return false;

    // Close Buy Positions Button
    if (!CreateButton(closeBuyButton, "CloseBuyButton", "Close Buys", 520, Y1_MID, 620, Y2_MID)) return false;

    // Close Sell Positions Button
    if (!CreateButton(closeSellButton, "CloseSellButton", "Close Sells", 630, Y1_MID, 730, Y2_MID)) return false;

    // Delete All Orders Button
    if (!CreateButton(deleteAllOrdersButton, "DeleteAllOrdersButton", "Delete All Orders", 130, Y1_BOTTOM , 270, Y2_BOTTOM )) return false;

    // Delete Limit Orders Button
    if (!CreateButton(deleteLimitOrdersButton, "DeleteLimitOrdersButton", "Delete Limits", 275, Y1_BOTTOM , 385, Y2_BOTTOM )) return false;

    // Delete Stop Orders Button
    if (!CreateButton(deleteStopOrdersButton, "DeleteStopOrdersButton", "Delete Stops", 390, Y1_BOTTOM , 515, Y2_BOTTOM )) return false;

    // Delete Stop Limit Orders Button
    if (!CreateButton(deleteStopLimitOrdersButton, "DeleteStopLimitOrdersButton", "Delete Stop Limits", 520, Y1_BOTTOM , 660, Y2_BOTTOM )) return false;

    return true; // All buttons created successfully
}

Вот результат, полученный с применением нового макета

Новая компоновка панели управления торговлей

Новый макет после интеграции новых кнопок

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


Реализация обработчиков кнопок

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

1. Обработчик кнопки Buy (OnBuyButtonClick)

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

//+------------------------------------------------------------------+
//| Handle Buy button click                                          |
//+------------------------------------------------------------------+
void OnBuyButtonClick()
{ 
    CTrade trade;
    double lotSize = 0.1; // Example lot size
    double slippage = 3;  // Example slippage
    double stopLoss = 0;  // Example stop loss (in points)
    double takeProfit = 0; // Example take profit (in points)

    // Open Buy order
 double askPrice;
if (SymbolInfoDouble(Symbol(), SYMBOL_ASK, askPrice) && askPrice > 0)
{
    if (trade.Buy(lotSize, Symbol(), askPrice, slippage, stopLoss, takeProfit))
    {
        Print("Buy order executed successfully.");
    }
    else
    {
        Print("Failed to execute Buy order. Error: ", GetLastError());
    }
}
else
{
    Print("Failed to retrieve Ask price. Error: ", GetLastError());
}


    // Execute Buy order logic here
    Print("Executing Buy operation");
}

2. Обработчик кнопки Sell (OnSellButtonClick)

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

//+------------------------------------------------------------------+
//| Handle Sell button click                                         |
//+------------------------------------------------------------------+
void OnSellButtonClick()
{
    CTrade trade;
    double lotSize = 0.1; // Example lot size
    double slippage = 3;  // Example slippage
    double stopLoss = 0;  // Example stop loss (in points)
    double takeProfit = 0; // Example take profit (in points)

    double bidPrice;
if (SymbolInfoDouble(Symbol(), SYMBOL_BID, bidPrice) && bidPrice > 0)
{
    // Open Sell order
    if (trade.Sell(lotSize, Symbol(), bidPrice, slippage, stopLoss, takeProfit))
    {
        Print("Sell order opened successfully.");
    }
    else
    {
        Print("Error opening sell order: ", trade.ResultRetcode());
    }
}
else
{
    Print("Failed to retrieve Bid price. Error: ", GetLastError());
}
}

3. Обработчик Close All Positions (OnCloseAllButtonClick)

Функция автоматизирует закрытие всех активных позиций, перебирая открытые сделки и используя CTrade. PositionClose для исполнения. Это особенно полезно для трейдеров, стремящихся быстро выйти из всех сделок для защиты от внезапной волатильности рынка или выполнения требования стратегии выхода.

//+------------------------------------------------------------------+
//| Handle Close All button click                                    |
//+------------------------------------------------------------------+
void OnCloseAllButtonClick()
{
    CPositionInfo position;
    for (int i = 0; i < PositionsTotal(); i++)
    {
        if (position.SelectByIndex(i))
        {
            CTrade trade;
            if (position.Type() == POSITION_TYPE_BUY)
                trade.PositionClose(position.Ticket());
            else if (position.Type() == POSITION_TYPE_SELL)
                trade.PositionClose(position.Ticket());
        }
    }
    Print("All positions closed.");
}

4. Обработчик Close Profitable Positions (OnCloseProfitButtonClick)

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

//+------------------------------------------------------------------+
//| Handle Close Profitable button click                             |
//+------------------------------------------------------------------+
void OnCloseProfitButtonClick()
{
    CPositionInfo position;
    for (int i = 0; i < PositionsTotal(); i++)
    {
        if (position.SelectByIndex(i) && position.Profit() > 0)
        {
            CTrade trade;
            if (position.Type() == POSITION_TYPE_BUY)
                trade.PositionClose(position.Ticket());
            else if (position.Type() == POSITION_TYPE_SELL)
                trade.PositionClose(position.Ticket());
        }
    }
    Print("Profitable positions closed.");
}

5. Обработчик Close Losing Positions (OnCloseLossButtonClick)

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

//+------------------------------------------------------------------+
//| Handle Close Losing button click                                 |
//+------------------------------------------------------------------+
void OnCloseLossButtonClick()
{
    CPositionInfo position;
    for (int i = 0; i < PositionsTotal(); i++)
    {
        if (position.SelectByIndex(i) && position.Profit() < 0)
        {
            CTrade trade;
            if (position.Type() == POSITION_TYPE_BUY)
                trade.PositionClose(position.Ticket());
            else if (position.Type() == POSITION_TYPE_SELL)
                trade.PositionClose(position.Ticket());
        }
    }
    Print("Losing positions closed.");
}


void OnCloseBuyButtonClick()
{
    // Close Buy positions logic
    Print("Closing Buy positions");
}

void OnCloseSellButtonClick()
{
    // Close Sell positions logic
    Print("Closing Sell positions");
}

6. Обработчик Delete All Orders (OnDeleteAllOrdersButtonClick)

Функция удаляет все отложенные ордера, гарантируя, что остаточные лимитные или стоп-ордера не повлияют на счет. Использование класса COrderInfo для извлечения и отмены ордеров помогает поддерживать портфель ордеров в порядке и предотвращает непреднамеренное выполнение.

//+------------------------------------------------------------------+
//| Handle Delete All Orders button click                            |
//+------------------------------------------------------------------+
void OnDeleteAllOrdersButtonClick()
{
    COrderInfo order;
    for (int i = 0; i < OrdersTotal(); i++)
    {
        if (order.SelectByIndex(i))
        {
            CTrade trade;
            trade.OrderDelete(order.Ticket());
        }
    }
    Print("All orders deleted.");
}

7. Обработчик Delete Limit Orders (OnDeleteLimitOrdersButtonClick)

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

//+------------------------------------------------------------------+
//| Handle Delete Limit Orders button click                          |
//+------------------------------------------------------------------+
void OnDeleteLimitOrdersButtonClick()
{
    COrderInfo order;
    for (int i = 0; i < OrdersTotal(); i++)
    {
        if (order.SelectByIndex(i) && order.Type() == ORDER_TYPE_BUY_STOP_LIMIT)
        {
            CTrade trade;
            trade.OrderDelete(order.Ticket());
        }
    }
    Print("All limit orders deleted.");
}

8. Обработчик Delete Stop Orders (OnDeleteStopOrdersButtonClick)

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

//+------------------------------------------------------------------+
//| Handle Delete Stop Orders button click                           |
//+------------------------------------------------------------------+
void OnDeleteStopOrdersButtonClick()
{
    COrderInfo order;
    for (int i = 0; i < OrdersTotal(); i++)
    {
        if (order.SelectByIndex(i) && order.Type() == ORDER_TYPE_BUY_STOP && ORDER_TYPE_SELL_STOP)
        {
            CTrade trade;
            trade.OrderDelete(order.Ticket());
        }
    }
    Print("All stop orders deleted.");
}

9. Обработчик Delete Stop Limit Orders (OnDeleteStopLimitOrdersButtonClick)

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

//+------------------------------------------------------------------+
//| Handle Delete Stop Limit Orders button click                     |
//+------------------------------------------------------------------+
void OnDeleteStopLimitOrdersButtonClick()
{
    COrderInfo order;
    for (int i = 0; i < OrdersTotal(); i++)
    {
        if (order.SelectByIndex(i) && order.Type() == ORDER_TYPE_BUY_LIMIT && ORDER_TYPE_SELL_STOP_LIMIT)
        {
            CTrade trade;
            trade.OrderDelete(order.Ticket());
        }
    }
    Print("All stop limit orders deleted.");
}

Интеграция в OnChartEvent:

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

//+------------------------------------------------------------------+
//| Handle chart events                                              |
//+------------------------------------------------------------------+
void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam)
{
    if (id == CHARTEVENT_OBJECT_CLICK)
    {
        // Panel navigation buttons
        if (sparam == "HomeButtonComm")
        {
            adminHomePanel.Show();
            communicationsPanel.Hide();
        }
        else if (sparam == "HomeButtonTrade")
        {
            adminHomePanel.Show();
            tradeManagementPanel.Hide();
        }

        if (sparam == "TradeMgmtAccessButton")
        {
            tradeManagementPanel.Show();
            adminHomePanel.Hide();
        }
        else if (sparam == "CommunicationsPanelAccessButton")
        {
            communicationsPanel.Show();
            adminHomePanel.Hide();
        }

        // Control buttons for panel resizing and closing
        else if (sparam == "MinimizeButton")
        {
            OnMinimizeButtonClick();
        }
        else if (sparam == "MaximizeButton")
        {
            OnMaximizeButtonClick();
        }
        else if (sparam == "CloseButton")
        {
            ExpertRemove();
            
        }
        {
        if (sparam == "LoginButton")
        {
            OnLoginButtonClick();
        }
        else if (sparam == "CloseAuthButton")
        {
            OnCloseAuthButtonClick();
        }
        else if (sparam == "TwoFALoginButton")
        {
            OnTwoFALoginButtonClick();
        }
        else if (sparam == "Close2FAButton")
        {
            OnClose2FAButtonClick();
        }
    }

    switch (id)
    {
        case CHARTEVENT_OBJECT_CLICK:
            if (sparam == "SendButton") OnSendButtonClick();
            else if (sparam == "ClearButton") OnClearButtonClick();
            else if (sparam == "ChangeFontButton") OnChangeFontButtonClick();
            else if (sparam == "ToggleThemeButton") OnToggleThemeButtonClick();
            else if (sparam == "MinimizeButton") OnMinimizeButtonClick();
            else if (sparam == "MaximizeButton") OnMaximizeButtonClick();
            else if (sparam == "CloseButton") OnCloseButtonClick();
            else if (StringFind(sparam, "QuickMessageButton") != -1)
            {
                long index = StringToInteger(StringSubstr(sparam, 18));
                OnQuickMessageButtonClick(index - 1);
            }
            break;

        case CHARTEVENT_OBJECT_ENDEDIT:
            if (sparam == "InputBox") OnInputChange();
            break;
    }
    
  } 

        // Trade management buttons
        if (sparam == "BuyButton") OnBuyButtonClick();
        else if (sparam == "SellButton") OnSellButtonClick();
        else if (sparam == "CloseAllButton") OnCloseAllButtonClick();
        else if (sparam == "CloseProfitButton") OnCloseProfitButtonClick();
        else if (sparam == "CloseLossButton") OnCloseLossButtonClick();
        else if (sparam == "CloseBuyButton") OnCloseBuyButtonClick();
        else if (sparam == "CloseSellButton") OnCloseSellButtonClick();
        else if (sparam == "DeleteAllOrdersButton") OnDeleteAllOrdersButtonClick();
        else if (sparam == "DeleteLimitOrdersButton") OnDeleteLimitOrdersButtonClick();
        else if (sparam == "DeleteStopOrdersButton") OnDeleteStopOrdersButtonClick();
        else if (sparam == "DeleteStopLimitOrdersButton") OnDeleteStopLimitOrdersButtonClick();
    }

Итак, в нашем окончательном коде довольно много строк:

//+------------------------------------------------------------------+
//|                                             Admin Panel.mq5      |
//|                           Copyright 2024, Clemence Benjamin      |
//|        https://www.mql5.com/en/users/billionaire2024/seller      |
//+------------------------------------------------------------------+
#property copyright "Copyright 2024, Clemence Benjamin"
#property link      "https://www.mql5.com/en/users/billionaire2024/seller"
#property description "A secure and responsive Admin Panel. Send messages to your telegram clients without leaving MT5"
#property version   "1.22"

#include <Trade\Trade.mqh>
#include <Controls\Dialog.mqh>
#include <Controls\Button.mqh>
#include <Controls\Edit.mqh>
#include <Controls\Label.mqh>

// Input parameters for quick messages
input string QuickMessage1 = "Updates";
input string QuickMessage2 = "Close all";
input string QuickMessage3 = "In deep profits";
input string QuickMessage4 = "Hold position";
input string QuickMessage5 = "Swing Entry";
input string QuickMessage6 = "Scalp Entry";
input string QuickMessage7 = "Book profit";
input string QuickMessage8 = "Invalid Signal";
input string InputChatId = "YOUR_CHAT_ID";
input string InputBotToken = "YOUR_BOT_TOKEN";

// Constants for 2FA
const string Hardcoded2FAChatId = "Replace chat ID with yours";
const string Hardcoded2FABotToken = "Replace with your bot token";

// Global variables
CDialog adminHomePanel, tradeManagementPanel, communicationsPanel;
CDialog authentication, twoFactorAuth;
CButton homeButtonComm, homeButtonTrade;

CButton sendButton, clearButton, changeFontButton, toggleThemeButton;
CButton loginButton, closeAuthButton, twoFALoginButton, close2FAButton;
CButton quickMessageButtons[8], minimizeButton, maximizeButton, closeButton;
CButton tradeMgmtAccessButton, communicationsPanelAccessButton;
CEdit inputBox, passwordInputBox, twoFACodeInput;
CLabel charCounter, passwordPromptLabel, feedbackLabel, twoFAPromptLabel, twoFAFeedbackLabel;
bool minimized = false;
bool darkTheme = false;
int MAX_MESSAGE_LENGTH = 4096;
string availableFonts[] = { "Arial", "Courier New", "Verdana", "Times New Roman" };
int currentFontIndex = 0;
string Password = "2024"; // Hardcoded password
string twoFACode = "";

// Button Declarations for Trade Management
CButton buyButton;                 // Button for Buy operations
CButton sellButton;                // Button for Sell operations
CButton closeAllButton;            // Button for closing all positions
CButton closeProfitButton;         // Button for closing profitable positions
CButton closeLossButton;           // Button for closing losing positions
CButton closeBuyButton;            // Button for closing Buy positions
CButton closeSellButton;           // Button for closing Sell positions
CButton deleteAllOrdersButton;     // Button for deleting all orders
CButton deleteLimitOrdersButton;   // Button for deleting limit orders
CButton deleteStopOrdersButton;    // Button for deleting stop orders
CButton deleteStopLimitOrdersButton; // Button for deleting stop limit orders

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
{
    if (!ShowAuthenticationPrompt())
    {
        Print("Authorization failed. Exiting...");
        return INIT_FAILED;
    }

    if (!adminHomePanel.Create(ChartID(), "Admin Home Panel", 0, 30, 30, 500, 500))
    {
        Print("Failed to create Admin Home Panel");
        return INIT_FAILED;
    }

    if (!CreateAdminHomeControls())
    {
        Print("Home panel control creation failed");
        return INIT_FAILED;
    }

    if (!communicationsPanel.Create(ChartID(), "Communications Panel", 0, 30, 30, 500, 500))
    {
        Print("Failed to create Communications panel dialog");
        return INIT_FAILED;
    }

    if (!tradeManagementPanel.Create(ChartID(), "Trade Management Panel", 0,260, 30, 1040, 170))
    {
        Print("Failed to create Trade Management panel dialog");
        return INIT_FAILED;
    }

    if (!CreateControls())
    {
        Print("Control creation failed");
        return INIT_FAILED;
    }

    if (!CreateTradeManagementControls())
    {
        Print("Trade management control creation failed");
        return INIT_FAILED;
    }

    adminHomePanel.Hide(); // Hide home panel by default on initialization
    communicationsPanel.Hide(); // Hide the Communications Panel
    tradeManagementPanel.Hide(); // Hide the Trade Management Panel
    return INIT_SUCCEEDED;
}

//+------------------------------------------------------------------+
//| Handle chart events                                              |
//+------------------------------------------------------------------+
void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam)
{
    if (id == CHARTEVENT_OBJECT_CLICK)
    {
        // Panel navigation buttons
        if (sparam == "HomeButtonComm")
        {
            adminHomePanel.Show();
            communicationsPanel.Hide();
        }
        else if (sparam == "HomeButtonTrade")
        {
            adminHomePanel.Show();
            tradeManagementPanel.Hide();
        }

        if (sparam == "TradeMgmtAccessButton")
        {
            tradeManagementPanel.Show();
            adminHomePanel.Hide();
        }
        else if (sparam == "CommunicationsPanelAccessButton")
        {
            communicationsPanel.Show();
            adminHomePanel.Hide();
        }

        // Control buttons for panel resizing and closing
        else if (sparam == "MinimizeButton")
        {
            OnMinimizeButtonClick();
        }
        else if (sparam == "MaximizeButton")
        {
            OnMaximizeButtonClick();
        }
        else if (sparam == "CloseButton")
        {
            ExpertRemove();
            
        }
        {
        if (sparam == "LoginButton")
        {
            OnLoginButtonClick();
        }
        else if (sparam == "CloseAuthButton")
        {
            OnCloseAuthButtonClick();
        }
        else if (sparam == "TwoFALoginButton")
        {
            OnTwoFALoginButtonClick();
        }
        else if (sparam == "Close2FAButton")
        {
            OnClose2FAButtonClick();
        }
    }

    switch (id)
    {
        case CHARTEVENT_OBJECT_CLICK:
            if (sparam == "SendButton") OnSendButtonClick();
            else if (sparam == "ClearButton") OnClearButtonClick();
            else if (sparam == "ChangeFontButton") OnChangeFontButtonClick();
            else if (sparam == "ToggleThemeButton") OnToggleThemeButtonClick();
            else if (sparam == "MinimizeButton") OnMinimizeButtonClick();
            else if (sparam == "MaximizeButton") OnMaximizeButtonClick();
            else if (sparam == "CloseButton") OnCloseButtonClick();
            else if (StringFind(sparam, "QuickMessageButton") != -1)
            {
                long index = StringToInteger(StringSubstr(sparam, 18));
                OnQuickMessageButtonClick(index - 1);
            }
            break;

        case CHARTEVENT_OBJECT_ENDEDIT:
            if (sparam == "InputBox") OnInputChange();
            break;
    }
    
  } 

        // Trade management buttons
        if (sparam == "BuyButton") OnBuyButtonClick();
        else if (sparam == "SellButton") OnSellButtonClick();
        else if (sparam == "CloseAllButton") OnCloseAllButtonClick();
        else if (sparam == "CloseProfitButton") OnCloseProfitButtonClick();
        else if (sparam == "CloseLossButton") OnCloseLossButtonClick();
        else if (sparam == "CloseBuyButton") OnCloseBuyButtonClick();
        else if (sparam == "CloseSellButton") OnCloseSellButtonClick();
        else if (sparam == "DeleteAllOrdersButton") OnDeleteAllOrdersButtonClick();
        else if (sparam == "DeleteLimitOrdersButton") OnDeleteLimitOrdersButtonClick();
        else if (sparam == "DeleteStopOrdersButton") OnDeleteStopOrdersButtonClick();
        else if (sparam == "DeleteStopLimitOrdersButton") OnDeleteStopLimitOrdersButtonClick();
    }


//+------------------------------------------------------------------+
//| Trade management button handlers                                 |
//+------------------------------------------------------------------+
void OnBuyButtonClick()
{ 
    CTrade trade;
    double lotSize = 0.1; //  lot size
    double slippage = 3;  // slippage
    double stopLoss = 0;  //  stop loss (in points)
    double takeProfit = 0; //  take profit (in points)

    // Open Buy order
 double askPrice;
if (SymbolInfoDouble(Symbol(), SYMBOL_ASK, askPrice) && askPrice > 0)
{
    if (trade.Buy(lotSize, Symbol(), askPrice, slippage, stopLoss, takeProfit))
    {
        Print("Buy order executed successfully.");
    }
    else
    {
        Print("Failed to execute Buy order. Error: ", GetLastError());
    }
}
else
{
    Print("Failed to retrieve Ask price. Error: ", GetLastError());
}


    // Execute Buy order logic here
    Print("Executing Buy operation");
}

//+------------------------------------------------------------------+
//| Handle Sell button click                                         |
//+------------------------------------------------------------------+
void OnSellButtonClick()
{
    CTrade trade;
    double lotSize = 0.1; // lot size
    double slippage = 3;  //  slippage
    double stopLoss = 0;  // stop loss (in points)
    double takeProfit = 0; //  take profit (in points)

    double bidPrice;
if (SymbolInfoDouble(Symbol(), SYMBOL_BID, bidPrice) && bidPrice > 0)
{
    // Open Sell order
    if (trade.Sell(lotSize, Symbol(), bidPrice, slippage, stopLoss, takeProfit))
    {
        Print("Sell order opened successfully.");
    }
    else
    {
        Print("Error opening sell order: ", trade.ResultRetcode());
    }
}
else
{
    Print("Failed to retrieve Bid price. Error: ", GetLastError());
}
}

//+------------------------------------------------------------------+
//| Handle Close All button click                                    |
//+------------------------------------------------------------------+
void OnCloseAllButtonClick()
{
    CPositionInfo position;
    for (int i = 0; i < PositionsTotal(); i++)
    {
        if (position.SelectByIndex(i))
        {
            CTrade trade;
            if (position.Type() == POSITION_TYPE_BUY)
                trade.PositionClose(position.Ticket());
            else if (position.Type() == POSITION_TYPE_SELL)
                trade.PositionClose(position.Ticket());
        }
    }
    Print("All positions closed.");
}


//+------------------------------------------------------------------+
//| Handle Close Profitable button click                             |
//+------------------------------------------------------------------+
void OnCloseProfitButtonClick()
{
    CPositionInfo position;
    for (int i = 0; i < PositionsTotal(); i++)
    {
        if (position.SelectByIndex(i) && position.Profit() > 0)
        {
            CTrade trade;
            if (position.Type() == POSITION_TYPE_BUY)
                trade.PositionClose(position.Ticket());
            else if (position.Type() == POSITION_TYPE_SELL)
                trade.PositionClose(position.Ticket());
        }
    }
    Print("Profitable positions closed.");
}


//+------------------------------------------------------------------+
//| Handle Close Losing button click                                 |
//+------------------------------------------------------------------+
void OnCloseLossButtonClick()
{
    CPositionInfo position;
    for (int i = 0; i < PositionsTotal(); i++)
    {
        if (position.SelectByIndex(i) && position.Profit() < 0)
        {
            CTrade trade;
            if (position.Type() == POSITION_TYPE_BUY)
                trade.PositionClose(position.Ticket());
            else if (position.Type() == POSITION_TYPE_SELL)
                trade.PositionClose(position.Ticket());
        }
    }
    Print("Losing positions closed.");
}

void OnCloseBuyButtonClick()
{
    // Close Buy positions logic
    Print("Closing Buy positions");
}

void OnCloseSellButtonClick()
{
    // Close Sell positions logic
    Print("Closing Sell positions");
}

//+------------------------------------------------------------------+
//| Handle Delete All Orders button click                            |
//+------------------------------------------------------------------+
void OnDeleteAllOrdersButtonClick()
{
    COrderInfo order;
    for (int i = 0; i < OrdersTotal(); i++)
    {
        if (order.SelectByIndex(i))
        {
            CTrade trade;
            trade.OrderDelete(order.Ticket());
        }
    }
    Print("All orders deleted.");
}

//+------------------------------------------------------------------+
//| Handle Delete Limit Orders button click                          |
//+------------------------------------------------------------------+
void OnDeleteLimitOrdersButtonClick()
{
    COrderInfo order;
    for (int i = 0; i < OrdersTotal(); i++)
    {
        if (order.SelectByIndex(i) && order.Type() == ORDER_TYPE_BUY_STOP_LIMIT)
        {
            CTrade trade;
            trade.OrderDelete(order.Ticket());
        }
    }
    Print("All limit orders deleted.");
}


//+------------------------------------------------------------------+
//| Handle Delete Stop Orders button click                           |
//+------------------------------------------------------------------+
void OnDeleteStopOrdersButtonClick()
{
    COrderInfo order;
    for (int i = 0; i < OrdersTotal(); i++)
    {
        if (order.SelectByIndex(i) && order.Type() == ORDER_TYPE_BUY_STOP && ORDER_TYPE_SELL_STOP)
        {
            CTrade trade;
            trade.OrderDelete(order.Ticket());
        }
    }
    Print("All stop orders deleted.");
}

//+------------------------------------------------------------------+
//| Handle Delete Stop Limit Orders button click                     |
//+------------------------------------------------------------------+
void OnDeleteStopLimitOrdersButtonClick()
{
    COrderInfo order;
    for (int i = 0; i < OrdersTotal(); i++)
    {
        if (order.SelectByIndex(i) && order.Type() == ORDER_TYPE_BUY_LIMIT && ORDER_TYPE_SELL_STOP_LIMIT)
        {
            CTrade trade;
            trade.OrderDelete(order.Ticket());
        }
    }
    Print("All stop limit orders deleted.");
}

//+------------------------------------------------------------------+
//| Show authentication input dialog                                 |
//+------------------------------------------------------------------+
bool ShowAuthenticationPrompt()
{
    if (!authentication.Create(ChartID(), "Authentication", 0, 100, 100, 500, 300))
    {
        Print("Failed to create authentication dialog");
        return false;
    }

    if (!passwordInputBox.Create(ChartID(), "PasswordInputBox", 0, 20, 70, 260, 95))
    {
        Print("Failed to create password input box");
        return false;
    }
    authentication.Add(passwordInputBox);

    if (!passwordPromptLabel.Create(ChartID(), "PasswordPromptLabel", 0, 20, 20, 260, 40))
    {
        Print("Failed to create password prompt label");
        return false;
    }
    passwordPromptLabel.Text("Enter password: Access Admin Panel");
    authentication.Add(passwordPromptLabel);

    if (!feedbackLabel.Create(ChartID(), "FeedbackLabel", 0, 20, 140, 380, 160))
    {
        Print("Failed to create feedback label");
        return false;
    }
    feedbackLabel.Text("");
    feedbackLabel.Color(clrRed); // Red color for incorrect attempts
    authentication.Add(feedbackLabel);

    if (!loginButton.Create(ChartID(), "LoginButton", 0, 20, 120, 100, 140))
    {
        Print("Failed to create login button");
        return false;
    }
    loginButton.Text("Login");
    authentication.Add(loginButton);

    if (!closeAuthButton.Create(ChartID(), "CloseAuthButton", 0, 120, 120, 200, 140))
    {
        Print("Failed to create close button for authentication");
        return false;
    }
    closeAuthButton.Text("Close");
    authentication.Add(closeAuthButton);

    authentication.Show();
    ChartRedraw();
    return true;
}

//+------------------------------------------------------------------+
//| Show two-factor authentication input dialog                      |
//+------------------------------------------------------------------+
void ShowTwoFactorAuthPrompt()
{
    if (!twoFactorAuth.Create(ChartID(), "Two-Factor Authentication", 0, 100, 100, 500, 300))
    {
        Print("Failed to create 2FA dialog");
        return;
    }

    if (!twoFACodeInput.Create(ChartID(), "TwoFACodeInput", 0, 20, 70, 260, 95))
    {
        Print("Failed to create 2FA code input box");
        return;
    }
    twoFactorAuth.Add(twoFACodeInput);

    if (!twoFAPromptLabel.Create(ChartID(), "TwoFAPromptLabel", 0, 20, 20, 380, 40))
    {
        Print("Failed to create 2FA prompt label");
        return;
    }
    twoFAPromptLabel.Text("Enter the 2FA code sent to your Telegram:");
    twoFactorAuth.Add(twoFAPromptLabel);

    if (!twoFAFeedbackLabel.Create(ChartID(), "TwoFAFeedbackLabel", 0, 20, 140, 380, 160))
    {
        Print("Failed to create 2FA feedback label");
        return;
    }
    twoFAFeedbackLabel.Text("");
    twoFAFeedbackLabel.Color(clrRed); // Red color for incorrect 2FA attempts
    twoFactorAuth.Add(twoFAFeedbackLabel);

    if (!twoFALoginButton.Create(ChartID(), "TwoFALoginButton", 0, 20, 120, 100, 140))
    {
        Print("Failed to create 2FA login button");
        return;
    }
    twoFALoginButton.Text("Verify");
    twoFactorAuth.Add(twoFALoginButton);

    if (!close2FAButton.Create(ChartID(), "Close2FAButton", 0, 120, 120, 200, 140))
    {
        Print("Failed to create close button for 2FA");
        return;
    }
    close2FAButton.Text("Close");
    twoFactorAuth.Add(close2FAButton);

    twoFactorAuth.Show();
    ChartRedraw();
}

//+------------------------------------------------------------------+
//| Admin Home Panel controls creation                               |
//+------------------------------------------------------------------+
bool CreateAdminHomeControls()
{
    long chart_id = ChartID();

    if (!tradeMgmtAccessButton.Create(chart_id, "TradeMgmtAccessButton", 0, 50, 50, 250, 90))
    {
        Print("Failed to create Trade Management Access button");
        return false;
    }
    tradeMgmtAccessButton.Text("Trade Management Panel");
    adminHomePanel.Add(tradeMgmtAccessButton);

    if (!communicationsPanelAccessButton.Create(chart_id, "CommunicationsPanelAccessButton", 0, 50, 100, 250, 140))
    {
        Print("Failed to create Communications Panel Access button");
        return false;
    }
    communicationsPanelAccessButton.Text("Communications Panel");
    adminHomePanel.Add(communicationsPanelAccessButton);

    if (!minimizeButton.Create(chart_id, "MinimizeButton", 0, 375, -22, 405, 0))
    {
        Print("Failed to create minimize button");
        return false;
    }
    minimizeButton.Text("_");
    adminHomePanel.Add(minimizeButton);

    if (!maximizeButton.Create(chart_id, "MaximizeButton", 0, 405, -22, 435, 0))
    {
        Print("Failed to create maximize button");
        return false;
    }
    maximizeButton.Text("[ ]");
    adminHomePanel.Add(maximizeButton);

    if (!closeButton.Create(chart_id, "CloseButton", 0, 435, -22, 465, 0))
    {
        Print("Failed to create close button");
        return false;
    }
    closeButton.Text("X");
    adminHomePanel.Add(closeButton);

    return true;
}
//Helper Function seamless Button creation 
bool CreateButton(CButton &button, const string name, const string text, int x1, int y1, int x2, int y2)
{
    long chart_id = ChartID();
    
    if (!button.Create(chart_id, name, 0, x1, y1, x2, y2))
    {
        Print("Failed to create button: ", name);
        return false;
    }
    
    button.Text(text);
    tradeManagementPanel.Add(button);
    return true;
}

//+------------------------------------------------------------------+
//| Create Trade Management Controls (Buttons)                       |
//+------------------------------------------------------------------+
bool CreateTradeManagementControls()
{
    // Coordinates for buttons (adjust as needed)
    const int Y1_TOP = 5, Y2_TOP = 40;
    const int Y1_MID = 50, Y2_MID = 70;
    const int Y1_BOTTOM = 80, Y2_BOTTOM = 100;

    // Create Buttons
    if (!CreateButton(buyButton, "BuyButton", "Buy", 130, Y1_TOP, 210, Y2_TOP)) return false;
    if (!CreateButton(sellButton, "SellButton", "Sell", 220, Y1_TOP, 320, Y2_TOP)) return false;
    if (!CreateButton(closeAllButton, "CloseAllButton", "Close All", 130, Y1_MID, 230, Y2_MID)) return false;
    if (!CreateButton(closeProfitButton, "CloseProfitButton", "Close Profitable", 240, Y1_MID, 380, Y2_MID)) return false;
    if (!CreateButton(closeLossButton, "CloseLossButton", "Close Losing", 390, Y1_MID, 510, Y2_MID)) return false;
    if (!CreateButton(closeBuyButton, "CloseBuyButton", "Close Buys", 520, Y1_MID, 620, Y2_MID)) return false;
    if (!CreateButton(closeSellButton, "CloseSellButton", "Close Sells", 630, Y1_MID, 730, Y2_MID)) return false;
    if (!CreateButton(deleteAllOrdersButton, "DeleteAllOrdersButton", "Delete All Orders", 130, Y1_BOTTOM , 270, Y2_BOTTOM )) return false;
    if (!CreateButton(deleteLimitOrdersButton, "DeleteLimitOrdersButton", "Delete Limits", 275, Y1_BOTTOM , 385, Y2_BOTTOM )) return false;
    if (!CreateButton(deleteStopOrdersButton, "DeleteStopOrdersButton", "Delete Stops", 390, Y1_BOTTOM , 515, Y2_BOTTOM )) return false;
    if (!CreateButton(deleteStopLimitOrdersButton, "DeleteStopLimitOrdersButton", "Delete Stop Limits", 520, Y1_BOTTOM , 660, Y2_BOTTOM )) return false;

    return true; // All buttons created successfully
}

//+------------------------------------------------------------------+
//| Handle login button click                                        |
//+------------------------------------------------------------------+
void OnLoginButtonClick()
{
    string enteredPassword = passwordInputBox.Text();
    if (enteredPassword == Password)
    {
        twoFACode = GenerateRandom6DigitCode();
        SendMessageToTelegram("A login attempt was made on the Admin Panel. Please use this code to verify your identity: " + twoFACode, Hardcoded2FAChatId, Hardcoded2FABotToken);

        authentication.Destroy();
        ShowTwoFactorAuthPrompt();
        Print("Password authentication successful. A 2FA code has been sent to your Telegram.");
    }
    else
    {
        feedbackLabel.Text("Wrong password. Try again.");
        passwordInputBox.Text("");
    }
    ///Handlers for the trade management
     
}

//+------------------------------------------------------------------+
//| Handle 2FA login button click                                    |
//+------------------------------------------------------------------+
void OnTwoFALoginButtonClick()
{
    // If 2FA is successful, show the trade management panel
    string enteredCode = twoFACodeInput.Text();
    if (enteredCode == twoFACode)
    {
        twoFactorAuth.Destroy();
        adminHomePanel.Show();
        Print("2FA authentication successful. Access granted to Trade Management Panel.");
    }
    else
    {
        twoFAFeedbackLabel.Text("Wrong code. Try again.");
        twoFACodeInput.Text("");
    }
}

//+------------------------------------------------------------------+
//| Handle close button for authentication                           |
//+------------------------------------------------------------------+
void OnCloseAuthButtonClick()
{
    authentication.Destroy();
    ExpertRemove(); // Exit the expert
    Print("Authentication dialog closed.");
}

//+------------------------------------------------------------------+
//| Handle close button for 2FA                                      |
//+------------------------------------------------------------------+
void OnClose2FAButtonClick()
{
    twoFactorAuth.Destroy();
    ExpertRemove();
    Print("2FA dialog closed.");
}

//+------------------------------------------------------------------+
//| Create necessary UI controls                                     |
//+------------------------------------------------------------------+
bool CreateControls()
{
    long chart_id = ChartID();
    

    if (!inputBox.Create(chart_id, "InputBox", 0, 5, 25, 460, 95))
    {
        Print("Failed to create input box");
        return false;
    }
    communicationsPanel.Add(inputBox);
    
    // Create Home Button for Communications Panel
    if (!homeButtonComm.Create(chart_id,  "HomeButtonComm", 0, 20, 120, 120,150))
    {
        Print("Failed to create Home button for Communications Panel");
        return false;
    }
    homeButtonComm.Text("Home 🏠");
    communicationsPanel.Add(homeButtonComm);

    // Create Home Button for Trade Management Panel
    if (!homeButtonTrade.Create(chart_id, "HomeButtonTrade", 0, 20, 10, 120, 30))
    {
        Print("Failed to create Home button for Trade Management Panel");
        return false;
    }
    homeButtonTrade.Text("Home 🏠");
    tradeManagementPanel.Add(homeButtonTrade);

    if (!charCounter.Create(chart_id, "CharCounter", 0, 380, 5, 460, 25))
    {
        Print("Failed to create character counter");
        return false;
    }
    charCounter.Text("0/" + IntegerToString(MAX_MESSAGE_LENGTH));
    communicationsPanel.Add(charCounter);

    if (!clearButton.Create(chart_id, "ClearButton", 0, 235, 95, 345, 125))
    {
        Print("Failed to create clear button");
        return false;
    }
    clearButton.Text("Clear");
    communicationsPanel.Add(clearButton);

    if (!sendButton.Create(chart_id, "SendButton", 0, 350, 95, 460, 125))
    {
        Print("Failed to create send button");
        return false;
    }
    sendButton.Text("Send");
    communicationsPanel.Add(sendButton);

    if (!changeFontButton.Create(chart_id, "ChangeFontButton", 0, 95, 95, 230, 115))
    {
        Print("Failed to create change font button");
        return false;
    }
    changeFontButton.Text("Font<>");
    communicationsPanel.Add(changeFontButton);

    if (!toggleThemeButton.Create(chart_id, "ToggleThemeButton", 0, 5, 95, 90, 115))
    {
        Print("Failed to create toggle theme button");
        return false;
    }
    toggleThemeButton.Text("Theme<>");
    communicationsPanel.Add(toggleThemeButton);

    if (!minimizeButton.Create(chart_id, "MinimizeButton", 0, 375, -22, 405, 0))
    {
        Print("Failed to create minimize button");
        return false;
    }
    minimizeButton.Text("_");
    communicationsPanel.Add(minimizeButton);

    if (!maximizeButton.Create(chart_id, "MaximizeButton", 0, 405, -22, 435, 0))
    {
        Print("Failed to create maximize button");
        return false;
    }
    maximizeButton.Text("[ ]");
    communicationsPanel.Add(maximizeButton);

    if (!closeButton.Create(chart_id, "CloseButton", 0, 435, -22, 465, 0))
    {
        Print("Failed to create close button");
        return false;
    }
    closeButton.Text("X");
    communicationsPanel.Add(closeButton);

    return CreateQuickMessageButtons();
    
}

//+------------------------------------------------------------------+
//| Create quick message buttons                                     |
//+------------------------------------------------------------------+
bool CreateQuickMessageButtons()
{
    string quickMessages[] = { QuickMessage1, QuickMessage2, QuickMessage3, QuickMessage4, QuickMessage5, QuickMessage6, QuickMessage7, QuickMessage8 };
    int startX = 5, startY = 160, width = 222, height = 65, spacing = 5;

    for (int i = 0; i < ArraySize(quickMessages); i++)
    {
        bool created = quickMessageButtons[i].Create(ChartID(), "QuickMessageButton" + IntegerToString(i + 1), 0,
            startX + (i % 2) * (width + spacing), startY + (i / 2) * (height + spacing), 
            startX + (i % 2) * (width + spacing) + width, startY + (i / 2) * (height + spacing) + height);

        if (!created)
        {
            Print("Failed to create quick message button ", i + 1);
            return false;
        }
        quickMessageButtons[i].Text(quickMessages[i]);
        communicationsPanel.Add(quickMessageButtons[i]);
    }
     
    return true;
}

//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
    communicationsPanel.Destroy();
    Print("Deinitialization complete");
}

//+------------------------------------------------------------------+
//| Handle custom message send button click                          |
//+------------------------------------------------------------------+
void OnSendButtonClick()
{
    string message = inputBox.Text();
    if (StringLen(message) > 0)
    {
        if (SendMessageToTelegram(message, InputChatId, InputBotToken))
            Print("Custom message sent: ", message);
        else
            Print("Failed to send custom message.");
    }
    else
    {
        Print("No message entered.");
    }
}
//+------------------------------------------------------------------+
//| Handle clear button click                                        |
//+------------------------------------------------------------------+
void OnClearButtonClick()
{
    inputBox.Text("");
    OnInputChange();
    Print("Input box cleared.");
}

//+------------------------------------------------------------------+
//| Handle quick message button click                                |
//+------------------------------------------------------------------+
void OnQuickMessageButtonClick(long index)
{
    string quickMessages[] = { QuickMessage1, QuickMessage2, QuickMessage3, QuickMessage4, QuickMessage5, QuickMessage6, QuickMessage7, QuickMessage8 };
    string message = quickMessages[(int)index];

    if (SendMessageToTelegram(message, InputChatId, InputBotToken))
        Print("Quick message sent: ", message);
    else
        Print("Failed to send quick message.");
}

//+------------------------------------------------------------------+
//| Update character counter                                         |
//+------------------------------------------------------------------+
void OnInputChange()
{
    int currentLength = StringLen(inputBox.Text());
    charCounter.Text(IntegerToString(currentLength) + "/" + IntegerToString(MAX_MESSAGE_LENGTH));
    ChartRedraw();
}

//+------------------------------------------------------------------+
//| Handle toggle theme button click                                 |
//+------------------------------------------------------------------+
void OnToggleThemeButtonClick()
{
    darkTheme = !darkTheme;
    UpdateThemeColors();
    Print("Theme toggled: ", darkTheme ? "Dark" : "Light");
}

//+------------------------------------------------------------------+
//| Update theme colors for the panel                                |
//+------------------------------------------------------------------+
void UpdateThemeColors()
{
    color textColor = darkTheme ? clrWhite : clrBlack;
    color buttonBgColor = darkTheme ? clrDarkSlateGray : clrGainsboro;
    color borderColor = darkTheme ? clrSlateGray : clrGray;
    color bgColor = darkTheme ? clrDarkBlue : clrWhite;

    
    UpdateButtonTheme(clearButton, textColor, buttonBgColor, borderColor);
    UpdateButtonTheme(sendButton, textColor, buttonBgColor, borderColor);
    UpdateButtonTheme(toggleThemeButton, textColor, buttonBgColor, borderColor);
    UpdateButtonTheme(changeFontButton, textColor, buttonBgColor, borderColor);
    UpdateButtonTheme(minimizeButton, textColor, buttonBgColor, borderColor);
    UpdateButtonTheme(maximizeButton, textColor, buttonBgColor, borderColor);
    UpdateButtonTheme(closeButton, textColor, buttonBgColor, borderColor);

    for (int i = 0; i < ArraySize(quickMessageButtons); i++)
    {
        UpdateButtonTheme(quickMessageButtons[i], textColor, buttonBgColor, borderColor);
    }

    ChartRedraw();
}

//+------------------------------------------------------------------+
//| Apply theme settings to a button                                 |
//+------------------------------------------------------------------+
void UpdateButtonTheme(CButton &button, color textColor, color bgColor, color borderColor)
{
    button.SetTextColor(textColor);
    button.SetBackgroundColor(bgColor);
    button.SetBorderColor(borderColor);
}

//+------------------------------------------------------------------+
//| Handle change font button click                                  |
//+------------------------------------------------------------------+
void OnChangeFontButtonClick()
{
    currentFontIndex = (currentFontIndex + 1) % ArraySize(availableFonts);
    SetFontForAll(availableFonts[currentFontIndex]);
    Print("Font changed to: ", availableFonts[currentFontIndex]);
    ChartRedraw();
}

//+------------------------------------------------------------------+
//| Set font for all input boxes and buttons                         |
//+------------------------------------------------------------------+
void SetFontForAll(string fontName)
{
    inputBox.Font(fontName);
    clearButton.Font(fontName);
    sendButton.Font(fontName);
    toggleThemeButton.Font(fontName);
    changeFontButton.Font(fontName);
    minimizeButton.Font(fontName);
    maximizeButton.Font(fontName);
    closeButton.Font(fontName);

    for (int i = 0; i < ArraySize(quickMessageButtons); i++)
    {
        quickMessageButtons[i].Font(fontName);
    }
}

//+------------------------------------------------------------------+
//| Generate a random 6-digit code for 2FA                           |
//+------------------------------------------------------------------+
string GenerateRandom6DigitCode()
{
    int code = MathRand() % 1000000; // Produces a 6-digit number
    return StringFormat("%06d", code); // Ensures leading zeros
}

//+------------------------------------------------------------------+
//| Handle minimize button click                                     |
//+------------------------------------------------------------------+
void OnMinimizeButtonClick()
{
    minimized = true;
    communicationsPanel.Hide();
    minimizeButton.Hide();
    maximizeButton.Show();
    closeButton.Show();
    Print("Panel minimized.");
}

//+------------------------------------------------------------------+
//| Handle maximize button click                                     |
//+------------------------------------------------------------------+
void OnMaximizeButtonClick()
{
    if (minimized)
    {
        communicationsPanel.Show();
        minimizeButton.Show();
        maximizeButton.Hide();
        closeButton.Hide();
        minimized = false;
        Print("Panel maximized.");
    }
}

//+------------------------------------------------------------------+
//| Handle close button click for admin panel                        |
//+------------------------------------------------------------------+
void OnCloseButtonClick()
{
    ExpertRemove();
    Print("Admin panel closed.");
}

//+------------------------------------------------------------------+
//| Send the message to Telegram                                     |
//+------------------------------------------------------------------+
bool SendMessageToTelegram(string message, string chatId, string botToken)
{
    string url = "https://api.telegram.org/bot" + botToken + "/sendMessage";
    string jsonMessage = "{\"chat_id\":\"" + chatId + "\", \"text\":\"" + message + "\"}";

    char postData[];
    ArrayResize(postData, StringToCharArray(jsonMessage, postData) - 1);

    int timeout = 5000;
    char result[];
    string responseHeaders;
    int responseCode = WebRequest("POST", url, "Content-Type: application/json\r\n", timeout, postData, result, responseHeaders);

    if (responseCode == 200)
    {
        Print("Message sent successfully: ", message);
        return true;
    }
    else
    {
        Print("Failed to send message. HTTP code: ", responseCode, " Error code: ", GetLastError());
        Print("Response: ", CharArrayToString(result));
        return false;
    }
}

//+------------------------------------------------------------------+


Тестирование

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

Результаты тестирования

Работа обработчиков кнопок панели управления торговлей

Кнопки панели администратора V1.22

Комментарии на вкладке Experts информируют об успешном выполнении ордера и закрытии


Заключение

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

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

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

Удачного разработки, коллеги-трейдеры!

Вернуться к содержанию

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

Прикрепленные файлы |
Алгоритм биржевого рынка — Exchange Market Algorithm (EMA) Алгоритм биржевого рынка — Exchange Market Algorithm (EMA)
Статья посвящена подробному анализу алгоритма Exchange Market Algorithm (EMA), который вдохновлен поведением трейдеров на фондовом рынке. Алгоритм моделирует процесс торговли акциями, где участники рынка с разным уровнем успеха применяют различные стратегии для максимизации прибыли.
Разработка инструментария для анализа движения цен (Часть 2): Скрипт аналитических комментариев Разработка инструментария для анализа движения цен (Часть 2): Скрипт аналитических комментариев
В продолжение нашей работы по упрощению взаимодействия с поведением цены мы рады представить еще один инструмент, который может значительно улучшить ваш анализ рынка и помочь вам принимать обоснованные решения. Этот инструмент отображает ключевые технические индикаторы, такие как цены предыдущего дня, значимые уровни поддержки и сопротивления, а также торговый объем, автоматически генерируя визуальные подсказки на графике.
Связь торговых роботов MetaTrader 5 с внешними брокерами через API и Python Связь торговых роботов MetaTrader 5 с внешними брокерами через API и Python
В настоящей статье мы обсудим реализацию MQL5 в партнерстве с Python для выполнения связанных с брокером операций. Представьте, что у вас есть постоянно работающий советник (EA), размещенный на VPS и совершающий сделки от вашего имени. В какой-то момент способность советника управлять средствами становится первостепенной. Она включает в себя такие операции, как пополнение вашего торгового счета и инициирование вывода средств. В данном обсуждении мы прольем свет на преимущества и практическую реализацию этих функций, обеспечивающих плавную интеграцию управления средствами в вашу торговую стратегию. Следите за обновлениями!
Разработка советника для анализа новостных событий о пробоях на основе календаря на MQL5 Разработка советника для анализа новостных событий о пробоях на основе календаря на MQL5
Волатильность, как правило, достигает пика во время важных новостных событий, создавая значительные возможности для пробоя. В настоящей статье мы расскажем о процессе реализации основанной на календаре стратегии прорыва. Мы рассмотрим все, начиная с создания класса для интерпретации и хранения календарных данных, разработки реалистичных бэк-тестов на основе этих данных и, наконец, реализации кода исполнения для реальной торговли.