The code
A script with inputs that posts one JSON message, with the chart's symbol, Bid and server time, to the URL you give it. It sends data to that server, so use a webhook you own.
Save it as MQL5/Scripts/webrequest-post-webhook.mq5 and compile it in MetaEditor (F7): 0 errors, 0 warnings.
#property script_show_inputs
input string InpUrl = "https://example.com/webhook"; // Webhook URL (its domain must be in the allowed list)
input string InpMessage = "Test from MetaTrader 5"; // Text to send
input int InpTimeout = 5000; // Timeout, milliseconds
// Escapes a string for a JSON value
string JsonEscape(const string text)
{
string s = text;
StringReplace(s, "\\", "\\\\");
StringReplace(s, "\"", "\\\"");
StringReplace(s, "\n", "\\n");
StringReplace(s, "\r", "");
return s;
}
// "https://hooks.example.com/abc" -> "https://hooks.example.com": the part the allowed-URL list needs
string Origin(const string url)
{
int scheme = StringFind(url, "://");
int slash = (scheme < 0) ? -1 : StringFind(url, "/", scheme + 3);
return slash < 0 ? url : StringSubstr(url, 0, slash);
}
void OnStart()
{
if(MQLInfoInteger(MQL_TESTER))
{
Print("WebRequest() doesn't run in the Strategy Tester");
return;
}
string json = StringFormat("{\"symbol\":\"%s\",\"bid\":%s,\"time\":\"%s\",\"text\":\"%s\"}",
_Symbol, DoubleToString(SymbolInfoDouble(_Symbol, SYMBOL_BID), _Digits),
TimeToString(TimeTradeServer(), TIME_DATE | TIME_SECONDS), JsonEscape(InpMessage));
char body[];
int size = StringToCharArray(json, body, 0, WHOLE_ARRAY, CP_UTF8);
ArrayResize(body, size - 1); // drop the terminating zero StringToCharArray() adds: it isn't part of the JSON
char reply[];
string replyHeaders;
string headers = "Content-Type: application/json\r\n";
ResetLastError();
int status = WebRequest("POST", InpUrl, headers, InpTimeout, body, reply, replyHeaders);
if(status == -1)
{
int err = GetLastError();
if(err == ERR_FUNCTION_NOT_ALLOWED) // 4014
PrintFormat("URL not allowed: add %s in Tools > Options > Expert Advisors, \"Allow WebRequest for listed URL\"", Origin(InpUrl));
else
PrintFormat("WebRequest failed: error %d", err); // 5200-5203: bad address, no connection, timeout, HTTP failure
return;
}
// status is the HTTP code: 200-299 means the server accepted it
PrintFormat("HTTP %d: %s", status, CharArrayToString(reply, 0, WHOLE_ARRAY, CP_UTF8));
}How it works
MQL_TESTER.WebRequest()doesn't run in the Strategy Tester; the script says so instead of failing.JsonEscape(). A quote or backslash in the message would break the JSON: they're escaped, newlines become\n.StringToCharArray(..., CP_UTF8). Converts the text to UTF-8 bytes, so accents and symbols arrive intact. The function also copies the string's terminating zero, whichArrayResize(body, size - 1)removes.- The headers string. Each header ends with
\r\n.Content-Type: application/jsontells the server how to read the body. - The return value. The HTTP status code (200 to 299 is success), or -1 when the request couldn't be made: then
GetLastError()says why. Origin(). The allowed-URL list needs the scheme and host, not the full path, so the script prints exactly what to add.
Pitfalls
- The URL isn't in the list.
WebRequest()returns -1 andGetLastError()is 4014 (ERR_FUNCTION_NOT_ALLOWED). Add the address in Tools > Options > Expert Advisors and tick "Allow WebRequest for listed URL". - The trailing zero. Sent along with the JSON, it makes some servers answer 400 (bad request) for a body that looks perfect in the log.
- Blocking the EA.
WebRequest()waits for the answer, up to the timeout, and the EA does nothing else meanwhile. Keep the timeout short, never call it on every tick, and don't expect it to work in an indicator: MetaTrader doesn't allow it there. - Network errors. Errors 5200 to 5203 mean an invalid URL, no connection, a timeout and a failed request. Retry later rather than in a tight loop.
- Secrets in the URL. A webhook URL with a token is saved in the program's inputs, presets and screenshots. Treat it like a password.
Variations
- Telegram. Post
{"chat_id": ..., "text": ...}tohttps://api.telegram.org/bot<token>/sendMessage, and addhttps://api.telegram.orgto the allowed list. - Discord. A Discord webhook takes
{"content": "..."}as its body. - From an EA. Call the same code from
OnTradeTransaction()to report each closed trade, as the CSV logger does with a file, or from an alert function. - The other direction. TradingView sends webhooks too; TradingView webhook alerts shows the message format on that side.
Questions
Why does WebRequest() return -1?
Read GetLastError() right after the call. 4014 means the URL isn't in the allowed list. 5200 to 5203 are network problems: an invalid address, no connection, a timeout or a failed request.
Can I use WebRequest() in the Strategy Tester or in an indicator?
No. It only runs in Expert Advisors and scripts on a live chart. To test the logic around it, log the message instead of sending it when MQLInfoInteger(MQL_TESTER) is true.
How do I send MT5 messages to Telegram?
Create a bot with BotFather, get your chat ID, add https://api.telegram.org to the allowed URLs, and POST JSON with chat_id and text to the bot's sendMessage address with WebRequest().
This program is a minimal example written to show one technique, not a trading system, and none of it is a recommendation to trade. Trading involves substantial risk of loss and is not suitable for every investor. Edgoro is a software tool, not a broker or financial adviser: nothing it produces is investment advice, and past or backtested performance does not guarantee future results.