OnTradeTransaction: unexpected data received in transaction event data... bug or something else?

 

Dear all -- I'm debugging some code for trade event processing with the newer OnTradeTransaction() function as opposed to the parameter-less OnTrade() which requires doing some "guesswork" going through lists of orders (active & historic), deals and positions...

void OnTradeTransaction(const MqlTradeTransaction& trans, const MqlTradeRequest& request, const MqlTradeResult& result);

Reading this excellent article MQL5 Cookbook: Processing of the TradeTransaction Event plus the very useful 'TradeTransaction Class' library by 'amrali' for MetaTrader 5 in the codebase, gave me a good working start. Given that OnTradeTransaction() receives parameters describing the generated trade event, I expected this to be far less painful -- I am not sure if some of the issues can vary between brokers and their specific MT5 server configurations.

One thing that stumped me is that the events seem to arrive in reverse (or random) sequence -- namely, I thought that trans.type == TRADE_TRANSACTION_REQUEST would always be first when a user- or expert- initiated trading operation is submitted, but in my case (broker??) it appears to come last.

The other situation that got me scratching my head is the data pictured below from one of the debugging sessions. My understanding was that, when a trading order is successful, e.g. result.retcode == 10009, the order number in the request structure (MqlTradeRequest) should be equal to the one in the result structure (MqlTradeResult), i.e. request.order == result.order. What could be the problem here?

Feel free to share any other tips & tricks, or lessons from the real world "battlefield", when it comes to trade event handling... Thanks in advance!!

MQL5 Cookbook: Processing of the TradeTransaction Event
MQL5 Cookbook: Processing of the TradeTransaction Event
  • www.mql5.com
This article considers capabilities of the MQL5 language from the point of view of the event-driven programming. The greatest advantage of this approach is that the program can receive information about phased implementation of a trade operation. The article also contains an example of receiving and processing information about ongoing trade operation using the TradeTransaction event handler. In my opinion, such an approach can be used for copying deals from one terminal to another.
 

1. Where did you get the idea that events should come sequentially?

2. See help: Basic Principles - a deal can be output to an external trading system.

3. Read  OnTradeTransaction - when not all fields are filled

4. Always work with the event TRADE_TRANSACTION_DEAL_ADD – adding a deal to history

5. For self-education use the "listener" code

//+------------------------------------------------------------------+
//|                                     TradeTransactionListener.mq5 |
//|                        Copyright 2016, MetaQuotes Software Corp. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2016, MetaQuotes Software Corp."
#property link      "https://www.mql5.com"
#property version   "1.00"
//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
//---
   PrintFormat("LAST PING=%.f ms",
               TerminalInfoInteger(TERMINAL_PING_LAST)/1000.);
//---
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
  {
//---

  }
//+------------------------------------------------------------------+
//| TradeTransaction function                                        |
//+------------------------------------------------------------------+
void OnTradeTransaction(const MqlTradeTransaction &trans,
                        const MqlTradeRequest &request,
                        const MqlTradeResult &result)
  {
//---
   static int counter=0;   // Counter of OnTradeTransaction() calls
   static uint lasttime=0; // Time of the last call of OnTradeTransaction()
//---
   uint time=GetTickCount();
//--- If the last transaction was performed more than 1 seconds ago,
   if(time-lasttime>1000)
     {
      counter=0; // then this is a new trade operation, an the counter can be reset
      if(IS_DEBUG_MODE)
         Print(" New trade operation");
     }
   lasttime=time;
   counter++;
   Print(counter,". ",__FUNCTION__);
//--- Result of trade request execution
   ulong            lastOrderID   =trans.order;
   ENUM_ORDER_TYPE  lastOrderType =trans.order_type;
   ENUM_ORDER_STATE lastOrderState=trans.order_state;
//--- The name of the symbol for which a transaction was performed
   string trans_symbol=trans.symbol;
//--- type of transaction
   ENUM_TRADE_TRANSACTION_TYPE  trans_type=trans.type;
   switch(trans.type)
     {
      case  TRADE_TRANSACTION_POSITION:   // Position modification
        {
         ulong pos_ID=trans.position;
         PrintFormat("MqlTradeTransaction: Position  #%d %s modified: SL=%.5f TP=%.5f",
                     pos_ID,trans_symbol,trans.price_sl,trans.price_tp);
        }
      break;
      case TRADE_TRANSACTION_REQUEST:     // Sending a trade request
         PrintFormat("MqlTradeTransaction: TRADE_TRANSACTION_REQUEST");
         break;
      case TRADE_TRANSACTION_DEAL_ADD:    // Adding a trade
        {
         ulong          lastDealID   =trans.deal;
         ENUM_DEAL_TYPE lastDealType =trans.deal_type;
         double        lastDealVolume=trans.volume;
         //--- Trade ID in an internal system - a ticket assigned by Moscow Exchange
         string Exchange_ticket="";
         if(HistoryDealSelect(lastDealID))
            Exchange_ticket=HistoryDealGetString(lastDealID,DEAL_EXTERNAL_ID);
         if(Exchange_ticket!="")
            Exchange_ticket=StringFormat("(MOEX deal=%s)",Exchange_ticket);

         PrintFormat("MqlTradeTransaction: %s deal #%d %s %s %.2f lot   %s",EnumToString(trans_type),
                     lastDealID,EnumToString(lastDealType),trans_symbol,lastDealVolume,Exchange_ticket);
        }
      break;
      case TRADE_TRANSACTION_HISTORY_ADD: // Adding an order to the history
        {
         //--- Order ID in an internal system - a ticket assigned by Moscow Exchange
         string Exchange_ticket="";
         if(lastOrderState==ORDER_STATE_FILLED)
           {
            if(HistoryOrderSelect(lastOrderID))
               Exchange_ticket=HistoryOrderGetString(lastOrderID,ORDER_EXTERNAL_ID);
            if(Exchange_ticket!="")
               Exchange_ticket=StringFormat("(MOEX ticket=%s)",Exchange_ticket);
           }
         PrintFormat("MqlTradeTransaction: %s order #%d %s %s %s   %s",EnumToString(trans_type),
                     lastOrderID,EnumToString(lastOrderType),trans_symbol,EnumToString(lastOrderState),Exchange_ticket);
        }
      break;
      default: // other transactions
        {
         //--- Order ID in an internal system - a ticket assigned by Moscow Exchange
         string Exchange_ticket="";
         if(lastOrderState==ORDER_STATE_PLACED)
           {
            if(OrderSelect(lastOrderID))
               Exchange_ticket=OrderGetString(ORDER_EXTERNAL_ID);
            if(Exchange_ticket!="")
               Exchange_ticket=StringFormat("MOEX ticket=%s",Exchange_ticket);
           }
         PrintFormat("MqlTradeTransaction: %s order #%d %s %s   %s",EnumToString(trans_type),
                     lastOrderID,EnumToString(lastOrderType),EnumToString(lastOrderState),Exchange_ticket);
        }
      break;
     }
//--- order ticket    
   ulong orderID_result=result.order;
   string retcode_result=GetRetcodeID(result.retcode);
   if(orderID_result!=0)
      PrintFormat("MqlTradeResult: order #%d retcode=%s ",orderID_result,retcode_result);
//---   
  }
//+------------------------------------------------------------------+
//| Converts numeric response codes to string mnemonics              |
//+------------------------------------------------------------------+
string GetRetcodeID(int retcode)
  {
   switch(retcode)
     {
      case 10004: return("TRADE_RETCODE_REQUOTE");             break;
      case 10006: return("TRADE_RETCODE_REJECT");              break;
      case 10007: return("TRADE_RETCODE_CANCEL");              break;
      case 10008: return("TRADE_RETCODE_PLACED");              break;
      case 10009: return("TRADE_RETCODE_DONE");                break;
      case 10010: return("TRADE_RETCODE_DONE_PARTIAL");        break;
      case 10011: return("TRADE_RETCODE_ERROR");               break;
      case 10012: return("TRADE_RETCODE_TIMEOUT");             break;
      case 10013: return("TRADE_RETCODE_INVALID");             break;
      case 10014: return("TRADE_RETCODE_INVALID_VOLUME");      break;
      case 10015: return("TRADE_RETCODE_INVALID_PRICE");       break;
      case 10016: return("TRADE_RETCODE_INVALID_STOPS");       break;
      case 10017: return("TRADE_RETCODE_TRADE_DISABLED");      break;
      case 10018: return("TRADE_RETCODE_MARKET_CLOSED");       break;
      case 10019: return("TRADE_RETCODE_NO_MONEY");            break;
      case 10020: return("TRADE_RETCODE_PRICE_CHANGED");       break;
      case 10021: return("TRADE_RETCODE_PRICE_OFF");           break;
      case 10022: return("TRADE_RETCODE_INVALID_EXPIRATION");  break;
      case 10023: return("TRADE_RETCODE_ORDER_CHANGED");       break;
      case 10024: return("TRADE_RETCODE_TOO_MANY_REQUESTS");   break;
      case 10025: return("TRADE_RETCODE_NO_CHANGES");          break;
      case 10026: return("TRADE_RETCODE_SERVER_DISABLES_AT");  break;
      case 10027: return("TRADE_RETCODE_CLIENT_DISABLES_AT");  break;
      case 10028: return("TRADE_RETCODE_LOCKED");              break;
      case 10029: return("TRADE_RETCODE_FROZEN");              break;
      case 10030: return("TRADE_RETCODE_INVALID_FILL");        break;
      case 10031: return("TRADE_RETCODE_CONNECTION");          break;
      case 10032: return("TRADE_RETCODE_ONLY_REAL");           break;
      case 10033: return("TRADE_RETCODE_LIMIT_ORDERS");        break;
      case 10034: return("TRADE_RETCODE_LIMIT_VOLUME");        break;
      case 10035: return("TRADE_RETCODE_INVALID_ORDER");       break;
      case 10036: return("TRADE_RETCODE_POSITION_CLOSED");     break;
      default:
         return("TRADE_RETCODE_UNKNOWN="+IntegerToString(retcode));
         break;
     }
//---
  }
//+------------------------------------------------------------------+
Basic Principles - Trading Operations - MetaTrader 5 Help
Basic Principles - Trading Operations - MetaTrader 5 Help
  • www.metatrader5.com
Before you proceed to study the trade functions of the platform, you must have a clear understanding of the basic terms: order, deal and position...