Enviar órdenes a MT4 desde Java vía IP - página 2

 
Mariop:

Es cierto. Gracias. No lo recordaba. En realidad no resuelve el problema por completo ya que necesito que las órdenes se ejecuten casi inmediatamente (y creo que con este método sólo puedo comprobarlo cada segundo, pero al menos no cada tick), pero efectivamente puedo construir fácilmente una solución temporal usándolo.

Aunque el OnTimer tiene la tasa más rápida alineada con el reloj del sistema, es decir, cada 15 ms, yo preferiría hacer un bucle en intervalos de 1ms (usando el Sleep(1) en el bucle).

 
Ovo:

Aunque el OnTimer tiene la tasa más rápida alineada con el reloj del sistema, es decir, cada 15 ms, yo preferiría hacer un bucle en intervalos de 1ms (usando el Sleep(1) en el bucle).

¿No sería eso un problema? Quiero decir, ¿no hay procesos internos en MT4 que puedan necesitar que se ejecute OnTick() para evitar que algún tipo de buffers internos acaben desbordados o sobredimensionados después de semanas de estar ejecutando (el EA) sin parar y sólo en bucle?

Lo pregunto desde la ignorancia, no tengo ni idea de si puede ser un problema, solo asumo que MT4 no fue concebido para ello.

¿Entonces crees(/has probado) que no será un problema? (Si es así, son grandes noticias).


BTW: ¿Cómo consigues que OnTimer() se ejecute por debajo del intervalo de 1 segundo? :?

 

Yo solía ejecutar EAs en un bucle iniciado en init() hace algunos años, por una razón similar - la lectura de datos externos con un retraso mínimo. Solía correr semanas sin ningún efecto secundario. El Sleep(1) es satisfactorio para que el impacto de la potencia de cálculo sea muy ligero.

La configuración del temporizador acepta milis.

 
Ovo:

Yo solía ejecutar EAs en un bucle iniciado en init() hace algunos años, por una razón similar - la lectura de datos externos con un retraso mínimo. Solía correr semanas sin ningún efecto secundario. El Sleep(1) es satisfactorio para que el impacto de la potencia de cálculo sea muy ligero.

La configuración del temporizador acepta milis.

Una noticia extraordinaria. Si ya lo has comprobado, entonces puedo hacerlo sin miedo a que se cuelgue. Muchas gracias por compartir tu experiencia en este tema. Problema resuelto =D
 
Mariop:

BTW: ¿Cómo se consigue que OnTimer() se ejecute por debajo del intervalo de 1 segundo? :?


EventSetMillisecondTimer

La función indica al terminal cliente que los eventos del temporizador deben ser generados en intervalos menores a un segundo para este Asesor Experto o indicador.

boolEventSetMillisecondTimer(
intmilliseconds// número de milisegundos
);

Parámetros

milisegundos

[in] Número de milisegundos que definen la frecuencia de los eventos del temporizador.

Valor devuelto

En caso de ejecución exitosa, devuelve true, de lo contrario - false. Para recibir un código de error, se debe llamar a la función GetLastError().

Nota

Esta función está diseñada para los casos en que serequiere un temporizador de alta resolución. Enotras palabras , loseventos del temporizador deben recibirse con más frecuencia que una vez por segundo. Si un temporizador convencional con el período de varios segundos es suficiente para usted, utilice EventSetTimer().

Usualmente, esta función debe ser llamada desde la función OnInit() o en el constructor de la clase. Para manejar los eventos provenientes del temporizador, un Asesor Experto o un indicador debe tener la función OnTimer() .

Cada Asesor Experto y cada indicador trabajan con su propio temporizador recibiendo eventos únicamente de este temporizador. Durante el cierre de la aplicación mql4, el temporizador se destruye forzosamente en caso de que haya sido creado pero no haya sido desactivado por la función EventKillTimer().

Sólo se puede lanzar un temporizador por cada programa. Cada aplicación mql4 y el gráfico tienen su propia cola de eventos donde se colocan todos los eventos recién llegados. Si la cola ya contiene el evento Timer o este evento está en la etapa de procesamiento, entonces el nuevo evento Timer no se añade a la cola de la aplicación mql4.


 
GumRai:

EventSetMillisecondTimer

La función indica al terminal del cliente que se generen eventos de temporizador a intervalos inferiores a un segundo para este Asesor Experto o indicador.

boolEventSetMillisecondTimer(
intmilliseconds// número de milisegundos
);

Parámetros

milisegundos

[in] Número de milisegundos que definen la frecuencia de los eventos del temporizador.

Valor devuelto

En caso de ejecución exitosa, devuelve true, en caso contrario - false. Para recibir un código de error, se debe llamar a la función GetLastError().

Nota

Esta función está diseñada para los casos en que se requiere un temporizador de alta resolución. Enotras palabras , loseventos del temporizador deben recibirse con más frecuencia que una vez por segundo. Si un temporizador convencional con un periodo de varios segundos es suficiente para usted, utilice EventSetTimer().

Normalmente, esta función debe ser llamada desde la función OnInit() o en el constructor de la clase. Para manejar los eventos provenientes del temporizador, un Asesor Experto o un indicador debe tener la función OnTimer() .

Cada Asesor Experto y cada indicador trabajan con su propio temporizador recibiendo eventos únicamente de este temporizador.Durante el cierre de la aplicación mql4, el temporizador se destruye forzosamente en caso de que haya sido creado pero no haya sido desactivado por la función EventKillTimer().

Sólo se puede lanzar un temporizador por cada programa. Cada aplicación mql4 y cada gráfico tienen su propia cola de eventos donde se colocan todos los eventos recién llegados. Si la cola ya contiene el evento Timer o este evento está en la etapa de procesamiento, entonces el nuevo evento Timer no se añade a la cola de mql4application.




Gracias por la explicación detallada; es culpa mía; lo leí demasiado rápido en Internet. (Los enlaces han intercambiado el dominio docs.mql4.com con forum.mql4.com. Por si acaso alguien más revisa este hilo...)


EDIT: Voy a aprovechar que hay un moderador por aquí: ¿Sabéis si existe alguna diferencia entre ejecutar OnTimer() cada N milisegundos y hacer el mismo bucle cada N milisegundos como se ha descrito en posts anteriores?

 

No veo por qué todo el alboroto con la temporización en "ms". A menos que se trate de algún tipo de "escalador de noticias de alta frecuencia" en una conexión de broker de muy baja latencia, los intervalos de un segundo en OnTimer() deberían ser más que suficientes como tiempo de respuesta.

Si se añaden todos los retrasos y la latencia de la ejecución de la aplicación JAVA, la comunicación IPC (LAN y WAN), la ejecución de la gestión de órdenes y la latencia del servidor Broker, no hay ninguna ventaja real en el uso de la temporización en "ms" para las estrategias normales.

Sin embargo, si se trata de algún tipo de "escalador de noticias de alta frecuencia", que se utilizará en un VPS con una conexión de baja latencia al corredor, entonces usted no debe estar jugando con JAVA y IPC en absoluto, y debe ser la codificación de la estrategia directamente en MQL muy compacto para obtener la menor latencia posible.

EDITAR:

El hecho de que usted está utilizando una aplicación externa con una alimentación de datos por separado (que está garantizado para desviarse en el precio, el volumen y el tiempo con respecto a la alimentación de MT4), muestra que no hay manera de que esto requeriría tan apretado "ms" sincronización. Un intervalo de sondeo de "un segundo" es más que adecuado.

Recuerde también, que el evento OnTick() también puede comprobar si hay mensajes entrantes, por lo que "un segundo" es sólo un caso limitante cuando no hay ticks entrantes. Y si no hay ticks entrantes, entonces tampoco hay suficiente actividad (volatilidad y liquidez) para justificar una respuesta tan rápida de "ms" de todos modos.

No complique las cosas. Recuerde el principio K.I.S.S.

 
Mariop:

EDIT: Voy a aprovechar que hay un moderador por aquí: ¿Sabéis si existe alguna diferencia entre ejecutar OnTimer() cada N milisegundos y hacer el mismo bucle cada N milisegundos como se describe en los posts anteriores?

El hecho de que sea moderador no significa que tenga más conocimientos que los demás. De hecho, los demás participantes en este hilo tienen obviamente más conocimientos que yo sobre el tema. Yo no sé nada de Java.

No entiendo el sentido de usar Sleep() en un evento de temporizador ya que pone al EA en espera y esto podría significar perder muchos eventos OnTick(). Por supuesto, dependiendo del EA, los eventos OnTick pueden no ser importantes.

 
Ovo:

Yo solía ejecutar EAs en un bucle iniciado en init() hace algunos años, por una razón similar - la lectura de datos externos con un retraso mínimo. Solía correr semanas sin ningún efecto secundario. El Sleep(1) es satisfactorio para que el impacto de la potencia de cálculo sea muy ligero.

Para un poco de diversión... el siguiente script acepta múltiples conexiones TCP/IP concurrentes, y escribe los mensajes entrantes delimitados por CR en el registro de Expertos. Por ejemplo, una vez que el script se está ejecutando puedes conectarte vía Telnet (al puerto 51234 por defecto), y cada línea de texto que escribas se imprimirá.

#property strict 
#property  show_inputs

// ---------------------------------------------------------------------
// User-configurable parameters
// ---------------------------------------------------------------------

input int PortNumber = 51234; // TCP/IP port number

// ---------------------------------------------------------------------
// Constants
// ---------------------------------------------------------------------

// Size of temporary buffer used to read from client sockets
#define  SOCKET_READ_BUFFER_SIZE 10000

// ---------------------------------------------------------------------
// Forward definitions of classes
// ---------------------------------------------------------------------

// Wrapper around a connected client socket
class Connection;

// ---------------------------------------------------------------------
// Winsock structure definitions and DLL imports
// ---------------------------------------------------------------------

struct sockaddr_in {
   short af_family;
   short port;
   int addr;
   int dummy1;
   int dummy2;
};

struct timeval {
   int secs;
   int usecs;
};

struct fd_set {
   int count;
   int single_socket;
   int dummy[63];
};

#import "Ws2_32.dll"
   int socket(int, int, int);   
   int bind(int, sockaddr_in&, int);
   int htons(int);
   int listen(int, int);
   int accept(int, int, int);
   int closesocket(int);
   int select(int, fd_set&, int, int, timeval&);
   int recv(int, uchar&[], int, int);
   int WSAGetLastError();
#import   


// ---------------------------------------------------------------------
// Global variables
// ---------------------------------------------------------------------

// Handle of main listening server socket
int ServerSocket;

// List of currently connected clients
Connection * Clients[];



// ---------------------------------------------------------------------
// Entry point 
// ---------------------------------------------------------------------

void OnStart()
{
   if (!TerminalInfoInteger(TERMINAL_DLLS_ALLOWED)) {Print("Requires \'Allow DLL imports\'");return;}

   // (Don't need to call WSAStartup because MT4 must have done this)

   // Create the main server socket
   ServerSocket = socket(2 /* AF_INET */, 1 /* SOCK_STREAM */, 6 /* IPPROTO_TCP */);
   if (ServerSocket == -1) {Print("ERROR " , WSAGetLastError() , " in socket creation");return;}
   
   // Bind the socket to the specified port number. In this example,
   // we only accept connections from localhost
   sockaddr_in service;
   service.af_family = 2 /* AF_INET */;
   service.addr = 0x100007F; // equivalent to inet_addr("127.0.0.1")
   service.port = (short)htons(PortNumber);
   if (bind(ServerSocket, service, 16 /* sizeof(service) */) == -1) {Print("ERROR " , WSAGetLastError() , " in socket bind");return;}

   // Put the socket into listening mode
   if (listen(ServerSocket, 0) == -1) {Print("ERROR " , WSAGetLastError() , " in socket listen");return;}
 

   // Listening loop, which continues until Remove Script is used
   timeval waitfor;
   waitfor.secs = 0;
   waitfor.usecs = 0;
   
   while (!IsStopped()) {
      // .........................................................
      // Do we have a new pending connection on the server socket?
      fd_set PollServerSocket;
      PollServerSocket.count = 1;
      PollServerSocket.single_socket = ServerSocket;

      int selres = select(0, PollServerSocket, 0, 0, waitfor);
      if (selres > 0) {
      
         Print("New incoming connection...");
         int NewClientSocket = accept(ServerSocket, 0, 0);
         if (NewClientSocket == -1) {
            Print("ERROR " , WSAGetLastError() , " in socket accept");

         } else {
            Print("...accepted");

            int ctarr = ArraySize(Clients);
            ArrayResize(Clients, ctarr + 1);
            Clients[ctarr] = new Connection(NewClientSocket);               
            Print("Got connection to client ", Clients[ctarr].GetID());
         }
      }
  
      // .........................................................
      // Process any incoming data from client connections
      // (including any which have just been accepted, above)
      int ctarr = ArraySize(Clients);
      for (int i = ctarr - 1; i >= 0; i--) {
         // Return value from ReadAnyPendingData() is true
         // if the socket still seems to be alive; false if 
         // the connection seems to have been closed, and should be discarded
         if (Clients[i].ReadAnyPendingData()) {
            // Socket still seems to be alive
            
         } else {
            // Socket appears to be dead. Delete, and remove from list
            Print("Lost connection to client ", Clients[i].GetID());
            
            delete Clients[i];
            for (int j = i + 1; j < ctarr; j++) {
               Clients[j - 1] = Clients[j];
            }
            ctarr--;
            ArrayResize(Clients, ctarr);           
         }
      }
      
      Sleep(10); // Sleep(1) appears to be a little too aggressive in this context
   }
}

// ---------------------------------------------------------------------
// Termination (could do this at the end of OnStart() instead.
// It's just a little clearer to do it here
// ---------------------------------------------------------------------

void OnDeinit(const int reason)
{
   closesocket(ServerSocket);
   
   for (int i = 0; i < ArraySize(Clients); i++) {
      delete Clients[i];
   }
}


// ---------------------------------------------------------------------
// Simple wrapper around each connected client socket
// ---------------------------------------------------------------------

class Connection {
private:
   // Client socket handle
   int mSocket;

   // Temporary buffer used to handle incoming data
   uchar mTempBuffer[SOCKET_READ_BUFFER_SIZE];
   
   // Stored-up data, waiting for a \r character 
   string mPendingData;
   
public:
   Connection(int ClientSocket) {mSocket = ClientSocket; mPendingData = "";}
   ~Connection() {closesocket(mSocket);}
   string GetID() {return IntegerToString(mSocket);}
   
   bool ReadAnyPendingData();
};

// Called repeatedly on a timer from OnStart(), to check whether any
// data is available on this client connection. Returns true if the 
// client still seems to be connected (*not* if there's new data); 
// returns false if the connection seems to be dead. 
bool Connection::ReadAnyPendingData()
{
   // Check the client socket for data-readability
   timeval waitfor;
   waitfor.secs = 0;
   waitfor.usecs = 0;

   fd_set PollClientSocket;
   PollClientSocket.count = 1;
   PollClientSocket.single_socket = mSocket;

   int selres = select(0, PollClientSocket, 0, 0, waitfor);
   if (selres > 0) {
      
      // Winsock says that there is data waiting to be read on this socket
      int res = recv(mSocket, mTempBuffer, SOCKET_READ_BUFFER_SIZE, 0);
      if (res > 0) {
         // Convert the buffer to a string, and add it to any pending
         // data which we already have on this connection
         string strIncoming = CharArrayToString(mTempBuffer, 0, res);
         mPendingData += strIncoming;
         
         // Do we have a complete message (or more than one) ending in \r?
         int idxTerm = StringFind(mPendingData, "\r");
         while (idxTerm >= 0) {
            if (idxTerm > 0) {
               string strMsg = StringSubstr(mPendingData, 0, idxTerm);         
               
               // Print the \r-terminated message in the log
               Print("#" , GetID() , ": " , strMsg);
            }               
         
            // Keep looping until we have extracted all the \r delimited 
            // messages, and leave any residue in the pending data 
            mPendingData = StringSubstr(mPendingData, idxTerm + 1);
            idxTerm = StringFind(mPendingData, "\r");
         }
         
         return true;
         
      } else {
         // recv() failed. Assume socket is dead
         return false;
      }
   
   } else if (selres == -1) {
      // Assume socket is dead
      return false;
      
   } else {
      // No pending data
      return true;
   }
}
 
jjc:

Para divertirse un poco... el siguiente script acepta múltiples conexiones TCP/IP concurrentes, y escribe los mensajes entrantes delimitados por CR en el registro de Expertos.

... diciendo lo obvio, si usted quisiera hacer lo anterior en un EA en lugar de un script, entonces simplemente lo cambiaría como sigue:

  • Mover todo el código de OnStart(), hasta e incluyendo la llamada a listen(), en el OnInit() del EA
  • Establecer un temporizador de alta frecuencia al final de OnInit(), por ejemplo EventSetMillisecondTimer(10)
  • Mover el código restante de OnStart() a OnTimer(), eliminando el bucle exterior "while (!IsStopped()) {}".
Voy a añadir una nota que - a menos que esto se ha solucionado en muy reciente MT4 construye - EventSetTimer() y EventSetMillisecondTimer() puede fallar si se utiliza en OnInit() cuando la plataforma MT4 se está iniciando con un EA ya adjunta a un gráfico. En mi experiencia es necesario comprobar los valores de retorno; reintentar; y potencialmente volver a configurar el temporizador en el primer OnTick() si los intentos de crearlo en OnInit() han sido infructuosos.
Razón de la queja: