Inviare ordini a MT4 da Java via IP - pagina 2

 
Mariop:

Vero. Grazie. Non me lo ricordavo. Non me lo ricordavo. In realtà non risolve completamente il problema poiché ho bisogno che gli ordini siano eseguiti quasi immediatamente (e penso che con questo metodo posso controllarlo solo ogni secondo, ma almeno non ogni tick), ma in effetti posso facilmente costruire una soluzione temporanea usandolo.

Anche se l'OnTimer ha il tasso più veloce allineato con l'orologio di sistema, cioè ogni 15 ms, preferirei un looping a intervalli di 1ms (usando lo Sleep(1) nel ciclo).

 
Ovo:

Anche se l'OnTimer ha il tasso più veloce allineato al clock di sistema, cioè ogni 15 ms, preferirei un looping a intervalli di 1ms (usando lo Sleep(1) nel ciclo).

Non sarebbe un problema? Voglio dire, non ci sono processi interni in MT4 che potrebbero aver bisogno di OnTick() per essere eseguiti per evitare che qualche tipo di buffer interno finisca in overflow o sovradimensionato dopo settimane di esecuzione (dell'EA) senza fermarsi e andare solo in loop?

Lo chiedo per ignoranza, non ho idea se possa essere un problema, solo supponendo che MT4 non sia stato concepito per farlo.

Quindi pensi(/hai provato) che non sarà un problema? (Se è così, sono ottime notizie).


BTW: come fai a far eseguire OnTimer() sotto l'intervallo di 1 sec? :?

 

Ero solito eseguire gli EA in un ciclo iniziato in init() alcuni anni fa, per una ragione simile - leggere i dati esterni con il minimo ritardo. Funzionava per settimane senza alcun effetto collaterale. Lo Sleep(1) è soddisfacente per rendere l'impatto sulla potenza di calcolo molto leggero.

Le impostazioni del timer accettano i millis.

 
Ovo:

Ero solito eseguire gli EA in un ciclo iniziato in init() alcuni anni fa, per una ragione simile - leggere i dati esterni con il minimo ritardo. Funzionava per settimane senza alcun effetto collaterale. Lo Sleep(1) è soddisfacente per rendere l'impatto sulla potenza di calcolo molto leggero.

Le impostazioni del timer accettano i millis.

Una notizia straordinaria. Se l'hai già controllato, allora posso farlo senza aver paura che vada in crash. Grazie mille per aver condiviso la tua esperienza su questo problema. Problema risolto =D
 
Mariop:

BTW: come fai a far sì che OnTimer() venga eseguito sotto l'intervallo di 1 sec? :?


EventSetMillisecondTimer

La funzione indica al terminale client che gli eventi timer devono essere generati a intervalli inferiori a un secondo per questo Expert Advisor o indicatore.

boolEventSetMillisecondTimer(
intmillisecondi// numero di millisecondi
);

Parametri

millisecondi

[Numero di millisecondi che definisce la frequenza degli eventi timer.

Valore restituito

In caso di esecuzione corretta, restituisce true, altrimenti - false. Per ricevere un codice di errore, dovrebbe essere chiamata la funzione GetLastError().

Nota

Questa funzione è progettata per i casi in cui è richiesto un timer ad alta risoluzione. In altre parole, glieventi del timer dovrebbero essere ricevuti più frequentemente di una volta al secondo. Se un timer convenzionale con un periodo di alcuni secondi è sufficiente per voi, usate EventSetTimer().

Di solito, questa funzione dovrebbe essere chiamata dalla funzione OnInit() o nel costruttore della classe. Per gestire gli eventi provenienti dal timer, un Expert Advisor o un indicatore dovrebbe avere la funzione OnTimer() .

Ogni Expert Advisor e ogni indicatore lavora con il proprio timer che riceve gli eventi solo da questo timer. Durante la chiusura dell'applicazione mql4, il timer viene forzatamente distrutto nel caso in cui sia stato creato ma non sia stato disabilitato dalla funzione EventKillTimer().

Solo un timer può essere lanciato per ogni programma. Ogni applicazione mql4 e il grafico hanno la loro coda di eventi dove tutti gli eventi appena arrivati vengono messi. Se la coda contiene già l'evento Timer o questo evento è in fase di elaborazione, allora il nuovo evento Timer non viene aggiunto alla coda dell'applicazione mql4.


 
GumRai:

EventSetMillisecondTimer

La funzione indica al terminale client che gli eventi timer devono essere generati a intervalli inferiori a un secondo per questo Expert Advisor o indicatore.

boolEventSetMillisecondTimer(
intmillisecondi// numero di millisecondi
);

Parametri

millisecondi

[Numero di millisecondi che definisce la frequenza degli eventi timer.

Valore restituito

In caso di successo dell'esecuzione, restituisce true, altrimenti - false. Per ricevere un codice di errore, dovrebbe essere chiamata la funzione GetLastError().

Nota

Questa funzione è progettata per i casi in cui è richiesto un timer ad alta risoluzione. In altre parole, glieventi del timer dovrebbero essere ricevuti più frequentemente di una volta al secondo. Se un timer convenzionale con un periodo di alcuni secondi è sufficiente per voi, usate EventSetTimer().

Di solito, questa funzione dovrebbe essere chiamata dalla funzione OnInit() o nel costruttore della classe. Per gestire gli eventi provenienti dal timer, un Expert Advisor o un indicatore dovrebbe avere la funzione OnTimer() .

Ogni Expert Advisor e ogni indicatore lavora con un proprio timer che riceve gli eventi solo da questo timer. Durante la chiusura dell'applicazione mql4, il timer viene forzatamente distrutto nel caso sia stato creato ma non sia stato disabilitato dalla funzione EventKillTimer().

Solo un timer può essere lanciato per ogni programma. Ogni applicazione e grafico mql4 ha la propria coda di eventi dove vengono messi tutti gli eventi appena arrivati. Se la coda contiene già un evento Timer o questo evento è in fase di elaborazione, allora il nuovo evento Timer non viene aggiunto alla coda di mql4application.




Grazie per la spiegazione dettagliata; colpa mia; l'ho letto troppo velocemente online. (I link hanno scambiato il dominio docs.mql4.com con forum.mql4.com. Solo nel caso che qualcun altro controlli questo thread...)


EDIT: approfitto del fatto che c'è un moderatore in giro: Sapete se esiste qualche differenza tra eseguire OnTimer() ogni N millisecondi e fare lo stesso looping ogni N millisec come descritto nei post precedenti?

 

Non capisco perché tutto questo trambusto con i tempi in "ms". A meno che non si tratti di una sorta di "scalper di notizie ad alta frequenza" su una connessione di broker a bassissima latenza, gli intervalli di un secondo su OnTimer() dovrebbero essere più che sufficienti come tempo di risposta.

Se si aggiungono tutti i ritardi e la latenza dell'esecuzione dell'applicazione JAVA, la comunicazione IPC (LAN e WAN), l'esecuzione della gestione degli ordini e la latenza del server del broker, non c'è alcun vantaggio reale nell'usare un tempo "ms" per le strategie normali.

Se invece si tratta di una sorta di "High Frequency News Scalper", da utilizzare su un VPS con una connessione a bassa latenza al broker, allora non si dovrebbe affatto scherzare con JAVA e IPC, e si dovrebbe codificare la strategia direttamente in MQL molto compatto per ottenere la più bassa latenza possibile.

EDIT:

Il fatto che tu stia usando un'app esterna con un feed di dati separato (che è garantito per deviare in prezzo, volume e tempi rispetto al feed MT4), dimostra che non c'è modo che questo richieda un timing così stretto "ms". Un intervallo di polling di "un secondo" è più che adeguato.

Ricordate anche che l'evento OnTick() può anche controllare i messaggi in arrivo, quindi "un secondo" è solo un caso limite quando non ci sono tick in arrivo. E se non ci sono tick in arrivo, allora non c'è nemmeno abbastanza attività (volatilità e liquidità) per giustificare una risposta così rapida in "ms".

Non complicare le cose! Ricordate il principio K.I.S.S.

 
Mariop:

EDIT: approfitto della presenza di un moderatore: Sapete se esiste qualche differenza tra eseguire OnTimer() ogni N millisecondi e fare lo stesso looping ogni N millisec come descritto nei post precedenti?

Solo perché sono un moderatore, non significa che io sia più informato di altri. Infatti, gli altri post in questo thread hanno ovviamente più conoscenza di me sull'argomento. Non so nulla di Java.

Non capisco il senso dell'uso di Sleep() in un evento timer in quanto mette l'EA in attesa e questo potrebbe significare perdere molti eventi OnTick(). Naturalmente, a seconda dell'EA, gli eventi OnTick potrebbero essere poco importanti.

 
Ovo:

Ero solito eseguire gli EA in un ciclo iniziato in init() alcuni anni fa, per una ragione simile - leggere i dati esterni con il minimo ritardo. Funzionava per settimane senza alcun effetto collaterale. Lo Sleep(1) è soddisfacente per rendere l'impatto sulla potenza di calcolo molto leggero.

Per un po' di divertimento... il seguente script accetta più connessioni TCP/IP contemporanee, e scrive i messaggi in entrata delimitati da CR nel log degli esperti. Per esempio, una volta che lo script è in esecuzione ci si può connettere via Telnet (alla porta 51234 per impostazione predefinita), e ogni linea di testo digitata verrà stampata.

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

Per un po' di divertimento... il seguente script accetta connessioni TCP/IP multiple simultanee e scrive i messaggi in entrata delimitati da CR nel log degli esperti.

... dicendo una cosa ovvia, se si volesse fare quanto sopra in un EA piuttosto che in uno script, allora lo si cambierebbe semplicemente come segue:

  • Spostare tutto il codice da OnStart(), fino alla chiamata a listen(), in OnInit() dell'EA
  • Impostare un timer ad alta frequenza alla fine di OnInit(), ad esempio EventSetMillisecondTimer(10)
  • Spostare il codice rimanente da OnStart() a OnTimer(), rimuovendo il ciclo esterno "while (!IsStopped()) {}".
Aggiungo una nota che - a meno che questo non sia stato risolto in recenti build di MT4 - EventSetTimer() e EventSetMillisecondTimer() possono fallire se usati in OnInit() quando la piattaforma MT4 si sta avviando con un EA già collegato a un grafico. Nella mia esperienza è necessario controllare i valori di ritorno; riprovare; e potenzialmente ricadere nell'impostazione del timer sul primo OnTick() se i tentativi di crearlo in OnInit() non hanno avuto successo.
Motivazione: