轻松快捷开发 MetaTrader 程序的函数库(第一部分)。 概念,数据管理和首期成果

Artyom Trishkin | 16 四月, 2019

内容

概述

在分析海量交易策略,订购用于 MetaTrader 5 和 MetaTrader 4 终端以及各种 MetaTrader 网站的应用程序,如脚本、指标和机器人开发订单时,我得出了一个结论,就是所有这些貌似多样性,大多基于相同的基本函数,动作和数值有规律地出现在不同的程序当中。

实际上,任何程序的逻辑都可以切分为许多相同的动作。 这些动作的结果可用来构建应用程序的逻辑。 MQL4/MQL5 论坛上反复提出一致性的问题即证实了这一点。 不同的用户针对他们解决的算法和任务提出了本质相同的问题。

考虑到这一切,我决定开发一个大型函数库,其中包括的内置函数用于请求和获取必要数据。 若要使用所提议函数库的数据集,用户只需采用问答方法来获取库函数收集的,以及存储在其数据库当中的大量完全不同的数据(按照不同的组合和排序参数)。

概念

任何数据类型都可以表示为具有相同属性的一组对象。
例如,时间序列可以表示为很长的有序列表,其每个后续单元存储的对象均具有一组属性,该属性与属于时间序列内的类似对象的所有其他集类型相同。 这种对象的数据由 MqlRates 结构表示:

存储有关价格、交易量和点差的信息结构

struct MqlRates 
  { 
   datetime time;         // 周期起始时间 
   double   open;         // 开盘价
   double   high;         // 每个周期的最高价
   double   low;          // 每个周期的最低价
   double   close;        // 收盘价
   long     tick_volume;  // 即时报价的交易量
   int      spread;       // 点差
   long     real_volume;  // 兑换交易量
  };

一组即时报价数据也可以被表示为有序列表,其中每个 MqlTick 结构表示的即时报价是具有固定属性集合的对象:

按品种存储最后价格的结构。 该结构设计用于及时获得有关当前价格的最必要数据。

struct MqlTick 
  { 
   datetime     time;          // 最后价格更新时间
   double       bid;           // 当前出价
   double       ask;           // 当前报价
   double       last;          // 最后一笔成交的当前价格(最后)
   ulong        volume;        // 当前最后价格的成交量
   long         time_msc;      // 最后一次价格更新的时间(以毫秒为单位) 
   uint         flags;         // 即时报价标志
   double       volume_real;   // 当前最后价格的交易量提升了准确度
  };

分析和准备程序逻辑所需的任何其他数据也被排列为简单的对象列表。

一个列表中的所有对象均具有此类对象特定的相同数据类型,无论它是订单、成交还是挂单的列表。 对于每个特定对象,我们将开发一个包括存储、排序和显示数据等最小必要函数的类。

函数库数据结构

如前所提,函数库由对象列表组成,并提供按该对象的任何自定义标准或可支持的属性选择任何列表项的能力。 函数将自行收集存储和处理所需的数据。 不需要人为干预。 用户仅应用其函数库查询的结果即可。

我将从最简单的话题开始,描述开发函数库的所有步骤,并逐步将新功能和数据添加到现有的程序中。 该函数库将在 “实时” 模式下开发。 在每篇文章中都会实现所需的编辑和附加内容。 我相信,这种表述风格最有效用,因为它引导读者进入开发。

函数库数据的最小结构是一组不同对象,用于描述数据的必要属性,而数据集合是存储相应对象的列表。

我们将使用来自标准库数据集合中 指向 CObject 类实例及其派生类的动态指针数组 的类来排列列表。 由于 CObject 标准库的基类对象 需要存储在这种列表当中,因此我们将简单地从 CObject 基类继承每个对象类。

首先实现

第一步,我们将为 MetaTrader 5 终端的对冲账户开发函数库。 在准备好最小功能后,我们将针对 MetaTrader 4 调整其操作。 下一步,在实现了帐户历史记录、当前市场持仓和挂单处理之后,我们将加入操控 MetaTrader 5 净持帐户的能力。 最后,我们将用各种完整函数填充函数库。

我们先从帐户历史开始。 许多策略以单一或另类的方式应用以往的交易结果,例如,前一笔成交的结果,它们的平仓方法(止损,止盈)、价位,等等。 此外,最后一天的结果可以作为当前操作的起点。 交易策略的种类是无限的,我们的任务是提供快速访问所有多样性。

首先,我们定义操控历史订单、成交、市价单和仓位等集合的术语。 MetaTrader 4 订单系统与 MetaTrader 5 订单系统有所区别。 虽然 MetaTrader 4 具有市价单和挂单,但在 MetaTrader 5 中,订单基本上只是生成一笔成交前的交易请求(市价单),而转变为成交后则会生成一笔持仓。 此外,还有挂单。 换言之,我们至少有三个对象 — 订单、成交和仓位。 为了避免被名称和定义分散注意力,我们调用类按照订单、成交和仓位来存储数据,这只是一个抽象的订单类。 进而,我们列表(文章开头提到的历史集合)中的所有内容都按照类型(订单、成交等)进行排序。

订单抽象类包含订单或成交的单据号,以及订单或成交的参数,其状态的全部数据。 状态会揭示对象的确切含义 — 一笔订单,一笔成交或仓位。

<终端数据文件夹>\MQL5\Include 中,创建保存库文件的 DoEasy 文件夹。 在此阶段,我们还需要在 DoEasy 文件夹中创建另两个目录:Objects 文件夹将存储对象类,而 Collections 将包含数据集合(对象列表)。
若要查找终端数据目录,请转至菜单的文件 -> 打开数据文件夹(Ctrl + Shift + D)。

现在我们可以创建第一个类(抽象订单类)。 在 Objects 文件夹中创建一个新类,并将其命名为 COrder (1)。 确保将标准库的 CObject (2) 类设置为基类。 在此情况下,我们的新对象类继承自 CObject 类,它可以放在标准库的 CArrayObj 对象列表中。


单击完成后,新的 Order.mqh (3) 文件将出现在函数库目录的 Objects 文件夹中。 目前,这只是类的一个片段:

//+------------------------------------------------------------------+
//|                                                        Order.mqh |
//|                                 版权所有 2018, MetaQuotes 软件公司 |
//|                             https://mql5.com/zh/users/artmedia70 |
//+------------------------------------------------------------------+
#property copyright "版权所有 2018, MetaQuotes 软件公司"
#property link      "https://mql5.com/zh/users/artmedia70"
#property version   "1.00"
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
class COrder : public CObject
  {
private:

public:
                     COrder();
                    ~COrder();
  };
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
COrder::COrder()
  {
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
COrder::~COrder()
  {
  }
//+------------------------------------------------------------------+

在尝试编译代码时,我们得到了五个错误。 它们指出派生 COrder 类的 CObject 基类缺失。 将 CObject 类文件包含在列表 中并再次编译。 目前一切都很好。

//+------------------------------------------------------------------+
//|                                                        Order.mqh |
//|                                 版权所有 2018, MetaQuotes 软件公司 |
//|                             https://mql5.com/zh/users/artmedia70 |
//+------------------------------------------------------------------+
#property copyright "版权所有 2018, MetaQuotes 软件公司"
#property link      "https://mql5.com/zh/users/artmedia70"
#property version   "1.00"
#include <Object.mqh>
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
class COrder : public CObject
  {
private:

public:
                     COrder();
                    ~COrder();
  };
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
COrder::COrder()
  {
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
COrder::~COrder()
  {
  }
//+------------------------------------------------------------------+

当选择下一个订单、成交或仓位时,在循环中按照订单、交易或仓位创建类的对象。 为了立即初始化对象字段,我们将创建一个私有 构造函数,并并传递对象类型(状态)和单据号以供后续识别。 但首先,我们将新的 Defines.mqh 包含文件放在项目的根文件夹中,该文件夹存储函数库所需的所有枚举,以及 宏替换,常量和结构。

目前,我们需要描述 订单状态 的枚举,和描述订单、成交或仓位的所有参数的枚举。 有三个订单参数的枚举: integer, realstring

//+------------------------------------------------------------------+
//|                                                      Defines.mqh |
//|                                 版权所有 2018, MetaQuotes 软件公司 |
//|                             https://mql5.com/zh/users/artmedia70 |
//+------------------------------------------------------------------+
#property copyright "版权所有 2018, MetaQuotes 软件公司"
#property link      "https://mql5.com/zh/users/artmedia70"
//+------------------------------------------------------------------+
//| 抽象订单类型 (状态)                                                |
//+------------------------------------------------------------------+
enum ENUM_ORDER_STATUS
  {
   ORDER_STATUS_MARKET_PENDING,                             // 当前挂单
   ORDER_STATUS_MARKET_ACTIVE,                              // 有效市价单
   ORDER_STATUS_HISTORY_ORDER,                              // 历史市价单
   ORDER_STATUS_HISTORY_PENDING,                            // 已删除的挂单
   ORDER_STATUS_BALANCE,                                    // 余额操作
   ORDER_STATUS_CREDIT,                                     // 赠金操作
   ORDER_STATUS_DEAL,                                       // 成交
   ORDER_STATUS_UNKNOWN                                     // 未知状态
  };
//+------------------------------------------------------------------+
//| 订单、成交、仓位整数型属性                                         |
//+------------------------------------------------------------------+
enum ENUM_ORDER_PROP_INTEGER
  {
   ORDER_PROP_TICKET = 0,                                   // 订单号
   ORDER_PROP_MAGIC,                                        // 订单魔幻数字
   ORDER_PROP_TIME_OPEN,                                    // 开单时间
   ORDER_PROP_TIME_CLOSE,                                   // 平单时间
   ORDER_PROP_TIME_EXP,                                     // 订单失效日期(针对挂单)
   ORDER_PROP_TYPE,                                         // 订单和成交类型
   ORDER_PROP_STATUS,                                       // 订单状态 (来自 ENUM_ORDER_STATUS 枚举)
   ORDER_PROP_REASON,                                       // 成交/订单/仓位原因或来源
   ORDER_PROP_POSITION_ID,                                  // 仓位 ID
   ORDER_PROP_POSITION_BY_ID,                               // 逆向仓位 ID
   ORDER_PROP_DEAL_ORDER,                                   // 成交执行所依据的订单
   ORDER_PROP_DEAL_ENTRY,                                   // 成交方向 – IN(入), OUT(出) 或 IN/OUT(翻转)
   ORDER_PROP_TIME_OPEN_MSC,                                // 开单时间,单位毫秒
   ORDER_PROP_TIME_CLOSE_MSC,                               // 平单时间,单位毫秒 (执行或删除时间 - ORDER_TIME_DONE_MSC)
   ORDER_PROP_TIME_UPDATE,                                  // 仓位变化时间,单位秒
   ORDER_PROP_TIME_UPDATE_MSC,                              // 仓位变化时间,单位毫秒
   ORDER_PROP_TICKET_FROM,                                  // 父订单号
   ORDER_PROP_TICKET_TO,                                    // 派生订单号
   ORDER_PROP_PROFIT_PT,                                    // 盈利,单位点数
   ORDER_PROP_CLOSE_BY_SL,                                  // 由止损平仓的标志
   ORDER_PROP_CLOSE_BY_TP,                                  // 由止盈平仓的标志
   ORDER_PROP_DIRECTION,                                    // 方向 (买入, 卖出)
  }; 
#define ORDER_PROP_INTEGER_TOTAL    (22)                    // 整数型属性的总数
//+------------------------------------------------------------------+
//| 订单、成交、仓位 实数型参数                                        |
//+------------------------------------------------------------------+
enum ENUM_ORDER_PROP_DOUBLE
  {
   ORDER_PROP_PRICE_OPEN = ORDER_PROP_INTEGER_TOTAL,        // 开单价 (MQL5 成交价)
   ORDER_PROP_PRICE_CLOSE,                                  // 平单价
   ORDER_PROP_PROFIT,                                       // 盈利
   ORDER_PROP_COMMISSION,                                   // 佣金
   ORDER_PROP_SWAP,                                         // 隔夜利息
   ORDER_PROP_VOLUME,                                       // 交易量
   ORDER_PROP_VOLUME_CURRENT,                               // 未执行的交易量
   ORDER_PROP_SL,                                           // 止损价
   ORDER_PROP_TP,                                           // 止盈价
   ORDER_PROP_PROFIT_FULL,                                  // 盈利+ 佣金 + 隔夜利息
   ORDER_PROP_PRICE_STOP_LIMIT,                             // 激活 StopLimit 订单时的限价订单价格
  };
#define ORDER_PROP_DOUBLE_TOTAL     (11)                    // 实数型属性的总数
//+------------------------------------------------------------------+
//| 订单、成交、仓位 字符串型参数                                        |
//+------------------------------------------------------------------+
enum ENUM_ORDER_PROP_STRING
  {
   ORDER_PROP_SYMBOL = (ORDER_PROP_INTEGER_TOTAL+ORDER_PROP_DOUBLE_TOTAL), // 订单品种
   ORDER_PROP_COMMENT,                                      // 订单注释
   ORDER_PROP_EXT_ID                                        // 外部交易系统中的订单 ID
  };
#define ORDER_PROP_STRING_TOTAL     (3)                     // 实字符串型属性的总数
//+------------------------------------------------------------------+

一些属性已被添加到枚举中:父订单号,衍生订单号,盈利点数,由止损或止盈平仓属性,方向和全部利润 — 考虑到佣金和隔夜利息。 这些数据通常在交易策略逻辑中运用,因此它们应存储在抽象订单类的字段中,并要在自定义程序中及时更新和接收。

为了将所有订单属性(整数,实数和字符串属性)规整在一起,请为每个属性类型创建包含三个枚举中每个属性的参数数量的宏替换
整数型属性枚举的编号从零开始,而其他类型属性的枚举编号从先前属性的总数开始。 所以,我们总是可以获得所需属性的索引,它是所请求属性的数量与此枚举的初始属性的数量之间的差值。

创建 Defines.mqh 文件后,将其与 COrder 类的当前文件连接,并在私有部分创建用于存储 订单号 的类成员变量,和三个用于存储 整数型实数型字符串型 订单属性的数组:

//+------------------------------------------------------------------+
//|                                                        Order.mqh |
//|                                 版权所有 2018, MetaQuotes 软件公司 |
//|                             https://mql5.com/zh/users/artmedia70 |
//+------------------------------------------------------------------+
#property copyright "版权所有 2018, MetaQuotes 软件公司"
#property link      "https://mql5.com/zh/users/artmedia70"
#property version   "1.00"
#include <Object.mqh>
#include "..\Defines.mqh"
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
class COrder : public CObject
  {
private:
   ulong             m_ticket;                                 // 选定的订单/成交单号 (MQL5)
   long              m_long_prop[ORDER_PROP_INTEGER_TOTAL];    // 整数型属性
   double            m_double_prop[ORDER_PROP_DOUBLE_TOTAL];   // 实数型属性
   string            m_string_prop[ORDER_PROP_STRING_TOTAL];   // 字符串型属性
public:
                     COrder();
                    ~COrder();
  };
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
COrder::COrder()
  {
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
COrder::~COrder()
  {
  }
//+------------------------------------------------------------------+

为了包含 Defines.mqh 文件,我们在引号中设置文件的相对路径,而不是应用包含 CObject 时使用的尖括号 (<>)。 这样做是为了转移函数库到任何其他目录时,文件之间的连接不会丢失,并且始终引用相对于当前目录的 Defines.mqh 文件的位置。

现在,我们在同一个私有部分创建两个方法。 第一个是 返回属性数组中所需属性的确切位置,而第二个是返回一个 在相应的受保护区域的 protected 构造函数。 保留默认构造函数,以便创建空订单对象时不必初始化其属性,并且删除析构函数(我们不需要它,它是在编译期间自动创建的):

//+------------------------------------------------------------------+
//|                                                        Order.mqh |
//|                                 版权所有 2018, MetaQuotes 软件公司 |
//|                             https://mql5.com/zh/users/artmedia70 |
//+------------------------------------------------------------------+
#property copyright "版权所有 2018, MetaQuotes 软件公司"
#property link      "https://mql5.com/zh/users/artmedia70"
#property version   "1.00"
#include <Object.mqh>
#include "..\Defines.mqh"
//+------------------------------------------------------------------+
//| 抽象订单类                                                        |
//+------------------------------------------------------------------+
class COrder : public CObject
  {
private:
   ulong             m_ticket;                                    // 选定的订单/成交单号 (MQL5)
   long              m_long_prop[ORDER_PROP_INTEGER_TOTAL];       // 整数型属性
   double            m_double_prop[ORDER_PROP_DOUBLE_TOTAL];      // 实数型属性
   string            m_string_prop[ORDER_PROP_STRING_TOTAL];      // 字符串型属性
   
   //--- 返回实数属性在数组中的实际位置索引
   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;}
public:
   //--- 默认构造函数
                     COrder(void){;}
protected:                                                                     
   //--- 受保护的参数构造函数                                
                     COrder(ENUM_ORDER_STATUS order_status,const ulong ticket);
  };
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
COrder::COrder(ENUM_ORDER_STATUS order_status,const ulong ticket)
  {
  }
//+------------------------------------------------------------------+

到目前为止,受保护的构造函数什么都未做。 它将用来在创建对象时立即初始化订单的所有属性,当创建对象时,应将单据号传递给类构造函数。 由于订单已被选定,我们可以获得所选订单的必要属性,并将它们写入对象属性的数组。 我们将在创建接收和返回订单数据的方法稍后时间执行此操作。

由于这是一个跨平台的函数库,因此使用分离的方法获取订单属性会更加便利。<br0 /> 在受保护的部分中,添加用于接收所选订单的 整数实数字符串 属性的方法说明。

//+------------------------------------------------------------------+
//|                                                        Order.mqh |
//|                                 版权所有 2018, MetaQuotes 软件公司 |
//|                             https://mql5.com/zh/users/artmedia70 |
//+------------------------------------------------------------------+
#property copyright "版权所有 2018, MetaQuotes 软件公司"
#property link      "https://mql5.com/zh/users/artmedia70"
#property version   "1.00"
#include <Object.mqh>
#include "..\Defines.mqh"
//+------------------------------------------------------------------+
//| 抽象订单类                                                        |
//+------------------------------------------------------------------+
class COrder : public CObject
  {
private:
   ulong             m_ticket;                                    // 选定的订单/成交单号 (MQL5)
   long              m_long_prop[ORDER_PROP_INTEGER_TOTAL];       // 整数型属性
   double            m_double_prop[ORDER_PROP_DOUBLE_TOTAL];      // 实数型属性
   string            m_string_prop[ORDER_PROP_STRING_TOTAL];      // 字符串型属性
   
   //--- 返回实数属性在数组中的实际位置索引
   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;}
public:
   //--- 默认构造函数
                     COrder(void){;}
protected:
   //--- 受保护的参数构造函数
                     COrder(ENUM_ORDER_STATUS order_status,const ulong ticket);
                     
   //--- 从其参数中获取并返回所选订单的整数型属性
   long              OrderMagicNumber(void)        const;
   long              OrderTicket(void)             const;
   long              OrderTicketFrom(void)         const;
   long              OrderTicketTo(void)           const;
   long              OrderPositionID(void)         const;
   long              OrderPositionByID(void)       const;
   long              OrderOpenTimeMSC(void)        const;
   long              OrderCloseTimeMSC(void)       const;
   long              OrderType(void)               const;
   long              OrderTypeByDirection(void)    const;
   long              OrderTypeFilling(void)        const;
   long              OrderTypeTime(void)           const;
   long              OrderReason(void)             const;
   long              DealOrder(void)               const;
   long              DealEntry(void)               const;
   bool              OrderCloseByStopLoss(void)    const;
   bool              OrderCloseByTakeProfit(void)  const;
   datetime          OrderOpenTime(void)           const;
   datetime          OrderCloseTime(void)          const;
   datetime          OrderExpiration(void)         const;
   datetime          PositionTimeUpdate(void)      const;
   datetime          PositionTimeUpdateMSC(void)   const;
   
   //--- 从参数中获取并返回所选订单的实数属性:(1)开盘价,(2)收盘价,(3)利润,
   //---  (4) 佣金, (5) 隔夜利息, (6) 交易量, (7) 未执行的交易量 (8) 止损价, (9) 止盈价 (10) StopLimit 订单价位
   double            OrderOpenPrice(void)          const;
   double            OrderClosePrice(void)         const;
   double            OrderProfit(void)             const;
   double            OrderCommission(void)         const;
   double            OrderSwap(void)               const;
   double            OrderVolume(void)             const;
   double            OrderVolumeCurrent(void)      const;
   double            OrderStopLoss(void)           const;
   double            OrderTakeProfit(void)         const;
   double            OrderPriceStopLimit(void)     const;
   
   //--- 从参数中获取并返回所选订单的字符串属性:(1)品名,(2)注释,(3)兑换 ID,
   string            OrderSymbol(void)             const;
   string            OrderComment(void)            const;
   string            OrderExternalID(void)         const;
   
//---
  };
//+------------------------------------------------------------------+
//| 闭合参数构造函数                                                   |
//+------------------------------------------------------------------+
COrder::COrder(ENUM_ORDER_STATUS order_status,const ulong ticket)
  {
  }
//+------------------------------------------------------------------+

目前,只声明了接收属性的方法。 虽然编译时没有错误,但它们尚未实现。 我们会利用刚刚添加的方法在类构造函数中填充订单属性数组。 当一切准备就绪时,这些数组能够令我们根据请求在程序中获取任何属性。

在标准库的 CObject 类中已声明了按照指定属性比较对象的虚拟方法。 不过,该方法应该在子类中实现。 所以,我们在抽象订单类中添加根据任何属性比较 COrder 对象的方法,以及用于访问订单属性的若干公共方法返回支持订单对象的特定属性标志的虚方法。 这些方法将在 COrder 类子类对象中实现。 稍后将需要按照其任何属性从集合列表中选择订单。 默认情况下,如果子类中未实现任何这些方法,则返回指示订单对该属性的支持标志。

//+------------------------------------------------------------------+
//|                                                        Order.mqh |
//|                                 版权所有 2018, MetaQuotes 软件公司 |
//|                             https://mql5.com/zh/users/artmedia70 |
//+------------------------------------------------------------------+
#property copyright "版权所有 2018, MetaQuotes 软件公司"
#property link      "https://mql5.com/zh/users/artmedia70"
#property version   "1.00"
#include <Object.mqh>
#include "..\Defines.mqh"
//+------------------------------------------------------------------+
//| 抽象订单类                                                        |
//+------------------------------------------------------------------+
class COrder : public CObject
  {
private:
   ulong             m_ticket;                                    // 选定的订单/成交单号 (MQL5)
   long              m_long_prop[ORDER_PROP_INTEGER_TOTAL];       // 整数型属性
   double            m_double_prop[ORDER_PROP_DOUBLE_TOTAL];      // 实数型属性
   string            m_string_prop[ORDER_PROP_STRING_TOTAL];      // 字符串型属性
   
   //--- 返回实数属性在数组中的实际位置索引
   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;}
public:
   //--- 默认构造函数
                     COrder(void){;}
protected:
   //--- 受保护的参数构造函数
                     COrder(ENUM_ORDER_STATUS order_status,const ulong ticket);
                     
   //--- 从其参数中获取并返回所选订单的整数型属性
   long              OrderMagicNumber(void)        const;
   long              OrderTicket(void)             const;
   long              OrderTicketFrom(void)         const;
   long              OrderTicketTo(void)           const;
   long              OrderPositionID(void)         const;
   long              OrderPositionByID(void)       const;
   long              OrderOpenTimeMSC(void)        const;
   long              OrderCloseTimeMSC(void)       const;
   long              OrderType(void)               const;
   long              OrderTypeByDirection(void)    const;
   long              OrderTypeFilling(void)        const;
   long              OrderTypeTime(void)           const;
   long              OrderReason(void)             const;
   long              DealOrder(void)               const;
   long              DealEntry(void)               const;
   bool              OrderCloseByStopLoss(void)    const;
   bool              OrderCloseByTakeProfit(void)  const;
   datetime          OrderOpenTime(void)           const;
   datetime          OrderCloseTime(void)          const;
   datetime          OrderExpiration(void)         const;
   datetime          PositionTimeUpdate(void)      const;
   datetime          PositionTimeUpdateMSC(void)   const;
   
   //--- 从参数中获取并返回所选订单的实数属性:(1)开盘价,(2)收盘价,(3)利润,
   //---  (4) 佣金, (5) 隔夜利息, (6) 交易量, (7) 未执行的交易量 (8) 止损价, (9) 止盈价 (10) StopLimit 订单价位
   double            OrderOpenPrice(void)          const;
   double            OrderClosePrice(void)         const;
   double            OrderProfit(void)             const;
   double            OrderCommission(void)         const;
   double            OrderSwap(void)               const;
   double            OrderVolume(void)             const;
   double            OrderVolumeCurrent(void)      const;
   double            OrderStopLoss(void)           const;
   double            OrderTakeProfit(void)         const;
   double            OrderPriceStopLimit(void)     const;
   
   //--- 从参数中获取并返回所选订单的字符串属性:(1)品名,(2)注释,(3)兑换 ID,
   string            OrderSymbol(void)             const;
   string            OrderComment(void)            const;
   string            OrderExternalID(void)         const;
   
public:
   //--- 从属性数组里返回 (1) 整数, (2) 实数,和 (3) 字符串型订单属性
   long              GetProperty(ENUM_ORDER_PROP_INTEGER property)      const { return m_long_prop[property];                    }
   double            GetProperty(ENUM_ORDER_PROP_DOUBLE property)       const { return m_double_prop[this.IndexProp(property)];  }
   string            GetProperty(ENUM_ORDER_PROP_STRING property)       const { return m_string_prop[this.IndexProp(property)];  }
   
   //--- 返回订单支持该属性的标志
   virtual bool      SupportProperty(ENUM_ORDER_PROP_INTEGER property)        { return true; }
   virtual bool      SupportProperty(ENUM_ORDER_PROP_DOUBLE property)         { return true; }
   virtual bool      SupportProperty(ENUM_ORDER_PROP_STRING property)         { return true; }
   
   //--- 按照所有可能的属性比较 COrder 对象
   virtual int       Compare(const CObject *node,const int mode=0) const;
   
//---
  };
//+------------------------------------------------------------------+
//| 闭合参数构造函数                                                   |
//+------------------------------------------------------------------+
COrder::COrder(ENUM_ORDER_STATUS order_status,const ulong ticket)
  {
  }
//+------------------------------------------------------------------+

实现按照指定属性比较两个订单的方法:

//+------------------------------------------------------------------+
//| 按照所有可能的属性比较 COrder 对象                                  |
//+------------------------------------------------------------------+
int COrder::Compare(const CObject *node,const int mode=0) const
  {
   const COrder *order_compared=node;
//--- 比较两个订单的整数型属性
   if(mode<ORDER_PROP_INTEGER_TOTAL)
     {
      long value_compared=order_compared.GetProperty((ENUM_ORDER_PROP_INTEGER)mode);
      long value_current=this.GetProperty((ENUM_ORDER_PROP_INTEGER)mode);
      return(value_current>value_compared ? 1 : value_current<value_compared ? -1 : 0);
     }
//--- 比较两个订单的实数型属性
   else if(mode<ORDER_PROP_DOUBLE_TOTAL+ORDER_PROP_INTEGER_TOTAL)
     {
      double value_compared=order_compared.GetProperty((ENUM_ORDER_PROP_DOUBLE)mode);
      double value_current=this.GetProperty((ENUM_ORDER_PROP_DOUBLE)mode);
      return(value_current>value_compared ? 1 : value_current<value_compared ? -1 : 0);
     }
//--- 比较两个订单的字符串型属性
   else if(mode<ORDER_PROP_DOUBLE_TOTAL+ORDER_PROP_INTEGER_TOTAL+ORDER_PROP_STRING_TOTAL)
     {
      string value_compared=order_compared.GetProperty((ENUM_ORDER_PROP_STRING)mode);
      string value_current=this.GetProperty((ENUM_ORDER_PROP_STRING)mode);
      return(value_current>value_compared ? 1 : value_current<value_compared ? -1 : 0);
     }
   return 0;
  }
//+------------------------------------------------------------------+

该方法将 指针传递给订单对象,其属性应与某个给定数值进行比较,以及订单属性枚举中的数值本身。
如果订单数值超过比较值,则系统返回 1,如果小于比较值,则系统返回 -1,否则返回 0。 执行比较的订单列表应按照所比较属性进行初步排序

现在,我们来实现接收订单属性,并将它们写入属性数组的方法。 这些是先前在私有部分中声明的方法。 由于接收订单属性的方法是跨平台的,因此我们使用接收 EA 魔幻数字作为示例来分析它们:

//+------------------------------------------------------------------+
//| 返回魔幻数字                                                   |
//+------------------------------------------------------------------+
long COrder::OrderMagicNumber() const
  {
#ifdef __MQL4__
   return ::OrderMagicNumber();
#else
   long res=0;
   switch((ENUM_ORDER_STATUS)this.GetProperty(ORDER_PROP_STATUS))
     {
      case ORDER_STATUS_MARKET_ACTIVE     : res=::PositionGetInteger(POSITION_MAGIC);           break;
      case ORDER_STATUS_MARKET_PENDING    : res=::OrderGetInteger(ORDER_MAGIC);                 break;
      case ORDER_STATUS_DEAL              : res=::HistoryDealGetInteger(m_ticket,DEAL_MAGIC);   break;
      case ORDER_STATUS_HISTORY_PENDING   :
      case ORDER_STATUS_HISTORY_ORDER     : res=::HistoryOrderGetInteger(m_ticket,ORDER_MAGIC); break;
      default                             : res=0;                                              break;
     }
   return res;
#endif
  }
//+------------------------------------------------------------------+

如果这是针对 MQL4 的代码,则由 OrderMagicNumber() MQL5 函数返回魔幻数字。 否则, 检查订单状态。 根据我们正在处理的内容,返回仓位,订单或成交的魔幻数字。

读取和写入高亮显示的订单/成交/仓位属性的其余方法以相同的方式进行。 您可以自己分析它们。

获取订单/成交/仓位整数型属性的方法:
//+------------------------------------------------------------------+
//| 返回单据号                                                        |
//+------------------------------------------------------------------+
long COrder::OrderTicket(void) const
  {
#ifdef __MQL4__
   return ::OrderTicket();
#else
   long res=0;
   switch((ENUM_ORDER_STATUS)this.GetProperty(ORDER_PROP_STATUS))
     {
      case ORDER_STATUS_MARKET_ACTIVE     :
      case ORDER_STATUS_MARKET_PENDING    :
      case ORDER_STATUS_HISTORY_PENDING   :
      case ORDER_STATUS_HISTORY_ORDER     :
      case ORDER_STATUS_DEAL              : res=(long)m_ticket;                                 break;
      default                             : res=0;                                              break;
     }
   return res;
#endif
  }
//+------------------------------------------------------------------+
//| 返回父订单号                                                   |
//+------------------------------------------------------------------+
long COrder::OrderTicketFrom(void) const
  {
   long ticket=0;
#ifdef __MQL4__
   string order_comment=::OrderComment();
   if(::StringFind(order_comment,"from #")>WRONG_VALUE) ticket=::StringToInteger(::StringSubstr(order_comment,6));
#endif
   return ticket;
  }
//+------------------------------------------------------------------+
//| 返回子订单号                                                   |
//+------------------------------------------------------------------+
long COrder::OrderTicketTo(void) const
  {
   long ticket=0;
#ifdef __MQL4__
   string order_comment=::OrderComment();
   if(::StringFind(order_comment,"to #")>WRONG_VALUE) ticket=::StringToInteger(::StringSubstr(order_comment,4));
#endif
   return ticket;
  }
//+------------------------------------------------------------------+
//| 返回仓位的 ID                                           |
//+------------------------------------------------------------------+
long COrder::OrderPositionID(void) const
  {
#ifdef __MQL4__
   return ::OrderMagicNumber();
#else
   long res=0;
   switch((ENUM_ORDER_STATUS)this.GetProperty(ORDER_PROP_STATUS))
     {
      case ORDER_STATUS_MARKET_ACTIVE     : res=::PositionGetInteger(POSITION_IDENTIFIER);            break;
      case ORDER_STATUS_MARKET_PENDING    : res=::OrderGetInteger(ORDER_POSITION_ID);                 break;
      case ORDER_STATUS_HISTORY_PENDING   :
      case ORDER_STATUS_HISTORY_ORDER     : res=::HistoryOrderGetInteger(m_ticket,ORDER_POSITION_ID); break;
      case ORDER_STATUS_DEAL              : res=::HistoryDealGetInteger(m_ticket,DEAL_POSITION_ID);   break;
      default                             : res=0;                                                    break;
     }
   return res;
#endif
  }
//+------------------------------------------------------------------+
//| 返回翻转仓位的 ID                                                |
//+------------------------------------------------------------------+
long COrder::OrderPositionByID(void) const
  {
#ifdef __MQL4__
   return 0;
#else
   long res=0;
   switch((ENUM_ORDER_STATUS)this.GetProperty(ORDER_PROP_STATUS))
     {
      case ORDER_STATUS_MARKET_PENDING    : res=::OrderGetInteger(ORDER_POSITION_BY_ID);                 break;
      case ORDER_STATUS_HISTORY_PENDING   :
      case ORDER_STATUS_HISTORY_ORDER     : res=::HistoryOrderGetInteger(m_ticket,ORDER_POSITION_BY_ID); break;
      default                             : res=0;                                                       break;
     }
   return res;
#endif
  }
//+------------------------------------------------------------------+
//| 返回开单时间,单位毫秒                                              |
//+------------------------------------------------------------------+
long COrder::OrderOpenTimeMSC(void) const
  {
#ifdef __MQL4__
   return (long)::OrderOpenTime();
#else
   long res=0;
   switch((ENUM_ORDER_STATUS)this.GetProperty(ORDER_PROP_STATUS))
     {
      case ORDER_STATUS_MARKET_ACTIVE     : res=::PositionGetInteger(POSITION_TIME_MSC);                 break;
      case ORDER_STATUS_MARKET_PENDING    : res=::OrderGetInteger(ORDER_TIME_SETUP_MSC);                 break;
      case ORDER_STATUS_HISTORY_PENDING   :
      case ORDER_STATUS_HISTORY_ORDER     : res=::HistoryOrderGetInteger(m_ticket,ORDER_TIME_SETUP_MSC); break;
      case ORDER_STATUS_DEAL              : res=::HistoryDealGetInteger(m_ticket,DEAL_TIME_MSC);         break;
      default                             : res=0;                                                       break;
     }
   return res;
#endif
  }
//+------------------------------------------------------------------+
//| 返回平单时间,单位毫秒                                              |
//+------------------------------------------------------------------+
long COrder::OrderCloseTimeMSC(void) const
  {
#ifdef __MQL4__
   return (long)::OrderCloseTime();
#else
   long res=0;
   switch((ENUM_ORDER_STATUS)this.GetProperty(ORDER_PROP_STATUS))
     {
      case ORDER_STATUS_HISTORY_PENDING   :
      case ORDER_STATUS_HISTORY_ORDER     : res=::HistoryOrderGetInteger(m_ticket,ORDER_TIME_DONE_MSC);     break;
      case ORDER_STATUS_DEAL              : res=(datetime)::HistoryDealGetInteger(m_ticket,DEAL_TIME_MSC);  break;
      default                             : res=0;                                                          break;
     }
   return res;
#endif
  }
//+------------------------------------------------------------------+
//| 返回类型                                                        |
//+------------------------------------------------------------------+
long COrder::OrderType(void) const
  {
#ifdef __MQL4__
   return (long)::OrderType();
#else
   long res=0;
   switch((ENUM_ORDER_STATUS)this.GetProperty(ORDER_PROP_STATUS))
     {
      case ORDER_STATUS_MARKET_ACTIVE     : res=::PositionGetInteger(POSITION_TYPE);            break;
      case ORDER_STATUS_MARKET_PENDING    : res=::OrderGetInteger(ORDER_TYPE);                  break;
      case ORDER_STATUS_HISTORY_PENDING   :
      case ORDER_STATUS_HISTORY_ORDER     : res=::HistoryOrderGetInteger(m_ticket,ORDER_TYPE);  break;
      case ORDER_STATUS_DEAL              : res=::HistoryDealGetInteger(m_ticket,DEAL_TYPE);    break;
      default                             : res=0;                                              break;
     }
   return res;
#endif
  }
//+------------------------------------------------------------------+
//| 返回方向类型                                                       |
//+------------------------------------------------------------------+
long COrder::OrderTypeByDirection(void) const
  {
   ENUM_ORDER_STATUS status=(ENUM_ORDER_STATUS)this.GetProperty(ORDER_PROP_STATUS);
   if(status==ORDER_STATUS_MARKET_ACTIVE)
     {
      return(this.OrderType()==POSITION_TYPE_BUY ? ORDER_TYPE_BUY : ORDER_TYPE_SELL);
     }
   if(status==ORDER_STATUS_MARKET_PENDING || status==ORDER_STATUS_HISTORY_PENDING)
     {
      return
        (
         this.OrderType()==ORDER_TYPE_BUY_LIMIT || 
         this.OrderType()==ORDER_TYPE_BUY_STOP 
         #ifdef __MQL5__                        ||
         this.OrderType()==ORDER_TYPE_BUY_STOP_LIMIT 
         #endif ? 
         ORDER_TYPE_BUY : 
         ORDER_TYPE_SELL
        );
     }
   if(status==ORDER_STATUS_HISTORY_ORDER)
     {
      return this.OrderType();
     }
   return WRONG_VALUE;
  }
//+------------------------------------------------------------------+
//| 按差额返回执行类型                                                  |
//+------------------------------------------------------------------+
long COrder::OrderTypeFilling(void) const
  {
#ifdef __MQL4__
   return (long)ORDER_FILLING_RETURN;
#else 
   long res=0;
   switch((ENUM_ORDER_STATUS)this.GetProperty(ORDER_PROP_STATUS))
     {
      case ORDER_STATUS_MARKET_PENDING    : res=::OrderGetInteger(ORDER_TYPE_FILLING);                break;
      case ORDER_STATUS_HISTORY_PENDING   :
      case ORDER_STATUS_HISTORY_ORDER     : res=::HistoryOrderGetInteger(m_ticket,ORDER_TYPE_FILLING);break;
      default                             : res=0;                                                    break;
     }
   return res;
#endif 
  }
//+------------------------------------------------------------------+
//| 退货订单生存期                                                   |
//+------------------------------------------------------------------+
long COrder::OrderTypeTime(void) const
  {
#ifdef __MQL4__
   return (long)ORDER_TIME_GTC;
#else 
   long res=0;
   switch((ENUM_ORDER_STATUS)this.GetProperty(ORDER_PROP_STATUS))
     {
      case ORDER_STATUS_MARKET_PENDING    : res=::OrderGetInteger(ORDER_TYPE_TIME);                break;
      case ORDER_STATUS_HISTORY_PENDING   :
      case ORDER_STATUS_HISTORY_ORDER     : res=::HistoryOrderGetInteger(m_ticket,ORDER_TYPE_TIME);break;
      default                             : res=0;                                                 break;
     }
   return res;
#endif 
  }
//+------------------------------------------------------------------+
//| 订单原因或来源                                                   |
//+------------------------------------------------------------------+
long COrder::OrderReason(void) const
  {
#ifdef __MQL4__
   return
     (
      this.OrderCloseByStopLoss()   ?  ORDER_REASON_SL      :
      this.OrderCloseByTakeProfit() ?  ORDER_REASON_TP      :  
      this.OrderMagicNumber()!=0    ?  ORDER_REASON_EXPERT  : WRONG_VALUE
     );
#else 
   long res=WRONG_VALUE;
   switch((ENUM_ORDER_STATUS)this.GetProperty(ORDER_PROP_STATUS))
     {
      case ORDER_STATUS_MARKET_ACTIVE     : res=::PositionGetInteger(POSITION_REASON);          break;
      case ORDER_STATUS_MARKET_PENDING    : res=::OrderGetInteger(ORDER_REASON);                break;
      case ORDER_STATUS_HISTORY_PENDING   :
      case ORDER_STATUS_HISTORY_ORDER     : res=::HistoryOrderGetInteger(m_ticket,ORDER_REASON);break;
      case ORDER_STATUS_DEAL              : res=::HistoryDealGetInteger(m_ticket,DEAL_REASON);  break;
      default                             : res=WRONG_VALUE;                                    break;
     }
   return res;
#endif 
  }
//+------------------------------------------------------------------+
//| 执行成交依据的订单                                              |
//+------------------------------------------------------------------+
long COrder::DealOrder(void) const
  {
#ifdef __MQL4__
   return ::OrderTicket();
#else 
   long res=0;
   switch((ENUM_ORDER_STATUS)this.GetProperty(ORDER_PROP_STATUS))
     {
      case ORDER_STATUS_MARKET_ACTIVE     : res=::PositionGetInteger(POSITION_IDENTIFIER);            break;
      case ORDER_STATUS_MARKET_PENDING    : res=::OrderGetInteger(ORDER_POSITION_ID);                 break;
      case ORDER_STATUS_HISTORY_PENDING   :
      case ORDER_STATUS_HISTORY_ORDER     : res=::HistoryOrderGetInteger(m_ticket,ORDER_POSITION_ID); break;
      case ORDER_STATUS_DEAL              : res=::HistoryDealGetInteger(m_ticket,DEAL_ORDER);         break;
      default                             : res=0;                                                    break;
     }
   return res;
#endif 
  }
//+------------------------------------------------------------------+
//| 成交方向 IN, OUT, IN/OUT                                        |
//+------------------------------------------------------------------+
long COrder::DealEntry(void) const
  {
#ifdef __MQL4__
   return ::OrderType();
#else 
   long res=WRONG_VALUE;
   switch((ENUM_ORDER_STATUS)this.GetProperty(ORDER_PROP_STATUS))
     {
      case ORDER_STATUS_DEAL  : res=::HistoryDealGetInteger(m_ticket,DEAL_ENTRY);break;
      default                 : res=WRONG_VALUE;                                 break;
     }
   return res;
#endif 
  }
//+------------------------------------------------------------------+
//| 返回由止损平仓的标志                                              |
//+------------------------------------------------------------------+
bool COrder::OrderCloseByStopLoss(void) const
  {
#ifdef __MQL4__
   return(::StringFind(::OrderComment(),"[sl")>WRONG_VALUE);
#else 
   return
     (
      this.Status()==ORDER_STATUS_HISTORY_ORDER ? this.OrderReason()==ORDER_REASON_SL : 
      this.Status()==ORDER_STATUS_DEAL ? this.OrderReason()==DEAL_REASON_SL : false
     );
#endif 
  }
//+------------------------------------------------------------------+
//| 返回由止盈平仓的标志                                              |
//+------------------------------------------------------------------+
bool COrder::OrderCloseByTakeProfit(void) const
  {
#ifdef __MQL4__
   return(::StringFind(::OrderComment(),"[tp")>WRONG_VALUE);
#else 
   return
     (
      this.Status()==ORDER_STATUS_HISTORY_ORDER ? this.OrderReason()==ORDER_REASON_TP : 
      this.Status()==ORDER_STATUS_DEAL ? this.OrderReason()==DEAL_REASON_TP : false
     );
#endif 
  }
//+------------------------------------------------------------------+
//| 返回开单时间                                                   |
//+------------------------------------------------------------------+
datetime COrder::OrderOpenTime(void) const
  {
#ifdef __MQL4__
   return ::OrderOpenTime();
#else 
   datetime res=0;
   switch((ENUM_ORDER_STATUS)this.GetProperty(ORDER_PROP_STATUS))
     {
      case ORDER_STATUS_MARKET_ACTIVE     : res=(datetime)::PositionGetInteger(POSITION_TIME);                 break;
      case ORDER_STATUS_MARKET_PENDING    : res=(datetime)::OrderGetInteger(ORDER_TIME_SETUP);                 break;
      case ORDER_STATUS_HISTORY_PENDING   :
      case ORDER_STATUS_HISTORY_ORDER     : res=(datetime)::HistoryOrderGetInteger(m_ticket,ORDER_TIME_SETUP); break;
      case ORDER_STATUS_DEAL              : res=(datetime)::HistoryDealGetInteger(m_ticket,DEAL_TIME);         break;
      default                             : res=0;                                                             break;
     }
   return res;
#endif 
  }
//+------------------------------------------------------------------+
//| 返回平单时间                                                   |
//+------------------------------------------------------------------+
datetime COrder::OrderCloseTime(void) const
  {
#ifdef __MQL4__
   return ::OrderCloseTime();
#else 
   datetime res=0;
   switch((ENUM_ORDER_STATUS)this.GetProperty(ORDER_PROP_STATUS))
     {
      case ORDER_STATUS_HISTORY_PENDING   :
      case ORDER_STATUS_HISTORY_ORDER     : res=(datetime)::HistoryOrderGetInteger(m_ticket,ORDER_TIME_DONE);  break;
      case ORDER_STATUS_DEAL              : res=(datetime)::HistoryDealGetInteger(m_ticket,DEAL_TIME);         break;
      default                             : res=0;                                                             break;
     }
   return res;
#endif 
  }
//+------------------------------------------------------------------+
//| 返回失效时间                                                       |
//+------------------------------------------------------------------+
datetime COrder::OrderExpiration(void) const
  {
#ifdef __MQL4__
   return ::OrderExpiration();
#else 
   datetime res=0;
   switch((ENUM_ORDER_STATUS)this.GetProperty(ORDER_PROP_STATUS))
     {
      case ORDER_STATUS_MARKET_PENDING    : res=(datetime)::OrderGetInteger(ORDER_TIME_EXPIRATION);                  break;
      case ORDER_STATUS_HISTORY_PENDING   : res=(datetime)::HistoryOrderGetInteger(m_ticket,ORDER_TIME_EXPIRATION);  break;
      default                             : res=0;                                                                   break;
     }
   return res;
#endif 
  }
//+------------------------------------------------------------------+
//| 仓位变更时间(秒)                                                  |
//+------------------------------------------------------------------+
datetime COrder::PositionTimeUpdate(void) const
  {
#ifdef __MQL4__
   return 0;
#else 
   datetime res=0;
   switch((ENUM_ORDER_STATUS)this.GetProperty(ORDER_PROP_STATUS))
     {
      case ORDER_STATUS_MARKET_ACTIVE  : res=(datetime)::PositionGetInteger(POSITION_TIME_UPDATE); break;
      default                          : res=0;                                                    break;
     }
   return res;
#endif 
  }
//+------------------------------------------------------------------+
//| 仓位变更时间(毫秒)                                              |
//+------------------------------------------------------------------+
datetime COrder::PositionTimeUpdateMSC(void) const
  {
#ifdef __MQL4__
   return 0;
#else 
   datetime res=0;
   switch((ENUM_ORDER_STATUS)this.GetProperty(ORDER_PROP_STATUS))
     {
      case ORDER_STATUS_MARKET_ACTIVE  : res=(datetime)::PositionGetInteger(POSITION_TIME_UPDATE_MSC);break;
      default                          : res=0;                                                       break;
     }
   return res;
#endif 
  }
//+------------------------------------------------------------------+

获取订单/成交/仓位实数属性的方法:

//+------------------------------------------------------------------+
//| 返回开单价                                                        |
//+------------------------------------------------------------------+
double COrder::OrderOpenPrice(void) const
  {
#ifdef __MQL4__
   return ::OrderOpenPrice();
#else 
   double res=0;
   switch((ENUM_ORDER_STATUS)this.GetProperty(ORDER_PROP_STATUS))
     {
      case ORDER_STATUS_MARKET_ACTIVE     : res=::PositionGetDouble(POSITION_PRICE_OPEN);          break;
      case ORDER_STATUS_MARKET_PENDING    : res=::OrderGetDouble(ORDER_PRICE_OPEN);                break;
      case ORDER_STATUS_HISTORY_PENDING   :
      case ORDER_STATUS_HISTORY_ORDER     : res=::HistoryOrderGetDouble(m_ticket,ORDER_PRICE_OPEN);break;
      case ORDER_STATUS_DEAL              : res=::HistoryDealGetDouble(m_ticket,DEAL_PRICE);       break;
      default                             : res=0;                                                 break;
     }
   return res;
#endif 
  }
//+------------------------------------------------------------------+
//| 返回平单价格                                                       |
//+------------------------------------------------------------------+
double COrder::OrderClosePrice(void) const
  {
#ifdef __MQL4__
   return ::OrderClosePrice();
#else 
   double res=0;
   switch((ENUM_ORDER_STATUS)this.GetProperty(ORDER_PROP_STATUS))
     {
      case ORDER_STATUS_HISTORY_PENDING   :
      case ORDER_STATUS_HISTORY_ORDER     : res=::HistoryOrderGetDouble(m_ticket,ORDER_PRICE_OPEN);break;
      case ORDER_STATUS_DEAL              : res=::HistoryDealGetDouble(m_ticket,DEAL_PRICE);       break;
      default                             : res=0;                                                 break;
     }
   return res;
#endif 
  }
//+------------------------------------------------------------------+
//| 返回盈利                                                   |
//+------------------------------------------------------------------+
double COrder::OrderProfit(void) const
  {
#ifdef __MQL4__
   return ::OrderProfit();
#else 
   double res=0;
   switch((ENUM_ORDER_STATUS)this.GetProperty(ORDER_PROP_STATUS))
     {
      case ORDER_STATUS_MARKET_ACTIVE  : res=::PositionGetDouble(POSITION_PROFIT);        break;
      case ORDER_STATUS_DEAL           : res=::HistoryDealGetDouble(m_ticket,DEAL_PROFIT);break;
      default                          : res=0;                                           break;
     }
   return res;
#endif 
  }
//+------------------------------------------------------------------+
//| 返回佣金                                                        |
//+------------------------------------------------------------------+
double COrder::OrderCommission(void) const
  {
#ifdef __MQL4__
   return ::OrderCommission();
#else 
   double res=0;
   switch((ENUM_ORDER_STATUS)this.GetProperty(ORDER_PROP_STATUS))
     {
      case ORDER_STATUS_DEAL  : res=::HistoryDealGetDouble(m_ticket,DEAL_COMMISSION);  break;
      default                 : res=0;                                                 break;
     }
   return res;
#endif 
  }
//+------------------------------------------------------------------+
//| 返回隔夜利息                                                       |
//+------------------------------------------------------------------+
double COrder::OrderSwap(void) const
  {
#ifdef __MQL4__
   return ::OrderSwap();
#else 
   double res=0;
   switch((ENUM_ORDER_STATUS)this.GetProperty(ORDER_PROP_STATUS))
     {
      case ORDER_STATUS_MARKET_ACTIVE  : res=::PositionGetDouble(POSITION_SWAP);          break;
      case ORDER_STATUS_DEAL           : res=::HistoryDealGetDouble(m_ticket,DEAL_SWAP);  break;
      default                          : res=0;                                           break;
     }
   return res;
#endif 
  }
//+------------------------------------------------------------------+
//| 返回交易量                                                        |
//+------------------------------------------------------------------+
double COrder::OrderVolume(void) const
  {
#ifdef __MQL4__
   return ::OrderLots();
#else 
   double res=0;
   switch((ENUM_ORDER_STATUS)this.GetProperty(ORDER_PROP_STATUS))
     {
      case ORDER_STATUS_MARKET_ACTIVE     : res=::PositionGetDouble(POSITION_VOLUME);                    break;
      case ORDER_STATUS_MARKET_PENDING    : res=::OrderGetDouble(ORDER_VOLUME_INITIAL);                  break;
      case ORDER_STATUS_HISTORY_PENDING   :
      case ORDER_STATUS_HISTORY_ORDER     : res=::HistoryOrderGetDouble(m_ticket,ORDER_VOLUME_INITIAL);  break;
      case ORDER_STATUS_DEAL              : res=::HistoryDealGetDouble(m_ticket,DEAL_VOLUME);            break;
      default                             : res=0;                                                       break;
     }
   return res;
#endif 
  }
//+------------------------------------------------------------------+
//| 返回未执行的交易量                                                  |
//+------------------------------------------------------------------+
double COrder::OrderVolumeCurrent(void) const
  {
#ifdef __MQL4__
   return ::OrderLots();
#else 
   double res=0;
   switch((ENUM_ORDER_STATUS)this.GetProperty(ORDER_PROP_STATUS))
     {
      case ORDER_STATUS_MARKET_PENDING    : res=::OrderGetDouble(ORDER_VOLUME_CURRENT);                  break;
      case ORDER_STATUS_HISTORY_PENDING   :
      case ORDER_STATUS_HISTORY_ORDER     : res=::HistoryOrderGetDouble(m_ticket,ORDER_VOLUME_CURRENT);  break;
      default                             : res=0;                                                       break;
     }
   return res;
#endif 
  }
//+------------------------------------------------------------------+
//| 返回止损价位                                                       |
//+------------------------------------------------------------------+
double COrder::OrderStopLoss(void) const
  {
#ifdef __MQL4__
   return ::OrderStopLoss();
#else 
   double res=0;
   switch((ENUM_ORDER_STATUS)this.GetProperty(ORDER_PROP_STATUS))
     {
      case ORDER_STATUS_MARKET_ACTIVE     : res=::PositionGetDouble(POSITION_SL);            break;
      case ORDER_STATUS_MARKET_PENDING    : res=::OrderGetDouble(ORDER_SL);                  break;
      case ORDER_STATUS_HISTORY_PENDING   :
      case ORDER_STATUS_HISTORY_ORDER     : res=::HistoryOrderGetDouble(m_ticket,ORDER_SL);  break;
      default                             : res=0;                                           break;
     }
   return res;
#endif 
  }
//+------------------------------------------------------------------+
//| 返回止盈价位                                                       |
//+------------------------------------------------------------------+
double COrder::OrderTakeProfit(void) const
  {
#ifdef __MQL4__
   return ::OrderTakeProfit();
#else 
   double res=0;
   switch((ENUM_ORDER_STATUS)this.GetProperty(ORDER_PROP_STATUS))
     {
      case ORDER_STATUS_MARKET_ACTIVE     : res=::PositionGetDouble(POSITION_TP);            break;
      case ORDER_STATUS_MARKET_PENDING    : res=::OrderGetDouble(ORDER_TP);                  break;
      case ORDER_STATUS_HISTORY_PENDING   :
      case ORDER_STATUS_HISTORY_ORDER     : res=::HistoryOrderGetDouble(m_ticket,ORDER_TP);  break;
      default                             : res=0;                                           break;
     }
   return res;
#endif 
  }
//+------------------------------------------------------------------+
//| 返回限价订单价格                                                   |
//| 若是 StopLimit 订单被触发                                        |
//+------------------------------------------------------------------+
double COrder::OrderPriceStopLimit(void) const
  {
#ifdef __MQL4__
   return 0;
#else 
   double res=0;
   switch((ENUM_ORDER_STATUS)this.GetProperty(ORDER_PROP_STATUS))
     {
      case ORDER_STATUS_MARKET_PENDING    : res=::OrderGetDouble(ORDER_PRICE_STOPLIMIT);                 break;
      case ORDER_STATUS_HISTORY_PENDING   :
      case ORDER_STATUS_HISTORY_ORDER     : res=::HistoryOrderGetDouble(m_ticket,ORDER_PRICE_STOPLIMIT); break;
      default                             : res=0;                                                       break;
     }
   return res;
#endif 
  }
//+------------------------------------------------------------------+

获取订单/成交/仓位字符串属性的方法:

//+------------------------------------------------------------------+
//| 返回品名                                                        |
//+------------------------------------------------------------------+
string COrder::OrderSymbol(void) const
  {
#ifdef __MQL4__
   return ::OrderSymbol();
#else 
   string res="";
   switch((ENUM_ORDER_STATUS)this.GetProperty(ORDER_PROP_STATUS))
     {
      case ORDER_STATUS_MARKET_ACTIVE     : res=::PositionGetString(POSITION_SYMBOL);           break;
      case ORDER_STATUS_MARKET_PENDING    : res=::OrderGetString(ORDER_SYMBOL);                 break;
      case ORDER_STATUS_HISTORY_PENDING   :
      case ORDER_STATUS_HISTORY_ORDER     : res=::HistoryOrderGetString(m_ticket,ORDER_SYMBOL); break;
      case ORDER_STATUS_DEAL              : res=::HistoryDealGetString(m_ticket,DEAL_SYMBOL);   break;
      default                             : res="";                                             break;
     }
   return res;
#endif 
  }
//+------------------------------------------------------------------+
//| 返回注释                                                        |
//+------------------------------------------------------------------+
string COrder::OrderComment(void) const
  {
#ifdef __MQL4__
   return ::OrderComment();
#else 
   string res="";
   switch((ENUM_ORDER_STATUS)this.GetProperty(ORDER_PROP_STATUS))
     {
      case ORDER_STATUS_MARKET_ACTIVE     : res=::PositionGetString(POSITION_COMMENT);          break;
      case ORDER_STATUS_MARKET_PENDING    : res=::OrderGetString(ORDER_COMMENT);                break;
      case ORDER_STATUS_HISTORY_PENDING   :
      case ORDER_STATUS_HISTORY_ORDER     : res=::HistoryOrderGetString(m_ticket,ORDER_COMMENT);break;
      case ORDER_STATUS_DEAL              : res=::HistoryDealGetString(m_ticket,DEAL_COMMENT);  break;
      default                             : res="";                                             break;
     }
   return res;
#endif 
  }
//+------------------------------------------------------------------+
//| 返回兑换所用的 ID                                                |
//+------------------------------------------------------------------+
string COrder::OrderExternalID(void) const
  {
#ifdef __MQL4__
   return "";
#else 
   string res="";
   switch((ENUM_ORDER_STATUS)this.GetProperty(ORDER_PROP_STATUS))
     {
      case ORDER_STATUS_MARKET_PENDING    : res=::OrderGetString(ORDER_EXTERNAL_ID);                  break;
      case ORDER_STATUS_HISTORY_PENDING   :
      case ORDER_STATUS_HISTORY_ORDER     : res=::HistoryOrderGetString(m_ticket,ORDER_EXTERNAL_ID);  break;
      case ORDER_STATUS_DEAL              : res=::HistoryDealGetString(m_ticket,DEAL_EXTERNAL_ID);    break;
      default                             : res="";                                                   break;
     }
   return res;
#endif 
  }
//+------------------------------------------------------------------+

我们已经声明并实现了从订单数据接收属性的私有方法。

现在是时候来实现受保护的类构造函数,按照传递给构造函数的订单输出所有属性的方法。

实现受保护的类构造函数:

//+------------------------------------------------------------------+
//| 闭合参数构造函数                                                   |
//+------------------------------------------------------------------+
COrder::COrder(ENUM_ORDER_STATUS order_status,const ulong ticket)
  {
//--- 保存整数型属性
   m_ticket=ticket;
   m_long_prop[ORDER_PROP_STATUS]                              = order_status;
   m_long_prop[ORDER_PROP_MAGIC]                               = this.OrderMagicNumber();
   m_long_prop[ORDER_PROP_TICKET]                              = this.OrderTicket();
   m_long_prop[ORDER_PROP_TIME_OPEN]                           = (long)(ulong)this.OrderOpenTime();
   m_long_prop[ORDER_PROP_TIME_CLOSE]                          = (long)(ulong)this.OrderCloseTime();
   m_long_prop[ORDER_PROP_TIME_EXP]                            = (long)(ulong)this.OrderExpiration();
   m_long_prop[ORDER_PROP_TYPE]                                = this.OrderType();
   m_long_prop[ORDER_PROP_DIRECTION]                           = this.OrderTypeByDirection();
   m_long_prop[ORDER_PROP_POSITION_ID]                         = this.OrderPositionID();
   m_long_prop[ORDER_PROP_REASON]                              = this.OrderReason();
   m_long_prop[ORDER_PROP_DEAL_ORDER]                          = this.DealOrder();
   m_long_prop[ORDER_PROP_DEAL_ENTRY]                          = this.DealEntry();
   m_long_prop[ORDER_PROP_POSITION_BY_ID]                      = this.OrderPositionByID();
   m_long_prop[ORDER_PROP_TIME_OPEN_MSC]                       = this.OrderOpenTimeMSC();
   m_long_prop[ORDER_PROP_TIME_CLOSE_MSC]                      = this.OrderCloseTimeMSC();
   m_long_prop[ORDER_PROP_TIME_UPDATE]                         = (long)(ulong)this.PositionTimeUpdate();
   m_long_prop[ORDER_PROP_TIME_UPDATE_MSC]                     = (long)(ulong)this.PositionTimeUpdateMSC();
   
//--- 保存实数型属性
   m_double_prop[this.IndexProp(ORDER_PROP_PRICE_OPEN)]        = this.OrderOpenPrice();
   m_double_prop[this.IndexProp(ORDER_PROP_PRICE_CLOSE)]       = this.OrderClosePrice();
   m_double_prop[this.IndexProp(ORDER_PROP_PROFIT)]            = this.OrderProfit();
   m_double_prop[this.IndexProp(ORDER_PROP_COMMISSION)]        = this.OrderCommission();
   m_double_prop[this.IndexProp(ORDER_PROP_SWAP)]              = this.OrderSwap();
   m_double_prop[this.IndexProp(ORDER_PROP_VOLUME)]            = this.OrderVolume();
   m_double_prop[this.IndexProp(ORDER_PROP_SL)]                = this.OrderStopLoss();
   m_double_prop[this.IndexProp(ORDER_PROP_TP)]                = this.OrderTakeProfit();
   m_double_prop[this.IndexProp(ORDER_PROP_VOLUME_CURRENT)]    = this.OrderVolumeCurrent();
   m_double_prop[this.IndexProp(ORDER_PROP_PRICE_STOP_LIMIT)]  = this.OrderPriceStopLimit();
   
//--- 保存字符串型属性
   m_string_prop[this.IndexProp(ORDER_PROP_SYMBOL)]            = this.OrderSymbol();
   m_string_prop[this.IndexProp(ORDER_PROP_COMMENT)]           = this.OrderComment();
   m_string_prop[this.IndexProp(ORDER_PROP_EXT_ID)]            = this.OrderExternalID();
   
//--- 保存其他整数型属性
   m_long_prop[ORDER_PROP_PROFIT_PT]                           = this.ProfitInPoints();
   m_long_prop[ORDER_PROP_TICKET_FROM]                         = this.OrderTicketFrom();
   m_long_prop[ORDER_PROP_TICKET_TO]                           = this.OrderTicketTo();
   m_long_prop[ORDER_PROP_CLOSE_BY_SL]                         = this.OrderCloseByStopLoss();
   m_long_prop[ORDER_PROP_CLOSE_BY_TP]                         = this.OrderCloseByTakeProfit();
   
//--- 保存额外的实数型属性
   m_double_prop[this.IndexProp(ORDER_PROP_PROFIT_FULL)]       = this.ProfitFull();
  }
//+------------------------------------------------------------------+

当我们在循环中传递历史或市价单/成交/仓位或选择新的订单/成交/仓位时,会创建从 COrder 类派生的新对象。 调用类构造函数时仅需接受单据号,之后在类构造函数中会含有订单状态和单据号。 接下来,使用上述方法简单地在 COrder 类构造函数中填充订单属性数组。

因此,每个新订单/成交/仓位都具有其独特的属性。 所有这些都将存储在列表中,而使用这些列表是函数库的主要效用。 列表可以按任何订单/成交/仓位属性进行排序。 此外,可以从所选择的列表中生成新列表。

在此阶段,已实现 COrder 抽象订单类的基本功能。 这是存储各种类型的订单、成交和仓位的基类。 所有按照订单、交易和仓位类型划分的用来创建对象的其他子类都将自它派生。

所创建的函数库是为了简化数据访问,并易于程序开发。
在当前阶段,我们有三个公共方法来访问 GetProperty(XXX) 抽象订单的属性。 您可以使用它们,但这不是很方便,因为您需要记住描述特定订单属性的枚举成员名称。 所以,我们将添加几个公共方法来获取必要的数据。 这些方法将具有合理的名称,以便即刻明晰按照特定方法可以获得什么属性。

这些方法可用于自定义程序,以便读取所选订单、成交或仓位的属性:
   //--- 返回 (1) 单据号, (2) 父订单号, (3) 派生订单号, (4) 魔幻数字, (5) 订单原因 (6) 仓位 ID
   //--- (7) 翻转仓位 ID, (8) 由止损平仓的标志, (9) 由止盈平仓的标志 (10) 开单时间, (11) 平单时间。
   //--- (12) 开单时间(毫秒), (13) 平单时间(毫秒) (14) 失效日期, (15) 类型, (16) 状态, (17) 方向
   long              Ticket(void)                                       const { return this.GetProperty(ORDER_PROP_TICKET);                     }
   long              TicketFrom(void)                                   const { return this.GetProperty(ORDER_PROP_TICKET_FROM);                }
   long              TicketTo(void)                                     const { return this.GetProperty(ORDER_PROP_TICKET_TO);                  }
   long              Magic(void)                                        const { return this.GetProperty(ORDER_PROP_MAGIC);                      }
   long              Reason(void)                                       const { return this.GetProperty(ORDER_PROP_REASON);                     }
   long              PositionID(void)                                   const { return this.GetProperty(ORDER_PROP_POSITION_ID);                }
   long              PositionByID(void)                                 const { return this.GetProperty(ORDER_PROP_POSITION_BY_ID);             }
   bool              IsCloseByStopLoss(void)                            const { return (bool)this.GetProperty(ORDER_PROP_CLOSE_BY_SL);          }
   bool              IsCloseByTakeProfit(void)                          const { return (bool)this.GetProperty(ORDER_PROP_CLOSE_BY_TP);          }
   datetime          TimeOpen(void)                                     const { return (datetime)this.GetProperty(ORDER_PROP_TIME_OPEN);        }
   datetime          TimeClose(void)                                    const { return (datetime)this.GetProperty(ORDER_PROP_TIME_CLOSE);       }
   datetime          TimeOpenMSC(void)                                  const { return (datetime)this.GetProperty(ORDER_PROP_TIME_OPEN_MSC);    }
   datetime          TimeCloseMSC(void)                                 const { return (datetime)this.GetProperty(ORDER_PROP_TIME_CLOSE_MSC);   }
   datetime          TimeExpiration(void)                               const { return (datetime)this.GetProperty(ORDER_PROP_TIME_EXP);         }
   ENUM_ORDER_TYPE   TypeOrder(void)                                    const { return (ENUM_ORDER_TYPE)this.GetProperty(ORDER_PROP_TYPE);      }
   ENUM_ORDER_STATUS Status(void)                                       const { return (ENUM_ORDER_STATUS)this.GetProperty(ORDER_PROP_STATUS);  }
   ENUM_ORDER_TYPE   TypeByDirection(void)                              const { return (ENUM_ORDER_TYPE)this.GetProperty(ORDER_PROP_DIRECTION); }
   
   //--- 返回 (1) 开单价, (2) 平单价, (3) 盈利, (4) 佣金, (5) 隔夜利息, (6) 交易量, 
   //--- (7) 未执行交易量 (8) 止损,和 (9) 止盈 (10) StopLimit 订单价位
   double            PriceOpen(void)                                    const { return this.GetProperty(ORDER_PROP_PRICE_OPEN);                 }
   double            PriceClose(void)                                   const { return this.GetProperty(ORDER_PROP_PRICE_CLOSE);                }
   double            Profit(void)                                       const { return this.GetProperty(ORDER_PROP_PROFIT);                     }
   double            Comission(void)                                    const { return this.GetProperty(ORDER_PROP_COMMISSION);                 }
   double            Swap(void)                                         const { return this.GetProperty(ORDER_PROP_SWAP);                       }
   double            Volume(void)                                       const { return this.GetProperty(ORDER_PROP_VOLUME);                     }
   double            VolumeCurrent(void)                                const { return this.GetProperty(ORDER_PROP_VOLUME_CURRENT);             }
   double            StopLoss(void)                                     const { return this.GetProperty(ORDER_PROP_SL);                         }
   double            TakeProfit(void)                                   const { return this.GetProperty(ORDER_PROP_TP);                         }
   double            PriceStopLimit(void)                               const { return this.GetProperty(ORDER_PROP_PRICE_STOP_LIMIT);           }
   
   //--- 返回 (1) 品名, (2) 注释, (3) 兑换 ID
   string            Symbol(void)                                       const { return this.GetProperty(ORDER_PROP_SYMBOL);                     }
   string            Comment(void)                                      const { return this.GetProperty(ORDER_PROP_COMMENT);                    }
   string            ExternalID(void)                                   const { return this.GetProperty(ORDER_PROP_EXT_ID);                     }

   //--- 获得全部订单利润
   double            ProfitFull(void)                                   const { return this.Profit()+this.Comission()+this.Swap();              }
   //--- 获得订单利润的点数
   int               ProfitInPoints(void) const;

实现按点数接收利润方法:

//+------------------------------------------------------------------+
//| 返回订单赢利点数                                                   |
//+------------------------------------------------------------------+
int COrder::ProfitInPoints(void) const
  {
   ENUM_ORDER_TYPE type=this.TypeOrder();
   string symbol=this.Symbol();
   double point=::SymbolInfoDouble(symbol,SYMBOL_POINT);
   if(type>ORDER_TYPE_SELL || point==0) return 0;
   if(this.Status()==ORDER_STATUS_HISTORY_ORDER)
      return int(type==ORDER_TYPE_BUY ? (this.PriceClose()-this.PriceOpen())/point : type==ORDER_TYPE_SELL ? (this.PriceOpen()-this.PriceClose())/point : 0);
   else if(this.Status()==ORDER_STATUS_MARKET_ACTIVE)
     {
      if(type==ORDER_TYPE_BUY)
         return int((::SymbolInfoDouble(symbol,SYMBOL_BID)-this.PriceOpen())/point);
      else if(type==ORDER_TYPE_SELL)
         return int((this.PriceOpen()-::SymbolInfoDouble(symbol,SYMBOL_ASK))/point);
     }
   return 0;
  }
//+------------------------------------------------------------------+

我们添加公共方法来描述订单对象的一些属性,以便您可以方便地按需显示它们:

   //--- 获取订单的(1)整数型,(2)实数型,和(3)字符串型属性的描述
   string            GetPropertyDescription(ENUM_ORDER_PROP_INTEGER property);
   string            GetPropertyDescription(ENUM_ORDER_PROP_DOUBLE property);
   string            GetPropertyDescription(ENUM_ORDER_PROP_STRING property);
   //--- 返回订单状态名
   string            StatusDescription(void) const;
   //--- 返回订单或仓位名
   string            TypeDescription(void) const;
   //--- 返回成交方向名
   string            DealEntryDescription(void) const;
   //--- 返回订单/仓位方向
   string            DirectionDescription(void) const;
   //--- 将订单属性的描述发送到流水账(full_prop = true  - 所有属性,false  - 仅支持的属性)
   void              Print(const bool full_prop=false);

在我们实现这些方法之前,我们要解决另一个问题:函数库和程序所基于的各种需求服务函数。 例如,对于这种情况,我们需要按照毫秒显示时间的函数,以及用两种语言之一接收消息的函数。 消息的语言取决于终端语言。

在函数库的根目录中创建一个新的包含文件


并取名为 DELib。 这是服务函数的库文件,可供库类本身和基于函数库的程序使用。


单击 “完成” 创建一个模板文件:

//+------------------------------------------------------------------+
//|                                                        DELib.mqh |
//|                                 版权所有 2018, MetaQuotes 软件公司 |
//|                             https://mql5.com/zh/users/artmedia70 |
//+------------------------------------------------------------------+
#property copyright "版权所有 2018, MetaQuotes 软件公司"
#property link      "https://mql5.com/zh/users/artmedia70"
//+------------------------------------------------------------------+
//| 定义                                                             |
//+------------------------------------------------------------------+
// #define MacrosHello   "Hello, world!"
// #define MacrosYear    2010
//+------------------------------------------------------------------+
//| DLL 导入                                                         |
//+------------------------------------------------------------------+
// #import "user32.dll"
//   int      SendMessageA(int hWnd,int Msg,int wParam,int lParam);
// #import "my_expert.dll"
//   int      ExpertRecalculate(int wParam,int lParam);
// #import
//+------------------------------------------------------------------+
//| EX5 导入                                                         |
//+------------------------------------------------------------------+
// #import "stdlib.ex5"
//   string ErrorDescription(int error_code);
// #import
//+------------------------------------------------------------------+

包含 Defines.mqh 文件并根据我们的需要修改模板:

//+------------------------------------------------------------------+
//|                                                        DELib.mqh |
//|                                 版权所有 2018, MetaQuotes 软件公司 |
//|                             https://mql5.com/zh/users/artmedia70 |
//+------------------------------------------------------------------+
#property copyright "版权所有 2018, MetaQuotes 软件公司"
#property link      "https://mql5.com/zh/users/artmedia70"
#property strict  // 对于 mql4 来说是必要的
//+------------------------------------------------------------------+
//| 包含文件                                                        |
//+------------------------------------------------------------------+
#include "Defines.mqh"
//+------------------------------------------------------------------+
//| 服务函数                                                        |
//+------------------------------------------------------------------+

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

由于我们已将 Defines.mqh 包含在此文件中,因此我们可以将此新文件(而不是 Defines.mqh)包含到 COrder 类文件中,如此它们在函数库中均可用。 此外,我们将用一个字符串,而不是两个字符串。
替换 Order.mqh 文件中的 include 指令
:

//+------------------------------------------------------------------+
//|                                                        Order.mqh |
//|                                 版权所有 2018, MetaQuotes 软件公司 |
//|                             https://mql5.com/zh/users/artmedia70 |
//+------------------------------------------------------------------+
#property copyright "版权所有 2018, MetaQuotes 软件公司"
#property link      "https://mql5.com/zh/users/artmedia70"
#property version   "1.00"
#include <Object.mqh>
#include "..\DELib.mqh"
//+------------------------------------------------------------------+
//| 抽象订单类                                                        |
//+------------------------------------------------------------------+

我们在 Defines.mqh 文件中添加 定义用户的国家语言 作为宏替换:

//+------------------------------------------------------------------+
//|                                                      Defines.mqh |
//|                                 版权所有 2018, MetaQuotes 软件公司 |
//|                             https://mql5.com/zh/users/artmedia70 |
//+------------------------------------------------------------------+
#property copyright "版权所有 2018, MetaQuotes 软件公司"
#property link      "https://mql5.com/zh/users/artmedia70"
//+------------------------------------------------------------------+
//| 宏替换                                                            |
//+------------------------------------------------------------------+
#define COUNTRY_LANG    "Russian"
//+------------------------------------------------------------------+

因此,如果终端语言不是英语,则用户将能够为显示的消息设置他们的母语。 不过,要实现这一点,我们被迫要在此键入所有消息来替换用户所需。

在 DELib.mqh 文件中添加以两种语言之一返回消息的函数

//+------------------------------------------------------------------+
//|                                                        DELib.mqh |
//|                                 版权所有 2018, MetaQuotes 软件公司 |
//|                             https://mql5.com/zh/users/artmedia70 |
//+------------------------------------------------------------------+
#property copyright "版权所有 2018, MetaQuotes 软件公司"
#property link      "https://mql5.com/zh/users/artmedia70"
#property strict  // 对于 mql4 来说是必要的
//+------------------------------------------------------------------+
//| 包含文件                                                        |
//+------------------------------------------------------------------+
#include "Defines.mqh"
//+------------------------------------------------------------------+
//| 服务函数                                                        |
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
//| 以两种语言之一返回文本                                              |
//+------------------------------------------------------------------+
string TextByLanguage(const string text_country_lang,const string text_en)
  {
   return(TerminalInfoString(TERMINAL_LANGUAGE)==COUNTRY_LANG ? text_country_lang : text_en);
  }
//+------------------------------------------------------------------+

该函数检查终端语言,如果它与 COUNTRY_LANG 宏替换中指定的语言匹配,则显示传递给 第一个函数参数 中的文本。 否则,将显示第二个函数参数(英语)中包含的文本。

我们还添加 以毫秒为单位显示时间的函数

//+------------------------------------------------------------------+
//|                                                        DELib.mqh |
//|                                 版权所有 2018, MetaQuotes 软件公司 |
//|                             https://mql5.com/zh/users/artmedia70 |
//+------------------------------------------------------------------+
#property copyright "版权所有 2018, MetaQuotes 软件公司"
#property link      "https://mql5.com/zh/users/artmedia70"
#property strict  // 对于 mql4 来说是必要的
//+------------------------------------------------------------------+
//| 包含文件                                                        |
//+------------------------------------------------------------------+
#include "Defines.mqh"
//+------------------------------------------------------------------+
//| 服务函数                                                        |
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
//| 以两种语言之一返回文本                                              |
//+------------------------------------------------------------------+
string TextByLanguage(const string text_country_lang,const string text_en)
  {
   return(TerminalInfoString(TERMINAL_LANGUAGE)==COUNTRY_LANG ? text_country_lang : text_en);
  }
//+------------------------------------------------------------------+
//| 返回毫秒时间                                                       |
//+------------------------------------------------------------------+
string TimeMSCtoString(const long time_msc)
  {
   return TimeToString(time_msc/1000,TIME_DATE|TIME_MINUTES|TIME_SECONDS)+"."+IntegerToString(time_msc%1000,3,'0');
  }
//+------------------------------------------------------------------+

此处一切都很简单:以毫秒为单位设置的时间 传递给函数。 若要以秒为单位计算时间,我们需要将传递给函数的值 除以 1000 。 若要计算毫秒数,取除式后的余数。 所有接收的数值都格式化为字符串,并返回给调用程序。

有时,您可能希望获得品种手数中的小数位数。 我们将这个函数输入到我们的服务函数文件中:

//+------------------------------------------------------------------+
//| 返回品种手数中的小数位数                                         |
//+------------------------------------------------------------------+
uint DigitsLots(const string symbol_name) 
  { 
   return (int)ceil(fabs(log(SymbolInfoDouble(symbol_name,SYMBOL_VOLUME_STEP))/log(10)));
  }
//+------------------------------------------------------------------+

除了服务函数之外,我们还需要三种方法来返回流水账中显示消息的订单原因、方向和成交类型。 将这三种方法添加到 COrder 类的受保护部分:

   //--- 返回 (1) 原因, (2) 方向, (3) 成交类型
   string            GetReasonDescription(const long reason)            const;
   string            GetEntryDescription(const long deal_entry)         const;
   string            GetTypeDealDescription(const long type_deal)       const;

它们的实现:

//+------------------------------------------------------------------+
//| 原因                                                             |
//+------------------------------------------------------------------+
string COrder::GetReasonDescription(const long reason) const
  {
#ifdef __MQL4__
   return
     (
      this.IsCloseByStopLoss()            ?  TextByLanguage("Срабатывание StopLoss","Due to StopLoss")                  :
      this.IsCloseByTakeProfit()          ?  TextByLanguage("Срабатывание TakeProfit","Due to TakeProfit")              :
      this.Reason()==ORDER_REASON_EXPERT  ?  TextByLanguage("Выставлен из mql4-программы","Placed from mql4 program")   :
      TextByLanguage("Свойство не поддерживается в MQL4","Property not supported in MQL4")
     );
#else 
   string res="";
   switch(this.Status())
     {
      case ORDER_STATUS_MARKET_ACTIVE        : 
         res=
           (
            reason==POSITION_REASON_CLIENT   ?  TextByLanguage("Позиция открыта из десктопного терминала","Position opened from desktop terminal") :
            reason==POSITION_REASON_MOBILE   ?  TextByLanguage("Позиция открыта из мобильного приложения","Position opened from mobile app") :
            reason==POSITION_REASON_WEB      ?  TextByLanguage("Позиция открыта из веб-платформы","Position opened from web platform") :
            reason==POSITION_REASON_EXPERT   ?  TextByLanguage("Позиция открыта из советника или скрипта","Position opened from EA or script") : ""
           );
         break;
      case ORDER_STATUS_MARKET_PENDING       :
      case ORDER_STATUS_HISTORY_PENDING      : 
      case ORDER_STATUS_HISTORY_ORDER        : 
         res=
           (
            reason==ORDER_REASON_CLIENT      ?  TextByLanguage("Ордер выставлен из десктопного терминала","Order set from desktop terminal") :
            reason==ORDER_REASON_MOBILE      ?  TextByLanguage("Ордер выставлен из мобильного приложения","Order set from mobile app") :
            reason==ORDER_REASON_WEB         ?  TextByLanguage("Ордер выставлен из веб-платформы","Oder set from web platform") :
            reason==ORDER_REASON_EXPERT      ?  TextByLanguage("Ордер выставлен советником или скриптом","Order set from EA or script") :
            reason==ORDER_REASON_SL          ?  TextByLanguage("Срабатывание StopLoss","Due to StopLoss") :
            reason==ORDER_REASON_TP          ?  TextByLanguage("Срабатывание TakeProfit","Due to TakeProfit") :
            reason==ORDER_REASON_SO          ?  TextByLanguage("Ордер выставлен в результате наступления Stop Out","Due to Stop Out") : ""
           );
         break;
      case ORDER_STATUS_DEAL                 : 
         res=
           (
            reason==DEAL_REASON_CLIENT       ?  TextByLanguage("Сделка проведена из десктопного терминала","Deal carried out from desktop terminal") :
            reason==DEAL_REASON_MOBILE       ?  TextByLanguage("Сделка проведена из мобильного приложения","Deal carried out from mobile app") :
            reason==DEAL_REASON_WEB          ?  TextByLanguage("Сделка проведена из веб-платформы","Deal carried out from web platform") :
            reason==DEAL_REASON_EXPERT       ?  TextByLanguage("Сделка проведена из советника или скрипта","Deal carried out from EA or script") :
            reason==DEAL_REASON_SL           ?  TextByLanguage("Срабатывание StopLoss","Due to StopLoss") :
            reason==DEAL_REASON_TP           ?  TextByLanguage("Срабатывание TakeProfit","Due to TakeProfit") :
            reason==DEAL_REASON_SO           ?  TextByLanguage("Сделка проведена в результате наступления Stop Out","Due to Stop Out") :
            reason==DEAL_REASON_ROLLOVER     ?  TextByLanguage("Сделка проведена по причине переноса позиции","Due to position rollover") :
            reason==DEAL_REASON_VMARGIN      ?  TextByLanguage("Сделка проведена по причине начисления/списания вариационной маржи","Due to variation margin") :
            reason==DEAL_REASON_SPLIT        ?  TextByLanguage("Сделка проведена по причине сплита (понижения цены) инструмента","Due to split") : ""
           );
         break;
      default                                : res="";   break;
     }
   return res;
#endif 
  }
//+------------------------------------------------------------------+

选中与 MQL4MQL5 的关联,根据订单状态验证输入中传递的订单原因,并返回其描述。

//+------------------------------------------------------------------+
//| 成交方向描述                                                       |
//+------------------------------------------------------------------+
string COrder::GetEntryDescription(const long deal_entry) const
  {
#ifdef __MQL4__
   return TextByLanguage("Свойство не поддерживается в MQL4","Property not supported in MQL4");
#else 
   string res="";
   switch(this.Status())
     {
      case ORDER_STATUS_MARKET_ACTIVE     : 
         res=TextByLanguage("Свойство не поддерживается у позиции","Property not supported for position"); 
         break;
      case ORDER_STATUS_MARKET_PENDING    :
      case ORDER_STATUS_HISTORY_PENDING   : 
         res=TextByLanguage("Свойство не поддерживается у отложенного ордера","Property not supported for pending order"); 
         break;
      case ORDER_STATUS_HISTORY_ORDER     : 
         res=TextByLanguage("Свойство не поддерживается у исторического ордера","Property not supported for history order"); 
         break;
      case ORDER_STATUS_DEAL              : 
         res=
           (
            deal_entry==DEAL_ENTRY_IN     ?  TextByLanguage("Вход в рынок","Entry to the market") :
            deal_entry==DEAL_ENTRY_OUT    ?  TextByLanguage("Выход из рынка","Out from the market") :
            deal_entry==DEAL_ENTRY_INOUT  ?  TextByLanguage("Разворот","Reversal") :
            deal_entry==DEAL_ENTRY_OUT_BY ?  TextByLanguage("Закрытие встречной позицией","Closing by opposite position") : ""
           );
         break;
      default                             : res=""; break;
     }
   return res;
#endif 
  }
//+------------------------------------------------------------------+

选中与 MQL4MQL5 的关联,根据订单状态验证输入中传递的成交方向,并返回其描述。

//+------------------------------------------------------------------+
//| 返回成交类型名                                                   |
//+------------------------------------------------------------------+
string COrder::GetTypeDealDescription(const long deal_type) const
  {
#ifdef __MQL4__
   return TextByLanguage("Свойство не поддерживается в MQL4","Property not supported in MQL4");
#else 
   string res="";
   switch(this.Status())
     {
      case ORDER_STATUS_MARKET_ACTIVE     : 
         res=TextByLanguage("Свойство не поддерживается у позиции","Property not supported for position"); 
         break;
      case ORDER_STATUS_MARKET_PENDING    :
      case ORDER_STATUS_HISTORY_PENDING   : 
         res=TextByLanguage("Свойство не поддерживается у отложенного ордера","Property not supported for pending order"); 
         break;
      case ORDER_STATUS_HISTORY_ORDER     : 
         res=TextByLanguage("Свойство не поддерживается у исторического ордера","Property not supported for history order"); 
         break;
      case ORDER_STATUS_DEAL              : 
         res=
           (
            deal_type==DEAL_TYPE_BUY                      ?  TextByLanguage("Сделка на покупку","Buy deal") :
            deal_type==DEAL_TYPE_SELL                     ?  TextByLanguage("Сделка на продажу","Sell deal") :
            deal_type==DEAL_TYPE_BALANCE                  ?  TextByLanguage("Начисление баланса","Balance accrual") :
            deal_type==DEAL_TYPE_CREDIT                   ?  TextByLanguage("Начисление кредита","Credit accrual") :
            deal_type==DEAL_TYPE_CHARGE                   ?  TextByLanguage("Дополнительные сборы","Extra charges") :
            deal_type==DEAL_TYPE_CORRECTION               ?  TextByLanguage("Корректирующая запись","Corrective entry") :
            deal_type==DEAL_TYPE_BONUS                    ?  TextByLanguage("Перечисление бонусов","Bonuses") :
            deal_type==DEAL_TYPE_COMMISSION               ?  TextByLanguage("Дополнительные комиссии","Additional comissions") :
            deal_type==DEAL_TYPE_COMMISSION_DAILY         ?  TextByLanguage("Комиссия, начисляемая в конце торгового дня","Commission accrued at the end of a trading day") :
            deal_type==DEAL_TYPE_COMMISSION_MONTHLY       ?  TextByLanguage("Комиссия, начисляемая в конце месяца","Commission accrued at the end of a month") :
            deal_type==DEAL_TYPE_COMMISSION_AGENT_DAILY   ?  TextByLanguage("Агентская комиссия, начисляемая в конце торгового дня","Agency commission charged at the end of a trading day") :
            deal_type==DEAL_TYPE_COMMISSION_AGENT_MONTHLY ?  TextByLanguage("Агентская комиссия, начисляемая в конце месяца","Agency commission charged at the end of a month") :
            deal_type==DEAL_TYPE_INTEREST                 ?  TextByLanguage("Начисления процентов на свободные средства","Accrued interest on free funds") :
            deal_type==DEAL_TYPE_BUY_CANCELED             ?  TextByLanguage("Отмененная сделка покупки","Canceled buy transaction") :
            deal_type==DEAL_TYPE_SELL_CANCELED            ?  TextByLanguage("Отмененная сделка продажи","Canceled sell transaction") :
            deal_type==DEAL_DIVIDEND                      ?  TextByLanguage("Начисление дивиденда","Accrued dividends") :
            deal_type==DEAL_DIVIDEND_FRANKED              ?  TextByLanguage("Начисление франкированного дивиденда","Accrual of franked dividend") :
            deal_type==DEAL_TAX                           ?  TextByLanguage("Начисление налога","Tax accrual") : ""
           );
         break;
      default                             : res=""; break;
     }
   return res;
#endif 
  }

选中与 MQL4MQL5 的关联,根据订单状态验证输入中传递的成交类型,并返回其描述。

实现描述订单属性的方法:

//+------------------------------------------------------------------+
//| 返回订单整数型属性的描述                                         |
//+------------------------------------------------------------------+
string COrder::GetPropertyDescription(ENUM_ORDER_PROP_INTEGER property)
  {
   return
     (
   //--- 一般属性
      property==ORDER_PROP_MAGIC             ?  TextByLanguage("Магик","Magic")+
         (!this.SupportProperty(property)    ?  TextByLanguage(": Свойство не поддерживается",": Property not supported") :
          ": "+(string)this.GetProperty(property)
         )  :
      property==ORDER_PROP_TICKET            ?  TextByLanguage("Тикет","Ticket")+
         (!this.SupportProperty(property)    ?  TextByLanguage(": Свойство не поддерживается",": Property not supported") :
          " #"+(string)this.GetProperty(property)
         )  :
      property==ORDER_PROP_TICKET_FROM       ?  TextByLanguage("Тикет родительского ордера","Ticket of parent order")+
         (!this.SupportProperty(property)    ?  TextByLanguage(": Свойство не поддерживается",": Property not supported") :
          " #"+(string)this.GetProperty(property)
         )  :
      property==ORDER_PROP_TICKET_TO         ?  TextByLanguage("Тикет наследуемого ордера","Inherited order ticket")+
         (!this.SupportProperty(property)    ?  TextByLanguage(": Свойство не поддерживается",": Property not supported") :
          " #"+(string)this.GetProperty(property)
         )  :
      property==ORDER_PROP_TIME_OPEN         ?  TextByLanguage("Время открытия","Open time")+
         (!this.SupportProperty(property)    ?  TextByLanguage(": Свойство не поддерживается",": Property not supported") :
          ": "+::TimeToString(this.GetProperty(property),TIME_DATE|TIME_MINUTES|TIME_SECONDS)
         )  :
      property==ORDER_PROP_TIME_CLOSE        ?  TextByLanguage("Время закрытия","Close time")+
         (!this.SupportProperty(property)    ?  TextByLanguage(": Свойство не поддерживается",": Property not supported") :
          ": "+::TimeToString(this.GetProperty(property),TIME_DATE|TIME_MINUTES|TIME_SECONDS)
         )  :
      property==ORDER_PROP_TIME_EXP          ?  TextByLanguage("Дата экспирации","Expiration date")+
         (!this.SupportProperty(property)    ?  TextByLanguage(": Свойство не поддерживается",": Property not supported") :
          (this.GetProperty(property)==0     ?  TextByLanguage(": Не задана",": Not set") :
          ": "+::TimeToString(this.GetProperty(property),TIME_DATE|TIME_MINUTES|TIME_SECONDS))
         )  :
      property==ORDER_PROP_TYPE              ?  TextByLanguage("Тип","Type")+": "+this.TypeDescription()                   :
      property==ORDER_PROP_DIRECTION         ?  TextByLanguage("Тип по направлению","Type by direction")+": "+this.DirectionDescription() :
      
      property==ORDER_PROP_REASON            ?  TextByLanguage("Причина","Reason")+
         (!this.SupportProperty(property)    ?  TextByLanguage(": Свойство не поддерживается",": Property not supported") :
          ": "+this.GetReasonDescription(this.GetProperty(property))
         )  :
      property==ORDER_PROP_POSITION_ID       ?  TextByLanguage("Идентификатор позиции","Position ID")+
         (!this.SupportProperty(property)    ?  TextByLanguage(": Свойство не поддерживается",": Property not supported") :
          ": #"+(string)this.GetProperty(property)
         )  :
      property==ORDER_PROP_DEAL_ORDER        ?  TextByLanguage("Сделка на основании ордера","Deal by order")+
         (!this.SupportProperty(property)    ?  TextByLanguage(": Свойство не поддерживается",": Property not supported") :
          ": #"+(string)this.GetProperty(property)
         )  :
      property==ORDER_PROP_DEAL_ENTRY        ?  TextByLanguage("Направление сделки","Deal direction")+
         (!this.SupportProperty(property)    ?  TextByLanguage(": Свойство не поддерживается",": Property not supported") :
          ": "+this.GetEntryDescription(this.GetProperty(property))
         )  :
      property==ORDER_PROP_POSITION_BY_ID    ?  TextByLanguage("Идентификатор встречной позиции","Opposite position ID")+
         (!this.SupportProperty(property)    ?  TextByLanguage(": Свойство не поддерживается",": Property not supported") :
          ": "+(string)this.GetProperty(property)
         )  :
      property==ORDER_PROP_TIME_OPEN_MSC     ?  TextByLanguage("Время открытия в милисекундах","Open time in milliseconds")+
         (!this.SupportProperty(property)    ?  TextByLanguage(": Свойство не поддерживается",": Property not supported") :
          ": "+(string)this.GetProperty(property)+" > "+TimeMSCtoString(this.GetProperty(property))
         )  :
      property==ORDER_PROP_TIME_CLOSE_MSC    ?  TextByLanguage("Время закрытия в милисекундах","Close time in milliseconds")+
         (!this.SupportProperty(property)    ?  TextByLanguage(": Свойство не поддерживается",": Property not supported") :
          ": "+(string)this.GetProperty(property)+" > "+TimeMSCtoString(this.GetProperty(property))
         )  :
      property==ORDER_PROP_TIME_UPDATE       ?  TextByLanguage("Время изменения позиции","Position change time")+
         (!this.SupportProperty(property)    ?  TextByLanguage(": Свойство не поддерживается",": Property not supported") :
          ": "+(this.GetProperty(property)!=0 ? ::TimeToString(this.GetProperty(property),TIME_DATE|TIME_MINUTES|TIME_SECONDS) : "0")
         )  :
      property==ORDER_PROP_TIME_UPDATE_MSC   ?  TextByLanguage("Время изменения позиции в милисекундах","Position change time in milliseconds")+
         (!this.SupportProperty(property)    ?  TextByLanguage(": Свойство не поддерживается",": Property not supported") :
          ": "+(this.GetProperty(property)!=0 ? (string)this.GetProperty(property)+" > "+TimeMSCtoString(this.GetProperty(property)) : "0")
         )  :
   //--- 附加属性
      property==ORDER_PROP_STATUS            ?  TextByLanguage("Статус","Status")+
         (!this.SupportProperty(property)    ?  TextByLanguage(": Свойство не поддерживается",": Property not supported") :
          ": \""+this.StatusDescription()+"\""
         )  :
      property==ORDER_PROP_PROFIT_PT         ?  TextByLanguage("Прибыль в пунктах","Profit in points")+
         (!this.SupportProperty(property)    ?  TextByLanguage(": Свойство не поддерживается",": Property not supported") :
          ": "+(string)this.GetProperty(property)
         )  :
      property==ORDER_PROP_CLOSE_BY_SL       ?  TextByLanguage("Закрытие по StopLoss","Close by StopLoss")+
         (!this.SupportProperty(property)    ?  TextByLanguage(": Свойство не поддерживается",": Property not supported") :
          ": "+(this.GetProperty(property) ? TextByLanguage("Да","Yes") : TextByLanguage("Нет","No"))
         )  :
      property==ORDER_PROP_CLOSE_BY_TP       ?  TextByLanguage("Закрытие по TakeProfit","Close by TakeProfit")+
         (!this.SupportProperty(property)    ?  TextByLanguage(": Свойство не поддерживается",": Property not supported") :
          ": "+(this.GetProperty(property) ? TextByLanguage("Да","Yes") : TextByLanguage("Нет","No"))
         )  :
      ""
     );
  }
//+------------------------------------------------------------------+
//| 返回订单实数型属性的描述                                         |
//+------------------------------------------------------------------+
string COrder::GetPropertyDescription(ENUM_ORDER_PROP_DOUBLE property)
  {
   int dg=(int)::SymbolInfoInteger(this.GetProperty(ORDER_PROP_SYMBOL),SYMBOL_DIGITS);
   int dgl=(int)DigitsLots(this.GetProperty(ORDER_PROP_SYMBOL));
   return
     (
      //--- 一般属性
      property==ORDER_PROP_PRICE_CLOSE       ?  TextByLanguage("Цена закрытия","Close price")+
         (!this.SupportProperty(property)    ?  TextByLanguage(": Свойство не поддерживается",": Property not supported") :
          ": "+::DoubleToString(this.GetProperty(property),dg)
         )  :
      property==ORDER_PROP_PRICE_OPEN        ?  TextByLanguage("Цена открытия","Open price")+
         (!this.SupportProperty(property)    ?  TextByLanguage(": Свойство не поддерживается",": Property not supported") :
          ": "+::DoubleToString(this.GetProperty(property),dg)
         )  :
      property==ORDER_PROP_SL                ?  TextByLanguage("Цена StopLoss","StopLoss price")+
         (!this.SupportProperty(property)    ?  TextByLanguage(": Свойство не поддерживается",": Property not supported") :
          (this.GetProperty(property)==0     ?  TextByLanguage(": Отсутствует",": Not set"):": "+::DoubleToString(this.GetProperty(property),dg))
         )  :
      property==ORDER_PROP_TP                ?  TextByLanguage("Цена TakeProfit","TakeProfit price")+
         (!this.SupportProperty(property)    ?  TextByLanguage(": Свойство не поддерживается",": Property not supported") :
          (this.GetProperty(property)==0     ?  TextByLanguage(": Отсутствует",": Not set"):": "+::DoubleToString(this.GetProperty(property),dg))
         )  :
      property==ORDER_PROP_PROFIT            ?  TextByLanguage("Прибыль","Profit")+
         (!this.SupportProperty(property)    ?  TextByLanguage(": Свойство не поддерживается",": Property not supported") :
          ": "+::DoubleToString(this.GetProperty(property),2)
         )  :
      property==ORDER_PROP_COMMISSION        ?  TextByLanguage("Комиссия","Comission")+
         (!this.SupportProperty(property)    ?  TextByLanguage(": Свойство не поддерживается",": Property not supported") :
          ": "+::DoubleToString(this.GetProperty(property),2)
         )  :
      property==ORDER_PROP_SWAP              ?  TextByLanguage("Своп","Swap")+
         (!this.SupportProperty(property)    ?  TextByLanguage(": Свойство не поддерживается",": Property not supported") :
          ": "+::DoubleToString(this.GetProperty(property),2)
         )  :
      property==ORDER_PROP_VOLUME            ?  TextByLanguage("Объём","Volume")+
         (!this.SupportProperty(property)    ?  TextByLanguage(": Свойство не поддерживается",": Property not supported") :
          ": "+::DoubleToString(this.GetProperty(property),dgl)
          ) :
      property==ORDER_PROP_VOLUME_CURRENT    ?  TextByLanguage("Невыполненный объём","Unfulfilled volume")+
         (!this.SupportProperty(property)    ?  TextByLanguage(": Свойство не поддерживается",": Property not supported") :
          ": "+::DoubleToString(this.GetProperty(property),dgl)
          ) :
      property==ORDER_PROP_PRICE_STOP_LIMIT  ? 
         TextByLanguage("Цена постановки Limit ордера при активации StopLimit ордера","Price of placing Limit order when StopLimit order activated")+
         (!this.SupportProperty(property)    ?  TextByLanguage(": Свойство не поддерживается",": Property not supported") :
          ": "+::DoubleToString(this.GetProperty(property),dg)
          ) :
      //--- 附加属性
      property==ORDER_PROP_PROFIT_FULL       ?  TextByLanguage("Прибыль+комиссия+своп","Profit+Comission+Swap")+
         (!this.SupportProperty(property)    ?  TextByLanguage(": Свойство не поддерживается",": Property not supported") :
          ": "+::DoubleToString(this.GetProperty(property),2)
          ) :
      ""
     );
  }
//+------------------------------------------------------------------+
//| 返回订单字符串型属性的描述                                         |
//+------------------------------------------------------------------+
string COrder::GetPropertyDescription(ENUM_ORDER_PROP_STRING property)
  {
   return
     (
      property==ORDER_PROP_SYMBOL         ?  TextByLanguage("Символ","Symbol")+": \""+this.GetProperty(property)+"\""            :
      property==ORDER_PROP_COMMENT        ?  TextByLanguage("Комментарий","Comment")+
         (this.GetProperty(property)==""  ?  TextByLanguage(": Отсутствует",": Not set"):": \""+this.GetProperty(property)+"\"") :
      property==ORDER_PROP_EXT_ID         ?  TextByLanguage("Идентификатор на бирже","ID on exchange")+
         (!this.SupportProperty(property) ?  TextByLanguage(": Свойство не поддерживается",": Property not supported") :
         (this.GetProperty(property)==""  ?  TextByLanguage(": Отсутствует",": Not set"):": \""+this.GetProperty(property)+"\"")):
      ""
     );
  }
//+------------------------------------------------------------------+

实现返回订单状态名称,订单或仓位的名称,成交方向名称,订单/仓位方向类型的描述,以及在流水账中方便地显示订单属性的方法。

首先,将另一个 宏替换 添加到 Defines.mqh 文件中,以便在流水日志中方便地显示函数/方法名称:

//+------------------------------------------------------------------+
//| 宏替换                                                            |
//+------------------------------------------------------------------+
#define COUNTRY_LANG    "Russian"            // 国家语言
#define DFUN           (__FUNCTION__+": ")   // "Function description"
//+------------------------------------------------------------------+

现在,我们可以简单地编写 DFUN,而不是完整地编写字符串,编译器将描述替换为宏中设置的字符串。

//+------------------------------------------------------------------+
//| 返回订单状态名                                                   |
//+------------------------------------------------------------------+
string COrder::StatusDescription(void) const
  {
   ENUM_ORDER_STATUS status=this.Status();
   ENUM_ORDER_TYPE   type=(ENUM_ORDER_TYPE)this.TypeOrder();
   return
     (
      status==ORDER_STATUS_BALANCE        ?  TextByLanguage("Балансная операция","Balance operation") :
      status==ORDER_STATUS_CREDIT         ?  TextByLanguage("Кредитная операция","Credits operation") :
      status==ORDER_STATUS_HISTORY_ORDER  || status==ORDER_STATUS_HISTORY_PENDING                     ?  
      (
       type>ORDER_TYPE_SELL ? TextByLanguage("Отложенный ордер","Pending order")                      :
       TextByLanguage("Ордер на ","The order to ")+(type==ORDER_TYPE_BUY ? TextByLanguage("покупку","buy") : TextByLanguage("продажу","sell"))
      )                                                                                               :
      status==ORDER_STATUS_DEAL           ?  TextByLanguage("Сделка","Deal")                          :
      status==ORDER_STATUS_MARKET_ACTIVE  ?  TextByLanguage("Позиция","Active position")              :
      status==ORDER_STATUS_MARKET_PENDING ?  TextByLanguage("Установленный отложенный ордер","Active pending order") :
      ""
     );
  }
//+------------------------------------------------------------------+
//| 返回订单或仓位名                                                  |
//+------------------------------------------------------------------+
string COrder::TypeDescription(void) const
  {
   if(this.Status()==ORDER_STATUS_DEAL)
      return this.GetTypeDealDescription(this.TypeOrder());
   else switch(this.TypeOrder())
     {
      case ORDER_TYPE_BUY              :  return "Buy";
      case ORDER_TYPE_BUY_LIMIT        :  return "Buy Limit";
      case ORDER_TYPE_BUY_STOP         :  return "Buy Stop";
      case ORDER_TYPE_SELL             :  return "Sell";
      case ORDER_TYPE_SELL_LIMIT       :  return "Sell Limit";
      case ORDER_TYPE_SELL_STOP        :  return "Sell Stop";
      #ifdef __MQL4__
      case ORDER_TYPE_BALANCE          :  return TextByLanguage("Балансовая операция","Balance operation");
      case ORDER_TYPE_CREDIT           :  return TextByLanguage("Кредитная операция","Credit operation");
      #else 
      case ORDER_TYPE_BUY_STOP_LIMIT   :  return "Buy Stop Limit";
      case ORDER_TYPE_SELL_STOP_LIMIT  :  return "Sell Stop Limit";
      #endif 
      default                          :  return TextByLanguage("Неизвестный тип","Unknown type");
     }
  }
//+------------------------------------------------------------------+
//| 返回成交方向名                                                   |
//+------------------------------------------------------------------+
string COrder::DealEntryDescription(void) const
  {
   return(this.Status()==ORDER_STATUS_DEAL ? this.GetEntryDescription(this.GetProperty(ORDER_PROP_DEAL_ENTRY)) : "");
  }
//+------------------------------------------------------------------+
//| 返回订单/仓位方向类型                                             |
//+------------------------------------------------------------------+
string COrder::DirectionDescription(void) const
  {
   if(this.Status()==ORDER_STATUS_DEAL)
      return this.TypeDescription();
   switch(this.TypeByDirection())
     {
      case ORDER_TYPE_BUY  :  return "Buy";
      case ORDER_TYPE_SELL :  return "Sell";
      default              :  return TextByLanguage("Неизвестный тип","Unknown type");
     }
  }
//+------------------------------------------------------------------+
//| 将订单属性发送到流水日志                                         |
//+------------------------------------------------------------------+
void COrder::Print(const bool full_prop=false)
  {
   ::Print("============= ",TextByLanguage("Начало списка параметров: \"","Beginning of the parameter list: \""),this.StatusDescription(),"\" =============");
   int beg=0, end=ORDER_PROP_INTEGER_TOTAL;
   for(int i=beg; i<end; i++)
     {
      ENUM_ORDER_PROP_INTEGER prop=(ENUM_ORDER_PROP_INTEGER)i;
      if(!full_prop && !this.SupportProperty(prop)) continue;
      ::Print(this.GetPropertyDescription(prop));
     }
   ::Print("------");
   beg=end; end+=ORDER_PROP_DOUBLE_TOTAL;
   for(int i=beg; i<end; i++)
     {
      ENUM_ORDER_PROP_DOUBLE prop=(ENUM_ORDER_PROP_DOUBLE)i;
      if(!full_prop && !this.SupportProperty(prop)) continue;
      ::Print(this.GetPropertyDescription(prop));
     }
   ::Print("------");
   beg=end; end+=ORDER_PROP_STRING_TOTAL;
   for(int i=beg; i<end; i++)
     {
      ENUM_ORDER_PROP_STRING prop=(ENUM_ORDER_PROP_STRING)i;
      if(!full_prop && !this.SupportProperty(prop)) continue;
      ::Print(this.GetPropertyDescription(prop));
     }
   ::Print("================== ",TextByLanguage("Конец списка параметров: \"","End of the parameter list: \""),this.StatusDescription(),"\" ==================\n");
  }
//+------------------------------------------------------------------+

现在已经实现了 COrder 抽象订单类的所有方法,我们可以看到我们在这个阶段真正得到了什么。

COrder 抽象类测试

我们来测试一下如何创建和填充数据的 COrder 基础对象。 进而,我们将基于其类型开发对象,并将它们存储在集合中。

智能交易系统 目录或子目录里,创建新的文件夹,并命名 TestDoEasy。 接着,在新创建的文件夹中创建另一个文件夹: Part01 。 测试 EA 的文件位于其中。

在编辑器的导航栏中右键单击 Part01,然后从弹出菜单中选择 “新建文件”,或者按下 Ctrl + N 并高亮显示 Part01 文件夹。 MQL 向导窗口将会打开,并选择 “EA 交易(模板)” 选项,单击 “下一步”,在名称输入字段中已指定的文件路径中加入新的 TestDoEasyPart01 文件名称。 单击 “下一步”,直到完成向导操作。 我们进行测试当中不需要任何其他事件处理程序,因此您可以将所有复选框留空。 单击 “完成” 以便创建新的 EA 模板:
//+------------------------------------------------------------------+
//|                                             TestDoEasyPart01.mq5 |
//|                                 版权所有 2018, MetaQuotes 软件公司 |
//|                             https://mql5.com/zh/users/artmedia70 |
//+------------------------------------------------------------------+
#property copyright "版权所有 2018, MetaQuotes 软件公司"
#property link      "https://mql5.com/zh/users/artmedia70"
#property version   "1.00"
//+------------------------------------------------------------------+
//| 智能交易系统初始化函数                                              |
//+------------------------------------------------------------------+
int OnInit()
  {
//---
   
//---
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//| 智能交易系统逆初始化函数                                          |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
//---
   
  }
//+------------------------------------------------------------------+
//| 智能交易系统即时报价函数                                         |
//+------------------------------------------------------------------+
void OnTick()
  {
//---
   
  }
//+------------------------------------------------------------------+

测试尽可能地简单,因此无需重新发明轮子。 因此,我们只包含标准库中的 COrder 抽象订单CArrayObj 类,并创建对象列表

//+------------------------------------------------------------------+
//|                                             TestDoEasyPart01.mq5 |
//|                                 版权所有 2018, MetaQuotes 软件公司 |
//|                             https://mql5.com/zh/users/artmedia70 |
//+------------------------------------------------------------------+
#property copyright "版权所有 2018, MetaQuotes 软件公司"
#property link      "https://mql5.com/zh/users/artmedia70"
#property version   "1.00"
//--- 包含
#include <DoEasy\Objects\Order.mqh>
#include <Arrays\ArrayObj.mqh>
//--- 全局变量
CArrayObj      list_all_orders;
//+------------------------------------------------------------------+
//| 智能交易系统初始化函数                                              |
//+------------------------------------------------------------------+

接下来,在 OnInit() 中将排序列表标志设置为对象列表,并在两个循环中用所有历史的成交订单 记录填充它。

然后i在终端的智能系统日志中循环显示每个所填充的列表对象数据:

//+------------------------------------------------------------------+
//| 智能交易系统初始化函数                                              |
//+------------------------------------------------------------------+
int OnInit()
  {
//---
   list_all_orders.Sort();
   list_all_orders.Clear();
   if(!HistorySelect(0,TimeCurrent()))
     {
      Print(DFUN,TextByLanguage(": Не удалось получить историю сделок и ордеров",": Failed to get history of deals and orders"));
      return INIT_FAILED;
     }
//--- 成交
   int total_deals=HistoryDealsTotal();
   for(int i=0; i<total_deals; i++)
     {
      ulong deal_ticket=::HistoryDealGetTicket(i);
      if(deal_ticket==0) continue;
      COrder *deal=new COrder(ORDER_STATUS_DEAL,deal_ticket);
      if(deal==NULL) continue;
      list_all_orders.InsertSort(deal);
     }
//--- 订单
   int total_orders=HistoryOrdersTotal();
   for(int i=0; i<total_orders; i++)
     {
      ulong order_ticket=::HistoryOrderGetTicket(i);
      if(order_ticket==0) continue;
      ENUM_ORDER_TYPE type=(ENUM_ORDER_TYPE)::HistoryOrderGetInteger(order_ticket,ORDER_TYPE);
      if(type==ORDER_TYPE_BUY || type==ORDER_TYPE_SELL)
        {
         COrder *order=new COrder(ORDER_STATUS_HISTORY_ORDER,order_ticket);
         if(order==NULL) continue;
         list_all_orders.InsertSort(order);
        }
      else
        {
         COrder *order=new COrder(ORDER_STATUS_HISTORY_PENDING,order_ticket);
         if(order==NULL) continue;
         list_all_orders.InsertSort(order);
        }
     }
//--- 在流水日志里显示
   int total=list_all_orders.Total();
   for(int i=0;i<total;i++)
     {
      COrder *order=list_all_orders.At(i);
      if(order==NULL)
         continue;
      order.Print();
     }
//---
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+

如果我们现在尝试编译 EA,我们会得到三个错误:


这些错误表明我们无法从外部访问类的受保护方法。 毕竟,我们是在类的受保护部分创建了 COrder 构造函数。 当我们用该类作为基类开发类型对象时,一切都很顺利,因为它们均含有开放的构造函数。 现在,为了执行测试,我们只需将构造函数移动到 COrder 类的公共部分:


现在所有编译都没有错误,交易账户的历史记录中所有订单和成交的数据都显示在终端流水日志中。


每个订单/成交的所有属性,包括不支持的订单/交易都会显示出来。


事实上我们已经开发了一些方法,而按照订单支持的特定属性返回标志的方法是虚拟的,以便在派生类中重新定义它们。 然后,这些派生类用自己的方式在流水日志中显示数据。 在这种情况下,所有内容都应该正确显示。 如果订单不支持该属性,则它不会显示在流水日志中,因为 COrder 类的 Print(const bool full_prop=false) 方法默认在日志中禁止显示不支持的属性标志,而类的 SupportProperty() 虚方法对于任何属性只简单地返回 'true'。

下一步是什么?

首期(也是最小的)部分准备就绪。 我们已经开发了一个基本对象,用于收集订单和成交以及市价单和仓位的历史记录。 到目前为止没有实际价值,但这只是一个开始。 这个单一的基本对象会成为系统在订单系统上存储和显示数据的基石。 我们的下一步是使用相同的原则开发其他必要的对象和集合。 我也将自动收集持续需要的数据。


在下一部分中,我将基于此抽象订单类开发几个类。 The classes will describe specific order, deal and position types. 我将开发第一个集合(历史订单和成交的集合),以及函数库的基础和主要对象 — 引擎。 我还会在后续函数库开发过程中尝试消除按照 MQL4 编译函数库时出现的错误。

下面是一个存档,其中包含第一部分中讨论的所有文件。 您可以下载并详细分析它们。 在评论中留下您的问题、意见和建议。

请注意,函数库的开发正在与文章撰写并行前进。 因此,在逐篇文章中也许会针对函数库中的方法和变量进行必要修订。 有鉴于此,前后文章中附件和说明文字的内容可能存在略微不一致。 例如,评论中可能会进行编辑,枚举成员的重新排列等。 这不会影响附加示例的执行。

返回目录