MQL5 Cookbook: Processing of the TradeTransaction Event
Denis Kirichenko | 9 October, 2014
Introduction
In this article I would like to introduce one of the ways to control trade events using the means of MQL5. I must mention that a few articles have already been dedicated to this topic. "Processing of trade events in Expert Advisor using the OnTrade() function" is one of them. I am not going to repeat other authors and will use another handler - OnTradeTransaction().
I would like to draw the readers' attention to the following point. The current version of the MQL5 language formally counts 14 event handlers of the Client Terminal. In addition, a programmer has a possibility to create custom events with EventChartCustom() and process them with OnChartEvent(). However, the term 'Event-driven programming' (EDP) is not mentioned in Documentation at all. It is strange, given the fact, that any program in MQL5 is created based on the EDP principles. For instance, the user is offered to make a choice at the step 'Events handler for the Expert' in a template of any Expert Advisor.
It is obvious that the mechanism of the Event-driven Programming is used in MQL5 one way or another. The language may contain program blocks consisting of two parts: selecting and processing of an event. Moreover, if we are talking about events of the Client Terminal, a programmer has control only over the second part, i.e. the event handler. To be fair, there are exceptions for some events. Timer and custom event are among them. Control of these events is left entirely for the programmer.
1. TradeTransaction Event
Before we go in depth into our topic, let us refer to the official information.
According to Documentation, the TradeTransaction event is a result of certain operations with a trading account. An operation itself consists of a number of stages determined by transactions. For instance, opening a position with a market order, one of the most popular operations with a trading account, is implemented in the following stages:
- Make a trade request;
- Verify the trade request;
- Send the trade request to the server;
- Receive a response about the trade order execution on the server.
Such sequence, though, only shows the logic of work of the terminal-server pair which is reflected in the strings of the EA code. From the point of view of the TradeTransaction trade event, opening a position on the market happens the following way:
- MQL5-program receives a notification from the server about the result of the completed request;
- The request in the form of an order with a unique ticket gets included into the list of open orders;
- The order gets deleted from the list of open orders after execution;
- Then, the order goes to the account history;
- The account history also contains data on the deal that the order execution results in.
So, opening a position requires five calls for the OnTradeTransaction() handler.
We shall discuss the program code in detail a little later and now we are going to have a close look at the header of the function. It has three input parameters.
void OnTradeTransaction( const MqlTradeTransaction& trans, // structure of the trade transaction const MqlTradeRequest& request, // structure of the request const MqlTradeResult& result // structure of the response );
These parameters are described in detail in Documentation. I would like to note that a parameter of the trade transaction structure is a kind of cast of the information that the handler receives during the current call.
I must also say a few words about the type of the trade transaction as we are going to come across it a lot.
In MQL5, ENUM_TRADE_TRANSACTION_TYPE is a special enumeration which is responsible for the the type of the trade transaction. To find out what type a trade transaction belongs to, we need to refer to the parameter-constant of the MqlTradeTransaction type.
struct MqlTradeTransaction { ulong deal; // Ticket of the deal ulong order; // Ticket of the order string symbol; // Trade symbol ENUM_TRADE_TRANSACTION_TYPE type; // Type of the trade transaction ENUM_ORDER_TYPE order_type; // Type of the order ENUM_ORDER_STATE order_state; // Status of the order ENUM_DEAL_TYPE deal_type; // Type of the deal ENUM_ORDER_TYPE_TIME time_type; // Order expiration type datetime time_expiration; // Order expiration time double price; // Price double price_trigger; // Price that triggers the Stop Limit order double price_sl; // Level of Stop Loss double price_tp; // Level of Take Profit double volume; // Volume in lots };
The fourth field of the structure is the very enumeration we are looking for.
2. Processing of Positions
Virtually all trade operations that concern processing of positions entail five calls of the OnTradeTransaction() handler. Among them are:
- opening of a position;
- position;
- position reversal;
- adding lots to the position;
- partial closing of a position.
Modifying a position is the only trade operation that calls the TradeTransaction event handler twice.
Since there is no information about what transaction types are responsible for certain trade operations, we are going to find it out through trial and error.
Before that, we will have to create a template of the Expert that will contain the TradeTransaction event handler. I named my version of the template TradeProcessor.mq5. I added a feature that enables displaying information on the values of the structure fields in the log. These values are the parameters of the event - handler. Analyzing those records will be time consuming, but in the end it will pay off by presenting the complete picture of events.
We need to launch the Expert in the debug mode on any of the charts in MetaTrader 5 terminal.
Open a position manually and take a look at the code. The first call of the handler will be like this (Fig. 1).
Fig.1. The type field is equal to TRADE_TRANSACTION_REQUEST
The following entries will appear in the log:
IO 0 17:37:53.233 TradeProcessor (EURUSD,H1) ---===Transaction===--- NK 0 17:37:53.233 TradeProcessor (EURUSD,H1) Ticket of the deal: 0 RR 0 17:37:53.233 TradeProcessor (EURUSD,H1) Type of the deal: DEAL_TYPE_BUY DE 0 17:37:53.233 TradeProcessor (EURUSD,H1) Ticket of the order: 0 JS 0 17:37:53.233 TradeProcessor (EURUSD,H1) Status of the order: ORDER_STATE_STARTED JN 0 17:37:53.233 TradeProcessor (EURUSD,H1) Type of the order: ORDER_TYPE_BUY FD 0 17:37:53.233 TradeProcessor (EURUSD,H1) Price: 0.0000 FN 0 17:37:53.233 TradeProcessor (EURUSD,H1) Level of Stop Loss: 0.0000 HF 0 17:37:53.233 TradeProcessor (EURUSD,H1) Level of Take Profit: 0.0000 FQ 0 17:37:53.233 TradeProcessor (EURUSD,H1) Price that triggers the Stop Limit order: 0.0000 RR 0 17:37:53.233 TradeProcessor (EURUSD,H1) Trade symbol: HD 0 17:37:53.233 TradeProcessor (EURUSD,H1) Pending order expiration time: 1970.01.01 00:00 GS 0 17:37:53.233 TradeProcessor (EURUSD,H1) Order expiration type: ORDER_TIME_GTC DN 0 17:37:53.233 TradeProcessor (EURUSD,H1) Type of the trade transaction TRADE_TRANSACTION_REQUEST FK 0 17:37:53.233 TradeProcessor (EURUSD,H1) Volume in lots: 0.00
In this block only the record concerning the transaction type is of interest to us. As we can see, this type belongs to the (TRADE_TRANSACTION_REQUEST) request.
Information about the request details can be obtained in the block "Request".
QG 0 17:37:53.233 TradeProcessor (EURUSD,H1) ---===Request===--- HL 0 17:37:53.233 TradeProcessor (EURUSD,H1) Type of the trade operation: TRADE_ACTION_DEAL EE 0 17:37:53.233 TradeProcessor (EURUSD,H1) Comment to the order: JP 0 17:37:53.233 TradeProcessor (EURUSD,H1) Deviation from the requested price: 0 GS 0 17:37:53.233 TradeProcessor (EURUSD,H1) Order expiration time: 1970.01.01 00:00 LF 0 17:37:53.233 TradeProcessor (EURUSD,H1) Magic number of the EA: 0 FM 0 17:37:53.233 TradeProcessor (EURUSD,H1) Ticket of the order: 22535869 EJ 0 17:37:53.233 TradeProcessor (EURUSD,H1) Price: 1.3137 QR 0 17:37:53.233 TradeProcessor (EURUSD,H1) Stop Loss level of the order: 0.0000 IJ 0 17:37:53.233 TradeProcessor (EURUSD,H1) Take Profit level of the order: 0.0000 KK 0 17:37:53.233 TradeProcessor (EURUSD,H1) StopLimit level of the order: 0.0000 FS 0 17:37:53.233 TradeProcessor (EURUSD,H1) Trade symbol: EURUSD RD 0 17:37:53.233 TradeProcessor (EURUSD,H1) Type of the order: ORDER_TYPE_BUY
Data on the result of the request execution will get to the block "Response".
KG 0 17:37:53.233 TradeProcessor (EURUSD,H1) ---===Response===--- JR 0 17:37:53.233 TradeProcessor (EURUSD,H1) Code of the operation result: 10009 GD 0 17:37:53.233 TradeProcessor (EURUSD,H1) Ticket of the deal: 15258202 NR 0 17:37:53.233 TradeProcessor (EURUSD,H1) Ticket of the order: 22535869 EF 0 17:37:53.233 TradeProcessor (EURUSD,H1) Volume of the deal: 0.11 MN 0 17:37:53.233 TradeProcessor (EURUSD,H1) Price of the deal: 1.3137 HJ 0 17:37:53.233 TradeProcessor (EURUSD,H1) Bid: 1.3135 PM 0 17:37:53.233 TradeProcessor (EURUSD,H1) Ask: 1.3137 OG 0 17:37:53.233 TradeProcessor (EURUSD,H1) Comment to the operation: RQ 0 17:37:53.233 TradeProcessor (EURUSD,H1) Request ID: 1
Having analyzed other parameters of the handler such as structures of the request and response, you can get additional information about the request at the first call.
The second call concerns adding the order to the list of open orders (Fig. 2).
Fig.2. The type field is equal to TRADE_TRANSACTION_ORDER_ADD
The block "Transaction" is the only one we need in the log.
MJ 0 17:41:12.280 TradeProcessor (EURUSD,H1) ---===Transaction===--- JN 0 17:41:12.280 TradeProcessor (EURUSD,H1) Ticket of the deal: 0 FG 0 17:41:12.280 TradeProcessor (EURUSD,H1) Type of the deal: DEAL_TYPE_BUY LM 0 17:41:12.280 TradeProcessor (EURUSD,H1) Ticket of the order: 22535869 LI 0 17:41:12.280 TradeProcessor (EURUSD,H1) Status of the order: ORDER_STATE_STARTED LP 0 17:41:12.280 TradeProcessor (EURUSD,H1) Type of the order: ORDER_TYPE_BUY QN 0 17:41:12.280 TradeProcessor (EURUSD,H1) Price: 1.3137 PD 0 17:41:12.280 TradeProcessor (EURUSD,H1) Level of Stop Loss: 0.0000 NL 0 17:41:12.280 TradeProcessor (EURUSD,H1) Level of Take Profit: 0.0000 PG 0 17:41:12.280 TradeProcessor (EURUSD,H1) Price that triggers the Stop Limit order: 0.0000 DL 0 17:41:12.280 TradeProcessor (EURUSD,H1) Trade symbol: EURUSD JK 0 17:41:12.280 TradeProcessor (EURUSD,H1) Pending order expiration time: 1970.01.01 00:00 QD 0 17:41:12.280 TradeProcessor (EURUSD,H1) Order expiration type: ORDER_TIME_GTC IQ 0 17:41:12.280 TradeProcessor (EURUSD,H1) Type of the trade transaction: TRADE_TRANSACTION_ORDER_ADD PL 0 17:41:12.280 TradeProcessor (EURUSD,H1) Volume in lots: 0.11
The order, as we can see, has already received its ticket and other parameters (symbol, price and volume) and is included in the list of open orders.
The third call of the event handler is connected with the deletion of the order from the list of the open ones (Fig. 3).
Fig.3. The type field is equal to TRADE_TRANSACTION_ORDER_DELETE
The block "Transaction" is the only one we need in the log.
PF 0 17:52:36.722 TradeProcessor (EURUSD,H1) ---===Transaction===--- OE 0 17:52:36.722 TradeProcessor (EURUSD,H1) Ticket of the deal: 0 KL 0 17:52:36.722 TradeProcessor (EURUSD,H1) Type of the deal: DEAL_TYPE_BUY EH 0 17:52:36.722 TradeProcessor (EURUSD,H1) Ticket of the order: 22535869 QM 0 17:52:36.722 TradeProcessor (EURUSD,H1) Status of the order: ORDER_STATE_STARTED QK 0 17:52:36.722 TradeProcessor (EURUSD,H1) Type of the order: ORDER_TYPE_BUY HS 0 17:52:36.722 TradeProcessor (EURUSD,H1) Price: 1.3137 MH 0 17:52:36.722 TradeProcessor (EURUSD,H1) Level of Stop Loss: 0.0000 OP 0 17:52:36.722 TradeProcessor (EURUSD,H1) Level of Take Profit: 0.0000 EJ 0 17:52:36.722 TradeProcessor (EURUSD,H1) Price that triggers the Stop Limit order: 0.0000 IH 0 17:52:36.722 TradeProcessor (EURUSD,H1) Trade symbol: EURUSD KP 0 17:52:36.722 TradeProcessor (EURUSD,H1) Pending order expiration time: 1970.01.01 00:00 LO 0 17:52:36.722 TradeProcessor (EURUSD,H1) Order expiration type: ORDER_TIME_GTC HG 0 17:52:36.722 TradeProcessor (EURUSD,H1) Type of the trade transaction: TRADE_TRANSACTION_ORDER_DELETE CG 0 17:52:36.722 TradeProcessor (EURUSD,H1) Volume in lots: 0.11
There is no new information in this block except the type of transaction.
The handler is called for the fourth time when a new historical order appears in the history (Fig. 4).
Fig.4. The type field is equal to TRADE_TRANSACTION_HISTORY_ADD
We can get the relevant information from the block "Transaction".
QO 0 17:57:32.234 TradeProcessor (EURUSD,H1) ---===Transaction==--- RJ 0 17:57:32.234 TradeProcessor (EURUSD,H1) Ticket of the deal: 0 NS 0 17:57:32.234 TradeProcessor (EURUSD,H1) Type of the deal: DEAL_TYPE_BUY DQ 0 17:57:32.234 TradeProcessor (EURUSD,H1) Ticket of the order: 22535869 EH 0 17:57:32.234 TradeProcessor (EURUSD,H1) Status of the order: ORDER_STATE_FILLED RL 0 17:57:32.234 TradeProcessor (EURUSD,H1) Type of the order: ORDER_TYPE_BUY KJ 0 17:57:32.234 TradeProcessor (EURUSD,H1) Price: 1.3137 NO 0 17:57:32.234 TradeProcessor (EURUSD,H1) Level of Stop Loss: 0.0000 PI 0 17:57:32.234 TradeProcessor (EURUSD,H1) Level of Take Profit: 0.0000 FS 0 17:57:32.234 TradeProcessor (EURUSD,H1) Price that triggers the Stop Limit order: 0.0000 JS 0 17:57:32.234 TradeProcessor (EURUSD,H1) Trade symbol: EURUSD LG 0 17:57:32.234 TradeProcessor (EURUSD,H1) Pending order expiration time: 1970.01.01 00:00 KP 0 17:57:32.234 TradeProcessor (EURUSD,H1) Order expiration type: ORDER_TIME_GTC OL 0 17:57:32.234 TradeProcessor (EURUSD,H1) Type of the trade transaction: TRADE_TRANSACTION_HISTORY_ADD JH 0 17:57:32.234 TradeProcessor (EURUSD,H1) Volume in lots: 0.00
At this stage, we can see that the order has been executed.
Finally, the last (the fifth) call takes place when a deal is added to the history (Fig. 5).
Fig.5. The type field is equal to TRADE_TRANSACTION_DEAL_ADD
In the log, again, we are interested only in the block "Transaction".
OE 0 17:59:40.718 TradeProcessor (EURUSD,H1) ---===Transaction===--- MS 0 17:59:40.718 TradeProcessor (EURUSD,H1) Ticket of the deal: 15258202 RJ 0 17:59:40.718 TradeProcessor (EURUSD,H1) Type of the deal: DEAL_TYPE_BUY HN 0 17:59:40.718 TradeProcessor (EURUSD,H1) Ticket of the order: 22535869 LK 0 17:59:40.718 TradeProcessor (EURUSD,H1) Status of the order: ORDER_STATE_STARTED LE 0 17:59:40.718 TradeProcessor (EURUSD,H1) Type of the order: ORDER_TYPE_BUY MM 0 17:59:40.718 TradeProcessor (EURUSD,H1) Price: 1.3137 PF 0 17:59:40.718 TradeProcessor (EURUSD,H1) Level of Stop Loss: 0.0000 NN 0 17:59:40.718 TradeProcessor (EURUSD,H1) Level of Take Profit: 0.0000 PI 0 17:59:40.718 TradeProcessor (EURUSD,H1) Price that triggers the Stop Limit order: 0.0000 DJ 0 17:59:40.718 TradeProcessor (EURUSD,H1) Trade symbol: EURUSD JM 0 17:59:40.718 TradeProcessor (EURUSD,H1) Pending order expiration time: 1970.01.01 00:00 QI 0 17:59:40.718 TradeProcessor (EURUSD,H1) Order expiration type: ORDER_TIME_GTC CK 0 17:59:40.718 TradeProcessor (EURUSD,H1) Type of the trade transaction: TRADE_TRANSACTION_DEAL_ADD RQ 0 17:59:40.718 TradeProcessor (EURUSD,H1) Volume in lots: 0.11
The important string in this block is the ticket of the deal.
I am going to present a scheme of the transaction. For the positions there will be only two of them. The first one looks like the one on Fig. 6.
Fig.6. The first scheme of the transaction process
All trade operations connected with processing of positions take place in accordance with this scheme. The only exception here is the operation of modifying a position. The last operation includes processing of the following two transactions (Fig. 7).
Fig.7. The second scheme of the transaction process
So, modification of a position cannot be traced in the history of deals and orders.
That is pretty much all about positions.
3. Processing of Pending Orders
With regard to pending orders, it should be noted that operations with them take fewer transactions. At the same time, there are more combinations of transaction types when working with orders.
To modify an order, the handler is called twice, similar to modifying a position. Placing and deleting an order takes three calls. The TradeTransaction event occurs four times at deleting the order or its execution.
Now we are going to place a pending order. We need to launch the Expert in the debug mode on any of the charts in MetaTrader 5 terminal again.
The first call of the handler will be connected with the request (Fig. 8).
Fig.8. The type field is equal to TRADE_TRANSACTION_REQUEST
The log will contain the following entries:
IO 0 18:13:33.195 TradeProcessor (EURUSD,H1) ---===Transaction===--- NK 0 18:13:33.195 TradeProcessor (EURUSD,H1) Ticket of the deal: 0 RR 0 18:13:33.195 TradeProcessor (EURUSD,H1) Type of the deal: DEAL_TYPE_BUY DE 0 18:13:33.195 TradeProcessor (EURUSD,H1) Ticket of the order: 0 JS 0 18:13:33.195 TradeProcessor (EURUSD,H1) Status of the order: ORDER_STATE_STARTED JN 0 18:13:33.195 TradeProcessor (EURUSD,H1) Type of the order: ORDER_TYPE_BUY FD 0 18:13:33.195 TradeProcessor (EURUSD,H1) Price: 0.0000 FN 0 18:13:33.195 TradeProcessor (EURUSD,H1) Level of Stop Loss: 0.0000 HF 0 18:13:33.195 TradeProcessor (EURUSD,H1) Level of Take Profit: 0.0000 FQ 0 18:13:33.195 TradeProcessor (EURUSD,H1) Price that triggers the Stop Limit order: 0.0000 RR 0 18:13:33.195 TradeProcessor (EURUSD,H1) Trade symbol: HD 0 18:13:33.195 TradeProcessor (EURUSD,H1) Pending order expiration time: 1970.01.01 00:00 GS 0 18:13:33.195 TradeProcessor (EURUSD,H1) Order expiration type: ORDER_TIME_GTC DN 0 18:13:33.195 TradeProcessor (EURUSD,H1) Type of the trade transaction: TRADE_TRANSACTION_REQUEST FK 0 18:13:33.195 TradeProcessor (EURUSD,H1) Volume in lots: 0.00 NS 0 18:13:33.195 TradeProcessor (EURUSD,H1) QG 0 18:13:33.195 TradeProcessor (EURUSD,H1) ---===Request==--- IQ 0 18:13:33.195 TradeProcessor (EURUSD,H1) Type of the trade operation: TRADE_ACTION_PENDING OE 0 18:13:33.195 TradeProcessor (EURUSD,H1) Order comment: PQ 0 18:13:33.195 TradeProcessor (EURUSD,H1) Deviation from the requested price: 0 QS 0 18:13:33.195 TradeProcessor (EURUSD,H1) Order expiration time: 1970.01.01 00:00 FI 0 18:13:33.195 TradeProcessor (EURUSD,H1) Magic number of the EA: 0 CM 0 18:13:33.195 TradeProcessor (EURUSD,H1) Ticket of the order: 22535983 PK 0 18:13:33.195 TradeProcessor (EURUSD,H1) Price: 1.6500 KR 0 18:13:33.195 TradeProcessor (EURUSD,H1) Stop Loss level of the order: 0.0000 OI 0 18:13:33.195 TradeProcessor (EURUSD,H1) Take Profit level of the order: 0.0000 QK 0 18:13:33.195 TradeProcessor (EURUSD,H1) StopLimit level of the order: 0.0000 QQ 0 18:13:33.195 TradeProcessor (EURUSD,H1) Trade symbol: GBPUSD RD 0 18:13:33.195 TradeProcessor (EURUSD,H1) Type of the order: ORDER_TYPE_BUY_LIMIT LS 0 18:13:33.195 TradeProcessor (EURUSD,H1) Order execution type: ORDER_FILLING_RETURN MN 0 18:13:33.195 TradeProcessor (EURUSD,H1) Order expiration type: ORDER_TIME_GTC IK 0 18:13:33.195 TradeProcessor (EURUSD,H1) Volume in lots: 0.14 NS 0 18:13:33.195 TradeProcessor (EURUSD,H1) CD 0 18:13:33.195 TradeProcessor (EURUSD,H1) ---===Response===--- RQ 0 18:13:33.195 TradeProcessor (EURUSD,H1) Code of the operation result: 10009 JI 0 18:13:33.195 TradeProcessor (EURUSD,H1) Ticket of the deal: 0 GM 0 18:13:33.195 TradeProcessor (EURUSD,H1) Ticket of the order: 22535983 LF 0 18:13:33.195 TradeProcessor (EURUSD,H1) Volume of the deal: 0.14 JN 0 18:13:33.195 TradeProcessor (EURUSD,H1) Price of the deal: 0.0000 MK 0 18:13:33.195 TradeProcessor (EURUSD,H1) Bid: 0.0000 CM 0 18:13:33.195 TradeProcessor (EURUSD,H1) Ask: 0.0000 IG 0 18:13:33.195 TradeProcessor (EURUSD,H1) Comment to the operation: DQ 0 18:13:33.195 TradeProcessor (EURUSD,H1) Request ID: 1
The second call of the handler will add the order to the list of the open ones (Fig. 9).
Fig.9. The type field is equal to TRADE_TRANSACTION_ORDER_ADDED
In the log we need to see only the "Transaction" block.
HJ 0 18:17:02.886 TradeProcessor (EURUSD,H1) ---===Transaction===--- GQ 0 18:17:02.886 TradeProcessor (EURUSD,H1) Ticket of the deal: 0 CH 0 18:17:02.886 TradeProcessor (EURUSD,H1) Type of the deal: DEAL_TYPE_BUY RL 0 18:17:02.886 TradeProcessor (EURUSD,H1) Ticket of the order: 22535983 II 0 18:17:02.886 TradeProcessor (EURUSD,H1) Status of the order: ORDER_STATE_STARTED OG 0 18:17:02.886 TradeProcessor (EURUSD,H1) Type of the order: ORDER_TYPE_BUY_LIMIT GL 0 18:17:02.886 TradeProcessor (EURUSD,H1) Price: 1.6500 IE 0 18:17:02.886 TradeProcessor (EURUSD,H1) Level of Stop Loss: 0.0000 CO 0 18:17:02.886 TradeProcessor (EURUSD,H1) Level of Take Profit: 0.0000 IF 0 18:17:02.886 TradeProcessor (EURUSD,H1) Price that triggers the Stop Limit order: 0.0000 PL 0 18:17:02.886 TradeProcessor (EURUSD,H1) Trade symbol: GBPUSD OL 0 18:17:02.886 TradeProcessor (EURUSD,H1) Pending order expiration time: 1970.01.01 00:00 HJ 0 18:17:02.886 TradeProcessor (EURUSD,H1) Order expiration type: ORDER_TIME_GTC LF 0 18:17:02.886 TradeProcessor (EURUSD,H1) Type of the trade transaction: TRADE_TRANSACTION_ORDER_ADD FR 0 18:17:02.886 TradeProcessor (EURUSD,H1) Volume in lots: 0.14
The third call of the handler will renew the data according to the placed order (Fig. 10).
In particular, the order status will receive the value of ORDER_STATE_PLACED.
Fig.10. The type field is equal to TRADE_TRANSACTION_ORDER_UPDATE
In the log, we need to see the records for the "Transaction" block.
HS 0 18:21:27.004 TradeProcessor (EURUSD,H1) ---===Transaction==--- GF 0 18:21:27.004 TradeProcessor (EURUSD,H1) Ticket of the deal: 0 CO 0 18:21:27.004 TradeProcessor (EURUSD,H1) Type of the deal: DEAL_TYPE_BUY RE 0 18:21:27.004 TradeProcessor (EURUSD,H1) Ticket of the order: 22535983 KM 0 18:21:27.004 TradeProcessor (EURUSD,H1) Status of the order: ORDER_STATE_PLACED QH 0 18:21:27.004 TradeProcessor (EURUSD,H1) Type of the order: ORDER_TYPE_BUY_LIMIT EG 0 18:21:27.004 TradeProcessor (EURUSD,H1) Price: 1.6500 GL 0 18:21:27.004 TradeProcessor (EURUSD,H1) Level of Stop Loss: 0.0000 ED 0 18:21:27.004 TradeProcessor (EURUSD,H1) Level of Take Profit: 0.0000 GO 0 18:21:27.004 TradeProcessor (EURUSD,H1) Price that triggers the Stop Limit order: 0.0000 RE 0 18:21:27.004 TradeProcessor (EURUSD,H1) Trade symbol: GBPUSD QS 0 18:21:27.004 TradeProcessor (EURUSD,H1) Pending order expiration time: 1970.01.01 00:00 JS 0 18:21:27.004 TradeProcessor (EURUSD,H1) Order expiration type: ORDER_TIME_GTC RD 0 18:21:27.004 TradeProcessor (EURUSD,H1) Type of the trade transaction: TRADE_TRANSACTION_ORDER_UPDATE JK 0 18:21:27.004 TradeProcessor (EURUSD,H1) Volume in lots: 0.14
The most important string here is the status of the order.
Unlike processing of positions, processing of pending orders cannot be implemented by a scheme. Every trade operation connected with a pending order will be unique from the point of view of trading transactions types.
Placing a pending order will take three transactions (Fig. 11).
Fig.11. Transactions, processing placement of a pending order
Modifying a pending order will generate two transactions (Fig. 12).
Fig.12. Transactions, processing modifying of a pending order
If the pending order is to be deleted, then the OnTradeTransaction() handler will be called four times (Fig. 13).
Fig.13. Transactions, processing deletion of a pending order
Deletion of a pending order is defined by the following scheme (Fig. 14).
Fig.14. Transactions, processing a deletion of a pending order
Triggering of a pending order, the last trade operation, will induce four different transactions (Fig. 15).
Fig.15. Transactions, processing activation of a pending order
I am not going to bring the log entries for every combination of transactions. If the reader feels so inclined, they can study them by executing the code.
4. Universal Handler
Let us take a look at the program that can work with the TradeTransaction event through the eyes of the end user. The end user is very likely to need a program that can flawlessly work both with the orders and positions. A programmer must write the code for OnTradeTransaction() the way that will let it identify all transactions and their combinations regardless of what was processed - a position or an order. Ideally, the program should be able to indicate what operation was executed upon completion of processing the series of transactions.
In the example below, a sequential processing of transaction is used. The developer of MQL5 states the following though:
So, if the requirement is to write a program working close to the ideal, you can improve the suggested example and make processing of transactions independent of the arrival order of transactions.
In general, positions and orders can have common types of transactions. There are 11 types of transactions. Only four out of that number have something to do with trading from the terminal:
- TRADE_TRANSACTION_DEAL_UPDATE;
- TRADE_TRANSACTION_DEAL_DELETE;
- TRADE_TRANSACTION_HISTORY_UPDATE;
- TRADE_TRANSACTION_HISTORY_DELETE.
We are not going to discuss them in this article. These types, according to the developer, were designed for extending functionality on the trading server side. I must admit that I have not previously dealt with such types before.
That leaves us with seven full-featured types that get processed in the OnTradeTransaction() most often.
In the body of the handler, the segment, defining the current transaction type will have an important role.
//--- ========== Types of transaction [START] switch(trans_type) { //--- 1) if it is a request case TRADE_TRANSACTION_REQUEST: { //--- break; } //--- 2) if it is an addition of a new open order case TRADE_TRANSACTION_ORDER_ADD: { //--- break; } //--- 3) if it is a deletion of an order from the list of open ones case TRADE_TRANSACTION_ORDER_DELETE: { //--- break; } //--- 4) if it is an addition of a new order to the history case TRADE_TRANSACTION_HISTORY_ADD: { //--- break; } //--- 5) if it is an addition of a deal to history case TRADE_TRANSACTION_DEAL_ADD: { //--- break; } //--- 6) if it is a modification of a position case TRADE_TRANSACTION_POSITION: { //--- break; } //--- 7) if it is a modification of an open order case TRADE_TRANSACTION_ORDER_UPDATE: { //--- break; } } //--- ========== Types of transactions [END]
We will try to define what trade operation we are processing by the current transaction type. To find out what we are working with - a position or an order, we shall delegate memorizing the type of the trade operation to the case-module of processing the request.
The module itself will look as follows:
//--- 1) if it is a request case TRADE_TRANSACTION_REQUEST: { //--- last_action=request.action; string action_str; //--- what is the request for? switch(last_action) { //--- а) on market case TRADE_ACTION_DEAL: { action_str="place a market order"; trade_obj=TRADE_OBJ_POSITION; break; } //--- б) place a pending order case TRADE_ACTION_PENDING: { action_str="place a pending order"; trade_obj=TRADE_OBJ_ORDER; break; } //--- в) modify position case TRADE_ACTION_SLTP: { trade_obj=TRADE_OBJ_POSITION; //--- StringConcatenate(action_str,request.symbol,": modify the levels of Stop Loss", " and Take Profit"); //--- break; } //--- г) modify order case TRADE_ACTION_MODIFY: { action_str="modify parameters of the pending order"; trade_obj=TRADE_OBJ_ORDER; break; } //--- д) delete order case TRADE_ACTION_REMOVE: { action_str="delete pending order"; trade_obj=TRADE_OBJ_ORDER; break; } } //--- if(InpIsLogging) Print("Request received: "+action_str); //--- break; }
Changing a few variables is not difficult in this case.
static ENUM_TRADE_REQUEST_ACTIONS last_action; // market operation at the first pass
The last_action variable will memorize why the event handler was launched at all.
static ENUM_TRADE_OBJ trade_obj; // specifies the trade object at the first pass
The variable trade_obj will keep in memory what was processed - a position or an order. For that we shall create the ENUM_TRADE_OBJ enumeration.
After that, we are going to proceed to the module which will process transactions of the TRADE_TRANSACTION_ORDER_ADD type:
//--- 2) if it is an addition of a new open order case TRADE_TRANSACTION_ORDER_ADD: { if(InpIsLogging) { if(trade_obj==TRADE_OBJ_POSITION) Print("Open a new market order: "+ EnumToString(trans.order_type)); //--- else if(trade_obj==TRADE_OBJ_ORDER) Print("Place a new pending order: "+ EnumToString(trans.order_type)); } //--- break; }
This module is rather simple. Since position was processed at the first step, a log entry "Open a new market order" will appear at the current one, otherwise "Place a new pending order". There are no more actions other than informative in this block.
Now it is the turn of the third module which processes the TRADE_TRANSACTION_ORDER_DELETE type:
//--- 3) if it is a deletion of an order from the list of open ones case TRADE_TRANSACTION_ORDER_DELETE: { if(InpIsLogging) PrintFormat("Order deleted from the list of open ones: #%d, "+ EnumToString(trans.order_type),trans.order); //--- break; }
This module also has only an informative role.
The fourth case-module processes the TRADE_TRANSACTION_HISTORY_ADD type:
//--- 4) if it is an addition of a new order to the history case TRADE_TRANSACTION_HISTORY_ADD: { if(InpIsLogging) PrintFormat("Order added to the history: #%d, "+ EnumToString(trans.order_type),trans.order); //--- if a pending order is being processed if(trade_obj==TRADE_OBJ_ORDER) { //--- if it is the third pass if(gTransCnt==2) { //--- if the order was canceled, check the deals datetime now=TimeCurrent(); //--- request the history of orders and deals HistorySelect(now-PeriodSeconds(PERIOD_H1),now); //--- attempt to find a deal for the order CDealInfo myDealInfo; int all_deals=HistoryDealsTotal(); //--- bool is_found=false; for(int deal_idx=all_deals;deal_idx>=0;deal_idx--) if(myDealInfo.SelectByIndex(deal_idx)) if(myDealInfo.Order()==trans.order) is_found=true; //--- if the deal was not found if(!is_found) { is_to_reset_cnt=true; //--- PrintFormat("Order canceled: #%d",trans.order); } } //--- if it is the fourth pass if(gTransCnt==3) { is_to_reset_cnt=true; PrintFormat("Order deleted: #%d",trans.order); } } //--- break; }
In addition to the record that the order was added to the history, this module carries out a check if we have been working with a pending order initially. In case we have done, we need to find out what number the current pass of the handler has. The thing is that this type of transaction can appear at the third pass when working with the order, if a pending order was canceled. At the fourth pass this type can appear when a pending order was deleted.
At the strings of the module checking the third pass, we need to refer to the history of deals once again. If a deal for the current order is not found, then we will consider such an order canceled.
The fifth case-module processes the TRADE_TRANSACTION_DEAL_ADD type. It is the largest block of the program by the size of the strings.
The deal is checked in this block. It is important to choose a deal by the ticket to get access to its properties. A deal type can provide information if position was open, closed etc. Information about triggering of a pending order can be retrieved there too. It is the only case when a pending order could generate a deal in the work context of the event handler TradeTransaction.
//--- 5) if it is an addition of a deal to history case TRADE_TRANSACTION_DEAL_ADD: { is_to_reset_cnt=true; //--- ulong deal_ticket=trans.deal; ENUM_DEAL_TYPE deal_type=trans.deal_type; //--- if(InpIsLogging) PrintFormat("Deal added to history: #%d, "+EnumToString(deal_type),deal_ticket); if(deal_ticket>0) { datetime now=TimeCurrent(); //--- request the history of orders and deals HistorySelect(now-PeriodSeconds(PERIOD_H1),now); //--- select a deal by the ticket if(HistoryDealSelect(deal_ticket)) { //--- check the deal CDealInfo myDealInfo; myDealInfo.Ticket(deal_ticket); long order=myDealInfo.Order(); //--- parameters of the deal ENUM_DEAL_ENTRY deal_entry=myDealInfo.Entry(); double deal_vol=0.; //--- if(myDealInfo.InfoDouble(DEAL_VOLUME,deal_vol)) if(myDealInfo.InfoString(DEAL_SYMBOL,deal_symbol)) { //--- position CPositionInfo myPos; double pos_vol=WRONG_VALUE; //--- if(myPos.Select(deal_symbol)) pos_vol=myPos.Volume(); //--- if the market was entered if(deal_entry==DEAL_ENTRY_IN) { //--- 1) opening of a position if(deal_vol==pos_vol) PrintFormat("\n%s: new position opened",deal_symbol); //--- 2) addition to the open position else if(deal_vol<pos_vol) PrintFormat("\n%s: addition to the current position",deal_symbol); } //--- if the market was exited else if(deal_entry==DEAL_ENTRY_OUT) { if(deal_vol>0.0) { //--- 1) closure of a position if(pos_vol==WRONG_VALUE) PrintFormat("\n%s: position closed",deal_symbol); //--- 2) partial closure of the open position else if(pos_vol>0.0) PrintFormat("\n%s: partial closing of the current position",deal_symbol); } } //--- if position was reversed else if(deal_entry==DEAL_ENTRY_INOUT) { if(deal_vol>0.0) if(pos_vol>0.0) PrintFormat("\n%s: position reversal",deal_symbol); } } //--- order activation if(trade_obj==TRADE_OBJ_ORDER) PrintFormat("Pending order activation: %d",order); } } //--- break; }
Transaction type TRADE_TRANSACTION_POSITION is unique and is processed only when a position gets modified:
//--- 6) if it is a modification of a position case TRADE_TRANSACTION_POSITION: { is_to_reset_cnt=true; //--- PrintFormat("Modification of a position: %s",deal_symbol); //--- if(InpIsLogging) { PrintFormat("New price of stop loss: %0."+ IntegerToString(_Digits)+"f",trans.price_sl); PrintFormat("New price of take profit: %0."+ IntegerToString(_Digits)+"f",trans.price_tp); } //--- break; }
The last case-module gets enabled at processing the TRADE_TRANSACTION_ORDER_UPDATE type.
This type appears only for work with a pending order. It launches at triggering of any trade operation, concerning pending orders, however the stage may vary.
//--- 7) if it is a modification of an open order case TRADE_TRANSACTION_ORDER_UPDATE: { //--- if it was the first pass if(gTransCnt==0) { trade_obj=TRADE_OBJ_ORDER; PrintFormat("Canceling the order: #%d",trans.order); } //--- if it was the second pass if(gTransCnt==1) { //--- if it is an order modification if(last_action==TRADE_ACTION_MODIFY) { PrintFormat("Pending order modified: #%d",trans.order); //--- clear counter is_to_reset_cnt=true; } //--- if it is deletion of the order if(last_action==TRADE_ACTION_REMOVE) { PrintFormat("Delete pending order: #%d",trans.order); } } //--- if it was the third pass if(gTransCnt==2) { PrintFormat("A new pending order was placed: #%d, "+ EnumToString(trans.order_type),trans.order); //--- clear counter is_to_reset_cnt=true; } //--- break; }
Summing up, if this type appeared at the first triggering of OnTradeTransaction(), then the order was either canceled or executed.
If the type appeared at the second launch of the event handler, then the order was either deleted or modified. To find out what exactly the order resulted in, refer to the static variable last_action which contains the data on the last trading operation.
The third launch of the event handler is the last case when this type can appear. The third launch completes the procedure of placing a pending order.
A boolean variable is_to_reset_cnt is also used in the code. It has a role of a flag for clearing a counter of the passes of the OnTradeTransaction() handle.
That is pretty much all about processing of the TradeTransaction event. I would also add a pause in the beginning of the call for handler. It will minimize a chance of a deal or an order being delayed in getting to the history.
Conclusion
In this article I tried to illustrate how different trade operations can be worked with and how one can retrieve information about things happening in the terminal.
The greatest advantage of this approach is that the program can receive information about phased implementation of a trade operation. In my opinion, such an approach can be used for copying deals from one terminal to another.