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.
This is what our end results will look like.
( : notice notification time and Period|| )
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.