分析ReConnecter.mq4脚本程序.

 

原程序是ReConnecter.mq4脚本程序. 用google将俄文注释翻译成中文.

由于mt4重新启动后, 脚本程序不能自动运行, 而ea是可以的, 因此希望将其改写到ea中.

所以希望能够明白每行代码的具体意义, 了解该脚本程序会受到何种限制.

原文: ReConnecter - A Script for Periodical Reconnections

Login()中 if (hwnd_parent != 0) {... ...} 那段代码, 可能分析的有问题. 麻烦大家帮忙看看.

谢谢!



//+------------------------------------------------------------------+

//|                                                  ReConnecter.mq4 | 脚本定期重新连接到了“我的最爱第一个帐户”

//|                                      Copyright ? 2008, komposter | 感谢参考getch (https://www.mql5.com/ru/users/getch)

//|                                      mailto:komposterius@mail.ru |

//+------------------------------------------------------------------+

#property copyright	"Copyright ? 2008, komposter"

#property link		"mailto:komposterius@mail.ru"

 

#property show_inputs

 

extern int	Pause_sec = 600;    // 暂停之间在几秒钟内重新连接

 

#include <WinUser32.mqh>

#import "user32.dll"

	int GetParent( int hWnd );

	int GetDlgItem( int hDlg, int nIDDlgItem );

	int GetLastActivePopup( int hWnd );

#import

 

#define VK_HOME		0x24

#define VK_DOWN		0x28

#define VK_ENTER	0x0D

 

#define PAUSE		1000

 

void init() {

	start();

}

 

void start() {

	if ( !IsDllsAllowed() ) {

		// mt4系统不允许调用DLL.

		Alert( "DLLs not alllowed!" );

		return;

	}

 

	while ( !IsStopped() ) {

		// ea正在执行中.

		// 重新登录.

	        Login(1);

		while ( !IsStopped() ) {

			// 通过查询历史交易定单, 来检查登录是否成功.

			if ( OrdersHistoryTotal() > 0 )

				break;

			// 登录不成功, 1秒钟后重新登录.

			Sleep(1000);

		}

		Print( "成功连接到该帐户 #", AccountNumber(), "! 下一步将在 ", Pause_sec/60, " 分..." );

 

 		// 在设定的时间之后, 重新登录.

		Sleep(Pause_sec*1000);

	}



	return;

}

 

// 连接到该帐户,位于窗口的行数数我的最爱标签导航

void Login( int Num ) {

	int hwnd, hwnd_parent = 0;

 

	// 取当前活动图表的窗口句柄.

	hwnd = WindowHandle(Symbol(), Period());

	while (!IsStopped()) {

		// 取其父窗口的句柄. (应该是mt4主窗口的窗口句柄)

		hwnd = GetParent(hwnd);

		if (hwnd == 0) break;

		hwnd_parent = hwnd;

	}

 

	if (hwnd_parent != 0) {

		// 找到主窗口

		// 可能是取mt4导航器 (用Ctrl+N打开) 的窗口句柄.

		hwnd = GetDlgItem(hwnd_parent, 0xE81C); // 找到我的最爱窗口导航

		// 可能是取mt4导航器树型目录中, MetaTrader选项.

		hwnd = GetDlgItem(hwnd, 0x52);

		// 可能是取导航器-->MetaTrader-->账号

		hwnd = GetDlgItem(hwnd, 0x8A70);

 

		// 可能是取"账号"中, 第一条记录的索引信息.

		PostMessageA(hwnd, WM_KEYDOWN, VK_HOME,0); // 第一行最喜爱的收藏窗口导航

 

		while (Num > 1) {

			// 选择其它账号登录.

			PostMessageA(hwnd, WM_KEYDOWN,VK_DOWN, 0); // 转移到所需行

			Num--;

		}

 

 		// 将登录信息发送到服务器.

		PostMessageA(hwnd, WM_KEYDOWN, VK_ENTER, 0);  // 登录

		Sleep(PAUSE);                                 // 等待

 

 		// 返回登录前的活动窗口.

		hwnd = GetLastActivePopup(hwnd_parent);  // 找到登录表单

		PostMessageA(hwnd, WM_KEYDOWN, VK_ENTER, 0); // 记录

	}

 

	return;

}


 
我做了个多种登录模式的重连脚本。EA暂时没弄。有空交流下。
 
//+------------------------------------------------------------------+
//|                                             断线自动重连脚本.mq4 |
//|                                                            "ldj" |
//|                                           "http://www.fxvip.net" |
//+------------------------------------------------------------------+
//scripts//脚本文件
#property copyright "ldj"
#property link      "http://www.fxvip.net"
#property show_inputs
//自动登录准备
//把要登录的账号添加到收藏夹,并保证列在第一位。 
extern string 邮件标记字符串="EA";
extern string 所有参数单位为秒="以下参数均折算为妙";
extern int 时差=18000; //本地时间与服务器时间的时间差,本地时间迟于服务器时间其值为负。单位秒。
extern int 允许时差=120;//允许服务器行情报价停滞的时间,建议设置2分钟即120秒。
extern int 检测间隔=20;//单位秒,不应小于20秒。
extern bool 邮件报警=false;
extern bool 声音报警=false;
//0、不自动登录。1、重新扫描服务器。2、重新登录第一个服务器。3、登录收藏夹第一个账号4、登录导航窗第一个账号
extern int 重连方式=1;

int ServerNum=0;

#include <WinUser32.mqh>
#import "user32.dll"
  int GetParent( int hWnd );
  int GetDlgItem( int hDlg, int nIDDlgItem );
  int GetLastActivePopup( int hWnd );
  int GetAncestor(int hWnd,int gaFlags);//GetAncestor(HWND hwnd,UINT gaFlags);
#import

#define VK_HOME 0x24
#define VK_DOWN 0x28
#define VK_ENTER 0x0D

#define PAUSE 1000
int LoginSleep=120; //不小于2分钟,避免频繁发送登录请求
int MailSleep=120;  //不小于2分钟,避免频繁发送邮件
datetime LoginTime,MailTime;

//+------------------------------------------------------------------+
//| script program init function                                     |
//+------------------------------------------------------------------+
void init()
{
      LoginTime=TimeLocal();
      MailTime=TimeLocal();
      if(检测间隔<10)检测间隔=10; //避免频繁检测系统资源消耗过多!
      return;
}
//+------------------------------------------------------------------+
//| script program start function                                    |
//+------------------------------------------------------------------+
void start()
{
	if ( !IsDllsAllowed() )
	{
		Alert( "禁止调用DLL文件" );
		return;
	}

   string 正文;
   double T;
   int week;
   
   while(IsStopped()==false)//如果没有接到脚本终止命令则一直运行
   {
     正文="服务器时间:"+TimeToStr(TimeCurrent())+"\n本地的时间:"+TimeToStr(TimeLocal())+"\n"+AccountCompany()+"平台"+"账号为:"+AccountNumber()+"的账户";
     
     T=(TimeLocal()-TimeCurrent())/360.0;
     Comment("服务器时间:",TimeToStr(TimeCurrent(),TIME_SECONDS),
             "\n本地的时间:",TimeToStr(TimeLocal(),TIME_SECONDS),
             "\n参考时差值:",MathRound(T)*360);
     week=TimeDayOfWeek(TimeLocal()-时差);
     if(week>=1 && week<=5)//服务器时间为 星期一  至  星期五,有星期天或星期六 K线的平台可适当修改。
     {
       if ((TimeLocal()-TimeCurrent()-时差)>允许时差)
       {
         if (LoginTime+LoginSleep<TimeLocal())
         {
           LoginTime=TimeLocal();
           switch(重连方式)
           {
             case 0 : break;
             case 1 : ReConnecter();break;
             case 2 : ReConnecterN(ServerNum);break;
             case 3 : ReLogin(1);break;
             case 4 : ReLogin2();break;
             default : break;
           }
         }
         //正文=AccountCompany()+"平台"+"账号为:"+AccountNumber()+"的账户";
         if (声音报警==true) Alert("从服务器上获取行情数据超时");
         if (邮件报警==true && MailTime+MailSleep<TimeLocal())
         { 
           SendMail("从服务器上获取行情数据超时",正文+"\n从服务器上获取行情数据超时及时处理\n"+邮件标记字符串);
           MailTime=TimeLocal();
         }
         Sleep(检测间隔*1000);
       }
     }
     if(IsConnected()==false)
     {
       if (LoginTime+LoginSleep<TimeLocal())
       {
          LoginTime=TimeLocal();
          switch(重连方式)
          {
            case 0 : break;
            case 1 : ReConnecter();break;
            case 2 : ReConnecterN(ServerNum);break;
            case 3 : ReLogin(1);break;
            case 4 : ReLogin2();break;
            default : break;
          }
       }
       //正文=AccountCompany()+"平台"+"账号为:"+AccountNumber()+"的账户";
       if (声音报警==true) Alert("服务器断线");
       if (邮件报警==true && MailTime+MailSleep<TimeLocal()) 
       {
         SendMail("服务器连接中断",正文+"\n与服务器连接中断请及时处理\n"+邮件标记字符串);
         MailTime=TimeLocal();
       }
       Sleep(检测间隔*1000);
     }
     Sleep(1*1000);
   }
   Comment("");
   return(0);
}
//+------------------------------------------------------------------+
 

一个帖子贴不下

//+------------------------------------------------------------------+
//| script program ReLogin function                                  |
//+------------------------------------------------------------------+
//在位于窗口的行数数我的最爱标签导航帐户连接
void ReLogin( int Num )
{
   int hwnd = WindowHandle(Symbol(), Period()); //当前图表的句柄 (Handle)
   int hwnd_parent = 0;
   while (!IsStopped())//如果没有接到脚本终止命令则一直运行
   {
      hwnd = GetParent(hwnd);//当前图表的父窗口句柄(Handle)
      if (hwnd == 0) break;  //句柄等于0退出while循环
      hwnd_parent = hwnd;
   }
   if (hwnd_parent != 0)  //获得收藏夹句柄
   {
     hwnd = GetDlgItem(hwnd_parent, 0xE81C); //59420 查找导航窗口句柄
     hwnd = GetDlgItem(hwnd, 0x52);          //82    选择导航窗口 *
     hwnd = GetDlgItem(hwnd, 0x8A70);        //35440 获得收藏夹窗口句柄
     PostMessageA(hwnd, WM_KEYDOWN, VK_HOME,0); //选择导航收藏夹第一个账号,上线最喜爱的收藏夹窗口导航
     while (Num > 1)  
     {
       PostMessageA(hwnd, WM_KEYDOWN,VK_DOWN, 0); //转移到所需行
       Num--;
     }
     PostMessageA(hwnd, WM_KEYDOWN, VK_ENTER, 0);  //输出enter 打开登录窗口
     Sleep(PAUSE);                                 // 等待1秒
     hwnd = GetLastActivePopup(hwnd_parent);       //选择登录窗口   
     //GetLastActivePopup**该函数确定指定窗口中的哪一个弹出式窗口是最近活动的窗口。
     PostMessageA(hwnd, WM_KEYDOWN, VK_ENTER, 0);  //输出enter 确认登录
   }

	return;
}
//+------------------------------------------------------------------+
//| script program Login2 function                                   |
//+------------------------------------------------------------------+
void ReLogin2()
{
  //导航窗口中第一个账号登录;
  int hwnd = WindowHandle(Symbol(), Period());
  int hwnd_parent = 0;
  while (!IsStopped())
  {
     hwnd = GetParent(hwnd);
     if (hwnd == 0) break;
     hwnd_parent = hwnd;
  }
  if (hwnd_parent != 0)  // 
  {
    PostMessageA(hwnd_parent, WM_COMMAND, 35429, 0); // 
    Sleep(PAUSE);                             // 
    hwnd = GetLastActivePopup(hwnd_parent);   // 
    PostMessageA(hwnd, WM_KEYDOWN, VK_ENTER, 0); // 
  }
  return;
}
//+------------------------------------------------------------------+
//| script program ReConnecter function                              |
//+------------------------------------------------------------------+
void ReConnecter()
{
  int hwnd = WindowHandle(Symbol(), Period()); //当前图表的句柄 (Handle)
  int hwnd_parent = 0;
  while (!IsStopped())//如果没有接到脚本终止命令则一直运行  获得mt4进程句柄
  {
    hwnd = GetParent(hwnd);//当前图表的父窗口句柄(Handle)
    if (hwnd == 0) break;  //句柄等于0退出while循环
    hwnd_parent = hwnd;
  }
  //int hMetaTrader = GetAncestor(WindowHandle(Symbol(),Period()),2); 
  if (hwnd_parent != 0) 
  {
    SendMessageA (hwnd_parent, WM_COMMAND, 37400, 0);//发送扫描服务器命令;
  }
  return;
}
//+------------------------------------------------------------------+
//| script program ReConnecter function                              |
//+------------------------------------------------------------------+
void ReConnecterN(int S_Num)
{
  int hwnd = WindowHandle(Symbol(), Period()); //当前图表的句柄 (Handle)
  int hwnd_parent = 0;
  while (!IsStopped())//如果没有接到脚本终止命令则一直运行  获得mt4进程句柄
  {
    hwnd = GetParent(hwnd);//当前图表的父窗口句柄(Handle)
    if (hwnd == 0) break;  //句柄等于0退出while循环
    hwnd_parent = hwnd;
  }
  //int hMetaTrader = GetAncestor(WindowHandle(Symbol(),Period()),2); 
  if (hwnd_parent != 0) 
  {
    int Sdig=33900+S_Num;
    Sdig=SendMessageA (hwnd_parent, WM_COMMAND, Sdig, 0);//发送扫描服务器命令;
    Print("Sdig=",Sdig);
  }
  return;
}
//+------------------------------------------------------------------+
 
xfxyldj 写道 >>
我做了个多种登录模式的重连脚本。EA暂时没弄。有空交流下。

“多种登陆模式”,如何理解?

除了“导航器-->MetaTrade-->账号”,还有其它方式吗?

 

1、导航-->收藏夹-->第一个账号登陆

2、导航-->常用 -->第一个账号登陆

3、发送扫描服务器指令重连指令(软件右下角)

4、发送指定序列中某个编号的服务器重连指令(软件右下角)

 

另外我觉得最好不要用EA来弄,EA在启动MT4的时候就开始工作了。这时候MT4还没有正常登陆可能会造成发送重新登录请求过于频繁。

如果要用EA来自动登录,我觉得需要解决一个控制问题。

1、重新启动MT4后等待MT4正常连接后再运行检测和自动重连工作。

 

我是这样想的。

static datetime next_connect;

int init() {
	next_connect = TimeCurrent() + 10;
}

int start() {
	datetime curr_time;

	curr_time = TimeCurrent();
	if (next_current < curr_time) {
		// 重新连接mt4服务器.
		... ...
		next_current = curr_time + 10;
	}
	... ...
}

用next_connect来定义,每隔10分钟重新连一次服务器。

只需要ea的start()中增加一次判断,就可以实现定时重新登陆的功能。

这样处理后,即使电脑重新启动,也可以继续执行ea。减少人工操作的步骤。

 
xfxyldj 写道 >>

1、导航-->收藏夹-->第一个账号登陆

2、导航-->常用 -->第一个账号登陆

3、发送扫描服务器指令重连指令(软件右下角)

4、发送指定序列中某个编号的服务器重连指令(软件右下角)


刚刚将做ea交易的账号加入了“收藏夹”。谢谢!

还需要时间去理解你的程序。由于很少在windows下编程,所以对win32api不怎么了解。

 
xfxyldj 写道 >>

另外我觉得最好不要用EA来弄,EA在启动MT4的时候就开始工作了。这时候MT4还没有正常登陆可能会造成发送重新登录请求过于频繁。

如果要用EA来自动登录,我觉得需要解决一个控制问题。

1、重新启动MT4后等待MT4正常连接后再运行检测和自动重连工作。

只有mt4系统将新的报价发到ea后,ea才能够启动运行。因此,个人认为ea中不需要检测mt4是否已经登陆的问题。

当然了,如果是其它系统就不能这样做了。这是因为,mt4将行情服务器和交易服务器设置在一起。其它软件的设置,基本上都不在一起。

因此,在其它交易商中交易时,需要检测与交易服务器连接是否正常的问题。

 
y2k_connect 写道 >>

只有mt4系统将新的报价发到ea后,ea才能够启动运行。因此,个人认为ea中不需要检测mt4是否已经登陆的问题。

当然了,如果是其它系统就不能这样做了。这是因为,mt4将行情服务器和交易服务器设置在一起。其它软件的设置,基本上都不在一起。

因此,在其它交易商中交易时,需要检测与交易服务器连接是否正常的问题。

我想你的逻辑不对。

在自动断线检测重连这个问题中,是要解决断线或者假死(没有行情数据到达)然后让系统重现登录下。

而当断线或者假死的时候是没有tick到达的,因此tick出发的start()函数就不能运行了,这样里面的代码就不可能再执行了。

所以必须把检测和重连的代码放在

while(IsStopped()==false)

这个死循环里面才可以。不然都没有tick了,EA根本就不执行任何代码了