Русский 中文 Español Deutsch 日本語 Português
preview
Trade transactions. Request and response structures, description and logging

Trade transactions. Request and response structures, description and logging

MetaTrader 5Examples | 24 November 2023, 08:54
3 227 0
Artyom Trishkin
Artyom Trishkin

Contents


Introduction

MQL5 features the OrderSend() function for placing pending orders, opening positions, as well as for changing orders and positions. The first input of the function is the structure of the MqlTradeRequest trade request. The 'action' field of the structure indicates the type of action to be performed, and the remaining fields are filled in depending on the selected action in the 'action' field. Thus, we send various requests to the server by passing the parameters necessary for a trade request to the function.

Trade Request structure (MqlTradeRequest)

The interaction between the client terminal and the trading server in terms of order placement operations is carried out through trade requests. The request is represented by a special predefined MqlTradeRequest structure, which contains all the fields necessary for performing trades:

struct MqlTradeRequest
  {
   ENUM_TRADE_REQUEST_ACTIONS    action;           // Type of a performed action
   ulong                         magic;            // EA stamp (magic number ID)
   ulong                         order;            // Order ticket
   string                        symbol;           // Symbol name
   double                        volume;           // Requested volume of a deal in lots
   double                        price;            // Price 
   double                        stoplimit;        // StopLimit order level
   double                        sl;               // Stop Loss order level
   double                        tp;               // Take Profit order level
   ulong                         deviation;        // Maximum acceptable deviation from the requested price
   ENUM_ORDER_TYPE               type;             // Order type
   ENUM_ORDER_TYPE_FILLING       type_filling;     // Order filling type
   ENUM_ORDER_TYPE_TIME          type_time;        // Order lifetime type
   datetime                      expiration;       // Order expiration time (for ORDER_TIME_SPECIFIED type orders)
   string                        comment;          // Order comment
   ulong                         position;         // Position ticket
   ulong                         position_by;      // Opposite position ticket
  };

The first 'action' field specifies the type of action to be performed.

Trading operation type. The value can be one of the ENUM_TRADE_REQUEST_ACTIONS enumeration values:

ID
Description
TRADE_ACTION_DEAL
Place an order for an instant deal with the specified parameters (set a market order)
TRADE_ACTION_PENDING
Place an order for performing a deal at specified conditions (pending order)
TRADE_ACTION_SLTP
Change open position Stop Loss and Take Profit
TRADE_ACTION_MODIFY
Change parameters of the previously placed trading order
TRADE_ACTION_REMOVE
Remove previously placed pending order
TRADE_ACTION_CLOSE_BY
Close a position by an opposite one

The structure fields must be filled out individually for each type of action.

After filling in the required fields of the structure, we can send a trade order to the server. We can also first check whether the structure is filled correctly using the OrderCheck() function, which transmits the query being checked and the variable of the MqlTradeCheckResult structure type. The result of the check will be set into the variable:

struct MqlTradeCheckResult
  {
   uint         retcode;             // Response code
   double       balance;             // Balance after performing a deal
   double       equity;              // Equity after performing a deal
   double       profit;              // Floating profit
   double       margin;              // Margin requirements
   double       margin_free;         // Free margin
   double       margin_level;        // Margin level
   string       comment;             // Comment on the response code (error description)
  };

The function returns false in case of insufficient funds as a result of checking the order or incorrectly filled in parameters. If the basic check of structures (pointers) is successful, the function returns truethis does not entail that the requested trading operation will be executed successfully. To obtain a detailed description of the result of executing a function, we should analyze the fields of the result structure presented above.

After successfully checking the completion of the fields of the trade order structure, it can be sent to the server. The success of the OrderSend() function execution does not mean that the order has been executed. We simply understand that the order has been processed and accepted by the server. The result of sending a trade order to the server will be the filled in fields of the MqlTradeResult structure, which contains the trade server’s response to the request sent by the OrderSend() function.

The result of the trading operation is returned to the variable of the MqlTradeResult type, which is passed as the second parameter to the OrderSend() function for conducting trading operations.

The terminal sets the request ID in the request_id field when sending it to the trade server using the OrdersSend() and OrderSendAsync() functions. The terminal receives messages about performed transactions from the trade server and submits them for processing by the OnTradeTransaction() function containing the following components as parameters:

  • description of the trade transaction in the MqlTradeTransaction structure;
  • description of the trade request sent from the OrderSend() or OrdersSendAsync() function. Request ID is sent by the terminal to the trade server, while the request itself and its request_id are stored in the terminal memory;
  • the trade request execution result as MqlTradeResult structure with request_id field containing ID of this request.

The OnTradeTransaction() function receives three input parameters but the last two should be analyzed only for transactions of TRADE_TRANSACTION_REQUEST type. In all other cases, data on the trade request and its execution result are not filled. Example of parameters analysis can be found in the Trade request structure section.

Setting request_id by the terminal for the trade request when sending it to the server is mainly introduced for working with OrderSendAsync() asynchronous function. This identifier allows to associate the performed action (OrderSend or OrderSendAsync functions call) with the result of this action sent to OnTradeTransaction().

Thus, we can control the acceptance of a trading order by the server, its placement in the trading system and execution in the OnTradeTransaction() function. In the current article, I will write the functions to display the fields of all structures involved in a trade request in the log, starting from filling out the structure of the trade order and ending with analyzing events in OnTradeTransaction(). The intended result is a small informer EA that prints out all trading events in the journal.

Handling trade orders boils down to the following:

  1. Fill in the structure of the trade request,
  2. Check the correctness of filling out the structure and the possibility of placing such an order,
  3. Send a trade order to the server,
  4. If necessary, analyze the structure of the result of sending an order to the server,
  5. Receive events and analyze messages in the log from the OnTradeTransaction() handler.
But before we start writing functions for outputting descriptions of structure fields to the log, we will write auxiliary functions that return descriptions of certain types of orders presented in the form of enumeration constants, as well as other useful and necessary functions.

Auxiliary functions

The EA presented at the end of the article will be controlled by pressing keyboard shortcuts. Let's write two functions to determine the Ctrl and Shift key presses:

//+------------------------------------------------------------------+
//| Return the state of the Ctrl key                                 |
//+------------------------------------------------------------------+
bool IsCtrlKeyPressed(void) 
  { 
   return(::TerminalInfoInteger(TERMINAL_KEYSTATE_CONTROL)<0);
  }
//+------------------------------------------------------------------+
//| Return the state of the Shift key                                |
//+------------------------------------------------------------------+
bool IsShiftKeyPressed(void) 
  { 
   return(::TerminalInfoInteger(TERMINAL_KEYSTATE_SHIFT)<0);
  }

Now, when detecting that a key has been pressed, we can check the flag for holding the Ctrl or Shift key, or holding them simultaneously to respond to a combination of control and alphanumeric keys.


When returning descriptions of structure fields, we will need to print trade server return codes. Let's make a function that returns a description of the server response code:

//+------------------------------------------------------------------+
//| Return a description of the trade server return code             |
//+------------------------------------------------------------------+
string RetcodeDescription(const uint retcode,bool ext_descr=false)
  {
   switch(retcode)
     {
      //--- Done
      case 0                                 :  return "OK (0)";
      //--- Requote
      case TRADE_RETCODE_REQUOTE             :  return "10004 REQUOTE"+(ext_descr ? " (Requote)" : "");
      //--- Request rejected
      case TRADE_RETCODE_REJECT              :  return "10006 REJECT"+(ext_descr ? " (Request rejected)" : "");
      //--- Request canceled by trader
      case TRADE_RETCODE_CANCEL              :  return "10007 CANCEL"+(ext_descr ? " (Request canceled by trader)" : "");
      //--- Order placed
      case TRADE_RETCODE_PLACED              :  return "10008 PLACED"+(ext_descr ? " (Order placed)" : "");
      //--- Request completed
      case TRADE_RETCODE_DONE                :  return "10009 DONE"+(ext_descr ? " (Request completed)" : "");
      //--- Request completed partially
      case TRADE_RETCODE_DONE_PARTIAL        :  return "10010 DONE_PARTIAL"+(ext_descr ? " (Only part of the request was completed)" : "");
      //--- Request processing error
      case TRADE_RETCODE_ERROR               :  return "10011 ERROR"+(ext_descr ? " (Request processing error)" : "");
      //--- Request canceled by timeout
      case TRADE_RETCODE_TIMEOUT             :  return "10012 TIMEOUT"+(ext_descr ? " (Request canceled by timeout)" : "");
      //--- Invalid request
      case TRADE_RETCODE_INVALID             :  return "10013 INVALID"+(ext_descr ? " (Invalid request)" : "");
      //--- Invalid volume in the request
      case TRADE_RETCODE_INVALID_VOLUME      :  return "10014 INVALID_VOLUME"+(ext_descr ? " (Invalid volume in the request)" : "");
      //--- Invalid price in the request
      case TRADE_RETCODE_INVALID_PRICE       :  return "10015 INVALID_PRICE"+(ext_descr ? " (Invalid price in the request)" : "");
      //--- Invalid stops in the request
      case TRADE_RETCODE_INVALID_STOPS       :  return "10016 INVALID_STOPS"+(ext_descr ? " (Invalid stops in the request)" : "");
      //--- Trading disabled
      case TRADE_RETCODE_TRADE_DISABLED      :  return "10017 TRADE_DISABLED"+(ext_descr ? " (Trade is disabled)" : "");
      //--- Market is closed
      case TRADE_RETCODE_MARKET_CLOSED       :  return "10018 MARKET_CLOSED"+(ext_descr ? " (Market is closed)" : "");
      //--- There is not enough money to complete the request
      case TRADE_RETCODE_NO_MONEY            :  return "10019 NO_MONEY"+(ext_descr ? " (There is not enough money to complete the request)" : "");
      //--- Prices changed
      case TRADE_RETCODE_PRICE_CHANGED       :  return "10020 PRICE_CHANGED"+(ext_descr ? " (Prices changed)" : "");
      //--- There are no quotes to process the request
      case TRADE_RETCODE_PRICE_OFF           :  return "10021 PRICE_OFF"+(ext_descr ? " (There are no quotes to process the request)" : "");
      //--- Invalid order expiration date in the request
      case TRADE_RETCODE_INVALID_EXPIRATION  :  return "10022 INVALID_EXPIRATION"+(ext_descr ? " (Invalid order expiration date in the request)" : "");
      //--- Order state changed
      case TRADE_RETCODE_ORDER_CHANGED       :  return "10023 ORDER_CHANGED"+(ext_descr ? " (Order state changed)" : "");
      //--- Too frequent requests
      case TRADE_RETCODE_TOO_MANY_REQUESTS   :  return "10024 TOO_MANY_REQUESTS"+(ext_descr ? " (Too frequent requests)" : "");
      //--- No changes in request
      case TRADE_RETCODE_NO_CHANGES          :  return "10025 NO_CHANGES"+(ext_descr ? " (No changes in request)" : "");
      //--- Autotrading disabled by server
      case TRADE_RETCODE_SERVER_DISABLES_AT  :  return "10026 SERVER_DISABLES_AT"+(ext_descr ? " (Autotrading disabled by server)" : "");
      //--- Autotrading disabled by client terminal
      case TRADE_RETCODE_CLIENT_DISABLES_AT  :  return "10027 CLIENT_DISABLES_AT"+(ext_descr ? " (Autotrading disabled by client terminal)" : "");
      //--- Request locked for processing
      case TRADE_RETCODE_LOCKED              :  return "10028 LOCKED"+(ext_descr ? " (Request locked for processing)" : "");
      //--- Order or position frozen
      case TRADE_RETCODE_FROZEN              :  return "10029 FROZEN"+(ext_descr ? " (Order or position frozen)" : "");
      //--- Invalid order filling type
      case TRADE_RETCODE_INVALID_FILL        :  return "10030 INVALID_FILL"+(ext_descr ? " (Invalid order filling type)" : "");
      //--- No connection with the trade server
      case TRADE_RETCODE_CONNECTION          :  return "10031 CONNECTION"+(ext_descr ? " (No connection with the trade server)" : "");
      //--- Operation allowed only for live accounts
      case TRADE_RETCODE_ONLY_REAL           :  return "10032 ONLY_REAL"+(ext_descr ? " (Operation is allowed only for live accounts)" : "");
      //--- Number of pending orders reached the limit
      case TRADE_RETCODE_LIMIT_ORDERS        :  return "10033 LIMIT_ORDERS"+(ext_descr ? " (The number of pending orders has reached the limit)" : "");
      //--- Volume of orders and positions for the symbol reached the limit
      case TRADE_RETCODE_LIMIT_VOLUME        :  return "10034 LIMIT_VOLUME"+(ext_descr ? " (The volume of orders and positions for the symbol has reached the limit)" : "");
      //--- Incorrect or prohibited order type
      case TRADE_RETCODE_INVALID_ORDER       :  return "10035 INVALID_ORDER"+(ext_descr ? " (Incorrect or prohibited order type)" : "");
      //--- Position with specified POSITION_IDENTIFIER already closed
      case TRADE_RETCODE_POSITION_CLOSED     :  return "10036 POSITION_CLOSED"+(ext_descr ? " (Position with the specified POSITION_IDENTIFIER has already been closed)" : "");
      //--- Close volume exceeds the current position volume
      case TRADE_RETCODE_INVALID_CLOSE_VOLUME:  return "10038 INVALID_CLOSE_VOLUME"+(ext_descr ? " (A close volume exceeds the current position volume)" : "");
      //--- Close order already exists for specified position
      case TRADE_RETCODE_CLOSE_ORDER_EXIST   :  return "10039 CLOSE_ORDER_EXIST"+(ext_descr ? " (A close order already exists for a specified position)" : "");
      //--- Number of positions reached the limit
      case TRADE_RETCODE_LIMIT_POSITIONS     :  return "10040 LIMIT_POSITIONS"+(ext_descr ? " (The number of positions has reached the limit)" : "");
      //--- Pending order activation request is rejected, order is canceled
      case TRADE_RETCODE_REJECT_CANCEL       :  return "10041 REJECT_CANCEL"+(ext_descr ? " (The pending order activation request is rejected, the order is canceled)" : "");
      //--- Request rejected, only long positions are allowed on symbol
      case TRADE_RETCODE_LONG_ONLY           :  return "10042 LONG_ONLY"+(ext_descr ? " (Only long positions are allowed)" : "");
      //--- Request rejected, only short positions are allowed on symbol
      case TRADE_RETCODE_SHORT_ONLY          :  return "10043 SHORT_ONLY"+(ext_descr ? " (Only short positions are allowed)" : "");
      //--- Request rejected, only position closing is allowed on symbol
      case TRADE_RETCODE_CLOSE_ONLY          :  return "10044 CLOSE_ONLY"+(ext_descr ? " (Only position closing is allowed)" : "");
      //--- Request rejected, position closing for trading account is allowed only by FIFO rule
      case TRADE_RETCODE_FIFO_CLOSE          :  return "10045 FIFO_CLOSE"+(ext_descr ? " (Position closing is allowed only by FIFO rule)" : "");
      //--- Request rejected, opposite positions on a single symbol are disabled for trading account
      case TRADE_RETCODE_HEDGE_PROHIBITED    :  return "10046 HEDGE_PROHIBITED"+(ext_descr ? " (Opposite positions on a single symbol are disabled)" : "");
      //--- Unknown return code 
      default                                :  return "Undefined ("+(string)retcode+")";
     }
  }

The function receives the server response code and the flag indicating the need to return an extended description. The function returns either a digital code with a constant description:

10034 LIMIT_VOLUME

or a detailed description of the server response code:

10034 LIMIT_VOLUME (The volume of orders and positions for the symbol has reached the limit)


The function that returns a description of the order type:

//+------------------------------------------------------------------+
//| Return the order type description                                |
//+------------------------------------------------------------------+
string OrderTypeDescription(const ENUM_ORDER_TYPE type,const bool ext_descr=false)
  {
//--- "Cut out" the order type from the string obtained from enum
   string res=StringSubstr(EnumToString(type),11);
//--- Convert all received characters to lowercase and
   if(res.Lower())
     {
      //--- replace the first letter from small to capital
      res.SetChar(0,ushort(res.GetChar(0)-0x20));
      int total=(int)res.Length();  // Text length
      int index=0;                  // index to start searching for "_" in a text
      //--- Search for underscores in a loop through all characters
      for(int i=0;i<total;i++)
        {
         int pos=StringFind(res,"_",index);
         //--- If an underscore is found,
         if(pos>0)
           {
            //--- replace it with space and convert the next letter to uppercase 
            res.SetChar(pos,' ');
            res.SetChar(pos+1,ushort(res.GetChar(pos+1)-0x20));
            //--- Set a new index for starting the search for "_"
            index=pos;
           }
        }
     }
   string descr="";
   switch(type)
     {
      case ORDER_TYPE_BUY              :  descr=" (Market Buy order)";                                                                          break;
      case ORDER_TYPE_SELL             :  descr=" (Market Sell order)";                                                                         break;
      case ORDER_TYPE_BUY_LIMIT        :  descr=" (Buy Limit pending order)";                                                                   break;
      case ORDER_TYPE_SELL_LIMIT       :  descr=" (Sell Limit pending order)";                                                                  break;
      case ORDER_TYPE_BUY_STOP         :  descr=" (Buy Stop pending order)";                                                                    break;
      case ORDER_TYPE_SELL_STOP        :  descr=" (Sell Stop pending order)";                                                                   break;
      case ORDER_TYPE_BUY_STOP_LIMIT   :  descr=" (Upon reaching the order price, a pending Buy Limit order is placed at the StopLimit price)"; break;
      case ORDER_TYPE_SELL_STOP_LIMIT  :  descr=" (Upon reaching the order price, a pending Sell Limit order is placed at the StopLimit price)";break;
      case ORDER_TYPE_CLOSE_BY         :  descr=" (Order to close a position by an opposite one)";                                              break;
      default: break;
     }
   return res+(!ext_descr ? "" : descr);
   /* Sample output:
      Sell Limit (Sell Limit pending order)
   */
  }

When sending a trade request using the OrderSend() function, an order type should be specified for some operations. The order type is specified in the type field of the MqlTradeRequest structure and can take values from the ENUM_ORDER_TYPE enumeration:

ID
Description
ORDER_TYPE_BUY
Market buy order
ORDER_TYPE_SELL
Market sell order
ORDER_TYPE_BUY_LIMIT
Buy Limit pending order
ORDER_TYPE_SELL_LIMIT
Sell Limit pending order
ORDER_TYPE_BUY_STOP
Buy Stop pending order
ORDER_TYPE_SELL_STOP
Sell Stop pending order
ORDER_TYPE_BUY_STOP_LIMIT
Upon reaching the order price, Buy Limit pending order is placed at StopLimit price
ORDER_TYPE_SELL_STOP_LIMIT
Upon reaching the order price, Sell Limit pending order is placed at StopLimit price
ORDER_TYPE_CLOSE_BY
Order for closing a position by an opposite one


The function that returns a description of the order type by execution:

//+------------------------------------------------------------------+
//| Return the order type description by execution                   |
//+------------------------------------------------------------------+
string OrderTypeFillingDescription(const ENUM_ORDER_TYPE_FILLING type_filling,const bool ext_descr=false)
  {
   string res=StringSubstr(EnumToString(type_filling),14);
//--- Convert all obtained symbols to lower case and replace the first letter from small to capital (for ORDER_FILLING_RETURN)
   if(type_filling==ORDER_FILLING_RETURN && res.Lower())
      res.SetChar(0,ushort(res.GetChar(0)-0x20));
   string descr="";
   switch(type_filling)
     {
      case ORDER_FILLING_FOK     :  descr=" (Fill or Kill. An order can be executed in the specified volume only)";                                               break;
      case ORDER_FILLING_IOC     :  descr=" (Immediate or Cancel. A deal with the volume maximally available in the market within that indicated in the order)";  break;
      case ORDER_FILLING_BOC     :  descr=" (Passive (Book or Cancel). The order can only be placed in the Depth of Market and cannot be immediately executed)";  break;
      case ORDER_FILLING_RETURN  :  descr=" (In case of partial filling, an order with remaining volume is not canceled but processed further)";                  break;
      default: break;
     }
   return res+(!ext_descr ? "" : descr);
   /* Sample output:
      Type filling: Return (In case of partial filling, an order with remaining volume is not canceled but processed further)
   */
  }

When sending a trade request using the OrderSend() function, the necessary volume execution policy can be set in the type_filling field, namely in the special MqlTradeRequest structure. The values from the ENUM_ORDER_TYPE_FILLING enumeration are available. To get the property value in a specific active/completed order, use the OrderGetInteger() or HistoryOrderGetInteger() function with the ORDER_TYPE_FILLING modifier.

Before sending an order with execution at the current time, in order for the ORDER_TYPE_FILLING value (execution type by volume) to be set correctly, we can use the SymbolInfoInteger() function for each financial instrument to get the value of the SYMBOL_FILLING_MODE property, which shows the volume filling types allowed for this symbol as a combination of flags. The ORDER_FILLING_RETURN filling type is enabled at all times except for the "Market execution" mode (SYMBOL_TRADE_EXECUTION_MARKET).


The function that returns a description of the trade transaction type:

//+------------------------------------------------------------------+
//| Return a description of the trade transaction type               |
//+------------------------------------------------------------------+
string TradeTransactionTypeDescription(const ENUM_TRADE_TRANSACTION_TYPE transaction,const bool ext_descr=false)
  {
//--- "Cut out" the transaction type from the string obtained from enum
   string res=StringSubstr(EnumToString(transaction),18);
//--- Convert all obtained symbols to lower case and replace the first letter from small to capital
   if(res.Lower())
      res.SetChar(0,ushort(res.GetChar(0)-0x20));
//--- Replace all underscore characters with space in the resulting line
   StringReplace(res,"_"," ");
   string descr="";
   switch(transaction)
     {
      case TRADE_TRANSACTION_ORDER_ADD       :  descr=" (Adding a new open order)";                                                                   break;
      case TRADE_TRANSACTION_ORDER_UPDATE    :  descr=" (Updating an open order)";                                                                    break;
      case TRADE_TRANSACTION_ORDER_DELETE    :  descr=" (Removing an order from the list of the open ones)";                                          break;
      case TRADE_TRANSACTION_DEAL_ADD        :  descr=" (Adding a deal to the history)";                                                              break;
      case TRADE_TRANSACTION_DEAL_UPDATE     :  descr=" (Updating a deal in the history)";                                                            break;
      case TRADE_TRANSACTION_DEAL_DELETE     :  descr=" (Deleting a deal from the history)";                                                          break;
      case TRADE_TRANSACTION_HISTORY_ADD     :  descr=" (Adding an order to the history as a result of execution or cancellation)";                   break;
      case TRADE_TRANSACTION_HISTORY_UPDATE  :  descr=" (Changing an order located in the orders history)";                                           break;
      case TRADE_TRANSACTION_HISTORY_DELETE  :  descr=" (Deleting an order from the orders history)";                                                 break;
      case TRADE_TRANSACTION_POSITION        :  descr=" (Changing a position not related to a deal execution)";                                       break;
      case TRADE_TRANSACTION_REQUEST         :  descr=" (The trade request has been processed by a server and processing result has been received)";  break;
      default: break;
     }
   return res+(!ext_descr ? "" : descr);
   /* Sample output:
      Order add (Adding a new open order)
   */
  }

The function returns descriptions of trade transaction types executed on the account.


The function that returns a description of the order status:

//+------------------------------------------------------------------+
//| Return an order state description                                |
//+------------------------------------------------------------------+
string OrderStateDescription(const ENUM_ORDER_STATE state,const bool ext_descr=false)
  {
//--- "Cut out" the order status from the string obtained from enum
   string res=StringSubstr(EnumToString(state),12);
//--- Convert all obtained symbols to lower case and replace the first letter from small to capital
   if(res.Lower())
      res.SetChar(0,ushort(res.GetChar(0)-0x20));
//--- Replace all underscore characters with space in the resulting line
   StringReplace(res,"_"," ");
   string descr="";
   switch(state)
     {
      case ORDER_STATE_STARTED         :  descr=" (Order checked, but not yet accepted by broker)";            break;
      case ORDER_STATE_PLACED          :  descr=" (Order accepted)";                                           break;
      case ORDER_STATE_CANCELED        :  descr=" (Order canceled by client)";                                 break;
      case ORDER_STATE_PARTIAL         :  descr=" (Order partially executed)";                                 break;
      case ORDER_STATE_FILLED          :  descr=" (Order fully executed)";                                     break;
      case ORDER_STATE_REJECTED        :  descr=" (Order rejected)";                                           break;
      case ORDER_STATE_EXPIRED         :  descr=" (Order expired)";                                            break;
      case ORDER_STATE_REQUEST_ADD     :  descr=" (Order is being registered (placing to the trading system))";break;
      case ORDER_STATE_REQUEST_MODIFY  :  descr=" (Order is being modified (changing its parameters))";        break;
      case ORDER_STATE_REQUEST_CANCEL  :  descr=" (Order is being deleted (deleting from the trading system))";break;
      default: break;
     }
   return res+(!ext_descr ? "" : descr);
   /* Sample output:
      Placed (Order accepted)
   */
  }

Each order has a status that describes its state. To get the information, use the OrderGetInteger() or HistoryOrderGetInteger() function with the ORDER_STATE modifier. Valid values are stored in the ENUM_ORDER_STATE enumeration. When sending a trade request to the server, the status of the order gradually changes. First, it is checked by the server, then placed in the trading system, and then triggered, generating a deal, or canceled. The list contains all possible order states, and they should be taken into account when working with trading orders.


The function that returns a description of the transaction type:

//+------------------------------------------------------------------+
//| Return the deal type description                                 |
//+------------------------------------------------------------------+
string DealTypeDescription(const ENUM_DEAL_TYPE type,const bool ext_descr=false)
  {
//--- "Cut out" the deal type from the string obtained from enum
   string res=StringSubstr(EnumToString(type),(type==DEAL_DIVIDEND || type==DEAL_DIVIDEND_FRANKED || type==DEAL_TAX ? 5 : 10));
//--- Convert all obtained symbols to lower case and replace the first letter from small to capital
   if(res.Lower())
      res.SetChar(0,ushort(res.GetChar(0)-0x20));
//--- Replace all underscore characters with space in the resulting line
   StringReplace(res,"_"," ");
   string descr="";
//--- Extended descriptions are added only to the deals whose description obtained from enum does not match the extended one
   switch(type)
     {
      case DEAL_TYPE_CHARGE                  : descr=" (Additional charge)";                          break;
      case DEAL_TYPE_COMMISSION              : descr=" (Additional commission)";                      break;
      case DEAL_TYPE_COMMISSION_DAILY        : descr=" (Daily commission)";                           break;
      case DEAL_TYPE_COMMISSION_MONTHLY      : descr=" (Monthly commission)";                         break;
      case DEAL_TYPE_COMMISSION_AGENT_DAILY  : descr=" (Daily agent commission)";                     break;
      case DEAL_TYPE_COMMISSION_AGENT_MONTHLY: descr=" (Monthly agent commission)";                   break;
      case DEAL_TYPE_INTEREST                : descr=" (Interest rate)";                              break;
      case DEAL_TYPE_BUY_CANCELED            : descr=" (Canceled buy deal)";                          break;
      case DEAL_TYPE_SELL_CANCELED           : descr=" (Canceled sell deal)";                         break;
      case DEAL_DIVIDEND                     : descr=" (Dividend operations)";                        break;
      case DEAL_DIVIDEND_FRANKED             : descr=" (Franked (non-taxable) dividend operations)";  break;
      case DEAL_TAX                          : descr=" (Tax charges)";                                break;
      default: break;
     }
   return res+(!ext_descr ? "" : descr);
   /* Sample output:
      Comission (Additional commission)
   */
  }

Each deal is characterized by a type. Possible values are listed in ENUM_DEAL_TYPE. To get data on deal type, use the HistoryDealGetInteger() function with the DEAL_TYPE modifier.


The function that returns a description of the order type by expiration:

//+------------------------------------------------------------------+
//| Return an order type description by expiration                   |
//+------------------------------------------------------------------+
string OrderTypeTimeDescription(const ENUM_ORDER_TYPE_TIME type_time,const bool ext_descr=false)
  {
//--- "Cut out" the order type by expiration from the string obtained from enum
   string res=StringSubstr(EnumToString(type_time),6);
//--- If the type by expiration is ORDER_TIME_GTC, add "Time GTC" to res
   if(type_time==ORDER_TIME_GTC)
      res="Time GTC";
//--- Otherwise, convert all obtained symbols to lower case and replace the first letter from small to capital
   else
     {
      if(res.Lower())
         res.SetChar(0,ushort(res.GetChar(0)-0x20));
      //--- Replace all underscore characters with space in the resulting line
      StringReplace(res,"_"," ");
     }
   string descr="";
   switch(type_time)
     {
      case ORDER_TIME_GTC           : descr=" (Good till cancel order)";                                          break;
      case ORDER_TIME_DAY           : descr=" (Good till current trade day order)";                               break;
      case ORDER_TIME_SPECIFIED     : descr=" (Good till expired order)";                                         break;
      case ORDER_TIME_SPECIFIED_DAY : descr=" (The order will be effective till 23:59:59 of the specified day)";  break;
      default: break;
     }
   return res+(!ext_descr ? "" : descr);
   /* Sample output:
      Time GTC (Good till cancel order)
   */
  }

Order lifetime can be set in the type_time field of the MqlTradeRequest structure when sending a trade request using the OrderSend() function. The values from the ENUM_ORDER_TYPE_TIME enumeration are valid. To get the property value, use the OrderGetInteger() or HistoryOrderGetInteger() function with the ORDER_TYPE_TIME modifier.

Trading transactions can occur with orders, deals and positions. There can be different types in each category of such transactions - addition, modification and deletion. But sometimes we only need to know what exactly the transaction was carried out with, without specifying details - with an order, a deal or a position. To achieve this, let's create a function that returns transaction affiliation:

//+------------------------------------------------------------------+
//| Define and set transaction affiliation values                    |
//+------------------------------------------------------------------+
void SetTransactionBelong(const ENUM_TRADE_TRANSACTION_TYPE type,bool &request_flag,bool &order_flag,bool &deal_flag,bool &position_flag)
  {
   switch(type)
     {
      //--- Request
      case TRADE_TRANSACTION_REQUEST         : request_flag=true; order_flag=false; deal_flag=false; position_flag=false; break;
      //--- Order
      case TRADE_TRANSACTION_ORDER_ADD       :
      case TRADE_TRANSACTION_ORDER_UPDATE    :
      case TRADE_TRANSACTION_ORDER_DELETE    :
      case TRADE_TRANSACTION_HISTORY_ADD     :
      case TRADE_TRANSACTION_HISTORY_UPDATE  :
      case TRADE_TRANSACTION_HISTORY_DELETE  : request_flag=false; order_flag=true; deal_flag=false; position_flag=false; break;
      //--- Deal
      case TRADE_TRANSACTION_DEAL_ADD        :
      case TRADE_TRANSACTION_DEAL_UPDATE     :
      case TRADE_TRANSACTION_DEAL_DELETE     : request_flag=false; order_flag=false; deal_flag=true; position_flag=false; break;
      //--- Position
      case TRADE_TRANSACTION_POSITION        : request_flag=false; order_flag=false; deal_flag=false; position_flag=true; break;
      //---
      default: break;
     }
  }

The function receives the transaction type and, by reference, three flag variables, which are set/reset depending on what exactly the transaction was carried out with.


The function that returns a description of the trade request type:

//+------------------------------------------------------------------+
//| Return the trade request type description                        |
//+------------------------------------------------------------------+
string TradeActionDescription(const ENUM_TRADE_REQUEST_ACTIONS action,const bool ext_descr=false)
  {
//--- "Cut out" the operation type from the string obtained from enum
   string res=StringSubstr(EnumToString(action),13);
//--- Convert all obtained symbols to lower case and replace the first letter from small to capital (except for TRADE_ACTION_SLTP)
   if(action!=TRADE_ACTION_SLTP && res.Lower())
      res.SetChar(0,ushort(res.GetChar(0)-0x20));
   string descr="";
   switch(action)
     {
      case TRADE_ACTION_DEAL     : descr=" (Place a trade order for an immediate execution with the specified parameters (market order))";break;
      case TRADE_ACTION_PENDING  : descr=" (Place a trade order for the execution under specified conditions (pending order))";           break;
      case TRADE_ACTION_SLTP     : descr=" (Modify Stop Loss and Take Profit values of an opened position)";                              break;
      case TRADE_ACTION_MODIFY   : descr=" (Modify the parameters of the order placed previously)";                                       break;
      case TRADE_ACTION_REMOVE   : descr=" (Delete the pending order placed previously)";                                                 break;
      case TRADE_ACTION_CLOSE_BY : descr=" (Close a position by an opposite one)";                                                        break;
      default: break;
     }
   return res+(!ext_descr ? "" : descr);
   /* Sample output:
      Pending (Place a trade order for the execution under specified conditions (pending order)):
   */
  }


Let's look at the structure of a trade request, the output of a description of its fields and create some trade functions that explain how to work with this structure.

MqlTradeRequest structure for creating a request when conducting trading operations

The interaction between the client terminal and the trading server in terms of order placement operations is carried out through trade requests. The request is represented by a special predefined MqlTradeRequest structure, which contains all the fields necessary for performing trades:

struct MqlTradeRequest
  {
   ENUM_TRADE_REQUEST_ACTIONS    action;           // Type of a performed action
   ulong                         magic;            // EA stamp (magic number ID)
   ulong                         order;            // Order ticket
   string                        symbol;           // Symbol name
   double                        volume;           // Requested volume of a deal in lots
   double                        price;            // Price 
   double                        stoplimit;        // StopLimit order level
   double                        sl;               // Stop Loss order level
   double                        tp;               // Take Profit order level
   ulong                         deviation;        // Maximum acceptable deviation from the requested price
   ENUM_ORDER_TYPE               type;             // Order type
   ENUM_ORDER_TYPE_FILLING       type_filling;     // Order filling type
   ENUM_ORDER_TYPE_TIME          type_time;        // Order lifetime type
   datetime                      expiration;       // Order expiration time (for ORDER_TIME_SPECIFIED type orders)
   string                        comment;          // Order comment
   ulong                         position;         // Position ticket
   ulong                         position_by;      // Opposite position ticket
  };

To send a trade order to the server, we need to fill out the structure fields in the appropriate way for the order. The 'action' field should be filled in. Depending on what action is written in this field, other fields of the structure necessary for this action are filled in as well. Accordingly, unused fields will contain zeros (provided that the fields of the structure are reset to zero before filling it). When displaying a description of the structure fields, some fields are not used and contain zeros. This should be displayed when sending an unused field to the log. For example, instead of using the number of decimal places for the value (1.12345), use only one (0.0) - this will make it clearer that this field is simply not filled.

However, in the final output of the structure to the log, we will take into account unused fields depending on the transaction, and simply not display them.


Type of a performed action

Trading operation type. The value can be one of the ENUM_TRADE_REQUEST_ACTIONS enumeration values.

//+------------------------------------------------------------------+
//| Return the type of a performed action as a string                |
//+------------------------------------------------------------------+
string MqlTradeRequestAction(const MqlTradeRequest &req,const uint header_width=0,const uint indent=0,const bool ext_descr=false)
  {
//--- Define the header text and the width of the header field
//--- If the header width is passed to the function equal to zero, then the width will be the size of the header line + 1
   string header="Action:";
   uint w=(header_width==0 ? header.Length()+1 : header_width);
//--- Return the property value with a header having the required width and indentation
   return StringFormat("%*s%-*s%-s",indent,"",w,header,TradeActionDescription(req.action,ext_descr));
   /* Sample output:
      Action: Pending (Place a trade order for the execution under specified conditions (pending order)):
   */
  }


Stamp of an Expert Advisor (the magic number identifier)

Expert ID. Allows arranging the analytical handling of trading orders. Each EA can set a unique ID when sending a trading request.
//+------------------------------------------------------------------+
//| Return the EA stamp (magic number) as a string                   |
//+------------------------------------------------------------------+
string MqlTradeRequestMagic(const MqlTradeRequest &req,const uint header_width=0,const uint indent=0)
  {
//--- Define the header text and the width of the header field
//--- If the header width is passed to the function equal to zero, then the width will be the size of the header line + 1
   string header="Magic:";
   uint w=(header_width==0 ? header.Length()+1 : header_width);
//--- Return the property value with a header having the required width and indentation
   return StringFormat("%*s%-*s%-llu",indent,"",w,header,req.magic);
   /* Sample output:
      Magic: 1024
   */
  }


Order ticket

Order ticket. Required for modifying pending orders.

//+------------------------------------------------------------------+
//| Return the order ticket as a string                              |
//+------------------------------------------------------------------+
string MqlTradeRequestOrder(const MqlTradeRequest &req,const uint header_width=0,const uint indent=0)
  {
//--- Define the header text and the width of the header field
//--- If the header width is passed to the function equal to zero, then the width will be the size of the header line + 1
   string header="Order:";
   uint w=(header_width==0 ? header.Length()+1 : header_width);
//--- Return the property value with a header having the required width and indentation
   return StringFormat("%*s%-*s%-llu",indent,"",w,header,req.order);
   /* Sample output:
      Order: 1835982676
   */
  }


Name of a trading instrument

The name of the trading instrument, for which the order is placed. Not required when modifying orders and closing positions.

//+------------------------------------------------------------------+
//| Return a trading instrument name                                 |
//+------------------------------------------------------------------+
string MqlTradeRequestSymbol(const MqlTradeRequest &req,const uint header_width=0,const uint indent=0)
  {
//--- Define the header text and the width of the header field
//--- If the header width is passed to the function equal to zero, then the width will be the size of the header line + 1
   string header="Symbol:";
   uint w=(header_width==0 ? header.Length()+1 : header_width);
//--- Return the property value with a header having the required width and indentation
   return StringFormat("%*s%-*s%-s",indent,"",w,header,req.symbol);
   /* Sample output:
      Symbol: EURUSD
   */
  }


Requested volume of a deal in lots

Requested volume of a deal in lots. A real volume when making a deal depends on an order execution type.

//+------------------------------------------------------------------+
//| Return the requested volume of a deal in lots as a string        |
//+------------------------------------------------------------------+
string MqlTradeRequestVolume(const MqlTradeRequest &req,const uint header_width=0,const uint indent=0)
  {
//--- Define the header text and the width of the header field
//--- If the header width is passed to the function equal to zero, then the width will be the size of the header line + 1
   string header="Volume:";
   uint w=(header_width==0 ? header.Length()+1 : header_width);
//--- Get the number of decimal places in the lot value. If the character is not specified, use 1
   int dg=(int)ceil(fabs(log10(SymbolInfoDouble(req.symbol,SYMBOL_VOLUME_STEP))));
//--- Return the property value with a header having the required width and indentation
   return StringFormat("%*s%-*s%-.*f",indent,"",w,header,dg,req.volume);
   /* Sample output:
      Volume: 0.10
   */
  }


Price

Price, at which an order should be executed. The price is not set in case of market orders for instruments of the "Market Execution" (SYMBOL_TRADE_EXECUTION_MARKET) type having the TRADE_ACTION_DEAL type.

//+------------------------------------------------------------------+
//| Return the price as a string                                     |
//+------------------------------------------------------------------+
string MqlTradeRequestPrice(const MqlTradeRequest &req,const uint header_width=0,const uint indent=0)
  {
//--- Define the header text and the width of the header field
//--- If the header width is passed to the function equal to zero, then the width will be the size of the header line + 1
   string header="Price:";
   uint w=(header_width==0 ? header.Length()+1 : header_width);
//--- Get the number of decimal places
   int dg=(req.price!=0 ? (int)SymbolInfoInteger(req.symbol,SYMBOL_DIGITS) : 1);
//--- Return the property value with a header having the required width and indentation
   return StringFormat("%*s%-*s%-.*f",indent,"",w,header,dg,req.price);
   /* Sample output:
      Price: 1.09586
   */
  }


StopLimit order level

For BuyStopLimit and SellStopLimit pending orders. A price a pending Limit order is set at when the price reaches the 'price' value (this condition is mandatory). The pending order is not passed to the trading system until that moment.

//+------------------------------------------------------------------+
//| Return the StopLimit order level as a string                     |
//+------------------------------------------------------------------+
string MqlTradeRequestStopLimit(const MqlTradeRequest &req,const uint header_width=0,const uint indent=0)
  {
//--- Define the header text and the width of the header field
//--- If the header width is passed to the function equal to zero, then the width will be the size of the header line + 1
   string header="StopLimit:";
   uint w=(header_width==0 ? header.Length()+1 : header_width);
//--- Get the number of decimal places
   int dg=(req.stoplimit!=0 ? (int)SymbolInfoInteger(req.symbol,SYMBOL_DIGITS) : 1);
//--- Return the property value with a header having the required width and indentation
   return StringFormat("%*s%-*s%-.*f",indent,"",w,header,dg,req.stoplimit);
   /* Sample output:
      StopLimit: 0.0
   */
  }


Stop Loss order level

A price a Stop Loss order is activated at when the price moves in an unfavorable direction.

//+------------------------------------------------------------------+
//| Return the Stop Loss order level as a string                     |
//+------------------------------------------------------------------+
string MqlTradeRequestStopLoss(const MqlTradeRequest &req,const uint header_width=0,const uint indent=0)
  {
//--- Define the header text and the width of the header field
//--- If the header width is passed to the function equal to zero, then the width will be the size of the header line + 1
   string header="SL:";
   uint w=(header_width==0 ? header.Length()+1 : header_width);
//--- Get the number of decimal places
   int dg=(req.sl!=0 ? (int)SymbolInfoInteger(req.symbol,SYMBOL_DIGITS) : 1);
//--- Return the property value with a header having the required width and indentation
   return StringFormat("%*s%-*s%-.*f",indent,"",w,header,dg,req.sl);
   /* Sample output:
      SL: 0.0
   */
  }


Take Profit order level

A price a Take Profit order is activated at when the price moves in a favorable direction.

//+------------------------------------------------------------------+
//| Return the Take Profit order level as a string                   |
//+------------------------------------------------------------------+
string MqlTradeRequestTakeProfit(const MqlTradeRequest &req,const uint header_width=0,const uint indent=0)
  {
//--- Define the header text and the width of the header field
//--- If the header width is passed to the function equal to zero, then the width will be the size of the header line + 1
   string header="TP:";
   uint w=(header_width==0 ? header.Length()+1 : header_width);
//--- Get the number of decimal places
   int dg=(req.tp!=0 ? (int)SymbolInfoInteger(req.symbol,SYMBOL_DIGITS) : 1);
//--- Return the property value with a header having the required width and indentation
   return StringFormat("%*s%-*s%-.*f",indent,"",w,header,dg,req.tp);
   /* Sample output:
      TP: 0.0
   */
  }


Deviation from the requested price

Maximum acceptable deviation from the requested price, specified in points.

//+------------------------------------------------------------------+
//| Return the maximum acceptable                                    |
//| deviation from the requested price                               |
//+------------------------------------------------------------------+
string MqlTradeRequestDeviation(const MqlTradeRequest &req,const uint header_width=0,const uint indent=0)
  {
//--- Define the header text and the width of the header field
//--- If the header width is passed to the function equal to zero, then the width will be the size of the header line + 1
   string header="Deviation:";
   uint w=(header_width==0 ? header.Length()+1 : header_width);
//--- Return the property value with a header having the required width and indentation
   return StringFormat("%*s%-*s%-llu",indent,"",w,header,req.deviation);
   /* Sample output:
      Deviation: 5
   */
  }


Order type

Order type. The value can be one of the ENUM_ORDER_TYPE enumeration values.

//+------------------------------------------------------------------+
//| Return the order type as a string                                |
//+------------------------------------------------------------------+
string MqlTradeRequestType(const MqlTradeRequest &req,const uint header_width=0,const uint indent=0,bool ext_descr=false)
  {
//--- Define the header text and the width of the header field
//--- If the header width is passed to the function equal to zero, then the width will be the size of the header line + 1
   string header="Type:";
   uint w=(header_width==0 ? header.Length()+1 : header_width);
//--- Return the property value with a header having the required width and indentation
   return StringFormat("%*s%-*s%-s",indent,"",w,header,OrderTypeDescription(req.type,ext_descr));
   /* Sample output:
      Type: Buy Limit (Buy Limit pending order)
   */
  }


Order filling type

Order filling type. The value can be one of the ENUM_ORDER_TYPE_FILLING values.

//+------------------------------------------------------------------+
//| Return the order type by execution as a string                   |
//+------------------------------------------------------------------+
string MqlTradeRequestTypeFilling(const MqlTradeRequest &req,const uint header_width=0,const uint indent=0,bool ext_descr=false)
  {
//--- Define the header text and the width of the header field
//--- If the header width is passed to the function equal to zero, then the width will be the size of the header line + 1
   string header="Type filling:";
   uint w=(header_width==0 ? header.Length()+1 : header_width);
//--- Return the property value with a header having the required width and indentation
   return StringFormat("%*s%-*s%-s",indent,"",w,header,OrderTypeFillingDescription(req.type_filling,ext_descr));
   /* Sample output:
      Type filling: Return (In case of partial filling, an order with remaining volume is not canceled but processed further)
   */
  }


Order lifetime type

Order type by expiration. The value can be one of the ENUM_ORDER_TYPE_TIME values.

//+------------------------------------------------------------------+
//| Return the order type by expiration time as a string             |
//+------------------------------------------------------------------+
string MqlTradeRequestTypeTime(const MqlTradeRequest &req,const uint header_width=0,const uint indent=0,bool ext_descr=false)
  {
//--- Define the header text and the width of the header field
//--- If the header width is passed to the function equal to zero, then the width will be the size of the header line + 1
   string header="Type time:";
   uint w=(header_width==0 ? header.Length()+1 : header_width);
//--- Return the property value with a header having the required width and indentation
   return StringFormat("%*s%-*s%-s",indent,"",w,header,OrderTypeTimeDescription(req.type_time,ext_descr));
   /* Sample output:
      Type time: Time GTC (Good till cancel order)
   */
  }


Order expiration date

Pending order expiration time (for ORDER_TIME_SPECIFIED type orders).

//+------------------------------------------------------------------+
//| Return the order expiration time as a string                     |
//+------------------------------------------------------------------+
string MqlTradeRequestExpiration(const MqlTradeRequest &req,const uint header_width=0,const uint indent=0)
  {
//--- Define the header text and the width of the header field
//--- If the header width is passed to the function equal to zero, then the width will be the size of the header line + 1
   string header="Expiration:";
   uint w=(header_width==0 ? header.Length()+1 : header_width);
//--- Return the property value with a header having the required width and indentation
   return StringFormat("%*s%-*s%-s",indent,"",w,header,(req.type_time==ORDER_TIME_SPECIFIED || req.type_time==ORDER_TIME_SPECIFIED_DAY ? (string)req.expiration : "0"));
   /* Sample output:
      Expiration: 0
   */
  }


Comment to an order

//+------------------------------------------------------------------+
//| Return the order comment                                         |
//+------------------------------------------------------------------+
string MqlTradeRequestComment(const MqlTradeRequest &req,const uint header_width=0,const uint indent=0)
  {
//--- Define the header text and the width of the header field
//--- If the header width is passed to the function equal to zero, then the width will be the size of the header line + 1
   string header="Comment:";
   uint w=(header_width==0 ? header.Length()+1 : header_width);
//--- Return the property value with a header having the required width and indentation
   return StringFormat("%*s%-*s%-s",indent,"",w,header,req.comment);
   /* Sample output:
      Comment: TestMqlTradeTransaction
   */
  }


Position ticket

Position ticket. Fill it when changing and closing a position for its clear identification. Usually, it is the same as the ticket of the order that opened the position.

//+------------------------------------------------------------------+
//| Return the position ticket as a string                           |
//+------------------------------------------------------------------+
string MqlTradeRequestPosition(const MqlTradeRequest &req,const uint header_width=0,const uint indent=0)
  {
//--- Define the header text and the width of the header field
//--- If the header width is passed to the function equal to zero, then the width will be the size of the header line + 1
   string header="Position:";
   uint w=(header_width==0 ? header.Length()+1 : header_width);
//--- Return the property value with a header having the required width and indentation
   return StringFormat("%*s%-*s%-llu",indent,"",w,header,req.position);
   /* Sample output:
      Position: 1835982676
   */
  }


Opposite position ticket

Opposite position ticket. It is used when closing a position by an opposite one (opened at the same symbol but in the opposite direction).

//+------------------------------------------------------------------+
//| Return the opposite position ticket as a string                  |
//+------------------------------------------------------------------+
string MqlTradeRequestPositionBy(const MqlTradeRequest &req,const uint header_width=0,const uint indent=0)
  {
//--- Define the header text and the width of the header field
//--- If the header width is passed to the function equal to zero, then the width will be the size of the header line + 1
   string header="Position by:";
   uint w=(header_width==0 ? header.Length()+1 : header_width);
//--- Return the property value with a header having the required width and indentation
   return StringFormat("%*s%-*s%-llu",indent,"",w,header,req.position_by);
   /* Sample output:
      Position by: 1835987626
   */
  }


Examples of use

For different types of trading orders, the structure fields are filled in individually according to the order. Let's look at some examples: we are going to create a function that logs descriptions of structure fields depending on the action being performed and trading functions for opening/closing positions and setting/removing pending orders.

The function that displays the trading request description in the journal:

//+------------------------------------------------------------------+
//| Display the trading request description in the journal           |
//+------------------------------------------------------------------+
void TradeRequestPrint(const MqlTradeRequest &req,const uint header_width=0,const uint indent=0,bool ext_descr=false)
  {
//--- Get execution type by symbol
   ENUM_SYMBOL_TRADE_EXECUTION exemode=(ENUM_SYMBOL_TRADE_EXECUTION)SymbolInfoInteger(req.symbol,SYMBOL_TRADE_EXEMODE);
//--- Header
   PrintFormat("Request %s:",TradeActionDescription(req.action,ext_descr));
//--- Place an order for an instant deal with the specified parameters (set a market order)
   if(req.action==TRADE_ACTION_DEAL)
     {
      //--- Depending on the transaction mode 
      switch(exemode)
        {
         //--- Trade order to open a position in the Request Execution mode
         //--- Trade order to open a position in the Instant Execution mode
         case SYMBOL_TRADE_EXECUTION_REQUEST    :
         case SYMBOL_TRADE_EXECUTION_INSTANT    :
            Print(MqlTradeRequestSymbol(req,header_width,indent));
            Print(MqlTradeRequestVolume(req,header_width,indent));
            Print(MqlTradeRequestPrice(req,header_width,indent));
            if(req.position==0)  // Only for opening a position 
               Print(MqlTradeRequestStopLoss(req,header_width,indent));
            if(req.position==0)  // Only for opening a position 
               Print(MqlTradeRequestTakeProfit(req,header_width,indent));
            Print(MqlTradeRequestDeviation(req,header_width,indent));
            Print(MqlTradeRequestType(req,header_width,indent,ext_descr));
            Print(MqlTradeRequestTypeFilling(req,header_width,indent,ext_descr));
            Print(MqlTradeRequestMagic(req,header_width,indent));
            if(req.position==0)  // Only for opening a position 
               Print(MqlTradeRequestComment(req,header_width,indent));
            //--- Closing
            if(req.position!=0)
               Print(MqlTradeRequestPosition(req,header_width,indent));
           break;
         //--- Trade order to open a position in the Market Execution mode
         //--- Trade order to open a position in the Exchange Execution mode
         case SYMBOL_TRADE_EXECUTION_MARKET     :
         case SYMBOL_TRADE_EXECUTION_EXCHANGE   :
            Print(MqlTradeRequestSymbol(req,header_width,indent));
            Print(MqlTradeRequestVolume(req,header_width,indent));
            Print(MqlTradeRequestType(req,header_width,indent,ext_descr));
            Print(MqlTradeRequestTypeFilling(req,header_width,indent,ext_descr));
            Print(MqlTradeRequestMagic(req,header_width,indent));
            if(req.position==0)  // Only for opening a position 
               Print(MqlTradeRequestComment(req,header_width,indent));
            //--- Closing
            if(req.position!=0)
               Print(MqlTradeRequestPosition(req,header_width,indent));
           break;
         default:
           break;
        }
     }
//--- Trading order to close a position by an opposite one
   if(req.action==TRADE_ACTION_CLOSE_BY)
     {
      Print(MqlTradeRequestSymbol(req,header_width,indent));
      Print(MqlTradeRequestPosition(req,header_width,indent));
      Print(MqlTradeRequestMagic(req,header_width,indent));
      Print(MqlTradeRequestComment(req,header_width,indent));
      Print(MqlTradeRequestPositionBy(req,header_width,indent));
     }
//--- Trading order to modify StopLoss and/or TakeProfit levels
   if(req.action==TRADE_ACTION_SLTP)
     {
      Print(MqlTradeRequestSymbol(req,header_width,indent));
      Print(MqlTradeRequestStopLoss(req,header_width,indent));
      Print(MqlTradeRequestTakeProfit(req,header_width,indent));
      Print(MqlTradeRequestPosition(req,header_width,indent));
     }
//--- Trading order to place a pending order
   if(req.action==TRADE_ACTION_PENDING)
     {
      Print(MqlTradeRequestSymbol(req,header_width,indent));
      Print(MqlTradeRequestVolume(req,header_width,indent));
      Print(MqlTradeRequestPrice(req,header_width,indent));
      if(req.type==ORDER_TYPE_BUY_STOP_LIMIT || req.type==ORDER_TYPE_SELL_STOP_LIMIT)
         Print(MqlTradeRequestStopLimit(req,header_width,indent));
      Print(MqlTradeRequestStopLoss(req,header_width,indent));
      Print(MqlTradeRequestTakeProfit(req,header_width,indent));
      Print(MqlTradeRequestType(req,header_width,indent,ext_descr));
      Print(MqlTradeRequestTypeFilling(req,header_width,indent,ext_descr));
      Print(MqlTradeRequestTypeTime(req,header_width,indent,ext_descr));
      Print(MqlTradeRequestExpiration(req,header_width,indent));
      Print(MqlTradeRequestMagic(req,header_width,indent));
      Print(MqlTradeRequestComment(req,header_width,indent));
     }
//--- Trading order to modify the price levels of a pending order
   if(req.action==TRADE_ACTION_MODIFY)
     {
      Print(MqlTradeRequestOrder(req,header_width,indent));
      Print(MqlTradeRequestPrice(req,header_width,indent));
      if(req.stoplimit!=0)
         Print(MqlTradeRequestStopLimit(req,header_width,indent));
      Print(MqlTradeRequestStopLoss(req,header_width,indent));
      Print(MqlTradeRequestTakeProfit(req,header_width,indent));
      Print(MqlTradeRequestTypeTime(req,header_width,indent,ext_descr));
      Print(MqlTradeRequestExpiration(req,header_width,indent));
     }
//--- Trading order to remove a pending order
   if(req.action==TRADE_ACTION_REMOVE)
     {
      Print(MqlTradeRequestOrder(req,header_width,indent));
     }
   /* Sample output:
      Request Pending (Place a trade order for the execution under specified conditions (pending order)):
        Symbol: EURUSD
        Volume: 0.10
        Price: 1.09586
        SL: 0.0
        TP: 0.0
        Type: Buy Limit (Buy Limit pending order)
        Type filling: Return (In case of partial filling, an order with remaining volume is not canceled but processed further)
        Type time: Time GTC (Good till cancel order)
        Expiration: 0
        Magic: 1024
        Comment: TestMqlTradeTransaction
   */
  }


OpenBuy to open a buy position:

//+------------------------------------------------------------------+
//| Open Buy position                                                |
//+------------------------------------------------------------------+
bool OpenBuy(const string symbol,const double lots,const uint magic,const ulong deviation=5,const string comment="")
  {
//--- Set a symbol for a request
   string symbol_name=(symbol==NULL ? Symbol() : symbol);
//--- Declare and initialize the request and result structures
   MqlTradeRequest request={};
   MqlTradeResult  result={};
//--- Get the normalized price
   double price=NormalizeDouble(SymbolInfoDouble(symbol_name,SYMBOL_ASK),(int)SymbolInfoInteger(symbol_name,SYMBOL_DIGITS));
//--- request parameters
   request.action    =  TRADE_ACTION_DEAL;                        // trading operation type
   request.symbol    =  symbol_name;                              // symbol
   request.volume    =  lots;                                     // position volume
   request.type      =  ORDER_TYPE_BUY;                           // order type
   request.price     =  price;                                    // open price
   request.deviation =  deviation;                                // allowed deviation from the price
   request.magic     =  magic;                                    // order MagicNumber
   request.comment   =  comment;                                  // Order comment
//--- If failed to send a request, inform of that and return 'false'
   if(!OrderSend(request,result))
     {
      PrintFormat("%s: OrderSend for %s %s error: %s",__FUNCTION__,
                  TradeActionDescription(request.action),OrderTypeDescription(request.type),RetcodeDescription(result.retcode,true)
                 );
      return false;
     }
//--- OrderSend OK, return 'true'
   return true;
  }


OpenSell to open a sell position:

//+------------------------------------------------------------------+
//| Open a Sell position                                             |
//+------------------------------------------------------------------+
bool OpenSell(const string symbol,const double lots,const uint magic,const ulong deviation=5,const string comment="")
  {
//--- Set a symbol for a request
   string symbol_name=(symbol==NULL ? Symbol() : symbol);
//--- Declare and initialize the request and result structures
   MqlTradeRequest request={};
   MqlTradeResult  result={};
//--- Get the normalized price
   double price=NormalizeDouble(SymbolInfoDouble(symbol_name,SYMBOL_BID),(int)SymbolInfoInteger(symbol_name,SYMBOL_DIGITS));
//--- request parameters
   request.action    =  TRADE_ACTION_DEAL;                        // trading operation type
   request.symbol    =  symbol_name;                              // symbol
   request.volume    =  lots;                                     // position volume
   request.type      =  ORDER_TYPE_SELL;                          // order type
   request.price     =  price;                                    // open price
   request.deviation =  deviation;                                // allowed deviation from the price
   request.magic     =  magic;                                    // order MagicNumber
   request.comment   =  comment;                                  // Order comment
//--- If failed to send a request, inform of that and return 'false'
   if(!OrderSend(request,result))
     {
      PrintFormat("%s: OrderSend for %s %s error: %s",__FUNCTION__,
                  TradeActionDescription(request.action),OrderTypeDescription(request.type),RetcodeDescription(result.retcode,true)
                 );
      return false;
     }
//--- OrderSend OK, return 'true'
   return true;
  }


ClosePosition to close a specified position:

//+------------------------------------------------------------------+
//| Close position by ticket                                         |
//+------------------------------------------------------------------+
bool ClosePosition(const ulong ticket,const ulong deviation=5)
  {
//--- Declare request and result structures
   MqlTradeRequest request;
   MqlTradeResult  result;
//--- If failed to select a position by ticket, inform of the error and leave
   ResetLastError();
   if(!PositionSelectByTicket(ticket))
     {
      Print("PositionSelectByTicket failed, error: ",(string)GetLastError());
      return false;
     }
//--- Get the data on the closed position
   string position_symbol=PositionGetString(POSITION_SYMBOL);                       // symbol 
   int    digits=(int)SymbolInfoInteger(position_symbol,SYMBOL_DIGITS);             // number of decimal places
   double volume=PositionGetDouble(POSITION_VOLUME);                                // position volume
   ENUM_POSITION_TYPE type=(ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);   // position type
   ulong magic=PositionGetInteger(POSITION_MAGIC);                                  // position MagicNumber
//--- Set request and result values to zero
   ZeroMemory(request);
   ZeroMemory(result);
//--- Set operation parameters
   request.action    =  TRADE_ACTION_DEAL;   // trading operation type
   request.position  =  ticket;              // position ticket
   request.symbol    =  position_symbol;     // symbol 
   request.volume    =  volume;              // position volume
   request.deviation =  deviation;           // allowed deviation from the price
   request.magic     =  magic;               // position MagicNumber
//--- Set order price and type depending on the position type
   if(type==POSITION_TYPE_BUY)
     {
      request.price=NormalizeDouble(SymbolInfoDouble(position_symbol,SYMBOL_BID),digits);
      request.type =ORDER_TYPE_SELL;
     }
   else
     {
      request.price=NormalizeDouble(SymbolInfoDouble(position_symbol,SYMBOL_ASK),digits);
      request.type =ORDER_TYPE_BUY;
     }
//--- If failed to send a request, inform of that and return 'false'
   if(!OrderSend(request,result))
     {
      PrintFormat("%s: OrderSend for %s %s #%llu error: %s",__FUNCTION__,
                  TradeActionDescription(request.action),OrderTypeDescription(request.type),ticket,RetcodeDescription(result.retcode,true)
                 );
      return false;
     }
//--- OrderSend OK, return 'true'
   return true;
  }


ClosePositionsAll to close all positions:

//+------------------------------------------------------------------+
//| Close all positions on a symbol                                  |
//+------------------------------------------------------------------+
uint ClosePositionsAll(const string symbol,const ulong magic=0)
  {
//--- Declare the variable for storing the result and the number of closed positions
   bool res=true;
   uint num=0;
//--- iterate over open positions
   int total=PositionsTotal();                     // number of open positions
   for(int i=total-1; i>=0; i--)
     {
      ulong  position_ticket=PositionGetTicket(i); // position ticket
      if(position_ticket==0)
         continue;
      if(PositionGetInteger(POSITION_MAGIC)!=magic)
         continue;
      //--- If MagicNumber is not specified (zero) or position magic number matches the specified one
      if(magic==0 || PositionGetInteger(POSITION_MAGIC)==magic)
        {
         //--- if the position symbol matches the one passed or NULL is passed, close the position on the ticket 
         if(symbol==NULL || PositionGetString(POSITION_SYMBOL)==symbol)
           {
            //--- Get the result of closing a position on a ticket and increase the counter of closed positions if successful 
            res &=ClosePosition(position_ticket);
            if(res)
               num++;
           }
        }
     }
   if(!res)
      PrintFormat("%s: Not all positions were able to close without errors",__FUNCTION__);
   return num;
  }


SetPending for placing a pending order:

//+------------------------------------------------------------------+
//| Place a specified pending order                                  |
//+------------------------------------------------------------------+
bool SetPending(const string symbol,const ENUM_ORDER_TYPE order_type,const double lots,const uint magic,const int distance,const int stoplimit=0,const string comment="")
  {
//--- Set a symbol for a request
   string symbol_name=(symbol==NULL ? Symbol() : symbol);
//--- Declare and initialize the request and result structures
   MqlTradeRequest request={};
   MqlTradeResult  result={};
//--- Set operation parameters
   request.action    =  TRADE_ACTION_PENDING;                     // trading operation type
   request.type      =  order_type;                               // order type
   request.symbol    =  symbol_name;                              // symbol
   request.volume    =  lots;                                     // volume
   request.magic     =  magic;                                    // order MagicNumber
   request.comment   =  comment;                                  // Order comment
//--- Get Point and Digits
   double point=SymbolInfoDouble(symbol_name,SYMBOL_POINT);
   int digits=(int)SymbolInfoInteger(symbol_name,SYMBOL_DIGITS);
//--- Calculate the distance from the price depending on the order type
//--- Buy order
   if(order_type==ORDER_TYPE_BUY_STOP)
      request.price=NormalizeDouble(SymbolInfoDouble(symbol_name,SYMBOL_ASK)+distance*point,digits);  // normalized setting price 
   if(order_type==ORDER_TYPE_BUY_LIMIT)
      request.price=NormalizeDouble(SymbolInfoDouble(symbol_name,SYMBOL_ASK)-distance*point,digits);  // normalized setting price 
   if(order_type==ORDER_TYPE_BUY_STOP_LIMIT)
     {
      request.price=NormalizeDouble(SymbolInfoDouble(symbol_name,SYMBOL_ASK)+distance*point,digits);  // normalized Stop order trigger price 
      request.stoplimit=NormalizeDouble(request.price-stoplimit*point,digits);                        // normalized Limit order setting price
     }
//--- Sell order
   if(order_type==ORDER_TYPE_SELL_STOP)
      request.price=NormalizeDouble(SymbolInfoDouble(symbol_name,SYMBOL_BID)-distance*point,digits);  // normalized setting price 
   if(order_type==ORDER_TYPE_SELL_LIMIT)
      request.price=NormalizeDouble(SymbolInfoDouble(symbol_name,SYMBOL_BID)+distance*point,digits);  // normalized setting price 
   if(order_type==ORDER_TYPE_SELL_STOP_LIMIT)
     {
      request.price=NormalizeDouble(SymbolInfoDouble(symbol_name,SYMBOL_BID)-distance*point,digits);  // normalized Stop order trigger price 
      request.stoplimit=NormalizeDouble(request.price+stoplimit*point,digits);                        // normalized Limit order setting price
     }
//--- If failed to send a request, inform of that and return 'false'
   if(!OrderSend(request,result))
     {
      PrintFormat("%s: OrderSend for %s %s error: %s",__FUNCTION__,
                  TradeActionDescription(request.action),OrderTypeDescription(request.type),RetcodeDescription(result.retcode,true)
                 );
      return false;
     }
//--- OrderSend OK, return 'true'
   return true;
  }


Based on the function presented above, you can create functions that set a specific type of a pending order:

SetBuyStop to set BuyStop pending order:

//+------------------------------------------------------------------+
//| Place BuyStop pending order                                      |
//+------------------------------------------------------------------+
bool SetBuyStop(const string symbol,const double lots,const uint magic,const int distance,const string comment="")
  {
//--- Call the SetPending function with BuyStop parameters
   return SetPending(symbol,ORDER_TYPE_BUY_STOP,lots,magic,distance,0,comment);
  }


SetBuyLimit to set BuyLimit pending order:

//+------------------------------------------------------------------+
//| Place BuyLimit pending order                                     |
//+------------------------------------------------------------------+
bool SetBuyLimit(const string symbol,const double lots,const uint magic,const int distance,const string comment="")
  {
//--- Call the SetPending function with BuyLimit parameters
   return SetPending(symbol,ORDER_TYPE_BUY_LIMIT,lots,magic,distance,0,comment);
  }


SetBuyStopLimit to set BuyStopLimit pending order:

//+------------------------------------------------------------------+
//| Place BuyStopLimit pending order                                 |
//+------------------------------------------------------------------+
bool SetBuyStopLimit(const string symbol,const double lots,const uint magic,const int distance,const int stoplimit,const string comment="")
  {
//--- Call the SetPending function with BuyStopLimit parameters
   return SetPending(symbol,ORDER_TYPE_BUY_STOP_LIMIT,lots,magic,distance,stoplimit,comment);
  }


SetSellStop to set SellStop pending order:

//+------------------------------------------------------------------+
//| Place SellStop pending order                                     |
//+------------------------------------------------------------------+
bool SetSellSellStop(const string symbol,const double lots,const uint magic,const int distance,const string comment="")
  {
//--- Call the SetPending function with SellStop parameters
   return SetPending(symbol,ORDER_TYPE_SELL_STOP,lots,magic,distance,0,comment);
  }


SetSellLimit to set SellLimit pending order:

//+------------------------------------------------------------------+
//| Place SellLimit pending order                                    |
//+------------------------------------------------------------------+
bool SetSellLimit(const string symbol,const double lots,const uint magic,const int distance,const string comment="")
  {
//--- Call the SetPending function with SellLimit parameters
   return SetPending(symbol,ORDER_TYPE_SELL_LIMIT,lots,magic,distance,0,comment);
  }


SetSellStopLimit to set SellStopLimit pending order:

//+------------------------------------------------------------------+
//| Place SellStopLimit pending order                                |
//+------------------------------------------------------------------+
bool SetSellStopLimit(const string symbol,const double lots,const uint magic,const int distance,const int stoplimit,const string comment="")
  {
//--- Call the SetPending function with SellStopLimit parameters
   return SetPending(symbol,ORDER_TYPE_SELL_STOP_LIMIT,lots,magic,distance,stoplimit,comment);
  }


DeleteOrder to remove a specified pending order:

//+------------------------------------------------------------------+
//| Delete a pending order by ticket                                 |
//+------------------------------------------------------------------+
bool DeleteOrder(const ulong ticket)
  {
//--- declare and initialize the trade request and result
   MqlTradeRequest request={};
   MqlTradeResult  result={};
   ResetLastError();
//--- If failed to select an order by ticket, inform of the error and leave
   if(!OrderSelect(ticket))
     {
      PrintFormat("%s: OrderSelect failed, error: %d",__FUNCTION__,GetLastError());
      return false;
     }
//--- Set request and result values to zero
   ZeroMemory(request);
   ZeroMemory(result);
//--- Set operation parameters
   request.action=TRADE_ACTION_REMOVE;                   // trading operation type
   request.order = ticket;                               // order ticket
//--- If failed to send a request, inform of that and return 'false'
   if(!OrderSend(request,result))
     {
      PrintFormat("%s: OrderSend for %s %s #%llu error: %s",__FUNCTION__,
                  TradeActionDescription(request.action),OrderTypeDescription((ENUM_ORDER_TYPE)OrderGetInteger(ORDER_TYPE)),ticket,RetcodeDescription(result.retcode,true)
                 );
      return false;
     }
//--- OrderSend OK, return 'true'
   return true;
  }


DeleteOrdersAll to remove all pending orders:

//+------------------------------------------------------------------+
//| Delete all pending orders by symbol                              |
//+------------------------------------------------------------------+
uint DeleteOrdersAll(const string symbol,const long magic=WRONG_VALUE)
  {
//--- Declare the variable for storing the result and the number of removed orders
   bool res=true;
   uint num=0;
//--- iterate over all placed pending orders
   int total=OrdersTotal(); // number of pending orders
   for(int i=total-1; i>=0; i--)
     {
      ulong  order_ticket=OrderGetTicket(i);             // order ticket
      if(order_ticket==0)
         continue;
      //--- If MagicNumber is not specified (zero) or order magic number matches the specified one
      if(magic==WRONG_VALUE || OrderGetInteger(ORDER_MAGIC)==magic)
        {
         //--- if the order symbol matches the one passed or NULL is passed, remove order by ticket
         if(symbol==NULL || OrderGetString(ORDER_SYMBOL)==symbol)
           {
            res &=DeleteOrder(order_ticket);
            if(res)
               num++;
           }
        }
     }
   if(!res)
      PrintFormat("%s: Not all orders were able to close without errors",__FUNCTION__);
   return num;
  }

All trading functions presented above are simple example functions. They do not handle or correct errors depending on the error code and server return code. You can implement such functions based on them, but this is beyond the scope of this article.

After filling out the structure of the trade order, we can check its validity. This can be done using the OrderCheck() function, which checks the sufficiency of funds to complete the required trading operation.

The function returns false in case of insufficient funds or incorrectly filled parameters. If the basic check of structures (pointers) is successful, the function returns truethis does not entail that the requested trading operation will be executed successfully. To obtain a detailed description of the function execution result, we need to analyze the result structure field.

Check results are placed to the MqlTradeCheckResult structure fields. Let's implement the necessary functions to display the descriptions of the structure fields.


MqlTradeCheckResult structure for checking a prepared request before sending it

I recommend checking a trade operation before sending a request for it to the trade server. The check is performed by the OrderCheck() function receiving the request being checked and the variable of the MqlTradeCheckResult structure type. The result of the check will be set into the variable.

struct MqlTradeCheckResult
  {
   uint         retcode;             // Response code
   double       balance;             // Balance after performing a deal
   double       equity;              // Equity after performing a deal
   double       profit;              // Floating profit
   double       margin;              // Margin requirements
   double       margin_free;         // Free margin
   double       margin_level;        // Margin level
   string       comment;             // Comment on the response code (error description)
  };


Response code

Return codeAll orders to perform trading operations are sent as the MqlTradeRequest trading request structure using the OrderSend() function. The result of executing the function is placed to the MqlTradeResult structure. Its retcode field contains the return code of the trade server. After checking the trade order, the response code can be read in the retcode field of the MqlTradeCheckResult structure.

//+------------------------------------------------------------------+
//| Return the response code as a string                             |
//+------------------------------------------------------------------+
string MqlTradeCheckResultRetcode(const MqlTradeCheckResult &result,const uint header_width=0,const uint indent=0,const bool ext_descr=false)
  {
//--- Define the header text and the width of the header field
//--- If the header width is passed to the function equal to zero, then the width will be the size of the header line + 1
   string header="Retcode:";
   uint w=(header_width==0 ? header.Length()+1 : header_width);
//--- Return the property value with a header having the required width and indentation
   return StringFormat("%*s%-*s%-s",indent,"",w,header,RetcodeDescription(result.retcode,ext_descr));
   /* Sample output:
      Retcode: OK (0)
   */
  }


Balance after performing a deal

The balance value after executing a trade operation.

//+----------------------------------------------------------------------+
//| Returns the balance as a string after a trade operation is performed |
//+----------------------------------------------------------------------+
string MqlTradeCheckResultBalance(const MqlTradeCheckResult &result,const uint header_width=0,const uint indent=0)
  {
//--- Define the header text and the width of the header field
//--- If the header width is passed to the function equal to zero, then the width will be the size of the header line + 1
   string header="Balance:";
   uint w=(header_width==0 ? header.Length()+1 : header_width);
//--- Get the account currency
   string currency=AccountInfoString(ACCOUNT_CURRENCY);
//--- Get the number of decimal places
   int dg=(int)AccountInfoInteger(ACCOUNT_CURRENCY_DIGITS);
//--- Return the property value with a header having the required width and indentation
   return StringFormat("%*s%-*s%-.*f %s",indent,"",w,header,dg,result.balance,currency);
   /* Sample output:
      Balance: 10016.80 USD
   */
  }


Equity after performing a deal

The equity value after executing a trade operation.

//+--------------------------------------------------------------------+
//| Return the equity as a string after a trade operation is performed |
//+--------------------------------------------------------------------+
string MqlTradeCheckResultEquity(const MqlTradeCheckResult &result,const uint header_width=0,const uint indent=0)
  {
//--- Define the header text and the width of the header field
//--- If the header width is passed to the function equal to zero, then the width will be the size of the header line + 1
   string header="Equity:";
   uint w=(header_width==0 ? header.Length()+1 : header_width);
//--- Get the account currency
   string currency=AccountInfoString(ACCOUNT_CURRENCY);
//--- Get the number of decimal places
   int dg=(int)AccountInfoInteger(ACCOUNT_CURRENCY_DIGITS);
//--- Return the property value with a header having the required width and indentation
   return StringFormat("%*s%-*s%-.*f %s",indent,"",w,header,dg,result.equity,currency);
   /* Sample output:
      Equity: 10016.80 USD
   */
  }


Floating profit

The floating profit after executing a trade operation.

//+---------------------------------------------------------------------------+
//|Return the floating profit as a string after a trade operation is performed|
//+---------------------------------------------------------------------------+
string MqlTradeCheckResultProfit(const MqlTradeCheckResult &result,const uint header_width=0,const uint indent=0)
  {
//--- Define the header text and the width of the header field
//--- If the header width is passed to the function equal to zero, then the width will be the size of the header line + 1
   string header="Profit:";
   uint w=(header_width==0 ? header.Length()+1 : header_width);
//--- Get the account currency
   string currency=AccountInfoString(ACCOUNT_CURRENCY);
//--- Get the number of decimal places
   int dg=(int)AccountInfoInteger(ACCOUNT_CURRENCY_DIGITS);
//--- Return the property value with a header having the required width and indentation
   return StringFormat("%*s%-*s%-.*f %s",indent,"",w,header,dg,result.profit,currency);
   /* Sample output:
      Profit: 0.00 USD
   */
  }


Margin requirements

The amount of margin required for the necessary trading operation .

//+------------------------------------------------------------------+
//| Return the margin,                                               |
//| required for the necessary trading operation, as a string        |
//+------------------------------------------------------------------+
string MqlTradeCheckResultMargin(const MqlTradeCheckResult &result,const uint header_width=0,const uint indent=0)
  {
//--- Define the header text and the width of the header field
//--- If the header width is passed to the function equal to zero, then the width will be the size of the header line + 1
   string header="Margin:";
   uint w=(header_width==0 ? header.Length()+1 : header_width);
//--- Get the account currency
   string currency=AccountInfoString(ACCOUNT_CURRENCY);
//--- Get the number of decimal places
   int dg=(int)AccountInfoInteger(ACCOUNT_CURRENCY_DIGITS);
//--- Return the property value with a header having the required width and indentation
   return StringFormat("%*s%-*s%-.*f %s",indent,"",w,header,dg,result.margin,currency);
   /* Sample output:
      Margin: 128.66 USD
   */
  }


Free margin

The value of equity to be left after conducting a trading operation.

//+------------------------------------------------------------------+
//| Return the value of equity                                       |
//| to be left after conducting a trading operation as a string      |
//+------------------------------------------------------------------+
string MqlTradeCheckResultMarginFree(const MqlTradeCheckResult &result,const uint header_width=0,const uint indent=0)
  {
//--- Define the header text and the width of the header field
//--- If the header width is passed to the function equal to zero, then the width will be the size of the header line + 1
   string header="Margin free:";
   uint w=(header_width==0 ? header.Length()+1 : header_width);
//--- Get the account currency
   string currency=AccountInfoString(ACCOUNT_CURRENCY);
//--- Get the number of decimal places
   int dg=(int)AccountInfoInteger(ACCOUNT_CURRENCY_DIGITS);
//--- Return the property value with a header having the required width and indentation
   return StringFormat("%*s%-*s%-.*f %s",indent,"",w,header,dg,result.margin_free,currency);
   /* Sample output:
      Margin free: 9888.14 USD
   */
  }


Margin level

Margin level to be set after completing the required trading operation.

//+-----------------------------------------------------------------------+
//| Return the margin level                                               |
//| to be set after completing the required trading operation as a string |
//+-----------------------------------------------------------------------+
string MqlTradeCheckResultMarginLevel(const MqlTradeCheckResult &result,const uint header_width=0,const uint indent=0)
  {
//--- Define the header text and the width of the header field
//--- If the header width is passed to the function equal to zero, then the width will be the size of the header line + 1
   string header="Margin level:";
   uint w=(header_width==0 ? header.Length()+1 : header_width);
//--- Return the property value with a header having the required width and indentation
   return StringFormat("%*s%-*s%-.2f %%",indent,"",w,header,result.margin_level);
   /* Sample output:
      Margin level: 7785.48 %
   */
  }


Comment to the response code

Comment on the response code, error description.

//+------------------------------------------------------------------+
//| Return comment on the response code, error description           |
//+------------------------------------------------------------------+
string MqlTradeCheckResultComment(const MqlTradeCheckResult &result,const uint header_width=0,const uint indent=0)
  {
//--- Define the header text and the width of the header field
//--- If the header width is passed to the function equal to zero, then the width will be the size of the header line + 1
   string header="Comment:";
   uint w=(header_width==0 ? header.Length()+1 : header_width);
//--- Return the property value with a header having the required width and indentation
   return StringFormat("%*s%-*s%-s",indent,"",w,header,result.comment);
   /* Sample output:
      Comment: Done
   */
  }


Examples of use

The function that displays all MqlTradeCheckResult structure fields in the journal:

//+------------------------------------------------------------------+
//| Display the MqlTradeCheckResult structure fields in the journal  |
//| as a result of OrderCheck                                        |
//+------------------------------------------------------------------+
void OrderCheckResultPrint(const MqlTradeCheckResult &result,const uint header_width=0,const uint indent=0,bool ext_descr=false)
  {
   Print("OrderCheck result:");
   Print(MqlTradeCheckResultRetcode(result,header_width,indent,ext_descr));
   Print(MqlTradeCheckResultBalance(result,header_width,indent));
   Print(MqlTradeCheckResultEquity(result,header_width,indent));
   Print(MqlTradeCheckResultProfit(result,header_width,indent));
   Print(MqlTradeCheckResultMargin(result,header_width,indent));
   Print(MqlTradeCheckResultMarginFree(result,header_width,indent));
   Print(MqlTradeCheckResultMarginLevel(result,header_width,indent));
   Print(MqlTradeCheckResultComment(result,header_width,indent));
   /* Sample output:
      OrderCheck result:
        Retcode:      Undefined (0)
        Balance:      10016.80 USD
        Equity:       10016.80 USD
        Profit:       0.00 USD
        Margin:       128.66 USD
        Margin free:  9888.14 USD
        Margin level: 7785.48 %
        Comment:      Done
   */
  }

After checking a completed trade order, and if an error is received, we can send the completed MqlTradeCheckResult structure to the journal to analyze the error.


MqlTradeResult structure with a server response to a request sent by the OrderSend() function

In response to a trade request for placing an order in the trading system, the trade server returns data containing information about the result of processing the trade request in the form of a special MqlTradeResult structure.

struct MqlTradeResult
  {
   uint     retcode;          // Operation result code
   ulong    deal;             // Deal ticket if executed
   ulong    order;            // Order ticket if placed
   double   volume;           // Deal volume confirmed by a broker
   double   price;            // Deal price confirmed by a broker
   double   bid;              // The current market Bid price (requote price)
   double   ask;              // The current market Ask price (requote price)
   string   comment;          // Broker comment to operation (by default, it is filled by the trade server return code description)
   uint     request_id;       // Request ID set by the terminal when sending 
   int      retcode_external; // Response code of an external trading system
  };

The result of the trading operation is returned to the variable of the MqlTradeResult type, which is passed as the second parameter to the OrderSend() function for conducting trading operations.

Implement the following functions to display the descriptions of the structure fields:

Operation result code

Return code of a trade server
//+------------------------------------------------------------------+
//| Return the operation result code as a string                     |
//+------------------------------------------------------------------+
string MqlTradeResultRetcode(const MqlTradeResult &result,const uint header_width=0,const uint indent=0,const bool ext_descr=false)
  {
//--- Define the header text and the width of the header field
//--- If the header width is passed to the function equal to zero, then the width will be the size of the header line + 1
   string header="Retcode:";
   uint w=(header_width==0 ? header.Length()+1 : header_width);
//--- Return the property value with a header having the required width and indentation
   return StringFormat("%*s%-*s%-s",indent,"",w,header,RetcodeDescription(result.retcode,ext_descr));
   /* Sample output:
      Retcode: 10009 DONE (Request completed)
   */
  }


Deal ticket

Deal ticket, if a deal has been performed. It is available for a trade operation of TRADE_ACTION_DEAL type.
//+------------------------------------------------------------------+
//| Return the deal ticket if it is completed                        |
//+------------------------------------------------------------------+
string MqlTradeResultDeal(const MqlTradeResult &result,const uint header_width=0,const uint indent=0)
  {
//--- Define the header text and the width of the header field
//--- If the header width is passed to the function equal to zero, then the width will be the size of the header line + 1
   string header="Deal:";
   uint w=(header_width==0 ? header.Length()+1 : header_width);
//--- Return the property value with a header having the required width and indentation
   return StringFormat("%*s%-*s%-llu",indent,"",w,header,result.deal);
   /* Sample output:
      Deal: 0
   */
  }


Order ticket

Order ticket if the order is set. It is available for the TRADE_ACTION_PENDING trading operation.
//+------------------------------------------------------------------+
//| Return the order ticket as a string if the order is set          |
//+------------------------------------------------------------------+
string MqlTradeResultOrder(const MqlTradeResult &result,const uint header_width=0,const uint indent=0)
  {
//--- Define the header text and the width of the header field
//--- If the header width is passed to the function equal to zero, then the width will be the size of the header line + 1
   string header="Order:";
   uint w=(header_width==0 ? header.Length()+1 : header_width);
//--- Return the property value with a header having the required width and indentation
   return StringFormat("%*s%-*s%-llu",indent,"",w,header,result.order);
   /* Sample output:
      Order: 1821552382
   */
  }


Deal volume

Deal volume confirmed by a broker. It depends on the order filling type.
//+------------------------------------------------------------------+
//| Return the deal volume confirmed by a broker as a string         |
//+------------------------------------------------------------------+
string MqlTradeResultVolume(const string symbol,const MqlTradeResult &result,const uint header_width=0,const uint indent=0)
  {
//--- Define the header text and the width of the header field
//--- If the header width is passed to the function equal to zero, then the width will be the size of the header line + 1
   string header="Volume:";
   uint w=(header_width==0 ? header.Length()+1 : header_width);
//--- Get the number of decimal places in the lot value
   int dg=(int)ceil(fabs(log10(SymbolInfoDouble(symbol,SYMBOL_VOLUME_STEP))));
//--- Return the property value with a header having the required width and indentation
   return StringFormat("%*s%-*s%-.*f",indent,"",w,header,dg,result.volume);
   /* Sample output:
      Volume: 0.10
   */
  }


Deal price

Deal price confirmed by a broker. It depends on the deviation field in the trade request and/or on the trade operation type.

//+------------------------------------------------------------------+
//| Return a deal price, confirmed by a broker, as a string          |
//+------------------------------------------------------------------+
string MqlTradeResultPrice(const string symbol,const MqlTradeResult &result,const uint header_width=0,const uint indent=0)
  {
//--- Define the header text and the width of the header field
//--- If the header width is passed to the function equal to zero, then the width will be the size of the header line + 1
   string header="Price:";
   uint w=(header_width==0 ? header.Length()+1 : header_width);
//--- Get the number of decimal places
   int dg=(result.price!=0 ? (int)SymbolInfoInteger(symbol,SYMBOL_DIGITS) : 1);
//--- Return the property value with a header having the required width and indentation
   return StringFormat("%*s%-*s%-.*f",indent,"",w,header,dg,result.price);
   /* Sample output:
      Price: 0.0
   */
  }


Current Bid market price

The current market Bid price (requote price).
//+------------------------------------------------------------------+
//| Return the current market Bid price as a string                  |
//+------------------------------------------------------------------+
string MqlTradeResultBid(const string symbol,const MqlTradeResult &result,const uint header_width=0,const uint indent=0)
  {
//--- Define the header text and the width of the header field
//--- If the header width is passed to the function equal to zero, then the width will be the size of the header line + 1
   string header="Bid:";
   uint w=(header_width==0 ? header.Length()+1 : header_width);
//--- Get the number of decimal places
   int dg=(result.bid!=0 ? (int)SymbolInfoInteger(symbol,SYMBOL_DIGITS) : 1);
//--- Return the property value with a header having the required width and indentation
   return StringFormat("%*s%-*s%-.*f",indent,"",w,header,dg,result.bid);
   /* Sample output:
      Bid: 0.0
   */
  }


Current Ask market price

The current market Ask price (requote price).
//+------------------------------------------------------------------+
//| Return the current market Ask price as a string                  |
//+------------------------------------------------------------------+
string MqlTradeResultAsk(const string symbol,const MqlTradeResult &result,const uint header_width=0,const uint indent=0)
  {
//--- Define the header text and the width of the header field
//--- If the header width is passed to the function equal to zero, then the width will be the size of the header line + 1
   string header="Ask:";
   uint w=(header_width==0 ? header.Length()+1 : header_width);
//--- Get the number of decimal places
   int dg=(result.ask!=0 ? (int)SymbolInfoInteger(symbol,SYMBOL_DIGITS) : 1);
//--- Return the property value with a header having the required width and indentation
   return StringFormat("%*s%-*s%-.*f",indent,"",w,header,dg,result.ask);
   /* Sample output:
      Ask: 0.0
   */
  }

When placing a pending order, the Bid and Ask prices in the structure remain empty. If the order was not accepted due to a requote, we can see the prices that existed at the time the order was rejected for placement in the Bid and Ask fields.


Broker's comment on a deal

Broker comment to operation (by default, it is filled by the trade server return code description).
//+------------------------------------------------------------------+
//| Return broker's comment on a deal                                |
//+------------------------------------------------------------------+
string MqlTradeResultComment(const MqlTradeResult &result,const uint header_width=0,const uint indent=0)
  {
//--- Define the header text and the width of the header field
//--- If the header width is passed to the function equal to zero, then the width will be the size of the header line + 1
   string header="Comment:";
   uint w=(header_width==0 ? header.Length()+1 : header_width);
//--- Return the property value with a header having the required width and indentation
   return StringFormat("%*s%-*s%-s",indent,"",w,header,result.comment);
   /* Sample output:
      Comment: Request executed
   */
  }


Request ID

Request ID set by the terminal when sending to the trade server.
//+------------------------------------------------------------------+
//| Return request ID as a string                                    |
//+------------------------------------------------------------------+
string MqlTradeResultRequestID(const MqlTradeResult &result,const uint header_width=0,const uint indent=0)
  {
//--- Define the header text and the width of the header field
//--- If the header width is passed to the function equal to zero, then the width will be the size of the header line + 1
   string header="Request ID:";
   uint w=(header_width==0 ? header.Length()+1 : header_width);
//--- Return the property value with a header having the required width and indentation
   return StringFormat("%*s%-*s%-lu",indent,"",w,header,result.request_id);
   /* Sample output:
      Request ID: 3768213261
   */
  }

Setting request_id by the terminal for the trade request when sending it to the server is mainly introduced for working with the OrderSendAsync() asynchronous function. This identifier allows to associate the performed action (OrderSend or OrderSendAsync functions call) with the result of this action sent to the OnTradeTransaction() handler.


External trading system response code

Error code returned by the external trading system. The use and types of these errors depend on the broker and the external trading system, to which trading operations are sent.
//+-------------------------------------------------------------------+
//| Return the response code of an external trading system as a string|
//+-------------------------------------------------------------------+
string MqlTradeResultRetcodeExternal(const MqlTradeResult &result,const uint header_width=0,const uint indent=0)
  {
//--- Define the header text and the width of the header field
//--- If the header width is passed to the function equal to zero, then the width will be the size of the header line + 1
   string header="Retcode External:";
   uint w=(header_width==0 ? header.Length()+1 : header_width-1);
//--- Return the property value with a header having the required width and indentation
   return StringFormat("%*s%-*s%- ld",indent,"",w,header,result.retcode_external);
   /* Sample output:
      Retcode External:  0
   */
  }


Examples of use

The function that displays all fields of the trade request result structure to the journal:

//+------------------------------------------------------------------+
//| Display MqlTradeResult structure fields in the journal           |
//| as a result of a trade request                                   |
//+------------------------------------------------------------------+
void TradeResultPrint(const string symbol,const MqlTradeResult &result,const uint header_width=0,const uint indent=0,bool ext_descr=false)
  {
   string symbol_name=(symbol==NULL ? Symbol() : symbol);
   Print("OrderSend result:");
   Print(MqlTradeResultRetcode(result,header_width,indent,ext_descr));
   Print(MqlTradeResultDeal(result,header_width,indent));
   Print(MqlTradeResultOrder(result,header_width,indent));
   Print(MqlTradeResultVolume(symbol_name,result,header_width,indent));
   Print(MqlTradeResultPrice(symbol_name,result,header_width,indent));
   Print(MqlTradeResultBid(symbol_name,result,header_width,indent));
   Print(MqlTradeResultAsk(symbol_name,result,header_width,indent));
   Print(MqlTradeResultComment(result,header_width,indent));
   Print(MqlTradeResultRequestID(result,header_width,indent));
   Print(MqlTradeResultRetcodeExternal(result,header_width,indent));
   /* Sample output:
      OrderSend result:
        Retcode:          10009 DONE (Request completed)
        Deal:             0
        Order:            1821671230
        Volume:           0.10
        Price:            0.0
        Bid:              0.0
        Ask:              0.0
        Comment:          Request executed
        Request ID:       3768213265
        Retcode External: 0
   */
  }


MqlTradeTransaction structure with a trading transaction description

As a result of performing certain actions with a trading account, its state changes. These actions include:

  • Sending a trade request by any MQL5 application in the client terminal using OrderSend andOrderSendAsync functions and its subsequent execution;
  • Sending a trade request through the graphical interface of the terminal and its subsequent execution;
  • Triggering of pending orders and stop orders on the server;
  • Performing operations on the trading server side.

As a result of these actions, the following trading transactions are performed for the account:

  • handling a trading request;
  • changing open orders;
  • changing order history;
  • changing deal history;
  • changing positions.

For example, when sending a buy market order, it is processed, a corresponding buy order is created for the account, the order is executed, removed from the list of open ones and added to the order history. Then the corresponding deal is added to the history and a new position is created. All these actions are trading transactions.

MQL5 features the OnTradeTransaction() handler for receiving trading transactions applied to an account. The first parameter of this handler receives the MqlTradeTransaction structure, which describes trading transactions.

struct MqlTradeTransaction
  {
   ulong                         deal;             // Deal ticket
   ulong                         order;            // Order ticket
   string                        symbol;           // Symbol name
   ENUM_TRADE_TRANSACTION_TYPE   type;             // Trading transaction type
   ENUM_ORDER_TYPE               order_type;       // Order type
   ENUM_ORDER_STATE              order_state;      // Order state
   ENUM_DEAL_TYPE                deal_type;        // Deal type
   ENUM_ORDER_TYPE_TIME          time_type;        // Order lifetime type
   datetime                      time_expiration;  // Order expiration time
   double                        price;            // Price 
   double                        price_trigger;    // Stop limit order trigger price
   double                        price_sl;         // Stop Loss level
   double                        price_tp;         // Take Profit level
   double                        volume;           // Volume in lots
   ulong                         position;         // Position ticket
   ulong                         position_by;      // Opposite position ticket
  };


Let's implement the following functions to return descriptions of all structure fields.

Deal ticket

//+------------------------------------------------------------------+
//| Return the deal ticket as a string                               |
//+------------------------------------------------------------------+
string MqlTradeTransactionDeal(const MqlTradeTransaction &trans,const uint header_width=0,const uint indent=0)
  {
//--- Define the header text and the width of the header field
//--- If the header width is passed to the function equal to zero, then the width will be the size of the header line + 1
   string header="Deal:";
   uint w=(header_width==0 ? header.Length()+1 : header_width);
//--- Return the property value with a header having the required width and indentation
   return StringFormat("%*s%-*s%-llu",indent,"",w,header,trans.deal);
   /* Sample output:
      Deal: 0
   */
  }


Order ticket

//+------------------------------------------------------------------+
//| Return the order ticket as a string                              |
//+------------------------------------------------------------------+
string MqlTradeTransactionOrder(const MqlTradeTransaction &trans,const uint header_width=0,const uint indent=0)
  {
//--- Define the header text and the width of the header field
//--- If the header width is passed to the function equal to zero, then the width will be the size of the header line + 1
   string header="Order:";
   uint w=(header_width==0 ? header.Length()+1 : header_width);
//--- Return the property value with a header having the required width and indentation
   return StringFormat("%*s%-*s%-llu",indent,"",w,header,trans.order);
   /* Sample output:
      Order: 1825990224
   */
  }


Name of a trading instrument

The name of the trading symbol the transaction was executed for.

//+------------------------------------------------------------------+
//| Return an order symbol as a string                               |
//+------------------------------------------------------------------+
string MqlTradeTransactionSymbol(const MqlTradeTransaction &trans,const uint header_width=0,const uint indent=0)
  {
//--- Define the header text and the width of the header field
//--- If the header width is passed to the function equal to zero, then the width will be the size of the header line + 1
   string header="Symbol:";
   uint w=(header_width==0 ? header.Length()+1 : header_width);
//--- Return the property value with a header having the required width and indentation
   return StringFormat("%*s%-*s%-s",indent,"",w,header,trans.symbol);
   /* Sample output:
      Symbol: EURUSD
   */
  }


Trade transaction type

Trading transaction type. The value can be any of the ENUM_TRADE_TRANSACTION_TYPE enumeration values.

//+------------------------------------------------------------------+
//| Return the trading transaction type as a string                  |
//+------------------------------------------------------------------+
string MqlTradeTransactionType(const MqlTradeTransaction &trans,const uint header_width=0,const uint indent=0,const bool ext_descr=false)
  {
//--- Define the header text and the width of the header field
//--- If the header width is passed to the function equal to zero, then the width will be the size of the header line + 1
   string header="Type:";
   uint w=(header_width==0 ? header.Length()+1 : header_width);
//--- Return the property value with a header having the required width and indentation
   return StringFormat("%*s%-*s%-s",indent,"",w,header,TradeTransactionTypeDescription(trans.type,ext_descr));
   /* Sample output:
      Type: Order delete (Removing an order from the list of the open ones)
   */
  }


Order type

Trading order type. The value can be one of the ENUM_ORDER_TYPE enumeration values.

//+------------------------------------------------------------------+
//| Return the order type as a string                                |
//+------------------------------------------------------------------+
string MqlTradeTransactionOrderType(const MqlTradeTransaction &trans,const uint header_width=0,const uint indent=0,const bool ext_descr=false)
  {
//--- Declare variables to indicate transaction affiliation
   bool trans_req=false;   // Request
   bool trans_order=false; // Order
   bool trans_deal=false;  // Deal
   bool trans_pos=false;   // Position
//--- Set transaction affiliation
   SetTransactionBelong(trans.type,trans_req,trans_order,trans_deal,trans_pos);
//--- Define the header text and the width of the header field
//--- If the header width is passed to the function equal to zero, then the width will be the size of the header line + 1
   string header="Order type:";
   uint w=(header_width==0 ? header.Length()+1 : header_width);
//--- Return the property value with a header having the required width and indentation
   return StringFormat("%*s%-*s%-s",indent,"",w,header,(trans_order ? OrderTypeDescription(trans.order_type,ext_descr) : "0"));
   /* Sample output:
      Order type: Sell Limit (Sell Limit pending order)
   */
  }

The function determines the affiliation of the trading transaction. If the transaction was carried out with an order, then its type is returned, otherwise - '0'.


Order status

Trading order status. The value can be one of the ENUM_ORDER_STATE enumeration values.

//+------------------------------------------------------------------+
//| Return the order status as a string                              |
//+------------------------------------------------------------------+
string MqlTradeTransactionOrderState(const MqlTradeTransaction &trans,const uint header_width=0,const uint indent=0,const bool ext_descr=false)
  {
//--- Declare variables to indicate transaction affiliation
   bool trans_req=false;   // Request
   bool trans_order=false; // Order
   bool trans_deal=false;  // Deal
   bool trans_pos=false;   // Position
//--- Set transaction affiliation
   SetTransactionBelong(trans.type,trans_req,trans_order,trans_deal,trans_pos);
//--- Define the header text and the width of the header field
//--- If the header width is passed to the function equal to zero, then the width will be the size of the header line + 1
   string header="Order state:";
   uint w=(header_width==0 ? header.Length()+1 : header_width);
//--- Return the property value with a header having the required width and indentation
   return StringFormat("%*s%-*s%-s",indent,"",w,header,(trans_order ? OrderStateDescription(trans.order_state,ext_descr) : "0"));
   /* Sample output:
      Order state: Started (Order checked, but not yet accepted by broker)
   */
  }

The function determines the affiliation of the trading transaction. If the transaction was carried out with an order, then its status is returned, otherwise - '0'.


Deal type

Deal type. The value can be one of the ENUM_DEAL_TYPE enumeration values.

//+------------------------------------------------------------------+
//| Return the deal type as a string                                 |
//+------------------------------------------------------------------+
string MqlTradeTransactionDealType(const MqlTradeTransaction &trans,const uint header_width=0,const uint indent=0,const bool ext_descr=false)
  {
//--- Declare variables to indicate transaction affiliation
   bool trans_req=false;   // Request
   bool trans_order=false; // Order
   bool trans_deal=false;  // Deal
   bool trans_pos=false;   // Position
//--- Set transaction affiliation
   SetTransactionBelong(trans.type,trans_req,trans_order,trans_deal,trans_pos);
//--- Define the header text and the width of the header field
//--- If the header width is passed to the function equal to zero, then the width will be the size of the header line + 1
   string header="Deal type:";
   uint w=(header_width==0 ? header.Length()+1 : header_width);
//--- Return the property value with a header having the required width and indentation
   return StringFormat("%*s%-*s%-s",indent,"",w,header,(trans_deal || trans_pos ? DealTypeDescription(trans.deal_type,ext_descr) : "0"));
   /* Sample output:
      Deal type: Buy
   */
  }

The function determines the affiliation of the trading transaction. If the transaction was carried out with a deal, then its type is returned, otherwise - '0'.


Order lifetime type

Order type by execution. The value can be one of the ENUM_ORDER_TYPE_TIME values.

//+------------------------------------------------------------------+
//| Return the order type by expiration time as a string             |
//+------------------------------------------------------------------+
string MqlTradeTransactionTimeType(const MqlTradeTransaction &trans,const uint header_width=0,const uint indent=0,const bool ext_descr=false)
  {
//--- Declare variables to indicate transaction affiliation
   bool trans_req=false;   // Request
   bool trans_order=false; // Order
   bool trans_deal=false;  // Deal
   bool trans_pos=false;   // Position
//--- Set transaction affiliation
   SetTransactionBelong(trans.type,trans_req,trans_order,trans_deal,trans_pos);
//--- Define the header text and the width of the header field
//--- If the header width is passed to the function equal to zero, then the width will be the size of the header line + 1
   string header="Time type:";
   uint w=(header_width==0 ? header.Length()+1 : header_width);
//--- Return the property value with a header having the required width and indentation
   return StringFormat("%*s%-*s%-s",indent,"",w,header,(trans_order ? OrderTypeTimeDescription(trans.time_type,ext_descr) : "0"));
   /* Sample output:
      Order type time: Time GTC (Good till cancel order)
   */
  }

The function determines the affiliation of the trading transaction. If the transaction was carried out with an order, then its type by lifetime is returned, otherwise — '0'.


Order expiration date

Pending order expiration time (for ORDER_TIME_SPECIFIED and ORDER_TIME_SPECIFIED_DAY type orders).

//+------------------------------------------------------------------+
//| Return the order expiration time as a string                     |
//+------------------------------------------------------------------+
string MqlTradeTransactionTimeExpiration(const MqlTradeTransaction &trans,const uint header_width=0,const uint indent=0)
  {
//--- Define the header text and the width of the header field
//--- If the header width is passed to the function equal to zero, then the width will be the size of the header line + 1
   string header="Time expiration:";
   uint w=(header_width==0 ? header.Length()+1 : header_width);
//--- Set the expiration time as a string. If 0, then set 0
   string tm=(trans.time_expiration==0 ? "0" : (string)trans.time_expiration);
//--- Return the property value with a header having the required width and indentation
   return StringFormat("%*s%-*s%-s",indent,"",w,header,tm);
   /* Sample output:
      Time expiration: 0
   */
  }

If the expiration time in the structure field is set to zero, then display '0', otherwise - the expiration time in date format. If we do not check the field value for zero, then the zero value will be displayed as 01.01.1970 00:00:00, which does not look very aesthetically pleasing for the missing order expiration time.


Price

Price. Depending on the type of trading transaction, this may be the price of an order, transaction or position.

//+------------------------------------------------------------------+
//| Return the order/deal/position price as a string                 |
//+------------------------------------------------------------------+
string MqlTradeTransactionPrice(const MqlTradeTransaction &trans,const uint header_width=0,const uint indent=0)
  {
//--- Define the header text and the width of the header field
//--- If the header width is passed to the function equal to zero, then the width will be the size of the header line + 1
   string header="Price:";
   uint w=(header_width==0 ? header.Length()+1 : header_width);
//--- Receive symbol's Digits. If the character is not specified, use 1
   int dg=(trans.symbol!="" ? (int)SymbolInfoInteger(trans.symbol,SYMBOL_DIGITS) : 1);
//--- Return the property value with a header having the required width and indentation
   return StringFormat("%*s%-*s%-.*f",indent,"",w,header,dg,trans.price);
   /* Sample output:
      Price: 1.10331
   */
  }


Stop limit order trigger price

Stop price (trigger price) of a stop limit order (ORDER_TYPE_BUY_STOP_LIMIT and ORDER_TYPE_SELL_STOP_LIMIT).

//+------------------------------------------------------------------+
//| Return the stop limit order trigger price as a string            |
//+------------------------------------------------------------------+
string MqlTradeTransactionPriceTrigger(const MqlTradeTransaction &trans,const uint header_width=0,const uint indent=0)
  {
//--- Define the header text and the width of the header field
//--- If the header width is passed to the function equal to zero, then the width will be the size of the header line + 1
   string header="Price trigger:";
   uint w=(header_width==0 ? header.Length()+1 : header_width);
//--- Receive symbol's Digits. If a symbol is not specified or there is no stop price, use 1
   int dg=(trans.symbol!="" ? (int)SymbolInfoInteger(trans.symbol,SYMBOL_DIGITS) : 1);
   if(trans.price_trigger==0)
      dg=1;
//--- Return the property value with a header having the required width and indentation
   return StringFormat("%*s%-*s%-.*f",indent,"",w,header,dg,trans.price_trigger);
   /* Sample output:
      Price trigger: 0.0
   */
  }

If the stop limit order price is not specified, then we will display '0.0', otherwise - the order price.


Stop Loss level

Stop Loss price. Depending on the trading transaction, it can refer to an order, a deal or a position.

//+------------------------------------------------------------------+
//| Return Stop Loss price as a string                               |
//+------------------------------------------------------------------+
string MqlTradeTransactionPriceSL(const MqlTradeTransaction &trans,const uint header_width=0,const uint indent=0)
  {
//--- Define the header text and the width of the header field
//--- If the header width is passed to the function equal to zero, then the width will be the size of the header line + 1
   string header="Price SL:";
   uint w=(header_width==0 ? header.Length()+1 : header_width);
//--- Receive symbol's Digits. If a symbol is not specified or a Stop Loss price is absent, use 1
   int dg=(trans.symbol!="" ? (int)SymbolInfoInteger(trans.symbol,SYMBOL_DIGITS) : 1);
   if(trans.price_sl==0)
      dg=1;
//--- Return the property value with a header having the required width and indentation
   return StringFormat("%*s%-*s%-.*f",indent,"",w,header,dg,trans.price_sl);
   /* Sample output:
      Price SL: 0.0
   */
  }

If Stop Loss is not set, we will display '0.0', otherwise — a Stop Loss price.


Take Profit level

Take Profit price. Depending on the trading transaction, it can refer to an order, a deal or a position.

//+------------------------------------------------------------------+
//| Return a Take Profit price as a string                           |
//+------------------------------------------------------------------+
string MqlTradeTransactionPriceTP(const MqlTradeTransaction &trans,const uint header_width=0,const uint indent=0)
  {
//--- Define the header text and the width of the header field
//--- If the header width is passed to the function equal to zero, then the width will be the size of the header line + 1
   string header="Price TP:";
   uint w=(header_width==0 ? header.Length()+1 : header_width);
//--- Receive symbol's Digits. If a symbol is not specified or a Take Profit price is absent, use 1
   int dg=(trans.symbol!="" ? (int)SymbolInfoInteger(trans.symbol,SYMBOL_DIGITS) : 1);
   if(trans.price_tp==0)
      dg=1;
//--- Return the property value with a header having the required width and indentation
   return StringFormat("%*s%-*s%-.*f",indent,"",w,header,dg,trans.price_tp);
   /* Sample output:
      Price TP: 0.0
   */
  }

If Take Profit price is not set, display '0.0', otherwise — Take Profit price.


Volume in lots

Volume in lots. Depending on a trading transaction type, it can define the current order volume, deal volume or position volume.

//+------------------------------------------------------------------+
//| Return volume in lots as a string                                |
//+------------------------------------------------------------------+
string MqlTradeTransactionVolume(const MqlTradeTransaction &trans,const uint header_width=0,const uint indent=0)
  {
//--- Define the header text and the width of the header field
//--- If the header width is passed to the function equal to zero, then the width will be the size of the header line + 1
   string header="Volume:";
   uint w=(header_width==0 ? header.Length()+1 : header_width);
//--- Get the number of decimal places in the lot value. If the character is not specified, use 1
   int dg=(trans.symbol!="" ? (int)ceil(fabs(log10(SymbolInfoDouble(trans.symbol,SYMBOL_VOLUME_STEP)))) : 1);
//--- Return the property value with a header having the required width and indentation
   return StringFormat("%*s%-*s%-.*f",indent,"",w,header,dg,trans.volume);
   /* Sample output:
      Volume: 0.10
   */
  }


Position ticket

Ticket of a position affected by a transaction.

//+------------------------------------------------------------------+
//| Return the position ticket as a string                           |
//+------------------------------------------------------------------+
string MqlTradeTransactionPosition(const MqlTradeTransaction &trans,const uint header_width=0,const uint indent=0)
  {
//--- Define the header text and the width of the header field
//--- If the header width is passed to the function equal to zero, then the width will be the size of the header line + 1
   string header="Position:";
   uint w=(header_width==0 ? header.Length()+1 : header_width);
//--- Return the property value with a header having the required width and indentation
   return StringFormat("%*s%-*s%-llu",indent,"",w,header,trans.position);
   /* Sample output:
      Position: 0
   */
  }


Opposite position ticket

Opposite position ticket. It is used when closing a position by an opposite one (opened at the same symbol but in the opposite direction).

//+------------------------------------------------------------------+
//| Return the opposite position ticket as a string                  |
//+------------------------------------------------------------------+
string MqlTradeTransactionPositionBy(const MqlTradeTransaction &trans,const uint header_width=0,const uint indent=0)
  {
//--- Define the header text and the width of the header field
//--- If the header width is passed to the function equal to zero, then the width will be the size of the header line + 1
   string header="Position by:";
   uint w=(header_width==0 ? header.Length()+1 : header_width);
//--- Return the property value with a header having the required width and indentation
   return StringFormat("%*s%-*s%-llu",indent,"",w,header,trans.position_by);
   /* Sample output:
      Position by: 0
   */
  }


Examples of use

Using the functions presented above, we can display all fields of the MqlTradeTransaction structure in the journal:

//+------------------------------------------------------------------+
//| Display the MqlTradeTransaction structure fields in the journal  |
//+------------------------------------------------------------------+
void MqlTradeTransactionPrint(const MqlTradeTransaction &trans,const uint header_width=0,const uint indent=0,const bool ext_descr=false)
  {
   Print(MqlTradeTransactionDeal(trans,header_width,indent));
   Print(MqlTradeTransactionOrder(trans,header_width,indent));
   Print(MqlTradeTransactionSymbol(trans,header_width,indent));
   Print(MqlTradeTransactionType(trans,header_width,indent,ext_descr));
   Print(MqlTradeTransactionOrderType(trans,header_width,indent,ext_descr));
   Print(MqlTradeTransactionOrderState(trans,header_width,indent,ext_descr));
   Print(MqlTradeTransactionDealType(trans,header_width,indent,ext_descr));
   Print(MqlTradeTransactionTimeType(trans,header_width,indent,ext_descr));
   Print(MqlTradeTransactionTimeExpiration(trans,header_width,indent));
   Print(MqlTradeTransactionPrice(trans,header_width,indent));
   Print(MqlTradeTransactionPriceTrigger(trans,header_width,indent));
   Print(MqlTradeTransactionPriceSL(trans,header_width,indent));
   Print(MqlTradeTransactionPriceTP(trans,header_width,indent));
   Print(MqlTradeTransactionVolume(trans,header_width,indent));
   Print(MqlTradeTransactionPosition(trans,header_width,indent));
   Print(MqlTradeTransactionPositionBy(trans,header_width,indent));
   /* Sample output:
      Deal:             0
      Order:            1829189788
      Symbol:           EURUSD
      Type:             Order add
      Order type:       Buy Limit
      Order state:      Started
      Deal type:        0
      Time type:        Time GTC
      Time expiration:  0
      Price:            1.09861
      Price trigger:    0.0
      Price SL:         0.0
      Price TP:         0.0
      Volume:           0.10
      Position:         0
      Position by:      0
   */
  }

The presented function displays all fields of the MqlTradeTransaction structure in the journal. The structure fields are filled in differently for different trading events:

The essential parameter for received transaction analysis is its type specified in the type field. For example, if a transaction is of TRADE_TRANSACTION_REQUEST type (a result of handling a trade request by the server has been received), the structure has only type filled in field. Other fields are not analyzed. In this case, we may analyze two additional request and result parameters submitted to OnTradeTransaction() handler, as shown below.

Knowing the type of a trading operation, we can decide to analyze the current state of orders, positions and deals on a trading account. Keep in mind that one trade request sent from the terminal to the server can generate several trade transactions. Their order of arriving at the terminal is not guaranteed.

The MqlTradeTransaction structure is filled in differently depending on the trading transaction type (ENUM_TRADE_TRANSACTION_TYPE):

TRADE_TRANSACTION_ORDER_* and TRADE_TRANSACTION_HISTORY_*

For trading transactions related to handling open orders (TRADE_TRANSACTION_ORDER_ADD, TRADE_TRANSACTION_ORDER_UPDATE and TRADE_TRANSACTION_ORDER_DELETE) and order history (TRADE_TRANSACTION_HISTORY_ADD, TRADE_TRANSACTION_HISTORY_UPDATE, TRADE_TRANSACTION_HISTORY_DELETE), the following fields are filled out in the MqlTradeTransaction structure:

  • order — order ticket;
  • symbol — name of a financial instrument in an order;
  • type — trading transaction type;
  • order_type — order type;
  • orders_state — current state of an order;
  • time_type — order execution type;
  • time_expiration — order expiration time (for order with ORDER_TIME_SPECIFIED and ORDER_TIME_SPECIFIED_DAY expiration type);
  • price — order price specified by the client;
  • price_trigger — stop limit order trigger stop price (only for ORDER_TYPE_BUY_STOP_LIMIT and ORDER_TYPE_SELL_STOP_LIMIT);
  • price_sl — Stop Loss order price (filled in if specified in the order);
  • price_tp — Take Profit order price (filled in if specified in the order);
  • volume — current order volume (not executed). The initial order volume can be found from the order history using the HistoryOrders* functions.
  • position — ticket of a position opened, changed or closed as a result of an order execution. Filled in for market orders only. Not filled in for TRADE_TRANSACTION_ORDER_ADD.
  • position_by — opposing position ticket. Filled in only for close by orders.

TRADE_TRANSACTION_DEAL_*

The following fields are filled in for the trading transactions related to deal processing (TRADE_TRANSACTION_DEAL_ADD, TRADE_TRANSACTION_DEAL_UPDATE and TRADE_TRANSACTION_DEAL_DELETE) in the MqlTradeTransaction structure:

  • deal — deal ticket;
  • order — ticket of an order a deal is based on;
  • symbol — name of a financial instrument in a deal;
  • type — trading transaction type;
  • deal_type — deal type;
  • price — deal price;
  • price_sl — Stop Loss price (filled in if specified in the order a deal is based on);
  • price_tp — Take Profit price (filled in if specified in the order a deal is based on);
  • volume — deal volume in lots.
  • position — ticket of a position opened, changed or closed as a result of a deal execution.
  • position_by — opposing position ticket. Filled in only for out by deals.

TRADE_TRANSACTION_POSITION

The following fields are filled out in the MqlTradeTransaction structure for the trading transactions related to changes in positions having nothing to do with deal execution (TRADE_TRANSACTION_POSITION):

  • symbol — position financial instrument name;
  • type — trading transaction type;
  • deal_type — position type (DEAL_TYPE_BUY or DEAL_TYPE_SELL);
  • price — weighted average position open price;
  • price_sl — Stop Loss price;
  • price_tp — Take Profit price;
  • volume — position size in lots if changed.
  • position — position ticket.

Changing a position (adding, changing or removing) as a result of a deal does not entail the TRADE_TRANSACTION_POSITION transaction.

TRADE_TRANSACTION_REQUEST

Only one field is filled out in the MqlTradeTransaction structure for trading transactions describing that a trading request has been handled by the server and the result has been received (TRADE_TRANSACTION_REQUEST):

  • type — trading transaction type;
Only one field should be analyzed for transactions of this type — type (trading transaction type). It is necessary to analyze the second and third parameters of the OnTradeTransaction function (request and result) to receive additional information.

Accordingly, we can create a function that takes into account incoming trading events and logs only those fields of the MqlTradeTransaction structure that are necessary for each type of transaction:

//+------------------------------------------------------------------+
//| Trading transaction informer                                     |
//+------------------------------------------------------------------+
void TradeTransactionInformer(const MqlTradeTransaction &trans,const MqlTradeRequest& request,const MqlTradeResult& result,
                              const uint header_width=0,const uint indent=0,const bool ext_descr=false)
  {
//--- Declare variables to indicate transaction affiliation
   bool trans_req=false;   // Request
   bool trans_order=false; // Order
   bool trans_deal=false;  // Deal
   bool trans_pos=false;   // Position
//--- Set transaction affiliation
   SetTransactionBelong(trans.type,trans_req,trans_order,trans_deal,trans_pos);
//--- Depending on the transaction affiliation, display descriptions of the corresponding structure fields
   //--- Trading request
   if(trans_req)
     {
      //--- Inform of the "trading request" transaction type and indicate that it is necessary to analyze the fields of the trading request and server response structure
      PrintFormat("Transaction %s\n%*sThe second and third parameters of OnTradeTransaction function (request and result) must be analyzed for additional data:",
         MqlTradeTransactionType(trans,0,0,true),indent,"");
      //--- Display the description of the trade request and the request result received from the server
      TradeRequestPrint(request,header_width,indent,ext_descr);
      TradeResultPrint(trans.symbol,result,header_width,indent,ext_descr);
     }
   //--- Otherwise, if the order/deal/position 
   else
     {
      //--- Display the transaction type in the journal
      PrintFormat("Transaction %s",MqlTradeTransactionType(trans,0,0,true));
      //--- Order
      if(trans_order)
        {
         Print(MqlTradeTransactionOrder(trans,header_width,indent));
         Print(MqlTradeTransactionSymbol(trans,header_width,indent));
         Print(MqlTradeTransactionOrderType(trans,header_width,indent,ext_descr));
         Print(MqlTradeTransactionOrderState(trans,header_width,indent,ext_descr));
         Print(MqlTradeTransactionTimeType(trans,header_width,indent,ext_descr));
         if(trans.time_type==ORDER_TIME_SPECIFIED || trans.time_type==ORDER_TIME_SPECIFIED_DAY)
            Print(MqlTradeTransactionTimeExpiration(trans,header_width,indent));
         Print(MqlTradeTransactionPrice(trans,header_width,indent));
         if(trans.order_type==ORDER_TYPE_BUY_STOP_LIMIT || trans.order_type==ORDER_TYPE_SELL_STOP_LIMIT)
            Print(MqlTradeTransactionPriceTrigger(trans,header_width,indent));
         Print(MqlTradeTransactionPriceSL(trans,header_width,indent));
         Print(MqlTradeTransactionPriceTP(trans,header_width,indent));
         Print(MqlTradeTransactionVolume(trans,header_width,indent));
         if(trans.type!=TRADE_TRANSACTION_ORDER_ADD && (trans.order_type==ORDER_TYPE_BUY || trans.order_type==ORDER_TYPE_SELL))
            Print(MqlTradeTransactionPosition(trans,header_width,indent));
         if(trans.order_type==ORDER_TYPE_CLOSE_BY)
            Print(MqlTradeTransactionPositionBy(trans,header_width,indent));
        }
      //--- Deal
      if(trans_deal)
        {
         Print(MqlTradeTransactionDeal(trans,header_width,indent));
         Print(MqlTradeTransactionOrder(trans,header_width,indent));
         Print(MqlTradeTransactionSymbol(trans,header_width,indent));
         Print(MqlTradeTransactionDealType(trans,header_width,indent,ext_descr));
         Print(MqlTradeTransactionPrice(trans,header_width,indent));
         Print(MqlTradeTransactionPriceSL(trans,header_width,indent));
         Print(MqlTradeTransactionPriceTP(trans,header_width,indent));
         Print(MqlTradeTransactionVolume(trans,header_width,indent));
         Print(MqlTradeTransactionPosition(trans,header_width,indent));
        }
      //--- Position
      if(trans_pos)
        {
         Print(MqlTradeTransactionSymbol(trans,header_width,indent));
         Print(MqlTradeTransactionDealType(trans,header_width,indent,ext_descr));
         Print(MqlTradeTransactionPrice(trans,header_width,indent));
         Print(MqlTradeTransactionPriceSL(trans,header_width,indent));
         Print(MqlTradeTransactionPriceTP(trans,header_width,indent));
         Print(MqlTradeTransactionVolume(trans,header_width,indent));
         Print(MqlTradeTransactionPosition(trans,header_width,indent));
        }
     }
   /* Sample output when setting the Sell Limit order:
      Transaction Type: Order add (Adding a new open order)
        Order: 1833072954
        Symbol: EURUSD
        Order type: Sell Limit (Sell Limit pending order)
        Order state: Started (Order checked, but not yet accepted by broker)
        Time type: Time GTC (Good till cancel order)
        Price: 1.10516
        Price SL: 0.0
        Price TP: 0.0
        Volume: 0.10
      Transaction Type: Order update (Updating an open order)
        Order: 1833072954
        Symbol: EURUSD
        Order type: Sell Limit (Sell Limit pending order)
        Order state: Placed (Order accepted)
        Time type: Time GTC (Good till cancel order)
        Price: 1.10516
        Price SL: 0.0
        Price TP: 0.0
        Volume: 0.10
      Transaction Type: Request (The trade request has been processed by a server and processing result has been received)
        The second and third parameters of OnTradeTransaction function (request and result) must be analyzed for additional data:
      Request Pending (Place a trade order for the execution under specified conditions (pending order)):
        Symbol: EURUSD
        Volume: 0.10
        Price: 1.10516
        SL: 0.0
        TP: 0.0
        Type: Sell Limit (Sell Limit pending order)
        Type filling: Return (In case of partial filling, an order with remaining volume is not canceled but processed further)
        Type time: Time GTC (Good till cancel order)
        Expiration: 0
        Magic: 1024
        Comment: TestMqlTradeTransaction
      OrderSend result:
        Retcode: 10009 DONE (Request completed)
        Deal: 0
        Order: 1833072954
        Volume: 0.10
        Price: 0.0
        Bid: 0.0
        Ask: 0.0
        Comment: 
        Request ID: 3768213464
        Retcode External:  0
      //---
      Here, when placing a Buy Limit pending order, we received three transactions:
         1. Request - a request to place a pending order was accepted by the server and a response was received. 
            To clarify information, it is necessary to analyze the fields of the MqlTradeRequest trade request structure 
            and the MqlTradeResult server response – 'request' and 'result', respectively.
         2. Adding a new pending order with the ticket #1829788294. Its status - Started.
         3. Changing an open order with the ticket #1829788294. Its status changed from Started to Placed.
      The order of transactions is not respected, but this is clearly indicated in the Help section concerning the OnTradeTransaction handler: 
      https://www.mql5.com/en/docs/event_handlers/ontradetransaction.
   */
  }


Trading transactions informer EA

To check the operation of the functions discussed above, let's create an EA that tracks all trading transactions and server responses, as well as displaying descriptions of incoming trading transactions in the journal. Let's implement it so that we can set pending orders (BuyStop, SellStop, BuyLimit and SellLimit) and open positions (Buy and Sell) by pressing the keys selected in the settings. By default, the following keys will be assigned to trade management:

Order type
Keyboard shortcuts for opening/installing
Keyboard shortcuts for closing/deleting
 Buy
 B
 X
 Sell
 S
 X
 Buy Stop
 Shift+B
 Shift+X, Ctrl+Shift+X
 Buy Limit
 Ctrl+Shift+B
 Shift+X, Ctrl+Shift+X
 Sell Stop
 Shift+S
 Shift+X, Ctrl+Shift+X
 Sell Limit
 Ctrl+Shift+S
 Shift+X, Ctrl+Shift+X

Keys can be assigned in any case in the settings. The EA will convert them to upper case automatically. We can set three keys - for buying, for selling and for closing positions/deleting orders. The Ctrl and Shift control keys for changing the type of pending orders in a request are assigned by default and cannot be changed. When pressing 'B', a Buy position is opened, when holding Shift and pressing 'B', a Buy Stop pending order is set. When simultaneously holding Ctrl and Shift, and pressing 'B', a Buy Limit pending order is set. The same applies to selling - simply pressing 'S' opens a Sell position, while holding down the control keys results in setting a Sell Stop and Sell Limit. Closing all open positions is carried out by pressing the 'X' button, and holding Shift or Ctrl+Shift (does not matter) and pressing 'X' will lead to the deletion of all pending orders.

//+------------------------------------------------------------------+
//|                                      TestMqlTradeTransaction.mq5 |
//|                                  Copyright 2023, MetaQuotes Ltd. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2023, MetaQuotes Ltd."
#property link      "https://www.mql5.com"
#property version   "1.00"
//--- input parameters
input double   InpLots        =  0.1;     /* Lots                       */ // Requested volume
input int      InpMagic       =  1024;    /* Magic number               */ // EA ID
input int      InpDistance    =  300;     /* Orders placement distance  */ // Distance for setting pending orders
input uint     InpDeviation   =  5;       /* Price deviation            */ // Allowed deviation from the price
input string   InpKeyBuy      =  "B";     /* Key to open Buy            */ // Key to open a Buy position (with Shift - Stop, with Ctrl+Shift - Limit)
input string   InpKeySell     =  "S";     /* Key to open Sell           */ // Key to open Sell (with Shift - Stop, with Ctrl+Shift - Limit)
input string   InpKeyClose    =  "X";     /* Key to close/delete        */ // Key for closing a position (without control keys) or deleting an order (with Shift or Shift+Ctrl)
//--- Global variables
ushort         key_buy;                   // Key to send a buy order 
ushort         key_sell;                  // Key to send a sell order
ushort         key_close;                 // Key to close or delete
//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- Convert the text assigned to Buy to uppercase and get the code of the first character
   string tmp=InpKeyBuy;
   tmp.Upper();
   key_buy=StringGetCharacter(tmp,0);
//--- Convert the text assigned to Sell to uppercase and get the code of the first character
   tmp=InpKeySell;
   tmp.Upper();
   key_sell=StringGetCharacter(tmp,0);
//--- Convert the text assigned to Close to uppercase and get the code of the first character
   tmp=InpKeyClose;
   tmp.Upper();
   key_close=StringGetCharacter(tmp,0);

//--- If the keys assigned to Buy and Sell match, report this and exit with an error
   if(key_sell==key_buy)
     {
      PrintFormat("The key assigned to Sell ('%c') is the same as the key assigned to Buy ('%c')",key_sell,key_buy);
      return INIT_PARAMETERS_INCORRECT;
     }
//--- If the keys assigned to Close and Buy match, report this and exit with an error
   if(key_close==key_buy)
     {
      PrintFormat("The key assigned to Close ('%c') is the same as the key assigned to Buy ('%c')",key_close,key_buy);
      return INIT_PARAMETERS_INCORRECT;
     }
//--- If the keys assigned to Close and Sell match, report this and exit with an error
   if(key_close==key_sell)
     {
      PrintFormat("The key assigned to Close ('%c') is the same as the key assigned to Sell ('%c')",key_close,key_sell);
      return INIT_PARAMETERS_INCORRECT;
     }
//--- Successful initialization. Display the assigned keys in the journal and return successful execution
   string kb="Key assigned to Buy: ";
   string ks="Key assigned to Sell: ";
   string kc="Key assigned to Close: ";
   PrintFormat("%-23s%c (key code %lu)\n%-23s%c (key code %lu)\n%-23s%c (key code %lu)",kb,key_buy,key_buy,ks,key_sell,key_sell,kc,key_close,key_close);
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
//---
   
  }
//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
  {
//---
   
  }
//+------------------------------------------------------------------+
//| ChartEvent function                                              |
//+------------------------------------------------------------------+
void OnChartEvent(const int id,
                  const long &lparam,
                  const double &dparam,
                  const string &sparam)
  {
//--- If a key is pressed
   if(id==CHARTEVENT_KEYDOWN)
     {
      //--- If neither Ctrl nor Shift are held 
      if(!IsCtrlKeyPressed() && !IsShiftKeyPressed())
        {
         //--- If the button is assigned to the Buy position, open Buy
         if(lparam==key_buy)
            OpenBuy(NULL,InpLots,InpMagic,InpDeviation,"TestMqlTradeTransaction");
         //--- If the button is assigned to the Sell position, open Sell
         if(lparam==key_sell)
            OpenSell(NULL,InpLots,InpMagic,InpDeviation,"TestMqlTradeTransaction");
         //--- If the button is assigned to closing positions, close all positions 
         if(lparam==key_close)
            ClosePositionsAll(Symbol());
        }
      //--- If only Shift is held 
      if(IsShiftKeyPressed() && !IsCtrlKeyPressed())
        {
         //--- If the button is assigned to Buy order, open Buy Stop order
         if(lparam==key_buy)
            SetBuyStop(NULL,InpLots,InpMagic,InpDistance,"TestMqlTradeTransaction");
         //--- If the button is assigned to Sell order, open Sell Stop order
         if(lparam==key_sell)
            SetSellSellStop(NULL,InpLots,InpMagic,InpDistance,"TestMqlTradeTransaction");
         //--- If the button is assigned to delete orders, delete all orders 
         if(lparam==key_close)
            DeleteOrdersAll(NULL);
        }
         //--- If Shift is held together with Ctrl
      if(IsShiftKeyPressed() && IsCtrlKeyPressed())
        {
         //--- If the button is assigned to Buy order, open Buy Limit order
         if(lparam==key_buy)
            SetBuyLimit(NULL,InpLots,InpMagic,InpDistance,"TestMqlTradeTransaction");
         //--- If the button is assigned to Sell order, open Sell Limit order
         if(lparam==key_sell)
            SetSellLimit(NULL,InpLots,InpMagic,InpDistance,"TestMqlTradeTransaction");
         //--- If the button is assigned to delete orders, delete all orders 
         if(lparam==key_close)
            DeleteOrdersAll(Symbol());
        }
     }
  }
//+------------------------------------------------------------------+
//| TradeTransaction function                                        |
//+------------------------------------------------------------------+
void OnTradeTransaction(const MqlTradeTransaction& trans,
                        const MqlTradeRequest& request,
                        const MqlTradeResult& result)
  {
//--- Set all incoming trade transactions to the journal
   TradeTransactionInformer(trans,request,result,18,2,true);
  }

Placing orders and opening positions is implemented in the OnChartEvent() handler of the EA. In the OnTradeTransaction() handler, call the TradeTransactionInformer() function considered above. It will display each incoming transaction in the journal. The EA uses all the functions discussed in the article in its work, so they should be included in the EA file for its error-free compilation.

When placing a BuyLimit order, the following entries of all processed transactions will be displayed in the journal:

Transaction Type: Order add (Adding a new open order)
  Order:            1838106218
  Symbol:           EURUSD
  Order type:       Buy Limit (Buy Limit pending order)
  Order state:      Started (Order checked, but not yet accepted by broker)
  Time type:        Time GTC (Good till cancel order)
  Price:            1.09449
  Price SL:         0.0
  Price TP:         0.0
  Volume:           0.10
Transaction Type: Order update (Updating an open order)
  Order:            1838106218
  Symbol:           EURUSD
  Order type:       Buy Limit (Buy Limit pending order)
  Order state:      Placed (Order accepted)
  Time type:        Time GTC (Good till cancel order)
  Price:            1.09449
  Price SL:         0.0
  Price TP:         0.0
  Volume:           0.10
Transaction Type: Request (The trade request has been processed by a server and processing result has been received)
  The second and third parameters of OnTradeTransaction function (request and result) must be analyzed for additional data:
Request Pending (Place a trade order for the execution under specified conditions (pending order)):
  Symbol:           EURUSD
  Volume:           0.10
  Price:            1.09449
  SL:               0.0
  TP:               0.0
  Type:             Buy Limit (Buy Limit pending order)
  Type filling:     Return (In case of partial filling, an order with remaining volume is not canceled but processed further)
  Type time:        Time GTC (Good till cancel order)
  Expiration:       0
  Magic:            1024
  Comment:          TestMqlTradeTransaction
OrderSend result:
  Retcode:          10009 DONE (Request completed)
  Deal:             0
  Order:            1838106218
  Volume:           0.10
  Price:            0.0
  Bid:              0.0
  Ask:              0.0
  Comment:          
  Request ID:       930808478
  Retcode External: 0

When subsequently deleting a placed order, we will receive the following entries in the terminal journal:

Transaction Type: Order update (Updating an open order)
  Order:            1838106218
  Symbol:           EURUSD
  Order type:       Buy Limit (Buy Limit pending order)
  Order state:      Request cancel (Order is being deleted (deleting from the trading system))
  Time type:        Time GTC (Good till cancel order)
  Price:            1.09449
  Price SL:         0.0
  Price TP:         0.0
  Volume:           0.10
Transaction Type: Order delete (Removing an order from the list of the open ones)
  Order:            1838106218
  Symbol:           EURUSD
  Order type:       Buy Limit (Buy Limit pending order)
  Order state:      Canceled (Order canceled by client)
  Time type:        Time GTC (Good till cancel order)
  Price:            1.09449
  Price SL:         0.0
  Price TP:         0.0
  Volume:           0.10
Transaction Type: History add (Adding an order to the history as a result of execution or cancellation)
  Order:            1838106218
  Symbol:           EURUSD
  Order type:       Buy Limit (Buy Limit pending order)
  Order state:      Canceled (Order canceled by client)
  Time type:        Time GTC (Good till cancel order)
  Price:            1.09449
  Price SL:         0.0
  Price TP:         0.0
  Volume:           0.10
Transaction Type: Request (The trade request has been processed by a server and processing result has been received)
  The second and third parameters of OnTradeTransaction function (request and result) must be analyzed for additional data:
Request Remove (Delete the pending order placed previously):
  Order:            1838106218
OrderSend result:
  Retcode:          10009 DONE (Request completed)
  Deal:             0
  Order:            1838106218
  Volume:           0.00
  Price:            0.0
  Bid:              0.0
  Ask:              0.0
  Comment:          
  Request ID:       930808479
  Retcode External: 0


Conclusion

We have considered all the structures for creating a trade order, checking it and sending it to the server, as well as obtaining a complete description of all trading transactions occurring on the account as a result of trading operations.

All proposed functions can be used "as is". Alternatively, you can modify them to suit your vision of their use.

The EA file with all the functions considered in the article is included in the attachment. You can download it and test everything yourself.

Translated from Russian by MetaQuotes Ltd.
Original article: https://www.mql5.com/ru/articles/13052

Attached files |
Developing a Replay System — Market simulation (Part 15): Birth of the SIMULATOR (V) - RANDOM WALK Developing a Replay System — Market simulation (Part 15): Birth of the SIMULATOR (V) - RANDOM WALK
In this article we will complete the development of a simulator for our system. The main goal here will be to configure the algorithm discussed in the previous article. This algorithm aims to create a RANDOM WALK movement. Therefore, to understand today's material, it is necessary to understand the content of previous articles. If you have not followed the development of the simulator, I advise you to read this sequence from the very beginning. Otherwise, you may get confused about what will be explained here.
Developing a Replay System — Market simulation (Part 14): Birth of the SIMULATOR (IV) Developing a Replay System — Market simulation (Part 14): Birth of the SIMULATOR (IV)
In this article we will continue the simulator development stage. this time we will see how to effectively create a RANDOM WALK type movement. This type of movement is very intriguing because it forms the basis of everything that happens in the capital market. In addition, we will begin to understand some concepts that are fundamental to those conducting market analysis.
The case for using a Composite Data Set this Q4 in weighing SPDR XLY's next performance The case for using a Composite Data Set this Q4 in weighing SPDR XLY's next performance
We consider XLY, SPDR’s consumer discretionary spending ETF and see if with tools in MetaTrader’s IDE we can sift through an array of data sets in selecting what could work with a forecasting model with a forward outlook of not more than a year.
How to create a simple Multi-Currency Expert Advisor using MQL5 (Part 4): Triangular moving average — Indicator Signals How to create a simple Multi-Currency Expert Advisor using MQL5 (Part 4): Triangular moving average — Indicator Signals
The Multi-Currency Expert Advisor in this article is Expert Advisor or trading robot that can trade (open orders, close orders and manage orders for example: Trailing Stop Loss and Trailing Profit) for more than one symbol pair only from one symbol chart. This time we will use only 1 indicator, namely Triangular moving average in multi-timeframes or single timeframe.