Dịch các thay đổi của sổ lệnh
Nếu cần, một chương trình MQL có thể tạo ra một sổ lệnh cho một biểu tượng tùy chỉnh bằng cách sử dụng hàm CustomBookAdd
. Điều này đặc biệt có thể hữu ích cho các công cụ từ các sàn giao dịch bên ngoài, chẳng hạn như tiền điện tử.
int CustomBookAdd(const string symbol, const MqlBookInfo &books[], uint count = WHOLE_ARRAY)
Hàm này phát trạng thái của sổ lệnh tới các chương trình MQL đã đăng ký cho biểu tượng tùy chỉnh symbol
bằng cách sử dụng dữ liệu từ mảng books
. Mảng này mô tả toàn bộ trạng thái của sổ lệnh, tức là tất cả các lệnh mua và bán. Trạng thái đã dịch hoàn toàn thay thế trạng thái trước đó và trở nên khả dụng thông qua hàm MarketBookGet
.
Bằng cách sử dụng tham số count
, bạn có thể chỉ định số phần tử của mảng books
sẽ được truyền vào hàm. Theo mặc định, toàn bộ mảng được sử dụng.
Hàm trả về một chỉ báo thành công (true
) hoặc lỗi (false
).
Để lấy các sổ lệnh được tạo bởi hàm CustomBookAdd
, một chương trình MQL cần chúng phải, như thường lệ, đăng ký nhận sự kiện bằng cách sử dụng MarketBookAdd
.
Việc cập nhật sổ lệnh không cập nhật giá Bid
và Ask
của công cụ. Để cập nhật các giá cần thiết, hãy thêm các tick bằng cách sử dụng CustomTicksAdd
.
Dữ liệu được truyền đi được kiểm tra tính đúng đắn: giá và khối lượng phải lớn hơn không, và đối với mỗi phần tử, loại, giá và khối lượng của nó phải được chỉ định (các trường volume
và/hoặc volume_real
). Nếu ít nhất một phần tử của sổ lệnh được mô tả không chính xác, hàm sẽ trả về lỗi.
Tham số Độ sâu Sổ lệnh (SYMBOL_TICKS_BOOKDEPTH) của công cụ tùy chỉnh cũng được kiểm tra. Nếu số mức bán hoặc mua trong sổ lệnh đã dịch vượt quá giá trị này, các mức thừa sẽ bị loại bỏ.
Khối lượng với độ chính xác tăng cao volume_real
được ưu tiên hơn khối lượng thông thường volume
. Nếu cả hai giá trị được chỉ định cho phần tử sổ lệnh, volume_real
sẽ được sử dụng.
Chú ý! Trong triển khai hiện tại,
CustomBookAdd
tự động khóa biểu tượng tùy chỉnh như thể nó đã được đăng ký bởiMarketBookAdd
, nhưng đồng thời, các sự kiệnOnBookEvent
không đến (về lý thuyết, chương trình tạo sổ lệnh có thể đăng ký chúng bằng cách gọi rõ ràngMarketBookAdd
và kiểm soát những gì các chương trình khác nhận được). Bạn có thể xóa khóa này bằng cách gọiMarketBookRelease
.Điều này có thể cần thiết do thực tế rằng các biểu tượng có đăng ký sổ lệnh không thể bị ẩn khỏi
Market Watch
bằng bất kỳ cách nào (cho đến khi tất cả các đăng ký rõ ràng hoặc ngầm được hủy từ các chương trình, và cửa sổ sổ lệnh được đóng). Do đó, các biểu tượng như vậy không thể bị xóa.
Ví dụ, hãy tạo một Expert Advisor không giao dịch PseudoMarketBook.mq5
, sẽ tạo ra một trạng thái giả của sổ lệnh từ lịch sử tick gần nhất. Điều này có thể hữu ích cho các biểu tượng mà sổ lệnh không được dịch, đặc biệt là cho Forex. Nếu muốn, bạn có thể sử dụng các biểu tượng tùy chỉnh như vậy để gỡ lỗi chính thức các thuật toán giao dịch của riêng bạn bằng cách sử dụng sổ lệnh.
Trong số các tham số đầu vào, chúng ta chỉ định độ sâu tối đa của sổ lệnh.
input uint CustomBookDepth = 20;
Tên của biểu tượng tùy chỉnh sẽ được hình thành bằng cách thêm hậu tố ".Pseudo" vào tên của biểu tượng biểu đồ hiện tại.
string CustomSymbol = _Symbol + ".Pseudo";
Trong trình xử lý OnInit
, chúng ta tạo một biểu tượng tùy chỉnh và đặt công thức của nó thành tên của biểu tượng ban đầu. Do đó, chúng ta sẽ nhận được một bản sao của biểu tượng ban đầu được cập nhật tự động bởi terminal, và chúng ta sẽ không cần phải bận tâm với việc sao chép báo giá hoặc tick.
int OnInit()
{
bool custom = false;
if(!PRTF(SymbolExist(CustomSymbol, custom)))
{
if(PRTF(CustomSymbolCreate(CustomSymbol, CustomPath, _Symbol)))
{
CustomSymbolSetString(CustomSymbol, SYMBOL_DESCRIPTION, "Pseudo book generator");
CustomSymbolSetString(CustomSymbol, SYMBOL_FORMULA, "\"" + _Symbol + "\"");
}
}
...
2
3
4
5
6
7
8
9
10
11
12
Nếu biểu tượng tùy chỉnh đã tồn tại, Expert Advisor có thể đề nghị người dùng xóa nó và hoàn thành công việc tại đó (người dùng nên đóng tất cả các biểu đồ với biểu tượng này trước).
else
{
if(IDYES == MessageBox(StringFormat("Delete existing custom symbol '%s'?",
CustomSymbol), "Please, confirm", MB_YESNO))
{
PRTF(MarketBookRelease(CustomSymbol));
PRTF(SymbolSelect(CustomSymbol, false));
PRTF(CustomRatesDelete(CustomSymbol, 0, LONG_MAX));
PRTF(CustomTicksDelete(CustomSymbol, 0, LONG_MAX));
if(!PRTF(CustomSymbolDelete(CustomSymbol)))
{
Alert("Can't delete ", CustomSymbol, ", please, check up and delete manually");
}
return INIT_PARAMETERS_INCORRECT;
}
}
...
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
Một đặc điểm đặc biệt của biểu tượng này là cài đặt thuộc tính SYMBOL_TICKS_BOOKDEPTH, cũng như đọc kích thước hợp đồng SYMBOL_TRADE_CONTRACT_SIZE, điều này sẽ cần thiết khi tạo khối lượng.
if(SymbolInfoInteger(_Symbol, SYMBOL_TICKS_BOOKDEPTH) != CustomBookDepth
&& SymbolInfoInteger(CustomSymbol, SYMBOL_TICKS_BOOKDEPTH) != CustomBookDepth)
{
Print("Adjusting custom market book depth");
CustomSymbolSetInteger(CustomSymbol, SYMBOL_TICKS_BOOKDEPTH, CustomBookDepth);
}
depth = (int)PRTF(SymbolInfoInteger(CustomSymbol, SYMBOL_TICKS_BOOKDEPTH));
contract = PRTF(SymbolInfoDouble(CustomSymbol, SYMBOL_TRADE_CONTRACT_SIZE));
return INIT_SUCCEEDED;
}
2
3
4
5
6
7
8
9
10
11
12
Thuật toán được khởi chạy trong trình xử lý OnTick
. Ở đây chúng ta gọi hàm GenerateMarketBook
chưa được viết. Nó sẽ điền vào mảng cấu trúc MqlBookInfo
được truyền bằng tham chiếu, và chúng ta sẽ gửi nó đến một biểu tượng tùy chỉnh bằng CustomBookAdd
.
void OnTick()
{
MqlBookInfo book[];
if(GenerateMarketBook(2000, book))
{
ResetLastError();
if(!CustomBookAdd(CustomSymbol, book))
{
Print("Can't add market books, ", E2S(_LastError));
ExpertRemove();
}
}
}
2
3
4
5
6
7
8
9
10
11
12
13
Hàm GenerateMarketBook
phân tích count
tick mới nhất và dựa trên chúng, mô phỏng trạng thái có thể có của sổ lệnh, dựa trên các giả thuyết sau:
- Những gì đã được mua có khả năng sẽ được bán
- Những gì đã được bán có khả năng sẽ được mua
Việc chia tick thành những tick tương ứng với mua và bán, trong trường hợp chung (khi không có cờ sàn giao dịch) có thể được ước lượng bởi chuyển động của chính giá:
- Chuyển động của giá
Ask
lên trên được coi là một lệnh mua - Chuyển động của giá
Bid
xuống dưới được coi là một lệnh bán
Kết quả là, chúng ta có thuật toán sau.
bool GenerateMarketBook(const int count, MqlBookInfo &book[])
{
MqlTick tick; // trung tâm sổ lệnh
if(!SymbolInfoTick(_Symbol, tick)) return false;
double buys[]; // khối lượng mua theo mức giá
double sells[]; // khối lượng bán theo mức giá
MqlTick ticks[];
CopyTicks(_Symbol, ticks, COPY_TICKS_ALL, 0, count); // yêu cầu lịch sử tick
for(int i = 1; i < ArraySize(ticks); ++i)
{
// chúng ta tin rằng ask bị đẩy lên bởi lệnh mua
int k = (int)MathRound((tick.ask - ticks[i].ask) / _Point);
if(ticks[i].ask > ticks[i - 1].ask)
{
// đã mua, có lẽ sẽ chốt lời bằng cách bán
if(k <= 0)
{
Place(sells, -k, contract / sqrt(sqrt(ArraySize(ticks) - i)));
}
}
// tin rằng bid bị đẩy xuống bởi lệnh bán
k = (int)MathRound((tick.bid - ticks[i].bid) / _Point);
if(ticks[i].bid < ticks[i - 1].bid)
{
// đã bán, có lẽ sẽ chốt lời bằng cách mua
if(k >= 0)
{
Place(buys, k, contract / sqrt(sqrt(ArraySize(ticks) - i)));
}
}
}
...
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
Hàm trợ giúp Place
điền vào mảng buys
và sells
, tích lũy khối lượng trong chúng theo mức giá. Chúng ta sẽ hiển thị điều này dưới đây. Chỉ số trong mảng được định nghĩa là khoảng cách tính bằng điểm từ giá tốt nhất hiện tại (Bid
hoặc Ask
). Kích thước của khối lượng tỷ lệ nghịch với độ cũ của tick, tức là các tick xa hơn trong quá khứ có ảnh hưởng ít hơn.
Sau khi các mảng được điền, một mảng cấu trúc MqlBookInfo
được hình thành dựa trên chúng.
for(int i = 0, k = 0; i < ArraySize(sells) && k < depth; ++i) // nửa trên của sổ lệnh
{
if(sells[i] > 0)
{
MqlBookInfo info = {};
info.type = BOOK_TYPE_SELL;
info.price = tick.ask + i * _Point;
info.volume = (long)sells[i];
info.volume_real = (double)(long)sells[i];
PUSH(book, info);
++k;
}
}
for(int i = 0, k = 0; i < ArraySize(buys) && k < depth; ++i) // nửa dưới của sổ lệnh
{
if(buys[i] > 0)
{
MqlBookInfo info = {};
info.type = BOOK_TYPE_BUY;
info.price = tick.bid - i * _Point;
info.volume = (long)buys[i];
info.volume_real = (double)(long)buys[i];
PUSH(book, info);
++k;
}
}
return ArraySize(book) > 0;
}
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
Hàm Place
rất đơn giản.
void Place(double &array[], const int index, const double value = 1)
{
const int size = ArraySize(array);
if(index >= size)
{
ArrayResize(array, index + 1);
for(int i = size; i <= index; ++i)
{
array[i] = 0;
}
}
array[index] += value;
}
2
3
4
5
6
7
8
9
10
11
12
13
Ảnh chụp màn hình sau đây cho thấy biểu đồ EURUSD với Expert Advisor PseudoMarketBook.mq5
đang chạy trên đó, và phiên bản kết quả của sổ lệnh.
Sổ lệnh tổng hợp của một biểu tượng tùy chỉnh dựa trên EURUSD