Вопрос о функции OnTradeTransaction

 

Согласно справки

Наличие идентификатора  запроса позволяет связать выполненное действие (вызов функций OrderSend или OrderSendAsync) с результатом этого действия, передаваемым в OnTradeTransaction


Тоесть, позволяет в функции OnTradeTransaction проконтролировать правильность выполнения распоряжения. Или я что-то не так понимаю?

Но тут-же следует оговорка:

При этом очередность поступления этих транзакций в терминал не гарантирована, поэтому нельзя свой торговый алгоритм строить на ожидании поступления одних торговых транзакций после прихода других. Кроме того, транзакции могут потеряться при доставке от сервера к терминалу.

Ну, не гарантированная последовательность это пол-беды. Но что делать с тем, что транзакция может потеряться и следовательно мы не можем использовать функцию OnTradeTransaction для проверок.

Вот и вытекает вопрос: А для чего тогда нужна эта функция обработки события? Абсолютно бесполезная примочка к терминалу??? Или я опять что-то не так понимаю?

 
Функция  OnTradeTransaction прежде всего необходима для создания асинхронных торговых экспертов. Если Вы не занимаетесь подобными разработками, то Вам скорее всего этот функционал изучать не нужно. Если кратко, то OnTradeTransaction необходимо использовать совместно с проверкой изменений торгового окружения. Желание обсуждать более подробно эту проблематику у меня пока нет.
 

Спасибо и на этом. Но даже если при создании, не мной, асинхронных торговых экспертов возможны потери транзакций, о какой проверке может идти речь?

А в общем меня ответ удовлетворяет. И более подробно обсуждать эту тему у меня не хватает знаний.

И ещё раз спасибо.

 
AlexeyVik:

Спасибо и на этом. Но даже если при создании, не мной, асинхронных торговых экспертов возможны потери транзакций, о какой проверке может идти речь?

А в общем меня ответ удовлетворяет. И более подробно обсуждать эту тему у меня не хватает знаний.

И ещё раз спасибо.

Я написал статью по этой проблеме.

В ней как раз рассказывается как не "потерять" ордер, выставленный командой OrderSendAsync 

Сейчас её проверяют, но если она не выйдет, то вот Вам пример из этой статьи (срочный рынок ФОРТС):

//+------------------------------------------------------------------+
//|                                                       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:

Я написал статью по этой проблеме.

В ней как раз рассказывается как не "потерять" ордер, выставленный командой OrderSendAssync 

Сейчас её проверяют, но если она не выйдет, то вот Вам пример из этой статьи (срочный рынок ФОРТС):

Как бы мы все поняли и хлопаем в ладоши от радости. Извините, но в Вашем коде черт ногу сломит. 
 
C-4:
Как бы мы все поняли и хлопаем в ладоши от радости. Извините, но в Вашем коде черт ногу сломит. 

А кто-то обещал, что будет просто? 

Извините, Василий, дождитесь выхода статьи....

 

P/S Если статья не выйдет, то я напишу объяснения к примеру здесь. 

 
Mikalas:

P/S Если статья не выйдет, то я напишу объяснения к примеру здесь. 

А почему она не должна выйти? Материал интересный.

По-моему, Вы, Mikalas, в своё время приложили массу усилий при выяснении многих моментов по тикетам и id ордера в диалоге с MQ. Так что ждём-с.

 
Mikalas:

Я написал статью по этой проблеме.

В ней как раз рассказывается как не "потерять" ордер, выставленный командой OrderSendAssync 

Сейчас её проверяют, но если она не выйдет, то вот Вам пример из этой статьи (срочный рынок ФОРТС):

Если не возражаете, вопрос. Почему делаете акцент на постановку ордера функцией OrderSendAssync()? Разве если ставить ордер функцией OrderSend() транзакция не потеряется? В этом случае больше гарантий?


Я по неопытности хотел в OnTradeTransactio() вставить принятие решения в том или ином случае. Но пока что-то не очень получается. А прочитав о возможной потере транзакций обратился к вам за разъяснениями.

В общем, чтобы писать для рынка ФОРТС надо понимать этот рынок. Пока это не для меня, а может и не надо мне это. Я решил для изучения mql5 переписать сов написанный на mql4. Пока изучаю...

 
AlexeyVik:

Если не возражаете, вопрос. Почему делаете акцент на постановку ордера функцией OrderSendAssync()? Разве если ставить ордер функцией OrderSend() транзакция не потеряется? В этом случае больше гарантий?


Я по неопытности хотел в OnTradeTransactio() вставить принятие решения в том или ином случае. Но пока что-то не очень получается. А прочитав о возможной потере транзакций обратился к вам за разъяснениями.

В общем, чтобы писать для рынка ФОРТС надо понимать этот рынок. Пока это не для меня, а может и не надо мне это. Я решил для изучения mql5 переписать сов написанный на mql4. Пока изучаю...

1. OrderSendAsync выполняется гораздо быстрее (она не ждёт ответа от сервера)

OrderSend гарантированно получает ответ от сервера. 

2. Пример будет отлично работать и на ФОРЕКС 

изменив result.retcode == TRADE_RETCODE_PLACED на result.retcode == TRADE_RETCODE_DONE 

 
Mikalas:

1. OrderSendAsync выполняется гораздо быстрее (она не ждёт ответа от сервера)

2. Пример будет отлично работать и на ФОРЕКС 

изменив result.retcode == TRADE_RETCODE_PLACED на result.retcode == TRADE_RETCODE_DONE 

Тоесть, если не применять асинхронность, то и отпадает необходимость в обработчике OnTradeTransaction поскольку сов будет ожидать ответ от сервера?

Но пока я не нашёл оптимальный способ определения что активировался стоп.ордер. Например поставлен рыночный Buy 0.1 и SellStop 0.3 а когда активируется 0.3 то останется 0.2 и в какой момент надо следить за ним... Получается что на каждом тике надо следить за двумя ордерами. А мне хотелось разгрузить сов и вести контроль только после свершения транзакции. Даже не вести контроль, а единожды проверить что произошло и определиться как поступить. Но видимо не судьба... Наверное лучше вернуться к старым проверенным методам...

 
AlexeyVik:

Тоесть, если не применять асинхронность, то и отпадает необходимость в обработчике OnTradeTransaction поскольку сов будет ожидать ответ от сервера?

Но пока я не нашёл оптимальный способ определения что активировался стоп.ордер. Например поставлен рыночный Buy 0.1 и SellStop 0.3 а когда активируется 0.3 то останется 0.2 и в какой момент надо следить за ним... Получается что на каждом тике надо следить за двумя ордерами. А мне хотелось разгрузить сов и вести контроль только после свершения транзакции. Даже не вести контроль, а единожды проверить что произошло и определиться как поступить. Но видимо не судьба... Наверное лучше вернуться к старым проверенным методам...

Да, можно не использовать OnTradeTransaction, но тогда Вам придётся копаться в истории, что на много снизит общее быстродействие советника.

Выбор есть у каждого разработчика!