Senden von Aufträgen an MT4 aus Java über IP - Seite 2

 
Mariop:

Das stimmt. Das wusste ich nicht. Daran hatte ich nicht gedacht. Eigentlich löst es das Problem nicht vollständig, da ich Aufträge fast sofort ausgeführt werden müssen (und ich denke, mit dieser Methode kann ich es nur jede Sekunde überprüfen, aber zumindest nicht jeden Tick), aber in der Tat kann ich leicht eine temporäre Lösung mit ihm bauen.

Obwohl der OnTimer die schnellste Rate hat, die sich an der Systemuhr orientiert, d.h. alle 15 ms, würde ich eine Schleife in 1ms-Intervallen bevorzugen (mit Sleep(1) in der Schleife).

 
Ovo:

Obwohl der OnTimer die schnellste Rate hat, die sich an der Systemuhr orientiert, d.h. alle 15 ms, würde ich eine Schleife in 1ms-Intervallen bevorzugen (unter Verwendung des Sleep(1) in der Schleife).

Wäre das nicht ein Problem? Ich meine, gibt es nicht interne Prozesse in MT4, die OnTick() ausführen müssen, um zu verhindern, dass interne Puffer überlaufen oder überdimensioniert sind, nachdem sie (der EA) wochenlang gelaufen sind, ohne anzuhalten und nur eine Schleife zu machen?

Ich frage das aus Unwissenheit, ich habe keine Ahnung, ob das ein Problem sein könnte, ich nehme nur an, dass MT4 nicht dafür entwickelt wurde.

Sie denken also (/ haben versucht) es wird nicht ein Problem sein? (Wenn ja, sind das großartige Neuigkeiten).


BTW: Wie bekommen Sie OnTimer() unter 1 sec Intervall ausgeführt werden?

 

Vor einigen Jahren habe ich EAs in einer Schleife laufen lassen, die in init() gestartet wurde, und zwar aus einem ähnlichen Grund - Lesen externer Daten mit minimaler Verzögerung. Es lief wochenlang ohne jegliche Nebenwirkung. Sleep(1) ist zufriedenstellend, um die Auswirkungen auf die Rechenleistung sehr gering zu halten.

Die Timer-Einstellungen akzeptieren Millis.

 
Ovo:

Vor einigen Jahren habe ich EAs in einer Schleife laufen lassen, die in init() gestartet wurde, und zwar aus einem ähnlichen Grund - Lesen externer Daten mit minimaler Verzögerung. Es lief wochenlang ohne jegliche Nebenwirkung. Sleep(1) ist zufriedenstellend, um die Auswirkungen auf die Rechenleistung sehr gering zu halten.

Die Timer-Einstellungen akzeptieren Millis.

Außergewöhnliche Neuigkeiten. Wenn Sie es bereits überprüft haben, dann kann ich es tun, ohne Angst vor einem Absturz zu haben. Vielen Dank für den Erfahrungsaustausch zu diesem Thema. Problem gelöst =D
 
Mariop:

BTW: Wie erreichen Sie, dass OnTimer() in einem Intervall von weniger als 1 Sekunde ausgeführt wird? :?


EventSetMillisecondTimer

Die Funktion zeigt dem Client-Terminal an, dass für diesen Expert Advisor oder Indikator Timer-Ereignisse in Intervallen von weniger als einer Sekunde erzeugt werden sollen.

boolEventSetMillisekundenTimer(
intmilliseconds// Anzahl der Millisekunden
);

Parameter

milliseconds

[in] Anzahl der Millisekunden, die die Häufigkeit der Timer-Ereignisse festlegen.

Rückgabewert

Im Falle einer erfolgreichen Ausführung wird true zurückgegeben, andernfalls - false. Um einen Fehlercode zu erhalten, sollte die Funktion GetLastError() aufgerufen werden.

Hinweis

Diese Funktion ist für die Fälle gedacht, in denen einhochauflösender Timer benötigt wird. Mitanderen Worten, Timer-Ereignisse sollten häufiger als einmal pro Sekunde empfangen werden. Wenn Ihnen ein herkömmlicher Timer mit einer Periode von mehreren Sekunden ausreicht, verwenden Sie EventSetTimer().

Normalerweise sollte diese Funktion von der Funktion OnInit() oder im Klassenkonstruktor aufgerufen werden. Um Ereignisse, die vom Timer kommen, zu verarbeiten, sollte ein Expert Advisor oder ein Indikator über die Funktion OnTimer() verfügen.

Jeder Expert Advisor und jeder Indikator arbeitet mit seinem eigenen Timer und empfängt Ereignisse ausschließlich von diesem Timer. Beim Herunterfahren der mql4-Anwendung wird der Timer zwangsweise zerstört, falls er zwar erstellt, aber nicht mit der Funktion EventKillTimer() deaktiviert wurde.

Für jedes Programm kann nur ein Timer gestartet werden. Jede mql4-Anwendung und jedes Diagramm haben ihre eigene Ereignis-Warteschlange, in die alle neu eingetroffenen Ereignisse gestellt werden. Wenn die Warteschlange bereits ein Timer-Ereignis enthält oder dieses Ereignis in der Verarbeitungsphase ist, wird das neue Timer-Ereignis nicht in die Warteschlange der mql4-Anwendung aufgenommen.


 
GumRai:

EventSetMillisecondTimer

Die Funktion gibt dem Clientterminal an, dass für diesen Expert Advisor oder Indikator Timer-Ereignisse in Intervallen von weniger als einer Sekunde erzeugt werden sollen.

boolEventSetMillisekundenTimer(
intmilliseconds// Anzahl der Millisekunden
);

Parameter

milliseconds

[in] Anzahl der Millisekunden, die die Häufigkeit der Timer-Ereignisse festlegen.

Rückgabewert

Im Falle einer erfolgreichen Ausführung wird true zurückgegeben, andernfalls - false. Um einen Fehlercode zu erhalten, sollte die Funktion GetLastError() aufgerufen werden.

Hinweis

Diese Funktion ist für die Fälle gedacht, in denen einhochauflösender Timer benötigt wird. Mitanderen Worten: Timer-Ereignisse sollten häufiger als einmal pro Sekunde empfangen werden. Wenn Ihnen ein herkömmlicher Timer mit einer Periode von mehreren Sekunden ausreicht, verwenden Sie EventSetTimer().

Normalerweise sollte diese Funktion von der OnInit()-Funktion oder im Klassenkonstruktor aufgerufen werden. Um Ereignisse zu verarbeiten, die vom Timer kommen, sollte ein Expert Advisor oder ein Indikator über die Funktion OnTimer() verfügen.

Jeder Expert Advisor und jeder Indikator arbeitet mit einem eigenen Timer, der ausschließlich Ereignisse von diesem Timer empfängt. Beim Herunterfahren der mql4-Anwendung wird der Timer zwangsweise zerstört, falls er zwar erstellt, aber nicht durch die Funktion EventKillTimer() deaktiviert wurde.

Für jedes Programm kann nur ein Timer gestartet werden. Jede mql4-Anwendung und jedes Diagramm haben ihre eigene Ereignis-Warteschlange, in der alle neu eingetroffenen Ereignisse abgelegt werden. Wenn die Warteschlange bereits ein Timer-Ereignis enthält oder sich dieses Ereignis in der Verarbeitungsphase befindet, wird das neue Timer-Ereignis nicht in die Warteschlange der mql4-Anwendung aufgenommen.




Danke für die ausführliche Erklärung; mein Fehler; ich habe es zu schnell online gelesen. (Links haben docs.mql4.com Domäne mit forum.mql4.com vertauscht. Nur für den Fall, dass noch jemand diesen Thread liest...)


EDIT: Ich werde die Anwesenheit eines Moderators ausnutzen: Wissen Sie, ob es einen Unterschied gibt zwischen dem Ausführen von OnTimer() alle N Millisekunden und dem Ausführen der gleichen Schleife alle N Millisekunden, wie in früheren Beiträgen beschrieben?

 

Ich verstehe nicht, warum die ganze Aufregung mit "ms" Timing. Es sei denn, es handelt sich um eine Art "High Frequency News Scalper" auf einer Broker-Verbindung mit sehr geringer Latenz, sollte ein Sekundenintervall bei OnTimer() als Antwortzeit mehr als ausreichend sein.

Wenn Sie alle Verzögerungen und Latenzen der JAVA-App-Ausführung, der IPC-Kommunikation (LAN & WAN), der Order-Management-Ausführung und der Broker-Server-Latenz addieren, gibt es keinen wirklichen Vorteil bei der Verwendung von "ms"-Timing für normale Strategien.

Wenn es sich jedoch um eine Art "High Frequency News Scalper" handelt, der auf einem VPS mit einer Verbindung mit geringer Latenz zum Broker eingesetzt werden soll, dann sollten Sie NICHT mit JAVA und IPC herumspielen und die Strategie direkt in sehr kompakter MQL kodieren, um die geringstmögliche Latenz zu erreichen.

EDIT:

Die Tatsache, dass Sie eine externe App mit einem separaten Datenfeed verwenden (der in Bezug auf Preis, Volumen und Timing garantiert vom MT4-Feed abweicht), zeigt, dass dies auf keinen Fall ein so enges "ms"-Timing erfordern würde. Ein Polling-Intervall von einer Sekunde ist mehr als ausreichend.

Denken Sie auch daran, dass das OnTick()-Ereignis auch auf eingehende Nachrichten prüfen kann, so dass "eine Sekunde" nur ein Grenzfall ist, wenn es keine eingehenden Ticks gibt. Und wenn es keine eingehenden Ticks gibt, dann ist auch nicht genug Aktivität (Volatilität und Liquidität) vorhanden, um eine so schnelle "ms"-Reaktion zu rechtfertigen.

Machen Sie die Dinge nicht kompliziert! Denken Sie an das K.I.S.S.-Prinzip.

 
Mariop:

EDIT: Ich werde die Anwesenheit eines Moderators ausnutzen: Wissen Sie, ob es einen Unterschied gibt zwischen dem Ausführen von OnTimer() alle N Millisekunden und dem Ausführen der gleichen Schleife alle N Millisekunden, wie in früheren Beiträgen beschrieben?

Nur weil ich ein Moderator bin, bedeutet das nicht, dass ich mehr weiß als andere. In der Tat haben die anderen Poster in diesem Thread offensichtlich mehr Wissen über dieses Thema als ich. Ich weiß nichts über Java.

Ich verstehe den Sinn der Verwendung von Sleep() in einem Timer-Ereignis nicht, da es den EA in eine Warteschleife versetzt und dies bedeuten könnte, dass eine Menge OnTick()-Ereignisse verpasst werden. Natürlich können OnTick-Ereignisse, je nach EA, unwichtig sein.

 
Ovo:

Vor einigen Jahren habe ich EAs in einer Schleife laufen lassen, die in init() gestartet wurde, und zwar aus einem ähnlichen Grund - Lesen externer Daten mit minimaler Verzögerung. Es lief wochenlang ohne jegliche Nebenwirkung. Der Sleep(1) ist ausreichend, um die Auswirkungen auf die Rechenleistung sehr gering zu halten.

Zum Spaß... das folgende Skript akzeptiert mehrere gleichzeitige TCP/IP-Verbindungen und schreibt eingehende CR-begrenzte Nachrichten in das Expertenprotokoll. Sobald das Skript läuft, können Sie beispielsweise eine Verbindung über Telnet herstellen (standardmäßig zu Port 51234), und jede Textzeile, die Sie eintippen, wird ausgedruckt.

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

Das folgende Skript akzeptiert mehrere gleichzeitige TCP/IP-Verbindungen und schreibt eingehende CR-getrennte Nachrichten in das Expertenprotokoll.

... um das Offensichtliche klarzustellen: Wenn Sie das oben Genannte in einem EA und nicht in einem Skript ausführen wollten, würden Sie es einfach wie folgt ändern:

  • Verschieben Sie den gesamten Code von OnStart() bis einschließlich des Aufrufs von listen() in das OnInit() des EA.
  • Richten Sie einen Hochfrequenz-Timer am Ende von OnInit() ein, z.B. EventSetMillisecondTimer(10)
  • Verschieben Sie den restlichen Code von OnStart() in OnTimer() und entfernen Sie die äußere "while (!IsStopped()) {}"-Schleife
Ich möchte noch anmerken, dass - sofern dies nicht in sehr aktuellen MT4-Builds behoben wurde - EventSetTimer() und EventSetMillisecondTimer() fehlschlagen können, wenn sie in OnInit() verwendet werden, wenn die MT4-Plattform mit einem EA startet, der bereits an einen Chart angehängt ist. Meiner Erfahrung nach ist es notwendig, die Rückgabewerte zu überprüfen, es erneut zu versuchen und möglicherweise auf das Einrichten des Timers im ersten OnTick() zurückzugreifen, wenn die Versuche, ihn in OnInit() zu erstellen, erfolglos waren.