MQL4 Book  Creation of a Normal Program  Event Tracking Function


Event Tracking Function


Many events take place during trading. A trader can see some of them directly in the symbol window, for example, market price changes or crossed indicator lines. Other events, though they are interesting for a trader, too, are not explicitly shown anywhere. A considerable part of those events can be detected and processed using MQL4.

For example, your dealing center may change the trading conditions shortly before important news are published or when the market becomes very active. In such cases, the spread or the minimum allowed distance for placing of orders and for the requested stop order prices may be increased. If this happens, it is necessary, first, to detect and take the new trading conditions into consideration, and, second, to inform the trader about these changes.

To solve these tasks, you may use the event tracking function in your Expert Advisor.

User-Defined Function Events()

int Events()

The function calculates the changes in the minimum distance required to place orders and their stop orders, as well as the changes in the list of market and pending orders available on the account. To execute the function, you should use the order accounting function Terminal() in your program. The values of the following global arrays are used:

  • Mas_Ord_New - the array of characteristics of orders available as of the moment of the function Terminal() execution;
  • Mas_Ord_Old - the array of characteristics of orders available as of the moment of the preceding execution of the function Terminal().

The values of the following global variables are used:
- Level_new - the current value of the minimum distance;
- Level_old - the preceding value of the minimum distance.

To display the messages, the function will use the data function Inform(). If the function Inform() is not included in the Expert Advisor, no messages will be shown.

The event tracking function Events() is formed as the include file Events.mqh:

//--------------------------------------------------------------------------------
// Events.mqh
// The code should be used for educational purpose only.
//--------------------------------------------------------------------------- 1 --
// Event tracking function.
// Global variables:
// Level_new            The new value of the minimum distance
// Level_old            The preceding value of the minimum distance
// Mas_Ord_New[31][9]   The last known array of orders
// Mas_Ord_Old[31][9]   The old array of orders
//--------------------------------------------------------------------------- 2 --
int Events()                              // User-defined function
  {
   bool Conc_Nom_Ord;                     // Matching orders in ..
   //.. the old and the new arrays
//--------------------------------------------------------------------------- 3 --
   Level_new=MarketInfo(Symbol(),MODE_STOPLEVEL );// Last known
   if (Level_old!=Level_new)              // New is not the same as old..
     {                                    // it means the condition have been changed
      Level_old=Level_new;                // New "old value"
      Inform(10,Level_new);               // Message: new distance
     }
//--------------------------------------------------------------------------- 4 --
   // Searching for lost, type-changed, partly closed and reopened orders
   for(int old=1;old<=Mas_Ord_Old[0][0];old++)// In the array of old orders
     {                                    // Assuming the..
      Conc_Nom_Ord=false;                 // ..orders don't match
      //--------------------------------------------------------------------- 5 --
      for(int new=1;new<=Mas_Ord_New[0][0];new++)//Cycle for the array ..
        {                                 //..of new orders
         //------------------------------------------------------------------ 6 --
         if (Mas_Ord_Old[old][4]==Mas_Ord_New[new][4])// Matched number
           {                              // Order type becomes ..
            if (Mas_Ord_New[new][6]!=Mas_Ord_Old[old][6])//.. different
               Inform(7,Mas_Ord_New[new][4]);// Message: modified:)
            Conc_Nom_Ord=true;            // The order is found, ..
            break;                        // ..so exiting ..
           }                              // .. the internal cycle
         //------------------------------------------------------------------ 7 --
                                          // Order number does not match
         if (Mas_Ord_Old[old][7]>0 &&     // MagicNumber matches
            Mas_Ord_Old[old][7]==Mas_Ord_New[new][7])//.. with the old one
           {               //it means it is reopened or partly closed
                                             // If volumes match,..
            if (Mas_Ord_Old[old][5]==Mas_Ord_New[new][5])
               Inform(8,Mas_Ord_Old[old][4]);// ..it is reopening
            else                             // Otherwise, it was..
               Inform(9,Mas_Ord_Old[old][4]);// ..partly closing
            Conc_Nom_Ord=true;               // The order is found, ..
            break;                           // ..so exiting ..
           }                                 // .. the internal cycle
        }
      //--------------------------------------------------------------------- 8 --
      if (Conc_Nom_Ord==false)               // If we are here,..
        {                                    // ..it means no order found:(
         if (Mas_Ord_Old[old][6]==0)
            Inform(1, Mas_Ord_Old[old][4]);  // Order Buy closed
         if (Mas_Ord_Old[old][6]==1)
            Inform(2, Mas_Ord_Old[old][4]);  // Order Sell closed
         if (Mas_Ord_Old[old][6]> 1)
            Inform(3, Mas_Ord_Old[old][4]);  // Pending order deleted
        }
     }
//--------------------------------------------------------------------------- 9 --
   // Search for new orders
   for(new=1; new<=Mas_Ord_New[0][0]; new++)// In the array of new orders
     {
      if (Mas_Ord_New[new][8]>0)            //This one is not new, but reopened
         continue;                          //..or partly closed
      Conc_Nom_Ord=false;                   // As long as no matches found
      for(old=1; old<=Mas_Ord_Old[0][0]; old++)// Searching for this order
        {                                   // ..in the array of old orders
         if (Mas_Ord_New[new][4]==Mas_Ord_Old[old][4])//Matched number..
           {                                          //.. of the order
            Conc_Nom_Ord=true;              // The order is found, ..
            break;                          // ..so exiting ..
           }                                // .. the internal cycle
        }
      if (Conc_Nom_Ord==false)              // If no matches found,..
        {                                   // ..the order is new :)
         if (Mas_Ord_New[new][6]==0)
            Inform(4, Mas_Ord_New[new][4]); // Order Buy opened
         if (Mas_Ord_New[new][6]==1)
            Inform(5, Mas_Ord_New[new][4]); // Order Sell opened
         if (Mas_Ord_New[new][6]> 1)
            Inform(6, Mas_Ord_New[new][4]); // Pending order placed
        }
     }
//-------------------------------------------------------------------------- 10 --
   return;
  
}
//-------------------------------------------------------------------------- 11 --

Global arrays and variables required for the function execution are described in block 1-2. In block 2-3, variable Conc_Nom_Ord used in the further code for orders analysis is opened.

The function tracks changes of the minimum distance for placing of orders and stop orders. For this, the current value of the minimum distance Level_new is calculated at each execution of the function (block 3-4) and then compared to the preceding one, the value of Level_old (obtained during the preceding execution of the function). If the values of these variables are not equal to each other, it means that the minimum distance has been changed by the dealing center shortly before the last execution of the function. In this case, the current value of the minimum distance is assigned to the variable Level_old (in order to consider it in the subsequent executions of the function), and the function Inform() is executed in order to display the corresponding message.

Generally, you can use a similar method to detect other events, for example, changes in spread, permissions to trade the given symbol (identifier MODE_TRADEALLOWED in the function MarketInfo()), the completion of a new bar (see Problem 27), the fact of crossing indicator lines (see Fig. 107 ), the fact of reaching a certain preset time, etc. The program may detect some events to use the obtained values in your EA, other events - to inform the user about them.

In blocks 4-10, the states of market and pending orders are analyzed. Information about most changes in orders is provided to the user. The analysis is performed in two stages. At the first stage, the program detects changes that relate to lost (closed or deleted), type-changing, partly closed and reopened orders (block 4-9). At the second stage (block 9-10), the new orders are searched for.

In blocks 4-9, the orders accounted in the array Mas_Ord_Old are analyzed. The amount of iterations in the external cycle 'for' is found according to the total amount of orders in the array (array element Mas_Ord_Old[0][0]). To check whether the order is kept as of the current moment, it is necessary to find a similar order in the orders array Mas_Ord_New. This search is performed in the internal cycle 'for' (block 6-8), the amount of iterations of which is equal to the amount of orders in the array (array element Mas_Ord_New[0][0]). We will further name the array Mas_Ord_Old 'old array', whereas the Mas_Ord_New - 'new array'.

In blocks 6-8, the program searches for only those orders, the characteristics of which are different. For example, in block 6-7, the order is checked for its number (see the correspondence of array indexes with order characteristics in Table 4). If the old-array order under check matches in number with one of the orders in the new array, it means that, at least, this order is not closed (or deleted). It is also necessary to check whether the order type is changed. If yes, it means that a pending order is modified into a market one. In this case, the corresponding message is displayed using the function Inform(). Independently on the fact of changing (or keeping unchanged) of the order type, this order will not be analyzed further: the program exits the internal cycle and, finally, starts a new iteration of the external cycle.

If the program finds at the execution of block 6-7 that the old-array order under check does not match in number with any orders from the new array, the control is passed to block 7-8. Here the program checks whether the current order from the new array has a nonzero MagicNumber (all orders opened and placed by the EA have a nonzero MagicNumber). If it has such a MagicNumber and this parameter coincides with the MagicNumber of the order from the old array under check, it means that this order is traded, but has been changed in some way. There are two situations when order number can be changed.

Situation 1. The order is partly closed. You can partly close a market order (not a pending one!) in two stages according to the technology accepted in MT 4. At the first stage, the initial order is completely closed. At the same time, a new market order of a smaller volume is opened with the same open price and with the same requested stop-order prices as in the initial order. This new order gets its unique name, other than the number of the initial order being partly closed.

Situation 2. The order is reopened by the dealing center. Some banks (due to their specific internal accounting rules) forcedly close all market orders at the end of trading day and immediately open market orders of the same type and with the same volume, but at the current price and minus swap. This event doesn't affect the economic results of a trading account in any way. Each newly opened order gets its unique number that doesn't match with the numbers of closed orders.

The difference between the two situations above is in the volumes of new orders: they are different in the first situation and they are unchanged in the second one. This difference is used in block 7-8 to distinguish between orders modified for different reasons. In both cases, the corresponding message is displayed ("the order is partly closed' or 'the order is reopened').

If the program has not detected the matching (block 6-7) or inheriting (block 7-8) of the order in the new array by the completion of the internal cycle, it means that the old-array order under check is closed or deleted. In this case, the control is passed to block 8-9, where one or another message will be displayed, according to the order type. In the above example, three kinds of messages are realized: for order Buy, for order Sell and for pending orders of all types. In a general case, this sequence can be slightly changed (extended) - you can create a group of the corresponding messages for each type of pending orders.

At the second stage, the program considers orders from the new order array (block 9-10). This is made in order to detect newly opened and placed orders. In the external cycle 'for', the program searches in all orders, the information about which is stored in the array of new orders. In order to identify the reopened or partly closed orders, the program uses a simple feature - the availability of comment. When partly closing or reopening an order, the server adds a comment that gives the number of the initial order. The EA above doesn't use comments, so the availability of a comment means that the order under check is not new.

If an order doesn't contain a comment, the program searches for an order with the same number in the old array. If the program finds the order having this number among old orders in the internal cycle 'for', it means that the order isn't new, but was opened before. However, if the number of the order from the new array doesn't match with any orders in the old array, it means that this order is an open market order or a placed pending one. In the lower part of block 9-10, the program calls to function Inform() in order to display the correspondent message, according to the order type.

The use of the considered function Events() turns out to be very helpful in practice. Once having used the function in an EA, the programmer usually uses it in his or her further work. It must be separately noted that the functions Events() and Terminal() are closely interrelated. If you are going to make changes in one of these functions (for example, to use other names for global arrays), you must make the corresponding changes in the other function. If you use comments in orders to realize your trading strategy, you should differently process the inheritance characteristic of the order (block 9-10), namely, you should use string functions to analyze the comment.

The amount of events considered in the function Events() can be highly increased. For example, if you want to completely display all events related to orders, you should add the analysis of order characteristics - changes in the requested stop-order prices and in the requested open prices of pending orders, as well as the closing method (whether the orders are closed as opposite orders or each is closed separately) and the reason for closing/deleting of orders (whether the price has reached the requested stop-order level or the order is closed on the trader's initiative, etc.).