请观看如何免费下载自动交易
请在Facebook上找到我们!
加入我们粉丝页
有趣的脚本?
因此发布一个链接 -
让其他人评价
喜欢这个脚本? 在MetaTrader 5客户端尝试它
EA

一个简单的交易面板 - MetaTrader 4EA

显示:
12121
等级:
(1)
已发布:
O_C_Panel.mq4 (41.85 KB) 预览
MQL5自由职业者 需要基于此代码的EA交易或指标吗?请在自由职业者服务中订购 进入自由职业者服务
MQL编程学习——制作按钮和输入框

开仓涉及到仓位大小,因此,需再创建一个输入框,用于输入手数。参考MT4自带面板,手数框也需要调整大小,故在左右两侧各添加一个“增加”、“减小”手数的调整按钮。

考虑到常规都是右手操作鼠标,右下操作相对会较方便。因此,将面板固定在右下角。同时,创建一个“显示/隐藏”此交易面板按钮。点击后隐藏面板,再次点击后显示面板。如下图示。



下面我们来说创建过程。

第一步:要在MT4上创建对象(按钮、输入框等都是对象,一个按钮就是一个对象),我们得先知道每个对象所必须的基本属性。比如,大小(想要弄多大?),坐标(放在哪里?),其上的字符文本(包括字体、字号、颜色等),背景颜色,边框颜色,以及边框样式,……等等,详细请看MQL帮助文件。下图是按钮创建示例所在的帮助文件的目录位置及相关创建过程。

 

把本页内容看完,你就知道创建一个按钮需要提供哪些数据了。

MT4的对象的大小以像素计数。在图表上,当图表角定义为左上开始时,从左上往右下,左上为(0,0),右下为最大(X,Y),相当于咱们学数学里的直角坐标系第4象限。如果图表角定义为右下开始,则相当于第2象限。

图表角参数,帮助文件里有。图表角定义从哪个角开始,决定该对象的坐标以哪个角为起点开始算。如下图

明白了第1步,接下来。

第二步,整体设计构想。要创建如图所示的面板。首先,计划大小。先预计宽高为200*80吧(完了以后觉得不好看,不合适,回头再调整修改数据即可)。从下往上,分3行。

第1行,设计2按钮,用于开SELL和开BUY。

第2行,4个按钮和1个输入框,第1个开SELL,第2个用于减小手数,第3个增加手数,第4个开BUY,输入框用于直接输入手数值。

第3行,3个按钮,分别用于平SELL,全平,平BUY。

最下方,创建一个符号“▼”,来处理“显示/隐藏此面板”。总共11个对象,其中10个按钮,1个编辑框。

第三步,整体构想设计好了,开始干。

打开MQL编辑器,创建“EA模板”,勾选“事件函数”(要实现点击相应按钮或输入手数等数据后执行设计的功能(开仓、平仓等),MT4规定是由事件函数负责处理,即 所有相关功能的代码必须写在“事件函数”里),如下图

 

当然,不勾选的话,以后,直接键入此函数或从其它地方复制过来也一样。

为了不与其它EA或在MT4直接开的单相互影响,定义一个魔术号来单独处理使用本面板的订单。

input int mMagic =88888; //订单识别号

高度分配。对面板整个3行对象的高度进行分配,第1行32,2行22,3行26,总共80。同时设计对象之间的间隙大小 。顺便考虑到适应不同电脑显示像素的适应性,增加scale校正DPI。如下

double scale=NormalizeDouble(TerminalInfoInteger(TERMINAL_SCREEN_DPI)/96.0,2);//
#define CONTROLS_GAP_X                      ((2*scale)<2?2:(int)(2*scale))       // X坐标间隙
#define CONTROLS_GAP_Y                      ((2*scale)<1?2:(int)(2*scale))       // Y坐标间隙
#define BUTTON_HEIGHT                       ((int)(32*scale))      // 
#define BUTTON_HEIGHT1                      ((int)(22*scale))      //
#define BUTTON_HEIGHT2                      ((int)(26*scale))      //

给11个对象分别起名字。随便起,只要能便于识别,程序中不同的地方用它的时候别搞错就行。下面是10个按钮名。

//---按钮事件名
string ButtonEventStr[1],
       ButtonEventStr0[]=
  {
   "_bid", "_ask","offon", 
   "_xsel","_dn", "_up", "_xbuy",
   "_csel","_cselbuy","_cbuy"
  };

为了将此面板 与其它EA或指标区别开来(避免同名,造成功能错乱或误删等操作),将以本图表ID号和自定义字符(示例中为“ABC_”)为所有按钮对象名前缀。

string Prefix="";
//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
  
   Prefix=IntegerToString(ChartID())+"ABC_";
   
   int str_size=ArraySize(ButtonEventStr0);
   ArrayResize(ButtonEventStr,str_size);
   for(int i=0; i<str_size; i++)
      ButtonEventStr[i]=ButtonEventStr0[i];
   AddPrefix(Prefix,ButtonEventStr);
   
  
   
//---
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//| 为所有对象名称添加前缀                               
//+------------------------------------------------------------------+
void AddPrefix(string aprefix,string &object_names[])
  {
   int object_size=ArraySize(object_names);
   for(int i=0; i<object_size; i++)
   {
      object_names[i]=aprefix+object_names[i];
   }
  } 


按钮名设计为数组,是为了在处理按钮事件时方便阅读,结构清晰。

设计起点和宽度,字号。一切准备完成。 编写CreateBtn()函数实现 创建按钮。

int    X,Y,Width=0;
int    Fonts=9;    //字号
......
//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
......
//---创建按钮对象
   X=(int)(4*scale);       //起点坐标X
   Y=(int)(10*scale);      //起点坐标Y
   Width=(int)(200*scale); //宽度
......
   
   CreateBtn(Prefix,X,Y,Width,Fonts); //创建按钮
   
//---
   return(INIT_SUCCEEDED);
  }

从第1行第1个按钮开始。在右下角同时创建“显示/隐藏面板”按钮。


    
        
        
        
        
bool CreateBtn(string p_fix,int x1,int y1,int width,int fonts,
               ENUM_BASE_CORNER corner=CORNER_RIGHT_LOWER,
               color            clr   =clrWhite)
 {
        string obj_name="";
        int x=0,y=0;
        int sel_w=0,dn_w=(int)(20*scale),updn_w=0,buy_w=0; //第一行宽度

//---第1行 2个按钮,开空和开多
   int temp_w=(width-CONTROLS_GAP_X)/2;
   x=x1+width;
   y=y1+BUTTON_HEIGHT;
   obj_name=p_fix+"_bid";
   if(!CreateButton(obj_name,x,y,DoubleToString(Bid,Digits),fonts+5,corner,clr,temp_w,BUTTON_HEIGHT))   return(false); 
        ObjectSetString(0,obj_name,OBJPROP_TOOLTIP,"点击现价开仓SELL订单");
   
   x=x1+width-(CONTROLS_GAP_X+temp_w);
   obj_name=p_fix+"_ask";
        if(!CreateButton(obj_name,x,y,DoubleToString(Ask,Digits),fonts+5,corner,clr,temp_w,BUTTON_HEIGHT))   return(false); 
   ObjectSetString(0,obj_name,OBJPROP_TOOLTIP,"点击现价开仓BUY订单");
//---右下角创建“显示/隐藏面板”按钮
   obj_name=p_fix+"offon";
        if(!CreateButton(obj_name,2*(int)(10*scale),y1,"▼",fonts,corner,clrLightGray,(int)(10*scale),(int)(10*scale)))   return(false); 
        ObjectSetInteger(0,obj_name,OBJPROP_BGCOLOR,clrNONE);
        ObjectSetInteger(0,obj_name,OBJPROP_BORDER_COLOR,clrNONE);
   ObjectSetString(0,obj_name,OBJPROP_TOOLTIP,"显示交易面板");    
//---第2行 
        temp_w=(width-2*CONTROLS_GAP_X-2*dn_w);
        sel_w=temp_w/3;  
        updn_w=temp_w-(2*sel_w); 
        y=y1+BUTTON_HEIGHT+BUTTON_HEIGHT1;
//--- SELL BID按钮
        x=x1+width; 
        obj_name=p_fix+"_xsel";
   if(!CreateButton(obj_name,x,y,"SELL",fonts,corner,clr,sel_w,BUTTON_HEIGHT1))   return(false); 
   ObjectSetString(0,obj_name,OBJPROP_TOOLTIP,"点击现价开仓SELL订单"); 
//--- "▼"按钮   
   x=x1+width-(CONTROLS_GAP_X+sel_w);
   obj_name=p_fix+"_dn";
   if(!CreateButton(obj_name,x,y,"▼",fonts,corner,clrBlack,dn_w,BUTTON_HEIGHT1))   return(false); 
   ObjectSetInteger(0,obj_name,OBJPROP_BGCOLOR,clrLightGray);
   ObjectSetInteger(0,obj_name,OBJPROP_BORDER_COLOR,clrNONE);
   ObjectSetString(0,obj_name,OBJPROP_TOOLTIP,"减少手数");
//--- LOTS
   x=x1+width-(CONTROLS_GAP_X+sel_w+dn_w);
   obj_name=p_fix+"_edit_updn";
   if(!CreateEdit(obj_name,x,y,DoubleToString(m_lots,2),fonts+2,corner,clrBlack,updn_w,BUTTON_HEIGHT1))   return(false); 
       
//--- "▲"按钮
   x=x1+width-(CONTROLS_GAP_X+sel_w+dn_w+updn_w);
   obj_name=p_fix+"_up";
   if(!CreateButton(obj_name,x,y,"▲",fonts,corner,clrBlack,dn_w,BUTTON_HEIGHT1))   return(false); 
   ObjectSetInteger(0,obj_name,OBJPROP_BGCOLOR,clrLightGray);
    ObjectSetInteger(0,obj_name,OBJPROP_BORDER_COLOR,clrNONE);
    ObjectSetString(0,obj_name,OBJPROP_TOOLTIP,"增加手数");
//--- BUY ASK按钮
   x=x1+width-(2*CONTROLS_GAP_X+sel_w+2*dn_w+updn_w);
   obj_name=p_fix+"_xbuy";
   if(!CreateButton(obj_name,x,y,"BUY",fonts,corner,clr,sel_w,BUTTON_HEIGHT1))   return(false); 
   ObjectSetString(0,obj_name,OBJPROP_TOOLTIP,"点击现价开仓BUY订单");  
//---第3行
        temp_w=(width-2*CONTROLS_GAP_X);
        sel_w=temp_w/3;  
        updn_w=temp_w-(2*sel_w); 
        y=y1+CONTROLS_GAP_Y+BUTTON_HEIGHT+BUTTON_HEIGHT1+BUTTON_HEIGHT2;
//--- 平空
        x=x1+width; 
        obj_name=p_fix+"_csel";
   if(!CreateButton(obj_name,x,y,"平 空",fonts+2,corner,clrBlack,sel_w,BUTTON_HEIGHT2))   return(false); 
   ObjectSetInteger(0,obj_name,OBJPROP_BGCOLOR,clrLightGray);
   ObjectSetString(0,obj_name,OBJPROP_TOOLTIP,"平仓SELL订单"); 
//--- 全 平
        x=x1+width-(CONTROLS_GAP_X+sel_w); 
        obj_name=p_fix+"_cselbuy";
   if(!CreateButton(obj_name,x,y,"全 平",fonts+2,corner,clrBlack,updn_w,BUTTON_HEIGHT2))   return(false); 
   ObjectSetInteger(0,obj_name,OBJPROP_BGCOLOR,clrLightGray);
   ObjectSetString(0,obj_name,OBJPROP_TOOLTIP,"全平所有EA订单"); 
//--- 平多
        x=x1+width-(2*CONTROLS_GAP_X+sel_w+updn_w);  
        obj_name=p_fix+"_cbuy";
   if(!CreateButton(obj_name,x,y,"平 多",fonts+2,corner,clrBlack,sel_w,BUTTON_HEIGHT2))   return(false); 
   ObjectSetInteger(0,obj_name,OBJPROP_BGCOLOR,clrLightGray);
   ObjectSetString(0,obj_name,OBJPROP_TOOLTIP,"平仓BUY订单"); 
   
        return(true); 
 }

CreateButton()函数负责创建一个按钮对象,可以从帮助文档复制过来,对一些属性按本例的需要进行修正一下即可。 第2行,第3行按钮按同样方法创建。

至此,10个按钮和1个编辑 框创建完毕。

上述创建对象工作只需在初始化函数OnInit()里做就行。因为,这些东西只需创建一次即可,以后在行情不断变化时,无需再次创建这些按钮等,也没必要频繁检测是否创建。

第四步,按钮实现。即,例如:当点击第1行第1个按钮(左边)时,要实现开仓SELL单,……。这个事需要在OnChartEvent()函数里实现。

首先,我们先处理编辑框。当我们在此编辑框输入一个数值时,程序读取这个数值,并规范为标准格式的手数形式,保存在m_lots变量中待用。

然后,处理按钮。10个按钮,有点多,全部代码写在这感觉不好看,我们设计一个函数E_Obj_Button(sparam),将点击事件来的常量 传递过去,单独分别处理这10个按钮事件。

//+------------------------------------------------------------------+
void OnChartEvent(const int id,
                  const long &lparam,
                  const double &dparam,
                  const string &sparam)
{
        if(id==CHARTEVENT_OBJECT_ENDEDIT)
        {   
      string spm=Prefix+"_edit_updn"; //我们的编辑框
      if(sparam==spm)//当用户修改了此编辑框数据
      {
        string text="";
        if(!ObjectGetString(0,spm,OBJPROP_TEXT,0,text)) return;  //获取编辑框字符,如果获取失败则返回
        m_lots=NormalizeDouble(StrToDouble(text),mdlots);  //规范为平台标准化手数格式并保存
        if(m_lots<mminlot) m_lots=mminlot; //小于平台规定的最小手数,按最小手数算
        if(m_lots>mmaxlot) m_lots=mmaxlot; //大了,按最大手数算
        ObjectSetString(0,spm,OBJPROP_TEXT,DoubleToString(m_lots,mdlots)); //重新显示在面板中
      }
        }
//---按钮事件
        if(id==CHARTEVENT_OBJECT_CLICK)
   {
      E_Obj_Button(sparam); //按钮事件处理函数
        }
}

E_Obj_Button(sparam) 函数 实现如下

//+-------------------------------------------------------------------------------------+
//| 处理按钮事件                                                                        |
//+-------------------------------------------------------------------------------------+
void E_Obj_Button(string spm)
  {
   int size=ArraySize(ButtonEventStr);
   int e=0;
   while(e<size)
     {
      if(ButtonEventStr[e]==spm)   break;
      e++;
     }
   
   int iticket=0;
        double ilots=0.0;
        
   switch(e)
   {
      case 0://       //开空
                        ilots=NormalizeDouble(m_lots,mdlots);
         iticket=OrderSend(Symbol(),OP_SELL,ilots,Bid,3,0,0,"MyEA_Btn",mMagic,0,clrRed);
                if(iticket<=0) 
                Print("开sell单失败。错误 #",GetLastError()); 
         ObjectSetInteger(0,spm,OBJPROP_STATE,false);   return ;
         break;
      case 1://       //开多1
                        ilots=NormalizeDouble(m_lots,mdlots);
         iticket=OrderSend(Symbol(),OP_BUY,ilots,Ask,3,0,0,"MyEA_Btn",mMagic,0,clrBlue);
                if(iticket<=0) 
                Print("开buy单失败。错误 #",GetLastError()); 
         ObjectSetInteger(0,spm,OBJPROP_STATE,false);   return ;
         break;
      case 2://
         if(ObjectGetInteger(0,spm,OBJPROP_STATE)==true)
         {
                HideObject(Prefix+"_");
                ObjectSetInteger(0,spm,OBJPROP_STATE,true);
         }
         else{ 
            ShowObject(Prefix+"_");
            ObjectSetInteger(0,spm,OBJPROP_STATE,false);
         }
         break;
      case 3://       //开空
                        ilots=NormalizeDouble(m_lots,mdlots);
         iticket=OrderSend(Symbol(),OP_SELL,ilots,Bid,3,0,0,"MyEA_Btn",mMagic,0,clrRed);
                if(iticket<=0) 
                Print("开sell单失败。错误 #",GetLastError()); 
         ObjectSetInteger(0,spm,OBJPROP_STATE,false);   return ;
         break;         
                case 4://减少手数
         ilots=m_lots-mstep;
         if(ilots<mminlot) ilots=mminlot;
         ObjectSetString(0,Prefix+"_edit_updn",OBJPROP_TEXT,DoubleToString(ilots,mdlots)); 
         m_lots=ilots;   
         ObjectSetInteger(0,spm,OBJPROP_STATE,false);
         break;
                case 5://增加手数
         ilots=m_lots+mstep;
         if(ilots>mmaxlot) ilots=mmaxlot;
         ObjectSetString(0,Prefix+"_edit_updn",OBJPROP_TEXT,DoubleToString(ilots,mdlots));
         m_lots=ilots;   //保存
         ObjectSetInteger(0,spm,OBJPROP_STATE,false);
          break;
      case 6://       //开多
                        ilots=NormalizeDouble(m_lots,mdlots);
         iticket=OrderSend(Symbol(),OP_BUY,ilots,Ask,3,0,0,"MyEA_Btn",mMagic,0,clrBlue);
                if(iticket<=0) 
                Print("开buy单失败。错误 #",GetLastError()); 
         ObjectSetInteger(0,spm,OBJPROP_STATE,false);   return ;
         break;
      case 7: //       //平空 
         if(!CloseOrderAll(OP_SELL,mMagic,"用户关闭所有sell单"))
            Print("关闭所有SELL订单失败。Err:",GetLastError());        
         Sleep(100);
         ObjectSetInteger(0,spm,OBJPROP_STATE,false);   return ;
         break; 
      case 8: //全部平仓
                        if(!CloseOrderAll(-1,mMagic,"用户关闭所有EA单"))
            Print("关闭所有EA订单失败。Err:",GetLastError());        
         Sleep(100);
         ObjectSetInteger(0,spm,OBJPROP_STATE,false);   return ;
         break;
      case 9: //平 多
                        if(!CloseOrderAll(OP_BUY,mMagic,"用户关闭所有buy单"))
            Print("关闭所有SELL订单失败。Err:",GetLastError());        
         Sleep(100);
         ObjectSetInteger(0,spm,OBJPROP_STATE,false);   return ;
         break;

     }

  }

第五步,参考MT4自带面板形式,当行情价格上涨或下跌时,开仓按钮呈红、蓝色变化。这个当然得在主函数OnTick()里处理了。我们用一个函数RefPanelBidAsk()来处理它。全部代码一行一行写在这也行,只是如果主函数还有其它功能实现代码,全弄在这有些不耐读。

第六步,去初始化。EA退出或切换图表周期 时,删除咱们所创建的所有相关对象。

//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
//---
   DeleteAll(Prefix);//删除所有相关对象
  }
//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
  {
//---刷新面板实时Bid/Ask价格
   RefPanelBidAsk();
//---
   RefOrders();
   


   
  }

删除函数如下

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void DeleteAll(string prefix)
 { 
   ObjectsDeleteAll(0,prefix,0,OBJ_BUTTON);
   ObjectsDeleteAll(0,prefix,0,OBJ_EDIT);
      
 }

RefPanelBidAsk()  函数实现刷新面板实时价格,并根据价格变化显示不同的颜色。

//+------------------------------------------------------------------+
//|刷新面板实时价格                                                  |
//+------------------------------------------------------------------+
void RefPanelBidAsk()
  {
   if(Bid>=StrToDouble(ObjectGetString(0,Prefix+"_bid",OBJPROP_TEXT,0)))
     {
      ObjectSetInteger(0,Prefix+"_bid",OBJPROP_BGCOLOR,clrMediumBlue) ;
      ObjectSetInteger(0,Prefix+"_xsel",OBJPROP_BGCOLOR,clrMediumBlue) ;
     }
   else
     {
      ObjectSetInteger(0,Prefix+"_bid",OBJPROP_BGCOLOR,clrCrimson) ;
      ObjectSetInteger(0,Prefix+"_xsel",OBJPROP_BGCOLOR,clrCrimson) ;
     }
   ObjectSetString(0,Prefix+"_bid",OBJPROP_TEXT,DoubleToString(Bid,Digits));
   
   if(Ask>=StrToDouble(ObjectGetString(0,Prefix+"_ask",OBJPROP_TEXT,0)))
     {
      ObjectSetInteger(0,Prefix+"_ask",OBJPROP_BGCOLOR,clrMediumBlue) ;
      ObjectSetInteger(0,Prefix+"_xbuy",OBJPROP_BGCOLOR,clrMediumBlue) ;
     }
   else
     {
      ObjectSetInteger(0,Prefix+"_ask",OBJPROP_BGCOLOR,clrCrimson) ;
      ObjectSetInteger(0,Prefix+"_xbuy",OBJPROP_BGCOLOR,clrCrimson) ;
     }
   ObjectSetString(0,Prefix+"_ask",OBJPROP_TEXT,DoubleToString(Ask,Digits));

  }

按设计,当点击“显示/隐藏交易面板”按钮时,要相应隐藏或显示面板。使用ShowObject()和HideObject()实现。

//+------------------------------------------------------------------+
//|设置对象可见                               |
//+------------------------------------------------------------------+
void ShowObject(string pfix,ENUM_OBJECT objtype=-1)
{
   int objtotal=ObjectsTotal();
   string objname="";
   for(int i=objtotal-1;i>=0;i--)
   {
      objname=ObjectName(0,i);
      if(StringFind(objname,pfix)>=0 && (objtype==-1 || (objtype!=-1 && ObjectType(objname)==objtype)))
      {
         ObjectSetInteger(0,objname,OBJPROP_TIMEFRAMES,OBJ_ALL_PERIODS);
      }
   }

}
//+------------------------------------------------------------------+
//|设置对象不可见                               |
//+------------------------------------------------------------------+
void HideObject(string pfix)
{
   int objtotal=ObjectsTotal();
   string objname="";
   for(int i=objtotal-1;i>=0;i--)
   {
      objname=ObjectName(0,i);
      if(StringFind(objname,pfix)>=0)
      {
         ObjectSetInteger(0,objname,OBJPROP_TIMEFRAMES,OBJ_NO_PERIODS);
      }
   }

}


至此,所有工作完成 。

有兴趣的欢迎讨论交流。完整源码文件 于附件中,可直接使用。


结论 实现批量开平仓

…如果将订单魔术号置0,则将包含在图表上手动开仓单。


K线显示本地时间 K线显示北京时间 K线显示本地时间 K线显示北京时间

K线显示本地时间 K线显示北京时间

黄金交易自动跟随,批量下单,批量修改止损 黄金交易自动跟随,批量下单,批量修改止损

提供止损和止盈,仓位的配置,个人主要是用于30分钟图,跟着趋势批量下单

EMA均线趋势系统-MT4版2025 EMA均线趋势系统-MT4版2025

这段代码是一个多周期 EMA 指标,同时带有均线汇聚信号提示。它计算了多个不同周期的 EMA,然后检查这些 EMA 是否汇聚,并根据汇聚情况生成交易信号。

价格比较 价格比较

优雅而时髦的双值 "价格 "比较。