Automatic notifications/updates when a trade setup forms via mql WebRequest

Written by Nicholas on Thu, 10/03/2019 - 12:42

The Idea:
As a trader who analyzes candle patterns, it might be tiring , to keep checking for trade setups every now and then (especially if you are trading on
smaller timeframes eg M15 or M30).

In this blog, we will explore how one could create a simple notification system that makes sure, one never missed a pattern if it forms (unless they wanted to) on any chart that the EA  will be running on.

Introduction:
The logic behind it;

  • Compare candle patterns  and if a pattern occurs/matches , call another function that sends the details of the event to a REST processing endpoint via mql5 WebRequest
  • This endpoint will then take that object (from the EA) and process it, before passing it on to a notification/message channel like Slack, Whastapp via their api(s).

Setting up MetaTrader to send WebRequests

For the metatrader on your machine to send requests to the external url(s) specified in your custom  expert advisor, you must Allow WebRequest for the listed urls.

To do this, click on Tools, and then Options, check Allow automated trading and Allow WebRequest from listed URLS,

make sure to enter your full url before clicking save. check function (void sendToUrl()), we will use the url value there.
metatrader options

This is what our end results will look like.
( : notice notification time and Period|| )

slack channel

 

We will get direct to code, please see comments in the code.

CODE (MQL5 and PHP as the endpoint consumer)

MQL5

//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
// candle properties,inside HTTP client,inside candle age call 
// candle properties the on tick have the age
void OnTick()
  {
      // create a string variable for custom time
      string currentTime;
 
      string timeLocalNow;
 
      string candleBirthTime;
 
      //string rawCurrentTime;
 
      datetime candleAge; // in seconds
 
      int candleAgeSec; // int
 
      // call custom functions to supply above variables
      // with the relevant values
      currentTime = TimeToString(TimeCurrent(),TIME_DATE|TIME_SECONDS);
      timeLocalNow = GetTheCurrentTime();
      candleBirthTime = GetCandleBirthTime();
      candleAge = GetCandleAge();
      candleAgeSec = Age();
 
      // our candle characteristics logic below
      // Create an array with price data 10 candles
      MqlRates priceData[];
 
      // Sort data from current candle to oldest (10th)
      ArraySetAsSeries(priceData,true);
 
      //Copy price data into array
      int Data = CopyRates(_Symbol,_Period,0,10,priceData);
 
      // closePrice for candle[1] in array
      double closePriceBarOne = priceData[1].close;
      // if bar 0 is a bear and has a flat top
      // i.e open == high
      if(priceData[0].open == priceData[0].high && candleAgeSec == 780)// Add more logic
        {
            sendToUrl();
        }
 
      // if bar 0 is a bull and has a flat bottom
      // i.e open == low
 
      if(priceData[0].open == priceData[0].low && candleAgeSec == 780) //Add more logic
        {
            sendToUrl();
        }
 
      //if(candleAgeSec == 31)// Send signal on when candle reach a specific age only
        //{
            //sendToUrl();         
        //}
 
      Comment ("Time Current Now: ", currentTime,
               "\n Time Local Now: ", timeLocalNow,
               "\n Candle BirthTime: ", candleBirthTime,
               "\n CandleAge in DateStamp: ", candleAge,
               "\n CandleAge in Sec: ", candleAgeSec,
               "\n ClosePriceBar[1]: ", closePriceBarOne);
 
  }
//+------------------------------------------------------------------+
 
string GetTheCurrentTime()// Returns the curent time so we can use that against candle age
{
   // create a string for time in seconds
   string TimeWithSeconds;
 
   // calculate time with seconds for local time
   TimeWithSeconds = TimeToString(TimeLocal(),TIME_DATE|TIME_SECONDS);
 
   return TimeWithSeconds;
}
 
string GetCandleBirthTime()// Gets the time the candle is formed (.open)
{
   string CandleBirthTime;
 
   MqlRates priceData[]; // create a price array
 
   // sort the array from the current candle downwards
   ArraySetAsSeries(priceData, true);
 
   //copy candle prices for 3 candles into array
   CopyRates(_Symbol,_Period,0,3,priceData);
 
   // create a counter for the candle
   static int candleCounter; //+1 with every new candle
 
   // create a Datetime variable for last time stamp
   // will hold the timestamp for the last time we
   // checked
   static datetime timeStampLastCheck;
 
   // create a dateime variable for current candle
   // we can check the candle age here now
   datetime timeStampCurrentCandle;
 
   // read time stamp for current in array
   timeStampCurrentCandle = priceData[0].time; // This be the close time
   // of candle[1], and the open time for candle[0], not sure test and confirm
 
   // if the current timestamp is different from last time
   if (timeStampCurrentCandle != timeStampLastCheck)
   {
      // remember current timestamp for next time @TODO add more logic
      timeStampLastCheck = timeStampCurrentCandle;
 
      // we should check candle age here too
      candleCounter = candleCounter+1;
 
   }
 
    // inside above if statement its called only on new candle AND NOT always
    CandleBirthTime = TimeToString(priceData[0].time,TIME_DATE|TIME_SECONDS);
 
    //return CandleBirthTime; 
 
    return CandleBirthTime;
}
 
datetime GetCandleAge()
{
   datetime timeNow;
 
   //timeNow = TimeLocal();
   timeNow = TimeCurrent();
 
   MqlRates priceData[]; // create a price array
 
   // sort the array from current candle downwards
   ArraySetAsSeries(priceData,true);
 
   int CandleAge;
 
   // copy candle prices for 3 candles into array
   CopyRates(_Symbol,_Period,0,3,priceData);
 
   // create a counter for the candle
   static int candleCounter; // +1 with every new candle
 
   // create a Datetine val for the last time stamp
   // will hold the timestamp for the last time we
   // checked
   static datetime timeStampLastCheck;
 
   // create datetim val for current candle 
   // we can the casndle age here now
   datetime timeStampCurrentCandle;
 
   // read time stamp for current in array
   timeStampCurrentCandle = priceData[0].time;// This be the close time
   // of candle[1]
 
   if (timeStampCurrentCandle != timeStampLastCheck)
      {
         // remember current timeStamp if different from last time @TODO add more logic
         timeStampLastCheck = timeStampCurrentCandle;
         candleCounter = candleCounter+1;
      }
 
      CandleAge = timeNow - (priceData[0].time);
 
      return CandleAge;
 
}
 
int GetCandleAgeSec()
{
   datetime timeNow;
 
   //timeNow = TimeLocal();
   timeNow = TimeCurrent();
 
   MqlRates priceData[]; // create a price array
 
   // sort the array from current candle downwards
   ArraySetAsSeries(priceData,true);
 
   int CandleAgeInSec;
 
   // copy candle prices for 3 candles into array
   CopyRates(_Symbol,_Period,0,3,priceData);
 
   // create a counter for the candle
   static int candleCounter; // +1 with every new candle
 
   // create a Datetine val for the last time stamp
   // will hold the timestamp for the last time we
   // checked
   static datetime timeStampLastCheck;
 
   // create datetim val for current candle 
   // we can the casndle age here now
   datetime timeStampCurrentCandle;
 
   // read time stamp for current in array
   timeStampCurrentCandle = priceData[0].time;// This be the close time
   // of candle[1]
 
   if (timeStampCurrentCandle != timeStampLastCheck)
      {
         // remember current timeStamp if different from last time
         timeStampLastCheck = timeStampCurrentCandle;
         candleCounter = candleCounter+1;
      }
 
      CandleAgeInSec = timeNow - priceData[0].time;
 
      //CandleAge = TimeToString(CandleAge,TIME_DATE|TIME_SECONDS);
 
      return CandleAgeInSec;
 
}
 
int Age()
{
   int Age;
 
   //Age = TimeToString(GetCandleAge(),TIME_DATE|TIME_SECONDS);
   Age = GetCandleAgeSec();
 
   return Age;
}
 
void sendToUrl()
{
   //----REST client's HTTP vars TODO modify to your own refer to
 
   string url = "https://mysite.com/api/incoming-forex-signals";
   char post[];
   char result[];
   string headers;
   int res;
   string signal = " WORK || Some setup on :: Symbol = "+ _Symbol + " Period || " + _Period;
   StringToCharArray(signal,post);
   //---reset last error
   ResetLastError();
   //---post data to REST API
   res = WebRequest("POST",url,NULL,NULL,50,post,ArraySize(post),result,headers);
   //---check errors
   if(res==-1)
     {
         Print("Error code = ",GetLastError());
         //--- maybe the url is not added show message to add it
         MessageBox("Add address '"+url+"' in Expert Advisors tab of the Options window","Error",MB_ICONINFORMATION);
     }
     else
     {
         //----successful
         Print("REST client's POST: ", signal);
         Print("Server Response: ", CharArrayToString(result,0,-1));
     }
}

PHP (consumer maybe any)

Using drupal controller to process the meta trader incoming obj, see $content = $request->getContent(); and change according to your consumer

make sure to run composer require nexylan/slack

<?php
 
namespace Drupal\forex_trend\Controller;
 
 
use Drupal\Component\Serialization\Json;
use Drupal\Core\Controller\ControllerBase;
use Nexy\Slack\Client;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
 
/**
 * using drupal controller to process the object from
 * meta trader,use can change how get and process the object
 * from meta trader depending on your type of consumer
 *
 * @TODO, add extra data to the object and validate before processing
 * like a token
 */
class mqlLogController extends ControllerBase {
 
  public function mqlLocalLogs (Request $request){
 
    $params = [];
    /**
     * Get the content object incoming from metatrader
     */
    $content = $request->getContent();
 
    /**
     * Slack settings
     * see https://github.com/nexylan/slack
     * @TODO change all names/phrases with fake
     */
    $settings = [
      'username' => 'FAKENAME',
      'channel' => '#fake_channel_name'
    ];
 
    $client = new Client('https://hooks.slack.com/services/FAKESLACKWEBPOINT', $settings);
 
    if (!empty($content)) {
 
      #$params = Json::decode($content); if you wish todo anything with the $content array
      $client->send('Hello with Json'. $content);
 
    }
 
    if (empty($content)) {
      $client->send('Hello No JSON');
    }
 
    // Process $params...
    return new JsonResponse($params);
  }
 
}

The above logic (mql5) only checks the characteristics of two candles to  ascertain if the setup/pattern is valid and sends the required chart details, if so. It can be improved to give more accurate and maybe useful notifications/alerts.

Blogs