Enviar pedidos para o MT4 a partir de Java via IP - página 2

 
Mariop:

É verdade. Obrigado. Eu não me lembrava disso. Na verdade, isso não resolve o problema completamente, pois preciso que as ordens sejam executadas quase imediatamente (e acho que com este método só posso verificá-lo a cada segundo, mas pelo menos não a cada carrapato), mas de fato posso facilmente construir uma solução temporária usando-o.

Embora o OnTimer tenha a taxa mais rápida alinhada com o relógio do sistema, ou seja, a cada 15 ms, eu preferiria o looping em intervalos de 1ms (usando o Sleep(1) no loop).

 
Ovo:

Embora o OnTimer tenha a taxa mais rápida alinhada com o relógio do sistema, ou seja, a cada 15 ms, eu preferiria o looping em intervalos de 1ms (usando o Sleep(1) no loop).

Isso não seria um problema? Quero dizer, não há processos internos no MT4 que possam precisar do OnTick() para serem executados para evitar que algum tipo de amortecedor interno acabe transbordando ou superdimensionado após semanas de funcionamento (o EA) sem parar e apenas fazer looping?

Estou perguntando por ignorância, não tenho idéia se poderia ser um problema, apenas assumindo que o MT4 não foi concebido para fazê-lo.

Então você acha que não será um problema? (Se sim, essas são ótimas notícias).


BTW: Como você consegue que o OnTimer() seja executado abaixo de um intervalo de 1 segundo? :?

 

Eu costumava executar EAs em um loop iniciado no init() alguns anos atrás, por uma razão semelhante - ler dados externos com o mínimo de atraso. Ele costumava rodar semanas sem nenhum efeito colateral. O Sleep(1) é satisfatório para tornar o impacto da potência computacional muito leve.

A configuração do temporizador aceita milis.

 
Ovo:

Eu costumava executar EAs em um loop iniciado no init() alguns anos atrás, por uma razão semelhante - ler dados externos com o mínimo de atraso. Ele costumava rodar semanas sem nenhum efeito colateral. O Sleep(1) é satisfatório para tornar o impacto da potência computacional muito leve.

A configuração do temporizador aceita milis.

Notícias extraordinárias. Se você já verificou, então eu posso fazer isso sem ter medo de cair. Muito obrigado por compartilhar sua experiência sobre este assunto. Problema resolvido =D
 
Mariop:

BTW: Como obter o OnTimer() para ser executado abaixo de um intervalo de 1 segundo? :?


EventoSetMillisecondTimer

A função indica ao terminal do cliente que os eventos do temporizador devem ser gerados em intervalos inferiores a um segundo para este Expert Advisor ou indicador.

boolEventSetMillisecondTimer(
emmilissegundos// número de milissegundos
);

Parâmetros

milissegundos

[em] Número de milissegundos que definem a freqüência dos eventos do temporizador.

Valor retornado

Em caso de execução bem sucedida, os retornos são verdadeiros, caso contrário - falsos. Para receber um código de erro, a função GetLastError() deve ser chamada.

Nota

Esta característica é projetada para os casos em que é necessário um temporizador de alta resolução. Em outras palavras, os eventos do timer devem ser recebidos com mais freqüência do que uma vez por segundo. Se um timer convencional com o período de vários segundos for suficiente para você, use EventSetTimer().

Normalmente, esta função deve ser chamada a partir da função OnInit() ou no construtor de classes. Para lidar com eventos vindos do timer, um Expert Advisor ou um indicador deve ter a função OnTimer().

Cada Expert Advisor e cada indicador trabalham com seu próprio timer recebendo eventos somente deste timer. Durante o desligamento da aplicação mql4, o timer é destruído à força caso tenha sido criado mas não tenha sido desativado pela função EventKillTimer().

Apenas um timer pode ser lançado para cada programa. Cada aplicação mql4 e gráfico têm sua própria fila de eventos onde todos os eventos recém-chegados são colocados. Se a fila já contém o evento Timer ou se este evento está na etapa de processamento, então o novo evento Timer não é adicionado à fila de aplicação mql4.


 
GumRai:

EventoSetMillisecondTimer

A função indica ao cliente final que os eventos temporizados devem ser gerados em intervalos inferiores a um segundo para este Expert Advisor ou Indicador.

boolEventSetMillisecondTimer(
emmilissegundos// número de milissegundos
);

Parâmetros

milissegundos

[em] Número de milissegundos que definem a freqüência dos eventos do temporizador.

Valor retornado

Em caso de execução sucessiva, os retornos são verdadeiros, caso contrário - falsos. Para receber um código de erro, a função GetLastError() deve ser chamada.

Nota

Esta característica é projetada para os casos em que é necessário um temporizador de alta resolução. Em outras palavras, os eventos do temporizador devem ser recebidos com mais freqüência do que uma vez por segundo. Se um timer convencional com o período de vários segundos for suficiente para você, use EventSetTimer().

Normalmente, esta função deve ser chamada a partir da função OnInit()ou no construtor de classes. Para lidar com eventos vindos do temporizador, um Expert Advisor ou um indicador deve ter a função OnTimer( ).

Cada Expert Advisor e cada indicador trabalha com seu próprio timer recebendo eventos somente deste timer. Durante o desligamento da aplicação mql4, o timer é destruído à força caso tenha sido criado mas não tenha sido desabilitado pela função EventKillTimer().

Apenas um timer pode ser lançado para cada programa. Cada aplicação mql4 e gráfico tem sua própria fila de eventos onde todos os eventos recém-chegados são colocados. Se a fila já contém o evento Timer ou este evento estiver em fase de processamento, então o novo evento Timer não é adicionado à fila de aplicação do mql4.




Obrigado pela explicação detalhada; minha culpa; li muito rapidamente online. (Os links têm trocado o domínio docs.mql4.com com forum.mql4.com. Caso alguém mais verifique este tópico...)


EDIT: Vou aproveitar o fato de um moderador estar por perto: Você sabe se existe alguma diferença entre executar OnTimer() cada N milisecs e fazer o mesmo looping a cada N milisec como descrito em posts anteriores?

 

Não vejo por que todo esse alvoroço com o "ms" de tempo. A menos que isto seja algum tipo de "Escalador de notícias de alta freqüência" em uma conexão de corretor de latência muito baixa, intervalos de um segundo no OnTimer() devem ser mais do que suficientes como tempo de resposta.

Se você adicionar todos os atrasos e latência da execução do aplicativo JAVA, comunicação IPC (LAN & WAN), execução de gerenciamento de ordens e latência do servidor do corretor, não há nenhuma vantagem real em usar a temporização "ms" para estratégias normais.

Se, no entanto, for algum tipo de "Escalador de notícias de alta freqüência", para ser usado em um VPS com uma conexão de baixa latência com o corretor, então você NÃO deve estar brincando com JAVA e IPC, e deve codificar a estratégia diretamente em MQL muito compacto para obter a menor latência possível.

EDIT:

O fato de você estar usando um aplicativo externo com uma alimentação de dados separada (que é garantida para desviar em preço, volume e tempo com relação à alimentação MT4), mostra que não há como isso exigir um tempo "ms" tão apertado. Um intervalo de votação de "um segundo" é mais do que adequado.

Lembre-se também, que o evento OnTick() também pode verificar as mensagens recebidas, portanto "um segundo" é apenas um caso limite quando não há ticks de entrada. E se não houver ticks de entrada, então também não há atividade suficiente (volatilidade e liquidez) para justificar uma resposta "ms" tão rápida de qualquer forma.

Não complique as coisas! Lembre-se do K.I.S.S. principal.

 
Mariop:

EDIT: Vou tirar vantagem de um moderador estar por perto: Você sabe se existe alguma diferença entre executar OnTimer() cada N milisecs e fazer o mesmo looping a cada N milisec como descrito em posts anteriores?

Só porque sou um moderador, isso não significa que eu seja mais conhecedor do que os outros. Na verdade, os outros cartazes deste tópico obviamente têm mais conhecimento do que eu sobre o assunto. Eu não sei nada sobre Java.

Eu não entendo o objetivo de usar Sleep() em um evento de temporizador, pois isso coloca a EA em espera e isso pode significar a falta de muitos eventos OnTick(). É claro que, dependendo do EA, os eventos OnTick podem não ser importantes.

 
Ovo:

Eu costumava executar EAs em um loop iniciado no init() alguns anos atrás, por uma razão semelhante - ler dados externos com o mínimo de atraso. Ele costumava rodar semanas sem nenhum efeito colateral. O Sleep(1) é satisfatório para tornar o impacto da potência computacional muito leve.

Para um pouco de diversão... o seguinte script aceita múltiplas conexões TCP/IP simultâneas, e grava mensagens de entrada delimitadas por CR para o log Experts. Por exemplo, uma vez que o script esteja rodando, você pode se conectar via Telnet (à porta 51234 por padrão), e cada linha de texto que você digitar será impressa.

#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 um pouco de diversão... o seguinte script aceita múltiplas conexões TCP/IP simultâneas, e escreve mensagens de entrada delimitadas por CR para o log Experts.

... declarando o óbvio, se você quisesse fazer o acima descrito em um EA em vez de um script, então você simplesmente o mudaria da seguinte forma:

  • Mova todo o código de OnStart(), até e incluindo a chamada para ouvir(), para o OnInit() da EA.
  • Configurar um temporizador de alta freqüência no final do OnInit(), por exemplo, EventSetMillisecondTimer(10)
  • Mova o código restante de OnStart() para OnTimer(), removendo o laço externo "while (!IsStopped()) {}".
Vou acrescentar uma nota que - a menos que isto tenha sido corrigido em construções muito recentes do MT4 - EventSetTimer() e EventSetMillisecondTimer() podem falhar se usados no OnInit() quando a plataforma MT4 está iniciando com uma EA já anexada a um gráfico. Em minha experiência, é necessário verificar os valores de retorno; tentar novamente; e potencialmente voltar a configurar o timer no primeiro OnTick() se as tentativas de criá-lo no OnInit() não foram bem sucedidas.
Razão: