Đọc dữ liệu Độ sâu Thị trường hiện tại
Sau khi thực hiện thành công hàm MarketBookAdd
, một chương trình MQL có thể truy vấn trạng thái sổ lệnh bằng hàm MarketBookGet
khi các sự kiện OnBookEvent đến. Hàm MarketBookGet
điền vào mảng cấu trúc MqlBookInfo
được truyền bằng tham chiếu với các giá trị Độ sâu Thị trường của ký hiệu được chỉ định.
bool MarketBookGet(string symbol, MqlBookInfo &book[])
Đối với mảng nhận, bạn có thể cấp phát trước bộ nhớ cho một số lượng bản ghi đủ lớn. Nếu mảng động có kích thước bằng 0 hoặc không đủ, thiết bị đầu cuối sẽ tự động cấp phát bộ nhớ cho nó.
Hàm trả về dấu hiệu thành công (true
) hoặc lỗi (false
).
MarketBookGet
thường được sử dụng trực tiếp trong mã xử lý OnBookEvent
hoặc trong các hàm được gọi từ nó.
Một bản ghi riêng về mức giá Độ sâu Thị trường được lưu trữ trong cấu trúc MqlBookInfo
.
struct MqlBookInfo
{
ENUM_BOOK_TYPE type; // loại yêu cầu
double price; // giá
long volume; // khối lượng
double volume_real; // khối lượng với độ chính xác cao hơn
};
2
3
4
5
6
7
Liệt kê ENUM_BOOK_TYPE chứa các thành viên sau.
Định danh | Mô tả |
---|---|
BOOK_TYPE_SELL | Yêu cầu bán |
BOOK_TYPE_BUY | Yêu cầu mua |
BOOK_TYPE_SELL_MARKET | Yêu cầu bán theo giá thị trường |
BOOK_TYPE_BUY_MARKET | Yêu cầu mua theo giá thị trường |
Trong sổ lệnh, các lệnh bán nằm ở nửa trên và các lệnh mua nằm ở nửa dưới. Thông thường, điều này dẫn đến một chuỗi các phần tử từ giá cao đến giá thấp. Nói cách khác, dưới chỉ số 0 là giá cao nhất, và bản ghi cuối cùng là giá thấp nhất, trong khi giữa chúng giá giảm dần. Trong trường hợp này, bước giá tối thiểu giữa các mức là SYMBOL_TRADE_TICK_SIZE
, tuy nhiên, các mức có khối lượng bằng 0 không được truyền tải, nghĩa là các phần tử liền kề có thể cách nhau một khoảng lớn.
Trong giao diện người dùng của thiết bị đầu cuối, cửa sổ sổ lệnh cung cấp tùy chọn bật/tắt Advanced Mode
, trong đó các mức có khối lượng bằng 0 cũng được hiển thị. Nhưng mặc định, ở chế độ tiêu chuẩn, các mức như vậy bị ẩn (bỏ qua trong bảng).
Trên thực tế, nội dung sổ lệnh đôi khi có thể mâu thuẫn với các quy tắc đã công bố. Đặc biệt, một số yêu cầu mua hoặc bán có thể rơi vào nửa đối diện của sổ lệnh (có lẽ ai đó đã đặt mua với giá cao bất lợi hoặc bán với giá thấp bất lợi, nhưng nhà cung cấp cũng có thể có lỗi tổng hợp dữ liệu). Kết quả là, do tuân thủ ưu tiên "tất cả lệnh bán từ trên xuống, tất cả lệnh mua từ dưới lên", thứ tự giá trong sổ lệnh sẽ bị vi phạm (xem ví dụ dưới đây). Ngoài ra, các giá trị giá lặp lại (các mức) có thể được tìm thấy cả trong một nửa của sổ lệnh và ở nửa đối diện.
Về lý thuyết, sự trùng hợp của giá mua và bán ở giữa sổ lệnh là đúng. Điều đó có nghĩa là chênh lệch bằng 0. Tuy nhiên, đáng tiếc, các mức trùng lặp cũng xảy ra ở độ sâu lớn hơn của sổ lệnh.
Khi chúng ta nói "nửa" của sổ lệnh, không nên hiểu theo nghĩa đen. Tùy thuộc vào thanh khoản, số lượng mức cung và cầu có thể không khớp. Nói chung, sổ lệnh không đối xứng.
Chương trình MQL phải kiểm tra tính đúng đắn của sổ lệnh (đặc biệt là thứ tự sắp xếp giá) và sẵn sàng xử lý các sai lệch tiềm ẩn.
Các tình huống bất thường ít nghiêm trọng hơn (tuy nhiên vẫn nên được tính đến trong thuật toán) bao gồm:
- Sổ lệnh giống nhau liên tiếp, không có thay đổi
- Sổ lệnh trống
- Sổ lệnh chỉ có một mức
Dưới đây là một đoạn của Độ sâu Thị trường thực tế nhận được từ một nhà môi giới. Các chữ cái 'S' và 'B' lần lượt đánh dấu giá của các yêu cầu bán và mua.
Lưu ý rằng các mức mua và bán thực sự chồng lấp: trực quan, điều này không quá rõ ràng, vì tất cả các bản ghi 'S' trong sổ lệnh được đặt đặc biệt lên trên (đầu mảng nhận), và các bản ghi 'B' ở dưới (cuối mảng). Tuy nhiên, hãy nhìn kỹ hơn: giá mua ở các phần tử 20 và 21 lần lượt là 143.23 và 138.86, cao hơn tất cả các đề nghị bán. Đồng thời, giá bán ở các phần tử 18 và 19 là 134.62 và 133.55, thấp hơn tất cả các đề nghị mua.
...
10 S 138.48 652
11 S 138.47 754
12 S 138.45 2256
13 S 138.43 300
14 S 138.42 14
15 S 138.40 1761
16 S 138.39 670 // Trùng lặp
17 S 138.11 200
18 S 134.62 420 // Thấp
19 S 133.55 10627 // Thấp
20 B 143.23 9564 // Cao
21 B 138.86 533 // Cao
22 B 138.39 739 // Trùng lặp
23 B 138.38 106
24 B 138.31 100
25 B 138.25 29
26 B 138.24 6072
27 B 138.23 571
28 B 138.21 17
29 B 138.20 201
30 B 138.19 1
...
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
Ngoài ra, giá 138.39 được tìm thấy cả ở nửa trên tại số 16 và ở nửa dưới tại số 22.
Lỗi trong sổ lệnh rất có thể xuất hiện trong điều kiện khắc nghiệt: với biến động mạnh hoặc thiếu thanh khoản.
Hãy kiểm tra việc nhận sổ lệnh bằng chỉ báo MarketBookDisplay.mq5
. Nó sẽ đăng ký các sự kiện Độ sâu Thị trường cho ký hiệu được chỉ định trong tham số WorkSymbol
(nếu bạn để trống, ký hiệu đang hoạt động của biểu đồ hiện tại được giả định).
input string WorkSymbol = ""; // WorkSymbol (nếu trống, sử dụng ký hiệu biểu đồ hiện tại)
const string _WorkSymbol = StringLen(WorkSymbol) == 0 ? _Symbol : WorkSymbol;
int digits;
void OnInit()
{
PRTF(MarketBookAdd(_WorkSymbol));
digits = (int)SymbolInfoInteger(_WorkSymbol, SYMBOL_DIGITS);
...
}
void OnDeinit(const int)
{
Comment("");
PRTF(MarketBookRelease(_WorkSymbol));
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
Trình xử lý OnBookEvent
được định nghĩa trong mã để xử lý sự kiện, trong đó MarketBookGet
được gọi, và tất cả các phần tử của mảng MqlBookInfo
kết quả được xuất dưới dạng bình luận nhiều dòng.
void OnBookEvent(const string &symbol)
{
if(symbol == _WorkSymbol) // chỉ lấy sổ lệnh của ký hiệu được yêu cầu
{
MqlBookInfo mbi[];
if(MarketBookGet(symbol, mbi)) // lấy sổ lệnh hiện tại
{
...
int half = ArraySize(mbi) / 2; // ước tính giữa sổ lệnh
bool correct = true;
// thu thập thông tin về các mức và khối lượng trong một dòng (có dấu gạch ngang)
string s = "";
for(int i = 0; i < ArraySize(mbi); ++i)
{
s += StringFormat("%02d %s %s %d %g\n", i,
(mbi[i].type == BOOK_TYPE_BUY ? "B" :
(mbi[i].type == BOOK_TYPE_SELL ? "S" : "?")),
DoubleToString(mbi[i].price, digits),
mbi[i].volume, mbi[i].volume_real);
if(i > 0) // tìm giữa sổ lệnh dựa trên thay đổi loại yêu cầu
{
if(mbi[i - 1].type == BOOK_TYPE_SELL
&& mbi[i].type == BOOK_TYPE_BUY)
{
half = i; // đây là giữa, vì đã có sự thay đổi loại
}
if(mbi[i - 1].price <= mbi[i].price)
{
correct = false; // thứ tự ngược = vấn đề dữ liệu
}
}
}
Comment(s + (!correct ? "\nINCORRECT BOOK" : ""));
...
}
}
}
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
38
39
Vì sổ lệnh thay đổi khá nhanh, việc theo dõi bình luận không quá tiện lợi. Do đó, chúng ta sẽ thêm một vài bộ đệm vào chỉ báo, trong đó chúng ta sẽ hiển thị nội dung của hai nửa sổ lệnh dưới dạng biểu đồ cột: bán và mua riêng biệt. Thanh số 0 sẽ tương ứng với các mức trung tâm hình thành chênh lệch. Khi số thanh tăng lên, "độ sâu thị trường" tăng lên, tức là các mức giá ngày càng xa được hiển thị: trong biểu đồ cột trên, điều này có nghĩa là giá thấp hơn với các lệnh mua, và ở dưới là giá cao hơn với các lệnh bán.
#property indicator_separate_window
#property indicator_plots 2
#property indicator_buffers 2
#property indicator_type1 DRAW_HISTOGRAM
#property indicator_color1 clrDodgerBlue
#property indicator_width1 2
#property indicator_label1 "Buys"
#property indicator_type2 DRAW_HISTOGRAM
#property indicator_color2 clrOrangeRed
#property indicator_width2 2
#property indicator_label2 "Sells"
double buys[], sells[];
2
3
4
5
6
7
8
9
10
11
12
13
14
15
Chúng ta sẽ cung cấp cơ hội để trực quan hóa sổ lệnh ở chế độ tiêu chuẩn và mở rộng (tức là bỏ qua hoặc hiển thị các mức có khối lượng bằng 0), cũng như hiển thị khối lượng dưới dạng phân số lô hoặc đơn vị. Cả hai tùy chọn đều có tương tự trong cửa sổ Độ sâu Thị trường tích hợp.
input bool AdvancedMode = false;
input bool ShowVolumeInLots = false;
2
Hãy thiết lập bộ đệm và lấy một số thuộc tính ký hiệu (mà chúng ta sẽ cần sau này) trong OnInit
.
int depth, digits;
double tick, contract;
void OnInit()
{
...
// thiết lập bộ đệm chỉ báo
SetIndexBuffer(0, buys);
SetIndexBuffer(1, sells);
ArraySetAsSeries(buys, true);
ArraySetAsSeries(sells, true);
// lấy các thuộc tính ký hiệu cần thiết
depth = (int)PRTF(SymbolInfoInteger(_WorkSymbol, SYMBOL_TICKS_BOOKDEPTH));
tick = SymbolInfoDouble(_WorkSymbol, SYMBOL_TRADE_TICK_SIZE);
contract = SymbolInfoDouble(_WorkSymbol, SYMBOL_TRADE_CONTRACT_SIZE);
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
Hãy thêm việc điền bộ đệm vào trình xử lý OnBookEvent
.
#define VOL(V) (ShowVolumeInLots ? V / contract : V)
void OnBookEvent(const string &symbol)
{
if(symbol == _WorkSymbol) // chỉ lấy sổ lệnh của ký hiệu được yêu cầu
{
MqlBookInfo mbi[];
if(MarketBookGet(symbol, mbi)) // lấy sổ lệnh hiện tại
{
// xóa bộ đệm đến độ sâu với biên độ gấp 10 lần độ sâu tối đa,
// vì chế độ mở rộng có thể có nhiều phần tử trống
for(int i = 0; i <= depth * 10; ++i)
{
buys[i] = EMPTY_VALUE;
sells[i] = EMPTY_VALUE;
}
... // tiếp tục, chúng ta tạo và hiển thị bình luận như trước
if(!correct) return;
// điền dữ liệu vào bộ đệm
if(AdvancedMode) // bật hiển thị các khoảng trống
{
for(int i = 0; i < ArraySize(mbi); ++i)
{
if(i < half)
{
int x = (int)MathRound((mbi[i].price - mbi[half - 1].price) / tick);
sells[x] = -VOL(mbi[i].volume_real);
}
else
{
int x = (int)MathRound((mbi[half].price - mbi[i].price) / tick);
buys[x] = VOL(mbi[i].volume_real);
}
}
}
else // chế độ tiêu chuẩn: chỉ hiển thị các phần tử quan trọng
{
for(int i = 0; i < ArraySize(mbi); ++i)
{
if(i < half)
{
sells[half - i - 1] = -VOL(mbi[i].volume_real);
}
else
{
buys[i - half] = VOL(mbi[i].volume_real);
}
}
}
}
}
}
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
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
Hình ảnh sau đây thể hiện cách chỉ báo hoạt động với cài đặt AdvancedMode=true
, ShowVolumeInLots=true
.
Nội dung sổ lệnh trong chỉ báo MarketBookDisplay.mq5 trên biểu đồ USDCNH
Các lệnh mua được hiển thị dưới dạng giá trị dương (thanh màu xanh ở trên), và các lệnh bán là âm (màu đỏ ở dưới). Để rõ ràng, có một cửa sổ Độ sâu Thị trường tiêu chuẩn ở bên phải với cùng cài đặt (ở chế độ nâng cao, khối lượng tính bằng lô), để bạn có thể đảm bảo rằng các giá trị khớp nhau.
Cần lưu ý rằng chỉ báo có thể không kịp vẽ lại đủ nhanh để giữ đồng bộ với sổ lệnh tích hợp. Điều này không có nghĩa là chương trình MQL không nhận được sự kiện kịp thời, mà chỉ là hiệu ứng phụ của việc hiển thị biểu đồ không đồng bộ. Các thuật toán làm việc thường có xử lý phân tích và đặt lệnh với sổ lệnh, thay vì trực quan hóa.
Trong trường hợp này, việc cập nhật biểu đồ được yêu cầu ngầm tại thời điểm gọi hàm Comment
.