DEV Community

loading...

Building a Telegram Chat with a MT4 Forex Trading Expert Advisor

dennislwm profile image dennislwm Originally published at tldr.pro on 惻12 min read

Building a Telegram Chat with a MT4 Forex Trading Expert Advisor

Introduction

Telegram isn't just for sending and receiving chat messages. It's also for automating your dialog flow, including work flow. Using a Telegram Bot gives you the ability to check prices, query status, manage trades, and even have a fun conversation. And if you're a serious crypto or forex trader, you can create your own Telegram Bot to manage your order flow.

In this tutorial you'll use a Telegram Bot to query your orders on a Metatrader 4 account. You'll create a Telegram Bot ["bot"], build an Expert Advisor ["EA"] that can listen and process messages from a user, as well as reply to the user with orders and account data.

TL;DR

YouTube video on Build Your MT4 Expert Advisor Telegram Bot

Prerequisites

  • Metatrader 4 ["MT4"] client and demo account with any broker.
  • Telegram Bot created in your Telegram account. The tutorial How to Create a New Telegram Bot walks you through creating a bot and configuring your MT4 client.
  • Postman Windows application to understand how the Telegram HTTP API works.

Step 1 - Peeking into Telegram's HTTP API with Postman

Before diving into the MT4 EA build, let's take a peek at how the Telegram HTTP API work, in particular the getUpdates method, with the Postman app.

The getUpdates method returns messages from all channels, groups, and chats that the Bot is a member of.

In other words, the JSON message returned by this function can get crowded very quickly, if the Bot is a member of more than one group or channel.

Each Bot can also have a private chat with whomever sends a private message to the Bot.

For example, my Bot belongs to both a channel TradeTitanSignal and a private chat where I can sent it private messages.

GET https://api.telegram.org/bot**token**/getUpdates
Enter fullscreen mode Exit fullscreen mode
 {
     "ok": true,
     "result": [
         {
             "update_id": 769794061,
             "channel_post": {
                 "message_id": 4,
                 "chat": {
                     "id": -1001326947729,
                     "title": "TradeTitanSignal",
                     "username": "tradetitansignal",
                     "type": "channel"
                 },
                 "date": 1569929874,
                 "text": "hi iā€™m dennis"
             }
         },
         {
             "update_id": 769794062,
             "message": {
                 "message_id": 4,
                 "from": {
                     "id": 902090608,
                     "is_bot": false,
                     "first_name": "Dennis",
                     "last_name": "Lee",
                     "language_code": "en"
                 },
                 "chat": {
                     "id": 902090608,
                     "first_name": "Dennis",
                     "last_name": "Lee",
                     "type": "private"
                 },
                 "date": 1569931564,
                 "text": "hi"
             }
         }
     ]
 }
Enter fullscreen mode Exit fullscreen mode

The above response in JSON format requires some explanation:

  • The update_id value represents a sequential number that is assigned to every message regardless of whether the message is from a channel post, or a private message, etc.
"update_id": 769794061,

           "update_id": 769794062,
Enter fullscreen mode Exit fullscreen mode
  • The update id is followed by the message type, i.e channel_post for a channel message, while a private message begins with message head.

  • A channel has a negative chat_id , so we may have to use chat_title to scan for a channel.

"chat": {
                   "id": -1001326947729,
                   "title": "TradeTitanSignal",
Enter fullscreen mode Exit fullscreen mode
  • A private chat has a positive chat_id , so we can use sendMessage method to chat to the person.
"chat": {
                   "id": 902090608,
                   "first_name": "Dennis",
Enter fullscreen mode Exit fullscreen mode
  • A channel post has chat_title and chat_username , but a private message has from_is_bot , from_first_name , from_last_name , chat_first_name , and chat_last_name.
"update_id": 769794061,
           "channel_post": {
               "message_id": 4,
               "chat": {
                   "id": -1001326947729,
                   "title": "TradeTitanSignal",
                   "username": "tradetitansignal",
                   "type": "channel"
               },
               "date": 1569929874,
               "text": "hi iā€™m dennis"
Enter fullscreen mode Exit fullscreen mode
  • Both channel post and private message have update_id , message_id , chat_id , chat_type , date and text. For both channel post and private message, the content can be accessed using text.
"update_id": 769794062,
           "message": {
               "message_id": 4,
               "from": {
                   "id": 902090608,
                   "is_bot": false,
                   "first_name": "Dennis",
                   "last_name": "Lee",
                   "language_code": "en"
               },
               "chat": {
                   "id": 902090608,
                   "first_name": "Dennis",
                   "last_name": "Lee",
                   "type": "private"
               },
               "date": 1569931564,
               "text": "hi"
Enter fullscreen mode Exit fullscreen mode
  • The response has a limit of 100 messages, but it doesn't clear automatically each time you call the getUpdate method, unless you pass it an offset parameter.

  • After processing the above messages, you should call the getUpdates method, but with an offset value equal to the highest update_id + 1, in this example above, i.e. 769794062 + 1.

GET https://api.telegram.org/bot**token**/getUpdates?offset=769794063
Enter fullscreen mode Exit fullscreen mode

We should get an empty response if there are no new messages.

It is important to note that calling the getUpdates method again, without the offset value, returns an empty response.

This is because the Telegram API server stores the last offset that we passed as a parameter, so that we don't have to specify the same offset again.

{
     "ok": true,
     "result": []
 }
Enter fullscreen mode Exit fullscreen mode

Step 2 - Creating a New MT4 Expert Advisor

In this section, let's create a new Expert Advisor ["EA"] in MetaEditor, and name the EA TelegramRecon.mq4.

Type the following code into the above MQ4 file:

#property copyright "Copyright 2019, Dennis Lee"
 #property link "https://github.com/dennislwm/MT4-Telegram-Bot-Recon"
 #property version "000.900"
 #property strict
 //---- Assert Basic externs
 #include <plusinit.mqh>
 #include <plusbig.mqh>
 #include <Telegram.mqh>
 //|-----------------------------------------------------------------------------------------|
 //| E X T E R N A L V A R I A B L E S |
 //|-----------------------------------------------------------------------------------------|
 extern string s1="-->TGR Settings<--";
 extern string s1_1="Token - Telegram API Token";
 input string TgrToken;
 //|-----------------------------------------------------------------------------------------|
 //| I N T E R N A L V A R I A B L E S |
 //|-----------------------------------------------------------------------------------------|
 CCustomBot bot;
 int intResult;
 //|-----------------------------------------------------------------------------------------|
 //| I N I T I A L I Z A T I O N |
 //|-----------------------------------------------------------------------------------------|
 int OnInit()
 {
    InitInit();
    BigInit();

    bot.Token(TgrToken);
    intResult=bot.GetMe();

 //--- create timer
    EventSetTimer(3);
    OnTimer();

 //---
    return(INIT_SUCCEEDED);
 }
 //|-----------------------------------------------------------------------------------------|
 //| D E I N I T I A L I Z A T I O N |
 //|-----------------------------------------------------------------------------------------|
 void OnDeinit(const int reason)
 {
 //--- destroy timer
    EventKillTimer();

    BigDeInit();
 }
 //+------------------------------------------------------------------+
 //| Expert tick function |
 //+------------------------------------------------------------------+
 void OnTick()
   {
 //---

   }
 void OnTimer()
 {
 //--- Assert intResult=0 (success)
    if( intResult!=0 ) {
       BigComment( "Error: "+GetErrorDescription(intResult) );
       return;
    }

    BigComment( "Bot name: "+bot.Name() );
 }
Enter fullscreen mode Exit fullscreen mode

First, we include the file Telegram.mqh , which provides the class CCustomBot to manage a Telegram Bot.

#include <Telegram.mqh>
Enter fullscreen mode Exit fullscreen mode

Second, we declare an input variable TgrToken , which the user must provide. This is the HTTP API token for the Telegram Bot.

input string TgrToken;
Enter fullscreen mode Exit fullscreen mode

Third, we declare two global variables:

(1) The variable bot is of type CCustomBot , which is a class defined in Telegram.mqh. This bot is used to send and process Telegram messages.

(2) The variable intResult is an integer, which holds the result of the bot.GetMe() method. The method returns zero if successful.

CCustomBot bot;
 int intResult;
Enter fullscreen mode Exit fullscreen mode

Fourth, in the OnInit() function, we call the bot.Token() method and passing it the variable TgrToken.

Then we call the bot.GetMe() method, which returns a zero if successful.

We then set the Timer to repeat every three seconds to call the OnTimer() function.

bot.Token(TgrToken);
    intResult=bot.GetMe();

 //--- create timer
    EventSetTimer(3);
    OnTimer();
Enter fullscreen mode Exit fullscreen mode

Finally, in the OnTimer() function, we check the variable intResult. If it is a non-zero value, then we display the Error Description on the chart.

Otherwise, if the value of intResult is zero (success), then we display the bot Name using the bot.Name() method.

if( intResult!=0 ) {
       BigComment( "Error: "+GetErrorDescription(intResult) );
       return;
    }

    BigComment( "Bot name: "+bot.Name() );
Enter fullscreen mode Exit fullscreen mode

Compile the above source code, and you should see the TelegramRecon EA in the Navigator under the Expert Advisors tab.

EA Attached to Chart

Step 3 - Running the MT4 EA for First Time

Before running the EA, we have to add a URL to the List of allowed WebRequest URLs in MT4.

Click on menu Tools --> Options (Ctrl+O), then click on menu tab Expert Advisors.

Check the box Allow WebRequest for listed URL, and add the URL https://api.telegram.org

Click OK button to save the dialog window.

Next, attach the EA to any chart, and in the Input dialog window, enter your unique HTTP API token in the Input field TgrToken.

If you had done every step above correctly, you should see your Bot Name displayed on the chart.

Configure MT4 Client

Step 4 - Building a Bot Query Tool

In order to build a Bot Query Tool, we have to be able to both send and process messages to and from a user respectively.

In this section, let's create a new include file in MetaEditor, and name the file CPlusBotRecon.mqh.

Type the following code into the above MQH file:

#property copyright "Copyright 2019, Dennis Lee"
 #property link "https://github.com/dennislwm/MT4-Telegram-Bot-Recon"
 #property strict
 //---- Assert Basic externs
 #include <PlusBotRecon.mqh>
 #include <Telegram.mqh>
 //|-----------------------------------------------------------------------------------------|
 //| M A I N P R O C E D U R E |
 //|-----------------------------------------------------------------------------------------|
 class CPlusBotRecon: public CCustomBot
 {
 public:
    void ProcessMessages(void)
    {
       string msg=NL;
       const string strOrderTicket="/orderticket";
       const string strHistoryTicket="/historyticket";
       int ticket=0;
       for( int i=0; i<m_chats.Total(); i++ ) {
          CCustomChat *chat=m_chats.GetNodeAtIndex(i);

          if( !chat.m_new_one.done ) {
             chat.m_new_one.done=true;

             string text=chat.m_new_one.message_text;

             if( text=="/ordertotal" ) {
                SendMessage( chat.m_id, BotOrdersTotal() );
             }

             if( text=="/ordertrade" ) {
                SendMessage( chat.m_id, BotOrdersTrade() );
             }
             if( StringFind( text, strOrderTicket )>=0 ) {
                ticket = StringToInteger( StringSubstr( text, StringLen(strOrderTicket)+1 ) );
                if( ticket>0 ) 
                   SendMessage( chat.m_id, BotOrdersTicket(ticket) );
                else {
                   msg = StringConcatenate(msg,"Correct format is: /orderticket **ticket**");
                   SendMessage( chat.m_id, msg );
                }
             }
             if( text=="/historytotal" ) {
                SendMessage( chat.m_id, BotOrdersHistoryTotal() );
             }
             if( StringFind( text, strHistoryTicket )>=0 ) {
                ticket = StringToInteger( StringSubstr( text, StringLen(strHistoryTicket)+1 ) );
                if( ticket>0 ) 
                   SendMessage( chat.m_id, BotHistoryTicket(ticket) );
                else {
                   msg = StringConcatenate(msg,"Correct format is: /historyticket **ticket**");
                   SendMessage( chat.m_id, msg );
                }
             }

             if( text=="/account" ) {
                SendMessage( chat.m_id, BotAccount() );
             }

             msg = StringConcatenate(msg,"My commands list:",NL);
             msg = StringConcatenate(msg,"/ordertotal-return count of orders",NL);
             msg = StringConcatenate(msg,"/ordertrade-return ALL opened orders",NL);
             msg = StringConcatenate(msg,"/orderticket **ticket** -return an order or a chain of history by ticket",NL);
             msg = StringConcatenate(msg,"/historytotal-return count of history",NL);
             msg = StringConcatenate(msg,"/historyticket **ticket** -return a history or chain of history by ticket",NL);
             msg = StringConcatenate(msg,"/account-return account info",NL);
             msg = StringConcatenate(msg,"/help-get help");
             if( text=="/help" ) {
                SendMessage( chat.m_id, msg );
             }
          }
       }
    }
 };
Enter fullscreen mode Exit fullscreen mode

First, we include both the files PlusBotRecon.mqh and Telegram.mqh. The first MQH file is one that we will create later that does all the order queries, while the latter MQH contains the class CCustomBot, as previously discussed.

#include <PlusBotRecon.mqh>
#include <Telegram.mqh>
Enter fullscreen mode Exit fullscreen mode

Second, we declare a new class CPlusBotRecon , which inherits all the methods and data of CCustomBot. In addition, we declare a new public method ProcessMessage().

class CPlusBotRecon: public CCustomBot
Enter fullscreen mode Exit fullscreen mode

The method ProcessMessage() checks and parses any messages into commands, prepended by a slash ["/"], that we defined as follows:

  1. /ordertotal - Return a count of opened orders
  2. /ordertrade - Return ALL opened orders, where EACH order includes ticket, symbol, type, lots, openprice, stoploss, takeprofit, and prevticket
  3. /orderticket ticket - Return an order by ticket
  4. /historytotal - Return a count of history
  5. /historyticket ticket - Return a history by ticket
  6. /account - Return account number, currency, balance, equity, margin, freemargin, and profit.
  7. /help - Display a list of bot commands

Finally, let's create a new include file in MetaEditor, and name the file PlusBotRecon.mqh.

Type the following code into the above MQH file:

#property copyright "Copyright 2019, Dennis Lee"
 #property link "https://github.com/dennislwm/MT5-MT4-Telegram-API-Bot"
 #property strict
 #define NL "\n"
 //|-----------------------------------------------------------------------------------------|
 //| O R D E R S S T A T U S |
 //|-----------------------------------------------------------------------------------------|
 string BotOrdersTotal(bool noPending=true)
 {
    return( "" );
 }
 string BotOrdersTrade(int pos=0, bool noPending=true)
 {
    return( "" );
 }
 string BotOrdersTicket(int ticket, bool noPending=true)
 {
    return( "" );
 }
 string BotHistoryTicket(int ticket, bool noPending=true)
 {
    return( "" );
 }
 string BotOrdersHistoryTotal(bool noPending=true)
 {
    return( "" );
 }
 //|-----------------------------------------------------------------------------------------|
 //| A C C O U N T S T A T U S |
 //|-----------------------------------------------------------------------------------------|
 string BotAccount(void)
 {
    return( "" );
 }
 //|-----------------------------------------------------------------------------------------|
 //| I N T E R N A L F U N C T I O N S |
 //|-----------------------------------------------------------------------------------------|
 string strBotInt(string key, int val)
 {
    return( StringConcatenate(NL,key,"=",val) );
 }
 string strBotDbl(string key, double val, int dgt=5)
 {
    return( StringConcatenate(NL,key,"=",NormalizeDouble(val,dgt)) );
 }
 string strBotTme(string key, datetime val)
 {
    return( StringConcatenate(NL,key,"=",TimeToString(val)) );
 }
 string strBotStr(string key, string val)
 {
    return( StringConcatenate(NL,key,"=",val) );
 }
 string strBotBln(string key, bool val)
 {
    string valType;
    if( val ) valType="true";
    else valType="false";
    return( StringConcatenate(NL,key,"=",valType) );
 }
 //|-----------------------------------------------------------------------------------------|
 //| E N D O F I N D I C A T O R |
 //|-----------------------------------------------------------------------------------------|
Enter fullscreen mode Exit fullscreen mode

For now, we simply return an empty string in each of our above function.

Let's modify our previous EA TelegramRecon.mq4 in MetaEditor.

Find the following code into the above MQ4 file and add the include file CPlusBotRecon.mqh below as follows:

#include <Telegram.mqh>
#include <CPlusBotRecon.mqh>
Enter fullscreen mode Exit fullscreen mode

Next, find and replace the following code:

CCustomBot bot;
Enter fullscreen mode Exit fullscreen mode

with our inherited class:

CPlusBotRecon bot;
Enter fullscreen mode Exit fullscreen mode

Next, find the BigComment code in the OnTimer() function and add two more lines below it as follows

BigComment( "Bot name: "+bot.Name() );      
 bot.GetUpdates();      
 bot.ProcessMessages();
Enter fullscreen mode Exit fullscreen mode

Compile and attach the EA to any chart, and in the Input dialog window, enter your unique HTTP API token in the Input field TgrToken.

Open your Telegram app and send a message "/help" to your Telegram Bot. You should get the following response:

Example Help

Step 5 - Implementing the Bot Commands

The final step is to actually implement the empty functions in our include file PlusBotRecon.mqh.

Type the following code into the above MQ4 file:

//|-----------------------------------------------------------------------------------------|
 //| O R D E R S S T A T U S |
 //|-----------------------------------------------------------------------------------------|
 string BotOrdersTotal(bool noPending=true)
 {
    int count=0;
    int total=OrdersTotal();
 //--- Assert optimize function by checking total > 0
    if( total<=0 ) return( strBotInt("Total", count) );   
 //--- Assert optimize function by checking noPending = false
    if( noPending==false ) return( strBotInt("Total", total) );

 //--- Assert determine count of all trades that are opened
    for(int i=0;i<total;i++) {
       OrderSelect( i, SELECT_BY_POS, MODE_TRADES );
    //--- Assert OrderType is either BUY or SELL
       if( OrderType() <= 1 ) count++;
    }
    return( strBotInt( "Total", count ) );
 }
 string BotOrdersTrade(int pos=0, bool noPending=true)
 {
    int count=0;
    string msg="";
    const string strPartial="from #";
    int total=OrdersTotal();
 //--- Assert optimize function by checking total > 0
    if( total<=0 ) return( msg );   
 //--- Assert determine count of all trades that are opened
    for(int i=0;i<total;i++) {
       OrderSelect( i, SELECT_BY_POS, MODE_TRADES );
    //--- Assert OrderType is either BUY or SELL if noPending=true
       if( noPending==true && OrderType() > 1 ) continue ;
       else count++;
    //--- Assert return trade by position if pos>0
       if( pos>0 && count!= pos ) continue;

       msg = StringConcatenate(msg, strBotInt( "Ticket",OrderTicket() ));
       msg = StringConcatenate(msg, strBotStr( "Symbol",OrderSymbol() ));
       msg = StringConcatenate(msg, strBotInt( "Type",OrderType() ));
       msg = StringConcatenate(msg, strBotDbl( "Lots",OrderLots(),2 ));
       msg = StringConcatenate(msg, strBotDbl( "OpenPrice",OrderOpenPrice(),5 ));
       msg = StringConcatenate(msg, strBotDbl( "CurPrice",OrderClosePrice(),5 ));
       msg = StringConcatenate(msg, strBotDbl( "StopLoss",OrderStopLoss(),5 ));
       msg = StringConcatenate(msg, strBotDbl( "TakeProfit",OrderTakeProfit(),5 ));
       msg = StringConcatenate(msg, strBotTme( "OpenTime",OrderOpenTime() ));
       msg = StringConcatenate(msg, strBotTme( "CloseTime",OrderCloseTime() ));

    //--- Assert Partial Trade has comment="from #<historyTicket>"
       if( StringFind( OrderComment(), strPartial )>=0 )
          msg = StringConcatenate(msg, strBotStr( "PrevTicket", StringSubstr(OrderComment(),StringLen(strPartial)) ));
       else
          msg = StringConcatenate(msg, strBotStr( "PrevTicket", "0" ));
    }
 //--- Assert msg isnt empty
    if( msg=="" ) return( msg );   

 //--- Assert append count of trades
    if( pos>0 ) 
       msg = StringConcatenate(strBotInt( "Count",1 ), msg);
    else
       msg = StringConcatenate(strBotInt( "Count",count ), msg);
    return( msg );
 }
 string BotOrdersTicket(int ticket, bool noPending=true)
 {
    string msg=NL;
    const string strPartial="from #";
    int total=OrdersTotal();
 //--- Assert optimize function by checking total > 0
    if( total<=0 ) return( msg );

 //--- Assert determine history by ticket
    if( OrderSelect( ticket, SELECT_BY_TICKET, MODE_TRADES )==false ) return( msg );

 //--- Assert OrderType is either BUY or SELL if noPending=true
    if( noPending==true && OrderType() > 1 ) return( msg );

 //--- Assert OrderTicket is found
    msg = StringConcatenate(msg, strBotInt( "Ticket",OrderTicket() ));
    msg = StringConcatenate(msg, strBotStr( "Symbol",OrderSymbol() ));
    msg = StringConcatenate(msg, strBotInt( "Type",OrderType() ));
    msg = StringConcatenate(msg, strBotDbl( "Lots",OrderLots(),2 ));
    msg = StringConcatenate(msg, strBotDbl( "OpenPrice",OrderOpenPrice(),5 ));
    msg = StringConcatenate(msg, strBotDbl( "CurPrice",OrderClosePrice(),5 ));
    msg = StringConcatenate(msg, strBotDbl( "StopLoss",OrderStopLoss(),5 ));
    msg = StringConcatenate(msg, strBotDbl( "TakeProfit",OrderTakeProfit(),5 ));
    msg = StringConcatenate(msg, strBotTme( "OpenTime",OrderOpenTime() ));
    msg = StringConcatenate(msg, strBotTme( "CloseTime",OrderCloseTime() ));
 //--- Assert Partial Trade has comment="from #<historyTicket>"
    if( StringFind( OrderComment(), strPartial )>=0 )
       msg = StringConcatenate(msg, strBotStr( "PrevTicket", StringSubstr(OrderComment(),StringLen(strPartial)) ));
    else
       msg = StringConcatenate(msg, strBotStr( "PrevTicket", "0" ));
    return( msg );
 }
 string BotHistoryTicket(int ticket, bool noPending=true)
 {
    string msg=NL;
    const string strPartial="from #";
    int total=OrdersHistoryTotal();
 //--- Assert optimize function by checking total > 0
    if( total<=0 ) return( msg );   
 //--- Assert determine history by ticket
    if( OrderSelect( ticket, SELECT_BY_TICKET, MODE_HISTORY )==false ) return( msg );

 //--- Assert OrderType is either BUY or SELL if noPending=true
    if( noPending==true && OrderType() > 1 ) return( msg );

 //--- Assert OrderTicket is found
    msg = StringConcatenate(msg, strBotInt( "Ticket",OrderTicket() ));
    msg = StringConcatenate(msg, strBotStr( "Symbol",OrderSymbol() ));
    msg = StringConcatenate(msg, strBotInt( "Type",OrderType() ));
    msg = StringConcatenate(msg, strBotDbl( "Lots",OrderLots(),2 ));
    msg = StringConcatenate(msg, strBotDbl( "OpenPrice",OrderOpenPrice(),5 ));
    msg = StringConcatenate(msg, strBotDbl( "ClosePrice",OrderClosePrice(),5 ));
    msg = StringConcatenate(msg, strBotDbl( "StopLoss",OrderStopLoss(),5 ));
    msg = StringConcatenate(msg, strBotDbl( "TakeProfit",OrderTakeProfit(),5 ));
    msg = StringConcatenate(msg, strBotTme( "OpenTime",OrderOpenTime() ));
    msg = StringConcatenate(msg, strBotTme( "CloseTime",OrderCloseTime() ));

 //--- Assert Partial Trade has comment="from #<historyTicket>"
    if( StringFind( OrderComment(), strPartial )>=0 )
       msg = StringConcatenate(msg, strBotStr( "PrevTicket", StringSubstr(OrderComment(),StringLen(strPartial)) ));
    else
       msg = StringConcatenate(msg, strBotStr( "PrevTicket", "0" ));
    return( msg );
 }
 string BotOrdersHistoryTotal(bool noPending=true)
 {
    return( strBotInt( "Total", OrdersHistoryTotal() ) );
 }
 //|-----------------------------------------------------------------------------------------|
 //| A C C O U N T S T A T U S |
 //|-----------------------------------------------------------------------------------------|
 string BotAccount(void)
 {
    string msg=NL;
    msg = StringConcatenate(msg, strBotInt( "Number",AccountNumber() ));
    msg = StringConcatenate(msg, strBotStr( "Currency",AccountCurrency() ));
    msg = StringConcatenate(msg, strBotDbl( "Balance",AccountBalance(),2 ));
    msg = StringConcatenate(msg, strBotDbl( "Equity",AccountEquity(),2 ));
    msg = StringConcatenate(msg, strBotDbl( "Margin",AccountMargin(),2 ));
    msg = StringConcatenate(msg, strBotDbl( "FreeMargin",AccountFreeMargin(),2 ));
    msg = StringConcatenate(msg, strBotDbl( "Profit",AccountProfit(),2 ));

    return( msg );
 }
Enter fullscreen mode Exit fullscreen mode

Example OrderTicket

Conclusion

In this tutorial, you used a Telegram Bot to query your orders from a Metatrader 4 client. You can use this approach to manage your order flow, view account details, open and close orders, or even broadcast trade signals to a Telegram group or channel.

Get the Source Code

You can download the above source code from GitHub repository MT4-Telegram-Bot-Recon.

What To Do Next

You can further extend your Bot in several meaningful ways:

  1. Implementing Authentication - This is to ensure that only approved users have access to the Bot commands.
  2. Implementing Open and Close Orders - This is to allow opening and closing orders using the Bot.
  3. Implementing Modify SL and TP - This is to allow modifying the StopLoss and TakeProfit of an order.
  4. Implementing Add and Delete Pending Orders - This is to manage pending orders using the Bot.
  5. Create a Chart Query Tool: This is to allow users to query chart values, such as prices and indicator values for an instrument.
  6. Broadcast Trading Signals in a Channel - This is to allow users to subscribe to your trade signals (one way communication).
  7. Copy Trading Signal to a MT4 Client - This is to allow users to trade your signals automatically (requires a Client Bot).

Discussion (20)

pic
Editor guide
Collapse
ravi9650 profile image
ravi9650

Hi Dennis,
Thanks for the great post/tutorial. Your code is working as EA. I am trying to use the snippets from it inside and indicator to send the telegram message to a channel but couldn't. Could you please post a code snippet to send telegram message when some event happens (e.g. MA crossover etc, MA Price crossover) etc. ? Your help is highly appreciated.

Collapse
wuyti profile image
Thomas

Hello Dennis,

I keep getting following error message when trying to send a picture with SendPhoto command: {"ok":false,"error_code":400,"description":"Bad Request: chat not found"}.

The sendMessage command to my channel works perfectly though.
Your help is highly appreciated.

Collapse
dantese747 profile image
dantese747

Hey Dennis, could you please explain 3 things?

1. How do you create push notifications in your "Telegram Recon Bot". Bc, I have notifications only when I press any option, like "/help" or "/ordertrade";
2. How do you transfer the notifications from the bot to your channel, this is another mystery?
3. How do you create "Type: as BUY or SELL", instead of "Type: 0 (buy) or 1 (sell)" how do you have it in the file - PlusBotRecon.

Really appreciate your answer.
Dantese

Collapse
caporalx profile image
caporalx

I manage to install and attach the 2 EA, one for each chart.

I manage to query the mt4 but I don't receive messages when a trade is placed.

I don't know the step I missed. Can someone help me?

Collapse
fxalex profile image
Dtrader

Telegram signal Alert triggers this message: "Bad Request: chat not found". any idea how to solve it? thanks
gyazo.com/940797ef5b9b894de771fd97...

Collapse
asikhemhen profile image
Asikhemhen

Thank you sir for sharing this info.

please mine Chart is showing "Error: HTTP Request Failed."

Please help.

Collapse
pavansantani profile image
pavansantani

Hi, Thank you for such a great system. But i have some problem while compiling Telegram Recon Ea in meta editor.

And, also the Telegram alert ea does not send pending order price levels when it sends msg to telegram channel.

Screen shot attached below.

Also, The /help command does not works.

Image link

ibb.co/ZMLRwqN

ibb.co/zZ7zdd9
ibb.co/T0df8yQ
ibb.co/yff490g

Collapse
dennislwm profile image
dennislwm Author

my bad.. I forgot to add the include/plusinit.mqh file in the git repository.

you should be able to compile the EA now

github.com/dennislwm/MT4-Telegram-...

Collapse
pavansantani profile image
pavansantani

Thank you so much for replying. but some more problem occurred.

Please let me know what i can do....

Image link
ibb.co/MhqYPcN
ibb.co/nz4yC41

Thread Thread
dennislwm profile image
dennislwm Author

I don't quite understand the error ...

There are only 54 lines TOTAL in the plusinit.mqh file, but your screen states error in line 274 in plusinit.mqh.

Could you send a screenshot of the line 274 in YOUR plusinit.mqh?

Thread Thread
pavansantani profile image
pavansantani

ibb.co/whLzkPR

Also checked for html error
ibb.co/7NvF3VP

Thread Thread
dennislwm profile image
dennislwm Author

snipboard.io/tzmSHP.jpg

This is the screenshot of MY plusinit.mqh. It's not a HTML file, but a Metatrader Query Language [MQL] file.

Are you sure you downloaded the correct file :)?

Thread Thread
pavansantani profile image
pavansantani

I am so sorry i pressed download button and it downloaded html file not .mqh file but extension remained mqh.

SORRY

and, one last question do we have to run both ea for proper function. :)

what is the diff between both???

and, THANK YOU SO MUCH FOR REPLYING.....

EA compiled but not working means no msg sent even after a pending order

nor help command anything working

ibb.co/CMMtvCk

ea settings

ibb.co/zxh6sHX

i am a complete unknown to windows postman

Thread Thread
dennislwm profile image
dennislwm Author

I wouldn't run both EAs unless there are two separate bots - one for each EA.

Each EA is a standalone.

The Telegram_Recon EA only returns opened orders. You will need to change EA code for pending orders to work.

Thread Thread
pavansantani profile image
pavansantani

OK. THANK YOU SO MUCH.

Collapse
guillebdev profile image
guillebdev

Hello friend, thanks for sharing something so valuable. I'm trying to make it work with a private channel but I can't get it to work, I really don't know anything about the code .. Could you send me the bot where I can send the trades to a private telegram channel? Thank you very much

Collapse
rickyzimm profile image
RickyZimm

Hello i have a question.

The TelegramAlert script is working and i added a pip win/loss on close notification but there is 1 thing i cannot program myself and that is order modify notification.

It notifies when Open, Closing, Pending but if i change my SL or TP i also want to be notified in telegram.
Is that possible?

So i open an order with no TP and SL ----> Telegram notification of new opened order.
I change TP or SL -----> Notify on telegram that a new TP or SL has been set

So what i want is:
I open a order...

--- OPEN ORDER ---
Symbol: EURO/USD
Type: BUY
Open Price: 1.24000

TP: 0.00000 SL: 0.00000

Now i add a SL and TP to the order.

--- MODIFIED ORDER ---
Symbol: EURO/USD
Type: BUY
Open Price: 1.24000
TP: 1.24100 SL: 1.23900

I dont want to control my trades with telegram, but only want to be notified if an order changes.
Can someone help with that?
That would be great. :-)

Collapse
nicolazucchia profile image
NicolaZucchia

Hi Dennis, I found very useful and precious your bot. Anyway, I tried to attach the TelegramAlert EA to a chart to send messages to telegram, and although I put my Bot Token and Channel name in the input values, MT4 tells me "Chat not found" as an error. I allowed DDL and Web Request, but still had no fortune.
Might you tell me if I am missing something to have it work properly?
Thanks

Collapse
yibee7 profile image
Yibee7

Hi,
I'm trying to compile Telegram.Mqh and attached is the error encountered.
Any suggestion where is the real issues here..
Thanks

Collapse
xhoose profile image
xhoose

Hello, thank you very much for the code, I would like to know how I can place an order in MT4 from telegram