Отслеживание ордера, после команды OrderSendAsync

29 апреля 2015, 05:57
Mikhail Filimonov
1
920

Отслеживание ордера, после команды OrderSendAsync

Михаил | 23 апреля, 2015

В статье рассказывается принцип отслеживания ордера после команды OrderSendAsync, если нет события TradeTransaction.

Введение

Сокращение времени выставления, удаления и модификации ордера является краеугольным камнем многих стратегий торговли.

В терминале МТ5 команда OrderSendAsync позволяет существенно сократить время исполнения приказов.  

Она (команда) OrderSendAsync, обрабатывается функцией OnTradeTransaction, но к сожалению,

событие TradeTransaction не гарантированно.

Идею отслеживания ордера после команды OrderSendAsync (при отсутствии событияTradeTransaction),

с использование уникальных номеров (magic number), предложил комрад Yurich, за что ему большое спасибо.

Проблему отслеживания ордеров рассмотрим на примере покупки-продажи.    


Обычный алгоритм

//+------------------------------------------------------------------+
//|                                                       Demo_1.mq5 |
//|                                          Copyright 2015, Mikalas |
//|                                              http://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2015, Mikalas"
#property link      "http://www.mql5.com"
#property version   "1.00"
//
#define BUY   true
#define SELL  false
//
bool          open_close;
ulong         magic_number; 
ulong         order_ticket;
uint          req_id; 
//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
{
//--- Установка переменных
  magic_number  = 1010000;
  open_close    = true; 
 
//---
  if( !ObjectCreate( 0, "buy_sell", OBJ_BUTTON, 0, 0, 0 ) )
  {
    MessageBox( "Кнопка 'Buy-Sell' не создана!", "Ошибка", MB_OK | MB_ICONHAND );
    return( INIT_FAILED );
  }
  else
  {
    ObjectSetInteger( 0, "buy_sell", OBJPROP_CORNER, CORNER_RIGHT_LOWER ); //LEFT
    ObjectSetInteger( 0, "buy_sell", OBJPROP_XDISTANCE, 40 );                //0
    ObjectSetInteger( 0, "buy_sell", OBJPROP_YDISTANCE, 18 );
    ObjectSetInteger( 0, "buy_sell", OBJPROP_XSIZE, 40 );
    ObjectSetInteger( 0, "buy_sell", OBJPROP_YSIZE, 18 );
//---  
    if ( open_close )
    {
      ObjectSetInteger( 0, "buy_sell", OBJPROP_BGCOLOR, clrWhite );
      ObjectSetString( 0, "buy_sell", OBJPROP_TEXT, "Buy" );
    }
    else
    {
      ObjectSetInteger( 0, "buy_sell", OBJPROP_BGCOLOR, clrWhite );
      ObjectSetString( 0, "buy_sell", OBJPROP_TEXT, "Sell" );
    }  
  }
  ChartRedraw();
  return( INIT_SUCCEEDED );
}
//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit( const int reason )
{
//--- удаление таймера
  ObjectDelete( 0, "buy_sell" );
}
//+------------------------------------------------------------------+
//| Expert set order function                                        |
//+------------------------------------------------------------------+
void SetOrder( const string aSymbol, const double price, const double volume, const bool buy_sell )
{
  MqlTradeRequest request = {0};
  MqlTradeResult  result  = {0};
  req_id = 0;
  order_ticket = 0;
//--- Fill structure
  request.magic        = magic_number;
  request.symbol       = aSymbol;
  request.volume       = volume; 
  request.deviation    = 50;
  request.type_filling = ORDER_FILLING_IOC;
  request.type_time    = ORDER_TIME_DAY;
  request.price        = price;
  request.action       = TRADE_ACTION_DEAL;
//---    
  if ( buy_sell )
  {
    request.type     = ORDER_TYPE_BUY;
  }
  else
  {
    request.type     = ORDER_TYPE_SELL;
  }
//--- Send order
  if ( OrderSendAsync( request, result ) )
  {
    if ( result.retcode == TRADE_RETCODE_PLACED ) 
    {
      req_id = result.request_id;
    }
  }
  else
  {
    Print( "Установка ордера не выполнена! ", aSymbol );
  }
}
//+------------------------------------------------------------------+
//| Expert Chart event function                                      |
//+------------------------------------------------------------------+
void OnChartEvent( const int id, const long& lparam, const double& dparam, const string& sparam )
{
  MqlTick cur_tick;
  
  if ( SymbolInfoTick( _Symbol, cur_tick ) )
  {
    long b_state;
  
    if ( id == CHARTEVENT_OBJECT_CLICK )
    {
      if ( sparam == "buy_sell" )
      {
        if ( ObjectGetInteger( 0, "buy_sell", OBJPROP_STATE, 0, b_state ) )
        {
          if ( b_state == 1 )
          {
            if ( open_close )
            {
              SetOrder( _Symbol, cur_tick.ask, 1, open_close ); 
              open_close = false;
              ObjectSetInteger( 0, "buy_sell", OBJPROP_STATE, false );
              ObjectSetString( 0, "buy_sell", OBJPROP_TEXT, "Sell" );
            }
            else
            {
              SetOrder( _Symbol, cur_tick.bid, 1, open_close );
              open_close = true;
              ObjectSetInteger( 0, "buy_sell", OBJPROP_STATE, false );
              ObjectSetString( 0, "buy_sell", OBJPROP_TEXT, "Buy" );
            }
          }
        }
      }
      ChartRedraw();
    }
  }
}
//+------------------------------------------------------------------+
//| Expert On Trade Transaction 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 ( ( req_id != 0 ) && ( result.request_id == req_id ) )
                                      {
                                        if ( result.retcode == TRADE_RETCODE_DONE )
                                        {
                                          order_ticket = result.order;
                                        }
                                        else
                                        {
                                          Print( "Не получен билет ордера!" );
                                        }
                                        req_id = 0;
                                      }  
                                    }  
                                    break;
                                     
    case TRADE_TRANSACTION_HISTORY_ADD: if ( order_ticket > 0 )
                                        {
                                          switch( trans.order_state )
                                          {
                                            case ORDER_STATE_FILLED:   Print( "Сделка совершена, Билет = ", order_ticket );
                                                                       order_ticket = 0; 
                                                                       break;
                                                            
                                             case ORDER_STATE_REJECTED:  
                                             case ORDER_STATE_CANCELED: 
                                             case ORDER_STATE_EXPIRED:  
                                                                        Print( "Сделка не совершена, Билет = ", order_ticket );
                                                                        order_ticket = 0;
                                                                        break;
                                           }                             
                                         }
                                         break;                                   
  }                                                                     
}
//+------------------------------------------------------------------+
//| The END                                                          |
//+------------------------------------------------------------------+

Если ордер отправлен (SetOrder()), то мы получаем номер отправки запроса (request_id), далее,

если ордер выставлен в рынокто мы получаем его билет (OnTradeTransaction --> TRADE_TRANSACTION_REQUEST --> order_ticket = result.order).

Зная билет ордера, мы можем отследить что с ним произошло.

Но если не произошло событие(события) TradeTransaction, узнать что

произошло с ордером невозможно, так как мы можем не получить билет ордера или

событий с ордером происходящие (отклонение биржей или брокером, удаление, экспирация или модификация). 

 Рассмотрим алгоритм, позволяющий отследить состояния ордера, при отсутствии событий TradeTransaction. 

 

Алгоритм, при отсутствии событий ТradeTransaction

//+------------------------------------------------------------------+
//|                                                       Demo_2.mq5 |
//|                                          Copyright 2015, Mikalas |
//|                                              http://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2015, Mikalas"
#property link      "http://www.mql5.com"
#property version   "1.00"
//
#define BUY   true
#define SELL  false
//
input uint    SecTimer     = 1000;               //Период проверки ответа сервера (мсек) 
//
int           timer_time;
bool          open_close; 
datetime      mem_start_time;
ulong         order_ticket;
uint          mem_tick;
ulong         magic_number;
ulong         magic_storage;
ulong         mem_magic;
//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
{
//--- Установка переменных
  magic_number  = 1010000;
  magic_storage = magic_number - 1;
  timer_time    = 500; 
  open_close    = true; 
 
//---
  if( !ObjectCreate( 0, "buy_sell", OBJ_BUTTON, 0, 0, 0 ) )
  {
    MessageBox( "Кнопка 'Buy-Sell' не создана!", "Ошибка", MB_OK | MB_ICONHAND );
    return( INIT_FAILED );
  }
  else
  {
    ObjectSetInteger( 0, "buy_sell", OBJPROP_CORNER, CORNER_RIGHT_LOWER ); //LEFT
    ObjectSetInteger( 0, "buy_sell", OBJPROP_XDISTANCE, 40 );                //0
    ObjectSetInteger( 0, "buy_sell", OBJPROP_YDISTANCE, 18 );
    ObjectSetInteger( 0, "buy_sell", OBJPROP_XSIZE, 40 );
    ObjectSetInteger( 0, "buy_sell", OBJPROP_YSIZE, 18 );
//---  
    if ( open_close )
    {
      ObjectSetInteger( 0, "buy_sell", OBJPROP_BGCOLOR, clrWhite );
      ObjectSetString( 0, "buy_sell", OBJPROP_TEXT, "Buy" );
    }
    else
    {
      ObjectSetInteger( 0, "buy_sell", OBJPROP_BGCOLOR, clrWhite );
      ObjectSetString( 0, "buy_sell", OBJPROP_TEXT, "Sell" );
    }  
  }
  //--- Установка таймера
  if ( !EventSetMillisecondTimer( timer_time ) )
  {
    MessageBox( "Таймер не установлен!", "Ошибка", MB_OK | MB_ICONHAND );
    return( INIT_FAILED );
  } 
  ChartRedraw();
  return( INIT_SUCCEEDED );
}
//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit( const int reason )
{
//--- удаление таймера
  EventKillTimer();
  ObjectDelete( 0, "buy_sell" );
}
//+------------------------------------------------------------------+
//| Expert timer function                                            |
//+------------------------------------------------------------------+
void OnTimer()
{
  CheckOrders();
}
//+------------------------------------------------------------------+
//| Expert set order function                                        |
//+------------------------------------------------------------------+
void SetOrder( const string aSymbol, const double price, const double volume, const bool buy_sell )
{
  MqlTradeRequest request = {0};
  MqlTradeResult  result  = {0};
  mem_start_time = TimeTradeServer();
  order_ticket   = 0;
  mem_tick       = GetTickCount();
  mem_magic      = magic_storage + 1;
  
  if ( mem_magic >= ( magic_number + 9999 ) ) mem_magic = magic_number;
   
//--- Fill structure
  request.magic        = mem_magic;
  request.symbol       = aSymbol;
  request.volume       = volume; 
  request.deviation    = 50;
  request.type_filling = ORDER_FILLING_IOC;
  request.type_time    = ORDER_TIME_DAY;
  request.price        = price;
  request.action       = TRADE_ACTION_DEAL;
//---    
  if ( buy_sell )
  {
    request.type     = ORDER_TYPE_BUY;
  }
  else
  {
    request.type     = ORDER_TYPE_SELL;
  }
//--- Send order
  if ( OrderSendAsync( request, result ) )
  {
    if ( result.retcode == TRADE_RETCODE_PLACED ) 
    {
      magic_storage = mem_magic;
    }
  }
  else
  {
    mem_magic      = 0;
    mem_start_time = 0;
    mem_tick       = 0;
    Print( "Установка ордера не выполнена! ", aSymbol );
  }
}
//+------------------------------------------------------------------+
//| Expert Chart event function                                      |
//+------------------------------------------------------------------+
void OnChartEvent( const int id, const long& lparam, const double& dparam, const string& sparam )
{
  MqlTick cur_tick;
  
  if ( SymbolInfoTick( _Symbol, cur_tick ) )
  {
    long b_state;
  
    if ( id == CHARTEVENT_OBJECT_CLICK )
    {
      if ( sparam == "buy_sell" )
      {
        if ( ObjectGetInteger( 0, "buy_sell", OBJPROP_STATE, 0, b_state ) )
        {
          if ( b_state == 1 )
          {
            if ( open_close )
            {
              SetOrder( _Symbol, cur_tick.ask, 1, open_close ); 
              open_close = false;
              ObjectSetInteger( 0, "buy_sell", OBJPROP_STATE, false );
              ObjectSetString( 0, "buy_sell", OBJPROP_TEXT, "Sell" );
            }
            else
            {
              SetOrder( _Symbol, cur_tick.bid, 1, open_close );
              open_close = true;
              ObjectSetInteger( 0, "buy_sell", OBJPROP_STATE, false );
              ObjectSetString( 0, "buy_sell", OBJPROP_TEXT, "Buy" );
            }
          }
        }
      }
      ChartRedraw();
    }
  }
}
//+------------------------------------------------------------------+
// 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 Check main order function                                  |
//+------------------------------------------------------------------+
ulong GetOrderTicket( const ulong m_magic, const datetime mem_time, const datetime cur_time )
{
  ulong a_ticket = FindOrder( m_magic );
  
  if ( a_ticket > 0 )
  {
    return( a_ticket );
  }
  else
  {
    a_ticket = FindHistoryOrder( m_magic, mem_time, cur_time );
    
    if ( a_ticket > 0 )
    {
      return( a_ticket );
    }
  }  
  return( 0 ); 
}
//+------------------------------------------------------------------+
// Expert Check main order function                                  |
//+------------------------------------------------------------------+
bool CheckOrdersTimer( 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 Check Orders function                                      |
//+------------------------------------------------------------------+
void CheckOrders()  
{
  if ( ( mem_tick > 0 ) && CheckOrdersTimer( mem_tick, SecTimer ) )
  {
    mem_tick = 0;
    //---
    if ( order_ticket > 0 )
    {
      mem_magic      = 0;
      //---
      if ( ( order_ticket > 0 ) && ( HistoryOrderSelect( order_ticket ) ) )
      {
        ENUM_ORDER_STATE order_state = ENUM_ORDER_STATE( HistoryOrderGetInteger( order_ticket, ORDER_STATE ) );
//---
        if ( ( order_state == ORDER_STATE_FILLED ) || ( order_state == ORDER_STATE_PARTIAL ) )
        {
          Print( "Сделка совершена! Билет = ", order_ticket );
        }
        else
        {
          Print( "Сделка не совершена! Билет = ", order_ticket );
        }   
      }
      //---
      mem_start_time = 0;
      order_ticket = 0;
    }
    else
    {
      if ( mem_magic > 0 )
      {
        order_ticket = GetOrderTicket( mem_magic, mem_start_time - 2000, TimeTradeServer() + 5000 );
        //---
        if ( order_ticket > 0 )
        {
          mem_tick = GetTickCount();
        }
        else
        {
          mem_magic      = 0;
          mem_start_time = 0;
          Print( "CheckOrders: Не получен билет ордера!" );
        }
      }
      else
      {
        mem_start_time = 0;
        Print( "CheckOrders: Не возможно получить билет ордера, mem_magic = 0!" );
      }
    }
  }
}
//+------------------------------------------------------------------+
//| The END                                                          |
//+------------------------------------------------------------------+

 

Дополнительные переменные, необходимые для отслеживания ордера:

input uint    SecTimer     = 1000;               //Период проверки ответа сервера (мсек) 
//
int           timer_time;
bool          open_close; 
datetime      mem_start_time;
ulong         order_ticket;
uint          mem_tick;
ulong         magic_number;
ulong         magic_storage;
ulong         mem_magic;

 

 SecTimer - период времени ожидания события TradeTransaction

 magic_number - начало диапазона магических чисел, задаваемый для каждого символа.

Т.е, например, для EURUSD - c 1010000 по 1019999,

для GBPUSD - c 102000 по1029999 и т.д

10000 уникальных номеров для одного символа вполне достаточно,

чтобы эти уникальные номера "разнести" во времени.

При достижении 10000 происходит новый цикл:

if ( mem_magic >= ( magic_number + 9999 ) ) mem_magic = magic_number;  //Переполнение, начинаем сначала

 Одинаковые уникальные номера будут, но они будут "разнесены" во времени, для чего

и служит переменная - mem_time.

mem_magic - уникальное число КАЖДОГО ордера. 

 magic_storage - переменная для хранения текущего уникального числа.

 mem_tick - начало отсчёта времени для сравнения с периодом ожидания события TradeTransaction.

Для цикла проверки времени создаётся миллисекундный таймер:

int OnInit()
{
       
  //--- Установка таймера
  if ( !EventSetMillisecondTimer( timer_time ) )    //0.5 cек.
  {
    MessageBox( "Таймер не установлен!", "Ошибка", MB_OK | MB_ICONHAND );
    return( INIT_FAILED );
  } 
  return( INIT_SUCCEEDED );
}

 Событие OnTimer() вызывает функцию CheckOrders();

И если время ожидания события TradeTransaction превышено,

if ( ( mem_tick > 0 ) && CheckOrdersTimer( mem_tick, SecTimer     ) )           //Проверка времени ожидания действия над ордером (установка, модификация или удаление) 
  {
   .......
  }

 то происходит выполнение функции.

Если мы не получили билет ордера в OnTradeTransaction, то

 mem_magic > 0 и мы пытаемся получить билет вызывая функцию:

 

GetOrderTicket( mem_magic, mem_time - 2000, TimeTradeServer() + 5000);

 Если же ордер был получен ( order_ticket > 0 ) но не пришло событие

OnTradeTransaction, то мы можем отследить его состояние, просмотрев историю. 


 

Заключение

В статье рассмотрены общие принципы отслеживания ордера (с примерами), после команды OrderSendAsync.

При проектировании советника, с использованием этого принципа, необходимо рассмотреть

более подробно все аспекты действия с ордером.

Это означает, что приведённый выше код, может и не дать

желаемого результата, если его просто скопировать в свой советник.

Данный метод (не примеры кода для ФОРЕКС, изложенные выше) отслеживания применён мною в советниках для ФОРТС,

где с успехом работает более 9 месяцев. 

С уважением, и успехов в программировании советников!  

Поделитесь с друзьями: