在前一篇文章中,我们已着手创建一个大型跨平台函数库,简化 MetaTrader 5 和 MetaTrader 4 平台的程序开发。 我们创建了 COrder 抽象对象,它是一个基础对象,用于存储历史订单和成交的数据,以及市价订单和仓位。
在这一部分中,我们将继续开发所有必要的对象,用来在集合中存储帐户历史数据,准备历史订单和成交的集合,以及修改和改进已创建的对象和枚举。
COrder 基础对象包含任意帐户对象的所有数据,无论是市价订单(执行动作的订单)、挂单、成交还是仓位。 为了令我们能够彼此分开地自由操作所有这些对象,我们将基于抽象类 COrder 开发若干个类。 这些类将准确地明示对象与其类型的关联。
历史订单和成交列表也许包含若干种类型的这类对象:已移除的挂单,下达的市价订单和成交(市价订单执行结果)。 MQL4 中还有两种对象类型:余额和信用操作(在 MQL5 中,这些数据存储在成交属性中)。
在函数库的 Objects 文件夹中创建新类 CHistoryOrder。
若要执行此操作,请右键单击 Objects 文件夹,然后选择“新建文件”菜单项(Ctrl+N)。 在新打开的 MQL5 向导中,选择“新类”并单击“下一步”。 在类名字段键入 CHistoryOrder (1),在基类字段指定 COrder (2) 抽象类的名字,并点击“完成”。
之后,会在 Objects 文件夹中生成 HistoryOrder.mqh (3) 文件。 打开它:
//+------------------------------------------------------------------+ //| HistoryOrder.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 CHistoryOrder : public COrder { private: public: CHistoryOrder(); ~CHistoryOrder(); }; //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ CHistoryOrder::CHistoryOrder() { } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ CHistoryOrder::~CHistoryOrder() { } //+------------------------------------------------------------------+
目前,这只是一个类模板。 如果我们尝试编译它,我们将看到五个已经熟悉的错误 — 从 COrder 派生的新类对其父类一无所知。 添加 包含文件 Order.mqh:
//+------------------------------------------------------------------+ //| HistoryOrder.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 "Order.mqh" //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ class CHistoryOrder : public COrder { private: public: CHistoryOrder(); ~CHistoryOrder(); }; //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ CHistoryOrder::CHistoryOrder() { } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ CHistoryOrder::~CHistoryOrder() { } //+------------------------------------------------------------------+
现在所有编译都没有问题。
该类很小。 我们需要重新定义 COrder 父类方法,它返回订单属性维护标志,以及设定在构造函数里将订单票据传递给类:
//+------------------------------------------------------------------+ //| HistoryOrder.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 "Order.mqh" //+------------------------------------------------------------------+ //| 历史市价单 | //+------------------------------------------------------------------+ class CHistoryOrder : public COrder { public: //--- 构造函数 CHistoryOrder(const ulong ticket) : COrder(ORDER_STATUS_HISTORY_ORDER,ticket) {} //--- 支持的整数型订单属性 virtual bool SupportProperty(ENUM_ORDER_PROP_INTEGER property); }; //+------------------------------------------------------------------+ //| 如果订单支持所传递的属性,则返回 'true' | //| 否则,返回 'false' | //+------------------------------------------------------------------+ bool CHistoryOrder::SupportProperty(ENUM_ORDER_PROP_INTEGER property) { if(property==ORDER_PROP_TIME_EXP || property==ORDER_PROP_DEAL_ENTRY || property==ORDER_PROP_TIME_UPDATE || property==ORDER_PROP_TIME_UPDATE_MSC ) return false; return true; } //+------------------------------------------------------------------+
因此,所选订单的票据 传递到类构造函数,而订单状态(历史订单)及其票据传递给 COrder 父对象的受保护构造函数。
我们还重新定义了父类的虚方法,返回对整数型订单属性的支持。 返回支持的订单实数型和字符串型属性的方法保持不变 — 这些父类方法总是返回 'true',我们假设历史订单支持所有实数型和字符串型属性,因此我们尚未重新定义它们。
检查支持订单整数型属性的方法。 如果这是到期时间,成交方向或仓位变更时间,则返回 'false'。 市价订单不支持此类属性。 支持所有其余属性,并返回 'true'。
以类似的方式,创建历史(已删除)挂单和 CHistoryDeal 历史成交类的 CHistoryPending 类:
//+------------------------------------------------------------------+ //| HistoryPending.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 "Order.mqh" //+------------------------------------------------------------------+ //| 删除挂单 | //+------------------------------------------------------------------+ class CHistoryPending : public COrder { public: //--- 构造函数 CHistoryPending(const ulong ticket) : COrder(ORDER_STATUS_HISTORY_PENDING,ticket) {} //--- 支持的订单属性 (1) 实数型, (2) 整数型 virtual bool SupportProperty(ENUM_ORDER_PROP_DOUBLE property); virtual bool SupportProperty(ENUM_ORDER_PROP_INTEGER property); }; //+------------------------------------------------------------------+ //| 如果订单支持所传递的属性,则返回 'true' | //| 否则,返回 'false' | //+------------------------------------------------------------------+ bool CHistoryPending::SupportProperty(ENUM_ORDER_PROP_INTEGER property) { if(property==ORDER_PROP_PROFIT_PT || property==ORDER_PROP_DEAL_ORDER || property==ORDER_PROP_DEAL_ENTRY || property==ORDER_PROP_TIME_UPDATE || property==ORDER_PROP_TIME_UPDATE_MSC || property==ORDER_PROP_TICKET_FROM || property==ORDER_PROP_TICKET_TO || property==ORDER_PROP_CLOSE_BY_SL || property==ORDER_PROP_CLOSE_BY_TP ) return false; return true; } //+------------------------------------------------------------------+ //| 如果订单支持所传递的属性,则返回 'true' | //| 否则,返回 'false' | //+------------------------------------------------------------------+ bool CHistoryPending::SupportProperty(ENUM_ORDER_PROP_DOUBLE property) { if(property==ORDER_PROP_COMMISSION || property==ORDER_PROP_SWAP || property==ORDER_PROP_PROFIT || property==ORDER_PROP_PROFIT_FULL || property==ORDER_PROP_PRICE_CLOSE ) return false; return true; } //+------------------------------------------------------------------+
//+------------------------------------------------------------------+ //| HistoryDeal.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 "Order.mqh" //+------------------------------------------------------------------+ //| 历史成交 | //+------------------------------------------------------------------+ class CHistoryDeal : public COrder { public: //--- 构造函数 CHistoryDeal(const ulong ticket) : COrder(ORDER_STATUS_DEAL,ticket) {} //--- 支持的成交属性 (1) 实数型, (2) 整数型 virtual bool SupportProperty(ENUM_ORDER_PROP_DOUBLE property); virtual bool SupportProperty(ENUM_ORDER_PROP_INTEGER property); }; //+------------------------------------------------------------------+ //| 如果订单支持所传递的属性,则返回 'true' | //| 否则,返回 'false' | //+------------------------------------------------------------------+ bool CHistoryDeal::SupportProperty(ENUM_ORDER_PROP_INTEGER property) { if(property==ORDER_PROP_TIME_EXP || property==ORDER_PROP_PROFIT_PT || property==ORDER_PROP_POSITION_BY_ID || property==ORDER_PROP_TIME_UPDATE || property==ORDER_PROP_TIME_UPDATE_MSC || property==ORDER_PROP_STATE || ( this.OrderType()==DEAL_TYPE_BALANCE && ( property==ORDER_PROP_POSITION_ID || property==ORDER_PROP_POSITION_BY_ID || property==ORDER_PROP_TICKET_FROM || property==ORDER_PROP_TICKET_TO || property==ORDER_PROP_DEAL_ORDER || property==ORDER_PROP_MAGIC || property==ORDER_PROP_TIME_CLOSE || property==ORDER_PROP_TIME_CLOSE_MSC || property==ORDER_PROP_CLOSE_BY_SL || property==ORDER_PROP_CLOSE_BY_TP ) ) ) return false; return true; } //+------------------------------------------------------------------+ bool CHistoryDeal::SupportProperty(ENUM_ORDER_PROP_DOUBLE property) { if(property==ORDER_PROP_TP || property==ORDER_PROP_SL || property==ORDER_PROP_PRICE_CLOSE || property==ORDER_PROP_VOLUME_CURRENT || property==ORDER_PROP_PRICE_STOP_LIMIT || ( this.OrderType()==DEAL_TYPE_BALANCE && ( property==ORDER_PROP_PRICE_OPEN || property==ORDER_PROP_COMMISSION || property==ORDER_PROP_SWAP || property==ORDER_PROP_VOLUME ) ) ) return false; return true; } //+------------------------------------------------------------------+
我们创建了三个订单对象,未来历史订单的集合将基于它们。 所有这些都是从 COrder 抽象订单基类继承而来的。 它们均有其属性,但只允许返回这些订单类型支持的属性。 所有这些都将位于单一集合列表(历史订单集合)中,我们将从中收集有关帐户历史记录当中任意组合和订单的所有必要数据。
利用 SupportProperty() 方法在流水账中显示订单属性时,并非所有已支持或不支持的属性都予以考虑。 例如,成交只考虑三种类型:买入,卖出和余额操作。
尚未顾及的属性,且在方法返回结果中未明确指定支持,则会打印输出。 然后,您可以将它们添加到方法中,以便不打印这些始终返回零值的属性,而不管情况如何(不支持的)。掌握帐户历史记录总是有帮助的。 终端提供这些信息,并提供在程序中获取它的工具。 但是,我们当前的任务需要一个自定义列表,我们可以对其进行排序和重新编排,以便将必要的数据返回给我们的程序 这意味着应在每次即时报价处检查先前帐户历史状态的变化。 如果检测到变化,则重新计算历史订单和成交列表。 但是在每次即时报价处对整个历史进行排序太耗费资源。 所以,我们只会将新数据添加到列表,而以前的数据已存储在列表中。
我们在 Collections 文件夹中创建新类 CHistoryCollection:
右键单击 Collections 文件夹,选择“新文件”,在 MQL 向导窗口中选择“新类”,然后单击“下一步”。 键入 CHistoryCollection 类名,将基类字段留空,然后单击“完成”。
在 Collections 文件夹中生成新文件 HistoryCollection:
//+------------------------------------------------------------------+ //| HistoryCollection.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 CHistoryCollection { private: public: CHistoryCollection(); ~CHistoryCollection(); }; //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ CHistoryCollection::CHistoryCollection() { } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ CHistoryCollection::~CHistoryCollection() { } //+------------------------------------------------------------------+
我们来充实它。
对于该列表,我们将使用CArrayObj 标准库的指向对象实例的动态列表。 在文件中包含它并立即定义(您可以使用右键单击关联菜单来执行此操作):
包含 CArrayObj 并类的私有部分 定义历史订单和成交的列表:
//+------------------------------------------------------------------+ //| HistoryCollection.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 <Arrays\ArrayObj.mqh> //+------------------------------------------------------------------+ //| 历史订单和成交集合 | //+------------------------------------------------------------------+ class CHistoryCollection { private: CArrayObj m_list_all_orders; // 历史订单和成交列表 public: CHistoryCollection(); ~CHistoryCollection(); }; //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ CHistoryCollection::CHistoryCollection() { } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ CHistoryCollection::~CHistoryCollection() { } //+------------------------------------------------------------------+
我们需要保存最后订单和成交的索引,并添加到集合中。 此外,我们需要知道过去和现在的订单和成交数量之间的差值,因此我们要创建用于存储它们的私有类成员:
//+------------------------------------------------------------------+ //| HistoryCollection.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 <Arrays\ArrayObj.mqh> //+------------------------------------------------------------------+ //| 历史订单和成交集合 | //+------------------------------------------------------------------+ class CHistoryCollection { private: CArrayObj m_list_all_orders; // 历史订单和成交列表 int m_index_order; // 将来自终端历史列表(MQL4,MQL5)的最后一笔订单的索引添加到集合里 int m_index_deal; // 将来自终端历史列表(MQL5)的最后一笔成交的索引添加到集合里 int m_delta_order; // 与过去的检查相比,订单数量的差值 int m_delta_deal; // 与过去的检查相比,成交数量的差值 public: CHistoryCollection(); ~CHistoryCollection(); }; //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ CHistoryCollection::CHistoryCollection() { } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ CHistoryCollection::~CHistoryCollection() { } //+------------------------------------------------------------------+
在首次启动期间,将重置类中的所有私有成员,并再次重新计算历史记录。 为此,只需在类构造函数中添加类成员初始化列表,并设置集合列表排序时所依据的默认条件。
目前,我们有默认的构造函数。 在编写实现之前,我们应先创建一个枚举,内中包含订单和成交集合列表中进行排序时的所有可能标准。
但首先,我们先来组织如何在流水账中更合理地显示整数型、实数型和字符串型订单属性。 从函数库根文件夹中打开 Defines.mqh 文件,并按所需顺序放置枚举成员:
//+------------------------------------------------------------------+ //| 订单、成交、仓位的整数型属性 | //+------------------------------------------------------------------+ enum ENUM_ORDER_PROP_INTEGER { ORDER_PROP_TICKET = 0, // 订单票据 ORDER_PROP_MAGIC, // 订单魔幻数字 ORDER_PROP_TIME_OPEN, // 开单时间 (MQL5 成交时间) ORDER_PROP_TIME_CLOSE, // 平仓时间 (MQL5 执行或删除时间 - ORDER_TIME_DONE) ORDER_PROP_TIME_OPEN_MSC, // 开单时间的毫秒值 (MQL5 成交时间毫秒值) ORDER_PROP_TIME_CLOSE_MSC, // 平单时间的毫秒值 (MQL5 执行或删除时间 - ORDER_TIME_DONE_MSC) ORDER_PROP_TIME_EXP, // 订单失效日期(对于挂单) ORDER_PROP_STATUS, // 订单状态 (来自 ENUM_ORDER_STATUS 枚举) ORDER_PROP_TYPE, // 订单类型 (MQL5 成交类型) ORDER_PROP_DIRECTION, // 方向 (买入,卖出) 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_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, // 由止盈平仓的标志 }; #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_SL, // 止损价 ORDER_PROP_TP, // 止盈价 ORDER_PROP_PROFIT, // 盈利 ORDER_PROP_COMMISSION, // 佣金 ORDER_PROP_SWAP, // 隔夜利息 ORDER_PROP_VOLUME, // 交易量 ORDER_PROP_VOLUME_CURRENT, // 未执行交易量 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) // 字符串型属性的总数 //+------------------------------------------------------------------+
现在我们将订单和成交的所有可能类型的枚举添加到同一个文件:
//+------------------------------------------------------------------+ //| 订单和成交的可能标准 | //+------------------------------------------------------------------+ enum ENUM_SORT_ORDERS_MODE { //--- 按整数型属性排序 SORT_BY_ORDER_TICKET = 0, // 按订单票据排序 SORT_BY_ORDER_MAGIC = 1, // 按订单魔幻数字排序 SORT_BY_ORDER_TIME_OPEN = 2, // 按订单开单时间排序 SORT_BY_ORDER_TIME_CLOSE = 3, // 按订单平仓时间排序 SORT_BY_ORDER_TIME_OPEN_MSC = 4, // 按订单开单毫秒值排序 SORT_BY_ORDER_TIME_CLOSE_MSC = 5, // 按订单平单毫秒值排序 SORT_BY_ORDER_TIME_EXP = 6, // 按订单失效日期排序 SORT_BY_ORDER_STATUS = 7, // 按订单状态排序 (市价单/挂单/成交) SORT_BY_ORDER_TYPE = 8, // 按订单类型排序 SORT_BY_ORDER_REASON = 10, // 按成交/订单/仓位 原因/来源排序 SORT_BY_ORDER_POSITION_ID = 11, // 按仓位 ID 排序ID SORT_BY_ORDER_POSITION_BY_ID = 12, // 按逆向仓位 ID 排序 SORT_BY_ORDER_DEAL_ORDER = 13, // 按成交所依据的订单排序 SORT_BY_ORDER_DEAL_ENTRY = 14, // 按成交方向排序 – IN, OUT 或 IN/OUT SORT_BY_ORDER_TIME_UPDATE = 15, // 按仓位变更时间秒值排序 SORT_BY_ORDER_TIME_UPDATE_MSC = 16, // 按仓位变更时间毫秒值排序 SORT_BY_ORDER_TICKET_FROM = 17, // 按父订单票据排序 SORT_BY_ORDER_TICKET_TO = 18, // 按派生订单票据排序 SORT_BY_ORDER_PROFIT_PT = 19, // 按订单赢利点数排序 SORT_BY_ORDER_CLOSE_BY_SL = 20, // 按订单由止损平仓标志排序 SORT_BY_ORDER_CLOSE_BY_TP = 21, // 按订单由止盈平仓标志排序 //--- 按实数型属性排序 SORT_BY_ORDER_PRICE_OPEN = ORDER_PROP_INTEGER_TOTAL,// 按开单价排序 SORT_BY_ORDER_PRICE_CLOSE = 23, // 按平单价排序 SORT_BY_ORDER_SL = 24, // 按止损价排序 SORT_BY_ORDER_TP = 25, // 按止盈价排序 SORT_BY_ORDER_PROFIT = 26, // 按盈利排序 SORT_BY_ORDER_COMMISSION = 27, // 按佣金排序 SORT_BY_ORDER_SWAP = 28, // 按隔夜利息排序 SORT_BY_ORDER_VOLUME = 29, // 按交易量排序 SORT_BY_ORDER_VOLUME_CURRENT = 30, // 按未执行交易量排序 SORT_BY_ORDER_PROFIT_FULL = 31, // 按盈利+佣金+隔夜利息标准排序 SORT_BY_ORDER_PRICE_STOP_LIMIT= 32, // 按 StopLimit 挂单激活时的限价单排序 //--- 按字符串型属性排序 SORT_BY_ORDER_SYMBOL = ORDER_PROP_INTEGER_TOTAL+ORDER_PROP_DOUBLE_TOTAL,// 按品名排序 SORT_BY_ORDER_COMMENT = 34, // 按注释排序 SORT_BY_ORDER_EXT_ID = 35 // 按外部交易系统中的订单 ID 排序 }; //+------------------------------------------------------------------+
注意:枚举成员的排序索引应与属性枚举成员的索引一致,因为列表应按照与搜索该列表相同值进行排序。
正如我们所看到的,在该列表排序时跳过了 ORDER_PROP_DIRECTION 属性,因为这是函数库需要的服务属性,就像我们之前添加的其他自定义属性一样。 不过,它们也许需要排序。 这就是它们予以保留的原因。
现在我们能够实现 CHistoryCollection 类的构造函数:
//+------------------------------------------------------------------+ //| HistoryCollection.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 <Arrays\ArrayObj.mqh> #include "..\DELib.mqh" //+------------------------------------------------------------------+ //| 历史订单和成交集合 | //+------------------------------------------------------------------+ class CHistoryCollection { private: CArrayObj m_list_all_orders; // 历史订单和成交列表 int m_index_order; // 将来自终端历史列表(MQL4,MQL5)的最后一笔订单的索引添加到集合里 int m_index_deal; // 将来自终端历史列表(MQL5)的最后一笔成交的索引添加到集合里 int m_delta_order; // 与过去的检查相比,订单数量的差值 int m_delta_deal; // 与过去的检查相比,成交数量的差值 public: CHistoryCollection(); ~CHistoryCollection(); }; //+------------------------------------------------------------------+ //| 构造函数 | //+------------------------------------------------------------------+ CHistoryCollection::CHistoryCollection(void) : m_index_deal(0), m_delta_deal(0), m_index_order(0), m_delta_order(0) { m_list_all_orders.Sort(SORT_BY_ORDER_TIME_CLOSE); } //+------------------------------------------------------------------+
我们来分析一下。
由于类构造函数仅使用新添加的枚举值,因此我们应该将 Defines.mqh 文件包含在类文件中。
在第一篇文章中准备抽象订单的基类时,我们开发了 DELib.mqh 服务函数库,并包含了 Defines.mqh 文件以及所有必需的枚举和其宏替换。 因此,我们将包括服务函数库。
在构造函数的初始化列表中,当前和前一个数字之间所有索引和数值的差值抹平,并在构造函数体中指定按平单时间排序作为默认值。
现在是时候开始从帐户收集信息并将其保存在集合列表中。 要执行此操作,在循环中遍历帐户历史记录以及列表中的每个特定订单。 如果订单或成交的数量与先前的检查相比发生了变化,则设置交易发生事件的标志。 需要将有关帐户历史记录中发生新事件的消息发送到外部程序。
在类的私有部分中声明交易事件标志,并使用 Refresh() 方法更新公有部分当中的历史集合:
//+------------------------------------------------------------------+ //| 历史订单和成交集合 | //+------------------------------------------------------------------+ class CHistoryCollection { private: CArrayObj m_list_all_orders; // 所有历史订单和成交的列表 bool m_is_trade_event; // 交易事件标志 int m_index_order; // 将来自终端历史列表(MQL4,MQL5)的最后一笔订单的索引添加到集合里 int m_index_deal; // 将来自终端历史列表(MQL5)的最后一笔成交的索引添加到集合里 int m_delta_order; // 与过去的检查相比,订单数量的差值 int m_delta_deal; // 与过去的检查相比,成交数量的差值 public: CHistoryCollection(); //--- 更新订单列表,按新订单编号填充数据,并设置交易事件标志 void Refresh(void); }; //+------------------------------------------------------------------+
要实现更新订单集合列表,我们需要更多的宏替换来请求完整的历史数据。 HistorySelect() 函数即用于此。 在其参数中传递所需数据的开始和结束日期。 若要获取帐户历史记录的完整列表,初始日期应传递 0,而结束日期应传递 TimeCurrent()。 然而,在这种情况下,返回的历史数据有时可能不完整。 为避免这种情况,输入的日期应超过当前服务器的时间从而替代 TimeCurrent()。 我打算输入最大可能日期:3000.12.31 23:59:59。 此处的另一个优点是自定义品种也许包含这样的日期,且所取历史记录仍然有效。
我们在 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") // 国家语言 #define DFUN (__FUNCTION__+": ") // 函数的说明 #define END_TIME (D'31.12.3000 23:59:59') // 请求帐户历史记录数据的结束时间 //+------------------------------------------------------------------+
现在,我们输入 END_TIME 宏定义,来替代 TimeCurrent() 作为结束时间。
实现订单集合列表的更新:
//+------------------------------------------------------------------+ //| 更新订单列表 | //+------------------------------------------------------------------+ void CHistoryCollection::Refresh(void) { #ifdef __MQL4__ int total=::OrdersHistoryTotal(),i=m_index_order; for(; i<total; i++) { if(!::OrderSelect(i,SELECT_BY_POS,MODE_HISTORY)) continue; ENUM_ORDER_TYPE order_type=(ENUM_ORDER_TYPE)::OrderType(); //--- 已平仓和余额/信用操作 if(order_type<ORDER_TYPE_BUY_LIMIT || order_type>ORDER_TYPE_SELL_STOP) { CHistoryOrder *order=new CHistoryOrder(::OrderTicket()); if(order==NULL) continue; m_list_all_orders.InsertSort(order); } else { //--- 已删除挂单 CHistoryPending *order=new CHistoryPending(::OrderTicket()); if(order==NULL) continue; m_list_all_orders.InsertSort(order); } } //--- int delta_order=i-m_index_order; this.m_index_order=i; this.m_delta_order=delta_order; this.m_is_trade_event=(this.m_delta_order!=0 ? true : false); //--- __MQL5__ #else if(!::HistorySelect(0,END_TIME)) return; //--- 订单 int total_orders=::HistoryOrdersTotal(),i=m_index_order; for(; 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) { CHistoryOrder *order=new CHistoryOrder(order_ticket); if(order==NULL) continue; m_list_all_orders.InsertSort(order); } else { CHistoryPending *order=new CHistoryPending(order_ticket); if(order==NULL) continue; m_list_all_orders.InsertSort(order); } } //--- 保存上次添加的订单索引,以及与上一次检查相比的差值 int delta_order=i-this.m_index_order; this.m_index_order=i; this.m_delta_order=delta_order; //--- 成交 int total_deals=::HistoryDealsTotal(),j=m_index_deal; for(; j<total_deals; j++) { ulong deal_ticket=::HistoryDealGetTicket(j); if(deal_ticket==0) continue; CHistoryDeal *deal=new CHistoryDeal(deal_ticket); if(deal==NULL) continue; m_list_all_orders.InsertSort(deal); } //--- 保存上次添加的成交索引,以及与上一次检查相比的差值 int delta_deal=j-this.m_index_deal; this.m_index_deal=j; this.m_delta_deal=delta_deal; //--- 在历史记录中设置新事件标志 this.m_is_trade_event=(this.m_delta_order+this.m_delta_deal); #endif } //+------------------------------------------------------------------+
检查与 MQL4 或 MQL5 的关联。 我们分析 MQL5 代码作为示例,因为它有点复杂。
首先,请求完整账户历史。 如果失败,退出直到下一次即时报价。 若历史记录请求成功后,将创建两个列表 — 订单和成交列表。
首先,在循环中顺着所有订单列表移动。 循环移动的初始索引是前一循环操作的结果(在首次启动时 = 0)。 这可令我们仅移动自上次检查以来在历史记录中出现的新订单,而非沿着整个历史记录移动这样的资源密集型循环。
接下来,获取订单票据及其类型。 根据订单类型检查的结果创建新对象(即可为历史市价订单亦或已删除挂单)并将其放入按排序形式的集合列表(我们之前已经设置了按平单时间排序)。
完成循环操作后,保存订单新索引,作为新循环启动的起点。 之前和当前循环操作结果之间的差值是新添加的订单数量。
处理成交的循环是相同的,除了不需要按类型划分成交。 代之,您可以立即将成交对象添加到集合列表中。
MQL4 代码中的循环几乎相同,只是循环取自帐户可用的整个可能历史记录。 历史记录长度由用户在终端的历史记录选项卡中指定,这意味着当程序需要时,用户可以确保使用整个历史记录。 另一种选择是使用 WinAPI 获取整个历史记录,这超出了文章的范围。
完成循环后,检查新订单和交易的数量。 如果该数字大于零,则设置交易发生事件的标志。
我们已实现的获取数据的历史集合类类似于第一部分中测试 EA 的实现,除了在当前情况下,按它们的状态分别创建历史订单的不同对象。 为了检验它是如何工作的,我们需要从外部获取所创建的历史订单集合列表(来自使用该函数库的程序;在我们的例子中,这是一个测试 EA)。
为此,将没有参数的 GetList() 方法添加到 CHistoryCollection 类的公有部分(在下一篇文章中,我将添加可接收参数的列表方法)://+------------------------------------------------------------------+ //| 历史订单和成交集合 | //+------------------------------------------------------------------+ class CHistoryCollection { private: CArrayObj m_list_all_orders; // 所有历史订单和成交的列表 bool m_is_trade_event; // 交易事件标志 int m_index_order; // 将来自终端历史列表(MQL4,MQL5)的最后一笔订单的索引添加到集合里 int m_index_deal; // 将来自终端历史列表(MQL5)的最后一笔成交的索引添加到集合里 int m_delta_order; // 与过去的检查相比,订单数量的差值 int m_delta_deal; // 与过去的检查相比,成交数量的差值 public: //--- 按原样返回完整的集合列表 CArrayObj* GetList(void) { return &m_list_all_orders; } //--- 构造函数 CHistoryCollection(); //--- 更新订单列表,按新订单编号填充数据,并设置交易事件 void Refresh(void); }; //+------------------------------------------------------------------+
从列表中可以看出,返回指向列表的指针(而非对象本身)。 这足以在程序中使用该列表。 然而,这不足以快速便捷地使用函数库。在下一篇专门讨论该函数库的文章中,我将会实现根据请求轻松访问必要的数据。 在后续的文章中,我会在自定义程序中创建利用函数库的特殊函数来进一步简化访问。
我们看看列表是如何填充的。 为此,创建一个小型 EA。 在 TestDoEasy 文件夹(我们已在第一部分中创建)中,生成 Part02 子文件夹。 它将包含名为 TestDoEasyPart02 的第二个测试 EA 文件,并且与所创建的集合关联一起:
//+------------------------------------------------------------------+ //| TestDoEasyPart02.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\Collections\HistoryCollection.mqh> //+------------------------------------------------------------------+ //| 智能系统初始化函数 | //+------------------------------------------------------------------+ int OnInit() { //--- //--- return(INIT_SUCCEEDED); } //+------------------------------------------------------------------+ //| 智能系统逆初始化函数 | //+------------------------------------------------------------------+ void OnDeinit(const int reason) { //--- } //+------------------------------------------------------------------+ //| 智能系统即时报价函数 | //+------------------------------------------------------------------+ void OnTick() { //--- } //+------------------------------------------------------------------+
由于列表内现在存储的各类型对象继承自单一父级 COrder,故此我们实现在流水账中显示所选订单对象的描述。 若要执行此操作,在输入中创建含有选择显示的订单类型的枚举,以及集合对象来读取以下位置的帐户数据:
//+------------------------------------------------------------------+ //| TestDoEasyPart02.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\Collections\HistoryCollection.mqh> //--- 枚举 enum ENUM_TYPE_ORDERS { TYPE_ORDER_MARKET, // 市价订单 TYPE_ORDER_PENDING, // 挂单 TYPE_ORDER_DEAL // 成交 }; //--- 输入参数 input ENUM_TYPE_ORDERS InpOrderType = TYPE_ORDER_DEAL; // 显示类型: //--- 全局变量 CHistoryCollection history; //+------------------------------------------------------------------+ //| 智能系统初始化函数 | //+------------------------------------------------------------------+
与之前的测试 EA 一样,来自函数库说明的第一部分,在 OnInit() 处理程序中读取帐户历史记录,并在循环中将有关的订单信息显示在流水日志里:
//+------------------------------------------------------------------+ //| 智能系统初始化函数 | //+------------------------------------------------------------------+ int OnInit() { //--- 更新历史记录 history.Refresh(); //--- 获取指向完整集合列表的指针 CArrayObj* list=history.GetList(); if(list==NULL) { Print("Could not get collection list"); return INIT_FAILED; } int total=list.Total(); for(int i=0;i<total;i++) { //--- 从列表中获取订单 COrder* order=list.At(i); if(order==NULL) continue; //--- 如果是一笔成交 if(order.Status()==ORDER_STATUS_DEAL && InpOrderType==TYPE_ORDER_DEAL) order.Print(); //--- 如果是一笔历史市价单 if(order.Status()==ORDER_STATUS_HISTORY_ORDER && InpOrderType==TYPE_ORDER_MARKET) order.Print(); //--- 如果是一笔已删除挂单 if(order.Status()==ORDER_STATUS_HISTORY_PENDING && InpOrderType==TYPE_ORDER_PENDING) order.Print(); } //--- return(INIT_SUCCEEDED); } //+------------------------------------------------------------------+
在此,创建一个指向集合列表的指针,并使用已在 CHistoryCollection 里创建的 GetList() 方法接收它。
接下来,在循环中,从列表中获取订单对象,检查其状态以及在 EA 设置中在流水日志里显示数据的权限。
根据结果,在流水日志里显示数据或无视。
尽管,我们从列表中获取 COrder 基础对象,但只有基本订单派生后代固有的数据显示在流水日志里 — 市价单、挂单和成交则由重定义的虚方法处理并返回标志,表明订单仅支持其固有属性。
编译并启动 EA。 流水日志显示所选订单和成交类型的数据:
如果您仔细查看显示的属性类型,我们会看到不常见的 MQL5 订单属性:利润,隔夜利息,佣金和赢利点数。
我们在已创建的对象中进行一些改进和附加。
将未支持的属性 MQL5 赢利点数 添加到 CHistoryOrder 类的 SupportProperty(ENUM_ORDER_PROP_INTEGER property) 方法中:
//+------------------------------------------------------------------+ //| 如果订单支持所传递的属性,则返回 'true' | //| 否则,返回 'false' | //+------------------------------------------------------------------+ bool CHistoryOrder::SupportProperty(ENUM_ORDER_PROP_INTEGER property) { if(property==ORDER_PROP_TIME_EXP || property==ORDER_PROP_DEAL_ENTRY || property==ORDER_PROP_TIME_UPDATE || property==ORDER_PROP_TIME_UPDATE_MSC #ifdef __MQL5__ || property==ORDER_PROP_PROFIT_PT #endif ) return false; return true; } //+------------------------------------------------------------------+
并添加另一个虚方法,返回订单支持的实数型属性:
//+------------------------------------------------------------------+ //| 历史市价单 | //+------------------------------------------------------------------+ class CHistoryOrder : public COrder { public: //--- 构造函数 CHistoryOrder(const ulong ticket) : COrder(ORDER_STATUS_HISTORY_ORDER,ticket) {} //--- 支持的订单整数型属性 virtual bool SupportProperty(ENUM_ORDER_PROP_INTEGER property); //--- 支持的订单实数型属性 virtual bool SupportProperty(ENUM_ORDER_PROP_DOUBLE property); }; //+------------------------------------------------------------------+
以及其实现:
//+------------------------------------------------------------------+ //| 如果订单支持所传递的属性,则返回 'true' | //| 否则,返回 'false' | //+------------------------------------------------------------------+ bool CHistoryOrder::SupportProperty(ENUM_ORDER_PROP_DOUBLE property) { #ifdef __MQL5__ if(property==ORDER_PROP_PROFIT || property==ORDER_PROP_PROFIT_FULL || property==ORDER_PROP_SWAP || property==ORDER_PROP_COMMISSION || property==ORDER_PROP_PRICE_STOP_LIMIT ) return false; #endif return true; } //+------------------------------------------------------------------+
如果这是 MQL5,则不支持利润、隔夜利息、佣金、全部盈利和 StopLimit 订单价格。 对于 MQL4 和其它 MQL5 中的实数型订单属性,返回订单支持该实数型属性的标志。
MQL5 订单还有 ORDER_STATE 属性。 这是在 ENUM_ORDER_STATE 枚举中设置的订单状态。
将其添加到订单整数型属性列表(Defines.mqh 文件中的 ENUM_ORDER_PROP_INTEGER 枚举):
//+------------------------------------------------------------------+ //| 订单、成交、仓位的整数型属性 | //+------------------------------------------------------------------+ enum ENUM_ORDER_PROP_INTEGER { ORDER_PROP_TICKET = 0, // 订单票据 ORDER_PROP_MAGIC, // 订单魔幻数字 ORDER_PROP_TIME_OPEN, // 开单时间 (MQL5 成交时间) ORDER_PROP_TIME_CLOSE, // 平仓时间 (MQL5 执行或删除时间 - ORDER_TIME_DONE) ORDER_PROP_TIME_OPEN_MSC, // 开单时间的毫秒值 (MQL5 成交时间毫秒值) ORDER_PROP_TIME_CLOSE_MSC, // 平单时间的毫秒值 (MQL5 执行或删除时间 - ORDER_TIME_DONE_MSC) ORDER_PROP_TIME_EXP, // 订单失效日期(对于挂单) ORDER_PROP_STATUS, // 订单状态 (来自 ENUM_ORDER_STATUS 枚举) ORDER_PROP_TYPE, // 订单类型 (MQL5 成交类型) ORDER_PROP_DIRECTION, // 方向 (买入,卖出) ORDER_PROP_REASON, // 成交/订单/仓位原因或来源 ORDER_PROP_STATE, // 订单状态 (来自 ENUM_ORDER_STATE enumeration) 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_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, // 由止盈平仓的标志 }; #define ORDER_PROP_INTEGER_TOTAL (23) // 整数型属性的总数 //+------------------------------------------------------------------+
并且确保在 ORDER_PROP_INTEGER_TOTAL 宏替换中将订单的整数型属性数量从 22 更改为 23,表示整数型属性的数量,并用于计算订单属性所必要的精确“地址” 。
在同一个文件中,将新属性添加到可能的订单排序条件中,这样我们就可以按照这个新属性对订单进行排序,即使随后更改了枚举,也可实现更便捷的索引计算 :
//+------------------------------------------------------------------+ //| 订单和成交的可能标准 | //+------------------------------------------------------------------+ #define FIRST_DBL_PROP (ORDER_PROP_INTEGER_TOTAL) #define FIRST_STR_PROP (ORDER_PROP_INTEGER_TOTAL+ORDER_PROP_DOUBLE_TOTAL) enum ENUM_SORT_ORDERS_MODE { //--- 按整数型属性排序 SORT_BY_ORDER_TICKET = 0, // 按订单票据排序 SORT_BY_ORDER_MAGIC = 1, // 按订单魔幻数字排序 SORT_BY_ORDER_TIME_OPEN = 2, // 按订单开单时间排序 SORT_BY_ORDER_TIME_CLOSE = 3, // 按订单平仓时间排序 SORT_BY_ORDER_TIME_OPEN_MSC = 4, // 按订单开单毫秒值排序 SORT_BY_ORDER_TIME_CLOSE_MSC = 5, // 按订单平单毫秒值排序 SORT_BY_ORDER_TIME_EXP = 6, // 按订单失效日期排序 SORT_BY_ORDER_STATUS = 7, // 按订单状态排序 (市价单/挂单/成交) SORT_BY_ORDER_TYPE = 8, // 按订单类型排序 SORT_BY_ORDER_REASON = 10, // 按成交/订单/仓位 原因/来源排序 SORT_BY_ORDER_STATE = 11, // 按订单状态排序 SORT_BY_ORDER_POSITION_ID = 12, // 按仓位 ID 排序 SORT_BY_ORDER_POSITION_BY_ID = 13, // 按逆向仓位 ID 排序 SORT_BY_ORDER_DEAL_ORDER = 14, // 按成交所依据的订单排序 SORT_BY_ORDER_DEAL_ENTRY = 15, // 按成交方向排序 – IN, OUT 或 IN/OUT SORT_BY_ORDER_TIME_UPDATE = 16, // 按仓位变更时间的秒数值排序 SORT_BY_ORDER_TIME_UPDATE_MSC = 17, // 按仓位变更时间的毫秒数值排序 SORT_BY_ORDER_TICKET_FROM = 18, // 按父订单票据排序 SORT_BY_ORDER_TICKET_TO = 19, // 按衍生订单票据排序 SORT_BY_ORDER_PROFIT_PT = 20, // 按订单利润点数排序 SORT_BY_ORDER_CLOSE_BY_SL = 21, // 按订单由止损平仓标志排序 SORT_BY_ORDER_CLOSE_BY_TP = 22, // 按订单由止损平仓标志排序 //--- 按实数型属性排序 SORT_BY_ORDER_PRICE_OPEN = FIRST_DBL_PROP, // 按开单价排序 SORT_BY_ORDER_PRICE_CLOSE = FIRST_DBL_PROP+1, // 按平单价排序 SORT_BY_ORDER_SL = FIRST_DBL_PROP+2, // 按止损价排序 SORT_BY_ORDER_TP = FIRST_DBL_PROP+3, // 按止盈价排序 SORT_BY_ORDER_PROFIT = FIRST_DBL_PROP+4, // 按盈利排序 SORT_BY_ORDER_COMMISSION = FIRST_DBL_PROP+5, // 按佣金排序 SORT_BY_ORDER_SWAP = FIRST_DBL_PROP+6, // 按隔夜利息排序 SORT_BY_ORDER_VOLUME = FIRST_DBL_PROP+7, // 按交易量排序 SORT_BY_ORDER_VOLUME_CURRENT = FIRST_DBL_PROP+8, // 按未执行交易量排序 SORT_BY_ORDER_PROFIT_FULL = FIRST_DBL_PROP+9, // 按 盈利+佣金+隔夜利息条件排序 SORT_BY_ORDER_PRICE_STOP_LIMIT= FIRST_DBL_PROP+10, // 按 StopLimit 挂单激活时的限价单排序 //--- 按字符串型属性排序 SORT_BY_ORDER_SYMBOL = FIRST_STR_PROP, // 按品名排序 SORT_BY_ORDER_COMMENT = FIRST_STR_PROP+1, // 按注释排序 SORT_BY_ORDER_EXT_ID = FIRST_STR_PROP+2 // 按外部交易系统中的订单 ID 排序 }; //+------------------------------------------------------------------+
在 Order.mqh 文件的 COrder 抽象订单类的受保护部分中,声明 OrderState() 方法,将其状态从 ENUM_ORDER_STATE 枚举改为订单属性:
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 OrderState(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;
并添加其实现:
//+------------------------------------------------------------------+ //| 返回订单状态 | //+------------------------------------------------------------------+ long COrder::OrderState(void) const { #ifdef __MQL4__ return ORDER_STATE_FILLED; #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_STATE); break; case ORDER_STATUS_MARKET_PENDING : res=::OrderGetInteger(ORDER_STATE); break; case ORDER_STATUS_MARKET_ACTIVE : case ORDER_STATUS_DEAL : default : res=0; break; } return res; #endif } //+------------------------------------------------------------------+
在 MQL4 的环境下,我们目前返回订单完整执行的状态。 在 MQL5 的环境下,即可返回 0(如果这是成交或仓位),亦或取决于订单状态返回订单状态(如果这是市价单或挂单)。
在 COrder 类的公有部分中,声明返回订单状态描述的方法:
//+------------------------------------------------------------------+ //| 订单对象属性的描述 | //+------------------------------------------------------------------+ //--- 获取订单的(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 StateDescription(void) const; //--- 返回成交状态名称 string DealEntryDescription(void) const; //--- 返回订单/仓位方向类型 string DirectionDescription(void) const; //--- 将订单属性的描述发送到流水日志(full_prop=true - 所有属性,false - 仅支持的属性) void Print(const bool full_prop=false);
以及其实现:
//+------------------------------------------------------------------+ //| 返回订单状态描述 | //+------------------------------------------------------------------+ string COrder::StateDescription(void) const { if(this.Status()==ORDER_STATUS_DEAL || this.Status()==ORDER_STATUS_MARKET_ACTIVE) return ""; else switch(this.StateOrder()) { case ORDER_STATE_STARTED : return TextByLanguage("Ордер проверен на корректность, но еще не принят брокером","Order checked for correctness, but not yet accepted by broker"); case ORDER_STATE_PLACED : return TextByLanguage("Ордер принят","Order accepted"); case ORDER_STATE_CANCELED : return TextByLanguage("Ордер снят клиентом","Order withdrawn by client"); case ORDER_STATE_PARTIAL : return TextByLanguage("Ордер выполнен частично","Order filled partially"); case ORDER_STATE_FILLED : return TextByLanguage("Ордер выполнен полностью","Order filled"); case ORDER_STATE_REJECTED : return TextByLanguage("Ордер отклонен","Order rejected"); case ORDER_STATE_EXPIRED : return TextByLanguage("Ордер снят по истечении срока его действия","Order withdrawn upon expiration"); case ORDER_STATE_REQUEST_ADD : return TextByLanguage("Ордер в состоянии регистрации (выставление в торговую систему)","Order in state of registration (placing in trading system)"); case ORDER_STATE_REQUEST_MODIFY : return TextByLanguage("Ордер в состоянии модификации","Order in state of modification."); case ORDER_STATE_REQUEST_CANCEL : return TextByLanguage("Ордер в состоянии удаления","Order in deletion state"); default : return TextByLanguage("Неизвестное состояние","Unknown state"); } } //+------------------------------------------------------------------+
如果这是成交或仓位,则返回空字符串,否则,检查订单状态并返回其描述。
将返回的订单状态描述添加到 GetPropertyDescription(ENUM_ORDER_PROP_INTEGER property) 方法的实现中:
//+------------------------------------------------------------------+ //| 返回订单整数型属性的描述 | //+------------------------------------------------------------------+ string COrder::GetPropertyDescription(ENUM_ORDER_PROP_INTEGER property) { return ( //--- 一般属性 property==ORDER_PROP_MAGIC ? TextByLanguage("Магик","Magic number")+ (!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_STATE ? TextByLanguage("Состояние","Statе")+ (!this.SupportProperty(property) ? TextByLanguage(": Свойство не поддерживается",": Property not supported") : ": \""+this.StateDescription()+"\"" ) : //--- 附加属性 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")) ) : "" ); } //+------------------------------------------------------------------+
我们已完成所有改进。
应该记住,函数库是“与时俱进”开发的,并且是测试版,因此可能在以后会有各种实现修订、变更以及附加。
在下一篇文章中,我将开发一个类,能够便利地支持选择任意标准对订单、成交和仓位进行排序,并创建市价单和仓位的集合。
下面附有当前版本函数库的所有文件,以及测试 EA 文件供您测试和下载。
在评论中提出您的问题、意见和建议。
返回目录
本社区仅针对特定人员开放
查看需注册登录并通过风险意识测评
5秒后跳转登录页面...
移动端课程