English Русский 中文 Deutsch 日本語 Português
Notificaciones por SMS sobre el estado del Asesor Experto

Notificaciones por SMS sobre el estado del Asesor Experto

MetaTrader 4Ejemplos | 11 mayo 2016, 12:50
999 0
Дмитрий
Дмитрий

Introducción

Mi trabajo interfiere a menudo con mi capacidad de hacer un seguimiento continuo del terminal. A veces son sólo 20 o 30 minutos y a veces puede ser un día entero. Así que decidí desarrollar un sistema que me permita recibir notificaciones por SMS acerca de cualquier situación crítica, que sea un apagón o que mi ordenador no arranca después de haber estado en el modo de suspensión durante el fin de semana. Estoy seguro de que este sistema tal y como se presenta en este artículo será de gran ayuda para muchos traders o servirá como base para los que consideran que no es suficiente tal y como está y quieren crear su propia "obra maestra".

En este artículo voy a presentar el desarrollo de una solución operativa para este propósito. Quiero mencionar que no tengo conocimientos en la programación Java, así que he pedido ayuda a un colega que es desarrollador Java para depurar y corregir los fallos de mi código.


Índice

  1. Características de Google Calendar
  2. Algunos comentarios acerca de la instalación de Google Data APIs
  3. El diagrama de bloques del sistema
  4. El código Java de la aplicación
  5. El código del archivo .bat
  6. El código del Asesor Experto
  7. Deficiencias
  8. Conclusión


Características de Google Calendar

Para el desarrollo del sistema de notificaciones por SMS he optado por Google Calendar ya que permite crear eventos en su calendario gratuito además de permitir usar los SMS para notificar cualquiera de estos eventos. He creado una cuenta en Google especialmente para este propósito.

En pocas palabras, el principio de funcionamiento es el siguiente: si no se elimina el mensaje actual sobre las 9:59 a.m. (ver la captura de pantalla anterior), es decir, si no se conecta el terminal con el servidor de Google para sustituir este evento por el siguiente, se enviará un SMS a su teléfono con la notificación correspondiente.


Algunos comentarios acerca de la instalación de Google Data APIs

Google proporciona la documentación Google Data APIs para que se puedan utilizar los servicios de Google en el desarrollo de los programas.
Toda la documentación que pueda necesitar un desarrollador Java para trabajar con Google Calendar está disponible en este enlace Google Calendar APIs and Tools.

Para comenzar con la programación tenemos que instalar Google Data Java Client Library. Podemos encontrar un enlace a la documentación relacionada con la librería para Eclipse en la misma página. Esto es lo que he utilizado y lo que recomiendo: Using Eclipse with Google Data APIs.


El diagrama de bloques del sistema

Creo que lo primero es comentar el diagrama de bloques:

  • el evento StopTXT sirve para poder parar de forma manual y remota el envío de los SMS, por si acaso;
  • el evento ReanimationTXT sirve para informar que por alguna razón se conectó el terminal después de habernos enviado el SMS correspondiente con un aviso;


El código Java de la aplicación

Todos los códigos fuente y la distribución que he utilizado están adjuntos al final del artículo.

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(); } } }


El código del archivo .bat

Puesto que la creación de los archivos .bat en MQL4 es imposible en la actualidad, he utilizado 2 archivos .bat con distintos modos.

@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%


El código del Asesor Experto

Se proporciona a continuación el código de las funciones a las que hay que llamar en el bloque de las "acciones planificadas" de acuerdo con los criterios de su sistema de trading.
Al finalizar la sesión de trading llamo a la función que programa los SMS para la siguiente mañana.

Los accesos están marcados con la etiqueta .lnk para que podamos establecer el modo de llamada "minimizado a un icono" en la ventana de la consola para evitar sustos cuando aparezca.

//+------------------------------------------------------------------+
//| 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);
}
//+------------------------------------------------------------------+


Deficiencias

En la práctica, es muy difícil sincronizar la operación con el reloj del terminal, el servidor de cotizaciones y el servidor de Google. Así que he utilizado la siguiente técnica en el código del Asesor Experto: se ajusta el SMS cada 5 minutos con el temporizador de 8 minutos. Esto funciona muy bien en el 95 % de los casos pero todavía tengo que lidiar con el 5 % restante; es la única pega.


Conclusión

Si está interesado y no le da pereza crear un cuenta en Google, puede comprobar fácilmente de que es capaz mi sencillo sistema.
Un vez más, debo recordar que muchos detalles de la implementación Java en este artículo no son del todo claros para mí. He recibido mucha ayuda por parte de algunos colegas desarrolladores (¡muchas gracias a Dima!), así que no voy a poder responder a todas sus preguntas al detalle.
¡Gracias por su tiempo!


Cambios motivados por los comentarios recibidos acerca del artículo

La versión actual contiene una modificación muy útil gracias a komposter en relación a la lógica de funcionamiento:

  • no se elimina el mensaje de ReanimationTXT si no se ha enviado;

El archivo TerminalWatch_03.rar está adjunto al artículo.

Traducción del ruso hecha por MetaQuotes Ltd.
Artículo original: https://www.mql5.com/ru/articles/1376

Archivos adjuntos |
TerminalWatch_02.zip (1622.08 KB)
TerminalWatch_03.zip (1622.04 KB)
Lite_EXPERT2.mqh: Un conjunto operativo para los desarrolladores de Asesores Expertos Lite_EXPERT2.mqh: Un conjunto operativo para los desarrolladores de Asesores Expertos
Este artículo es una continuación de la serie de artículos "Asesores Expertos basados en sistemas populares de trading, y un poco de alquimia en la optimización de robots". Permite familiarizar los lectores con una librería de funciones más universales del archivo Lite_EXPERT2.mqh.
Asesor Experto para el trading en un canal Asesor Experto para el trading en un canal
El Asesor Experto traza las líneas del canal. Las líneas superiores e inferiores del canal actúan como niveles de soporte y resistencia. El Asesor Experto etiqueta los puntos de referencia, informa mediante un sonido cada vez que el precio alcanza o cruza las líneas del canal y dibuja las marcas pertinentes. Después de la formación de los fractales, aparecen las flechas correspondientes en las últimas barras. Las rupturas de las líneas pueden sugerir una posible tendencia creciente. El código del Asesor Experto lleva comentarios detallados.
Optimización visual de la rentabilidad de indicadores y señales Optimización visual de la rentabilidad de indicadores y señales
Este artículo es una continuación y un desarrollo de mi artículo anterior "Pruebas visuales y rentabilidad de los indicadores y señales". Después de haber añadido un poco de interactividad al proceso de cambio de los parámetros y cambiado los objetivos del estudio, he podido conseguir una nueva herramienta que no sólo muestra los posibles resultados del trading en base a las señales que se usan, sino también nos permite obtener inmediatamente la distribución de las transacciones, el gráfico del balance y el resultado final del trading moviendo los botones deslizantes virtuales que controlan los valores de los parámetros de la señal en el gráfico principal.
Métodos sencillos para predecir las direcciones de las velas japonesas Métodos sencillos para predecir las direcciones de las velas japonesas
Basta con conocer la dirección del movimiento del precio para conseguir resultados positivos en el trading. Se pueden obtener algunas informaciones acerca de la posible dirección del precio a partir de las velas japonesas. Este artículo aborda algunos métodos sencillos para predecir la dirección de las velas japonesas.