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:

Deals History Test

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.

One Response to “ Orders , positions and deals . Part II ”

  1. gordon on February 4, 2010 at 9:14 am

    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.