English Русский Español Deutsch 日本語 Português
轻松快捷开发 MetaTrader 程序的函数库 (第 三十部分) :延后交易请求 - 管理请求对象

轻松快捷开发 MetaTrader 程序的函数库 (第 三十部分) :延后交易请求 - 管理请求对象

MetaTrader 5示例 | 24 三月 2020, 09:11
3 207 0
Artyom Trishkin
Artyom Trishkin

内容


概念

文章的第二十六部分开始,我们逐步开发并测试了操控延后交易请求的概念。 在上一篇文章中,我们遵照函数库对象的一般概念创建了相对应的延后请求对象类。 本次,我们将着手允许管理延后请求对象的类。

最初,我打算创建一个独立的、拥有所有必要方法的类来管理延后请求。 但是事实证明,函数库的主要 CTrading 类与将要创建的管理延后请求的新类是如此紧密相关,以至于令管理延后请求对象的新类成为主要交易类的后代会容易得多。

延后请求对象的完整管理在类计时器中执行,故此我们将基准交易类计时器定为虚拟,这意味着延后请求管理类的计时器也将是虚拟的。 然后,与基准交易类计时器相关的所有内容都在类计时器中设定,而该类中用于管理延后请求对象的所有内容都应在类的计时器中设定。

除了管理延后请求对象的类之外,我们还将创建一个小型类来分配暂停,从而避免使用 Sleep() 函数,该函数会在延迟时间内停止程序执行。 借助暂停对象,我们将不再依赖即时报价,这意味着我们在周末也能够测试需要等待的代码。 暂停控制将在计时器中执行。

在布置“魔幻数字”订单属性中存储一些数据时,我必须设置相同的方法,以便返回魔幻数字中指定的魔术 ID 和组值,在不同的类需要该数据。 不过,我遗忘了一个事实,即我已经创建了所有函数库对象的基准对象,我们需要从中继承新创建的对象。 在这种情况下,对象获得基准对象的事件功能,以及在类之间一些原理和目标相似的重复方法。 因此,该对象包含向魔幻数字写入和获取数据,以及管理在日志里显示消息 — 该标志指定每个类在日志里所能显示的各种消息的记录级别。 从基准对象继承所有这些类并获取必要的方法,比之在每个新类中重新设置相同的内容更加合理。

考虑到上述情况,我们需要实现暂停对象类(将在后续文章中用到),对现成的代码进行一些优化,从而将不同类里的重复方法转移到基准对象类,并创建管理延后请求的类。 此外,我们将添加一些新属性来改进延后请求对象类,这些属性令我们能够比较两个相互关联对象的状态 — 订单属性的当前状态,和相应请求对象属性的状态。 延后请求操作完成的事实必须要登记,以便在等待将请求发送到服务器时,及时将其从活动请求列表中删除。

与往常一样,首先,我们在 Datas.mqh 文件中添加新消息索引

   MSG_LIB_TEXT_PEND_REQUEST_STATUS_REMOVE,           // Pending request to delete a pending order
   MSG_LIB_TEXT_PEND_REQUEST_STATUS_MODIFY,           // Pending request to modify pending order parameters
   
   MSG_LIB_TEXT_PEND_REQUEST_ACTUAL_VOLUME,           // Actual volume
   MSG_LIB_TEXT_PEND_REQUEST_ACTUAL_PRICE,            // Actual order price
   MSG_LIB_TEXT_PEND_REQUEST_ACTUAL_STOPLIMIT,        // Actual StopLimit order price
   MSG_LIB_TEXT_PEND_REQUEST_ACTUAL_SL,               // Actual StopLoss order price
   MSG_LIB_TEXT_PEND_REQUEST_ACTUAL_TP,               // Actual TakeProfit order price
   MSG_LIB_TEXT_PEND_REQUEST_ACTUAL_TYPE_FILLING,     // Actual order filling type
   MSG_LIB_TEXT_PEND_REQUEST_ACTUAL_TYPE_TIME,        // Actual order expiration type
   MSG_LIB_TEXT_PEND_REQUEST_ACTUAL_EXPIRATION,       // Actual order lifetime
   
  };
//+------------------------------------------------------------------+

与这些新索引相对应的文本

   {"Отложенный запрос на удаление отложенного ордера","Pending request to remove pending order"},
   {"Отложенный запрос на модификацию параметров отложенного ордера","Pending request to modify pending order parameters"},
   
   {"Фактический объем","Actual volume"},
   {"Фактическая цена установки ордера","Actual order placement price"},
   {"Фактическая цена установки StopLimit-ордера","Actual StopLimit order placement price"},
   {"Фактическая цена установки StopLoss-ордера","Actual StopLoss order placement price"},
   {"Фактическая цена установки TakeProfit-ордера","Actual TakeProfit order placement price"},   
   {"Фактический тип заливки ордера","Actual order filling type"},
   {"Фактический тип экспирации ордера","Actual of order expiration type"},
   {"Фактическое время жизни ордера","Actual of order lifetime"},
   
  };
//+---------------------------------------------------------------------+


暂停对象

在函数库服务类和函数的文件夹 \MQL5\Include\DoEasy\Services\ 之下,在 Pause.mqh 文件中创建新的 CPause 类,并立即添加我们需要的所有内容:

//+------------------------------------------------------------------+
//|                                                        Pause.mqh |
//|                        Copyright 2019, MetaQuotes Software Corp. |
//|                             https://mql5.com/en/users/artmedia70 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2019, MetaQuotes Software Corp."
#property link      "https://mql5.com/en/users/artmedia70"
#property version   "1.00"
#property strict    // Necessary for mql4
//+------------------------------------------------------------------+
//| Include files                                                    |
//+------------------------------------------------------------------+
#include "DELib.mqh"
//+------------------------------------------------------------------+
//| Pause class                                                      |
//+------------------------------------------------------------------+
class CPause
  {
private:
   ulong             m_start;                               // Countdown start
   ulong             m_time_begin;                          // Pause countdown start time
   ulong             m_wait_msc;                            // Pause in milliseconds
public:
//--- Set the new (1) countdown start time and (2) pause in milliseconds
   void              SetTimeBegin(const ulong time)         { this.m_time_begin=time; this.m_start=::GetTickCount();    }
   void              SetWaitingMSC(const ulong pause)       { this.m_wait_msc=pause;                                    }

//--- Return (1) the time passed from the countdown start in milliseconds, (2) waiting completion flag
//--- (3) pause countdown start time, (4) pause in milliseconds
   ulong             Passed(void)                     const { return ::GetTickCount()-this.m_start;                     }
   bool              IsCompleted(void)                const { return this.Passed()>this.m_wait_msc;                     }
   ulong             TimeBegin(void)                  const { return this.m_time_begin;                                 }
   ulong             TimeWait(void)                   const { return this.m_wait_msc;                                   }

//--- Return the description (1) of the time passed till the countdown starts in milliseconds,
//--- (2) pause countdown start time, (3) pause in milliseconds
   string            PassedDescription(void)          const { return ::TimeToString(this.Passed()/1000,TIME_SECONDS);   }
   string            TimeBeginDescription(void)       const { return ::TimeMSCtoString(this.m_time_begin);              }
   string            WaitingMSCDescription(void)      const { return (string)this.m_wait_msc;                           }
   string            WaitingSECDescription(void)      const { return ::TimeToString(this.m_wait_msc/1000,TIME_SECONDS); }

//--- Constructor
                     CPause(void) : m_start(::GetTickCount()){;}
  };
//+------------------------------------------------------------------+

包含服务函数文件 DELib.mqh,显示以毫秒为单位的时间 — 在日志中显示暂停开始时间的辅助类方法需要它。

在类的私密部分中,声明三个计算暂停所需的类成员变量:

  • m_start 变量是必需的,用于在创建暂停对象时指定参考数字,并在以前创建的暂停对象中写入新的倒计时开始参考数字。
  • m_time_begin 变量存储指定的暂停倒计时开始时间,它仅用于显示信息消息。
  • m_wait_msc 变量包含暂停的毫秒数 — 在指定的毫秒数流逝后,暂停被视为结束。

我相信,类方法不需要解释。

  • 首先,设置新倒计时开始时间的 SetTimeBegin() 方法将已流逝的时间写入 m_time_begin 变量。 然后,它将一个新的倒计时开始参考数字设置到 m_start 变量。 由 GetTickCount() 函数返回的结果在该类中可作为任何倒计时的参考数字。
  • Passed() 方法返回暂停流逝的毫秒数,返回的数值为当前参考时间测量值与设置在 m_start 中的起始暂停倒计时值之间的毫秒差。

其余的类方法一目了然,勿需解释。

稍后我们将需要该类。
为了令暂停类可以从函数库、以及基于函数库的程序中的任何地方进行访问,请将 CPause 类文件包括在 DELib.mqh 函数库服务函数的文件中

//+------------------------------------------------------------------+
//|                                                        DELib.mqh |
//|                        Copyright 2019, MetaQuotes Software Corp. |
//|                             https://mql5.com/en/users/artmedia70 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2019, MetaQuotes Software Corp."
#property link      "https://mql5.com/en/users/artmedia70"
#property strict  // Necessary for mql4
//+------------------------------------------------------------------+
//| Include files                                                    |
//+------------------------------------------------------------------+
#include "..\Defines.mqh"
#include "Message.mqh"
#include "TimerCounter.mqh"
#include "Pause.mqh"
//+------------------------------------------------------------------+


一点点的代码优化

现在是时候把类之间重复的方法移到所有函数库对象的基准对象当中了,然后从基准对象继承这些类(如果尚未完成)。

首先,将新的变量和方法写入所有函数库对象的基准对象类:

//+------------------------------------------------------------------+
//| Base object class for all library objects                        |
//+------------------------------------------------------------------+
#define  CONTROLS_TOTAL    (10)
class CBaseObj : public CObject
  {
private:
   int               m_long_prop_total;
   int               m_double_prop_total;
   //--- Fill in the object property array
   template<typename T> bool  FillPropertySettings(const int index,T &array[][CONTROLS_TOTAL],T &array_prev[][CONTROLS_TOTAL],int &event_id);
protected:
   CArrayObj         m_list_events_base;                       // Object base event list
   CArrayObj         m_list_events;                            // Object event list
   ENUM_LOG_LEVEL    m_log_level;                              // Logging level
   MqlTick           m_tick;                                   // Tick structure for receiving quote data
   double            m_hash_sum;                               // Object data hash sum
   double            m_hash_sum_prev;                          // Object data hash sum during the previous check
   int               m_digits_currency;                        // Number of decimal places in an account currency
   int               m_global_error;                           // Global error code
   long              m_chart_id;                               // Control program chart ID
   bool              m_is_event;                               // Object event flag
   int               m_event_code;                             // Object event code
   int               m_event_id;                               // Event ID (equal to the object property value)
   string            m_name;                                   // Object name
   string            m_folder_name;                            // Name of the folder storing CBaseObj descendant objects 
   bool              m_first_start;                            // First launch flag
   int               m_type;                                   // Object type (corresponds to the collection IDs)

//--- Data for storing, controlling and returning tracked properties:
//--- [Property index][0] Controlled property increase value
//--- [Property index][1] Controlled property decrease value
//--- [Property index][2] Controlled property value level
//--- [Property index][3] Property value
//--- [Property index][4] Property value change
//--- [Property index][5] Flag of a property change exceeding the increase value
//--- [Property index][6] Flag of a property change exceeding the decrease value
//--- [Property index][7] Flag of a property increase exceeding the control level
//--- [Property index][8] Flag of a property decrease being less than the control level
//--- [Property index][9] Flag of a property value being equal to the control level
   long              m_long_prop_event[][CONTROLS_TOTAL];         // The array for storing object's integer properties values and controlled property change values
   double            m_double_prop_event[][CONTROLS_TOTAL];       // The array for storing object's real properties values and controlled property change values
   long              m_long_prop_event_prev[][CONTROLS_TOTAL];    // The array for storing object's controlled integer properties values during the previous check
   double            m_double_prop_event_prev[][CONTROLS_TOTAL];  // The array for storing object's controlled real properties values during the previous check

//--- Return (1) time in milliseconds, (2) milliseconds from the MqlTick time value
   long              TickTime(void)                            const { return #ifdef __MQL5__ this.m_tick.time_msc #else this.m_tick.time*1000 #endif ;  }
   ushort            MSCfromTime(const long time_msc)          const { return #ifdef __MQL5__ ushort(this.TickTime()%1000) #else 0 #endif ;              }
//--- return the flag of the event code presence in the event object
   bool              IsPresentEventFlag(const int change_code) const { return (this.m_event_code & change_code)==change_code; }
//--- Return the number of decimal places of the account currency
   int               DigitsCurrency(void)                      const { return this.m_digits_currency; }
//--- Returns the number of decimal places in the 'double' value
   int               GetDigits(const double value)             const;

//--- Set the size of the array of controlled (1) integer and (2) real object properties
   bool              SetControlDataArraySizeLong(const int size);
   bool              SetControlDataArraySizeDouble(const int size);
//--- Check the array size of object properties
   bool              CheckControlDataArraySize(bool check_long=true);
   
//--- Check the list of object property changes and create an event
   void              CheckEvents(void);
   
//--- (1) Pack a 'ushort' number to a passed 'long' number
   long              UshortToLong(const ushort ushort_value,const uchar to_byte,long &long_value);
   
protected:
//--- (1) convert a 'ushort' value to a specified 'long' number byte
   long              UshortToByte(const ushort value,const uchar to_byte)  const;
   
public:
//--- Set the value of the pbject property controlled (1) increase, (2) decrease, (3) control level
   template<typename T> void  SetControlledValueINC(const int property,const T value);
   template<typename T> void  SetControlledValueDEC(const int property,const T value);
   template<typename T> void  SetControlledValueLEVEL(const int property,const T value);

//--- (1) Set, (2) return the error logging level
   void              SetLogLevel(const ENUM_LOG_LEVEL level)                  { this.m_log_level=level;                                               }
   ENUM_LOG_LEVEL    GetLogLevel(void)                                  const { return this.m_log_level;                                              }
//--- Return the set value of the controlled (1) integer and (2) real object properties increase
   long              GetControlledLongValueINC(const int property)      const { return this.m_long_prop_event[property][0];                           }
   double            GetControlledDoubleValueINC(const int property)    const { return this.m_double_prop_event[property-this.m_long_prop_total][0];  }
//--- Return the set value of the controlled (1) integer and (2) real object properties decrease
   long              GetControlledLongValueDEC(const int property)      const { return this.m_long_prop_event[property][1];                           }
   double            GetControlledDoubleValueDEC(const int property)    const { return this.m_double_prop_event[property-this.m_long_prop_total][1];  }
//--- Return the specified control level of object's (1) integer and (2) real properties
   long              GetControlledLongValueLEVEL(const int property)    const { return this.m_long_prop_event[property][2];                           }
   double            GetControlledDoubleValueLEVEL(const int property)  const { return this.m_double_prop_event[property-this.m_long_prop_total][2];  }

//--- Return the current value of the object (1) integer and (2) real property
   long              GetPropLongValue(const int property)               const { return this.m_long_prop_event[property][3];                           }
   double            GetPropDoubleValue(const int property)             const { return this.m_double_prop_event[property-this.m_long_prop_total][3];  }
//--- Return the change value of the controlled (1) integer and (2) real object property
   long              GetPropLongChangedValue(const int property)        const { return this.m_long_prop_event[property][4];                           }
   double            GetPropDoubleChangedValue(const int property)      const { return this.m_double_prop_event[property-this.m_long_prop_total][4];  }
//--- Return the flag of an (1) integer and (2) real property value change exceeding the increase value
   long              GetPropLongFlagINC(const int property)             const { return this.m_long_prop_event[property][5];                           }
   double            GetPropDoubleFlagINC(const int property)           const { return this.m_double_prop_event[property-this.m_long_prop_total][5];  }
//--- Return the flag of an (1) integer and (2) real property value change exceeding the decrease value
   long              GetPropLongFlagDEC(const int property)             const { return this.m_long_prop_event[property][6];                           }
   double            GetPropDoubleFlagDEC(const int property)           const { return this.m_double_prop_event[property-this.m_long_prop_total][6];  }
//--- Return the flag of an (1) integer and (2) real property value increase exceeding the control level
   long              GetPropLongFlagMORE(const int property)            const { return this.m_long_prop_event[property][7];                           }
   double            GetPropDoubleFlagMORE(const int property)          const { return this.m_double_prop_event[property-this.m_long_prop_total][7];  }
//--- Return the flag of an (1) integer and (2) real property value decrease being less than the control level
   long              GetPropLongFlagLESS(const int property)            const { return this.m_long_prop_event[property][8];                           }
   double            GetPropDoubleFlagLESS(const int property)          const { return this.m_double_prop_event[property-this.m_long_prop_total][8];  }
//--- Return the flag of an (1) integer and (2) real property being equal to the control level
   long              GetPropLongFlagEQUAL(const int property)           const { return this.m_long_prop_event[property][9];                           }
   double            GetPropDoubleFlagEQUAL(const int property)         const { return this.m_double_prop_event[property-this.m_long_prop_total][9];  }

//--- Reset the variables of (1) tracked and (2) controlled object data (can be reset in the descendants)
   void              ResetChangesParams(void);
   virtual void      ResetControlsParams(void);
//--- Add the (1) object event and (2) the object event reason to the list
   bool              EventAdd(const ushort event_id,const long lparam,const double dparam,const string sparam);
   bool              EventBaseAdd(const int event_id,const ENUM_BASE_EVENT_REASON reason,const double value);
//--- Set/return the occurred event flag to the object data
   void              SetEvent(const bool flag)                       { this.m_is_event=flag;                   }
   bool              IsEvent(void)                             const { return this.m_is_event;                 }
//--- Return (1) the list of events, (2) the object event code and (3) the global error code
   CArrayObj        *GetListEvents(void)                             { return &this.m_list_events;             }
   int               GetEventCode(void)                        const { return this.m_event_code;               }
   int               GetError(void)                            const { return this.m_global_error;             }
//--- Return (1) an event object and (2) a base event by its number in the list
   CEventBaseObj    *GetEvent(const int shift=WRONG_VALUE,const bool check_out=true);
   CBaseEvent       *GetEventBase(const int index);
//--- Return the number of (1) object events
   int               GetEventsTotal(void)                      const { return this.m_list_events.Total();      }
//--- (1) Set and (2) return the chart ID of the control program
   void              SetChartID(const long id)                       { this.m_chart_id=id;                     }
   long              GetChartID(void)                          const { return this.m_chart_id;                 }
//--- (1) Set the sub-folder name, (2) return the folder name for storing descendant object files
   void              SetSubFolderName(const string name)             { this.m_folder_name=DIRECTORY+name;      }
   string            GetFolderName(void)                       const { return this.m_folder_name;              }
//--- Return the object name
   string            GetName(void)                             const { return this.m_name;                     }
//--- Update the object data to search for changes (Calling from the descendants: CBaseObj::Refresh())
   virtual void      Refresh(void);
//--- Return an object type
   virtual int       Type(void)                                const { return this.m_type;                     }
//--- Return an object event description
   string            EventDescription(const int property,
                                      const ENUM_BASE_EVENT_REASON reason,
                                      const int source,
                                      const string value,
                                      const string property_descr,
                                      const int digits);

//--- Data location in the magic number int value
      //-----------------------------------------------------------
      //  bit   32|31       24|23       16|15        8|7         0|
      //-----------------------------------------------------------
      //  byte    |     3     |     2     |     1     |     0     |
      //-----------------------------------------------------------
      //  data    |   uchar   |   uchar   |         ushort        |
      //-----------------------------------------------------------
      //  descr   |pend req id| id2 | id1 |          magic        |
      //-----------------------------------------------------------
      
//--- Set the ID of the (1) first group, (2) second group, (3) pending request to the magic number value
   void              SetGroupID1(const uchar group,uint &magic)            { magic &=0xFFF0FFFF; magic |= uint(this.ConvToXX(group,0)<<16);  }
   void              SetGroupID2(const uchar group,uint &magic)            { magic &=0xFF0FFFFF; magic |= uint(this.ConvToXX(group,1)<<16);  }
   void              SetPendReqID(const uchar id,uint &magic)              { magic &=0x00FFFFFF; magic |= (uint)id<<24;                      }
//--- Convert the value of 0 - 15 into the necessary uchar number bits (0 - lower, 1 - upper ones)
   uchar             ConvToXX(const uchar number,const uchar index)  const { return((number>15 ? 15 : number)<<(4*(index>1 ? 1 : index)));   }
//--- Return (1) the specified magic number, the ID of (2) the first group, (3) second group, (4) pending request from the magic number value
   ushort            GetMagicID(const uint magic)                    const { return ushort(magic & 0xFFFF);                                  }
   uchar             GetGroupID1(const uint magic)                   const { return uchar(magic>>16) & 0x0F;                                 }
   uchar             GetGroupID2(const uint magic)                   const { return uchar((magic>>16) & 0xF0)>>4;                            }
   uchar             GetPendReqID(const uint magic)                  const { return uchar(magic>>24) & 0xFF;                                 }

//--- Constructor
                     CBaseObj();
  };
//+------------------------------------------------------------------+

受保护的变量(可用于子代,但类程序之外不可见)存储记录级别值,它针对需要在日志中显示信息的每个基准对象子代对象。 它所接收日志记录级别来自 Defines.mqh 中设置的 ENUM_LOG_LEVEL 枚举:

//+------------------------------------------------------------------+
//|  Logging level                                                   |
//+------------------------------------------------------------------+
enum ENUM_LOG_LEVEL
  {
   LOG_LEVEL_NO_MSG,                                        // Trading logging disabled
   LOG_LEVEL_ERROR_MSG,                                     // Only trading errors
   LOG_LEVEL_ALL_MSG                                        // Full logging
  };
//+------------------------------------------------------------------+

分别调用公开的 SetLogLevel()GetLogLevel()方法设置和返回变量值。

设置和返回订单/持仓魔幻数字中所存储各种 ID 值的方法也是公开的
我们之前已研究过这些方法,故在此详述没有意义。

品种的基准交易对象在 \MQL5\Include\DoEasy\Objects\Trade\TradeObj.mqh 文件中。

我们进行必要的改进:将函数库基准对象文件包含其中,并令 CBaseObj 对象类作为其父类

//+------------------------------------------------------------------+
//|                                                     TradeObj.mqh |
//|                        Copyright 2019, MetaQuotes Software Corp. |
//|                             https://mql5.com/en/users/artmedia70 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2019, MetaQuotes Software Corp."
#property link      "https://mql5.com/en/users/artmedia70"
#property version   "1.00"
//+------------------------------------------------------------------+
//| Include files                                                    |
//+------------------------------------------------------------------+
#include "..\..\Services\DELib.mqh"
#include "..\..\Objects\BaseObj.mqh"
//+------------------------------------------------------------------+
//| Trading object class                                             |
//+------------------------------------------------------------------+
class CTradeObj : public CBaseObj
  {

进行了这些改进之后,品种的交易对象类成为所有函数库对象的基准对象的后代。 现在,我们需要删除 CBaseObj 父类中已经存在的所有变量和方法。

从类的私密部分中删除父类中存在的不必要变量

   SActions                   m_datas;
   MqlTick                    m_tick;                                            // Tick structure for receiving prices
   MqlTradeRequest            m_request;                                         // Trade request structure
   MqlTradeResult             m_result;                                          // trade request execution result
   ENUM_SYMBOL_CHART_MODE     m_chart_mode;                                      // Price type for constructing bars
   ENUM_ACCOUNT_MARGIN_MODE   m_margin_mode;                                     // Margin calculation mode
   ENUM_ORDER_TYPE_FILLING    m_type_filling;                                    // Filling policy
   ENUM_ORDER_TYPE_TIME       m_type_time;                                       // Order type per expiration
   int                        m_symbol_expiration_flags;                         // Flags of order expiration modes for a trading object symbol
   ulong                      m_magic;                                           // Magic number
   string                     m_symbol;                                          // Symbol
   string                     m_comment;                                         // Comment
   ulong                      m_deviation;                                       // Slippage in points
   double                     m_volume;                                          // Volume
   datetime                   m_expiration;                                      // Order expiration time (for ORDER_TIME_SPECIFIED type order)
   bool                       m_async_mode;                                      // Flag of asynchronous sending of a trade request
   ENUM_LOG_LEVEL             m_log_level;                                       // Logging level
   int                        m_stop_limit;                                      // Distance of placing a StopLimit order in points
   bool                       m_use_sound;                                       // The flag of using sounds of the object trading events
   uint                       m_multiplier;                                      // The spread multiplier to adjust levels relative to StopLevel

从类的公开部分删除设置和返回日志记录级别的方法:

//--- (1) Return the margin calculation mode, (2) hedge account flag
   ENUM_ACCOUNT_MARGIN_MODE   GetMarginMode(void)                                const { return this.m_margin_mode;           }
   bool                       IsHedge(void) const { return this.GetMarginMode()==ACCOUNT_MARGIN_MODE_RETAIL_HEDGING;          }
//--- (1) Set, (2) return the error logging level
   void                       SetLogLevel(const ENUM_LOG_LEVEL level)                  { this.m_log_level=level;              }
   ENUM_LOG_LEVEL             GetLogLevel(void)                                  const { return this.m_log_level;             }
//--- (1) Set, (2) return the filling policy
   void                       SetTypeFilling(const ENUM_ORDER_TYPE_FILLING type)       { this.m_type_filling=type;            }
   ENUM_ORDER_TYPE_FILLING    GetTypeFilling(void)                               const { return this.m_type_filling;          }

从类的构造函数初始化清单里删除日志记录级别初始化

//+------------------------------------------------------------------+
//| Constructor                                                      |
//+------------------------------------------------------------------+
CTradeObj::CTradeObj(void) : m_magic(0),
                             m_deviation(5),
                             m_stop_limit(0),
                             m_expiration(0),
                             m_async_mode(false),
                             m_type_filling(ORDER_FILLING_FOK),
                             m_type_time(ORDER_TIME_GTC),
                             m_comment(::MQLInfoString(MQL_PROGRAM_NAME)+" by DoEasy"),
                             m_log_level(LOG_LEVEL_ERROR_MSG)

将日志记录级别初始化添加到方法主体当中:

//+------------------------------------------------------------------+
//| Constructor                                                      |
//+------------------------------------------------------------------+
CTradeObj::CTradeObj(void) : m_magic(0),
                             m_deviation(5),
                             m_stop_limit(0),
                             m_expiration(0),
                             m_async_mode(false),
                             m_type_filling(ORDER_FILLING_FOK),
                             m_type_time(ORDER_TIME_GTC),
                             m_comment(::MQLInfoString(MQL_PROGRAM_NAME)+" by DoEasy")

  {
   //--- Margin calculation mode
   this.m_margin_mode=
     (
      #ifdef __MQL5__ (ENUM_ACCOUNT_MARGIN_MODE)::AccountInfoInteger(ACCOUNT_MARGIN_MODE)
      #else /* MQL4 */ ACCOUNT_MARGIN_MODE_RETAIL_HEDGING #endif 
     );
   //--- Spread multiplier
   this.m_multiplier=1;
   //--- Set default sounds and flags of using sounds
   this.m_use_sound=false;
   this.m_log_level=LOG_LEVEL_ERROR_MSG;
   this.InitSounds();
  }
//+------------------------------------------------------------------+


基准抽象订单对象在 \MQL5\Include\DoEasy\Objects\Orders\Order.mqh 文件中。

我们进行必要的改进:将函数库基准对象文件包含其中,并令 CBaseObj 对象类作为其父类

//+------------------------------------------------------------------+
//|                                                        Order.mqh |
//|                        Copyright 2019, MetaQuotes Software Corp. |
//|                             https://mql5.com/en/users/artmedia70 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2019, MetaQuotes Software Corp."
#property link      "https://mql5.com/en/users/artmedia70"
#property version   "1.00"
#property strict    // Necessary for mql4
//+------------------------------------------------------------------+
//| Include files                                                    |
//+------------------------------------------------------------------+
#include <Object.mqh>
#include "..\..\Services\DELib.mqh"
#include "..\..\Objects\BaseObj.mqh"
//+------------------------------------------------------------------+
//| Abstract order class                                             |
//+------------------------------------------------------------------+
class COrder : public CBaseObj
  {

从类的私密部分中删除接收写在订单魔幻数字里的 ID 的方法在魔幻数字值里保留 ID 所占位置):

//+------------------------------------------------------------------+
//| Abstract order class                                             |
//+------------------------------------------------------------------+
class COrder : public CObject
  {
private:
   ulong             m_ticket;                                    // Selected order/deal ticket (MQL5)
   long              m_long_prop[ORDER_PROP_INTEGER_TOTAL];       // Integer properties
   double            m_double_prop[ORDER_PROP_DOUBLE_TOTAL];      // Real properties
   string            m_string_prop[ORDER_PROP_STRING_TOTAL];      // String properties

//--- Return the index of the array the order's (1) double and (2) string properties are located at
   int               IndexProp(ENUM_ORDER_PROP_DOUBLE property)      const { return(int)property-ORDER_PROP_INTEGER_TOTAL;                         }
   int               IndexProp(ENUM_ORDER_PROP_STRING property)      const { return(int)property-ORDER_PROP_INTEGER_TOTAL-ORDER_PROP_DOUBLE_TOTAL; }

//--- Data location in the magic number int value                    
      //-----------------------------------------------------------
      //  bit   32|31       24|23       16|15        8|7         0|
      //-----------------------------------------------------------
      //  byte    |     3     |     2     |     1     |     0     |
      //-----------------------------------------------------------
      //  data    |   uchar   |   uchar   |         ushort        |
      //-----------------------------------------------------------
      //  descr   |pend req id| id2 | id1 |          magic        |
      //-----------------------------------------------------------
//--- Return (1) the specified magic number, the ID of (2) the first group, (3) second group, (4) pending request from the magic number value
   ushort            GetMagicID(void)                                const { return ushort(this.Magic() & 0xFFFF);                                 }
   uchar             GetGroupID1(void)                               const { return uchar(this.Magic()>>16) & 0x0F;                                }
   uchar             GetGroupID2(void)                               const { return uchar((this.Magic()>>16) & 0xF0)>>4;                           }
   uchar             GetPendReqID(void)                              const { return uchar(this.Magic()>>24) & 0xFF;                                }

public:

在封闭的类构造器中,将魔幻数字的规范添加到接收 ID 的方法

//+------------------------------------------------------------------+
//| Closed parametric constructor                                    |
//+------------------------------------------------------------------+
COrder::COrder(ENUM_ORDER_STATUS order_status,const ulong ticket)
  {
//--- Save integer properties
   this.m_ticket=ticket;
   this.m_long_prop[ORDER_PROP_STATUS]                               = order_status;
   this.m_long_prop[ORDER_PROP_MAGIC]                                = this.OrderMagicNumber();
   this.m_long_prop[ORDER_PROP_TICKET]                               = this.OrderTicket();
   this.m_long_prop[ORDER_PROP_TIME_EXP]                             = this.OrderExpiration();
   this.m_long_prop[ORDER_PROP_TYPE_FILLING]                         = this.OrderTypeFilling();
   this.m_long_prop[ORDER_PROP_TYPE_TIME]                            = this.OrderTypeTime();
   this.m_long_prop[ORDER_PROP_TYPE]                                 = this.OrderType();
   this.m_long_prop[ORDER_PROP_STATE]                                = this.OrderState();
   this.m_long_prop[ORDER_PROP_DIRECTION]                            = this.OrderTypeByDirection();
   this.m_long_prop[ORDER_PROP_POSITION_ID]                          = this.OrderPositionID();
   this.m_long_prop[ORDER_PROP_REASON]                               = this.OrderReason();
   this.m_long_prop[ORDER_PROP_DEAL_ORDER_TICKET]                    = this.DealOrderTicket();
   this.m_long_prop[ORDER_PROP_DEAL_ENTRY]                           = this.DealEntry();
   this.m_long_prop[ORDER_PROP_POSITION_BY_ID]                       = this.OrderPositionByID();
   this.m_long_prop[ORDER_PROP_TIME_OPEN]                            = this.OrderOpenTimeMSC();
   this.m_long_prop[ORDER_PROP_TIME_CLOSE]                           = this.OrderCloseTimeMSC();
   this.m_long_prop[ORDER_PROP_TIME_UPDATE]                          = this.PositionTimeUpdateMSC();
   
//--- Save real properties
   this.m_double_prop[this.IndexProp(ORDER_PROP_PRICE_OPEN)]         = this.OrderOpenPrice();
   this.m_double_prop[this.IndexProp(ORDER_PROP_PRICE_CLOSE)]        = this.OrderClosePrice();
   this.m_double_prop[this.IndexProp(ORDER_PROP_PROFIT)]             = this.OrderProfit();
   this.m_double_prop[this.IndexProp(ORDER_PROP_COMMISSION)]         = this.OrderCommission();
   this.m_double_prop[this.IndexProp(ORDER_PROP_SWAP)]               = this.OrderSwap();
   this.m_double_prop[this.IndexProp(ORDER_PROP_VOLUME)]             = this.OrderVolume();
   this.m_double_prop[this.IndexProp(ORDER_PROP_SL)]                 = this.OrderStopLoss();
   this.m_double_prop[this.IndexProp(ORDER_PROP_TP)]                 = this.OrderTakeProfit();
   this.m_double_prop[this.IndexProp(ORDER_PROP_VOLUME_CURRENT)]     = this.OrderVolumeCurrent();
   this.m_double_prop[this.IndexProp(ORDER_PROP_PRICE_STOP_LIMIT)]   = this.OrderPriceStopLimit();
   
//--- Save string properties
   this.m_string_prop[this.IndexProp(ORDER_PROP_SYMBOL)]             = this.OrderSymbol();
   this.m_string_prop[this.IndexProp(ORDER_PROP_COMMENT)]            = this.OrderComment();
   this.m_string_prop[this.IndexProp(ORDER_PROP_EXT_ID)]             = this.OrderExternalID();
   
//--- Save additional integer properties
   this.m_long_prop[ORDER_PROP_PROFIT_PT]                            = this.ProfitInPoints();
   this.m_long_prop[ORDER_PROP_TICKET_FROM]                          = this.OrderTicketFrom();
   this.m_long_prop[ORDER_PROP_TICKET_TO]                            = this.OrderTicketTo();
   this.m_long_prop[ORDER_PROP_CLOSE_BY_SL]                          = this.OrderCloseByStopLoss();
   this.m_long_prop[ORDER_PROP_CLOSE_BY_TP]                          = this.OrderCloseByTakeProfit();
   this.m_long_prop[ORDER_PROP_MAGIC_ID]                             = this.GetMagicID((uint)this.GetProperty(ORDER_PROP_MAGIC));
   this.m_long_prop[ORDER_PROP_GROUP_ID1]                            = this.GetGroupID1((uint)this.GetProperty(ORDER_PROP_MAGIC));
   this.m_long_prop[ORDER_PROP_GROUP_ID2]                            = this.GetGroupID2((uint)this.GetProperty(ORDER_PROP_MAGIC));
   this.m_long_prop[ORDER_PROP_PEND_REQ_ID]                          = this.GetPendReqID((uint)this.GetProperty(ORDER_PROP_MAGIC));
   
//--- Save additional real properties
   this.m_double_prop[this.IndexProp(ORDER_PROP_PROFIT_FULL)]        = this.ProfitFull();
   
//--- Save additional string properties
   this.m_string_prop[this.IndexProp(ORDER_PROP_COMMENT_EXT)]        = "";
  }
//+------------------------------------------------------------------+


基准交易类位于 \MQL5\Include\DoEasy\Trading.mqh 中。

我们来进行必要的改进。 由于该方法已成为将来管理延后请求类的父方法,因此将指向集合对象的指针,和指向延后请求的指针代码从私密部分移至受保护部分

//+------------------------------------------------------------------+
//| Trading class                                                    |
//+------------------------------------------------------------------+
class CTrading : public CBaseObj
  {
protected:
   CAccount            *m_account;                       // Pointer to the current account object
   CSymbolsCollection  *m_symbols;                       // Pointer to the symbol collection list
   CMarketCollection   *m_market;                        // Pointer to the list of the collection of market orders and positions
   CHistoryCollection  *m_history;                       // Pointer to the list of the collection of historical orders and deals
   CEventsCollection   *m_events;                        // Pointer to the event collection list
   CArrayObj            m_list_request;                  // List of pending requests
private:

从私密部分删除存储日志记录级别的变量

private:
   CArrayInt            m_list_errors;                   // Error list
   bool                 m_is_trade_disable;              // Flag disabling trading
   bool                 m_use_sound;                     // The flag of using sounds of the object trading events
   uchar                m_total_try;                     // Number of trading attempts
   ENUM_LOG_LEVEL       m_log_level;                     // Logging level
   MqlTradeRequest      m_request;                       // Trading request prices
   ENUM_TRADE_REQUEST_ERR_FLAGS m_error_reason_flags;    // Flags of error source in a trading method
   ENUM_ERROR_HANDLING_BEHAVIOR m_err_handling_behavior; // Behavior when handling error

在类的公开部分,添加返回整个交易类对象的方法并将类计时器变为虚拟

public:
//--- Return itself
   CTrading            *GetObject(void)    { return &this;   }
//--- Constructor
                        CTrading();
//--- Timer
   virtual void         OnTimer(void);
//--- Get the pointers to the lists (make sure to call the method in program's OnInit() since the symbol collection list is created there)

在创建延后请求的方法中,添加将订单对象传递给方法,然后删除操控订单魔幻数字中设置 ID 的方法

//--- Create a pending request
   bool                 CreatePendingRequest(const ENUM_PEND_REQ_STATUS status,
                                             const uchar id,
                                             const uchar attempts,
                                             const ulong wait,
                                             const MqlTradeRequest &request,
                                             const int retcode,
                                             CSymbol *symbol_obj,
                                             COrder *order);

//--- Data location in the magic number int value
      //-----------------------------------------------------------
      //  bit   32|31       24|23       16|15        8|7         0|
      //-----------------------------------------------------------
      //  byte    |     3     |     2     |     1     |     0     |
      //-----------------------------------------------------------
      //  data    |   uchar   |   uchar   |         ushort        |
      //-----------------------------------------------------------
      //  descr   |pend req id| id2 | id1 |          magic        |
      //-----------------------------------------------------------
      
//--- Set the ID of the (1) first group, (2) second group, (3) pending request to the magic number value
   void              SetGroupID1(const uchar group,uint &magic)            { magic &=0xFFF0FFFF; magic |= uint(ConvToXX(group,0)<<16);    }
   void              SetGroupID2(const uchar group,uint &magic)            { magic &=0xFF0FFFFF; magic |= uint(ConvToXX(group,1)<<16);    }
   void              SetPendReqID(const uchar id,uint &magic)              { magic &=0x00FFFFFF; magic |= (uint)id<<24;                   }
//--- Convert the value of 0 - 15 into the necessary uchar number bits (0 - lower, 1 - upper ones)
   uchar             ConvToXX(const uchar number,const uchar index)  const { return((number>15 ? 15 : number)<<(4*(index>1 ? 1 : index)));}
//--- Return (1) the specified magic number, the ID of (2) the first group, (3) second group, (4) pending request from the magic number value
   ushort            GetMagicID(const uint magic)                    const { return ushort(magic & 0xFFFF);                               }
   uchar             GetGroupID1(const uint magic)                   const { return uchar(magic>>16) & 0x0F;                              }
   uchar             GetGroupID2(const uint magic)                   const { return uchar((magic>>16) & 0xF0)>>4;                         }
   uchar             GetPendReqID(const uint magic)                  const { return uchar(magic>>24) & 0xFF;                              }

  };
//+------------------------------------------------------------------+

从类计时器的实现中删除所有内容,并将其留空:

//+------------------------------------------------------------------+
//| Timer                                                            |
//+------------------------------------------------------------------+
void CTrading::OnTimer(void)
  {
  }
//+------------------------------------------------------------------+

在父类的计时器里会处理其他交易类的操作,在此,为以防万一,我们需要编写代码,处理仅在该类中的某些事情(在当前实现中,将来用于管理延后请求的类)。

在检查交易限制的 CheckTradeConstraints() 方法中,即在检查交易操作期间交易量有效性的模块中,需要大量数值(补充条件), 检查交易量有效性,看由输入传递的数值是否大于零

//--- If closing or not removing/modifying
   if(action_type<ACTION_TYPE_CLOSE_BY || action_type==ACTION_TYPE_CLOSE)
     {
      //--- In case of close-only, write the error code to the list and return 'false' - there is no point in further checks
      if(symbol_obj.TradeMode()==SYMBOL_TRADE_MODE_CLOSEONLY)
        {
         this.m_error_reason_flags=TRADE_REQUEST_ERR_FLAG_ERROR_IN_LIST;
         this.AddErrorCodeToList(MSG_SYM_TRADE_MODE_CLOSEONLY);
         return false;
        }
      //--- Check the minimum volume
      if(volume>0)
        {
         if(volume<symbol_obj.LotsMin())
           {
            //--- The volume in a request is less than the minimum allowed one.
            //--- add the error code to the list
            this.m_error_reason_flags |=TRADE_REQUEST_ERR_FLAG_ERROR_IN_LIST;
            this.AddErrorCodeToList(MSG_LIB_TEXT_REQ_VOL_LESS_MIN_VOLUME);
            //--- If the EA behavior during the trading error is set to "abort trading operation",
            //--- return 'false' - there is no point in further checks
            if(this.m_err_handling_behavior==ERROR_HANDLING_BEHAVIOR_BREAK)
               return false;
            //--- If the EA behavior during a trading error is set to
            //--- "correct parameters" or "create a pending request",
            //--- write 'false' to the result
            else res &=false;
           }
         //--- Check the maximum volume
         else if(volume>symbol_obj.LotsMax())
           {
            //--- The volume in the request exceeds the maximum acceptable one.
            //--- add the error code to the list
            this.m_error_reason_flags |=TRADE_REQUEST_ERR_FLAG_ERROR_IN_LIST;
            this.AddErrorCodeToList(MSG_LIB_TEXT_REQ_VOL_MORE_MAX_VOLUME);
            //--- If the EA behavior during the trading error is set to "abort trading operation",
            //--- return 'false' - there is no point in further checks
            if(this.m_err_handling_behavior==ERROR_HANDLING_BEHAVIOR_BREAK)
               return false;
            //--- If the EA behavior during a trading error is set to
            //--- "correct parameters" or "create a pending request",
            //--- write 'false' to the result
            else res &=false;
           }
         //--- Check the minimum volume gradation
         double step=symbol_obj.LotsStep();
         if(fabs((int)round(volume/step)*step-volume)>0.0000001)
           {
            //--- The volume in the request is not a multiple of the minimum gradation of the lot change step
            //--- add the error code to the list
            this.m_error_reason_flags |=TRADE_REQUEST_ERR_FLAG_ERROR_IN_LIST;
            this.AddErrorCodeToList(MSG_LIB_TEXT_INVALID_VOLUME_STEP);
            //--- If the EA behavior during the trading error is set to "abort trading operation",
            //--- return 'false' - there is no point in further checks
            if(this.m_err_handling_behavior==ERROR_HANDLING_BEHAVIOR_BREAK)
               return false;
            //--- If the EA behavior during a trading error is set to
            //--- "correct parameters" or "create a pending request",
            //--- write 'false' to the result
            else res &=false;
           }
        }
     }

//--- When opening a position

接着:

在需要创建延后请求对象的情况下,接收订单对象的方法。 如果没有这样的对象(在开仓和下挂单的方法中),则将 NULL 传递给该方法。

在开仓的方法中,传递 NULL 作为最后一个参数

      //--- If the check result is "waiting" - set the last error code to the return structure and display the message in the journal,
      //--- create a pending request and return 'false' (OpenPosition)
      if(method>ERROR_CODE_PROCESSING_METHOD_REFRESH)
        {
         //--- If the trading request magic number, has no pending request ID
         if(this.GetPendReqID((uint)magic)==0)
           {
            //--- Play the error sound
            if(this.IsUseSounds())
               trade_obj.PlaySoundError(action,order_type);
            //--- set the last error code to the return structure
            int code=this.m_list_errors.At(this.m_list_errors.Total()-1);
            if(code!=NULL)
              {
               if(code==MSG_LIB_TEXT_TERMINAL_NOT_TRADE_ENABLED || code==MSG_LIB_TEXT_EA_NOT_TRADE_ENABLED)
                  code=10027;
               trade_obj.SetResultRetcode(code);
               trade_obj.SetResultComment(CMessage::Text(trade_obj.GetResultRetcode()));
              }
            //--- Waiting time in milliseconds:
            //--- for the "Wait and repeat" handling method, the waiting value corresponds to the 'method' value,
            ulong wait=method;
            //--- Look for the least of the possible IDs. If failed to find
            //--- or in case of an error while updating the current symbol data, return 'false'
            int id=this.GetFreeID();
            if(id<1 || !symbol_obj.RefreshRates())
               return false;
            //--- Write the pending request object ID to the magic number and fill in the remaining unfilled fields of the trading request structure
            uint mn=(magic==ULONG_MAX ? (uint)trade_obj.GetMagic() : (uint)magic);
            this.SetPendReqID((uchar)id,mn);
            this.m_request.magic=mn;
            this.m_request.action=TRADE_ACTION_DEAL;
            this.m_request.symbol=symbol_obj.Name();
            this.m_request.type=order_type;
            //--- Set the number of trading attempts and create a pending request
            uchar attempts=(this.m_total_try < 1 ? 1 : this.m_total_try);
            this.CreatePendingRequest(PEND_REQ_STATUS_OPEN,(uchar)id,attempts,wait,this.m_request,trade_obj.GetResultRetcode(),symbol_obj,NULL);
           }
         return false;
        }

在下挂单的方法中,将 NULL 作为订单对象传递给创建延后请求的方法,而在处理已存在订单和持仓的方法中,将订单对象传递给创建延后请求的方法,例如,在修改现有持仓的止价订单方法中传递订单对象

      //--- If the check result is "waiting" - set the last error code to the return structure and display the message in the journal,
      //--- create a pending request and return 'false' (ModifyPosition)
      if(method>ERROR_CODE_PROCESSING_METHOD_REFRESH)
        {
         //--- If the pending request object with the position ticket is not present in the list
         if(this.GetIndexPendingRequestByPosition(ticket)==WRONG_VALUE)
           {
            //--- Play the error sound
            if(this.IsUseSounds())
               trade_obj.PlaySoundError(action,order_type,(sl<0 ? false : true),(tp<0 ? false : true));
            //--- set the last error code to the return structure
            int code=this.m_list_errors.At(this.m_list_errors.Total()-1);
            if(code!=NULL)
              {
               if(code==MSG_LIB_TEXT_TERMINAL_NOT_TRADE_ENABLED || code==MSG_LIB_TEXT_EA_NOT_TRADE_ENABLED)
                  code=10027;
               trade_obj.SetResultRetcode(code);
               trade_obj.SetResultComment(CMessage::Text(trade_obj.GetResultRetcode()));
              }
            //--- Waiting time in milliseconds:
            //--- for the "Wait and repeat" handling method, the waiting value corresponds to the 'method' value,
            ulong wait=method;
            //--- Look for the least of the possible IDs. If failed to find
            //--- or in case of an error while updating the current symbol data, return 'false'
            int id=this.GetFreeID();
            if(id<1 || !symbol_obj.RefreshRates())
               return false;
            //--- Write a type of a conducted operation, as well as a symbol and a ticket of a modified position to the request structure
            this.m_request.action=TRADE_ACTION_SLTP;
            this.m_request.symbol=symbol_obj.Name();
            this.m_request.position=ticket;
            this.m_request.type=order_type;
            this.m_request.volume=order.Volume();
            //--- Set the number of trading attempts and create a pending request
            uchar attempts=(this.m_total_try < 1 ? 1 : this.m_total_try);
            this.CreatePendingRequest(PEND_REQ_STATUS_SLTP,(uchar)id,attempts,wait,this.m_request,trade_obj.GetResultRetcode(),symbol_obj,order);
           }
         return false;
        }

在操控已知订单或持仓的其余方法中,将订单对象传递给创建延后请求的方法。

在测试期间,事实证明 ClosePosition() 平仓方法在部分平仓的情况下有时会收到无效平仓交易量。 发出零交易量,且跳过了验证,故以后不再重复。 为了修复这个问题,我们已经添加了在平仓时检查交易量的方法,如此可定义是否启用交易。 现在,我们在创建延后请求时添加正确的交易量,并将其发送到验证方法之中
为此,在方法中添加一些代码

//--- Write a deviation and a comment to the request structure
   this.m_request.deviation=(deviation==ULONG_MAX ? trade_obj.GetDeviation() : deviation);
   this.m_request.comment=(comment==NULL ? trade_obj.GetComment() : comment);
   this.m_request.volume=(volume==WRONG_VALUE || volume>order.Volume() ? order.Volume() : symbol_obj.NormalizedLot(volume));
//--- If there are trading and volume limitations
//--- there are limitations on FreezeLevel - play the error sound and exit
   ENUM_ERROR_CODE_PROCESSING_METHOD method=this.CheckErrors(volume,0,action,order_type,symbol_obj,trade_obj,DFUN,0,0,0,ticket);
   if(method!=ERROR_CODE_PROCESSING_METHOD_OK)
     {

在此,我们按照以下方式将交易量添加到交易请求结构中:如果将 -1 传递给该方法,或者传递给该方法的交易量超过了持仓量,则将全部持仓量(完全平仓)写入该结构。 否则,将常规化的交易量(它也许是无效的,例如,部分平仓时将 0.05 交易量除以 2,该方法将收到 0.025,这会引起错误)传递给该方法。

在修改挂单 ModifyOrder() 的方法中,将魔幻数字 ID订单量添加到交易请求结构中:

//--- Write the magic number, volume, filling type, as well as expiration date and type to the request structure
   this.m_request.magic=order.GetMagicID((uint)order.Magic());
   this.m_request.volume=order.Volume();
   this.m_request.type_filling=(type_filling>WRONG_VALUE ? type_filling : order.TypeFilling());
   this.m_request.expiration=(expiration>WRONG_VALUE ? expiration : order.TimeExpiration());
   this.m_request.type_time=(type_time>WRONG_VALUE ? type_time : order.TypeTime());

//--- If there are trading limitations,
//--- StopLevel or FreezeLevel limitations, play the error sound and exit
   ENUM_ERROR_CODE_PROCESSING_METHOD method=this.CheckErrors(0,this.m_request.price,action,order_type,symbol_obj,trade_obj,DFUN,this.m_request.stoplimit,this.m_request.sl,this.m_request.tp,ticket);
   if(method!=ERROR_CODE_PROCESSING_METHOD_OK)
     {

这对于真实交易根本没用,因为订单是按票据进行修改的。 然而,在延后订单情况下,这可以为我们在日志中正确显示有关已修改订单的记录。

创建延后请求的方法略微进行了修改:

//+------------------------------------------------------------------+
//| Create a pending request                                         |
//+------------------------------------------------------------------+
bool CTrading::CreatePendingRequest(const ENUM_PEND_REQ_STATUS status,
                                    const uchar id,
                                    const uchar attempts,
                                    const ulong wait,
                                    const MqlTradeRequest &request,
                                    const int retcode,
                                    CSymbol *symbol_obj,
                                    COrder *order)
  {
   //--- Create a new pending request object depending on a request status
   CPendRequest *req_obj=NULL;
   switch(status)
     {
      case PEND_REQ_STATUS_OPEN     : req_obj=new CPendReqOpen(id,symbol_obj.BidLast(),symbol_obj.Time(),request,retcode);    break;
      case PEND_REQ_STATUS_CLOSE    : req_obj=new CPendReqClose(id,symbol_obj.BidLast(),symbol_obj.Time(),request,retcode);   break;
      case PEND_REQ_STATUS_SLTP     : req_obj=new CPendReqSLTP(id,symbol_obj.BidLast(),symbol_obj.Time(),request,retcode);    break;
      case PEND_REQ_STATUS_PLACE    : req_obj=new CPendReqPlace(id,symbol_obj.BidLast(),symbol_obj.Time(),request,retcode);   break;
      case PEND_REQ_STATUS_REMOVE   : req_obj=new CPendReqRemove(id,symbol_obj.BidLast(),symbol_obj.Time(),request,retcode);  break;
      case PEND_REQ_STATUS_MODIFY   : req_obj=new CPendReqModify(id,symbol_obj.BidLast(),symbol_obj.Time(),request,retcode);  break;
      default: req_obj=NULL;
        break;
     }
   if(req_obj==NULL)
     {
      if(this.m_log_level>LOG_LEVEL_NO_MSG)
         ::Print(DFUN,CMessage::Text(MSG_LIB_TEXT_FAILING_CREATE_PENDING_REQ));
      return false;
     }
   //--- If failed to add the request to the list, display the appropriate message,
   //--- remove the created object and return 'false'
   if(!this.m_list_request.Add(req_obj))
     {
      if(this.m_log_level>LOG_LEVEL_NO_MSG)
         ::Print(DFUN,CMessage::Text(MSG_LIB_TEXT_FAILING_CREATE_PENDING_REQ));
      delete req_obj;
      return false;
     }
   //--- Fill in the properties of a successfully created object by the values passed to the method
   req_obj.SetTimeActivate(symbol_obj.Time()+wait);
   req_obj.SetWaitingMSC(wait);
   req_obj.SetCurrentAttempt(0);
   req_obj.SetTotalAttempts(attempts);
   if(order!=NULL)
     {
      req_obj.SetActualVolume(order.Volume());
      req_obj.SetActualPrice(order.PriceOpen());
      req_obj.SetActualStopLimit(order.PriceStopLimit());
      req_obj.SetActualSL(order.StopLoss());
      req_obj.SetActualTP(order.TakeProfit());
      req_obj.SetActualTypeFilling(order.TypeFilling());
      req_obj.SetActualTypeTime(order.TypeTime());
      req_obj.SetActualExpiration(order.TimeExpiration());
     }
   else
     {
      req_obj.SetActualVolume(request.volume);
      req_obj.SetActualPrice(request.price);
      req_obj.SetActualStopLimit(request.stoplimit);
      req_obj.SetActualSL(request.sl);
      req_obj.SetActualTP(request.tp);
      req_obj.SetActualTypeFilling(request.type_filling);
      req_obj.SetActualTypeTime(request.type_time);
      req_obj.SetActualExpiration(request.expiration);
     }
   //--- Display a brief description of a created pending request
   if(this.m_log_level>LOG_LEVEL_NO_MSG)
     {
      ::Print(CMessage::Text(MSG_LIB_TEXT_PEND_REQUEST_CREATED)," #",req_obj.ID(),":");
      req_obj.PrintShort();
     }
   //--- successful
   return true;
  }
//+------------------------------------------------------------------+

展望未来(因为我们现在正在编辑基准交易类),应该注意的是,当我们完结延后请求对象时,将引入设置其实际值的方法,这将令我们能够比较对象状态 - 是否已变化,或者请求是否已被激活。 我们用订单属性的状态与延后请求对象属性中设置的值进行比较。 如果它们彼此完全相等,则假定延后请求已完成其功能。

我们在创建延后交易请求的方法中检查作为订单对象传递的内容。 如果传递了 NULL 之外的其他值(订单对象存在),则在延后请求对象中设置来自订单对象的初始参数否则,会从交易请求结构中取值设置这些参数

这些就是基准交易类的变化。

现在,我们改进延后请求对象类。

现在,我们有了延后请求对象的新属性。 将它们写入 Defines.mqh 文件。

将新属性添加到延后请求整数型属性之中,然后将整数型属性的数量从 19 更改为 22

//+------------------------------------------------------------------+
//| Integer properties of a pending trading request                  |
//+------------------------------------------------------------------+
enum ENUM_PEND_REQ_PROP_INTEGER
  {
   PEND_REQ_PROP_STATUS = 0,                                // Trading request status (from the ENUM_PEND_REQ_STATUS enumeration)
   PEND_REQ_PROP_TYPE,                                      // Trading request type (from the ENUM_PEND_REQ_TYPE enumeration)
   PEND_REQ_PROP_ID,                                        // Trading request ID
   PEND_REQ_PROP_RETCODE,                                   // Result a request is based on
   PEND_REQ_PROP_TIME_CREATE,                               // Request creation time
   PEND_REQ_PROP_TIME_ACTIVATE,                             // Next attempt activation time
   PEND_REQ_PROP_WAITING,                                   // Waiting time between requests
   PEND_REQ_PROP_CURRENT_ATTEMPT,                           // Current attempt index
   PEND_REQ_PROP_TOTAL,                                     // Number of attempts
   PEND_REQ_PROP_ACTUAL_TYPE_FILLING,                       // Actual order filling type
   PEND_REQ_PROP_ACTUAL_TYPE_TIME,                          // Actual order expiration type
   PEND_REQ_PROP_ACTUAL_EXPIRATION,                         // Actual order lifetime
   //--- MqlTradeRequest
   PEND_REQ_PROP_MQL_REQ_ACTION,                            // Type of a performed action in the request structure
   PEND_REQ_PROP_MQL_REQ_TYPE,                              // Order type in the request structure
   PEND_REQ_PROP_MQL_REQ_MAGIC,                             // EA stamp (magic number ID) in the request structure
   PEND_REQ_PROP_MQL_REQ_ORDER,                             // Order ticket in the request structure
   PEND_REQ_PROP_MQL_REQ_POSITION,                          // Position ticket in the request structure
   PEND_REQ_PROP_MQL_REQ_POSITION_BY,                       // Opposite position ticket in the request structure
   PEND_REQ_PROP_MQL_REQ_DEVIATION,                         // Maximum acceptable deviation from a requested price in the request structure
   PEND_REQ_PROP_MQL_REQ_EXPIRATION,                        // Order expiration time (for ORDER_TIME_SPECIFIED type orders) in the request structure
   PEND_REQ_PROP_MQL_REQ_TYPE_FILLING,                      // Order filling type in the request structure
   PEND_REQ_PROP_MQL_REQ_TYPE_TIME,                         // Order lifetime type in the request structure
  }; 
#define PEND_REQ_PROP_INTEGER_TOTAL (22)                    // Total number of integer event properties
#define PEND_REQ_PROP_INTEGER_SKIP  (0)                     // Number of request properties not used in sorting
//+------------------------------------------------------------------+

添加新属性,然后将其数量从 6 更改为 11

//+------------------------------------------------------------------+
//| Real properties of a pending trading request                     |
//+------------------------------------------------------------------+
enum ENUM_PEND_REQ_PROP_DOUBLE
  {
   PEND_REQ_PROP_PRICE_CREATE = PEND_REQ_PROP_INTEGER_TOTAL,// Price at the moment of a request generation
   PEND_REQ_PROP_ACTUAL_VOLUME,                             // Actual volume
   PEND_REQ_PROP_ACTUAL_PRICE,                              // Actual order price
   PEND_REQ_PROP_ACTUAL_STOPLIMIT,                          // Actual stoplimit order price
   PEND_REQ_PROP_ACTUAL_SL,                                 // Actual stoploss order price
   PEND_REQ_PROP_ACTUAL_TP,                                 // Actual takeprofit order price
   //--- MqlTradeRequest
   PEND_REQ_PROP_MQL_REQ_VOLUME,                            // Requested volume of a deal in lots in the request structure
   PEND_REQ_PROP_MQL_REQ_PRICE,                             // Price in the request structure
   PEND_REQ_PROP_MQL_REQ_STOPLIMIT,                         // StopLimit level in the request structure
   PEND_REQ_PROP_MQL_REQ_SL,                                // Stop Loss level in the request structure
   PEND_REQ_PROP_MQL_REQ_TP,                                // Take Profit level in the request structure
  };
#define PEND_REQ_PROP_DOUBLE_TOTAL  (11)                    // Total number of event's real properties
#define PEND_REQ_PROP_DOUBLE_SKIP   (0)                     // Number of order properties not used in sorting
//+------------------------------------------------------------------+

添加按新属性排序到可能的排序条件列表中:

//+------------------------------------------------------------------+
//| Possible pending request sorting criteria                        |
//+------------------------------------------------------------------+
#define FIRST_PREQ_DBL_PROP         (PEND_REQ_PROP_INTEGER_TOTAL-PEND_REQ_PROP_INTEGER_SKIP)
#define FIRST_PREQ_STR_PROP         (PEND_REQ_PROP_INTEGER_TOTAL-PEND_REQ_PROP_INTEGER_SKIP+PEND_REQ_PROP_DOUBLE_TOTAL-PEND_REQ_PROP_DOUBLE_SKIP)
enum ENUM_SORT_PEND_REQ_MODE
  {
//--- Sort by integer properties
   SORT_BY_PEND_REQ_STATUS = 0,                             // Sort by a trading request status (from the ENUM_PEND_REQ_STATUS enumeration)
   SORT_BY_PEND_REQ_TYPE,                                   // Sort by a trading request type (from the ENUM_PEND_REQ_TYPE enumeration)
   SORT_BY_PEND_REQ_ID,                                     // Sort by a trading request ID
   SORT_BY_PEND_REQ_RETCODE,                                // Sort by a result a request is based on
   SORT_BY_PEND_REQ_TIME_CREATE,                            // Sort by a request generation time
   SORT_BY_PEND_REQ_TIME_ACTIVATE,                          // Sort by next attempt activation time
   SORT_BY_PEND_REQ_WAITING,                                // Sort by a waiting time between requests
   SORT_BY_PEND_REQ_CURENT,                                 // Sort by the current attempt index
   SORT_BY_PEND_REQ_TOTAL,                                  // Sort by a number of attempts
   SORT_BY_PEND_REQ_ACTUAL_TYPE_FILLING,                    // Sort by actual order filling type
   SORT_BY_PEND_REQ_ACTUAL_TYPE_TIME,                       // Sort by actual order expiration type
   SORT_BY_PEND_REQ_ACTUAL_EXPIRATION,                      // Sort by actual order lifetime
   //--- MqlTradeRequest
   SORT_BY_PEND_REQ_MQL_REQ_ACTION,                         // Sort by a type of a performed action in the request structure
   SORT_BY_PEND_REQ_MQL_REQ_TYPE,                           // Sort by an order type in the request structure
   SORT_BY_PEND_REQ_MQL_REQ_MAGIC,                          // Sort by an EA stamp (magic number ID) in the request structure
   SORT_BY_PEND_REQ_MQL_REQ_ORDER,                          // Sort by an order ticket in the request structure
   SORT_BY_PEND_REQ_MQL_REQ_POSITION,                       // Sort by a position ticket in the request structure
   SORT_BY_PEND_REQ_MQL_REQ_POSITION_BY,                    // Sort by an opposite position ticket in the request structure
   SORT_BY_PEND_REQ_MQL_REQ_DEVIATION,                      // Sort by a maximum acceptable deviation from a requested price in the request structure
   SORT_BY_PEND_REQ_MQL_REQ_EXPIRATION,                     // Sort by an order expiration time (for ORDER_TIME_SPECIFIED type orders) in the request structure
   SORT_BY_PEND_REQ_MQL_REQ_TYPE_FILLING,                   // Sort by an order filling type in the request structure
   SORT_BY_PEND_REQ_MQL_REQ_TYPE_TIME,                      // Sort by order lifetime type in the request structure
//--- Sort by real properties
   SORT_BY_PEND_REQ_PRICE_CREATE = FIRST_PREQ_DBL_PROP,     // Sort by a price at the moment of a request generation
   SORT_BY_PEND_REQ_ACTUAL_VOLUME,                          // Sort by initial volume
   SORT_BY_PEND_REQ_ACTUAL_PRICE,                           // Sort by actual order price
   SORT_BY_PEND_REQ_ACTUAL_STOPLIMIT,                       // Sort by actual stoplimit order price
   SORT_BY_PEND_REQ_ACTUAL_SL,                              // Sort by actual stoploss order price
   SORT_BY_PEND_REQ_ACTUAL_TP,                              // Sort by actual takeprofit order price
   //--- MqlTradeRequest
   SORT_BY_PEND_REQ_MQL_REQ_VOLUME,                         // Sort by a requested volume of a deal in lots in the request structure
   SORT_BY_PEND_REQ_MQL_REQ_PRICE,                          // Sort by a price in the request structure
   SORT_BY_PEND_REQ_MQL_REQ_STOPLIMIT,                      // Sort by StopLimit order level in the request structure
   SORT_BY_PEND_REQ_MQL_REQ_SL,                             // Sort by StopLoss order level in the request structure
   SORT_BY_PEND_REQ_MQL_REQ_TP,                             // Sort by TakeProfit order level in the request structure
//--- Sort by string properties
   //--- MqlTradeRequest
   SORT_BY_PEND_REQ_MQL_SYMBOL = FIRST_PREQ_STR_PROP,       // Sort by a trading instrument name in the request structure
   SORT_BY_PEND_REQ_MQL_COMMENT                             // Sort by an order comment in the request structure
  };
//+------------------------------------------------------------------+

现在,我们专注于对象。

打开抽象延后请求基准对象类 \MQL5\Include\DoEasy\Objects\PendRequest\PendRequest.mqh 的文件,然后进行必要的修改。

在类的私密部分中,声明暂停类对象,而在受保护部分中,声明两个重载方法,一个方法比较对象属性值与传递给方法的值第二个方法设置返回标志,指示延后请求已完成每个订单/持仓参数的修改操作

//+------------------------------------------------------------------+
//| Abstract pending trading request class                           |
//+------------------------------------------------------------------+
class CPendRequest : public CObject
  {
private:
   MqlTradeRequest   m_request;                                         // Trade request structure
   CPause            m_pause;                                           // Pause class object
//--- Copy trading request data
   void              CopyRequest(const MqlTradeRequest &request);
//--- Return the index of the array the request (1) double and (2) string properties are actually located at
   int               IndexProp(ENUM_PEND_REQ_PROP_DOUBLE property)const { return(int)property-PEND_REQ_PROP_INTEGER_TOTAL;                               }
   int               IndexProp(ENUM_PEND_REQ_PROP_STRING property)const { return(int)property-PEND_REQ_PROP_INTEGER_TOTAL-PEND_REQ_PROP_DOUBLE_TOTAL;    }
protected:
   int               m_digits;                                          // Number of decimal places in a quote
   int               m_digits_lot;                                      // Number of decimal places in the symbol lot value
   bool              m_is_hedge;                                        // Hedging account flag
   long              m_long_prop[PEND_REQ_PROP_INTEGER_TOTAL];          // Request integer properties
   double            m_double_prop[PEND_REQ_PROP_DOUBLE_TOTAL];         // Request real properties
   string            m_string_prop[PEND_REQ_PROP_STRING_TOTAL];         // Request string properties
//--- Protected parametric constructor
                     CPendRequest(const ENUM_PEND_REQ_STATUS status,
                                  const uchar id,
                                  const double price,
                                  const ulong time,
                                  const MqlTradeRequest &request,
                                  const int retcode);
//--- Return (1) the magic number specified in the settings, (2) hedging account flag, (3) flag indicating the real property is equal to the value
   ushort            GetMagicID(void)                                      const { return ushort(this.GetProperty(PEND_REQ_PROP_MQL_REQ_MAGIC) & 0xFFFF);}
   bool              IsHedge(void)                                         const { return this.m_is_hedge;                                               }
   bool              IsEqualByMode(const int mode,const double value)      const;
   bool              IsEqualByMode(const int mode,const long value)        const;
//--- Return the flags indicating the pending request has completed changing each of the order/position parameters
   bool              IsCompletedVolume(void)                               const;
   bool              IsCompletedPrice(void)                                const;
   bool              IsCompletedStopLimit(void)                            const;
   bool              IsCompletedStopLoss(void)                             const;
   bool              IsCompletedTakeProfit(void)                           const;
   bool              IsCompletedTypeFilling(void)                          const;
   bool              IsCompletedTypeTime(void)                             const;
   bool              IsCompletedExpiration(void)                           const;
public:

在类的公开部分中,声明返回延后请求操作完成标志的虚拟方法返回当前完整请求对象的方法,和设置并返回暂停对象参数的方法

public:
//--- Default constructor
                     CPendRequest(){;}
//--- Set request (1) integer, (2) real and (3) string properties
   void              SetProperty(ENUM_PEND_REQ_PROP_INTEGER property,long value) { this.m_long_prop[property]=value;                                     }
   void              SetProperty(ENUM_PEND_REQ_PROP_DOUBLE property,double value){ this.m_double_prop[this.IndexProp(property)]=value;                   }
   void              SetProperty(ENUM_PEND_REQ_PROP_STRING property,string value){ this.m_string_prop[this.IndexProp(property)]=value;                   }
//--- Return (1) integer, (2) real and (3) string request properties from the properties array
   long              GetProperty(ENUM_PEND_REQ_PROP_INTEGER property)      const { return this.m_long_prop[property];                                    }
   double            GetProperty(ENUM_PEND_REQ_PROP_DOUBLE property)       const { return this.m_double_prop[this.IndexProp(property)];                  }
   string            GetProperty(ENUM_PEND_REQ_PROP_STRING property)       const { return this.m_string_prop[this.IndexProp(property)];                  }

//--- Return the flag of the request supporting the property
   virtual bool      SupportProperty(ENUM_PEND_REQ_PROP_INTEGER property)        { return true; }
   virtual bool      SupportProperty(ENUM_PEND_REQ_PROP_DOUBLE property)         { return true; }
   virtual bool      SupportProperty(ENUM_PEND_REQ_PROP_STRING property)         { return true; }

//--- Return the flag indicating the pending request has completed its work
   virtual bool      IsCompleted(void)                                     const { return false;}
//--- Return itself
   CPendRequest     *GetObject(void)                                             { return &this;}

//--- Compare CPendRequest objects by a specified property (to sort the lists by a specified request object property)
   virtual int       Compare(const CObject *node,const int mode=0) const;
//--- Compare CPendRequest objects by all properties (to search for equal request objects)
   bool              IsEqual(CPendRequest* compared_obj);
//--- Return (1) the elapsed number of milliseconds, (2) waiting time completion from the pause object,
//--- time of the (3) pause countdown start in milliseconds and (4) waiting time
   ulong             PausePassed(void)                                     const { return this.m_pause.Passed();                 }
   bool              PauseIsCompleted(void)                                const { return this.m_pause.IsCompleted();            }
   ulong             PauseTimeBegin(void)                                  const { return this.m_pause.TimeBegin();              }
   ulong             PauseTimeWait(void)                                   const { return this.m_pause.TimeWait();               }
//--- Return the description (1) of an elapsed number of pause waiting seconds,
//--- (2) pause countdown start time, as well as waiting time (3) in milliseconds and (4) in seconds
   string            PausePassedDescription(void)                          const { return this.m_pause.PassedDescription();      }
   string            PauseTimeBeginDescription(void)                       const { return this.m_pause.TimeBeginDescription();   }
   string            PauseWaitingMSCDescription(void)                      const { return this.m_pause.WaitingMSCDescription();  }
   string            PauseWaitingSecDescription(void)                      const { return this.m_pause.WaitingSECDescription();  }
   
//+------------------------------------------------------------------+

在此,父对象中的 IsCompleted() 方法始终返回 “false”,并分别由每个后代对象实现。 操控暂停对象的方法只是调用本文开头所试验对象的相应方法。

将返回实际订单属性的方法添加到方法模块当中,以简化对延后请求对象属性的访问:

//+------------------------------------------------------------------+
//| Methods of a simplified access to the request object properties  |
//+------------------------------------------------------------------+
//--- Return (1) request structure, (2) status, (3) type, (4) price at the moment of the request generation,
//--- (5) request generation time, (6) next attempt activation time,
//--- (7) waiting time between requests, (8) current attempt index,
//--- (9) number of attempts, (10) request ID
//--- (11) result a request is based on,
//--- (12) order ticket, (13) position ticket, (14) trading operation type
   MqlTradeRequest      MqlRequest(void)                                   const { return this.m_request;                                                }
   ENUM_PEND_REQ_STATUS Status(void)                                       const { return (ENUM_PEND_REQ_STATUS)this.GetProperty(PEND_REQ_PROP_STATUS);  }
   ENUM_PEND_REQ_TYPE   TypeRequest(void)                                  const { return (ENUM_PEND_REQ_TYPE)this.GetProperty(PEND_REQ_PROP_TYPE);      }
   double               PriceCreate(void)                                  const { return this.GetProperty(PEND_REQ_PROP_PRICE_CREATE);                  }
   ulong                TimeCreate(void)                                   const { return this.GetProperty(PEND_REQ_PROP_TIME_CREATE);                   }
   ulong                TimeActivate(void)                                 const { return this.GetProperty(PEND_REQ_PROP_TIME_ACTIVATE);                 }
   ulong                WaitingMSC(void)                                   const { return this.GetProperty(PEND_REQ_PROP_WAITING);                       }
   uchar                CurrentAttempt(void)                               const { return (uchar)this.GetProperty(PEND_REQ_PROP_CURRENT_ATTEMPT);        }
   uchar                TotalAttempts(void)                                const { return (uchar)this.GetProperty(PEND_REQ_PROP_TOTAL);                  }
   uchar                ID(void)                                           const { return (uchar)this.GetProperty(PEND_REQ_PROP_ID);                     }
   int                  Retcode(void)                                      const { return (int)this.GetProperty(PEND_REQ_PROP_RETCODE);                  }
   ulong                Order(void)                                        const { return this.GetProperty(PEND_REQ_PROP_MQL_REQ_ORDER);                 }
   ulong                Position(void)                                     const { return this.GetProperty(PEND_REQ_PROP_MQL_REQ_POSITION);              }
   ENUM_TRADE_REQUEST_ACTIONS Action(void)                                 const { return (ENUM_TRADE_REQUEST_ACTIONS)this.GetProperty(PEND_REQ_PROP_MQL_REQ_ACTION);   }

//--- Return the actual (1) volume, (2) order, (3) limit order,
//--- (4) stoploss order and (5) takeprofit order prices, (6) order filling type,
//--- (7) order expiration type and (8) order lifetime
   double               ActualVolume(void)                                 const { return this.GetProperty(PEND_REQ_PROP_ACTUAL_VOLUME);                 }
   double               ActualPrice(void)                                  const { return this.GetProperty(PEND_REQ_PROP_ACTUAL_PRICE);                  }
   double               ActualStopLimit(void)                              const { return this.GetProperty(PEND_REQ_PROP_ACTUAL_STOPLIMIT);              }
   double               ActualSL(void)                                     const { return this.GetProperty(PEND_REQ_PROP_ACTUAL_SL);                     }
   double               ActualTP(void)                                     const { return this.GetProperty(PEND_REQ_PROP_ACTUAL_TP);                     }
   ENUM_ORDER_TYPE_FILLING ActualTypeFilling(void)                         const { return (ENUM_ORDER_TYPE_FILLING)this.GetProperty(PEND_REQ_PROP_ACTUAL_TYPE_FILLING); }
   ENUM_ORDER_TYPE_TIME ActualTypeTime(void)                               const { return (ENUM_ORDER_TYPE_TIME)this.GetProperty(PEND_REQ_PROP_ACTUAL_TYPE_TIME);       }
   datetime             ActualExpiration(void)                             const { return (datetime)this.GetProperty(PEND_REQ_PROP_ACTUAL_EXPIRATION);   }


每次检查下一个延后请求对象时,会为创建的请求对象写入相应的订单/持仓属性。 这些方法允许从请求对象接收相应订单的当前属性。

在同一模块中,改进设置延后请求创建时间的方法,和设置等待时间的方法

//--- Set (1) the price when creating a request, (2) request creation time,
//--- (3) current attempt time, (4) waiting time between requests,
//--- (5) current attempt index, (6) number of attempts, (7) ID,
//--- (8) order ticket, (9) position ticket
   void                 SetPriceCreate(const double price)                       { this.SetProperty(PEND_REQ_PROP_PRICE_CREATE,price);                   }
   void                 SetTimeCreate(const ulong time)
                          {
                           this.SetProperty(PEND_REQ_PROP_TIME_CREATE,time);
                           this.m_pause.SetTimeBegin(time);
                          }
   void                 SetTimeActivate(const ulong time)                        { this.SetProperty(PEND_REQ_PROP_TIME_ACTIVATE,time);                   }
   void                 SetWaitingMSC(const ulong miliseconds)
                          { 
                           this.SetProperty(PEND_REQ_PROP_WAITING,miliseconds);
                           this.m_pause.SetWaitingMSC(miliseconds);
                          }

除了将传递的值设置到延后请求对象之外,这些方法现在同时将这些值设置到暂停对象

在同一模块中,编写将实际订单对象属性设置到请求对象的方法

//--- Set the actual (1) volume, (2) order, (3) limit order,
//--- (4) stoploss order and (5) takeprofit order prices, (6) order filling type,
//--- (7) order expiration type and (8) order lifetime
   void                 SetActualVolume(const double volume)                     { this.SetProperty(PEND_REQ_PROP_ACTUAL_VOLUME,volume);                 }
   void                 SetActualPrice(const double price)                       { this.SetProperty(PEND_REQ_PROP_ACTUAL_PRICE,price);                   }
   void                 SetActualStopLimit(const double price)                   { this.SetProperty(PEND_REQ_PROP_ACTUAL_STOPLIMIT,price);               }
   void                 SetActualSL(const double price)                          { this.SetProperty(PEND_REQ_PROP_ACTUAL_SL,price);                      }
   void                 SetActualTP(const double price)                          { this.SetProperty(PEND_REQ_PROP_ACTUAL_TP,price);                      }
   void                 SetActualTypeFilling(const ENUM_ORDER_TYPE_FILLING type) { this.SetProperty(PEND_REQ_PROP_ACTUAL_TYPE_FILLING,type);             }
   void                 SetActualTypeTime(const ENUM_ORDER_TYPE_TIME type)       { this.SetProperty(PEND_REQ_PROP_ACTUAL_TYPE_TIME,type);                }
   void                 SetActualExpiration(const datetime expiration)           { this.SetProperty(PEND_REQ_PROP_ACTUAL_EXPIRATION,expiration);         }

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

在显示对象属性的模块中,添加定义返回实际订单属性描述的方法。 在最后,添加虚拟方法,该方法返回延后请求对象的说明“标头”(简要对象描述)

//+------------------------------------------------------------------+
//| Descriptions of request object properties                        |
//+------------------------------------------------------------------+
//--- Get description of a request (1) integer, (2) real and (3) string property
   string               GetPropertyDescription(ENUM_PEND_REQ_PROP_INTEGER property);
   string               GetPropertyDescription(ENUM_PEND_REQ_PROP_DOUBLE property);
   string               GetPropertyDescription(ENUM_PEND_REQ_PROP_STRING property);

//--- Return the names of pending request object parameters
   string               StatusDescription(void)                const;
   string               TypeRequestDescription(void)           const;
   string               IDDescription(void)                    const;
   string               RetcodeDescription(void)               const;
   string               TimeCreateDescription(void)            const;
   string               TimeActivateDescription(void)          const;
   string               TimeWaitingDescription(void)           const;
   string               CurrentAttemptDescription(void)        const;
   string               TotalAttemptsDescription(void)         const;
   string               PriceCreateDescription(void)           const;
   
   string               TypeFillingActualDescription(void)     const;
   string               TypeTimeActualDescription(void)        const;
   string               ExpirationActualDescription(void)      const;
   string               VolumeActualDescription(void)          const;
   string               PriceActualDescription(void)           const;
   string               StopLimitActualDescription(void)       const;
   string               StopLossActualDescription(void)        const;
   string               TakeProfitActualDescription(void)      const;
   
//--- Return the names of trading request structures parameters in the request object
   string               MqlReqActionDescription(void)          const;
   string               MqlReqMagicDescription(void)           const;
   string               MqlReqOrderDescription(void)           const;
   string               MqlReqSymbolDescription(void)          const;
   string               MqlReqVolumeDescription(void)          const;
   string               MqlReqPriceDescription(void)           const;
   string               MqlReqStopLimitDescription(void)       const;
   string               MqlReqStopLossDescription(void)        const;
   string               MqlReqTakeProfitDescription(void)      const;
   string               MqlReqDeviationDescription(void)       const;
   string               MqlReqTypeOrderDescription(void)       const;
   string               MqlReqTypeFillingDescription(void)     const;
   string               MqlReqTypeTimeDescription(void)        const;
   string               MqlReqExpirationDescription(void)      const;
   string               MqlReqCommentDescription(void)         const;
   string               MqlReqPositionDescription(void)        const;
   string               MqlReqPositionByDescription(void)      const;

//--- Display (1) description of request properties (full_prop=true - all properties, false - only supported ones),
//--- (2) short message about the request, (3) short request name (2 and 3 - implementation in the class descendants)
   void                 Print(const bool full_prop=false);
   virtual void         PrintShort(void){;}
   virtual string       Header(void){return NULL;}
  };
//+------------------------------------------------------------------+

稍后将描述实际订单对象属性的说明方法。 延后请求标头描述的虚拟方法在此处返回 NULL,并将在后代对象中实现。

在类构造函数中,添加暂停对象初始化,参数为请求对象生成时间和其等待时间

//+------------------------------------------------------------------+
//| Constructor                                                      |
//+------------------------------------------------------------------+
CPendRequest::CPendRequest(const ENUM_PEND_REQ_STATUS status,
                           const uchar id,
                           const double price,
                           const ulong time,
                           const MqlTradeRequest &request,
                           const int retcode)
  {
   this.CopyRequest(request);
   this.m_is_hedge=#ifdef __MQL4__ true #else bool(::AccountInfoInteger(ACCOUNT_MARGIN_MODE)==ACCOUNT_MARGIN_MODE_RETAIL_HEDGING) #endif;
   this.m_digits=(int)::SymbolInfoInteger(this.GetProperty(PEND_REQ_PROP_MQL_REQ_SYMBOL),SYMBOL_DIGITS);
   int dg=(int)DigitsLots(this.GetProperty(PEND_REQ_PROP_MQL_REQ_SYMBOL));
   this.m_digits_lot=(dg==0 ? 1 : dg);
   this.SetProperty(PEND_REQ_PROP_STATUS,status);
   this.SetProperty(PEND_REQ_PROP_ID,id);
   this.SetProperty(PEND_REQ_PROP_RETCODE,retcode);
   this.SetProperty(PEND_REQ_PROP_TYPE,this.GetProperty(PEND_REQ_PROP_RETCODE)>0 ? PEND_REQ_TYPE_ERROR : PEND_REQ_TYPE_REQUEST);
   this.SetProperty(PEND_REQ_PROP_TIME_CREATE,time);
   this.SetProperty(PEND_REQ_PROP_PRICE_CREATE,price);
   this.m_pause.SetTimeBegin(this.GetProperty(PEND_REQ_PROP_TIME_CREATE));
   this.m_pause.SetWaitingMSC(this.GetProperty(PEND_REQ_PROP_WAITING));
  }
//+------------------------------------------------------------------+

所以,在创建新的延后请求对象之后,立即在其暂停对象里设置等待开始时间和暂停持续时间。

在类主体之外,实现返回标志的方法,这些标志指示请求对象的实数型和整数型属性与传递给方法的值是否相等:

//+---------------------------------------------------------------------+
//| Return the flag indicating the real value is equal to the passed one|
//+---------------------------------------------------------------------+
bool CPendRequest::IsEqualByMode(const int mode,const double value) const
  {
   CPendRequest *req=new CPendRequest();
   if(req==NULL)
      return false;
   req.SetProperty((ENUM_PEND_REQ_PROP_DOUBLE)mode,value);
   bool res=!this.Compare(req,mode);
   delete req;
   return res;
  }
//+------------------------------------------------------------------------+
//| Return the flag indicating the integer value is equal to the passed one|
//+------------------------------------------------------------------------+
bool CPendRequest::IsEqualByMode(const int mode,const long value) const
  {
   CPendRequest *req=new CPendRequest();
   if(req==NULL)
      return false;
   req.SetProperty((ENUM_PEND_REQ_PROP_INTEGER)mode,value);
   bool res=!this.Compare(req,mode);
   delete req;
   return res;
  }
//+------------------------------------------------------------------+

方法接收比较对象属性的模式,以及传递给方法的参数值
该模式取自实数型(ENUM_PEND_REQ_PROP_DOUBLE)或整数型属性(ENUM_PEND_REQ_PROP_INTEGER)的枚举值之一来指代对象属性。 该属性值会与传递给方法的值进行比较。
在方法里,创建一个新的临时延后请求对象相应属性接收传递给方法的值
标准库的 CObject 基准对象的 Compare() 方法返回 0,表示相等。 不过,该方法是虚拟的,并由类后代实现。 在此,实现 Compare() 方法,若当前对象值超过所比较对象的相同属性值时,返回 1。 如果该值较低,则返回 -1;如果相等,则返回 0。
因此,如果比较的值相等,则 res 值将得到 false
不过,我们还在其中为验证结果加入逻辑取反。 故此,如果比较方法返回 0,则 !false 等于 true。 若为 1 或 -1,则 !true 等于 false
写入比较结果后,临时对象被删除,并返回比较结果

这些方法按类型返回实际订单和请求对象属性值的比较标志:

//+------------------------------------------------------------------+
//| Return the flag of a successful volume change                    |
//+------------------------------------------------------------------+
bool CPendRequest::IsCompletedVolume(void) const
  {
   return this.IsEqualByMode(PEND_REQ_PROP_ACTUAL_VOLUME,this.GetProperty(PEND_REQ_PROP_MQL_REQ_VOLUME));
  }
//+------------------------------------------------------------------+
//| Return the flag of a successful price modification               |
//+------------------------------------------------------------------+
bool CPendRequest::IsCompletedPrice(void) const
  {
   return this.IsEqualByMode(PEND_REQ_PROP_ACTUAL_PRICE,this.GetProperty(PEND_REQ_PROP_MQL_REQ_PRICE));
  }
//+-------------------------------------------------------------------+
//| Return the flag of a successful StopLimit order price modification|
//+-------------------------------------------------------------------+
bool CPendRequest::IsCompletedStopLimit(void) const
  {
   return this.IsEqualByMode(PEND_REQ_PROP_ACTUAL_STOPLIMIT,this.GetProperty(PEND_REQ_PROP_MQL_REQ_STOPLIMIT));
  }
//+------------------------------------------------------------------+
//| Return the flag of a successful StopLoss order modification      |
//+------------------------------------------------------------------+
bool CPendRequest::IsCompletedStopLoss(void) const
  {
   return this.IsEqualByMode(PEND_REQ_PROP_ACTUAL_SL,this.GetProperty(PEND_REQ_PROP_MQL_REQ_SL));
  }
//+------------------------------------------------------------------+
//| Return the flag of a successful TakeProfit order modification    |
//+------------------------------------------------------------------+
bool CPendRequest::IsCompletedTakeProfit(void) const
  {
   return this.IsEqualByMode(PEND_REQ_PROP_ACTUAL_TP,this.GetProperty(PEND_REQ_PROP_MQL_REQ_TP));
  }
//+------------------------------------------------------------------+
//| Return the flag of a successful order filling type modification  |
//+------------------------------------------------------------------+
bool CPendRequest::IsCompletedTypeFilling(void) const
  {
   return this.IsEqualByMode(PEND_REQ_PROP_ACTUAL_TYPE_FILLING,this.GetProperty(PEND_REQ_PROP_MQL_REQ_TYPE_FILLING));
  }
//+-------------------------------------------------------------------+
//| Return the flag of a successful order expiration type modification|
//+-------------------------------------------------------------------+
bool CPendRequest::IsCompletedTypeTime(void) const
  {
   return this.IsEqualByMode(PEND_REQ_PROP_ACTUAL_TYPE_TIME,this.GetProperty(PEND_REQ_PROP_MQL_REQ_TYPE_TIME));
  }
//+------------------------------------------------------------------+
//| Return the flag of a successful order lifetime modification      |
//+------------------------------------------------------------------+
bool CPendRequest::IsCompletedExpiration(void) const
  {
   return this.IsEqualByMode(PEND_REQ_PROP_ACTUAL_EXPIRATION,this.GetProperty(PEND_REQ_PROP_MQL_REQ_EXPIRATION));
  }
//+------------------------------------------------------------------+

这些方法返回比较两个相应属性的结果 — 调用我们刚刚研究的方法,比较实际属性与在对象中设置的属性。

在返回对象属性描述的方法实现里添加新属性:

//+------------------------------------------------------------------+
//| Return the description of a request integer property             |
//+------------------------------------------------------------------+
string CPendRequest::GetPropertyDescription(ENUM_PEND_REQ_PROP_INTEGER property)
  {
   return
     (
      property==PEND_REQ_PROP_STATUS               ?  this.StatusDescription()            :
      property==PEND_REQ_PROP_TYPE                 ?  this.TypeRequestDescription()       :
      property==PEND_REQ_PROP_ID                   ?  this.IDDescription()                :
      property==PEND_REQ_PROP_RETCODE              ?  this.RetcodeDescription()           :
      property==PEND_REQ_PROP_TIME_CREATE          ?  this.TimeCreateDescription()        :
      property==PEND_REQ_PROP_TIME_ACTIVATE        ?  this.TimeActivateDescription()      :
      property==PEND_REQ_PROP_WAITING              ?  this.TimeWaitingDescription()       :
      property==PEND_REQ_PROP_CURRENT_ATTEMPT      ?  this.CurrentAttemptDescription()    :
      property==PEND_REQ_PROP_TOTAL                ?  this.TotalAttemptsDescription()     :
      property==PEND_REQ_PROP_ACTUAL_TYPE_FILLING  ?  this.TypeFillingActualDescription() :
      property==PEND_REQ_PROP_ACTUAL_TYPE_TIME     ?  this.TypeTimeActualDescription()    :
      property==PEND_REQ_PROP_ACTUAL_EXPIRATION    ?  this.ExpirationActualDescription()  :
      //--- MqlTradeRequest
      property==PEND_REQ_PROP_MQL_REQ_ACTION       ?  this.MqlReqActionDescription()      :
      property==PEND_REQ_PROP_MQL_REQ_TYPE         ?  this.MqlReqTypeOrderDescription()   :
      property==PEND_REQ_PROP_MQL_REQ_MAGIC        ?  this.MqlReqMagicDescription()       :
      property==PEND_REQ_PROP_MQL_REQ_ORDER        ?  this.MqlReqOrderDescription()       :
      property==PEND_REQ_PROP_MQL_REQ_POSITION     ?  this.MqlReqPositionDescription()    :
      property==PEND_REQ_PROP_MQL_REQ_POSITION_BY  ?  this.MqlReqPositionByDescription()  :
      property==PEND_REQ_PROP_MQL_REQ_DEVIATION    ?  this.MqlReqDeviationDescription()   :
      property==PEND_REQ_PROP_MQL_REQ_EXPIRATION   ?  this.MqlReqExpirationDescription()  :
      property==PEND_REQ_PROP_MQL_REQ_TYPE_FILLING ?  this.MqlReqTypeFillingDescription() :
      property==PEND_REQ_PROP_MQL_REQ_TYPE_TIME    ?  this.MqlReqTypeTimeDescription()    :
      ::EnumToString(property)
     );
  }
//+------------------------------------------------------------------+

//+------------------------------------------------------------------+
//| Return the description of the request real property              |
//+------------------------------------------------------------------+
string CPendRequest::GetPropertyDescription(ENUM_PEND_REQ_PROP_DOUBLE property)
  {
   return
     (
      property==PEND_REQ_PROP_PRICE_CREATE      ?  this.PriceCreateDescription()          :
      property==PEND_REQ_PROP_ACTUAL_VOLUME     ?  this.VolumeActualDescription()         :
      property==PEND_REQ_PROP_ACTUAL_PRICE      ?  this.PriceActualDescription()          :
      property==PEND_REQ_PROP_ACTUAL_STOPLIMIT  ?  this.StopLimitActualDescription()      :
      property==PEND_REQ_PROP_ACTUAL_SL         ?  this.StopLossActualDescription()       :
      property==PEND_REQ_PROP_ACTUAL_TP         ?  this.TakeProfitActualDescription()     :
      //--- MqlTradeRequest
      property==PEND_REQ_PROP_MQL_REQ_VOLUME    ?  this.MqlReqVolumeDescription()         :
      property==PEND_REQ_PROP_MQL_REQ_PRICE     ?  this.MqlReqPriceDescription()          :
      property==PEND_REQ_PROP_MQL_REQ_STOPLIMIT ?  this.MqlReqStopLimitDescription()      :
      property==PEND_REQ_PROP_MQL_REQ_SL        ?  this.MqlReqStopLossDescription()       :
      property==PEND_REQ_PROP_MQL_REQ_TP        ?  this.MqlReqTakeProfitDescription()     :
      ::EnumToString(property)
     );
  }
//+------------------------------------------------------------------+

在方法中检查传递给它的对象属性,并返回相应的方法描述。

返回请求对象中写入的实际订单属性描述的方法:

//+------------------------------------------------------------------+
//| Return the description of the actual order filling mode          |
//+------------------------------------------------------------------+
string CPendRequest::TypeFillingActualDescription(void) const
  {
   return CMessage::Text(MSG_LIB_TEXT_PEND_REQUEST_ACTUAL_TYPE_FILLING)+": "+OrderTypeFillingDescription((ENUM_ORDER_TYPE_FILLING)this.GetProperty(PEND_REQ_PROP_ACTUAL_TYPE_FILLING));
  }
//+------------------------------------------------------------------+
//| Return the actual order lifetime type                            |
//+------------------------------------------------------------------+
string CPendRequest::TypeTimeActualDescription(void) const
  {
   return CMessage::Text(MSG_LIB_TEXT_PEND_REQUEST_ACTUAL_TYPE_TIME)+": "+OrderTypeTimeDescription((ENUM_ORDER_TYPE_TIME)this.GetProperty(PEND_REQ_PROP_ACTUAL_TYPE_TIME));
  }
//+------------------------------------------------------------------+
//| Return the description of the actual order lifetime              |
//+------------------------------------------------------------------+
string CPendRequest::ExpirationActualDescription(void) const
  {
   return CMessage::Text(MSG_LIB_TEXT_PEND_REQUEST_ACTUAL_EXPIRATION)+": "+
          (this.GetProperty(PEND_REQ_PROP_ACTUAL_EXPIRATION)>0 ? 
           ::TimeToString(this.GetProperty(PEND_REQ_PROP_ACTUAL_EXPIRATION)) : 
           CMessage::Text(MSG_LIB_PROP_NOT_SET));
  }
//+------------------------------------------------------------------+

//+------------------------------------------------------------------+
//| Return the description of the actual order volume                |
//+------------------------------------------------------------------+
string CPendRequest::VolumeActualDescription(void) const
  {
   return CMessage::Text(MSG_LIB_TEXT_PEND_REQUEST_ACTUAL_VOLUME)+": "+::DoubleToString(this.GetProperty(PEND_REQ_PROP_ACTUAL_VOLUME),this.m_digits_lot);
  }
//+------------------------------------------------------------------+
//| Return the description of the actual order price                 |
//+------------------------------------------------------------------+
string CPendRequest::PriceActualDescription(void) const
  {
   return CMessage::Text(MSG_LIB_TEXT_PEND_REQUEST_ACTUAL_PRICE)+": "+::DoubleToString(this.GetProperty(PEND_REQ_PROP_ACTUAL_PRICE),this.m_digits);
  }
//+------------------------------------------------------------------+
//| Return the description of the actual StopLimit order price       |
//+------------------------------------------------------------------+
string CPendRequest::StopLimitActualDescription(void) const
  {
   return CMessage::Text(MSG_LIB_TEXT_PEND_REQUEST_ACTUAL_STOPLIMIT)+": "+::DoubleToString(this.GetProperty(PEND_REQ_PROP_ACTUAL_STOPLIMIT),this.m_digits);
  }
//+------------------------------------------------------------------+
//| Return the description of the actual StopLoss order price        |
//+------------------------------------------------------------------+
string CPendRequest::StopLossActualDescription(void) const
  {
   return CMessage::Text(MSG_LIB_TEXT_PEND_REQUEST_ACTUAL_SL)+": "+::DoubleToString(this.GetProperty(PEND_REQ_PROP_ACTUAL_SL),this.m_digits);
  }
//+------------------------------------------------------------------+
//| Return the description of the actual TakeProfit order price      |
//+------------------------------------------------------------------+
string CPendRequest::TakeProfitActualDescription(void) const
  {
   return CMessage::Text(MSG_LIB_TEXT_PEND_REQUEST_ACTUAL_TP)+": "+::DoubleToString(this.GetProperty(PEND_REQ_PROP_ACTUAL_TP),this.m_digits);
  }
//+------------------------------------------------------------------+

这些方法准备并返回由方法返回的属性相对应的消息文本。

这些就是抽象延后请求基准对象的全部修改。

现在,我们对基准请求对象的后代类进行修改。

在开仓延后请求对象类文件 \MQL5\Include\DoEasy\Objects\PendRequest\PendReqOpen.mqh 里进行修改。

添加定义返回请求简短名称的虚拟方法

//+------------------------------------------------------------------+
//| Pending request for opening a position                           |
//+------------------------------------------------------------------+
class CPendReqOpen : public CPendRequest
  {
public:
//--- Constructor
                     CPendReqOpen(const uchar id,
                                  const double price,
                                  const ulong time,
                                  const MqlTradeRequest &request,
                                  const int retcode) : CPendRequest(PEND_REQ_STATUS_OPEN,id,price,time,request,retcode) {}
                                  
//--- Supported deal properties (1) real, (2) integer
   virtual bool      SupportProperty(ENUM_PEND_REQ_PROP_INTEGER property);
   virtual bool      SupportProperty(ENUM_PEND_REQ_PROP_DOUBLE property);
   virtual bool      SupportProperty(ENUM_PEND_REQ_PROP_STRING property);
//--- Display a brief message with request data and (2) a short request name in the journal
   virtual void      PrintShort(void);
   virtual string    Header(void);
  };
//+------------------------------------------------------------------+

其实现:

//+------------------------------------------------------------------+
//| Return the short request name                                    |
//+------------------------------------------------------------------+
string CPendReqOpen::Header(void)
  {
   return CMessage::Text(MSG_LIB_TEXT_PEND_REQUEST_STATUS_OPEN)+", ID #"+(string)this.GetProperty(PEND_REQ_PROP_ID);
  }
//+------------------------------------------------------------------+

修改持仓止价单的延后请求类(PendReqSLTP.mqh 文件):

//+------------------------------------------------------------------+
//| Pending request to modify position stop orders                   |
//+------------------------------------------------------------------+
class CPendReqSLTP : public CPendRequest
  {
public:
//--- Constructor
                     CPendReqSLTP(const uchar id,
                                  const double price,
                                  const ulong time,
                                  const MqlTradeRequest &request,
                                  const int retcode) : CPendRequest(PEND_REQ_STATUS_SLTP,id,price,time,request,retcode) {}
                                  
//--- Supported deal properties (1) real, (2) integer
   virtual bool      SupportProperty(ENUM_PEND_REQ_PROP_INTEGER property);
   virtual bool      SupportProperty(ENUM_PEND_REQ_PROP_DOUBLE property);
   virtual bool      SupportProperty(ENUM_PEND_REQ_PROP_STRING property);
//--- Return the flag indicating the pending request has completed its work
   virtual bool      IsCompleted(void) const;
//--- Display a brief message with request data and (2) a short request name in the journal
   virtual void      PrintShort(void);
   virtual string    Header(void);
  };
//+------------------------------------------------------------------+

将新属性添加到返回标志的方法中,该标志指示对象可支持的某些属性:

//+------------------------------------------------------------------+
//| Return 'true' if an order supports a passed                      |
//| integer property, otherwise return 'false'                       |
//+------------------------------------------------------------------+
bool CPendReqSLTP::SupportProperty(ENUM_PEND_REQ_PROP_INTEGER property)
  {
   if(property==PEND_REQ_PROP_MQL_REQ_POSITION_BY  ||
      property==PEND_REQ_PROP_MQL_REQ_ORDER        ||
      property==PEND_REQ_PROP_MQL_REQ_EXPIRATION   ||
      property==PEND_REQ_PROP_MQL_REQ_DEVIATION    ||
      property==PEND_REQ_PROP_MQL_REQ_TYPE_FILLING ||
      property==PEND_REQ_PROP_MQL_REQ_TYPE_TIME    ||
      property==PEND_REQ_PROP_ACTUAL_EXPIRATION    ||
      property==PEND_REQ_PROP_ACTUAL_TYPE_TIME     ||
      property==PEND_REQ_PROP_ACTUAL_TYPE_FILLING
      
     ) return false;
   return true;
  }
//+------------------------------------------------------------------+
//| Return 'true' if an order supports a passed                      |
//| real property, otherwise return 'false'                          |
//+------------------------------------------------------------------+
bool CPendReqSLTP::SupportProperty(ENUM_PEND_REQ_PROP_DOUBLE property)
  {
   if(property==PEND_REQ_PROP_PRICE_CREATE         ||
      property==PEND_REQ_PROP_ACTUAL_SL            ||
      property==PEND_REQ_PROP_ACTUAL_TP            ||
      property==PEND_REQ_PROP_MQL_REQ_SL           ||
      property==PEND_REQ_PROP_MQL_REQ_TP
     ) return true;
   return false;
  }
//+------------------------------------------------------------------+

虚拟方法返回标志,指示延后请求已完成其操作:

//+----------------------------------------------------------------------+
//| Return the flag indicating the pending request has completed its work|
//+----------------------------------------------------------------------+
bool CPendReqSLTP::IsCompleted(void) const
  {
   bool res=true;
   res &= this.IsCompletedStopLoss();
   res &= this.IsCompletedTakeProfit();
   return res;
  }
//+------------------------------------------------------------------+

在此,将止损和止盈修改完成的验证结果赋值到 res 变量中。 若只要一个验证返回 false,则总结果为 false。 从该方法返回最终结果。

返回请求简短名称的方法:

//+------------------------------------------------------------------+
//| Return the short request name                                    |
//+------------------------------------------------------------------+
string CPendReqSLTP::Header(void)
  {
   return CMessage::Text(MSG_LIB_TEXT_PEND_REQUEST_STATUS_SLTP)+", ID #"+(string)this.GetProperty(PEND_REQ_PROP_ID);
  }
//+------------------------------------------------------------------+

平仓的延后请求类(PendReqClose.mqh 文件):

//+------------------------------------------------------------------+
//| Pending request to close a position                              |
//+------------------------------------------------------------------+
class CPendReqClose : public CPendRequest
  {
public:
//--- Constructor
                     CPendReqClose(const uchar id,
                                   const double price,
                                   const ulong time,
                                   const MqlTradeRequest &request,
                                   const int retcode) : CPendRequest(PEND_REQ_STATUS_CLOSE,id,price,time,request,retcode) {}
                                  
//--- Supported deal properties (1) real, (2) integer
   virtual bool      SupportProperty(ENUM_PEND_REQ_PROP_INTEGER property);
   virtual bool      SupportProperty(ENUM_PEND_REQ_PROP_DOUBLE property);
   virtual bool      SupportProperty(ENUM_PEND_REQ_PROP_STRING property);
//--- Return the flag indicating the pending request has completed its work
   virtual bool      IsCompleted(void) const;
//--- Display a brief message with request data and (2) a short request name in the journal
   virtual void      PrintShort(void);
   virtual string    Header(void);
  };
//+------------------------------------------------------------------+

该方法返回指示请求已完成的标志:

//+----------------------------------------------------------------------+
//| Return the flag indicating the pending request has completed its work|
//+----------------------------------------------------------------------+
bool CPendReqClose::IsCompleted(void) const
  {
   return this.IsCompletedVolume();
  }
//+------------------------------------------------------------------+

返回请求简短名称的方法:

//+------------------------------------------------------------------+
//| Return the short request name                                    |
//+------------------------------------------------------------------+
string CPendReqClose::Header(void)
  {
   return CMessage::Text(MSG_LIB_TEXT_PEND_REQUEST_STATUS_CLOSE)+", ID #"+(string)this.GetProperty(PEND_REQ_PROP_ID);
  }
//+------------------------------------------------------------------+

下订单的延后请求类(PendReqPlace.mqh 文件):

//+------------------------------------------------------------------+
//| Pending request to place a pending order                         |
//+------------------------------------------------------------------+
class CPendReqPlace : public CPendRequest
  {
public:
//--- Constructor
                     CPendReqPlace(const uchar id,
                                   const double price,
                                   const ulong time,
                                   const MqlTradeRequest &request,
                                   const int retcode) : CPendRequest(PEND_REQ_STATUS_PLACE,id,price,time,request,retcode) {}
                                  
//--- Supported deal properties (1) real, (2) integer
   virtual bool      SupportProperty(ENUM_PEND_REQ_PROP_INTEGER property);
   virtual bool      SupportProperty(ENUM_PEND_REQ_PROP_DOUBLE property);
   virtual bool      SupportProperty(ENUM_PEND_REQ_PROP_STRING property);
//--- Display a brief message with request data and (2) a short request name in the journal
   virtual void      PrintShort(void);
   virtual string    Header(void);
  };
//+------------------------------------------------------------------+

返回请求简短名称的方法:

//+------------------------------------------------------------------+
//| Return the short request name                                    |
//+------------------------------------------------------------------+
string CPendReqPlace::Header(void)
  {
   return CMessage::Text(MSG_LIB_TEXT_PEND_REQUEST_STATUS_PLACE)+", ID #"+(string)this.GetProperty(PEND_REQ_PROP_ID);
  }
//+------------------------------------------------------------------+

修改挂单参数的延后请求类(PendReqModify.mqh 文件):

//+------------------------------------------------------------------+
//| Pending request to modify pending order parameters               |
//+------------------------------------------------------------------+
class CPendReqModify : public CPendRequest
  {
public:
//--- Constructor
                     CPendReqModify(const uchar id,
                                    const double price,
                                    const ulong time,
                                    const MqlTradeRequest &request,
                                    const int retcode) : CPendRequest(PEND_REQ_STATUS_MODIFY,id,price,time,request,retcode) {}
                                  
//--- Supported deal properties (1) real, (2) integer
   virtual bool      SupportProperty(ENUM_PEND_REQ_PROP_INTEGER property);
   virtual bool      SupportProperty(ENUM_PEND_REQ_PROP_DOUBLE property);
   virtual bool      SupportProperty(ENUM_PEND_REQ_PROP_STRING property);
//--- Return the flag indicating the pending request has completed its work
   virtual bool      IsCompleted(void) const;
//--- Display a brief message with request data and (2) a short request name in the journal
   virtual void      PrintShort(void);
   virtual string    Header(void);
  };
//+------------------------------------------------------------------+

该方法返回指示请求已完成的标志:

//+----------------------------------------------------------------------+
//| Return the flag indicating the pending request has completed its work|
//+----------------------------------------------------------------------+
bool CPendReqModify::IsCompleted(void) const
  {
   bool res=true;
   res &= this.IsCompletedPrice();
   res &= this.IsCompletedStopLimit();
   res &= this.IsCompletedStopLoss();
   res &= this.IsCompletedTakeProfit();
   res &= this.IsCompletedTypeFilling();
   res &= this.IsCompletedTypeTime();
   res &= this.IsCompletedExpiration();
   return res;
  }
//+------------------------------------------------------------------+

返回请求简短名称的方法:

//+------------------------------------------------------------------+
//| Return the short request name                                    |
//+------------------------------------------------------------------+
string CPendReqModify::Header(void)
  {
   return CMessage::Text(MSG_LIB_TEXT_PEND_REQUEST_STATUS_MODIFY)+", ID #"+(string)this.GetProperty(PEND_REQ_PROP_ID);
  }
//+------------------------------------------------------------------+

删除挂单的延后请求类(PendReqRemove.mqh 文件):

//+------------------------------------------------------------------+
//| Pending request to remove a pending order                        |
//+------------------------------------------------------------------+
class CPendReqRemove : public CPendRequest
  {
public:
//--- Constructor
                     CPendReqRemove(const uchar id,
                                    const double price,
                                    const ulong time,
                                    const MqlTradeRequest &request,
                                    const int retcode) : CPendRequest(PEND_REQ_STATUS_REMOVE,id,price,time,request,retcode) {}
                                  
//--- Supported deal properties (1) real, (2) integer
   virtual bool      SupportProperty(ENUM_PEND_REQ_PROP_INTEGER property);
   virtual bool      SupportProperty(ENUM_PEND_REQ_PROP_DOUBLE property);
   virtual bool      SupportProperty(ENUM_PEND_REQ_PROP_STRING property);
//--- Display a brief message with request data and (2) a short request name in the journal
   virtual void      PrintShort(void);
   virtual string    Header(void);
  };
//+------------------------------------------------------------------+

返回请求简短名称的方法:

//+------------------------------------------------------------------+
//| Return the short request name                                    |
//+------------------------------------------------------------------+
string CPendReqRemove::Header(void)
  {
   return CMessage::Text(MSG_LIB_TEXT_PEND_REQUEST_STATUS_REMOVE)+", ID #"+(string)this.GetProperty(PEND_REQ_PROP_ID);
  }
//+------------------------------------------------------------------+

正如我们在类清单及其相应的方法中看到的那样,此处的所有内容都很琐碎 — 每个方法仅采用其固有的抽象延后请求对象属性。 简短的请求名称由相应的文本消息组成。

抽象延后交易请求对象基类的衍生类的改进至此完成。


管理延后请求对象的类

现在,我们收尾管理延后请求对象类。

如上所述,该类将派生自 CTrading 交易类,故所有数据和方法在 CTrading、CPendRequest 和新添加的 CTradingControl 类中都紧密相关。 在当前的实现中,该类很小巧,可以在计时器中操作。 整个代码将从 CTrading 类计时器代码移至计时器,并进行一些较小的修改。

在 \MQL5\Include\DoEasy\TradingControl.mqh 中创建新的 CTradingControl 类。 在创建它时将其基类设置为 CTrading 类。 确保在创建该类后立即将其包括在 Trading.mqh 文件里

//+------------------------------------------------------------------+
//|                                               PendReqControl.mqh |
//|                        Copyright 2019, MetaQuotes Software Corp. |
//|                             https://mql5.com/en/users/artmedia70 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2019, MetaQuotes Software Corp."
#property link      "https://mql5.com/en/users/artmedia70"
#property version   "1.00"
#property strict    // Necessary for mql4
//+------------------------------------------------------------------+
//| Include files                                                    |
//+------------------------------------------------------------------+
#include "Trading.mqh"
//+------------------------------------------------------------------+
//| Class for managing pending trading requests                      |
//+------------------------------------------------------------------+
class CTradingControl : public CTrading
  {
private:
//--- Set actual order/position data to a pending request object
   void                 SetActualProperties(CPendRequest *req_obj,const COrder *order);
public:
//--- Return itself
   CTradingControl     *GetObject(void)            { return &this;   }
//--- Timer
   virtual void         OnTimer(void);
//--- Constructor
                        CTradingControl();
  };
//+------------------------------------------------------------------+


该类具有 SetActualProperties() 私密方法,该方法在延后请求对象设置实际的订单/持仓数据。
在公开部分,GetObject() 方法返回指向管理延后请求的整体对象的指针,而 OnTimer() 虚拟方法是管理延后请求类的计时器。

在类构造函数中,清除延后请求代码,并为其设置列表已排序标志

//+------------------------------------------------------------------+
//| Constructor                                                      |
//+------------------------------------------------------------------+
CTradingControl::CTradingControl()
  {
   this.m_list_request.Clear();
   this.m_list_request.Sort();
  }
//+------------------------------------------------------------------+

该列表属于 CTrading 类,因为其为创建延后交易请求的交易类。

在类主体之外,实现在请求对象里设置真实订单数据的方法:

//+------------------------------------------------------------------+
//| Set order/position data to a pending request object              |
//+------------------------------------------------------------------+
void CTradingControl::SetActualProperties(CPendRequest *req_obj,const COrder *order)
  {
   req_obj.SetActualExpiration(order.TimeExpiration());
   req_obj.SetActualPrice(order.PriceOpen());
   req_obj.SetActualSL(order.StopLoss());
   req_obj.SetActualStopLimit(order.PriceStopLimit());
   req_obj.SetActualTP(order.TakeProfit());
   req_obj.SetActualTypeFilling(order.TypeFilling());
   req_obj.SetActualTypeTime(order.TypeTime());
   req_obj.SetActualVolume(order.Volume());
  }
//+------------------------------------------------------------------+

该方法接收指向延后请求对象的指针,和指向订单对象的指针。 接下来,调用上述方法在请求对象中设置相应的订单属性值。

类计时器跟踪延后请求列表中的所有存在请求对象。 检查每个延后请求的生存期。 如果已结束,则删除请求。 如果该请求已被激活(帐户历史记录中存在相应的交易事件,或者现有订单/持仓的魔幻数字中含有特定延后请求 ID),则该请求被视为已完全操作,并从列表中删除。

如果该请求尚未激活,且其激活时间已经到来,则将延后请求中设置的交易订单发送到服务器。


该方法具有验证终端侧执行交易操作的能力 — 在 EA 设置中的 “自动交易” 按钮和 “允许自动交易” 选项。 如果已根据 10027 错误码创建了延后交易请求(终端禁用了自动交易),且交易者清除了错误原因(单击自动交易按钮,或在 EA 设置中选中 “允许自动交易” 选项),延后请求会被立即处理。 设置新的延后请求激活时间,因用户修复了错误从而执行了激活,此刻无需等待。 取而代之,订单应立即发送到服务器,从而避免后续的重新报价。

由于该方法很庞大,因此我尽力在代码注释中全面阐述如何在类的计时器内执行管理延后请求的所有操作。

管理延后请求的类计时器:

//+------------------------------------------------------------------+
//| Timer                                                            |
//+------------------------------------------------------------------+
void CTradingControl::OnTimer(void)
  {
   //--- In a loop by the list of pending requests
   int total=this.m_list_request.Total();
   for(int i=total-1;i>WRONG_VALUE;i--)
     {
      //--- receive the next request object
      CPendRequest *req_obj=this.m_list_request.At(i);
      if(req_obj==NULL)
         continue;
      
      //--- get the request structure and the symbol object a trading operation should be performed for
      MqlTradeRequest request=req_obj.MqlRequest();
      CSymbol *symbol_obj=this.m_symbols.GetSymbolObjByName(request.symbol);
      if(symbol_obj==NULL || !symbol_obj.RefreshRates())
         continue;
      
      //--- Set the flag disabling trading in the terminal by two properties simultaneously
      //--- (the AutoTrading button in the terminal and the Allow Automated Trading option in the EA settings)
      //--- If any of the two properties is 'false', the flag is 'false' as well
      bool terminal_trade_allowed=::TerminalInfoInteger(TERMINAL_TRADE_ALLOWED);
      terminal_trade_allowed &=::MQLInfoInteger(MQL_TRADE_ALLOWED);
      //--- If a request object is based on the error code
      if(req_obj.TypeRequest()==PEND_REQ_TYPE_ERROR)
        {
         //--- if the error has been caused by trading disabled on the terminal side and has been eliminated
         if(req_obj.Retcode()==10027 && terminal_trade_allowed)
           {
            //--- if the current attempt has not exceeded the defined number of trading attempts yet
            if(req_obj.CurrentAttempt()<req_obj.TotalAttempts()+1)
              {
               //--- Set the request creation time equal to its creation time minus waiting time, i.e. send the request immediately
               //--- Also, decrease the number of a successful attempt since during the next attempt, its number is increased, and if this is the last attempt,
               //--- it is not executed. However, this is related to fixing the error cause by a user, which means we need to give more time for the last attempt
               req_obj.SetTimeCreate(req_obj.TimeCreate()-req_obj.WaitingMSC());
               req_obj.SetCurrentAttempt(uchar(req_obj.CurrentAttempt()>0 ? req_obj.CurrentAttempt()-1 : 0));
              }
           }
        }

      //--- if the current attempt exceeds the defined number of trading attempts,
      //--- or the current time exceeds the waiting time of all attempts
      //--- remove the current request object and move on to the next one
      if(req_obj.CurrentAttempt()>req_obj.TotalAttempts() || req_obj.CurrentAttempt()>=UCHAR_MAX || 
         (long)symbol_obj.Time()>long(req_obj.TimeCreate()+req_obj.WaitingMSC()*req_obj.TotalAttempts()))
        {
         if(this.m_log_level>LOG_LEVEL_NO_MSG)
            ::Print(req_obj.Header(),": ",CMessage::Text(MSG_LIB_TEXT_PEND_REQUEST_DELETED));
         this.m_list_request.Delete(i);
         continue;
        }
      
      //--- If this is a position opening or placing a pending order
      if((req_obj.Action()==TRADE_ACTION_DEAL && req_obj.Position()==0) || req_obj.Action()==TRADE_ACTION_PENDING)
        {
         //--- Get the pending request ID
         uchar id=this.GetPendReqID((uint)request.magic);
         //--- Get the list of orders/positions containing the order/position with the pending request ID
         CArrayObj *list=this.m_market.GetList(ORDER_PROP_PEND_REQ_ID,id,EQUAL);
         if(::CheckPointer(list)==POINTER_INVALID)
            continue;
         //--- If the order/position is present, the request is handled: remove it and proceed to the next
         if(list.Total()>0)
           {
            if(this.m_log_level>LOG_LEVEL_NO_MSG)
               ::Print(req_obj.Header(),": ",CMessage::Text(MSG_LIB_TEXT_PEND_REQUEST_EXECUTED));
            this.m_list_request.Delete(i);
            continue;
           }
        }
      //--- Otherwise: full and partial position closure, removing an order, modifying order parameters and position stop orders
      else
        {
         CArrayObj *list=NULL;
         //--- if this is a position closure, including a closure by an opposite one
         if((req_obj.Action()==TRADE_ACTION_DEAL && req_obj.Position()>0) || req_obj.Action()==TRADE_ACTION_CLOSE_BY)
           {
            //--- Get a position with the necessary ticket from the list of open positions
            list=this.m_market.GetList(ORDER_PROP_TICKET,req_obj.Position(),EQUAL);
            if(::CheckPointer(list)==POINTER_INVALID)
               continue;
            //--- If the market has no such position - the request is handled: remove it and proceed to the next one
            if(list.Total()==0)
              {
               if(this.m_log_level>LOG_LEVEL_NO_MSG)
                  ::Print(req_obj.Header(),": ",CMessage::Text(MSG_LIB_TEXT_PEND_REQUEST_EXECUTED));
               this.m_list_request.Delete(i);
               continue;
              }
            //--- Otherwise, if the position still exists, this is a partial closure
            else
              {
               //--- Get the list of all account trading events
               list=this.m_events.GetList();
               if(list==NULL)
                  continue;
               //--- In the loop from the end of the account trading event list
               int events_total=list.Total();
               for(int j=events_total-1; j>WRONG_VALUE; j--)
                 {
                  //--- get the next trading event
                  CEvent *event=list.At(j);
                  if(event==NULL)
                     continue;
                  //--- If this event is a partial closure or there was a partial closure when closing by an opposite one
                  if(event.TypeEvent()==TRADE_EVENT_POSITION_CLOSED_PARTIAL || event.TypeEvent()==TRADE_EVENT_POSITION_CLOSED_PARTIAL_BY_POS)
                    {
                     //--- If a position ticket in a trading event coincides with the ticket in a pending trading request
                     if(event.PositionID()==req_obj.Position())
                       {
                        //--- Get a position object from the list of market positions
                        CArrayObj *list_orders=this.m_market.GetList(ORDER_PROP_POSITION_ID,req_obj.Position(),EQUAL);
                        if(list_orders==NULL || list_orders.Total()==0)
                           break;
                        COrder *order=list_orders.At(list_orders.Total()-1);
                        if(order==NULL)
                           break;
                        //--- Set actual position data to the pending request object
                        this.SetActualProperties(req_obj,order);
                        //--- If (executed request volume + unexecuted request volume) is equal to the requested volume in a pending request -
                        //--- the request is handled: remove it and break the loop by the list of account trading events
                        if(req_obj.GetProperty(PEND_REQ_PROP_MQL_REQ_VOLUME)==event.VolumeOrderExecuted()+event.VolumeOrderCurrent())
                          {
                           if(this.m_log_level>LOG_LEVEL_NO_MSG)
                              ::Print(req_obj.Header(),": ",CMessage::Text(MSG_LIB_TEXT_PEND_REQUEST_EXECUTED));
                           this.m_list_request.Delete(i);
                           break;
                          }
                       }
                    }
                 }
               //--- If a handled pending request object was removed by the trading event list in the loop, move on to the next one
               if(::CheckPointer(req_obj)==POINTER_INVALID)
                  continue;
              }
           }
         //--- If this is a modification of position stop orders
         if(req_obj.Action()==TRADE_ACTION_SLTP)
           {
            //--- Get the list of all account trading events
            list=this.m_events.GetList();
            if(list==NULL)
               continue;
            //--- In the loop from the end of the account trading event list
            int events_total=list.Total();
            for(int j=events_total-1; j>WRONG_VALUE; j--)
              {
               //--- get the next trading event
               CEvent *event=list.At(j);
               if(event==NULL)
                  continue;
               //--- If this is a change of the position's stop orders
               if(event.TypeEvent()>TRADE_EVENT_MODIFY_ORDER_TAKE_PROFIT)
                 {
                  //--- If a position ticket in a trading event coincides with the ticket in a pending trading request
                  if(event.PositionID()==req_obj.Position())
                    {
                     //--- Get a position object from the list of market positions
                     CArrayObj *list_orders=this.m_market.GetList(ORDER_PROP_POSITION_ID,req_obj.Position(),EQUAL);
                     if(list_orders==NULL || list_orders.Total()==0)
                        break;
                     COrder *order=list_orders.At(list_orders.Total()-1);
                     if(order==NULL)
                        break;
                     //--- Set actual position data to the pending request object
                     this.SetActualProperties(req_obj,order);
                     //--- If all modifications have worked out -
                     //--- the request is handled: remove it and break the loop by the list of account trading events
                     if(req_obj.IsCompleted())
                       {
                        if(this.m_log_level>LOG_LEVEL_NO_MSG)
                           ::Print(req_obj.Header(),": ",CMessage::Text(MSG_LIB_TEXT_PEND_REQUEST_EXECUTED));
                        this.m_list_request.Delete(i);
                        break;
                       }
                    }
                 }
              }
            //--- If a handled pending request object was removed by the trading event list in the loop, move on to the next one
            if(::CheckPointer(req_obj)==POINTER_INVALID)
               continue;
           }
         //--- If this is a pending order removal
         if(req_obj.Action()==TRADE_ACTION_REMOVE)
           {
            //--- Get the list of removed pending orders from the historical list
            list=this.m_history.GetList(ORDER_PROP_STATUS,ORDER_STATUS_HISTORY_PENDING,EQUAL);
            if(::CheckPointer(list)==POINTER_INVALID)
               continue;
            //--- Leave a single order with the necessary ticket in the list
            list=CSelect::ByOrderProperty(list,ORDER_PROP_TICKET,req_obj.Order(),EQUAL);
            //--- If the order is present, the request is handled: remove it and proceed to the next
            if(list.Total()>0)
              {
               if(this.m_log_level>LOG_LEVEL_NO_MSG)
                  ::Print(req_obj.Header(),": ",CMessage::Text(MSG_LIB_TEXT_PEND_REQUEST_EXECUTED));
               this.m_list_request.Delete(i);
               continue;
              }
           }
         //--- If this is a pending order modification
         if(req_obj.Action()==TRADE_ACTION_MODIFY)
           {
            //--- Get the list of all account trading events
            list=this.m_events.GetList();
            if(list==NULL)
               continue;
            //--- In the loop from the end of the account trading event list
            int events_total=list.Total();
            for(int j=events_total-1; j>WRONG_VALUE; j--)
              {
               //--- get the next trading event
               CEvent *event=list.At(j);
               if(event==NULL)
                  continue;
               //--- If this event involves any change of modified pending order parameters
               if(event.TypeEvent()>TRADE_EVENT_TRIGGERED_STOP_LIMIT_ORDER && event.TypeEvent()<TRADE_EVENT_MODIFY_POSITION_STOP_LOSS_TAKE_PROFIT)
                 {
                  //--- If an order ticket in a trading event coincides with the ticket in a pending trading request
                  if(event.TicketOrderEvent()==req_obj.Order())
                    {
                     //--- Get an order object from the list
                     CArrayObj *list_orders=this.m_market.GetList(ORDER_PROP_TICKET,req_obj.Order(),EQUAL);
                     if(list_orders==NULL || list_orders.Total()==0)
                        break;
                     COrder *order=list_orders.At(0);
                     if(order==NULL)
                        break;
                     //--- Set actual order data to the pending request object
                     this.SetActualProperties(req_obj,order);
                     //--- If all modifications have worked out -
                     //--- the request is handled: remove it and break the loop by the list of account trading events
                     if(req_obj.IsCompleted())
                       {
                        if(this.m_log_level>LOG_LEVEL_NO_MSG)
                           ::Print(req_obj.Header(),": ",CMessage::Text(MSG_LIB_TEXT_PEND_REQUEST_EXECUTED));
                        this.m_list_request.Delete(i);
                        break;
                       }
                    }
                 }
              }
           }
        }
      
      //--- Exit if the pending request object has been removed after checking its operation
      if(::CheckPointer(req_obj)==POINTER_INVALID)
         return;
      //--- Set the request activation time in the request object
      req_obj.SetTimeActivate(req_obj.TimeCreate()+req_obj.WaitingMSC()*(req_obj.CurrentAttempt()+1));
      
      //--- If the current time is less than the request activation time,
      //--- this is not the request time - move on to the next request in the list
      if((long)symbol_obj.Time()<(long)req_obj.TimeActivate())
         continue;
      
      //--- Set the attempt number in the request object
      req_obj.SetCurrentAttempt(uchar(req_obj.CurrentAttempt()+1));
      
      //--- Display the number of a trading attempt in the journal

      if(this.m_log_level>LOG_LEVEL_NO_MSG)
        {
         ::Print(CMessage::Text(MSG_LIB_TEXT_RE_TRY_N)+(string)req_obj.CurrentAttempt()+":");
         req_obj.PrintShort();
        }
      
      //--- Depending on the type of action performed in the trading request 
      switch(request.action)
        {
         //--- Opening/closing a position
         case TRADE_ACTION_DEAL :
            //--- If no ticket is present in the request structure - this is opening a position
            if(request.position==0)
               this.OpenPosition((ENUM_POSITION_TYPE)request.type,request.volume,request.symbol,request.magic,request.sl,request.tp,request.comment,request.deviation,request.type_filling);
            //--- If the ticket is present in the request structure - this is a position closure
            else
               this.ClosePosition(request.position,request.volume,request.comment,request.deviation);
            break;
         //--- Modify StopLoss/TakeProfit position
         case TRADE_ACTION_SLTP :
            this.ModifyPosition(request.position,request.sl,request.tp);
            break;
         //--- Close by an opposite one
         case TRADE_ACTION_CLOSE_BY :
            this.ClosePositionBy(request.position,request.position_by);
            break;
         //---
         //--- Place a pending order
         case TRADE_ACTION_PENDING :
            this.PlaceOrder(request.type,request.volume,request.symbol,request.price,request.stoplimit,request.sl,request.tp,request.magic,request.comment,request.expiration,request.type_time,request.type_filling);
            break;
         //--- Modify a pending order
         case TRADE_ACTION_MODIFY :
            this.ModifyOrder(request.order,request.price,request.sl,request.tp,request.stoplimit,request.expiration,request.type_time,request.type_filling);
            break;
         //--- Remove a pending order
         case TRADE_ACTION_REMOVE :
            this.DeleteOrder(request.order);
            break;
         //---
         default:
            break;
        }
     }
  }
//+------------------------------------------------------------------+

至此,这就是我们在管理延后交易请求类中所需要做的全部事情。 如果您对类计时器操作有任何疑问,请在评论中提问。

现在,我们在 CEngine 函数库基准对象类中进行修改。

由于现在采用的是后代(管理延后请求的类),而非交易类本身,所以交易类文件清单里替换包含:

//+------------------------------------------------------------------+
//|                                                       Engine.mqh |
//|                        Copyright 2019, MetaQuotes Software Corp. |
//|                             https://mql5.com/en/users/artmedia70 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2019, MetaQuotes Software Corp."
#property link      "https://mql5.com/en/users/artmedia70"
#property version   "1.00"
//+------------------------------------------------------------------+
//| Include files                                                    |
//+------------------------------------------------------------------+
#include "Services\TimerCounter.mqh"
#include "Collections\HistoryCollection.mqh"
#include "Collections\MarketCollection.mqh"
#include "Collections\EventsCollection.mqh"
#include "Collections\AccountsCollection.mqh"
#include "Collections\SymbolsCollection.mqh"
#include "Collections\ResourceCollection.mqh"
#include "Trading.mqh"
//+------------------------------------------------------------------+

包含管理延后交易请求的类文件

//+------------------------------------------------------------------+
//|                                                       Engine.mqh |
//|                        Copyright 2019, MetaQuotes Software Corp. |
//|                             https://mql5.com/en/users/artmedia70 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2019, MetaQuotes Software Corp."
#property link      "https://mql5.com/en/users/artmedia70"
#property version   "1.00"
//+------------------------------------------------------------------+
//| Include files                                                    |
//+------------------------------------------------------------------+
#include "Services\TimerCounter.mqh"
#include "Collections\HistoryCollection.mqh"
#include "Collections\MarketCollection.mqh"
#include "Collections\EventsCollection.mqh"
#include "Collections\AccountsCollection.mqh"
#include "Collections\SymbolsCollection.mqh"
#include "Collections\ResourceCollection.mqh"
#include "TradingControl.mqh"
//+------------------------------------------------------------------+

而且代替交易类对象

//+------------------------------------------------------------------+
//| Library basis class                                              |
//+------------------------------------------------------------------+
class CEngine
  {
private:
   CHistoryCollection   m_history;                       // Collection of historical orders and deals
   CMarketCollection    m_market;                        // Collection of market orders and deals
   CEventsCollection    m_events;                        // Event collection
   CAccountsCollection  m_accounts;                      // Account collection
   CSymbolsCollection   m_symbols;                       // Symbol collection
   CResourceCollection  m_resource;                      // Resource list
   CTrading             m_trading;                       // Trading class object
   CArrayObj            m_list_counters;                 // List of timer counters

我们将采用管理交易请求的类对象

//+------------------------------------------------------------------+
//| Library basis class                                              |
//+------------------------------------------------------------------+
class CEngine
  {
private:
   CHistoryCollection   m_history;                       // Collection of historical orders and deals
   CMarketCollection    m_market;                        // Collection of market orders and deals
   CEventsCollection    m_events;                        // Event collection
   CAccountsCollection  m_accounts;                      // Account collection
   CSymbolsCollection   m_symbols;                       // Symbol collection
   CResourceCollection  m_resource;                      // Resource list
   CTradingControl      m_trading;                       // Trading management object
   CArrayObj            m_list_counters;                 // List of timer counters

这些就是当前函数库中的所有修改。


测试

为了测试新的函数库版本,我们采用来自上一篇文章中的 EA,并将其保存在 \MQL5\Experts\TestDoEasy\Part30\ 之下,命名为 TestDoEasyPart30.mq5

编译 EA,并在模拟账户的图表上启动它。

我们需要检查延后请求对象的行为,以防终端中的自动交易按钮被禁用。
在 EA 设置中,将止损和止盈的距离设置为 0。
为此采用以下两个可自定义的参数:

  • StopLoss in points
  • TakeProfit in points.


之后,单击终端中的“自动交易”按钮来禁用自动交易,并尝试利用 EA 交易面板上的按钮发送交易请求(例如,放置 SellLimit 挂单)。
结果则是由于终端禁用交易而导致错误延后请求被创建
立即单击“自动交易”以便修复该错误
新创建的交易请求应立即被激活。 它会将交易请求发送到服务器,并在执行之后,请求对象被删除:

2019.12.27 04:16:28.894 automated trading is disabled
2019.12.27 04:16:33.177 CTrading::PlaceOrder<uint,int,uint,uint>: Invalid request:
2019.12.27 04:16:33.177 There is no permission to conduct trading operations in the terminal (the "AutoTrading" button is disabled)
2019.12.27 04:16:33.177 Correction of trade request parameters ...
2019.12.27 04:16:33.178 Pending request created #1:
2019.12.27 04:16:33.178 Pending request to place a pending order: 
2019.12.27 04:16:33.178 - GBPUSD 0.10 Pending order Sell Limit, Price 1.30088
2019.12.27 04:16:33.178 - Pending request ID: #1, Created 2019.12.26 23:16:30.003, Attempts 5, Wait 00:00:20, End 2019.12.26 23:18:10.003
2019.12.27 04:16:33.178 
2019.12.27 04:16:37.397 automated trading is enabled
2019.12.27 04:16:37.472 Retry trading attempt #1:
2019.12.27 04:16:37.472 Pending request to place a pending order: 
2019.12.27 04:16:37.472 - GBPUSD 0.10 Pending order Sell Limit, Price 1.30088
2019.12.27 04:16:37.472 - Pending request ID: #1, Created 2019.12.26 23:16:10.003, Attempts 5, Wait 00:00:20, End 2019.12.26 23:17:50.003
2019.12.27 04:16:37.472 
2019.12.27 04:16:37.977 - Pending order placed: 2019.12.26 23:16:38.325 -
2019.12.27 04:16:37.977 GBPUSD Placed 0.10 Pending order Sell Limit #500442708 at price 1.30088, Magic number 26148987 (123), G1: 15, G2: 8, ID: 1
2019.12.27 04:16:37.979 OnDoEasyEvent: Pending order placed
2019.12.27 04:16:38.024 Pending request to place a pending order, ID #1: Deleted due completed

现在,我们需要检查是否可以通过新放置的挂单修改止价单的能力(因为其未经它们已被设置)。
再次禁用自动交易,然后在测试 EA 交易面板上单击 “Set StopLoss”。
得到因终端禁用交易操作导致的错误

修改挂单参数的延后请求被创建

在延后请求发送过一次交易尝试后,在终端中启用自动交易挂单的止损被设定,而延后请求本身由于已执行从而被删除

2019.12.27 04:24:11.671 automated trading is disabled
2019.12.27 04:24:16.653 CTrading::ModifyOrder<int,int,double,int>: Invalid request:
2019.12.27 04:24:16.653 There is no permission to conduct trading operations in the terminal (the "AutoTrading" button is disabled)
2019.12.27 04:24:16.653 Correction of trade request parameters ...
2019.12.27 04:24:16.653 Pending request created #1:
2019.12.27 04:24:16.653 Pending request to modify pending order parameters: 
2019.12.27 04:24:16.653 - GBPUSD 0.10 Pending order Sell Limit #500442708, Price 1.30088, StopLoss order level 1.30208
2019.12.27 04:24:16.653 - Order execution type: The order is executed at an available volume, unfulfilled remains in the market (Return)
2019.12.27 04:24:16.653 - Order expiration type: Good till cancel order
2019.12.27 04:24:16.653 - Pending request ID: #1, Created 2019.12.26 23:24:00.270, Attempts 5, Wait 00:00:20, End 2019.12.26 23:25:40.270
2019.12.27 04:24:16.653 
2019.12.27 04:24:25.803 Retry trading attempt #1:
2019.12.27 04:24:25.803 Pending request to modify pending order parameters: 
2019.12.27 04:24:25.803 - GBPUSD 0.10 Pending order Sell Limit #500442708, Price 1.30088, StopLoss order level 1.30208
2019.12.27 04:24:25.803 - Order execution type: The order is executed at an available volume, unfulfilled remains in the market (Return)
2019.12.27 04:24:25.803 - Order expiration type: Good till cancel order
2019.12.27 04:24:25.803 - Pending request ID: #1, Created 2019.12.26 23:24:00.270, Attempts 5, Wait 00:00:20, End 2019.12.26 23:25:40.270
2019.12.27 04:24:25.803 
2019.12.27 04:24:29.770 automated trading is enabled
2019.12.27 04:24:30.022 Retry trading attempt #1:
2019.12.27 04:24:30.022 Pending request to modify pending order parameters: 
2019.12.27 04:24:30.022 - GBPUSD 0.10 Pending order Sell Limit #500442708, Price 1.30088, StopLoss order level 1.30208
2019.12.27 04:24:30.022 - Order execution type: The order is executed at an available volume, unfulfilled remains in the market (Return)
2019.12.27 04:24:30.022 - Order expiration type: Good till cancel order
2019.12.27 04:24:30.022 - Pending request ID: #1, Created 2019.12.26 23:23:40.270, Attempts 5, Wait 00:00:20, End 2019.12.26 23:25:20.270
2019.12.27 04:24:30.022 
2019.12.27 04:24:30.405 - Modified order StopLoss: 2019.12.26 23:16:38.325 -
2019.12.27 04:24:30.405 GBPUSD Pending order Sell Limit #500442708: Modified order StopLoss: [0.00000 --> 1.30208], Magic number 26148987 (123), G1: 15, G2: 8, ID: 1
2019.12.27 04:24:30.405 OnDoEasyEvent: Modified order StopLoss
2019.12.27 04:24:30.601 Pending request to modify pending order parameters, ID #1: Deleted due completed

如我们所见,启用自动交易后,来自延后请求对象的重复请求,与其第一次尝试重复交易请求时的数字相同,其逻辑在以下情况下增加另一次尝试,因用户消除了导致出错的原因(在终端中启用自动交易),现已正常工作。

现在,针对设置止盈进行相同的操作
所有操作都再次正常(在延后请求对象执行第二次交易尝试之后),已由延后请求对象设置止盈,且该请求已被删除:

2019.12.27 04:32:46.843 automated trading is disabled
2019.12.27 04:32:50.810 CTrading::ModifyOrder<int,int,int,double>: Invalid request:
2019.12.27 04:32:50.810 There is no permission to conduct trading operations in the terminal (the "AutoTrading" button is disabled)
2019.12.27 04:32:50.810 Correction of trade request parameters ...
2019.12.27 04:32:50.810 Pending request created #1:
2019.12.27 04:32:50.810 Pending request to modify pending order parameters: 
2019.12.27 04:32:50.810 - GBPUSD 0.10 Pending order Sell Limit #500442708, Price 1.30088, StopLoss order level 1.30208, TakeProfit order level 1.29888
2019.12.27 04:32:50.810 - Order execution type: The order is executed at an available volume, unfulfilled remains in the market (Return)
2019.12.27 04:32:50.810 - Order expiration type: Good till cancel order
2019.12.27 04:32:50.810 - Pending request ID: #1, Created 2019.12.26 23:32:47.943, Attempts 5, Wait 00:00:20, End 2019.12.26 23:34:27.943
2019.12.27 04:32:50.810 
2019.12.27 04:33:08.782 Retry trading attempt #1:
2019.12.27 04:33:08.782 Pending request to modify pending order parameters: 
2019.12.27 04:33:08.782 - GBPUSD 0.10 Pending order Sell Limit #500442708, Price 1.30088, StopLoss order level 1.30208, TakeProfit order level 1.29888
2019.12.27 04:33:08.782 - Order execution type: The order is executed at an available volume, unfulfilled remains in the market (Return)
2019.12.27 04:33:08.782 - Order expiration type: Good till cancel order
2019.12.27 04:33:08.782 - Pending request ID: #1, Created 2019.12.26 23:32:47.943, Attempts 5, Wait 00:00:20, End 2019.12.26 23:34:27.943
2019.12.27 04:33:08.782 
2019.12.27 04:33:29.984 Retry trading attempt #2:
2019.12.27 04:33:29.984 Pending request to modify pending order parameters: 
2019.12.27 04:33:29.984 - GBPUSD 0.10 Pending order Sell Limit #500442708, Price 1.30088, StopLoss order level 1.30208, TakeProfit order level 1.29888
2019.12.27 04:33:29.984 - Order execution type: The order is executed at an available volume, unfulfilled remains in the market (Return)
2019.12.27 04:33:29.984 - Order expiration type: Good till cancel order
2019.12.27 04:33:29.984 - Pending request ID: #1, Created 2019.12.26 23:32:47.943, Attempts 5, Wait 00:00:20, End 2019.12.26 23:34:27.943
2019.12.27 04:33:29.984 
2019.12.27 04:33:31.999 automated trading is enabled
2019.12.27 04:33:32.250 Retry trading attempt #2:
2019.12.27 04:33:32.250 Pending request to modify pending order parameters: 
2019.12.27 04:33:32.250 - GBPUSD 0.10 Pending order Sell Limit #500442708, Price 1.30088, StopLoss order level 1.30208, TakeProfit order level 1.29888
2019.12.27 04:33:32.250 - Order execution type: The order is executed at an available volume, unfulfilled remains in the market (Return)
2019.12.27 04:33:32.250 - Order expiration type: Good till cancel order
2019.12.27 04:33:32.250 - Pending request ID: #1, Created 2019.12.26 23:32:27.943, Attempts 5, Wait 00:00:20, End 2019.12.26 23:34:07.943
2019.12.27 04:33:32.250 
2019.12.27 04:33:32.352 - Modified order TakeProfit: 2019.12.26 23:24:26.509 -
2019.12.27 04:33:32.352 GBPUSD Pending order Sell Limit #500442708: Modified order TakeProfit: [0.00000 --> 1.29888], Magic number 26148987 (123), G1: 15, G2: 8, ID: 1
2019.12.27 04:33:32.352 OnDoEasyEvent: Modified order TakeProfit
2019.12.27 04:33:32.754 Pending request to modify pending order parameters, ID #1: Deleted due completed

测试表明,我们现在能够针对相同的订单或持仓进行不同的交易操作(针对相同票据的交易操作)。 在以前的版本中,我们每次交易只能用一个票据。 之后,延后请求对象始终假定操作已完成。 现在,我们已修复了该行为。

在今后的函数库开发过程中,我们将逐步检查和调试与延后请求有关的其余交易操作,因为这需要仔细而冗长的测试,从而检测和消除各种异常情况。

下一步是什么?

在下一篇文章中,我们将继续开发延后交易请求概念。

文后附有当前版本函数库的所有文件,以及测试 EA 文件,供您测试和下载。
请在评论中留下您的问题、意见和建议。

返回目录

系列中的前几篇文章:

第一部分 概念,数据管理
第二部分 历史订单和成交集合
第三部分 在场订单和持仓集合,安排搜索
第四部分 交易事件, 概念
第五部分 交易事件类和集合。 将事件发送至程序
第六部分 净持帐户事件
第七部分 StopLimit 挂单激活事件,为订单和持仓修改事件准备功能
第八部分 订单和持仓修改事件
第九部分 与 MQL4 的兼容性 — 准备数据
第十部分 与 MQL4 的兼容性 - 开仓和激活挂单事件
第十一部分 与 MQL4 的兼容性 - 平仓事件
第十二部分 帐户对象类和帐户对象集合
第十三部分 账户对象事件
第十四部分 品种对象
第十五部份 品种对象集合
第十六部分 品种集合事件
第十七部分 函数库对象之间的交互
第十八部分 帐户与任意其他函数库对象的交互
第十九部分 函数库消息类
第二十部分 创建和存储程序资源
第二十一部分 交易类 - 基准跨平台交易对象
第二十二部分 交易类 - 基准交易类,限制验证
第二十三部分 交易类 - 基准交易类,有效参数验证
第二十四部分 交易类 - 基准交易类,无效参数的自动纠正
第二十五部分 交易类 - 基准交易类,处理交易服务器返回的错误
第二十六部分 操控延后交易请求 - 首次实现(开仓)
第二十七部分 操控延后交易请求 - 下挂单
第二十八部分 操控延后交易请求 - 平仓、删除和修改
第二十九部分 操控延后交易请求 - 请求对象类

本文由MetaQuotes Ltd译自俄文
原文地址: https://www.mql5.com/ru/articles/7481

附加的文件 |
MQL5.zip (3642.78 KB)
MQL4.zip (3642.77 KB)
连续前行优化 (第三部分): 将机器人适配为自动优化器 连续前行优化 (第三部分): 将机器人适配为自动优化器
第三部分充当前两部分之间的桥梁:它阐述的是第一篇文章中研究的 DLL,以及第二篇文章中论述的报告下载对象之间的交互机制。 我们将分析从 DLL 导入的包装类的创建过程,该类可依据交易历史记录形成 XML 文件。 我们还将研究一种与此包装器进行交互的方法。
神经网络轻松制作 神经网络轻松制作
人工智能往往伴随着极其复杂和难以理解的事物。 同时,人工智能在日常生活中也越来越多地被提及。 不同的媒体也经常发布有关运用神经网络成就的新闻。 本文的目在于展示任何人都可以轻松创建神经网络,并在交易中运用 AI 成就。
寻找市场形态的计量经济学方法:自相关,热点图和散点图 寻找市场形态的计量经济学方法:自相关,热点图和散点图
本文研讨季节性特征的扩展研究:自相关热点图和散点图。 本文之目的是展示“市场记忆”的季节性,它通过任意顺序增量的最大相关性来表达。
轻松快捷开发 MetaTrader 程序的函数库 (第 二十九部分) :延后交易请求 - 请求对象类 轻松快捷开发 MetaTrader 程序的函数库 (第 二十九部分) :延后交易请求 - 请求对象类
在之前的文章里,我们检验了延后交易请求的概念。 实际上,延后请求是由特定条件执行的正常交易订单。 在本文中,我们会创建完整的延后请求对象类 — 基准请求对象及其后代。