Yêu cầu đồng bộ và không đồng bộ
Trước khi đi vào chi tiết, hãy nhớ rằng mỗi chương trình MQL được thực thi trong một luồng riêng của nó, và do đó, việc xử lý song song không đồng bộ các giao dịch (và các sự kiện khác) chỉ có thể thực hiện được nhờ vào việc một chương trình MQL khác thực hiện điều đó. Đồng thời, cần đảm bảo trao đổi thông tin giữa các chương trình. Chúng ta đã biết một vài cách để làm điều này: biến toàn cục của terminal và tệp. Trong Phần 7 của cuốn sách, chúng ta sẽ khám phá các tính năng khác như tài nguyên đồ họa và cơ sở dữ liệu.
Thực tế, hãy tưởng tượng một Expert Advisor tương tự như TradeTransactions.mq5
chạy song song với Expert Advisor giao dịch và lưu các giao dịch nhận được (không nhất thiết là tất cả các trường, mà chỉ những trường chọn lọc ảnh hưởng đến việc ra quyết định) vào các biến toàn cục. Sau đó, Expert Advisor có thể kiểm tra các biến toàn cục ngay sau khi gửi yêu cầu tiếp theo và đọc kết quả từ chúng mà không cần rời khỏi hàm hiện tại. Hơn nữa, nó không cần trình xử lý OnTradeTransaction
của riêng mình.
Tuy nhiên, việc tổ chức chạy một Expert Advisor bên thứ ba không hề dễ dàng. Về mặt kỹ thuật, điều này có thể được thực hiện bằng cách tạo một đối tượng biểu đồ và áp dụng một mẫu biểu đồ với Expert Advisor giám sát giao dịch được định nghĩa trước. Nhưng có một cách đơn giản hơn. Điều quan trọng là các sự kiện của OnTradeTransaction
không chỉ được truyền tới Expert Advisor mà còn tới các chỉ báo. Đổi lại, một chỉ báo là loại chương trình MQL dễ khởi chạy nhất: chỉ cần gọi iCustom
.
Ngoài ra, việc sử dụng chỉ báo mang lại một lợi ích nữa: nó có thể mô tả bộ đệm chỉ báo có sẵn từ các chương trình bên ngoài thông qua CopyBuffer
, và sắp xếp một ring buffer
trong đó để lưu trữ các giao dịch đến từ terminal (kết quả yêu cầu). Do đó, không cần phải xử lý với các biến toàn cục.
Chú ý! Sự kiện
OnTradeTransaction
không được tạo ra cho các chỉ báo trong trình kiểm tra, vì vậy bạn chỉ có thể kiểm tra hoạt động của cặp Expert Advisor-chỉ báo trực tuyến.
Hãy gọi chỉ báo này là TradeTransactionRelay.mq5
và mô tả một bộ đệm trong đó. Nó có thể được làm ẩn vì nó sẽ ghi dữ liệu không thể hiển thị, nhưng chúng ta để nó hiển thị để chứng minh khái niệm.
#property indicator_chart_window
#property indicator_buffers 1
#property indicator_plots 1
double Buffer[];
void OnInit()
{
SetIndexBuffer(0, Buffer, INDICATOR_DATA);
}
2
3
4
5
6
7
8
9
10
Trình xử lý OnCalculate
trống rỗng.
int OnCalculate(const int rates_total,
const int prev_calculated,
const int begin,
const double &price[])
{
return rates_total;
}
2
3
4
5
6
7
Trong mã, chúng ta cần một bộ chuyển đổi đã sẵn sàng từ double
sang ulong
và ngược lại, vì các ô bộ đệm có thể làm hỏng các giá trị ulong
lớn nếu chúng được ghi vào đó bằng cách ép kiểu đơn giản (xem Số thực).
#include <MQL5Book/ConverterT.mqh>
Converter<ulong,double> cnv;
2
Dưới đây là hàm OnTradeTransaction
.
#define FIELD_NUM 6 // các trường quan trọng nhất trong MqlTradeResult
void OnTradeTransaction(const MqlTradeTransaction &transaction,
const MqlTradeRequest &request,
const MqlTradeResult &result)
{
if(transaction.type == TRADE_TRANSACTION_REQUEST)
{
ArraySetAsSeries(Buffer, true);
// lưu trữ FIELD_NUM trường kết quả vào các ô bộ đệm liên tiếp
const int offset = (int)((result.request_id * FIELD_NUM)
% (Bars(_Symbol, _Period) / FIELD_NUM * FIELD_NUM));
Buffer[offset + 1] = result.retcode;
Buffer[offset + 2] = cnv[result.deal];
Buffer[offset + 3] = cnv[result.order];
Buffer[offset + 4] = result.volume;
Buffer[offset + 5] = result.price;
// phép gán này phải đứng cuối,
// vì nó là cờ báo kết quả đã sẵn sàng
Buffer[offset + 0] = result.request_id;
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
Chúng ta quyết định chỉ giữ lại sáu trường quan trọng nhất của cấu trúc MqlTradeResult
. Nếu muốn, bạn có thể mở rộng cơ chế cho toàn bộ cấu trúc, nhưng để truyền trường chuỗi comment
, bạn sẽ cần một mảng ký tự mà bạn phải dự trữ khá nhiều phần tử.
Do đó, mỗi kết quả giờ chiếm sáu ô bộ đệm liên tiếp. Chỉ số của ô đầu tiên trong số sáu ô này được xác định dựa trên ID yêu cầu: số này chỉ đơn giản được nhân với 6. Vì có thể có nhiều yêu cầu, việc ghi hoạt động theo nguyên tắc của một bộ đệm vòng, tức là chỉ số kết quả được chuẩn hóa bằng cách chia lấy dư ('%') theo kích thước của bộ đệm chỉ báo, là số lượng thanh được làm tròn lên 6. Khi số lượng yêu cầu vượt quá kích thước, bản ghi sẽ đi vòng từ các phần tử ban đầu.
Vì việc đánh số các thanh bị ảnh hưởng bởi sự hình thành của các thanh mới, nên khuyến nghị đặt chỉ báo trên các khung thời gian lớn, chẳng hạn như D1. Sau đó, chỉ vào đầu ngày mới có khả năng (nhưng khá hiếm) xảy ra tình huống khi việc đánh số các thanh trong chỉ báo sẽ thay đổi trực tiếp trong quá trình xử lý giao dịch tiếp theo, và sau đó kết quả được chỉ báo ghi lại sẽ không được Expert Advisor đọc (có thể bỏ sót một giao dịch).
Chỉ báo đã sẵn sàng. Bây giờ hãy bắt đầu triển khai một phiên bản sửa đổi mới của Expert Advisor thử nghiệm OrderSendTransaction3.mq5
(hoan hô, đây là phiên bản mới nhất của nó). Hãy mô tả biến handle
cho tay cầm chỉ báo và tạo chỉ báo trong OnInit
.
int handle = 0;
int OnInit()
{
...
const static string indicator = "MQL5Book/p6/TradeTransactionRelay";
handle = iCustom(_Symbol, PERIOD_D1, indicator);
if(handle == INVALID_HANDLE)
{
Alert("Can't start indicator ", indicator);
return INIT_FAILED;
}
return INIT_SUCCEEDED;
}
2
3
4
5
6
7
8
9
10
11
12
13
14
Để đọc kết quả yêu cầu từ bộ đệm chỉ báo, hãy chuẩn bị một hàm trợ giúp AwaitAsync
. Tham số đầu tiên của nó nhận một tham chiếu đến cấu trúc MqlTradeRequestSync
. Nếu thành công, kết quả thu được từ bộ đệm chỉ báo với handle
sẽ được ghi vào cấu trúc này. Định danh của yêu cầu mà chúng ta quan tâm đã phải ở trong cấu trúc lồng nhau, trong trường result.request_id
. Tất nhiên, ở đây chúng ta phải đọc dữ liệu theo cùng nguyên tắc, tức là trong sáu thanh.
#define FIELD_NUM 6 // các trường quan trọng nhất trong MqlTradeResult
#define TIMEOUT 1000 // 1 giây
bool AwaitAsync(MqlTradeRequestSync &r, const int _handle)
{
Converter<ulong,double> cnv;
const int offset = (int)((r.result.request_id * FIELD_NUM)
% (Bars(_Symbol, _Period) / FIELD_NUM * FIELD_NUM));
const uint start = GetTickCount();
// chờ kết quả hoặc hết thời gian
while(!IsStopped() && GetTickCount() - start < TIMEOUT)
{
double array[];
if((CopyBuffer(_handle, 0, offset, FIELD_NUM, array)) == FIELD_NUM)
{
ArraySetAsSeries(array, true);
// khi request_id được tìm thấy, điền các trường khác với kết quả
if((uint)MathRound(array[0]) == r.result.request_id)
{
r.result.retcode = (uint)MathRound(array[1]);
r.result.deal = cnv[array[2]];
r.result.order = cnv[array[3]];
r.result.volume = array[4];
r.result.price = array[5];
PrintFormat("Got Req=%d at %d ms",
r.result.request_id, GetTickCount() - start);
Print(TU::StringOf(r.result));
return true;
}
}
}
Print("Timeout for: ");
Print(TU::StringOf(r));
return false;
}
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
Bây giờ chúng ta đã có hàm này, hãy viết một thuật toán giao dịch theo phong cách đồng bộ-không đồng bộ: như một chuỗi các bước trực tiếp, mỗi bước chờ bước trước đó sẵn sàng nhờ thông báo từ chương trình chỉ báo song song trong khi vẫn nằm trong một hàm.
void OnTimer()
{
EventKillTimer();
MqlTradeRequestSync::AsyncEnabled = true;
MqlTradeRequestSync request;
request.magic = Magic;
request.deviation = Deviation;
const double volume = Volume == 0 ?
SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MIN) : Volume;
...
}
2
3
4
5
6
7
8
9
10
11
12
13
14
Bước 1.
Print("Start trade");
ResetLastError();
if((bool)(Type == MARKET_BUY ? request.buy(volume) : request.sell(volume)))
{
Print("OK Open?");
}
if(!(AwaitAsync(request, handle) && request.completed()))
{
Print("Failed Open");
return;
}
...
2
3
4
5
6
7
8
9
10
11
12
13
Bước 2.
Print("SL/TP modification");
...
if(request.adjust(SL, TP))
{
Print("OK Adjust?");
}
if(!(AwaitAsync(request, handle) && request.completed()))
{
Print("Failed Adjust");
}
2
3
4
5
6
7
8
9
10
11
Bước 3.
Print("Close down");
if(request.close(request.result.position))
{
Print("OK Close?");
}
if(!(AwaitAsync(request, handle) && request.completed()))
{
Print("Failed Close");
}
Print("Finish");
}
2
3
4
5
6
7
8
9
10
11
12
13
Xin lưu ý rằng các gọi phương thức completed
giờ đây không được thực hiện sau khi gửi yêu cầu mà sau khi kết quả được nhận bởi hàm AwaitAsync
.
Nếu không, mọi thứ rất giống với phiên bản đầu tiên của thuật toán này, nhưng giờ đây nó được xây dựng dựa trên các gọi hàm không đồng bộ và phản ứng với các sự kiện không đồng bộ.
Có lẽ điều này không có vẻ quan trọng trong ví dụ cụ thể này về chuỗi các thao tác trên một vị thế duy nhất. Tuy nhiên, chúng ta có thể sử dụng kỹ thuật tương tự để gửi và kiểm soát một loạt lệnh. Và sau đó, lợi ích sẽ trở nên rõ ràng. Sau một lát, chúng ta sẽ chứng minh điều này với sự trợ giúp của một Expert Advisor lưới và đồng thời so sánh hiệu suất của hai hàm: OrderSend
và OrderSendAsync
.
Nhưng ngay bây giờ, khi chúng ta hoàn thành loạt Expert Advisor OrderSendTransaction
, hãy chạy phiên bản mới nhất và xem trong nhật ký sự thực thi đều đặn, tuyến tính của tất cả các bước.
Start trade
OK Open?
Got Req=1 at 62 ms
DONE, D=1282677007, #=1300045365, V=0.01, @ 1.10564, Bid=1.10564, Ask=1.10564, Order placed, Req=1
Waiting for position for deal D=1282677007
SL/TP modification
OK Adjust?
Got Req=2 at 63 ms
DONE, Order placed, Req=2
Close down
OK Close?
Got Req=3 at 78 ms
DONE, D=1282677008, #=1300045366, V=0.01, @ 1.10564, Bid=1.10564, Ask=1.10564, Order placed, Req=3
Finish
2
3
4
5
6
7
8
9
10
11
12
13
14
Thời gian với độ trễ phản hồi có thể phụ thuộc đáng kể vào máy chủ, thời gian trong ngày và biểu tượng. Tất nhiên, một phần thời gian ở đây không được dành cho yêu cầu giao dịch với xác nhận mà cho việc thực thi hàm CopyBuffer
. Theo quan sát của chúng ta, nó mất không quá 16 ms (trong một chu kỳ của bộ đếm thời gian hệ thống tiêu chuẩn, những ai muốn có thể phân tích chương trình bằng bộ đếm thời gian có độ chính xác cao GetMicrosecondCount
).
Hãy bỏ qua sự khác biệt giữa trạng thái (DONE) và mô tả chuỗi ("Order placed"). Sự thật là bình luận (cũng như các trường ask/bid
) vẫn còn trong cấu trúc từ lúc nó được gửi bởi hàm OrderSendAsync
, và trạng thái cuối cùng trong trường retcode
được ghi bởi hàm AwaitAsync
của chúng ta. Điều quan trọng với chúng ta là trong cấu trúc với kết quả, số vé (deal
và order
), giá thực hiện (price
) và khối lượng (volume
) được cập nhật.
Dựa trên ví dụ đã xem xét trước đó của OrderSendTransaction3.mq5
, hãy tạo một phiên bản mới của Expert Advisor lưới PendingOrderGrid3.mq5
(phiên bản trước được cung cấp trong phần Hàm để đọc thuộc tính vị thế). Nó sẽ có thể thiết lập một lưới lệnh hoàn chỉnh ở chế độ đồng bộ hoặc không đồng bộ, tùy theo lựa chọn của người dùng. Chúng ta cũng sẽ phát hiện thời gian thiết lập toàn bộ lưới để so sánh.
Chế độ được điều khiển bởi biến đầu vào EnableAsyncSetup
. Biến handle
được phân bổ cho tay cầm chỉ báo.
input bool EnableAsyncSetup = false;
int handle;
2
3
Trong quá trình khởi tạo, trong trường hợp chế độ không đồng bộ, chúng ta tạo một phiên bản của chỉ báo TradeTransactionRelay
.
int OnInit()
{
...
if(EnableAsyncSetup)
{
const uint start = GetTickCount();
const static string indicator = "MQL5Book/p6/TradeTransactionRelay";
handle = iCustom(_Symbol, PERIOD_D1, indicator);
if(handle == INVALID_HANDLE)
{
Alert("Can't start indicator ", indicator);
return INIT_FAILED;
}
PrintFormat("Started in %d ms", GetTickCount() - start);
}
...
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
Để đơn giản hóa việc lập trình, chúng ta đã thay thế mảng hai chiều request
bằng mảng một chiều trong hàm SetupGrid
.
uint SetupGrid()
{
... // trước đây:
MqlTradeRequestSyncLog request[]; // MqlTradeRequestSyncLog request[][2];
ArrayResize(request, GridSize * 2); // ArrayResize(request, GridSize);
...
}
2
3
4
5
6
7
Tiếp theo trong vòng lặp qua mảng, thay vì các gọi kiểu request[i][1]
, chúng ta sử dụng cách định địa chỉ request[i * 2 + 1]
.
Sự chuyển đổi nhỏ này là cần thiết vì các lý do sau. Vì chúng ta sử dụng mảng cấu trúc này cho các truy vấn khi tạo lưới, và chúng ta cần đợi tất cả kết quả, hàm AwaitAsync
giờ đây nên nhận tham chiếu đến một mảng làm tham số đầu tiên của nó. Một mảng một chiều dễ xử lý hơn.
Đối với mỗi yêu cầu, vị trí của nó trong bộ đệm chỉ báo được tính toán dựa trên request_id
: tất cả các vị trí được đặt vào mảng offset
. Khi nhận được xác nhận yêu cầu, các phần tử tương ứng của mảng được đánh dấu là đã xử lý bằng cách ghi giá trị -1 vào đó. Số lượng yêu cầu đã thực hiện được đếm trong biến done
. Khi nó bằng kích thước của mảng, toàn bộ lưới đã sẵn sàng.
bool AwaitAsync(MqlTradeRequestSyncLog &r[], const int _handle)
{
Converter<ulong,double> cnv;
int offset[];
const int n = ArraySize(r);
int done = 0;
ArrayResize(offset, n);
for(int i = 0; i < n; ++i)
{
offset[i] = (int)((r[i].result.request_id * FIELD_NUM)
% (Bars(_Symbol, _Period) / FIELD_NUM * FIELD_NUM));
}
const uint start = GetTickCount();
while(!IsStopped() && done < n && GetTickCount() - start < TIMEOUT)
for(int i = 0; i < n; ++i)
{
if(offset[i] == -1) continue; // bỏ qua các phần tử trống
double array[];
if((CopyBuffer(_handle, 0, offset[i], FIELD_NUM, array)) == FIELD_NUM)
{
ArraySetAsSeries(array, true);
if((uint)MathRound(array[0]) == r[i].result.request_id)
{
r[i].result.retcode = (uint)MathRound(array[1]);
r[i].result.deal = cnv[array[2]];
r[i].result.order = cnv[array[3]];
r[i].result.volume = array[4];
r[i].result.price = array[5];
PrintFormat("Got Req=%d at %d ms",
r[i].result.request_id, GetTickCount() - start);
Print(TU::StringOf(r[i].result));
offset[i] = -1; // đánh dấu đã xử lý
done++;
}
}
}
return done == n;
}
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
Quay lại hàm SetupGrid
, hãy cho thấy cách gọi AwaitAsync
sau vòng lặp gửi yêu cầu.
uint SetupGrid()
{
...
const uint start = GetTickCount();
for(int i = 0; i < (int)GridSize / 2; ++i)
{
// các gọi của buyLimit/sellStopLimit/sellLimit/buyStopLimit
}
if(EnableAsyncSetup)
{
if(!AwaitAsync(request, handle))
{
Print("Timeout");
return TRADE_RETCODE_ERROR;
}
}
PrintFormat("Done %d requests in %d ms (%d ms/request)",
GridSize * 2, GetTickCount() - start,
(GetTickCount() - start) / (GridSize * 2));
...
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
Nếu xảy ra hết thời gian khi thiết lập lưới (không phải tất cả yêu cầu đều nhận được xác nhận trong thời gian được phân bổ), chúng ta sẽ trả về mã TRADE_RETCODE_ERROR
, và Expert Advisor sẽ cố gắng "quay lại" những gì nó đã tạo ra.
Điều quan trọng cần lưu ý là chế độ không đồng bộ chỉ nhằm thiết lập một lưới đầy đủ khi chúng ta cần gửi một loạt yêu cầu. Nếu không, chế độ đồng bộ vẫn sẽ được sử dụng. Do đó, chúng ta phải đặt cờ MqlTradeRequestSync::AsyncEnabled
thành true
trước vòng lặp gửi và đặt lại thành false
sau đó. Tuy nhiên, hãy chú ý đến điều sau. Các lỗi có thể xảy ra bên trong vòng lặp, do đó nó bị kết thúc sớm, trả về mã cuối cùng từ máy chủ. Vì vậy, nếu chúng ta đặt reset không đồng bộ sau vòng lặp, không có gì đảm bảo rằng nó sẽ được reset.
Để giải quyết vấn đề này, một lớp nhỏ AsyncSwitcher
được thêm vào tệp MqlTradeSync.mqh
. Lớp này kiểm soát việc bật và tắt chế độ không đồng bộ từ hàm tạo và hàm hủy của nó. Điều này phù hợp với khái niệm quản lý tài nguyên RAII được thảo luận trong phần Quản lý mô tả tệp.
class AsyncSwitcher
{
public:
AsyncSwitcher(const bool enabled = true)
{
MqlTradeRequestSync::AsyncEnabled = enabled;
}
~AsyncSwitcher()
{
MqlTradeRequestSync::AsyncEnabled = false;
}
};
2
3
4
5
6
7
8
9
10
11
12
Bây giờ, để kích hoạt tạm thời chế độ không đồng bộ một cách an toàn, chúng ta có thể chỉ cần mô tả đối tượng cục bộ AsyncSwitcher
trong hàm SetupGrid
. Mã sẽ tự động quay lại chế độ đồng bộ khi thoát khỏi hàm theo bất kỳ cách nào.
uint SetupGrid()
{
...
AsyncSwitcher sync(EnableAsyncSetup);
...
for(int i = 0; i < (int)GridSize / 2; ++i)
{
...
}
...
}
2
3
4
5
6
7
8
9
10
11
Expert Advisor đã sẵn sàng. Hãy thử chạy nó hai lần: ở chế độ đồng bộ và không đồng bộ cho một lưới đủ lớn (10 cấp, bước lưới 200).
Đối với lưới 10 cấp, chúng ta sẽ nhận được 20 yêu cầu, vì vậy dưới đây là một số nhật ký. Đầu tiên, chế độ đồng bộ được sử dụng. Hãy làm rõ rằng dòng chữ về sự sẵn sàng của các yêu cầu được hiển thị trước các thông báo về yêu cầu vì những thông báo sau được tạo ra bởi các hàm hủy cấu trúc khi hàm thoát ra. Tốc độ xử lý là 51ms mỗi yêu cầu.
Start setup at 1.10379
Done 20 requests in 1030 ms (51 ms/request)
TRADE_ACTION_PENDING, EURUSD, ORDER_TYPE_BUY_LIMIT, V=0.01, ORDER_FILLING_FOK, @ 1.10200, »
» ORDER_TIME_GTC, M=1234567890, G[1.10400]
DONE, #=1300978336, V=0.01, Request executed, Req=1
TRADE_ACTION_PENDING, EURUSD, ORDER_TYPE_SELL_STOP_LIMIT, V=0.01, ORDER_FILLING_FOK, @ 1.10200, »
» X=1.10400, ORDER_TIME_GTC, M=1234567890, G[1.10400]
DONE, #=1300978337, V=0.01, Request executed, Req=2
TRADE_ACTION_PENDING, EURUSD, ORDER_TYPE_BUY_LIMIT, V=0.01, ORDER_FILLING_FOK, @ 1.10000, »
» ORDER_TIME_GTC, M=1234567890, G[1.10400]
DONE, #=1300978343, V=0.01, Request executed, Req=5
TRADE_ACTION_PENDING, EURUSD, ORDER_TYPE_SELL_STOP_LIMIT, V=0.01, ORDER_FILLING_FOK, @ 1.10000, »
» X=1.10200, ORDER_TIME_GTC, M=1234567890, G[1.10400]
DONE, #=1300978344, V=0.01, Request executed, Req=6
TRADE_ACTION_PENDING, EURUSD, ORDER_TYPE_BUY_LIMIT, V=0.01, ORDER_FILLING_FOK, @ 1.09800, »
» ORDER_TIME_GTC, M=1234567890, G[1.10400]
DONE, #=1300978348, V=0.01, Request executed, Req=9
TRADE_ACTION_PENDING, EURUSD, ORDER_TYPE_SELL_STOP_LIMIT, V=0.01, ORDER_FILLING_FOK, @ 1.09800, »
» X=1.10000, ORDER_TIME_GTC, M=1234567890, G[1.10400]
DONE, #=1300978350, V=0.01, Request executed, Req=10
...
TRADE_ACTION_PENDING, EURUSD, ORDER_TYPE_SELL_LIMIT, V=0.01, ORDER_FILLING_FOK, @ 1.10600, »
» ORDER_TIME_GTC, M=1234567890, G[1.10400]
DONE, #=1300978339, V=0.01, Request executed, Req=3
TRADE_ACTION_PENDING, EURUSD, ORDER_TYPE_BUY_STOP_LIMIT, V=0.01, ORDER_FILLING_FOK, @ 1.10600, »
» X=1.10400, ORDER_TIME_GTC, M=1234567890, G[1.10400]
DONE, #=1300978340, V=0.01, Request executed, Req=4
TRADE_ACTION_PENDING, EURUSD, ORDER_TYPE_SELL_LIMIT, V=0.01, ORDER_FILLING_FOK, @ 1.10800, »
» ORDER_TIME_GTC, M=1234567890, G[1.10400]
DONE, #=1300978345, V=0.01, Request executed, Req=7
TRADE_ACTION_PENDING, EURUSD, ORDER_TYPE_BUY_STOP_LIMIT, V=0.01, ORDER_FILLING_FOK, @ 1.10800, »
» X=1.10600, ORDER_TIME_GTC, M=1234567890, G[1.10400]
DONE, #=1300978347, V=0.01, Request executed, Req=8
...
TRADE_ACTION_PENDING, EURUSD, ORDER_TYPE_SELL_LIMIT, V=0.01, ORDER_FILLING_FOK, @ 1.11400, »
» ORDER_TIME_GTC, M=1234567890, G[1.10400]
DONE, #=1300978365, V=0.01, Request executed, Req=19
TRADE_ACTION_PENDING, EURUSD, ORDER_TYPE_BUY_STOP_LIMIT, V=0.01, ORDER_FILLING_FOK, @ 1.11400, »
» X=1.11200, ORDER_TIME_GTC, M=1234567890, G[1.10400]
DONE, #=1300978366, V=0.01, Request executed, Req=20
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
Giữa lưới khớp với giá 1.10400. Hệ thống gán số cho các yêu cầu theo thứ tự nhận được, và việc đánh số của chúng trong mảng tương ứng với thứ tự chúng ta đặt lệnh: từ mức cơ sở trung tâm, chúng ta dần dần phân kỳ sang hai bên. Do đó, đừng ngạc nhiên khi sau cặp 1 và 2 (cho mức 1.10200) là 5 và 6 (1.10000), vì 3 và 4 (1.10600) đã được gửi trước đó.
Ở chế độ không đồng bộ, các hàm hủy được đi trước bởi các thông báo về sự sẵn sàng của các yêu cầu cụ thể nhận được trong AwaitAsync
theo thời gian thực, và không nhất thiết theo thứ tự các yêu cầu được gửi (ví dụ, yêu cầu 49 và 50 "vượt qua" 47 và 48).
Started in 16 ms
Start setup at 1.10356
Got Req=41 at 109 ms
DONE, #=1300979180, V=0.01, Order placed, Req=41
Got Req=42 at 109 ms
DONE, #=1300979181, V=0.01, Order placed, Req=42
Got Req=43 at 125 ms
DONE, #=1300979182, V=0.01, Order placed, Req=43
Got Req=44 at 140 ms
DONE, #=1300979183, V=0.01, Order placed, Req=44
Got Req=45 at 156 ms
DONE, #=1300979184, V=0.01, Order placed, Req=45
Got Req=46 at 172 ms
DONE, #=1300979185, V=0.01, Order placed, Req=46
Got Req=49 at 172 ms
DONE, #=1300979188, V=0.01, Order placed, Req=49
Got Req=50 at 172 ms
DONE, #=1300979189, V=0.01, Order placed, Req=50
Got Req=47 at 172 ms
DONE, #=1300979186, V=0.01, Order placed, Req=47
Got Req=48 at 172 ms
DONE, #=1300979187, V=0.01, Order placed, Req=48
Got Req=51 at 172 ms
DONE, #=1300979190, V=0.01, Order placed, Req=51
Got Req=52 at 203 ms
DONE, #=1300979191, V=0.01, Order placed, Req=52
Got Req=55 at 203 ms
DONE, #=1300979194, V=0.01, Order placed, Req=55
Got Req=56 at 203 ms
DONE, #=1300979195, V=0.01, Order placed, Req=56
Got Req=53 at 203 ms
DONE, #=1300979192, V=0.01, Order placed, Req=53
Got Req=54 at 203 ms
DONE, #=1300979193, V=0.01, Order placed, Req=54
Got Req=57 at 218 ms
DONE, #=1300979196, V=0.01, Order placed, Req=57
Got Req=58 at 218 ms
DONE, #=1300979198, V=0.01, Order placed, Req=58
Got Req=59 at 218 ms
DONE, #=1300979199, V=0.01, Order placed, Req=59
Got Req=60 at 218 ms
DONE, #=1300979200, V=0.01, Order placed, Req=60
Done 20 requests in 234 ms (11 ms/request)
...
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
Do tất cả các yêu cầu được thực thi song song, tổng thời gian gửi (234ms) chỉ hơi nhiều hơn thời gian của một yêu cầu đơn lẻ (ở đây khoảng 100ms, nhưng bạn sẽ có thời gian của riêng bạn). Kết quả là, chúng ta đạt tốc độ 11ms mỗi yêu cầu, nhanh hơn 5 lần so với phương pháp đồng bộ. Vì các yêu cầu được gửi gần như đồng thời, chúng ta không thể biết thời gian thực hiện của từng yêu cầu, và mili giây cho biết thời điểm đến của kết quả của một yêu cầu cụ thể từ lúc bắt đầu gửi nhóm chung.
Các nhật ký tiếp theo, như trong trường hợp trước, chứa tất cả các trường yêu cầu và kết quả được in từ các hàm hủy cấu trúc. Dòng "Order placed" vẫn không thay đổi sau OrderSendAsync
, vì chỉ báo phụ trợ của chúng ta TradeTransactionRelay.mq5
không công bố toàn bộ cấu trúc MqlTradeResult
từ thông điệp TRADE_TRANSACTION_REQUEST.
...
TRADE_ACTION_PENDING, EURUSD, ORDER_TYPE_BUY_LIMIT, V=0.01, ORDER_FILLING_FOK, @ 1.10200, »
» ORDER_TIME_GTC, M=1234567890, G[1.10400]
DONE, #=1300979180, V=0.01, Order placed, Req=41
TRADE_ACTION_PENDING, EURUSD, ORDER_TYPE_SELL_STOP_LIMIT, V=0.01, ORDER_FILLING_FOK, @ 1.10200, »
» X=1.10400, ORDER_TIME_GTC, M=1234567890, G[1.10400]
DONE, #=1300979181, V=0.01, Order placed, Req=42
TRADE_ACTION_PENDING, EURUSD, ORDER_TYPE_BUY_LIMIT, V=0.01, ORDER_FILLING_FOK, @ 1.10000, »
» ORDER_TIME_GTC, M=1234567890, G[1.10400]
DONE, #=1300979184, V=0.01, Order placed, Req=45
TRADE_ACTION_PENDING, EURUSD, ORDER_TYPE_SELL_STOP_LIMIT, V=0.01, ORDER_FILLING_FOK, @ 1.10000, »
» X=1.10200, ORDER_TIME_GTC, M=1234567890, G[1.10400]
DONE, #=1300979185, V=0.01, Order placed, Req=46
TRADE_ACTION_PENDING, EURUSD, ORDER_TYPE_BUY_LIMIT, V=0.01, ORDER_FILLING_FOK, @ 1.09800, »
» ORDER_TIME_GTC, M=1234567890, G[1.10400]
DONE, #=1300979188, V=0.01, Order placed, Req=49
TRADE_ACTION_PENDING, EURUSD, ORDER_TYPE_SELL_STOP_LIMIT, V=0.01, ORDER_FILLING_FOK, @ 1.09800, »
» X=1.10000, ORDER_TIME_GTC, M=1234567890, G[1.10400]
DONE, #=1300979189, V=0.01, Order placed, Req=50
...
TRADE_ACTION_PENDING, EURUSD, ORDER_TYPE_SELL_LIMIT, V=0.01, ORDER_FILLING_FOK, @ 1.10600, »
» ORDER_TIME_GTC, M=1234567890, G[1.10400]
DONE, #=1300979182, V=0.01, Order placed, Req=43
TRADE_ACTION_PENDING, EURUSD, ORDER_TYPE_BUY_STOP_LIMIT, V=0.01, ORDER_FILLING_FOK, @ 1.10600, »
» X=1.10400, ORDER_TIME_GTC, M=1234567890, G[1.10400]
DONE, #=1300979183, V=0.01, Order placed, Req=44
TRADE_ACTION_PENDING, EURUSD, ORDER_TYPE_SELL_LIMIT, V=0.01, ORDER_FILLING_FOK, @ 1.10800, »
» ORDER_TIME_GTC, M=1234567890, G[1.10400]
DONE, #=1300979186, V=0.01, Order placed, Req=47
TRADE_ACTION_PENDING, EURUSD, ORDER_TYPE_BUY_STOP_LIMIT, V=0.01, ORDER_FILLING_FOK, @ 1.10800, »
» X=1.10600, ORDER_TIME_GTC, M=1234567890, G[1.10400]
DONE, #=1300979187, V=0.01, Order placed, Req=48
...
TRADE_ACTION_PENDING, EURUSD, ORDER_TYPE_SELL_LIMIT, V=0.01, ORDER_FILLING_FOK, @ 1.11400, »
» ORDER_TIME_GTC, M=1234567890, G[1.10400]
DONE, #=1300979199, V=0.01, Order placed, Req=59
TRADE_ACTION_PENDING, EURUSD, ORDER_TYPE_BUY_STOP_LIMIT, V=0.01, ORDER_FILLING_FOK, @ 1.11400, »
» X=1.11200, ORDER_TIME_GTC, M=1234567890, G[1.10400]
DONE, #=1300979200, V=0.01, Order placed, Req=60
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
Cho đến nay, Expert Advisor lưới của chúng ta có một cặp lệnh chờ tại mỗi cấp: giới hạn và dừng-giới hạn. Để tránh sự trùng lặp như vậy, chúng ta chỉ giữ lại các lệnh giới hạn. Đây sẽ là phiên bản cuối cùng của PendingOrderGrid4.mq5
, cũng có thể chạy ở chế độ đồng bộ và không đồng bộ. Chúng ta sẽ không đi sâu vào mã nguồn chi tiết mà chỉ ghi nhận những khác biệt chính so với phiên bản trước.
Trong hàm SetupGrid
, chúng ta cần một mảng cấu trúc có kích thước bằng GridSize
và không nhân đôi. Số lượng yêu cầu cũng sẽ giảm đi 2 lần: các phương thức duy nhất được sử dụng cho chúng là buyLimit
và sellLimit
.
Hàm CheckGrid
kiểm tra tính toàn vẹn của lưới theo cách khác. Trước đây, việc thiếu một lệnh dừng-giới hạn đôi tại cấp có lệnh giới hạn được coi là lỗi. Điều này có thể xảy ra khi một lệnh dừng-giới hạn được kích hoạt trên máy chủ từ một cấp lân cận. Tuy nhiên, sơ đồ này không thể khôi phục lưới nếu xảy ra biến động giá mạnh hai chiều (đỉnh) trên một thanh: nó sẽ không chỉ loại bỏ các lệnh giới hạn ban đầu mà còn cả các lệnh mới được tạo từ các lệnh dừng-giới hạn. Giờ đây, thuật toán trung thực kiểm tra các cấp trống ở cả hai bên của giá hiện tại và tạo các lệnh giới hạn ở đó bằng RepairGridLevel
. Hàm trợ giúp này trước đây đặt các lệnh dừng-giới hạn.
Cuối cùng, trình xử lý OnTradeTransaction
xuất hiện trong PendingOrderGrid4.mq5
. Việc kích hoạt một lệnh chờ sẽ dẫn đến thực hiện một giao dịch (và thay đổi cấu hình lưới cần được sửa chữa), vì vậy chúng ta kiểm soát các giao dịch theo biểu tượng và số ma thuật đã cho. Khi phát hiện một giao dịch, hàm CheckGrid
được gọi ngay lập tức, ngoài việc nó vẫn được thực thi vào đầu mỗi thanh.
void OnTradeTransaction(const MqlTradeTransaction &transaction,
const MqlTradeRequest &,
const MqlTradeResult &)
{
if(transaction.type == TRADE_TRANSACTION_DEAL_ADD)
{
if(transaction.symbol == _Symbol)
{
DealMonitor dm(transaction.deal); // chọn giao dịch
if(dm.get(DEAL_MAGIC) == Magic)
{
CheckGrid();
}
}
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
Cần lưu ý rằng sự hiện diện của OnTradeTransaction
là không đủ để viết các Expert Advisor có khả năng chống lại các ảnh hưởng bên ngoài không lường trước. Tất nhiên, các sự kiện cho phép bạn phản ứng nhanh với tình huống, nhưng chúng ta không có gì đảm bảo rằng Expert Advisor sẽ không bị tắt (hoặc ngoại tuyến) vì lý do này hay lý do khác trong một khoảng thời gian và bỏ qua giao dịch này hay giao dịch kia. Do đó, trình xử lý OnTradeTransaction
chỉ nên giúp tăng tốc các quá trình mà chương trình có thể thực hiện mà không cần nó. Đặc biệt, để khôi phục trạng thái của nó một cách chính xác sau khi khởi động.
Tuy nhiên, ngoài sự kiện OnTradeTransaction
, MQL5 cung cấp một sự kiện khác, đơn giản hơn: OnTrade
.