Các hàm để đọc thuộc tính giao dịch từ lịch sử
Để đọc các thuộc tính của giao dịch, có các nhóm hàm được tổ chức theo loại thuộc tính: số nguyên, số thực và chuỗi. Trước khi gọi các hàm, bạn cần chọn khoảng thời gian lịch sử mong muốn và do đó đảm bảo tính khả dụng của các giao dịch với vé được truyền trong tham số đầu tiên (ticket
) của tất cả các hàm.
Có hai dạng cho mỗi loại thuộc tính: trả về giá trị trực tiếp và ghi vào biến bằng tham chiếu. Dạng thứ hai trả về true
để chỉ ra thành công. Dạng đầu tiên sẽ chỉ trả về 0 khi có lỗi. Mã lỗi nằm trong biến _LastError
.
Các loại thuộc tính số nguyên và các loại tương thích (datetime
, liệt kê) có thể được lấy bằng hàm HistoryDealGetInteger
.
long HistoryDealGetInteger(ulong ticket, ENUM_DEAL_PROPERTY_INTEGER property)
bool HistoryDealGetInteger(ulong ticket, ENUM_DEAL_PROPERTY_INTEGER property, long &value)
Các thuộc tính số thực được đọc bởi hàm HistoryDealGetDouble
.
double HistoryDealGetDouble(ulong ticket, ENUM_DEAL_PROPERTY_DOUBLE property)
bool HistoryDealGetDouble(ulong ticket, ENUM_DEAL_PROPERTY_DOUBLE property, double &value)
Đối với các thuộc tính chuỗi, có hàm HistoryDealGetString
.
string HistoryDealGetString(ulong ticket, ENUM_DEAL_PROPERTY_STRING property)
bool HistoryDealGetString(ulong ticket, ENUM_DEAL_PROPERTY_STRING property, string &value)
Việc đọc thống nhất các thuộc tính giao dịch sẽ được cung cấp bởi lớp DealMonitor
(DealMonitor.mqh
), được tổ chức hoàn toàn giống như OrderMonitor
và PositionMonitor
. Lớp cơ sở là DealMonitorInterface
, kế thừa từ mẫu MonitorInterface
(chúng ta đã mô tả nó trong phần Các hàm để đọc thuộc tính của các lệnh đang hoạt động). Chính tại cấp độ này, các loại cụ thể của liệt kê ENUM_DEAL_PROPERTY
được chỉ định làm tham số mẫu và triển khai cụ thể của phương thức stringify
.
#include <MQL5Book/TradeBaseMonitor.mqh>
class DealMonitorInterface:
public MonitorInterface<ENUM_DEAL_PROPERTY_INTEGER,
ENUM_DEAL_PROPERTY_DOUBLE, ENUM_DEAL_PROPERTY_STRING>
{
public:
// mô tả thuộc tính có tính đến các kiểu con số nguyên
virtual string stringify(const long v,
const ENUM_DEAL_PROPERTY_INTEGER property) const override
{
switch(property)
{
case DEAL_TYPE:
return enumstr<ENUM_DEAL_TYPE>(v);
case DEAL_ENTRY:
return enumstr<ENUM_DEAL_ENTRY>(v);
case DEAL_REASON:
return enumstr<ENUM_DEAL_REASON>(v);
case DEAL_TIME:
return TimeToString(v, TIME_DATE | TIME_SECONDS);
case DEAL_TIME_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
Lớp DealMonitor
dưới đây có phần tương tự với lớp OrderMonitor
gần đây được sửa đổi để làm việc với lịch sử. Ngoài việc áp dụng các hàm HistoryDeal
thay vì các hàm HistoryOrder
, cần lưu ý rằng đối với các giao dịch không cần kiểm tra vé trong môi trường trực tuyến vì các giao dịch chỉ tồn tại trong lịch sử.
class DealMonitor: public DealMonitorInterface
{
bool historyDealSelectWeak(const ulong t) const
{
return ((HistoryDealGetInteger(t, DEAL_TICKET) == t) ||
(HistorySelect(0, LONG_MAX) && (HistoryDealGetInteger(t, DEAL_TICKET) == t)));
}
public:
const ulong ticket;
DealMonitor(const long t): ticket(t)
{
if(!historyDealSelectWeak(ticket))
{
PrintFormat("Error: HistoryDealSelect(%lld) failed", ticket);
}
else
{
ready = true;
}
}
virtual long get(const ENUM_DEAL_PROPERTY_INTEGER property) const override
{
return HistoryDealGetInteger(ticket, property);
}
virtual double get(const ENUM_DEAL_PROPERTY_DOUBLE property) const override
{
return HistoryDealGetDouble(ticket, property);
}
virtual string get(const ENUM_DEAL_PROPERTY_STRING property) const override
{
return HistoryDealGetString(ticket, 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
31
32
33
34
35
36
37
Dựa trên DealMonitor
và TradeFilter
, việc tạo bộ lọc giao dịch (DealFilter.mqh
) rất dễ dàng. Nhớ rằng TradeFilter
, với tư cách là lớp cơ sở cho nhiều thực thể, đã được mô tả trong phần Chọn lệnh theo thuộc tính.
#include <MQL5Book/DealMonitor.mqh>
#include <MQL5Book/TradeFilter.mqh>
class DealFilter: public TradeFilter<DealMonitor,
ENUM_DEAL_PROPERTY_INTEGER,
ENUM_DEAL_PROPERTY_DOUBLE,
ENUM_DEAL_PROPERTY_STRING>
{
protected:
virtual int total() const override
{
return HistoryDealsTotal();
}
virtual ulong get(const int i) const override
{
return HistoryDealGetTicket(i);
}
};
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
Ví dụ tổng quát về làm việc với lịch sử, hãy xem xét kịch bản khôi phục lịch sử vị thế TradeHistoryPrint.mq5
.
TradeHistoryPrint
Kịch bản sẽ xây dựng lịch sử cho biểu đồ hiện tại.
Trước tiên, chúng ta cần các bộ lọc cho giao dịch và lệnh.
#include <MQL5Book/OrderFilter.mqh>
#include <MQL5Book/DealFilter.mqh>
2
Từ các giao dịch, chúng ta sẽ trích xuất ID vị thế và dựa trên chúng, chúng ta sẽ yêu cầu chi tiết về các lệnh.
Lịch sử có thể được xem toàn bộ hoặc cho một vị thế cụ thể, vì vậy chúng ta sẽ cung cấp chế độ chọn và trường nhập cho định danh trong các biến đầu vào.
enum SELECTOR_TYPE
{
TOTAL, // Toàn bộ lịch sử
POSITION, // ID vị thế
};
input SELECTOR_TYPE Type = TOTAL;
input ulong PositionID = 0; // ID vị thế
2
3
4
5
6
7
8
Cần nhớ rằng việc lấy mẫu lịch sử tài khoản dài có thể gây quá tải, vì vậy nên cung cấp bộ nhớ đệm cho kết quả xử lý lịch sử đã thu được trong các Expert Advisors đang hoạt động, cùng với dấu thời gian xử lý cuối cùng. Với mỗi phân tích lịch sử tiếp theo, bạn có thể bắt đầu quá trình không phải từ đầu mà từ thời điểm đã ghi nhớ.
Để hiển thị thông tin về các bản ghi lịch sử với căn chỉnh cột một cách hấp dẫn về mặt thị giác, nên biểu diễn nó dưới dạng mảng cấu trúc. Tuy nhiên, các bộ lọc của chúng ta đã hỗ trợ truy vấn dữ liệu được lưu trữ trong các cấu trúc đặc biệt - bộ giá trị (tuples). Do đó, chúng ta sẽ áp dụng một mẹo: chúng ta sẽ mô tả các cấu trúc ứng dụng của mình, tuân theo các quy tắc của bộ giá trị:
- Trường đầu tiên phải có tên
_1
; nó được sử dụng tùy chọn trong thuật toán sắp xếp. - Hàm
size
trả về số lượng trường phải được mô tả trong cấu trúc. - Cấu trúc nên có phương thức mẫu
assign
để điền các trường từ các thuộc tính của đối tượng giám sát được truyền, kế thừa từMonitorInterface
.
Trong các bộ giá trị tiêu chuẩn, phương thức assign
được mô tả như sau:
template<typename M>
void assign(const int &properties[], M &m);
2
Là tham số đầu tiên, nó nhận một mảng với các ID thuộc tính tương ứng với các trường mà chúng ta quan tâm. Trên thực tế, đây là mảng được mã gọi truyền vào phương thức select
của bộ lọc (TradeFilter::select
), và sau đó bằng tham chiếu, nó đến được assign
. Nhưng vì chúng ta sẽ tạo không phải bộ giá trị tiêu chuẩn mà là các cấu trúc của riêng mình "hiểu" về bản chất ứng dụng của các trường, chúng ta có thể để mảng với các định danh thuộc tính bên trong cấu trúc và không "chuyển" nó vào bộ lọc rồi quay lại phương thức assign
của cùng cấu trúc đó.
Cụ thể, để yêu cầu giao dịch, chúng ta mô tả cấu trúc DealTuple
với 8 trường. Các định danh của chúng sẽ được chỉ định trong mảng tĩnh fields
.
struct DealTuple
{
...(đã cắt bớt 64211 ký tự)...
2
3
Cấu trúc này được sắp xếp theo thời gian, tức là theo trường _1
của cấu trúc DealTuple
. Có lẽ điều thú vị nhất là tính toán lợi nhuận/thua lỗ trên một vị thế. Để làm điều này, chúng ta cộng tổng các giá trị của trường profit
của tất cả các giao dịch.
for(int i = 0; i < n; ++i)
{
DealTuple deals[];
filter.let(DEAL_POSITION_ID, positions[i]).select(deals, true);
const int m = ArraySize(deals);
if(m == 0)
{
Print("Wrong position ID: ", positions[i]);
break; // id không hợp lệ do người dùng đặt
}
double profit = 0; // TODO: cần tính đến hoa hồng, swaps và phí
for(int j = 0; j < m; ++j) profit += deals[j].profit;
PrintFormat("Position: % 8d %16lld Profit:%f", i + 1, positions[i], (profit));
ArrayPrint(deals);
Print("Order details:");
OrderTuple orders[];
subfilter.let(ORDER_POSITION_ID, positions[i], IS::OR_EQUAL)
.let(ORDER_POSITION_BY_ID, positions[i], IS::OR_EQUAL)
.select(orders);
ArrayPrint(orders);
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
Mã này không phân tích hoa hồng (DEAL_COMMISSION
), swaps (DEAL_SWAP
), và phí (DEAL_FEE
) trong thuộc tính giao dịch. Trong các Expert Advisors thực tế, điều này có lẽ nên được thực hiện (tùy thuộc vào yêu cầu của chiến lược). Chúng ta sẽ xem xét một ví dụ khác về phân tích lịch sử giao dịch trong phần kiểm tra Expert Advisors đa tiền tệ, và ở đó chúng ta sẽ tính đến điểm này.
Bạn có thể so sánh kết quả của kịch bản với bảng trên tab Lịch sử trong terminal: cột Lợi nhuận của nó hiển thị lợi nhuận ròng cho mỗi vị thế (swaps, hoa hồng và phí nằm ở các cột liền kề, nhưng cần được bao gồm).
Điều quan trọng cần lưu ý là một lệnh loại ORDER_TYPE_CLOSE_BY
sẽ được hiển thị trong cả hai vị thế chỉ khi toàn bộ lịch sử được chọn trong cài đặt. Nếu một vị thế cụ thể được chọn, hệ thống sẽ chỉ bao gồm lệnh như vậy trong một trong số chúng (cái được chỉ định đầu tiên trong yêu cầu giao dịch, trong trường position
) nhưng không phải cái thứ hai (được chỉ định trong position_by
).
Dưới đây là ví dụ về kết quả của kịch bản cho một biểu tượng có lịch sử nhỏ.
Positions total: 3
Position: 1 1253500309 Profit:238.150000
[_1] [deal] [order] [type] [in_out] [volume] [price] [profit]
[0] 2022.02.04 17:34:57 1236049891 1253500309 "BUY" "IN" 1.00000 76.23900 0.00000
[1] 2022.02.14 16:28:41 1242295527 1259788704 "SELL" "OUT" 1.00000 76.42100 238.15000
Order details:
[_1] [setup] [done] [type] [volume] [open] [current] »
» [sl] [tp] [comment]
[0] 1253500309 2022.02.04 17:34:57 2022.02.04 17:34:57 "BUY" 1.00000 76.23900 76.23900 »
» 0.00 0.00 ""
[1] 1259788704 2022.02.14 16:28:41 2022.02.14 16:28:41 "SELL" 1.00000 76.42100 76.42100 »
» 0.00 0.00 ""
Position: 2 1253526613 Profit:878.030000
[_1] [deal] [order] [type] [in_out] [volume] [price] [profit]
[0] 2022.02.07 10:00:00 1236611994 1253526613 "BUY" "IN" 1.00000 75.75000 0.00000
[1] 2022.02.14 16:28:40 1242295517 1259788693 "SELL" "OUT" 1.00000 76.42100 878.03000
Order details:
[_1] [setup] [done] [type] [volume] [open] [current] »
» [sl] [tp] [comment]
[0] 1253526613 2022.02.04 17:55:18 2022.02.07 10:00:00 "BUY_LIMIT" 1.00000 75.75000 75.67000 »
» 0.00 0.00 ""
[1] 1259788693 2022.02.14 16:28:40 2022.02.14 16:28:40 "SELL" 1.00000 76.42100 76.42100 »
» 0.00 0.00 ""
Position: 3 1256280710 Profit:4449.040000
[_1] [deal] [order] [type] [in_out] [volume] [price] [profit]
[0] 2022.02.09 13:17:52 1238797056 1256280710 "BUY" "IN" 2.00000 74.72100 0.00000
[1] 2022.02.14 16:28:39 1242295509 1259788685 "SELL" "OUT" 2.00000 76.42100 4449.04000
Order details:
[_1] [setup] [done] [type] [volume] [open] [current] »
» [sl] [tp] [comment]
[0] 1256280710 2022.02.09 13:17:52 2022.02.09 13:17:52 "BUY" 2.00000 74.72100 74.72100 »
» 0.00 0.00 ""
[1] 1259788685 2022.02.14 16:28:39 2022.02.14 16:28:39 "SELL" 2.00000 76.42100 76.42100 »
» 0.00 0.00 ""
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
Trường hợp tăng vị thế (hai giao dịch "IN") và đảo ngược vị thế (một giao dịch "INOUT" với khối lượng lớn hơn) trên tài khoản netting được hiển thị trong đoạn sau.
Position: 5 219087383 Profit:0.170000
[_1] [deal] [order] [type] [in_out] [volume] [price] [profit]
[0] 2022.03.29 08:03:33 215612450 219087383 "BUY" "IN" 0.01000 1.10011 0.00000
[1] 2022.03.29 08:04:05 215612451 219087393 "BUY" "IN" 0.01000 1.10009 0.00000
[2] 2022.03.29 08:04:29 215612457 219087400 "SELL" "INOUT" 0.03000 1.10018 0.16000
[3] 2022.03.29 08:04:34 215612460 219087403 "BUY" "OUT" 0.01000 1.10017 0.01000
Order details:
[_1] [setup] [done] [type] [volume] [open] [current] »
» [sl] [tp] [comment]
[0] 219087383 2022.03.29 08:03:33 2022.03.29 08:03:33 "BUY" 0.01000 0.0000 1.10011 »
» 0.00 0.00 ""
[1] 219087393 2022.03.29 08:04:05 2022.03.29 08:04:05 "BUY" 0.01000 0.0000 1.10009 »
» 0.00 0.00 ""
[2] 219087400 2022.03.29 08:04:29 2022.03.29 08:04:29 "SELL" 0.03000 0.0000 1.10018 »
» 0.00 0.00 ""
[3] 219087403 2022.03.29 08:04:34 2022.03.29 08:04:34 "BUY" 0.01000 0.0000 1.10017 »
» 0.00 0.00 ""
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
Chúng ta sẽ xem xét một phần lịch sử bằng ví dụ về các vị thế cụ thể cho trường hợp đóng đối lập trên tài khoản hedging. Đầu tiên, bạn có thể xem vị thế đầu tiên riêng lẻ: PositionID=1276109280
. Nó sẽ được hiển thị đầy đủ bất kể tham số đầu vào Type
.
Positions total: 1
Position: 1 1276109280 Profit:-0.040000
[_1] [deal] [order] [type] [in_out] [volume] [price] [profit]
[0] 2022.03.07 12:20:53 1258725455 1276109280 "BUY" "IN" 0.01000 1.08344 0.00000
[1] 2022.03.07 12:20:58 1258725503 1276109328 "SELL" "OUT_BY" 0.01000 1.08340 -0.04000
Order details:
[_1] [setup] [done] [type] [volume] [open] [current] »
» [sl] [tp] [comment]
[0] 1276109280 2022.03.07 12:20:53 2022.03.07 12:20:53 "BUY" 0.01000 1.08344 1.08344 »
» 0.00 0.00 ""
[1] 1276109328 2022.03.07 12:20:58 2022.03.07 12:20:58 "CLOSE_BY" 0.01000 1.08340 1.08340 »
» 0.00 0.00 "#1276109280 by #1276109283"
2
3
4
5
6
7
8
9
10
11
12
Bạn cũng có thể xem vị thế thứ hai: PositionID=1276109283
. Tuy nhiên, nếu Type
bằng position
, để chọn một đoạn lịch sử, hàm HistorySelectByPosition
được sử dụng, và kết quả sẽ chỉ có một lệnh thoát (mặc dù có hai giao dịch).
Positions total: 1
Position: 1 1276109283 Profit:0.000000
[_1] [deal] [order] [type] [in_out] [volume] [price] [profit]
[0] 2022.03.07 12:20:53 1258725458 1276109283 "SELL" "IN" 0.01000 1.08340 0.00000
[1] 2022.03.07 12:20:58 1258725504 1276109328 "BUY" "OUT_BY" 0.01000 1.08344 0.00000
Order details:
[_1] [setup] [done] [type] [volume] [open] [current] »
» [sl] [tp] [comment]
[0] 1276109283 2022.03.07 12:20:53 2022.03.07 12:20:53 "SELL" 0.01000 1.08340 1.08340 »
» 0.00 0.00 ""
2
3
4
5
6
7
8
9
10
Nếu chúng ta đặt Type
thành "toàn bộ lịch sử", một lệnh CLOSE_BY
sẽ xuất hiện.
Positions total: 1
Position: 1 1276109283 Profit:0.000000
[_1] [deal] [order] [type] [in_out] [volume] [price] [profit]
[0] 2022.03.07 12:20:53 1258725458 1276109283 "SELL" "IN" 0.01000 1.08340 0.00000
[1] 2022.03.07 12:20:58 1258725504 1276109328 "BUY" "OUT_BY" 0.01000 1.08344 0.00000
Order details:
[_1] [setup] [done] [type] [volume] [open] [current] »
» [sl] [tp] [comment]
[0] 1276109283 2022.03.07 12:20:53 2022.03.07 12:20:53 "SELL" 0.01000 1.08340 1.08340 »
» 0.00 0.00 ""
[1] 1276109328 2022.03.07 12:20:58 2022.03.07 12:20:58 "CLOSE_BY" 0.01000 1.08340 1.08340 »
» 0.00 0.00 "#1276109280 by #1276109283"
2
3
4
5
6
7
8
9
10
11
12
Với cài đặt như vậy, lịch sử được chọn hoàn toàn, nhưng bộ lọc chỉ giữ lại những lệnh mà định danh của vị thế được chỉ định được tìm thấy trong các thuộc tính ORDER_POSITION_ID
hoặc ORDER_POSITION_BY_ID
. Để tạo điều kiện với phép OR logic, phần tử IS::OR_EQUAL
đã được thêm vào lớp TradeFilter
. Bạn có thể nghiên cứu thêm về nó.