Envoyer des ordres à MT4 depuis Java via IP - page 2

 
Mariop:

C'est vrai. Merci. Je ne m'en souvenais pas. En fait, cela ne résout pas complètement le problème puisque j'ai besoin que les ordres soient exécutés presque immédiatement (et je pense qu'avec cette méthode je ne peux le vérifier que toutes les secondes, mais au moins pas à chaque tick), mais en effet je peux facilement construire une solution temporaire en l'utilisant.

Bien que le OnTimer ait la fréquence la plus rapide alignée sur l'horloge système, c'est-à-dire toutes les 15 ms, je préférerais boucler par intervalles de 1 ms (en utilisant le Sleep(1) dans la boucle).

 
Ovo:

Bien que le OnTimer ait la fréquence la plus rapide alignée sur l'horloge système, c'est-à-dire toutes les 15 ms, je préférerais boucler par intervalles de 1 ms (en utilisant le Sleep(1) dans la boucle).

Cela ne poserait-il pas un problème ? Je veux dire qu'il n'y a pas de processus internes dans MT4 qui peuvent avoir besoin que OnTick() soit exécuté pour éviter que des tampons internes ne finissent par déborder ou être surdimensionnés après des semaines de fonctionnement (de l'EA) sans s'arrêter et juste en boucle ?

Je demande cela par ignorance, je n'ai aucune idée si cela peut être un problème, je suppose simplement que MT4 n'a pas été conçu pour le faire.

Donc vous pensez(/avez essayé) que ce ne sera pas un problème ? (Si oui, ce sont d'excellentes nouvelles).


BTW : Comment faites-vous pour que OnTimer() soit exécuté en dessous de l'intervalle de 1 sec : ?

 

Il y a quelques années, j'avais l'habitude d'exécuter des EA dans une boucle démarrée dans init(), pour une raison similaire - lire des données externes avec un délai minimum. Cela durait des semaines sans aucun effet secondaire. Le Sleep(1) est satisfaisant pour rendre l'impact de la puissance de calcul très léger.

Les paramètres du timer acceptent millis.

 
Ovo:

Il y a quelques années, j'avais l'habitude d'exécuter des EA dans une boucle démarrée dans init(), pour une raison similaire - lire des données externes avec un délai minimum. Cela durait des semaines sans aucun effet secondaire. Le Sleep(1) est satisfaisant pour rendre l'impact de la puissance de calcul très léger.

Les paramètres du timer acceptent millis.

Des nouvelles extraordinaires. Si vous l'avez déjà vérifié, alors je peux le faire sans avoir peur qu'il se plante. Merci beaucoup d'avoir partagé votre expérience sur cette question. Problème résolu =D
 
Mariop:

BTW : Comment faire pour que OnTimer() soit exécuté en dessous d'un intervalle de 1 sec : ?


EventSetMillisecondTimer

Cette fonction indique au terminal client que des événements de minuterie doivent être générés à des intervalles inférieurs à une seconde pour ce conseiller expert ou cet indicateur.

boolEventSetMillisecondTimer(
intmillisecondes// nombre de millisecondes
) ;

Paramètres

millisecondes

[in] Nombre de millisecondes définissant la fréquence des événements de la minuterie.

Valeur retournée

En cas d'exécution réussie, renvoie true, sinon - false. Pour recevoir un code d'erreur, la fonction GetLastError() doit être appelée.

Note

Cette fonction est conçue pour les cas où uneminuterie haute résolution est nécessaire. En d'autres termes, les événements de la minuterie doivent être reçus plus fréquemment qu'une fois par seconde. Si une minuterie conventionnelle avec une période de plusieurs secondes est suffisante pour vous, utilisez EventSetTimer().

En général, cette fonction doit être appelée à partir de la fonction OnInit() ou dans le constructeur de la classe. Pour gérer les événements provenant de la minuterie, un Expert Advisor ou un indicateur doit avoir la fonction OnTimer() .

Chaque Expert Advisor et chaque indicateur fonctionne avec sa propre minuterie et reçoit uniquement les événements de cette minuterie. Lors de l'arrêt de l'application mql4, le timer est détruit de force s'il a été créé mais n'a pas été désactivé par la fonction EventKillTimer().

Un seul timer peut être lancé pour chaque programme. Chaque application mql4 et chaque graphique ont leur propre file d'attente d'événements où sont placés tous les événements nouvellement arrivés. Si la file d'attente contient déjà l'événement Timer ou si cet événement est en cours de traitement, alors le nouvel événement Timer n'est pas ajouté à la file d'attente de l'application mql4.


 
GumRai:

EventSetMillisecondTimer

Cette fonction indique au terminal du client que des événements de minuterie doivent être générés à des intervalles inférieurs à une seconde pour ce conseiller expert ou cet indicateur.

boolEventSetMillisecondTimer(
intmilliseconds// nombre de millisecondes
) ;

Paramètres

millisecondes

[in] Nombre de millisecondes définissant la fréquence des événements de la minuterie.

Valeur retournée

En cas d'exécution réussie, renvoie true, sinon - false. Pour recevoir un code d'erreur, la fonction GetLastError() doit être appelée.

Note

Cette fonction est conçue pour les cas où une minuterie haute résolution est nécessaire. En d'autres termes, les événements de la minuterie doivent être reçus plus fréquemment qu'une fois par seconde. Si une minuterie conventionnelle avec une période de plusieurs secondes est suffisante pour vous, utilisez EventSetTimer().

En général, cette fonction doit être appelée à partir de la fonction OnInit() ou dans le constructeur de la classe. Pour gérer les événements provenant du minuteur, un Expert Advisor ou un indicateur doit avoir la fonction OnTimer() .

Lors de l'arrêt de l'application mql4, le timer est détruit de force s'il a été créé mais n'a pas été désactivé par la fonction EventKillTimer().

Un seul timer peut être lancé pour chaque programme. Chaque application mql4 et chaque graphique ont leur propre file d'attente d'événements où sont placés tous les événements nouvellement arrivés. Si la file d'attente contient déjà un événement Timer ou si cet événement est en cours de traitement, alors le nouvel événement Timer n'est pas ajouté à la file d'attente de l'application mql4.




Merci pour l'explication détaillée ; ma faute ; je l'ai lu trop rapidement en ligne. (Les liens ont interchangé le domaine docs.mql4.com avec forum.mql4.com. Juste au cas où quelqu'un d'autre vérifierait ce fil de discussion...)


EDIT : Je vais profiter de la présence d'un modérateur : Savez-vous s'il existe une différence entre exécuter OnTimer() tous les N millisecondes et faire la même boucle tous les N millisecondes comme décrit dans les posts précédents ?

 

Je ne vois pas pourquoi toute cette agitation autour du timing "ms". À moins qu'il ne s'agisse d'une sorte de "High Frequency News Scalper" sur une connexion de courtier à très faible latence, des intervalles d'une seconde sur OnTimer() devraient être plus que suffisants comme temps de réponse.

Si vous ajoutez tous les délais et la latence de l'exécution de l'application JAVA, de la communication IPC (LAN & WAN), de l'exécution de la gestion des ordres et de la latence du serveur du courtier, il n'y a pas de réel avantage à utiliser un timing en "ms" pour des stratégies normales.

Cependant, s'il s'agit d'une sorte de "High Frequency News Scalper", à utiliser sur un VPS avec une connexion à faible latence vers le courtier, alors vous ne devriez PAS vous embêter avec JAVA et IPC du tout, et devriez coder la stratégie directement en MQL très compact pour obtenir la latence la plus faible possible.

EDIT :

Le fait que vous utilisiez une application externe avec une alimentation en données séparée (qui est garantie de dévier en termes de prix, de volume et de timing par rapport à l'alimentation MT4), montre qu'il n'y a aucune chance que cela nécessite un timing aussi serré en "ms". Un intervalle d'interrogation d'une seconde est plus qu'adéquat.

Rappelez-vous également que l'événement OnTick() peut également vérifier les messages entrants, donc "une seconde" n'est un cas limite que lorsqu'il n'y a pas de ticks entrants. Et s'il n'y a pas de ticks entrants, alors il n'y a pas non plus assez d'activité (volatilité et liquidité) pour justifier une réponse aussi rapide en "ms" de toute façon.

Ne compliquez pas les choses ! Rappelez-vous le principe K.I.S.S..

 
Mariop:

EDIT : Je vais profiter de la présence d'un modérateur : Savez-vous s'il existe une différence entre exécuter OnTimer() tous les N millisecs et faire la même boucle tous les N millisecs comme décrit dans les posts précédents ?

Ce n'est pas parce que je suis un modérateur que je suis plus compétent que les autres. En fait, les autres posteurs de ce fil ont manifestement plus de connaissances que moi sur le sujet. Je ne connais rien à Java.

Je ne comprends pas l'intérêt d'utiliser Sleep() dans un événement de timer car cela met l'EA en attente et cela pourrait signifier manquer beaucoup d'événements OnTick(). Bien sûr, selon l'EA, les événements OnTick peuvent être sans importance.

 
Ovo:

Il y a quelques années, j'avais l'habitude d'exécuter des EA dans une boucle démarrée dans init(), pour une raison similaire - lire des données externes avec un délai minimum. Cela durait des semaines sans aucun effet secondaire. Le Sleep(1) est satisfaisant pour rendre l'impact de la puissance de calcul très léger.

Pour s'amuser un peu... le script suivant accepte plusieurs connexions TCP/IP simultanées, et écrit les messages entrants délimités par CR dans le journal des experts. Par exemple, une fois que le script est exécuté, vous pouvez vous connecter via Telnet (au port 51234 par défaut), et chaque ligne de texte que vous tapez sera imprimée.

#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:

Pour s'amuser un peu... le script suivant accepte plusieurs connexions TCP/IP simultanées, et écrit les messages entrants délimités par CR dans le journal des experts.

... pour aller droit au but, si vous vouliez faire ce qui précède dans un EA plutôt que dans un script, il vous suffirait de le modifier comme suit :

  • Déplacez tout le code de OnStart(), jusqu'à et y compris l'appel à listen(), dans OnInit() de l'EA.
  • Configurez un timer haute fréquence à la fin de OnInit(), par exemple EventSetMillisecondTimer(10).
  • Déplacez le code restant de OnStart() dans OnTimer(), en supprimant la boucle externe "while (!IsStopped()) {}".
J'ajouterai une note que - à moins que cela n'ait été corrigé dans des versions très récentes de MT4 - EventSetTimer() et EventSetMillisecondTimer() peuvent échouer s'ils sont utilisés dans OnInit() lorsque la plateforme MT4 démarre avec un EA déjà attaché à un graphique. D'après mon expérience, il est nécessaire de vérifier les valeurs de retour, de réessayer et éventuellement de revenir à la configuration du timer lors du premier OnTick() si les tentatives de création dans OnInit() ont échoué.
Raison: