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


