从 MQL5 向 Discord 发送消息,创建 Discord-MetaTrader 5 机器人
目录
- 概述
- 创建 Discord Webhook
- 从 MetaTrader 5 向 Discord 发送第一条消息
- 在 MQL5 中创建 Discord 类
- Discord 机器人的身份
- 处理嵌入内容和图像文件
- 向 Discord 发送交易通知
- 结论
概述
我们不再生活在互联网和原始数字时代的早期阶段;如今,几乎所有东西都可以与互联网上的任何东西连接;这只取决于一个人是否愿意做所需的工作。
API(应用程序编程接口)是一组规则和协议,允许不同的软件应用程序相互通信,它的存在使多个软件或应用程序更容易连接到另一个,因此它使我们今天看到的连接最紧密的互联网。
要使用任何提供的 API,您必须遵守规则和协议,而 API 提供商必须授予您安全的方式(如果有的话)来访问它。
在 MetaTrader 5 和外部应用程序之间进行通信并不是一件新鲜事;以前在几个提供可靠 API 的应用程序上进行过此操作,MQL5 开发人员使用这些应用程序使用 Web 请求发送和接收信息。MQL5 交易者进行此类通信最常用的应用程序是 Telegram 。
在本文中,我们将讨论如何使用 MQL5 编程语言将消息和交易信息(信号)从 MetaTrader 5 发送到 Discord。
创建 Discord Webhook
对于那些还不熟悉 Discord 的人来说:
Discord 是一个免费的通信平台,允许用户通过文本、语音和视频聊天,并共享屏幕。它在与朋友和社区联系方面很受欢迎,特别是在在线游戏中,但也用于各种其他团体,如读书俱乐部或缝纫圈。
与 Telegram 类似,这是一个允许用户相互交流的通信平台。这两个平台都提供了 API,允许在平台之外进行通信,但在集成和自动化方面,Discord 远远领先于 Telegram。
该平台允许用户创建灵活的社区并管理不同类型的应用程序(也称为机器人)。
您可以通过多种方式创建机器人,并自动执行与 Discord 的通信和信息共享过程,但最简单的方法是在社区的某个频道中使用 Webhook 。以下是创建方法。
![]()
图 01.
从您的社区,转到服务器设置。
![]()
图 02.
从服务器设置转到“集成”>“Webhooks”。
![]()
图 03.
点击“新建 Webhook”按钮创建 Webhook。将创建一个名为 Captain Hook 的 Webhook 。您可以随意将此名称修改为您想要的任何名称。
![]()
图 04.
修改名称后,您可以选择要将此 webhook 应用于的频道。在这个服务器上,我有一个名为“交易信号”的频道,我想把这个 Webhook 应用到这个频道上。参见图 01。
最后,我们需要复制 Webhook URL 。这是我们的 API 网关,我们可以将其与MQL5 中的 Web 请求一起使用。
![]()
图 05.
从 MetaTrader 5 向 Discord 发送第一条消息
我们首先需要做的就是将 添加到 MetaTrader 5 的允许 URL 列表中,否则,接下来我们要讨论的所有内容都将无法正常工作。
在 MetaTrader 5 中,转到 “工具”>“选项”>“EA 交易”> “确保已选中‘允许对列出的 URL 发送 Web 请求’” ,然后继续在下面的表单中添加 URL。
使用 Webhook URL,我们可以在给定 JSON 格式数据的情况下向这个 API 端点发送 POST 请求。
文件名: Discord EA.mq5
#include <jason.mqh> //https://www.mql5.com/en/code/13663 string discord_webhook_url = "https://discord.com/api/webhooks/1384809399767269527/105Kp27yKnQDpKD01VdEb01GS5P-KH5o5rYKuJb_xD_D8O23GPkGLXGn9pHBB1aOt4wR"; //+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit() { //--- CJAVal js(NULL, jtUNDEF); //Creating a json object string raw_json = "{\"content\": \"Hello world from MetaTrader5!\"}"; bool b = js.Deserialize(raw_json); //Parse JSON string into a structured object string json; js.Serialize(json); //Convert the object to a valid JSON string //--- Sending a Post webrequest char data[]; ArrayResize(data, StringToCharArray(json, data, 0, StringLen(json))); //--- serialize to string //--- send data char res_data[]; //For storing the body of the response string res_headers=NULL; //For storing response headers (if needed) string headers="Content-Type: application/json; charset=utf-8"; uint timeout=10000; //10 seconds timeout for the HTTP request int ret = WebRequest("POST", discord_webhook_url, headers, timeout, data, res_data, res_headers); //Send a post request if (ret==-1) { printf("func=%s line=%d, Failed to send a webrequest. Error = %s",__FUNCTION__,__LINE__,ErrorDescription(GetLastError())); return false; } //--- Check if the post request was successful or not if (ret==204) { if (MQLInfoInteger(MQL_DEBUG)) Print("Message sent to discord successfully"); } else { printf("Failed to send message to discord. Json response Error = %s",CharArrayToString(res_data)); } //--- return(INIT_SUCCEEDED); }
您要发送到 Discord 的所有信息和消息必须始终采用 JSON 格式。
JSON 对象中 content 键下的所有内容都代表发送到 Discord 的消息正文。
运行上述代码会向 Discord 发送一条简单的消息。
非常好!然而,处理 JSON 数据和其他所有内容只是为了向 Discord 发送一条简单的消息,这是一个令人厌烦的过程,让我们把所有内容都放在一个类中,以便更容易发送消息,而不必每次都担心技术问题。
在 MQL5 中创建 Discord 类
为了确保我们有一个标准的功能类,让我们在其中添加日志记录,以跟踪 API 端点和整个发送请求过程产生的错误。
文件名:Discord.mqh。
#include <errordescription.mqh> #include <logging.mqh> #include <jason.mqh> class CDiscord { protected: string m_webhook_url; string m_headers; uint m_timeout; CLogging logging; bool PostRequest(const string json); string GetFormattedJson(const string raw_json); public: CDiscord(const string webhook_url, const string headers="Content-Type: application/json; charset=utf-8", const uint timeout=10000); ~CDiscord(void); bool SendMessage(const string message); bool SendEmbeds(const string title, const string description_, color clr, const string footer_text, const datetime time); }; //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ CDiscord::CDiscord(const string webhook_url, string headers="Content-Type: application/json; charset=utf-8", uint timeout=10000): m_webhook_url(webhook_url), m_headers(headers), m_timeout(timeout) { //--- Initialize the logger logging.Config("Discord server"); if (!logging.init()) return; logging.info("Initialized",MQLInfoString(MQL_PROGRAM_NAME),__LINE__); }
我们对发送 POST 请求和将文本转换为 JSON 格式的重复性过程进行了封装。
string CDiscord::GetFormattedJson(const string raw_json) { CJAVal js(NULL, jtUNDEF); bool b = js.Deserialize(raw_json); string json; js.Serialize(json); return json; } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ bool CDiscord::PostRequest(const string json) { char res[]; //--- serialize to string char data[]; ArrayResize(data, StringToCharArray(json, data, 0, StringLen(json))); //--- send data char res_data[]; string res_headers=NULL; int ret = WebRequest("POST", m_webhook_url, m_headers, m_timeout, data, res_data, res_headers); //Send a post request if (ret==-1) { printf("func=%s line=%d, Failed to send a webrequest. Error = %s",__FUNCTION__,__LINE__,ErrorDescription(GetLastError())); return false; } //--- Check if the post request was successful or not if (ret==204) { if (MQLInfoInteger(MQL_DEBUG)) Print("Message sent to discord successfully"); logging.info("Message sent to discord successfully",MQLInfoString(MQL_PROGRAM_NAME), __LINE__); } else { printf("Failed to send message to discord. Json response Error = %s",CharArrayToString(res_data)); logging.error(CharArrayToString(res_data), MQLInfoString(MQL_PROGRAM_NAME), __LINE__); } return true; }
下面是一个向 Discord 发送消息的简单函数。
bool CDiscord::SendMessage(const string message) { string raw_json = StringFormat("{\"content\": \"%s\"}",message); string json = GetFormattedJson(raw_json); //Deserialize & Serialize the message in JSON format return PostRequest(json); }
使用方法如下。
文件名: Discord EA.mq5
#include <Discord.mqh> CDiscord *discord; string discord_webhook_url = "https://discord.com/api/webhooks/1384809399767269527/105Kp27yKnQDpKD01VdEb01GS5P-KH5o5rYKuJb_xD_D8O23GPkGLXGn9pHBB1aOt4wR"; //+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit() { //--- discord = new CDiscord(discord_webhook_url); discord.SendMessage("Hello from MetaTrader5!"); }
这个简单而强大的函数可以用来发送不同类型的消息,因为 Discord 使用的是 Markdown 格式的文本。
Markdown 消息示例
discord.SendMessage( "# h1 header\n" "## h2 header\n" "### h3 header\n\n" "**bold text** normal text\n" "[MQL5](https://www.mql5.com)\n" "" );
输出。
这甚至不是你用 Discord 提供的 Markdown 文本编辑器能做的最酷的事情。让我们与 Discord 分享一些代码行。
discord.SendMessage("```MQL5\n" //we put the type of language for markdown highlighters after three consecutive backticks "if (CheckPointer(discord)!=POINTER_INVALID)\n" " delete discord;\n" "```"); discord.SendMessage("```Python\n" "while(True):\n" " break\n" "```");
输出。
这是为了向您展示 Markdown 文本编辑器的强大功能。
在消息中添加表情符号
表情符号在短信中非常有用;它们增添了优雅,提高了可读性,更不用说注入了一点幽默。
与其他使用由特定数字代码制作的表情符号的通信平台不同,Discord 使用独特的语法直接在原始文本消息中识别和呈现表情符号。
你只需要将表情符号的名称放在两个冒号之间 —— 一个冒号放在表情符号名称的开头,另一个冒号放在表情符号名称的结尾。
当这些表情符号的代码插入到短信中时,它们将在 Discord 中呈现为表情符号。
discord.SendMessage(":rotating_light: Trade Alert!"); 信息。
有成千上万的表情符号,很难把它们全部记录下来。这是速查表 —— https://github.com/ikatyang/emoji-cheat-sheet/blob/master/README.md 。 我创建了一个简单的库文件,其中包含一些表情符号的代码和语法,请随时根据自己的喜好添加更多表情符号的代码。
文件名:discord emojis.mqh
#define DISCORD_EMOJI_ROCKET ":rocket:" #define DISCORD_EMOJI_CHART_UP ":chart_with_upwards_trend:" #define DISCORD_EMOJI_CHART_DOWN ":chart_with_downwards_trend:" #define DISCORD_EMOJI_BAR_CHART ":bar_chart:" #define DISCORD_EMOJI_BOMB ":bomb:" #define DISCORD_EMOJI_THUMBS_UP ":thumbsup:" #define DISCORD_EMOJI_THUMBS_DOWN ":thumbsdown:" #define DISCORD_EMOJI_WARNING ":warning:" #define DISCORD_EMOJI_JOY ":joy:" #define DISCORD_EMOJI_SOB ":sob:" #define DISCORD_EMOJI_SMILE ":smile:" #define DISCORD_EMOJI_FIRE ":fire:" #define DISCORD_EMOJI_STAR ":star:" #define DISCORD_EMOJI_BLUSH ":blush:" #define DISCORD_EMOJI_THINKING ":thinking:" #define DISCORD_EMOJI_ROTATING_LIGHT ":rotating_light:" #define DISCORD_EMOJI_X ":x:" #define DISCORD_EMOJI_WHITE_CHECK_MARK ":white_check_mark:" #define DISCORD_EMOJI_BALLOT_BOX_WITH_CHECK ":ballot_box_with_check:" #define DISCORD_EMOJI_HASH ":hash:" #define DISCORD_EMOJI_ASTERISK ":asterisk:" #define DISCORD_EMOJI_ZERO ":zero:" #define DISCORD_EMOJI_ONE ":one:" #define DISCORD_EMOJI_TWO ":two:" #define DISCORD_EMOJI_THREE ":three:" #define DISCORD_EMOJI_FOUR ":four:" #define DISCORD_EMOJI_FIVE ":five:" #define DISCORD_EMOJI_SIX ":six:" #define DISCORD_EMOJI_SEVEN ":seven:" #define DISCORD_EMOJI_EIGHT ":eight:" #define DISCORD_EMOJI_NINE ":nine:" #define DISCORD_EMOJI_TEN ":keycap_ten:" #define DISCORD_EMOJI_RED_CIRCLE ":red_circle:" #define DISCORD_EMOJI_GREEN_CIRCLE ":green_circle:"
发送消息。
discord.SendMessage(DISCORD_EMOJI_ROTATING_LIGHT " Trade Alert! " DISCORD_EMOJI_JOY); 消息结果。
提及用户和角色
这个简单的消息可以处理提及和角色,这对有效的消息传递至关重要。
- @everyone — 此通知会推送至频道中的所有用户,即使他们处于离线状态。
- @here — 这会通知聊天中所有非空闲用户。所有在线用户。
- <@user_id> — 此操作会通知特定用户或多个用户。
- <@&role_id> — 此操作会通知所有分配到特定角色的用户。例如,向所有被指定为手动交易者的用户发送交易信号。
使用示例。
discord.SendMessage("@everyone This is an information about a trading account"); discord.SendMessage("@here This is a quick trading signals for all of you that are active");
消息结果。
Discord 机器人的身份
webhook 的好处在于它不局限于默认外观(名称和头像);你可以在发送消息请求时随时更改这些值。
此功能非常方便,因为您可以使用多个交易机器人向一两个社区发送某种信息,所有这些机器人共享一个 webhook,能够使用不同的身份,有助于区分发件人和收到的消息。
每次调用(启动)discord 类以使身份全局化时,我们都可以将此身份作为必要条件。
class CDiscord { protected: //... //... public: string m_name; string m_avatar_url; CDiscord(const string webhook_url, const string name, const string avatar_url, const string headers="Content-Type: application/json; charset=utf-8", const uint timeout=10000); }
我们将这些变量设为公有声明,因为用户可能希望在过程中(在类初始化之后)修改或访问它们。
这使得跟踪分配给此 Discord 信使的每个用户名的日志变得更加容易。
CDiscord::CDiscord(const string webhook_url, const string name, const string avatar_url, const string headers="Content-Type: application/json; charset=utf-8", const uint timeout=10000): m_webhook_url(webhook_url), m_headers(headers), m_timeout(timeout), m_name(name), m_avatar_url(avatar_url) { //--- Initialize the logger logging.Config(m_name); if (!logging.init()) return; logging.info("Initialized",MQLInfoString(MQL_PROGRAM_NAME),__LINE__); }
现在,我们必须在找到 JSON 字符串的任何地方附加此身份(姓名和头像)信息。
bool CDiscord::SendMessage(const string message) { string raw_json = StringFormat("{" "\"username\": \"%s\"," "\"avatar_url\": \"%s\"," "\"content\": \"%s\"" "}", m_name, m_avatar_url, message); string json = GetFormattedJson(raw_json); //Deserialize & Serialize the message in JSON format return PostRequest(json); }
在 SendJSON() 函数内部,我们需要将此标识设置为可选,因为用户可能首先有不同的想法,这就是为什么他们选择使用一个接受原始 JSON 字符串的函数,该字符串使他们能够控制要发送的信息。
bool CDiscord::SendJSON(const string raw_json, bool use_id=true) { CJAVal js; js.Deserialize(raw_json); if (use_id) //if a decides to use the ids assigned to the class constructor, we append that information to the json object { js["username"] = m_name; js["avatar_url"] = m_avatar_url; } string json; js.Serialize(json); return PostRequest(json); }
让我们为我们的机器人设置一个不同的身份。
#include <Discord.mqh> CDiscord *discord; input string discord_webhook_url = "https://discord.com/api/webhooks/1384809399767269527/105Kp27yKnQDpKD01VdEb01GS5P-KH5o5rYKuJb_xD_D8O23GPkGLXGn9pHBB1aOt4wR"; input string avatar_url_ = "https://imgur.com/m7sVf51.jpeg"; input string bots_name = "Signals Bot"; //+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit() { //--- discord = new CDiscord(discord_webhook_url, bots_name, avatar_url_); discord.SendMessage("Same webhook but, different Identity :smile: cheers!"); //--- return(INIT_SUCCEEDED); }
结果。
处理嵌入式文件和图像文件
尽管在创建 Discord 类时已将图像附加到上一节中讨论的消息中,但仍有一种正确的方法可以将文件和嵌入项附加到消息中。
我们可以通过独立的 JSON 请求来实现这一点。
discord.SendJSON( "{" "\"content\": \"Here's the latest chart update:\"," "\"embeds\": [" "{" "\"title\": \"Chart Update\"," "\"image\": {" "\"url\": \"https://imgur.com/SBsomI7.png\"" "}" "}" "]" "}" );
结果。
遗憾的是,尽管 Discord 能够接收文件,但您无法使用 Web Request 直接从 MQL5 发送图像。 目前,您所能做的最好的事情就是从在线托管文件的某个链接中共享图像。
在之前的例子中,我必须使用 imgur.com 。
将 MetaTrader 5 的交易通知发送到 Discord
交易者经常利用 MetaTrader 5 将信息发送到外部平台的功能,发送有关其交易活动的更新信息。让我们使用这个机器人(EA 交易)来完成这项任务。
首先是交易开仓通知。
01:发送交易开仓通知
OnTradeTransaction 函数非常适合这项任务。
以下是用于检查最近头寸和交易状态的条件检查器。
#define IS_TRANSACTION_POSITION_OPENED (trans.type == TRADE_TRANSACTION_DEAL_ADD && HistoryDealSelect(trans.deal) && (ENUM_DEAL_ENTRY)HistoryDealGetInteger(trans.deal, DEAL_ENTRY) == DEAL_ENTRY_IN) #define IS_TRANSACTION_POSITION_CLOSED (trans.type == TRADE_TRANSACTION_DEAL_ADD && HistoryDealSelect(trans.deal) && (ENUM_DEAL_ENTRY)HistoryDealGetInteger(trans.deal, DEAL_ENTRY) == DEAL_ENTRY_OUT && ((ENUM_DEAL_REASON)HistoryDealGetInteger(trans.deal, DEAL_REASON) != DEAL_REASON_SL && (ENUM_DEAL_REASON)HistoryDealGetInteger(trans.deal, DEAL_REASON) != DEAL_REASON_TP)) #define IS_TRANSACTION_POSITION_MODIFIED (trans.type == TRADE_TRANSACTION_POSITION)
void OnTradeTransaction(const MqlTradeTransaction& trans, const MqlTradeRequest& request, const MqlTradeResult& result) { ulong deal_ticket = trans.deal; ulong position_ticket = trans.position; //--- m_deal.Ticket(deal_ticket); //select a deal by it's ticket if (IS_TRANSACTION_POSITION_OPENED) { if (deal_ticket==0) return; if (!m_position.SelectByTicket(position_ticket)) //select a position by ticket return; if (!m_symbol.Name(m_deal.Symbol())) //select a symbol from a position { printf("line=%d Failed to select symbol %s. Error = %s",__LINE__,m_deal.Symbol(),ErrorDescription(GetLastError())); return; } string message = DISCORD_EMOJI_ROTATING_LIGHT " **TRADE OPENED ALERT** \n\n" "- **Symbol:**"+m_position.Symbol()+"\n" "- **Trade Type:** "+ (m_position.PositionType()==POSITION_TYPE_BUY ? DISCORD_EMOJI_GREEN_CIRCLE: DISCORD_EMOJI_RED_CIRCLE) +" "+m_position.TypeDescription()+"\n" "- **Entry Price:** `"+DoubleToString(m_deal.Price(), m_symbol.Digits())+"`\n" "- **Stop Loss:** `"+DoubleToString(m_position.StopLoss(), m_symbol.Digits())+"`\n" "- **Take Profit:** `"+DoubleToString(m_position.TakeProfit(), m_symbol.Digits())+"`\n" "- **Time UTC:** `"+TimeToString(TimeGMT())+"`\n" "- **Position Ticket:** `"+(string)position_ticket+"`\n" "> "DISCORD_EMOJI_WARNING" *Risk what you can afford to lose.*"; discord.SendMessage(message); } //... //... Other lines of code //... }
请注意:
在格式化市场价值(如入场价格、止损价和止盈价等)时,我用一个反引号(`)将每个值包裹起来。这会在 Discord 中创建一个内联代码块,这有助于在视觉上分隔数字,使用户更容易复制。
Discord 使用:
- 单个反引号 (`) 可以格式化行内代码 —— 非常适合价格等简短值。
- 三个反引号(```)格式化多行代码块 —— 用于较大的代码块或消息。
我使用 MetaTrader 5 的一键交易功能手动开了两个反向交易,结果如下。
02:发送交易修改通知
if (IS_TRANSACTION_POSITION_MODIFIED) { if (!m_position.SelectByTicket(position_ticket)) { printf("Failed to modify a position. Erorr = %s",ErrorDescription(GetLastError())); return; } if (!m_symbol.Name(m_deal.Symbol())) { printf("line=%d Failed to select symbol %s. Error = %s",__LINE__,m_deal.Symbol(),ErrorDescription(GetLastError())); return; } string message = DISCORD_EMOJI_BAR_CHART " **TRADE MODIFIED ALERT** \n\n" "- **Symbol:** `"+m_position.Symbol()+"`\n" "- **Trade Type:** "+ (m_position.PositionType()==POSITION_TYPE_BUY ? DISCORD_EMOJI_GREEN_CIRCLE: DISCORD_EMOJI_RED_CIRCLE) +" "+m_position.TypeDescription()+"\n" "- **Entry Price:** `"+DoubleToString(m_position.PriceOpen(), m_symbol.Digits())+"`\n" "- **New Stop Loss:** `"+DoubleToString(m_position.StopLoss(), m_symbol.Digits())+"`\n" "- **New Take Profit:** `"+DoubleToString(m_position.TakeProfit(), m_symbol.Digits())+"`\n" "- **Time UTC:** `"+TimeToString(TimeGMT())+"`\n" "- **Position Ticket:** `"+(string)position_ticket+"`\n" "> "DISCORD_EMOJI_WARNING" *Risk what you can afford to lose.*"; discord.SendMessage(message); }
此消息中的所有内容与发送交易通知的消息相同,只是更改了止损和止盈字段的名称。
03:发送平仓交易通知
if (IS_TRANSACTION_POSITION_CLOSED) { if (!m_symbol.Name(m_deal.Symbol())) { printf("line=%d Failed to select symbol %s. Error = %s",__LINE__,m_deal.Symbol(),ErrorDescription(GetLastError())); return; } m_symbol.RefreshRates(); //Get recent ask and bid prices long reason_integer; m_deal.InfoInteger(DEAL_REASON, reason_integer); string reason_text = GetDealReasonText(reason_integer); string message = DISCORD_EMOJI_X " **TRADE CLOSED ALERT**\n\n" "- **Symbol:** `" + m_deal.Symbol() + "`\n" "- **Trade Type:** " + (m_position.PositionType() == POSITION_TYPE_BUY ? DISCORD_EMOJI_GREEN_CIRCLE : DISCORD_EMOJI_RED_CIRCLE) + " " + m_position.TypeDescription() + "\n" "- **Entry Price:** `" + DoubleToString(m_deal.Price(), m_symbol.Digits()) + "`\n" "- **Exit Price:** `" + (m_position.PositionType() == POSITION_TYPE_BUY ? (DoubleToString(m_symbol.Bid(), m_symbol.Digits())) : (DoubleToString(m_symbol.Ask(), m_symbol.Digits()))) + "`\n" "- **Profit:** " + (m_deal.Profit() >= 0 ? DISCORD_EMOJI_THUMBS_UP : DISCORD_EMOJI_THUMBS_DOWN) + " `" + DoubleToString(m_deal.Profit(), 2) + "`\n" "- **Close Reason:** `" + reason_text+ "`\n" "- **Commission:** `" + DoubleToString(m_position.Commission(), 2) + "`\n" "- **Swap:** `" + DoubleToString(m_position.Swap(), 2)+ "`\n" "- **Time (UTC):** `" + TimeToString(TimeGMT()) + "`\n" "- **Deal Ticket:** `" + string(deal_ticket) + "`\n" "> "DISCORD_EMOJI_WARNING" *Risk what you can afford to lose.*"; discord.SendMessage(message); }
在收盘交易信息中,当交易以盈利收盘时,我们会放上一个点赞的表情符号,当交易亏损收盘时,会放一个点踩的表情符号。与开设和修改仓位不同,我们当时有一个仓位编号。已平仓的头寸不再是头寸,而是一笔交易,因此我们使用交易单号来表示它。
这笔交易是手动完成的,因此其原因变为客户端(桌面终端) ,这是根据 ENUM_DEAL_REASON 得出的。
string GetDealReasonText(long reason) { switch((int)reason) { case DEAL_REASON_CLIENT: return "Client (Desktop Terminal)"; case DEAL_REASON_MOBILE: return "Mobile App"; case DEAL_REASON_WEB: return "Web Platform"; case DEAL_REASON_EXPERT: return "Expert Advisor"; case DEAL_REASON_SL: return "Stop Loss Hit"; case DEAL_REASON_TP: return "Take Profit Hit"; case DEAL_REASON_SO: return "Stop Out (Margin)"; case DEAL_REASON_ROLLOVER: return "Rollover Execution"; case DEAL_REASON_VMARGIN: return "Variation Margin Charged"; case DEAL_REASON_SPLIT: return "Stock Split Adjustment"; case DEAL_REASON_CORPORATE_ACTION: return "Corporate Action"; default: return "Unknown Reason"; } }
底线
Discord webhook 是 MetaTrader 5 和 Discord 集成的良好起点,它能够实现这两个强大平台之间的通信。但是,正如我之前所说,所有 API 都有规则和限制 —— Discord Webhook API 也不例外,因为 Webhook 请求有速率限制。
- 使用单个 Webhook,您每 2 秒最多只能发送 5 条消息。
- 单个 Webhook 每分钟每个频道最多允许发送 30 条消息。
如果超过此限制,Discord 将返回 HTTP 429(请求过多)。为了解决这个问题,您可以在 MQL5 代码中添加延迟或队列。
此外,在整篇文章中,我都在谈论 Discord 机器人,但 Webhook 与 Discord 机器人非常不同。MQL5 和 Discord 之间的整个工作流程就是我所说的机器人。
机器人本质上是在 Discord 中运行的程序,提供从基本命令到复杂自动化的广泛功能。而另一方面,Webhook 只是简单的 URL,允许外部应用程序向特定的 Discord 频道发送自动消息。
下面表格列出了 Discord Webhook 和 Discord 机器人之间的区别。
| Webhooks | 机器人 | |
|---|---|---|
| 功能 |
|
|
| 定制 |
|
|
| 负载和安全 |
|
|
Discord 机器人相当复杂,对于 MQL5 程序员来说不太实用,因为我们通常只需要一个功能来提醒其他交易者交易的进展情况。更不用说,要创建一个 Discord 机器人,你需要几个 Python 模块。
但是,如果你愿意,你可以开发这个功能齐全的 Discord 机器人,它可以与 MetaTrader 5 配合使用。
由于我们有一个 MetaTrader 5-Python 软件包,您可以在 Python 环境中实现这两个平台之间的完全集成。
致以最诚挚的问候。
附件表
| 文件名 | 描述与用途 |
|---|---|
| Include\discord emojis.mqh | 包含与 Discord 兼容的表情符号代码和语法。 |
| Include\discord.mqh | 它有 CDiscord 类,用于以 JSON 格式向 Discord 平台发送消息。 |
| Include\errordescription.mqh | 包含 MetaTrader 5 以 MQL5 语言生成的所有错误代码的描述。 |
| Include\jason.mqh | 一个用于 JSON 协议序列化和反序列化的库。 |
| Include\logging.mqh | 用于记录 CDiscord 类(Webhook 发送器)产生的所有信息和错误的库。 |
| Experts\Discord EA.mq5 | 一个用于测试 Discord webhook 并向指定的 Discord webhook URL 发送交易警报的 EA 交易。 |
本文由MetaQuotes Ltd译自英文
原文地址: https://www.mql5.com/en/articles/18550
注意: MetaQuotes Ltd.将保留所有关于这些材料的权利。全部或部分复制或者转载这些材料将被禁止。
本文由网站的一位用户撰写,反映了他们的个人观点。MetaQuotes Ltd 不对所提供信息的准确性负责,也不对因使用所述解决方案、策略或建议而产生的任何后果负责。
成功餐饮经营者算法(SRA)
新手在交易中的10个基本错误
从新手到专家:使用 MQL5 制作动画新闻标题(二)












