Professional Documents
Culture Documents
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.
Deal
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.
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.
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.
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.
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 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.
Should call theses functions to fill
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().
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.
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 thePositionsTotal() 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.
Should call the filling function before
calling these ones
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 functionHistoryDealsTotal(). 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, whichclears 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).
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 fromglobal variable. In general cases, however, the ticket information is absent, and thus we
need to turn to the help of theOrderGetTicket(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:
//--- 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.
#property script_show_inputs
//---
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.
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).
//--- 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.
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.
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.
#property script_show_inputs
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.
To fill the cache with only one deal by its ticket, use the HistoryDealSelect(ticket) function.
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.
//--- 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);
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.
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:
Conclusion
The entire trading subsystem platform MetaTrader 5 is well thought out and user-friendly More-ever, the
abundance oftrading 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.