0% found this document useful (0 votes)
69 views11 pages

Orders Positions and Deals

Uploaded by

paseq
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
69 views11 pages

Orders Positions and Deals

Uploaded by

paseq
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 11

Automated Trading, Strategy Testing and Optimization Articles

Orders, Positions, and Deals in MetaTrader 5 [ru]


Rashid Umarov | 1 February, 2011

Trading terms
The ultimate goal of a trader is to extract profits through the means of trading operations on the financial markets. This article
describes the terms and processes of the MetaTrader 5 trading platform, the knowledge of which is necessary for a proper
understanding of the work of trade functions of the MQL5 language.

Orders — are the trade operation requests, received by the trading server, formulated in complience to the MetaTrader 5
platform requirements. If the request is incorrect, it will not appear on the trading platform in the form of an order. Orders
can be of immediate execution, such as to buy or sell a certain volume, at the current market price on a specified financial
instrument. Another type of orders - are pending orders, which contain an order to commit a trading operation under the
presence of certain condition. Pending orders may also contain a time restriction on their actions - the order expiration date.

The placed (pending) orders, that are waiting for the conditions of their execution or cancellation, are shown on the "Trade"
tab in the terminal. These orders can be modified or canceled. The placing, cancellation, and modification of orders is done
using the OrderSend() function. If the order was canceled or reached the order expiration time, or if the order was executed,
it moves into the orders history. Executed and canceled orders are shown in the "History" tab of the client terminal. Orders
from the history are not available for modification.

Deals - are the result of an execution of an order (a command to commit the deal). Each deal is based on a one particular
order, but a single order can generate a set of deals. For example, an order to buy 10 lots may be carried out by several
successive deals with partial filling. The deals are always stored in the trading history and cannot be modified. In the terminal,
the deals are displayed in the "History" tab.

Positions are the contracts, bought or sold on a financial instrument. A long position (Long) is formed as a result of buys in
anticipation of a price increase, a short position (Short) is the result of the sale of asset, in the anticipation of future price
decrease. For each account, for every financial instrument, there can be only one position. For each symbol, at any given
time, there can be only one open position - long or short.

The position volume may increase as a result of a new trading operation in the same direction. Meaning that the volume of
the long positions will be increased after the new buy (Buy deal), and will be decreased after the sale (Sell deal). The position
is closed, if the volume of commitments became equal to zero as a result of trading operation. Such operation is called closing
the position.

Note: Active orders and positions are always displayed on the "Trade" tab, and the deals and orders from the history are always
reflected in the "History" tab. The active order from the "Trade" tab should not be confused with the historical orders from the
"History" tab.

How the terminal receives and stores the trade information from the server
The terminal stores the trading history in a special base, and receives only the missing history of deals and completed orders on the
trading account, at each connection to the trading server. This is done to save up on traffic. When closing the MetaTrader 5 client
terminal or changing the current active account, the entire history is recorded on the hard disk and read from at the time of the next
launch of the terminal.

All databases are recorded to the disk in an encrypted form and the encryption key is dependent to the computer on which the
terminal is installed. This protects the user of the terminal against unauthorized access to his data, in case of copying.

During the connection to the account, the terminal loads the saved account base, with the history of the account, and sends the
trading server a request to synchronize its own database of history with the history of the account on the trading server. Further,
after a successful connection to the account, the trading server sends the terminal a report about the ongoing trading events, related
to this account.

Trading event are the following changes in the account:

withdrawal and balance operations;


charge of commissions, swaps and taxes;
placing, deletion and modification of orders;
the execution of deals based on orders;
opening and closing of positions;
change in the volume and direction of positions.

In the case of termination of the connection with the trading server, the terminal periodically attempts to reconnect. After the
reconnection with the server, the terminal requests all of the recent changes in the trading history to maintain the integrity of data
in its own database of the history.

The trading history, displayed in the "History" tab of the terminal, is taken from the base of the terminal history, and the changes of
the period, displayed in the terminal of history can only increase the range of the history, stored in this database. Decreasing the
period of the displayed history does not lead to a physical removal of the history from the base of the terminal.

This means that the installation of a shorter interval of the displayed history, does not reduce the depth of the stored trading history.
But if we specify a wider interval range, for the display in the "History" tab, then such an action could lead to a request, from the
trading server, of a more profound history, if the terminal's own base does not yet have the requested data for that period.

The general scheme of interaction between the terminal and the MetaTrader 5 trading server is demonstrated at following Figure:
The client terminal sends a synchronization request to its own trading history base, during the start of the terminal, during the
reconnection with the server after a connection failure, during a switch from one account to another, and during the direct request
for the missing trading history.

In its turn, the trading server independently, without any requests from the terminal, sends a client messages about the trading
events, taking place on the account: the changes of the state of orders and positions, the conduction of deals based on orders, the
charging of commissions, the balance and withdrawal of money, and so on.

Access to the trading history from the MQL5-program


The terminal can operate simultaneously with a number of indicators, scripts, and EAs, and all of these programs can request the
information they need about trading: orders, deals, and positions. The direct work of the mql5-program with the database of the
terminal is excluded, due to the considerations of overall stability, security and performance.

Each mql5-program by request receives for its work a "model" of the trading environment in its cache. A cache is a special area of
memory for a fast access to data. For example, before beginning processing the order, the order needed must be obtained in the
cache of the mql5-program. All of the further work, when referring to the order, will be made with the cached copy of that order.

The work with the positions, deals, and orders from the history is carried out in a similar way. The general scheme of obtaining the
trading information from the MQL5 program is shown at figure:

Before the data about the trading history becomes available for the processing of mql5-program, they must be requested from the
terminal database. After the request, the obtained data will be placed in its own cache of the mql5-program.

Note: the data in the cache is not automatically synchronized with the terminal database, and therefore, it must be constantly
updated to maintain an appropriate status of the data in the cache.

There is a possibility of consequences if the cache is used improperly.

if the data requested could not be obtained, the cache will be empty and will not contain the necessary data.
if the data in the cache required updates, but the updating was not requested, then working with such data can lead to
unpredictable results. For example, the data on the current position has not been updated, and the program does not know
anything about the open position for the given symbol and about the growing loss for it.

The function for working with the cache


The trading history may contain thousands of executed orders and deals that are not needed for current work of the mql5-program.
Therefore, the work with cache is built on the principle of requests the cache always contains the information that was uploaded at
the last connection to the database of the terminal. If you need to obtain the whole history of orders and deals, you need to
explicitly request it by specifying the desired interval.

For each type of information, an independent cache is formed. The data about the orders is stored in the order's cache, the
information about the positions is stored in the position's cache, the data on deals and orders is stored in the respective instances of
the cache's history.

Before requesting the information from the cache, it needs to be filled.

Note: Any request for filling the cache previously clears it, regardless of the result of the requests' execution.

The trading functions can be separated into two categories: the functions for filling the cache and the functions for reading the
information from the cache.

The function for filling the cache


For processing the trading history, it must first be obtained and located in the appropriate cache. Functions that form a cache can be
divided into two subgroups.

The function for filling the trading cache (active orders and positions):

The OrderSelect(ticket) - copies the active order by its ticket (from the terminal base) into the cache of the current order
for the further request of its properties using the OrderGetDouble(), OrderGetInteger() and OrderGetString() functions;
The OrderGetTicket(index) - copies, from the terminal base of the active order, by its index in the orders list of orders
terminal base into the cache of the current orders for further request to the properties using the OrderGetDouble(),
OrderGetInteger() and OrderGetString() functions. The total number of orders in the base of the terminal can be obtained
using the OrdersTotal() function;
The PositionSelect(symbol) - copies the open position by the symbol name (from the base of the terminal) into the cache
for the further request of its properties using the PositionGetDouble(), PositionGetInteger() and PositionGetString() functions;
The PositionGetSymbol(index) - copies the open position by its index in the position list (from the base of the terminal) of
the terminal base into the cache for the further request of its properties using the PositionGetDouble(), PositionGetInteger()
and PositionGetString() functions. The total number of positions in the base of the terminal can be obtained by the
PositionsTotal() function.

The function of filling the history cache:

The HistoryOrderSelect(ticket) - copies the history order by its ticket into the cache of the history orders (from the base of
the terminal) for the further calls to its properties by the HistoryOrderGetDouble(), HistoryOrderGetInteger() and
HistoryOrderGetString() functions;
The HistoryDealSelect(ticket) - copies the deal by its ticket into the deals cache (from the base of the terminal) for the
further calls to its properties by the HistoryDealGetDouble(), HistoryDealGetInteger() and HistoryDealGetString() functions ;

We need to separately consider the two functions, which affect the available, in the cache, trading history in general:

The HistorySelect(start, end) - fills the history cache with deals and orders for the specified interval of the server's time. From
the results of the execution of this function, depends the values, that are returned from HistoryDealsTotal() and
HistoryOrdersTotal();
The HistorySelectByPosition (position_ID) - fills the history cache with deals and orders, having the specified identifier
position. The result of the execution of this function, also affects the HistoryDealsTotal() and HistoryOrdersTotal().

OrderSelect and OrderGetTicket

The OrderSelect(ticket) and OrderGetTicket() general functions work in the same way, - they fill the cache of active orders with one
single order. The OrderSelect(ticket) is intended for the case where a ticket order is known in advance. The OrderGetTicket(), in
conjunction with OrdersTotal() allows for the examination of all of the available orders in the base terminal of orders.

After a call to any of these functions, the cache of the active orders contains the information of only one order, if the order is
successfully selected. Otherwise, there is nothing in the cache of active orders. The result of the execution of the function
OrdersTotal() does not change - it always returns the actual number of active orders in the base of the terminal, regardless of
whether the cache is full.
PositionSelect and PositionGetSymbol

Just like for the orders, these two functions also work in the same way for positions - they fill the cache of positions with a single
position. The PositionGetSymbol(index) requires the number in the list of the positions base, as a parameter, and the
PositionSelect(symbol) fills the cache based on the symbol name, on which the position is opened. The name of the symbol, in turn,
can be obtained by the PositionGetSymbol(index) function.

After performing any of these functions, the cache of positions contains data only on one position, if the function is executed
successfully. Otherwise, there is nothing in the cache of positions. The result of the execution of the PositionsTotal() function
does not depend on whether the cache is filled, - it always returns the actual number of open positions in the base terminal for
all symbols.

HistoryOrderSelect

The HistoryOrderSelect(ticket) choses into the cache the historical order from the base of the terminal by its ticket. The function is
intended for being used when the ticket of the required order is known in advance.

If the execution is successful, the cache will contain a single order, and the HistoryOrdersTotal() function return a single unit.
Otherwise, the cache of historical orders will be empty and the HistoryOrdersTotal() function will return a zero.

HistoryDealSelect

The HistoryDealSelect(ticket) selects the deal from the base terminal based by its ticket. The function is intended for being used
when the ticket of the deal is known in advance.

If the execution is successful, the cache will contain a single deal, and the HistoryDealsTotal() function will return 1. Otherwise, the
cache of deal will be empty and the HistoryDealsTotal() function will return a zero.

The function for obtaining information from the cache


Before requesting information about the properties of the position, deal or order, it is necessary to update the corresponding cache
of the mql5-program. This is due to the fact that the requested information may have already been updated, and this means that
the copy, stored in the cache, is already outdated.

Orders
In order to obtain information on active orders, it must first be copied into the cache of active orders of one of the two
functions: OrderGetTicket() or OrderSelect(). It is for the order, which is stored in the cache, that the property values will be
given out, when the corresponding functions are called:
1. OrderGetDouble(type_property)
2. OrderGetInteger(type_property)
3. OrderGetString(type_property)

These functions obtain all of the data from the cache, therefore, in order to guarantee the obtainment of accurate data for the
order, it is recommended to call the function that fills the cache.

Positions
For obtaining the information about a position, it must be previously selected and copied into the cache, using one of the two
functions: PositionGetSymbol or PositionSelect. It is from this cache, that the property values of position will be given out,
when the corresponding functions are called:

1. PositionGetDouble(type_property)
2. PositionGetInteger(type_property)
3. PositionGetString(type_property)

Since these functions receive all of their data from the cache, then in order to guarantee the obtainment of accurate data for the
position, it is recommended to call the function that fills the cache of positions.

Historical orders
For obtaining information about an order from the history, it is required to first create the cache of historical orders, using one
of the three functions: HistorySelect(start, end), HistorySelectByPosition() or HistoryOrderSelect(ticket). If the implementation
is successful, the cache will store the number of orders, returned by the HistoryDealsTotal() function. The access to the
properties of these orders is carried out by each element on the ticket, using the appropriate function:

1. HistoryOrderGetDouble(ticket_order, type_property)
2. HistoryOrderGetInteger(ticket_order, type_property)
3. HistoryOrderGetString(ticket_order, type_property)

The ticket of the historical order can be found out using the HistoryOrderGetTicket(index) function, by its index in the cache of
historical orders. In order to have a guaranteed receipt of accurate data on the order, it is recommended to call the function that fills
the cache of historical orders.

Deals
For obtaining information about a specific deal in the history, it is needed to first create the deals cache, using one of the
three functions: HistorySelect (start, end), HistorySelectByPosition() or HistoryDealSelect (ticket). If the function
implementation is successful, the cache will store the deal in the amount returned by the function HistoryDealsTotal(). Access
to the properties of these deals is carried out, based on the ticket, using the appropriate functions:
1. HistoryDealGetDouble(ticket_deals, type_property)
2. HistoryDealGetInteger(ticket_deals, type_property)
3. HistoryDealGetString(ticket_deals, type_property)

The ticket of the deals can be obtained, using the HistoryDealGetTicket(index) function, by its index in the cache of deals. In order
to have a guaranteed receipt of accurate data about the deal, it is recommended to call the function that fills the deals cache.

The function for obtaining the ticket from the cache history

The HistoryOrderGetTicket (index) return the ticket of the historical order, by its index from the cache of the historical orders (not
from the terminal base!). The obtained ticket can be used in the HistoryOrderSelect (ticket) function, which clears the cache and
re-fill it with only one order, in the case of success. Recall that the value, returned from HistoryOrdersTotal() depends on the
number of orders in the cache.

The HistoryDealGetTicket(index) returns the ticket of the deal by its index from the cache of deals. The ticket of the deal can be
used by the function HistoryDealSelect(ticket), which clears the cache, and re-fills the cache with only one deal, in the case of
success. The value, returned by the HistoryDealsTotal() function depends on the number of deals in the cache.

Note: Before calling the HistoryOrderGetTicket (index) and HistoryDealGetTicket (index) functions, you need to fill the history
cache with historical orders and deals in a sufficient volume. To do this, use one of the functions: HistorySelect (start, end),
HistorySelectByPosition (position_ID), HistoryOrderSelect (ticket), and HistoryDealSelect (ticket).

Obtaining information using active orders


Checking the current active orders is a standard procedure. If it is needed to obtain information about some specific order, then,
knowing its ticket, this can be done using the function OrderSelect(ticket).

bool selected= OrderSelect (ticket);


if (selected)
  {
   double price_open= OrderGetDouble( ORDER_PRICE_OPEN );
   datetime time_setup= OrderGetInteger ( ORDER_TIME_SETUP );
   string symbol= OrderGetString( ORDER_SYMBOL );
   PrintFormat ( "Ордер #%d for %s was set at %s" ,ticket,symbol, TimeToString (time_setup));
  }
else
  {
   PrintFormat ( "Error selecting order with ticket %d. Error %d" ,ticket, GetLastError ());
  }

In the above example, it is assumed that the ticket of the order is known in advance, for example, it is obtained from global variable.
In general cases, however, the ticket information is absent, and thus we need to turn to the help of the OrderGetTicket(index)
function, which also selects one order and places it into the cache, but only the order number, in the list of current orders, needs to
be specified as the parameter.

The overall algorithm for working with orders (analogous with deals and positions) is the following:

1. Obtain the total number of orders, using the OrdersTotal() function;


2. Organize the loop through a search of all of the orders, by their indexes in the list;
3. Copy, one by one, each order into the cache, using the OrderGetTicket() function;
4. Obtain the correct order data from the cache, using the OrderGetDouble(), OrderGetInteger(), and OrderGetString() functions.
If needed, analyze the obtained data and take the appropriate actions.

Here is a brief example of such an algorithm:

input long my_magic = 555 ;


//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
void OnStart ()
  {
//--- obtain the total number of orders
   int orders= OrdersTotal ();
//--- scan the list of orders
   for ( int i= 0 ;i<orders;i++)
     {
      ResetLastError();
      //--- copy into the cache, the order by its number in the list
      ulong ticket= OrderGetTicket(i);
      if (ticket!= 0 ) // if the order was successfully copied into the cache, work with it
        {
         double price_open  = OrderGetDouble( ORDER_PRICE_OPEN );
         datetime time_setup= OrderGetInteger ( ORDER_TIME_SETUP );
         string symbol      = OrderGetString( ORDER_SYMBOL );
         long magic_number  = OrderGetInteger ( ORDER_MAGIC );
         if (magic_number== my_magic )
           {
            //  process the order with the specified ORDER_MAGIC
           }
         PrintFormat ( "Order #%d for %s was set out %s,
ORDER_MAGIC=%d" ,ticket,symbol, TimeToString (time_setup),magic_number);
        }
      else         // call OrderGetTicket() was completed unsuccessfully
        {
         PrintFormat ( "Error when obtaining an order from the list to the cache. Error code:
%d" , GetLastError ());
        }
     }
  }

Obtaining the information on open positions


The constant monitoring of open positions is not just a standard procedure, but it should certainly be implemented in each instance.
For obtaining information on specific positions, it is sufficient enough to know the name of the instrument by which it is open. To do
this, use the function PositionSelect(index). For those cases, in which the EA is working on only one symbol (on the symbol of the
chart, to which it is attached), the name of the symbol can be obtained by a Symbol() function or from a predefined variable
_Symbol.

//--- we will look for the position by the symbol of the chart, on which the EA is working
   string symbol= Symbol ();
//--- attempt to get the position
   bool selected= PositionSelect(symbol);
   if (selected) // if the position is selected
     {
      long pos_id            = PositionGetInteger ( POSITION_IDENTIFIER);
      double price           = PositionGetDouble ( POSITION_PRICE_OPEN);
      ENUM_POSITION_TYPE type=( ENUM_POSITION_TYPE ) PositionGetInteger ( POSITION_TYPE );
      long pos_magic         = PositionGetInteger ( POSITION_MAGIC);
      string comment         = PositionGetString ( POSITION_COMMENT );
      PrintFormat ( "Position #%d by %s: POSITION_MAGIC=%d, price=%G, type=%s, commentary=%s",
                 pos_id, symbol, pos_magic, price, EnumToString (type), comment);
     }
   else        // if selecting the position was unsuccessful
     {
      PrintFormat ( "Unsuccessful selection of the position by the symbol %s.
Error" ,symbol, GetLastError ());
     }
  }

In a general case, the information on the symbol can be obtained, using the PositionGetSymbol (index) function, which selects one
position and places it into the cache. As a parameter, it is necessary to specify the index of position in the list of open positions. This
is best done through a search of all positions in the loop.

The overall algorithm for working with positions:

1. Obtain the total number of positions, using the PositionsTotal() function;


2. Organize the loop through searching all of the positions by their indexes in the list;
3. Copy, one by one, each position into the cache, using the PositionGetSymbol() function;
4. Obtain the required position data from the cache using the PositionGetDouble(), PositionGetInteger(), and PositionGetString()
functions. If needed, analyze the obtained data and take the appropriate actions.

An example of such an algorithm:

#property script_show_inputs
input long my_magic= 555 ;
//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
void OnStart ()
  {
//--- obtain the total number of positions
   int positions= PositionsTotal();
//--- scan the list of orders
   for ( int i= 0 ;i<positions;i++)
     {
      ResetLastError();
      //--- copy into the cache, the position by its number in the list
      string symbol= PositionGetSymbol (i); //  obtain the name of the symbol by which the position was
opened
      if (symbol!= "" ) // the position was copied into the cache, work with it
        {
         long pos_id            = PositionGetInteger ( POSITION_IDENTIFIER);
         double price           = PositionGetDouble ( POSITION_PRICE_OPEN);
         ENUM_POSITION_TYPE type=( ENUM_POSITION_TYPE ) PositionGetInteger ( POSITION_TYPE );
         long pos_magic         = PositionGetInteger ( POSITION_MAGIC);
         string comment         = PositionGetString ( POSITION_COMMENT );
         if (pos_magic==my_magic)
           {
           //  process the position with a specified POSITION_MAGIC
           }
         PrintFormat ( "Position #%d by %s: POSITION_MAGIC=%d, price=%G, type=%s, commentary=%s",
                     pos_id,symbol,pos_magic,price, EnumToString (type),comment);
        }
      else           // call to PositionGetSymbol() was unsuccessful
        {
         PrintFormat ( "Error when receiving into the cache the position with index %d."+
                     " Error code: %d" , i, GetLastError ());
        }
     }
  }

Rules for working with the history cache


Often, the code for working with the history cache is written by the programmer in such a way, that it works smoothly only if the
history contains 5-10 deals and orders. A typical example of a wrong approach - loading the entire trading history into the cache,
and processing it in a loop, searching through all of the orders and trades:

//---
   datetime start= 0 ;           // initial time set to 1970 year
   datetime end= TimeCurrent ();   // the ending time set to the current server time
//--- request into the cache of the program the entire trading history
   HistorySelect (start,end);
//--- obtain the number of all of the orders in the history
   int history_orders= HistoryOrdersTotal ();
//--- now scan through all of the orders
   for ( int i= 0 ;i<history_orders;i++)
     {
     //  processing each order in the history
     }  
    ...
      
//--- obtain the number of all deals in the history
   int deals= HistoryDealsTotal ();
//--- now scan through all of the deals
   for ( int i= 0 ;i<deals;i++)
     {
     //  process each deal in the history
     }

The attempt to handle all of the trading history, in the majority of cases, is wrong. When the number of processed deals/orders
becomes around thousands and tens of thousands, the work of the program drastically slows down.

Note: always cautiously refer to all of the cases of calling the HistorySelect() function! Unthoughtful and excessive loading of all
of the available trading history into the cache of the mql5-program, degrades its performance.

This is primarily important for testing - the user discovers that the tester suddenly becomes thoughtful, and begins looking for the
reasons for this in the client terminal. Therefore, firstly always think about optimizing the code of the program MQL5 (EA and
indicators, which are called from the EA). Do not rely on the fact that the computer is made of iron and has many kernels.

For the proper work of the EA and the indicator online, this is just as important. A non-optimal code of the program can paralyze
the work of even the most powerful computer.

The correct algorithm for working with the trading history:

1. Determine the need for requesting the trading history into the cache. If this is not necessary, then do not perform the
following actions;
2. Determine the final date of the trading history (perhaps the history up to the moment is not necessary);
3. Calculate the initial date of the trading history, starting from the ending date. Usually, the EAs require the trading history, no
deeper than a single day or week;
4. Obtain the tickets of deals and historical orders for obtaining the properties, by the known tickets:
HistoryOrderGetDouble()
HistoryOrderGetInteger()
HistoryOrderGetString()
HistoryDealGetDouble()
HistoryDealGetInteger()
HistoryDealGetString()
5. If the tickets are not known, and if it is necessary, organize a cycle through sorting;
6. In the loop, obtain the ticket for each deal/order from the cache of the trading history, by the index
(HistoryOrderGetTicket(Index) and HistoryDealGetTicket(Index));
7. Obtain the necessary properties of orders and deals by the known ticket (see point 4).

An example of a code for this algorithm:

//--- the variable, which is set in true only during the change in the trading history
   bool TradeHistoryChanged= false ;
//--- here we check for the changes in the history and put out the TradeHistoryChanged=true if needed
//... the needed code
//--- check  if there are changes in the trading history or not
   if (!TradeHistoryChanged) return ;
//--- if the history has changed, then it makes sense to load it into the cache
//--- the ending time set for the current server time
   datetime end= TimeCurrent ();
//--- the beginning time is set to 3 days ago
   datetime start=end- 3 * PeriodSeconds ( PERIOD_D1);
//--- request in the cache of the program, the trading history for the last 3 days
   HistorySelect (start,end);
//--- obtain the number of orders in the cache of the history
   int history_orders= HistoryOrdersTotal ();
//--- now scan through the orders
   for ( int i= 0 ;i<history_orders;i++)
     {
      //--- obtain the ticket of the historical order
      ulong ticket= HistoryOrderGetTicket (i);
      //--- work with this order - receive its problems
      long order_magic= HistoryOrderGetInteger (ticket, ORDER_MAGIC );
      // obtain the rest of the properties for the order by the ticket
      // ...
     }

The basic idea presented by this example - is that first you must verify the fact of changes taking place in the trading history.
One of the options is to, inside the OnTrade() function, set for the global variable TradeHistoryChanged, the value of true, since the
Trade event always returns with any type of trading event.

If the trading history has not changed, then there is no need to upload the trading history into the cache again, and waste resources
of the CPU. This is logical and does not require any explanation. If the trading history has changed, then we upload only the
necessary part of it, and go through each deal/order only once. Avoid unnecessary repeat cycles.

Note: each request to the cache of the entire trading history, done by the function HistorySelect(), and each cycle of processing
deals and orders from the history, have to be grounded. Otherwise, your computer's resources will be spent inefficiently.

Examples of correct and incorrect work with the trading history are attached to this article, as files WrongWorkWithHistory.mq5 and
RightWorkWithHistory.mq5.

Obtaining information by orders from the history


Working with historical orders is almost no different from working with the active orders, with but one exception. If the number of
active orders in the cache of the mql5-program can not be more than one, then the result HistoryOrdersTotal(), and the number of
historical orders in the cache, depends on how much trading history has been loaded by the HistorySelect(start, end),
HistorySelectByPosition() or HistoryOrderSelection() function.

Note: If the trading history has not been loaded into the cache of the mql5-program by one of the functions HistorySelect(),
HistorySelectByPosition() or HistoryOrderSelect(), then working with historical orders and deals is impossible. Be sure to request
the required history of deals and orders before receiving the data on trading history.

For example, we provide a script, which searches for the last order of the last day, and displays information for it.

// --- determining the time intervals of the required trading history


   datetime end= TimeCurrent ();                 // current server time
   datetime start=end- PeriodSeconds ( PERIOD_D1); // set the beginning for 24 hours ago
//--- request in the cache of the program the trading history for a day
   HistorySelect (start,end);
//--- receive the number of orders in the history
   int history_orders= HistoryOrdersTotal ();
//--- obtain the ticket of the order, which has the last index in the list, from the history
   ulong order_ticket= HistoryOrderGetTicket (history_orders - 1 );
   if (order_ticket> 0 ) // obtain in the cache the historical order, work with it
     {
      //--- order status
      ENUM_ORDER_STATE state=( ENUM_ORDER_STATE ) HistoryOrderGetInteger (order_ticket, ORDER_STATE );
      long order_magic      = HistoryOrderGetInteger (order_ticket, ORDER_MAGIC );
      long pos_ID           = HistoryOrderGetInteger (order_ticket,ORDER_POSITION_ID);
      PrintFormat ( "Order #%d: ORDER_MAGIC=#%d, ORDER_STATE=%d, ORDER_POSITION_ID=%d" ,
                  order_ticket,order_magic, EnumToString (state),pos_ID);
     }
   else              // unsuccessful attempt to obtain the order
     {
      PrintFormat ( "In total, in the history of %d orders, we couldn't select the order" +
                  " with the index %d. Error %d" ,history_orders,history_orders - 1 , GetLastError ());
     }

In more general cases, it is needed to sort through the orders in the loop from the cache, and analyze them. The general
algorithm will be as follows:

1. Determine the time ranges of the sufficient history, if the history is loaded by the function HistorySelect() - it is not
recommended to load the entire trading history into the cache;
2. Load into the cache of the program, the trading history HistorySelect(), HistorySelectByPosition() or HistoryOrderSelect
(ticket) functions;
3. Obtain the total number of orders in the cache, using the HistoryOrdersTotal();
4. Organize the cycle by a search through all of the orders by their indexes in the list;
5. Obtain a ticket of the orders in the cache, using the HistoryOrderGetTicket() function ;
6. Obtain the data of the order from the cache, by using the HistoryOrderGetDouble(), HistoryOrderGetInteger(), and
HistoryOrderGetString() functions. If needed, analyze the obtained data and take the appropriate actions.

An example of such an algorithm:

#property script_show_inputs
input long my_magic = 999 ;
//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
void OnStart ()
  {
// --- setting the time intervals of the required trading history
   datetime end= TimeCurrent ();                 // the current server time
   datetime start=end- PeriodSeconds ( PERIOD_D1); // set the beginning for 24 hours ago
//--- request into the cache of the program the needed interval of the trading history
   HistorySelect (start,end);
//--- obtain the number of orders in history
   int history_orders= HistoryOrdersTotal ();
//--- now scroll through all of the orders
   for ( int i= 0 ;i<history_orders;i++)
     {
      //--- obtain the ticket of the order by its number in the list
      ulong order_ticket= HistoryOrderGetTicket (i);
      if (order_ticket> 0 ) //  obtain in the cache, the historical order, and work with it
        {
         //--- time of execution
         datetime time_done= HistoryOrderGetInteger (order_ticket, ORDER_TIME_DONE );
         long order_magic  = HistoryOrderGetInteger (order_ticket, ORDER_MAGIC );
         long pos_ID       = HistoryOrderGetInteger (order_ticket,ORDER_POSITION_ID);
         if (order_magic== my_magic )
           {
           //  process the position with the set ORDER_MAGIC
           }
         PrintFormat ( "Order #%d: ORDER_MAGIC=#%d, time_done %s, ORDER_POSITION_ID=%d" ,
                     order_ticket,order_magic, TimeToString (time_done),pos_ID);
        }
      else               // unsuccessful attempt to obtain the order from the history
        {
         PrintFormat ( "we were not able to select the order with the index %d. Error %d" ,
                     i, GetLastError ());
        }
     }
  }

Note: always cautiously refer to all of the cases of calling the function HistorySelect()! Unthoughtful and excessive loading of all
of the available trading history into the cache of the mql5-program, degrades its performance.

Obtaining information on deals from the history


Processing of deals has the same features as the processing of historical orders. The number of deals in the trading history and the
result of the execution of HistoryDealsTotal(), depends on how much of the trading history has been loaded into the cache by the
HistorySelect(start, end) or HistorySelectByPosition() function.

To fill the cache with only one deal by its ticket, use the HistoryDealSelect(ticket) function.

// --- determining the time intervals of the required trading history


   datetime end= TimeCurrent ();                 // current sever time
   datetime start=end- PeriodSeconds ( PERIOD_D1); // set the beginning for 24 hours ago
//--- request in the cache of the program the needed interval of the trading history
   HistorySelect (start,end);
//--- obtain the number of deals in history
   int deals= HistoryDealsTotal ();
//--- obtain the ticket for the deal, which has the last index in the list
   ulong deal_ticket= HistoryDealGetTicket (deals - 1 );
   if (deal_ticket> 0 ) // we obtained in the cache of the deal, and work with it
     {
      //--- the ticket order, based on which the deal was made
      ulong order     = HistoryDealGetInteger (deal_ticket, DEAL_ORDER );
      long order_magic= HistoryDealGetInteger (deal_ticket, DEAL_MAGIC );
      long pos_ID     = HistoryDealGetInteger (deal_ticket,DEAL_POSITION_ID);
      PrintFormat ( "Deal #%d for the order #%d with the ORDER_MAGIC=%d  that participated in the
position",
                  deals- 1 ,order,order_magic,pos_ID);
     }
   else              // unsuccessful attempt of obtaining a deal
     {
      PrintFormat ( "In total, in the history %d of deals, we couldn't select a deal" +
                  " with the index %d. Error %d" ,deals,deals - 1 , GetLastError ());
     }

In more general cases, it is needed to search in the deal loop from the cache, and make analyze them. The general algorithm will
be as follows:

1. Determine the boundaries of the sufficient history, if history loads by the HistorySelect(start, end) function - then it is not
recommended to load the entire history of trade into the cache;
2. Load into the cache of the program, the trading history of the functions HistorySelect() or HistorySelectByPosition();
3. Obtain the total number of deals in the history, using the HistoryDealsTotal() function;
4. Organize the cycle by searching through all of the deals, by their numbers in the list;
5. Determine the ticket of the next deal in the cache, by using the HistoryDealGetTicket();
6. Obtain the information about the deal from the cache, by using the functions HistoryDealGetDouble(),
HistoryDealGetInteger(), and HistoryDealGetString(). If needed, analyze the obtained data and take the appropriate actions.

An example of such an algorithm for calculating the profits and losses:

input long my_magic = 111 ;


//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
void OnStart ()
  {
// --- determine the time intervals of the required trading history
   datetime end= TimeCurrent ();                  // current server time
   datetime start=end- PeriodSeconds ( PERIOD_D1); // set the beginning time to 24 hours ago
//--- request in the cache of the program the needed interval of the trading history
   HistorySelect (start,end);
//--- obtain the number of deals in the history
   int deals= HistoryDealsTotal ();
   int returns= 0 ;
   double profit= 0 ;
   double loss= 0 ;
//--- scan through all of the deals in the history
   for ( int i= 0 ;i<deals;i++)
     {
      //--- obtain the ticket of the deals by its index in the list
      ulong deal_ticket= HistoryDealGetTicket (i);
      if (deal_ticket> 0 ) // obtain into the cache the deal, and work with it
        {
         string symbol             = HistoryDealGetString (deal_ticket, DEAL_SYMBOL );
         datetime time             = HistoryDealGetInteger (deal_ticket, DEAL_TIME);
         ulong order               = HistoryDealGetInteger (deal_ticket, DEAL_ORDER );
         long order_magic          = HistoryDealGetInteger (deal_ticket, DEAL_MAGIC );
         long pos_ID               = HistoryDealGetInteger (deal_ticket,DEAL_POSITION_ID);
         ENUM_DEAL_ENTRY entry_type=( ENUM_DEAL_ENTRY ) HistoryDealGetInteger (deal_ticket, DEAL_ENTRY );
         //--- process the deals with the indicated DEAL_MAGIC
         if (order_magic== my_magic )
           {
            //... necessary actions
           }
         //--- calculate the losses and profits with a fixed results
         if (entry_type== DEAL_ENTRY_OUT)
          {
            //--- increase the number of deals
            returns++;
            //--- result of fixation
            double result= HistoryDealGetDouble (deal_ticket, DEAL_PROFIT );
            //--- input the positive results into the summarized profit
            if (result> 0 ) profit+=result;
            //--- input the negative results into the summarized losses
            if (result< 0 ) loss+=result;
           }
        }
      else // unsuccessful attempt to obtain a deal
        {
         PrintFormat ( "We couldn't select a deal, with the index %d. Error %d" ,
                     i, GetLastError ());
        }
     }
   //--- output the results of the calculations
   PrintFormat ( "The total number of %d deals with a financial result. Profit=%.2f , Loss= %.2f" ,
               returns,profit,loss);
  }

Note: always cautiously refer to all of the cases of calling the function HistorySelect()! Unthoughtful and excessive loading of all
of the available trading history into the cache of the mql5-program, degrades its performance.

Obtaining in the cache of the history by the identifier of the position (POSITION_IDENTIFIER)
The HistorySelectByPosition (position_ID) function just like the HistorySelect (start, end) function fills the cache with deals and
orders from the history, but only under one condition - they must have the specified identifier of the position
(POSITION_IDENTIFIER). The identifier of the position - is a unique number, which is automatically assigned to each re-opened
position, and does not change throughout its life. Meanwhile. it must be kept in mind, that the change of the position (shift of the
type of the position from POSITION_TYPE_BUY to POSITION_TYPE_SELL) does not change the identifier of the position.
Each open position is the result of one or more deals on that instrument. Therefore, to analyze the position changes, during its
lifetime, each deal and order, based on which the deal was done, is assigned an identifier to the position, in which this deal
participated. Thus, knowing the identifier of the current open positions, we can reconstruct the entire history - find all of the orders
and deals that have changed it.

The HistorySelectByPosition(position_ID) function serves to spare the programmer from having to write their own code for iterating
through the entire trading history in search of such information. A typical algorithm for working with this function:

1. Obtain the right position identifier;


2. Obtain, using the function HistorySelectByPosition(), into the cache of the trading history, all of the orders and deals, the
identifier of which, equals the identifier of the current position;
3. Process the trading history according to the algorithm.

Conclusion
The entire trading subsystem platform MetaTrader 5 is well thought out and user-friendly More-ever, the abundance of trading
functions, allows us to solve each specific problem in the most efficient way.

But even despite the fact that the specialized trading classes from the standard library, allow us not to worry about too many
nuances, and write programs on a high level, without going into implementation, the understanding of the basics, will allow us to
create more reliable and efficient trading EAs.

All of the given examples can be found in the files, attached to this article.

Attached files
demo_positiongetsymbolbyindex.mq5 (2.08 KB)
demo_positionselectbysymbol.mq5 (1.65 KB)
demo_ordergetticketbyindex.mq5 (1.87 KB)
demo_orderselectbyticket.mq5 (1.31 KB)
demo_historydealselectbyindex.mq5 (2.92 KB)
demo_historydealselectbyticket.mq5 (1.96 KB)
demo_historyorderselectbyindex.mq5 (2.22 KB)
demo_historyorderselectbyticket.mq5 (2.02 KB)
wrongworkwithhistory.mq5 (1.42 KB)
rightworkwithhistory.mq5 (3.85 KB)

You might also like