请 [注册] 或 [登录]  | 返回主站

量化交易吧 /  数理科学 帖子:3353041 新帖:2

轻松快捷开发 MetaTrader 程序的函数库(第六部分):净持帐户事件

螺罗丝发表于:7 月 8 日 15:52回复(1)

内容

  • 帐户类型的异同
  • 在净持结算帐户上实现事件处理
  • 测试对冲和净持结算账户的表现
  • 下一步是什么?

帐户类型的异同

若要跟踪净持结算帐户事件,我们需要了解对冲和净持结算帐户之间的差异。

差别与仓位的表述形式有关。 对冲账户允许我们在单一品种上同时开立任意数量、任意方向的持仓,而净持结算账户同时只允许一笔一个方向。 对冲账户允许通过逆向仓位的交易量来平仓。
在这种情况下:

  • 如果逆向仓位的交易量小于已平仓位的交易量,则逆反仓位彻底平仓,而原持仓则是部分抵消,
  • 如果逆向仓位的交易量大于已平仓位的交易量,则逆向仓位部分平仓,而原持仓则完全抵消,
  • 如果这两笔仓位的交易量相等,两者均平仓;
  • 每笔仓位 ID 等于开仓订单的票据。 此 ID 在整笔仓位生存周期内不会改变;
  • 每笔仓位都有自己的票据,其值等于导致开仓的订单票据;
  • 如果我们发送一个请求,在当前持仓方向上加仓,则会开立一笔带有新 ID 和票据的新仓位。

在净持结算账户中,在一个品种上处理一笔持仓排除了通过逆向仓位平仓的可能性。 不过,在类似的情况下(当逆向订单激活时),此持仓可以部分或全部平仓,或者改变其方向:

  • 如果激活的逆向订单的交易量小于当前那笔持仓,则持仓部分平仓,
  • 如果激活的逆向订单的交易量等于当前那笔持仓,则持仓全部平仓,
  • 如果激活的逆向订单的交易量大于当前那笔持仓,则持仓方向改变(反转),
  • 每笔仓位 ID 等于开仓订单的票据。 此 ID 在整笔仓位生存周期内不会改变;
  • 每笔仓位的票据等于导致仓位逆转的订单票据。 票据可能与 ID 不同。 在某种程度上,在对冲账户中多笔仓位的票据会重复;
  • 如果我们发送请求,在当前持仓方向上加仓,则激活订单的交易量将会被添加到当前持仓的交易量。 仓位的票据不会改变。

在净持结算帐户上实现事件处理

为了跟踪净持结算账户事件,我们将简单地按账户类型划分处理仓位事件。 这增加了代码量,但由于功能分离反而令逻辑更清晰。 我们将会化代码,并在稍后删除所有冗余 — 在调试并确保操作稳定之后。

将新常量添加到事件类型枚举时,我注意到排序有时会错误地工作。 检查这种行为的原因揭示了两个主要因素,一是事件/订单属性匹配按此类型排序,二是它们在枚举中的位置,与每个常量的编号无关。 例如,如果某个属性不用于搜索,则应跳过该属性,并将枚举常量的正确编号分配给搜索方法。 此外,排序中未使用的事件/订单属性也应放在属性类型列表的末尾。 若要计算以下属性类型的初始数量,应从初始属性类型索引中减去先前类型属性数量中未使用的属性数。

为了验证排序方法枚举的创建,在 DELib.mqh 服务函数文件中添加了一个小函数:

//+------------------------------------------------------------------+
//| 在流水日志中显示所有排序枚举常量                                           |
//+------------------------------------------------------------------+
void EnumNumbersTest()
  {
   string enm="ENUM_SORT_ORDERS_MODE";
   string t=StringSubstr(enm,5,5)+"BY";
   Print("Search of the values of the enumaration ",enm,":");
   ENUM_SORT_ORDERS_MODE type=0;
   while(StringFind(EnumToString(type),t)==0)
     {
      Print(enm,"[",type,"]=",EnumToString(type));
      if(type>500) break;
      type++;
     }
   Print("\nNumber of members of the ",enm,"=",type);
  }
//+------------------------------------------------------------------+

若要检查特定排序类型枚举的内容,应该手工输入两个函数的代码(我没有找到一种在 ENUM_SORT_ORDERS_MODE 中自动设置某个枚举 type=0; 代码的方法)。

现在,如果我们在测试 EA 的 OnInit() 处理程序中调用此函数,则指定枚举的所有常量名称和相应的索引都将显示在流水日志中。
检查枚举时,我检测到它们创建错误。 为了修复这个问题,我稍微修改了 Defines.mqh 文件中的枚举。
我在枚举中为常量设置了不同的顺序 — 在排序中未使用的属性放在对象属性枚举常量列表的末尾。 还有,应添加用于搜索和排序的 未使用属性数的宏替换在计算排序枚举中的初始属性索引时,会用到这些宏替换,从而计算枚举中初始常量的正确索引。
此外,应添加针对净持结算账户事件的新常量类型用于存储对冲账户里魔幻数字和逆向仓位品种的常量

订单和仓位通常需要分组,以便可以同时处理一组选定的订单和仓位。 该函数库允许通过简单地将组 ID 添加到抽象订单属性来实现它。 这可将具有相似 ID 的任何订单和仓位排列到单个列表中,并与所选组一起处理。
这样的 ID 被添加到订单属性和订单排序列表里

下面是修改后的 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 DFUN_ERR_LINE            (__FUNCTION__+(TerminalInfoString(TERMINAL_LANGUAGE)=="Russian" ? ", Page " : ", Line ")+(string)__LINE__+": ")
#define DFUN                     (__FUNCTION__+": ")        // "函数描述"
#define COUNTRY_LANG             ("Russian")                // 国家语言
#define END_TIME                 (D'31.12.3000 23:59:59')   // 请求帐户历史记录数据的结束日期
#define TIMER_FREQUENCY          (16)                       // 函数库定时器的最小频率(以毫秒为单位)
#define COLLECTION_PAUSE         (250)                      // 订单和成交集合计时器暂停,以毫秒为单位
#define COLLECTION_COUNTER_STEP  (16)                       // 订单和成交集合计时器的计数器增量
#define COLLECTION_COUNTER_ID    (1)                        // 订单和成交集合计时器的计数器 ID
#define COLLECTION_HISTORY_ID    (0x7778+1)                 // 历史集合清单ID
#define COLLECTION_MARKET_ID     (0x7778+2)                 // 在场集合清单 ID
#define COLLECTION_EVENTS_ID     (0x7778+3)                 // 事件集合列表 ID
//+------------------------------------------------------------------+
//| 结构                                                               |  
//+------------------------------------------------------------------+

//+------------------------------------------------------------------+
//| 枚举                                                               |
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
//| 搜索和排序数据                                                       |
//+------------------------------------------------------------------+
enum ENUM_COMPARER_TYPE
  {
   EQUAL,                                                   // 等于
   MORE,                                                    // 多于
   LESS,                                                    // 少于
   NO_EQUAL,                                                // 不等于
   EQUAL_OR_MORE,                                           // 大于等于
   EQUAL_OR_LESS                                            // 小于等于
  };
//+------------------------------------------------------------------+
//| 按时间排序的可能选项                                                  |
//+------------------------------------------------------------------+
enum ENUM_SELECT_BY_TIME
  {
   SELECT_BY_TIME_OPEN,                                     // 按开仓时间
   SELECT_BY_TIME_CLOSE,                                    // 按平仓时间
   SELECT_BY_TIME_OPEN_MSC,                                 // 按开仓时间的毫秒值
   SELECT_BY_TIME_CLOSE_MSC,                                // 按平仓时间的毫秒值
  };
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
//| 处理订单的数据                                                       |
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
//| 抽象订单类型 (状况)                                                 |
//+------------------------------------------------------------------+
enum ENUM_ORDER_STATUS
  {
   ORDER_STATUS_MARKET_PENDING,                             // 在场挂单
   ORDER_STATUS_MARKET_ORDER,                               // 在场订单
   ORDER_STATUS_MARKET_POSITION,                            // 持仓
   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,                                    // 开单时间 (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,                                         // 订单/成交类型
   ORDER_PROP_REASON,                                       // 成交/订单/仓位原因或来源
   ORDER_PROP_STATE,                                        // 订单状态(来自 ENUM_ORDER_STATE 枚举)
   ORDER_PROP_POSITION_ID,                                  // 仓位 ID
   ORDER_PROP_POSITION_BY_ID,                               // 逆向仓位 ID
   ORDER_PROP_DEAL_ORDER_TICKET,                            // 触发成交的订单的票据
   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,                                  // 由止盈平仓标志
   ORDER_PROP_GROUP_ID,                                     // 订单/仓位分组 ID
   ORDER_PROP_DIRECTION,                                    // 基于方向的类型(买入,卖出)
  }; 
#define ORDER_PROP_INTEGER_TOTAL    (24)                    // 整数型属性的总数
#define ORDER_PROP_INTEGER_SKIP     (1                    // 排序中未使用的订单属性数量
//+------------------------------------------------------------------+
//| 订单, 成交, 仓位的实数型属性                                           |
//+------------------------------------------------------------------+
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 挂单激活时的 Limit 订单价
  };
#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)                     // 字符串型属性的总数
//+------------------------------------------------------------------+
//| 订单和成交排序的可能标准                                              |
//+------------------------------------------------------------------+
#define FIRST_ORD_DBL_PROP          (ORDER_PROP_INTEGER_TOTAL-ORDER_PROP_INTEGER_SKIP)
#define FIRST_ORD_STR_PROP          (ORDER_PROP_INTEGER_TOTAL+ORDER_PROP_DOUBLE_TOTAL-ORDER_PROP_INTEGER_SKIP)
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          =  9,                      // 按成交/订单/仓位原因/来源排序
   SORT_BY_ORDER_STATE           =  10,                     // 按订单状态排序
   SORT_BY_ORDER_POSITION_ID     =  11,                     // 按仓位 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_GROUP_ID        =  22,                     // 按订单/仓位组 ID 排序
   //--- 按实数型属性排序
   SORT_BY_ORDER_PRICE_OPEN      =  FIRST_ORD_DBL_PROP,     // 按开单价排序
   SORT_BY_ORDER_PRICE_CLOSE     =  FIRST_ORD_DBL_PROP+1,   // 按平单价排序
   SORT_BY_ORDER_SL              =  FIRST_ORD_DBL_PROP+2,   // 按止损价排序
   SORT_BY_ORDER_TP              =  FIRST_ORD_DBL_PROP+3,   // 按止盈价排序
   SORT_BY_ORDER_PROFIT          =  FIRST_ORD_DBL_PROP+4,   // 按盈利排序
   SORT_BY_ORDER_COMMISSION      =  FIRST_ORD_DBL_PROP+5,   // 按佣金排序
   SORT_BY_ORDER_SWAP            =  FIRST_ORD_DBL_PROP+6,   // 按隔夜利息排序
   SORT_BY_ORDER_VOLUME          =  FIRST_ORD_DBL_PROP+7,   // 按交易量排序
   SORT_BY_ORDER_VOLUME_CURRENT  =  FIRST_ORD_DBL_PROP+8,   // 按未执行交易量排序
   SORT_BY_ORDER_PROFIT_FULL     =  FIRST_ORD_DBL_PROP+9,   // 按盈利+佣金+隔夜利息条件排序
   SORT_BY_ORDER_PRICE_STOP_LIMIT=  FIRST_ORD_DBL_PROP+10,  // 按激活StopLimit 挂单时的 Limit 挂单价格排序
   //--- 按字符串型属性排序
   SORT_BY_ORDER_SYMBOL          =  FIRST_ORD_STR_PROP,     // 按品种排序
   SORT_BY_ORDER_COMMENT         =  FIRST_ORD_STR_PROP+1,   // 按注释排序
   SORT_BY_ORDER_EXT_ID          =  FIRST_ORD_STR_PROP+2    // 按外部交易系统中的订单 ID 排序
  };
//+------------------------------------------------------------------+
//| 用于处理帐户事件的数据                                               |
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
//| 账户交易事件标志列表                                                  |
//+------------------------------------------------------------------+
enum ENUM_TRADE_EVENT_FLAGS
  {
   TRADE_EVENT_FLAG_NO_EVENT        =  0,                   // 无事件
   TRADE_EVENT_FLAG_ORDER_PLASED    =  1,                   // 下挂单
   TRADE_EVENT_FLAG_ORDER_REMOVED   =  2,                   // 删除挂单
   TRADE_EVENT_FLAG_ORDER_ACTIVATED =  4,                   // 挂单由价格激活
   TRADE_EVENT_FLAG_POSITION_OPENED =  8,                   // 开仓
   TRADE_EVENT_FLAG_POSITION_CHANGED=  16,                  // 持仓变更
   TRADE_EVENT_FLAG_POSITION_REVERSE=  32,                  // 持仓反转
   TRADE_EVENT_FLAG_POSITION_CLOSED =  64,                  // 平仓
   TRADE_EVENT_FLAG_ACCOUNT_BALANCE =  128,                 // 余额操作 (按成交类型厘清)
   TRADE_EVENT_FLAG_PARTIAL         =  256,                 // 部分执行
   TRADE_EVENT_FLAG_BY_POS          =  512,                 // 由逆向仓位执行
   TRADE_EVENT_FLAG_SL              =  1024,                // 由止损执行
   TRADE_EVENT_FLAG_TP              =  2048                 // 由止盈执行
  };
//+------------------------------------------------------------------+
//| 帐户上可能的交易事件列表                                               |
//+------------------------------------------------------------------+
enum ENUM_TRADE_EVENT
  {
   TRADE_EVENT_NO_EVENT = 0,                                // 无交易事件
   TRADE_EVENT_PENDING_ORDER_PLASED,                        // 下挂单
   TRADE_EVENT_PENDING_ORDER_REMOVED,                       // 挂单删除
//--- 与 ENUM_DEAL_TYPE 枚举成员匹配的枚举成员
//--- (下面的常数顺序不应该改变,不应该添加/删除常量)
   TRADE_EVENT_ACCOUNT_CREDIT = DEAL_TYPE_CREDIT,           // 收取信贷 (3)
   TRADE_EVENT_ACCOUNT_CHARGE,                              // 额外费用
   TRADE_EVENT_ACCOUNT_CORRECTION,                          // 账目调整
   TRADE_EVENT_ACCOUNT_BONUS,                               // 收取奖金
   TRADE_EVENT_ACCOUNT_COMISSION,                           // 额外佣金
   TRADE_EVENT_ACCOUNT_COMISSION_DAILY,                     // 当日结束时收取的佣金
   TRADE_EVENT_ACCOUNT_COMISSION_MONTHLY,                   // 当月结束时收取的佣金
   TRADE_EVENT_ACCOUNT_COMISSION_AGENT_DAILY,               // 交易日结束时收取的代理佣金
   TRADE_EVENT_ACCOUNT_COMISSION_AGENT_MONTHLY,             // 交易月结束时收取的代理佣金
   TRADE_EVENT_ACCOUNT_INTEREST,                            // 可用资金的应计利息
   TRADE_EVENT_BUY_CANCELLED,                               // 取消买入成交
   TRADE_EVENT_SELL_CANCELLED,                              // 取消卖出成交
   TRADE_EVENT_DIVIDENT,                                    // 应计股息
   TRADE_EVENT_DIVIDENT_FRANKED,                            // 应计派发股息
   TRADE_EVENT_TAX                        = DEAL_TAX,       // 应计税款
//--- 与 ENUM_DEAL_TYPE 枚举中的 DEAL_TYPE_BALANCE 成交类型相关的常量
   TRADE_EVENT_ACCOUNT_BALANCE_REFILL     = DEAL_TAX+1,     // 账户余额充值
   TRADE_EVENT_ACCOUNT_BALANCE_WITHDRAWAL = DEAL_TAX+2,     // 从账户中提取资金
//--- 其余可能的交易事件
//--- (可以更改下面的常量顺序,可以添加/删除常量)
   TRADE_EVENT_PENDING_ORDER_ACTIVATED    = DEAL_TAX+3,     // 挂单由价格激活
   TRADE_EVENT_PENDING_ORDER_ACTIVATED_PARTIAL,             // 挂单由价格部分激活
   TRADE_EVENT_POSITION_OPENED,                             // 开仓
   TRADE_EVENT_POSITION_OPENED_PARTIAL,                     // 部分开仓
   TRADE_EVENT_POSITION_CLOSED,                             // 平仓
   TRADE_EVENT_POSITION_CLOSED_BY_POS,                      // 由逆向仓位平仓
   TRADE_EVENT_POSITION_CLOSED_BY_SL,                       // 由止损平仓
   TRADE_EVENT_POSITION_CLOSED_BY_TP,                       // 由止盈平仓
   TRADE_EVENT_POSITION_REVERSED_BY_MARKET,                 // 由新成交逆转持仓(净持结算)
   TRADE_EVENT_POSITION_REVERSED_BY_PENDING,                // 由激活挂单(净持结算)来平仓
   TRADE_EVENT_POSITION_REVERSED_BY_MARKET_PARTIAL,         // 由部分入场订单执行(净持结算)的持仓逆转
   TRADE_EVENT_POSITION_REVERSED_BY_PENDING_PARTIAL,        // 由部分挂单激活(净持结算)进行的持仓逆转
   TRADE_EVENT_POSITION_VOLUME_ADD_BY_MARKET,               // 由新成交加仓(净持结算)
   TRADE_EVENT_POSITION_VOLUME_ADD_BY_MARKET_PARTIAL,       // 由部分执行入场订单(净持结算)向某笔持仓增加交易量
   TRADE_EVENT_POSITION_VOLUME_ADD_BY_PENDING,              // 由激活挂单(净持结算)为持仓增加交易量
   TRADE_EVENT_POSITION_VOLUME_ADD_BY_PENDING_PARTIAL,      // 由部分激活挂单(净持结算)为某笔持仓增加交易量
   TRADE_EVENT_POSITION_CLOSED_PARTIAL,                     // 部分平仓
   TRADE_EVENT_POSITION_CLOSED_PARTIAL_BY_POS,              // 持仓由逆向仓位部分平仓
   TRADE_EVENT_POSITION_CLOSED_PARTIAL_BY_SL,               // 持仓由止损部分平仓
   TRADE_EVENT_POSITION_CLOSED_PARTIAL_BY_TP                // 持仓由止盈部分平仓
  };
//+------------------------------------------------------------------+
//| 事件状况                                                            |
//+------------------------------------------------------------------+
enum ENUM_EVENT_STATUS
  {
   EVENT_STATUS_MARKET_POSITION,                            // 持仓事件(开仓,部分开仓,部分平仓,增加交易量,逆转
   EVENT_STATUS_MARKET_PENDING,                             // 在场挂单事件(放置)
   EVENT_STATUS_HISTORY_PENDING,                            // 历史挂单事件(删除)
   EVENT_STATUS_HISTORY_POSITION,                           // 历史仓位事件(平仓)
   EVENT_STATUS_BALANCE,                                    // 余额操作事件(累计余额,提取资金以及来自 ENUM_DEAL_TYPE 枚举的事件)
  };
//+------------------------------------------------------------------+
//| 事件原因                                                            |
//+------------------------------------------------------------------+
enum ENUM_EVENT_REASON
  {
   EVENT_REASON_REVERSE,                                    // 持仓逆转(净持结算)
   EVENT_REASON_REVERSE_PARTIALLY,                          // 执行部分请求造成的持仓逆转(净持结算)
   EVENT_REASON_REVERSE_BY_PENDING,                         // 挂单激活造成的持仓逆转(净持结算)
   EVENT_REASON_REVERSE_BY_PENDING_PARTIALLY,               // 挂单部分执行造成的持仓逆转(净持结算)
   //--- 与持仓逆转相关的所有常数应位于上面的列表中
   EVENT_REASON_ACTIVATED_PENDING,                          // 挂单激活
   EVENT_REASON_ACTIVATED_PENDING_PARTIALLY,                // 挂单部分激活
   EVENT_REASON_CANCEL,                                     // 取消
   EVENT_REASON_EXPIRED,                                    // 订单到期
   EVENT_REASON_DONE,                                       // 请求已完整执行
   EVENT_REASON_DONE_PARTIALLY,                             // 请求部分执行
   EVENT_REASON_VOLUME_ADD,                                 // 持仓增加交易量(净持)
   EVENT_REASON_VOLUME_ADD_PARTIALLY,                       // 持仓由部分请求执行增加交易量(净持结算)
   EVENT_REASON_VOLUME_ADD_BY_PENDING,                      // 激活挂单时持仓增加交易量(净持结算)
   EVENT_REASON_VOLUME_ADD_BY_PENDING_PARTIALLY,            // 部分执行挂单时持仓增加交易量(净持结算)
   EVENT_REASON_DONE_SL,                                    // 由止损平仓
   EVENT_REASON_DONE_SL_PARTIALLY,                          // 由止损部分平仓
   EVENT_REASON_DONE_TP,                                    // 由止盈平仓
   EVENT_REASON_DONE_TP_PARTIALLY,                          // 由止盈部分平仓
   EVENT_REASON_DONE_BY_POS,                                // 由逆向仓位平仓
   EVENT_REASON_DONE_PARTIALLY_BY_POS,                      // 由逆向仓位部分平仓
   EVENT_REASON_DONE_BY_POS_PARTIALLY,                      // 由部分交易量将逆向持仓平仓
   EVENT_REASON_DONE_PARTIALLY_BY_POS_PARTIALLY,            // 由部分交易量将逆向持仓部分平仓
   //--- 与 ENUM_DEAL_TYPE 枚举中的 DEAL_TYPE_BALANCE 交易类型相关的常量
   EVENT_REASON_BALANCE_REFILL,                             // 账户充值
   EVENT_REASON_BALANCE_WITHDRAWAL,                         // 从账户中提取资金
   //--- 常量列表与 ENUM_TRADE_EVENT 枚举中的 TRADE_EVENT_ACCOUNT_CREDIT 相关,并相对于 ENUM_DEAL_TYPE(EVENT_REASON_ACCOUNT_CREDIT-3)偏移 +13
   EVENT_REASON_ACCOUNT_CREDIT,                             // 信贷充值
   EVENT_REASON_ACCOUNT_CHARGE,                             // 额外费用
   EVENT_REASON_ACCOUNT_CORRECTION,                         // 账目调整
   EVENT_REASON_ACCOUNT_BONUS,                              // 收取奖金
   EVENT_REASON_ACCOUNT_COMISSION,                          // 额外佣金
   EVENT_REASON_ACCOUNT_COMISSION_DAILY,                    // 交易日结束时收取的佣金
   EVENT_REASON_ACCOUNT_COMISSION_MONTHLY,                  // 交易日结束时收取的佣金
   EVENT_REASON_ACCOUNT_COMISSION_AGENT_DAILY,              // 交易日结束时收取的代理佣金
   EVENT_REASON_ACCOUNT_COMISSION_AGENT_MONTHLY,            // 交易月结束时收取的代理佣金
   EVENT_REASON_ACCOUNT_INTEREST,                           // 可用资金的应计利息
   EVENT_REASON_BUY_CANCELLED,                              // 取消买入成交
   EVENT_REASON_SELL_CANCELLED,                             // 取消卖出成交
   EVENT_REASON_DIVIDENT,                                   // 累积红利
   EVENT_REASON_DIVIDENT_FRANKED,                           // 累积分红
   EVENT_REASON_TAX                                         // 税款
  };
#define REASON_EVENT_SHIFT    (EVENT_REASON_ACCOUNT_CREDIT-3)
//+------------------------------------------------------------------+
//| 事件的整数型属性                                                    |
//+------------------------------------------------------------------+
enum ENUM_EVENT_PROP_INTEGER
  {
   EVENT_PROP_TYPE_EVENT = 0,                               // 帐户交易事件类型(来自 ENUM_TRADE_EVENT 枚举)
   EVENT_PROP_TIME_EVENT,                                   // 事件时间(以毫秒为单位)
   EVENT_PROP_STATUS_EVENT,                                 // 事件状况(来自 ENUM_EVENT_STATUS 枚举)
   EVENT_PROP_REASON_EVENT,                                 // 事件原因(来自 ENUM_EVENT_REASON 枚举)
   //---
   EVENT_PROP_TYPE_DEAL_EVENT,                              // 成交事件类型
   EVENT_PROP_TICKET_DEAL_EVENT,                            // 成交事件票据
   EVENT_PROP_TYPE_ORDER_EVENT,                             // 成交事件所基于的订单类型(最后的开仓订单)
   EVENT_PROP_TICKET_ORDER_EVENT,                           // 成交事件所基于的订单票据(最后的开仓订单)
   //---
   EVENT_PROP_TIME_ORDER_POSITION,                          // 首笔成交所基于的订单时间(对冲账户的首笔开仓订单)
   EVENT_PROP_TYPE_ORDER_POSITION,                          // 首笔成交所基于的订单类型(对冲账户的首笔开仓订单)
   EVENT_PROP_TICKET_ORDER_POSITION,                        // 首笔成交所基于的订单票据(对冲账户的首笔开仓订单)
   EVENT_PROP_POSITION_ID,                                  // 仓位 ID
   //---
   EVENT_PROP_POSITION_BY_ID,                               // 逆向仓位 ID
   EVENT_PROP_MAGIC_ORDER,                                  // 订单/成交/仓位魔幻数字
   EVENT_PROP_MAGIC_BY_ID,                                  // 逆向仓位的魔幻数字
   //---
   EVENT_PROP_TYPE_ORD_POS_BEFORE,                          // 方向改变前的持仓类型
   EVENT_PROP_TICKET_ORD_POS_BEFORE,                        // 方向改变前的开仓订单票据
   EVENT_PROP_TYPE_ORD_POS_CURRENT,                         // 当前持仓类型
   EVENT_PROP_TICKET_ORD_POS_CURRENT                        // 当前持仓票据
  }; 
#define EVENT_PROP_INTEGER_TOTAL (19)                       // 整数型事件属性的总数
#define EVENT_PROP_INTEGER_SKIP  (4                       // 按事件属性中排序时未使用的事件属性数量
//+------------------------------------------------------------------+
//| 事件的实数型属性                                                    |
//+------------------------------------------------------------------+
enum ENUM_EVENT_PROP_DOUBLE
  {
   EVENT_PROP_PRICE_EVENT = EVENT_PROP_INTEGER_TOTAL,       // 价格发生在
   EVENT_PROP_PRICE_OPEN,                                   // 订单/成交/仓位开盘价
   EVENT_PROP_PRICE_CLOSE,                                  // 订单/成交/仓位收盘价
   EVENT_PROP_PRICE_SL,                                     // 订单/成交/仓位的止损价格
   EVENT_PROP_PRICE_TP,                                     // 订单/成交/仓位的止盈价格
   EVENT_PROP_VOLUME_ORDER_INITIAL,                         // 请求的订单交易量
   EVENT_PROP_VOLUME_ORDER_EXECUTED,                        // 已执行的订单交易量
   EVENT_PROP_VOLUME_ORDER_CURRENT,                         // 剩余的订单交易量
   EVENT_PROP_VOLUME_POSITION_EXECUTED,                     // 成交后当前执行的持仓量
   EVENT_PROP_PROFIT                                        // 盈利
  };
#define EVENT_PROP_DOUBLE_TOTAL  (10)                       // 事件的实数型属性总数
//+------------------------------------------------------------------+
//| 事件的字符串型属性                                                   |
//+------------------------------------------------------------------+
enum ENUM_EVENT_PROP_STRING
  {
   EVENT_PROP_SYMBOL = (EVENT_PROP_INTEGER_TOTAL+EVENT_PROP_DOUBLE_TOTAL), // 订单品种
   EVENT_PROP_SYMBOL_BY_ID                                  // 逆向仓位品种
  };
#define EVENT_PROP_STRING_TOTAL     (2)                     // 事件的字符串型属性的总数
//+------------------------------------------------------------------+
//| 可能的事件排序标准                                                   |
//+------------------------------------------------------------------+
#define FIRST_EVN_DBL_PROP       (EVENT_PROP_INTEGER_TOTAL-EVENT_PROP_INTEGER_SKIP)
#define FIRST_EVN_STR_PROP       (EVENT_PROP_INTEGER_TOTAL+EVENT_PROP_DOUBLE_TOTAL-EVENT_PROP_INTEGER_SKIP)
enum ENUM_SORT_EVENTS_MODE
  {
   //--- 按整数型属性排序
   SORT_BY_EVENT_TYPE_EVENT               = 0,                       // 按事件类型排序
   SORT_BY_EVENT_TIME_EVENT               = 1,                       // 按事件时间排序
   SORT_BY_EVENT_STATUS_EVENT             = 2,                       // 按事件状况排序 (来自 ENUM_EVENT_STATUS 枚举)
   SORT_BY_EVENT_REASON_EVENT             = 3,                       // 按事件原因排序 (来自 ENUM_EVENT_REASON 枚举)
   SORT_BY_EVENT_TYPE_DEAL_EVENT          = 4,                       // 按成交事件类型排序
   SORT_BY_EVENT_TICKET_DEAL_EVENT        = 5,                       // 按成交事件票据排序
   SORT_BY_EVENT_TYPE_ORDER_EVENT         = 6,                       // 按成交事件所基于的订单类型排序(最后的开仓订单)
   SORT_BY_EVENT_TICKET_ORDER_EVENT       = 7,                       // 按成交事件所基于的订单票据排序(最后的开仓订单)
   SORT_BY_EVENT_TIME_ORDER_POSITION      = 8,                       // 按成交事件所基于的订单时间排序(最后的开仓订单)
   SORT_BY_EVENT_TYPE_ORDER_POSITION      = 9,                       // 按成交事件所基于的订单类型排序(首笔开仓订单)
   SORT_BY_EVENT_TICKET_ORDER_POSITION    = 10,                      // 按成交事件所基于的订单票据排序(首笔开仓订单)
   SORT_BY_EVENT_POSITION_ID              = 11,                      // 按仓位 ID 排序
   SORT_BY_EVENT_POSITION_BY_ID           = 12,                      // 按逆向仓位 ID 排序
   SORT_BY_EVENT_MAGIC_ORDER              = 13,                      // 按订单/成交/仓位魔幻数字
   SORT_BY_EVENT_MAGIC_BY_ID              = 14,                      // 按逆向仓位魔幻数字排序
   //--- 按实数型属性排序
   SORT_BY_EVENT_PRICE_EVENT              =  FIRST_EVN_DBL_PROP,     // 按事件发生时的价格排序
   SORT_BY_EVENT_PRICE_OPEN               =  FIRST_EVN_DBL_PROP+1,   // 按开仓价排序
   SORT_BY_EVENT_PRICE_CLOSE              =  FIRST_EVN_DBL_PROP+2,   // 按平仓价排序
   SORT_BY_EVENT_PRICE_SL                 =  FIRST_EVN_DBL_PROP+3,   // 按平仓止损价排序
   SORT_BY_EVENT_PRICE_TP                 =  FIRST_EVN_DBL_PROP+4,   // 按平仓止盈价排序
   SORT_BY_EVENT_VOLUME_ORDER_INITIAL     =  FIRST_EVN_DBL_PROP+5,   // 按初始交易量排序
   SORT_BY_EVENT_VOLUME_ORDER_EXECUTED    =  FIRST_EVN_DBL_PROP+6,   // 按当前交易量排序
   SORT_BY_EVENT_VOLUME_ORDER_CURRENT     =  FIRST_EVN_DBL_PROP+7,   // 按剩余交易量排序
   SORT_BY_EVENT_VOLUME_POSITION_EXECUTED =  FIRST_EVN_DBL_PROP+8,   // 按剩余交易量排序
   SORT_BY_EVENT_PROFIT                   =  FIRST_EVN_DBL_PROP+9,   // 按盈利排序
   //--- 按字符串型属性排序
   SORT_BY_EVENT_SYMBOL                   =  FIRST_EVN_STR_PROP,     // 按订单/仓位/成交的品种
   SORT_BY_EVENT_SYMBOL_BY_ID                                        // 按逆向仓位的品种排序
  };
//+------------------------------------------------------------------+

由于我们已将订单组 ID 添加到文章主题中,因此理应修改抽象订单对象。 我们添加 返回分配给订单的组 ID 的方法,和设置组 ID 数值的方法

//+------------------------------------------------------------------+
//| 简化访问订单对象属性的方法                                            |
//+------------------------------------------------------------------+
   //--- 返回 (1) 单据, (2) 父单据, (3) 派生单据, (4) 魔幻数字, (5) 订单原因,
   //--- (6) 仓位 ID, (7) 逆向仓位 ID, (8) 分组 ID, (9) 类型, (10) 由止损平仓的标志,
   //--- (11) 由止盈平仓的标志 (12) 开盘时间, (13) 收盘时间, (14) 开盘时间毫秒值,
   //--- (15) 收盘时间毫秒值, (16) 到期日, (17) 状态, (18) 状况, (19) 订单方向类型
   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);             }
   long              GroupID(void)                                      const { return this.GetProperty(ORDER_PROP_GROUP_ID);                   }
   long              TypeOrder(void)                                    const { return this.GetProperty(ORDER_PROP_TYPE);                       }
   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_STATE  State(void)                                        const { return (ENUM_ORDER_STATE)this.GetProperty(ORDER_PROP_STATE);    }
   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;
//--- 设置分组 ID
   void              SetGroupID(long group_id)                                { this.SetProperty(ORDER_PROP_GROUP_ID,group_id);                 }
   

默认情况下,分组 ID 将设置为零。 为达此目的,在 COrder 类的闭合构造函数中将 order 属性值设置为零

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

组 ID 的描述添加到返回属性描述的方法中:

//+------------------------------------------------------------------+
//| 返回订单的整数型属性的描述                                            |
//+------------------------------------------------------------------+
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("Тикет родительского ордера","Parent order ticket")+
         (!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("Время открытия","Time open")+
         (!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_TICKET ?  TextByLanguage("Сделка на основании ордера с тикетом","Deal by order ticket")+
         (!this.SupportProperty(property)    ?  TextByLanguage(": Свойство не поддерживается",": Property not supported") :
          ": #"+(string)this.GetProperty(property)
         )  :
      property==ORDER_PROP_DEAL_ENTRY        ?  TextByLanguage("Направление сделки","Deal entry")+
         (!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") :
          ": "+TimeMSCtoString(this.GetProperty(property))+" ("+(string)this.GetProperty(property)+")"
         )  :
      property==ORDER_PROP_TIME_CLOSE_MSC    ?  TextByLanguage("Время закрытия в милисекундах","Close time in milliseconds")+
         (!this.SupportProperty(property)    ?  TextByLanguage(": Свойство не поддерживается",": Property not supported") :
          ": "+TimeMSCtoString(this.GetProperty(property))+" ("+(string)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("Время изменения позиции в милисекундах","Time to change the position in milliseconds")+
         (!this.SupportProperty(property)    ?  TextByLanguage(": Свойство не поддерживается",": Property not supported") :
          ": "+(this.GetProperty(property)!=0 ? TimeMSCtoString(this.GetProperty(property))+" ("+(string)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"))
         )  :
      property==ORDER_PROP_GROUP_ID          ?  TextByLanguage("Идентификатор группы","Group ID")+
         (!this.SupportProperty(property)    ?  TextByLanguage(": Свойство не поддерживается",": Property not supported") :
          ": "+(string)this.GetProperty(property)
         )  :
      ""
     );
  }
//+------------------------------------------------------------------+

完成这些修改后,您可以将分组 ID 分配给任何订单/仓位,从而将订单和仓位分派到特定分组中,以便仅处理特定分组。 默认情况下,0 分配给所有新的开仓以及设定订单。 不过,您可以使用 SetGroupID(group_index) 方法将另一个分组分配给任何订单/仓位。 此外,您可以使用 GroupID() 方法找出任何订单的分组。

我们回到在净持结算帐户上实现事件跟踪。
为了按帐户类型划分功能,我们将在 Event.mqh 文件中的 CEvent 抽象事件类的私有部分添加一个类成员变量:

protected:
   ENUM_TRADE_EVENT  m_trade_event;                                  // 交易事件
   bool              m_is_hedge;                                     // 对冲账户标志
   long              m_chart_id;                                     // 控制程序所在的图表 ID
   int               m_digits_acc;                                   // 帐户货币的小数位数
   long              m_long_prop[EVENT_PROP_INTEGER_TOTAL];          // 事件整数型属性
   double            m_double_prop[EVENT_PROP_DOUBLE_TOTAL];         // 事件实数型属性
   string            m_string_prop[EVENT_PROP_STRING_TOTAL];         // 事件字符串型属性
//--- 返回在交易事件中存在的旗帜
   bool              IsPresentEventFlag(const int event_code)  const { return (this.m_event_code & event_code)==event_code;            }

添加返回方法的声明
(对冲账户)魔幻数字和逆向仓位的品种
(考虑净持结算账户的持仓逆转) 先前持仓的订单类型和票据当前持仓的订单更改方向前的持仓类型和票据改变方向后的持仓类型和票据到方法列表,简化了对“公有部分”的访问:

//+------------------------------------------------------------------+
//| 简化访问事件对象属性的方法                                              |
//+------------------------------------------------------------------+
//--- 返回 (1) 事件类型, (2) 事件时间毫秒值, (3) 时间状况, (4) 事件原因, (5) 成交类型, (6) 成交票据, 
//---(7) 成交执行时所基于的订单类型, (8) 开仓时的订单类型, (9) 最后一笔开仓单据, 
//--- (10) 首笔开仓单据, (11) 仓位 ID, (12) 逆向仓位 ID, (13) 魔幻数字, (14) 逆向仓位魔幻数字, (15) 开仓时间
   ENUM_TRADE_EVENT  TypeEvent(void)                                    const { return (ENUM_TRADE_EVENT)this.GetProperty(EVENT_PROP_TYPE_EVENT);           }
   long              TimeEvent(void)                                    const { return this.GetProperty(EVENT_PROP_TIME_EVENT);                             }
   ENUM_EVENT_STATUS Status(void)                                       const { return (ENUM_EVENT_STATUS)this.GetProperty(EVENT_PROP_STATUS_EVENT);        }
   ENUM_EVENT_REASON Reason(void)                                       const { return (ENUM_EVENT_REASON)this.GetProperty(EVENT_PROP_REASON_EVENT);        }
   ENUM_DEAL_TYPE    TypeDeal(void)                                     const { return (ENUM_DEAL_TYPE)this.GetProperty(EVENT_PROP_TYPE_DEAL_EVENT);        }
   long              TicketDeal(void)                                   const { return this.GetProperty(EVENT_PROP_TICKET_DEAL_EVENT);                      }
   ENUM_ORDER_TYPE   TypeOrderEvent(void)                               const { return (ENUM_ORDER_TYPE)this.GetProperty(EVENT_PROP_TYPE_ORDER_EVENT);      }
   ENUM_ORDER_TYPE   TypeFirstOrderPosition(void)                       const { return (ENUM_ORDER_TYPE)this.GetProperty(EVENT_PROP_TYPE_ORDER_POSITION);   }
   long              TicketOrderEvent(void)                             const { return this.GetProperty(EVENT_PROP_TICKET_ORDER_EVENT);                     }
   long              TicketFirstOrderPosition(void)                     const { return this.GetProperty(EVENT_PROP_TICKET_ORDER_POSITION);                  }
   long              PositionID(void)                                   const { return this.GetProperty(EVENT_PROP_POSITION_ID);                            }
   long              PositionByID(void)                                 const { return this.GetProperty(EVENT_PROP_POSITION_BY_ID);                         }
   long              Magic(void)                                        const { return this.GetProperty(EVENT_PROP_MAGIC_ORDER);                            }
   long              MagicCloseBy(void)                                 const { return this.GetProperty(EVENT_PROP_MAGIC_BY_ID);                            }
   long              TimePosition(void)                                 const { return this.GetProperty(EVENT_PROP_TIME_ORDER_POSITION);                    }

//--- 当持仓方向改变时,返回 (1) 前持仓订单类型, (2) 前持仓单据,
//--- (3) 当前持仓订单类型, (4) 当前持仓单据,
//--- (5) 持仓类型,和 (6) 方向变化前的票据, (7) 仓位类型,和 (8) 方向变化后的票据
   ENUM_ORDER_TYPE   TypeOrderPosPrevious(void)                         const { return (ENUM_ORDER_TYPE)this.GetProperty(EVENT_PROP_TYPE_ORD_POS_BEFORE);   }
   long              TicketOrderPosPrevious(void)                       const { return (ENUM_ORDER_TYPE)this.GetProperty(EVENT_PROP_TICKET_ORD_POS_BEFORE); }
   ENUM_ORDER_TYPE   TypeOrderPosCurrent(void)                          const { return (ENUM_ORDER_TYPE)this.GetProperty(EVENT_PROP_TYPE_ORD_POS_CURRENT);  }
   long              TicketOrderPosCurrent(void                       const { return (ENUM_ORDER_TYPE)this.GetProperty(EVENT_PROP_TICKET_ORD_POS_CURRENT);}
   ENUM_POSITION_TYPE TypePositionPrevious(void                       const { return PositionTypeByOrderType(this.TypeOrderPosPrevious());                }
   ulong             TicketPositionPrevious(void)                       const { return this.TicketOrderPosPrevious();                                       }
   ENUM_POSITION_TYPE TypePositionCurrent(void)                         const { return PositionTypeByOrderType(this.TypeOrderPosCurrent());                 }
   ulong             TicketPositionCurrent(void                       const { return this.TicketOrderPosCurrent();                                        }
   
//--- 返回 (1) 事件发生时的价格所在, (2) 开盘价, (3) 收盘价,
//--- (4) 止损价, (5) 止盈价, (6) 盈利, (7) 请求的订单交易量, 
//--- (8) 执行的订单交易量, (9) 剩余的订单交易量, (10) 执行的仓量
   double            PriceEvent(void)                                   const { return this.GetProperty(EVENT_PROP_PRICE_EVENT);                            }
   double            PriceOpen(void)                                    const { return this.GetProperty(EVENT_PROP_PRICE_OPEN);                             }
   double            PriceClose(void)                                   const { return this.GetProperty(EVENT_PROP_PRICE_CLOSE);                            }
   double            PriceStopLoss(void)                                const { return this.GetProperty(EVENT_PROP_PRICE_SL);                               }
   double            PriceTakeProfit(void)                              const { return this.GetProperty(EVENT_PROP_PRICE_TP);                               }
   double            Profit(void)                                       const { return this.GetProperty(EVENT_PROP_PROFIT);                                 }
   double            VolumeOrderInitial(void)                           const { return this.GetProperty(EVENT_PROP_VOLUME_ORDER_INITIAL);                   }
   double            VolumeOrderExecuted(void)                          const { return this.GetProperty(EVENT_PROP_VOLUME_ORDER_EXECUTED);                  }
   double            VolumeOrderCurrent(void)                           const { return this.GetProperty(EVENT_PROP_VOLUME_ORDER_CURRENT);                   }
   double            VolumePositionExecuted(void)                       const { return this.GetProperty(EVENT_PROP_VOLUME_POSITION_EXECUTED);               }
//--- 返回 (1) 品种,和 (2) 逆向仓位品种
   string            Symbol(void)                                       const { return this.GetProperty(EVENT_PROP_SYMBOL);                                 }
   string            SymbolCloseBy(void)                                const { return this.GetProperty(EVENT_PROP_SYMBOL_BY_ID);                           }
   
//+------------------------------------------------------------------+

方法很简单:返回订单的相应事件属性,返回开仓或持仓变化时的单据,返回仓位类型(按所触发的订单类型),类型名称使用来自 DELib.mqh 服务函数文件中之前描述的 PositionTypeByOrderType() 函数。

在类构造函数中设置在帐户类型上保存数据

//+------------------------------------------------------------------+
//| 构造函数                                                           |
//+------------------------------------------------------------------+
CEvent::CEvent(const ENUM_EVENT_STATUS event_status,const int event_code,const ulong ticket) : m_event_code(event_code)
  {
   this.m_long_prop[EVENT_PROP_STATUS_EVENT]       =  event_status;
   this.m_long_prop[EVENT_PROP_TICKET_ORDER_EVENT] =  (long)ticket;
   this.m_is_hedge=bool(::AccountInfoInteger(ACCOUNT_MARGIN_MODE)==ACCOUNT_MARGIN_MODE_RETAIL_HEDGING);
   this.m_digits_acc=(int)::AccountInfoInteger(ACCOUNT_CURRENCY_DIGITS);
   this.m_chart_id=::ChartID();
  }
//+------------------------------------------------------------------+

添加返回发生成交事件时的订单名称首笔开仓订单导致当前持仓开仓(净持结算,对冲)或变化(净持结算)的订单当前持仓类型的名称导致开仓的订单类型以前持仓的类型名称,等方法的定义至事件属性描述方法:

//+------------------------------------------------------------------+
//| 订单对象属性的描述                                                   |
//+------------------------------------------------------------------+
//--- 获取订单的(1)整数型,(2)实数型,和(3)字符串型属性的描述
   string            GetPropertyDescription(ENUM_EVENT_PROP_INTEGER property);
   string            GetPropertyDescription(ENUM_EVENT_PROP_DOUBLE property);
   string            GetPropertyDescription(ENUM_EVENT_PROP_STRING property);
//--- 返回事件的 (1) 状况,和 (2) 类型
   string            StatusDescription(void)                const;
   string            TypeEventDescription(void)             const;
//--- 返回 (1) 事件成交订单, (2) 仓位的父订单, (3) 当前仓位订单,和 (4) 当前仓位的名字
//--- 返回 (5) 订单,和 (6) 方向变化前仓位的名字
   string            TypeOrderDealDescription(void)         const;
   string            TypeOrderFirstDescription(void)        const;
   string            TypeOrderEventDescription(void)        const;
   string            TypePositionCurrentDescription(void)   const;
   string            TypeOrderPreviousDescription(void)     const;
   string            TypePositionPreviousDescription(void)  const;
//--- 返回成交/订单/仓位原因的名字
   string            ReasonDescription(void)                const;

并在类的实体之外 实现它们:

//+------------------------------------------------------------------+
//| 返回订单/仓位/成交的名称                                             |
//+------------------------------------------------------------------+
string CEvent::TypeOrderDealDescription(void) const
  {
   ENUM_EVENT_STATUS status=this.Status();
   return
     (
      status==EVENT_STATUS_MARKET_PENDING  || status==EVENT_STATUS_HISTORY_PENDING  ?  OrderTypeDescription((ENUM_ORDER_TYPE)this.GetProperty(EVENT_PROP_TYPE_ORDER_EVENT))      :
      status==EVENT_STATUS_MARKET_POSITION || status==EVENT_STATUS_HISTORY_POSITION ?  PositionTypeDescription((ENUM_POSITION_TYPE)this.GetProperty(EVENT_PROP_TYPE_DEAL_EVENT)) :
      status==EVENT_STATUS_BALANCE  ?  DealTypeDescription((ENUM_DEAL_TYPE)this.GetProperty(EVENT_PROP_TYPE_DEAL_EVENT))  :  
      TextByLanguage("Неизвестный тип ордера","Unknown order type")
     );
  }
//+------------------------------------------------------------------+
//| 返回首笔开仓订单名                                                   |
//+------------------------------------------------------------------+
string CEvent::TypeOrderFirstDescription(void) const
  {
   return OrderTypeDescription((ENUM_ORDER_TYPE)this.GetProperty(EVENT_PROP_TYPE_ORDER_POSITION));
  }
//+------------------------------------------------------------------+
//| 返回仓位更改的订单名称                                               |
//+------------------------------------------------------------------+
string CEvent::TypeOrderEventDescription(void) const
  {
   return OrderTypeDescription(this.TypeOrderEvent());
  }
//+------------------------------------------------------------------+
//| 返回当前仓位的名称                                                   |
//+------------------------------------------------------------------+
string CEvent::TypePositionCurrentDescription(void) const
  {
   return PositionTypeDescription(this.TypePositionCurrent());
  }
//+------------------------------------------------------------------+
//| 返回方向更改前订单的名称                                               |
//+------------------------------------------------------------------+
string CEvent::TypeOrderPreviousDescription(void) const
  {
   return OrderTypeDescription(this.TypeOrderPosPrevious());
  }
//+------------------------------------------------------------------+
//| 返回方向更改之前仓位的名称                                            |
//+------------------------------------------------------------------+
string CEvent::TypePositionPreviousDescription(void) const
  {
   return PositionTypeDescription(this.TypePositionPrevious());
  }
//+------------------------------------------------------------------+

这些方法很简单,与返回订单和仓位类型的方法类似。 仅有的区别在于 DELIB.mqh 文件中函数返回订单类型和仓位类型描述:PositionTypeDescription()OrderTypeDescription()

现在,ReasonDescription() 方法理应改善,返回与净持结算帐户的事件原因相关的新添加的枚举描述:

//+------------------------------------------------------------------+
//| 返回成交/订单/仓位原因的名称                                          |
//+------------------------------------------------------------------+
string CEvent::ReasonDescription(void) const
  {
   ENUM_EVENT_REASON reason=this.Reason();
   return 
     (
      reason==EVENT_REASON_ACTIVATED_PENDING                ?  TextByLanguage("Активирован отложенный ордер","Pending order activated")                           :
      reason==EVENT_REASON_ACTIVATED_PENDING_PARTIALLY      ?  TextByLanguage("Частичное срабатывание отложенного ордера","Pending order partially triggered")    :
      reason==EVENT_REASON_CANCEL                           ?  TextByLanguage("Отмена","Canceled")                                                                :
      reason==EVENT_REASON_EXPIRED                          ?  TextByLanguage("Истёк срок действия","Expired")                                                    :
      reason==EVENT_REASON_DONE                             ?  TextByLanguage("Рыночный запрос, выполненный в полном объёме","Fully completed market request")    :
      reason==EVENT_REASON_DONE_PARTIALLY                   ?  TextByLanguage("Выполненный частично рыночный запрос","Partially completed market request")        :
      reason==EVENT_REASON_VOLUME_ADD                       ?  TextByLanguage("Добавлен объём к позиции","Added volume to position")                              :
      reason==EVENT_REASON_VOLUME_ADD_PARTIALLY             ?  TextByLanguage("Добавлен объём к позиции частичным исполнением заявки","Volume added to position by partially completed request")                 :
      reason==EVENT_REASON_VOLUME_ADD_BY_PENDING            ?  TextByLanguage("Добавлен объём к позиции активацией отложенного ордера","Added volume to position by triggered pending order")                      :
      reason==EVENT_REASON_VOLUME_ADD_BY_PENDING_PARTIALLY  ?  TextByLanguage("Добавлен объём к позиции частичной активацией отложенного ордера","Added volume to position by partially triggered pending order")  :
      reason==EVENT_REASON_REVERSE                          ?  TextByLanguage("Разворот позиции","Position reversal")  :
      reason==EVENT_REASON_REVERSE_PARTIALLY                ?  TextByLanguage("Разворот позиции частичным исполнением заявки","Position reversal by partially completing request")                             :
      reason==EVENT_REASON_REVERSE_BY_PENDING               ?  TextByLanguage("Разворот позиции при срабатывании отложенного ордера","Position reversal on a triggered pending order")                               :
      reason==EVENT_REASON_REVERSE_BY_PENDING_PARTIALLY     ?  TextByLanguage("Разворот позиции при при частичном срабатывании отложенного ордера","Position reversal on a partially triggered pending order")       :
      reason==EVENT_REASON_DONE_SL                          ?  TextByLanguage("Закрытие по StopLoss","Close by StopLoss triggered")                               :
      reason==EVENT_REASON_DONE_SL_PARTIALLY                ?  TextByLanguage("Частичное закрытие по StopLoss","Partial close by StopLoss triggered")           :
      reason==EVENT_REASON_DONE_TP                          ?  TextByLanguage("Закрытие по TakeProfit","Close by TakeProfit triggered")                           :
      reason==EVENT_REASON_DONE_TP_PARTIALLY                ?  TextByLanguage("Частичное закрытие по TakeProfit","Partial close by TakeProfit triggered")       :
      reason==EVENT_REASON_DONE_BY_POS                      ?  TextByLanguage("Закрытие встречной позицией","Closed by opposite position")                        :
      reason==EVENT_REASON_DONE_PARTIALLY_BY_POS            ?  TextByLanguage("Частичное закрытие встречной позицией","Closed partially by opposite position")    :
      reason==EVENT_REASON_DONE_BY_POS_PARTIALLY            ?  TextByLanguage("Закрытие частью объёма встречной позиции","Closed by incomplete volume of opposite position") :
      reason==EVENT_REASON_DONE_PARTIALLY_BY_POS_PARTIALLY  ?  TextByLanguage("Частичное закрытие частью объёма встречной позиции","Closed partially by incomplete volume of opposite position")  :
      reason==EVENT_REASON_BALANCE_REFILL                   ?  TextByLanguage("Пополнение баланса","Balance refill")                                              :
      reason==EVENT_REASON_BALANCE_WITHDRAWAL               ?  TextByLanguage("Снятие средств с баланса","Withdrawal from balance")                          :
      reason==EVENT_REASON_ACCOUNT_CREDIT                   ?  TextByLanguage("Начисление кредита","Credit")                                                      :
      reason==EVENT_REASON_ACCOUNT_CHARGE                   ?  TextByLanguage("Дополнительные сборы","Additional charge")                                         :
      reason==EVENT_REASON_ACCOUNT_CORRECTION               ?  TextByLanguage("Корректирующая запись","Correction")                                               :
      reason==EVENT_REASON_ACCOUNT_BONUS                    ?  TextByLanguage("Перечисление бонусов","Bonus")                                                     :
      reason==EVENT_REASON_ACCOUNT_COMISSION                ?  TextByLanguage("Дополнительные комиссии","Additional commission")                                  :
      reason==EVENT_REASON_ACCOUNT_COMISSION_DAILY          ?  TextByLanguage("Комиссия, начисляемая в конце торгового дня","Daily commission")                   :
      reason==EVENT_REASON_ACCOUNT_COMISSION_MONTHLY        ?  TextByLanguage("Комиссия, начисляемая в конце месяца","Monthly commission")                        :
      reason==EVENT_REASON_ACCOUNT_COMISSION_AGENT_DAILY    ?  TextByLanguage("Агентская комиссия, начисляемая в конце торгового дня","Daily agent commission")   :
      reason==EVENT_REASON_ACCOUNT_COMISSION_AGENT_MONTHLY  ?  TextByLanguage("Агентская комиссия, начисляемая в конце месяца","Monthly agent commission")        :
      reason==EVENT_REASON_ACCOUNT_INTEREST                 ?  TextByLanguage("Начисления процентов на свободные средства","Interest rate")                       :
      reason==EVENT_REASON_BUY_CANCELLED                    ?  TextByLanguage("Отмененная сделка покупки","Canceled buy deal")                                    :
      reason==EVENT_REASON_SELL_CANCELLED                   ?  TextByLanguage("Отмененная сделка продажи","Canceled sell deal")                                   :
      reason==EVENT_REASON_DIVIDENT                         ?  TextByLanguage("Начисление дивиденда","Dividend operations")                                       :
      reason==EVENT_REASON_DIVIDENT_FRANKED                 ?  TextByLanguage("Начисление франкированного дивиденда","Franked (non-taxable) dividend operations") :
      reason==EVENT_REASON_TAX                              ?  TextByLanguage("Начисление налога","Tax charges")                                                  :
      EnumToString(reason)
     );
  }
//+------------------------------------------------------------------+

在函数库论述的第五部分,我们已开发出解码交易事件代码的方法。 我们来回忆一下它的逻辑:

将事件代码传递给该方法,然后检查事件代码标志。 如果代码具有 checked 标志,则设置相应的交易事件。 由于事件代码可能有多个标志,因此会检查事件的所有可能标志,并根据它们的组合定义事件类型。 接着,将事件类型添加到相应的类变量中,并将其输入到事件对象的属性(EVENT_PROP_TYPE_EVENT)中。

现在我们只需要在交易事件代码中添加匹配可能的净持账户事件的跟踪新标记:

//+------------------------------------------------------------------+
//| 解码事件代码并设置交易事件                                            |
//+------------------------------------------------------------------+
void CEvent::SetTypeEvent(void)
  {
//--- 下挂单(检查事件代码是否匹配,因为此处只能有一个标志)
   if(this.m_event_code==TRADE_EVENT_FLAG_ORDER_PLASED)
     {
      this.m_trade_event=TRADE_EVENT_PENDING_ORDER_PLASED;
      this.SetProperty(EVENT_PROP_TYPE_EVENT,this.m_trade_event);
      return;
     }
//--- 删除挂单(检查事件代码是否匹配,因为此处只能有一个标志)
   if(this.m_event_code==TRADE_EVENT_FLAG_ORDER_REMOVED)
     {
      this.m_trade_event=TRADE_EVENT_PENDING_ORDER_REMOVED;
      this.SetProperty(EVENT_PROP_TYPE_EVENT,this.m_trade_event);
      return;
     }
//--- 开仓(检查事件代码中是否存在多个标志)
   if(this.IsPresentEventFlag(TRADE_EVENT_FLAG_POSITION_OPENED))
     {
   //--- 如果存在持仓变化
      if(this.IsPresentEventFlag(TRADE_EVENT_FLAG_POSITION_CHANGED))
        {
         //--- 如果挂单被价格激活
         if(this.IsPresentEventFlag(TRADE_EVENT_FLAG_ORDER_ACTIVATED))
           {
            //--- 如果这是一个持仓逆转
            if(this.IsPresentEventFlag(TRADE_EVENT_FLAG_POSITION_REVERSE))
              {
               //--- 检查部分平仓标志并设置 
               //--- “由激活挂单造成持仓逆转”或“由部分激活挂单造成持仓逆转”交易事件
               this.m_trade_event=
                 (
                  !this.IsPresentEventFlag(TRADE_EVENT_FLAG_PARTIAL) ? 
                  TRADE_EVENT_POSITION_REVERSED_BY_PENDING : 
                  TRADE_EVENT_POSITION_REVERSED_BY_PENDING_PARTIAL
                 );
               this.SetProperty(EVENT_PROP_TYPE_EVENT,this.m_trade_event);
               return;
              }
            //--- 如果这是加仓
            else
              {
               //--- 检查部分开仓标志并设置交易事件 
               //--- “某笔持仓由激活挂单增加交易量”或“持仓由部分激活挂单增加交易量”
               this.m_trade_event=
                 (
                  !this.IsPresentEventFlag(TRADE_EVENT_FLAG_PARTIAL) ? 
                  TRADE_EVENT_POSITION_VOLUME_ADD_BY_PENDING : 
                  TRADE_EVENT_POSITION_VOLUME_ADD_BY_PENDING_PARTIAL
                 );
               this.SetProperty(EVENT_PROP_TYPE_EVENT,this.m_trade_event);
               return;
              }
           }
         //--- 如果持仓由入场成交改变
         else
           {
            //--- 如果这是一个持仓逆转
            if(this.IsPresentEventFlag(TRADE_EVENT_FLAG_POSITION_REVERSE))
              {
               //--- 检查部分开仓标志,并设置“持仓逆转”或“持仓由部分执行逆转”交易事件
               this.m_trade_event=
                 (
                  !this.IsPresentEventFlag(TRADE_EVENT_FLAG_PARTIAL) ? 
                  TRADE_EVENT_POSITION_REVERSED_BY_MARKET : 
                  TRADE_EVENT_POSITION_REVERSED_BY_MARKET_PARTIAL
                 );
               this.SetProperty(EVENT_PROP_TYPE_EVENT,this.m_trade_event);
               return;
              }
            //--- 如果这是加仓
            else
              {
               //--- 检查部分开仓标志,并设置“加仓”或“由部分执行加仓”交易事件
               this.m_trade_event=
                 (
                  !this.IsPresentEventFlag(TRADE_EVENT_FLAG_PARTIAL) ? 
                  TRADE_EVENT_POSITION_VOLUME_ADD_BY_MARKET : 
                  TRADE_EVENT_POSITION_VOLUME_ADD_BY_MARKET_PARTIAL
                 );
               this.SetProperty(EVENT_PROP_TYPE_EVENT,this.m_trade_event);
               return;
              }
           }
        }
   //--- 如果开新仓
      else
        {
         //--- 如果挂单被价格激活
         if(this.IsPresentEventFlag(TRADE_EVENT_FLAG_ORDER_ACTIVATED))
           {
            //--- 检查部分开仓标志,并设置“挂单激活”或“挂单部分激活”交易事件
            this.m_trade_event=(!this.IsPresentEventFlag(TRADE_EVENT_FLAG_PARTIAL) ? TRADE_EVENT_PENDING_ORDER_ACTIVATED : TRADE_EVENT_PENDING_ORDER_ACTIVATED_PARTIAL);
            this.SetProperty(EVENT_PROP_TYPE_EVENT,this.m_trade_event);
            return;
           }
         //--- 检查部分开仓标志,并设置“开仓”或“部分开仓”交易事件
         this.m_trade_event=(!this.IsPresentEventFlag(TRADE_EVENT_FLAG_PARTIAL) ? TRADE_EVENT_POSITION_OPENED : TRADE_EVENT_POSITION_OPENED_PARTIAL);
         this.SetProperty(EVENT_PROP_TYPE_EVENT,this.m_trade_event);
         return;
        }
     }
     
//--- 平仓(检查事件代码中是否存在多个标志)
   if(this.IsPresentEventFlag(TRADE_EVENT_FLAG_POSITION_CLOSED))
     {
      //--- 如果某笔持仓由止损平仓
      if(this.IsPresentEventFlag(TRADE_EVENT_FLAG_SL))
        {
         //---  检查部分结束标志,并设置“由止损平仓”或“由止损部分平仓”交易事件
         this.m_trade_event=(!this.IsPresentEventFlag(TRADE_EVENT_FLAG_PARTIAL) ? TRADE_EVENT_POSITION_CLOSED_BY_SL : TRADE_EVENT_POSITION_CLOSED_PARTIAL_BY_SL);
         this.SetProperty(EVENT_PROP_TYPE_EVENT,this.m_trade_event);
         return;
        }
      //--- 如果某笔持仓由止盈平仓
      else if(this.IsPresentEventFlag(TRADE_EVENT_FLAG_TP))
        {
         //---  检查部分结束标志,并设置“由止盈平仓”或“由止盈部分平仓”交易事件
         this.m_trade_event=(!this.IsPresentEventFlag(TRADE_EVENT_FLAG_PARTIAL) ? TRADE_EVENT_POSITION_CLOSED_BY_TP : TRADE_EVENT_POSITION_CLOSED_PARTIAL_BY_TP);
         this.SetProperty(EVENT_PROP_TYPE_EVENT,this.m_trade_event);
         return;
        }
      //--- 如果一笔持仓由逆向仓位平仓
      else if(this.IsPresentEventFlag(TRADE_EVENT_FLAG_BY_POS))
        {
         //---  检查部分结束标志,并设置“由逆向平仓”或“由逆向部分平仓”交易事件
         this.m_trade_event=(!this.IsPresentEventFlag(TRADE_EVENT_FLAG_PARTIAL) ? TRADE_EVENT_POSITION_CLOSED_BY_POS : TRADE_EVENT_POSITION_CLOSED_PARTIAL_BY_POS);
         this.SetProperty(EVENT_PROP_TYPE_EVENT,this.m_trade_event);
         return;
        }
      //--- 如果已平仓
      else
        {
         //---  检查部分结束标志,并设置“已平仓”或“已部分平仓”交易事件
         this.m_trade_event=(!this.IsPresentEventFlag(TRADE_EVENT_FLAG_PARTIAL) ? TRADE_EVENT_POSITION_CLOSED : TRADE_EVENT_POSITION_CLOSED_PARTIAL);
         this.SetProperty(EVENT_PROP_TYPE_EVENT,this.m_trade_event);
         return;
        }
     }
//--- 账户上的余额操作(按交易类型澄清事件)
   if(this.m_event_code==TRADE_EVENT_FLAG_ACCOUNT_BALANCE)
     {
      //--- 初始化交易事件
      this.m_trade_event=TRADE_EVENT_NO_EVENT;
      //--- 取成交类型
      ENUM_DEAL_TYPE deal_type=(ENUM_DEAL_TYPE)this.GetProperty(EVENT_PROP_TYPE_DEAL_EVENT);
      //--- 如果成交是余额操作
      if(deal_type==DEAL_TYPE_BALANCE)
        {
        //--- 检查成交利润,并设置事件(资金存款或取款)
         this.m_trade_event=(this.GetProperty(EVENT_PROP_PROFIT)>0 ? TRADE_EVENT_ACCOUNT_BALANCE_REFILL : TRADE_EVENT_ACCOUNT_BALANCE_WITHDRAWAL);
        }
      //--- 其余的余额操作类型与从 DEAL_TYPE_CREDIT 开始的 ENUM_DEAL_TYPE 枚举匹配
      else if(deal_type>DEAL_TYPE_BALANCE)
        {
        //--- 设置事件
         this.m_trade_event=(ENUM_TRADE_EVENT)deal_type;
        }
      this.SetProperty(EVENT_PROP_TYPE_EVENT,this.m_trade_event);
      return;
     }
  }
//+------------------------------------------------------------------+

整个逻辑非常简单,并在代码中进行了阐述。 因此,我不打算赘述 <if-else> 方法。

我们已在抽象事件类中进行了修改。 我们提供完整的清单:

//+------------------------------------------------------------------+
//|                                                        Event.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"
#property strict    // 出于 mql4 需要
//+------------------------------------------------------------------+
//| 包含文件                                                            |
//+------------------------------------------------------------------+
#include <Object.mqh>
#include "\..\..\Services\DELib.mqh"
#include "..\..\Collections\HistoryCollection.mqh"
#include "..\..\Collections\MarketCollection.mqh"
//+------------------------------------------------------------------+
//| 抽象事件类                                                         |
//+------------------------------------------------------------------+
class CEvent : public CObject
  {
private:
   int               m_event_code;                                   // 事件代码
//--- 返回事件的(1)实数型,和(2)字符串型属性于数组中的索引
   int               IndexProp(ENUM_EVENT_PROP_DOUBLE property)const { return(int)property-EVENT_PROP_INTEGER_TOTAL;                         }
   int               IndexProp(ENUM_EVENT_PROP_STRING property)const { return(int)property-EVENT_PROP_INTEGER_TOTAL-EVENT_PROP_DOUBLE_TOTAL; }
protected:
   ENUM_TRADE_EVENT  m_trade_event;                                  // 交易事件
   bool              m_is_hedge;                                     // 对冲账户标志
   long              m_chart_id;                                     // 控制程序所在的图表 ID
   int               m_digits_acc;                                   // 帐户货币的小数位数
   long              m_long_prop[EVENT_PROP_INTEGER_TOTAL];          // 事件整数型属性
   double            m_double_prop[EVENT_PROP_DOUBLE_TOTAL];         // 事件实数型属性
   string            m_string_prop[EVENT_PROP_STRING_TOTAL];         // 事件字符串型属性
//--- 返回在交易事件中存在的旗帜
   bool              IsPresentEventFlag(const int event_code)  const { return (this.m_event_code & event_code)==event_code;            }

//--- 受保护的参数构造函数
                     CEvent(const ENUM_EVENT_STATUS event_status,const int event_code,const ulong ticket);
public:
//--- 默认构造函数
                     CEvent(void){;}
 
//--- 设置事件的 (1) 整数型, (2) 实数型,和 (3) 字符串型属性
   void              SetProperty(ENUM_EVENT_PROP_INTEGER property,long value) { this.m_long_prop[property]=value;                      }
   void              SetProperty(ENUM_EVENT_PROP_DOUBLE property,double value){ this.m_double_prop[this.IndexProp(property)]=value;    }
   void              SetProperty(ENUM_EVENT_PROP_STRING property,string value){ this.m_string_prop[this.IndexProp(property)]=value;    }
//--- 返回来自属性数组里的事件 (1) 整数型, (2) 实数型,和 (3) 字符串型属性
   long              GetProperty(ENUM_EVENT_PROP_INTEGER property)      const { return this.m_long_prop[property];                     }
   double            GetProperty(ENUM_EVENT_PROP_DOUBLE property)       const { return this.m_double_prop[this.IndexProp(property)];   }
   string            GetProperty(ENUM_EVENT_PROP_STRING property)       const { return this.m_string_prop[this.IndexProp(property)];   }

//--- 返回事件支持该属性的标志
   virtual bool      SupportProperty(ENUM_EVENT_PROP_INTEGER property)        { return true; }
   virtual bool      SupportProperty(ENUM_EVENT_PROP_DOUBLE property)         { return true; }
   virtual bool      SupportProperty(ENUM_EVENT_PROP_STRING property)         { return true; }

//--- 设置控制程序所在的图表 ID
   void              SetChartID(const long id)                                { this.m_chart_id=id;                                    }
//--- 解码事件代码,并设置交易事件,(2)返回交易事件
   void              SetTypeEvent(void);
   ENUM_TRADE_EVENT  TradeEvent(void)                                   const { return this.m_trade_event;                             }
//--- 将事件发送到图表(在继承类里实现)
   virtual void      SendEvent(void) {;}

//--- 按指定的属性比较 CEvent 对象(按指定的事件对象属性对列表进行排序)
   virtual int       Compare(const CObject *node,const int mode=0) const;
//--- 按所有属性比较 CEvent 对象(搜索相等的事件对象)
   bool              IsEqual(CEvent* compared_event);
//+------------------------------------------------------------------+
//| 简化访问事件对象属性的方法                                            |
//+------------------------------------------------------------------+
//--- 返回 (1) 事件类型, (2) 事件时间毫秒值, (3) 时间状况, (4) 事件原因, (5) 成交类型, (6) 成交票据, 
//--- (7) 成交执行时所基于的订单类型, (8) 开仓时的订单类型, (9) 最后一笔开仓单据, 
//--- (10) 首笔开仓单据, (11) 仓位 ID, (12) 逆向仓位 ID, (13) 魔幻数字, (14) 逆向仓位魔幻数字, (15) 开仓时间
   ENUM_TRADE_EVENT  TypeEvent(void)                                    const { return (ENUM_TRADE_EVENT)this.GetProperty(EVENT_PROP_TYPE_EVENT);           }
   long              TimeEvent(void)                                    const { return this.GetProperty(EVENT_PROP_TIME_EVENT);                             }
   ENUM_EVENT_STATUS Status(void)                                       const { return (ENUM_EVENT_STATUS)this.GetProperty(EVENT_PROP_STATUS_EVENT);        }
   ENUM_EVENT_REASON Reason(void)                                       const { return (ENUM_EVENT_REASON)this.GetProperty(EVENT_PROP_REASON_EVENT);        }
   ENUM_DEAL_TYPE    TypeDeal(void)                                     const { return (ENUM_DEAL_TYPE)this.GetProperty(EVENT_PROP_TYPE_DEAL_EVENT);        }
   long              TicketDeal(void)                                   const { return this.GetProperty(EVENT_PROP_TICKET_DEAL_EVENT);                      }
   ENUM_ORDER_TYPE   TypeOrderEvent(void)                               const { return (ENUM_ORDER_TYPE)this.GetProperty(EVENT_PROP_TYPE_ORDER_EVENT);      }
   ENUM_ORDER_TYPE   TypeFirstOrderPosition(void)                       const { return (ENUM_ORDER_TYPE)this.GetProperty(EVENT_PROP_TYPE_ORDER_POSITION);   }
   long              TicketOrderEvent(void)                             const { return this.GetProperty(EVENT_PROP_TICKET_ORDER_EVENT);                     }
   long              TicketFirstOrderPosition(void)                     const { return this.GetProperty(EVENT_PROP_TICKET_ORDER_POSITION);                  }
   long              PositionID(void)                                   const { return this.GetProperty(EVENT_PROP_POSITION_ID);                            }
   long              PositionByID(void)                                 const { return this.GetProperty(EVENT_PROP_POSITION_BY_ID);                         }
   long              Magic(void)                                        const { return this.GetProperty(EVENT_PROP_MAGIC_ORDER);                            }
   long              MagicCloseBy(void)                                 const { return this.GetProperty(EVENT_PROP_MAGIC_BY_ID);                            }
   long              TimePosition(void)                                 const { return this.GetProperty(EVENT_PROP_TIME_ORDER_POSITION);                    }

//--- 当持仓方向改变时,返回 (1) 前持仓订单类型, (2) 前持仓单据,
//--- (3) 当前持仓订单类型, (4) 当前持仓单据,
//--- (5) 持仓类型,和 (6) 方向变化前的票据, (7) 仓位类型,和 (8) 方向变化后的票据
   ENUM_ORDER_TYPE   TypeOrderPosPrevious(void)                         const { return (ENUM_ORDER_TYPE)this.GetProperty(EVENT_PROP_TYPE_ORD_POS_BEFORE);   }
   long              TicketOrderPosPrevious(void)                       const { return (ENUM_ORDER_TYPE)this.GetProperty(EVENT_PROP_TICKET_ORD_POS_BEFORE); }
   ENUM_ORDER_TYPE   TypeOrderPosCurrent(void)                          const { return (ENUM_ORDER_TYPE)this.GetProperty(EVENT_PROP_TYPE_ORD_POS_CURRENT);  }
   long              TicketOrderPosCurrent(void)                        const { return (ENUM_ORDER_TYPE)this.GetProperty(EVENT_PROP_TICKET_ORD_POS_CURRENT);}
   ENUM_POSITION_TYPE TypePositionPrevious(void)                        const { return PositionTypeByOrderType(this.TypeOrderPosPrevious());                }
   ulong             TicketPositionPrevious(void)                       const { return this.TicketOrderPosPrevious();                                       }
   ENUM_POSITION_TYPE TypePositionCurrent(void)                         const { return PositionTypeByOrderType(this.TypeOrderPosCurrent());                 }
   ulong             TicketPositionCurrent(void)                        const { return this.TicketOrderPosCurrent();                                        }
   
//--- 返回 (1) 事件发生时的价格所在, (2) 开盘价, (3) 收盘价,
//--- (4) 止损价, (5) 止盈价, (6) 盈利, (7) 请求的订单交易量, 
//--- (8) 执行的订单交易量, (9) 剩余的订单交易量, (10) 执行的仓量
   double            PriceEvent(void)                                   const { return this.GetProperty(EVENT_PROP_PRICE_EVENT);                            }
   double            PriceOpen(void)                                    const { return this.GetProperty(EVENT_PROP_PRICE_OPEN);                             }
   double            PriceClose(void)                                   const { return this.GetProperty(EVENT_PROP_PRICE_CLOSE);                            }
   double            PriceStopLoss(void)                                const { return this.GetProperty(EVENT_PROP_PRICE_SL);                               }
   double            PriceTakeProfit(void)                              const { return this.GetProperty(EVENT_PROP_PRICE_TP);                               }
   double            Profit(void)                                       const { return this.GetProperty(EVENT_PROP_PROFIT);                                 }
   double            VolumeOrderInitial(void)                           const { return this.GetProperty(EVENT_PROP_VOLUME_ORDER_INITIAL);                   }
   double            VolumeOrderExecuted(void)                          const { return this.GetProperty(EVENT_PROP_VOLUME_ORDER_EXECUTED);                  }
   double            VolumeOrderCurrent(void)                           const { return this.GetProperty(EVENT_PROP_VOLUME_ORDER_CURRENT);                   }
   double            VolumePositionExecuted(void)                       const { return this.GetProperty(EVENT_PROP_VOLUME_POSITION_EXECUTED);               }
//--- Return a (1) symbol and (2) opposite position symbol
   string            Symbol(void)                                       const { return this.GetProperty(EVENT_PROP_SYMBOL);                                 }
   string            SymbolCloseBy(void)                                const { return this.GetProperty(EVENT_PROP_SYMBOL_BY_ID);                           }
   
//+------------------------------------------------------------------+
//| 订单对象属性的描述                                                   |
//+------------------------------------------------------------------+
//--- 返回订单的(1)整数型,(2)实数型,和(3)字符串型属性的描述
   string            GetPropertyDescription(ENUM_EVENT_PROP_INTEGER property);
   string            GetPropertyDescription(ENUM_EVENT_PROP_DOUBLE property);
   string            GetPropertyDescription(ENUM_EVENT_PROP_STRING property);
//--- 返回事件的 (1) 状况,和 (2) 类型
   string            StatusDescription(void)                const;
   string            TypeEventDescription(void)             const;
//--- 返回(1)事件成交订单,(2)仓位父订单,(3)当前持仓的订单,(4)当前持仓的名称
//--- 返回方向改变之前(5)订单,和(6)持仓的名称
   string            TypeOrderDealDescription(void)         const;
   string            TypeOrderFirstDescription(void)        const;
   string            TypeOrderEventDescription(void)        const;
   string            TypePositionCurrentDescription(void)   const;
   string            TypeOrderPreviousDescription(void)     const;
   string            TypePositionPreviousDescription(void)  const;
//--- 返回成交/订单/仓位原因的名字
   string            ReasonDescription(void)                const;

//---  显示(1)订单属性的描述(full_prop=true  - 所有属性,false  - 仅支持的属性),
//--- (2) 简要事件消息(在继承类中实现)
   void              Print(const bool full_prop=false);
   virtual void      PrintShort(void) {;}
  };
//+------------------------------------------------------------------+
//| 构造函数                                                            |
//+------------------------------------------------------------------+
CEvent::CEvent(const ENUM_EVENT_STATUS event_status,const int event_code,const ulong ticket) : m_event_code(event_code)
  {
   this.m_long_prop[EVENT_PROP_STATUS_EVENT]       =  event_status;
   this.m_long_prop[EVENT_PROP_TICKET_ORDER_EVENT] =  (long)ticket;
   this.m_is_hedge=bool(::AccountInfoInteger(ACCOUNT_MARGIN_MODE)==ACCOUNT_MARGIN_MODE_RETAIL_HEDGING);
   this.m_digits_acc=(int)::AccountInfoInteger(ACCOUNT_CURRENCY_DIGITS);
   this.m_chart_id=::ChartID();
  }
//+------------------------------------------------------------------+
//| 按指定的属性比较 CEvent 对象                                          |
//+------------------------------------------------------------------+
int CEvent::Compare(const CObject *node,const int mode=0) const
  {
   const CEvent *event_compared=node;
//--- 比较两个事件的整数型属性
   if(mode<EVENT_PROP_INTEGER_TOTAL)
     {
      long value_compared=event_compared.GetProperty((ENUM_EVENT_PROP_INTEGER)mode);
      long value_current=this.GetProperty((ENUM_EVENT_PROP_INTEGER)mode);
      return(value_current>value_compared ? 1 : value_current<value_compared ? -1 : 0);
     }
//--- 比较两个对象的整数型属性
   if(mode<EVENT_PROP_DOUBLE_TOTAL+EVENT_PROP_INTEGER_TOTAL)
     {
      double value_compared=event_compared.GetProperty((ENUM_EVENT_PROP_DOUBLE)mode);
      double value_current=this.GetProperty((ENUM_EVENT_PROP_DOUBLE)mode);
      return(value_current>value_compared ? 1 : value_current<value_compared ? -1 : 0);
     }
//--- 比较两个对象的字符串型属性
   else if(mode<EVENT_PROP_DOUBLE_TOTAL+EVENT_PROP_INTEGER_TOTAL+EVENT_PROP_STRING_TOTAL)
     {
      string value_compared=event_compared.GetProperty((ENUM_EVENT_PROP_STRING)mode);
      string value_current=this.GetProperty((ENUM_EVENT_PROP_STRING)mode);
      return(value_current>value_compared ? 1 : value_current<value_compared ? -1 : 0);
     }
   return 0;
  }
//+------------------------------------------------------------------+
//| 按所有属性比较 CEvent 对象                                           |
//+------------------------------------------------------------------+
bool CEvent::IsEqual(CEvent *compared_event)
  {
   int beg=0, end=EVENT_PROP_INTEGER_TOTAL;
   for(int i=beg; i<end; i++)
     {
      ENUM_EVENT_PROP_INTEGER prop=(ENUM_EVENT_PROP_INTEGER)i;
      if(this.GetProperty(prop)!=compared_event.GetProperty(prop)) return false; 
     }
   beg=end; end+=EVENT_PROP_DOUBLE_TOTAL;
   for(int i=beg; i<end; i++)
     {
      ENUM_EVENT_PROP_DOUBLE prop=(ENUM_EVENT_PROP_DOUBLE)i;
      if(this.GetProperty(prop)!=compared_event.GetProperty(prop)) return false; 
     }
   beg=end; end+=EVENT_PROP_STRING_TOTAL;
   for(int i=beg; i<end; i++)
     {
      ENUM_EVENT_PROP_STRING prop=(ENUM_EVENT_PROP_STRING)i;
      if(this.GetProperty(prop)!=compared_event.GetProperty(prop)) return false; 
     }
//---
   return true;
  }
//+------------------------------------------------------------------+
//| 解码事件代码并设置交易事件                                            |
//+------------------------------------------------------------------+
void CEvent::SetTypeEvent(void)
  {
//--- 下挂单(检查事件代码是否匹配,因为此处只能有一个标志)
   if(this.m_event_code==TRADE_EVENT_FLAG_ORDER_PLASED)
     {
      this.m_trade_event=TRADE_EVENT_PENDING_ORDER_PLASED;
      this.SetProperty(EVENT_PROP_TYPE_EVENT,this.m_trade_event);
      return;
     }
//--- 删除挂单(检查事件代码是否匹配,因为此处只能有一个标志)
   if(this.m_event_code==TRADE_EVENT_FLAG_ORDER_REMOVED)
     {
      this.m_trade_event=TRADE_EVENT_PENDING_ORDER_REMOVED;
      this.SetProperty(EVENT_PROP_TYPE_EVENT,this.m_trade_event);
      return;
     }
//--- 开仓(检查事件代码中是否存在多个标志)
   if(this.IsPresentEventFlag(TRADE_EVENT_FLAG_POSITION_OPENED))
     {
   //--- 如果现有持仓被修改
      if(this.IsPresentEventFlag(TRADE_EVENT_FLAG_POSITION_CHANGED))
        {
         //--- 如果挂单被价格激活
         if(this.IsPresentEventFlag(TRADE_EVENT_FLAG_ORDER_ACTIVATED))
           {
            //--- 如果这是一个持仓逆转
            if(this.IsPresentEventFlag(TRADE_EVENT_FLAG_POSITION_REVERSE))
              {
               //--- 检查部分平仓标志并设置 
               //--- “由激活挂单造成持仓逆转”或“由部分激活挂单造成持仓逆转”交易事件
               this.m_trade_event=
                 (
                  !this.IsPresentEventFlag(TRADE_EVENT_FLAG_PARTIAL) ? 
                  TRADE_EVENT_POSITION_REVERSED_BY_PENDING : 
                  TRADE_EVENT_POSITION_REVERSED_BY_PENDING_PARTIAL
                 );
               this.SetProperty(EVENT_PROP_TYPE_EVENT,this.m_trade_event);
               return;
              }
            //--- 如果这是加仓
            else
              {
               //--- 检查部分开仓标志并设置交易事件 
               //--- “某笔持仓由激活挂单增加交易量”或“持仓由部分激活挂单增加交易量”
               this.m_trade_event=
                 (
                  !this.IsPresentEventFlag(TRADE_EVENT_FLAG_PARTIAL) ? 
                  TRADE_EVENT_POSITION_VOLUME_ADD_BY_PENDING : 
                  TRADE_EVENT_POSITION_VOLUME_ADD_BY_PENDING_PARTIAL
                 );
               this.SetProperty(EVENT_PROP_TYPE_EVENT,this.m_trade_event);
               return;
              }
           }
         //--- 如果持仓由入场成交改变
         else
           {
            //--- 如果这是一个持仓逆转
            if(this.IsPresentEventFlag(TRADE_EVENT_FLAG_POSITION_REVERSE))
              {
               //--- 检查部分开仓标志,并设置“持仓逆转”或“持仓由部分执行逆转”交易事件
               this.m_trade_event=
                 (
                  !this.IsPresentEventFlag(TRADE_EVENT_FLAG_PARTIAL) ? 
                  TRADE_EVENT_POSITION_REVERSED_BY_MARKET : 
                  TRADE_EVENT_POSITION_REVERSED_BY_MARKET_PARTIAL
                 );
               this.SetProperty(EVENT_PROP_TYPE_EVENT,this.m_trade_event);
               return;
              }
            //--- 如果这是加仓
            else
              {
               //--- 检查部分开仓标志,并设置“加仓”或“由部分执行加仓”交易事件
               this.m_trade_event=
                 (
                  !this.IsPresentEventFlag(TRADE_EVENT_FLAG_PARTIAL) ? 
                  TRADE_EVENT_POSITION_VOLUME_ADD_BY_MARKET : 
                  TRADE_EVENT_POSITION_VOLUME_ADD_BY_MARKET_PARTIAL
                 );
               this.SetProperty(EVENT_PROP_TYPE_EVENT,this.m_trade_event);
               return;
              }
           }
        }
   //--- 如果开新仓
      else
        {
         //--- 如果挂单被价格激活
         if(this.IsPresentEventFlag(TRADE_EVENT_FLAG_ORDER_ACTIVATED))
           {
            //--- 检查部分开仓标志,并设置“挂单激活”或“挂单部分激活”交易事件
            this.m_trade_event=(!this.IsPresentEventFlag(TRADE_EVENT_FLAG_PARTIAL) ? TRADE_EVENT_PENDING_ORDER_ACTIVATED : TRADE_EVENT_PENDING_ORDER_ACTIVATED_PARTIAL);
            this.SetProperty(EVENT_PROP_TYPE_EVENT,this.m_trade_event);
            return;
           }
         //--- 检查部分开仓标志,并设置“开仓”或“部分开仓”交易事件
         this.m_trade_event=(!this.IsPresentEventFlag(TRADE_EVENT_FLAG_PARTIAL) ? TRADE_EVENT_POSITION_OPENED : TRADE_EVENT_POSITION_OPENED_PARTIAL);
         this.SetProperty(EVENT_PROP_TYPE_EVENT,this.m_trade_event);
         return;
        }
     }
     
//--- 平仓(检查事件代码中是否存在多个标志)
   if(this.IsPresentEventFlag(TRADE_EVENT_FLAG_POSITION_CLOSED))
     {
      //--- 如果某笔持仓由止损平仓
      if(this.IsPresentEventFlag(TRADE_EVENT_FLAG_SL))
        {
         //--- 检查部分结束标志,并设置“由止损平仓位”或“由止损部分平仓”交易事件
         this.m_trade_event=(!this.IsPresentEventFlag(TRADE_EVENT_FLAG_PARTIAL) ? TRADE_EVENT_POSITION_CLOSED_BY_SL : TRADE_EVENT_POSITION_CLOSED_PARTIAL_BY_SL);
         this.SetProperty(EVENT_PROP_TYPE_EVENT,this.m_trade_event);
         return;
        }
      //--- 如果某笔持仓由止盈平仓
      else if(this.IsPresentEventFlag(TRADE_EVENT_FLAG_TP))
        {
         //---  检查部分结束标志,并设置“由止盈平仓”或“由止盈部分平仓”交易事件
         this.m_trade_event=(!this.IsPresentEventFlag(TRADE_EVENT_FLAG_PARTIAL) ? TRADE_EVENT_POSITION_CLOSED_BY_TP : TRADE_EVENT_POSITION_CLOSED_PARTIAL_BY_TP);
         this.SetProperty(EVENT_PROP_TYPE_EVENT,this.m_trade_event);
         return;
        }
      //--- 如果一笔持仓由逆向仓位平仓
      else if(this.IsPresentEventFlag(TRADE_EVENT_FLAG_BY_POS))
        {
         //---  检查部分结束标志,并设置“由逆向平仓”或“由逆向部分平仓”交易事件
         this.m_trade_event=(!this.IsPresentEventFlag(TRADE_EVENT_FLAG_PARTIAL) ? TRADE_EVENT_POSITION_CLOSED_BY_POS : TRADE_EVENT_POSITION_CLOSED_PARTIAL_BY_POS);
         this.SetProperty(EVENT_PROP_TYPE_EVENT,this.m_trade_event);
         return;
        }
      //--- 如果已平仓
      else
        {
         //---  检查部分结束标志,并设置“已平仓”或“已部分平仓”交易事件
         this.m_trade_event=(!this.IsPresentEventFlag(TRADE_EVENT_FLAG_PARTIAL) ? TRADE_EVENT_POSITION_CLOSED : TRADE_EVENT_POSITION_CLOSED_PARTIAL);
         this.SetProperty(EVENT_PROP_TYPE_EVENT,this.m_trade_event);
         return;
        }
     }
//--- 账户上的余额操作(按交易类型澄清事件)
   if(this.m_event_code==TRADE_EVENT_FLAG_ACCOUNT_BALANCE)
     {
      //--- 初始化交易事件
      this.m_trade_event=TRADE_EVENT_NO_EVENT;
      //--- 取成交类型
      ENUM_DEAL_TYPE deal_type=(ENUM_DEAL_TYPE)this.GetProperty(EVENT_PROP_TYPE_DEAL_EVENT);
      //--- 如果成交是余额操作
      if(deal_type==DEAL_TYPE_BALANCE)
        {
        //--- 检查成交利润,并设置事件(资金存款或取款)
         this.m_trade_event=(this.GetProperty(EVENT_PROP_PROFIT)>0 ? TRADE_EVENT_ACCOUNT_BALANCE_REFILL : TRADE_EVENT_ACCOUNT_BALANCE_WITHDRAWAL);
        }
      //--- 其余的余额操作类型与从 DEAL_TYPE_CREDIT 开始的 ENUM_DEAL_TYPE 枚举匹配
      else if(deal_type>DEAL_TYPE_BALANCE)
        {
        //--- 设置事件
         this.m_trade_event=(ENUM_TRADE_EVENT)deal_type;
        }
      this.SetProperty(EVENT_PROP_TYPE_EVENT,this.m_trade_event);
      return;
     }
  }
//+------------------------------------------------------------------+
//| 返回事件的整数型属性描述                                              |
//+------------------------------------------------------------------+
string CEvent::GetPropertyDescription(ENUM_EVENT_PROP_INTEGER property)
  {
   return
     (
      property==EVENT_PROP_TYPE_EVENT              ?  TextByLanguage("Тип события","Event's type")+": "+this.TypeEventDescription()                                                       :
      property==EVENT_PROP_TIME_EVENT              ?  TextByLanguage("Время события","Time of event")+": "+TimeMSCtoString(this.GetProperty(property))                                    :
      property==EVENT_PROP_STATUS_EVENT            ?  TextByLanguage("Статус события","Status of event")+": \""+this.StatusDescription()+"\""                                             :
      property==EVENT_PROP_REASON_EVENT            ?  TextByLanguage("Причина события","Reason of event")+": "+this.ReasonDescription()                                                   :
      property==EVENT_PROP_TYPE_DEAL_EVENT         ?  TextByLanguage("Тип сделки","Deal's type")+": "+DealTypeDescription((ENUM_DEAL_TYPE)this.GetProperty(property))                     :
      property==EVENT_PROP_TICKET_DEAL_EVENT       ?  TextByLanguage("Тикет сделки","Deal's ticket")+" #"+(string)this.GetProperty(property)                                              :
      property==EVENT_PROP_TYPE_ORDER_EVENT        ?  TextByLanguage("Тип ордера события","Event's order type")+": "+OrderTypeDescription((ENUM_ORDER_TYPE)this.GetProperty(property))    :
      property==EVENT_PROP_TYPE_ORDER_POSITION     ?  TextByLanguage("Тип ордера позиции","Position's order type")+": "+OrderTypeDescription((ENUM_ORDER_TYPE)this.GetProperty(property)) :
      property==EVENT_PROP_TICKET_ORDER_POSITION   ?  TextByLanguage("Тикет первого ордера позиции","Position's first order ticket")+" #"+(string)this.GetProperty(property)              :
      property==EVENT_PROP_TICKET_ORDER_EVENT      ?  TextByLanguage("Тикет ордера события","Event's order ticket")+" #"+(string)this.GetProperty(property)                               :
      property==EVENT_PROP_POSITION_ID             ?  TextByLanguage("Идентификатор позиции","Position ID")+" #"+(string)this.GetProperty(property)                                       :
      property==EVENT_PROP_POSITION_BY_ID          ?  TextByLanguage("Идентификатор встречной позиции","Opposite position's ID")+" #"+(string)this.GetProperty(property)                  :
      property==EVENT_PROP_MAGIC_ORDER             ?  TextByLanguage("Магический номер","Magic number")+": "+(string)this.GetProperty(property)                                           :
      property==EVENT_PROP_MAGIC_BY_ID             ?  TextByLanguage("Магический номер встречной позиции","Magic number of opposite position")+": "+(string)this.GetProperty(property)    :
      property==EVENT_PROP_TIME_ORDER_POSITION     ?  TextByLanguage("Время открытия позиции","Position's opened time")+": "+TimeMSCtoString(this.GetProperty(property))                  :
      property==EVENT_PROP_TYPE_ORD_POS_BEFORE     ?  TextByLanguage("Тип ордера позиции до смены направления","Type order of position before changing direction")                        :
      property==EVENT_PROP_TICKET_ORD_POS_BEFORE   ?  TextByLanguage("Тикет ордера позиции до смены направления","Ticket order of position before changing direction")                    :
      property==EVENT_PROP_TYPE_ORD_POS_CURRENT    ?  TextByLanguage("Тип ордера текущей позиции","Type order of current position")                                                       :
      property==EVENT_PROP_TICKET_ORD_POS_CURRENT  ?  TextByLanguage("Тикет ордера текущей позиции","Ticket order of current position")                                                   :
      EnumToString(property)
     );
  }
//+------------------------------------------------------------------+
//| 返回事件的实数型属性描述                                               |
//+------------------------------------------------------------------+
string CEvent::GetPropertyDescription(ENUM_EVENT_PROP_DOUBLE property)
  {
   int dg=(int)::SymbolInfoInteger(this.GetProperty(EVENT_PROP_SYMBOL),SYMBOL_DIGITS);
   int dgl=(int)DigitsLots(this.GetProperty(EVENT_PROP_SYMBOL));
   return
     (
      property==EVENT_PROP_PRICE_EVENT             ?  TextByLanguage("Цена события","Price at the time of the event")+": "+::DoubleToString(this.GetProperty(property),dg)          :
      property==EVENT_PROP_PRICE_OPEN              ?  TextByLanguage("Цена открытия","Open price")+": "+::DoubleToString(this.GetProperty(property),dg)                             :
      property==EVENT_PROP_PRICE_CLOSE             ?  TextByLanguage("Цена закрытия","Close price")+": "+::DoubleToString(this.GetProperty(property),dg)                            :
      property==EVENT_PROP_PRICE_SL                ?  TextByLanguage("Цена StopLoss","StopLoss price")+": "+::DoubleToString(this.GetProperty(property),dg)                         :
      property==EVENT_PROP_PRICE_TP                ?  TextByLanguage("Цена TakeProfit","TakeProfit price")+": "+::DoubleToString(this.GetProperty(property),dg)                     :
      property==EVENT_PROP_VOLUME_ORDER_INITIAL    ?  TextByLanguage("Начальный объём ордера","Initial order volume")+": "+::DoubleToString(this.GetProperty(property),dgl)         :
      property==EVENT_PROP_VOLUME_ORDER_EXECUTED   ?  TextByLanguage("Исполненный объём ордера","Executed order volume")+": "+::DoubleToString(this.GetProperty(property),dgl)      :
      property==EVENT_PROP_VOLUME_ORDER_CURRENT    ?  TextByLanguage("Оставшийся объём ордера","Remaining order volume")+": "+::DoubleToString(this.GetProperty(property),dgl)      :
      property==EVENT_PROP_VOLUME_POSITION_EXECUTED ? TextByLanguage("Текущий объём позиции","Current position volume")+": "+::DoubleToString(this.GetProperty(property),dgl)       :
      property==EVENT_PROP_PROFIT                  ?  TextByLanguage("Профит","Profit")+": "+::DoubleToString(this.GetProperty(property),this.m_digits_acc)                         :
      EnumToString(property)
     );
  }
//+------------------------------------------------------------------+
//| 返回事件的字符串型属性描述                                              |
//+------------------------------------------------------------------+
string CEvent::GetPropertyDescription(ENUM_EVENT_PROP_STRING property)
  {
   return
     (
      property==EVENT_PROP_SYMBOL ? TextByLanguage("Символ","Symbol")+": \""+this.GetProperty(property)+"\""   :
      TextByLanguage("Символ встречной позиции","Symbol of opposite position")+": \""+this.GetProperty(property)+"\""
     );
  }
//+------------------------------------------------------------------+
//| 返回事件的状况名                                                    |
//+------------------------------------------------------------------+
string CEvent::StatusDescription(void) const

  {
   ENUM_EVENT_STATUS status=(ENUM_EVENT_STATUS)this.GetProperty(EVENT_PROP_STATUS_EVENT);
   return
     (
      status==EVENT_STATUS_MARKET_PENDING    ?  TextByLanguage("Установлен отложенный ордер","Pending order placed") :
      status==EVENT_STATUS_MARKET_POSITION   ?  TextByLanguage("Открыта позиция","Position opened")                 :
      status==EVENT_STATUS_HISTORY_PENDING   ?  TextByLanguage("Удален отложенный ордер","Pending order removed")    :
      status==EVENT_STATUS_HISTORY_POSITION  ?  TextByLanguage("Закрыта позиция","Position closed")                  :
      status==EVENT_STATUS_BALANCE           ?  TextByLanguage("Балансная операция","Balance operation")             :
      TextByLanguage("Неизвестный статус","Unknown status")
     );
  }
//+------------------------------------------------------------------+
//| 返回交易事件名称                                                    |
//+------------------------------------------------------------------+
string CEvent::TypeEventDescription(void) const
  {
   ENUM_TRADE_EVENT event=this.TypeEvent();
   return
     (
      event==TRADE_EVENT_PENDING_ORDER_PLASED                  ?  TextByLanguage("Отложенный ордер установлен","Pending order placed")                                  :
      event==TRADE_EVENT_PENDING_ORDER_REMOVED                 ?  TextByLanguage("Отложенный ордер удалён","Pending order removed")                                     :
      event==TRADE_EVENT_ACCOUNT_CREDIT                        ?  TextByLanguage("Начисление кредита","Credit")                                                         :
      event==TRADE_EVENT_ACCOUNT_CHARGE                        ?  TextByLanguage("Дополнительные сборы","Additional charge")                                            :
      event==TRADE_EVENT_ACCOUNT_CORRECTION                    ?  TextByLanguage("Корректирующая запись","Correction")                                                  :
      event==TRADE_EVENT_ACCOUNT_BONUS                         ?  TextByLanguage("Перечисление бонусов","Bonus")                                                        :
      event==TRADE_EVENT_ACCOUNT_COMISSION                     ?  TextByLanguage("Дополнительные комиссии","Additional commission")                                     :
      event==TRADE_EVENT_ACCOUNT_COMISSION_DAILY               ?  TextByLanguage("Комиссия, начисляемая в конце торгового дня","Daily commission")                      :
      event==TRADE_EVENT_ACCOUNT_COMISSION_MONTHLY             ?  TextByLanguage("Комиссия, начисляемая в конце месяца","Monthly commission")                           :
      event==TRADE_EVENT_ACCOUNT_COMISSION_AGENT_DAILY         ?  TextByLanguage("Агентская комиссия, начисляемая в конце торгового дня","Daily agent commission")      :
      event==TRADE_EVENT_ACCOUNT_COMISSION_AGENT_MONTHLY       ?  TextByLanguage("Агентская комиссия, начисляемая в конце месяца","Monthly agent commission")           :
      event==TRADE_EVENT_ACCOUNT_INTEREST                      ?  TextByLanguage("Начисления процентов на свободные средства","Interest rate")                          :
      event==TRADE_EVENT_BUY_CANCELLED                         ?  TextByLanguage("Отмененная сделка покупки","Canceled buy deal")                                       :
      event==TRADE_EVENT_SELL_CANCELLED                        ?  TextByLanguage("Отмененная сделка продажи","Canceled sell deal")                                      :
      event==TRADE_EVENT_DIVIDENT                              ?  TextByLanguage("Начисление дивиденда","Dividend operations")                                          :
      event==TRADE_EVENT_DIVIDENT_FRANKED                      ?  TextByLanguage("Начисление франкированного дивиденда","Franked (non-taxable) dividend operations")    :
      event==TRADE_EVENT_TAX                                   ?  TextByLanguage("Начисление налога","Tax charges")                                                     :
      event==TRADE_EVENT_ACCOUNT_BALANCE_REFILL                ?  TextByLanguage("Пополнение средств на балансе","Balance refill")                                      :
      event==TRADE_EVENT_ACCOUNT_BALANCE_WITHDRAWAL            ?  TextByLanguage("Снятие средств с баланса","Withdrawals")                                              :
      event==TRADE_EVENT_PENDING_ORDER_ACTIVATED               ?  TextByLanguage("Отложенный ордер активирован ценой","Pending order activated")                        :
      event==TRADE_EVENT_PENDING_ORDER_ACTIVATED_PARTIAL       ?  TextByLanguage("Отложенный ордер активирован ценой частично","Pending order activated partially")     :
      event==TRADE_EVENT_POSITION_OPENED                       ?  TextByLanguage("Позиция открыта","Position opened")                                                  :
      event==TRADE_EVENT_POSITION_OPENED_PARTIAL               ?  TextByLanguage("Позиция открыта частично","Position opened partially")                               :
      event==TRADE_EVENT_POSITION_CLOSED                       ?  TextByLanguage("Позиция закрыта","Position closed")                                                   :
      event==TRADE_EVENT_POSITION_CLOSED_PARTIAL               ?  TextByLanguage("Позиция закрыта частично","Position closed partially")                                :
      event==TRADE_EVENT_POSITION_CLOSED_BY_POS                ?  TextByLanguage("Позиция закрыта встречной","Position closed by opposite position")                    :
      event==TRADE_EVENT_POSITION_CLOSED_PARTIAL_BY_POS        ?  TextByLanguage("Позиция закрыта встречной частично","Position closed partially by opposite position") :
      event==TRADE_EVENT_POSITION_CLOSED_BY_SL                 ?  TextByLanguage("Позиция закрыта по StopLoss","Position closed by StopLoss")                           :
      event==TRADE_EVENT_POSITION_CLOSED_BY_TP                 ?  TextByLanguage("Позиция закрыта по TakeProfit","Position closed by TakeProfit")                       :
      event==TRADE_EVENT_POSITION_CLOSED_PARTIAL_BY_SL         ?  TextByLanguage("Позиция закрыта частично по StopLoss","Position closed partially by StopLoss")        :
      event==TRADE_EVENT_POSITION_CLOSED_PARTIAL_BY_TP         ?  TextByLanguage("Позиция закрыта частично по TakeProfit","Position closed partially by TakeProfit")    :
      event==TRADE_EVENT_POSITION_REVERSED_BY_MARKET           ?  TextByLanguage("Разворот позиции по рыночному запросу","Position reversal by market request")         :
      event==TRADE_EVENT_POSITION_REVERSED_BY_PENDING          ?  TextByLanguage("Разворот позиции срабатыванием отложенного ордера","Position reversal by a triggered pending order")                            :
      event==TRADE_EVENT_POSITION_VOLUME_ADD_BY_MARKET         ?  TextByLanguage("Добавлен объём к позиции по рыночному запросу","Added volume to position by market request")                                    :
      event==TRADE_EVENT_POSITION_VOLUME_ADD_BY_PENDING        ?  TextByLanguage("Добавлен объём к позиции активацией отложенного ордера","Added volume to position by activation of pending order")        :
      
      event==TRADE_EVENT_POSITION_REVERSED_BY_MARKET_PARTIAL   ?  TextByLanguage("Разворот позиции частичным исполнением запроса","Position reversal by partial completion of market request")                   :
      event==TRADE_EVENT_POSITION_REVERSED_BY_PENDING_PARTIAL  ?  TextByLanguage("Разворот позиции частичным срабатыванием отложенного ордера","Position reversal by partially triggered pending order")        :
      event==TRADE_EVENT_POSITION_VOLUME_ADD_BY_MARKET_PARTIAL ?  TextByLanguage("Добавлен объём к позиции частичным исполнением запроса","Added volume to position by partial completion of market request")    :
      event==TRADE_EVENT_POSITION_VOLUME_ADD_BY_PENDING_PARTIAL ? TextByLanguage("Добавлен объём к позиции активацией отложенного ордера","Added volume to position by partially triggering a pending order")  :
      TextByLanguage("Нет торгового события","No trade event")
     );   
  }
//+------------------------------------------------------------------+
//| 返回订单/仓位/成交的名称                                             |
//+------------------------------------------------------------------+
string CEvent::TypeOrderDealDescription(void) const
  {
   ENUM_EVENT_STATUS status=this.Status();
   return
     (
      status==EVENT_STATUS_MARKET_PENDING  || status==EVENT_STATUS_HISTORY_PENDING  ?  OrderTypeDescription((ENUM_ORDER_TYPE)this.GetProperty(EVENT_PROP_TYPE_ORDER_EVENT))      :
      status==EVENT_STATUS_MARKET_POSITION || status==EVENT_STATUS_HISTORY_POSITION ?  PositionTypeDescription((ENUM_POSITION_TYPE)this.GetProperty(EVENT_PROP_TYPE_DEAL_EVENT)) :
      status==EVENT_STATUS_BALANCE  ?  DealTypeDescription((ENUM_DEAL_TYPE)this.GetProperty(EVENT_PROP_TYPE_DEAL_EVENT))  :  
      TextByLanguage("Неизвестный тип ордера","Unknown order type")
     );
  }
//+------------------------------------------------------------------+
//| 返回首笔开仓订单名                                                   |
//+------------------------------------------------------------------+
string CEvent::TypeOrderFirstDescription(void) const
  {
   return OrderTypeDescription((ENUM_ORDER_TYPE)this.GetProperty(EVENT_PROP_TYPE_ORDER_POSITION));
  }
//+------------------------------------------------------------------+
//| 返回仓位更改的订单名称                                               |
//+------------------------------------------------------------------+
string CEvent::TypeOrderEventDescription(void) const
  {
   return OrderTypeDescription(this.TypeOrderEvent());
  }
//+------------------------------------------------------------------+
//| 返回当前仓位的名称                                                   |
//+------------------------------------------------------------------+
string CEvent::TypePositionCurrentDescription(void) const
  {
   return PositionTypeDescription(this.TypePositionCurrent());
  }
//+------------------------------------------------------------------+
//| 返回方向更改前订单的名称                                               |
//+------------------------------------------------------------------+
string CEvent::TypeOrderPreviousDescription(void) const
  {
   return OrderTypeDescription(this.TypeOrderPosPrevious());
  }
//+------------------------------------------------------------------+
//| 返回方向更改之前仓位的名称                                            |
//+------------------------------------------------------------------+
string CEvent::TypePositionPreviousDescription(void) const
  {
   return PositionTypeDescription(this.TypePositionPrevious());
  }
//+------------------------------------------------------------------+
//| 返回成交/订单/仓位原因的名称                                          |
//+------------------------------------------------------------------+
string CEvent::ReasonDescription(void) const
  {
   ENUM_EVENT_REASON reason=this.Reason();
   return 
     (
      reason==EVENT_REASON_ACTIVATED_PENDING                ?  TextByLanguage("Активирован отложенный ордер","Pending order activated")                           :
      reason==EVENT_REASON_ACTIVATED_PENDING_PARTIALLY      ?  TextByLanguage("Частичное срабатывание отложенного ордера","Pending order partially triggered")    :
      reason==EVENT_REASON_CANCEL                           ?  TextByLanguage("Отмена","Canceled")                                                                :
      reason==EVENT_REASON_EXPIRED                          ?  TextByLanguage("Истёк срок действия","Expired")                                                    :
      reason==EVENT_REASON_DONE                             ?  TextByLanguage("Рыночный запрос, выполненный в полном объёме","Fully completed market request")    :
      reason==EVENT_REASON_DONE_PARTIALLY                   ?  TextByLanguage("Выполненный частично рыночный запрос","Partially completed market request")        :
      reason==EVENT_REASON_VOLUME_ADD                       ?  TextByLanguage("Добавлен объём к позиции","Added volume to position")                              :
      reason==EVENT_REASON_VOLUME_ADD_PARTIALLY             ?  TextByLanguage("Добавлен объём к позиции частичным исполнением заявки","Volume added to the position by partially completed request")                 :
      reason==EVENT_REASON_VOLUME_ADD_BY_PENDING            ?  TextByLanguage("Добавлен объём к позиции активацией отложенного ордера","Added volume to position by triggering pending order")                      :
      reason==EVENT_REASON_VOLUME_ADD_BY_PENDING_PARTIALLY  ?  TextByLanguage("Добавлен объём к позиции частичной активацией отложенного ордера","Added volume to position by triggering pending order partially")  :
      reason==EVENT_REASON_REVERSE                          ?  TextByLanguage("Разворот позиции","Position reversal")  :
      reason==EVENT_REASON_REVERSE_PARTIALLY                ?  TextByLanguage("Разворот позиции частичным исполнением заявки","Position reversal by partial completion of request")                             :
      reason==EVENT_REASON_REVERSE_BY_PENDING               ?  TextByLanguage("Разворот позиции при срабатывании отложенного ордера","Position reversal when triggering pending order")                               :
      reason==EVENT_REASON_REVERSE_BY_PENDING_PARTIALLY     ?  TextByLanguage("Разворот позиции при при частичном срабатывании отложенного ордера","Position reversal on partially triggered pending order")       :
      reason==EVENT_REASON_DONE_SL                          ?  TextByLanguage("Закрытие по StopLoss","Close by StopLoss triggered")                               :
      reason==EVENT_REASON_DONE_SL_PARTIALLY                ?  TextByLanguage("Частичное закрытие по StopLoss","Partial close by StopLoss triggered")           :
      reason==EVENT_REASON_DONE_TP                          ?  TextByLanguage("Закрытие по TakeProfit","Close by TakeProfit triggered")                           :
      reason==EVENT_REASON_DONE_TP_PARTIALLY                ?  TextByLanguage("Частичное закрытие по TakeProfit","Partial close by TakeProfit triggered")       :
      reason==EVENT_REASON_DONE_BY_POS                      ?  TextByLanguage("Закрытие встречной позицией","Closed by opposite position")                        :
      reason==EVENT_REASON_DONE_PARTIALLY_BY_POS            ?  TextByLanguage("Частичное закрытие встречной позицией","Closed partially by opposite position")    :
      reason==EVENT_REASON_DONE_BY_POS_PARTIALLY            ?  TextByLanguage("Закрытие частью объёма встречной позиции","Closed by incomplete volume of opposite position") :
      reason==EVENT_REASON_DONE_PARTIALLY_BY_POS_PARTIALLY  ?  TextByLanguage("Частичное закрытие частью объёма встречной позиции","Closed partially by incomplete volume of opposite position")  :
      reason==EVENT_REASON_BALANCE_REFILL                   ?  TextByLanguage("Пополнение баланса","Balance refill")                                              :
      reason==EVENT_REASON_BALANCE_WITHDRAWAL               ?  TextByLanguage("Снятие средств с баланса","Withdrawal from balance")                          :
      reason==EVENT_REASON_ACCOUNT_CREDIT                   ?  TextByLanguage("Начисление кредита","Credit")                                                      :
      reason==EVENT_REASON_ACCOUNT_CHARGE                   ?  TextByLanguage("Дополнительные сборы","Additional charge")                                         :
      reason==EVENT_REASON_ACCOUNT_CORRECTION               ?  TextByLanguage("Корректирующая запись","Correction")                                               :
      reason==EVENT_REASON_ACCOUNT_BONUS                    ?  TextByLanguage("Перечисление бонусов","Bonus")                                                     :
      reason==EVENT_REASON_ACCOUNT_COMISSION                ?  TextByLanguage("Дополнительные комиссии","Additional commission")                                  :
      reason==EVENT_REASON_ACCOUNT_COMISSION_DAILY          ?  TextByLanguage("Комиссия, начисляемая в конце торгового дня","Daily commission")                   :
      reason==EVENT_REASON_ACCOUNT_COMISSION_MONTHLY        ?  TextByLanguage("Комиссия, начисляемая в конце месяца","Monthly commission")                        :
      reason==EVENT_REASON_ACCOUNT_COMISSION_AGENT_DAILY    ?  TextByLanguage("Агентская комиссия, начисляемая в конце торгового дня","Daily agent commission")   :
      reason==EVENT_REASON_ACCOUNT_COMISSION_AGENT_MONTHLY  ?  TextByLanguage("Агентская комиссия, начисляемая в конце месяца","Monthly agent commission")        :
      reason==EVENT_REASON_ACCOUNT_INTEREST                 ?  TextByLanguage("Начисления процентов на свободные средства","Interest rate")                       :
      reason==EVENT_REASON_BUY_CANCELLED                    ?  TextByLanguage("Отмененная сделка покупки","Canceled buy deal")                                    :
      reason==EVENT_REASON_SELL_CANCELLED                   ?  TextByLanguage("Отмененная сделка продажи","Canceled sell deal")                                   :
      reason==EVENT_REASON_DIVIDENT                         ?  TextByLanguage("Начисление дивиденда","Dividend operations")                                       :
      reason==EVENT_REASON_DIVIDENT_FRANKED                 ?  TextByLanguage("Начисление франкированного дивиденда","Franked (non-taxable) dividend operations") :
      reason==EVENT_REASON_TAX                              ?  TextByLanguage("Начисление налога","Tax charges")                                                  :
      EnumToString(reason)
     );
  }
//+------------------------------------------------------------------+
//| 在流水日志中显示事件属性                                              |
//+------------------------------------------------------------------+
void CEvent::Print(const bool full_prop=false)
  {
   ::Print("============= ",TextByLanguage("Начало списка параметров события: \"","Beginning of event parameter list: \""),this.StatusDescription(),"\" =============");
   int beg=0, end=EVENT_PROP_INTEGER_TOTAL;
   for(int i=beg; i<end; i++)
     {
      ENUM_EVENT_PROP_INTEGER prop=(ENUM_EVENT_PROP_INTEGER)i;
      if(!full_prop && !this.SupportProperty(prop)) continue;
      ::Print(this.GetPropertyDescription(prop));
     }
   ::Print("------");
   beg=end; end+=EVENT_PROP_DOUBLE_TOTAL;
   for(int i=beg; i<end; i++)
     {
      ENUM_EVENT_PROP_DOUBLE prop=(ENUM_EVENT_PROP_DOUBLE)i;
      if(!full_prop && !this.SupportProperty(prop)) continue;
      ::Print(this.GetPropertyDescription(prop));
     }
   ::Print("------");
   beg=end; end+=EVENT_PROP_STRING_TOTAL;
   for(int i=beg; i<end; i++)
     {
      ENUM_EVENT_PROP_STRING prop=(ENUM_EVENT_PROP_STRING)i;
      if(!full_prop && !this.SupportProperty(prop)) continue;
      ::Print(this.GetPropertyDescription(prop));
     }
   ::Print("================== ",TextByLanguage("Конец списка параметров: \"","End of parameter list: \""),this.StatusDescription(),"\" ==================\n");
  }
//+------------------------------------------------------------------+

由于对冲和净持结算账户的差异仅在处理仓位时很明显,CEventPositionOpenCEventPositionClose 派生类的 CEvent 抽象类需要微调 — 仅改进在流水日志里显示事件消息的方法。 类的其余方法保持不变。

打开 EventPositionOpen.mqh 文件,并添加创建并返回简要事件描述的私有方法:

//+------------------------------------------------------------------+
//| 开仓事件                                                           |
//+------------------------------------------------------------------+
class CEventPositionOpen : public CEvent
  {
private:
//--- 创建并返回简要事件消息
   string            EventsMessage(void);  
public:
//--- 构造函数
                     CEventPositionOpen(const int event_code,const ulong ticket=0) : CEvent(EVENT_STATUS_MARKET_POSITION,event_code,ticket) {}
//--- 支持的 (1) 实数型,和 (2) 整数型订单属性
   virtual bool      SupportProperty(ENUM_EVENT_PROP_INTEGER property);
   virtual bool      SupportProperty(ENUM_EVENT_PROP_DOUBLE property);
//--- (1) 在流水日志里显示一条简要事件消息, (2) 发送一条事件至图表
   virtual void      PrintShort(void);
   virtual void      SendEvent(void);
  };
//+------------------------------------------------------------------+

我们在类实体外编写它的实现:

//+------------------------------------------------------------------+
//| 创建并返回短事件消息                                                  |
//+------------------------------------------------------------------+
string CEventPositionOpen::EventsMessage(void)
  {
//--- 事件品种报价中的小数位数
   int digits=(int)::SymbolInfoInteger(this.Symbol(),SYMBOL_DIGITS);
//--- (1) 标题, (2) 已执行订单交易量, (3) 已执行仓位量, (4) 事件价格,
//--- (5) 止损价格, (6) 止盈价格, (7) 魔幻数字, (6) 按账户货币的赢利额
   string head="- "+this.TypeEventDescription()+": "+TimeMSCtoString(this.TimePosition())+" -\n";
   string vol_ord=::DoubleToString(this.VolumeOrderExecuted(),DigitsLots(this.Symbol()));
   string vol_pos=::DoubleToString(this.VolumePositionExecuted(),DigitsLots(this.Symbol()));
   string price=TextByLanguage(" по цене "," at price ")+::DoubleToString(this.PriceEvent(),digits);
   string sl=(this.PriceStopLoss()>0 ? ", sl "+ ::DoubleToString(this.PriceStopLoss(),digits) : "");
   string tp=(this.PriceTakeProfit()>0 ? ", tp "+ ::DoubleToString(this.PriceTakeProfit(),digits) : "");
   string magic=(this.Magic()!=0 ? TextByLanguage(", магик ",", magic ")+(string)this.Magic() : "");
   string profit=TextByLanguage(", профит ",", profit ")+::DoubleToString(this.Profit(),this.m_digits_acc)+" "+::AccountInfoString(ACCOUNT_CURRENCY);
   //---
   string text="";
   //--- 持仓逆转
   if(this.GetProperty(EVENT_PROP_REASON_EVENT)<EVENT_REASON_ACTIVATED_PENDING)
     {
      //--- EURUSD: Buy #xx changed to 0.1 Sell #xx (0.2 SellLimit order #XX) at х.ххххх, sl х.ххххх, tp x.xxxxx, magic, profit xxxx
      text=
        (
         this.Symbol()+" "+
         this.TypePositionPreviousDescription()+" #"+(string)this.TicketPositionPrevious()+
         TextByLanguage(" изменен на "," turned to ")+vol_pos+" "+this.TypePositionCurrentDescription()+" #"+(string)this.TicketPositionCurrent()+
         " ["+vol_ord+" "+this.TypeOrderEventDescription()+" #"+(string)this.TicketOrderEvent()+" ]"+price+sl+tp+magic+profit
        );
     }
   else
     {
      //--- 加仓
      if(this.GetProperty(EVENT_PROP_TICKET_ORDER_EVENT)!=this.GetProperty(EVENT_PROP_POSITION_ID))
        {
         //--- EURUSD: Added 0.1 to Buy #xx (BuyLimit order #XX) at х.ххххх, magic
         text=
           (
            this.Symbol()+" "+
            TextByLanguage("Добавлено ","Added ")+vol_ord+TextByLanguage(" к "," to ")+
            this.TypePositionCurrentDescription()+" #"+(string)this.TicketPositionCurrent()+
            " ["+vol_ord+" "+this.TypeOrderEventDescription()+" #"+(string)this.TicketOrderEvent()+" ]"+price+magic
           );
        }
      //--- 开仓
      else
        {
         //--- EURUSD: Opened 0.1 Buy #xx (BuyLimit order #XX) at х.ххххх, sl х.ххххх, tp x.xxxxx, magic
         text=
           (
            this.Symbol()+" "+
            TextByLanguage("Открыт ","Open ")+vol_pos+" "+
            this.TypePositionCurrentDescription()+" #"+(string)this.TicketPositionCurrent()+
            " ["+vol_ord+" "+this.TypeOrderEventDescription()+" #"+(string)this.TicketOrderEvent()+" ]"+price+sl+tp+magic
           );
        }
     }
   return head+text;
  }
//+------------------------------------------------------------------+

该方法根据事件状况,和某些存在的事件对象属性创建消息变体。
例如,如果设置了止损,则会在文本中加入 “sl” 标题及其价格。 否则,插入空字符串,而非止损项目。 对其他一些事件属性也是如此。 方法清单的注释包含创建事件文本的条件,以及方法返回的文本的样本

方法中创建的文本在 PrintShort() 方法里显示到日志中,在事件集合类调用 Refresh() 时会顺序调用在 CEventPositionOpen 类中重新定义的 CEvent 类的虚方法 SendEvent

下面是 CEventPositionOpen 类的完整清单:

//+------------------------------------------------------------------+
//|                                            EventPositionOpen.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 "Event.mqh"
//+------------------------------------------------------------------+
//| 开仓事件                                                            |
//+------------------------------------------------------------------+
class CEventPositionOpen : public CEvent
  {
private:
//--- 创建并返回简要事件消息
   string            EventsMessage(void);  
public:
//--- 构造函数
                     CEventPositionOpen(const int event_code,const ulong ticket=0) : CEvent(EVENT_STATUS_MARKET_POSITION,event_code,ticket) {}
//--- 支持的 (1) 实数型,和 (2) 整数型订单属性
   virtual bool      SupportProperty(ENUM_EVENT_PROP_INTEGER property);
   virtual bool      SupportProperty(ENUM_EVENT_PROP_DOUBLE property);
//--- (1) 在流水日志里显示事件简要消息, (2) 发送事件至图表
   virtual void      PrintShort(void);
   virtual void      SendEvent(void); 
  };
//+------------------------------------------------------------------+
//| 返回 'true' 如果事件支持所传递的                                      |
//| 整数型属性,否则,返回 'false'                                       |
//+------------------------------------------------------------------+
bool CEventPositionOpen::SupportProperty(ENUM_EVENT_PROP_INTEGER property)
  {
   return(property==EVENT_PROP_POSITION_BY_ID ? false : true);
  }
//+------------------------------------------------------------------+
//| 返回 'true' 如果事件支持所传递的                                          |
//| 实数型属性,否则,返回 'false'                                       |
//+------------------------------------------------------------------+
bool CEventPositionOpen::SupportProperty(ENUM_EVENT_PROP_DOUBLE property)
  {
   if(property==EVENT_PROP_PRICE_CLOSE ||
      property==EVENT_PROP_PROFIT
     ) return false;
   return true;

  }
//+------------------------------------------------------------------+
//| 在流水帐中显示有关事件的简要消息                                         |
//+------------------------------------------------------------------+
void CEventPositionOpen::PrintShort(void)
  {
   ::Print(this.EventsMessage());
  }
//+------------------------------------------------------------------+
//| 发送事件至图表                                                       |
//+------------------------------------------------------------------+
void CEventPositionOpen::SendEvent(void)
  {
   this.PrintShort();
   ::EventChartCustom(this.m_chart_id,(ushort)this.m_trade_event,this.PositionID(),this.PriceOpen(),this.Symbol());
  }
//+------------------------------------------------------------------+
//| 创建并返回短事件消息                                                  |
//+------------------------------------------------------------------+
string CEventPositionOpen::EventsMessage(void)
  {
//--- 事件品种报价中的小数位数
   int digits=(int)::SymbolInfoInteger(this.Symbol(),SYMBOL_DIGITS);
//--- (1) 标题, (2) 已执行订单交易量, (3) 已执行仓位量, (4) 事件价格,
//--- (5) 止损价格, (6) 止盈价格, (7) 魔幻数字, (6) 按账户货币的赢利额
   string head="- "+this.TypeEventDescription()+": "+TimeMSCtoString(this.TimePosition())+" -\n";
   string vol_ord=::DoubleToString(this.VolumeOrderExecuted(),DigitsLots(this.Symbol()));
   string vol_pos=::DoubleToString(this.VolumePositionExecuted(),DigitsLots(this.Symbol()));
   string price=TextByLanguage(" по цене "," at price ")+::DoubleToString(this.PriceEvent(),digits);
   string sl=(this.PriceStopLoss()>0 ? ", sl "+ ::DoubleToString(this.PriceStopLoss(),digits) : "");
   string tp=(this.PriceTakeProfit()>0 ? ", tp "+ ::DoubleToString(this.PriceTakeProfit(),digits) : "");
   string magic=(this.Magic()!=0 ? TextByLanguage(", магик ",", magic ")+(string)this.Magic() : "");
   string profit=TextByLanguage(", профит ",", profit ")+::DoubleToString(this.Profit(),this.m_digits_acc)+" "+::AccountInfoString(ACCOUNT_CURRENCY);
   //---
   string text="";
   //--- 持仓逆转
   if(this.GetProperty(EVENT_PROP_REASON_EVENT)<EVENT_REASON_ACTIVATED_PENDING)
     {
      //--- EURUSD: Buy #xx changed to 0.1 Sell #xx [0.2 SellLimit order #XX] at х.ххххх, sl х.ххххх, tp x.xxxxx, magic, profit xxxx
      text=
        (
         this.Symbol()+" "+
         this.TypePositionPreviousDescription()+" #"+(string)this.TicketPositionPrevious()+
         TextByLanguage(" изменен на "," turned to ")+vol_pos+" "+this.TypePositionCurrentDescription()+" #"+(string)this.TicketPositionCurrent()+
         " ["+vol_ord+" "+this.TypeOrderEventDescription()+" #"+(string)this.TicketOrderEvent()+" ]"+price+sl+tp+magic+profit
        );
     }
   else
     {
      //--- 加仓
      if(this.GetProperty(EVENT_PROP_TICKET_ORDER_EVENT)!=this.GetProperty(EVENT_PROP_POSITION_ID))
        {
         //--- EURUSD: Added 0.1 to Buy #xx [BuyLimit order #XX] at х.ххххх, magic
         text=
           (
            this.Symbol()+" "+
            TextByLanguage("Добавлено ","Added ")+vol_ord+TextByLanguage(" к "," to ")+
            this.TypePositionCurrentDescription()+" #"+(string)this.TicketPositionCurrent()+
            " ["+vol_ord+" "+this.TypeOrderEventDescription()+" #"+(string)this.TicketOrderEvent()+" ]"+price+magic
           );
        }
      //--- 开仓
      else
        {
         //--- EURUSD: Opened 0.1 Buy #xx [BuyLimit order #XX] at х.ххххх, sl х.ххххх, tp x.xxxxx, magic
         text=
           (
            this.Symbol()+" "+
            TextByLanguage("Открыт ","Open ")+vol_pos+" "+
            this.TypePositionCurrentDescription()+" #"+(string)this.TicketPositionCurrent()+
            " ["+vol_ord+" "+this.TypeOrderEventDescription()+" #"+(string)this.TicketOrderEvent()+" ]"+price+sl+tp+magic
           );
        }
     }
   return head+text;
  }
//+------------------------------------------------------------------+

与此类似,修改 CEventPositionClose 类:

//+------------------------------------------------------------------+
//|                                           EventPositionClose.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 "Event.mqh"
//+------------------------------------------------------------------+
//| 开仓事件                                                           |
//+------------------------------------------------------------------+
class CEventPositionClose : public CEvent
  {
private:
//--- 创建并返回简要事件消息
   string            EventsMessage(void);  
public:
//--- 构造函数
                     CEventPositionClose(const int event_code,const ulong ticket=0) : CEvent(EVENT_STATUS_HISTORY_POSITION,event_code,ticket) {}
//--- 支持的 (1) 实数型,和 (2) 整数型订单属性
   virtual bool      SupportProperty(ENUM_EVENT_PROP_INTEGER property);
   virtual bool      SupportProperty(ENUM_EVENT_PROP_DOUBLE property);
//--- (1) 在流水日志里显示事件简要消息, (2) 发送事件至图表
   virtual void      PrintShort(void);
   virtual void      SendEvent(void);
  };
//+------------------------------------------------------------------+
//| 返回 'true' 如果事件支持所传递的                                      |
//| 整数型属性,否则,返回 'false'                                       |
//+------------------------------------------------------------------+
bool CEventPositionClose::SupportProperty(ENUM_EVENT_PROP_INTEGER property)
  {
   return true;
  }
//+------------------------------------------------------------------+
//| 返回 'true' 如果事件支持所传递的                                      |
//| 实数型属性,否则,返回 'false'                                       |
//+------------------------------------------------------------------+
bool CEventPositionClose::SupportProperty(ENUM_EVENT_PROP_DOUBLE property)
  {
   return true;
  }
//+------------------------------------------------------------------+
//| 在流水帐中显示有关事件的简要消息                                       |
//+------------------------------------------------------------------+
void CEventPositionClose::PrintShort(void)
  {
   ::Print(this.EventsMessage());
  }
//+------------------------------------------------------------------+
//| 发送事件至图表                                                       |
//+------------------------------------------------------------------+
void CEventPositionClose::SendEvent(void)
  {
   this.PrintShort();
   ::EventChartCustom(this.m_chart_id,(ushort)this.m_trade_event,this.PositionID(),this.PriceClose(),this.Symbol());
  }
//+------------------------------------------------------------------+
//| 创建并返回短事件消息                                                  |
//+------------------------------------------------------------------+
string CEventPositionClose::EventsMessage(void)
  {
//--- 事件品种报价中的小数位数
   int digits=(int)::SymbolInfoInteger(this.Symbol(),SYMBOL_DIGITS);
//--- (1) 标题, (2) 已执行订单交易量, (3) 已执行仓位量, (4) 事件价格,
//--- (5) 止损价格, (6) 止盈价格, (7) 魔幻数字, (6) 按账户货币的赢利额, (7,8) 关闭消息选项
   string head="- "+this.TypeEventDescription()+": "+TimeMSCtoString(this.TimePosition())+" -\n";
   string vol_ord=::DoubleToString(this.VolumeOrderExecuted(),DigitsLots(this.Symbol()));
   string vol_pos=::DoubleToString(this.VolumePositionExecuted(),DigitsLots(this.Symbol()));
   string price=TextByLanguage(" по цене "," at price ")+::DoubleToString(this.PriceEvent(),digits);
   string sl=(this.PriceStopLoss()>0 ? ", sl "+ ::DoubleToString(this.PriceStopLoss(),digits) : "");
   string tp=(this.PriceTakeProfit()>0 ? ", tp "+ ::DoubleToString(this.PriceTakeProfit(),digits) : "");
   string magic=(this.Magic()!=0 ? TextByLanguage(", магик ",", magic ")+(string)this.Magic() : "");
   string profit=TextByLanguage(", профит ",", profit ")+::DoubleToString(this.Profit(),this.m_digits_acc)+" "+::AccountInfoString(ACCOUNT_CURRENCY);
   string close=TextByLanguage("Закрыт ","Close ");
   string in_pos="";
   //---
   if(this.GetProperty(EVENT_PROP_TYPE_EVENT)>TRADE_EVENT_POSITION_VOLUME_ADD_BY_PENDING_PARTIAL)
     {
      close=TextByLanguage("Закрыт объём ","Closed volume ")+vol_ord;
      in_pos=TextByLanguage(" в "," in ");
     }
   string opposite=
     (
      this.IsPresentEventFlag(TRADE_EVENT_FLAG_BY_POS)   ? 
      TextByLanguage(" встречным "," by opposite ")+this.SymbolCloseBy()+" "+
      this.TypeOrderDealDescription()+" #"+(string)this.PositionByID()+(this.MagicCloseBy()> 0 ? "("+(string)this.MagicCloseBy()+" ]" : "")
                                                         : ""
     );
   //--- EURUSD: Closed 0.1 Sell #xx [0.2 SellLimit order #XX] at х.ххххх, sl х.ххххх, tp x.xxxxx, magic, profit xxxx
   string text=
     (
      this.Symbol()+" "+close+in_pos+this.TypePositionCurrentDescription()+" #"+(string)this.TicketPositionCurrent()+
      opposite+" ["+vol_ord+" "+this.TypeOrderEventDescription()+" #"+(string)this.TicketOrderEvent()+" ]"+price+sl+tp+magic+profit
     );
   return head+text;
  }
//+------------------------------------------------------------------+

为了在净持帐户里工作,新任务修改了所有事件对象类。
现在我们来处理 CEventCollection 事件集合类。

以前,CreateNewEvent() 方法(在第五部分中阐述)拥有存储交易事件代码的局部变量。
我们从新事件创建方法中删除它,并 在类的私有部分中声明它为私有类成员。 此外,为对冲净持结算帐户类型添加创建新事件的必要方法的声明,即按仓位 ID 返回所有 InOut 成交列表的方法,以及通过其 ID 获取持仓对象的方法。

//+------------------------------------------------------------------+
//| 帐户事件的集合                                                       |
//+------------------------------------------------------------------+
class CEventsCollection : public CListObj
  {
private:
   CListObj          m_list_events;                   // 事件列表
   bool              m_is_hedge;                      // 对冲账户标志
   long              m_chart_id;                      // 控制程序所在的图表 ID
   int               m_trade_event_code;              // 交易事件代码
   ENUM_TRADE_EVENT  m_trade_event;                   // 账户交易事件
   CEvent            m_event_instance;                // 按属性搜索的事件对象
   
//--- 根据订单状况创建交易事件
   void              CreateNewEvent(COrder* order,CArrayObj* list_history,CArrayObj* list_market);
//--- 为(1)对冲账户,(2)净持结算账户创建一个事件
   void              NewDealEventHedge(COrder* deal,CArrayObj* list_history,CArrayObj* list_market);
   void              NewDealEventNetto(COrder* deal,CArrayObj* list_history,CArrayObj* list_market);
//--- 选择并返回在场挂单列表
   CArrayObj*        GetListMarketPendings(CArrayObj* list);
//--- 从列表中选择并返回历史(1)已删除的挂单,(2)成交,(3)所有已平订单的列表 
   CArrayObj*        GetListHistoryPendings(CArrayObj* list);
   CArrayObj*        GetListDeals(CArrayObj* list);
   CArrayObj*        GetListCloseByOrders(CArrayObj* list);
//--- 返回(1)按其 ID 所有仓位订单,(2)按其 ID 成交的清单
//--- (3)按仓位 ID 的所有入场成交,(4)按仓位 ID 的所有离场成交,
//--- (5)按仓位 ID 的所有持仓逆转成交
   CArrayObj*        GetListAllOrdersByPosID(CArrayObj* list,const ulong position_id);
   CArrayObj*        GetListAllDealsByPosID(CArrayObj* list,const ulong position_id);
   CArrayObj*        GetListAllDealsInByPosID(CArrayObj* list,const ulong position_id);
   CArrayObj*        GetListAllDealsOutByPosID(CArrayObj* list,const ulong position_id);
   CArrayObj*        GetListAllDealsInOutByPosID(CArrayObj* list,const ulong position_id);
//--- 返回按其仓位 ID 的所有成交的(1)IN,(2)OUT 的总交易量
   double            SummaryVolumeDealsInByPosID(CArrayObj* list,const ulong position_id);
   double            SummaryVolumeDealsOutByPosID(CArrayObj* list,const ulong position_id);
//--- 返回 (1) 首笔, (2) 最后,和 (3) 从所有仓位订单列表中筛选出的平仓订单,
//--- (4) 按单据,(5)按持仓 ID,
//--- (6) 最后一笔,和 (7) 按仓位 ID 的倒数第二笔 InOut 成交
   COrder*           GetFirstOrderFromList(CArrayObj* list,const ulong position_id);
   COrder*           GetLastOrderFromList(CArrayObj* list,const ulong position_id);
   COrder*           GetCloseByOrderFromList(CArrayObj* list,const ulong position_id);
   COrder*           GetHistoryOrderByTicket(CArrayObj* list,const ulong order_ticket);
   COrder*           GetPositionByID(CArrayObj* list,const ulong position_id);
//--- 返回事件列表中事件对象存在的标志
   bool              IsPresentEventInList(CEvent* compared_event);
   
public:
//--- 从 begin_time 到 end_time 范围内的时间中选择集合中的事件
   CArrayObj        *GetListByTime(const datetime begin_time=0,const datetime end_time=0);
//--- 按“原样”返回完整的事件集合列表
   CArrayObj        *GetList(void)                                                                       { return &this.m_list_events;                                           }
//--- 返回按(1)整数型,(2)实数型,和(3)字符串型属性符合比较标准筛选出的列表
   CArrayObj        *GetList(ENUM_EVENT_PROP_INTEGER property,long value,ENUM_COMPARER_TYPE mode=EQUAL)  { return CSelect::ByEventProperty(this.GetList(),property,value,mode);  }
   CArrayObj        *GetList(ENUM_EVENT_PROP_DOUBLE property,double value,ENUM_COMPARER_TYPE mode=EQUAL) { return CSelect::ByEventProperty(this.GetList(),property,value,mode);  }
   CArrayObj        *GetList(ENUM_EVENT_PROP_STRING property,string value,ENUM_COMPARER_TYPE mode=EQUAL) { return CSelect::ByEventProperty(this.GetList(),property,value,mode);  }
//--- 更新事件列表
   void              Refresh(CArrayObj* list_history,
                             CArrayObj* list_market,
                             const bool is_history_event,
                             const bool is_market_event,
                             const int  new_history_orders,
                             const int  new_market_pendings,
                             const int  new_market_positions,
                             const int  new_deals);
//--- 设置控制程序所在的图表 ID
   void              SetChartID(const long id)        { this.m_chart_id=id;         }
//--- 返回帐户上的最后一笔交易事件
   ENUM_TRADE_EVENT  GetLastTradeEvent(void)    const { return this.m_trade_event;  }
//--- 重置最后一笔交易事件
   void              ResetLastTradeEvent(void)        { this.m_trade_event=TRADE_EVENT_NO_EVENT;   }
//--- 构造函数
                     CEventsCollection(void);
  };
//+------------------------------------------------------------------+

在类的构造函数初始化列表中重置交易事件代码

//+------------------------------------------------------------------+
//| 构造函数                                                           |
//+------------------------------------------------------------------+
CEventsCollection::CEventsCollection(void) : m_trade_event(TRADE_EVENT_NO_EVENT),m_trade_event_code(TRADE_EVENT_FLAG_NO_EVENT)
  {
   this.m_list_events.Clear();
   this.m_list_events.Sort(SORT_BY_EVENT_TIME_EVENT);
   this.m_list_events.Type(COLLECTION_EVENTS_ID);
   this.m_is_hedge=bool(::AccountInfoInteger(ACCOUNT_MARGIN_MODE)==ACCOUNT_MARGIN_MODE_RETAIL_HEDGING);
   this.m_chart_id=::ChartID();
  }
//+------------------------------------------------------------------+

净持结算账户可能会经历多次交易操作。 它可能在单一品种的一笔持仓上多次进行更改。 它们也许包括因激活较小交易量的逆向订单触发的部分平仓时的交易量变化,以及当触发相同方向的订单时的持仓量增加。


然而,最有趣的变化发生在触发具有较大交易量的逆向订单时的持仓。 在这种情况下,持仓会分配到新的票证。 票证对应于触发的订单,并且持仓类型变化为逆向仓位(持仓反转)。 持仓 ID 保持不变,等于在账户里触发首笔开仓的第一笔订单的票据。


我们需要在其整个生存周期中跟踪所有仓位方向变化,以便(1)在流水日志里正确显示仓位反转项目,以及(2)能够在我们的程序中获得持仓反转事件的数据。 为此,我们需要从 ENUM_DEAL_ENTRY 枚举中访问具有 DEAL_ENTRY_INOUT 仓位更改方法的所有成交。

在这种情况下,我们只需要在它们发生时按顺序安排此类成交,并取必要的成交。 成交本身拥有触发它的所有订单属性。

因此,如果我们有成交订单,我们可以收到一个方向改变的票证,以及触发仓位反转的订单类型,以及新的止损和止盈价位等。 我们需要得到这种通过其 ID 创建所有 InPut 成交列表的功能,以便我们开发的函数库使用时更加容易。

我们来研究通过其 ID 接收仓位的所有 InPut 成交的方法:

//+------------------------------------------------------------------+
//| 返回所有逆转成交的列表 (IN_OUT)                                      |
//| 按其仓位 ID                                                        |
//+------------------------------------------------------------------+
CArrayObj* CEventsCollection::GetListAllDealsInOutByPosID(CArrayObj *list,const ulong position_id)
  {
   if(list.Type()!=COLLECTION_HISTORY_ID)
     {
      Print(DFUN,TextByLanguage("Ошибка. Список не является списком исторической коллекции","Error. The list is not a list of the history collection"));
      return NULL;
     }
   CArrayObj* list_deals=this.GetListAllDealsByPosID(list,position_id);
   list_deals=CSelect::ByOrderProperty(list_deals,ORDER_PROP_DEAL_ENTRY,DEAL_ENTRY_INOUT,EQUAL);
   return list_deals;
  }
//+------------------------------------------------------------------+

检查传递给方法的列表的类型。 如果它不是历史订单和成交集合之一,则错误警告,并返回 NULL

我们需要在类中针对列表进行所有这些检查,以便我们自行纠错。 它们将在调试后删除,不会给计算带来不必要的检查负担。

接下来,我们按仓位 ID 接收成交列表(该方法已在前一篇文章中探讨),通过 InOut 仓位更改方法对获得的列表进行排序,并返回最终列表

若要接收持仓的数据,或定义其缺失,创建一个按其 ID 接收持仓对象的方法

//+------------------------------------------------------------------+
//| 按其 ID 返回仓位                                                    |
//+------------------------------------------------------------------+
COrder* CEventsCollection::GetPositionByID(CArrayObj *list,const ulong position_id)
  {
   if(list.Type()!=COLLECTION_MARKET_ID)
     {
      Print(DFUN,TextByLanguage("Ошибка. Список не является списком рыночной коллекции","Error. The list is not a list of the market collection"));
      return NULL;
     }
   CArrayObj* list_orders=CSelect::ByOrderProperty(list,ORDER_PROP_STATUS,ORDER_STATUS_MARKET_POSITION,EQUAL);
   list_orders=CSelect::ByOrderProperty(list_orders,ORDER_PROP_POSITION_ID,position_id,EQUAL);
   if(list_orders==NULL || list_orders.Total()==0) return NULL;
   COrder* order=list_orders.At(0);
   return(order!=NULL ? order : NULL);
  }
//+------------------------------------------------------------------+

该方法很简单,就像函数库中的其他类似方法一样。 检查所选列表的类型。 如果它不是在场订单和持仓集合的列表,则警告错误,并返回 NULL

接着,仅从传递给方法的列表中获取持仓对象,并按传递给方法的仓位 ID 对其进行排序
如果未能获得列表,或没有对象,则返回 NULL — 没有请求的仓位。
接下来,从列表中接收单个持仓对象(市场中只能有一笔指定 ID 的仓位),并返回对象本身,或当结束时有错误则为 NULL

上一篇文章中论述了创建新 CreateNewEvent() 事件对象的方法
在此我只展示实现的变化。
以下局部变量已从方法中删除

int trade_event_code

它在我们所创建类的私有部分中已变为类成员

方法逻辑保持不变,但现在它还有能力调用必要方法处理我们正在工作的帐户类型。 如果是对冲账户,会调用对冲账户创建新事件的方法。 否则, 使用为净持结算帐户创建新事件的方法

//+------------------------------------------------------------------+
//| 根据订单状况创建交易事件                                               |
//+------------------------------------------------------------------+
void CEventsCollection::CreateNewEvent(COrder* order,CArrayObj* list_history,CArrayObj* list_market)
  {
   this.m_trade_event_code=TRADE_EVENT_FLAG_NO_EVENT;
   ENUM_ORDER_STATUS status=order.Status();
//--- 下挂单
   if(status==ORDER_STATUS_MARKET_PENDING)
     {
      this.m_trade_event_code=TRADE_EVENT_FLAG_ORDER_PLASED;
      CEvent* event=new CEventOrderPlased(this.m_trade_event_code,order.Ticket());
      if(event!=NULL)
        {
         event.SetProperty(EVENT_PROP_TIME_EVENT,order.TimeOpenMSC());                             // 事件时间
         event.SetProperty(EVENT_PROP_REASON_EVENT,EVENT_REASON_DONE);                             // 事件原因 (来自 ENUM_EVENT_REASON 枚举)
         event.SetProperty(EVENT_PROP_TYPE_DEAL_EVENT,order.TypeOrder());                          // 事件成交类型
         event.SetProperty(EVENT_PROP_TICKET_DEAL_EVENT,order.Ticket());                           // 事件单据
         event.SetProperty(EVENT_PROP_TYPE_ORDER_EVENT,order.TypeOrder());                         // 事件订单类型
         event.SetProperty(EVENT_PROP_TYPE_ORDER_POSITION,order.TypeOrder());                      // 事件仓位类型
         event.SetProperty(EVENT_PROP_TICKET_ORDER_EVENT,order.Ticket());                          // 事件单据
         event.SetProperty(EVENT_PROP_TICKET_ORDER_POSITION,order.Ticket());                       // 单据
         event.SetProperty(EVENT_PROP_POSITION_ID,order.PositionID());                             // 仓位 ID
         event.SetProperty(EVENT_PROP_POSITION_BY_ID,order.PositionByID());                        // 逆向仓位 ID
         event.SetProperty(EVENT_PROP_MAGIC_BY_ID,order.Magic());                                  // 逆向仓位魔幻数字
            
         event.SetProperty(EVENT_PROP_TYPE_ORD_POS_BEFORE,order.TypeOrder());                      // 方向改变之前仓位订单类型
         event.SetProperty(EVENT_PROP_TICKET_ORD_POS_BEFORE,order.Ticket());                       // 方向改变之前仓位订单票据
         event.SetProperty(EVENT_PROP_TYPE_ORD_POS_CURRENT,order.TypeOrder());                     // 当前仓位订单类型
         event.SetProperty(EVENT_PROP_TICKET_ORD_POS_CURRENT,order.Ticket());                      // 当前仓位订单票据
            
         event.SetProperty(EVENT_PROP_MAGIC_ORDER,order.Magic());                                  // 订单魔幻数字
         event.SetProperty(EVENT_PROP_TIME_ORDER_POSITION,order.TimeOpenMSC());                    // 订单时间
         event.SetProperty(EVENT_PROP_PRICE_EVENT,order.PriceOpen());                              // 事件价格
         event.SetProperty(EVENT_PROP_PRICE_OPEN,order.PriceOpen());                               // 订单价格
         event.SetProperty(EVENT_PROP_PRICE_CLOSE,order.PriceClose());                             // 平单价格
         event.SetProperty(EVENT_PROP_PRICE_SL,order.StopLoss());                                  // 止损单价格
         event.SetProperty(EVENT_PROP_PRICE_TP,order.TakeProfit());                                // 止盈单价格
         event.SetProperty(EVENT_PROP_VOLUME_ORDER_INITIAL,order.Volume());                        // 请求的订单交易量
         event.SetProperty(EVENT_PROP_VOLUME_ORDER_EXECUTED,order.Volume()-order.VolumeCurrent()); // 执行的订单交易量
         event.SetProperty(EVENT_PROP_VOLUME_ORDER_CURRENT,order.VolumeCurrent());                 // 剩余 (未执行) 订单交易量
         event.SetProperty(EVENT_PROP_VOLUME_POSITION_EXECUTED,0);                                 // 已执行仓量
         event.SetProperty(EVENT_PROP_PROFIT,order.Profit());                                      // 盈利
         event.SetProperty(EVENT_PROP_SYMBOL,order.Symbol());                                      // 订单品种
         event.SetProperty(EVENT_PROP_SYMBOL_BY_ID,order.Symbol());                                // 逆向仓位品种
         //--- 设置控制程序所在图表 ID,解码事件代码,并设置事件类型
         event.SetChartID(this.m_chart_id);
         event.SetTypeEvent();
         //--- 如果事件对象不在列表中,则添加它
         if(!this.IsPresentEventInList(event))
           {
            this.m_list_events.InsertSort(event);
            //--- 发送有关事件的消息,并设置最后交易事件的数值
            event.SendEvent();
            this.m_trade_event=event.TradeEvent();
           }
         //--- 如果事件已存在于列表中,则删除新的事件对象,并显示调试消息
         else
           {
            ::Print(DFUN_ERR_LINE,TextByLanguage("Такое событие уже есть в списке","This event already in the list."));
            delete event;
           }
        }
     }
//--- 挂单已删除
   if(status==ORDER_STATUS_HISTORY_PENDING)
     {
      this.m_trade_event_code=TRADE_EVENT_FLAG_ORDER_REMOVED;
      CEvent* event=new CEventOrderRemoved(this.m_trade_event_code,order.Ticket());
      if(event!=NULL)
        {
         ENUM_EVENT_REASON reason=
           (
            order.State()==ORDER_STATE_CANCELED ? EVENT_REASON_CANCEL :
            order.State()==ORDER_STATE_EXPIRED  ? EVENT_REASON_EXPIRED : EVENT_REASON_DONE
           );
         event.SetProperty(EVENT_PROP_TIME_EVENT,order.TimeCloseMSC());                            // 事件时间
         event.SetProperty(EVENT_PROP_REASON_EVENT,reason);                                        // 事件原因 (来自 ENUM_EVENT_REASON 枚举)
         event.SetProperty(EVENT_PROP_TYPE_DEAL_EVENT,order.TypeOrder());                          // 事件成交类型
         event.SetProperty(EVENT_PROP_TICKET_DEAL_EVENT,order.Ticket());                           // 事件单据
         event.SetProperty(EVENT_PROP_TYPE_ORDER_EVENT,order.TypeOrder());                         // 成交事件所基于的订单类型(最后的开仓订单)
         event.SetProperty(EVENT_PROP_TYPE_ORDER_POSITION,order.TypeOrder());                      // 成交事件所基于的订单类型(首笔开仓订单)
         event.SetProperty(EVENT_PROP_TICKET_ORDER_EVENT,order.Ticket());                          // 成交事件所基于的订单票据(最后的开仓订单)
         event.SetProperty(EVENT_PROP_TICKET_ORDER_POSITION,order.Ticket());                       // 成交事件所基于的订单票据(首笔开仓订单)
         event.SetProperty(EVENT_PROP_POSITION_ID,order.PositionID());                             // 仓位 ID
         event.SetProperty(EVENT_PROP_POSITION_BY_ID,order.PositionByID());                        // 逆向仓位 ID
         event.SetProperty(EVENT_PROP_MAGIC_BY_ID,order.Magic());                                  // 逆向仓位魔幻数字
            
         event.SetProperty(EVENT_PROP_TYPE_ORD_POS_BEFORE,order.TypeOrder());                      // 方向改变之前仓位订单类型
         event.SetProperty(EVENT_PROP_TICKET_ORD_POS_BEFORE,order.Ticket());                       // 方向改变之前仓位订单票据
         event.SetProperty(EVENT_PROP_TYPE_ORD_POS_CURRENT,order.TypeOrder());                     // 当前仓位订单类型
         event.SetProperty(EVENT_PROP_TICKET_ORD_POS_CURRENT,order.Ticket());                      // 当前仓位订单票据
            
         event.SetProperty(EVENT_PROP_MAGIC_ORDER,order.Magic());                                  // 订单魔幻数字
         event.SetProperty(EVENT_PROP_TIME_ORDER_POSITION,order.TimeOpenMSC());                    // 开仓成交事件所基于的订单时间(首笔开仓订单)
         event.SetProperty(EVENT_PROP_PRICE_EVENT,order.PriceOpen());                              // 事件价格
         event.SetProperty(EVENT_PROP_PRICE_OPEN,order.PriceOpen());                               // 开单价格
         event.SetProperty(EVENT_PROP_PRICE_CLOSE,order.PriceClose());                             // 平单价格
         event.SetProperty(EVENT_PROP_PRICE_SL,order.StopLoss());                                  // 止损单价格
         event.SetProperty(EVENT_PROP_PRICE_TP,order.TakeProfit());                                // 止盈单价格
         event.SetProperty(EVENT_PROP_VOLUME_ORDER_INITIAL,order.Volume());                        // 请求的订单交易量
         event.SetProperty(EVENT_PROP_VOLUME_ORDER_EXECUTED,order.Volume()-order.VolumeCurrent()); // 执行的订单交易量
         event.SetProperty(EVENT_PROP_VOLUME_ORDER_CURRENT,order.VolumeCurrent());                 // 剩余 (未执行) 订单交易量
         event.SetProperty(EVENT_PROP_VOLUME_POSITION_EXECUTED,0);                                 // 已执行仓量
         event.SetProperty(EVENT_PROP_PROFIT,order.Profit());                                      // 盈利
         event.SetProperty(EVENT_PROP_SYMBOL,order.Symbol());                                      // 订单品种
         event.SetProperty(EVENT_PROP_SYMBOL_BY_ID,order.Symbol());                                // 逆向仓位品种
         //--- 设置控制程序所在图表 ID,解码事件代码,并设置事件类型
         event.SetChartID(this.m_chart_id);
         event.SetTypeEvent();
         //--- 添加事件对象(如果列表中不存在)
         if(!this.IsPresentEventInList(event))
           {
            this.m_list_events.InsertSort(event);
            //--- 发送有关该事件的消息,并设置最后一个交易事件的数值
            event.SendEvent();
            this.m_trade_event=event.TradeEvent();
           }
         //---  如果事件已在列表中,则删除新事件对象,并显示调试消息
         else
           {
            ::Print(DFUN_ERR_LINE,TextByLanguage("Такое событие уже есть в списке","This event already in the list."));
            delete event;
           }
        }
     }
//--- 开仓 (__MQL4__)
   if(status==ORDER_STATUS_MARKET_POSITION)
     {
      this.m_trade_event_code=TRADE_EVENT_FLAG_POSITION_OPENED;
      CEvent* event=new CEventPositionOpen(this.m_trade_event_code,order.Ticket());
      if(event!=NULL)
        {
         event.SetProperty(EVENT_PROP_TIME_EVENT,order.TimeOpen());                                // 事件时间
         event.SetProperty(EVENT_PROP_REASON_EVENT,EVENT_REASON_DONE);                             // 事件原因 (来自 ENUM_EVENT_REASON 枚举)
         event.SetProperty(EVENT_PROP_TYPE_DEAL_EVENT,order.TypeOrder());                          // 事件成交类型
         event.SetProperty(EVENT_PROP_TICKET_DEAL_EVENT,order.Ticket());                           // 事件成交单据
         event.SetProperty(EVENT_PROP_TYPE_ORDER_EVENT,order.TypeOrder());                         // 成交事件所基于的订单类型(最后的开仓订单)
         event.SetProperty(EVENT_PROP_TYPE_ORDER_POSITION,order.TypeOrder());                      // 成交事件所基于的订单类型(首笔开仓订单)
         event.SetProperty(EVENT_PROP_TICKET_ORDER_EVENT,order.Ticket());                          // 成交事件所基于的订单票据(最后的开仓订单)
         event.SetProperty(EVENT_PROP_TICKET_ORDER_POSITION,order.Ticket());                       // 成交事件所基于的订单票据(首笔开仓订单)
         event.SetProperty(EVENT_PROP_POSITION_ID,order.PositionID());                             // 仓位 ID
         event.SetProperty(EVENT_PROP_POSITION_BY_ID,order.PositionByID());                        // 逆向仓位 ID
         event.SetProperty(EVENT_PROP_MAGIC_BY_ID,order.Magic());                                  // 逆向仓位魔幻数字
            
         event.SetProperty(EVENT_PROP_TYPE_ORD_POS_BEFORE,order.TypeOrder());                      // 方向改变之前仓位订单类型
         event.SetProperty(EVENT_PROP_TICKET_ORD_POS_BEFORE,order.Ticket());                       // 方向改变之前仓位订单票据
         event.SetProperty(EVENT_PROP_TYPE_ORD_POS_CURRENT,order.TypeOrder());                     // 当前仓位订单类型
         event.SetProperty(EVENT_PROP_TICKET_ORD_POS_CURRENT,order.Ticket());                      // 当前仓位订单票据
            
         event.SetProperty(EVENT_PROP_MAGIC_ORDER,order.Magic());                                  // 订单/成交/仓位魔幻数字
         event.SetProperty(EVENT_PROP_TIME_ORDER_POSITION,order.TimeOpen());                       // 触发开仓成交的订单时间(首笔开仓订单)
         event.SetProperty(EVENT_PROP_PRICE_EVENT,order.PriceOpen());                              // 事件价格
         event.SetProperty(EVENT_PROP_PRICE_OPEN,order.PriceOpen());                               // 订单/成交/仓位开盘价
         event.SetProperty(EVENT_PROP_PRICE_CLOSE,order.PriceClose());                             // 订单/成交/仓位收盘价
         event.SetProperty(EVENT_PROP_PRICE_SL,order.StopLoss());                                  // 仓位止损价
         event.SetProperty(EVENT_PROP_PRICE_TP,order.TakeProfit());                                // 仓位止盈价
         event.SetProperty(EVENT_PROP_VOLUME_ORDER_INITIAL,order.Volume());                        // 请求的订单交易量
         event.SetProperty(EVENT_PROP_VOLUME_ORDER_EXECUTED,order.Volume()-order.VolumeCurrent()); // 执行的订单交易量
         event.SetProperty(EVENT_PROP_VOLUME_ORDER_CURRENT,order.VolumeCurrent());                 // 剩余 (未执行) 订单交易量
         event.SetProperty(EVENT_PROP_VOLUME_POSITION_EXECUTED,order.Volume());                    // 已执行的仓量
         event.SetProperty(EVENT_PROP_PROFIT,order.Profit());                                      // 盈利
         event.SetProperty(EVENT_PROP_SYMBOL,order.Symbol());                                      // 订单品种
         event.SetProperty(EVENT_PROP_SYMBOL_BY_ID,order.Symbol());                                // 逆向仓位品种
         //--- 设置控制程序所在图表 ID,解码事件代码,并设置事件类型
         event.SetChartID(this.m_chart_id);
         event.SetTypeEvent();
         //--- 添加事件对象(如果列表中不存在)
         if(!this.IsPresentEventInList(event))
           {
            this.m_list_events.InsertSort(event);
            //--- 发送有关事件的消息,并设置最后交易事件的数值
            event.SendEvent();
            this.m_trade_event=event.TradeEvent();
           }
         //--- 如果事件已存在于列表中,则删除新的事件对象,并显示调试消息
         else
           {
            ::Print(DFUN_ERR_LINE,TextByLanguage("Такое событие уже есть в списке","This event already in the list."));
            delete event;
           }
        }
     }
//--- 新成交 (__MQL5__)
   if(status==ORDER_STATUS_DEAL)
     {
      //--- 新的余额操作
      if((ENUM_DEAL_TYPE)order.TypeOrder()>DEAL_TYPE_SELL)
        {
         this.m_trade_event_code=TRADE_EVENT_FLAG_ACCOUNT_BALANCE;
         CEvent* event=new CEventBalanceOperation(this.m_trade_event_code,order.Ticket());
         if(event!=NULL)
           {
            ENUM_EVENT_REASON reason=
              (
               (ENUM_DEAL_TYPE)order.TypeOrder()==DEAL_TYPE_BALANCE ? (order.Profit()>0 ? EVENT_REASON_BALANCE_REFILL : EVENT_REASON_BALANCE_WITHDRAWAL) :
               (ENUM_EVENT_REASON)(order.TypeOrder()+REASON_EVENT_SHIFT)
              );
            event.SetProperty(EVENT_PROP_TIME_EVENT,order.TimeOpenMSC());                 // Event time
            event.SetProperty(EVENT_PROP_REASON_EVENT,reason);                            // 事件原因 (来自 ENUM_EVENT_REASON 枚举)
            event.SetProperty(EVENT_PROP_TYPE_DEAL_EVENT,order.TypeOrder());              // 事件成交类型
            event.SetProperty(EVENT_PROP_TICKET_DEAL_EVENT,order.Ticket());               // 事件单据
            event.SetProperty(EVENT_PROP_TYPE_ORDER_EVENT,order.TypeOrder());             // 触发成交事件所基于的订单类型(最后的开仓订单)
            event.SetProperty(EVENT_PROP_TYPE_ORDER_POSITION,order.TypeOrder());          // 触发成交事件所基于的订单类型(首笔开仓订单)
            event.SetProperty(EVENT_PROP_TICKET_ORDER_EVENT,order.Ticket());              // 触发成交事件所基于的单据(最后的开仓订单)
            event.SetProperty(EVENT_PROP_TICKET_ORDER_POSITION,order.Ticket());           // 触发成交事件所基于的单据(首笔开仓订单)
            event.SetProperty(EVENT_PROP_POSITION_ID,order.PositionID());                 // 仓位 ID
            event.SetProperty(EVENT_PROP_POSITION_BY_ID,order.PositionByID());            // 逆向仓位 ID
            event.SetProperty(EVENT_PROP_MAGIC_BY_ID,order.Magic());                      // 逆向仓位魔幻数字
            
            event.SetProperty(EVENT_PROP_TYPE_ORD_POS_BEFORE,order.TypeOrder());          // 方向改变之前仓位订单类型
            event.SetProperty(EVENT_PROP_TICKET_ORD_POS_BEFORE,order.Ticket());           // 方向改变之前仓位单据
            event.SetProperty(EVENT_PROP_TYPE_ORD_POS_CURRENT,order.TypeOrder());         // 当前仓位订单类型
            event.SetProperty(EVENT_PROP_TICKET_ORD_POS_CURRENT,order.Ticket());          // 当前仓位单据
            
            event.SetProperty(EVENT_PROP_MAGIC_ORDER,order.Magic());                      // 订单/成交/仓位魔幻数字
            event.SetProperty(EVENT_PROP_TIME_ORDER_POSITION,order.TimeOpenMSC());        // 触发开仓成交的订单时间(首笔开仓订单)
            event.SetProperty(EVENT_PROP_PRICE_EVENT,order.PriceOpen());                  // 事件价格
            event.SetProperty(EVENT_PROP_PRICE_OPEN,order.PriceOpen());                   // 订单/成交/仓位开盘价
            event.SetProperty(EVENT_PROP_PRICE_CLOSE,order.PriceOpen());                  // 订单/成交/仓位收盘价
            event.SetProperty(EVENT_PROP_PRICE_SL,0);                                     // 成交止损价
            event.SetProperty(EVENT_PROP_PRICE_TP,0);                                     // 成交止盈价
            event.SetProperty(EVENT_PROP_VOLUME_ORDER_INITIAL,order.Volume());            // 请求的成交量
            event.SetProperty(EVENT_PROP_VOLUME_ORDER_EXECUTED,order.Volume());           // 已执行的成交量
            event.SetProperty(EVENT_PROP_VOLUME_ORDER_CURRENT,0);                         // 剩余 (未执行) 成交量
            event.SetProperty(EVENT_PROP_VOLUME_POSITION_EXECUTED,order.Volume());        // 已执行的仓量
            event.SetProperty(EVENT_PROP_PROFIT,order.Profit());                          // 盈利
            event.SetProperty(EVENT_PROP_SYMBOL,order.Symbol());                          // 订单品种
            event.SetProperty(EVENT_PROP_SYMBOL_BY_ID,order.Symbol());                    // 逆向仓位品种
            //--- 设置控制程序所在图表 ID,解码事件代码,并设置事件类型
            event.SetChartID(this.m_chart_id);
            event.SetTypeEvent();
            //--- 如果事件对象不在列表中,则添加它
            if(!this.IsPresentEventInList(event))
              {
               //--- 发送有关事件的消息,并设置最后交易事件的数值
               this.m_list_events.InsertSort(event);
               event.SendEvent();
               this.m_trade_event=event.TradeEvent();
              }
            //--- 如果事件已存在于列表中,则删除新的事件对象,并显示调试消息
            else
              {
               ::Print(DFUN_ERR_LINE,TextByLanguage("Такое событие уже есть в списке","This event already in the list."));
               delete event;
              }
           }
        }
      //--- 如果这不是余额操作
      else
        {
         if(this.m_is_hedge)
            this.NewDealEventHedge(order,list_history,list_market);
         else
            this.NewDealEventNetto(order,list_history,list_market);
        }
     }
  }
//+------------------------------------------------------------------+

为对冲账户创建新事件的方法:

//+------------------------------------------------------------------+
//| 创建对冲帐户事件                                                    |
//+------------------------------------------------------------------+
void CEventsCollection::NewDealEventHedge(COrder* deal,CArrayObj* list_history,CArrayObj* list_market)
  {
   //--- 入场
   if(deal.GetProperty(ORDER_PROP_DEAL_ENTRY)==DEAL_ENTRY_IN)
     {
      this.m_trade_event_code=TRADE_EVENT_FLAG_POSITION_OPENED;
      int reason=EVENT_REASON_DONE;
      //--- 在持仓方向搜索所有开仓成交,并计算其总交易量
      double volume_in=this.SummaryVolumeDealsInByPosID(list_history,deal.PositionID());
      //--- 从所有仓位订单列表中获取成交订单,和最后一笔开仓订单
      ulong order_ticket=deal.GetProperty(ORDER_PROP_DEAL_ORDER_TICKET);
      COrder* order_first=this.GetHistoryOrderByTicket(list_history,order_ticket);
      COrder* order_last=this.GetLastOrderFromList(list_history,deal.PositionID());
      //--- 按票证取得开仓
      COrder* position=this.GetPositionByID(list_market,deal.PositionID());
      double vol_position=(position!=NULL ? position.Volume() : 0);
      //--- 如果没有最后一笔订单,则首笔和最后一笔仓位订单重合
      if(order_last==NULL)
         order_last=order_first;
      if(order_first!=NULL)
        {
         //--- 如果开立了部分订单交易量,则这是部分执行
         if(this.SummaryVolumeDealsInByPosID(list_history,deal.PositionID())<order_first.Volume())
           {
            this.m_trade_event_code+=TRADE_EVENT_FLAG_PARTIAL;
            reason=EVENT_REASON_DONE_PARTIALLY;
           }
         //--- 如果开单原是挂单,则激活挂订单
         if(order_first.TypeOrder()>ORDER_TYPE_SELL && order_first.TypeOrder()<ORDER_TYPE_CLOSE_BY)
           {
            this.m_trade_event_code+=TRADE_EVENT_FLAG_ORDER_ACTIVATED;
            //--- 如果部分执行订单,则将部分订单执行设置为事件原因
            reason=
              (this.SummaryVolumeDealsInByPosID(list_history,deal.PositionID())<order_first.Volume() ? 
               EVENT_REASON_ACTIVATED_PENDING_PARTIALLY : 
               EVENT_REASON_ACTIVATED_PENDING
              );
           }
         CEvent* event=new CEventPositionOpen(this.m_trade_event_code,deal.PositionID());
         if(event!=NULL)
           {
            event.SetProperty(EVENT_PROP_TIME_EVENT,deal.TimeOpenMSC());                        // 事件时间 (开仓时间)
            event.SetProperty(EVENT_PROP_REASON_EVENT,reason);                                  // 事件原因 (来自 ENUM_EVENT_REASON 枚举)
            event.SetProperty(EVENT_PROP_TYPE_DEAL_EVENT,deal.TypeOrder());                     // 事件成交类型
            event.SetProperty(EVENT_PROP_TICKET_DEAL_EVENT,deal.Ticket());                      // 事件成交单据
            event.SetProperty(EVENT_PROP_TYPE_ORDER_POSITION,order_first.TypeOrder());          // 触发成交事件所基于的订单类型(首笔开仓订单)
            event.SetProperty(EVENT_PROP_TICKET_ORDER_POSITION,order_first.Ticket());           // 触发成交事件所基于的单据(首笔开仓订单)
            event.SetProperty(EVENT_PROP_TYPE_ORDER_EVENT,order_last.TypeOrder());              // 触发成交事件所基于的订单类型(末笔开仓订单)
            event.SetProperty(EVENT_PROP_TICKET_ORDER_EVENT,order_last.Ticket());               // 触发成交事件所基于的单据(末笔开仓订单)
            event.SetProperty(EVENT_PROP_POSITION_ID,deal.PositionID());                        // 仓位 ID
            event.SetProperty(EVENT_PROP_POSITION_BY_ID,order_last.PositionByID());             // 逆向仓位 ID
            //---
            event.SetProperty(EVENT_PROP_MAGIC_BY_ID,deal.Magic());                             // 逆向仓位魔幻数字
            event.SetProperty(EVENT_PROP_TYPE_ORD_POS_BEFORE,order_first.TypeOrder());          // 方向改变之前仓位订单类型
            event.SetProperty(EVENT_PROP_TICKET_ORD_POS_BEFORE,order_first.Ticket());           // 方向改变之前仓位单据
            event.SetProperty(EVENT_PROP_TYPE_ORD_POS_CURRENT,order_first.TypeOrder());         // 当前仓位订单类型
            event.SetProperty(EVENT_PROP_TICKET_ORD_POS_CURRENT,order_first.Ticket());          // 当前仓位单据
            event.SetProperty(EVENT_PROP_SYMBOL_BY_ID,deal.Symbol());                           // 逆向仓位品种
            //---
            event.SetProperty(EVENT_PROP_MAGIC_ORDER,deal.Magic());                             // 订单/成交/仓位魔幻数字
            event.SetProperty(EVENT_PROP_TIME_ORDER_POSITION,order_first.TimeOpenMSC());        // 触发开仓成交的订单时间(首笔开仓订单)
            event.SetProperty(EVENT_PROP_PRICE_EVENT,deal.PriceOpen());                         // 事件价格 (开仓价格)
            event.SetProperty(EVENT_PROP_PRICE_OPEN,order_first.PriceOpen());                   // 开单价格 (开仓订单价格)
            event.SetProperty(EVENT_PROP_PRICE_CLOSE,order_last.PriceClose());                  // 开单价格 (平仓订单价格)
            event.SetProperty(EVENT_PROP_PRICE_SL,order_first.StopLoss());                      // 止损价格 (仓位的止损订单价格)
            event.SetProperty(EVENT_PROP_PRICE_TP,order_first.TakeProfit());                    // 止盈价格 (仓位的止盈订单价格)
            event.SetProperty(EVENT_PROP_VOLUME_ORDER_INITIAL,order_first.Volume());                                 // 请求的订单交易量
            event.SetProperty(EVENT_PROP_VOLUME_ORDER_EXECUTED,(order_first.Volume()-order_first.VolumeCurrent()));  // 执行的订单交易量
            event.SetProperty(EVENT_PROP_VOLUME_ORDER_CURRENT,order_first.VolumeCurrent());                          // 剩余 (未执行) 订单交易量
            event.SetProperty(EVENT_PROP_VOLUME_POSITION_EXECUTED,vol_position);                                     // 已执行的仓量
            event.SetProperty(EVENT_PROP_PROFIT,deal.ProfitFull());                             // 盈利
            event.SetProperty(EVENT_PROP_SYMBOL,deal.Symbol());                                 // 订单品种
            //--- 设置控制程序所在图表 ID,解码事件代码,并设置事件类型
            event.SetChartID(this.m_chart_id);
            event.SetTypeEvent();
            //--- 如果事件对象不在列表中,则添加它
            if(!this.IsPresentEventInList(event))
              {
               this.m_list_events.InsertSort(event);
               //--- 发送有关事件的消息,并设置最后交易事件的数值
               event.SendEvent();
               this.m_trade_event=event.TradeEvent();
              }
            //--- 如果事件已存在于列表中,则删除新的事件对象,并显示调试消息
            else
              {
               ::Print(DFUN_ERR_LINE,TextByLanguage("Такое событие уже есть в списке","This event already in the list."));
               delete event;
              }
           }
        }
     }
   //--- 离场
   else if(deal.GetProperty(ORDER_PROP_DEAL_ENTRY)==DEAL_ENTRY_OUT)
     {
      this.m_trade_event_code=TRADE_EVENT_FLAG_POSITION_CLOSED;
      int reason=EVENT_REASON_DONE;
      //--- 从所有开仓订单列表中取得首笔和末笔开仓订单
      COrder* order_first=this.GetFirstOrderFromList(list_history,deal.PositionID());
      COrder* order_last=this.GetLastOrderFromList(list_history,deal.PositionID());
      //--- 按票证取得开仓
      COrder* position=this.GetPositionByID(list_market,deal.PositionID());
      double vol_position=(position!=NULL ? position.Volume() : 0);
      if(order_first!=NULL && order_last!=NULL)
        {
         //--- 在开仓和平仓方向上搜索所有仓位成交,并计算其总仓量
         double volume_in=this.SummaryVolumeDealsInByPosID(list_history,deal.PositionID());
         double volume_out=this.SummaryVolumeDealsOutByPosID(list_history,deal.PositionID());
         //--- 计算当前平仓的交易量
         int dgl=(int)DigitsLots(deal.Symbol());
         double volume_current=::NormalizeDouble(volume_in-volume_out,dgl);
         //--- 如果订单交易量仅部分平仓,则这是部分执行
         if(volume_current>0)
           {
            this.m_trade_event_code+=TRADE_EVENT_FLAG_PARTIAL;
           }
         //--- 如果平单部分执行,则将部分订单执行设置为事件原因
         if(order_last.VolumeCurrent()>0)
           {
            reason=EVENT_REASON_DONE_PARTIALLY;
           }
         //--- 如果平仓标志设置为止损,则由止损执行平仓
         //--- 如果止损单部分执行,则设置止损部分平仓原因
         if(order_last.IsCloseByStopLoss())
           {
            this.m_trade_event_code+=TRADE_EVENT_FLAG_SL;
            reason=(order_last.VolumeCurrent()>0 ? EVENT_REASON_DONE_SL_PARTIALLY : EVENT_REASON_DONE_SL);
           }
         //--- 如果平仓标志设置为止盈,则由止盈执行平仓
         //--- 如果止盈单部分执行,则设置止盈部分平仓原因
         else if(order_last.IsCloseByTakeProfit())
           {
            this.m_trade_event_code+=TRADE_EVENT_FLAG_TP;
            reason=(order_last.VolumeCurrent()>0 ? EVENT_REASON_DONE_TP_PARTIALLY : EVENT_REASON_DONE_TP);
           }
         //---
         CEvent* event=new CEventPositionClose(this.m_trade_event_code,deal.PositionID());
         if(event!=NULL)
           {
            event.SetProperty(EVENT_PROP_TIME_EVENT,deal.TimeOpenMSC());                        // 事件时间 (开仓时间)
            event.SetProperty(EVENT_PROP_REASON_EVENT,reason);                                  // 事件原因 (来自 ENUM_EVENT_REASON 枚举)
            event.SetProperty(EVENT_PROP_TYPE_DEAL_EVENT,deal.TypeOrder());                     // 事件成交类型
            event.SetProperty(EVENT_PROP_TICKET_DEAL_EVENT,deal.Ticket());                      // 事件成交单据
            event.SetProperty(EVENT_PROP_TYPE_ORDER_POSITION,order_first.TypeOrder());          // 触发成交事件所基于的订单类型(首笔开仓订单)
            event.SetProperty(EVENT_PROP_TYPE_ORDER_EVENT,order_last.TypeOrder());              // 触发成交事件所基于的订单类型(末笔开仓订单)
            event.SetProperty(EVENT_PROP_TICKET_ORDER_POSITION,order_first.Ticket());           // 触发成交事件所基于的单据(首笔开仓订单)
            event.SetProperty(EVENT_PROP_TICKET_ORDER_EVENT,order_last.Ticket());               // 触发成交事件所基于的单据(末笔开仓订单)
            event.SetProperty(EVENT_PROP_POSITION_ID,deal.PositionID());                        // 仓位 ID
            event.SetProperty(EVENT_PROP_POSITION_BY_ID,order_last.PositionByID());             // 逆向仓位 ID
            //---
            event.SetProperty(EVENT_PROP_MAGIC_BY_ID,order_last.Magic());                       // 逆向仓位魔幻数字
            event.SetProperty(EVENT_PROP_TYPE_ORD_POS_BEFORE,order_first.TypeOrder());          // 方向改变之前仓位订单类型
            event.SetProperty(EVENT_PROP_TICKET_ORD_POS_BEFORE,order_first.Ticket());           // 方向改变之前仓位单据
            event.SetProperty(EVENT_PROP_TYPE_ORD_POS_CURRENT,order_first.TypeOrder());         // 当前仓位订单类型
            event.SetProperty(EVENT_PROP_TICKET_ORD_POS_CURRENT,order_first.Ticket());          // 当前仓位单据
            event.SetProperty(EVENT_PROP_SYMBOL_BY_ID,order_last.Symbol());                     // 逆向仓位品种
            //---
            event.SetProperty(EVENT_PROP_MAGIC_ORDER,deal.Magic());                             // 订单/成交/仓位魔幻数字
            event.SetProperty(EVENT_PROP_TIME_ORDER_POSITION,order_first.TimeOpenMSC());        // 触发开仓成交的订单时间(首笔开仓订单)
            event.SetProperty(EVENT_PROP_PRICE_EVENT,deal.PriceOpen());                         // 事件价格 (开仓价格)
            event.SetProperty(EVENT_PROP_PRICE_OPEN,order_first.PriceOpen());                   // 开单价格 (开仓订单价格)
            event.SetProperty(EVENT_PROP_PRICE_CLOSE,order_last.PriceClose());                  // 平单价格  (仓位的末笔平单价格)
            event.SetProperty(EVENT_PROP_PRICE_SL,order_first.StopLoss());                      // 止损价格 (仓位的止损订单价格)
            event.SetProperty(EVENT_PROP_PRICE_TP,order_first.TakeProfit());                    // 止盈价格 (仓位的止盈订单价格)
            event.SetProperty(EVENT_PROP_VOLUME_ORDER_INITIAL,order_last.Volume());                               // 请求的订单交易量
            event.SetProperty(EVENT_PROP_VOLUME_ORDER_EXECUTED,order_last.Volume()-order_last.VolumeCurrent());   // 执行的订单交易量
            event.SetProperty(EVENT_PROP_VOLUME_ORDER_CURRENT,order_last.VolumeCurrent());                        // 剩余 (未执行) 订单交易量
            event.SetProperty(EVENT_PROP_VOLUME_POSITION_EXECUTED,vol_position);                                  // 剩余 (当前) 持仓量
            //---
            event.SetProperty(EVENT_PROP_PROFIT,deal.ProfitFull());                             // 盈利
            event.SetProperty(EVENT_PROP_SYMBOL,deal.Symbol());                                 // 订单品种
            //--- 设置控制程序所在图表 ID,解码事件代码,并设置事件类型
            event.SetChartID(this.m_chart_id);
            event.SetTypeEvent();
            //--- 如果事件对象不在列表中,则添加它
            if(!this.IsPresentEventInList(event))
              {
               this.m_list_events.InsertSort(event);
               //--- 发送有关事件的消息,并设置最后交易事件的数值
               event.SendEvent();
               this.m_trade_event=event.TradeEvent();
              }
            //--- 如果事件已存在于列表中,则删除新的事件对象,并显示调试消息
            else
              {
               ::Print(DFUN_ERR_LINE,TextByLanguage("Такое событие уже есть в списке","This event already in the list."));
               delete event;
              }
           }
        }
     }
   //--- 逆行仓位
   else if(deal.GetProperty(ORDER_PROP_DEAL_ENTRY)==DEAL_ENTRY_OUT_BY)
     {
      this.m_trade_event_code=TRADE_EVENT_FLAG_POSITION_CLOSED;
      int reason=EVENT_REASON_DONE_BY_POS;
      //--- 从所有开仓订单列表中取得首笔和末笔开仓订单
      COrder* order_first=this.GetFirstOrderFromList(list_history,deal.PositionID());
      COrder* order_close=this.GetCloseByOrderFromList(list_history,deal.PositionID());
      //--- 按 ID 获取开仓
      COrder* position=this.GetPositionByID(list_market,order_first.PositionID());
      double vol_position=(position!=NULL ? position.Volume() : 0);
      if(order_first!=NULL && order_close!=NULL)
        {
         //--- 添加由逆向仓位平仓的标志
         this.m_trade_event_code+=TRADE_EVENT_FLAG_BY_POS;
      
         //--- 取平仓的第一笔订单
         Print(DFUN,"PositionByID=",order_close.PositionByID());
         CArrayObj* list_close_by=this.GetListAllOrdersByPosID(list_history,order_close.PositionByID());
         COrder* order_close_by=list_close_by.At(0);
         if(order_close_by==NULL)
            return;
         //--- 在开仓和平仓的方向上搜索所有平仓成交,并计算其总交易量
         double volume_in=this.SummaryVolumeDealsInByPosID(list_history,deal.PositionID());
         double volume_out=this.SummaryVolumeDealsOutByPosID(list_history,deal.PositionID());//+order_close.Volume();
         //--- 计算当前平仓的交易量
         int dgl=(int)DigitsLots(deal.Symbol());
         double volume_current=::NormalizeDouble(volume_in-volume_out,dgl);
         //--- 按开/平仓方向搜索所有逆向仓位成交,并计算其总仓量
         double volume_opp_in=this.SummaryVolumeDealsInByPosID(list_history,order_close.PositionByID());
         double volume_opp_out=this.SummaryVolumeDealsOutByPosID(list_history,order_close.PositionByID());
         //--- 计算当前的逆向仓位交易量
         double volume_opp_current=::NormalizeDouble(volume_opp_in-volume_opp_out,dgl);
         //--- 如果已平仓交易量是部分平仓,则这是部分平仓
         if(volume_current>0 || order_close.VolumeCurrent()>0)
           {
            //--- 添加部分平仓标志
            this.m_trade_event_code+=TRADE_EVENT_FLAG_PARTIAL;
            //--- 如果逆向仓位部分平仓,则平仓是由逆向仓位交易量的一部分
            reason=(volume_opp_current>0 ? EVENT_REASON_DONE_PARTIALLY_BY_POS_PARTIALLY : EVENT_REASON_DONE_PARTIALLY_BY_POS);
           }
         //--- 如果持仓全部平仓,且逆向仓位交易量仅部分执行,则由逆向仓位部分交易量平仓
         else
           {
            if(volume_opp_current>0)
              {
               reason=EVENT_REASON_DONE_BY_POS_PARTIALLY;
              }
           }
         CEvent* event=new CEventPositionClose(this.m_trade_event_code,deal.PositionID());
         if(event!=NULL)
           {
            event.SetProperty(EVENT_PROP_TIME_EVENT,deal.TimeOpenMSC());                        // 事件时间
            event.SetProperty(EVENT_PROP_REASON_EVENT,reason);                                  // 事件原因 (来自 ENUM_EVENT_REASON 枚举)
            event.SetProperty(EVENT_PROP_TYPE_DEAL_EVENT,deal.TypeOrder());                     // 事件成交类型
            event.SetProperty(EVENT_PROP_TICKET_DEAL_EVENT,deal.Ticket());                      // 事件成交单据
            event.SetProperty(EVENT_PROP_TYPE_ORDER_EVENT,order_close.TypeOrder());             // 成交事件所基于的订单类型(最后的开仓订单)
            event.SetProperty(EVENT_PROP_TICKET_ORDER_EVENT,order_close.Ticket());              // 成交事件所基于的订单票据(最后的开仓订单)
            event.SetProperty(EVENT_PROP_TIME_ORDER_POSITION,order_first.TimeOpenMSC());        // 开仓成交事件所基于的订单时间(首笔开仓订单)
            event.SetProperty(EVENT_PROP_TYPE_ORDER_POSITION,order_first.TypeOrder());          // 成交事件所基于的订单类型(首笔开仓订单)
            event.SetProperty(EVENT_PROP_TICKET_ORDER_POSITION,order_first.Ticket());           // 成交事件所基于的订单票据(首笔开仓订单)
            event.SetProperty(EVENT_PROP_POSITION_ID,deal.PositionID());                        // 仓位 ID
            event.SetProperty(EVENT_PROP_POSITION_BY_ID,order_close.PositionByID());            // 逆向仓位 ID
            //---
            event.SetProperty(EVENT_PROP_MAGIC_BY_ID,order_close_by.Magic());                   // 逆向仓位魔幻数字
            event.SetProperty(EVENT_PROP_TYPE_ORD_POS_BEFORE,order_first.TypeOrder());          // 方向改变之前仓位订单类型
            event.SetProperty(EVENT_PROP_TICKET_ORD_POS_BEFORE,order_first.Ticket());           // 方向改变之前仓位单据
            event.SetProperty(EVENT_PROP_TYPE_ORD_POS_CURRENT,order_first.TypeOrder());         // 当前仓位订单类型
            event.SetProperty(EVENT_PROP_TICKET_ORD_POS_CURRENT,order_first.Ticket());          // 当前仓位单据
            event.SetProperty(EVENT_PROP_SYMBOL_BY_ID,order_close_by.Symbol());                 // 逆向仓位品种
            //---
            event.SetProperty(EVENT_PROP_MAGIC_ORDER,deal.Magic());                             // 订单/成交/仓位魔幻数字
            event.SetProperty(EVENT_PROP_PRICE_EVENT,deal.PriceOpen());                         // 事件价格
            event.SetProperty(EVENT_PROP_PRICE_OPEN,order_first.PriceOpen());                   // 订单/成交/仓位开盘价
            event.SetProperty(EVENT_PROP_PRICE_CLOSE,deal.PriceClose());                        // 订单/成交/仓位收盘价
            event.SetProperty(EVENT_PROP_PRICE_SL,order_first.StopLoss());                      // 止损价格 (仓位的止损订单价格)
            event.SetProperty(EVENT_PROP_PRICE_TP,order_first.TakeProfit());                    // 止盈价格 (仓位的止盈订单价格)
            event.SetProperty(EVENT_PROP_VOLUME_ORDER_INITIAL,::NormalizeDouble(volume_in,dgl));// 初始交易量
            event.SetProperty(EVENT_PROP_VOLUME_ORDER_EXECUTED,deal.Volume());                  // 平仓交易量
            event.SetProperty(EVENT_PROP_VOLUME_ORDER_CURRENT,volume_current);                  // 剩余 (当前) 交易量
            event.SetProperty(EVENT_PROP_VOLUME_POSITION_EXECUTED,vol_position);                // 剩余 (当前) 交易量
            event.SetProperty(EVENT_PROP_PROFIT,deal.ProfitFull());                             // 盈利
            event.SetProperty(EVENT_PROP_SYMBOL,deal.Symbol());                                 // 订单品种
            //--- 设置控制程序所在图表 ID,解码事件代码,并设置事件类型
            event.SetChartID(this.m_chart_id);
            event.SetTypeEvent();
            //--- 如果事件对象不在列表中,则添加它
            if(!this.IsPresentEventInList(event))
              {
               this.m_list_events.InsertSort(event);
               //--- 发送有关事件的消息,并设置最后交易事件的数值
               event.SendEvent();
               this.m_trade_event=event.TradeEvent();
              }
            //--- 如果事件已存在于列表中,则删除新的事件对象,并显示调试消息
            else
              {
               ::Print(DFUN_ERR_LINE,TextByLanguage("Такое событие уже есть в списке","This event already in the list."));
               delete event;
              }
           }
        }
     }
  }
//+------------------------------------------------------------------+

该方法非常大,尽管所有操作都相似,且在方法清单里有注释描述。 我相信,方法代码不应该导致任何问题。

为净持结算帐户创建新事件的方法具有相同的逻辑:

//+------------------------------------------------------------------+
//| 为净持结算帐户创建一个事件                                            |
//+------------------------------------------------------------------+
void CEventsCollection::NewDealEventNetto(COrder *deal,CArrayObj *list_history,CArrayObj *list_market)
  {
//--- 准备仓位历史数据
//--- 所有成交和持仓方向变化的列表
   CArrayObj* list_deals=this.GetListAllDealsByPosID(list_history,deal.PositionID());
   CArrayObj* list_changes=this.GetListAllDealsInOutByPosID(list_history,deal.PositionID());
   if(list_deals==NULL || list_changes==NULL)
      return;
   list_deals.Sort(SORT_BY_ORDER_TIME_OPEN_MSC);
   list_changes.Sort(SORT_BY_ORDER_TIME_OPEN_MSC);
   if(!list_changes.InsertSort(list_deals.At(0)))
      return;
   
//--- 首笔和末笔仓位成交的订单
   CArrayObj* list_tmp=this.GetListAllOrdersByPosID(list_history,deal.PositionID());
   COrder* order_first_deal=list_tmp.At(0);
   list_tmp=CSelect::ByOrderProperty(list_tmp,ORDER_PROP_TICKET,deal.GetProperty(ORDER_PROP_DEAL_ORDER_TICKET),EQUAL);
   COrder* order_last_deal=list_tmp.At(list_tmp.Total()-1);
   if(order_first_deal==NULL || order_last_deal==NULL)
      return;
//--- 首笔和末笔仓位成交的订单类型和票据
   ENUM_ORDER_TYPE type_order_first_deal=(ENUM_ORDER_TYPE)order_first_deal.TypeOrder();
   ENUM_ORDER_TYPE type_order_last_deal=(ENUM_ORDER_TYPE)order_last_deal.TypeOrder();
   ulong ticket_order_first_deal=order_first_deal.Ticket();
   ulong ticket_order_last_deal=order_last_deal.Ticket();
   
//--- 当前和之前的持仓
   COrder* position_current=list_changes.At(list_changes.Total()-1);
   COrder* position_previous=(list_changes.Total()>1 ? list_changes.At(list_changes.Total()-2) : position_current);
   if(position_current==NULL || position_previous==NULL)
      return;
   ENUM_ORDER_TYPE type_position_current=(ENUM_ORDER_TYPE)position_current.TypeOrder();
   ulong ticket_position_current=position_current.GetProperty(ORDER_PROP_DEAL_ORDER_TICKET);
   ENUM_ORDER_TYPE type_position_previous=(ENUM_ORDER_TYPE)position_previous.TypeOrder();
   ulong ticket_position_previous=position_previous.GetProperty(ORDER_PROP_DEAL_ORDER_TICKET);

//--- 按票据获取持仓,并记下其交易量
   COrder* position=this.GetPositionByID(list_market,deal.PositionID());
   double vol_position=(position!=NULL ? position.Volume() : 0);
//--- 已执行的订单交易量
   double vol_order_done=order_last_deal.Volume()-order_last_deal.VolumeCurrent();
//--- 剩余 (未执行) 订单交易量
   double vol_order_current=order_last_deal.VolumeCurrent();

//--- 入场
   if(deal.GetProperty(ORDER_PROP_DEAL_ENTRY)==DEAL_ENTRY_IN)
     {
      this.m_trade_event_code=TRADE_EVENT_FLAG_POSITION_OPENED;
      int num_deals=list_deals.Total();
      int reason=(num_deals>1 ? EVENT_REASON_VOLUME_ADD : EVENT_REASON_DONE);
      //--- 如果这不是该仓位的首笔成交,则添加仓位更改标志
      if(num_deals>1)
        {
         this.m_trade_event_code+=TRADE_EVENT_FLAG_POSITION_CHANGED;
        }
      //--- 如果订单交易量部分开立,则表示部分执行
      if(order_last_deal.VolumeCurrent()>0)
        {
         this.m_trade_event_code+=TRADE_EVENT_FLAG_PARTIAL;
         //--- 如果这不是首笔仓位成交,则交易量是由部分执行增加,否则 - 部分开仓
         reason=(num_deals>1 ? EVENT_REASON_VOLUME_ADD_PARTIALLY : EVENT_REASON_DONE_PARTIALLY);
        }
      //--- 如果开单原是挂单,则激活挂订单
      if(order_last_deal.TypeOrder()>ORDER_TYPE_SELL && order_last_deal.TypeOrder()<ORDER_TYPE_CLOSE_BY)
        {
         this.m_trade_event_code+=TRADE_EVENT_FLAG_ORDER_ACTIVATED;
         //--- 如果这不是首笔仓位成交
         if(num_deals>1)
           {
            //--- 如果订单部分执行,则设置持仓由挂单部分执行增加交易量作为事件原因,
            //--- 否则,添加持仓由执行挂单增加交易量
            reason=
              (order_last_deal.VolumeCurrent()>0 ? 
               EVENT_REASON_VOLUME_ADD_BY_PENDING_PARTIALLY  : 
               EVENT_REASON_VOLUME_ADD_BY_PENDING
              );
           }
         //--- 如果这是一笔新仓位
         else
           {
            //--- 如果部分执行订单,则将挂单部分执行设置为事件原因,
            //--- 否则,持仓由激活挂单开仓
            reason=
              (order_last_deal.VolumeCurrent()>0 ? 
               EVENT_REASON_ACTIVATED_PENDING_PARTIALLY  : 
               EVENT_REASON_ACTIVATED_PENDING
              );
           }
        }
      CEvent* event=new CEventPositionOpen(this.m_trade_event_code,deal.PositionID());
      if(event!=NULL)
        {
         //--- 事件成交参数
         event.SetProperty(EVENT_PROP_TIME_EVENT,deal.TimeOpenMSC());                  // 事件时间(开仓时间)
         event.SetProperty(EVENT_PROP_REASON_EVENT,reason);                            // 事件原因 (来自 ENUM_EVENT_REASON 枚举)
         event.SetProperty(EVENT_PROP_TYPE_DEAL_EVENT,deal.TypeOrder());               // 事件成交类型
         event.SetProperty(EVENT_PROP_TICKET_DEAL_EVENT,deal.Ticket());                // 事件成交单据
         event.SetProperty(EVENT_PROP_MAGIC_ORDER,deal.Magic());                       // 订单/成交/仓位魔幻数字
         event.SetProperty(EVENT_PROP_PRICE_EVENT,deal.PriceOpen());                   // 事件价格 (开仓价格)
         event.SetProperty(EVENT_PROP_PROFIT,deal.ProfitFull());                       // 盈利
         event.SetProperty(EVENT_PROP_SYMBOL,deal.Symbol());                           // 订单品种
         event.SetProperty(EVENT_PROP_SYMBOL_BY_ID,deal.Symbol());                     // 逆向仓位品种
         event.SetProperty(EVENT_PROP_POSITION_ID,deal.PositionID());                  // 仓位 ID
         
         //--- 事件订单参数
         event.SetProperty(EVENT_PROP_TYPE_ORDER_EVENT,type_order_last_deal);          // 触发成交事件所基于的订单类型(最后的开仓订单)
         event.SetProperty(EVENT_PROP_TICKET_ORDER_EVENT,ticket_order_last_deal);      // 触发成交事件所基于的单据(末笔开仓订单)
         event.SetProperty(EVENT_PROP_POSITION_BY_ID,order_last_deal.PositionByID());  // 逆向仓位 ID
         event.SetProperty(EVENT_PROP_PRICE_CLOSE,order_last_deal.PriceClose());       // 平单价格  (仓位的末笔平单价格)
         event.SetProperty(EVENT_PROP_VOLUME_ORDER_INITIAL,order_last_deal.Volume());  // 请求的订单交易量
         event.SetProperty(EVENT_PROP_VOLUME_ORDER_EXECUTED,vol_order_done);           // 执行的订单交易量
         event.SetProperty(EVENT_PROP_VOLUME_ORDER_CURRENT,vol_order_current);         // 剩余 (未执行) 订单交易量
         event.SetProperty(EVENT_PROP_MAGIC_BY_ID,deal.Magic());                       // 逆向仓位魔幻数字
            
         //--- 仓位参数
         event.SetProperty(EVENT_PROP_TYPE_ORDER_POSITION,type_order_first_deal);      // 触发首笔开仓成交事件的订单类型
         event.SetProperty(EVENT_PROP_TICKET_ORDER_POSITION,ticket_order_first_deal);  // 触发成交事件所基于的单据(首笔开仓订单)
         event.SetProperty(EVENT_PROP_TIME_ORDER_POSITION,order_first_deal.TimeOpenMSC());  // 触发开仓成交的订单时间(首笔开仓订单)
         event.SetProperty(EVENT_PROP_PRICE_OPEN,order_first_deal.PriceOpen());        // 首笔开i仓价格
         event.SetProperty(EVENT_PROP_PRICE_SL,order_first_deal.StopLoss());           // 止损价格 (仓位的止损订单价格)
         event.SetProperty(EVENT_PROP_PRICE_TP,order_first_deal.TakeProfit());         // 止盈价格 (仓位的止损订单价格)
         event.SetProperty(EVENT_PROP_TYPE_ORD_POS_BEFORE,type_position_previous);     // 方向改变之前仓位订单类型
         event.SetProperty(EVENT_PROP_TICKET_ORD_POS_BEFORE,ticket_position_previous); // 方向改变之前仓位单据
         event.SetProperty(EVENT_PROP_TYPE_ORD_POS_CURRENT,type_position_current);     // 当前仓位订单类型
         event.SetProperty(EVENT_PROP_TICKET_ORD_POS_CURRENT,ticket_position_current); // 当前仓位单据
         event.SetProperty(EVENT_PROP_VOLUME_POSITION_EXECUTED,vol_position);          // 已执行的仓量
         
         //--- 设置控制程序所在图表 ID,解码事件代码,并设置事件类型
         event.SetChartID(this.m_chart_id);
         event.SetTypeEvent();
         //--- 如果事件对象不在列表中,则添加它
         if(!this.IsPresentEventInList(event))
           {
            this.m_list_events.InsertSort(event);
            //--- 发送有关事件的消息,并设置最后交易事件的数值
            event.SendEvent();
            this.m_trade_event=event.TradeEvent();
           }
         //--- 如果事件已存在于列表中,则删除新的事件对象,并显示调试消息
         else
           {
            ::Print(DFUN_ERR_LINE,TextByLanguage("Такое событие уже есть в списке","This event already in the list."));
            delete event;
           }
        }
     }
//--- 持仓逆转
   else if(deal.GetProperty(ORDER_PROP_DEAL_ENTRY)==DEAL_ENTRY_INOUT)
     {
      this.m_trade_event_code=TRADE_EVENT_FLAG_POSITION_OPENED+TRADE_EVENT_FLAG_POSITION_CHANGED+TRADE_EVENT_FLAG_POSITION_REVERSE;
      int reason=EVENT_REASON_REVERSE;
      //--- 如果并非整体订单交易量,则是部分执行
      if(order_last_deal.VolumeCurrent()>0)
        {
         this.m_trade_event_code+=TRADE_EVENT_FLAG_PARTIAL;
         reason=EVENT_REASON_REVERSE_PARTIALLY;
        }
      //--- 如果开单原是挂单,则激活挂订单
      if(order_last_deal.TypeOrder()>ORDER_TYPE_SELL && order_last_deal.TypeOrder()<ORDER_TYPE_CLOSE_BY)
        {
         this.m_trade_event_code+=TRADE_EVENT_FLAG_ORDER_ACTIVATED;
         //--- 如果部分执行订单,则设置持仓反转由挂单部分执行为事件原因
         reason=
           (order_last_deal.VolumeCurrent()>0 ? 
            EVENT_REASON_REVERSE_BY_PENDING_PARTIALLY  : 
            EVENT_REASON_REVERSE_BY_PENDING
           );
        }
      CEvent* event=new CEventPositionOpen(this.m_trade_event_code,deal.PositionID());
      if(event!=NULL)
        {
         //--- 事件成交参数
         event.SetProperty(EVENT_PROP_TIME_EVENT,deal.TimeOpenMSC());                  // 事件时间(开仓时间)
         event.SetProperty(EVENT_PROP_REASON_EVENT,reason);                            // 事件原因 (来自 ENUM_EVENT_REASON 枚举)
         event.SetProperty(EVENT_PROP_TYPE_DEAL_EVENT,deal.TypeOrder());               // 事件成交类型
         event.SetProperty(EVENT_PROP_TICKET_DEAL_EVENT,deal.Ticket());                // 事件成交单据
         event.SetProperty(EVENT_PROP_MAGIC_ORDER,deal.Magic());                       // 订单/成交/仓位魔幻数字
         event.SetProperty(EVENT_PROP_PRICE_EVENT,deal.PriceOpen());                   // 事件价格 (开仓价格)
         event.SetProperty(EVENT_PROP_PROFIT,deal.ProfitFull());                       // 盈利
         event.SetProperty(EVENT_PROP_SYMBOL,deal.Symbol());                           // 订单品种
         event.SetProperty(EVENT_PROP_SYMBOL_BY_ID,deal.Symbol());                     // 逆向仓位品种
         event.SetProperty(EVENT_PROP_POSITION_ID,deal.PositionID());                  // 仓位 ID
            
         //--- 事件订单参数
         event.SetProperty(EVENT_PROP_TYPE_ORDER_EVENT,type_order_last_deal);          // 触发成交事件所基于的订单类型(最后的开仓订单)
         event.SetProperty(EVENT_PROP_TICKET_ORDER_EVENT,ticket_order_last_deal);      // 触发成交事件所基于的单据(末笔开仓订单)
         event.SetProperty(EVENT_PROP_POSITION_BY_ID,order_last_deal.PositionByID());  // 逆向仓位 ID
         event.SetProperty(EVENT_PROP_PRICE_CLOSE,order_last_deal.PriceClose());       // 平单价格  (仓位的末笔平单价格)
         event.SetProperty(EVENT_PROP_VOLUME_ORDER_INITIAL,order_last_deal.Volume());  // 请求的订单交易量
         event.SetProperty(EVENT_PROP_VOLUME_ORDER_EXECUTED,vol_order_done);           // 执行的订单交易量
         event.SetProperty(EVENT_PROP_VOLUME_ORDER_CURRENT,vol_order_current);         // 剩余 (未执行) 订单交易量
         event.SetProperty(EVENT_PROP_MAGIC_BY_ID,deal.Magic());                       // 逆向仓位魔幻数字
            
         //--- 仓位参数
         event.SetProperty(EVENT_PROP_TYPE_ORDER_POSITION,type_order_first_deal);      // 触发首笔开仓成交事件的订单类型
         event.SetProperty(EVENT_PROP_TICKET_ORDER_POSITION,ticket_order_first_deal);  // 触发成交事件所基于的单据(首笔开仓订单)
         event.SetProperty(EVENT_PROP_TIME_ORDER_POSITION,order_first_deal.TimeOpenMSC());  // 触发开仓成交的订单时间(首笔开仓订单)
         event.SetProperty(EVENT_PROP_PRICE_OPEN,order_first_deal.PriceOpen());        // 首笔开i仓价格
         event.SetProperty(EVENT_PROP_PRICE_SL,order_first_deal.StopLoss());           // 止损价格 (仓位的止损订单价格)
         event.SetProperty(EVENT_PROP_PRICE_TP,order_first_deal.TakeProfit());         // 止盈价格 (仓位的止损订单价格)
         event.SetProperty(EVENT_PROP_TYPE_ORD_POS_BEFORE,type_position_previous);     // 方向改变之前仓位订单类型
         event.SetProperty(EVENT_PROP_TICKET_ORD_POS_BEFORE,ticket_position_previous); // 方向改变之前仓位单据
         event.SetProperty(EVENT_PROP_TYPE_ORD_POS_CURRENT,type_position_current);     // 当前仓位订单类型
         event.SetProperty(EVENT_PROP_TICKET_ORD_POS_CURRENT,ticket_position_current); // 当前仓位单据
         event.SetProperty(EVENT_PROP_VOLUME_POSITION_EXECUTED,vol_position);          // 已执行的仓量
         
         //--- 设置控制程序所在图表 ID,解码事件代码,并设置事件类型
         event.SetChartID(this.m_chart_id);
         event.SetTypeEvent();
         //--- 如果事件对象不在列表中,则添加它
         if(!this.IsPresentEventInList(event))
           {
            this.m_list_events.InsertSort(event);
            //--- 发送有关事件的消息,并设置最后交易事件的数值
            event.SendEvent();
            this.m_trade_event=event.TradeEvent();
           }
         //--- 如果事件已存在于列表中,则删除新的事件对象,并显示调试消息
         else
           {
            ::Print(DFUN_ERR_LINE,TextByLanguage("Такое событие уже есть в списке","This event already in the list."));
            delete event;
           }
        }
     }
//--- 离场
   else if(deal.GetProperty(ORDER_PROP_DEAL_ENTRY)==DEAL_ENTRY_OUT)
     {
      this.m_trade_event_code=TRADE_EVENT_FLAG_POSITION_CLOSED;
      int reason=EVENT_REASON_DONE;
      //--- 如果具有 ID 的仓位仍在继续持有,则意味着部分执行
      if(this.GetPositionByID(list_market,deal.PositionID())!=NULL)
        {
         this.m_trade_event_code+=TRADE_EVENT_FLAG_PARTIAL;
        }
      //--- 如果平仓单部分执行,则将平仓订单部分执行设置为事件原因
      if(order_last_deal.VolumeCurrent()>0)
        {
         reason=EVENT_REASON_DONE_PARTIALLY;
        }
      //--- 如果平仓标志设置为止损,则由止损执行平仓
      //--- 如果止损单部分执行,则设置止损部分平仓原因
      if(order_last_deal.IsCloseByStopLoss())
        {
         this.m_trade_event_code+=TRADE_EVENT_FLAG_SL;
         reason=(order_last_deal.VolumeCurrent()>0 ? EVENT_REASON_DONE_SL_PARTIALLY : EVENT_REASON_DONE_SL);
        }
      //--- 如果平仓标志设置为止盈,则由止盈执行平仓
      //--- 如果止盈单部分执行,则设置止盈部分平仓原因
      else if(order_last_deal.IsCloseByTakeProfit())
        {
         this.m_trade_event_code+=TRADE_EVENT_FLAG_TP;
         reason=(order_last_deal.VolumeCurrent()>0 ? EVENT_REASON_DONE_TP_PARTIALLY : EVENT_REASON_DONE_TP);
        }
      //---
      CEvent* event=new CEventPositionClose(this.m_trade_event_code,deal.PositionID());
      if(event!=NULL)
        {
         //--- 事件成交参数
         event.SetProperty(EVENT_PROP_TIME_EVENT,deal.TimeOpenMSC());                  // 事件时间(开仓时间)
         event.SetProperty(EVENT_PROP_REASON_EVENT,reason);                            // 事件原因 (来自 ENUM_EVENT_REASON 枚举)
         event.SetProperty(EVENT_PROP_TYPE_DEAL_EVENT,deal.TypeOrder());               // 事件成交类型
         event.SetProperty(EVENT_PROP_TICKET_DEAL_EVENT,deal.Ticket());                // 事件成交单据
         event.SetProperty(EVENT_PROP_MAGIC_ORDER,deal.Magic());                       // 订单/成交/仓位魔幻数字
         event.SetProperty(EVENT_PROP_PRICE_EVENT,deal.PriceOpen());                   // 事件价格 (开仓价格)
         event.SetProperty(EVENT_PROP_PROFIT,deal.ProfitFull());                       // 盈利
         event.SetProperty(EVENT_PROP_SYMBOL,deal.Symbol());                           // 订单品种
         event.SetProperty(EVENT_PROP_SYMBOL_BY_ID,deal.Symbol());                     // 逆向仓位品种
         event.SetProperty(EVENT_PROP_POSITION_ID,deal.PositionID());                  // 仓位 ID
         
         //--- 事件订单参数
         event.SetProperty(EVENT_PROP_TYPE_ORDER_EVENT,type_order_last_deal);          // 触发成交事件所基于的订单类型(最后的开仓订单)
         event.SetProperty(EVENT_PROP_TICKET_ORDER_EVENT,ticket_order_last_deal);      // 触发成交事件所基于的单据(末笔开仓订单)
         event.SetProperty(EVENT_PROP_POSITION_BY_ID,order_last_deal.PositionByID());  // 逆向仓位 ID
         event.SetProperty(EVENT_PROP_PRICE_CLOSE,order_last_deal.PriceClose());       // 平单价格  (仓位的末笔平单价格)
         event.SetProperty(EVENT_PROP_VOLUME_ORDER_INITIAL,order_last_deal.Volume());  // 请求的订单交易量
         event.SetProperty(EVENT_PROP_VOLUME_ORDER_EXECUTED,vol_order_done);           // 执行的订单交易量
         event.SetProperty(EVENT_PROP_VOLUME_ORDER_CURRENT,vol_order_current);         // 剩余 (未执行) 订单交易量
         event.SetProperty(EVENT_PROP_MAGIC_BY_ID,order_last_deal.Magic());            // 逆向仓位魔幻数字
            
         //--- 仓位参数
         event.SetProperty(EVENT_PROP_TYPE_ORDER_POSITION,type_order_first_deal);      // 触发首笔开仓成交事件的订单类型
         event.SetProperty(EVENT_PROP_TICKET_ORDER_POSITION,ticket_order_first_deal);  // 触发成交事件所基于的单据(首笔开仓订单)
         event.SetProperty(EVENT_PROP_TIME_ORDER_POSITION,order_first_deal.TimeOpenMSC());  // 触发开仓成交的订单时间(首笔开仓订单)
         event.SetProperty(EVENT_PROP_PRICE_OPEN,order_first_deal.PriceOpen());        // 首笔开i仓价格
         event.SetProperty(EVENT_PROP_PRICE_SL,order_first_deal.StopLoss());           // 止损价格 (仓位的止损订单价格)
         event.SetProperty(EVENT_PROP_PRICE_TP,order_first_deal.TakeProfit());         // 止盈价格 (仓位的止损订单价格)
         event.SetProperty(EVENT_PROP_TYPE_ORD_POS_BEFORE,type_position_previous);     // 方向改变之前仓位订单类型
         event.SetProperty(EVENT_PROP_TICKET_ORD_POS_BEFORE,ticket_position_previous); // 方向改变之前仓位单据
         event.SetProperty(EVENT_PROP_TYPE_ORD_POS_CURRENT,type_position_current);     // 当前仓位订单类型
         event.SetProperty(EVENT_PROP_TICKET_ORD_POS_CURRENT,ticket_position_current); // 当前仓位单据
         event.SetProperty(EVENT_PROP_VOLUME_POSITION_EXECUTED,vol_position);          // 已执行的仓量
         
         //--- 设置控制程序所在图表 ID,解码事件代码,并设置事件类型
         event.SetChartID(this.m_chart_id);
         event.SetTypeEvent();
         //--- 如果事件对象不在列表中,则添加它
         if(!this.IsPresentEventInList(event))
           {
            this.m_list_events.InsertSort(event);
            //--- 发送有关事件的消息,并设置最后交易事件的数值
            event.SendEvent();
            this.m_trade_event=event.TradeEvent();
           }
         //--- 如果事件已存在于列表中,则删除新的事件对象,并显示调试消息
         else
           {
            ::Print(DFUN_ERR_LINE,TextByLanguage("Такое событие уже есть в списке","This event already in the list."));
            delete event;
           }
        }
     }
  }  
//+------------------------------------------------------------------+

CreateNewEvent(),NewDealEventHedge() 和 NewDealEventNetto() 方法具有相同的逻辑和操作。 因此,将它们合并起来是合理的。 但到目前为止,我们已经完成的任务如上所示(从“简单-到-复杂”的基础上)。 正如我已经提到的,类的代码及其方法会在以后进行优化。

我们已经在事件集合类中实现了修改,以便处理对冲和净持帐户类型。 下面附带的函数库文件中提供了该类的完整清单。 代码非常臃肿。

测试对冲和净持结算账户的表现

为了检查已实现的更改,基于上一篇文章中的测试 EA 创建相应的测试 EA。
将其命名为 TestDoEasyPart06.mq5,并保存在新的 \MQL5\Experts\TestDoEasy\ Part06 目录中。

从 EA 的 OnInit() 处理程序中删除检查帐户类型的代码:

int OnInit()
  {
//--- 检查帐户类型
   if(!engine.IsHedge())
     {
      Alert(TextByLanguage("Ошибка. Счёт должен быть хеджевым","Error. Account must be hedge"));
      return INIT_FAILED;
     }
//--- 设置全局变量

代之,添加调用检查创建枚举有效性的函数,以便按对象属性进行搜索和排序:

int OnInit()
  {
//--- 调用该函数会在日志中显示枚举常量列表 
//--- (列表在 DELib.mqh 文件的 22 和 25 字符行中设置)用于检查常量的有效性
   //EnumNumbersTest();
//--- 设置全局变量

为了部分平仓,净持结算账户需要放置与持仓方向相反的仓位,且交易量足以部分平仓。 因此,我们需要在 PressButtonEvents() 按钮按下事件处理函数中稍作修正。
如要部分平多头持仓:

      //--- 如果 BUTT_CLOSE_BUY2 按钮被按下: 以最大利润平一半多头持仓
      else if(button==EnumToString(BUTT_CLOSE_BUY2))
        {
         //--- 获取所有持仓的清单
         CArrayObj* list=engine.GetListMarketPosition();
         //--- 从列表中仅选择多头持仓
         list=CSelect::ByOrderProperty(list,ORDER_PROP_TYPE,POSITION_TYPE_BUY,EQUAL);
         //--- 参考佣金和隔夜利息,按利润对列表进行排序
         list.Sort(SORT_BY_ORDER_PROFIT_FULL);
         //--- 获取具有最大利润的多头持仓索引
         int index=CSelect::FindOrderMax(list,ORDER_PROP_PROFIT_FULL);
         if(index>WRONG_VALUE)
           {
            COrder* position=list.At(index);
            if(position!=NULL)
              {
               //--- 计算已平仓交易量,并按票证将多头持仓平一半
               if(engine.IsHedge())
                  trade.PositionClosePartial(position.Ticket(),NormalizeLot(position.Symbol(),position.Volume()/2.0));
               else
                  trade.Sell(NormalizeLot(position.Symbol(),position.Volume()/2.0));
              }
           }
        }

检查帐户。 若为对冲账户,部分平仓,否则(如果净持结算)— 发送开空头仓位订单,其成交量等于当前多头持仓的一半

关闭部分空头持仓:

      //--- 如果 BUTT_CLOSE_SELL2 按钮被按下: 以最大利润平一半空头持仓
      else if(button==EnumToString(BUTT_CLOSE_SELL2))
        {
         //--- 获取所有持仓的清单
         CArrayObj* list=engine.GetListMarketPosition();
         //--- 从列表中仅选择空头持仓
         list=CSelect::ByOrderProperty(list,ORDER_PROP_TYPE,POSITION_TYPE_SELL,EQUAL);
         //--- 参考佣金和隔夜利息,按利润对列表进行排序
         list.Sort(SORT_BY_ORDER_PROFIT_FULL);
         //--- 获取具有最大利润的空头持仓索引
         int index=CSelect::FindOrderMax(list,ORDER_PROP_PROFIT_FULL);
         if(index>WRONG_VALUE)
           {
            COrder* position=list.At(index);
            if(position!=NULL)
              {
               //--- 计算已平仓交易量,并按票证将空头持仓平一半
               if(engine.IsHedge())
                  trade.PositionClosePartial(position.Ticket(),NormalizeLot(position.Symbol(),position.Volume()/2.0));
               else
                  trade.Buy(NormalizeLot(position.Symbol(),position.Volume()/2.0));
              }
           }
        }

检查帐户。 若为对冲账户,部分平仓,否则(如果净持结算)— 发送开多头仓位订单,其成交量等于当前空头持仓的一半

这些是为 EA 在净持结算帐户上运行而应实现的必要更改。

测试 EA 的完整清单:

//+------------------------------------------------------------------+
//|                                             TestDoEasyPart06.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\Engine.mqh>
#include <Trade\Trade.mqh>
//--- 枚举
enum ENUM_BUTTONS
  {
   BUTT_BUY,
   BUTT_BUY_LIMIT,
   BUTT_BUY_STOP,
   BUTT_BUY_STOP_LIMIT,
   BUTT_CLOSE_BUY,
   BUTT_CLOSE_BUY2,
   BUTT_CLOSE_BUY_BY_SELL,
   BUTT_SELL,
   BUTT_SELL_LIMIT,
   BUTT_SELL_STOP,
   BUTT_SELL_STOP_LIMIT,
   BUTT_CLOSE_SELL,
   BUTT_CLOSE_SELL2,
   BUTT_CLOSE_SELL_BY_BUY,
   BUTT_DELETE_PENDING,
   BUTT_CLOSE_ALL,
   BUTT_PROFIT_WITHDRAWAL
  };
#define TOTAL_BUTT   (17)
//--- 结构
struct SDataButt
  {
   string      name;
   string      text;
  };
//--- 输入参数
input ulong    InpMagic       =  123;  // 魔幻数字
input double   InpLots        =  0.1;  // 手数
input uint     InpStopLoss    =  50;   // 止损点数
input uint     InpTakeProfit  =  50;   // 止盈点数
input uint     InpDistance    =  50;   // 挂单距离 (点数)
input uint     InpDistanceSL  =  50;   // StopLimit 订单距离 (点数)
input uint     InpSlippage    =  0;    // 滑点点数
input double   InpWithdrawal  =  10;   // 提款(在测试器中)
input uint     InpButtShiftX  =  40;   // 按钮的 X 偏移
input uint     InpButtShiftY  =  10;   // 按钮的 Y 偏移
//--- 全局变量
CEngine        engine;
CTrade         trade;
SDataButt      butt_data[TOTAL_BUTT];
string         prefix;
double         lot;
double         withdrawal=(InpWithdrawal<0.1 ? 0.1 : InpWithdrawal);
ulong          magic_number;
uint           stoploss;
uint           takeprofit;
uint           distance_pending;
uint           distance_stoplimit;
uint           slippage;
//+------------------------------------------------------------------+
//| 智能系统初始化函数                                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- 调用该函数会在日志中显示枚举常量列表, 
//--- (列表在 DELib.mqh 文件的 22 和 25 字符行中设置)用于检查常量的有效性
   //EnumNumbersTest();
//--- 设置全局变量
   prefix=MQLInfoString(MQL_PROGRAM_NAME)+"_";
   for(int i=0;i<TOTAL_BUTT;i++)
     {
      butt_data[i].name=prefix+EnumToString((ENUM_BUTTONS)i);
      butt_data[i].text=EnumToButtText((ENUM_BUTTONS)i);
     }
   lot=NormalizeLot(Symbol(),fmax(InpLots,MinimumLots(Symbol())*2.0));
   magic_number=InpMagic;
   stoploss=InpStopLoss;
   takeprofit=InpTakeProfit;
   distance_pending=InpDistance;
   distance_stoplimit=InpDistanceSL;
   slippage=InpSlippage;
//--- 创建按钮
   if(!CreateButtons(InpButtShiftX,InpButtShiftY))
      return INIT_FAILED;
//--- 设定交易参数
   trade.SetDeviationInPoints(slippage);
   trade.SetExpertMagicNumber(magic_number);
   trade.SetTypeFillingBySymbol(Symbol());
   trade.SetMarginMode();
   trade.LogLevel(LOG_LEVEL_NO);
//---
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//| 智能系统逆初始化函数                                                  |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
//--- 删除对象
   ObjectsDeleteAll(0,prefix);
   Comment("");
  }
//+------------------------------------------------------------------+
//| 智能系统即时报价函数                                                  |
//+------------------------------------------------------------------+
void OnTick()
  {
//---
   static ENUM_TRADE_EVENT last_event=WRONG_VALUE;
   if(MQLInfoInteger(MQL_TESTER))
     {
      engine.OnTimer();
      int total=ObjectsTotal(0);
      for(int i=0;i<total;i++)
        {
         string obj_name=ObjectName(0,i);
         if(StringFind(obj_name,prefix+"BUTT_")<0)
            continue;
         PressButtonEvents(obj_name);
        }
     }
   if(engine.LastTradeEvent()!=last_event)
     {
      Comment("\nLast trade event: ",EnumToString(engine.LastTradeEvent()));
      last_event=engine.LastTradeEvent();
     }
  }
//+------------------------------------------------------------------+
//| 计时器函数                                                         |
//+------------------------------------------------------------------+
void OnTimer()
  {
   if(!MQLInfoInteger(MQL_TESTER))
      engine.OnTimer();
  }
//+------------------------------------------------------------------+
//| ChartEvent 函数                                                    |
//+------------------------------------------------------------------+
void OnChartEvent(const int id,
                  const long &lparam,
                  const double &dparam,
                  const string &sparam)
  {
   if(MQLInfoInteger(MQL_TESTER))
      return;
   if(id==CHARTEVENT_OBJECT_CLICK && StringFind(sparam,"BUTT_")>0)
     {
      PressButtonEvents(sparam);
     }
   if(id>=CHARTEVENT_CUSTOM)
     {
      ushort event=ushort(id-CHARTEVENT_CUSTOM);
      Print(DFUN,"id=",id,", event=",EnumToString((ENUM_TRADE_EVENT)event),", lparam=",lparam,", dparam=",DoubleToString(dparam,Digits()),", sparam=",sparam);
     } 
  }
//+------------------------------------------------------------------+
//| 创建按钮面板                                                        |
//+------------------------------------------------------------------+
bool CreateButtons(const int shift_x=30,const int shift_y=0)
  {
   int h=18,w=84,offset=2;
   int cx=offset+shift_x,cy=offset+shift_y+(h+1)*(TOTAL_BUTT/2)+2*h+1;
   int x=cx,y=cy;
   int shift=0;
   for(int i=0;i<TOTAL_BUTT;i++)
     {
      x=x+(i==7 ? w+2 : 0);
      if(i==TOTAL_BUTT-3) x=cx;
      y=(cy-(i-(i>6 ? 7 : 0))*(h+1));
      if(!ButtonCreate(butt_data[i].name,x,y,(i<TOTAL_BUTT-3 ? w : w*2+2),h,butt_data[i].text,(i<4 ? clrGreen : i>6 && i<11 ? clrRed : clrBlue)))
        {
         Alert(TextByLanguage("Не удалось создать кнопку \"","Could not create button \""),butt_data[i].text);
         return false;
        }
     }
   ChartRedraw(0);
   return true;
  }
//+------------------------------------------------------------------+
//| 创建按钮                                                            |
//+------------------------------------------------------------------+
bool ButtonCreate(const string name,const int x,const int y,const int w,const int h,const string text,const color clr,const string font="Calibri",const int font_size=8)
  {
   if(ObjectFind(0,name)<0)
     {
      if(!ObjectCreate(0,name,OBJ_BUTTON,0,0,0)) 
        { 
         Print(DFUN,TextByLanguage("не удалось создать кнопку! Код ошибки=","Could not create button! Error code="),GetLastError()); 
         return false; 
        } 
      ObjectSetInteger(0,name,OBJPROP_SELECTABLE,false);
      ObjectSetInteger(0,name,OBJPROP_HIDDEN,true);
      ObjectSetInteger(0,name,OBJPROP_XDISTANCE,x);
      ObjectSetInteger(0,name,OBJPROP_YDISTANCE,y);
      ObjectSetInteger(0,name,OBJPROP_XSIZE,w);
      ObjectSetInteger(0,name,OBJPROP_YSIZE,h);
      ObjectSetInteger(0,name,OBJPROP_CORNER,CORNER_LEFT_LOWER);
      ObjectSetInteger(0,name,OBJPROP_ANCHOR,ANCHOR_LEFT_LOWER);
      ObjectSetInteger(0,name,OBJPROP_FONTSIZE,font_size);
      ObjectSetString(0,name,OBJPROP_FONT,font);
      ObjectSetString(0,name,OBJPROP_TEXT,text);
      ObjectSetInteger(0,name,OBJPROP_COLOR,clr);
      ObjectSetString(0,name,OBJPROP_TOOLTIP,"\n");
      ObjectSetInteger(0,name,OBJPROP_BORDER_COLOR,clrGray);
      return true;
     }
   return false;
  }
//+------------------------------------------------------------------+
//| 返回按钮状况                                                        |
//+------------------------------------------------------------------+
bool ButtonState(const string name)
  {
   return (bool)ObjectGetInteger(0,name,OBJPROP_STATE);
  }
//+------------------------------------------------------------------+
//| 设置按钮状况                                                        |
//+------------------------------------------------------------------+
void ButtonState(const string name,const bool state)
  {
   ObjectSetInteger(0,name,OBJPROP_STATE,state);
  }
//+------------------------------------------------------------------+
//| 将枚举变换为按钮文本                                                  |
//+------------------------------------------------------------------+
string EnumToButtText(const ENUM_BUTTONS member)
  {
   string txt=StringSubstr(EnumToString(member),5);
   StringToLower(txt);
   StringReplace(txt,"buy","Buy");
   StringReplace(txt,"sell","Sell");
   StringReplace(txt,"_limit"," Limit");
   StringReplace(txt,"_stop"," Stop");
   StringReplace(txt,"close_","Close ");
   StringReplace(txt,"2"," 1/2");
   StringReplace(txt,"_by_"," by ");
   StringReplace(txt,"profit_","Profit ");
   StringReplace(txt,"delete_","Delete ");
   return txt;
  }
//+------------------------------------------------------------------+
//| 处理按按钮动作                                                       |
//+------------------------------------------------------------------+
void PressButtonEvents(const string button_name)
  {
   //--- 将按钮名称转换为其字符串 ID
   string button=StringSubstr(button_name,StringLen(prefix));
   //--- 如果按下按钮
   if(ButtonState(button_name))
     {
      //--- 如果按下 BUTT_BUY按钮:开多头仓位
      if(button==EnumToString(BUTT_BUY))
        {
         //--- 获取相对于 StopLevel 的正确止损和止盈价位
         double sl=CorrectStopLoss(Symbol(),ORDER_TYPE_BUY,0,stoploss);
         double tp=CorrectTakeProfit(Symbol(),ORDER_TYPE_BUY,0,takeprofit);
         //--- 开多头仓位
         trade.Buy(NormalizeLot(Symbol(),lot),Symbol(),0,sl,tp);
        }
      //--- 如果按下 BUTT_BUY_LIMIT 按钮,下 BuyLimit 挂单
      else if(button==EnumToString(BUTT_BUY_LIMIT))
        {
         //--- 获取相对于 StopLevel 的正确挂单位置
         double price_set=CorrectPricePending(Symbol(),ORDER_TYPE_BUY_LIMIT,distance_pending);
         //--- 参考 StopLevel,获取相对于订单放置级别的正确止损和止盈价位
         double sl=CorrectStopLoss(Symbol(),ORDER_TYPE_BUY_LIMIT,price_set,stoploss);
         double tp=CorrectTakeProfit(Symbol(),ORDER_TYPE_BUY_LIMIT,price_set,takeprofit);
         //--- 设置 BuyLimit 订单
         trade.BuyLimit(lot,price_set,Symbol(),sl,tp);
        }
      //--- 如果 BUTT_BUY_STOP 按钮按下: 设置 BuyStop
      else if(button==EnumToString(BUTT_BUY_STOP))
        {
         //--- 获取相对于 StopLevel 的正确挂单位置
         double price_set=CorrectPricePending(Symbol(),ORDER_TYPE_BUY_STOP,distance_pending);
         //--- 参考 StopLevel,获取相对于订单放置级别的正确止损和止盈价位
         double sl=CorrectStopLoss(Symbol(),ORDER_TYPE_BUY_STOP,price_set,stoploss);
         double tp=CorrectTakeProfit(Symbol(),ORDER_TYPE_BUY_STOP,price_set,takeprofit);
         //--- 设置 BuyStop 挂单
         trade.BuyStop(lot,price_set,Symbol(),sl,tp);
        }
      //--- 如果 BUTT_BUY_STOP_LIMIT 按钮按下: 设置 BuyStopLimit
      else if(button==EnumToString(BUTT_BUY_STOP_LIMIT))
        {
         //--- 获取相对于 StopLevel 的正确 BuyStop 挂单位置
         double price_set_stop=CorrectPricePending(Symbol(),ORDER_TYPE_BUY_STOP,distance_pending);
         //--- 参考 StopLevel,计算相对于 BuyStop 价位的 BuyLimit 挂单价格
         double price_set_limit=CorrectPricePending(Symbol(),ORDER_TYPE_BUY_LIMIT,distance_stoplimit,price_set_stop);
         //--- 参考 StopLevel,获取相对于订单放置级别的正确止损和止盈价位
         double sl=CorrectStopLoss(Symbol(),ORDER_TYPE_BUY_STOP,price_set_limit,stoploss);
         double tp=CorrectTakeProfit(Symbol(),ORDER_TYPE_BUY_STOP,price_set_limit,takeprofit);
         //--- 设置 BuyStopLimit 挂单
         trade.OrderOpen(Symbol(),ORDER_TYPE_BUY_STOP_LIMIT,lot,price_set_limit,price_set_stop,sl,tp);
        }
      //--- 如果按下 BUTT_SELL 按钮:开空头仓位
      else if(button==EnumToString(BUTT_SELL))
        {
         //--- 获取相对于 StopLevel 的正确止损和止盈价位
         double sl=CorrectStopLoss(Symbol(),ORDER_TYPE_SELL,0,stoploss);
         double tp=CorrectTakeProfit(Symbol(),ORDER_TYPE_SELL,0,takeprofit);
         //--- 开空头仓位
         trade.Sell(lot,Symbol(),0,sl,tp);
        }
      //--- 如果 BUTT_SELL_LIMIT 按钮按下: 设置 SellLimit
      else if(button==EnumToString(BUTT_SELL_LIMIT))
        {
         //--- 获取相对于 StopLevel 的正确挂单位置
         double price_set=CorrectPricePending(Symbol(),ORDER_TYPE_SELL_LIMIT,distance_pending);
         //--- 参考 StopLevel,获取相对于订单放置级别的正确止损和止盈价位
         double sl=CorrectStopLoss(Symbol(),ORDER_TYPE_SELL_LIMIT,price_set,stoploss);
         double tp=CorrectTakeProfit(Symbol(),ORDER_TYPE_SELL_LIMIT,price_set,takeprofit);
         //--- 设置 SellLimit 挂单
         trade.SellLimit(lot,price_set,Symbol(),sl,tp);
        }
      //--- 如果 BUTT_SELL_STOP 按钮按下: 设置 SellStop
      else if(button==EnumToString(BUTT_SELL_STOP))
        {
         //--- 获取相对于 StopLevel 的正确挂单位置
         double price_set=CorrectPricePending(Symbol(),ORDER_TYPE_SELL_STOP,distance_pending);
         //--- 参考 StopLevel,获取相对于订单放置级别的正确止损和止盈价位
         double sl=CorrectStopLoss(Symbol(),ORDER_TYPE_SELL_STOP,price_set,stoploss);
         double tp=CorrectTakeProfit(Symbol(),ORDER_TYPE_SELL_STOP,price_set,takeprofit);
         //--- 设置 SellStop 挂单
         trade.SellStop(lot,price_set,Symbol(),sl,tp);
        }
      //--- 如果 BUTT_SELL_STOP_LIMIT 按钮按下: 设置 SellStopLimit
      else if(button==EnumToString(BUTT_SELL_STOP_LIMIT))
        {
         //--- 获取相对于 StopLevel 的正确 SellStop 挂单位置
         double price_set_stop=CorrectPricePending(Symbol(),ORDER_TYPE_SELL_STOP,distance_pending);
         //--- 参考 StopLevel,计算相对于 SellStop 价位的 SellLimit 挂单价格
         double price_set_limit=CorrectPricePending(Symbol(),ORDER_TYPE_SELL_LIMIT,distance_stoplimit,price_set_stop);
         //--- 参考 StopLevel,获取相对于订单放置级别的正确止损和止盈价位
         double sl=CorrectStopLoss(Symbol(),ORDER_TYPE_SELL_STOP,price_set_limit,stoploss);
         double tp=CorrectTakeProfit(Symbol(),ORDER_TYPE_SELL_STOP,price_set_limit,takeprofit);
         //--- 设置 SellStopLimit 挂单
         trade.OrderOpen(Symbol(),ORDER_TYPE_SELL_STOP_LIMIT,lot,price_set_limit,price_set_stop,sl,tp);
        }
      //--- 如果 BUTT_CLOSE_BUY 按钮被按下: 以最大利润平多头持仓
      else if(button==EnumToString(BUTT_CLOSE_BUY))
        {
         //--- 获取所有持仓的清单
         CArrayObj* list=engine.GetListMarketPosition();
         //--- 从列表中仅选择多头持仓
         list=CSelect::ByOrderProperty(list,ORDER_PROP_TYPE,POSITION_TYPE_BUY,EQUAL);
         //--- 参考佣金和隔夜利息,按利润对列表进行排序
         list.Sort(SORT_BY_ORDER_PROFIT_FULL);
         //--- 获取具有最大利润的多头持仓索引
         int index=CSelect::FindOrderMax(list,ORDER_PROP_PROFIT_FULL);
         if(index>WRONG_VALUE)
           {
            COrder* position=list.At(index);
            if(position!=NULL)
              {
               //--- 获取多头持仓票据,并按票证平仓
               trade.PositionClose(position.Ticket());
              }
           }
        }
      //--- 如果 BUTT_CLOSE_BUY2 按钮被按下: 以最大利润平一半多头持仓
      else if(button==EnumToString(BUTT_CLOSE_BUY2))
        {
         //--- 获取所有持仓的清单
         CArrayObj* list=engine.GetListMarketPosition();
         //--- 从列表中仅选择多头持仓
         list=CSelect::ByOrderProperty(list,ORDER_PROP_TYPE,POSITION_TYPE_BUY,EQUAL);
         //--- 参考佣金和隔夜利息,按利润对列表进行排序
         list.Sort(SORT_BY_ORDER_PROFIT_FULL);
         //--- 获取具有最大利润的多头持仓索引
         int index=CSelect::FindOrderMax(list,ORDER_PROP_PROFIT_FULL);
         if(index>WRONG_VALUE)
           {
            COrder* position=list.At(index);
            if(position!=NULL)
              {
               //--- 计算已平仓交易量,并按票证将多头持仓平一半
               if(engine.IsHedge())
                  trade.PositionClosePartial(position.Ticket(),NormalizeLot(position.Symbol(),position.Volume()/2.0));
               else
                  trade.Sell(NormalizeLot(position.Symbol(),position.Volume()/2.0));
              }
           }
        }
      //--- 如果 BUTT_CLOSE_BUY_BY_SELL 按钮被按下: 以最大利润逆向空头仓位平最大盈利多头仓位
      else if(button==EnumToString(BUTT_CLOSE_BUY_BY_SELL))
        {
         //--- 获取所有持仓的清单
         CArrayObj* list_buy=engine.GetListMarketPosition();
         //--- 从列表中仅选择多头持仓
         list_buy=CSelect::ByOrderProperty(list_buy,ORDER_PROP_TYPE,POSITION_TYPE_BUY,EQUAL);
         //--- 参考佣金和隔夜利息,按利润对列表进行排序
         list_buy.Sort(SORT_BY_ORDER_PROFIT_FULL);
         //--- 获取具有最大利润的多头持仓索引
         int index_buy=CSelect::FindOrderMax(list_buy,ORDER_PROP_PROFIT_FULL);
         //--- 获取所有持仓的清单
         CArrayObj* list_sell=engine.GetListMarketPosition();
         //--- 从列表中仅选择空头持仓
         list_sell=CSelect::ByOrderProperty(list_sell,ORDER_PROP_TYPE,POSITION_TYPE_SELL,EQUAL);
         //--- 参考佣金和隔夜利息,按利润对列表进行排序
         list_sell.Sort(SORT_BY_ORDER_PROFIT_FULL);
         //--- 获取具有最大利润的空头持仓索引
         int index_sell=CSelect::FindOrderMax(list_sell,ORDER_PROP_PROFIT_FULL);
         if(index_buy>WRONG_VALUE && index_sell>WRONG_VALUE)
           {
            //--- 选择具有最大利润的多头持仓
            COrder* position_buy=list_buy.At(index_buy);
            选择具有最大利润的空头持仓
            COrder* position_sell=list_sell.At(index_sell);
            if(position_buy!=NULL && position_sell!=NULL)
              {
               //--- 按逆向空头将多头平仓
               trade.PositionCloseBy(position_buy.Ticket(),position_sell.Ticket());
              }
           }
        }
      //--- 如果 BUTT_CLOSE_SELL 按钮被按下: 以最大利润平空头持仓
      else if(button==EnumToString(BUTT_CLOSE_SELL))
        {
         //--- 获取所有持仓的清单
         CArrayObj* list=engine.GetListMarketPosition();
         //--- 从列表中仅选择空头持仓
         list=CSelect::ByOrderProperty(list,ORDER_PROP_TYPE,POSITION_TYPE_SELL,EQUAL);
         //--- 参考佣金和隔夜利息,按利润对列表进行排序
         list.Sort(SORT_BY_ORDER_PROFIT_FULL);
         //--- 获取具有最大利润的空头持仓索引
         int index=CSelect::FindOrderMax(list,ORDER_PROP_PROFIT_FULL);
         if(index>WRONG_VALUE)
           {
            COrder* position=list.At(index);
            if(position!=NULL)
              {
               //--- 获取空头持仓票据,并按票证平仓
               trade.PositionClose(position.Ticket());
              }
           }
        }
      //--- 如果 BUTT_CLOSE_SELL2 按钮被按下: 以最大利润平一半空头持仓
      else if(button==EnumToString(BUTT_CLOSE_SELL2))
        {
         //--- 获取所有持仓的清单
         CArrayObj* list=engine.GetListMarketPosition();
         //--- 从列表中仅选择空头持仓
         list=CSelect::ByOrderProperty(list,ORDER_PROP_TYPE,POSITION_TYPE_SELL,EQUAL);
         //--- 参考佣金和隔夜利息,按利润对列表进行排序
         list.Sort(SORT_BY_ORDER_PROFIT_FULL);
         //--- 获取具有最大利润的空头持仓索引
         int index=CSelect::FindOrderMax(list,ORDER_PROP_PROFIT_FULL);
         if(index>WRONG_VALUE)
           {
            COrder* position=list.At(index);
            if(position!=NULL)
              {
               //--- 计算已平仓交易量,并按票证将空头持仓平一半
               if(engine.IsHedge())
                  trade.PositionClosePartial(position.Ticket(),NormalizeLot(position.Symbol(),position.Volume()/2.0));
               else
                  trade.Buy(NormalizeLot(position.Symbol(),position.Volume()/2.0));
              }
           }
        }
      //--- 如果 BUTT_CLOSE_SELL_BY_BUY 按钮被按下: 以最大利润逆向多头仓位平最大盈利空头仓位
      else if(button==EnumToString(BUTT_CLOSE_SELL_BY_BUY))
        {
         //--- 获取所有持仓的清单
         CArrayObj* list_sell=engine.GetListMarketPosition();
         //--- 从列表中仅选择空头持仓
         list_sell=CSelect::ByOrderProperty(list_sell,ORDER_PROP_TYPE,POSITION_TYPE_SELL,EQUAL);
         //--- 参考佣金和隔夜利息,按利润对列表进行排序
         list_sell.Sort(SORT_BY_ORDER_PROFIT_FULL);
         //--- 获取具有最大利润的空头持仓索引
         int index_sell=CSelect::FindOrderMax(list_sell,ORDER_PROP_PROFIT_FULL);
         //--- 获取所有持仓的清单
         CArrayObj* list_buy=engine.GetListMarketPosition();
         //--- 从列表中仅选择多头持仓
         list_buy=CSelect::ByOrderProperty(list_buy,ORDER_PROP_TYPE,POSITION_TYPE_BUY,EQUAL);
         //--- 参考佣金和隔夜利息,按利润对列表进行排序
         list_buy.Sort(SORT_BY_ORDER_PROFIT_FULL);
         //--- 获取具有最大利润的多头持仓索引
         int index_buy=CSelect::FindOrderMax(list_buy,ORDER_PROP_PROFIT_FULL);
         if(index_sell>WRONG_VALUE && index_buy>WRONG_VALUE)
           {
            选择具有最大利润的空头持仓
            COrder* position_sell=list_sell.At(index_sell);
            //--- 选择具有最大利润的多头持仓
            COrder* position_buy=list_buy.At(index_buy);
            if(position_sell!=NULL && position_buy!=NULL)
              {
               //--- 按逆向多头将空头平仓
               trade.PositionCloseBy(position_sell.Ticket(),position_buy.Ticket());
              }
           }
        }
      //--- 如果 BUTT_CLOSE_ALL 按钮被按下: 从最小盈利的持仓开始平所有持仓
      else if(button==EnumToString(BUTT_CLOSE_ALL))
        {
         //--- 获取所有持仓的清单
         CArrayObj* list=engine.GetListMarketPosition();
         if(list!=NULL)
           {
            //--- 参考佣金和隔夜利息,按利润对列表进行排序
            list.Sort(SORT_BY_ORDER_PROFIT_FULL);
            int total=list.Total();
            //--- 在循环中从利润最少的持仓开始
            for(int i=0;i<total;i++)
              {
               COrder* position=list.At(i);
               if(position==NULL)
                  continue;
               //--- 按票证平每笔持仓
               trade.PositionClose(position.Ticket());
              }
           }
        }
      //--- 如果按下 BUTT_DELETE_PENDING 按钮:删除首笔挂单
      else if(button==EnumToString(BUTT_DELETE_PENDING))
        {
         //--- 获取所有订单的列表
         CArrayObj* list=engine.GetListMarketPendings();
         if(list!=NULL)
           {
            //--- 按下单时间排序列表
            list.Sort(SORT_BY_ORDER_TIME_OPEN_MSC);
            int total=list.Total();
            //--- 在循环中从时间最久的持仓开始
            for(int i=total-1;i>=0;i--)
              {
               COrder* order=list.At(i);
               if(order==NULL)
                  continue;
               //--- 按票证删除订单
               trade.OrderDelete(order.Ticket());
              }
           }
        }
      //--- 如果 BUTT_PROFIT_WITHDRAWAL 按钮被按下: 从账户里出金
      if(button==EnumToString(BUTT_PROFIT_WITHDRAWAL))
        {
         //--- 如果程序是在测试器中启动
         if(MQLInfoInteger(MQL_TESTER))
           {
            //--- 模拟出金
            TesterWithdrawal(withdrawal);
           }
        }
      //--- 等待 1/10 秒
      Sleep(100);
      //--- “取消”按钮并重绘图表
      ButtonState(button_name,false);
      ChartRedraw();
     }
  }
//+------------------------------------------------------------------+

编译 EA,在对冲账户上启动它,并尝试按钮:


有关帐户事件的短消息显示在日志中,而图表注释则描述帐户上发生的最后一个事件。

现在我们切换到净持结算帐户,并启动测试:


在这种情况下,日志仅包含净持额结算账户上可能的仓位事件相关的条目 — 现在开立新仓,EA 处理单一持仓。 但是,分配给它的票据是不同的。 这可从开始时看出 — 当持仓从空头 #2 反转到多头 #3 之后。


下一步是什么?

接下来,我们将实现跟踪 StopLimit 订单激活,并准备跟踪订单和持仓修改的功能。

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

返回目录

系列中的前几篇文章:

第一部分 概念,数据管理。
第二部分 历史订单和成交集合交易。
第三部分 在场订单和持仓,分派搜索。
第四部分 交易事件, 概念。
第五部分 交易事件的类和集合, 将事件发送到程序。


全部回复

0/140

量化课程

    移动端课程