外汇EA编写教程:MQL5食谱:处理自定义图表事件

介绍

本文从逻辑上继续文章MQL5 CookBook:处理典型的图表事件。它涵盖了自定义图表事件的工作方式。读者可以在这里找到定制的事件开发和处理例程。本文讨论的所有思想都是用面向对象的工具实现的。

自定义事件主题非常广泛,特别是当程序员和开发人员在他们的工作中引入想法时。

1。自定义图表事件

显然,这个事件是用户定义的。由程序员决定什么任务或模块可以作为事件的形式。MQL5开发人员可以创建自己的事件并扩展其语言功能以实现复杂的算法。

自定义事件是第二种可能的图表事件类型。第一类是典型事件。尽管文档中没有“典型图表事件”这样的术语,但我建议将其用于第一部分中的十种图表事件类型。

开发人员只建议枚举所有图表事件-枚举图表事件。

根据文档,自定义事件有65535个标识符。自定义事件的第一个和最后一个标识符由显式数字值chartevent_custom和chartevent_custom_last设置,数字对应于1000和66534(图例1)。

图例.1 自定义事件的第一个和最后一个标识符

传说。1自定义事件的第一个和最后一个标识符

简单计算,考虑到将生成第一个和最后一个标识符:66534-1000+1=65535。

在使用自定义事件之前,必须首先设置它们。从这个意义上说,开发人员成为未来EA中实现事件概念算法的规划者和作者。对自定义事件进行分类非常有用。这种认知方法不能消除歧义,但一定会降低歧义的程度,安排推理的路径。

让我们把这样一个自定义事件标准作为源。例如,开发人员Sergeev提出了一个自动化交易原型的想法。他把所有事件分为三组(传奇。2)。

图例.2 自定义事件源分组

传说。2自定义事件源分组

然后,根据这个主要思想,可以根据自己的组成员身份开发定制事件。

让我们试着从一些简单的事情开始。首先,我们采用第一组,包括指标事件。可以属于该组的事件有:创建和删除一个指标,接收打开和关闭仓库的信号。第二组包括修改订单和职位状态的事件。在我们的例子中,开仓和平仓都在这个组中。这很简单。最后,最难规范化的组是外部事件组。

让我们使用两个事件:启用和禁用手动事务。

图例.3 自定义事件源

传说。3自定义事件源

主要例子可以通过扣除(从一般到特殊)(利润)来确定。3)。稍后将使用此示例在相应的类(表1)中创建事件类型。

表格 1 自定义事件

表1定制事件

这个表还不能称为“事件概念”,但它是一个开始。这是另一种方式。众所周知,一个抽象的交易系统模型由三个子系统组成——基本模块(图例)。4)。

图例.4 抽象系统模型

数字。4抽象系统模型

基于“源”的自定义事件可以按事件生成条件分类:

  1. 信号子系统;
  2. 仓库跟踪子系统;
  3. 资金管理子系统。

例如,后者可能包括达到撤销许可级别、通过设置值增加交易量、增加损失限额百分比等事件。

2。图表事件处理器和生成器

以下行将专用于图表事件处理程序和生成器。对于处理自定义图表事件,其原理类似于处理一个典型的图表事件。

在处理器中,onchartevent()函数需要四个常量作为参数。显然,开发人员使用这个机制来实现事件识别并获得更多关于它的信息。在我看来,这是一个非常复杂的程序机制。

函数eventChartCustom()生成自定义图形事件。值得注意的是,创建的自定义图表事件可以同时用于图表“本身”和“外部人员”。在我看来,关于自我图表和外部图表影响的最有趣的文章是在MetaTrader5中实现一个多货币模型。

在我看来,有一个不一致的事实,即事件标识符在生成器中是ushort,在处理器中是int。在处理器中使用ushort数据类型也是合乎逻辑的。

三。自定义事件类

正如我前面提到的,事件的概念取决于EA开发人员。现在,让我们从表1继续讨论事件。首先,我们对自定义事件类ceventbase及其派生类(legend)进行排序。5)。

图例.5 事件类的层次结构

传说。5事件类
的层次结构

基本分类如下:

//+------------------------------------------------------------------+
//| Class CEventBase.                                                |
//| Purpose: base class for a custom event                           |
//| Derives from class CObject.                                      |
//+------------------------------------------------------------------+
class CEventBase : public CObject
  {
protected:
   ENUM_EVENT_TYPE   m_type;
   ushort            m_id;
   SEventData        m_data;

public:
   void              CEventBase(void)
     {
      this.m_id=0;
      this.m_type=EVENT_TYPE_NULL;
     };
   void             ~CEventBase(void){};
   //--
   bool              Generate(const ushort _event_id,const SEventData &_data,
                              const bool _is_custom=true);
   ushort            GetId(void) {return this.m_id;};

private:
   virtual bool      Validate(void) {return true;};
  };

事件类型由枚举事件类型枚举设置:

//+------------------------------------------------------------------+
//| A custom event type enumeration                                  |
//+------------------------------------------------------------------+
enum ENUM_EVENT_TYPE
  {
   EVENT_TYPE_NULL=0,      // no event
   //---
   EVENT_TYPE_INDICATOR=1, // indicator event
   EVENT_TYPE_ORDER=2,     // order event
   EVENT_TYPE_EXTERNAL=3,  // external event
  };

数据成员包括事件标识符和数据结构。

ceventBase类的generate()方法处理事件生成。getid()方法返回事件ID,虚方法validate()检查事件标识符的值。起初,我在类中包含了事件处理方法,但后来我意识到每个事件都是唯一的,而且这个抽象方法还不够。我最终放弃了向ceventprocessor类添加自定义事件处理的委托任务。

4。自定义事件处理器类

ceventProcessor类假定生成和处理八个给定的事件。类的数据成员如下:

//+------------------------------------------------------------------+
//| Class CEventProcessor.                                           |
//| Purpose: base class for an event processor EA                    |
//+------------------------------------------------------------------+
class CEventProcessor
  {
//+----------------------------Data members--------------------------+
protected:
   ulong             m_magic;
   //--- flags
   bool              m_is_init;
   bool              m_is_trade;
   //---
   CEventBase       *m_ptr_event;
   //---
   CTrade            m_trade;
   //---
   CiMA              m_fast_ema;
   CiMA              m_slow_ema;
   //---
   CButton           m_button;
   bool              m_button_state;
//+------------------------------------------------------------------+
  };

属性列表中还有初始化和事务标志。如果启动不正确,第一个标志不允许EA进行交易。第二个检查事务权限。

还有一个指向对象ceventbase类型的指针,用于多态性和不同类型的事件。ctrade类的实例可以访问事务操作。

CIMA类型对象易于处理从度量接收到的数据。为了简化程序,我使用了两个移动平均值作为要接收的事务信号。还有一个“cButton”类的实例,将用于手动启动/禁用EA。

分类依据“模块过程函数宏”的原理:

//+------------------------------------------------------------------+
//| Class CEventProcessor.                                           |
//| Purpose: base class for an event processor EA                    |
//+------------------------------------------------------------------+
class CEventProcessor
  {
//+-------------------------------Methods----------------------------+
public:
   //--- constructor/destructor
   void              CEventProcessor(const ulong _magic);
   void             ~CEventProcessor(void);

   //--- Modules
   //--- event generating
   bool              Start(void);
   void              Finish(void);
   void              Main(void);
   //--- event processing
   void              ProcessEvent(const ushort _event_id,const SEventData &_data);

private:
   //--- Procedures
   void              Close(void);
   void              Open(void);

   //--- Functions
   ENUM_ORDER_TYPE   CheckCloseSignal(const ENUM_ORDER_TYPE _close_sig);
   ENUM_ORDER_TYPE   CheckOpenSignal(const ENUM_ORDER_TYPE _open_sig);
   bool              GetIndicatorData(double &_fast_vals[],double &_slow_vals[]);

   //--- Macros
   void              ResetEvent(void);
   bool              ButtonStop(void);
   bool              ButtonResume(void);
  };

在模块中,只生成三个事件:开始是-开始(),结束是-完成(),主程序是-主程序()。第四个模块processEvent()既是事件处理器又是生成器。

4.1启动模块

此模块设计为在OnInit()处理器中调用。

//+------------------------------------------------------------------+
//| Start module                                                     |
//+------------------------------------------------------------------+
bool CEventProcessor::Start(void)
  {
//--- create an indicator event object
   this.m_ptr_event=new CIndicatorEvent();
   if(CheckPointer(this.m_ptr_event)==POINTER_DYNAMIC)
     {
      SEventData data;
      data.lparam=(long)this.m_magic;
      //--- generate CHARTEVENT_CUSTOM+1 event
      if(this.m_ptr_event.Generate(1,data))
         //--- create a button
         if(this.m_button.Create(0,"Start_stop_btn",0,25,25,150,50))
            if(this.ButtonStop())
              {
               this.m_button_state=false;
               return true;
              }
     }

//---
   return false;
  }

在此模块中创建指向事件对象的指针。稍后,将生成指针创建事件。最后创建一个按钮。它切换到停止模式。这意味着按下按钮后,EA将停止工作。

SeventData结构也参与了该方法的定义。这是一个将参数传递给自定义事件生成器的简单容器。这里只填写结构字段——它是一个长整型字段。它将保存EA的神奇数字。

4.2端模块

此模块假定应在OnDeInit()处理器中调用它。

//+------------------------------------------------------------------+
//| Finish  module                                                   |
//+------------------------------------------------------------------+
void CEventProcessor::Finish(void)
  {
//--- reset the event object
   this.ResetEvent();
//--- create an indicator event object
   this.m_ptr_event=new CIndicatorEvent();
   if(CheckPointer(this.m_ptr_event)==POINTER_DYNAMIC)
     {
      SEventData data;
      data.lparam=(long)this.m_magic;
      //--- generate CHARTEVENT_CUSTOM+2 event
      bool is_generated=this.m_ptr_event.Generate(2,data,false);
      //--- process CHARTEVENT_CUSTOM+2 event
      if(is_generated)
         this.ProcessEvent(CHARTEVENT_CUSTOM+2,data);
     }
  }

在这里,前一个事件指针归零,并生成“指示器丢失”事件。我必须指出,如果自定义事件是在OnDeInit()处理器中生成的,那么您将得到一个运行时错误4001(外部异常错误)。因此,此方法中执行的事件生成和处理都不会调用OnChartEvent()。

第三,使用SeventData结构存储EA的幻数。

4.3主程序模块

此模块假定应该在ontick()处理器中调用它。

//+------------------------------------------------------------------+
//| Main  module                                                     |
//+------------------------------------------------------------------+
void CEventProcessor::Main(void)
  {
//--- a new bar object
   static CisNewBar newBar;

//--- if initialized     
   if(this.m_is_init)
      //--- if not paused   
      if(this.m_is_trade)
         //--- if a new bar
         if(newBar.isNewBar())
           {
            //--- close module
            this.Close();
            //--- open module
            this.Open();
           }
  }

在此模块中调用过程open()和close()。第一个过程可以产生“接收打开位置信号”的事件和“接收关闭位置信号”的第二个事件。当新列出现时,模块的当前版本将完全正常工作。用于检测新列的类由KonstantinGruzdev描述。

4.4事件处理模块

此模块假定应在OnChartEvent()处理器中调用它。这个模块的大小和功能都是最大的。

//+------------------------------------------------------------------+
//| Process event module                                             |
//+------------------------------------------------------------------+
void CEventProcessor::ProcessEvent(const ushort _event_id,const SEventData &_data)
  {
//--- check event id
   if(_event_id==CHARTEVENT_OBJECT_CLICK)
     {
      //--- button click
      if(StringCompare(_data.sparam,this.m_button.Name())==0)
        {
         //--- button state
         bool button_curr_state=this.m_button.Pressed();
         //--- to stop
         if(button_curr_state && !this.m_button_state)
           {
            if(this.ButtonResume())
              {
               this.m_button_state=true;
               //--- reset the event object
               this.ResetEvent();
               //--- create an external event object
               this.m_ptr_event=new CExternalEvent();
               //---
               if(CheckPointer(this.m_ptr_event)==POINTER_DYNAMIC)
                 {
                  SEventData data;
                  data.lparam=(long)this.m_magic;
                  data.dparam=(double)TimeCurrent();
                  //--- generate CHARTEVENT_CUSTOM+7 event
                  ushort curr_id=7;
                  if(!this.m_ptr_event.Generate(curr_id,data))
                     PrintFormat("Failed to generate an event: %d",curr_id);
                 }
              }
           }
         //--- to resume
         else if(!button_curr_state && this.m_button_state)
           {
            if(this.ButtonStop())
              {
               this.m_button_state=false;
               //--- reset the event object
               this.ResetEvent();
               //--- create an external event object
               this.m_ptr_event=new CExternalEvent();
               //---
               if(CheckPointer(this.m_ptr_event)==POINTER_DYNAMIC)
                 {
                  SEventData data;
                  data.lparam=(long)this.m_magic;
                  data.dparam=(double)TimeCurrent();
                  //--- generate CHARTEVENT_CUSTOM+8 event
                  ushort curr_id=8;
                  if(!this.m_ptr_event.Generate(curr_id,data))
                     PrintFormat("Failed to generate an event: %d",curr_id);
                 }
              }
           }
        }
     }
//--- user event 
   else if(_event_id>CHARTEVENT_CUSTOM)
     {
      long magic=_data.lparam;
      ushort curr_event_id=this.m_ptr_event.GetId();
      //--- check magic
      if(magic==this.m_magic)
         //--- check id
         if(curr_event_id==_event_id)
           {
            //--- process the definite user event 
            switch(_event_id)
              {
               //--- 1) indicator creation
               case CHARTEVENT_CUSTOM+1:
                 {
                  //--- create a fast ema
                  if(this.m_fast_ema.Create(_Symbol,_Period,21,0,MODE_EMA,PRICE_CLOSE))
                     if(this.m_slow_ema.Create(_Symbol,_Period,55,0,MODE_EMA,PRICE_CLOSE))
                        if(this.m_fast_ema.Handle()!=INVALID_HANDLE)
                           if(this.m_slow_ema.Handle()!=INVALID_HANDLE)
                             {
                              this.m_trade.SetExpertMagicNumber(this.m_magic);
                              this.m_trade.SetDeviationInPoints(InpSlippage);
                              //---
                              this.m_is_init=true;
                             }
                  //---
                  break;
                 }
               //--- 2) indicator deletion
               case CHARTEVENT_CUSTOM+2:
                 {
                  //---release indicators
                  bool is_slow_released=IndicatorRelease(this.m_fast_ema.Handle());
                  bool is_fast_released=IndicatorRelease(this.m_slow_ema.Handle());
                  if(!(is_slow_released && is_fast_released))
                    {
                     //--- to log?
                     if(InpIsLogging)
                        Print("Failed to release the indicators!");
                    }
                  //--- reset the event object
                  this.ResetEvent();
                  //---
                  break;
                 }
               //--- 3) check open signal
               case CHARTEVENT_CUSTOM+3:
                 {
                  MqlTick last_tick;
                  if(SymbolInfoTick(_Symbol,last_tick))
                    {
                     //--- signal type
                     ENUM_ORDER_TYPE open_ord_type=(ENUM_ORDER_TYPE)_data.dparam;
                     //---
                     double open_pr,sl_pr,tp_pr,coeff;
                     open_pr=sl_pr=tp_pr=coeff=0.;
                     //---
                     if(open_ord_type==ORDER_TYPE_BUY)
                       {
                        open_pr=last_tick.ask;
                        coeff=1.;
                       }
                     else if(open_ord_type==ORDER_TYPE_SELL)
                       {
                        open_pr=last_tick.bid;
                        coeff=-1.;
                       }
                     sl_pr=open_pr-coeff*InpStopLoss*_Point;
                     tp_pr=open_pr+coeff*InpStopLoss*_Point;

                     //--- to normalize prices
                     open_pr=NormalizeDouble(open_pr,_Digits);
                     sl_pr=NormalizeDouble(sl_pr,_Digits);
                     tp_pr=NormalizeDouble(tp_pr,_Digits);
                     //--- open the position
                     if(!this.m_trade.PositionOpen(_Symbol,open_ord_type,InpTradeLot,open_pr,
                        sl_pr,tp_pr))
                       {
                        //--- to log?
                        if(InpIsLogging)
                           Print("Failed to open the position: "+_Symbol);
                       }
                     else
                       {
                        //--- pause
                        Sleep(InpTradePause);
                        //--- reset the event object
                        this.ResetEvent();
                        //--- create an order event object
                        this.m_ptr_event=new COrderEvent();
                        if(CheckPointer(this.m_ptr_event)==POINTER_DYNAMIC)
                          {
                           SEventData data;
                           data.lparam=(long)this.m_magic;
                           data.dparam=(double)this.m_trade.ResultDeal();
                           //--- generate CHARTEVENT_CUSTOM+5 event
                           ushort curr_id=5;
                           if(!this.m_ptr_event.Generate(curr_id,data))
                              PrintFormat("Failed to generate an event: %d",curr_id);
                          }
                       }
                    }
                  //---
                  break;
                 }
               //--- 4) check close signal
               case CHARTEVENT_CUSTOM+4:
                 {
                  if(!this.m_trade.PositionClose(_Symbol))
                    {
                     //--- to log?
                     if(InpIsLogging)
                        Print("Failed to close the position: "+_Symbol);
                    }
                  else
                    {
                     //--- pause
                     Sleep(InpTradePause);
                     //--- reset the event object
                     this.ResetEvent();
                     //--- create an order event object
                     this.m_ptr_event=new COrderEvent();
                     if(CheckPointer(this.m_ptr_event)==POINTER_DYNAMIC)
                       {
                        SEventData data;
                        data.lparam=(long)this.m_magic;
                        data.dparam=(double)this.m_trade.ResultDeal();
                        //--- generate CHARTEVENT_CUSTOM+6 event
                        ushort curr_id=6;
                        if(!this.m_ptr_event.Generate(curr_id,data))
                           PrintFormat("Failed to generate an event: %d",curr_id);
                       }
                    }
                  //---
                  break;
                 }
               //--- 5) position opening
               case CHARTEVENT_CUSTOM+5:
                 {
                  ulong ticket=(ulong)_data.dparam;
                  ulong deal=(ulong)_data.dparam;
                  //---
                  datetime now=TimeCurrent();
                  //--- check the deals & orders history
                  if(HistorySelect(now-PeriodSeconds(PERIOD_H1),now))
                     if(HistoryDealSelect(deal))
                       {
                        double deal_vol=HistoryDealGetDouble(deal,DEAL_VOLUME);
                        ENUM_DEAL_ENTRY deal_entry=(ENUM_DEAL_ENTRY)HistoryDealGetInteger(deal,DEAL_ENTRY);
                        //---
                        if(deal_entry==DEAL_ENTRY_IN)
                          {
                           //--- to log?
                           if(InpIsLogging)
                             {
                              Print("/nNew position for: "+_Symbol);
                              PrintFormat("Volume: %0.2f",deal_vol);
                             }
                          }
                       }
                  //---
                  break;
                 }
               //--- 6) position closing
               case CHARTEVENT_CUSTOM+6:
                 {
                  ulong ticket=(ulong)_data.dparam;
                  ulong deal=(ulong)_data.dparam;
                  //---
                  datetime now=TimeCurrent();
                  //--- check the deals & orders history
                  if(HistorySelect(now-PeriodSeconds(PERIOD_H1),now))
                     if(HistoryDealSelect(deal))
                       {
                        double deal_vol=HistoryDealGetDouble(deal,DEAL_VOLUME);
                        ENUM_DEAL_ENTRY deal_entry=(ENUM_DEAL_ENTRY)HistoryDealGetInteger(deal,DEAL_ENTRY);
                        //---
                        if(deal_entry==DEAL_ENTRY_OUT)
                          {
                           //--- to log?
                           if(InpIsLogging)
                             {
                              Print("/nClosed position for: "+_Symbol);
                              PrintFormat("Volume: %0.2f",deal_vol);
                             }
                          }
                       }
                  //---
                  break;
                 }
               //--- 7) stop trading
               case CHARTEVENT_CUSTOM+7:
                 {
                  datetime stop_time=(datetime)_data.dparam;
                  //---
                  this.m_is_trade=false;                  
                  //--- to log?                  
                  if(InpIsLogging)
                     PrintFormat("Expert trading is stopped at: %s",
                                 TimeToString(stop_time,TIME_DATE|TIME_MINUTES|TIME_SECONDS));
                  //---
                  break;
                 }
               //--- 8) resume trading 
               case CHARTEVENT_CUSTOM+8:
                 {
                  datetime resume_time=(datetime)_data.dparam;
                  this.m_is_trade=true;                  
                  //--- to log?                  
                  if(InpIsLogging)                     
                     PrintFormat("Expert trading is resumed at: %s",
                                 TimeToString(resume_time,TIME_DATE|TIME_MINUTES|TIME_SECONDS));
                  //---
                  break;
                 }
              }
           }
     }
  }

它由两部分组成。第一部分处理与“button”对象单击相关的事件。此单击将生成一个外部自定义事件,该事件将由后续处理器处理。

第二部分设计用于处理生成的自定义事件。它由两个街区组成。处理相关事件后,将生成新事件。在第一个块中处理“接收打开的仓库信号”事件。如果处理成功,将生成新的订单事件“打开仓库”。“接收仓库信号”事件在第二个块中处理。如果信号已被处理,则发生“清算”事件。

此EA客户事件处理器。对于使用ceventprocessor类,MQ5是一个很好的例程。EA旨在创建事件并对其做出适当的响应。使用opp范式,我们可以最小化源代码的行数。EA的源代码可以在本文的附件中找到。

在我看来,没有必要每次都参考自定义事件机制。在策略中,有许多微小的、微不足道的和微不足道的东西可以采取不同的形式。

结论

在本文中,我试图描述MQL5环境中自定义事件的工作原理。我希望本文中的想法能够引起不同经验的程序员的兴趣,而不仅仅是新手。

我很高兴MQL5语言不断发展。在不久的将来,可能会有类模板和指向函数的指针。然后,我们可以写出一个指向任何对象的绝对委托。

存档中的源文件可以放在项目文件夹中。就我而言,它是mql5/projects/chartuserevent文件夹。

本文由MetaQuotes Software Corp.翻译自俄语原文
,网址为https://www.mql5.com/ru/articles/1163。

附加文件下载zip chartUserEvent_en.zip(85.61 KB)

 

 


MyFxtop迈投(www.myfxtop.com)-靠谱的外汇跟单社区,免费跟随高手做交易!

 

免责声明:本文系转载自网络,如有侵犯,请联系我们立即删除,另:本文仅代表作者个人观点,与迈投财经无关。其原创性以及文中陈述文字和内容未经本站证实,对本文以及其中全部或者部分内容、文字的真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。

著作权归作者所有。
商业转载请联系作者获得授权,非商业转载请注明出处。

風險提示

MyFxtops邁投所列信息僅供參考,不構成投資建議,也不代表任何形式的推薦或者誘導行為。MyFxtops邁投非外匯經紀商,不接觸妳的任何資金。 MYFXTOPS不保證客戶盈利,不承擔任何責任。從事外彙和差價合約等金融產品的槓桿交易具有高風險,損失有可能超過本金,請量力而行,入市前需充分了解潛在的風險。過去的交易成績並不代表以後的交易成績。依據各地區法律法規,MyFxtops邁投不向中國大陸、美國、加拿大、朝鮮居民提供服務。

邁投公眾號

聯繫我們

客服QQ:981617007
Email: service@myfxtop.com

MyFxtops 邁投