
EA 状态短信通知
简介
我的工作经常影响对终端的持续监视。 这可能是 20-30 分钟,也可能是一整天。 因此我决定开发一个系统,能够在出现任何紧急情况时发送短信通知我,无论是出现停电还是诸如我的电脑在周末后没有退出睡眠模式等问题。 我相信本文提供的系统对于很多交易者来说会非常有用,或者对于那些觉得它不够好但想要创建他们自己的‘杰作’的人,可以将其作为基础。
本文中,我想展示这一特定任务的解决方案的规划。 需要注意的是,我几乎不具备 Java 开发的专业技能,所以不得不请求作为 Java 开发工程师的朋友帮助我调试并修正错误代码。
计划
- Google Calendar 功能
- 关于安装 Google Data APIs 的简述
- 系统流程图
- Java 应用程序代码
- .bat 文件代码
- Expert Advisor 代码
- 缺点
- 总结
Google Calendar 功能
在开发短信通知系统时,我决定选用 Google Calendar,因为它允许在其免费的 Calendar 中创建事件,你可以使用短信作为通知任何这种事件的方式。 为了实现这个特定的目的,我使用 Google 创建了一个专门的单独配置文件。
简而言之,工作原理描述如下:如果当前的信息未在 9.59 am 之前删除(请查看上面的截屏),即如果终端没有连接 Google 的服务器并将当前信息替换为不同的信息,就会有一条短信发送到你的手机进行通知。
关于安装 Google Data APIs 的简述
为了能够在开发你的软件解决方案时使用 Google 服务,Google 提供了 Google Data APIs 文档。
Java 开发工程师在使用 Google Calendar 时所需的所有文档都可以使用下面的链接找到:Google Calendar APIs and Tools。
要开始编程,你必须安装 Google Data Java Client Library。 在同一页上,你可以找到一个跟 Eclipse 的库相关文档的链接。 这就是我所使用并且想推荐给你的: Using Eclipse with Google Data APIs。
系统流程图
我想我应该先评论流程图:
- StopTXT 事件 要求能够在必要时手动和远程停止短信发送。
- StopTXT 事件 要求在相应的警告短信发送给你 之后, 通知你终端出于某种原因已经连接上了。
Java 应用程序代码
所使用的全部源代码和分配均附在文章末尾。
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(); } } }
.bat 文件代码
由于当前无法在 MQL4 中创建 .bat 文件,我使用了 2 个具有不同 Mode 的 .bat 文件。
@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%
Expert Advisor 代码
下面提供了一些函数的代码。根据你的交易系统的规则,这些函数需要在‘预定操作’程序块中调用。
在交易时段结束时,我调用了预定第二天早上的短信的函数。
链接具有 .lnk 标记,我们将控制台窗口设置为‘最小化到图标’调用模式,以免在它弹出时被吓到。
//+------------------------------------------------------------------+ //| 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); } //+------------------------------------------------------------------+
缺点
在实践中,很难将 Terminal、Quotes Server 和 Google Server 的时钟与运行同步。 因此,在 Expert Advisor 的代码中我使用了以下技巧: 使用 8 分钟的定时器将短信设置为每 5 分钟进行通知。 这在 95% 的情形下工作良好,但我不得不处理剩下的 5%——这是唯一的美中不足。
总结
如果你觉得很有趣,想注册一个 Google 帐户,那你就可以检验我这个并不复杂的系统的能力了。
再说一次,对于本文提供的很多 Java 实现的细节,我并非完全清楚。 我从作为开发工程师的朋友那里得到很大的帮助(非常感谢 Dima!),所以无法非常精准的回答你的所有问题。
感谢你们的阅读!
受本文的评论启发进行的改动
当前的版本包含了一个由 komposter 在运行逻辑上进行的有用改动:
- 如果复原的信息尚未发送,则不予删除。
请查看本文的附件 TerminalWatch_03.rar 。
本文由MetaQuotes Ltd译自俄文
原文地址: https://www.mql5.com/ru/articles/1376


