Các hàm đọc thuộc tính của lệnh đang hoạt động
Các bộ hàm có thể được sử dụng để lấy giá trị của tất cả thuộc tính lệnh khác nhau đối với lệnh đang hoạt động và lệnh lịch sử. Phần này mô tả các hàm để đọc thuộc tính của lệnh đang hoạt động. Đối với các hàm truy cập thuộc tính của lệnh trong lịch sử, xem phần liên quan.
Các thuộc tính số nguyên có thể được đọc bằng hàm OrderGetInteger
, hàm này có hai dạng: dạng đầu tiên trả về trực tiếp giá trị của thuộc tính, dạng thứ hai trả về dấu hiệu logic của thành công (true
) hoặc lỗi (false
), và tham số thứ hai được truyền bằng tham chiếu sẽ được điền giá trị của thuộc tính.
long OrderGetInteger(ENUM_ORDER_PROPERTY_INTEGER property)
bool OrderGetInteger(ENUM_ORDER_PROPERTY_INTEGER property, long &value)
Cả hai hàm đều cho phép bạn lấy thuộc tính lệnh được yêu cầu thuộc loại tương thích với số nguyên (datetime
, long/ulong
hoặc liệt kê). Mặc dù nguyên mẫu đề cập đến long
, từ góc độ kỹ thuật, giá trị được lưu trữ dưới dạng ô 8 byte, có thể được ép kiểu sang các loại tương thích mà không cần chuyển đổi biểu diễn nội bộ, đặc biệt là sang ulong
, được sử dụng cho tất cả các vé.
Một cặp hàm tương tự được thiết kế cho các thuộc tính kiểu số thực double
.
double OrderGetDouble(ENUM_ORDER_PROPERTY_DOUBLE property)
bool OrderGetDouble(ENUM_ORDER_PROPERTY_DOUBLE property, double &value)
Cuối cùng, các thuộc tính chuỗi có thể truy cập thông qua một cặp hàm OrderGetString
.
string OrderGetString(ENUM_ORDER_PROPERTY_STRING property)
bool OrderGetString(ENUM_ORDER_PROPERTY_STRING property, string &value)
Tham số đầu tiên của tất cả các hàm là định danh của thuộc tính mà chúng ta quan tâm. Đây phải là một phần tử của một trong các liệt kê — ENUM_ORDER_PROPERTY_INTEGER
, ENUM_ORDER_PROPERTY_DOUBLE
, hoặc ENUM_ORDER_PROPERTY_STRING
— đã được thảo luận trong phần trước.
Lưu ý rằng trước khi gọi bất kỳ hàm nào ở trên, bạn nên chọn một lệnh bằng OrderSelect
hoặc OrderGetTicket
.
Để đọc tất cả các thuộc tính của một lệnh cụ thể, chúng ta sẽ phát triển lớp OrderMonitor
(OrderMonitor.mqh
) hoạt động theo cùng nguyên tắc với các bộ giám sát ký hiệu (SymbolMonitor.mqh
) và tài khoản giao dịch (AccountMonitor.mqh
) đã được xem xét trước đó.
Các lớp giám sát này và các lớp khác được thảo luận trong sách cung cấp một cách thống nhất để phân tích thuộc tính thông qua các phiên bản quá tải của phương thức ảo get
.
Nhìn xa hơn một chút, hãy nói rằng các giao dịch và vị thế cũng có cách nhóm thuộc tính tương tự theo ba loại giá trị chính, và chúng ta cũng cần triển khai các bộ giám sát cho chúng. Về vấn đề này, việc tách thuật toán chung vào một lớp trừu tượng cơ sở MonitorInterface
(TradeBaseMonitor.mqh
) là hợp lý. Đây là một lớp mẫu với ba tham số nhằm chỉ định các loại liệt kê cụ thể, cho các nhóm thuộc tính số nguyên (I), số thực (D), và chuỗi (S).
#include <MQL5Book/EnumToArray.mqh>
template<typename I, typename D, typename S>
class MonitorInterface
{
protected:
bool ready;
public:
MonitorInterface(): ready(false) { }
bool isReady() const
{
return ready;
}
...
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
Do việc tìm kiếm một lệnh (giao dịch hoặc vị thế) trong môi trường giao dịch có thể thất bại vì nhiều lý do, lớp này có một biến dự trữ ready
mà các lớp dẫn xuất sẽ phải ghi dấu hiệu khởi tạo thành công, tức là việc chọn một đối tượng để đọc thuộc tính của nó.
Một số phương thức ảo thuần túy khai báo quyền truy cập vào các thuộc tính của các loại tương ứng.
virtual long get(const I property) const = 0;
virtual double get(const D property) const = 0;
virtual string get(const S property) const = 0;
virtual long get(const int property, const long) const = 0;
virtual double get(const int property, const double) const = 0;
virtual string get(const int property, const string) const = 0;
...
2
3
4
5
6
7
Trong ba phương thức đầu tiên, loại thuộc tính được chỉ định bởi một trong các tham số mẫu. Trong ba phương thức tiếp theo, loại được chỉ định bởi tham số thứ hai của chính phương thức: điều này là cần thiết vì các phương thức cuối cùng không nhận hằng số của một liệt kê cụ thể mà chỉ đơn giản là một số nguyên làm tham số đầu tiên. Một mặt, điều này thuận tiện cho việc đánh số liên tục các định danh (các hằng số liệt kê của ba loại không giao nhau). Mặt khác, chúng ta cần một nguồn khác để xác định loại giá trị vì loại trả về bởi hàm/phương thức không tham gia vào quá trình chọn quá tải phù hợp.
Cách tiếp cận này cho phép bạn lấy các thuộc tính dựa trên nhiều đầu vào khác nhau có sẵn trong mã gọi. Tiếp theo, chúng ta sẽ tạo các lớp dựa trên OrderMonitor
(cũng như DealMonitor
và PositionMonitor
trong tương lai) để chọn các đối tượng theo một tập hợp các điều kiện tùy ý, và ở đó tất cả các phương thức này sẽ được sử dụng.
Thông thường, các chương trình cần lấy biểu diễn chuỗi của bất kỳ thuộc tính nào, ví dụ, để ghi nhật ký. Trong các bộ giám sát mới, điều này được thực hiện bởi các phương thức stringify
. Rõ ràng, chúng lấy giá trị của các thuộc tính được yêu cầu thông qua các lời gọi phương thức get
đã đề cập ở trên.
virtual string stringify(const long v, const I property) const = 0;
virtual string stringify(const I property) const
{
return stringify(get(property), property);
}
virtual string stringify(const D property, const string format = NULL) const
{
if(format == NULL) return (string)get(property);
return StringFormat(format, get(property));
}
virtual string stringify(const S property) const
{
return get(property);
}
...
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
Phương thức duy nhất chưa được triển khai là phiên bản đầu tiên của stringify
cho loại long
. Điều này là do nhóm thuộc tính số nguyên, như chúng ta đã thấy trong phần trước, thực tế chứa các loại ứng dụng khác nhau, bao gồm ngày giờ, liệt kê và số nguyên. Do đó, chỉ các lớp dẫn xuất mới có thể cung cấp việc chuyển đổi của chúng thành các chuỗi dễ hiểu. Tình huống này phổ biến cho tất cả các thực thể giao dịch, không chỉ lệnh mà còn giao dịch và vị thế mà chúng ta sẽ xem xét thuộc tính của chúng sau này.
Khi một thuộc tính số nguyên chứa một phần tử liệt kê (ví dụ: ENUM_ORDER_TYPE
, ORDER_TYPE_FILLING
, v.v.), bạn nên sử dụng hàm EnumToString
để chuyển đổi nó thành chuỗi. Nhiệm vụ này được thực hiện bởi một phương thức trợ giúp enumstr
. Chúng ta sẽ sớm thấy việc sử dụng rộng rãi của nó trong các lớp giám sát cụ thể, bắt đầu với OrderMonitor
sau vài đoạn.
template<typename E>
static string enumstr(const long v)
{
return EnumToString((E)v);
}
2
3
4
5
Để ghi nhật ký tất cả các thuộc tính của một loại cụ thể, chúng ta đã tạo phương thức list2log
sử dụng stringify
trong một vòng lặp.
template<typename E>
void list2log() const
{
E e = (E)0; // suppress warning 'possible use of uninitialized variable'
int array[];
const int n = EnumToArray(e, array, 0, USHORT_MAX);
Print(typename(E), " Count=", n);
for(int i = 0; i < n; ++i)
{
e = (E)array[i];
PrintFormat("% 3d %s=%s", i, EnumToString(e), stringify(e));
}
}
2
3
4
5
6
7
8
9
10
11
12
13
Cuối cùng, để đơn giản hóa việc ghi nhật ký các thuộc tính của cả ba nhóm, có một phương thức print
gọi list2log
ba lần cho mỗi nhóm thuộc tính.
virtual void print() const
{
if(!ready) return;
Print(typename(this));
list2log<I>();
list2log<D>();
list2log<S>();
}
2
3
4
5
6
7
8
9
Với lớp mẫu cơ sở MonitorInterface
trong tay, chúng ta mô tả OrderMonitorInterface
, nơi chúng ta chỉ định các loại liệt kê cụ thể cho lệnh từ phần trước và cung cấp triển khai của stringify
cho các thuộc tính số nguyên của lệnh.
class OrderMonitorInterface:
public MonitorInterface<ENUM_ORDER_PROPERTY_INTEGER,
ENUM_ORDER_PROPERTY_DOUBLE, ENUM_ORDER_PROPERTY_STRING>
{
public:
// mô tả các thuộc tính theo các kiểu phụ
virtual string stringify(const long v,
const ENUM_ORDER_PROPERTY_INTEGER property) const override
{
switch(property)
{
case ORDER_TYPE:
return enumstr<ENUM_ORDER_TYPE>(v);
case ORDER_STATE:
return enumstr<ENUM_ORDER_STATE>(v);
case ORDER_TYPE_FILLING:
return enumstr<ENUM_ORDER_TYPE_FILLING>(v);
case ORDER_TYPE_TIME:
return enumstr<ENUM_ORDER_TYPE_TIME>(v);
case ORDER_REASON:
return enumstr<ENUM_ORDER_REASON>(v);
case ORDER_TIME_SETUP:
case ORDER_TIME_EXPIRATION:
case ORDER_TIME_DONE:
return TimeToString(v, TIME_DATE | TIME_SECONDS);
case ORDER_TIME_SETUP_MSC:
case ORDER_TIME_DONE_MSC:
return STR_TIME_MSC(v);
}
return (string)v;
}
};
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
Macro STR_TIME_MSC
để hiển thị thời gian tính bằng mili giây được định nghĩa như sau:
#define STR_TIME_MSC(T) (TimeToString((T) / 1000, TIME_DATE | TIME_SECONDS) \
+ StringFormat("'%03d", (T) % 1000))
2
Bây giờ chúng ta sẵn sàng mô tả lớp cuối cùng để đọc thuộc tính của bất kỳ lệnh nào: OrderMonitor
dẫn xuất từ OrderMonitorInterface
. Vé lệnh được truyền vào hàm tạo, và nó được chọn trong môi trường giao dịch bằng OrderSelect
.
class OrderMonitor: public OrderMonitorInterface
{
public:
const ulong ticket;
OrderMonitor(const long t): ticket(t)
{
if(!OrderSelect(ticket))
{
PrintFormat("Error: OrderSelect(%lld) failed: %s",
ticket, E2S(_LastError));
}
else
{
ready = true;
}
}
...
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
Phần chính của bộ giám sát bao gồm việc định nghĩa lại các hàm ảo để đọc thuộc tính. Ở đây chúng ta thấy các lời gọi hàm OrderGetInteger
, OrderGetDouble
, và OrderGetString
.
virtual long get(const ENUM_ORDER_PROPERTY_INTEGER property) const override
{
return OrderGetInteger(property);
}
virtual double get(const ENUM_ORDER_PROPERTY_DOUBLE property) const override
{
return OrderGetDouble(property);
}
virtual string get(const ENUM_ORDER_PROPERTY_STRING property) const override
{
return OrderGetString(property);
}
virtual long get(const int property, const long) const override
{
return OrderGetInteger((ENUM_ORDER_PROPERTY_INTEGER)property);
}
virtual double get(const int property, const double) const override
{
return OrderGetDouble((ENUM_ORDER_PROPERTY_DOUBLE)property);
}
virtual string get(const int property, const string) const override
{
return OrderGetString((ENUM_ORDER_PROPERTY_STRING)property);
}
};
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
Đoạn mã này được trình bày ở dạng ngắn: các toán tử để làm việc với lệnh trong lịch sử đã bị xóa khỏi nó. Chúng ta sẽ thấy mã đầy đủ của OrderMonitor
sau khi khám phá khía cạnh này trong các phần tiếp theo.
Điều quan trọng cần lưu ý là đối tượng giám sát không lưu trữ bản sao của các thuộc tính của nó. Do đó, việc truy cập các phương thức get
phải được thực hiện ngay sau khi tạo đối tượng và, tương ứng, gọi OrderSelect
. Để đọc các thuộc tính ở một khoảng thời gian sau, bạn sẽ cần phân bổ lại lệnh trong bộ nhớ đệm nội bộ của chương trình MQL, ví dụ, bằng cách gọi phương thức refresh
.
void refresh()
{
ready = OrderSelect(ticket);
}
2
3
4
Hãy thử nghiệm hoạt động của OrderMonitor
bằng cách thêm nó vào Expert Advisor MarketOrderSend.mq5
. Một phiên bản mới có tên MarketOrderSendMonitor.mq5
kết nối tệp OrderMonitor.mqh
bằng chỉ thị #include
, và trong thân hàm OnTimer
(trong khối xác nhận mở vị thế thành công trên một lệnh) tạo một đối tượng giám sát và gọi phương thức print
của nó.
#include <MQL5Book/OrderMonitor.mqh>
...
void OnTimer()
{
...
const ulong order = (wantToBuy ?
request.buy(volume, Price) :
request.sell(volume, Price));
if(order != 0)
{
Print("OK Order: #=", order);
if(request.completed())
{
Print("OK Position: P=", request.result.position);
OrderMonitor m(order);
m.print();
...
}
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
Trong nhật ký, chúng ta sẽ thấy các dòng mới chứa tất cả các thuộc tính của lệnh.
OK Order: #=1287846602
Waiting for position for deal D=1270417032
OK Position: P=1287846602
MonitorInterface<ENUM_ORDER_PROPERTY_INTEGER, »
» ENUM_ORDER_PROPERTY_DOUBLE,ENUM_ORDER_PROPERTY_STRING>
ENUM_ORDER_PROPERTY_INTEGER Count=14
0 ORDER_TIME_SETUP=2022.03.21 13:28:59
1 ORDER_TIME_EXPIRATION=1970.01.01 00:00:00
2 ORDER_TIME_DONE=2022.03.21 13:28:59
3 ORDER_TYPE=ORDER_TYPE_BUY
4 ORDER_TYPE_FILLING=ORDER_FILLING_FOK
5 ORDER_TYPE_TIME=ORDER_TIME_GTC
6 ORDER_STATE=ORDER_STATE_FILLED
7 ORDER_MAGIC=1234567890
8 ORDER_POSITION_ID=1287846602
9 ORDER_TIME_SETUP_MSC=2022.03.21 13:28:59'572
10 ORDER_TIME_DONE_MSC=2022.03.21 13:28:59'572
11 ORDER_POSITION_BY_ID=0
12 ORDER_TICKET=1287846602
13 ORDER_REASON=ORDER_REASON_EXPERT
ENUM_ORDER_PROPERTY_DOUBLE Count=7
0 ORDER_VOLUME_INITIAL=0.01
1 ORDER_VOLUME_CURRENT=0.0
2 ORDER_PRICE_OPEN=1.10275
3 ORDER_PRICE_CURRENT=1.10275
4 ORDER_PRICE_STOPLIMIT=0.0
5 ORDER_SL=0.0
6 ORDER_TP=0.0
ENUM_ORDER_PROPERTY_STRING Count=3
0 ORDER_SYMBOL=EURUSD
1 ORDER_COMMENT=
2 ORDER_EXTERNAL_ID=
TRADE_ACTION_DEAL, EURUSD, ORDER_TYPE_BUY, V=0.01, ORDER_FILLING_FOK, »
» @ 1.10275, P=1287846602, M=1234567890
DONE, D=1270417032, #=1287846602, V=0.01, @ 1.10275, Bid=1.10275, Ask=1.10275, »
» Request executed, Req=3
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
Dòng thứ tư bắt đầu đầu ra từ phương thức print
, bao gồm tên đầy đủ của đối tượng giám sát MonitorInterface
cùng với các loại tham số (trong trường hợp này là bộ ba ENUM_ORDER_PROPERTY) và sau đó là tất cả các thuộc tính của một lệnh cụ thể.
Tuy nhiên, việc in thuộc tính không phải là hành động thú vị nhất mà một bộ giám sát có thể cung cấp. Nhiệm vụ chọn lệnh theo điều kiện (giá trị của các thuộc tính tùy ý) được các Expert Advisor yêu cầu nhiều hơn. Sử dụng bộ giám sát như một công cụ hỗ trợ, chúng ta sẽ tạo một cơ chế lọc lệnh tương tự như những gì chúng ta đã làm cho các ký hiệu: SymbolFilter.mqh
.