English Русский Español Deutsch 日本語
preview
在MQL5中创建交易管理员面板(第六部分):多功能界面(一)

在MQL5中创建交易管理员面板(第六部分):多功能界面(一)

MetaTrader 5示例 |
483 0
Clemence Benjamin
Clemence Benjamin

核心内容:


概述

基于我对MetaTrader 5的使用经验,单个图表一次只能支持一个EA、多个指标以及一个脚本。每个交易时段只能使用一个EA的这一限制,凸显了创建一个高度多功能面板的必要性,该面板能够在无需更换图表上的EA的情况下处理各种任务。以下是该面板可以执行的一些操作。

  • 订单管理
  • 持仓管理
  • 市场分析与数据展示
  • 图形与图表功能
  • 风险管理与报告等

从上述列表中可以看出,现有管理员面板目前并未集成这些功能。今天,我们的目标是重新设计面板的逻辑,以支持之前缺失的多种功能。我附上了一张图片,展示MetaTrader 5中的这一限制,即无法在单张图表上添加多个EA。在下面的情况中,如果要添加一个新的EA,将会覆盖当前正在运行的EA。

在图表上添加一个EA,覆盖之前已添加的EA。

EA无法共享同一张图表

在开始设计我们的程序以增加新功能之前,让我们先看一下现有管理员面板目前提供的功能,将在此基础上拓展。

以下是一张图表,展示了从程序初始化到其所有功能完全实现的基本流程。流程从登录对话框提示开始,登录成功后,系统会提示用户进行两步验证(2FA)。在该步骤中,会发送一个6位验证码到用户的Telegram账户。一旦验证成功,管理员面板及其功能即可解锁使用。

当前管理员面板

我们当前面板的基本流程

为了添加我计划中的功能,我打算利用多个对话框类面板,类似于网站的工作方式。登录后,用户将被引导至主页,该主页提供了网站所提供内容的整体概述。在这个主页面上,按钮和链接将引导用户进入平台的其他面板或功能。从理论上讲,使用我下面插入的模块图,新面板的功能将得到显著增强,为用户提供流畅且动态的体验。

设想的新面板

新的管理员面板

我们之前的管理员面板很简单,主要侧重于交易管理员与Telegram频道观众(包括群组和频道)之间的通信。基于上述设计,现在扩展我们的程序要容易得多。我们将引入一个管理员主页面板,该面板将包含访问其他功能面板的按钮。曾经被称为管理员面板的,现在将被称为通信面板。此外,我们还将添加一个单独的交易管理面板,允许管理员在不离开图表的情况下直接管理交易。这个交易管理面板将包括诸如开立新订单和管理现有订单等子功能。



探索其他可能的面板功能和管理员角色。

在交易管理方面,让我们重点关注以下两个最重要的领域,为便于理解扩展流程,这里仅作简要概述,尽管我们可以做的远不止这些。

订单管理

  • 快速开仓和平仓按钮:提供快速开仓和平仓的选项,调整订单规模,或使用预设参数设置挂单。
  • 订单修改:允许直接从面板修改止损(SL)、止盈(TP)或追踪止损水平。
  • 订单概览与筛选:展示已开仓、挂单和已平仓订单,并按订单类型、交易品种或状态进行筛选。

持仓管理
  • 快速持仓操作:提供关闭所有持仓、关闭特定类型持仓(例如,所有多头或空头持仓)以及反转持仓的按钮。
  • 风险管理工具:实时显示风险敞口、保证金使用情况、盈亏和账户权益等指标。
  • 自动平仓条件:提供在特定时间、特定新闻事件发生时或市场条件满足特定标准时自动平仓的选项。



在MQL5中实现多功能界面

我们将把原先作为主面板的管理员面板修改为可通过管理员主页面板访问的子面板,命名为“通信面板”。这种方法将把所有与通信相关的元素集中在一个地方,以便更好地组合。此外,如前所述,我们将引入一个单独的交易管理面板,该面板也可从管理员主页面板访问。为了确保用户获得流畅的体验,我们将允许用户在管理员主页面板和子面板之间轻松切换。

值得注意的是,在现阶段,我们正在使用修改后的Dialog、Edit和Button库文件来有效地管理主题。如果您使用未修改的版本,可能会遇到编译错误。为了避免这种情况,请确保您可以访问MQL5目录中的include/control文件夹,并用我们正在使用的扩展版本覆盖现有文件。这一步对于确保项目的兼容性和正常功能至关重要。

接下来,我们将专注于开发代码以引入新功能,首先从实现管理员主页面板开始,然后将之前的管理员面板过渡到通信面板。我们还将致力于交易管理员面板的开发,该面板将允许直接从图表进行无缝交易操作。我们的目标是创建流畅的用户体验,在主面板和子面板之间轻松导航,确保所有功能高效且无冲突地工作。 


代码开发

之前的源代码有602行,现在随着我们添加更多的功能,代码将有所扩展。然而,我们使用的修改方法旨在保持代码的清晰性和有组织性,无论代码变得多长。

对于运行良好的代码部分,避免修改这部分代码至关重要,只需专注于需要更新的区域。上面的模块图展示了引入新功能的简化方法。

假设您已经对MQL5中用于创建面板窗口的Dialog类和坐标系有了很好的理解,那么现在我们将引入一个名为管理员主页面板的新对话框窗口。该面板将在初始化时显示,并在成功通过密码和2FA验证后,授予访问其他具有不同功能的子窗口的权限。

这种方法确保程序结构良好且易于导航,同时扩展了其功能。

在包含必要的库文件后,我们首先声明全局变量。

// Global variables
CDialog adminHomePanel, tradeManagementPanel, communicationsPanel;

在初始化函数(OnInit())中,我们希望程序首先通过调用ShowAuthenticationPrompt()显示一个身份验证提示框。如果身份验证失败,程序将打印错误信息,并以INIT_FAILED状态退出。

程序将尝试使用Create方法创建三个与当前图表关联的对话框面板(管理员主页面板通信面板交易管理面板),并在每一步检查是否创建成功;如果任何面板创建失败,程序将打印相应的错误信息并退出。

接下来,程序将通过CreateAdminHomeControls()为管理员主页面板设置控件,并通过CreateControls()为其他面板设置控件,同样确保每个操作都成功完成。

最后,程序将隐藏所有已创建的面板,以防止它们立即显示(身份验证面板除外,以便进行验证流程),如果所有操作都成功,则返回 INIT_SUCCEEDED ,表示EA已准备好运行。

//+------------------------------------------------------------------+
//| 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 (!CreateControls())
    {
        Print("Control creation failed");
        return INIT_FAILED;
    }
    if (!tradeManagementPanel.Create(ChartID(), "Trade Management Panel", 0, 30, 30, 500, 500))
    {
        Print("Failed to create Communictions panel dialog");
        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;
}

函数CreateAdminHomeControls()负责在EA的管理员面板的管理员主页面板中创建并添加各种控制按钮。该函数首先使用ChartID()获取当前图表ID,然后尝试创建一个“交易管理访问”按钮;如果创建失败,则打印错误信息并返回false。如果创建成功,该函数将设置按钮的文本,并将其添加到 管理员主页面板中。

该过程会重复进行,以创建“通信面板访问”按钮、最小化按钮、最大化按钮和关闭按钮。每次创建时,都会检查是否成功,设置相应的显示文本,并将其添加到面板中。如果所有按钮都成功创建并添加,则该函数返回true,表示控件成功创建。

//+------------------------------------------------------------------+
//| 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;
}

另一个关键函数是OnChartEvent,该函数旨在处理MetaTrader 5中的各种图表事件,以响应用户与应用程序图形元素的交互。当用户点击图表上的某个对象时,该函数会检查事件类型 (CHARTEVENT_OBJECT_CLICK),然后使用字符串参数sparam确定具体点击的是哪个对象。

例如,如果点击了“交易管理访问按钮”,该函数会显示交易管理面板l,同时隐藏管理员主页面板,确保用户在应用程序内获得无缝的导航体验。类似的逻辑也适用于“通信面板访问按钮",点击该按钮会显示通信面板,而其他按钮则用于最小化、最大化或关闭应用程序。 

这种结构化的事件处理对于维护直观的界面至关重要,使用户能够根据自身操作高效且有效地与应用程序进行交互。

//+------------------------------------------------------------------+
//| Handle chart events                                              |
//+------------------------------------------------------------------+
void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam)
{
    if (id == CHARTEVENT_OBJECT_CLICK)
    {
        if (sparam == "TradeMgmtAccessButton")
        {
           tradeManagementPanel.Show();
           adminHomePanel.Hide();
        }
        else if (sparam == "CommunicationsPanelAccessButton")
        {
            communicationsPanel.Show();
            adminHomePanel.Hide();
        }
        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();
        }
    }

为清晰起见,我已将之前所有被称为管理员面板的内容重命名为“通信面板”。以下代码段中需修改的行已用红色高亮显示。

// Global variables
CDialog adminPanel;    //To rename it to communicationsPanel
CDialog authentication, twoFactorAuth;
CButton sendButton, clearButton, changeFontButton, toggleThemeButton;
CButton loginButton, closeAuthButton, twoFALoginButton, close2FAButton;
CButton quickMessageButtons[8], minimizeButton, maximizeButton, closeButton;
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 = "";

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

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

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

    adminPanel.Hide();
    Print("Initialization complete");
    return INIT_SUCCEEDED;
}

//+------------------------------------------------------------------+
//| 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();
}

//+------------------------------------------------------------------+
//| Handle chart events                                              |
//+------------------------------------------------------------------+
void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam)
{
    if (id == CHARTEVENT_OBJECT_CLICK)
    {
        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;
    }
}

//+------------------------------------------------------------------+
//| 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("");
    }
}

//+------------------------------------------------------------------+
//| Handle 2FA login button click                                    |
//+------------------------------------------------------------------+
void OnTwoFALoginButtonClick()
{
    string enteredCode = twoFACodeInput.Text();
    if (enteredCode == twoFACode)
    {
        twoFactorAuth.Destroy();
        adminPanel.Show();
        Print("2FA authentication successful.");
    }
    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;
    }
    adminPanel.Add(inputBox);

    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));
    adminPanel.Add(charCounter);

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

    if (!sendButton.Create(chart_id, "SendButton", 0, 350, 95, 460, 125))
    {
        Print("Failed to create send button");
        return false;
    }
    sendButton.Text("Send");
    adminPanel.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<>");
    adminPanel.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<>");
    adminPanel.Add(toggleThemeButton);

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

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

    if (!closeButton.Create(chart_id, "CloseButton", 0, 435, -22, 465, 0))
    {
        Print("Failed to create close button");
        return false;
    }
    closeButton.Text("X");
    adminPanel.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]);
        adminPanel.Add(quickMessageButtons[i]);
    }
    return true;
}

//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
    adminPanel.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;
    adminPanel.Hide();
    minimizeButton.Hide();
    maximizeButton.Show();
    closeButton.Show();
    Print("Panel minimized.");
}

//+------------------------------------------------------------------+
//| Handle maximize button click                                     |
//+------------------------------------------------------------------+
void OnMaximizeButtonClick()
{
    if (minimized)
    {
        adminPanel.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;
    }
}

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

在基本不改变代码原有功能的前提下,我仅对所有涉及原管理员面板的引用进行了重命名和编辑。以下是整合后的代码,现已融入新功能以供初步测试。

//+------------------------------------------------------------------+
//|                                             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.21"

#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 with your chat ID from telegram";
const string Hardcoded2FABotToken = "Replace with your bot token from telegram";

// Global variables
CDialog adminHomePanel, tradeManagementPanel, communicationsPanel;
CDialog authentication, twoFactorAuth;
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 = "";

//+------------------------------------------------------------------+
//| 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 (!CreateControls())
    {
        Print("Control creation failed");
        return INIT_FAILED;
    }
    if (!tradeManagementPanel.Create(ChartID(), "Trade Management Panel", 0, 30, 30, 500, 500))
    {
        Print("Failed to create Communictions panel dialog");
        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;
}

//+------------------------------------------------------------------+
//| 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;
}

//+------------------------------------------------------------------+
//| Handle chart events                                              |
//+------------------------------------------------------------------+
void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam)
{
    if (id == CHARTEVENT_OBJECT_CLICK)
    {
        if (sparam == "TradeMgmtAccessButton")
        {
           tradeManagementPanel.Show();
           adminHomePanel.Hide();
        }
        else if (sparam == "CommunicationsPanelAccessButton")
        {
            communicationsPanel.Show();
            adminHomePanel.Hide();
        }
        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;
    }
}

//+------------------------------------------------------------------+
//| 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("");
    }
}

//+------------------------------------------------------------------+
//| 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);

    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;
    }
}

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

这段代码编译完成后,我们对程序进行了初步测试。接下来,我将分享测试结果的图片,并在下方提供一些反馈和评论。


初步测试

以下是管理员面板中新集成功能的初步测试:

管理员面板初步测试

初步测试具备多项功能的管理员面板

总体而言,我们的首次发布表现良好,但是遇到了一个问题,即被困在单个窗口中,无法切换回主面板以访问其他功能。解决方案很明确,我们需要添加导航按钮,以便在窗口和功能之间无缝切换。接下来,我们将添加必要的按钮,并简要介绍交易管理面板的关键功能,然后进行今天的最终测试。正如您所见,交易管理面板目前为空,尚未实现任何功能。


面板间切换

这是一个简单但重要的步骤。我们将在子面板中添加按钮,以便轻松切换回主面板。为了添加一个“主页”按钮,方便导航回管理员主页面板,我们需要在每个子面板(即通信面板和交易管理面板)上创建一个专用按钮。这些按钮将在OnChartEvent函数中被识别,允许它们在点击时触发主面板的显示,同时隐藏子面板。这种方法涉及在每个面板设置中扩展控件创建函数,以包含一个“主页”按钮,并更新 OnChartEvent处理程序,以根据用户交互管理面板显示的内容。请按照以下步骤轻松理解。

步骤1:为每个子面板定义homeButton

CButton homeButtonComm, homeButtonTrade; // 每个子面板的“主页”按钮

步骤2:在每个面板的CreateControls中创建“主页”按钮。

CreateControls 函数中,我们将添加以下代码来创建并定位homeButton:

bool CreateControls()
{
    // Create Home Button for Communications Panel
    if (!homeButtonComm.Create(ChartID(), "HomeButtonComm", 0, 20, 400, 120, 420))
    {
        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(ChartID(), "HomeButtonTrade", 0, 20, 400, 120, 420))
    {
        Print("Failed to create Home button for Trade Management Panel");
        return false;
    }
    homeButtonTrade.Text("Home");
    tradeManagementPanel.Add(homeButtonTrade);

    return true;
}

步骤3:更新 OnChartEvent以处理“主页”按钮点击事件。

在此步骤中,我们将添加案例以处理每个主页按钮的点击事件,并在点击时切换回管理员主页面板

void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam)
{
    if (id == CHARTEVENT_OBJECT_CLICK)
    {
        if (sparam == "HomeButtonComm")
        {
            adminHomePanel.Show();
            communicationsPanel.Hide();
        }
        else if (sparam == "HomeButtonTrade")
        {
            adminHomePanel.Show();
            tradeManagementPanel.Hide();
        }
        else if (sparam == "TradeMgmtAccessButton")
        {
           tradeManagementPanel.Show();
           adminHomePanel.Hide();
        }
        else if (sparam == "CommunicationsPanelAccessButton")
        {
            communicationsPanel.Show();
            adminHomePanel.Hide();
        }
        else if (sparam == "MinimizeButton")
        {
            OnMinimizeButtonClick();
        }
        else if (sparam == "MaximizeButton")
        {
            OnMaximizeButtonClick();
        }
        else if (sparam == "CloseButton")
        {
            ExpertRemove();
        }
    }    
}

除了导航按钮外,交易管理面板仍需完成大量工作来添加更多与交易相关的功能。今天,我们先专注于添加一些用于持仓管理的独特按钮。稍后,我们将集成响应这些点击按钮的逻辑和必要的类。

首先,我们将先声明全局按钮。

CButton buyButton, sellButton, closePosButton, modifyPosButton, setSLButton, setTPButton; /// 全局层面声明持仓管理按钮

接下来,我们将专门为交易和持仓管理创建按钮。 

// Create the Trade Management Panel controls
bool CreateTradeManagementControls()
{
    long chart_id = ChartID();
    
    // Buy Button
    if (!buyButton.Create(chart_id, "BuyButton", 0, 50, 50, 150, 90))
    {
        Print("Failed to create Buy button");
        return false;
    }
    buyButton.Text("Buy");
    tradeManagementPanel.Add(buyButton);
    
    // Sell Button
    if (!sellButton.Create(chart_id, "SellButton", 0, 160, 50, 260, 90))
    {
        Print("Failed to create Sell button");
        return false;
    }
    sellButton.Text("Sell");
    tradeManagementPanel.Add(sellButton);

    // Close Position Button
    if (!closePosButton.Create(chart_id, "ClosePosButton", 0, 50, 100, 190, 140))
    {
        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, 200, 100, 340, 140))
    {
        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, 50, 150, 150, 190))
    {
        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, 160, 150, 260, 190))
    {
        Print("Failed to create Set Take-Profit button");
        return false;
    }
    setTPButton.Text("Set TP");
    tradeManagementPanel.Add(setTPButton);

    return true;
}

最后,以下是整合了所有功能后的完整源代码。

//+------------------------------------------------------------------+
//|                                             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.21"

#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 = "7049213628";
const string Hardcoded2FABotToken = "7491148147:AAHjzHVL1S74RG0Ib-pN2bgG7wEKD2Rd2MU";

// Global variables
CDialog adminHomePanel, tradeManagementPanel, communicationsPanel;
CDialog authentication, twoFactorAuth;
CButton homeButtonComm, homeButtonTrade;
CButton buyButton, sellButton, closePosButton, modifyPosButton, setSLButton, setTPButton;/// Position management buttons at global level declaration.
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 = "";

//+------------------------------------------------------------------+
//| 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, 30, 30, 500, 500))
    {
        Print("Failed to create Communictions panel dialog");
        return INIT_FAILED;
    }

    if (!CreateControls())
    {
        Print("Control creation failed");
        return INIT_FAILED;
    }
    if (!CreateTradeManagementControls())
    {
        Print("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;
}

//+------------------------------------------------------------------+
//| 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;
}
// Create the Trade Management Panel controls
bool CreateTradeManagementControls()
{
    long chart_id = ChartID();
    
    // Buy Button
    if (!buyButton.Create(chart_id, "BuyButton", 0, 50, 50, 150, 90))
    {
        Print("Failed to create Buy button");
        return false;
    }
    buyButton.Text("Buy");
    tradeManagementPanel.Add(buyButton);
    
    // Sell Button
    if (!sellButton.Create(chart_id, "SellButton", 0, 160, 50, 260, 90))
    {
        Print("Failed to create Sell button");
        return false;
    }
    sellButton.Text("Sell");
    tradeManagementPanel.Add(sellButton);

    // Close Position Button
    if (!closePosButton.Create(chart_id, "ClosePosButton", 0, 50, 100, 190, 140))
    {
        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, 200, 100, 340, 140))
    {
        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, 50, 150, 150, 190))
    {
        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, 160, 150, 260, 190))
    {
        Print("Failed to create Set Take-Profit button");
        return false;
    }
    setTPButton.Text("Set TP");
    tradeManagementPanel.Add(setTPButton);

    return true;
}

//+------------------------------------------------------------------+
//| Handle chart events                                              |
//+------------------------------------------------------------------+
void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam)
{
    if (id == CHARTEVENT_OBJECT_CLICK)
    {
    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();
        }
        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;
    }
}

//+------------------------------------------------------------------+
//| 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("");
    }
}

//+------------------------------------------------------------------+
//| 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;
    }
}

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


测试结果

实际上,完全有可能为单个EA程序添加多种功能。通过融入新颖且令人兴奋的功能,我们极大地扩展了管理员面板EA的能力,使其得到了显著地增强。以下是成功编译并运行更新后的代码后所呈现的界面截图。

启动管理员面板v1.21

在MetaTrader 5上启动管理员面板V1.21



结论

在本项目中,我们从专注于Telegram通信的简易管理员面板起步,将其扩展为一个功能更全面、用户更友好的界面。如今,我们的1.21版本设置包含一个作为主仪表盘的管理员主页面板,可以轻松地访问通信面板和全新的交易管理面板。该方法使所有内容都井然有序,通过双因素认证(2FA)保障安全,且易于导航。接下来,我们将深入探索交易管理面板的功能,以持续在一个集成工具中提升交易与通信体验。 

至于主页按钮图标,我只是从Telegram上复制了一个图标并粘贴过来。我们将在未来的更新中探讨更多关于图标和设计选择的细节。欢迎在下方评论区分享您的观点。

返回内容目录

本文由MetaQuotes Ltd译自英文
原文地址: https://www.mql5.com/en/articles/16240

附加的文件 |
从Python到MQL5:量子启发式交易系统的探索之旅 从Python到MQL5:量子启发式交易系统的探索之旅
本文探讨了量子启发式交易系统的开发过程,该系统从Python原型过渡到MQL5实现,以应用于现实世界的交易中。该系统运用了量子计算原理(如叠加态和纠缠态)来分析市场状态,尽管这是在经典计算机上使用量子模拟器运行的。该系统的关键特性包括:采用三量子比特系统,可同时分析八种市场状态;设置24小时的回溯观察期;并运用七种技术指标进行市场分析。尽管准确率看似一般,但若结合恰当的风险管理策略,该系统仍能提供显著的优势。
从基础到中级:数组和字符串(一) 从基础到中级:数组和字符串(一)
在今天的文章中,我们将开始探索一些特殊的数据类型。首先,我们将定义什么是字符串,并解释如何使用一些基本过程。这将使我们能够处理这类数据,这可能很有趣,尽管有时对初学者来说有点困惑。此处提供的内容仅用于教育目的。在任何情况下,除了学习和掌握所提出的概念外,都不应出于任何目的使用此应用程序。
您应当知道的 MQL5 向导技术(第 42 部分):ADX 振荡器 您应当知道的 MQL5 向导技术(第 42 部分):ADX 振荡器
ADX 是一些交易者用来衡量主流趋势强度的另一个相对热门的技术指标。作为其它两个指标的组合,它体现为振荡器,在本文中我们借助 MQL5 向导汇编、及其支持类,来探索其形态。
交易中的神经网络:受控分段(终章) 交易中的神经网络:受控分段(终章)
我们继续上一篇文章中开启的工作,使用 MQL5 构建 RefMask3D 框架。该框架旨在全面研究点云中的多模态互动和特征分析,随后基于自然语言提供的描述进行目标对象识别。