通过IP从Java发送订单到MT4 - 页 2

 
Mariop:

确实如此。谢谢。我不记得了。事实上,这并不能完全解决问题,因为我需要订单几乎立即被执行(我想用这种方法我只能每秒检查一次,但至少不是每一次勾选),但确实我可以用它轻松建立一个临时解决方案。

虽然OnTimer有最快的速率与系统时钟对齐,即每15ms,但我更喜欢以1ms的间隔循环(在循环中使用Sleep(1))。

 
Ovo:

虽然OnTimer有最快的速率与系统时钟对齐,即每15ms,但我更喜欢以1ms的间隔循环(在循环中使用Sleep(1))。

这不会是个问题吗?我的意思是,在MT4中没有内部进程可能需要执行OnTick(),以避免某种内部缓冲区在运行(EA)数周后溢出或过大,而不停止和只是循环?

我是在无知的情况下问的,我不知道这是否会成为一个问题,只是假设MT4的设计不是为了做这个。

所以你认为(/已经尝试)这不会是一个问题?(如果是这样,那就是好消息了)。


BTW:你如何让OnTimer() 在1秒的间隔内执行?

 

几年前,我曾经在init()中开始循环运行EA,原因类似--以最小的延迟读取外部数据。它曾经运行了数周而没有任何副作用。Sleep(1)是令人满意的,它使计算能力 的影响非常小。

定时器设置接受毫秒。

 
Ovo:

几年前,我曾经在init()中开始循环运行EA,原因类似--以最小的延迟读取外部数据。它曾经运行了数周而没有任何副作用。Sleep(1)是令人满意的,它使计算能力的影响非常小。

定时器设置接受毫秒。

非凡的消息。如果你已经检查过 了,那么我就可以不用担心它崩溃了。非常感谢你对这个问题的经验分享。问题解决了 =D
 
Mariop:

BTW: 如何让OnTimer()在1秒的间隔以下执行? :?


事件SetMillisecondTimer

该函数向客户终端表明,该专家顾问或指标的定时器 事件应以小于一秒的间隔产生。

bool EventSetMillisecondTimer(
int milliseconds// milliseconds数
);

参数

毫秒

[定义定时器事件的频率的毫秒数。

返回值

在成功执行的情况下,返回true,否则 - false。要接收一个错误代码,应该调用GetLastError() 函数。

注意

这个功能是为 需要高分辨率定时器 情况而设计的 。换句话说, 定时器事件的接收应该比每秒一次更频繁。如果一个周期为几秒的传统定时器对你来说已经足够了,请使用EventSetTimer()

通常,这个函数应该从OnInit() 函数或在类构造函数 中调用。为了处理来自定时器的事件,专家顾问或指标应该有OnTimer() 函数。

每个专家顾问和每个指标都有自己的定时器,只从这个定时器接收事件。 在mql4应用程序关闭期间,如果定时器已经创建但没有被EventKillTimer() 函数禁用,那么它将被强行销毁。

每个程序只能启动一个定时器。每个mql4程序和图表都有自己的事件队列,所有新到的事件都放在那里。如果队列中已经包含了Timer 事件或者该事件处于处理阶段,那么新的Timer事件就不会被添加到mql4应用程序队列中。


 
GumRai:

事件SetMillisecondTimer

该函数向客户终端指出,该专家顾问或指标的定时器 事件应以小于一秒的间隔产生。

bool EventSetMillisecondTimer(
int milliseconds// milliseconds数量
);

参数

毫秒

[定义定时器事件的频率的毫秒数。

返回值

在成功执行的情况下,返回true,否则返回false。要接收一个错误代码,应该调用GetLastError() 函数。

注意

这个功能是为 需要高分辨率的定时器 情况而设计的 。换句话说, 定时器事件的接收频率应该超过每秒一次。如果一个周期为几秒钟的传统定时器对你来说已经足够了,请使用EventSetTimer()

通常,这个函数应该在OnInit() 函数或类的构造函数 中调用。为了处理来自计时器的事件,专家顾问或指标应该有OnTimer() 函数。

每个专家顾问和每个指标都有自己的定时器,只从这个定时器接收事件。在mql4应用程序关闭期间,如果定时器已经创建,但没有被EventKillTimer() 函数禁用,则会被强行销毁。

每个程序只能启动一个定时器。每个mql4程序和图表都有自己的事件队列,所有新到的事件都放在那里。如果队列中已经包含了Timer 事件或者这个事件处于处理阶段,那么新的Timer事件就不会被添加到mql4应用程序队列中。




谢谢你的详细解释;我的错;我在网上读得太快了。(链接已将docs.mql4.com 域名与forum.mql4.com 互换。以防有人检查这个线程...)


编辑:我想利用版主在身边的机会。你知道每隔N个毫秒执行OnTimer()和之前帖子中描述的每隔N个毫秒做同样的循环是否存在任何区别?

 

我不明白为什么对 "毫秒 "计时如此大惊小怪。除非这是在非常低延迟 的经纪商连接上的某种"高频新闻缩放器",否则OnTimer() 的一秒间隔作为响应时间应该是绰绰有余的。

如果你把JAVA应用程序的执行、IPC(局域网和广域网)通信、订单管理的执行和经纪商服务器的延迟加在一起,对于正常的策略来说,使用 "ms "计时并没有真正的优势。

但是,如果是某种"高频新闻缩放器",在VPS上使用,与经纪商的连接延迟较低,那么你就不应该搞什么JAVA和IPC,而应该直接用非常紧凑的MQL编码策略,以获得尽可能低的延迟。

编辑。

你使用一个外部应用程序和一个独立的数据源(保证在价格、数量和时间上与MT4的数据源有偏差),这表明不可能需要如此严格的 "毫秒 "时间 一秒钟 "的轮询间隔是绰绰有余的。

请记住,OnTick() 事件也可以检查传入的信息,所以 "一秒钟 "只是在没有传入的信息时的一个限制性案例。如果没有传入的ticks,那么也没有足够的活动(波动性和流动性)来证明这样一个快速的 "ms "响应是合理的。

不要把事情复杂化!记住K.I.S.S.原则。

 
Mariop:

编辑:我想利用版主在身边的机会。你知道每隔N毫秒执行一次OnTimer()和之前帖子中描述的每隔N毫秒做一次同样的循环是否存在任何区别?

仅仅因为我是版主,这并不意味着我比其他人更了解情况。事实上,这个主题中的其他发帖人显然比我更了解这个主题。我对Java一无所知。

我不明白在定时器事件中使用Sleep() 的意义,因为它使EA处于暂停状态,这可能意味着错过很多OnTick()事件。当然,根据EA的情况,OnTick事件可能并不重要。

 
Ovo:

几年前,我曾经在init()中开始循环运行EA,原因类似--以最小的延迟读取外部数据。它曾经运行了数周而没有任何副作用。Sleep(1)是令人满意的,它使计算能力的影响非常小。

为了好玩......下面的脚本接受多个并发的TCP/IP连接,并将传入的以CR为分隔符的信息写到专家日志中。例如,一旦脚本运行,你可以通过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为分隔符的信息写到专家日志中。

......显而易见,如果你想在EA中而不是在脚本中完成上述工作,那么你只需按以下方式进行修改。

  • 将OnStart()中的所有代码,包括对listen()的调用,都移到EA的OnInit()中。
  • 在OnInit()的最后设置一个高频定时器,例如EventSetMillisecondTimer(10)
  • 将OnStart()中的剩余代码移到OnTimer()中,删除外部的 "while (!IsStopped()) {}"循环。
我要补充说明的是--除非在最近的MT4版本中得到修复--当MT4平台启动时,如果在OnInit()中使用已经连接到图表的EA,EventSetTimer()和EventSetMillisecondTimer()可能会失败。根据我的经验,如果在OnInit()中创建计时器的尝试不成功,有必要检查 返回值;重试;并有可能退回到在第一个OnTick()中设置计时器。