是否可以从另一台 PC 远程控制一台 PC 上的 EA?

Is it possible to remotely control an EA on a PC from another PC?

我有一个 EA(即机器人),我希望人们在他们的 PC 上安装它。是否可以根据他们的订阅条件从我的 PC 激活或停用它?说明:如果 A 先生订阅了这个月,我将是从我的 PC 上激活它的人。这意味着,即使 EA 在他们的系统上,他们也无法激活或停用它。

您可以使用您的 PC(需要确保您拥有静态 IP 地址,然后启动 Web 应用程序)。更简单的是租一个VPS。然后,创建一个简单的网络应用程序,可能使用 Django (Python) 或 PHP,带有 REST 网络服务和管理面板。

EA端:编译每个机器人,EX4文件提供给客户端。您的客户将 http://yourwebsite.org/ 添加到 MT4 中允许的 url 列表中,然后使用 EA。当 EA 附加到图表时,调用 OnInit() 函数,在该块内使用 WebRequest() 函数让您的 EA 联系您的网站并询问它是否可以工作(可能客户端可能会传递登录名和密码,或者帐号和经纪人名称(如果您愿意,可以使用客户名称)。网络服务器接收该数据并进行验证。

另一个问题是如何让你的EA工作一段时间。最简单的方法是每天用相同的验证请求调用网络服务器一次(随机时间似乎更好)。如果验证失败 - EA 停止工作。

最后,考虑一下您将如何停用您的 EA...EA 可能会打开大量交易和挂单,如果它用 ExpertRemove() 自杀,这些交易将保留在MT4。因此,最好通知客户 EA 不再活跃,并遵循现有订单,尽可能在收支平衡时关闭所有订单,或其他取决于您的 EA 逻辑的解决方案。以下代码对一些客户有效,没有任何投诉,欢迎您使用它(使用您的超级管理员、密码(如果需要)和域名)。

class CLicenseOnline : public CObject
  {
private:
   string               m_login;
   string               m_password;
   datetime             m_nextCheck;
   int                  m_prevResult;
   string               m_url;
   int                  m_strategyId;
public:
                    CLicenseOnline(const string login,const string password,const int id):
                                m_login(login),m_password(password),m_nextCheck(0),m_strategyId(id),m_prevResult(-1)
 {

  bool isCheckingRequired=false;
  if(CLicenseOnline::isSuperAdmin(login,password))
    {
     printf("%i %s - Hello, SUPER ADMIN!",__LINE__,__FILE__);
     isCheckingRequired=true;
    }
  isCheckingRequired= isCheckingRequired || IsTesting();
  if(isCheckingRequired)
    {
     m_nextCheck=INT_MAX;
     m_prevResult=1;
    }
  else
    {
     m_url=CLicenseOnline::genUrl(login,password);
    }
 }
                   ~CLicenseOnline(){}
   int                  check()
 {
  if(TimeCurrent()>m_nextCheck)
    {
     int result=this.checkMain();
     switch(result)
       {
        case 1: m_nextCheck=this.generateNextDate();    m_prevResult=1;break;
        default:
        case 0: m_nextCheck=TimeCurrent()+PeriodSeconds(PERIOD_M1);  m_prevResult=0;break;
        case-1: m_nextCheck=TimeCurrent()+PeriodSeconds(PERIOD_H1);  m_prevResult=-1;break;
       }
    }
  return(m_prevResult);
 }
    static string           genUrl(const string login,const string password)
 {
  const string http="localhost";
  return(StringFormat("http://%s/verify/?Login=%s&&Password=%s&&Check=%d",http,login,password,2147483647));
 }
    static string           getHttpResponce(const string url)
 {
  char data[],res[];
  string cookies=NULL, headers=NULL,result;
  ResetLastError();
  int answer = WebRequest("GET",url,cookies,NULL,5000,data,0,res,headers);
  if(answer==200)
    {
     result = CharArrayToString(res);
     return(result);        
    }
  //printf("%i - result=%d|%s|size=%d; %d",__LINE__,answer,result,ArraySize(res),GetLastError());      
  return(NULL);
 }

private:
static bool             isSuperAdmin(const string login,const string password)
 {
  static string 
        superAdminLogin="Admin", 
        superAdminPassword="password";
           //ATTENTION! Edit the login and password here!
  return login==superAdminLogin && password==superAdminPassword;
 }
datetime             generateNextDate()const
 {
  return(iTime(_Symbol,PERIOD_D1,0)+PeriodSeconds(PERIOD_D1)+MathRand()%PeriodSeconds(PERIOD_D1));
 }
int                  checkMain()const
 {
  string respond=CLicenseOnline::getHttpResponce(m_url);
  if(respond==NULL)
     return(0);//try later
  CJAVal js(NULL,jtUNDEF);
  if(!js.Deserialize(respond))
    {
     printf("%i %s - failed to deserialize %s",__LINE__,__FUNCTION__,respond);
     return(-1);
    }
  int retCode=(int)js["key"].ToInt();
  switch(retCode)
    {
     case -1: Alert("incorrect password");return(0);
     case -2: Alert("incorrect key!");return(0);
     case -3: Alert("incorrect request method!");return(0);
     case -4: 
     case -5: Alert("no such login");return(0);
     default:
        Alert(StringFormat("%i %s - incorrect login/password/no such user!",__LINE__,__FUNCTION__));
        return(0);
     case 200:
       {
        CJAVal *valueJs=js["value"];
        if(!this.checkStatus(valueJs["Status"].ToStr()))
           return(-1);
        if(!this.checkAccount(
              (int)valueJs["Allow_account_1"].ToInt(),(int)valueJs["Allow_account_2"].ToInt(),(int)valueJs["Allow_account_3"].ToInt()))
           return -1;
        bool strategyX=(bool)valueJs["Allow_strategy_"+(string)m_strategyId].ToBool();
        if(!stategyX)
          {
           return(-1);
          }
        return(1);
       }
    }
  return(-1);
 }
bool                 checkStatus(const string status)const
 {      //printf("%i %s - status = |%s|%d",__LINE__,__FUNCTION__,status,IsDemo());
  if(status=="demo")
    {
     if(!IsDemo())
       {
        string message=StringFormat("your login %s is allowed to trade on Demo accounts only!",m_login);
        Alert(message);
        printf("%i %s - %s",__LINE__,__FILE__,message);
        return(false);
       }
     return(true);
    }
  if(status!="active")
    {
     string message=StringFormat("status of your login [%s] is [%s] so not allowed to trade!",m_login,status);
     Alert(message);
     printf("%i %s - %s",__LINE__,__FILE__,message);
     return(false);
    }
  return(true);
 } 
bool                 checkAccount(const int acc1,const int acc2,const int acc3)const
 {
  if(acc1==0 && acc2==0 && acc3==0)
     return(true);
  int currentAccount=AccountNumber();
  if(acc1==currentAccount || acc2==currentAccount || acc3==currentAccount)
     return(true);
  string message=StringFormat("allowed accounts are only %d, %d and %d, your account %d is not allowed!",acc1,acc2,acc3,currentAccount);
  Alert(message);
  printf("%i %s - %s",__LINE__,__FUNCTION__,message);
  return(false);
 }
};