Отслеживание ордера, после команды 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 месяцев.
С уважением, и успехов в программировании советников!