Отправка ордеров в MT4 из Java через IP - страница 2

 
Mariop:

Правда. Спасибо. Я не помнил об этом. На самом деле это не решает проблему полностью, поскольку мне нужно, чтобы ордера выполнялись почти мгновенно (и я думаю, что с этим методом я могу проверять их только каждую секунду, но, по крайней мере, не каждый тик), но действительно я могу легко построить временное решение, используя его.

Хотя OnTimer имеет самую быструю скорость, согласованную с системными часами, т.е. каждые 15 мс, я бы предпочел циклы с интервалом в 1 мс (используя Sleep(1) в цикле).

 
Ovo:

Хотя OnTimer имеет самую быструю скорость, согласованную с системными часами, т.е. каждые 15 мс, я бы предпочел зацикливание с интервалом в 1 мс (используя Sleep(1) в цикле).

Не будет ли это проблемой? Я имею в виду, нет ли в MT4 внутренних процессов, которым может понадобиться выполнение OnTick(), чтобы избежать переполнения или переполнения внутренних буферов после нескольких недель работы (советника) без остановки и просто цикла?

Я спрашиваю это по незнанию, я понятия не имею, может ли это быть проблемой, просто предполагаю, что MT4 не был создан для этого.

Так вы думаете (/пробовали), что это не будет проблемой? (Если да, то это отличные новости).


BTW: Как заставить OnTimer() выполняться с интервалом менее 1 секунды :?

 

Несколько лет назад я запускал советников в цикле, запускаемом в init(), по аналогичной причине - чтение внешних данных с минимальной задержкой. Он работал неделями без каких-либо побочных эффектов. Sleep(1) удовлетворителен, чтобы сделать воздействие вычислительной мощности очень легким.

Настройки таймера принимают значения миллисекунд.

 
Ovo:

Несколько лет назад я запускал советников в цикле, запускаемом в init(), по аналогичной причине - чтение внешних данных с минимальной задержкой. Он работал неделями без каких-либо побочных эффектов. Sleep(1) удовлетворителен, чтобы сделать воздействие вычислительной мощности очень легким.

Настройки таймера принимают значения миллисекунд.

Необычная новость. Если вы уже проверили это, то я могу делать это, не опасаясь, что это приведет к краху. Большое спасибо, что поделились своим опытом в этом вопросе. Проблема решена =D
 
Mariop:

BTW: Как заставить OnTimer() выполняться с интервалом менее 1 секунды :?


EventSetMillisecondTimer

Функция указывает клиентскому терминалу, что для данного эксперта или индикатора должны генерироваться события таймера с интервалом менее одной секунды.

boolEventSetMillisecondTimer(
intmilliseconds// количество миллисекунд
);

Параметры

миллисекунды

[in] Количество миллисекунд, определяющее частоту событий таймера.

Возвращаемое значение

В случае успешного выполнения возвращает true, в противном случае - false. Для получения кода ошибки следует вызвать функцию GetLastError().

Примечание

Данная функция предназначена для случаев , когдатребуется таймер с высоким разрешением. Другими словами, события таймера должны поступать чаще, чем раз в секунду. Если вам достаточно обычного таймера с периодом в несколько секунд, используйте функцию EventSetTimer().

Обычно эту функцию следует вызывать из функции OnInit() или в конструкторе класса. Для обработки событий, поступающих от таймера, советник или индикатор должен иметь функцию OnTimer() .

Каждый эксперт и каждый индикатор работают со своим таймером, получая события только от этого таймера. При завершении работы приложения mql4 таймер принудительно уничтожается, если он был создан, но не был отключен функцией EventKillTimer().

Для каждой программы может быть запущен только один таймер. Каждое приложение mql4 и график имеют свою очередь событий, куда помещаются все вновь поступающие события. Если очередь уже содержит событие Timer или это событие находится в стадии обработки, то новое событие Timer не добавляется в очередь приложения mql4.


 
GumRai:

EventSetMillisecondTimer

Функция указывает клиентскому терминалу, что для данного эксперта или индикатора должны генерироваться события таймера с интервалом менее одной секунды.

boolEventSetMillisecondTimer(
intmilliseconds// количество миллисекунд
);

Параметры

миллисекунды

[in] Количество миллисекунд, определяющее частоту событий таймера.

Возвращаемое значение

В случае успешного выполнения возвращает true, в противном случае - false. Для получения кода ошибки необходимо вызвать функцию GetLastError().

Примечание

Эта функция предназначена для тех случаев , когдатребуется таймер высокого разрешения. Другими словами, события таймера должны поступать чаще, чем раз в секунду. Если вам достаточно обычного таймера с периодом в несколько секунд, используйте функцию EventSetTimer().

Обычно эта функция вызывается из функции OnInit() или в конструкторе класса. Для обработки событий, поступающих от таймера, советник или индикатор должен иметь функцию OnTimer() .

Каждый эксперт и каждый индикатор работают со своим таймером, получая события только от этого таймера. При выключении приложения mql4 таймер принудительно уничтожается, если он был создан, но не был отключен функцией EventKillTimer().

Для каждой программы может быть запущен только один таймер. Каждое приложение mql4 и график имеют собственную очередь событий, в которую помещаются все вновь поступающие события. Если в очереди уже есть событие Timer или это событие находится в стадии обработки, то новое событие Timer не добавляется в очередь mql4-приложения.




Спасибо за подробное объяснение; моя вина; я слишком быстро прочитал это в Интернете. (В ссылках домен docs.mql4.com заменен на forum.mql4.com. На всякий случай, если кто-нибудь еще проверит эту тему...)


EDIT: Я собираюсь воспользоваться тем, что модератор находится рядом: Знаете ли вы, существует ли разница между выполнением OnTimer() каждые N миллисекунд и выполнением того же цикла каждые N миллисекунд, как описано в предыдущих сообщениях?

 

Я не понимаю, зачем вся эта суета с таймингом "мс". Если только это не какой-то "высокочастотный скальпер новостей" на брокерском соединении с очень низкой задержкой, интервалов в одну секунду на OnTimer() должно быть более чем достаточно в качестве времени отклика.

Если вы добавите все задержки и латентность выполнения приложения JAVA, IPC (LAN и WAN) связи, выполнения управления ордерами и латентность сервера брокера, то не будет никакого реального преимущества в использовании "мс" тайминга для обычных стратегий.

Однако если это что-то вроде "высокочастотного новостного скальпера", который будет использоваться на VPS с низкой задержкой соединения с брокером, тогда вам вообще не следует возиться с JAVA и IPC, а следует кодировать стратегию непосредственно на очень компактном MQL, чтобы получить минимально возможную задержку.

EDIT:

Тот факт, что вы используете внешнее приложение с отдельным каналом данных (который гарантированно будет отклоняться по цене, объему и времени по отношению к каналу MT4), показывает, что это никак не может потребовать такого жесткого "мс" тайминга. Интервал опроса "одна секунда" более чем достаточен.

Помните также, что событие OnTick() также может проверять входящие сообщения, поэтому "одна секунда" - это только предельный случай, когда нет входящих тиков. А если входящих тиков нет, то и активности (волатильности и ликвидности) недостаточно, чтобы оправдать столь быстрый ответ "мс".

Не усложняйте ситуацию! Помните принцип K.I.S.S..

 
Mariop:

EDIT: Я собираюсь воспользоваться тем, что модератор находится рядом: Знаете ли вы, существует ли разница между выполнением OnTimer() каждые N миллисекунд и выполнением того же цикла каждые N миллисекунд, как описано в предыдущих сообщениях?

То, что я являюсь модератором, не означает, что я более осведомлен, чем другие. На самом деле, другие постеры в этой теме, очевидно, имеют больше знаний, чем я, по данному вопросу. Я ничего не знаю о Java.

Я не понимаю смысла использования Sleep() в событии таймера, так как это переводит советника в режим ожидания, а это может означать пропуск большого количества событий OnTick(). Конечно, в зависимости от советника, события OnTick могут быть не важны.

 
Ovo:

Несколько лет назад я запускал советников в цикле, запускаемом в init(), по аналогичной причине - чтение внешних данных с минимальной задержкой. Он работал неделями без каких-либо побочных эффектов. Sleep(1) удовлетворителен, чтобы сделать воздействие на вычислительную мощность очень легким.

Для развлечения... следующий скрипт принимает несколько одновременных TCP/IP-соединений и записывает входящие сообщения с разделителями CR в журнал Experts. Например, после запуска скрипта вы можете подключиться через Telnet (по умолчанию к порту 51234), и каждая строка текста, которую вы вводите, будет распечатана.

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

Для развлечения... следующий скрипт принимает несколько одновременных TCP/IP-соединений и записывает входящие сообщения с разделителем CR в журнал Experts.

... Говоря очевидное, если бы вы хотели сделать вышеописанное в советнике, а не в скрипте, то вы бы просто изменили его следующим образом:

  • Переместите весь код из OnStart(), вплоть до вызова listen(), в OnInit() советника.
  • Установите высокочастотный таймер в конце OnInit(), например, EventSetMillisecondTimer(10).
  • Переместите оставшийся код из OnStart() в OnTimer(), удалив внешний цикл "while (!IsStopped()) {}".
Добавлю, что - если это не было исправлено в последних сборках MT4 - EventSetTimer() и EventSetMillisecondTimer() могут не сработать, если используются в OnInit(), когда платформа MT4 запускается с советником, уже прикрепленным к графику. По моему опыту, необходимо проверить возвращаемые значения, повторить попытку и, возможно, вернуться к установке таймера в первом OnTick(), если попытки создать его в OnInit() были безуспешными.
Причина обращения: