English Русский Español Deutsch 日本語 Português
preview
在 MQL5 中创建交易管理员面板(第一部分):构建消息接口

在 MQL5 中创建交易管理员面板(第一部分):构建消息接口

MetaTrader 5示例 | 7 四月 2025, 12:52
366 0
Clemence Benjamin
Clemence Benjamin

引言

可以通过 MetaTrader 5 直接与您的系统用户进行互动。这是管理员能够通过系统与用户实时分享咨询的一种方式。它也可以作为是否将最新生成的系统信号发送到社交渠道上的确认手段。如果你读到文章的最后,你将能够看到如何创建一个交互式的界面,如这张图所示:

系统管理员面板。

系统管理员面板:Boom 500 Index,H1


管理员面板

管理员通讯面板(MetaTrader 5)和 Telegram 应用(接收来自管理员的实时消息)

在当今高度互联的世界中,数百万笔交易在毫秒级时间内执行,拥有高效的通讯渠道在交易生态系统中比以往任何时候都更为关键。考虑到如今超过 70% 的交易者依赖像 Telegram 这样的即时通讯平台来获取实时交易数据,由此显现出在交易环境中集成通讯解决方案的趋势。这种转变凸显了交易者需要接收EA提供的算法化的即时信息的重要性。

对于使用 MetaTrader 5 的交易者和开发者来说,挑战在于弥合自动化信号生成与远距离有效人为干预之间的差距。管理员面板为系统管理员提供了与全球分散的交易者进行快速直接通讯的能力。通过利用这一系统,管理员可以无缝地验证或否决信号,并通过像 Telegram 这样的平台向交易者提供必要的评论或建议。

本文讨论了使用 MQL5 来创建这一关键管理员面板的过程。提供了这一增强交易信息交互和监督程序的逐步开发指南。我们将探索如何利用 MQL5 的强大功能构建一个全面的系统,该系统有助于有效地管理信号,并与交易者进行即时通讯,从而彻底改变全球交易操作的进行方式。

信号流程图

信号流程图

该流程图展示了系统、用户和管理员的角色以及相关应用。

以下是我们在本次讨论中将要涵盖的主要内容:

  1. 理解 MetaTrader 5 GUI 开发中的坐标
  2. 理解 MQL5 开发中的库文件
  3. 在MQL5中创建一个自定义管理员面板算法
  4. 集成 Telegram API 用于与交易者的通信
  5. 测试管理员面板
  6. 使用建议
  7. 结论


理解 MetaTrader 5 GUI 开发中的坐标

在 MetaTrader 5 平台内开发图形用户界面(GUI)时,理解定义图表上图形元素定位和尺寸的坐标系统至关重要。

坐标系统:

MetaTrader 5 中的坐标系统是一个由水平(x)轴和垂直(y)轴定义的二维空间。每个图表窗口都有自己的坐标系统,从左上角开始,该点被指定为原点(0,0)。

定位元素:

要在图表的坐标空间内定位元素,你通常需要使用坐标指定两个点:

  • 左上角:由坐标(x1, y1)定义。这是元素的起始位置。
  • 右下角:由坐标(x2, y2)定义。这标记了元素的对角线终点。

宽度和高度计算:

GUI 元素的尺寸是从这些坐标之间的差值推导出来的:

  • 宽度:计算为(宽度 = x2 - x1)
  • 高度:计算为(高度 = y2 - y1)

这些计算确定了元素的大小,确保它适合在图表上指定的边界内。

面板布局示例

示例 GUI 布局 & MQL5 中的示例用法

实际应用示例

在创建对话框或图形控件(如面板或按钮)时,使用这些坐标有助于你明确且精确地定位和设置大小。这种方法确保所有 GUI 组件都能一致地摆放并按比例缩放,以适应图表窗口中的可用空间,从而构建一个协调一致的用户界面。理解和有效利用这个坐标系统是创建直观且视觉上吸引人的 MetaTrader 5 环境内应用程序的基础。

另一个示例:在 MetaTrader 5 中创建一个带有按钮的简单面板

让我们使用前面描述的坐标系统,在 MetaTrader 5 图表窗口中创建一个带有按钮的简单面板。

定义坐标:

首先,我们将为面板和按钮定义坐标。我们将面板放置在图表的左上角,并在该面板内放置按钮。

面板坐标:

  • 左上角 (x1, y1):(10, 10)
  • 右下角 (x2, y2):(200, 100)

这些坐标将创建一个面板,它从图表窗口顶部向下 10 像素、从左侧向右 10 像素开始,宽度为 190 像素(200 减去 10),高度为 90 像素(100 减去 10)。

按钮坐标:

  • 左上角 (x1, y1):(20, 20)
  • 右下角 (x2, y2):(180, 60)

这些坐标将把按钮放置在面板内,从面板顶部向下 20 像素、从左侧向右 20 像素开始,宽度为 160 像素(180 减去 20),高度为 40 像素(60 减去 20)。至少你现在心里有了这些数学概念。


理解 MQL5 开发中的库文件

在 MQL5 中,".mqh" 文件是头文件,用于在 MetaTrader 5 平台内组织和模块化代码,该平台主要用于开发交易算法和自定义指标。这些文件是复杂 MQL5 项目中使得代码能够被高效且可维护管理的关键组成部分。

在 MetaEditor 中,你可以按照以下路径找到 MQL5 文件夹中的 include 文件:

定位包含文件

在MQL5中定位包含文件

以下是对它们用途和使用方法的解释:

文件用途:

1. 代码复用性:".mqh" 文件旨在存储可复用的代码组件,例如函数定义、类声明和宏定义,这些组件可以在多个 MQL5 程序中共享。
2. 模块化:通过将代码分离到不同的文件中,开发者可以创建模块化的应用程序。这使得特定的代码功能可以被隔离、维护和独立开发。
3. 组织性:使用头文件有助于逻辑地组织代码。开发者可以将应用程序的不同部分分别存储在不同的文件中,例如将工具函数或常量放在专门的头文件中。

".mqh" 文件的典型内容:

  •  函数声明:可以在多个脚本中使用的函数。
  •  类和结构体:用于面向对象编程的类和结构体的定义和实现。
  •  常量和宏:定义的可以在全局范围内使用的常量值和宏。
  •  包含:该文件还可以包含其他 .mqh 文件,从而创建一个包含功能的层次结构或链。

如何使用 ".mqh" 文件:

要在您的 MQL5 脚本(例如 ".mq5" 或另一个 ".mqh" 文件)中使用 ".mqh" 文件,您需要使用 #include 指令将其包含进来。此处是一个示例:
#include <MyLibrary.mqh>

该指令告诉编译器在 #include 指令出现的位置包含 "MyLibrary.mqh" 的内容,从而使您的脚本能够访问包含在头文件中定义的函数、类或宏。

好处:

  • 提高可读性:通过将复杂代码抽象到头文件中,主脚本更简洁且易于理解。
  • 简化维护:可以在单个头文件中进行更改或更新,所有包含该文件的脚本将自动继承这些更新。
  • 协作:在团队环境中,将代码分散到多个文件中可以促进更好的协作,因为不同的团队成员可以在不发生冲突的情况下分别处理代码库的不同部分。

例如,在这个项目中,我们将实现汇总在下表中的文件:

文件名 文件类型 说明
Dialog.mqh: 库文件
  • 该头文件可能包含用于创建和管理对话框窗口、处理事件以及自定义对话框在 GUI 中的外观和行为的类定义和方法实现。

  • 通过使用对话框,我们可以促进更复杂的用户交互,封装各种控件(如按钮和文本框)以在图表上下文中直接获取输入或提供信息。

Button.mqh: 库文件
  •   按钮是基础的 UI 元素,使用户能够触发操作或提交数据。该头文件通常提供创建按钮、处理点击事件以及自定义按钮属性和行为的方法。

  • 它可能包含响应用户交互的事件监听器或回调函数的实现,使将功能与用户操作集成变得更容易。
Edit.mqh:  库文件
  •   编辑控件,通常称为文本框或输入字段,允许用户输入或修改文本数据。为将文本输入功能集成到应用程序中,该头文件提供了必要的结构。
  • 我们可以为这些编辑控件设置属性,如默认文本、字体和输入验证规则,确保精确捕获用户输入并适当处理。


在MQL5中创建一个自定义管理员面板算法

在 MetaEditor 中创建一个新的 Expert 模板,命名为“Admin Panel”或任何其他唯一名称。仅保留开发者属性——您的姓名、版本和版权信息,并清除模板的其余部分。然后按照以下指南构建您的面板。

在 MetaEditor 中创建一个新的EA

在 MetaEditor 中创建一个新的EA

在构建“Admin Panel.mq5”程序时,我们确保每个元素都有特定的用途,同时保持连贯的流程。我们程序的基础是包含关键库文件,如“Trade.mqh”、“Dialog.mqh”、“Button.mqh”和“Edit.mqh”,这些文件提供了创建交互式基于图表的界面所需的基本类和函数。通过引入这些库,我们利用了现有的资源,使我们能够专注于自定义功能。

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

然后,我们定义了“CScalableDialog”类,用于在图表中管理一个可缩放且可拖动的面板。这个类扩展了“CDialog”,为我们的面板提供了灵活的基础。在设计这个类时,我实现了诸如“HandleDragging”之类的方法,以便用户能够轻松移动面板,以及“SetSize”方法,以允许动态调整大小。这使我们能够创建一个用户友好的界面,能够适应不同的屏幕尺寸和用户偏好,使该工具具有多功能性。

class CScalableDialog : public CDialog
{
protected:
    bool m_Dragging;
    int m_OffsetX, m_OffsetY;
    int m_width, m_height;

public:
    CScalableDialog() : m_Dragging(false), m_OffsetX(1020), m_OffsetY(720), m_width(500), m_height(400) {}

    void HandleDragging(const int id, const long lparam, const double dparam)
    {
        if (id == CHARTEVENT_MOUSE_MOVE && m_Dragging)
        {
            int new_x = (int)lparam - m_OffsetX;
            int new_y = (int)dparam - m_OffsetY;
            SetXPosition(new_x, new_y);
        }
        else if (id == CHARTEVENT_OBJECT_CLICK)
        {
            m_OffsetX = (int)lparam;
            m_OffsetY = (int)dparam;
            m_Dragging = true;
        }
        else if (id == CHARTEVENT_CLICK)
        {
            m_Dragging = false;
        }
    }

    void SetSize(int width, int height)
    {
        m_width = width;
        m_height = height;
        UpdateDialogSize();
    }

    void UpdateDialogSize()
    {
        ObjectSetInteger(ChartID(), Name(), OBJPROP_XSIZE, m_width);
        ObjectSetInteger(ChartID(), Name(), OBJPROP_YSIZE, m_height);
    }

    void SetXPosition(int x, int y)
    {
        ObjectSetInteger(ChartID(), Name(), OBJPROP_XDISTANCE, x);
        ObjectSetInteger(ChartID(), Name(), OBJPROP_YDISTANCE, y);
        UpdateDialogSize();
    }
};

在 OnInit 函数中,我们将重点放在初始化界面组件上。这包括创建按钮、输入框以及设置面板布局。我特别注意确保每个元素都被正确地定位并且功能正常。adminPanel.Create 方法建立了主对话框,而随后的几行代码添加了像 “sendButton”(发送按钮)、“quickMessageButton”(快速消息按钮)以及用于最小化和关闭面板的工具按钮。在这里,我确保了所有组件都能在面板内顺畅地交互。 

int OnInit()
{
    long chart_id = ChartID();

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

    if (!inputBox.Create(chart_id, "InputBox", 0, 5, 5, 460,50 ))
    {
        Print("Failed to create input box");
        return INIT_FAILED;
    }
    adminPanel.Add(inputBox);

    if (!sendButton.Create(chart_id, "SendButton", 0, 270, 50, 460, 80))
    {
        Print("Failed to create send button");
        return INIT_FAILED;
    }
    sendButton.Text("Send Message");
    adminPanel.Add(sendButton);

    if (!quickMessageButton.Create(chart_id, "QuickMessageButton", 0, 180, 200, 350, 230))
    {
        Print("Failed to create quick message button");
        return INIT_FAILED;
    }
    quickMessageButton.Text("Invalid Signal");
    adminPanel.Add(quickMessageButton);

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

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

    adminPanel.Show();
    ChartSetInteger(ChartID(), CHART_EVENT_OBJECT_CREATE, true);
    ChartSetInteger(ChartID(), CHART_EVENT_OBJECT_DELETE, true);
    ChartSetInteger(ChartID(), CHART_EVENT_MOUSE_WHEEL, true);
    ChartRedraw();

    Print("Initialization complete");
    return INIT_SUCCEEDED;
}

OnChartEvent 函数是我们管理交互逻辑的地方。这个函数处理各种用户操作,例如点击、鼠标移动以及对象创建事件。通过调用诸如 HandleDragging 这样的方法,我们使面板能够动态响应用户输入。OnChartEvent 中的 switch-case 结构使我们能够高效地将不同事件路由到对应的处理程序,确保界面保持迅速响应且直观。 

void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam)
{
    adminPanel.HandleDragging(id, lparam, dparam);

    switch(id)
    {
        case CHARTEVENT_KEYDOWN:
            Print("Key pressed: code=", lparam);
            break;
        
        case CHARTEVENT_MOUSE_MOVE:
            Print("Mouse move: x=", lparam, "  y=", dparam);
            break;

        case CHARTEVENT_OBJECT_CREATE:
            Print("Created object: ", sparam);
            break;

        case CHARTEVENT_OBJECT_DELETE:
            Print("Deleted object: ", sparam);
            break;

        case CHARTEVENT_CLICK:
            Print("Mouse click on chart: x=", lparam, "  y=", dparam);
            break;

        case CHARTEVENT_OBJECT_CLICK:
            if (sparam == "SendButton")
            {
                OnSendButtonClick();
            }
            else if (sparam == "QuickMessageButton")
            {
                OnQuickMessageButtonClick();
            }
            else if (sparam == "MinimizeButton")
            {
                OnMinimizeButtonClick();
            }
            else if (sparam == "CloseButton")
            {
                OnCloseButtonClick();
            }
            break;
    }
}

最后,处理点击按键的函数(OnSendButtonClick、OnQuickMessageButtonClick、OnMinimizeButtonClick 和 OnCloseButtonClick)是程序根据用户输入执行特定操作的地方。例如,OnSendButtonClick 从输入框中获取消息并将其发送到 Telegram,同时提供操作成功的反馈。这些函数虽然简单,但至关重要,因为它们将用户操作转化为有意义的结果。在这里,我确保了界面不仅功能正常,而且符合用户的期望。
void OnSendButtonClick()
{
    string customMessage = inputBox.Text();
    if (SendMessageToTelegram(customMessage))
    {
        Print("Message sent: ", customMessage);
    }
    else
    {
        Print("Failed to send message.");
    }
}

void OnQuickMessageButtonClick()
{
    if (SendMessageToTelegram(QuickMessage))
    {
        Print("Quick message sent: ", QuickMessage);
    }
    else
    {
        Print("Failed to send quick message.");
    }
}

void OnMinimizeButtonClick()
{
    static bool minimized = false;
    if (minimized)
    {
        adminPanel.SetSize(500, 400);
        minimized = false;
    }
    else
    {
        adminPanel.SetSize(500, 30);
        minimized = true;
    }
}

void OnCloseButtonClick()
{
    adminPanel.Destroy();
    Print("Panel closed.");
}


集成 Telegram API 用于与交易者的通信

在将 Telegram 消息集成到 MQL5 程序中时,我们创建了 SendMessageToTelegram 函数,以便在我们的交易界面和 Telegram 之间进行通信。该函数首先定义了两个关键变量:“botToken” 和 “chatId”。这些是由 Telegram 提供的唯一标识符,使我们能够验证机器人(BOT)并指定将消息发送到哪里去。通过硬编码这些值,我们确保消息能够安全且高效地发送到正确的目的地。您可以访问 api.telegram.org 或我之前写的 文章,以了解更多关于“BOT TOKEN”和“CHAT ID”的信息。

string botToken = "BOT TOKEN";
string chatId = "CHAT ID";

然后,我们构建了 “url” 字符串,这是 Telegram Bot API 的端点,将其与 “botToken” 结合起来,形成发送消息所需的完整 URL。这一步至关重要,因为它建立了与 Telegram 服务器的连接,使我们的系统能够以编程方式与之交互。与此同时,我们声明了一个 “char” 数组 “post_data”,用于存储消息的有效载体,该有效载体将以 JSON 格式进行格式化,以满足 API 的要求。

string url = "https://api.telegram.org/bot" + botToken + "/sendMessage";
char post_data[];

为了以 JSON 格式格式化消息,我们将 “chatId” 和实际的 “message” 文本连接到 “jsonMessage” 字符串中。然后,使用 “StringToCharArray” 将该字符串转换为字符数组,并使用 “ArrayResize” 调整数组的大小,以确保它能够正确地容纳数据。这一步确保了消息结构与 Telegram API 所需要的数据结构兼容,这对于成功通信至关重要。

string jsonMessage = "{\"chat_id\":\"" + chatId + "\", \"text\":\"" + message + "\"}";
ArrayResize(post_data, StringToCharArray(jsonMessage, post_data));

该函数随后设置了一个 “timeout” 变量,以定义程序应等待 Telegram 服务器响应的时间。我们准备了一个 “result” 数组来存储服务器的响应,以及一个 “responseHeaders” 字符串来存储返回的任何额外信息。这些变量对于处理响应和诊断请求过程中可能出现的问题非常重要。

int timeout = 5000;
char result[];
string responseHeaders;

“WebRequest” 函数是与 Telegram API 实际通信的地方。我们向 URL 发送 “POST” 请求,传递必要的头部数据、超时时间和 “post_data”。如果请求成功,通过 HTTP 200 响应表示,该函数会打印成功消息并返回 “true”。否则,它会打印 HTTP 状态码和错误消息,返回 “false”。这种错误处理对于调试和确保我们的消息发送功能的可靠性至关重要。

int res = WebRequest("POST", url, "Content-Type: application/json\r\n", timeout, post_data, result, responseHeaders);

if (res == 200) // HTTP 200 OK
{
    Print("Message sent successfully: ", message);
    return true;
}
else
{
    Print("Failed to send message. HTTP code: ", res, " 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 responsive Admin Panel. Send messages to your telegram clients without leaving MT5"
#property version   "1.06"

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

//+------------------------------------------------------------------+
//| Creating a scalable and draggable panel                          |
//+------------------------------------------------------------------+
class CScalableDialog : public CDialog
{
protected:
    bool m_Dragging;
    int m_OffsetX, m_OffsetY;
    int m_width, m_height;

public:
    CScalableDialog() : m_Dragging(false), m_OffsetX(1020), m_OffsetY(720), m_width(500), m_height(400) {}

    // Handle the event to allow dragging
    void HandleDragging(const int id, const long lparam, const double dparam)
    {
        if (id == CHARTEVENT_MOUSE_MOVE && m_Dragging)
        {
            int new_x = (int)lparam - m_OffsetX;
            int new_y = (int)dparam - m_OffsetY;
            SetXPosition(new_x, new_y); // Update the position without changing size
        }
        else if (id == CHARTEVENT_OBJECT_CLICK)
        {
            m_OffsetX = (int)lparam;
            m_OffsetY = (int)dparam;
            m_Dragging = true;
        }
        else if (id == CHARTEVENT_CLICK)
        {
            m_Dragging = false;
        }
    }

    void SetSize(int width, int height)
{
    m_width = width;
    m_height = height;

    // Call the method to update the size of the dialog
    UpdateDialogSize();
}

void UpdateDialogSize()
{
    // Adjust the internal layout or size of the controls within the dialog here
    // Example: Resize or reposition child controls based on the new dimensions

    // Ensure that dialog dimensions are respected within its design
    ObjectSetInteger(ChartID(), Name(), OBJPROP_CORNER, 0); // This aligns the dialog to the chart corner
    ObjectSetInteger(ChartID(), Name(), OBJPROP_XSIZE, m_width);  // Width of the dialog
    ObjectSetInteger(ChartID(), Name(), OBJPROP_YSIZE, m_height); // Height of the dialog
}


  void SetXPosition(int x, int y)
{
    // Set the X and Y positions of the dialog panel
    ObjectSetInteger(ChartID(), Name(), OBJPROP_XDISTANCE, x);
    ObjectSetInteger(ChartID(), Name(), OBJPROP_YDISTANCE, y);

    // Call the method to update the size of the dialog
    UpdateDialogSize();
}


};

//+------------------------------------------------------------------+
//| Inputs                                                           |
//+------------------------------------------------------------------+
input string QuickMessage = "Invalid Signal"; // Default quick message

//+------------------------------------------------------------------+
//| Global variables                                                 |
//+------------------------------------------------------------------+
CScalableDialog adminPanel;
CButton sendButton;
CButton quickMessageButton;
CButton minimizeButton;
CButton closeButton;
CEdit inputBox;

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
{
    long chart_id = ChartID();

    // Create the dialog
    if (!adminPanel.Create(chart_id, "Admin Panel", 0, 30, 30, 500, 400))
    {
        Print("Failed to create dialog");
        return INIT_FAILED;
    }

    // Create the input box
    if (!inputBox.Create(chart_id, "InputBox", 0, 5, 5, 460,50 ))
    {
        Print("Failed to create input box");
        return INIT_FAILED;
    }
    adminPanel.Add(inputBox);

    // Create the send button for custom messages
    if (!sendButton.Create(chart_id, "SendButton", 0, 270, 50, 460, 80))
    {
        Print("Failed to create send button");
        return INIT_FAILED;
    }
    sendButton.Text("Send Message");
    adminPanel.Add(sendButton);

    // Create the quick message button
    if (!quickMessageButton.Create(chart_id, "QuickMessageButton", 0, 180, 200, 350, 230))
    {
        Print("Failed to create quick message button");
        return INIT_FAILED;
    }
    quickMessageButton.Text("Invalid Signal");
    adminPanel.Add(quickMessageButton);

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

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

    adminPanel.Show();

    // Enable chart events
    ChartSetInteger(ChartID(), CHART_EVENT_OBJECT_CREATE, true);
    ChartSetInteger(ChartID(), CHART_EVENT_OBJECT_DELETE, true);
    ChartSetInteger(ChartID(), CHART_EVENT_MOUSE_WHEEL, true);
    ChartRedraw();

    Print("Initialization complete");
    return INIT_SUCCEEDED;
}

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

//+------------------------------------------------------------------+
//| Expert event handling function                                   |
//+------------------------------------------------------------------+
void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam)
{
    // Handle dragging and other events
    adminPanel.HandleDragging(id, lparam, dparam);

    // Handle different types of events
    switch(id)
    {
        case CHARTEVENT_KEYDOWN:
            Print("Key pressed: code=", lparam);
            break;
        
        case CHARTEVENT_MOUSE_MOVE:
            Print("Mouse move: x=", lparam, "  y=", dparam);
            break;

        case CHARTEVENT_OBJECT_CREATE:
            Print("Created object: ", sparam);
            break;

        case CHARTEVENT_OBJECT_DELETE:
            Print("Deleted object: ", sparam);
            break;

        case CHARTEVENT_CLICK:
            Print("Mouse click on chart: x=", lparam, "  y=", dparam);
            break;

        case CHARTEVENT_OBJECT_CLICK:
            if (sparam == "SendButton")
            {
                OnSendButtonClick();
            }
            else if (sparam == "QuickMessageButton")
            {
                OnQuickMessageButtonClick();
            }
            else if (sparam == "MinimizeButton")
            {
                OnMinimizeButtonClick();
            }
            else if (sparam == "CloseButton")
            {
                OnCloseButtonClick();
            }
            break;

        default:
            if (id > CHARTEVENT_CUSTOM)
                Print("Custom event ID=", id, " lparam=", lparam, " dparam=", dparam, " sparam=", sparam);
            break;
    }
}

//+------------------------------------------------------------------+
//| Function to handle custom message send button click              |
//+------------------------------------------------------------------+
void OnSendButtonClick()
{
    string message = inputBox.Text();
    if (message != "")
    {
        if(SendMessageToTelegram(message))
            Print("Custom message sent: ", message);
        else
            Print("Failed to send custom message.");
    }
    else
    {
        Print("No message entered.");
    }
}

//+------------------------------------------------------------------+
//| Function to handle quick message button click                    |
//+------------------------------------------------------------------+
void OnQuickMessageButtonClick()
{
    if(SendMessageToTelegram(QuickMessage))
        Print("Quick Message Button Clicked - Quick message sent: ", QuickMessage);
    else
        Print("Failed to send quick message.");
}

//+------------------------------------------------------------------+
//| Function to handle minimize button click                         |
//+------------------------------------------------------------------+
void OnMinimizeButtonClick()
{
    static bool minimized = false;
    if (minimized)
    {
        // Restore full size
        adminPanel.SetSize(500, 400);  // Restore full size (400x200)
    }
    else
    {
        // Minimize to header only
        adminPanel.SetSize(100, 80);   // Minimize height to 30 (keeping the width 400)
    }
    minimized = !minimized;
}

//+------------------------------------------------------------------+
//| Function to handle close button click                            |
//+------------------------------------------------------------------+
void OnCloseButtonClick()
{
    adminPanel.Destroy();
    Print("Admin Panel closed.");
}

///+------------------------------------------------------------------+
//| Function to send the message to Telegram                         |
//+------------------------------------------------------------------+
bool SendMessageToTelegram(string message)
{
    // Replace with your bot token and chat ID
    string botToken = "Your BOT TOKEN";
    string chatId = "Your Chat ID";

    string url = "https://api.telegram.org/bot" + botToken + "/sendMessage";
    char post_data[];

    // Prepare the message data
    string jsonMessage = "{\"chat_id\":\"" + chatId + "\", \"text\":\"" + message + "\"}";

    // Resize the character array to fit the JSON payload
    ArrayResize(post_data, StringToCharArray(jsonMessage, post_data));

    int timeout = 5000;
    char result[];
    string responseHeaders;

    // Make the WebRequest
    int res = WebRequest("POST", url, "Content-Type: application/json\r\n", timeout, post_data, result, responseHeaders);

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


测试管理员面板

编译成功后,我们启动了程序,它按预期运行了。

通过EA启动管理员面板

通过EA启动管理员面:Boom 500 Index


使用建议

如果您已经创建了一个 Telegram 机器人和频道,并且可以访问 Telegram 机器人 API,您只需要编辑下面的源代码,将您的机器人令牌(BOT Token)和聊天 ID 硬编码进去,就可以使用管理员面板了。或者,您也可以直接获取这些令牌,并将它们输入到下面附件中提供的编译好的 EA 中,而无需修改源代码。

输入您的机器人令牌(Bot Token)和聊天 ID

输入机器人令牌(Bot Token)和聊天 ID


结论

总之,在 MQL5 中开发管理员面板EA,它代表了 MetaTrader 5 平台在直接管理以及与交易者沟通方面的一个重大进步。通过将动态且可扩展的图形用户界面与 Telegram 的实时消息功能相结合,这个工具提高了交易操作的效率和响应能力。能够直接从面板发送快速消息或自定义通知,确保了关键信息能够即时传达,没有任何延迟。这个项目强调了将用户友好的界面与强大的通信工具相结合的潜力,为实现互动性更强和更加有效的交易管理解决方案铺平了道路。

进一步的开发是不可或缺的,包括添加其他快速按钮以减少输入和发送消息之间的时间,尤其是在对最近市场行为做出反应时。我们将在本系列的后续文章中更深入地探讨这一点。感谢您们,交易者们!祝您开发顺利!

您的反馈总是受欢迎的。请在您的项目中探索附件中的文件。

 文件名 文件描述
Admin Panel.mq5 管理面板源代码。
Admin Panel.ex5 准备运行管理员面板EA。您只需要输入正确的机器人令牌(Bot Token)和聊天 ID。
 (terminal64_bK0FbSvNmw)  一张展示管理员面板如何工作的图片。

返回顶部

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

附加的文件 |
Admin_Panel.mq5 (21.75 KB)
Admin_Panel.ex5 (121.76 KB)
数据科学与机器学习(第 15 部分):SVM,每个交易员工具箱中的必备工具 数据科学与机器学习(第 15 部分):SVM,每个交易员工具箱中的必备工具
探索支持向量机 (SVM,Support Vector Machines) 在塑造未来交易中不可或缺的作用。本综合指南探讨了 SVM 如何提升您的交易策略,增强决策能力,并在金融市场中释放新的机会。通过实际应用、分步教程和专家见解深入了解 SVM 的世界。为自己配备必要的工具,帮助您应对现代交易的复杂性。使用 SVM 提升您的交易能力 — 这是每个交易者工具箱中的必备工具。
威廉·江恩(William Gann)方法(第三部分):占星术是否有效? 威廉·江恩(William Gann)方法(第三部分):占星术是否有效?
行星和恒星的位置会影响金融市场吗?让我们借助统计数据和大数据,踏上一段令人兴奋的探索之旅,进入星星与股票图表交汇的世界。
使用MQL5实现抛物线SAR趋势策略的自动化交易:打造高效的EA 使用MQL5实现抛物线SAR趋势策略的自动化交易:打造高效的EA
在本文中,我们将通过MQL5实现抛物线SAR趋势策略的自动化交易:打造高效的EA。该EA将根据抛物线SAR指标识别出的趋势进行交易。
您应当知道的 MQL5 向导技术(第 29 部分):继续学习率与 MLP 您应当知道的 MQL5 向导技术(第 29 部分):继续学习率与 MLP
我们主要验证自适应学习率,圆满考察学习率对智能系统性能的敏感性。这些学习率旨在在训练过程中针对层中的每个参数进行自定义,故我们评估潜在收益相较于预期的性能损失。