Kiểm tra yêu cầu: OrderCheck
Để thực hiện bất kỳ hoạt động giao dịch nào, chương trình MQL trước tiên phải điền cấu trúc MqlTradeRequest
với dữ liệu cần thiết. Trước khi gửi nó đến máy chủ bằng các hàm giao dịch, việc kiểm tra tính chính xác về mặt hình thức và đánh giá hậu quả của yêu cầu, đặc biệt là số lượng ký quỹ sẽ cần và số quỹ tự do còn lại, là điều hợp lý. Việc kiểm tra này được thực hiện bởi hàm OrderCheck
.
bool OrderCheck(const MqlTradeRequest &request, MqlTradeCheckResult &result)
Nếu không đủ quỹ hoặc các tham số được điền không chính xác, hàm trả về false
. Ngoài ra, hàm cũng phản hồi bằng cách từ chối khi giao dịch bị vô hiệu hóa, cả trong toàn bộ terminal lẫn đối với một chương trình cụ thể. Để kiểm tra mã lỗi, hãy xem trường retcode
của cấu trúc result
.
Việc kiểm tra thành công cấu trúc request
và môi trường giao dịch kết thúc với trạng thái true
, tuy nhiên, điều này không đảm bảo rằng hoạt động được yêu cầu chắc chắn sẽ thành công nếu được lặp lại bằng các hàm OrderSend
hoặc OrderSendAsync
. Điều kiện giao dịch có thể thay đổi giữa các lần gọi hoặc nhà môi giới trên máy chủ có thể áp dụng các cài đặt cho một hệ thống giao dịch bên ngoài cụ thể mà không thể đáp ứng trong thuật toán kiểm tra hình thức do OrderCheck
thực hiện.
Để có được mô tả về kết quả tài chính dự kiến, bạn nên phân tích các trường của cấu trúc result
.
Không giống như hàm OrderCalcMargin
chỉ tính toán ký quỹ ước tính cần thiết cho một vị thế hoặc lệnh được đề xuất, OrderCheck
xem xét, dù ở chế độ đơn giản hóa, trạng thái tổng quát của tài khoản giao dịch. Vì vậy, nó điền trường margin
trong cấu trúc MqlTradeCheckResult
và các trường liên quan khác (margin_free
, margin_level
) với các biến tích lũy sẽ được hình thành sau khi thực hiện lệnh. Ví dụ, nếu một vị thế đã được mở cho bất kỳ công cụ nào tại thời điểm gọi OrderCheck
và yêu cầu đang được kiểm tra làm tăng vị thế, trường margin
sẽ phản ánh số tiền ký quỹ, bao gồm cả các nghĩa vụ ký quỹ trước đó. Nếu lệnh mới chứa một hoạt động theo hướng ngược lại, ký quỹ sẽ không tăng (trong thực tế, nó nên giảm, vì một vị thế nên được đóng hoàn toàn trên tài khoản netting và ký quỹ phòng ngừa nên được áp dụng cho các vị thế ngược nhau trên tài khoản hedging; tuy nhiên, hàm không thực hiện các tính toán chính xác như vậy).
Trước hết, OrderCheck
hữu ích cho lập trình viên ở giai đoạn đầu làm quen với API giao dịch để thử nghiệm với các yêu cầu mà không cần gửi chúng đến máy chủ.
Hãy kiểm tra hiệu suất của hàm OrderCheck
bằng một Expert Advisor không giao dịch đơn giản CustomOrderCheck.mq5
. Chúng ta đã làm nó thành một Expert Advisor thay vì một script để thuận tiện sử dụng: theo cách này, nó sẽ vẫn trên biểu đồ sau khi khởi chạy với các cài đặt hiện tại, có thể dễ dàng chỉnh sửa bằng cách thay đổi các tham số đầu vào riêng lẻ. Với một script, chúng ta sẽ phải bắt đầu lại bằng cách đặt các trường mỗi lần từ giá trị mặc định.
Để chạy kiểm tra, hãy thiết lập một bộ đếm thời gian trong OnInit
.
void OnInit()
{
// khởi tạo thực thi đang chờ
EventSetTimer(1);
}
2
3
4
5
Đối với trình xử lý bộ đếm thời gian, thuật toán chính sẽ được triển khai ở đó. Ngay từ đầu, chúng ta hủy bộ đếm thời gian vì chúng ta cần mã được thực thi một lần, sau đó chờ người dùng thay đổi các tham số.
void OnTimer()
{
// thực thi mã một lần và chờ cài đặt mới từ người dùng
EventKillTimer();
...
}
2
3
4
5
6
Các tham số đầu vào của Expert Advisor hoàn toàn lặp lại tập hợp các trường của cấu trúc yêu cầu giao dịch.
input ENUM_TRADE_REQUEST_ACTIONS Action = TRADE_ACTION_DEAL;
input ulong Magic;
input ulong Order;
input string Symbol; // Symbol (trống = _Symbol hiện tại)
input double Volume; // Khối lượng (0 = lô tối thiểu)
input double Price; // Giá (0 = Ask hiện tại)
input double StopLimit;
input double SL;
input double TP;
input ulong Deviation;
input ENUM_ORDER_TYPE Type;
input ENUM_ORDER_TYPE_FILLING Filling;
input ENUM_ORDER_TYPE_TIME ExpirationType;
input datetime ExpirationTime;
input string Comment;
input ulong Position;
input ulong PositionBy;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
Nhiều tham số trong số đó không ảnh hưởng đến việc kiểm tra và hiệu suất tài chính nhưng được giữ lại để bạn có thể chắc chắn về điều này.
Theo mặc định, trạng thái của các biến tương ứng với yêu cầu mở một vị thế với lô tối thiểu của công cụ hiện tại. Đặc biệt, tham số Type
mà không được khởi tạo rõ ràng sẽ nhận giá trị 0, tương đương với thành viên ORDER_TYPE_BUY của cấu trúc ENUM_ORDER_TYPE. Trong tham số Action
, chúng ta đã chỉ định một khởi tạo rõ ràng vì 0 không tương ứng với bất kỳ phần tử nào của liệt kê ENUM_TRADE_REQUEST_ACTIONS (phần tử đầu tiên của TRADE_ACTION_DEAL là 1).
void OnTimer()
{
...
// khởi tạo các cấu trúc bằng số không
MqlTradeRequest request = {};
MqlTradeCheckResult result = {};
// giá trị mặc định
const bool kindOfBuy = (Type & 1) == 0;
const string symbol = StringLen(Symbol) == 0 ? _Symbol : Symbol;
const double volume = Volume == 0 ?
SymbolInfoDouble(symbol, SYMBOL_VOLUME_MIN) : Volume;
const double price = Price == 0 ?
SymbolInfoDouble(symbol, kindOfBuy ? SYMBOL_ASK : SYMBOL_BID) : Price;
...
2
3
4
5
6
7
8
9
10
11
12
13
14
15
Hãy điền vào cấu trúc. Các robot thực tế thường chỉ cần gán một vài trường, nhưng vì bài kiểm tra này là chung, chúng ta phải đảm bảo rằng bất kỳ tham số nào người dùng nhập vào đều được truyền đi.
request.action = Action;
request.magic = Magic;
request.order = Order;
request.symbol = symbol;
request.volume = volume;
request.price = price;
request.stoplimit = StopLimit;
request.sl = SL;
request.tp = TP;
request.deviation = Deviation;
request.type = Type;
request.type_filling = Filling;
request.type_time = ExpirationType;
request.expiration = ExpirationTime;
request.comment = Comment;
request.position = Position;
request.position_by = PositionBy;
...
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
Lưu ý rằng ở đây chúng ta chưa chuẩn hóa giá và lô, mặc dù điều này là cần thiết trong chương trình thực tế. Do đó, bài kiểm tra này cho phép nhập các giá trị "không đồng đều" và đảm bảo rằng chúng dẫn đến lỗi. Trong các ví dụ sau, chuẩn hóa sẽ được bật.
Sau đó, chúng ta gọi OrderCheck
và ghi lại các cấu trúc request
và result
. Chúng ta chỉ quan tâm đến trường retcode
của cấu trúc sau, vì vậy nó được in thêm với "giải mã" dưới dạng văn bản, macro TRCSTR (TradeRetcode.mqh
). Bạn cũng có thể phân tích trường chuỗi comment
, nhưng định dạng của nó có thể thay đổi để phù hợp hơn cho việc hiển thị cho người dùng.
ResetLastError();
PRTF(OrderCheck(request, result));
StructPrint(request, ARRAYPRINT_HEADER);
Print(TRCSTR(result.retcode));
StructPrint(result, ARRAYPRINT_HEADER, 2);
...
2
3
4
5
6
Việc xuất các cấu trúc được cung cấp bởi hàm trợ giúp StructPrint
dựa trên ArrayPrint
. Vì điều này, chúng ta vẫn sẽ nhận được hiển thị dữ liệu "thô". Đặc biệt, các phần tử của liệt kê được biểu diễn bằng số "nguyên trạng". Sau này, chúng ta sẽ phát triển một hàm để hiển thị cấu trúc MqlTradeRequest
một cách minh bạch hơn (thân thiện với người dùng) (xem TradeUtils.mqh
).
Để hỗ trợ phân tích kết quả, ở đầu hàm OnTimer
, chúng ta sẽ hiển thị trạng thái hiện tại của tài khoản, và ở cuối, để so sánh, chúng ta sẽ tính toán ký quỹ cho hoạt động giao dịch đã cho bằng hàm OrderCalcMargin
.
void OnTimer()
{
PRTF(AccountInfoDouble(ACCOUNT_EQUITY));
PRTF(AccountInfoDouble(ACCOUNT_PROFIT));
PRTF(AccountInfoDouble(ACCOUNT_MARGIN));
PRTF(AccountInfoDouble(ACCOUNT_MARGIN_FREE));
PRTF(AccountInfoDouble(ACCOUNT_MARGIN_LEVEL));
...
// điền vào cấu trúc MqlTradeRequest
// gọi OrderCheck và in kết quả
...
double margin = 0;
ResetLastError();
PRTF(OrderCalcMargin(Type, symbol, volume, price, margin));
PRTF(margin);
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
Dưới đây là ví dụ về nhật ký cho XAUUSD với cài đặt mặc định.
AccountInfoDouble(ACCOUNT_EQUITY)=15565.22 / ok
AccountInfoDouble(ACCOUNT_PROFIT)=0.0 / ok
AccountInfoDouble(ACCOUNT_MARGIN)=0.0 / ok
AccountInfoDouble(ACCOUNT_MARGIN_FREE)=15565.22 / ok
AccountInfoDouble(ACCOUNT_MARGIN_LEVEL)=0.0 / ok
OrderCheck(request,result)=true / ok
[action] [magic] [order] [symbol] [volume] [price] [stoplimit] [sl] [tp] [deviation] [type] »
1 0 0 "XAUUSD" 0.01 1899.97 0.00 0.00 0.00 0 0 »
» [type_filling] [type_time] [expiration] [comment] [position] [position_by] [reserved]
» 0 0 1970.01.01 00:00:00 "" 0 0 0
OK_0
[retcode] [balance] [equity] [profit] [margin] [margin_free] [margin_level] [comment] [reserved]
0 15565.22 15565.22 0.00 19.00 15546.22 81922.21 "Done" 0
OrderCalcMargin(Type,symbol,volume,price,margin)=true / ok
margin=19.0 / ok
2
3
4
5
6
7
8
9
10
11
12
13
14
15
Ví dụ tiếp theo cho thấy ước tính về sự gia tăng ký quỹ dự kiến trên tài khoản, nơi đã có một vị thế mở mà chúng ta định tăng gấp đôi.
AccountInfoDouble(ACCOUNT_EQUITY)=9999.540000000001 / ok
AccountInfoDouble(ACCOUNT_PROFIT)=-0.83 / ok
AccountInfoDouble(ACCOUNT_MARGIN)=79.22 / ok
AccountInfoDouble(ACCOUNT_MARGIN_FREE)=9920.32 / ok
AccountInfoDouble(ACCOUNT_MARGIN_LEVEL)=12622.49431961626 / ok
OrderCheck(request,result)=true / ok
[action] [magic] [order] [symbol] [volume] [price] [stoplimit] [sl] [tp] [deviation] [type] »
1 0 0 "PLZL.MM" 1.0 12642.0 0.0 0.0 0.0 0 0 »
» [type_filling] [type_time] [expiration] [comment] [position] [position_by] [reserved]
» 0 0 1970.01.01 00:00:00 "" 0 0 0
OK_0
[retcode] [balance] [equity] [profit] [margin] [margin_free] [margin_level] [comment] [reserved]
0 10000.87 9999.54 -0.83 158.26 9841.28 6318.43 "Done" 0
OrderCalcMargin(Type,symbol,volume,price,margin)=true / ok
margin=79.04000000000001 / ok
2
3
4
5
6
7
8
9
10
11
12
13
14
15
Hãy thử thay đổi bất kỳ tham số yêu cầu nào và xem liệu yêu cầu có thành công không. Các kết hợp tham số không chính xác sẽ gây ra mã lỗi từ danh sách tiêu chuẩn, nhưng vì có nhiều tùy chọn không hợp lệ hơn số lượng được dự trữ (các lỗi phổ biến nhất), hàm thường có thể trả về mã chung TRADE_RETCODE_INVALID (10013). Về vấn đề này, nên triển khai kiểm tra cấu trúc của riêng bạn với mức độ chẩn đoán cao hơn.
Khi gửi các yêu cầu thực tế đến máy chủ, mã TRADE_RETCODE_INVALID tương tự được sử dụng trong các tình huống bất ngờ khác nhau, ví dụ, khi cố gắng chỉnh sửa lại một lệnh mà hoạt động sửa đổi đã bắt đầu (nhưng chưa hoàn thành) trong hệ thống giao dịch bên ngoài.