Question about the OnTradeTransaction function

 

According to the help.

позволяет связать выполненное действие request identifier (OrderSend or OrderSendAsync functions call) with the result of this action passed to OnTradeTransaction


In other words, it allows the OnTradeTransaction function to check if the order was correctly executed. Or am I misunderstanding something?

But there is a caveat here:

The order of arrival of these transactions to the terminal is not guaranteed, so we cannot build our trading algorithm on waiting for some trade transactions to arrive after others. In addition, transactions may be lost in delivery from the server to the terminal.

Well, not guaranteed consistency is half the trouble. But what about the fact that a transaction may get lost and hence we cannot use the OnTradeTransaction function for checks.

So the question arises: What then do we need this event handler function for? Is it an absolutely useless add-on for the terminal? Or I'm misunderstanding something again?

 
TheOnTradeTransaction function is necessary first of all for creating asynchronous trading Expert Advisors. If you are not engaged in such developments, you most likely do not need to study this function. In brief,OnTradeTransaction must be used together with checking changes of the trade environment. I don't want to discuss this problem in details.
 

Thank you for that as well. But even if there are possible transaction losses when creating asynchronous trading experts, not by me, what kind of verification can we talk about?

And in general I am satisfied with the answer. I do not have enough knowledge to discuss this subject in details.

Thanks again.

 
AlexeyVik :

Thanks for that too. But even if when creating, not by me, asynchronous trading experts, transaction losses are possible, what kind of verification can we talk about?

In general, the answer satisfies me. And I do not have enough knowledge to discuss this topic in more detail.

And thanks again.

I wrote an article on this issue.

It just tells how not to "lose" the order placed by the OrderSendAsync command

Now it is being checked, but if it does not come out, then here is an example from this article (forward market FORTS):

 //+------------------------------------------------------------------+
//|                                                       Demo_1.mq5 |
//|                                          Copyright 2015, Mikalas |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2015, Mikalas"
#property link        "https://www.mql5.com"
#property version    "1.00"

input uint TimerPeriod = 1000 ;     //Время ожидания проверки ордера (1 сек.)   

ulong       order_ticket;
uint        request_id;
//
ulong       magic_number = 1010000 ;   //по 10000 на каждый символ
ulong       magic_storage;
ulong       mem_magic;
datetime    mem_time;
uint        mem_tick;
ulong       deal_volume;             //Счётчик заливки ордера
//
//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit ()
{
  deal_volume = 0 ;             
   //--- Установка таймера
   if ( ! EventSetMillisecondTimer ( 500 ) )     //0.5 cек.
  {
     MessageBox ( "Таймер не установлен!" , "Ошибка" , MB_OK | MB_ICONHAND );
     return ( INIT_FAILED );
  } 
   return ( INIT_SUCCEEDED );
}
//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit ( const int reason )
{
//--- удаление таймера
   EventKillTimer ();
}
//+------------------------------------------------------------------+
//| Expert Get retcode function                                      |
//+------------------------------------------------------------------+
string GetRetCode( const uint code )
{
   string retcode;
//---  
   switch ( code )
  {
     case 10004 : retcode = "Реквота" ;
         break ;
     case 10006 : retcode = "Запрос отвергнут" ;
         break ;
     case 10007 : retcode = "Запрос отменен трейдером" ;
         break ;
     case 10008 : retcode = "Ордер размещен" ;
         break ;
     case 10009 : retcode = "Заявка выполнена" ;
         break ;
     case 10010 : retcode = "Заявка выполнена частично" ;
         break ;
     case 10011 : retcode = "Ошибка обработки запроса" ;
         break ;
     case 10012 : retcode = "Запрос отменен по истечению времени" ;
         break ;
     case 10013 : retcode = "Неправильный запрос" ;
         break ;
     case 10014 : retcode = "Неправильный объем в запросе" ;
         break ;
     case 10015 : retcode = "Неправильная цена в запросе" ;
         break ;
     case 10016 : retcode = "Неправильные стопы в запросе" ;
         break ;
     case 10017 : retcode = "Торговля запрещена" ;
         break ;
     case 10018 : retcode = "Рынок закрыт" ;
         break ;
     case 10019 : retcode = "Нет достаточных денежных средств для выполнения запроса" ;
         break ;
     case 10020 : retcode = "Цены изменились" ;
         break ;
     case 10021 : retcode = "Отсутствуют котировки для обработки запроса" ;
         break ;
     case 10022 : retcode = "Неверная дата истечения ордера в запросе" ;
         break ;
     case 10023 : retcode = "Состояние ордера изменилось" ;
         break ;
     case 10024 : retcode = "Слишком частые запросы" ;
         break ;
     case 10025 : retcode = "В запросе нет изменений" ;
         break ;
     case 10026 : retcode = "Автотрейдинг запрещен сервером" ;
         break ;
     case 10027 : retcode = "Автотрейдинг запрещен клиентским терминалом" ;
         break ;
     case 10028 : retcode = "Запрос заблокирован для обработки" ;
         break ;
     case 10029 : retcode = "Ордер или позиция заморожены" ;
         break ;
     case 10030 : retcode = "Указан неподдерживаемый тип исполнения ордера по остатку" ;
         break ;
     case 10031 : retcode = "Нет соединения с торговым сервером" ;
         break ;
     case 10032 : retcode = "Операция разрешена только для реальных счетов" ;
         break ;
     case 10033 : retcode = "Достигнут лимит на количество отложенных ордеров" ;
         break ;
     case 10034 : retcode = "Достигнут лимит на объем ордеров и позиций для данного символа" ;
         break ;
     case 10035 : retcode = "Неверный или запрещённый тип ордера" ;
         break ;
     case 10036 : retcode = "Позиция с указанным POSITION_IDENTIFIER уже закрыта" ;
         break ;
     default : retcode = "Нет кода возврата." ;  
         break ; 
  }
   return ( retcode );
}
//+------------------------------------------------------------------+
//| Expert place order function                                      |
//+------------------------------------------------------------------+
bool PlaceOrder( const string a_symbol, const double price, const double volume, const bool buy_sell )
{
   MqlTradeRequest request = { 0 };
   MqlTradeResult   result  = { 0 };
//---  
  order_ticket = 0 ;
  request_id = 0 ;
  mem_time = TimeTradeServer ();   //Время установки ордера (для для сокращения поиска в истории)
  mem_tick = GetTickCount ();       //Начало отсчёта времени (для проверки, если не сработала функция OnTradeTransaction)   
  mem_magic = magic_storage + 1 ;   //Уникальный номер (magic) ордера
  
   if ( mem_magic >= ( magic_number + 9999 ) ) mem_magic = magic_number;   //Переполнение, начинаем сначала
     
//--- Fill structure
  request.action = TRADE_ACTION_PENDING ;
  request.magic  = mem_magic;
  request.symbol = a_symbol;
  request.volume = volume;
  request.price  = price;
    
   if ( buy_sell )
  {
    request.type       = ORDER_TYPE_BUY_LIMIT ;
  }
   else
  {
    request.type       = ORDER_TYPE_SELL_LIMIT ;
  } 
  request.comment      = "Отложенный ордер..." ;      
  request.type_filling = ORDER_FILLING_RETURN ;
  request.type_time    = ORDER_TIME_DAY ;
   
//--- Send order
   if ( OrderSendAsync ( request, result ) )
  {
     if ( result.retcode == TRADE_RETCODE_PLACED ) 
    {
      request_id = result.request_id;
      magic_storage = mem_magic;
       return ( true );
    }
     else
    {
      mem_magic = 0 ;
      mem_time = 0 ;
      mem_tick = 0 ;
       Print ( "Ордер не отправлен! " , a_symbol, " Код возврата = " , GetRetCode( result.retcode ) );
       return ( false );
    }
  }
   else
  {
    mem_magic = 0 ;
    mem_time = 0 ;
    mem_tick = 0 ;
     Print ( "Ордер не отправлен! " , a_symbol, " Код возврата = " , GetRetCode( result.retcode ) );
     return ( false );
  }
}
//+------------------------------------------------------------------+
//| Expert place order function                                      |
//+------------------------------------------------------------------+
void OnTick ()
{
   MqlTick a_tick;
  
   if ( SymbolInfoTick ( _Symbol , a_tick ) )
  {
    PlaceOrder( _Symbol , a_tick.ask, 1 , false );   //Устанавливаем ордер
  }     
}

//+------------------------------------------------------------------+
//| TradeTransaction function                                        |
//+------------------------------------------------------------------+
void OnTradeTransaction ( const MqlTradeTransaction & trans,
                         const MqlTradeRequest & request,
                         const MqlTradeResult & result)
{
   switch ( trans.type )
  {
     case TRADE_TRANSACTION_REQUEST :     if ( trans.order_state == ORDER_STATE_STARTED )
                                        {
                                           if ( ( request_id != 0 ) && ( result.request_id == request_id ) )
                                          {
                                             if ( result.retcode == TRADE_RETCODE_PLACED )
                                            {
                                              order_ticket = result.order;         //Получаем билет ордера
                                              mem_tick     = GetTickCount ();       //Начало отсчёта времени (для проверки, если не сработает функция OnTradeTransaction) 
                                            }
                                             else
                                            {
                                              mem_tick = 0 ;
                                               Print ( "OnTradeTransaction: Не получен билет ордера. Запрос = " , request_id );
                                            }
                                            request_id = 0 ;
                                            mem_magic  = 0 ;
                                            mem_time   = 0 ;
                                          }  
                                        }
                                         break ;
                                    
     case TRADE_TRANSACTION_DEAL_ADD :     if ( trans.order_state == ORDER_STATE_STARTED )
                                        {
                                           if ( ( order_ticket != 0 ) && ( trans.order == order_ticket ) )
                                          { 
                                             if ( ! OrderSelect ( order_ticket ) ) order_ticket = 0 ;     //Обнуляем билет, так как ордер залился полностью.
                                             Print ( "OnTradeTransaction: Сделка совершена, билет = " , trans.order ); 
                                          } 
                                        }
                                         break ;
                                     
     case TRADE_TRANSACTION_HISTORY_ADD : if ( (order_ticket != 0 ) && ( trans.order == order_ticket ) )
                                        {
                                          order_ticket = 0 ;
                                          
                                           switch ( trans.order_state )
                                          {
                                             case ORDER_STATE_REJECTED : Print ( "OnTradeTransaction: Ордер отклонён." );
                                                                       break ;
                                                                       
                                             case ORDER_STATE_CANCELED : Print ( "OnTradeTransaction: Ордер удалён." ); 
                                                                       break ;
                                                                     
                                             case ORDER_STATE_EXPIRED :   Print ( "OnTradeTransaction: Срок действия ордера окончен." );
                                                                       break ;
                                          }   
                                        }
                                         break ;     
                                        
     case TRADE_TRANSACTION_ORDER_UPDATE : if ( trans.order_state == ORDER_STATE_REQUEST_MODIFY )
                                         {
                                           if ( ( order_ticket != 0 ) && ( trans.order == order_ticket ) )
                                           {
                                             Print ( "OnTradeTransaction: Ордер в состоянии модифицикации." );
                                              mem_tick = GetTickCount ();  
                                           }
                                         }
                                         break ;                                                                                          
  }     
}
//+------------------------------------------------------------------+
// Expert find histiry order function                                |
//+------------------------------------------------------------------+
ulong FindHistoryOrder( const ulong a_magic, const datetime a_time, const datetime b_time )
{
   if ( HistorySelect ( a_time, b_time ) )
  {
     for ( int i = HistoryOrdersTotal () - 1 ; i >= 0 ; i-- )
    {
       ulong cur_ticket = HistoryOrderGetTicket ( i );
      
       if ( ulong ( HistoryOrderGetInteger ( cur_ticket, ORDER_MAGIC ) ) == a_magic ) return ( cur_ticket );
    }
  }  
   return ( 0 ) ;
}
//+------------------------------------------------------------------+
// Expert find order function                                        |
//+------------------------------------------------------------------+
ulong FindOrder( const ulong a_magic )
{
   for ( int i = OrdersTotal () - 1 ; i >= 0 ; i-- )
  {
     ulong cur_ticket = OrderGetTicket ( i );
    
     if ( ulong ( OrderGetInteger ( ORDER_MAGIC ) ) == a_magic ) return ( cur_ticket );
  }
   return ( 0 );
}
//+------------------------------------------------------------------+
//| Expert Get order tiket function                                  |
//+------------------------------------------------------------------+
ulong GetOrderTicket( const ulong m_magic, const datetime m_time, const datetime cur_time )
{
   ulong a_ticket = FindOrder( m_magic );                         //Ищем действующий ордер
  
   if ( a_ticket > 0 )
  {
     return ( a_ticket );
  }
   else
  {
    a_ticket = FindHistoryOrder( m_magic, m_time, cur_time );   //Ищем ордер в истории
    
     if ( a_ticket > 0 )
    {
       return ( a_ticket );
    }
  }  
   return ( 0 ); 
}
//+------------------------------------------------------------------+
//| Expert Check time function                                      |
//+------------------------------------------------------------------+
bool CheckTime( const uint start_value, const uint per_value )
{
   uint end_value = GetTickCount ();
  
   if ( end_value < start_value )
  {
     if ( ( start_value - end_value ) >= per_value ) return ( true );
  } 
   else
  {
     if ( ( end_value - start_value ) >= per_value ) return ( true );
  }
   return ( false );
}
//+------------------------------------------------------------------+
// Expert Get History Deals function                                 |
//+------------------------------------------------------------------+
uint GetHistoryDeals( const ulong ticket )
{
   int deals_total = HistoryDealsTotal ();
   uint ticket_deals = 0 ;
          
   for ( int i = 0 ; i < deals_total; i++ )
  {
     ulong deal_ticket = HistoryDealGetTicket ( i );
     ulong ord_ticket = ulong ( HistoryDealGetInteger ( deal_ticket, DEAL_ORDER ) );
        
     if ( ( ord_ticket > 0 ) && ( ord_ticket == ticket ) ) ticket_deals++;
  }
           
   if ( ticket_deals > 0 ) return ( ticket_deals );
  
   return ( 0 );
}
//+------------------------------------------------------------------+
//| Expert Check order function                                      |
//+------------------------------------------------------------------+
void CheckOrder()
{
   if ( ( mem_tick > 0 ) && CheckTime( mem_tick, TimerPeriod ) )           //Проверка времени ожидания действия над ордером (установка, модификация или удадение) 
  {
     if ( mem_magic > 0 )                                                   //Нет билета, получаем билет
    {
      order_ticket = GetOrderTicket( mem_magic, mem_time, TimeTradeServer () );
      
       if ( order_ticket > 0 )
      {
        mem_tick  = GetTickCount ();                                       //Начало отсчёта времени, если в будующем вновь не сработает функция OnTradeTransaction 
      }
       else
      {
        mem_tick  = 0 ;
         Print ( "Timeout: Билет ордера не найден!" );  
      }
      mem_magic = 0 ;
      mem_time  = 0 ;
    }
     else                                                                  
    {
       if ( order_ticket > 0 )                                             //Есть билет, смотрим что произошло с ордером
      {
         if ( OrderSelect ( order_ticket ) )                                 //Ордер рабочий 
        {
           double init_volume = OrderGetDouble ( ORDER_VOLUME_INITIAL );
           double cur_volume = OrderGetDouble ( ORDER_VOLUME_CURRENT );
          
           if ( init_volume != cur_volume )
          {
             ulong d_volume = ( ulong ( init_volume - cur_volume ) - deal_volume );
            deal_volume = deal_volume + d_volume;
            
             if ( d_volume > 0 )
            { 
               Print ( "Timeout: Сделка совершена, билет = " , order_ticket, " Объём = " , d_volume );
            }  
          }
          mem_tick = GetTickCount ();
        }
         else                                                                //Ордер исполнился, удален или экпирировался   
        {
           if ( HistoryOrderSelect ( order_ticket ) )
          {
             datetime ord_time = datetime ( HistoryOrderGetInteger ( order_ticket, ORDER_TIME_SETUP ) );
            
             if ( HistorySelect ( ord_time, TimeTradeServer () ) )
            {
               uint deals = GetHistoryDeals( order_ticket );
              
               if ( deals > 0 )
              {
                 Print ( "Timeout: Сделка совершена, билет = " , order_ticket );
              }
            }
          }
           else
          {
             Print ( "Timeout: Ордер " , order_ticket, " не найден в истории!" );
          } 
          deal_volume  = 0 ;
          order_ticket = 0 ;
          mem_tick     = 0 ;
        }
      }
       else
      {
        deal_volume = 0 ;
        mem_tick    = 0 ;
         Print ( "Timeout: Нет билета ордера!" );
      }
    }
  }
}
//+------------------------------------------------------------------+
//| Expert timer function                                            |
//+------------------------------------------------------------------+
void OnTimer ()
{
  CheckOrder();
}
//+------------------------------------------------------------------+
 
Mikalas:

I have written an article on this problem.

It tells exactly how not to "lose" an order placed by the OrderSendAssync command

It is being tested now, but if it fails, here is an example from this article (FORTS futures market):

As if we all get it and clap our hands for joy. Sorry, but it's a hell of a lot more complicated than that in your code.
 
C-4:
As if we all get it and clap our hands for joy. Sorry, but your code is a mess.

Did someone promise it would be easy?

Sorry, Vasili, wait until the article comes out....

P/S If the article doesn't come out, I'll write explanations for example here.

 
Mikalas:

P/S If the article doesn't come out, I'll post an explanation for example here.

Why shouldn't it come out? The material is interesting.

I think you, Mikalas, have made a lot of effort in figuring out a lot of things about tickets and order id in your dialogue with MQ. So wait for it.

 
Mikalas:

I have written an article on this problem.

It tells exactly how not to "lose" an order placed by the OrderSendAssync command

It's being tested now, but if it doesn't come out, here's an example for you from that article (FORTS futures market):

If you don't mind me asking. Why do you focus on placing an order using the OrderSendAssync() function? If we place an order using the OrderSend() function, won't the transaction be lost? In this case, there are more guarantees?


In my inexperience, I wanted to insert decision making in this or that case into OnTradeTransactio(). But so far it has not worked so well. And having read about a possible loss of transactions, I asked you for clarifications.

In general, to write for the FORTS market one must understand this market. For now it's not for me, maybe I don't need it. I decided to rewrite my EA written in mql4 in order to study mql5. I am studying it now.

 
AlexeyVik:

If you don't mind, a question. Why do you focus on placing an order with OrderSendAssync()? If you place an order using the OrderSend() function, won't the transaction be lost? In this case, there are more guarantees?


In my inexperience, I wanted to insert decision making in this or that case into OnTradeTransactio(). But so far it has not worked so well. And having read about a possible loss of transactions, I asked you for clarifications.

In general, to write for the FOREX market, you need to understand this market. This is not for me yet, and maybe I don't need it. I decided to rewrite my EA written in mql4 to study mql5. I am studying it now.

1. OrderSendAsync is much faster (it doesn't wait for a response from the server).

OrderSend is guaranteed to get a response from the server.

2. The example will work perfectly on FOREX

by changingresult.retcode == TRADE_RETCODE_PLACED toresult.retcode == TRADE_RETCODE_DONE

 
Mikalas:

1. OrderSendAsync is much faster (it does not wait for a response from the server)

2. The example will work perfectly on FOREX as well

by changingresult.retcode == TRADE_RETCODE_PLACED toresult.retcode == TRADE_RETCODE_DONE

I.e. if asynchrony is not applied, then there is no need in the OnTradeTransaction handler as the owls will wait for a response from the server?

But so far, I have not found the best way to determine if a Stop order has been activated. For example, I have set market Buy 0.1 and SellStop 0.3 and when it activates 0.3, it will stay 0.2 and at what point should I follow it... So it turns out that we have to follow two orders at every tick. And I wanted to relieve the Owls and control only when the transaction happens. I wanted to not even control, but to check what happened and decide what to do. But I guess this is not the case... Perhaps it is better to go back to the old tried and tested methods...

 
AlexeyVik:

So, if we don't use asynchrony, there is no need in OnTradeTransaction handler because the owls will wait for a response from the server?

But so far I haven't found the best way to detect if a stop order has been activated. For example, I have set market Buy 0.1 and SellStop 0.3, but when 0.3 is activated, it will stay 0.2 and at what point should I follow it... So it turns out that we have to follow two orders at every tick. And I wanted to relieve the Owls and control only when the transaction happens. I wanted to not even control, but to check what happened and decide what to do. But I guess this is not the case... Perhaps it's better to go back to the old, tried and tested methods...

Yes, you can avoid using OnTradeTransaction, but in this case, you will have to look through the history, which will reduce the overall performance of the EA.

Every developer has a choice!