EA Status SMS Notifications
Introduction
My work often interferes with my ability to continuously monitor the terminal. It may be just 20-30 minutes but it may well be a whole day. I therefore decided to develop a system that would send an SMS to notify me of any critical situation, from a blackout to a problem like my computer not coming out of sleep mode after the weekend. I am sure that the system as provided in this article will be useful for many traders or will serve as a basis for those who don't find it good enough the way it is and would like to create their own 'masterpiece'.
In this article, I am going to demonstrate the development of a working solution to the specified task. It should be noted that I'm not even anywhere close to having professional skills in Java development so I had to ask my fellow who is a Java developer to help me debug and fix my failing code.
Plan
- Google Calendar features
- A few words about installation of Google Data APIs
- System flowchart
- Java application code
- The .bat file code
- Code of the Expert Advisor
- Shortcomings
- Conclusion
Google Calendar features
In developing the SMS notification system, I decided in favor of Google Calendar as it allows creating events in its free Calendar and you can use SMS as the means of notification of any such event. For the specified purpose, I created a special separate profile with Google.
In a nutshell, the description of the operating principle is as follows: if the current message is not deleted by 9.59 am (please see the above screenshot), i.e. if the Terminal does not get connected with Google server to initiate the replacement of the current message with a different one, an SMS will be sent to your phone with the appropriate notification.
A few words about installation of Google Data APIs
To be able to use Google services in developing your software solutions, Google provides the Google Data APIs documentation.
All the documentation that a Java developer may need when working with Google Calendar can be found using this link Google Calendar APIs and Tools.
To start programming, you will have to install Google Data Java Client Library. On the same page, you can find a link to the documentation related to the library for Eclipse. This is what I used and would recommend it to you: Using Eclipse with Google Data APIs.
System flowchart
I think that I should first comment upon the flowchart:
- the StopTXT event is required to be able to manually and remotely stop the flow of SMS just in case;
- the ReanimationTXT event is required to inform that for some reason the terminal managed to get connected after the corresponding SMS with a warning was sent to you;
Java application code
All the source codes and distribution used are attached at the end of the article.
package Calendar; /* INSTRUCTION: This is a command line application. So please execute this template with the following arguments: arg[0] = username arg[1] = password */ import com.google.gdata.client.GoogleService; import com.google.gdata.client.Query; import com.google.gdata.client.calendar.CalendarService; import com.google.gdata.data.DateTime; import com.google.gdata.data.PlainTextConstruct; import com.google.gdata.data.calendar.CalendarEntry; import com.google.gdata.data.calendar.CalendarEventEntry; import com.google.gdata.data.calendar.CalendarEventFeed; import com.google.gdata.data.calendar.CalendarFeed; import com.google.gdata.data.extensions.Reminder; import com.google.gdata.data.extensions.When; import com.google.gdata.data.extensions.Reminder.Method; import com.google.gdata.util.AuthenticationException; import com.google.gdata.util.ServiceException; import java.io.IOException; import java.net.MalformedURLException; import java.net.URL; import java.text.SimpleDateFormat; import java.util.Date; import java.util.GregorianCalendar; import java.util.List; public class Calendar { public static void main(String[] args) { try { //--- EXTERNAL VARIABLES ---------------------------- String alertTxt = args[2]; String reanimationTxt = args[3]; String stopSMS = args[4]; int timePeriod = Integer.parseInt(args[5]); // interval in minutes at which the terminal // will get connected int mode = Integer.parseInt(args[6]); int startHour = Integer.parseInt(args[7]); int startMin = Integer.parseInt(args[8]); //--------------------------------------------------- //--- INTERNAL VARIABLES -------------------------- URL feedUrl = new URL("http://www.google.com/calendar/feeds/default/private/full"); //link for working with events URL calendarUrl = new URL("http://www.google.com/calendar/feeds/default/allcalendars/full"); //link for working with calendars //--------------------------------------------------- System.out.println(">>---------- Start ----------<<"); // Create a new Calendar service, connect to Google CalendarService myService = new CalendarService("My Application"); myService.setUserCredentials(args[0], args[1]); // LOGIN AND PASSWORD AS ARGUMENTS // DECLARE VARIABLES FOR EVENT SEARCH Query myQuery = new Query(feedUrl); CalendarEventFeed myResultsFeed; CalendarEventEntry firstMatchEntry = null; String myEntryTitle; URL deleteUrl; When timeVar; SimpleDateFormat formater = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss"); Date newDate = new Date(); CalendarEventEntry myEntry; When eventTimes = new When(); DateTime startTime = null; DateTime endTime = null; int reminderMinutes; Method methodType = Method.SMS; Reminder reminder = new Reminder(); CalendarEventEntry insertedEntry; // PREPARE SEARCH QUERY myQuery.setFullTextQuery(stopSMS); // stopSMS TEXT EVENT myResultsFeed = myService.query(myQuery, CalendarEventFeed.class); if (myResultsFeed.getEntries().size() > 0) { System.out.println(">> STOP event found. \n>> Finish"); return; } // PREPARE SEARCH QUERY myQuery.setFullTextQuery(reanimationTxt); // reanimationTxt TEXT EVENT myResultsFeed = myService.query(myQuery, CalendarEventFeed.class); if (myResultsFeed.getEntries().size() > 0) { // IF FOUND System.out.println(">> REANIMATION event found"); // GET TITLE OF THE FIRST MATCH ENTRY firstMatchEntry = (CalendarEventEntry) myResultsFeed.getEntries().get(0); myEntryTitle = firstMatchEntry.getTitle().getPlainText(); // IF THE reanimationTxt EVENT FOUND, DELETE IT deleteUrl = new URL(firstMatchEntry.getEditLink().getHref()); myService.getRequestFactory().setHeader("If-Match", "*"); myService.delete(deleteUrl); System.out.println(">> ...deleting REANIMATION event"); } // PREPARE SEARCH QUERY myQuery.setFullTextQuery(alertTxt); // alertTxt TEXT EVENT myResultsFeed = myService.query(myQuery, CalendarEventFeed.class); if (myResultsFeed.getEntries().size() > 0) { // IF FOUND System.out.println(">> >> ALERT event found"); // GET TITLE OF THE FIRST MATCH ENTRY firstMatchEntry = (CalendarEventEntry) myResultsFeed.getEntries().get(0); myEntryTitle = firstMatchEntry.getTitle().getPlainText(); timeVar = firstMatchEntry.getTimes().get(0); System.out.println(">> >> event start&stop time : " + timeVar.getStartTime() + "\n>> >> : " + timeVar.getEndTime()); System.out.println(">> >> event start (milliseconds): " + timeVar.getStartTime().getValue()); System.out.println(">> >> new event start (milliseconds): " + (newDate.getTime() - (1000 * 60 * timePeriod))); if (timeVar.getStartTime().getValue() > newDate.getTime() - (1000 * 60 * 0)) { // IF THE START TIME OF THE OLD EVENT IS LESS THAN timePeriod MINUTES AGO // DELETE IT deleteUrl = new URL(firstMatchEntry.getEditLink().getHref()); myService.getRequestFactory().setHeader("If-Match", "*"); myService.delete(deleteUrl); System.out.println(">> >> event start > new event start"); System.out.println(">> >> ...deleting event"); System.out.println(">> >> #M### ##M #M# "); System.out.println(">> >> ### ### ### "); System.out.println(">> >> ### ### ### ### "); System.out.println(">> >> ### ### ### ### "); System.out.println(">> >> M## ### #M "); System.out.println(">> >> ### ### #### ### "); System.out.println(">> >> ### ### ### ### "); System.out.println(">> >> ### ### #M# ### "); System.out.println(">> >> ### ### ### ### "); System.out.println(">> >> ### ### ### "); System.out.println(">> >> #### ### ### "); } else { // THE TIME IS MORE THAN timePeriod MINUTES AGO, WHICH MEANS THAT THE sms HAS ALREADY BEEN SENT // DELETE IT deleteUrl = new URL(firstMatchEntry.getEditLink().getHref()); myService.getRequestFactory().setHeader("If-Match", "*"); myService.delete(deleteUrl); System.out.println(">> >> SMS has gone already"); System.out.println(">> >> event start < new event start"); System.out.println(">> >> ...deleting event"); System.out.println(">> >> .M#### M##M ###M . "); System.out.println(">> >> ### #### #### ### "); System.out.println(">> >> ### ### #### #### #M# ### "); System.out.println(">> >> ### ###M# ### "); System.out.println(">> >> #M#### # "); System.out.println(">> >> # ### # # @## #M#### "); System.out.println(">> >> ### # # ### "); System.out.println(">> >> #M# ### ### # # ### M## ### "); System.out.println(">> >> ### ### ### ### ### ### ### "); System.out.println(">> >> # ### ### ### # "); System.out.println(">> >> #### ### # ### #### "); // CREATE NEW EVENT myEntry = new CalendarEventEntry(); myEntry.setTitle(new PlainTextConstruct(reanimationTxt)); // SET ITS TITLE newDate.setTime(newDate.getTime() + 1000 * 60 * 1); // CURRENT TIME + 1 MINUTE startTime = DateTime.parseDateTime(formater.format(newDate)); // INSERTS THE LINE USING THE PATTERN "2009-06-30T20:55:00" System.out.println(">> >> creating REANIMATION event"); System.out.println(">> >> event starttime: " + formater.format(newDate)); newDate.setTime(newDate.getTime() + 1000 * 60 * 0); // AGAIN ADD THE CURRENT TIME // + 1 MINUTE endTime = DateTime.parseDateTime(formater.format(newDate)); System.out.println(">> >> event stoptime : " + formater.format(newDate)); eventTimes.setStartTime(startTime); eventTimes.setEndTime(endTime); myEntry.addTime(eventTimes); reminderMinutes = 0; // REMIND 1 MINUTE BEFORE reminder.setMinutes(reminderMinutes); reminder.setMethod(methodType); myEntry.getReminder().add(reminder); // CREATE A NEW Calendar service, CONNECT TO Google CalendarService tempService = new CalendarService("My Application"); tempService.setUserCredentials(args[0], args[1]); // LOGIN AND PASSWORDS AS ARGUMENTS // PUT EVENT IN QUEUE tempService.insert(feedUrl, myEntry); } } // CREATE A NEW Calendar service, CONNECT TO Google CalendarService basicService = new CalendarService("basic Application"); basicService.setUserCredentials(args[0], args[1]); // LOGIN AND PASSWORD AS ARGUMENTS // CREATE NEW EVENT CalendarEventEntry basicEntry = new CalendarEventEntry(); basicEntry.setTitle(new PlainTextConstruct(alertTxt)); // SET ITS TITLE if (mode == 0) { // STANDARD DAILY SITUATION newDate.setTime(newDate.getTime() + 1000 * 60 * timePeriod); // CURRENT TIME + timePeriod MINUTES startTime = DateTime.parseDateTime(formater.format(newDate)); // INSERTS THE LINE USING THE PATTERN "2009-06-30T20:55:00" newDate.setTime(newDate.getTime() + 1000 * 60 * 0); // AGAIN ADD THE CURRENT TIME // + timePeriod MINUTES endTime = DateTime.parseDateTime(formater.format(newDate)); } if (mode == 1) { // SCHEDULE EVENT FOR THE NEXT MORNING OR MONDAY MORNING Date curDate = new Date(); GregorianCalendar gDate = new GregorianCalendar(); gDate.setTime(curDate); // SET THE REQUIRED HOUR AND MINUTE gDate.set(GregorianCalendar.HOUR_OF_DAY, startHour); gDate.set(GregorianCalendar.MINUTE, startMin); gDate.set(GregorianCalendar.SECOND, 0); if (gDate.get(GregorianCalendar.DAY_OF_WEEK) < 6) { // MO - THU gDate.set(GregorianCalendar.DAY_OF_MONTH, gDate.get(GregorianCalendar.DAY_OF_MONTH) + 1); } else { // MO gDate.set(GregorianCalendar.DAY_OF_MONTH, gDate.get(GregorianCalendar.DAY_OF_MONTH) + 3); } // System.out.println("gDate "+ gDate.getTime()); startTime = DateTime.parseDateTime(formater.format(gDate.getTime())); gDate.set(GregorianCalendar.MINUTE, gDate.get(GregorianCalendar.MINUTE) + timePeriod); endTime = DateTime.parseDateTime(formater.format(gDate.getTime())); System.out.println(">> nextday event should be created"); } //System.out.println("creating ALERT event!"); System.out.println(">> creating ALERT event"); System.out.println(">> event starttime: " + startTime.toString()); System.out.println(">> event stoptime : " + endTime.toString()); eventTimes.setStartTime(startTime); eventTimes.setEndTime(endTime); basicEntry.addTime(eventTimes); reminderMinutes = 0; // REMIND 1 MINUTE BEFORE reminder.setMinutes(reminderMinutes); reminder.setMethod(methodType); basicEntry.getReminder().add(reminder); // PUT EVENT IN QUEUE basicService.insert(feedUrl, basicEntry); System.out.println(">>---------- Finish ----------<<"); } catch (AuthenticationException e) { e.printStackTrace(); } catch (MalformedURLException e) { e.printStackTrace(); } catch (ServiceException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } } }
The .bat file code
Since creation of .bat files in MQL4 is currently impossible, I use 2 .bat files that have different Modes.
@rem +-----------------------------------------------------------------------------------------+ @rem | The application sends SMS | @rem | ------------------------------------------------------ | @rem | Rules: | @rem | | @rem | USER - sender's account | @rem | PASS - sender's password | @rem | ALER - Alert, text of the alert message | @rem | (only use Latin characters) | @rem | REAN - Reanimated, text of the message notifying about the reestablished connection | @rem | (only use Latin characters) | @rem | STOP - STOP, title of the event that forbids execution | @rem | of the program actions | @rem | (only use Latin characters) | @rem | PERI - Period, period-timer for sending Alert message | @rem | MODE - Mode | @rem | (0 - standard work, | @rem | 1 - scheduling the message to be sent tomorrow morning or | @rem | on Monday morning) | @rem | HOUR - Hour "of tomorrow morning or Monday morning" at | @rem | which the message is required to be sent. | @rem | MINU - Minutes, minutes "of tomorrow morning..." | @rem +-----------------------------------------------------------------------------------------+ @rem ECHO off set USER=forex.myaccount set PASS=mypassword set ALER=Terminal_Alert set REAN=Trading_Resolved set STOP=STOP set PERI=5 set MODE=0 set HOUR=8 set MINU=20 set ARGS= %USER% %PASS% %ALER% %REAN% %STOP% %PERI% %MODE% %HOUR% %MINU% java -jar "d:\Documents\forex\Deltabank Trader 4\TerminalWatch\terminalWatcher.jar" %ARGS% @rem ECHO %ARGS%
Code of the Expert Advisor
The functions whose code is provided below will need to be called in the 'scheduled actions' block according to the rules of your Trading System.
At the end of the trading session, I call the function that schedules SMS for the next morning.
Links are tagged with .lnk so that we can set the 'Minimize to icon' call mode for the console window not to get startled when it pops up.
//+------------------------------------------------------------------+ //| Sends a status SMS | //+------------------------------------------------------------------+ int sendSMS() { string destination = StringConcatenate(TerminalPath(),"\TerminalWatch\launch.lnk"); ShellExecuteA(WindowHandle(Symbol(),0),"open", destination, NULL, NULL,1); } //+------------------------------------------------------------------+ //+------------------------------------------------------------------+ //| Sends a status SMS the next morning | //+------------------------------------------------------------------+ int sendSMSnextDate() { string destination = StringConcatenate(TerminalPath(),"\TerminalWatch\launch_next_date.lnk"); ShellExecuteA(WindowHandle(Symbol(),0),"open", destination, NULL, NULL,1); } //+------------------------------------------------------------------+
Shortcomings
In practice, it is very hard to synchronize the operation with the clock of the Terminal, Quotes Server and Google Server. Therefore, in the code of the Expert Advisor I use the following technique: SMS is set every 5 minutes with the 8 minute timer. This works well in 95% of cases but I still have to deal with the remaining 5% - the only fly in the ointment.
Conclusion
If you find it interesting and feel like signing up for a Google account, you can easily check what my unsophisticated system is capable of.
Once again, I should say that many details of the Java implementation provided in the article are not completely clear to me. I got some great help from my fellow developers (Thanks a lot Dima!) so I will not be able to answer all your questions to the utmost accuracy.
Thank you for your time!
Changes inspired by the comments received on the article
The current version contains a useful amendment by komposter regarding the operation logic:
- the reanimation message is not deleted if it has not been sent;
Please find TerminalWatch_03.rar attached to the article.
Translated from Russian by MetaQuotes Ltd.
Original article: https://www.mql5.com/ru/articles/1376
- Free trading apps
- Over 8,000 signals for copying
- Economic news for exploring financial markets
You agree to website policy and terms of use