Sở hữu ngay máy chủ VPS Robot Forex khi giao dịch tại HotForex

Nội dung:

Trong phần đầu tiên của loạt bài viết , chúng ta đã bắt đầu tạo một thư viện đa nền tảng lớn đơn giản hóa việc phát triển các chương trình cho các nền tảng MetaTrader 5 và MetaTrader 4.
Trong phần thứ hai , chúng ta đã nối lại sự phát triển của thư viện và thực hiện việc thu thập các đơn đặt hàng và giao dịch lịch sử.

Ở đây chúng ta sẽ tạo một lớp để lựa chọn và sắp xếp các đơn hàng, giao dịch và vị trí thuận tiện trong danh sách bộ sưu tập, thực hiện đối tượng thư viện cơ sở có tên Engine và thêm bộ sưu tập các lệnh và vị trí thị trường vào thư viện.

Hiện tại, một cấu trúc lưu trữ dữ liệu nhất định đã xuất hiện. Chúng ta sẽ tuân thủ nó khi tạo các bộ sưu tập các loại đối tượng khác nhau:

Một đối tượng Engine duy nhất sẽ được tạo để lưu trữ và quản lý các bộ sưu tập, cũng như để trao đổi dữ liệu giữa chương trình và thư viện. Engine là để trở thành đối tượng cơ sở của toàn bộ thư viện. Các chương trình dựa trên thư viện là để tham khảo nó để có được dữ liệu. Bên cạnh đó, nó là để tích lũy toàn bộ thư viện tự động.

Sắp xếp tìm kiếm

Để sử dụng dữ liệu dễ dàng và thuận tiện từ các bộ sưu tập thư viện, chúng ta sẽ triển khai tìm kiếm, sắp xếp và hiển thị dữ liệu theo yêu cầu. Để đạt được điều này, hãy tạo một lớp đặc biệt và đặt tên là CSelect.

Tất cả các yêu cầu dữ liệu là để đi qua nó.

Trong thư mục Bộ sưu tập , tạo lớp CSelect mới . Không cần thiết phải đặt lớp cơ sở. Sau khi hoàn thành thao tác MQL Wizard, tệp Select.mqh mới được tạo trong thư mục Bộ sưu tập:

//+------------------------------------------------------------------+
//|                                                       Select.mqh |
//|                                  Copyright 2019, Forex 365 Corp. |
//|                                              https://forex365.vn |
//+------------------------------------------------------------------+
#property copyright "Copyright 2019, Forex 365 Corp."
#property link      "https://forex365.vn"
#property version   "1.00"
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
class CSelect
  {
private:

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

Để thực hiện tìm kiếm, đặt tất cả các chế độ của nó. Để làm điều đó, hãy tạo bảng liệt kê mô tả các chế độ so sánh đối tượng trong quá trình tìm kiếm. Phép liệt kê sẽ được tạo trong tệp Defines.mqh:

//+------------------------------------------------------------------+
//|                                                      Defines.mqh |
//|                                  Copyright 2019, Forex 365 Corp. |
//|                                              https://forex365.vn |
//+------------------------------------------------------------------+
#property copyright "Copyright 2019, Forex 365 Corp."
#property link      "https://forex365.vn"
//+------------------------------------------------------------------+
//| Macro substitutions                                              |
//+------------------------------------------------------------------+
#define COUNTRY_LANG   ("Russian")              // Country language
#define DFUN           (__FUNCTION__+": ")      // "Function description"
#define END_TIME       (D'31.12.3000 23:59:59') // Final data for requesting account history data
//+------------------------------------------------------------------+
//| Search                                                           |
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
//| Search data                                                      |
//+------------------------------------------------------------------+
enum ENUM_COMPARER_TYPE
  {
   EQUAL,                                                   // Equal
   MORE,                                                    // More
   LESS,                                                    // Less
   NO_EQUAL,                                                // Not equal
   EQUAL_OR_MORE,                                           // Equal or more
   EQUAL_OR_LESS                                            // Equal or less
  };
//+------------------------------------------------------------------+

Trong tệp lớp CSelect, từ thư viện chuẩn, kết nối lớp của danh sách các con trỏ động với các thể hiện đối tượng, lớp COrder và thư viện của các hàm dịch vụ DELib.mqh (cùng với tệp Defines.mqh). Bên cạnh đó, khai báo một đối tượng lưu trữ đặc biệt có sẵn cho toàn bộ thư viện ở cấp độ toàn cầu. Đó là lưu trữ các bản sao của danh sách được tạo trong quá trình sắp xếp. Nếu danh sách mới được tạo không được đính kèm vào một đối tượng lưu trữ, chúng sẽ bị xóa sau khi nhu cầu về chúng biến mất. Điều này có nghĩa là chúng ta cần phân bổ thêm tài nguyên cho việc theo dõi ở đâu, khi nào và tại sao chúng ta cần một danh sách nhất định có thể gây mất chuỗi logic và rò rỉ bộ nhớ do các đối tượng không bị xóa. Nếu chúng ta đính kèm chúng vào danh sách, việc theo dõi được thực hiện bởi hệ thống con thiết bị đầu cuối, nó luôn xóa cả danh sách lưu trữ và nội dung của nó theo thời gian.

An toàn & bảo mật vốn đầu tư tại HotForex

Để làm một so sánh, chúng ta cần tạo ra một phương pháp như vậy. Hãy khai báo một phương thức tĩnh mẫu để so sánh hai giá trị.

//+------------------------------------------------------------------+
//|                                                       Select.mqh |
//|                                  Copyright 2019, Forex 365 Corp. |
//|                                              https://forex365.vn |
//+------------------------------------------------------------------+
#property copyright "Copyright 2019, Forex 365 Corp."
#property link      "https://forex365.vn"
#property version   "1.00"
//+------------------------------------------------------------------+
//| Include files                                                    |
//+------------------------------------------------------------------+
#include <Arrays\ArrayObj.mqh>
#include "..\Objects\Order.mqh"
//+------------------------------------------------------------------+
//| Storage list                                                     |
//+------------------------------------------------------------------+
CArrayObj   ListStorage; // Storage object for storing sorted collection lists
//+------------------------------------------------------------------+
//| Class for sorting objects meeting the criterion                  |
//+------------------------------------------------------------------+
class CSelect
  {
private:
   //--- Method for comparing two values
   template<typename T>
   static bool       CompareValues(T value1,T value2,ENUM_COMPARER_TYPE mode);
public:

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

Thực hiện phương pháp so sánh:

//+------------------------------------------------------------------+
//| Method of comparing two values                                   |
//+------------------------------------------------------------------+
template<typename T>
bool CSelect::CompareValues(T value1,T value2,ENUM_COMPARER_TYPE mode)
  {
   return
     (
      mode==EQUAL && value1==value2          ?  true  :
      mode==NO_EQUAL && value1!=value2       ?  true  :
      mode==MORE && value1>value2            ?  true  :
      mode==LESS && value1<value2            ?  true  :
      mode==EQUAL_OR_MORE && value1>=value2  ?  true  :
      mode==EQUAL_OR_LESS && value1<=value2  ?  true  :  false
     );
  }
//+------------------------------------------------------------------+

Hai giá trị cùng loại và chế độ so sánh được truyền cho phương thức.
Tiếp theo, một so sánh đơn giản tùy thuộc vào phương pháp được áp dụng được thực hiện (bằng / không bằng / nhiều hơn / ít hơn / bằng hoặc nhiều hơn / bằng hoặc ít hơn) và kết quả được trả về.

Bây giờ hãy tạo một số phương pháp để tìm kiếm danh sách. Trong phần công khai của lớp CSelect, khai báo ba phương thức tĩnh để tìm kiếm đơn hàng theo một tiêu chí nhất định:

//+------------------------------------------------------------------+
//| Class for sorting objects meeting the criterion                  |
//+------------------------------------------------------------------+
class CSelect
  {
private:
   //--- Two values comparison method
   template<typename T>
   static bool       CompareValues(T value1,T value2,ENUM_COMPARER_TYPE mode);
public:
   //--- Return the list of orders with one out of (1) integer, (2) real and (3) string properties meeting a specified criterion
   static CArrayObj *ByOrderProperty(CArrayObj *list_source,ENUM_ORDER_PROP_INTEGER property,long value,ENUM_COMPARER_TYPE mode);
   static CArrayObj *ByOrderProperty(CArrayObj *list_source,ENUM_ORDER_PROP_DOUBLE property,double value,ENUM_COMPARER_TYPE mode);
   static CArrayObj *ByOrderProperty(CArrayObj *list_source,ENUM_ORDER_PROP_STRING property,string value,ENUM_COMPARER_TYPE mode);
  };
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
//| Return the list of orders having one of the integer              |
//| properties meeting a specified criterion                         |
//+------------------------------------------------------------------+
CArrayObj *CSelect::ByOrderProperty(CArrayObj *list_source,ENUM_ORDER_PROP_INTEGER property,long value,ENUM_COMPARER_TYPE mode)
  {
   if(list_source==NULL) return NULL;
   CArrayObj *list=new CArrayObj();
   if(list==NULL) return NULL;
   list.FreeMode(false);
   ListStorage.Add(list);
   int total=list_source.Total();
   for(int i=0; i<total; i++)
     {
      COrder *order=list_source.At(i);
      if(!order.SupportProperty(property)) continue;
      long order_prop=order.GetProperty(property);
      if(CompareValues(order_prop,value,mode)) list.Add(order);
     }
   return list;
  }
//+------------------------------------------------------------------+
//| Return the list of orders having one of the real                 |
//| properties meeting a specified criterion                         |
//+------------------------------------------------------------------+
CArrayObj *CSelect::ByOrderProperty(CArrayObj *list_source,ENUM_ORDER_PROP_DOUBLE property,double value,ENUM_COMPARER_TYPE mode)
  {
   if(list_source==NULL) return NULL;
   CArrayObj *list=new CArrayObj();
   if(list==NULL) return NULL;
   list.FreeMode(false);
   ListStorage.Add(list);
   for(int i=0; i<list_source.Total(); i++)
     {
      COrder *order=list_source.At(i);
      if(!order.SupportProperty(property)) continue;
      double order_prop=order.GetProperty(property);
      if(CompareValues(order_prop,value,mode)) list.Add(order);
     }
   return list;
  }
//+------------------------------------------------------------------+
//| Return the list of orders having one of the string               |
//| properties meeting a specified criterion                         |
//+------------------------------------------------------------------+
CArrayObj *CSelect::ByOrderProperty(CArrayObj *list_source,ENUM_ORDER_PROP_STRING property,string value,ENUM_COMPARER_TYPE mode)
  {
   if(list_source==NULL) return NULL;
   CArrayObj *list=new CArrayObj();
   if(list==NULL) return NULL;
   list.FreeMode(false);
   ListStorage.Add(list);
   for(int i=0; i<list_source.Total(); i++)
     {
      COrder *order=list_source.At(i);
      if(!order.SupportProperty(property)) continue;
      string order_prop=order.GetProperty(property);
      if(CompareValues(order_prop,value,mode)) list.Add(order);
     }
   return list;
  }
//+------------------------------------------------------------------+

Chúng ta hãy xem xét kỹ hơn bằng cách sử dụng tìm kiếm theo tiêu chí chuỗi làm ví dụ:

  • Phương thức nhận con trỏ vào danh sách bộ sưu tập , một thuộc tính mà danh sách mới sẽ được dựa trên và chế độ so sánh . Các tài sản phải đáp ứng các tiêu chí tìm kiếm.
  • Sau đó, danh sách được kiểm tra tính hợp lệ và NULL được trả về nếu nó không hợp lệ.
  • Nếu kiểm tra được thông qua, hãy tạo một đối tượng danh sách mới và đặt cờ quản lý bộ nhớ thủ công cho nó . Nếu điều này không được thực hiện, thì khi đối tượng danh sách này bị xóa, tất cả các con trỏ tới các đối tượng thứ tự được lưu trữ trong bộ sưu tập cũng bị xóa, điều này không thể chấp nhận được. Bạn có thể tìm thấy các chi tiết trong trợ giúp trong danh sách động của con trỏ, đặc biệt là phương pháp này .
  • Tiếp theo, thêm danh sách vừa tạo vào danh sách lưu trữ và bắt đầu sắp xếp các đối tượng trong một vòng lặp:
    • nhận được một đơn đặt hàng từ danh sách . Nếu nó không hỗ trợ một thuộc tính được chỉ định trong tìm kiếm, hãy bỏ qua nó và chọn thuộc tính tiếp theo .
    • Tiếp theo, thuộc tính thứ tự  được so sánh với thuộc tính được truyền để so sánh theo chế độ đã chỉ định (nhiều hơn / ít hơn / bằng, v.v.). Nếu tiêu chí so sánh được đáp ứng, thứ tự này sẽ được thêm vào danh sách mới được tạo .
    • Vào cuối vòng lặp, danh sách được trả về chương trình gọi .

Thêm sáu phương thức khác để tìm kiếm và trả về một chỉ mục đơn hàng với giá trị tối đa và tối thiểu của thuộc tính được chỉ định:

//+------------------------------------------------------------------+
//| Class for sorting objects meeting the criterion                  |
//+------------------------------------------------------------------+
class CSelect
  {
private:
   //--- Two values comparison method
   template<typename T>
   static bool       CompareValues(T value1,T value2,ENUM_COMPARER_TYPE mode);
public:
   //--- Return the list of orders with one out of (1) integer, (2) real and (3) string properties meeting a specified criterion
   static CArrayObj *ByOrderProperty(CArrayObj *list_source,ENUM_ORDER_PROP_INTEGER property,long value,ENUM_COMPARER_TYPE mode);
   static CArrayObj *ByOrderProperty(CArrayObj *list_source,ENUM_ORDER_PROP_DOUBLE property,double value,ENUM_COMPARER_TYPE mode);
   static CArrayObj *ByOrderProperty(CArrayObj *list_source,ENUM_ORDER_PROP_STRING property,string value,ENUM_COMPARER_TYPE mode);
   //--- Return the order index with the maximum value of the (1) integer, (2) real and (3) string properties
   static int        FindOrderMax(CArrayObj* list_source,ENUM_ORDER_PROP_INTEGER property);
   static int        FindOrderMax(CArrayObj* list_source,ENUM_ORDER_PROP_DOUBLE property); 
   static int        FindOrderMax(CArrayObj* list_source,ENUM_ORDER_PROP_STRING property); 
   //--- Return the order index with the minimum value of the (1) integer, (2) real and (3) string properties
   static int        FindOrderMin(CArrayObj* list_source,ENUM_ORDER_PROP_INTEGER property);
   static int        FindOrderMin(CArrayObj* list_source,ENUM_ORDER_PROP_DOUBLE property); 
   static int        FindOrderMin(CArrayObj* list_source,ENUM_ORDER_PROP_STRING property); 
  };
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
//| Return the order index in the list                               |
//| with the maximum integer property value                          |
//+------------------------------------------------------------------+
int CSelect::FindOrderMax(CArrayObj *list_source,ENUM_ORDER_PROP_INTEGER property)
  {
   if(list_source==NULL) return WRONG_VALUE;
   int index=0;
   COrder *max_order=NULL;
   int total=list_source.Total();
   if(total==0) return WRONG_VALUE;
   for(int i=1; i<total; i++)
     {
      COrder *order=list_source.At(i);
      long order1_prop=order.GetProperty(property);
      max_order=list_source.At(index);
      long order2_prop=max_order.GetProperty(property);
      if(CompareValues(order1_prop,order2_prop,MORE)) index=i;
     }
   return index;
  }
//+------------------------------------------------------------------+
//| Return the order index in the list                               |
//| with the maximum real property value                             |
//+------------------------------------------------------------------+
int CSelect::FindOrderMax(CArrayObj *list_source,ENUM_ORDER_PROP_DOUBLE property)
  {
   if(list_source==NULL) return WRONG_VALUE;
   int index=0;
   COrder *max_order=NULL;
   int total=list_source.Total();
   if(total==0) return WRONG_VALUE;
   for(int i=1; i<total; i++)
     {
      COrder *order=list_source.At(i);
      double order1_prop=order.GetProperty(property);
      max_order=list_source.At(index);
      double order2_prop=max_order.GetProperty(property);
      if(CompareValues(order1_prop,order2_prop,MORE)) index=i;
     }
   return index;
  }
//+------------------------------------------------------------------+
//| Return the order index in the list                               |
//| with the maximum string property value                           |
//+------------------------------------------------------------------+
int CSelect::FindOrderMax(CArrayObj *list_source,ENUM_ORDER_PROP_STRING property)
  {
   if(list_source==NULL) return WRONG_VALUE;
   int index=0;
   COrder *max_order=NULL;
   int total=list_source.Total();
   if(total==0) return WRONG_VALUE;
   for(int i=1; i<total; i++)
     {
      COrder *order=list_source.At(i);               
      string order1_prop=order.GetProperty(property);
      max_order=list_source.At(index);                   
      string order2_prop=max_order.GetProperty(property);
      if(CompareValues(order1_prop,order2_prop,MORE)) index=i;
     }
   return index;
  }
//+------------------------------------------------------------------+

Chúng ta hãy xem xét kỹ hơn bằng cách tìm kiếm chỉ mục đơn hàng với giá trị chuỗi tối đa:

  • Phương thức nhận con trỏ đến danh sách bộ sưu tập và thuộc tính để tìm kiếm một đơn hàng có giá trị tối đa của thuộc tính đó.
  • Sau đó, danh sách được kiểm tra tính hợp lệ và WRONG_VALUE (-1) được trả về nếu nó không hợp lệ.
  • Khai báo chỉ mục đơn hàng với giá trị tối đa , khởi tạo nó bằng 0 và tạo một đối tượng thứ tự trống để lưu trữ các giá trị so sánh.
  • Tiếp theo, di chuyển dọc theo danh sách bộ sưu tập từ thứ tự thứ hai trong một vòng lặp:
    • Lấy giá trị đích với chỉ mục vòng lặp từ đơn hàng, lấy giá trị đích với chỉ mục ‘chỉ mục’ từ đơn hàng và so sánh hai giá trị thu được. Nếu giá trị đích của đơn hàng đầu tiên (với chỉ số vòng lặp) vượt quá thứ tự của đơn hàng thứ hai (với chỉ mục ‘chỉ mục’), chỉ định chỉ mục của đơn hàng có giá trị lớn hơn cho biến ‘chỉ mục’.
    • Khi hoàn thành vòng lặp, biến ‘chỉ mục’ sẽ có chỉ mục của đơn hàng có giá trị mục tiêu cao nhất. Trả nó về chương trình gọi .

Các phương thức trả về chỉ mục đơn hàng có giá trị thấp nhất của thuộc tính được chỉ định được sắp xếp tương tự:

//+------------------------------------------------------------------+
//| Return the order index in the list                               |
//| with the minimum integer property value                          |
//+------------------------------------------------------------------+
int CSelect::FindOrderMin(CArrayObj* list_source,ENUM_ORDER_PROP_INTEGER property)
  {
   int index=0;
   COrder* min_order=NULL;
   int total=list_source.Total();
   if(total==0) return WRONG_VALUE;
   for(int i=1; i<total; i++){
      COrder* order=list_source.At(i);
      long order1_prop=order.GetProperty(property);
      min_order=list_source.At(index);
      long order2_prop=min_order.GetProperty(property);
      if(CompareValues(order1_prop,order2_prop,LESS)) index=i;
      }
   return index;
  }
//+------------------------------------------------------------------+
//| Return the order index in the list                               |
//| with the minimum real property value                             |
//+------------------------------------------------------------------+
int CSelect::FindOrderMin(CArrayObj* list_source,ENUM_ORDER_PROP_DOUBLE property)
  {
   int index=0;
   COrder* min_order=NULL;
   int total=list_source.Total();
   if(total== 0) return WRONG_VALUE;
   for(int i=1; i<total; i++){
      COrder* order=list_source.At(i);
      double order1_prop=order.GetProperty(property);
      min_order=list_source.At(index);
      double order2_prop=min_order.GetProperty(property);
      if(CompareValues(order1_prop,order2_prop,LESS)) index=i;
      }
   return index;
  }
//+------------------------------------------------------------------+
//| Return the order index in the list                               |
//| with the minimum string property value                           |
//+------------------------------------------------------------------+
int CSelect::FindOrderMin(CArrayObj* list_source,ENUM_ORDER_PROP_STRING property)
  {
   int index=0;
   COrder* min_order=NULL;
   int total=list_source.Total();
   if(total==0) return WRONG_VALUE;
   for(int i=1; i<total; i++){
      COrder* order=list_source.At(i);
      string order1_prop=order.GetProperty(property);
      min_order=list_source.At(index);
      string order2_prop=min_order.GetProperty(property);
      if(CompareValues(order1_prop,order2_prop,LESS)) index=i;
      }
   return index;
  }
//+------------------------------------------------------------------+

Bây giờ chúng ta có thể thêm sắp xếp danh sách bộ sưu tập theo thời gian và tiêu chí được chỉ định vào lớp bộ sưu tập các đơn đặt hàng lịch sử.

Đầu tiên, thêm tùy chọn sắp xếp theo thời gian vào tệp Defines.mqh:

//+------------------------------------------------------------------+
//|                                                      Defines.mqh |
//|                                  Copyright 2019, Forex 365 Corp. |
//|                                              https://forex365.vn |
//+------------------------------------------------------------------+
#property copyright "Copyright 2019, Forex 365 Corp."
#property link      "https://forex365.vn"

//+------------------------------------------------------------------+
//| Macro substitutions                                              |
//+------------------------------------------------------------------+
#define COUNTRY_LANG   ("Russian")              // Country language
#define DFUN           (__FUNCTION__+": ")      // "Function description"
#define END_TIME       (D'31.12.3000 23:59:59') // Final data for requesting account history data
//+------------------------------------------------------------------+
//| Search                                                           |
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
//| Search and sorting data                                          |
//+------------------------------------------------------------------+
enum ENUM_COMPARER_TYPE
  {
   EQUAL,                                                   // Equal
   MORE,                                                    // More
   LESS,                                                    // Less
   NO_EQUAL,                                                // Not equal
   EQUAL_OR_MORE,                                           // Equal or more
   EQUAL_OR_LESS                                            // Equal or less
  };
//+------------------------------------------------------------------+
//| Possible options of selecting by time                            |
//+------------------------------------------------------------------+
enum ENUM_SELECT_BY_TIME
  {
   SELECT_BY_TIME_OPEN,                                     // By open time
   SELECT_BY_TIME_CLOSE,                                    // By close time
   SELECT_BY_TIME_OPEN_MSC,                                 // By open time in milliseconds
   SELECT_BY_TIME_CLOSE_MSC,                                // By close time in milliseconds
  };
//+------------------------------------------------------------------+

Include lớp CSelect vào tệp HistoryCollection.mqh. Để thực hiện việc này, thay thế chuỗi bao gồm các chức năng dịch vụ bằng chuỗi bao gồm tệp lớp CSelect:

//+------------------------------------------------------------------+
//| Include files                                                    |
//+------------------------------------------------------------------+
#include <Arrays\ArrayObj.mqh>
#include "..\DELib.mqh"
#include "..\Objects\HistoryOrder.mqh"
#include "..\Objects\HistoryPending.mqh"
#include "..\Objects\HistoryDeal.mqh"
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
//| Include files                                                    |
//+------------------------------------------------------------------+
#include <Arrays\ArrayObj.mqh>
#include "Select.mqh"
#include "..\Objects\HistoryOrder.mqh"
#include "..\Objects\HistoryPending.mqh"
#include "..\Objects\HistoryDeal.mqh"
//+------------------------------------------------------------------+

Bây giờ, chúng ta đã bao gồm tệp lớp CSelect thay vì các chức năng dịch vụ. Chúng ta đã bao gồm Order.mqh cho Select.mqh, trong khi tệp chức năng dịch vụ đã được bao gồm trong tệp Order.mqh.

Trong phần công khai của lớp CHistoryCollection, hãy khai báo phương thức chọn đơn hàng từ bộ sưu tập theo thời gian đã chỉ định trong phạm vi ngày, trong phần riêng tư, thêm lớp thứ tự trừu tượng COrder để hoạt động như một thứ tự mẫu để tìm kiếm Các giá trị:

//+------------------------------------------------------------------+
//| Collection of historical orders and deals                        |
//+------------------------------------------------------------------+
class CHistoryCollection
  {
private:
   CArrayObj         m_list_all_orders;      // List of all historical orders and deals
   COrder            m_order_instance;       // Order object to search by a property
   bool              m_is_trade_event;       // Trading event flag
   int               m_index_order;          // Index of the last order added to the collection from the terminal history list (MQL4, MQL5)
   int               m_index_deal;           // Index of the last deal added to the collection from the terminal history list (MQL5)
   int               m_delta_order;          // Difference in the number of orders as compared to the past check
   int               m_delta_deal;           // Difference in the number of deals as compared to the past check
public:
   //--- Return the full collection list 'as is'
   CArrayObj        *GetList(void) { return &m_list_all_orders;  }
   //--- Select orders from the collection with time from begin_time to end_time
   CArrayObj        *GetListByTime(const datetime begin_time=0,const datetime end_time=0,
                                   const ENUM_SELECT_BY_TIME select_time_mode=SELECT_BY_TIME_CLOSE);
   //--- Constructor
                     CHistoryCollection();
   //--- Update the list of orders, fill data on the number of new ones and set the trading event flag
   void              Refresh(void);
  };
//+------------------------------------------------------------------+

Trước khi thực hiện phương thức chọn các đơn hàng từ bộ sưu tập trong phạm vi ngày, hãy thêm các phương thức đặt các thuộc tính số nguyên, thực và chuỗi vào lớp thứ tự trừu tượng COrder trong tệp Order.mqh (trong trường hợp này là phương thức viết số nguyên Thuộc tính được yêu cầu để thêm tham số vào thứ tự dữ liệu thời gian mẫu):

public:
   //--- Set (1) integer, (2) real and (3) string order properties
   void              SetProperty(ENUM_ORDER_PROP_INTEGER property,long value) { m_long_prop[property]=value;                     }
   void              SetProperty(ENUM_ORDER_PROP_DOUBLE property,long value)  { m_long_prop[property]=value;                     }
   void              SetProperty(ENUM_ORDER_PROP_STRING property,long value)  { m_long_prop[property]=value;                     }
   //--- Return (1) integer, (2) real and (3) string order properties from the properties array
   long              GetProperty(ENUM_ORDER_PROP_INTEGER property)      const { return m_long_prop[property];                    }
   double            GetProperty(ENUM_ORDER_PROP_DOUBLE property)       const { return m_double_prop[this.IndexProp(property)];  }
   string            GetProperty(ENUM_ORDER_PROP_STRING property)       const { return m_string_prop[this.IndexProp(property)];  }

   //--- Return the flag of the order supporting the property
   virtual bool      SupportProperty(ENUM_ORDER_PROP_INTEGER property)        { return true; }
   virtual bool      SupportProperty(ENUM_ORDER_PROP_DOUBLE property)         { return true; }
   virtual bool      SupportProperty(ENUM_ORDER_PROP_STRING property)         { return true; }

   //--- Compare COrder objects with one another by all possible properties
   virtual int       Compare(const CObject *node,const int mode=0) const;

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

Trong tệp HistoryCollection.mqh, thực hiện phương pháp chọn đơn hàng từ bộ sưu tập trong phạm vi ngày:

//+------------------------------------------------------------------+
//| Select orders from the collection                                |
//| from begin_time to end_time                                      |
//+------------------------------------------------------------------+
CArrayObj *CHistoryCollection::GetListByTime(const datetime begin_time=0,const datetime end_time=0,
                                             const ENUM_SELECT_BY_TIME select_time_mode=SELECT_BY_TIME_CLOSE)
  {
   ENUM_ORDER_PROP_INTEGER property=
     (
      select_time_mode==SELECT_BY_TIME_CLOSE       ?  ORDER_PROP_TIME_CLOSE      : 
      select_time_mode==SELECT_BY_TIME_OPEN        ?  ORDER_PROP_TIME_OPEN       :
      select_time_mode==SELECT_BY_TIME_CLOSE_MSC   ?  ORDER_PROP_TIME_CLOSE_MSC  : 
      ORDER_PROP_TIME_OPEN_MSC
     );

   CArrayObj *list=new CArrayObj();
   if(list==NULL)
     {
      ::Print(DFUN+TextByLanguage("Ошибка создания временного списка","Error creating temporary list"));
      return NULL;
     }
   datetime begin=begin_time,end=(end_time==0 ? END_TIME : end_time);
   if(begin_time>end_time) begin=0;
   list.FreeMode(false); 
   ListStorage.Add(list);
   //---
   m_order_instance.SetProperty(property,begin);
   int index_begin=m_list_all_orders.SearchGreatOrEqual(&m_order_instance);
   if(index_begin==WRONG_VALUE)
      return list;
   m_order_instance.SetProperty(property,end);
   int index_end=m_list_all_orders.SearchLessOrEqual(&m_order_instance);
   if(index_end==WRONG_VALUE)
      return list;
   for(int i=index_begin; i<=index_end; i++)
      list.Add(m_list_all_orders.At(i));
   return list;
  }
//+------------------------------------------------------------------+

Vậy chúng ta có gì ở đây?

  • Phương thức nhận thời gian bắt đầu phạm vi lịch sử bắt buộc , thời gian kết thúc phạm vi và chế độ chọn ngày phạm vi từ bảng liệt kê ENUM_SELECT_BY_TIME.
  • Thuộc tính cần thiết để tìm kiếm và so sánh trong các thuộc tính thứ tự được đặt tùy thuộc vào thời gian danh sách được sắp xếp theo. Nếu nó đã được sắp xếp theo thời gian mở, thuộc tính tìm kiếm sẽ là thời gian mở. Nếu nó đã được sắp xếp theo thời gian đóng, thời gian đóng được so sánh trong các đơn đặt hàng, v.v.
  • Các danh sách mới sau đó được tạo ra. Nó sẽ chứa các đơn đặt hàng tương ứng với tiêu chí phạm vi ngày và cuối cùng sẽ được trả lại cho chương trình gọi.
  • Phạm vi ngày bắt đầu và ngày kết thúc sau đó được kiểm tra.
  • Nếu số không được chuyển thành ngày kết thúc phạm vi, hãy đặt ngày tối đa trong tương lai , sau đó kiểm tra ngày bắt đầu phạm vi. Nếu vượt quá ngày kết thúc phạm vi, ngày sớm nhất được đặt làm ngày bắt đầu phạm vi . Trong trường hợp này, nếu ngày được đặt không chính xác, danh sách từ đầu lịch sử tài khoản cho đến hết phạm vi được cung cấp. Một ngày trước đó được coi là bắt đầu phạm vi, trong khi một ngày gần với thời gian hiện tại được coi là kết thúc phạm vi.
  • Các lá cờ của công tác quản lý bộ nhớ bằng tay (đã nói ở trên) sau đó được thiết lập cho danh sách mới được tạo ra, và danh sách được gắn vào đối tượng lưu trữ .
  • Tiếp theo, ngày ban đầu được đặt thành thứ tự mẫu để tìm kiếm trong danh sách bộ sưu tập và việc tìm kiếm chỉ mục đơn hàng với ngày ban đầu được thực hiện bằng phương thức SearchGreatOrEqual() được kế thừa bởi lớp COrder từ CObject gốc.
  • Nếu không tìm thấy chỉ mục, điều này có nghĩa là không có đơn hàng nào muộn hơn ngày đã chỉ định và danh sách trống được trả về .
  • Tiếp theo, điều tương tự cũng được thực hiện khi tìm kiếm ngày kết thúc: ngày kết thúc được sử dụng trong tìm kiếm được đặt thành mẫu và tìm kiếm chỉ mục đơn hàng với ngày kết thúc bằng phương thức SearchLessOrEqual () được thực hiện. Nếu không tìm thấy chỉ mục, điều này có nghĩa là không có đơn hàng nào sớm hơn ngày đích và danh sách trống được trả về.
  • Tiếp theo, tất cả các đơn đặt hàng trong phạm vi được thêm vào danh sách trong vòng lặp từ chỉ mục thứ tự ngày bắt đầu đến thứ tự ngày kết thúc một, và danh sách đã điền được trả về chương trình gọi.

Trong phần công khai, khai báo các phương thức trả về danh sách theo các thuộc tính số nguyên, thực và chuỗi đã chọn đáp ứng tiêu chí so sánh:

//+------------------------------------------------------------------+
//| Collection of historical orders and deals                        |
//+------------------------------------------------------------------+
class CHistoryCollection
  {
private:
   CArrayObj         m_list_all_orders;      // List of all historical orders and deals
   COrder            m_order_instance;       // Order object to search by a property
   bool              m_is_trade_event;       // Trading event flag
   int               m_index_order;          // Index of the last order added to the collection from the terminal history list (MQL4, MQL5)
   int               m_index_deal;           // Index of the last deal added to the collection from the terminal history list (MQL5)
   int               m_delta_order;          // Difference in the number of orders as compared to the past check
   int               m_delta_deal;           // Difference in the number of deals as compared to the past check
public:
   //--- Return the full collection list 'as is'
   CArrayObj        *GetList(void) { return &m_list_all_orders;  }
   //--- Select orders from the collection with time from begin_time to end_time
   CArrayObj        *GetListByTime(const datetime begin_time=0,const datetime end_time=0,
                                   const ENUM_SELECT_BY_TIME select_time_mode=SELECT_BY_TIME_CLOSE);
   //--- Return the list by selected (1) integer, (2) real and (3) string properties meeting the compared criterion
   CArrayObj*        GetList(ENUM_ORDER_PROP_INTEGER property,long value,ENUM_COMPARER_TYPE mode=EQUAL)  { return CSelect::ByOrderProperty(this.GetList(),property,value,mode); }
   CArrayObj*        GetList(ENUM_ORDER_PROP_DOUBLE property,double value,ENUM_COMPARER_TYPE mode=EQUAL) { return CSelect::ByOrderProperty(this.GetList(),property,value,mode); }
   CArrayObj*        GetList(ENUM_ORDER_PROP_STRING property,string value,ENUM_COMPARER_TYPE mode=EQUAL) { return CSelect::ByOrderProperty(this.GetList(),property,value,mode); }
   //--- Constructor
                     CHistoryCollection();
   //--- Update the list of orders, fill data on the number of new ones and set the trading event flag
   void              Refresh(void);
  };
//+------------------------------------------------------------------+

Phương thức nhận thuộc tính đích của đơn hàng, giá trị để so sánh và chế độ so sánh (nhiều hơn / ít hơn / bằng / không bằng / bằng hoặc nhiều hơn / bằng hoặc ít hơn). Danh sách được sắp xếp theo các thuộc tính, giá trị và phương thức so sánh được yêu cầu được trả về với sự trợ giúp của các phương thức lớp CSelect được mô tả trước đó.

Hãy kiểm tra nhận các danh sách cần thiết bằng các phương pháp khác nhau.

//+------------------------------------------------------------------+
//|                                           TestDoEasyPart03_1.mq5 |
//|                                  Copyright 2019, Forex 365 Corp. |
//|                                              https://forex365.vn |
//+------------------------------------------------------------------+
#property copyright "Copyright 2019, Forex 365 Corp."
#property link      "https://forex365.vn"
#property version   "1.00"
//--- includes
#include <DoEasy\Collections\HistoryCollection.mqh>
//--- enums
enum ENUM_TYPE_ORDERS
  {
   TYPE_ORDER_MARKET,   // Market orders
   TYPE_ORDER_PENDING,  // Pending orders
   TYPE_ORDER_DEAL      // Deals
  };
//--- input parameters
input ENUM_TYPE_ORDERS  InpOrderType   =  TYPE_ORDER_DEAL;  // Show type:
input datetime          InpTimeBegin   =  0;                // Start date of required range
input datetime          InpTimeEnd     =  END_TIME;         // End date of required range  
//--- global variables
CHistoryCollection history;
//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- update history
   history.Refresh();
//--- get collection list in the date range
   CArrayObj* list=history.GetListByTime(InpTimeBegin,InpTimeEnd,SELECT_BY_TIME_CLOSE);
   if(list==NULL)
     {
      Print("Could not get collection list");
      return INIT_FAILED;
     }
   int total=list.Total();
   for(int i=0;i<total;i++)
     {
      //--- get the order from the list
      COrder* order=list.At(i);
      if(order==NULL) continue;
      //--- if this is a deal
      if(order.Status()==ORDER_STATUS_DEAL && InpOrderType==TYPE_ORDER_DEAL)
         order.Print();
      //--- if this is a historical market order
      if(order.Status()==ORDER_STATUS_HISTORY_ORDER && InpOrderType==TYPE_ORDER_MARKET)
         order.Print();
      //--- if this is a removed pending order
      if(order.Status()==ORDER_STATUS_HISTORY_PENDING && InpOrderType==TYPE_ORDER_PENDING)
         order.Print();
     }
//---
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
//---
   
  }
//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
  {
//---
   
  }
//+------------------------------------------------------------------+

Bây giờ, thay vì danh sách đầy đủ, chúng ta nhận được danh sách được chọn theo phạm vi ngày đã chỉ định bằng phương thức GetListByTime (). Biên dịch và khởi chạy EA với các cài đặt mặc định. Tất cả các giao dịch cho toàn bộ lịch sử tài khoản được hiển thị trong journal tab:

Nhấn F7 và đặt ngày kết thúc của phạm vi yêu cầu trong cài đặt. Cá nhân tôi đã nhập lịch sử tài khoản, xác định khi nào việc bổ sung diễn ra, xác định ngày giao dịch tiếp theo (giao dịch đầu tiên xảy ra sau khi bổ sung tài khoản)

Và chọn phạm vi sao cho giao dịch đầu tiên nằm ngoài phạm vi đó: 2018.01.22 – 2018.02.01.
Do đó, chỉ có một thỏa thuận (bổ sung tài khoản) được hiển thị trên journal tab:

Bây giờ, hãy lưu EA TestDo EASPart03_1.mq5 dưới tên TestDo EASPart03_2mq5. Xóa các đầu vào và thay đổi cách nhận dữ liệu về giao dịch:

//+------------------------------------------------------------------+
//|                                           TestDoEasyPart03_2.mq5 |
//|                                  Copyright 2019, Forex 365 Corp. |
//|                                              https://forex365.vn |
//+------------------------------------------------------------------+
#property copyright "Copyright 2019, Forex 365 Corp."
#property link      "https://forex365.vn"
#property version   "1.00"
//--- includes
#include <DoEasy\Collections\HistoryCollection.mqh>
//--- global variables
CHistoryCollection history;
//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- update history
   history.Refresh();
//--- receive only deals from the collection list
   CArrayObj* list=history.GetList(ORDER_PROP_STATUS,ORDER_STATUS_DEAL,EQUAL);
//--- sort the obtained list by balance operations
   list=CSelect::ByOrderProperty(list,ORDER_PROP_TYPE,DEAL_TYPE_BALANCE,EQUAL);
   if(list==NULL)
     {
      Print("Could not get collection list");
      return INIT_FAILED;
     }
   int total=list.Total();
   for(int i=0;i<total;i++)
     {
      //--- get the order from the list
      COrder* order=list.At(i);
      if(order==NULL) continue;
      order.Print();
     }
//---
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
//---
   
  }
//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
  {
//---
   
  }
//+------------------------------------------------------------------+

Đầu tiên, lấy danh sách tất cả các giao dịch (đánh dấu các trạng thái thứ tự của loại giao dịch trong danh sách) và sắp xếp danh sách thu được theo loại ‘hoạt động cân bằng’. Chế độ so sánh bằng được sử dụng trong cả hai trường hợp.
Do đó, chỉ hoạt động ‘Bổ sung cân bằng’ được hiển thị trên journal – tạp chí:

Mặc dù trong ví dụ trước, chúng ta phải xem xét phạm vi giao dịch trong tab lịch sử tài khoản của thiết bị đầu cuối để hiển thị hoạt động số dư, ở đây chúng ta đã lấy được ngay sau khi sắp xếp danh sách theo tiêu chí bắt buộc.

Bất kỳ cách nào khác để có được dữ liệu cần thiết đều theo cùng một nguyên tắc. Ví dụ, để có được ‘bổ sung cân bằng’ tương tự, chúng ta có thể tìm thấy các chỉ số của các giao dịch có lợi nhuận cao nhất và ít nhất.

Ví dụ trong EA thử nghiệm TestDo EASPart03_3.mq5:

//+------------------------------------------------------------------+
//|                                           TestDoEasyPart03_3.mq5 |
//|                                  Copyright 2019, Forex 365 Corp. |
//|                                              https://forex365.vn |
//+------------------------------------------------------------------+
#property copyright "Copyright 2019, Forex 365 Corp."
#property link      "https://forex365.vn"
#property version   "1.00"
//--- includes
#include <DoEasy\Collections\HistoryCollection.mqh>
//--- global variables
CHistoryCollection history;
//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- update history
   history.Refresh();
//--- receive only deals from the collection list
   CArrayObj* list=history.GetList(ORDER_PROP_STATUS,ORDER_STATUS_DEAL,EQUAL);
   if(list==NULL)
     {
      Print(TextByLanguage("Не удалось получить список","Could not get list"));
      return INIT_FAILED;
     }
//--- Get the index of the most profitable deal (first balance replenishment)
   int index=CSelect::FindOrderMax(list,ORDER_PROP_PROFIT);
   if(index!=WRONG_VALUE)
     {
      //--- get the deal from the list by index
      COrder* order=list.At(index);
      if(order!=NULL)
         order.Print();
     }
   else
      Print(TextByLanguage("Не найден индекс ордера с максимальным значением профита","Order index with maximum profit value not found"));
//--- Get the index of the least profitable deal
   index=CSelect::FindOrderMin(list,ORDER_PROP_PROFIT);
   if(index!=WRONG_VALUE)
     {
      //--- get the deal from the list by index
      COrder* order=list.At(index);
      if(order!=NULL)
         order.Print();
     }
   else
      Print(TextByLanguage("Не найден индекс ордера с минимальным значением профита","Order index with minimum profit value not found"));
//---
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
//---
   
  }
//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
  {
//---
   
  }
//+------------------------------------------------------------------+

Sau khi hoàn thành, hai giao dịch được hiển thị trên tạp chí – một giao dịch có lợi nhuận cao nhất (bổ sung số dư) và một giao dịch có lợi nhuận ít nhất.

Đối tượng cơ sở động cơ là lõi thư viện

Một chương trình tùy chỉnh làm việc kết hợp với thư viện sẽ gửi dữ liệu đến thư viện và nhận dữ liệu từ nó. Để đạt được điều này, sẽ thuận tiện hơn khi có một lớp duy nhất kết nối với chương trình và tích lũy tất cả các hành động có thể để liên lạc giữa thư viện và chương trình. Tất nhiên, lớp nên đảm nhận tất cả các chức năng dịch vụ có thể sẽ hoạt động ở chế độ nền và sẽ không yêu cầu bất kỳ hành động tốn kém nào từ người dùng. Do đó, chúng ta sẽ tạo một lớp cơ bản làm cơ sở thư viện và đặt tên là Engine .

Trong thư mục gốc của thư viện, tạo lớp CEngine mới dựa trên CObject cơ bản và kết nối lớp bộ sưu tập lịch sử với nó:

//+------------------------------------------------------------------+
//|                                                       Engine.mqh |
//|                                  Copyright 2019, Forex 365 Corp. |
//|                                              https://forex365.vn |
//+------------------------------------------------------------------+
#property copyright "Copyright 2019, Forex 365 Corp."
#property link      "https://forex365.vn"
#property version   "1.00"
//+------------------------------------------------------------------+
//| Include files                                                    |
//+------------------------------------------------------------------+
#include "Collections\HistoryCollection.mqh"
//+------------------------------------------------------------------+
//| Library basis class                                              |
//+------------------------------------------------------------------+
class CEngine : public CObject
  {
private:
//--- Collection of historical orders and deals
   CHistoryCollection   m_history;
public:
                        CEngine();
                       ~CEngine();
  };
//+------------------------------------------------------------------+
//| CEngine constructor                                              |
//+------------------------------------------------------------------+
CEngine::CEngine()
  {
  }
//+------------------------------------------------------------------+
//| CEngine destructor                                               |
//+------------------------------------------------------------------+
CEngine::~CEngine()
  {
  }
//+------------------------------------------------------------------+

Tất cả các hành động chúng ta đã thực hiện trong khi thực hiện việc thu thập các đơn đặt hàng lịch sử trong các EA thử nghiệm đã được thực hiện trong trình xử lý OnInit (). Nói cách khác, chúng chỉ được thực hiện một lần khi khởi chạy EA, biên dịch lại hoặc thay đổi các tham số của nó. Điều này là đủ để kiểm tra nhanh nhưng không được chấp nhận trong chương trình làm việc. Vì vậy, hãy bắt đầu sắp xếp mọi thứ theo thứ tự.

Đầu tiên, tạo trình xử lý OnTimer () trong phần chung của lớp, cũng như việc triển khai bên ngoài thân lớp để cập nhật tất cả các bộ sưu tập:

//+------------------------------------------------------------------+
//|                                                       Engine.mqh |
//|                                  Copyright 2019, Forex 365 Corp. |
//|                                              https://forex365.vn |
//+------------------------------------------------------------------+
#property copyright "Copyright 2019, Forex 365 Corp."
#property link      "https://forex365.vn"
#property version   "1.00"
//+------------------------------------------------------------------+
//| Include files                                                    |
//+------------------------------------------------------------------+
#include "Collections\HistoryCollection.mqh"
//+------------------------------------------------------------------+
//| Library basis class                                              |
//+------------------------------------------------------------------+
class CEngine : public CObject
  {
private:
//--- Collection of historical orders and deals
   CHistoryCollection   m_history;
public:
//--- Timer
   void                 OnTimer(void);
                        CEngine();
                       ~CEngine();
  };
//+------------------------------------------------------------------+
//| CEngine constructor                                              |
//+------------------------------------------------------------------+
CEngine::CEngine()
  {
  }
//+------------------------------------------------------------------+
//| CEngine destructor                                               |
//+------------------------------------------------------------------+
CEngine::~CEngine()
  {
  }
//+------------------------------------------------------------------+
//| CEngine timer                                                    |
//+------------------------------------------------------------------+
void CEngine::OnTimer(void)
  {
   
  }
//+------------------------------------------------------------------+

Chúng ta hãy tạo lớp dịch vụ (bộ đếm thời gian) vì rất có thể sẽ có độ trễ hẹn giờ khác nhau cho các sự kiện khác nhau. Lớp này là để đếm thời gian trễ cần thiết và một thể hiện bộ đếm riêng biệt sẽ được khai báo cho mỗi bộ đếm thời gian.

Đầu tiên, thêm các thay thế macro mới vào các tệp Defines.mqh. Chỉ định tần suất của bộ đếm thời gian thư viện và bộ đếm thời gian bộ đếm tạm dừng tính bằng mili giây, bộ đếm bộ đếm thời gian bộ sưu tập và ID của đơn đặt hàng lịch sử và giao dịch bộ đếm thời gian cập nhật trong chúng (khi phát triển thư viện, bạn có thể cần một số bộ đếm yêu cầu ID riêng lẻ)

//+------------------------------------------------------------------+
//| Macro substitutions                                              |
//+------------------------------------------------------------------+
#define COUNTRY_LANG             ("Russian")                // Country language
#define DFUN                     (__FUNCTION__+": ")        // "Function description"
#define END_TIME                 (D'31.12.3000 23:59:59')   // End date for requesting account history data
#define TIMER_FREQUENCY          (16)                       // Minimal frequency of the library timer in milliseconds
#define COLLECTION_PAUSE         (250)                      // Orders and deals collection timer pause in milliseconds
#define COLLECTION_COUNTER_STEP  (16)                       // Increment of the orders and deals collection timer counter
#define COLLECTION_COUNTER_ID    (1)                        // Orders and deals collection timer counter ID
//+------------------------------------------------------------------+

Trong thư mục gốc của thư viện, hãy tạo thư mục Dịch vụ mới, cũng như lớp CTimerCorer mới trong đó. Di chuyển tệp chức năng dịch vụ DELib.mqh vào thư mục này ngay lập tức. Đây là nơi thích hợp cho nó.
Sau khi di chuyển DELib.mqh sang thư mục mới, hãy thay đổi địa chỉ của tệp chức năng dịch vụ trong tệp Order.mqh thay thế địa chỉ:

//+------------------------------------------------------------------+
//|                                                        Order.mqh |
//|                                  Copyright 2019, Forex 365 Corp. |
//|                                              https://forex365.vn |
//+------------------------------------------------------------------+
#property copyright "Copyright 2019, Forex 365 Corp."
#property link      "https://forex365.vn"
#property version   "1.00"
#property strict    // Necessary for mql4
//+------------------------------------------------------------------+
//| Include files                                                    |
//+------------------------------------------------------------------+
#include <Object.mqh>
#include "..\DELib.mqh"
//+------------------------------------------------------------------+

thay bằng:

//+------------------------------------------------------------------+
//|                                                        Order.mqh |
//|                                  Copyright 2019, Forex 365 Corp. |
//|                                              https://forex365.vn |
//+------------------------------------------------------------------+
#property copyright "Copyright 2019, Forex 365 Corp."
#property link      "https://forex365.vn"
#property version   "1.00"
#property strict    // Necessary for mql4
//+------------------------------------------------------------------+
//| Include files                                                    |
//+------------------------------------------------------------------+
#include <Object.mqh>
#include "..\Services\DELib.mqh"
//+------------------------------------------------------------------+

Bây giờ hãy xem xét lớp bộ đếm thời gian. Lớp học đơn giản. Vì vậy, hãy nhìn vào danh sách của nó và tập trung vào hoạt động của nó:

//+------------------------------------------------------------------+
//|                                                 TimerCounter.mqh |
//|                                  Copyright 2019, Forex 365 Corp. |
//|                                              https://forex365.vn |
//+------------------------------------------------------------------+
#property copyright "Copyright 2019, Forex 365 Corp."
#property link      "https://forex365.vn"
#property version   "1.00"
//+------------------------------------------------------------------+
//| Include files                                                    |
//+------------------------------------------------------------------+
#include <Object.mqh>
#include "DELib.mqh"
//+------------------------------------------------------------------+
//| Timer counter class                                              |
//+------------------------------------------------------------------+
class CTimerCounter : public CObject
  {
private:  
   int               m_counter_id;   
   ulong             m_counter;      
   ulong             m_counter_step; 
   ulong             m_counter_pause;
public:
   //--- Return the waiting completion flag
   bool              IsTimeDone(void);
   //--- Set the counter parameters
   void              SetParams(const ulong step,const ulong pause)         { this.m_counter_step=step; this.m_counter_pause=pause;  }
   //--- Return th counter id
   virtual  int      Type(void)                                      const { return this.m_counter_id;                              }
   //--- Compare counter objects
   virtual int       Compare(const CObject *node,const int mode=0)   const;
   //--- Constructor
                     CTimerCounter(const int id);
  };
//+------------------------------------------------------------------+
//| CTimerCounter constructor                                        |
//+------------------------------------------------------------------+
CTimerCounter::CTimerCounter(const int id) : m_counter(0),m_counter_step(16),m_counter_pause(16)
  {
   this.m_counter_id=id;
  }
//+------------------------------------------------------------------+
//| CTimerCounter returns the pause completion flag                  |
//+------------------------------------------------------------------+
bool CTimerCounter::IsTimeDone(void)
  {
   if(this.m_counter>=ULONG_MAX)
      this.m_counter=0;
   if(this.m_counter<this.m_counter_pause)
     {
      this.m_counter+=this.m_counter_step;
      return false;
     }
   this.m_counter=0;
   return true;
  }
//+------------------------------------------------------------------+
//| Compare CTimerCounter objects by id                              |
//+------------------------------------------------------------------+
int CTimerCounter::Compare(const CObject *node,const int mode=0) const
  {
   const CTimerCounter *counter_compared=node;
   int value_compared=counter_compared.Type();
   int value_current=this.Type();
   return(value_current>value_compared ? 1 : value_current<value_compared ? -1 : 0);
   return 0;
  }
  
//+------------------------------------------------------------------+

Vì chúng ta đã di chuyển DELib.mqh vào cùng một thư mục nơi đặt lớp đối tác, nên chúng ta nên đưa nó trực tiếp từ cùng một thư mục . Defines.mqh được đưa vào DELib.mqh, có nghĩa là lớp sẽ thấy tất cả các thay thế vĩ mô.

  • Bốn biến thành viên lớp được khai báo trong phần tin: timer ID , hẹn giờ truy cập , hẹn giờ increment và tạm dừng .
  • Các phương pháp thiết lập các thông số truy cập cần thiết nằm ở phần nào. Phương pháp vượt qua bước hẹn giờ và tạm dừng . Các giá trị được truyền ngay lập tức được gán cho các biến thành viên lớp.
  • Trong hàm tạo của lớp , các tham số mặc định của bộ đếm thời gian (0) , bước hẹn giờ (16) và tạm dừng hẹn giờ (16) được chỉ định trong danh sách khởi tạo. Các tham số bước và tạm dừng cho phép bộ định thời hoạt động không chậm trễ, trong khi chờ thời gian tạm dừng đạt được.
  • Giá trị được truyền bởi đầu vào được gán cho ID bộ đếm trong thân công cụ xây dựng lớp.

Phương thức trả về cờ hoàn thành tạm dừng được sắp xếp theo cách đơn giản:

  • Đầu tiên, hệ thống kiểm tra tràn bộ đếm biến . Nếu giá trị vượt quá mức tối đa có thể, bộ đếm được đặt lại về 0.
  • Tiếp theo, hệ thống so sánh bộ đếm thời gian và giá trị tạm dừng . Nếu giá trị bộ đếm nhỏ hơn giá trị tạm dừng, số gia được thêm vào bộ đếm và ‘false’ được trả về.
  • Nếu giá trị bộ đếm vượt quá hoặc bằng với tạm dừng, bộ đếm được đặt lại và sự kiện hoàn thành thời gian chờ bộ đếm được trả về .

Phương thức trả về ID bộ đếm Loại () được tạo ảo. Trong phân phối lớp CObject, có một phương thức ảo trả về loại đối tượng:

//--- method of identifying the object
   virtual int       Type(void)                                    const { return(0);      }

Giả định rằng phương thức này sẽ được định nghĩa lại trong các lớp con cháu và trả về ID của đối tượng lớp con cháu CObject (kiểu \u003d 0 được trả về cho chính CObject). Hãy sử dụng cơ hội này và trả lại ID truy cập bằng cách xác định lại phương thức ảo:

 virtual  int      Type(void)                                    const { return this.m_counter_id; }

Phương pháp ảo để so sánh hai đối tượng truy cập rất đơn giản:

//+------------------------------------------------------------------+
//| Compare CTimerCounter objects by id                              |
//+------------------------------------------------------------------+
int CTimerCounter::Compare(const CObject *node,const int mode=0) const
  {
   const CTimerCounter *counter_compared=node;
   int value_compared=counter_compared.Type();
   int value_current=this.Type();
   return(value_current>value_compared ? 1 : value_current<value_compared ? -1 : 0);
   return 0;
  }
//+------------------------------------------------------------------+

Nhận một liên kết đến đối tượng nguồn, lấy ID của nó và lấy ID của bộ đếm hiện tại. Tiếp theo, trả về kết quả của một so sánh đơn giản bằng nhiều hơn / ít hơn / bằng nhau.

Hãy tiếp tục điền vào lớp CEngine. Bao gồm lớp bộ đếm thời gian vào tệp Engine.mqh:

//+------------------------------------------------------------------+
//| Include files                                                    |
//+------------------------------------------------------------------+
#include "Collections\HistoryCollection.mqh"
#include "Services\TimerCounter.mqh"
//+------------------------------------------------------------------+

Thêm đối tượng danh sách của các bộ đếm thời gian, cũng như phương thức trả về chỉ mục bộ đếm trong danh sách bằng ID của nó cho phần riêng tư và phương thức tạo bộ đếm cho công chúng (để phương thức có thể truy cập được từ bên ngoài và Có thể tạo các quầy tùy chỉnh trong các chương trình).

Trong trình xây dựng lớp, khởi tạo bộ đếm thời gian mili giây, đặt cờ danh sách đã sắp xếp và tạo bộ đếm của bộ đếm thời gian cập nhật bộ sưu tập lịch sử và giao dịch. Đặt hủy bộ định thời trong bộ hủy lớp:

//+------------------------------------------------------------------+
//| Library basis class                                              |
//+------------------------------------------------------------------+
class CEngine : public CObject
  {
private:
//--- List of timer counters
   CArrayObj            m_list_counters;           
//--- Collection of historical orders and deals
   CHistoryCollection   m_history;
//--- Return the counter index by id
   int                  CounterIndex(const int id) const;
public:
//--- Create the timer counter
   void                 CreateCounter(const int counter_id,const ulong frequency,const ulong pause);
//--- Timer
   void                 OnTimer(void);
                        CEngine();
                       ~CEngine();
  };
//+------------------------------------------------------------------+
//| CEngine constructor                                              |
//+------------------------------------------------------------------+
CEngine::CEngine()
  {
   ::EventSetMillisecondTimer(TIMER_FREQUENCY);
   this.m_list_counters.Sort();
   this.CreateCounter(COLLECTION_COUNTER_ID,COLLECTION_COUNTER_STEP,COLLECTION_MIN_PAUSE);
  }
//+------------------------------------------------------------------+
//| CEngine destructor                                               |
//+------------------------------------------------------------------+
CEngine::~CEngine()
  {
   ::EventKillTimer();
  }
//+------------------------------------------------------------------+

Thực hiện phương thức trả về chỉ mục truy cập bằng ID của nó:

//+------------------------------------------------------------------+
//| Return the counter index in the list by id                       |
//+------------------------------------------------------------------+
int CEngine::CounterIndex(const int id) const
  {
   int total=this.m_list_counters.Total();
   for(int i=0;i<total;i++)
     {
      CTimerCounter* counter=this.m_list_counters.At(i);
      if(counter==NULL) continue;
      if(counter.Type()==id) 
         return i;
     }
   return WRONG_VALUE;
  }
//+------------------------------------------------------------------+

Vì hầu như không có nhiều quầy, tôi đã sắp xếp bảng liệt kê đơn giản nhất có tính năng tìm kiếm và so sánh. Nếu bộ đếm có ID này được tìm thấy trong danh sách, hệ thống sẽ trả về chỉ mục của nó trong danh sách, nếu không, hệ thống sẽ trả về -1.

Hãy xem xét phương pháp tạo bộ đếm thời gian:

//+------------------------------------------------------------------+
//| Create the timer counter                                         |
//+------------------------------------------------------------------+
void CEngine::CreateCounter(const int id,const ulong step,const ulong pause)
  {
   if(this.CounterIndex(id)>WRONG_VALUE)
     {
      ::Print(TextByLanguage("Ошибка. Уже создан счётчик с идентификатором ","Error. Already created counter with id "),(string)id);
      return;
     }
   m_list_counters.Sort();
   CTimerCounter* counter=new CTimerCounter(id);
   if(counter==NULL)
      ::Print(TextByLanguage("Не удалось создать счётчик таймера ","Failed to create timer counter "),(string)id);
   counter.SetParams(step,pause);
   if(this.m_list_counters.Search(counter)==WRONG_VALUE)
      this.m_list_counters.Add(counter);
   else
     {
      string t1=TextByLanguage("Ошибка. Счётчик с идентификатором ","Error. Counter with ID ")+(string)id;
      string t2=TextByLanguage(", шагом ",", step ")+(string)step;
      string t3=TextByLanguage(" и паузой "," and pause ")+(string)pause;
      ::Print(t1+t2+t3+TextByLanguage(" уже существует"," already exists"));
      delete counter;
     }
  }
//+------------------------------------------------------------------+

Đầu tiên, kiểm tra ID truy cập được truyền cho phương thức. Nếu một ID như vậy đã có sẵn, hãy hiển thị thông báo phù hợp trong nhật ký và thoát khỏi phương thức – bộ đếm có ID này đã tồn tại.
Vì việc tìm kiếm chỉ có thể được tiến hành trong một danh sách được sắp xếp, nên cờ phân loại được đặt cho danh sách, một đối tượng truy cập mới được tạo, việc tạo thành công của nó được kiểm tra và các thuộc tính bắt buộc của bộ đếm được đặt.

Sau đó, việc tìm kiếm cho cùng một bộ đếm được thực hiện trong danh sách. Nếu nó không được tìm thấy, bộ đếm mới được thêm vào danh sách.
Mặt khác, thông báo chứa tất cả các tham số được hình thành và hiển thị trong tạp chí. Sau đó, đối tượng truy cập được loại bỏ vì chúng ta đã có một đối tượng tương tự.

Kiểm tra cuối cùng cho tất cả các tham số của bộ đếm mới được tạo phù hợp với bộ đếm đã tồn tại hiện đang dư thừa – Kiểm tra ID ngay từ đầu phương thức ngăn chặn việc tạo một đối tượng có ID đã có trong danh sách. Tôi đã để nó cho những thay đổi có thể trong tương lai.
Để cho lớp CEngine biết khi nào cần xử lý tình huống giao dịch, cần lưu ý về những thay đổi đã xảy ra trong số lượng đơn đặt hàng và giao dịch lịch sử. Để làm điều này, hãy thêm các phương thức trả về số lượng đơn đặt hàng và giao dịch mới xuất hiện trong danh sách vào lớp CHistoryCollection:

//+------------------------------------------------------------------+
//| Collection of historical orders and deals                        |
//+------------------------------------------------------------------+
class CHistoryCollection
  {
private:
   CArrayObj         m_list_all_orders;      // List of all historical orders and deals
   COrder            m_order_instance;       // Order object to search by a property
   bool              m_is_trade_event;       // Trading event flag
   int               m_index_order;          // Index of the last order added to the collection from the terminal history list (MQL4, MQL5)
   int               m_index_deal;           // Index of the last deal added to the collection from the terminal history list (MQL5)
   int               m_delta_order;          // Difference in the number of orders as compared to the past check
   int               m_delta_deal;           // Difference in the number of deals as compared to the past check
public:
   //--- Select orders from the collection with time from begin_time to end_time
   CArrayObj        *GetListByTime(const datetime begin_time=0,const datetime end_time=0,
                                   const ENUM_SELECT_BY_TIME select_time_mode=SELECT_BY_TIME_CLOSE);
   //--- Return the full collection list 'as is'
   CArrayObj        *GetList(void)                                                                       { return &m_list_all_orders;                                            }
   //--- Return the list by selected (1) integer, (2) real and (3) string properties meeting the compared criterion
   CArrayObj        *GetList(ENUM_ORDER_PROP_INTEGER property,long value,ENUM_COMPARER_TYPE mode=EQUAL)  { return CSelect::ByOrderProperty(this.GetList(),property,value,mode);  }
   CArrayObj        *GetList(ENUM_ORDER_PROP_DOUBLE property,double value,ENUM_COMPARER_TYPE mode=EQUAL) { return CSelect::ByOrderProperty(this.GetList(),property,value,mode);  }
   CArrayObj        *GetList(ENUM_ORDER_PROP_STRING property,string value,ENUM_COMPARER_TYPE mode=EQUAL) { return CSelect::ByOrderProperty(this.GetList(),property,value,mode);  }
   //--- Return the number of (1) new orders and (2) new deals
   int               NewOrders(void)                                                                     { return m_delta_order; }
   int               NewDeals(void)                                                                      { return m_delta_deal;  }
   
   //--- Constructor
                     CHistoryCollection();
   //--- Update the list of orders, fill data on the number of new ones and set the trading event flag
   void              Refresh(void);
  };
//+------------------------------------------------------------------+

Các phương thức chỉ đơn giản trả về các giá trị của các biến thành viên lớp thích hợp.

Bây giờ trong CEngine, chúng ta có thể kiểm tra trạng thái của chúng trong bộ đếm thời gian của lớp:

//+------------------------------------------------------------------+
//| CEngine timer                                                    |
//+------------------------------------------------------------------+
void CEngine::OnTimer(void)
  {
   //--- Timer of the historical orders and deals collection
   int index=this.CounterIndex(COLLECTION_COUNTER_ID);
   if(index>WRONG_VALUE)
     {
      CTimerCounter* counter=this.m_list_counters.At(index);
      if(counter.IsTimeDone())
        {
         this.m_history.Refresh();
         if(this.m_history.NewOrders()>0)
           {
            Print(DFUN,TextByLanguage("Изменилось количество исторических ордеров: NewOrders=","Number of historical orders changed: NewOrders="),this.m_history.NewOrders());
           }
         if(this.m_history.NewDeals()>0)
           {
            Print(DFUN,TextByLanguage("Изменилось количество сделок: NewDeals=","Number of deals changed: NewDeals="),this.m_history.NewOrders());
           }
        }
     }
  }
//+------------------------------------------------------------------+

Lấy chỉ mục của bộ đếm thời gian của bộ sưu tập lịch sử và giao dịch của bộ đếm thời gian, đưa con trỏ đến bộ đếm thời gian theo chỉ số của nó, kiểm tra việc hoàn thành thời gian trễ của bộ hẹn giờ và cập nhật bộ sưu tập (chỉ đơn hàng hoặc giao dịch được thêm lần cuối nếu có).

Nếu số lượng đơn đặt hàng lịch sử thay đổi, hiển thị thông báo trong tạp chí. Các giao dịch cũng vậy.
Hãy tạo một EA đơn giản để kiểm tra. Trong Experts\TestDo EAS\Part3, tạo EA với bộ đếm thời gian có tên TestDo EASPart03_4.mq5. Để tạo mẫu của EA bằng bộ hẹn giờ, hãy kiểm tra OnTimer trên trang thứ hai của MQL Wizard:

Nhấn Next cho đến khi hoàn thành thao tác của Wizard. Kết quả là một mẫu EA trống được tạo ra. Kết nối tệp thư viện chính với nó, tạo đối tượng lớp thư viện và gọi bộ định thời thư viện trong bộ định thời EA.

//+------------------------------------------------------------------+
//|                                           TestDoEasyPart03_4.mq5 |
//|                                  Copyright 2019, Forex 365 Corp. |
//|                                              https://forex365.vn |
//+------------------------------------------------------------------+
#property copyright "Copyright 2019, Forex 365 Corp."
#property link      "https://forex365.vn"
#property version   "1.00"
//--- includes
#include <DoEasy\Engine.mqh>
//--- global variables
CEngine        engine;
//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
//---

//---
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
//---
   
  }
//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
  {
//---
   
  }
//+------------------------------------------------------------------+
//| Timer function                                                   |
//+------------------------------------------------------------------+
void OnTimer()
  {
   engine.OnTimer();
  }
//+------------------------------------------------------------------+

Đây là tất cả những gì bắt buộc phải được thực hiện trong EA để có được dữ liệu về những thay đổi trong lịch sử tài khoản.

Nếu chúng ta khởi chạy EA ngay bây giờ, đặt một lệnh chờ xử lý và xóa nó, một mục liên quan đến việc thay đổi số lượng đơn đặt hàng lịch sử sẽ xuất hiện trong tạp chí.
Nếu chúng ta mở một vị trí, hai mục xuất hiện trong tạp chí:

  1. liên quan đến sự thay đổi số lượng đơn đặt hàng (lệnh thị trường mở đã được gửi) và
  2. liên quan đến sự thay đổi số lượng giao dịch (lệnh thị trường đã được kích hoạt và tạo ra thỏa thuận “Gia nhập thị trường”).

Nếu chúng ta đóng một vị trí, hai mục sẽ xuất hiện lại trong tạp chí:

  1. về sự xuất hiện của lệnh đóng cửa thị trường
  2. liên quan đến sự xuất hiện của thỏa thuận mới (lệnh đóng cửa thị trường đã được kích hoạt và tạo ra thỏa thuận “Thoát khỏi thị trường”)

Trong lần ra mắt đầu tiên, thông báo về các đơn đặt hàng và giao dịch thay đổi trong lịch sử tài khoản được hiển thị trên tạp chí. Điều này xảy ra bởi vì thư viện đọc toàn bộ lịch sử trong lần khởi chạy đầu tiên, do đó, sự khác biệt giữa số lượng đơn đặt hàng và giao dịch (thư viện không biết gì về chúng trong lần khởi chạy đầu tiên) và số lượng tất cả các đơn đặt hàng và giao dịch được tính trong toàn bộ tài khoản vòng lặp lịch sử sẽ bằng số đầy đủ của họ. Điều này là bất tiện và không cần thiết. Do đó, chúng ta cần loại bỏ các thông báo thay đổi số giả như vậy. Chúng ta có thể làm điều đó theo hai cách:

  1. không hiển thị gì trên tạp chí trong lần ra mắt đầu tiên
  2. hiển thị thông báo trạng thái tài khoản trong lần khởi chạy đầu tiên

Vì chúng ta sẽ triển khai bộ sưu tập và hiển thị các số liệu thống kê cần thiết trong các phần tiếp theo của mô tả, bây giờ chúng ta hãy sử dụng tùy chọn đầu tiên (không hiển thị gì trong tạp chí trong lần khởi chạy đầu tiên) và chỉ tạo sơ khai cho lần khởi chạy đầu tiên:

Trong phần riêng của lớp CEngine, thêm cờ của lần khởi chạy đầu tiên và phương thức kiểm tra và đặt lại cờ:

//+------------------------------------------------------------------+
//| Library basis class                                              |
//+------------------------------------------------------------------+
class CEngine : public CObject
  {
private:
   CHistoryCollection   m_history;                       // Collection of historical orders and deals
   CArrayObj            m_list_counters;                 // List of timer counters
   bool                 m_first_start;                   // First launch flag
//--- Return the counter index by id
   int                  CounterIndex(const int id) const;
//--- Return the first launch flag
   bool                 IsFirstStart(void);
public:
//--- Create the timer counter
   void                 CreateCounter(const int id,const ulong frequency,const ulong pause);
//--- Timer
   void                 OnTimer(void);
                        CEngine();
                       ~CEngine();
  };
//+------------------------------------------------------------------+

Và thêm việc thực hiện phương thức kiểm tra và đặt lại cờ khởi chạy đầu tiên ngoài thân lớp:

//+------------------------------------------------------------------+
//| Return the first launch flag, reset the flag                     |
//+------------------------------------------------------------------+
bool CEngine::IsFirstStart(void)
  {
   if(this.m_first_start)
     {
      this.m_first_start=false;
      return true;             
     }
   return false;
  }
//+------------------------------------------------------------------+

Tất cả đều rất đơn giản ở đây: nếu cờ được đặt, hãy đặt lại và trả về ‘true’, nếu không thì trả về ‘false’.

Bây giờ chúng ta cần đặt cờ trong danh sách khởi tạo, để nó luôn sẵn sàng để được kích hoạt.

//+------------------------------------------------------------------+
//| CEngine constructor                                              |
//+------------------------------------------------------------------+
CEngine::CEngine() : m_first_start(true)
  {
   ::EventSetMillisecondTimer(TIMER_FREQUENCY);
   this.m_list_counters.Sort();
   this.CreateCounter(COLLECTION_COUNTER_ID,COLLECTION_COUNTER_STEP,COLLECTION_PAUSE);
  }
//+------------------------------------------------------------------+

Tất cả đã được thiết lập. Từ giờ trở đi, không có sự kiện lịch sử tài khoản không cần thiết nào liên quan đến phép tính lịch sử đầu tiên sẽ xuất hiện trong lần khởi chạy chương trình đầu tiên.

Chúng ta có thể kiểm tra điều này bằng cách khởi chạy EA thử nghiệm từ MQL5 \ Experts \ TestDo EAS \ Part2 \ TestDo EASPart03_4.mq5 và đảm bảo rằng không có thông báo nào về việc thêm đơn đặt hàng và giao dịch vào lịch sử tài khoản xuất hiện trong tạp chí Experts trong lần ra mắt đầu tiên.

Đối tượng của các lệnh và vị trí thị trường đang hoạt động

Tôi tin rằng, bây giờ đã đến lúc tạm thời ngừng thêm chức năng vào lớp CEngine và bắt đầu thực hiện đối tượng và việc thu thập các lệnh và vị trí thị trường. Sau khi triển khai hoàn tất, chúng ta sẽ tiếp tục phát triển chức năng của đối tượng cơ sở Động cơ, vì chức năng này ảnh hưởng đến cả lịch sử của tài khoản và trạng thái hiện tại của tài khoản.

Trong thư mục Đối tượng của thư viện, hãy tạo lớp CMarketPocation mới dựa trên thứ tự trừu tượng của thư viện COrder -đây là một đối tượng vị trí thị trường:

Sau khi nhấp vào Kết thúc, một mẫu lớp có tên MarketPocation.mqh được tạo. Chúng ta hãy thêm lớp COrder vào nó ngay lập tức:

//+------------------------------------------------------------------+
//|                                               MarketPosition.mqh |
//|                                  Copyright 2019, Forex 365 Corp. |
//|                                              https://forex365.vn |
//+------------------------------------------------------------------+
#property copyright "Copyright 2019, Forex 365 Corp."
#property link      "https://forex365.vn"
#property version   "1.00"
//+------------------------------------------------------------------+
//| Include files                                                    |
//+------------------------------------------------------------------+
#include "Order.mqh"
//+------------------------------------------------------------------+
//| Market position                                                  |
//+------------------------------------------------------------------+
class CMarketPosition : public COrder
  {
private:

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

Thay đổi hàm tạo để vé vị trí được chuyển đến nó, đặt trạng thái ‘vị trí thị trường’ cho lớp cha (COrder) trong danh sách khởi tạo và gửi vé cho nó. Khai báo ba phương thức ảo trả về cờ của các thuộc tính số nguyên, thực và chuỗi theo vị trí:

//+------------------------------------------------------------------+
//| Market position                                                  |
//+------------------------------------------------------------------+
class CMarketPosition : public COrder
  {
public:
   //--- Constructor
                     CMarketPosition(const ulong ticket=0) : COrder(ORDER_STATUS_MARKET_POSITION,ticket) {}
   //--- Supported position properties (1) real, (2) integer
   virtual bool      SupportProperty(ENUM_ORDER_PROP_INTEGER property);
   virtual bool      SupportProperty(ENUM_ORDER_PROP_DOUBLE property);
   virtual bool      SupportProperty(ENUM_ORDER_PROP_STRING property);
  };
//+------------------------------------------------------------------+

và thêm thực hiện các phương thức này bên ngoài lớp

//+------------------------------------------------------------------+
//| Return 'true' if the position supports the passed                |
//| integer property, otherwise return 'false'                       |
//+------------------------------------------------------------------+
bool CMarketPosition::SupportProperty(ENUM_ORDER_PROP_INTEGER property)
  {
   if(property==ORDER_PROP_TIME_CLOSE     || 
      property==ORDER_PROP_TIME_CLOSE_MSC ||
      property==ORDER_PROP_TIME_EXP       ||
      property==ORDER_PROP_POSITION_BY_ID ||
      property==ORDER_PROP_DEAL_ORDER     ||
      property==ORDER_PROP_DEAL_ENTRY     ||
      property==ORDER_PROP_CLOSE_BY_SL    ||
      property==ORDER_PROP_CLOSE_BY_TP
     #ifdef __MQL5__                      ||
      property==ORDER_PROP_TICKET_FROM    ||
      property==ORDER_PROP_TICKET_TO
     #endif 
     ) return false;
   return true;
}
//+------------------------------------------------------------------+
//| Return 'true' if the position supports the passed                |
//| real property, otherwise return 'false'                          |
//+------------------------------------------------------------------+
bool CMarketPosition::SupportProperty(ENUM_ORDER_PROP_DOUBLE property)
  {
   if(property==ORDER_PROP_PRICE_CLOSE || property==ORDER_PROP_PRICE_STOP_LIMIT) return false;
   return true;
  }
//+------------------------------------------------------------------+
//| Return 'true' if the position supports the passed                |
//| string property, otherwise return 'false'                        |
//+------------------------------------------------------------------+
bool CMarketPosition::SupportProperty(ENUM_ORDER_PROP_STRING property)
  {
   if(property==ORDER_PROP_EXT_ID) return false;
   return true;
  }
//+------------------------------------------------------------------+

Tất cả mọi thứ ở đây tương tự như việc tạo ra các đối tượng của các đơn đặt hàng và giao dịch lịch sử được thảo luận trong phần thứ hai của mô tả thư viện.

Bây giờ hãy tạo một đối tượng đặt hàng đang chờ xử lý thị trường theo cách tương tự. Hãy tạo một CMarketPending lớp mới dựa trên thứ tự trừu tượng của thư viện COrder trong thư mục Đối tượng và nhập các thay đổi đã quen thuộc của mẫu lớp được tạo bởi MQL Wizard cho nó:

//+------------------------------------------------------------------+
//|                                                MarketPending.mqh |
//|                                  Copyright 2019, Forex 365 Corp. |
//|                                              https://forex365.vn |
//+------------------------------------------------------------------+
#property copyright "Copyright 2019, Forex 365 Corp."
#property link      "https://forex365.vn"
#property version   "1.00"
//+------------------------------------------------------------------+
//| Include files                                                    |
//+------------------------------------------------------------------+
#include "Order.mqh"
//+------------------------------------------------------------------+
//| Market pending order                                             |
//+------------------------------------------------------------------+
class CMarketPending : public COrder
  {
public:
   //--- Constructor
                     CMarketPending(const ulong ticket=0) : COrder(ORDER_STATUS_MARKET_PENDING,ticket) {}
   //--- Supported order properties (1) real, (2) integer
   virtual bool      SupportProperty(ENUM_ORDER_PROP_DOUBLE property);
   virtual bool      SupportProperty(ENUM_ORDER_PROP_INTEGER property);
  };
//+------------------------------------------------------------------+
//| Return 'true' if the order supports the passed                   |
//| integer property, otherwise, return 'false'                      |
//+------------------------------------------------------------------+
bool CMarketPending::SupportProperty(ENUM_ORDER_PROP_INTEGER property)
  {
   if(property==ORDER_PROP_PROFIT_PT         ||
      property==ORDER_PROP_DEAL_ORDER        ||
      property==ORDER_PROP_DEAL_ENTRY        ||
      property==ORDER_PROP_TIME_UPDATE       ||
      property==ORDER_PROP_TIME_CLOSE        ||
      property==ORDER_PROP_TIME_CLOSE_MSC    ||
      property==ORDER_PROP_TIME_UPDATE_MSC   ||
      property==ORDER_PROP_TICKET_FROM       ||
      property==ORDER_PROP_TICKET_TO         ||
      property==ORDER_PROP_CLOSE_BY_SL       ||
      property==ORDER_PROP_CLOSE_BY_TP
     ) return false;
   return true;
  }
//+------------------------------------------------------------------+
//| Return 'true' if the order supports the passed                   |
//| real property, otherwise, return 'false'                         |
//+------------------------------------------------------------------+
bool CMarketPending::SupportProperty(ENUM_ORDER_PROP_DOUBLE property)
  {
   if(property==ORDER_PROP_COMMISSION  ||
      property==ORDER_PROP_SWAP        ||
      property==ORDER_PROP_PROFIT      ||
      property==ORDER_PROP_PROFIT_FULL ||
      property==ORDER_PROP_PRICE_CLOSE
     ) return false;
   return true;
  }
//+------------------------------------------------------------------+

Chuyển trạng thái ‘lệnh chờ’ cho COrder cơ bản trong danh sách khởi tạo của hàm tạo của lớp.

Chúng ta đã hoàn thành việc phát triển các đối tượng cần thiết để tạo ra bộ sưu tập các lệnh và vị trí thị trường.
Thu thập các đơn đặt hàng và vị trí thị trường hoạt động

Thu thập các đơn đặt hàng và vị trí thị trường hoạt động

Trong khi tạo ra bộ sưu tập các đơn đặt hàng và giao dịch lịch sử, chúng tôi tuân thủ quy tắc rằng không có điểm nào trong việc liên tục kiểm tra toàn bộ lịch sử. Vì vậy, chúng ta đã thêm các đơn đặt hàng và giao dịch mới vào danh sách được tạo trước đó chỉ khi số của chúng thay đổi. Khi sử dụng danh sách các vị trí thị trường, cần lưu ý một quy tắc hoàn toàn khác – danh sách trên mỗi đánh dấu phải có liên quan.

Để đạt được điều này:

  1. Theo dõi sự thay đổi về số lượng đơn đặt hàng đang chờ xử lý, số lượng vị trí hoạt động cho tài khoản phòng hộ (vì chỉ có một vị trí trên tài khoản lưới) và trong khối lượng vị trí (tăng hoặc giảm khối lượng ở vị trí lưới, đóng một phần của một trong các các vị trí phòng ngừa rủi ro),
  2. Đảm bảo cập nhật dữ liệu trên từng vị trí bảo hiểm rủi ro hiện có hoặc một lưới duy nhất tại mỗi vị trí để luôn có dữ liệu trạng thái vị trí liên quan.

Hãy nhớ về các giá trị được chỉ định ở đánh dấu cuối cùng để so sánh chúng với cùng một dữ liệu trên dữ liệu hiện tại và cập nhật vị trí hoặc tạo lại toàn bộ danh sách nếu có thay đổi. May mắn thay, danh sách này không lớn và việc tạo lại nó không mất nhiều thời gian.

Hãy bắt đầu. Trong thư mục Bộ sưu tập, tạo lớp CMarketCollection mới. Để thực hiện việc này, nhấp chuột phải vào thư mục Bộ sưu tập và chọn ‘Tệp mới’. Trong Trình hướng dẫn MQL mới mở, chọn ‘Lớp mới’ và nhấp vào Tiếp theo.

Nhập tên của lớp CMarketCollection và nhấp vào Kết thúc. Mẫu lớp MarketCollection.mqh được tạo:

//+------------------------------------------------------------------+
//|                                             MarketCollection.mqh |
//|                                  Copyright 2019, Forex 365 Corp. |
//|                                              https://forex365.vn |
//+------------------------------------------------------------------+
#property copyright "Copyright 2019, Forex 365 Corp."
#property link      "https://forex365.vn"
#property version   "1.00"
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
class CMarketCollection
  {
private:

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

Đầu tiên, bao gồm tất cả các lớp được chuẩn bị, cũng như các lớp cần thiết để thực hiện việc thu thập các đơn đặt hàng và vị trí thị trường và tìm kiếm nó:

//+------------------------------------------------------------------+
//|                                             MarketCollection.mqh |
//|                                  Copyright 2019, Forex 365 Corp. |
//|                                              https://forex365.vn |
//+------------------------------------------------------------------+
#property copyright "Copyright 2019, Forex 365 Corp."
#property link      "https://forex365.vn"
#property version   "1.00"
//+------------------------------------------------------------------+
//| Include files                                                    |
//+------------------------------------------------------------------+
#include <Arrays\ArrayObj.mqh>
#include "Select.mqh"
#include "..\Objects\MarketPending.mqh" 
#include "..\Objects\MarketPosition.mqh"

Trong phần riêng của lớp, tạo cấu trúc, chỉ định các biến để lưu trữ tất cả các giá trị được theo dõi đã đề cập trước đó trong đó (số lượng đơn đặt hàng và vị trí, v.v.) và tạo hai biến thành viên lớp với kiểu cấu trúc này để lưu trữ dữ liệu hiện tại và trước đó:

//+------------------------------------------------------------------+
//|                                             MarketCollection.mqh |
//|                                  Copyright 2019, Forex 365 Corp. |
//|                                              https://forex365.vn |
//+------------------------------------------------------------------+
#property copyright "Copyright 2019, Forex 365 Corp."
#property link      "https://forex365.vn"
#property version   "1.00"
//+------------------------------------------------------------------+
//| Include files                                                    |
//+------------------------------------------------------------------+
#include <Arrays\ArrayObj.mqh>
#include "Select.mqh"
#include "..\Objects\MarketPending.mqh" 
#include "..\Objects\MarketPosition.mqh"
//+------------------------------------------------------------------+
//| Collection of historical orders and deals                        |
//+------------------------------------------------------------------+
class CMarketCollection
  {
private:
   struct MqlDataCollection
     {
      long           hash_sum_acc;           // Hash sum of all orders and positions on the account
      int            total_pending;          // Number of pending orders on the account
      int            total_positions;        // Number of positions on the account
      double         total_volumes;          // Total volume of orders and positions on the account
     };
   MqlDataCollection m_struct_curr_market;   // Current data on market orders and positions on the account
   MqlDataCollection m_struct_prev_market;   // Previous data on market orders and positions on the account
public:
                     CMarketCollection();
                    ~CMarketCollection();
  };
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
CMarketCollection::CMarketCollection()
  {
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
CMarketCollection::~CMarketCollection()
  {
  }
//+------------------------------------------------------------------+

Hãy để chúng ta tập trung vào tổng băm trong cấu trúc.

Số lượng đơn đặt hàng và vị trí không đủ nếu chúng ta muốn xác định chính xác một sự kiện tài khoản xảy ra. Một lệnh chờ có thể được gỡ bỏ thay đổi tổng số đơn đặt hàng và vị trí trên tài khoản. Mặt khác, một lệnh chờ có thể được kích hoạt và trở thành một vị trí. Trong trường hợp này, tổng số đơn đặt hàng và vị trí không thay đổi (đối với tài khoản bảo hiểm rủi ro và MQL4) – số lượng vị trí tăng, nhưng số lượng đơn đặt hàng giảm. Kết quả là tổng số vẫn giữ nguyên. Điều này không phù hợp với chúng ta.

Hãy xem xét vé. Thêm / xóa đơn đặt hàng đang chờ xử lý sẽ thay đổi tổng số vé trên tài khoản, trong khi kích hoạt đơn hàng đang chờ xử lý sẽ không thay đổi tổng số vé trên tài khoản.

Hãy xem xét tổng khối lượng. Đặt / xóa đơn đặt hàng đang chờ xử lý – tổng khối lượng trên tài khoản đã thay đổi, mở, đóng hoặc sửa đổi một vị trí – tổng khối lượng trên tài khoản đã thay đổi. Tùy chọn này có vẻ phù hợp nhưng kích hoạt đơn đặt hàng đang chờ xử lý không thay đổi tổng khối lượng.

Vì vậy, chúng ta hãy xem xét một thuộc tính vị trí khác – thời gian thay đổi của nó tính bằng mili giây: mở một vị trí mới làm thay đổi tổng thời gian thay đổi vị trí, đóng một phần làm thay đổi thời gian thay đổi vị trí và thêm âm lượng trên tài khoản lưới thay đổi tổng thay đổi vị trí thời gian.

Các tùy chọn phù hợp nhất để xác định chính xác thay đổi tài khoản đã xảy ra là gì? Vé + thời gian thay đổi vị trí. Hãy kiểm tra:

  • Mở một vị trí – tổng số vé đã thay đổi + tổng thời gian thay đổi vị trí đã thay đổi – có một sự thay đổi
  • Đóng một vị trí – tổng số vé đã thay đổi + tổng thời gian thay đổi vị trí đã thay đổi – có một sự thay đổi
  • Đã đặt một lệnh chờ – tổng số vé đã thay đổi + tổng thời gian thay đổi vị trí không thay đổi – có một sự thay đổi
  • Đã xóa đơn đặt hàng đang chờ xử lý – tổng số vé đã thay đổi + tổng thời gian thay đổi vị trí không thay đổi – có một thay đổi
  • Đã kích hoạt đơn đặt hàng đang chờ xử lý – tổng số vé không thay đổi + tổng thời gian thay đổi vị trí đã thay đổi – có thay đổi
  • Đóng một vị trí một phần – tổng số vé đã thay đổi + tổng thời gian thay đổi vị trí đã thay đổi – có một sự thay đổi
  • Thêm một khối lượng vào một vị trí – tổng số vé không thay đổi + tổng thời gian thay đổi vị trí đã thay đổi – có một sự thay đổi

Trong phần riêng của lớp, tạo danh sách động các con trỏ tới các đối tượng sẽ được sử dụng làm danh sách tập hợp các lệnh và vị trí đang chờ xử lý của thị trường. Ngoài ra, tạo hai cờ: cờ sự kiện giao dịch trên tài khoản và cờ biểu thị sự thay đổi âm lượng vị trí đã xảy ra để nhận dạng đơn giản hóa một sự kiện giao dịch trong lớp CEngine, cũng như ba biến thành viên lớp để đặt giá trị thay đổi âm lượng, số lượng vị trí mới và đơn đặt hàng đang chờ xử lý.

Trong phần chung, khai báo phương thức cập nhật danh sách bộ sưu tập và viết triển khai hàm tạo của lớp bên ngoài lớp:

//+------------------------------------------------------------------+
//| Collection of market orders and positions                        |
//+------------------------------------------------------------------+
class CMarketCollection
  {
private:
   struct MqlDataCollection
     {
      long           hash_sum_acc;           // Hash sum of all orders and positions on the account
      int            total_pending;          // Number of pending orders on the account
      int            total_positions;        // Number of positions on the account
      double         total_volumes;          // Total volume of orders and positions on the account
     };
   MqlDataCollection m_struct_curr_market;   // Current data on market orders and positions on the account
   MqlDataCollection m_struct_prev_market;   // Previous data on market orders and positions on the account
   CArrayObj         m_list_all_orders;      // List of pending orders and positions on the account
   bool              m_is_trade_event;       // Trading event flag
   bool              m_is_change_volume;     // Total volume change flag
   double            m_change_volume_value;  // Total volume change value
   int               m_new_positions;        // Number of new positions
   int               m_new_pendings;         // Number of new pending orders
public:
   //--- Constructor
                     CMarketCollection(void);
   //--- Update the list of pending orders and positions
   void              Refresh(void);
  };
//+------------------------------------------------------------------+
//| Constructor                                                      |
//+------------------------------------------------------------------+
CMarketCollection::CMarketCollection(void) : m_is_trade_event(false),m_is_change_volume(false),m_change_volume_value(0)
  {
   m_list_all_orders.Sort(SORT_BY_ORDER_TIME_OPEN);
   ::ZeroMemory(this.m_struct_prev_market);
   this.m_struct_prev_market.hash_sum_acc=WRONG_VALUE;
  }
//+------------------------------------------------------------------+

Đặt lại các cờ thay đổi khối lượng sự kiện và vị trí giao dịch và đặt lại giá trị thay đổi âm lượng trong danh sách khởi tạo của hàm tạo lớp.

Trong phần thân của hàm tạo, đặt sắp xếp thứ tự thị trường và danh sách vị trí theo thời gian mở, đặt lại tất cả các cấu trúc biến của trạng thái trước đó của tài khoản ngoại trừ tổng băm trước đó – đặt -1 cho nó (để xác định lần khởi chạy đầu tiên).

Thêm phương thức lưu dữ liệu tài khoản hiện được thu thập trong cấu trúc dữ liệu trước đó vào phần riêng tư của lớp để kiểm tra các thay đổi tiếp theo về số lượng đơn đặt hàng và vị trí trên tài khoản. Thêm ba phương thức trả về số lượng đơn đặt hàng mới đang chờ xử lý, số lượng vị trí mới và phương thức trả lại cờ của một sự kiện giao dịch xảy ra trên tài khoản cho phần công khai của lớp:

//+------------------------------------------------------------------+
//| Collection of market orders and positions                        |
//+------------------------------------------------------------------+
class CMarketCollection
  {
private:
   struct MqlDataCollection
     {
      long           hash_sum_acc;           // Hash sum of all orders and positions on the account
      int            total_pending;          // Number of pending orders on the account
      int            total_positions;        // Number of positions on the account
      double         total_volumes;          // Total volume of orders and positions on the account
     };
   MqlDataCollection m_struct_curr_market;   // Current data on market orders and positions on the account
   MqlDataCollection m_struct_prev_market;   // Previous data on market orders and positions on the account
   CArrayObj         m_list_all_orders;      // List of pending orders and positions on the account
   bool              m_is_trade_event;       // Trading event flag
   bool              m_is_change_volume;     // Total volume change flag
   double            m_change_volume_value;  // Total volume change value
   int               m_new_positions;        // Number of new positions
   int               m_new_pendings;         // Number of new pending orders
   //--- Save the current values of the account data status as previous ones
   void              SavePrevValues(void)             { this.m_struct_prev_market=this.m_struct_curr_market;   }
public:
   //--- Return the number of (1) new pending orders, (2) new positions, (3) occurred trading event flag
   int               NewOrders(void)    const         { return this.m_new_pendings;                            }
   int               NewPosition(void)  const         { return this.m_new_positions;                           }
   bool              IsTradeEvent(void) const         { return this.m_is_trade_event;                          }
   //--- Constructor
                     CMarketCollection(void);
   //--- Update the list of pending orders and positions
   void              Refresh(void);
  };
//+------------------------------------------------------------------+

Hãy thực hiện phương pháp cập nhật trạng thái thị trường hiện tại:

//+------------------------------------------------------------------+
//| Update the order list                                            |
//+------------------------------------------------------------------+
void CMarketCollection::Refresh(void)
  {
   ::ZeroMemory(this.m_struct_curr_market);
   this.m_is_trade_event=false;            
   this.m_is_change_volume=false;          
   this.m_new_pendings=0;                  
   this.m_new_positions=0;                 
   this.m_change_volume_value=0;           
   m_list_all_orders.Clear();              
#ifdef __MQL4__
   int total=::OrdersTotal();
   for(int i=0; i<total; i++)
     {
      if(!::OrderSelect(i,SELECT_BY_POS)) continue;
      long ticket=::OrderTicket();
      ENUM_ORDER_TYPE type=(ENUM_ORDER_TYPE)::OrderType();
      if(type==ORDER_TYPE_BUY || type==ORDER_TYPE_SELL)
        {
         CMarketPosition *position=new CMarketPosition(ticket);
         if(position==NULL) continue;
         if(this.m_list_all_orders.InsertSort(position))
           {
            this.m_struct_market.hash_sum_acc+=ticket;
            this.m_struct_market.total_volumes+=::OrderLots();
            this.m_struct_market.total_positions++;
           }
         else
           {
            ::Print(DFUN,TextByLanguage("Не удалось добавить позицию в список","Failed to add position to list"));
            delete position;
           }
        }
      else
        {
         CMarketPending *order=new CMarketPending(ticket);
         if(order==NULL) continue;
         if(this.m_list_all_orders.InsertSort(order))
           {
            this.m_struct_market.hash_sum_acc+=ticket;
            this.m_struct_market.total_volumes+=::OrderLots();
            this.m_struct_market.total_pending++;
           }
         else
           {
            ::Print(DFUN,TextByLanguage("Не удалось добавить ордер в список","Failed to add order to list"));
            delete order;
           }
        }
     }
//--- MQ5
#else    
//--- Positions
   int total_positions=::PositionsTotal();
   for(int i=0; i<total_positions; i++)
     {
      ulong ticket=::PositionGetTicket(i);
      if(ticket==0) continue;
      CMarketPosition *position=new CMarketPosition(ticket);
      if(position==NULL) continue;
      if(this.m_list_all_orders.InsertSort(position))
        {
         this.m_struct_curr_market.hash_sum_acc+=(long)::PositionGetInteger(POSITION_TIME_UPDATE_MSC);
         this.m_struct_curr_market.total_volumes+=::PositionGetDouble(POSITION_VOLUME);
         this.m_struct_curr_market.total_positions++;
        }
      else
        {
         ::Print(DFUN,TextByLanguage("Не удалось добавить позицию в список","Failed to add position to list"));
         delete position;
        }
     }
//--- Orders
   int total_orders=::OrdersTotal();
   for(int i=0; i<total_orders; i++)
     {
      ulong ticket=::OrderGetTicket(i);
      if(ticket==0) continue;
      CMarketPending *order=new CMarketPending(ticket);
      if(order==NULL) continue;
      if(this.m_list_all_orders.InsertSort(order))
        {
         this.m_struct_curr_market.hash_sum_acc+=(long)ticket;
         this.m_struct_curr_market.total_volumes+=::OrderGetDouble(ORDER_VOLUME_INITIAL);
         this.m_struct_curr_market.total_pending++;
        }
      else
        {
         ::Print(DFUN,TextByLanguage("Не удалось добавить ордер в список","Failed to add order to list"));
         delete order;
        }
     }
#endif 
//--- First launch
   if(this.m_struct_prev_market.hash_sum_acc==WRONG_VALUE)
     {
      this.SavePrevValues();                              
     }                                                    
//--- If the hash sum of all orders and positions changed
   if(this.m_struct_curr_market.hash_sum_acc!=this.m_struct_prev_market.hash_sum_acc)
     {
      this.m_new_pendings=this.m_struct_curr_market.total_pending-this.m_struct_prev_market.total_pending;
      this.m_new_positions=this.m_struct_curr_market.total_positions-this.m_struct_prev_market.total_positions;
      this.m_change_volume_value=::NormalizeDouble(this.m_struct_curr_market.total_volumes-this.m_struct_prev_market.total_volumes,4);
      this.m_is_change_volume=(this.m_change_volume_value!=0 ? true : false);
      this.m_is_trade_event=true;
      this.SavePrevValues();
     }
  }
//+------------------------------------------------------------------+

Trước khi phân tích phương pháp, chúng ta hãy thực hiện một mô hình nhỏ: vì chúng ta liên tục cần dữ liệu liên quan đến tất cả các lệnh và vị trí thị trường, chúng ta có thể xóa danh sách tại mỗi đánh dấu và điền dữ liệu từ môi trường thị trường. Ngoài ra, chúng ta có thể điền vào danh sách một lần và chỉ thay đổi dữ liệu có thể thay đổi. Ngay từ cái nhìn đầu tiên, dường như sẽ nhanh hơn khi chỉ thay đổi dữ liệu. Nhưng để làm điều này, chúng ta cần phải:

  • đi qua danh sách các đơn đặt hàng thị trường và vị trí thiết bị đầu cuối, và điền vào danh sách thư viện với chúng,
  • tại mỗi đánh dấu, đi qua danh sách các đơn hàng và vị trí thị trường đầu cuối, lấy dữ liệu thay đổi, tìm kiếm các đơn hàng và vị trí có cùng một vé trong danh sách thư viện và cập nhật dữ liệu hiện có,
  • nếu một đơn đặt hàng bị xóa hoặc một vị trí bị đóng, hãy xóa nó khỏi danh sách thư viện.

Điều này có vẻ tốn kém hơn là chỉ đơn giản là xóa danh sách thư viện và điền nó theo lệnh thị trường và vị trí đầu cuối trong một vòng lặp.

Do đó, hãy làm theo cách đơn giản hơn: xóa danh sách và điền lại. Tất nhiên, không có gì ngăn chúng ta thử phương pháp liên quan đến tìm kiếm và cập nhật dữ liệu trong danh sách thư viện đã có sẵn. Chúng ta sẽ thử nó khi làm việc với thư viện và cải thiện tốc độ làm việc của nó (cơ sở ‘đơn giản đến phức tạp’).

Bây giờ hãy xem cách thức cập nhật danh sách bộ sưu tập các lệnh và vị trí thị trường được sắp xếp.

Khi bắt đầu phương thức, cấu trúc của dữ liệu thị trường hiện tại, cờ sự kiện, giá trị thay đổi âm lượng, cũng như tất cả các biến liên quan đến số lượng đơn đặt hàng và vị trí được đặt lại và danh sách bộ sưu tập sẽ bị xóa.

Sau đó, kiểm tra thuộc về MQL4 hoặc MQL5 được tiến hành.
Vì ở giai đoạn này, chúng ta chuẩn bị mã MQL5, chúng ta hãy xem phiên bản MQL5:

Không giống như MQL4, trong MQL5, các đơn hàng và vị trí được lưu trữ trong các danh sách khác nhau.
Do đó, lấy tổng số vị trí trên tài khoản, sau đó di chuyển dọc theo tất cả các vị trí đầu cuối trong một vòng lặp, chọn vé của vị trí tiếp theo, tạo một đối tượng vị trí và thêm nó vào danh sách bộ sưu tập các đơn đặt hàng đang hoạt động và vị trí thư viện.

Theo cùng một cách, thêm tất cả các đơn đặt hàng đang chờ xử lý hiện có trên tài khoản. Nhận tổng số đơn đặt hàng trên tài khoản chỉ cho các đơn đặt hàng đang chờ xử lý và di chuyển dọc theo danh sách đơn hàng thiết bị đầu cuối trong một vòng lặp nhận vé đặt hàng và thêm một đối tượng đơn hàng vào danh sách thu thập các đơn hàng và vị trí hoạt động của thư viện.

Sau khi cả hai vòng lặp hoàn tất, danh sách bộ sưu tập các đơn hàng và vị trí hoạt động của thư viện sẽ chứa các đối tượng của đơn hàng và vị trí hiện có trên tài khoản. Tiếp theo, kiểm tra cờ khởi chạy đầu tiên (giá trị của tổng băm ‘trước’ bằng -1 được sử dụng làm cờ ở đây). Nếu đây là lần khởi chạy đầu tiên, các giá trị của tất cả các giá trị ‘quá khứ’ sẽ được sao chép vào cấu trúc lưu trữ các giá trị này bằng phương thức SavePrevValues ​​(). Nếu đây không phải là lần chạy đầu tiên, thì giá trị của tổng băm trong quá khứ được so sánh với giá trị của giá trị hiện tại được tính khi chuyển các vòng thu thập dữ liệu tài khoản vào danh sách thu thập. Nếu tổng băm trước đó không bằng tổng hiện tại, thì một thay đổi đã xảy ra trên tài khoản

Trong trường hợp này, sự khác biệt giữa số lượng đơn đặt hàng hiện tại và trước đó được đặt trong biến lưu trữ số lượng đơn đặt hàng tài khoản mới, trong khi chênh lệch giữa số lượng vị trí hiện tại và trước đó được đặt trong biến lưu trữ số lượng vị trí tài khoản mới . Lưu giá trị, theo đó tổng khối lượng tài khoản đã thay đổi, đặt cờ thay đổi âm lượng, cũng như cờ của một sự kiện giao dịch đã xảy ra và cuối cùng, thêm các giá trị mới vào cấu trúc của các giá trị ‘trước đó’ bằng phương thức SavePrevValues ​​() để xác minh tiếp theo.

Phương thức SavePrevValues ​​() chỉ đơn giản là sao chép cấu trúc với các giá trị hiện tại sang cấu trúc với các cấu trúc trước đó.

Để kiểm tra hoạt động của phương pháp cập nhật danh sách lệnh và vị trí thị trường và công việc chung của nó với phương pháp cập nhật danh sách đơn hàng và giao dịch lịch sử, hãy sử dụng EA thử nghiệm cuối cùng từ thư mục Part03 có tên TestDo EASPart03_4.mq5:

//+------------------------------------------------------------------+
//|                                           TestDoEasyPart03_4.mq5 |
//|                                  Copyright 2019, Forex 365 Corp. |
//|                                              https://forex365.vn |
//+------------------------------------------------------------------+
#property copyright "Copyright 2019, Forex 365 Corp."
#property link      "https://forex365.vn"
#property version   "1.00"
//--- includes
#include <DoEasy\Engine.mqh>
//--- global variables
CEngine        engine;
//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
//---

//---
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
//--- 
   
  }
//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
  {
//---
   
  }
//+------------------------------------------------------------------+
//| Timer function                                                   |
//+------------------------------------------------------------------+
void OnTimer()
  {
   engine.OnTimer();
  }
//+------------------------------------------------------------------+

Để xem các thay đổi được triển khai khi thêm và theo dõi các bộ sưu tập các lệnh và vị trí thị trường, hãy thêm các chuỗi sau vào trình xử lý sự kiện Timer của lớp CEngine:

//+------------------------------------------------------------------+
//| CEngine timer                                                    |
//+------------------------------------------------------------------+
void CEngine::OnTimer(void)
  {
   //--- Timer of the collections of historical orders and deals, as well as of market orders and positions
   int index=this.CounterIndex(COLLECTION_COUNTER_ID);
   if(index>WRONG_VALUE)
     {
      CTimerCounter* counter=this.m_list_counters.At(index);
      if(counter!=NULL && counter.IsTimeDone())
        {
         //--- Update the lists 
         this.m_market.Refresh(); 
         this.m_history.Refresh();
         //--- First launch actions
         if(this.IsFirstStart())
           {
            return;
           }
         //--- Check the market status change
         if(this.m_market.IsTradeEvent())
           {
            Print(DFUN,TextByLanguage("Новое торговое событие на счёте","New trading event on account"));
           }
         //--- Check the account history change
         if(this.m_history.IsTradeEvent())
           {
            Print(DFUN,TextByLanguage("Новое торговое событие в истории счёта","New trading event in account history"));
           }
        }
     }
  }
//+------------------------------------------------------------------+

Mọi thứ đều đơn giản ở đây. Hoàn thành chờ đợi trong bộ đếm thời gian bộ sưu tập được kiểm tra đầu tiên. Nếu tạm dừng kết thúc, danh sách các lệnh và vị trí thị trường, cũng như các đơn đặt hàng và giao dịch lịch sử được cập nhật. Không có hành động được yêu cầu nào trong lần ra mắt đầu tiên. Khi nhận được cờ của một sự kiện tài khoản xảy ra, hãy hiển thị phương pháp thích hợp trong tạp chí. Điều tương tự được thực hiện khi nhận cờ sự kiện trong lịch sử tài khoản.

Biên dịch EA thử nghiệm và khởi chạy nó. Bây giờ, nếu chúng ta mở một vị trí, hai mục xuất hiện trong tạp chí:

2019.02.28 17:36:24.678 CEngine::OnTimer: New trading event on the account
2019.02.28 17:36:24.678 CEngine::OnTimer: New trading event in the account history

Sự kiện đầu tiên chỉ ra rằng một sự kiện giao dịch đã xảy ra trên một tài khoản, trong khi sự kiện thứ hai thông báo về việc thêm một sự kiện mới vào lịch sử tài khoản. Trong trường hợp này, một sự kiện giao dịch trên tài khoản bao gồm tăng số lượng vị trí lên 1, trong khi một sự kiện mới trong lịch sử tài khoản có nghĩa là sự xuất hiện của một lệnh thị trường mở mới duy nhất và một giao dịch mới – ‘gia nhập thị trường’.

Trong bài sau có gì?

Trong bài viết tiếp theo, chúng ta sẽ tiếp tục phát triển phần tử thư viện chính (lớp CEngine) và thực hiện các sự kiện xử lý đến từ bộ sưu tập và gửi chúng đến chương trình.

Tất cả các tệp của phiên bản hiện tại của thư viện được đính kèm bên dưới cùng với các tệp EA thử nghiệm để bạn kiểm tra và tải xuống.
Để lại câu hỏi, ý kiến ​​và đề xuất của bạn trong bình luận hoặc live chat.

HotForex tặng thưởng 100% và miễn phí nạp rút tiền

BÌNH LUẬN

Vui lòng nhập bình luận của bạn
Vui lòng nhập tên của bạn ở đây

Website này sử dụng Akismet để hạn chế spam. Tìm hiểu bình luận của bạn được duyệt như thế nào.