Orders , positions and deals . Part II
[Versiunea romaneasca] [MQLmagazine.com in romana] [English edition]
In the precedent article we talked about how the positional system differs from the old order system used in MT4. This article will continue with the functions that deal with the new system.
As you probably knew , as a MetaTrader programmer, the only element that allows the trader to know his profits, losses and exposure per each instrument is the order. That is, all begins with the selection and analysis of every order, be it a current order, or a past order, selectable with OrderSelect(). Sure, the total number of orders is retrieved by OrdersTotal() and OrdersHistoryTotal(). Since MT4 has a very accentuated monoasset orientation, given almost entirely by the monoasset limitation of the Strategy Tester, there was no need for position functions, since traders had limited possibilities to create multiasset EAs.
We begin with what was already known from MQL4, and that is working with orders. First of all, the entire plethora of order functions from MQL4 collapses to a few:
- OrdersTotal(), that retrieves the number of orders;
- OrderSelect(), the usual order selection function;
- OrderGetDouble(), OrderGetInteger(), OrderGetString(), to retrieve informations about orders;
- OrderGetTicket(), which is an improved version of OrderTicket() from MQL4; OrderTicket() from MQL4 retrieves the ticket of the selected order; whereas OrderGetTicket() from MQL5 receives the index of the order as parameter, and it also selects the order, making redundant further application of OrderSelect() upon the result.
- and of course, OrderSend() ; by the way, almost forgot, there is no OrderClose().
The OrdersTotal() function retrieves the number of orders; of course, only the number of the pending orders, since these are the only orders left; whatever order that is either market or pending that gets filled is incorporated into position and vanishes.
The OrderSelect() function has the following prototype:
1 2 3 4 | bool OrderSelect( ulong ticket // Order ticket uint timeout=0 // Timeout in milliseconds ); |
You can see major differences compared to the old MQL4’s OrderSelect(). That OrderSelect() was working with both tickets/index numbers, and for both current and past orders. Now the function requests the ticket number, which means that the old addressing mode
OrderSelect(index,SELECT_BY_POS,MODE_TRADES)
translates into
OrderGetTicket(index)
Retrieving the properties of the selected order is being done with OrderGetDouble(), OrderGetInteger(), OrderGetString(), following the general rule established by MQL5 , that functions that return properties of anything to be centralized in less functions, one function per each of the main data types.
Each of three functions has two prototypes, depending on how they are called. One prototype allows them to be called as functions, admitting as parameter only the property that is to be inquired; the other prototype allows them to be called as procedures, admitting as parameter the property, and also a variable of the same type included in the function’s name, that will receive the value of the received property. For instance, you may call OrderGetDouble() as
sl=OrderGetDouble(ORDER_SL);
or as
OrderGetDouble(ORDER_SL,sl);
(of course, with sl previously declared as double). Similar behaviour applies for the other two functions.
The property that is to be received as parameter by the OrderGetDouble function is of type ENUM_ORDER_PROPERTY_DOUBLE:
| Identifier | Description | Result type |
| ORDER_VOLUME_INITIAL | Order initial volume | double |
| ORDER_VOLUME_CURRENT | Order current volume | double |
| ORDER_PRICE_OPEN | Price specified in the order | double |
| ORDER_SL | Stop Loss value | double |
| ORDER_TP | Take Profit value | double |
| ORDER_PRICE_CURRENT | The current price of the order symbol | double |
| ORDER_PRICE_STOPLIMIT | The Limit order price for the StopLimit order | double |
All of these are known things. I don’t see anything in the table that should raise eyebrows.
Similar case happens with OrderGetString(), whose admittable parameters are of type ENUM_ORDER_PROPERTY_STRING:
| Identifier | Description | Result type |
| ORDER_SYMBOL | Symbol of the order | string |
| ORDER_COMMENT | Order comment | string |
But the really interesting function is OrderGetInteger(). Because the analysis of OrderGetInteger() , as well as OrderSend(), reveal MetaQuotes’ intentions about MetaTrader5. The admittable parameters are of type ENUM_ORDER_PROPERTY_INTEGER:
| Identifier | Description | Result type |
| ORDER_TIME_SETUP | Order setup time | datetime |
| ORDER_TYPE | Order type | ENUM_ORDER_TYPE |
| ORDER_STATE | Order state | ENUM_ORDER_STATE |
| ORDER_TIME_EXPIRATION | Order expiration time | datetime |
| ORDER_TIME_DONE | Order execution or cancellation time | datetime |
| ORDER_TYPE_FILLING | Order filling type | ENUM_ORDER_TYPE_FILLING |
| ORDER_TYPE_TIME | Order lifetime | ENUM_ORDER_TYPE_TIME |
| ORDER_MAGIC | ID of an Expert Advisor that has placed the order (designed to ensure that each Expert Advisor places its own unique number) | long |
First of all, you can see that not all the types are int, but int is actually a family of types. The ENUM types are types that contain special numeric constants, which are integer constants, so compatible with int. So we have the ORDER_TYPE, that is pretty straightforward, similar to MQL4, with the difference that two new types are added (sure, constants differ from the ones from MQL4 as spelling, but the description is the same):
ENUM_ORDER_TYPE:
| Identifier | Description |
| ORDER_TYPE_BUY | Market Buy order |
| ORDER_TYPE_SELL | Market Sell order |
| ORDER_TYPE_BUY_LIMIT | Buy Limit pending order |
| ORDER_TYPE_SELL_LIMIT | Sell Limit pending order |
| ORDER_TYPE_BUY_STOP | Buy Stop pending order |
| ORDER_TYPE_SELL_STOP | Sell Stop pending order |
| ORDER_TYPE_BUY_STOP_LIMIT | Upon reaching the order price, a pending Buy Limit order is places at the StopLimit price (*New in MQL5*) |
| ORDER_TYPE_SELL_STOP_LIMIT | Upon reaching the order price, a pending Sell Limit order is places at the StopLimit price (*New in MQL5*) |
The ENUM_ORDER_TRADE_TYPE specifies the type of order in terms of expiration:
| Identifier | Description |
| ORDER_TIME_GTC | Good till cancel order |
| ORDER_TIME_DAY | Good till current trade day order |
| ORDER_TIME_SPECIFIED | Good till expired order |
The thing that really raises eyebrows here is ENUM_ORDER_TYPE_FILLING:
ENUM_ORDER_TYPE_FILLING:
| Identifier | Description |
| ORDER_FILLING_AON | The deal can be executed exclusively with a specified volume at the equal or better price than the order specified price. If there is no sufficient volume of offers on the order symbol, the order will not be executed. |
| ORDER_FILLING_CANCEL | An agreement to execute the deal with maximal market volume at the equal or better price than the order specified price. In this case an additional order for volume unfilled will not be placed. |
| ORDER_FILLING_RETURN | An agreement to execute the deal with maximal market volume at the equal or better price than the order specified price. In this case an additional order for volume unfilled will be placed. |
Why the need for filling parameter in a retail trading station ? This really tells us that MetaQuotes is really preparing MetaTrader5 for Level II trading. “Better price” ? Weren’t we specifying how worse could be the accepted price with the Slippage parameter ? If the execution is going to be controlled to give better fills to the trader, including by slashing order automatically into smaller pending pieces , it’s surely level II. Because in the context of Level I retail trading, this would be meaningless. Why? Because of the latency. Either the price is better and order is executed entirely at the new price, or the price is worse, and there is a tolerance to this, defined by slippage. If the new quote is a binary stream, 50% better, 50% worser, then you would get better prices in 50% of the situations, and not have a fill in the other 50%. Slippage means a higher tolerance, so you admit, that in probably 70% of the 50% worse, you are content with a worser execution. The new kind of execution tweak can only be a sign of level II. Translated into level I, it means that the broker allows you to have the order partially/totally filled at the indicated price (plus/minus the new slippage, called “deviation”). In level II there is no deviation. Level II trading does not admit worse price fills. Traders might get filled or partially filled or not filled at all. The new filling parameter will give brokers the ability to organize level II market for the retail trader. So you could have a pending Buy on the Bid, or a pending Sell on the Ask, and have these orders filled by other participants in the ECN. The level I trader, even if he writes the same program as the level II trader, will experience first fills when the Ask falls over the requested Buy price, or when the Bid falls over the requested Sell price, which means way later than the requirement for the Level II. However, on level II, execution is a market game, whereas on level I, execution is a broker obligation. These changes are confirmed by the constants within ENUM_ORDER_STATE, which describe accurately the state of an order, even the acceptance dynamics. This would have been a nonsense in MT4, where an order could be integrally executed or not at all:
ENUM_ORDER_STATE:
| Identifier | Description |
| ORDER_STATE_STARTED | Order checked, but not yet accepted by broker |
| ORDER_STATE_PLACED | Order accepted |
| ORDER_STATE_CANCELED | Order canceled by client |
| ORDER_STATE_PARTIAL | Order partially executed |
| ORDER_STATE_FILLED | Order fully executed |
| ORDER_STATE_REJECTED | Order rejected |
| ORDER_STATE_EXPIRED | Order expired |
An interesting note about ORDER_MAGIC. MetaQuotes still pushes the idea that the magic is to be used as expert id. Initially, MQL5 had this ORDER_EXPERT_ID instead of ORDER_MAGIC, but in the end they returned to the magic number concept. Sure, the magic can be used as expert id, but expert_id would have been a deceptive name, indicating that this would be the only approach of the magic. As you have seen in our article Object Oriented Trading : an OOP approach to trading there are plenty of ways to build useful magic numbers with intentional descriptions, that have a meaning for the EA itself and can be used as action triggers.
The OrderSend() function has the following prototype:
1 2 3 4 | bool OrderSend( MqlTradeRequest& request // query structure MqlTradeResult& result // structure of the answer ); |
It’s more elegant than MQL4’s OrderSend(). The function accepts the parameters in a MqlTradeRequest structure, and answers in a MqlTradeResult structure. Which is better compared to MQL4, where the function returns only the order ticket, and sets the error in an MT4 system variable, which is interogated by the trader with GetLastError(). Now the MqlTradeResult contains all the answers in the same structure.
MqlTradeRequest members:
| Field | Type | Description |
| action | ENUM_TRADE_REQUEST_ACTIONS | Trade operation type. |
| magic | ulong | Number to identify the expert or to specify a meaning to the order |
| order | ulong | Order ticket. It is used for modifying pending orders. |
| symbol | string | Symbol of the order. It is not necessary for order modification and position close operations. |
| volume | double | Requested order volume in lots. Note that the real volume of the deal will depend on the order execution type. |
| price | double | Price, reaching which the order must be executed. For the market orders of TRADE_ACTION_DEAL type. Not necessary to define a price. |
| stoplimit | double | The price value, at which the StopLimit pending order will be placed, when price reaches the price value (this condition is obligatory). Until then the pending order is not placed). |
| sl | double | Stop Loss price in case of the unfavorable price movement |
| tp | double | Take Profit price in the case of the favorable price movement |
| deviation | ulong | The maximal price deviation, specified in points |
| type | ENUM_ORDER_TYPE | Order type. |
| type_filling | ENUM_ORDER_TYPE_FILLING | Order execution type. |
| type_time | ENUM_ORDER_TYPE_TIME | Order execution time. |
| expiration | datetime | Order expiration time (for orders of ORDER_TIME_SPECIFIED type) |
| comment | string | Order comment |
As you can see, a novelty of MT5 is user manufactured ticket, unlike in MT4 where the server gives tickets to orders. Up to some extent, this ticket may play the role of a secondary magic, or even swap roles with the magic, however I’d suggest a usage of the timestamp as ticket.
MqlTradeResult members:
| Field | Type | Description |
| retcode | uint | Return code of a trade server |
| deal | ulong | Deal ticket, if a deal has been performed. It is available for a trade operation of TRADE_ACTION_DEAL type |
| order | ulong | Order ticket, if a ticket has been placed. It is available for a trade operation of TRADE_ACTION_PENDING type |
| volume | double | Deal volume, confirmed by broker. It depends on the order filling type |
| price | double | Deal price, confirmed by broker. It dependens on the deviation field of the trade request and/or on the trade operation |
| bid | double | The current market Bid price (requote price) |
| ask | double | The current market Ask price (requote price) |
| comment | string | The broker comment to operation (by default it is filled by the operation description) |
The most important field here is retcode , which is the server’s answer. This takes over from GetLastError() from MQL4. Based on this retcode we will write “Reliable” versions of the OrderSend(), as we all did in MQL4, treating more and more errors and resending orders. Follows deal and order, which show the positional system right from this structure. Orders that are sent as TRADE_ACTION_DEAL, i.e. market orders, don’t have a lifetime of their own, they live from the moment they are sent until they are accepted by the broker, meaning 1-2 seconds maximum (given that your broker really hates you). That’s why their ticket, specified by the trader in the MqlTraderRequest structure returns here on the deal field, not on the order field.
Now volume is a really interesting field. Volume behaves as the trade filling was set. For instance, order could not be filled entirely, and the volume says how much was filled. Question remains, when a pending order has a partial execution, how is this reported realtime ? OnTrade() has no parameters now to report fills. Sure, a pending order should not have a partial execution, in the Level I retail trading. Because the order was there when the market touched it on the market execution side. But if we’re on Level II, a lot of executions of the pending orders (which are, as I said above, near the market), executions are mostly partial. And OnTrade() has to be upgraded to answer realtime fills to the calling program.
The price field is useful, containing the confirmed price. In MQL4 you should have selected the order and retrieved the real opening price with OrderOpenPrice(), so this field shortens this search. I see that
bid and ask fields have more informational roles , as well as comment.
Positions
I’m not going to explain again how positions work, as I did this in the Part I of this article. First of the functions that has to be known is PositionsTotal() that returns the number of open positions. Similar to the OrderSelect() and OrderGetTicket() we have the position equivalents PositionSelect() and PositionGetSymbol(). They both select a position to work with it further.
The PositionSelect() function has the following prototype
1 2 3 4 | bool PositionSelect( string symbol // Symbol name uint timeout=0 // Timeout in milliseconds ); |
Really looks like OrderSelect(), doesn’t it ? Requests the symbol and a timeout. Similar case with PositionGetSymbol(), which admits as parameter the position’s index within current open positions.
Next, there are the position interogation functions, on the same model as the order ones: PositionGetDouble(), PositionGetInteger(), PositionGetString(). They have quite the same prototype, with the difference that the main parameter is of type ENUM_POSITION_PROPERTY_DOUBLE, ENUM_POSITION_PROPERTY_STRING or ENUM_POSITION_PROPERTY_INTEGER.
ENUM_POSITION_PROPERTY_DOUBLE
| Identifier | Description | Result type |
| POSITION_VOLUME | Position volume | double |
| POSITION_PRICE_OPEN | Position open price | double |
| POSITION_SL | Stop Loss level of opened position | double |
| POSITION_TP | Take Profit level of opened position | double |
| POSITION_PRICE_CURRENT | Current price of the position symbol | double |
| POSITION_COMMISSION | Commission | double |
| POSITION_SWAP | Cummulative swap | double |
| POSITION_PROFIT | Current profit | double |
Now you really have to understand why POSITION_PRICE_OPEN has to be averaged. Supposedly you open a 1 lot 1.3000 long and 3 lots at 1.3300 long. After you do this, your position will be 4 lots long. If market goes down at 1.3100, you will be in loss. If the position would be 4 lots long, opening at 1.3000, it would make no sense to be in loss at 1.3100. This is why the opening price has to averaged, for the results to have a correct interpretation.
ENUM_POSITION_PROPERTY_INTEGER
| Identifier | Description | Result type |
| POSITION_TIME | Position open time | datetime |
| POSITION_TYPE | Position type | ENUM_POSITION_TYPE (can be POSITION_TYPE_BUY or POSITION_TYPE_SELL) |
| POSITION_MAGIC | Position magic number | long |
ENUM_POSITION_PROPERTY_STRING
| Identifier | Description | Result type |
| POSITION_SYMBOL | Symbol of the position | string |
| POSITION_COMMENT | Position comment | string |
Out of these properties, the strangest seem to be POSITION_MAGIC and POSITION_COMMENT. These are order-related properties, because each order has its own meaning. Position itself is a result of orders. That means POSITION_MAGIC and POSITION_COMMENT coincide with the last order fill on that symbol.
History
MetaQuotes has included two ways to read history : the deals, meaning past position shifts , as well as the old MQL4 style, order history. Unlike history implementation in MQL4, now the period inquired has to be selected with HistorySelect(). The function receives two datetime parameters : date_from and date_to. This makes the function extremely useful in the case OnTrade() will not be updated and won’t report which order has been filled, because the mass of orders that has to be scanned becomes very small. Sure, from_date can be zero, so that would mean the history from the beginning. But it is way better to keep a record of the time when last scan was done, so that only new executions are scanned, maybe just the last execution. On the other hand, the to_date has an interesting meaning now, because there are two functions that deal with the current server time. One of them is known from MQL4, it is TimeCurrent(), that retrieves last known server time, and a new one, TimeTradeServer(), which is the same thing, but more accurate, it’s being calculated using local computer time, but in the same time zone as TimeCurrent().
Once selection has been done, we have : the total deals number , retrievable with HistoryDealsTotal(), the ticket per index, selectable with HistoryDealGetTicket() and the regular selection function HistoryDealSelect() ; these two have the same syntax as the OrderGetTicket() and OrderSelect(). The inquiry functions are made similar to the order functions, and we have HistoryDealGetDouble(), HistoryDealGetInteger(), HistoryDealGetString().
ENUM_DEAL_PROPERTY_DOUBLE
| Identifier | Description | Result type |
| DEAL_VOLUME | Deal volume | double |
| DEAL_PRICE | Deal price | double |
| DEAL_COMMISSION | Deal commission | double |
| DEAL_SWAP | Cumulative swap on close | double |
| DEAL_PROFIT | Deal profit | double |
ENUM_DEAL_PROPERTY_INTEGER
| Identifier | Description | Result type |
| DEAL_ORDER | Deal order number | long |
| DEAL_TIME | Deal time | datetime |
| DEAL_TYPE | Deal type | ENUM_DEAL_TYPE (can be DEAL_TYPE_ ..BUY, ..SELL, ..BALANCE, ..CREDIT, ..CHARGE, ..CORRECTION) |
| DEAL_ENTRY | Deal entry – entry in, entry out, reverse | ENUM_DEAL_ENTRY (can be DEAL_ENTRY_ ..IN, ..OUT, ..INOUT, ..STATE |
| DEAL_MAGIC | Deal magic number (see ORDER_MAGIC) | long |
ENUM_DEAL_PROPERTY_STRING
| Identifier | Description | Result type |
| DEAL_SYMBOL | Deal symbol | string |
| DEAL_COMMENT | Deal comment | string |
For instance, after executing the following trades (manual) on the demo account:
Sell EURUSD 0.1 lots @ 1.41548
Sell EURUSD 0.3 lots @ 1.41600
Buy EURUSD 0.5 lots @ 1.41647
Sell EURUSD 0.1 lots @ 1.41648
we ran the following script to see what’s in the deals history.
1 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 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 | //+------------------------------------------------------------------+ //| History Test.mq5 | //| Copyright Bogdan Caramalac | //| http://mqlmagazine.com | //+------------------------------------------------------------------+ #property copyright "Bogdan Caramalac" #property link "http://mqlmagazine.com" #property version "1.00" #property script_show_inputs //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ string EnumDealEntry(long e) { switch(e) { case DEAL_ENTRY_IN: return("DEAL_ENTRY_IN "); case DEAL_ENTRY_INOUT: return("DEAL_ENTRY_INOUT "); case DEAL_ENTRY_OUT: return("DEAL_ENTRY_OUT "); case DEAL_ENTRY_STATE: return("DEAL_ENTRY_STATE "); default: return("DEAL ENTRY UNKNOWN"); } } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ string EnumDealType(long t) { switch(t) { case DEAL_TYPE_BUY: return("DEAL_TYPE_BUY "); case DEAL_TYPE_SELL: return("DEAL_TYPE_SELL "); case DEAL_TYPE_BALANCE: return("DEAL_TYPE_BALANCE "); case DEAL_TYPE_CREDIT: return("DEAL_TYPE_CREDIT "); case DEAL_TYPE_CORRECTION: return("DEAL_TYPE_CORRECTION"); default: return("DEAL TYPE UNKNOWN "); } } //+------------------------------------------------------------------+ //| Script program start function | //+------------------------------------------------------------------+ void OnStart() { long ticket; int deals=HistoryDealsTotal(); datetime from_date=0; datetime to_date=TimeTradeServer(); HistorySelect(from_date,to_date); deals=HistoryDealsTotal(); Print("Total history deals=",HistoryDealsTotal()); for(int i=deals-1;i>=0;i--) { ticket=HistoryDealGetTicket(i); Print(" ",i," ",DoubleToString(HistoryDealGetDouble(ticket,DEAL_VOLUME),2)," ", EnumDealType(HistoryDealGetInteger(ticket,DEAL_TYPE))," ", DoubleToString(HistoryDealGetDouble(ticket,DEAL_PRICE),SymbolInfoInteger(Symbol(),SYMBOL_DIGITS))," ", EnumDealEntry(HistoryDealGetInteger(ticket,DEAL_ENTRY))," ", DoubleToString(HistoryDealGetDouble(ticket,DEAL_PROFIT),2)); } Print("Index Volume Type Price Entry Profit"); } //+------------------------------------------------------------------+ |
And the result is:

Sure, as I said above, the old style of order history has been kept. The functions working with order history are: HistoryOrdersTotal(), HistoryOrderSelect(), HistoryOrderGetTicket(), HistoryOrderGetDouble(), HistoryOrderGetInteger(), HistoryOrderGetString(). They all abide HistorySelect(), except for HistoryOrderSelect(). The parameters of the functions are exactly like at their counterparts working with current orders.
Regarding HistorySelect() using the datetime parameters date_from and date_to, it’s worth mentioning that this solves a very bad design decision that was implemented in MQL4/MT4:
the only orders that could be seen via code were the ones that were filtered in the ‘Account History’ tab, but the filtering relied on user settings! (right click -> ‘Last month’, ‘Last 3 months’, etc…).
The implementation in MQL5/MT5 brings control back to the program where it should be.