Mục lục:

  1. Giới thiệu
  2. Khái niệm cơ bản
  3. Cấu trúc dữ liệu
  4. Ví dụ
  5. Kiểm tra
  6. Phần tiếp theo có gì?

Giới thiệu

Trong khi phân tích một số lượng lớn chiến lược giao dịch, các đơn đặt hàng phát triển ứng dụng cho thiết bị đầu cuối MetaTrader 5MetaTrader 4 và các trang web khác nhau liên quan đến tập lệnh, chỉ báo và robot cho MetaTrader, ta đã đi đến kết luận rằng tất cả sự đa dạng này chủ yếu dựa trên cùng các chức năng cơ bản, hành động và giá trị xuất hiện thường xuyên trong các chương trình khác nhau.

Trong thực tế, logic của bất kỳ chương trình nào có thể được chia thành nhiều hành động giống hệt nhau. Kết quả của những hành động này được sử dụng để xây dựng logic của các ứng dụng. Điều này đã được xác nhận nhiều lần bởi tính đồng nhất của các câu hỏi được hỏi trên các diễn đàn MQL4/MQL5. Những người dùng khác nhau hỏi về cơ bản cùng một câu hỏi về các thuật toán và nhiệm vụ họ giải quyết.
Xem xét tất cả những điều này, ta quyết định phát triển một thư viện lớn có các chức năng tích hợp sẵn để yêu cầu và lấy dữ liệu cần thiết. Để sử dụng tập dữ liệu của thư viện được đề xuất, người dùng chỉ cần sử dụng phương pháp trả lời câu hỏi để có được một lượng lớn dữ liệu hoàn toàn khác nhau (trong các kết hợp và sắp xếp tham số khác nhau) thư viện thu thập và lưu trữ trong cơ sở dữ liệu của nó.

Khái niệm cơ bản

Bất kỳ loại dữ liệu có thể được biểu diễn dưới dạng một tập hợp các đối tượng có cùng thuộc tính.
Ví dụ, một khung thời gian có thể được biểu diễn dưới dạng một danh sách dài, mỗi ô tiếp theo lưu trữ một đối tượng có một tập các thuộc tính cùng loại với tất cả các tập hợp khác thuộc về các đối tượng tương tự trong các mốc thời gian. Dữ liệu của một đối tượng như vậy được biểu thị bằng cấu trúc MqlRates:

Cấu trúc để lưu trữ thông tin về giá, khối lượng và chênh lệch bid/ask.

struct MqlRates 
  { 
   datetime time;         // period start time 
   double   open;         // open price 
   double   high;         // highest price per period 
   double   low;          // lowest price per period 
   double   close;        // close price 
   long     tick_volume;  // tick volume 
   int      spread;       // spread 
   long     real_volume;  // exchange volume 
  };

Một tập hợp dữ liệu đánh dấu cũng có thể được mô tả như một danh sách có thứ tự trong đó một đánh dấu được biểu thị bởi cấu trúc MqlTick là một đối tượng với một tập các thuộc tính cố định:

Cấu trúc để lưu trữ giá cuối cùng bằng ký hiệu. Cấu trúc được thiết kế để có được dữ liệu cần thiết nhất về giá hiện tại một cách kịp thời.

struct MqlTick 
  { 
   datetime     time;          // Last price update time 
   double       bid;           // Current Bid price 
   double       ask;           // Current Ask price 
   double       last;          // Current price of the last deal (Last) 
   ulong        volume;        // Volume for the current Last price 
   long         time_msc;      // Time of the last price update in milliseconds 
   uint         flags;         // Tick flags 
   double       volume_real;   // Volume for the current Last price with increased accuracy 
  };

Bất kỳ dữ liệu nào khác cần thiết để phân tích và chuẩn bị logic chương trình cũng được sắp xếp dưới dạng danh sách đối tượng đơn giản. Tất cả các đối tượng trong một danh sách sẽ có cùng loại dữ liệu cụ thể cho loại đối tượng này bất kể đó là danh sách các đơn đặt hàng, giao dịch hoặc đơn đặt hàng đang chờ xử lý. Đối với mỗi đối tượng cụ thể, chúng ta sẽ phát triển một lớp có chức năng tối thiểu cần thiết để lưu trữ, sắp xếp và hiển thị dữ liệu.

Cấu trúc dữ liệu

Như đã đề cập, thư viện bao gồm danh sách đối tượng và cung cấp khả năng chọn bất kỳ mục nào trong danh sách theo bất kỳ tiêu chí tùy chỉnh hoặc thuộc tính nào được đối tượng đó hỗ trợ. Thư viện sẽ tự thu thập dữ liệu cần thiết để lưu trữ và xử lý. Không cần sự can thiệp của con người. Người dùng sẽ chỉ áp dụng kết quả của các truy vấn thư viện của họ.

ta sẽ mô tả tất cả các bước phát triển thư viện bắt đầu với các chủ đề đơn giản nhất và dần dần thêm chức năng và dữ liệu mới vào cái đã có. Thư viện sẽ được phát triển ở chế độ ‘live’. Yêu cầu chỉnh sửa và bổ sung sẽ được thực hiện trong mỗi bài viết. ta tin rằng, phong cách trình bày này là hữu ích nhất vì nó liên quan đến độc giả vào sự phát triển.
Cấu trúc tối thiểu của dữ liệu thư viện là một tập hợp các đối tượng khác nhau để mô tả các thuộc tính của dữ liệu cần thiết, trong khi các bộ sưu tập dữ liệu là danh sách lưu trữ các đối tượng tương ứng.

Chúng ta sẽ sử dụng lớp của mảng động của con trỏ tới các thể hiện của lớp CObject và các lớp dẫn xuất của nó từ bộ sưu tập dữ liệu thư viện chuẩn để sắp xếp các danh sách. Vì các đối tượng của lớp cơ sở của thư viện chuẩn CObject là bắt buộc để lưu trữ trong danh sách như vậy, nên chúng ta sẽ chỉ kế thừa từng lớp đối tượng của chúng ta từ lớp cơ sở CObject.

Ví dụ

Đầu tiên, chúng ta sẽ phát triển thư viện cho các tài khoản hedge của thiết bị đầu cuối MetaTrader 5. Sau khi chuẩn bị chức năng tối thiểu, chúng ta sẽ điều chỉnh hoạt động của nó thành MetaTrader 4. Tiếp theo, sau khi triển khai lịch sử tài khoản, vị trí thị trường hiện tại và xử lý đơn đặt hàng đang chờ xử lý, chúng ta sẽ thêm khả năng hoạt động với tài khoản lưới MetaTrader 5. Cuối cùng, chúng ta sẽ lấp đầy thư viện với một loạt các chức năng.

Hãy bắt đầu với lịch sử tài khoản. Nhiều chiến lược áp dụng kết quả của các giao dịch trong quá khứ bằng cách này hay cách khác, ví dụ, kết quả của các giao dịch trước đó, phương thức đóng cửa của họ (dừng lỗ, chốt lời), giá cả, v.v. Bên cạnh đó, có thể sử dụng kết quả cho ngày cuối cùng Như một điểm khởi đầu cho công việc hiện tại. Các loại chiến lược giao dịch là vô hạn, và nhiệm vụ của chúng ta là cung cấp quyền truy cập nhanh vào tất cả sự đa dạng.

Trước tiên, hãy xác định thuật ngữ để làm việc với các bộ sưu tập historical orders, deals, market orders and positions. Hệ thống đặt hàng MetaTrader 4 khác với hệ thống MetaTrader 5. Mặc dù MetaTrader 4 có tính năng thị trường và các đơn đặt hàng đang chờ xử lý, nhưng trong MetaTrader 5, một đơn hàng về cơ bản là một yêu cầu giao dịch (market order) tạo ra một giao dịch (deal), trong khi giao dịch deal lần lượt tạo ra một vị thế-positions. Bên cạnh đó, có những đơn đặt hàng đang chờ xử lý. Nói cách khác, chúng ta có ít nhất ba đối tượng là: order, deal và position. Để tránh bị phân tâm bởi các tên và định nghĩa, hãy gọi lớp để lưu trữ dữ liệu theo order, deal và position chỉ đơn giản là một lớp thứ tự trừu tượng. Hơn nữa, mọi thứ sẽ được sắp xếp theo loại (đơn đặt hàng order, giao dịch deal, v.v.) trong danh sách của chúng ta (bộ sưu tập các đơn đặt hàng lịch sử được đề cập ở đầu bài viết).

Lớp trừu tượng đơn hàng dùng để chứa order hoặc deal ticket, cũng như toàn bộ dữ liệu về các tham số của đơn đặt hàng hoặc giao dịch và trạng thái của đơn hàng. Trạng thái hiển thị chính xác đối tượng là: order, deal hoặc 1 position.

Trong thư mục dữ liệu đầu cuối \MQL5\Include, hãy tạo thư mục DoEasy nơi lưu trữ các tệp thư viện. Ở giai đoạn này, chúng ta sẽ cần thêm hai thư mục trong thư mục DoEasy: thư mục Objects sẽ lưu trữ các lớp đối tượng, trong khi Collections sẽ chứa các bộ sưu tập dữ liệu (danh sách đối tượng).
Để tìm thư mục dữ liệu đầu cuối, hãy chuyển đến File -> Open Data Folder (Ctrl + Shift + D).

Bây giờ chúng ta có thể tạo lớp đầu tiên (lớp thứ tự trừu tượng). Tạo một lớp mới trong thư mục Objects và đặt tên là COrder (1). Đảm bảo đặt lớp CObject (2) của thư viện chuẩn làm cơ sở. Trong trường hợp này, lớp đối tượng mới của chúng ta được kế thừa từ lớp CObject và nó có thể được đặt trong danh sách các đối tượng CArrayObj của thư viện chuẩn.

Sau khi nhấp vào Finish, tệp Order.mqh (3) mới xuất hiện trong thư mục Objects của thư mục thư viện. Hiện tại, đây chỉ là một tác phẩm của lớp:

//+------------------------------------------------------------------+
//|                                                        Order.mqh |
//|                        Copyright 2018, MetaQuotes Software Corp. |
//+------------------------------------------------------------------+
#property copyright "Copyright 2018, MetaQuotes Software Corp."
#property version   "1.00"
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
class COrder : public CObject
  {
private:

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

Hãy thử cố gắng biên dịch mã, chúng ta nhận được năm lỗi. Chúng chỉ ra sự vắng mặt của lớp CObject, lớp COrder có nguồn gốc từ. Bao gồm tệp lớp CObject vào danh sách và biên dịch lại một lần nữa. Bây giờ mọi thứ đều ổn.

//+------------------------------------------------------------------+
//|                                                        Order.mqh |
//|                        Copyright 2018, MetaQuotes Software Corp. |
//+------------------------------------------------------------------+
#property copyright "Copyright 2018, MetaQuotes Software Corp."
#property version   "1.00"
#include <Object.mqh>
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
class COrder : public CObject
  {
private:

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

Đối tượng của lớp được tạo trong một vòng lặp theo đơn đặt hàng, giao dịch hoặc vị trí khi thứ tự, giao dịch hoặc vị trí tiếp theo được chọn. Để khởi tạo các trường của đối tượng ngay lập tức, chúng ta sẽ tạo một hàm tạo riêng, loại đối tượng (trạng thái) và vé được chuyển đến để nhận dạng tiếp theo. Nhưng trước tiên, hãy đặt Defines.mqh mới bao gồm tệp vào thư mục gốc của dự án, nơi lưu trữ tất cả các liệt kê cần thiết cho thư viện, cũng như thay thế macro, hằng và cấu trúc.

Hiện tại, chúng ta cần các bảng liệt kê mô tả trạng thái đơn hàng và bảng liệt kê mô tả tất cả các tham số của đơn hàng, giao dịch hoặc vị trí. Sẽ có ba bảng liệt kê với các tham số thứ tự: số nguyên, số thực và chuỗi như sau:

//+------------------------------------------------------------------+
//|                                                      Defines.mqh |
//|                        Copyright 2018, MetaQuotes Software Corp. |
//+------------------------------------------------------------------+
#property copyright "Copyright 2018, MetaQuotes Software Corp."
//+------------------------------------------------------------------+
//| Abstract order type (status)                                     |
//+------------------------------------------------------------------+
enum ENUM_ORDER_STATUS
  {
   ORDER_STATUS_MARKET_PENDING,                             // Current pending order
   ORDER_STATUS_MARKET_ACTIVE,                              // Active market order
   ORDER_STATUS_HISTORY_ORDER,                              // History market order
   ORDER_STATUS_HISTORY_PENDING,                            // Removed pending order
   ORDER_STATUS_BALANCE,                                    // Balance operation
   ORDER_STATUS_CREDIT,                                     // Credit operation
   ORDER_STATUS_DEAL,                                       // Deal
   ORDER_STATUS_UNKNOWN                                     // Unknown status
  };
//+------------------------------------------------------------------+
//| Order, deal, position integer properties                         |
//+------------------------------------------------------------------+
enum ENUM_ORDER_PROP_INTEGER
  {
   ORDER_PROP_TICKET = 0,                                   // Order ticket
   ORDER_PROP_MAGIC,                                        // Order magic
   ORDER_PROP_TIME_OPEN,                                    // Open time
   ORDER_PROP_TIME_CLOSE,                                   // Close time
   ORDER_PROP_TIME_EXP,                                     // Order expiration date (for pending orders)
   ORDER_PROP_TYPE,                                         // Order and deal type
   ORDER_PROP_STATUS,                                       // Order status (from the ENUM_ORDER_STATUS enumeration)
   ORDER_PROP_REASON,                                       // Deal/order/position reason or source
   ORDER_PROP_POSITION_ID,                                  // Position ID
   ORDER_PROP_POSITION_BY_ID,                               // Opposite position ID
   ORDER_PROP_DEAL_ORDER,                                   // Order, based on which a deal is executed
   ORDER_PROP_DEAL_ENTRY,                                   // Deal direction – IN, OUT or IN/OUT
   ORDER_PROP_TIME_OPEN_MSC,                                // Open time in milliseconds
   ORDER_PROP_TIME_CLOSE_MSC,                               // Close time in milliseconds (execution or removal time - ORDER_TIME_DONE_MSC)
   ORDER_PROP_TIME_UPDATE,                                  // Position change time in seconds
   ORDER_PROP_TIME_UPDATE_MSC,                              // Position change time in milliseconds
   ORDER_PROP_TICKET_FROM,                                  // Parent order ticket
   ORDER_PROP_TICKET_TO,                                    // Derived order ticket
   ORDER_PROP_PROFIT_PT,                                    // Profit in points
   ORDER_PROP_CLOSE_BY_SL,                                  // Flag of closing by StopLoss
   ORDER_PROP_CLOSE_BY_TP,                                  // Flag of closing by TakeProfit
   ORDER_PROP_DIRECTION,                                    // Direction (Buy, Sell)
  }; 
#define ORDER_PROP_INTEGER_TOTAL    (22)                    // Total number of integer properties
//+------------------------------------------------------------------+
//| Order, deal, position real properties                            |
//+------------------------------------------------------------------+
enum ENUM_ORDER_PROP_DOUBLE
  {
   ORDER_PROP_PRICE_OPEN = ORDER_PROP_INTEGER_TOTAL,        // Open price (MQL5 deal price)
   ORDER_PROP_PRICE_CLOSE,                                  // Close price
   ORDER_PROP_PROFIT,                                       // Profit
   ORDER_PROP_COMMISSION,                                   // Commission
   ORDER_PROP_SWAP,                                         // Swap
   ORDER_PROP_VOLUME,                                       // Volume
   ORDER_PROP_VOLUME_CURRENT,                               // Unexecuted volume
   ORDER_PROP_SL,                                           // StopLoss price
   ORDER_PROP_TP,                                           // TakeProfit price
   ORDER_PROP_PROFIT_FULL,                                  // Profit+commission+swap
   ORDER_PROP_PRICE_STOP_LIMIT,                             // Limit order price when StopLimit order is activated
  };
#define ORDER_PROP_DOUBLE_TOTAL     (11)                    // Total number of real properties
//+------------------------------------------------------------------+
//| Order, deal, position string properties                          |
//+------------------------------------------------------------------+
enum ENUM_ORDER_PROP_STRING
  {
   ORDER_PROP_SYMBOL = (ORDER_PROP_INTEGER_TOTAL+ORDER_PROP_DOUBLE_TOTAL), // Order symbol
   ORDER_PROP_COMMENT,                                      // Order comment
   ORDER_PROP_EXT_ID                                        // Order ID in an external trading system
  };
#define ORDER_PROP_STRING_TOTAL     (3)                     // Total number of string properties
//+------------------------------------------------------------------+

Một số tài sản cũng được đặt vào bảng liệt kê: vé đặt hàng mẹ, vé đặt hàng xuất phát, lợi nhuận theo điểm, tài sản đóng cửa bằng cách dừng lỗ hoặc chốt lãi, định hướng và lợi nhuận đầy đủ – xem xét hoa hồng và hoán đổi. Những dữ liệu này thường được sử dụng trong logic chiến lược giao dịch, vì vậy chúng nên được lưu trữ trong các trường thứ tự trừu tượng, cũng như được cập nhật và nhận trong các chương trình tùy chỉnh ngay lập tức.

Để kết hợp tất cả các thuộc tính thứ tự (số nguyên, số thực và chuỗi), tạo các thay thế macro chứa số lượng tham số trong mỗi ba kiểu liệt kê cho mỗi loại thuộc tính.

Việc đánh số liệt kê các thuộc tính số nguyên bắt đầu từ 0, trong khi việc đánh số các liệt kê của các thuộc tính khác bắt đầu bằng tổng số thuộc tính trước đó. Do đó, chúng ta luôn có thể lấy chỉ số của tài sản mà chúng ta cần là sự khác biệt giữa số lượng tài sản được yêu cầu và số tài sản ban đầu của bảng liệt kê này.

Sau khi tạo tệp Defines.mqh, hãy kết nối nó với tệp hiện tại của lớp COrder và tạo trong phần riêng tư biến thành viên lớp để lưu trữ phiếu đặt hàng và ba mảng để lưu trữ các thuộc tính thứ tự số nguyên, thực và chuỗi:

//+------------------------------------------------------------------+
//|                                                        Order.mqh |
//|                        Copyright 2018, MetaQuotes Software Corp. |
//+------------------------------------------------------------------+
#property copyright "Copyright 2018, MetaQuotes Software Corp."
#property version   "1.00"
#include <Object.mqh>
#include "..\Defines.mqh"
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
class COrder : public CObject
  {
private:
   ulong             m_ticket;                                 // Selected order/deal ticket (MQL5)
   long              m_long_prop[ORDER_PROP_INTEGER_TOTAL];    // Integer properties
   double            m_double_prop[ORDER_PROP_DOUBLE_TOTAL];   // Real properties
   string            m_string_prop[ORDER_PROP_STRING_TOTAL];   // String properties
public:
                     COrder();
                    ~COrder();
  };
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
COrder::COrder()
  {
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
COrder::~COrder()
  {
  }
//+------------------------------------------------------------------+

Để bao gồm tệp Defines.mqh, chúng ta đặt đường dẫn tương đối đến tệp trong dấu ngoặc kép thay vì áp dụng dấu ngoặc nhọn () mà chúng ta đã sử dụng khi bao gồm CObject. Điều này được thực hiện để khi chuyển thư viện sang bất kỳ thư mục nào khác, kết nối giữa các tệp không bị mất và luôn đề cập đến vị trí của tệp Defines.mqh so với thư mục hiện tại.

Bây giờ, hãy tạo hai phương thức trong cùng một phần riêng tư. Cái đầu tiên là trả về một vị trí chính xác của thuộc tính cần thiết trong mảng thuộc tính, trong khi cái thứ hai là trả về một hàm tạo được bảo vệ trong phần được bảo vệ tương ứng. Để lại hàm tạo mặc định để tạo một đối tượng thứ tự trống mà không khởi tạo các thuộc tính của nó và loại bỏ hàm hủy (chúng ta không cần nó và nó sẽ được tạo tự động trong quá trình biên dịch):

//+------------------------------------------------------------------+
//|                                                        Order.mqh |
//|                        Copyright 2018, MetaQuotes Software Corp. |
//+------------------------------------------------------------------+
#property copyright "Copyright 2018, MetaQuotes Software Corp."
#property version   "1.00"
#include <Object.mqh>
#include "..\Defines.mqh"
//+------------------------------------------------------------------+
//| Abstract order class                                             |
//+------------------------------------------------------------------+
class COrder : public CObject
  {
private:
   ulong             m_ticket;                                    // Selected order/deal ticket (MQL5)
   long              m_long_prop[ORDER_PROP_INTEGER_TOTAL];       // Integer properties
   double            m_double_prop[ORDER_PROP_DOUBLE_TOTAL];      // Real properties
   string            m_string_prop[ORDER_PROP_STRING_TOTAL];      // String properties
   
   //--- Return the array index the double property is actually located at
   int               IndexProp(ENUM_ORDER_PROP_DOUBLE property)   const { return (int)property-ORDER_PROP_INTEGER_TOTAL;                  }
   //--- Return the array index the string property is actually located at
   int               IndexProp(ENUM_ORDER_PROP_STRING property)   const { return (int)property-ORDER_PROP_INTEGER_TOTAL-ORDER_PROP_DOUBLE_TOTAL;}
public:
   //--- Default constructor
                     COrder(void){;}
protected:                                                                     
   //--- Protected parametric constructor                                
                     COrder(ENUM_ORDER_STATUS order_status,const ulong ticket);
  };
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
COrder::COrder(ENUM_ORDER_STATUS order_status,const ulong ticket)
  {
  }
//+------------------------------------------------------------------+

Cho đến nay, các nhà xây dựng được bảo vệ không làm gì. Nó sẽ được sử dụng để khởi tạo ngay lập tức tất cả các thuộc tính của đơn hàng, vé được chuyển đến hàm tạo của lớp, khi tạo một đối tượng. Vì thứ tự đã được chọn, chúng ta có thể có được các thuộc tính cần thiết của một thứ tự đã chọn và ghi chúng vào các mảng của các thuộc tính đối tượng. Chúng ta sẽ thực hiện việc này muộn hơn một chút sau khi tạo phương thức nhận và trả lại dữ liệu đơn hàng.

Vì đây là một thư viện đa nền tảng, sẽ thuận tiện hơn để tạo các phương thức riêng biệt để có được các thuộc tính đơn hàng.
Trong phần được bảo vệ, thêm mô tả về các phương thức để nhận các thuộc tính số nguyên, thực và chuỗi của một đơn hàng đã chọn.

//+------------------------------------------------------------------+
//|                                                        Order.mqh |
//|                        Copyright 2018, MetaQuotes Software Corp. |
//+------------------------------------------------------------------+
#property copyright "Copyright 2018, MetaQuotes Software Corp."
#property version   "1.00"
#include <Object.mqh>
#include "..\Defines.mqh"
//+------------------------------------------------------------------+
//| Abstract order class                                             |
//+------------------------------------------------------------------+
class COrder : public CObject
  {
private:
   ulong             m_ticket;                                    // Selected order/deal ticket (MQL5)
   long              m_long_prop[ORDER_PROP_INTEGER_TOTAL];       // Integer properties
   double            m_double_prop[ORDER_PROP_DOUBLE_TOTAL];      // Real properties
   string            m_string_prop[ORDER_PROP_STRING_TOTAL];      // String properties
   
   //--- Return the index of the array the double property is actually located at
   int               IndexProp(ENUM_ORDER_PROP_DOUBLE property)   const { return (int)property-ORDER_PROP_INTEGER_TOTAL;                        }
   //--- Return the index of the array the string property is actually located at
   int               IndexProp(ENUM_ORDER_PROP_STRING property)   const { return (int)property-ORDER_PROP_INTEGER_TOTAL-ORDER_PROP_DOUBLE_TOTAL;}
public:
   //--- Default constructor
                     COrder(void){;}
protected:
   //--- Protected parametric constructor
                     COrder(ENUM_ORDER_STATUS order_status,const ulong ticket);
                     
   //--- Get and return integer properties of a selected order from its parameters
   long              OrderMagicNumber(void)        const;
   long              OrderTicket(void)             const;
   long              OrderTicketFrom(void)         const;
   long              OrderTicketTo(void)           const;
   long              OrderPositionID(void)         const;
   long              OrderPositionByID(void)       const;
   long              OrderOpenTimeMSC(void)        const;
   long              OrderCloseTimeMSC(void)       const;
   long              OrderType(void)               const;
   long              OrderTypeByDirection(void)    const;
   long              OrderTypeFilling(void)        const;
   long              OrderTypeTime(void)           const;
   long              OrderReason(void)             const;
   long              DealOrder(void)               const;
   long              DealEntry(void)               const;
   bool              OrderCloseByStopLoss(void)    const;
   bool              OrderCloseByTakeProfit(void)  const;
   datetime          OrderOpenTime(void)           const;
   datetime          OrderCloseTime(void)          const;
   datetime          OrderExpiration(void)         const;
   datetime          PositionTimeUpdate(void)      const;
   datetime          PositionTimeUpdateMSC(void)   const;
   
   //--- Get and return real properties of a selected order from its parameters: (1) open price, (2) close price, (3) profit,
   //---  (4) commission, (5) swap, (6) volume, (7) unexecuted volume (8) StopLoss price, (9) TakeProfit price (10) StopLimit order price
   double            OrderOpenPrice(void)          const;
   double            OrderClosePrice(void)         const;
   double            OrderProfit(void)             const;
   double            OrderCommission(void)         const;
   double            OrderSwap(void)               const;
   double            OrderVolume(void)             const;
   double            OrderVolumeCurrent(void)      const;
   double            OrderStopLoss(void)           const;
   double            OrderTakeProfit(void)         const;
   double            OrderPriceStopLimit(void)     const;
   
   //--- Get and return string properties of a selected order from its parameters: (1) symbol, (2) comment, (3) ID at an exchange
   string            OrderSymbol(void)             const;
   string            OrderComment(void)            const;
   string            OrderExternalID(void)         const;
   
//---
  };
//+------------------------------------------------------------------+
//| Closed parametric constructor                                    |
//+------------------------------------------------------------------+
COrder::COrder(ENUM_ORDER_STATUS order_status,const ulong ticket)
  {
  }
//+------------------------------------------------------------------+

Hiện tại, chỉ có các phương thức nhận tài sản đã được khai báo. Chúng chưa được thực hiện, mặc dù lớp được biên dịch không có lỗi. Mảng thuộc tính thứ tự sẽ được điền vào hàm tạo của lớp bằng các phương thức chúng ta vừa thêm. Khi tất cả đã sẵn sàng, các mảng này sẽ cho phép chúng ta có được trong chương trình bất kỳ tài sản nào theo yêu cầu.

Phương thức ảo so sánh các đối tượng theo các thuộc tính được chỉ định được khai báo trong lớp CObject của thư viện chuẩn. Tuy nhiên, phương thức nên được thực hiện trong các lớp con. Do đó, hãy thêm vào lớp thứ tự trừu tượng phương thức để so sánh các đối tượng COrder theo bất kỳ thuộc tính nào của nó, cũng như một số phương thức công khai để truy cập các thuộc tính đơn hàng và các phương thức ảo trả về các cờ để hỗ trợ thuộc tính cụ thể của đối tượng đơn hàng. Các phương thức này sẽ được thực hiện trong các đối tượng hậu duệ của lớp COrder. Điều này sẽ được yêu cầu sau này để chọn các đơn đặt hàng từ danh sách bộ sưu tập bởi bất kỳ thuộc tính nào của chúng. Theo mặc định, nếu bất kỳ phương thức nào trong số các phương thức này không được triển khai trong lớp con cháu, cờ cho biết sự hỗ trợ của đơn hàng cho thuộc tính đó được trả về.

//+------------------------------------------------------------------+
//|                                                        Order.mqh |
//|                        Copyright 2018, MetaQuotes Software Corp. |
//+------------------------------------------------------------------+
#property copyright "Copyright 2018, MetaQuotes Software Corp."
#property version   "1.00"
#include <Object.mqh>
#include "..\Defines.mqh"
//+------------------------------------------------------------------+
//| Abstract order class                                             |
//+------------------------------------------------------------------+
class COrder : public CObject
  {
private:
   ulong             m_ticket;                                    // Selected order/deal ticket (MQL5)
   long              m_long_prop[ORDER_PROP_INTEGER_TOTAL];       // Integer properties
   double            m_double_prop[ORDER_PROP_DOUBLE_TOTAL];      // Real properties
   string            m_string_prop[ORDER_PROP_STRING_TOTAL];      // String properties
   
   //--- Return the index of the array the double property is actually located at
   int               IndexProp(ENUM_ORDER_PROP_DOUBLE property)   const { return (int)property-ORDER_PROP_INTEGER_TOTAL;                        }
   //--- Return the index of the array the string property is actually located at
   int               IndexProp(ENUM_ORDER_PROP_STRING property)   const { return (int)property-ORDER_PROP_INTEGER_TOTAL-ORDER_PROP_DOUBLE_TOTAL;}
public:
   //--- Default constructor
                     COrder(void){;}
protected:
   //--- Protected parametric constructor
                     COrder(ENUM_ORDER_STATUS order_status,const ulong ticket);
                     
   //--- Get and return integer properties of a selected order from its parameters
   long              OrderMagicNumber(void)        const;
   long              OrderTicket(void)             const;
   long              OrderTicketFrom(void)         const;
   long              OrderTicketTo(void)           const;
   long              OrderPositionID(void)         const;
   long              OrderPositionByID(void)       const;
   long              OrderOpenTimeMSC(void)        const;
   long              OrderCloseTimeMSC(void)       const;
   long              OrderType(void)               const;
   long              OrderTypeByDirection(void)    const;
   long              OrderTypeFilling(void)        const;
   long              OrderTypeTime(void)           const;
   long              OrderReason(void)             const;
   long              DealOrder(void)               const;
   long              DealEntry(void)               const;
   bool              OrderCloseByStopLoss(void)    const;
   bool              OrderCloseByTakeProfit(void)  const;
   datetime          OrderOpenTime(void)           const;
   datetime          OrderCloseTime(void)          const;
   datetime          OrderExpiration(void)         const;
   datetime          PositionTimeUpdate(void)      const;
   datetime          PositionTimeUpdateMSC(void)   const;
   
   //--- Get and return real properties of a selected order from its parameters: (1) open price, (2) close price, (3) profit,
   //---  (4) commission, (5) swap, (6) volume, (7) unexecuted volume (8) StopLoss price, (9) TakeProfit price (10) StopLimit order price
   double            OrderOpenPrice(void)          const;
   double            OrderClosePrice(void)         const;
   double            OrderProfit(void)             const;
   double            OrderCommission(void)         const;
   double            OrderSwap(void)               const;
   double            OrderVolume(void)             const;
   double            OrderVolumeCurrent(void)      const;
   double            OrderStopLoss(void)           const;
   double            OrderTakeProfit(void)         const;
   double            OrderPriceStopLimit(void)     const;
   
   //--- Get and return string properties of a selected order from its parameters: (1) symbol, (2) comment, (3) ID at an exchange
   string            OrderSymbol(void)             const;
   string            OrderComment(void)            const;
   string            OrderExternalID(void)         const;
   
public:
   //--- Return (1) integer, (2) real and (3) string order properties from the property 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 by all possible properties
   virtual int       Compare(const CObject *node,const int mode=0) const;
   
//---
  };
//+------------------------------------------------------------------+
//| Closed parametric constructor                                    |
//+------------------------------------------------------------------+
COrder::COrder(ENUM_ORDER_STATUS order_status,const ulong ticket)
  {
  }
//+------------------------------------------------------------------+

Thực hiện phương pháp so sánh hai đơn hàng theo một thuộc tính được chỉ định:

//+------------------------------------------------------------------+
//| Compare COrder objects by all possible properties                |
//+------------------------------------------------------------------+
int COrder::Compare(const CObject *node,const int mode=0) const
  {
   const COrder *order_compared=node;
//--- compare integer properties of two orders
   if(mode<ORDER_PROP_INTEGER_TOTAL)
     {
      long value_compared=order_compared.GetProperty((ENUM_ORDER_PROP_INTEGER)mode);
      long value_current=this.GetProperty((ENUM_ORDER_PROP_INTEGER)mode);
      return(value_current>value_compared ? 1 : value_current<value_compared ? -1 : 0);
     }
//--- compare real properties of two orders
   else if(mode<ORDER_PROP_DOUBLE_TOTAL+ORDER_PROP_INTEGER_TOTAL)
     {
      double value_compared=order_compared.GetProperty((ENUM_ORDER_PROP_DOUBLE)mode);
      double value_current=this.GetProperty((ENUM_ORDER_PROP_DOUBLE)mode);
      return(value_current>value_compared ? 1 : value_current<value_compared ? -1 : 0);
     }
//--- compare string properties of two orders
   else if(mode<ORDER_PROP_DOUBLE_TOTAL+ORDER_PROP_INTEGER_TOTAL+ORDER_PROP_STRING_TOTAL)
     {
      string value_compared=order_compared.GetProperty((ENUM_ORDER_PROP_STRING)mode);
      string value_current=this.GetProperty((ENUM_ORDER_PROP_STRING)mode);
      return(value_current>value_compared ? 1 : value_current<value_compared ? -1 : 0);
     }
   return 0;
  }
//+------------------------------------------------------------------+

Phương thức nhận con trỏ tới đối tượng thứ tự có thuộc tính nên được so sánh với một giá trị nhất định, cũng như chính giá trị từ phép liệt kê thuộc tính thứ tự.

Nếu giá trị đơn hàng vượt quá giá trị so sánh, hệ thống trả về 1, nếu nó nhỏ hơn giá trị so sánh, hệ thống trả về -1, nếu không – 0. Danh sách các đơn hàng được thực hiện so sánh được sắp xếp sơ bộ theo thuộc tính so sánh.

Bây giờ, hãy thực hiện các phương thức để nhận các thuộc tính đơn hàng và ghi chúng vào các mảng thuộc tính. Đây là những phương pháp đã được khai báo trước đó trong phần riêng tư. Vì các phương thức nhận thuộc tính đơn hàng là đa nền tảng, hãy phân tích chúng bằng cách sử dụng số ma thuật EA làm ví dụ:

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

Nếu đây là mã cho MQL4, số ma thuật Magic được trả về bởi hàm MQL5 OrderMagicNumber(). Nếu không, hãy kiểm tra trạng thái đơn hàng. Tùy thuộc vào những gì chúng ta đang giải quyết, trả về số Magic của position, order or deal.

Các phương pháp còn lại để đọc và viết các thuộc tính của order/deal/position được tô sáng được thực hiện theo cùng một cách. Bạn có thể tự mình phân tích chúng.

Các phương thức để có được các thuộc tính order/deal/position:

//+------------------------------------------------------------------+
//| Return the ticket                                                |
//+------------------------------------------------------------------+
long COrder::OrderTicket(void) const
  {
#ifdef __MQL4__
   return ::OrderTicket();
#else
   long res=0;
   switch((ENUM_ORDER_STATUS)this.GetProperty(ORDER_PROP_STATUS))
     {
      case ORDER_STATUS_MARKET_ACTIVE     :
      case ORDER_STATUS_MARKET_PENDING    :
      case ORDER_STATUS_HISTORY_PENDING   :
      case ORDER_STATUS_HISTORY_ORDER     :
      case ORDER_STATUS_DEAL              : res=(long)m_ticket;                                 break;
      default                             : res=0;                                              break;
     }
   return res;
#endif
  }
//+------------------------------------------------------------------+
//| Return the parent order ticket                                   |
//+------------------------------------------------------------------+
long COrder::OrderTicketFrom(void) const
  {
   long ticket=0;
#ifdef __MQL4__
   string order_comment=::OrderComment();
   if(::StringFind(order_comment,"from #")>WRONG_VALUE) ticket=::StringToInteger(::StringSubstr(order_comment,6));
#endif
   return ticket;
  }
//+------------------------------------------------------------------+
//| Return the child order ticket                                    |
//+------------------------------------------------------------------+
long COrder::OrderTicketTo(void) const
  {
   long ticket=0;
#ifdef __MQL4__
   string order_comment=::OrderComment();
   if(::StringFind(order_comment,"to #")>WRONG_VALUE) ticket=::StringToInteger(::StringSubstr(order_comment,4));
#endif
   return ticket;
  }
//+------------------------------------------------------------------+
//| Return position ID                                               |
//+------------------------------------------------------------------+
long COrder::OrderPositionID(void) const
  {
#ifdef __MQL4__
   return ::OrderMagicNumber();
#else
   long res=0;
   switch((ENUM_ORDER_STATUS)this.GetProperty(ORDER_PROP_STATUS))
     {
      case ORDER_STATUS_MARKET_ACTIVE     : res=::PositionGetInteger(POSITION_IDENTIFIER);            break;
      case ORDER_STATUS_MARKET_PENDING    : res=::OrderGetInteger(ORDER_POSITION_ID);                 break;
      case ORDER_STATUS_HISTORY_PENDING   :
      case ORDER_STATUS_HISTORY_ORDER     : res=::HistoryOrderGetInteger(m_ticket,ORDER_POSITION_ID); break;
      case ORDER_STATUS_DEAL              : res=::HistoryDealGetInteger(m_ticket,DEAL_POSITION_ID);   break;
      default                             : res=0;                                                    break;
     }
   return res;
#endif
  }
//+------------------------------------------------------------------+
//| Return the opposite position ID                                  |
//+------------------------------------------------------------------+
long COrder::OrderPositionByID(void) const
  {
#ifdef __MQL4__
   return 0;
#else
   long res=0;
   switch((ENUM_ORDER_STATUS)this.GetProperty(ORDER_PROP_STATUS))
     {
      case ORDER_STATUS_MARKET_PENDING    : res=::OrderGetInteger(ORDER_POSITION_BY_ID);                 break;
      case ORDER_STATUS_HISTORY_PENDING   :
      case ORDER_STATUS_HISTORY_ORDER     : res=::HistoryOrderGetInteger(m_ticket,ORDER_POSITION_BY_ID); break;
      default                             : res=0;                                                       break;
     }
   return res;
#endif
  }
//+------------------------------------------------------------------+
//| Return open time in milliseconds                                 |
//+------------------------------------------------------------------+
long COrder::OrderOpenTimeMSC(void) const
  {
#ifdef __MQL4__
   return (long)::OrderOpenTime();
#else
   long res=0;
   switch((ENUM_ORDER_STATUS)this.GetProperty(ORDER_PROP_STATUS))
     {
      case ORDER_STATUS_MARKET_ACTIVE     : res=::PositionGetInteger(POSITION_TIME_MSC);                 break;
      case ORDER_STATUS_MARKET_PENDING    : res=::OrderGetInteger(ORDER_TIME_SETUP_MSC);                 break;
      case ORDER_STATUS_HISTORY_PENDING   :
      case ORDER_STATUS_HISTORY_ORDER     : res=::HistoryOrderGetInteger(m_ticket,ORDER_TIME_SETUP_MSC); break;
      case ORDER_STATUS_DEAL              : res=::HistoryDealGetInteger(m_ticket,DEAL_TIME_MSC);         break;
      default                             : res=0;                                                       break;
     }
   return res;
#endif
  }
//+------------------------------------------------------------------+
//| Return close time in milliseconds                                |
//+------------------------------------------------------------------+
long COrder::OrderCloseTimeMSC(void) const
  {
#ifdef __MQL4__
   return (long)::OrderCloseTime();
#else
   long res=0;
   switch((ENUM_ORDER_STATUS)this.GetProperty(ORDER_PROP_STATUS))
     {
      case ORDER_STATUS_HISTORY_PENDING   :
      case ORDER_STATUS_HISTORY_ORDER     : res=::HistoryOrderGetInteger(m_ticket,ORDER_TIME_DONE_MSC);     break;
      case ORDER_STATUS_DEAL              : res=(datetime)::HistoryDealGetInteger(m_ticket,DEAL_TIME_MSC);  break;
      default                             : res=0;                                                          break;
     }
   return res;
#endif
  }
//+------------------------------------------------------------------+
//| Return the type                                                  |
//+------------------------------------------------------------------+
long COrder::OrderType(void) const
  {
#ifdef __MQL4__
   return (long)::OrderType();
#else
   long res=0;
   switch((ENUM_ORDER_STATUS)this.GetProperty(ORDER_PROP_STATUS))
     {
      case ORDER_STATUS_MARKET_ACTIVE     : res=::PositionGetInteger(POSITION_TYPE);            break;
      case ORDER_STATUS_MARKET_PENDING    : res=::OrderGetInteger(ORDER_TYPE);                  break;
      case ORDER_STATUS_HISTORY_PENDING   :
      case ORDER_STATUS_HISTORY_ORDER     : res=::HistoryOrderGetInteger(m_ticket,ORDER_TYPE);  break;
      case ORDER_STATUS_DEAL              : res=::HistoryDealGetInteger(m_ticket,DEAL_TYPE);    break;
      default                             : res=0;                                              break;
     }
   return res;
#endif
  }
//+------------------------------------------------------------------+
//| Return the type by direction                                     |
//+------------------------------------------------------------------+
long COrder::OrderTypeByDirection(void) const
  {
   ENUM_ORDER_STATUS status=(ENUM_ORDER_STATUS)this.GetProperty(ORDER_PROP_STATUS);
   if(status==ORDER_STATUS_MARKET_ACTIVE)
     {
      return(this.OrderType()==POSITION_TYPE_BUY ? ORDER_TYPE_BUY : ORDER_TYPE_SELL);
     }
   if(status==ORDER_STATUS_MARKET_PENDING || status==ORDER_STATUS_HISTORY_PENDING)
     {
      return
        (
         this.OrderType()==ORDER_TYPE_BUY_LIMIT || 
         this.OrderType()==ORDER_TYPE_BUY_STOP 
         #ifdef __MQL5__                        ||
         this.OrderType()==ORDER_TYPE_BUY_STOP_LIMIT 
         #endif ? 
         ORDER_TYPE_BUY : 
         ORDER_TYPE_SELL
        );
     }
   if(status==ORDER_STATUS_HISTORY_ORDER)
     {
      return this.OrderType();
     }
   return WRONG_VALUE;
  }
//+------------------------------------------------------------------+
//| Return execution type by residue                                 |
//+------------------------------------------------------------------+
long COrder::OrderTypeFilling(void) const
  {
#ifdef __MQL4__
   return (long)ORDER_FILLING_RETURN;
#else 
   long res=0;
   switch((ENUM_ORDER_STATUS)this.GetProperty(ORDER_PROP_STATUS))
     {
      case ORDER_STATUS_MARKET_PENDING    : res=::OrderGetInteger(ORDER_TYPE_FILLING);                break;
      case ORDER_STATUS_HISTORY_PENDING   :
      case ORDER_STATUS_HISTORY_ORDER     : res=::HistoryOrderGetInteger(m_ticket,ORDER_TYPE_FILLING);break;
      default                             : res=0;                                                    break;
     }
   return res;
#endif 
  }
//+------------------------------------------------------------------+
//| Return order lifetime                                            |
//+------------------------------------------------------------------+
long COrder::OrderTypeTime(void) const
  {
#ifdef __MQL4__
   return (long)ORDER_TIME_GTC;
#else 
   long res=0;
   switch((ENUM_ORDER_STATUS)this.GetProperty(ORDER_PROP_STATUS))
     {
      case ORDER_STATUS_MARKET_PENDING    : res=::OrderGetInteger(ORDER_TYPE_TIME);                break;
      case ORDER_STATUS_HISTORY_PENDING   :
      case ORDER_STATUS_HISTORY_ORDER     : res=::HistoryOrderGetInteger(m_ticket,ORDER_TYPE_TIME);break;
      default                             : res=0;                                                 break;
     }
   return res;
#endif 
  }
//+------------------------------------------------------------------+
//| Order reason or source                                           |
//+------------------------------------------------------------------+
long COrder::OrderReason(void) const
  {
#ifdef __MQL4__
   return
     (
      this.OrderCloseByStopLoss()   ?  ORDER_REASON_SL      :
      this.OrderCloseByTakeProfit() ?  ORDER_REASON_TP      :  
      this.OrderMagicNumber()!=0    ?  ORDER_REASON_EXPERT  : WRONG_VALUE
     );
#else 
   long res=WRONG_VALUE;
   switch((ENUM_ORDER_STATUS)this.GetProperty(ORDER_PROP_STATUS))
     {
      case ORDER_STATUS_MARKET_ACTIVE     : res=::PositionGetInteger(POSITION_REASON);          break;
      case ORDER_STATUS_MARKET_PENDING    : res=::OrderGetInteger(ORDER_REASON);                break;
      case ORDER_STATUS_HISTORY_PENDING   :
      case ORDER_STATUS_HISTORY_ORDER     : res=::HistoryOrderGetInteger(m_ticket,ORDER_REASON);break;
      case ORDER_STATUS_DEAL              : res=::HistoryDealGetInteger(m_ticket,DEAL_REASON);  break;
      default                             : res=WRONG_VALUE;                                    break;
     }
   return res;
#endif 
  }
//+------------------------------------------------------------------+
//| Order, based on which a deal is performed                        |
//+------------------------------------------------------------------+
long COrder::DealOrder(void) const
  {
#ifdef __MQL4__
   return ::OrderTicket();
#else 
   long res=0;
   switch((ENUM_ORDER_STATUS)this.GetProperty(ORDER_PROP_STATUS))
     {
      case ORDER_STATUS_MARKET_ACTIVE     : res=::PositionGetInteger(POSITION_IDENTIFIER);            break;
      case ORDER_STATUS_MARKET_PENDING    : res=::OrderGetInteger(ORDER_POSITION_ID);                 break;
      case ORDER_STATUS_HISTORY_PENDING   :
      case ORDER_STATUS_HISTORY_ORDER     : res=::HistoryOrderGetInteger(m_ticket,ORDER_POSITION_ID); break;
      case ORDER_STATUS_DEAL              : res=::HistoryDealGetInteger(m_ticket,DEAL_ORDER);         break;
      default                             : res=0;                                                    break;
     }
   return res;
#endif 
  }
//+------------------------------------------------------------------+
//| Deal direction IN, OUT, IN/OUT                                   |
//+------------------------------------------------------------------+
long COrder::DealEntry(void) const
  {
#ifdef __MQL4__
   return ::OrderType();
#else 
   long res=WRONG_VALUE;
   switch((ENUM_ORDER_STATUS)this.GetProperty(ORDER_PROP_STATUS))
     {
      case ORDER_STATUS_DEAL  : res=::HistoryDealGetInteger(m_ticket,DEAL_ENTRY);break;
      default                 : res=WRONG_VALUE;                                 break;
     }
   return res;
#endif 
  }
//+------------------------------------------------------------------+
//| Return the flag of closing a position by StopLoss                |
//+------------------------------------------------------------------+
bool COrder::OrderCloseByStopLoss(void) const
  {
#ifdef __MQL4__
   return(::StringFind(::OrderComment(),"[sl")>WRONG_VALUE);
#else 
   return
     (
      this.Status()==ORDER_STATUS_HISTORY_ORDER ? this.OrderReason()==ORDER_REASON_SL : 
      this.Status()==ORDER_STATUS_DEAL ? this.OrderReason()==DEAL_REASON_SL : false
     );
#endif 
  }
//+------------------------------------------------------------------+
//| Return the flag of closing position by TakeProfit                |
//+------------------------------------------------------------------+
bool COrder::OrderCloseByTakeProfit(void) const
  {
#ifdef __MQL4__
   return(::StringFind(::OrderComment(),"[tp")>WRONG_VALUE);
#else 
   return
     (
      this.Status()==ORDER_STATUS_HISTORY_ORDER ? this.OrderReason()==ORDER_REASON_TP : 
      this.Status()==ORDER_STATUS_DEAL ? this.OrderReason()==DEAL_REASON_TP : false
     );
#endif 
  }
//+------------------------------------------------------------------+
//| Return open time                                                 |
//+------------------------------------------------------------------+
datetime COrder::OrderOpenTime(void) const
  {
#ifdef __MQL4__
   return ::OrderOpenTime();
#else 
   datetime res=0;
   switch((ENUM_ORDER_STATUS)this.GetProperty(ORDER_PROP_STATUS))
     {
      case ORDER_STATUS_MARKET_ACTIVE     : res=(datetime)::PositionGetInteger(POSITION_TIME);                 break;
      case ORDER_STATUS_MARKET_PENDING    : res=(datetime)::OrderGetInteger(ORDER_TIME_SETUP);                 break;
      case ORDER_STATUS_HISTORY_PENDING   :
      case ORDER_STATUS_HISTORY_ORDER     : res=(datetime)::HistoryOrderGetInteger(m_ticket,ORDER_TIME_SETUP); break;
      case ORDER_STATUS_DEAL              : res=(datetime)::HistoryDealGetInteger(m_ticket,DEAL_TIME);         break;
      default                             : res=0;                                                             break;
     }
   return res;
#endif 
  }
//+------------------------------------------------------------------+
//| Return close time                                                |
//+------------------------------------------------------------------+
datetime COrder::OrderCloseTime(void) const
  {
#ifdef __MQL4__
   return ::OrderCloseTime();
#else 
   datetime res=0;
   switch((ENUM_ORDER_STATUS)this.GetProperty(ORDER_PROP_STATUS))
     {
      case ORDER_STATUS_HISTORY_PENDING   :
      case ORDER_STATUS_HISTORY_ORDER     : res=(datetime)::HistoryOrderGetInteger(m_ticket,ORDER_TIME_DONE);  break;
      case ORDER_STATUS_DEAL              : res=(datetime)::HistoryDealGetInteger(m_ticket,DEAL_TIME);         break;
      default                             : res=0;                                                             break;
     }
   return res;
#endif 
  }
//+------------------------------------------------------------------+
//| Return expiration time                                           |
//+------------------------------------------------------------------+
datetime COrder::OrderExpiration(void) const
  {
#ifdef __MQL4__
   return ::OrderExpiration();
#else 
   datetime res=0;
   switch((ENUM_ORDER_STATUS)this.GetProperty(ORDER_PROP_STATUS))
     {
      case ORDER_STATUS_MARKET_PENDING    : res=(datetime)::OrderGetInteger(ORDER_TIME_EXPIRATION);                  break;
      case ORDER_STATUS_HISTORY_PENDING   : res=(datetime)::HistoryOrderGetInteger(m_ticket,ORDER_TIME_EXPIRATION);  break;
      default                             : res=0;                                                                   break;
     }
   return res;
#endif 
  }
//+------------------------------------------------------------------+
//| Position change time in seconds                                  |
//+------------------------------------------------------------------+
datetime COrder::PositionTimeUpdate(void) const
  {
#ifdef __MQL4__
   return 0;
#else 
   datetime res=0;
   switch((ENUM_ORDER_STATUS)this.GetProperty(ORDER_PROP_STATUS))
     {
      case ORDER_STATUS_MARKET_ACTIVE  : res=(datetime)::PositionGetInteger(POSITION_TIME_UPDATE); break;
      default                          : res=0;                                                    break;
     }
   return res;
#endif 
  }
//+------------------------------------------------------------------+
//| Position change time in milliseconds                             |
//+------------------------------------------------------------------+
datetime COrder::PositionTimeUpdateMSC(void) const
  {
#ifdef __MQL4__
   return 0;
#else 
   datetime res=0;
   switch((ENUM_ORDER_STATUS)this.GetProperty(ORDER_PROP_STATUS))
     {
      case ORDER_STATUS_MARKET_ACTIVE  : res=(datetime)::PositionGetInteger(POSITION_TIME_UPDATE_MSC);break;
      default                          : res=0;                                                       break;
     }
   return res;
#endif 
  }
//+------------------------------------------------------------------+

Các phương thức để có được các thuộc tính thực của order/deal/position:

//+------------------------------------------------------------------+
//| Return open price                                                |
//+------------------------------------------------------------------+
double COrder::OrderOpenPrice(void) const
  {
#ifdef __MQL4__
   return ::OrderOpenPrice();
#else 
   double res=0;
   switch((ENUM_ORDER_STATUS)this.GetProperty(ORDER_PROP_STATUS))
     {
      case ORDER_STATUS_MARKET_ACTIVE     : res=::PositionGetDouble(POSITION_PRICE_OPEN);          break;
      case ORDER_STATUS_MARKET_PENDING    : res=::OrderGetDouble(ORDER_PRICE_OPEN);                break;
      case ORDER_STATUS_HISTORY_PENDING   :
      case ORDER_STATUS_HISTORY_ORDER     : res=::HistoryOrderGetDouble(m_ticket,ORDER_PRICE_OPEN);break;
      case ORDER_STATUS_DEAL              : res=::HistoryDealGetDouble(m_ticket,DEAL_PRICE);       break;
      default                             : res=0;                                                 break;
     }
   return res;
#endif 
  }
//+------------------------------------------------------------------+
//| Return close price                                               |
//+------------------------------------------------------------------+
double COrder::OrderClosePrice(void) const
  {
#ifdef __MQL4__
   return ::OrderClosePrice();
#else 
   double res=0;
   switch((ENUM_ORDER_STATUS)this.GetProperty(ORDER_PROP_STATUS))
     {
      case ORDER_STATUS_HISTORY_PENDING   :
      case ORDER_STATUS_HISTORY_ORDER     : res=::HistoryOrderGetDouble(m_ticket,ORDER_PRICE_OPEN);break;
      case ORDER_STATUS_DEAL              : res=::HistoryDealGetDouble(m_ticket,DEAL_PRICE);       break;
      default                             : res=0;                                                 break;
     }
   return res;
#endif 
  }
//+------------------------------------------------------------------+
//| Return profit                                                    |
//+------------------------------------------------------------------+
double COrder::OrderProfit(void) const
  {
#ifdef __MQL4__
   return ::OrderProfit();
#else 
   double res=0;
   switch((ENUM_ORDER_STATUS)this.GetProperty(ORDER_PROP_STATUS))
     {
      case ORDER_STATUS_MARKET_ACTIVE  : res=::PositionGetDouble(POSITION_PROFIT);        break;
      case ORDER_STATUS_DEAL           : res=::HistoryDealGetDouble(m_ticket,DEAL_PROFIT);break;
      default                          : res=0;                                           break;
     }
   return res;
#endif 
  }
//+------------------------------------------------------------------+
//| Return commission                                                |
//+------------------------------------------------------------------+
double COrder::OrderCommission(void) const
  {
#ifdef __MQL4__
   return ::OrderCommission();
#else 
   double res=0;
   switch((ENUM_ORDER_STATUS)this.GetProperty(ORDER_PROP_STATUS))
     {
      case ORDER_STATUS_DEAL  : res=::HistoryDealGetDouble(m_ticket,DEAL_COMMISSION);  break;
      default                 : res=0;                                                 break;
     }
   return res;
#endif 
  }
//+------------------------------------------------------------------+
//| Return swap                                                      |
//+------------------------------------------------------------------+
double COrder::OrderSwap(void) const
  {
#ifdef __MQL4__
   return ::OrderSwap();
#else 
   double res=0;
   switch((ENUM_ORDER_STATUS)this.GetProperty(ORDER_PROP_STATUS))
     {
      case ORDER_STATUS_MARKET_ACTIVE  : res=::PositionGetDouble(POSITION_SWAP);          break;
      case ORDER_STATUS_DEAL           : res=::HistoryDealGetDouble(m_ticket,DEAL_SWAP);  break;
      default                          : res=0;                                           break;
     }
   return res;
#endif 
  }
//+------------------------------------------------------------------+
//| Return volume                                                    |
//+------------------------------------------------------------------+
double COrder::OrderVolume(void) const
  {
#ifdef __MQL4__
   return ::OrderLots();
#else 
   double res=0;
   switch((ENUM_ORDER_STATUS)this.GetProperty(ORDER_PROP_STATUS))
     {
      case ORDER_STATUS_MARKET_ACTIVE     : res=::PositionGetDouble(POSITION_VOLUME);                    break;
      case ORDER_STATUS_MARKET_PENDING    : res=::OrderGetDouble(ORDER_VOLUME_INITIAL);                  break;
      case ORDER_STATUS_HISTORY_PENDING   :
      case ORDER_STATUS_HISTORY_ORDER     : res=::HistoryOrderGetDouble(m_ticket,ORDER_VOLUME_INITIAL);  break;
      case ORDER_STATUS_DEAL              : res=::HistoryDealGetDouble(m_ticket,DEAL_VOLUME);            break;
      default                             : res=0;                                                       break;
     }
   return res;
#endif 
  }
//+------------------------------------------------------------------+
//| Return unexecuted volume                                         |
//+------------------------------------------------------------------+
double COrder::OrderVolumeCurrent(void) const
  {
#ifdef __MQL4__
   return ::OrderLots();
#else 
   double res=0;
   switch((ENUM_ORDER_STATUS)this.GetProperty(ORDER_PROP_STATUS))
     {
      case ORDER_STATUS_MARKET_PENDING    : res=::OrderGetDouble(ORDER_VOLUME_CURRENT);                  break;
      case ORDER_STATUS_HISTORY_PENDING   :
      case ORDER_STATUS_HISTORY_ORDER     : res=::HistoryOrderGetDouble(m_ticket,ORDER_VOLUME_CURRENT);  break;
      default                             : res=0;                                                       break;
     }
   return res;
#endif 
  }
//+------------------------------------------------------------------+
//| Return StopLoss price                                            |
//+------------------------------------------------------------------+
double COrder::OrderStopLoss(void) const
  {
#ifdef __MQL4__
   return ::OrderStopLoss();
#else 
   double res=0;
   switch((ENUM_ORDER_STATUS)this.GetProperty(ORDER_PROP_STATUS))
     {
      case ORDER_STATUS_MARKET_ACTIVE     : res=::PositionGetDouble(POSITION_SL);            break;
      case ORDER_STATUS_MARKET_PENDING    : res=::OrderGetDouble(ORDER_SL);                  break;
      case ORDER_STATUS_HISTORY_PENDING   :
      case ORDER_STATUS_HISTORY_ORDER     : res=::HistoryOrderGetDouble(m_ticket,ORDER_SL);  break;
      default                             : res=0;                                           break;
     }
   return res;
#endif 
  }
//+------------------------------------------------------------------+
//| Return TakeProfit price                                          |
//+------------------------------------------------------------------+
double COrder::OrderTakeProfit(void) const
  {
#ifdef __MQL4__
   return ::OrderTakeProfit();
#else 
   double res=0;
   switch((ENUM_ORDER_STATUS)this.GetProperty(ORDER_PROP_STATUS))
     {
      case ORDER_STATUS_MARKET_ACTIVE     : res=::PositionGetDouble(POSITION_TP);            break;
      case ORDER_STATUS_MARKET_PENDING    : res=::OrderGetDouble(ORDER_TP);                  break;
      case ORDER_STATUS_HISTORY_PENDING   :
      case ORDER_STATUS_HISTORY_ORDER     : res=::HistoryOrderGetDouble(m_ticket,ORDER_TP);  break;
      default                             : res=0;                                           break;
     }
   return res;
#endif 
  }
//+------------------------------------------------------------------+
//| Return Limit order price                                         |
//| when StopLimit order is activated                                |
//+------------------------------------------------------------------+
double COrder::OrderPriceStopLimit(void) const
  {
#ifdef __MQL4__
   return 0;
#else 
   double res=0;
   switch((ENUM_ORDER_STATUS)this.GetProperty(ORDER_PROP_STATUS))
     {
      case ORDER_STATUS_MARKET_PENDING    : res=::OrderGetDouble(ORDER_PRICE_STOPLIMIT);                 break;
      case ORDER_STATUS_HISTORY_PENDING   :
      case ORDER_STATUS_HISTORY_ORDER     : res=::HistoryOrderGetDouble(m_ticket,ORDER_PRICE_STOPLIMIT); break;
      default                             : res=0;                                                       break;
     }
   return res;
#endif 
  }
//+------------------------------------------------------------------+

Các phương thức để có được các thuộc tính chuỗi order/deal/position:

//+------------------------------------------------------------------+
//| Return symbol                                                    |
//+------------------------------------------------------------------+
string COrder::OrderSymbol(void) const
  {
#ifdef __MQL4__
   return ::OrderSymbol();
#else 
   string res="";
   switch((ENUM_ORDER_STATUS)this.GetProperty(ORDER_PROP_STATUS))
     {
      case ORDER_STATUS_MARKET_ACTIVE     : res=::PositionGetString(POSITION_SYMBOL);           break;
      case ORDER_STATUS_MARKET_PENDING    : res=::OrderGetString(ORDER_SYMBOL);                 break;
      case ORDER_STATUS_HISTORY_PENDING   :
      case ORDER_STATUS_HISTORY_ORDER     : res=::HistoryOrderGetString(m_ticket,ORDER_SYMBOL); break;
      case ORDER_STATUS_DEAL              : res=::HistoryDealGetString(m_ticket,DEAL_SYMBOL);   break;
      default                             : res="";                                             break;
     }
   return res;
#endif 
  }
//+------------------------------------------------------------------+
//| Return comment                                                   |
//+------------------------------------------------------------------+
string COrder::OrderComment(void) const
  {
#ifdef __MQL4__
   return ::OrderComment();
#else 
   string res="";
   switch((ENUM_ORDER_STATUS)this.GetProperty(ORDER_PROP_STATUS))
     {
      case ORDER_STATUS_MARKET_ACTIVE     : res=::PositionGetString(POSITION_COMMENT);          break;
      case ORDER_STATUS_MARKET_PENDING    : res=::OrderGetString(ORDER_COMMENT);                break;
      case ORDER_STATUS_HISTORY_PENDING   :
      case ORDER_STATUS_HISTORY_ORDER     : res=::HistoryOrderGetString(m_ticket,ORDER_COMMENT);break;
      case ORDER_STATUS_DEAL              : res=::HistoryDealGetString(m_ticket,DEAL_COMMENT);  break;
      default                             : res="";                                             break;
     }
   return res;
#endif 
  }
//+------------------------------------------------------------------+
//| Return ID used by an exchange                                    |
//+------------------------------------------------------------------+
string COrder::OrderExternalID(void) const
  {
#ifdef __MQL4__
   return "";
#else 
   string res="";
   switch((ENUM_ORDER_STATUS)this.GetProperty(ORDER_PROP_STATUS))
     {
      case ORDER_STATUS_MARKET_PENDING    : res=::OrderGetString(ORDER_EXTERNAL_ID);                  break;
      case ORDER_STATUS_HISTORY_PENDING   :
      case ORDER_STATUS_HISTORY_ORDER     : res=::HistoryOrderGetString(m_ticket,ORDER_EXTERNAL_ID);  break;
      case ORDER_STATUS_DEAL              : res=::HistoryDealGetString(m_ticket,DEAL_EXTERNAL_ID);    break;
      default                             : res="";                                                   break;
     }
   return res;
#endif 
  }
//+------------------------------------------------------------------+

Chúng ta đã khai báo và thực hiện các phương thức riêng để nhận các thuộc tính từ dữ liệu order. Bây giờ là lúc triển khai hàm tạo của lớp được bảo vệ bằng cách viết cho nó các phương thức lưu tất cả các thuộc tính của đơn hàng có ticket đã được chuyển cho hàm tạo.

Thực hiện trình xây dựng lớp được bảo vệ:

//+------------------------------------------------------------------+
//| Closed parametric constructor                                    |
//+------------------------------------------------------------------+
COrder::COrder(ENUM_ORDER_STATUS order_status,const ulong ticket)
  {
//--- Save integer properties
   m_ticket=ticket;
   m_long_prop[ORDER_PROP_STATUS]                              = order_status;
   m_long_prop[ORDER_PROP_MAGIC]                               = this.OrderMagicNumber();
   m_long_prop[ORDER_PROP_TICKET]                              = this.OrderTicket();
   m_long_prop[ORDER_PROP_TIME_OPEN]                           = (long)(ulong)this.OrderOpenTime();
   m_long_prop[ORDER_PROP_TIME_CLOSE]                          = (long)(ulong)this.OrderCloseTime();
   m_long_prop[ORDER_PROP_TIME_EXP]                            = (long)(ulong)this.OrderExpiration();
   m_long_prop[ORDER_PROP_TYPE]                                = this.OrderType();
   m_long_prop[ORDER_PROP_DIRECTION]                           = this.OrderTypeByDirection();
   m_long_prop[ORDER_PROP_POSITION_ID]                         = this.OrderPositionID();
   m_long_prop[ORDER_PROP_REASON]                              = this.OrderReason();
   m_long_prop[ORDER_PROP_DEAL_ORDER]                          = this.DealOrder();
   m_long_prop[ORDER_PROP_DEAL_ENTRY]                          = this.DealEntry();
   m_long_prop[ORDER_PROP_POSITION_BY_ID]                      = this.OrderPositionByID();
   m_long_prop[ORDER_PROP_TIME_OPEN_MSC]                       = this.OrderOpenTimeMSC();
   m_long_prop[ORDER_PROP_TIME_CLOSE_MSC]                      = this.OrderCloseTimeMSC();
   m_long_prop[ORDER_PROP_TIME_UPDATE]                         = (long)(ulong)this.PositionTimeUpdate();
   m_long_prop[ORDER_PROP_TIME_UPDATE_MSC]                     = (long)(ulong)this.PositionTimeUpdateMSC();
   
//--- Save real properties
   m_double_prop[this.IndexProp(ORDER_PROP_PRICE_OPEN)]        = this.OrderOpenPrice();
   m_double_prop[this.IndexProp(ORDER_PROP_PRICE_CLOSE)]       = this.OrderClosePrice();
   m_double_prop[this.IndexProp(ORDER_PROP_PROFIT)]            = this.OrderProfit();
   m_double_prop[this.IndexProp(ORDER_PROP_COMMISSION)]        = this.OrderCommission();
   m_double_prop[this.IndexProp(ORDER_PROP_SWAP)]              = this.OrderSwap();
   m_double_prop[this.IndexProp(ORDER_PROP_VOLUME)]            = this.OrderVolume();
   m_double_prop[this.IndexProp(ORDER_PROP_SL)]                = this.OrderStopLoss();
   m_double_prop[this.IndexProp(ORDER_PROP_TP)]                = this.OrderTakeProfit();
   m_double_prop[this.IndexProp(ORDER_PROP_VOLUME_CURRENT)]    = this.OrderVolumeCurrent();
   m_double_prop[this.IndexProp(ORDER_PROP_PRICE_STOP_LIMIT)]  = this.OrderPriceStopLimit();
   
//--- Save string properties
   m_string_prop[this.IndexProp(ORDER_PROP_SYMBOL)]            = this.OrderSymbol();
   m_string_prop[this.IndexProp(ORDER_PROP_COMMENT)]           = this.OrderComment();
   m_string_prop[this.IndexProp(ORDER_PROP_EXT_ID)]            = this.OrderExternalID();
   
//--- Save additional integer properties
   m_long_prop[ORDER_PROP_PROFIT_PT]                           = this.ProfitInPoints();
   m_long_prop[ORDER_PROP_TICKET_FROM]                         = this.OrderTicketFrom();
   m_long_prop[ORDER_PROP_TICKET_TO]                           = this.OrderTicketTo();
   m_long_prop[ORDER_PROP_CLOSE_BY_SL]                         = this.OrderCloseByStopLoss();
   m_long_prop[ORDER_PROP_CLOSE_BY_TP]                         = this.OrderCloseByTakeProfit();
   
//--- Save additional real properties
   m_double_prop[this.IndexProp(ORDER_PROP_PROFIT_FULL)]       = this.ProfitFull();
  }
//+------------------------------------------------------------------+

Khi chúng ta chuyển qua lịch sử hoặc market orders/deals/positions trong một vòng lặp hoặc chọn market orders/deals/positions, một đối tượng mới xuất phát từ lớp COrder được tạo ra. Trình xây dựng lớp có trạng thái đơn hàng và ticket được gọi trong trình tạo lớp chỉ nhận được vé đặt hàng. Tiếp theo, các mảng thuộc tính thứ tự được điền đơn giản vào hàm tạo của lớp COrder bằng các phương thức được mô tả ở trên. Vì vậy, mỗi market orders/deals/positions mới là có tài sản duy nhất của nó. Tất cả chúng sẽ được lưu trữ trong danh sách, trong khi làm việc với các danh sách đó là hoạt động chính của thư viện. Các danh sách có thể được sắp xếp theo bất kỳ thuộc tính order/deal/position. Bên cạnh đó, có thể tạo danh sách mới trong số những danh sách đã chọn.

Ở giai đoạn này, chức năng cơ bản của lớp thứ tự trừu tượng COrder đã được triển khai. Đây là một lớp cơ bản để lưu trữ các loại đơn đặt hàng, giao dịch và vị trí. Tất cả những thứ khác sẽ được bắt nguồn từ nó để tạo các đối tượng chia cho các loại đơn đặt hàng, giao dịch và vị trí. 

Thư viện được tạo để truy cập đơn giản vào dữ liệu và phát triển chương trình dễ dàng.
Ở trạng thái hiện tại, chúng ta có ba phương thức công khai để truy cập các thuộc tính của thứ tự trừu tượng GetProperty(XXX). Bạn có thể sử dụng chúng, nhưng điều này không thuận tiện lắm vì bạn cần nhớ tên thành viên của bảng liệt kê mô tả một thuộc tính đơn hàng cụ thể. Do đó, chúng ta sẽ thêm một số phương thức công khai để có được dữ liệu cần thiết. Các phương thức này sẽ có tên hợp lý để ngay lập tức rõ ràng thuộc tính nào có thể thu được bằng cách sử dụng một phương thức cụ thể.

Các phương thức này có thể được sử dụng trong các chương trình tùy chỉnh để đọc các thuộc tính của order/deal/position đã chọn:

   //--- Return (1) ticket, (2) parent order ticket, (3) derived order ticket, (4) magic number, (5) order reason (6) position ID
   //--- (7) opposite position ID, (8) flag of closing by StopLoss, (9) flag of closing by TakeProfit (10) open time, (11) close time,
   //--- (12) open time in milliseconds, (13) close time in milliseconds (14) expiration date, (15) type, (16) status, (17) direction
   long              Ticket(void)                                       const { return this.GetProperty(ORDER_PROP_TICKET);                     }
   long              TicketFrom(void)                                   const { return this.GetProperty(ORDER_PROP_TICKET_FROM);                }
   long              TicketTo(void)                                     const { return this.GetProperty(ORDER_PROP_TICKET_TO);                  }
   long              Magic(void)                                        const { return this.GetProperty(ORDER_PROP_MAGIC);                      }
   long              Reason(void)                                       const { return this.GetProperty(ORDER_PROP_REASON);                     }
   long              PositionID(void)                                   const { return this.GetProperty(ORDER_PROP_POSITION_ID);                }
   long              PositionByID(void)                                 const { return this.GetProperty(ORDER_PROP_POSITION_BY_ID);             }
   bool              IsCloseByStopLoss(void)                            const { return (bool)this.GetProperty(ORDER_PROP_CLOSE_BY_SL);          }
   bool              IsCloseByTakeProfit(void)                          const { return (bool)this.GetProperty(ORDER_PROP_CLOSE_BY_TP);          }
   datetime          TimeOpen(void)                                     const { return (datetime)this.GetProperty(ORDER_PROP_TIME_OPEN);        }
   datetime          TimeClose(void)                                    const { return (datetime)this.GetProperty(ORDER_PROP_TIME_CLOSE);       }
   datetime          TimeOpenMSC(void)                                  const { return (datetime)this.GetProperty(ORDER_PROP_TIME_OPEN_MSC);    }
   datetime          TimeCloseMSC(void)                                 const { return (datetime)this.GetProperty(ORDER_PROP_TIME_CLOSE_MSC);   }
   datetime          TimeExpiration(void)                               const { return (datetime)this.GetProperty(ORDER_PROP_TIME_EXP);         }
   ENUM_ORDER_TYPE   TypeOrder(void)                                    const { return (ENUM_ORDER_TYPE)this.GetProperty(ORDER_PROP_TYPE);      }
   ENUM_ORDER_STATUS Status(void)                                       const { return (ENUM_ORDER_STATUS)this.GetProperty(ORDER_PROP_STATUS);  }
   ENUM_ORDER_TYPE   TypeByDirection(void)                              const { return (ENUM_ORDER_TYPE)this.GetProperty(ORDER_PROP_DIRECTION); }
   
   //--- Return (1) open price, (2) close price, (3) profit, (4) commission, (5) swap, (6) volume, 
   //--- (7) unexecuted volume (8) StopLoss and (9) TakeProfit (10) StopLimit order price
   double            PriceOpen(void)                                    const { return this.GetProperty(ORDER_PROP_PRICE_OPEN);                 }
   double            PriceClose(void)                                   const { return this.GetProperty(ORDER_PROP_PRICE_CLOSE);                }
   double            Profit(void)                                       const { return this.GetProperty(ORDER_PROP_PROFIT);                     }
   double            Comission(void)                                    const { return this.GetProperty(ORDER_PROP_COMMISSION);                 }
   double            Swap(void)                                         const { return this.GetProperty(ORDER_PROP_SWAP);                       }
   double            Volume(void)                                       const { return this.GetProperty(ORDER_PROP_VOLUME);                     }
   double            VolumeCurrent(void)                                const { return this.GetProperty(ORDER_PROP_VOLUME_CURRENT);             }
   double            StopLoss(void)                                     const { return this.GetProperty(ORDER_PROP_SL);                         }
   double            TakeProfit(void)                                   const { return this.GetProperty(ORDER_PROP_TP);                         }
   double            PriceStopLimit(void)                               const { return this.GetProperty(ORDER_PROP_PRICE_STOP_LIMIT);           }
   
   //--- Return (1) symbol, (2) comment, (3) ID at an exchange
   string            Symbol(void)                                       const { return this.GetProperty(ORDER_PROP_SYMBOL);                     }
   string            Comment(void)                                      const { return this.GetProperty(ORDER_PROP_COMMENT);                    }
   string            ExternalID(void)                                   const { return this.GetProperty(ORDER_PROP_EXT_ID);                     }

   //--- Get the full order profit
   double            ProfitFull(void)                                   const { return this.Profit()+this.Comission()+this.Swap();              }
   //--- Get order profit in points
   int               ProfitInPoints(void) const;

Thực hiện tính TakeProfit theo Point:

//+------------------------------------------------------------------+
//| Return order profit in points                                    |
//+------------------------------------------------------------------+
int COrder::ProfitInPoints(void) const
  {
   ENUM_ORDER_TYPE type=this.TypeOrder();
   string symbol=this.Symbol();
   double point=::SymbolInfoDouble(symbol,SYMBOL_POINT);
   if(type>ORDER_TYPE_SELL || point==0) return 0;
   if(this.Status()==ORDER_STATUS_HISTORY_ORDER)
      return int(type==ORDER_TYPE_BUY ? (this.PriceClose()-this.PriceOpen())/point : type==ORDER_TYPE_SELL ? (this.PriceOpen()-this.PriceClose())/point : 0);
   else if(this.Status()==ORDER_STATUS_MARKET_ACTIVE)
     {
      if(type==ORDER_TYPE_BUY)
         return int((::SymbolInfoDouble(symbol,SYMBOL_BID)-this.PriceOpen())/point);
      else if(type==ORDER_TYPE_SELL)
         return int((this.PriceOpen()-::SymbolInfoDouble(symbol,SYMBOL_ASK))/point);
     }
   return 0;
  }
//+------------------------------------------------------------------+

Và hãy thêm các phương thức công khai để mô tả một số thuộc tính của đối tượng đặt hàng để bạn có thể hiển thị chúng theo yêu cầu một cách thuận tiện:

   //--- Get description of an order's (1) integer, (2) real and (3) string property
   string            GetPropertyDescription(ENUM_ORDER_PROP_INTEGER property);
   string            GetPropertyDescription(ENUM_ORDER_PROP_DOUBLE property);
   string            GetPropertyDescription(ENUM_ORDER_PROP_STRING property);
   //--- Return order status name
   string            StatusDescription(void) const;
   //--- Return order or position name
   string            TypeDescription(void) const;
   //--- Return the deal direction name
   string            DealEntryDescription(void) const;
   //--- Return order/position direction
   string            DirectionDescription(void) const;
   //--- Send description of order properties to journal (full_prop=true - all properties, false - only supported ones)
   void              Print(const bool full_prop=false);

Trước khi chúng ta thực hiện các phương pháp này, chúng ta hãy giải quyết một vấn đề khác: thư viện và các chương trình dựa trên nó yêu cầu các chức năng dịch vụ khác nhau. Ví dụ, trong trường hợp này, chúng ta cần chức năng hiển thị thời gian với mili giây và chức năng nhận tin nhắn bằng một trong hai ngôn ngữ. Ngôn ngữ của tin nhắn là phụ thuộc vào ngôn ngữ đầu cuối.

Tạo một tệp bao gồm mới trong thư mục gốc của thư viện:

Chọn Finish để tạo template file:

//+------------------------------------------------------------------+
//|                                                        DELib.mqh |
//|                        Copyright 2018, MetaQuotes Software Corp. |
//+------------------------------------------------------------------+
#property copyright "Copyright 2018, MetaQuotes Software Corp."
//+------------------------------------------------------------------+
//| defines                                                          |
//+------------------------------------------------------------------+
// #define MacrosHello   "Hello, world!"
// #define MacrosYear    2010
//+------------------------------------------------------------------+
//| DLL imports                                                      |
//+------------------------------------------------------------------+
// #import "user32.dll"
//   int      SendMessageA(int hWnd,int Msg,int wParam,int lParam);
// #import "my_expert.dll"
//   int      ExpertRecalculate(int wParam,int lParam);
// #import
//+------------------------------------------------------------------+
//| EX5 imports                                                      |
//+------------------------------------------------------------------+
// #import "stdlib.ex5"
//   string ErrorDescription(int error_code);
// #import
//+------------------------------------------------------------------+

Include file: Defines.mqh

//+------------------------------------------------------------------+
//|                                                        DELib.mqh |
//|                        Copyright 2018, MetaQuotes Software Corp. |
//+------------------------------------------------------------------+
#property copyright "Copyright 2018, MetaQuotes Software Corp."
#property strict  // Necessary for mql4
//+------------------------------------------------------------------+
//| Include files                                                    |
//+------------------------------------------------------------------+
#include "Defines.mqh"
//+------------------------------------------------------------------+
//| Service functions                                                |
//+------------------------------------------------------------------+

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

Vì chúng ta đã include Defines.mqh vào tệp này, chúng ta có thể đưa tệp mới (chứ không phải Defines.mqh) vào tệp lớp COrder, để cả hai đều có sẵn trong thư viện. Bên cạnh đó, chúng ta sẽ làm điều đó trong một chuỗi chứ không phải hai.
Thay thế lệnh bao gồm trong tệp Order.mqh:

//+------------------------------------------------------------------+
//|                                                        Order.mqh |
//|                        Copyright 2018, MetaQuotes Software Corp. |
//+------------------------------------------------------------------+
#property copyright "Copyright 2018, MetaQuotes Software Corp."
#property version   "1.00"
#include <Object.mqh>
#include "..\DELib.mqh"
//+------------------------------------------------------------------+
//| Abstract order class                                             |
//+------------------------------------------------------------------+

Chúng ta cũng hãy thêm định nghĩa ngôn ngữ quốc gia của người dùng dưới dạng thay thế macro trong tệp Defines.mqh:

//+------------------------------------------------------------------+
//|                                                      Defines.mqh |
//|                        Copyright 2018, MetaQuotes Software Corp. |
//+------------------------------------------------------------------+
#property copyright "Copyright 2018, MetaQuotes Software Corp."
//+------------------------------------------------------------------+
//| Macro substitutions                                              |
//+------------------------------------------------------------------+
#define COUNTRY_LANG    "Vietnamese"

Do đó, người dùng sẽ có thể đặt ngôn ngữ gốc của họ cho các tin nhắn được hiển thị nếu ngôn ngữ đầu cuối không phải là tiếng Anh. Tuy nhiên, để đạt được điều này, chúng ta sẽ phải thay thế tất cả các tin nhắn trong tương lai bằng tiếng Việt Nam, chúng ta phải nhập vào đây bằng các ngôn ngữ theo yêu cầu của người dùng.

Thêm chức năng trả lại tin nhắn bằng một trong hai ngôn ngữ trong tệp DELib.mqh:

//+------------------------------------------------------------------+
//|                                                        DELib.mqh |
//|                        Copyright 2018, MetaQuotes Software Corp. |
//+------------------------------------------------------------------+
#property copyright "Copyright 2018, MetaQuotes Software Corp."
#property strict  // Necessary for mql4
//+------------------------------------------------------------------+
//| Include files                                                    |
//+------------------------------------------------------------------+
#include "Defines.mqh"
//+------------------------------------------------------------------+
//| Service functions                                                |
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
//| Return the text in one of two languages                          |
//+------------------------------------------------------------------+
string TextByLanguage(const string text_country_lang,const string text_en)
  {
   return(TerminalInfoString(TERMINAL_LANGUAGE)==COUNTRY_LANG ? text_country_lang : text_en);
  }

Hàm kiểm tra ngôn ngữ đầu cuối và nếu nó phù hợp với các ngôn ngữ được chỉ định trong thay thế macro COUNTRY_LANG, văn bản được truyền trong tham số chức năng đầu tiên được hiển thị. Mặt khác, văn bản chứa trong tham số chức năng thứ hai (tiếng Anh) được hiển thị.
Chúng ta cũng hãy thêm chức năng hiển thị thời gian với mili giây:

//+------------------------------------------------------------------+
//|                                                        DELib.mqh |
//|                        Copyright 2018, MetaQuotes Software Corp. |
//+------------------------------------------------------------------+
#property copyright "Copyright 2018, MetaQuotes Software Corp."
#property strict  // Necessary for mql4
//+------------------------------------------------------------------+
//| Include file                                                     |
//+------------------------------------------------------------------+
#include "Defines.mqh"
//+------------------------------------------------------------------+
//| Service functions                                                |
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
//| Return text in one of the two languages                          |
//+------------------------------------------------------------------+
string TextByLanguage(const string text_country_lang,const string text_en)
  {
   return(TerminalInfoString(TERMINAL_LANGUAGE)==COUNTRY_LANG ? text_country_lang : text_en);
  }
//+------------------------------------------------------------------+
//| Return time with milliseconds                                    |
//+------------------------------------------------------------------+
string TimeMSCtoString(const long time_msc)
  {
   return TimeToString(time_msc/1000,TIME_DATE|TIME_MINUTES|TIME_SECONDS)+"."+IntegerToString(time_msc%1000,3,'0');
  }
//+------------------------------------------------------------------+

Ở đây, tất cả đều đơn giản: thời gian được đặt tính bằng mili giây được truyền cho hàm. Để tính thời gian tính bằng giây, chúng ta cần chia giá trị được truyền cho hàm thành 1000. Để tính mili giây, hãy lấy phần dư của phép chia này. Tất cả các giá trị nhận được được định dạng dưới dạng chuỗi và được trả về chương trình gọi.

Đôi khi, bạn có thể muốn lấy số lượng vị trí thập phân trong một lô biểu tượng. Hãy nhập chức năng này vào tệp chức năng dịch vụ của chúng ta:

//+------------------------------------------------------------------+
//| Return the number of decimal places in a symbol lot              |
//+------------------------------------------------------------------+
uint DigitsLots(const string symbol_name) 
  { 
   return (int)ceil(fabs(log(SymbolInfoDouble(symbol_name,SYMBOL_VOLUME_STEP))/log(10)));
  }
//+------------------------------------------------------------------+

Ngoài các chức năng dịch vụ, chúng ta sẽ cần ba phương thức trả về lý do đặt hàng, hướng và loại giao dịch để hiển thị thông báo trong tạp chí. Thêm ba phương thức vào phần được bảo vệ của lớp COrder:

 //--- Return (1) reason, (2) direction, (3) deal type
   string            GetReasonDescription(const long reason)            const;
   string            GetEntryDescription(const long deal_entry)         const;
   string            GetTypeDealDescription(const long type_deal)       const;

Và thực hiện hàm:

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

Liên kết với MQL4 hoặc MQL5 được kiểm tra, lý do đơn hàng được chuyển trong các đầu vào được xác minh theo trạng thái đơn hàng và mô tả của nó được trả về.

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

Liên kết với MQL4 hoặc MQL5 được kiểm tra, hướng giao dịch được chuyển trong các đầu vào được xác minh theo trạng thái đơn hàng và mô tả của nó được trả về.

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

Liên kết với MQL4 hoặc MQL5 được kiểm tra, loại giao dịch được chuyển trong các đầu vào được xác minh theo trạng thái đơn hàng và mô tả của nó được trả về. Thực hiện các phương pháp mô tả các thuộc tính thứ tự:

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

Thực hiện các phương thức trả về tên của trạng thái đơn hàng, tên của đơn hàng hoặc vị trí, tên của hướng giao dịch, mô tả loại hướng lệnh / vị trí và phương thức hiển thị thuận tiện các thuộc tính đơn hàng trong tạp chí .

Đầu tiên, thêm một thay thế macro khác vào tệp Defines.mqh để hiển thị thuận tiện tên hàm / phương thức trong tạp chí:

//+------------------------------------------------------------------+
//| Macro substitutions                                              |
//+------------------------------------------------------------------+
#define COUNTRY_LANG    "Russian"            // Country language
#define DFUN           (__FUNCTION__+": ")   // "Function description"
//+------------------------------------------------------------------+

Bây giờ, thay vì viết các chuỗi đầy đủ, chúng ta chỉ cần viết DFUN và trình biên dịch thay đổi mô tả thành các chuỗi được đặt trong macro.

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

Bây giờ tất cả các phương thức của lớp thứ tự trừu tượng COrder đã được thực hiện, chúng ta có thể thấy những gì chúng ta đã thực sự có được ở giai đoạn này.

Kiểm tra thứ tự trừu tượng COrder
Hãy thử và xem các đối tượng cơ sở COrder được tạo và chứa đầy dữ liệu như thế nào. Hơn nữa, chúng ta sẽ phát triển các đối tượng theo các loại dựa trên nó và lưu trữ chúng trong các bộ sưu tập.
Trong thư mục Experts hoặc thư mục con, hãy tạo một thư mục mới và gọi nó là TestDo EAS. Tiếp theo, tạo một thư mục khác bên trong thư mục mới được tạo: Part01. Tập tin EA thử nghiệm được đặt trong đó.

Nhấp chuột phải vào Part01 trong trình điều hướng của trình chỉnh sửa và chọn ‘Tệp mới’ từ menu bật lên hoặc nhấn Ctrl + N với thư mục Part01 được tô sáng. Cửa sổ MQL Wizard mở ra với tùy chọn ‘Expert Advisor (mẫu)’ đã được chọn, nhấp vào Tiếp theo và thêm tên của tệp TestDo EASPart01 mới vào đường dẫn tệp đã được chỉ định trong trường nhập tên. Nhấn Next cho đến khi bạn kết thúc thao tác wizard. Chúng ta sẽ không cần bất kỳ trình xử lý sự kiện bổ sung nào cho thử nghiệm, vì vậy bạn có thể để trống tất cả các hộp kiểm. Nhấp vào Kết thúc để tạo mẫu EA mới:

//+------------------------------------------------------------------+
//|                                             TestDoEasyPart01.mq5 |
//|                        Copyright 2018, MetaQuotes Software Corp. |
//+------------------------------------------------------------------+
#property copyright "Copyright 2018, MetaQuotes Software Corp."
#property version   "1.00"
//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
//---
   
//---
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
//---
   
  }
//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
  {
//---
   
  }
//+------------------------------------------------------------------+

Ví dụ càng đơn giản càng tốt, vì vậy không cần phải phát minh lại. Vì vậy, chúng ta chỉ đơn giản bao gồm lớp của thứ tự trừu tượng COrder và lớp CArrayObj từ thư viện chuẩn và tạo danh sách đối tượng:

//+------------------------------------------------------------------+
//|                                             TestDoEasyPart01.mq5 |
//|                        Copyright 2018, MetaQuotes Software Corp. |
//+------------------------------------------------------------------+
#property copyright "Copyright 2018, MetaQuotes Software Corp."
#property version   "1.00"
//--- includes
#include <DoEasy\Objects\Order.mqh>
#include <Arrays\ArrayObj.mqh>
//--- global variables
CArrayObj      list_all_orders;
//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+

Tiếp theo, đặt cờ danh sách đã sắp xếp vào danh sách đối tượng trong OnInit () và điền vào đó với tất cả các giao dịch và đơn hàng lịch sử trong hai vòng.

Sau đó hiển thị dữ liệu trên từng đối tượng của danh sách đã điền trong Experts journal của thiết bị đầu cuối trong vòng lặp:

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
//---
   list_all_orders.Sort();
   list_all_orders.Clear();
   if(!HistorySelect(0,TimeCurrent()))
     {
      Print(DFUN,TextByLanguage(": Не удалось получить историю сделок и ордеров",": Failed to get history of deals and orders"));
      return INIT_FAILED;
     }
//--- Deals
   int total_deals=HistoryDealsTotal();
   for(int i=0; i<total_deals; i++)
     {
      ulong deal_ticket=::HistoryDealGetTicket(i);
      if(deal_ticket==0) continue;
      COrder *deal=new COrder(ORDER_STATUS_DEAL,deal_ticket);
      if(deal==NULL) continue;
      list_all_orders.InsertSort(deal);
     }
//--- Orders
   int total_orders=HistoryOrdersTotal();
   for(int i=0; i<total_orders; i++)
     {
      ulong order_ticket=::HistoryOrderGetTicket(i);
      if(order_ticket==0) continue;
      ENUM_ORDER_TYPE type=(ENUM_ORDER_TYPE)::HistoryOrderGetInteger(order_ticket,ORDER_TYPE);
      if(type==ORDER_TYPE_BUY || type==ORDER_TYPE_SELL)
        {
         COrder *order=new COrder(ORDER_STATUS_HISTORY_ORDER,order_ticket);
         if(order==NULL) continue;
         list_all_orders.InsertSort(order);
        }
      else
        {
         COrder *order=new COrder(ORDER_STATUS_HISTORY_PENDING,order_ticket);
         if(order==NULL) continue;
         list_all_orders.InsertSort(order);
        }
     }
//--- Display in the journal
   int total=list_all_orders.Total();
   for(int i=0;i<total;i++)
     {
      COrder *order=list_all_orders.At(i);
      if(order==NULL)
         continue;
      order.Print();
     }
//---
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+

Nếu bây giờ chúng ta cố gắng biên dịch EA, chúng ta sẽ gặp ba lỗi:

Các lỗi chỉ ra rằng chúng ta không thể truy cập các phương thức được bảo vệ của lớp từ bên ngoài. Rốt cuộc, chúng ta đã tạo ra hàm tạo COrder trong phần được bảo vệ của lớp. Khi chúng ta sử dụng lớp làm cơ sở để phát triển các đối tượng theo loại, mọi thứ sẽ ổn vì chúng sẽ có các hàm tạo mở. Bây giờ, để thực hiện kiểm tra, chúng ta chỉ cần di chuyển hàm tạo đến phần chung của lớp COrder:

Bây giờ tất cả được biên dịch không có lỗi và dữ liệu về tất cả các đơn đặt hàng và giao dịch trong lịch sử tài khoản giao dịch được hiển thị trên tạp chí thiết bị đầu cuối.

Tất cả các thuộc tính của mỗi đơn hàng / giao dịch, bao gồm cả những thứ không được hỗ trợ, được hiển thị.

Thực tế là chúng ta đã phát triển các phương thức trả về các cờ để hỗ trợ các thuộc tính cụ thể theo thứ tự này là ảo, để chúng được định nghĩa lại trong các lớp dẫn xuất. Các lớp dẫn xuất này sau đó được sử dụng để hiển thị dữ liệu trong tạp chí. Trong trường hợp đó, tất cả nên được hiển thị chính xác. Nếu có một thuộc tính không được hỗ trợ theo thứ tự, nó sẽ không được hiển thị trong tạp chí, vì phương thức Print (const bool full_prop \u003d false) của lớp COrder có cờ mặc định để vô hiệu hóa hiển thị các thuộc tính không được hỗ trợ trong tạp chí, trong khi Các phương thức ảo SupportProperty () của lớp chỉ cần trả về ‘true’ cho bất kỳ thuộc tính nào.

Trong phần tiếp theo có gì mới?

Phần đầu tiên (và nhỏ nhất) đã hoàn thành. Chúng ta đã phát triển một đối tượng cơ bản cho việc thu thập các đơn đặt hàng và giao dịch lịch sử cũng như cho việc thu thập các đơn đặt hàng và vị trí thị trường. Đối tượng cơ bản duy nhất này sẽ trở thành chìa khóa cho hệ thống lưu trữ và hiển thị dữ liệu trên hệ thống đặt hàng. Bước tiếp theo của chúng ta là phát triển các đối tượng và bộ sưu tập cần thiết khác bằng cách sử dụng các nguyên tắc tương tự. Chúng ta cũng sẽ tự động thu thập dữ liệu liên tục yêu cầu.