Что возвращает функция OrderSend() и стоит ли доверять возвращаемому значению? - страница 2

 
Stanislav KorotkyВ контексте ситуации - несколько экземпляров советника, на каждом тике каждый из них пытается повторить отправить сделку в случае проблемы, а успешность сделки не регистрируется - у Вас нет оснований утверждать, что ошибка в терминале. Больше похоже на штатное исполнение кода, а ордер на бирже - совсем не тот, для которого вернулось false.

Большое кол-во работающих экземпляров советника, и как следствие большое кол-во ордеров и сделок, а также отсутствие положительных квитанций в журнале "Эксперты" серьезно затрудняют локализацию ошибок. Вынужден согласится с Вами, что вывод об ошибке в работе терминала был преждевременным, просто на тот момент не находилось другого объяснения такому странному поведению. В связи с этим в эксперт был добавлен дополнительный вывод в журнал положительных подтверждений и решено провести испытания только на одном экземпляре советника. По результатам отпишусь...

 

Сегодня обнаружилась следующая ошибка.

В ответ на отправку запроса на торговый сервер советник получил неизвестный код результата операции, retcode=10001, хотя ордер все равно был отправлен на биржу и исполнен в полном объема 2 лота. Действительно, в справочнике такого кода нет, все коды возврата торгового сервера начинаются с кода 10004.

Для подтверждения успешной передачи ордера на торговой сервер (биржу) советник ожидал получить один из двух кодов код возврата:

TRADE_RETCODE_PLACED = 10008  // Ордер размещен   (возвращает биржевой рынок MOEX)
TRADE_RETCODE_DONE    = 10009  // Заявка выполнена (возвращает внебиржевой рынок FOREX и тестер стратегий!!!

Однако вместо этого советник получил неизвестный код возврата 10001 и посчитал, что ордер не был отправлен на биржу, и поэтому чуть позже повторил отправку ордера, который также был исполнен.

RF      0       18:29:18.250    RECTANGLES (Si-9.16,H4) [0034-0021 Si-9.16] *** На торговый сервер отправлен ордер ORDER_TYPE_BUY volume=2.0 TRADE_ACTION_DEAL magic=#340021202002101007 [B+0034-0021-202-002-101-007]
LR      0       18:29:18.250    RECTANGLES (Si-9.16,H4) [0034-0021 Si-9.16] *** Ошибка отправки запроса на торговый сервер: retcode=10001 Неизвестный код результата операции, ticket #37922221

EF      0       18:29:21.864    RECTANGLES (Si-9.16,H4) [0034-0021 Si-9.16] *** На торговый сервер отправлен ордер ORDER_TYPE_BUY volume=2.0 TRADE_ACTION_DEAL magic=#340021202002101007 [B+0034-0021-202-002-101-007]
MS      0       18:29:21.864    RECTANGLES (Si-9.16,H4) [0034-0021 Si-9.16] *** Успешно отправлен запрос на торговый сервер, код возврата #10008 Ордер размещен, ticket #37922242


Вот кусок программы, которая отправляет и контролирует отправку ордера, выдает в журнал сообщения об успешном или не успешном выполнении.

//+--------------------------------------------------------------------------+
//| Отправить торговый запрос на сервер (рыночный ордер)
//| В случае успеха функция возвращает тикет ордера (или 0 в случае неудачи)
//+--------------------------------------------------------------------------+
ulong OrderSend2(const ENUM_OPER_CODE oper_code, const int order_volume, const ulong magic)
{

// Подготовительные операции по заполнению структуры....   

// Отправить запрос на торговый сервер и получить код результата операции
   ResetLastError(); // Сбросить значение предопределенной переменной _LastError в ноль

   bool ok=OrderSend(sTradeRequest, sTradeResult); // две структуры: запроса и ответа

   uint retcode=sTradeResult.retcode; // код результата операции
   Order.ticket=sTradeResult.order;   // Тикет ордера, если он выставлен (возвращает сервер)

   Print(EXPERT_PREFIX, "На торговый сервер отправлен ордер ", EnumToString(sTradeRequest.type), // Тип ордера
         " volume=", sTradeRequest.volume,        // Запрашиваемый объем сделки в лотах
         " ", EnumToString(sTradeRequest.action), // Тип выполняемого действия
         " magic=#", sTradeRequest.magic,         // Штамп эксперта (идентификатор magic number)
         " [", sTradeRequest.comment, "]");       // Комментарий к ордеру
       
// Анализируем код возврата торгового сервера
   if (Order.ticket>0 && 
      (retcode==TRADE_RETCODE_PLACED ||   // Ордер размещен   (10008) (возвращает биржевой рынок MOEX)
       retcode==TRADE_RETCODE_DONE   )) { // Заявка выполнена (10009) (возвращает внебирж. рынок FOREX и тестер стратегий!!!)

      Print(EXPERT_PREFIX, "Успешно отправлен запрос на торговый сервер, код возврата #", retcode, " ", retcode_descr(retcode), ", ticket #", Order.ticket);
   }
   else {
   // err=GetLastError(); Print("*** Структура торгового запроса не прошла базовую проверку, err=", err, " ", ErrorToString(err));
      Print(EXPERT_PREFIX, "Ошибка отправки запроса на торговый сервер: retcode=", retcode, " ", retcode_descr(retcode), ", ticket #", Order.ticket);
      Order.PrintToLog(); // Распечатать параметры структуры Order
      Order.ticket=0;
   }
   return(Order.ticket);
}

Как видно из текста, советник совершенно не интересуется результатом работы функции OrderSend(), анализируется только код возврата торгового сервера, и на основе этого анализа принимаются решения. Это было сделано намерено, и похоже не зря. Скорее всего, функция OrderSend() также бы вернула значение FALSE, хотя ордер, повторюсь, был передан на биржу и исполнен в полном объеме.

 
Eugene Myzrov:

Сегодня обнаружилась следующая ошибка.

В ответ на отправку запроса на торговый сервер советник получил неизвестный код результата операции, retcode=10001, хотя ордер все равно был отправлен на биржу и исполнен в полном объема 2 лота. Действительно, в справочнике такого кода нет, все коды возврата торгового сервера начинаются с кода 10004.

Для подтверждения успешной передачи ордера на торговой сервер (биржу) советник ожидал получить один из двух кодов код возврата:

TRADE_RETCODE_PLACED = 10008  // Ордер размещен   (возвращает биржевой рынок MOEX)
TRADE_RETCODE_DONE    = 10009  // Заявка выполнена (возвращает внебиржевой рынок FOREX и тестер стратегий!!!

Однако вместо этого советник получил неизвестный код возврата 10001 и посчитал, что ордер не был отправлен на биржу, и поэтому чуть позже повторил отправку ордера, который также был исполнен.


Вот кусок программы, которая отправляет и контролирует отправку ордера, выдает в журнал сообщения об успешном или не успешном выполнении.

Как видно из текста, советник совершенно не интересуется результатом работы функции OrderSend(), анализируется только код возврата торгового сервера, и на основе этого анализа принимаются решения. Это было сделано намерено, и похоже не зря. Скорее всего, функция OrderSend() также бы вернула значение FALSE, хотя ордер, повторюсь, был передан на биржу и исполнен в полном объеме.

Без полного журнала терминала Ваше сообщение нельзя анализировать.
 
Также нет кода о заполнении структур и торгового запроса.
 
Karputov VladimirТакже нет кода о заполнении структур и торгового запроса.

Хорошо, я все все пришлю на сервис-деск. Но, как Вы догадываетесь, советник отправил не одну сотню запросов, заполняя корректно структуры торгового запроса по одному и тому же алгоритму, прежде чем он вместо ожидаемого 10008 (TRADE_RETCODE_PLACED) получил неизвестный код возврата 10001. Что это за код, как его интерпретировать, и почему его нет в документации?

 
Eugene Myzrov:

Хорошо, я все все пришлю на сервис-деск. Но, как Вы догадываетесь, советник отправил не одну сотню запросов, заполняя корректно структуры торгового запроса по одному и тому же алгоритму, прежде чем он получил неизвестный код возврата 10001. Что это за код, как его интерпретировать, и почему его нет в документации?

Я думаю могут быть два варианта:

  1. код возврата 10001 - это зарезервированный код и в данном случае была какая-то непредвиденная ситуация
  2. код возврата 10001, в данном конкретном случае, - просто "мусор", который был интерпретирован как 10001.

 
Karputov Vladimir: Без полного журнала терминала Ваше сообщение нельзя анализировать. Также нет кода о заполнении структур и торгового запроса.
Отправил в сервис-деск полные журналы терминала и советника за 16.06.2016, а также код заполнения структуры торгового запроса.
 
Eugene Myzrov:
Отправил в сервис-деск полные журналы терминала и советника за 16.06.2016, а также код заполнения структуры торгового запроса.
Я не сервисдеск  и хотел увидеть журналы и заполнение структур для общего развития, так сказать. Вдруг сообщество сможет найти спорный или сомнительный момент.
 
Karputov VladimirЯ не сервисдеск  и хотел увидеть журналы и заполнение структур для общего развития, так сказать. Вдруг сообщество сможет найти спорный или сомнительный момент.
Можно, вот кусок журнала за интересующее нас время 18:29:18.115 когда была исполнена сделка #26367967 на покупку объемом 2.00 Si-9.16 по цене 67368 согласно ордера #37922221, однако советник на отправку этого ордера вместо ожидаемого кода возврата 10008 получил код 10001. Полагаю, что в это время функция OrderSend() вернула значение FALSE, почему я и открыл эту тему на форуме. К сожалению, вывод в журнал этого значения я добавил позже, уже после того, как была обнаружена эта ошибка.

OO      0       17:02:22.603    Trades  '00000': deal #26361397 sell 1.00 Si-9.16 at 67650 done (based on order #37911656)
GM      0       18:28:38.179    Trades  '00000': buy limit 1.00 Si-9.16 at 67113 (67113)
JG      0       18:28:38.322    Trades  '00000': buy limit 1.00 Si-9.16 at 67113 (67113) placed for execution in 143 ms
KD      0       18:29:18.115    Trades  '00000': exchange buy 2.00 Si-9.16 at market placed for execution in 109 ms
DQ      0       18:29:18.115    Trades  '00000': deal #26367967 buy 2.00 Si-9.16 at 67368 done (based on order #37922221)
LQ      0       18:29:18.250    Trades  '00000': exchange buy 2.00 Si-9.16 at market
CH      0       18:29:21.745    Trades  '00000': exchange buy 2.00 Si-9.16 at market
GP      0       18:29:21.860    Trades  '00000': exchange buy 2.00 Si-9.16 at market placed for execution in 115 ms
QM      0       18:29:21.860    Trades  '00000': deal #26367973 buy 1.00 Si-9.16 at 67374 done (based on order #37922242)
CN      0       18:29:21.861    Trades  '00000': deal #26367974 buy 1.00 Si-9.16 at 67374 done (based on order #37922242)
EI      0       18:29:21.891    Trades  '00000': cancel order #37922088 buy limit 1.00 Si-9.16 at 67113 (67113)
DF      0       18:29:21.899    Trades  '00000': cancel order #37922088 buy limit 1.00 Si-9.16 at 67113 (67113) placed for execution in 8 ms
LD      0       18:32:48.871    Trades  '00000': sell limit 1.00 Si-9.16 at 67629 (67629)
JR      0       18:32:48.965    Trades  '00000': sell limit 1.00 Si-9.16 at 67629 (67629) placed for execution in 94 ms
CS      0       18:59:04.461    Trades  '00000': deal #26370151 sell 5.00 Si-9.16 at 67486 done (based on order #0)
KK      0       18:59:04.488    Trades  '00000': deal #26370152 buy 5.00 Si-9.16 at 67486 done (based on order #0)
NF      0       19:07:04.932    Experts expert RECTANGLES (Si-9.16,H4) removed
 
А вот необходимые структуры и часть кода, который подготавливает и отправляет запрос на биржу

 input uint    DEVIATION          =        0; // Проскальзывание [п]

//+-------------------------------------------+
//| Структура для работа со свойствами ордера
//+-------------------------------------------+
struct SOrder
{
   double            volume     ; // Объем ордера (вещественный!)
   double            price      ; // Цена  ордера (открытия или закрытия)
   ulong             ticket     ; // Тикет ордера
   ulong             magic      ; // Magic ордера
   datetime          time       ; // Время формирования запроса
   ENUM_ORDER_TYPE   type       ; // Тип ордера:
                                  //      ORDER_TYPE_BUY             - Рыночный ордер на покупку
                                  //      ORDER_TYPE_SELL            - Рыночный ордер на продажу
                                  //      ORDER_TYPE_BUY_LIMIT       - Отложенный ордер Buy  Limit
                                  //      ORDER_TYPE_SELL_LIMIT      - Отложенный ордер Sell Limit
                                  //      ORDER_TYPE_BUY_STOP        - Отложенный ордер Buy  Stop
                                  //      ORDER_TYPE_SELL_STOP       - Отложенный ордер Sell Stop
                                  //      ORDER_TYPE_BUY_STOP_LIMIT  - По достижении цены ордера выставляется отложенный ордер Buy  Limit по цене StopLimit
                                  //      ORDER_TYPE_SELL_STOP_LIMIT - По достижении цены ордера выставляется отложенный ордер Sell Limit по цене StopLimit
   ENUM_OPER_CODE    oper_code  ; // код      операции "open buy";"open sell";"close buy";"close sell";
   string            oper_name  ; // название операции "open buy";"open sell";"close buy";"close sell";
   string            symbol     ; // Имя символа, по которому подготовлен запрос
   string            comment    ; // Комментарий к ордеру
// Вывести на печать параметры ордера
   void              PrintToLog();
// Распечатать параметры структуры Order
   void              Print2();

};

//+--------------------------------------------------------------------------+
//| Отправить торговый запрос на сервер (рыночный ордер)
//| В случае успеха функция возвращает тикет ордера (или 0 в случае неудачи)
//+--------------------------------------------------------------------------+
ulong OrderSend2(const ENUM_OPER_CODE oper_code, const int order_volume, const ulong magic)
{
// if (TRADE_MODE==TRADE_MODE_VIRTUAL) {VIRTUAL_TICKET+=1; return(VIRTUAL_TICKET);}

// Преобразовать MAGIC в строку - комментарий к ордеру
   string sMagic = MagicToString(magic);
// Для быстрого получения наиболее востребованной информации о текущих ценах
   SymbolInfoTick(_Symbol, sTick); // ссылка на структуру
// Очистить 2 структуры
   ZeroMemory(Order);
   ZeroMemory(Deal );

   Order.oper_code = oper_code;                                   // код операции
   Order.symbol    = _Symbol;                                     // Имя символа, по которому подготовлен запрос
   Order.time      = TimeCurrent();                               // время формирования запроса
   Order.magic     = magic;                                       // Magic ордера
   Order.ticket    = 0;                                           // тикет ордера (возвращает сервер)
   Order.volume    = MathAbs(order_volume);                       // Объем ордера

   switch (oper_code) {
   case OPER_BUY_IN:
      Order.type      = ORDER_TYPE_BUY;                           // ордер на покупку
      Order.oper_name = "buy/in";                                 // название операции
      Order.comment   = "B+"+sMagic;                              // Комментарий к ордеру
      Order.price     = NormalizeDouble(sTick.ask, _Digits);      // Текущая цена Ask
      break;
   case OPER_BUY_OUT:
      Order.type      = ORDER_TYPE_SELL;                          // ордер на продажу
      Order.oper_name = "buy/out";                                // название операции
      Order.comment   = "B-"+sMagic;                              // Комментарий к ордеру
      Order.price     = NormalizeDouble(sTick.bid, _Digits);      // Текущая цена Bid
      break;

   case OPER_SELL_IN:
      Order.type      = ORDER_TYPE_SELL;                          // ордер на продажу
      Order.oper_name = "sell/in";                                // название операции
      Order.comment   = "S+"+sMagic;                              // Комментарий к ордеру
      Order.price     = NormalizeDouble(sTick.bid, _Digits);      // Текущая цена Bid
      break;
   case OPER_SELL_OUT:
      Order.type      = ORDER_TYPE_BUY;                           // ордер на покупку
      Order.oper_name = "sell/out";                               // название операции
      Order.comment   = "S-"+sMagic;                              // Комментарий к ордеру
      Order.price     = NormalizeDouble(sTick.ask, _Digits);      // Текущая цена Ask
      break;
   }
// Очистить 3 структуры
   ZeroMemory(sTradeRequest    ); // запрос
   ZeroMemory(sTradeResult     ); // результат
   ZeroMemory(sTradeCheckResult); // структура ответа

// Подготовить запрос
   sTradeRequest.action       = TRADE_ACTION_DEAL; // рыночный ордер - немедленное исполнение
   sTradeRequest.type         = Order.type;        // Рыночный ордер на покупку/продажу
   sTradeRequest.magic        = Order.magic;       // Штамп эксперта (идентификатор magic number)
   sTradeRequest.symbol       = Order.symbol;      // Имя торгового инструмента (не требуется при закрытии позиций)
   sTradeRequest.volume       = Order.volume;      // Запрашиваемый объем сделки в лотах
   sTradeRequest.price        = 0; // Order.price; // Текущая цена (для типа TRADE_ACTION_DEAL указание цены не требуется!)
   sTradeRequest.comment      = Order.comment;     // Комментарий к ордеру
   sTradeRequest.deviation    = DEVIATION;         // Максимально приемлемое отклонение от запрашиваемой цены
   sTradeRequest.type_filling = ORDER_FILLING_FOK; // Тип ордера по исполнению - все или ничего
   sTradeRequest.type_time    = ORDER_TIME_GTC;    // Ордер будет находится в очереди до тех пор, пока не будет снят
// sTradeRequest.expiration   = 0;                 // Срок истечения ордера (для ордеров типа ORDER_TIME_SPECIFIED)
// sTradeRequest.order        = 0;                 // Тикет ордера (только для модификации отложенных ордеров)
// sTradeRequest.stoplimit    = 0;                 // Уровень StopLimit ордера
// sTradeRequest.sl           = 0;                 // Уровень Stop Loss ордера
// sTradeRequest.tp           = 0;                 // Уровень Take Profit ордера

   int err;
   ResetLastError(); // Сбросить значение предопределенной переменной _LastError в ноль

// Проверить достаточность средств для совершения требуемой торговой операции. 
// Результаты проверки помещаются в поля структуры MqlTradeCheckResult.
   if (!OrderCheck(sTradeRequest, sTradeCheckResult)) {
      err=GetLastError();
      Print("*** Торговый запрос не прошел предварительную проверку перед отправкой, err=", err, " ", ErrorToString(err));
   // Распечатать параметры структуры ответа MqlTradeCheckResult
      PrintTradeCheckResult(sTradeCheckResult); 
      return(0);
   }

// Отправить запрос на торговый сервер и получить код результата операции
   ResetLastError(); // Сбросить значение предопределенной переменной _LastError в ноль
   bool ok=OrderSend(sTradeRequest, sTradeResult); // две структуры: запроса и ответа

   uint retcode=sTradeResult.retcode; // код результата операции
   Order.ticket=sTradeResult.order;   // Тикет ордера, если он выставлен (возвращает сервер)

   Print(EXPERT_PREFIX, "На торговый сервер отправлен ордер ", EnumToString(sTradeRequest.type), // Тип ордера
         " volume=", sTradeRequest.volume, // Запрашиваемый объем сделки в лотах
         " ", EnumToString(sTradeRequest.action), // Тип выполняемого действия
         " magic=#", sTradeRequest.magic,         // Штамп эксперта (идентификатор magic number)
         " [", sTradeRequest.comment, "]");       // Комментарий к ордеру
       
// Анализируем код возврата торгового сервера
   if (Order.ticket>0 && 
      (retcode==TRADE_RETCODE_PLACED ||   // Ордер размещен   (10008) (возвращает биржевой рынок MOEX)
       retcode==TRADE_RETCODE_DONE   )) { // Заявка выполнена (10009) (возвращает внебирж. рынок FOREX и тестер стратегий!!!)

      Print(EXPERT_PREFIX, "Успешно отправлен запрос на торговый сервер, код возврата #", retcode, " ", retcode_descr(retcode), ", ticket #", Order.ticket);
   }
   else {
   // err=GetLastError(); Print("*** Структура торгового запроса не прошла базовую проверку, err=", err, " ", ErrorToString(err));
      Print(EXPERT_PREFIX, "Ошибка отправки запроса на торговый сервер: retcode=", retcode, " ", retcode_descr(retcode), ", ticket #", Order.ticket, " OrderSend()=", ok);
      Order.PrintToLog(); // Распечатать параметры структуры Order
      Order.ticket=0; // под вопросом, возможно придется удалить эту строчку!!!
   }
   return(Order.ticket);
}