EA Status SMS Notifications

unknown | 24 September, 2013

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

  1. Google Calendar features
  2. A few words about installation of Google Data APIs
  3. System flowchart
  4. Java application code
  5. The .bat file code
  6. Code of the Expert Advisor
  7. Shortcomings
  8. 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:


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:

Please find TerminalWatch_03.rar attached to the article.