Skip to content

FX-Git

Metatrader, R and Python for Happi Online Traders

  • Home
  • Python, R for Trading
    • Articles
    • RStudio (External)
  • Pricing
  • Telegram
  • Login
  • Timeline
  • Home
  • Python, R for Trading
    • Articles
    • RStudio (External)
  • Pricing
  • Telegram
  • Login
  • Timeline

Monthly Archives: June 2020

  • Home 2020
  • June

Monte Carlo Analysis of Two Metatrader Signal Providers

Introduction

TL;DR

We perform a comparative analysis of two Metatrader Signal Providers. The initial Monte Carlo Analysis shows that the first signal provider has a significantly higher return. However, we will show you that an extended MCA, which account for slippages, changes the ranking of the signal providers and ensures a realistic simulation and analysis of returns.

Stylized Facts

  • When evaluating a signal provider, a higher average profit ensures a sufficient buffer for slippages.

  • An extended Monte Carlo Analysis, which account for slippages, ensures a realistic simulation and analysis of returns.

  • As we will show below, an extended MCA may change the ranking of the signal providers, which in turn affects your portfolio.

Step 1 - Get Statistics

In order to perform a Monte Carlo Analysis ["MCA"] of a Signal Provider, we require several inputs:

  • Total Trades

  • Avg Profit ($)

  • Avg Loss ($)

  • Win Percentage

Let us do a comparative analysis of Automated Robot 1 ["AR1"] and Automated Trading Robot 2 ["ATR2"].

Statistics of AR1

  • Total Trades: 311

  • Avg Profit ($): 7.92

  • Avg Loss ($): -7.48

  • Win Percentage (decimal): 0.9389

Statistics of ATR2

  • Total Trades: 657

  • Avg Profit ($): 10.79

  • Avg Loss ($): -8.29

  • Win Percentage (decimal): 0.9452

Trade Simulation

The MCA function requires a time series of returns (zoo object) as input, hence we need to simulate the returns, given the statistics above.

The function monteSimulateReturnsZoo() returns a time series of returns (zoo object) and accepts FIVE inputs:

  • tradesNum - a integer for total trades

  • pAvgNum - a double for average profit in dollars

  • lAvgNum - a double for average loss in dollars

  • wPctNum - a double for winning percentage in decimal

  • initNum - a double for initial capital in dollars (default: 10000)

Type the following command in R to simulate returns for both AR1 and ATR2:

 > AR1Zoo <- monteSimulateReturnsZoo( 311, 7.92, -7.45, 0.9389 )
 > ATR2Zoo <- monteSimulateReturnsZoo( 657, 10.79, -8.29, 0.9452 )

Step 2 - Monte Carlo Analysis

A Monte Carlo Analysis is a series of random walk forward returns that shows a set of possible outcomes. Each line represents a possible return of a client who follows the given signal provider. Hence, if there are a hundred lines, then these represents 100 possible returns of each client.

Advantages

We use MCA to look at the worst and average lines from all possible returns to evaluate a signal provider.

For example, from the worst case line, we can estimate the worst case drawdown of our portfolio. If this percentage is too high for our risk appetite, then we should not follow the signal.

Next, we look at the average line, where we can expect to get a similar return for our portfolio. Hence, we should estimate our initial capital to ensure a significant average profit, net of fees. In other words, if the average net profit isn't sufficient, then we should not follow the signal or we could increase our initial capital.

Disadvantages

As the MCA is a series of random walk forward returns, each set may not cover all possible outcomes in the universe. Hence, when we look at the worst or average lines from a set, we are only looking within a subset of the whole universe.

The output of the MCA is only as good as the inputs. We perform an Extended MCA to allow for slippages, which affect the outcomes.

MCA in R

The function MonteGrowReturns() returns a Monte object and accepts SEVEN inputs:

  • rawZoo - a zoo object of time series returns

  • setNum - an integer of number of lines

  • sizeNum - an integer of block size, where blocks are kept together (default: 1)

  • initNum - a double for initial capital in dollars (default: 10000)

  • replaceBln - a boolean for sampling replacement (default: TRUE)

  • summary - a boolean for summary (default: TRUE)

  • debug - a boolean for debugging (default: FALSE)

Type the following command in R to perform MCA for both AR1 and ATR2:

 > AR1Monte <- MonteGrowReturns(AR1Zoo, 100)
 > ATR2Monte <- MonteGrowReturns(ATR2Zoo, 100)
 > plot(AR1Monte)
 > plot(ATR2Monte)

Note: At writing, there appears to be an error in the summary, hence we set summary to FALSE. Otherwise, we should accept all other default values.

Results

 # Summary of AR1
 Initial equity balance    : 10000.00 
 Absolute maximum drawdown :   -51.33 ( -0.4%) [set = 99]
 Absolute median drawdown  :   -25.87 ( -0.2%)
 Absolute minimum drawdown :   -14.90 ( -0.1%) [set = 88]
 Maximum equity balance    : 12341.67 ( 23.4%) [set = 70]
 Median equity balance     : 12157.23 ( 21.6%)
 Minimum equity balance    : 11960.57 ( 19.6%) [set = 99]

The MCA plot of AR1 (shown below) appears to have higher returns (between 19.6% and 23.4% over 10 months) for all its possible outcomes, compared to the returns for the MCA plot of ATR2.

Step 3 - Get Extended Statistics

In order to perform a more accurate MCA, we need to account for slippages, hence we require further inputs:

  • Worst Broker Slippage (pip)

  • Total Profit ($) = Gross Profit ($) + Gross Loss ($)

  • Total Profit (pip) = Gross Profit (pip) + Gross Loss (pip)

  • Pip to Dollar (decimal)

We calculate the Pip to Dollar conversion factor as follows:

   Pip to Dollar = Total Profit (pip) / Total Profit ($)
  • Worst Broker Slippage ($)

We calculate the Worst Broker Slippage ($) as follows:

   Worst Broker Slippage ($) = Pip to Dollar * Worst Broker Slippage (pip)
   Adjusted Avg Profit ($)   = Avg Profit ($) - Worst Broker Slippage ($)
   Adjusted Avg Loss ($)     = Avg Loss ($) - Worst Broker Slippage ($)
  • Percent Decrease (decimal)

After adjusting both the average profit and loss for slippage, it follows that the winning percentage should decrease. However, we don't have the exact number of profitable trades that are less than slippage, e.g. profit of 1.54 will be a loss after slippage of 1.74.

We estimate the Percentage Decrease (decimal) as follows:

   Percentage Decrease (decimal)     = Worst Broker Slippage ($) / Avg Profit ($)
   Adjusted Win Percentage (decimal) = Win Percentage * (1 - Percentage Decrease)

Extended Statistics of AR1

  • Worst Broker Slippage (pip): 21.7

  • Total Profit ($) = 2313.92 - 141.48 = $2,172.44

  • Total Profit (pip) = 27607 - 548 = 27,049

  • Pip to Dollar (decimail) = 2172.44 / 27049 = 0.080315

  • Worst Broker Slippage ($) = 0.080315 * 21.7 = $1.74

  • Percentage Decrease (decimal) = 1.74 / 7.92 = 0.22

Adjusted Statistics of AR1

  • Adj Avg Profit ($): 7.92 - 1.74 = 6.18

  • Adj Avg Loss ($): -7.48 - 1.74 = -9.22

  • Adj Win Percentage (decimal) = (1 - 0.22) * 0.9389 = 0.7323

Extended Statistics of ATR2

  • Worst Broker Slippage (pip): 21.7

  • Total Profit ($) = 6701.56 - 298.58 = 6402.98

  • Total Profit (pip) = 50848 - 1469 = 49379

  • Pip to Dollar (decimal) = 6402.98 / 49379 = 0.12967

  • Worst Broker Slippage ($) = 0.12967 * 21.7 = 2.81

  • Percentage Decrease (decimal) = 2.81 / 10.79 = 0.26

Adjusted Statistics of ATR2

  • Adj Avg Profit ($) = 10.79 - 2.81 = 7.98

  • Adj Avg Loss ($) = -8.29 - 2.81 = -11.10

  • Adj Win Percentage (decimal) = (1 - 0.26) * 0.9452 = 0.6987

Trade Simulation

Type the following command in R to simulate returns for both AR1 and ATR2:

 > AdjAR1Zoo <- monteSimulateReturnsZoo( 311, 6.18, -9.22, 0.7323 )
 > AdjATR2Zoo <- monteSimulateReturnsZoo( 657, 7.98, -11.10, 0.6987 )

Step 4 - Extended MCA

Extended MCA in R

Type the following command in R to perform MCA for both adjusted AR1 and ATR2:

 > AdjAR1Monte <- MonteGrowReturns(AdjAR1Zoo, 100)
 > AdjATR2Monte <- MonteGrowReturns(AdjATR2Zoo, 100)
 > plot(AdjAR1Monte)
 > plot(AdjATR2Monte)

Note: At writing, there appears to be an error in the summary, hence we set summary to FALSE. Otherwise, we should accept all other default values.

Result for AR1

 Initial equity balance    : 10000.00 
 Absolute maximum drawdown :  -227.92 ( -2.2%) [set = 41]
 Absolute median drawdown  :   -89.13 ( -0.9%)
 Absolute minimum drawdown :   -48.98 ( -0.5%) [set = 39]
 Maximum equity balance    : 10841.58 (  8.4%) [set = 30]
 Median equity balance     : 10495.86 (  5.0%)
 Minimum equity balance    : 10074.93 (  0.7%) [set = 14]

The extended MCA plot of AR1 appears to show a significant decrease in possible returns (between 0.7% and 8.4% over ten months), compared to its previous MCA returns (250% to 320% over the same period).

Result for AR2

 Initial equity balance    : 10000.00 
 Absolute maximum drawdown :  -322.22 ( -3.0%) [set = 93]
 Absolute median drawdown  :  -122.35 ( -1.1%)
 Absolute minimum drawdown :   -74.47 ( -0.7%) [set = 2]
 Maximum equity balance    : 12490.06 ( 24.9%) [set = 39]
 Median equity balance     : 11892.47 ( 18.9%)
 Minimum equity balance    : 11339.41 ( 13.4%) [set = 3]

Similarly, the extended MCA plot of ATR2 (shown below) appears to show a decrease in possible returns (between 13.4% to 24.9% over ten months), compared to its previous MCA returns (19.6% to 23.4% over the same period).

However, the possible returns of ATR2 (10-25%) are significantly higher than AR1 (1-9%). Hence, we should follow ATR2 signal.

Conclusion

In this article, you performed a comparative analysis of two Metatrader Signal Providers. The initial Monte Carlo Analysis showed that the first signal provider has a significantly higher return. However, the extended MCA, which account for slippages, changes the ranking of the signal providers and ensures a realistic simulation and analysis of returns.

Get the Source Code

You can download the above source code from GitHub repository FX-Git-Pro.

  • 15 Jun, 2020
  • (0) Comments
  • By admin
  • Trading Psychology & Money Management

Building a Telegram Chat with a MT4 Forex Trading Expert Advisor

TL;DR

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.

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

 {
     "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"
             }
         }
     ]
 }

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,
  • 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",
  • 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",
  • 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"
  • 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"
  • 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

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": []
 }

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

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

 #include 

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;

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;

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();

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() );

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

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.

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 
 #include 
 //|-----------------------------------------------------------------------------------------|
 //|                               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=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 );
             }
          }
       }
    }
 };

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 
 #include 

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

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                              |
 //|-----------------------------------------------------------------------------------------|

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 
 #include 

Next, find and replace the following code:

 CCustomBot bot;

with our inherited class:

 CPlusBotRecon bot;

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();

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:

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 0
    if( total<=0 ) return( msg );   
 //--- Assert determine count of all trades that are opened
    for(int i=0;i 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 #"
       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 #"
    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 #"
    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 );
 }

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).
  • 8 Jun, 2020
  • (0) Comments
  • By admin
  • Chatbots

Building a Signal-based Metatrader Portfolio

TL;DR

The Myfxbook website automatically fetches data from your Metatrader account. This allows you to monitor the overall performance of your account as well as to break down the performance by different signal providers.

URL: http://www.myfxbook.com/members/dennislwm/fxgit/6201375

Copy Signal from MQL5

Choose a Signal

There are many variables when choosing a Signal Provider. I have ranked some of these variables from highest to lowest weight for your consideration:

  • Live Account - look only for live accounts. A signal provider that trades a demo account can take unnecessary risks.

  • Reviews - look for positive and high quality user reviews. A higher number of reviews is also a good indicator.

  • Profile Rating - the Signal Provider rating by MQL community. Avoid profiles with very low ratings.

  • Maximum drawdown - ideally the maximum drawdown should be below 40%. A higher number requires a higher tolerance for losses.

  • Maximum consecutive: - look for tolerable consecutive losses. A higher number indicates a higher risk of ruin for your account. A higher number of Maximum consecutive wins is also a good indicator.

  • Slippage - this indicator is important if your Signal provider uses a scalping strategy, i.e. Avg profit is less than 5 pips. Otherwise, this indicator does not make a significant difference.

  • Profit factor - I don't rate this indicator as highly as those indicators above. Having said that, you still need a high profit factor to mitigate slippage, loss connections, and to break even after signal fees.

  • Trades per week - look for between 5 and 15 trades per week. Too few trades can lead to lower performance, but too many trades can indicate a martigale strategy.

  • Avg holding time - I usually prefer more than 8 hours or more. Shorter time usually means a scalping strategy. However, this is a matter of individual preference.

  • Long / short trades - the ratio should ideally be 1:1. However, a 6:4 or 4:6 ratio is still acceptable.

  • Started - look for older signals that shows survivorship, especially during periods of volatility.

  • Price - a higher price may not indicate better quality. However, this has to be affordable as it will affect your monthly profits.

  • Reliability, Number of symbols - don't place too much emphasis on these indicator.

My Automated Robot 1 Signal

  • Revews - 4/4 good reviews with average rating of 4.5 stars

  • Profile Rating: 424 [58 - Openness; 194 - Friends; 291 - Signals provider; 29 - Reviews]

  • Maximum drawdown: 34.5%

  • Maximum consecutive: 8 losses (-4.78 AUD) vs 193 wins (1,649.50 AUD)

  • Slippage - median 4.77 pips vs avg profit 3 pips (RED FLAG)

  • Profit factor: 17.66

  • Trades per week: 5

  • Avg holding time: 11 hours

  • Long / short trades: 176:123 (58.9% vs 41.1%)

  • Started: 23/03/2020

  • Price: 30 USD per month

Feedback Loop

Analysing the actual performance will give some feedback on the above criteria. Hence, this helps us to refine our evaluation for future signal providers.

Fund Your Metatrader Account

Open a Metatrader Account

In order to fund your Metatrader account, you must first open an account. I chose a reputable local broker in Singapore. Obviously, you will need to do some research before choosing a broker.

There are some brokers who offer both MT4 and MT5 platforms, and this would be an added bonus. The reason is rather straightforward, you can copy MT4 signals to your MT4 account, and you can copy MT5 signals to your MT5 account, but you can't copy from MT4 to MT5, and vice versa.

Fund Your Account

You don't have to start big in order to trade signals, as the copied lot sizes can be pro-rated to your account balance.

I suggest starting at $1,000, and then grow your account slowly as you gain more experience and knowledge.

Publish on Myfxbook

The Myfxbook website automatically fetches data from your Metatrader account. This allows you to monitor the overall performance of your account as well as to break down the performance by different signal providers.

URL: http://www.myfxbook.com/members/dennislwm/fxgit/6201375

  • 8 Jun, 2020
  • (0) Comments
  • By admin
  • Signal Trade Copiers

Zero-Lag CurrencySlopeStrength Indicator

TL;DR

The CurrencySlopeStrength ["CSS"] indicator measures the strength of the currencies, however it does not measure the rate of change (or slope) of the strength. By integrating the GradientVolatility indicator with the CSS, i.e. GradientCSS indicator, you can have a zero-lag CSS indicator.

Introduction

I have been busy working on my own trading ideas. Below is an example of a component that I am working on, which is part of a bigger picture to develop my own set of tools and indicators, for a complete trading system.

My system has six key components: (i) multi-pair; (ii) market strength; (iii) trend; (iv) momentum; (v) price action; and (vi) parameter-less. Each individual component is not new, however this system will be unique and different from others as it uses my own integration of custom indicators. I may post again to further elaborate on my system in the near future.

GradientVolatility Integrated with CSS

As mentioned above, one of my own trading idea was to integrate the GradientVolatility indicator with the CurrencySlopeStrength (CSS) indicator. See the attached picture below.

The CSS measures the strength of the currencies, however it does not measure the rate of change (or slope) of the strength. For example, in the picture, the CSS showed that AUD is negative and decelerating, but it does not show the rate of deceleration. The rate of change is captured using the GradientCSS.

There is a lot of potential for this indicator, as shown yesterday, when I gained a total of +73 pips from two long EurNzd trades based on their rates of change. See the attached history (ignore the other two non-EurNzd trades).

Trade with the GradientCSS Indicator

The best part for trading on this indicator is that there won't be any optimization, as there are very few parameters. Hence, I do not have to worry about a high variance (over-fitting).

Note: There is a problem when calling the GradientCSS indicator with the iCustom() function, as it does not seem to like R very much.

Conclusion

Just an update, I have completely rewritten the GradientCSS indicator from the ground up to use MQL code only, by replacing the lm() function from R. Then the iCustom() function should be able to work with this indicator.

The GradientCSS indicator and its source code is available to my Patron subscribers.

  • 6 Jun, 2020
  • (0) Comments
  • By admin
  • Utilities Indicators and Scripts
Recent Posts
  • Monte Carlo Analysis of Two Metatrader Signal Providers
  • Building a Telegram Chat with a MT4 Forex Trading Expert Advisor
  • Building a Signal-based Metatrader Portfolio
  • Zero-Lag CurrencySlopeStrength Indicator
Categories
  • Chatbots
  • Signal Trade Copiers
  • Trading Psychology & Money Management
  • Utilities Indicators and Scripts
Tags
copier indicator metatrader montecarloanalysis mt4 mt5 portfolio R signal telegram trading tutorial
Archives
  • June 2020
Login with Patreon

© 2019 All Right Reserved | Arowana WordPress Theme