OrderSendAsync не возвращает номер тикета (OnTradeTransaction - ловля блох или асинхронных хаос? ) - страница 2

 
Vladimir Belozercev:

Алексей, спасибо за Ваш комментарий. Я понял Вашу мысль, но увы, это не тот случай...

Это чистый эксперимент без каких бы то ни было инфлюэнций. Отправка одной позиции - ловля транзакций (лог приложил, если любопытно - можете взглянуть). И поверьте, в этой теме я давно, и даже используя OrderSend я завершение операции "выставляю" ТОЛЬКО по транзакции. Я бы наверно и продолжал не нем "сидеть", если бы не "поведенческие" изменения в демо-сервере MetaQuotes, которые я описал выше. И, отчасти, я даже благодарен этому обстоятельству, потому что это позволило выявить слабые места в обработке, но к сожалению, из доступных методов, нормального решения и получается, поэтому и появился этот тред.

Вопрос ведь не в том, что в OnTradeTransactionпридут не все транзакции. При правильной обработке, не нарушая поточности обработки, вы получите их ВСЕ, но... Вопрос в том, что при работе с OrderSendAsync, из идентификационных параметров вы гарантировано получаете только result.request_id. При этом result.request_id в транзакции фигурирует только в типе TRADE_TRANSACTION_REQUEST, который вам придет ОДИН раз.

Все остальные транзакции по этому приказу вы сможете идентифицировать только по номеру тикета. При этом, если ТОРГОВЫЕ ТРАНЗАКЦИИ прошли раньше, чем ТРАНЗАКЦИЯ ЗАПРОСА, то их идентификация превращается в весьма витиеватое занятие.

Ну я не знаю чего можно не получить из

void OnTradeTransaction(const MqlTradeTransaction& trans,
                        const MqlTradeRequest& request,
                        const MqlTradeResult& result)
 {
  if(trans.type == TRADE_TRANSACTION_REQUEST)
   {
    Print("trans.type - ", EnumToString(trans.type));
    Print("result.retcode = ", result.retcode);
    Print("result.order = ", result.order);
    Print("result.deal = ", result.deal);
    Print("request.magic = ", request.magic);
    Print("request.symbol = ", request.symbol);
    Print("request.type = ", EnumToString(request.type));
    Print("request.volume = ", request.volume);
    Print("request.price = ", request.price);
    Print("request.sl = ", request.sl);
    Print("request.tp = ", request.tp);
   }
 }

Вот результат

2020.03.18 14:44:55.685 2019.12.23 00:00:30   trans.type - TRADE_TRANSACTION_REQUEST
2020.03.18 14:44:55.896 2019.12.23 00:00:30   result.retcode = 10009
2020.03.18 14:44:56.106 2019.12.23 00:00:30   result.order = 2
2020.03.18 14:44:56.309 2019.12.23 00:00:30   result.deal = 2
2020.03.18 14:44:56.532 2019.12.23 00:00:30   request.magic = 3
2020.03.18 14:44:56.698 2019.12.23 00:00:30   request.symbol = EURUSD
2020.03.18 14:44:56.896 2019.12.23 00:00:30   request.type = ORDER_TYPE_SELL
2020.03.18 14:44:57.088 2019.12.23 00:00:30   request.volume = 0.1
2020.03.18 14:44:57.291 2019.12.23 00:00:30   request.price = 1.1075
2020.03.18 14:44:57.500 2019.12.23 00:00:30   request.sl = 1.1095
2020.03.18 14:44:57.980 2019.12.23 00:00:30   request.tp = 1.1055

В этом случае только одна проблема. Для ловли закрытия позиции надо ловить их по TRADE_TRANSACTION_DEAL_ADD, по TRADE_TRANSACTION_REQUEST этого не отловить.

 
Vladimir Mikhailov:


Использую только OrderSendAsync при реальной торговле на МОЕХ. Причина - различное время исполнения заявок от нескольких миллисекунд до десятков секунд.

То, что OrderSendAsync исполняет заявки быстрее - распространенное заблуждение. Обе функции OrderSend и OrderSendAsync строго равны по производительности, но...

конкретно на moex есть такая проблема, ответ может доходить несколько десятков секунд, хотя сделка по факту уже открыта. И тогда разорвать цепочку Запрос - ответ, с помощью OrderSendAsync становится полезным. Это я к тому, что по сути OrderSendAsync многими используется как хак и как способ обойти этот глюк, хотя правильное решение должно быть на совести разработчиков серверной части МetaTrader 5 для Moex.

 
Vladimir Belozercev:

Владимир, большое спасибо за развернутый ответ! Но, к Вашему счастью и к моему сожалению, у Вас ситуация немного проще. У Вас все приказы "обезличены", т.е. выполнены по принципу - "выстрелил-и забыл".

У меня немного другой случай. Мне каждый приказ нужно привязать к "источнику" сигнала, для последующего управления, при этом, если ТОРГОВЫЕ ТРАНЗАКЦИИ прошли раньше, чем ТРАНЗАКЦИЯ ЗАПРОСА, то их идентификация, без предварительной буферизации всех полученных, невозможна. А с буферизацией почти все преимущества использования OrderSendAsync теряются из-за появления больших вычислительных расходов.

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

 
Vasiliy Sokolov:

То, что OrderSendAsync исполняет заявки быстрее - распространенное заблуждение. Обе функции OrderSend и OrderSendAsync строго равны по производительности, но...

конкретно на moex есть такая проблема, ответ может доходить несколько десятков секунд, хотя сделка по факту уже открыта. И тогда разорвать цепочку Запрос - ответ, с помощью OrderSendAsync становится полезным. Это я к тому, что по сути OrderSendAsync многими используется как хак и как способ обойти этот глюк, хотя правильное решение должно быть на совести разработчиков серверной части МetaTrader 5 для Moex.

Дело не в том, какой метод быстрее выполняет заявки, а в том что OrderSend ожидает ответа от сервера, что совсем не нужно в моих торговых алгоритмах.

 
Alexey Viktorov:

Ну я не знаю чего можно не получить из

Вот результат

В этом случае только одна проблема. Для ловли закрытия позиции надо ловить их по TRADE_TRANSACTION_DEAL_ADD, по TRADE_TRANSACTION_REQUEST этого не отловить.

Алексей, к сожалению Вы не прочли внимательно мой ответ и пост в целом... Посмотрите хронологию событий:

Последовательность событий (команды и транзакции)  Ключ идентификации
 Момент появления ключа
 OrderSendAsync
 request_id
2020.03.17 20:42:09 (Srv 15:42:09)        ( 0-0)     TRADE_TRANSACTION_ORDER_ADD / DEAL_TYPE_BUY:
Тикет позиции
 
2020.03.17 20:42:10 (Srv 15:42:09)        ( 0-0)     TRADE_TRANSACTION_DEAL_ADD / DEAL_TYPE_BUY:
Тикет позиции  
2020.03.17 20:42:10 (Srv 15:42:10)        ( 0-0)     TRADE_TRANSACTION_ORDER_DELETE / DEAL_TYPE_BUY:
Тикет позиции  
2020.03.17 20:42:10 (Srv 15:42:10)        ( 0-0)     TRADE_TRANSACTION_HISTORY_ADD / DEAL_TYPE_BUY:
Тикет позиции  
2020.03.17 20:42:10 (Srv 15:42:10)   ;TRADE_TRANSACTION_REQUEST;;;
2020.03.17 20:42:10 (Srv 15:42:10)   ;------------Request :
Request: TRADE_ACTION_DEAL
Order ticket: 547197733
2020.03.17 20:42:10 (Srv 2020.03.17 15:42:10)   ;------------Result  :
TradeResult 10009 Desc=Заявка выполнена
Request ID: 2
Order ticket: 547197733
Deal ticket: 524982252
request_id  Тикет позиции

OrderSendAsync в результирующей структуре не вернул тикет. Из хронологии транзакций видно, что ключевой элемент для идентификации транзакций торгового исполнения (первые 4-ре) появился после того, как они произошли, в самом конце, в TRADE_TRANSACTION_REQUEST.

 
Vladimir Mikhailov:

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

Да хранение - не проблема. Это все есть. Проблема как раз в привязке (см. временную диаграмму выше).

  1. Функция OrderSendAsync не возвращает ТИКЕТ в 70-80% случаев;
  2. Функция OrderSendAsync гарантированно (я надеюсь) возвращает только request_id;
  3. Транзакции торгового исполнения (первые 4-ре) можно привязать только по ТИКЕТУ;
  4. А ТИКЕТ появляется в конце, когда уже торговое исполнение прошло...
 
Vasiliy Sokolov:

То, что OrderSendAsync исполняет заявки быстрее - распространенное заблуждение. Обе функции OrderSend и OrderSendAsync строго равны по производительности, но...

конкретно на moex есть такая проблема, ответ может доходить несколько десятков секунд, хотя сделка по факту уже открыта. И тогда разорвать цепочку Запрос - ответ, с помощью OrderSendAsync становится полезным. Это я к тому, что по сути OrderSendAsync многими используется как хак и как способ обойти этот глюк, хотя правильное решение должно быть на совести разработчиков серверной части МetaTrader 5 для Moex.

Безусловно, с большинством недостатков OrderSend можно мириться, но я столкнулся с тем, что функция переставала отвечать более МИНУТЫ (подтверждается логированием исполнения: GetTickCount до и после), без видимых на то причин !

Вот с этим "бонусом" я мириться уже не мог... И вот я тут.

 
Vladimir Belozercev:

Безусловно, с большинством недостатков OrderSend можно мириться, но я столкнулся с тем, что функция переставала отвечать более МИНУТЫ (подтверждается логированием исполнения: GetTickCount до и после), без видимых на то причин !

Вот с этим "бонусом" я мириться уже не мог... И вот я тут.

Вы не первый. Почитайте посты Михаила. Он много об этом писал здесь несколько лет назад. Ему вроде попробовали отвечать разрабы, но поезд и ныне там. И он кстати тоже использует асинхронную отправку заявок.

 

Не испытывал совсем проблем с OrderSend. Возможно, по причине отсутствия MOEX.

Но если бы нужен был OrderSendAsync, то не использовал бы OnTradeTransaction, а воспользовался бы подобным решением.

TradeTransactions
TradeTransactions
  • www.mql5.com
Асинхронные торговые приказы обладают огромным преимуществом - высокая скорость при массовой отправке. Однако, распространению таких приказов мешает некоторое неудобство - данные о результате приказа возможно увидеть только в OnTradeTransaction. Такое обстоятельство заставляет обывателя строить событийную модель своей ТС, если хочется...
 

Ну я не понимаю в чём трудности.

Вот код советника

int countPos = 0;

int OnInit()
 {
  trade.SetAsyncMode(true);
  trade.SetExpertMagicNumber(3);
  return(INIT_SUCCEEDED);
 }

void OnTick()
 {
  if(countPos == 0)
   {
    double ask = SymbolInfoDouble(_Symbol, SYMBOL_BID);
    for(int i = 0; i < 2; i++)
      trade.PositionOpen(_Symbol, ORDER_TYPE_SELL, 0.1, ask, ask+200*_Point, ask-200*_Point);
   }
 }

void OnTradeTransaction(const MqlTradeTransaction& trans,
                        const MqlTradeRequest& request,
                        const MqlTradeResult& result)
 {
  if(trans.type == TRADE_TRANSACTION_REQUEST)
   {
    countPos++;
    Print("trans.type - ", EnumToString(trans.type));
    Print("result.retcode = ", result.retcode);
    Print("result.order = ", result.order);
    Print("result.deal = ", result.deal);
    HistoryOrderSelect(result.order);
    long position = HistoryOrderGetInteger(result.order, ORDER_POSITION_ID);
    Print("position = ", position);
    Print("request.magic = ", request.magic);
    Print("request.symbol = ", request.symbol);
    Print("request.type = ", EnumToString(request.type));
    Print("request.volume = ", request.volume);
    Print("request.price = ", request.price);
    Print("request.sl = ", request.sl);
    Print("request.tp = ", request.tp);
   }
 }

И вот результат выполнения на счёте Netting.

2020.03.18 16:00:19.477 2019.12.23 00:00:30   trans.type - TRADE_TRANSACTION_REQUEST
2020.03.18 16:00:19.477 2019.12.23 00:00:30   result.retcode = 10009
2020.03.18 16:00:19.477 2019.12.23 00:00:30   result.order = 2
2020.03.18 16:00:19.477 2019.12.23 00:00:30   result.deal = 2
2020.03.18 16:00:27.122 2019.12.23 00:00:30   position = 2
2020.03.18 16:00:27.122 2019.12.23 00:00:30   request.magic = 3
2020.03.18 16:00:27.122 2019.12.23 00:00:30   request.symbol = EURUSD
2020.03.18 16:00:27.122 2019.12.23 00:00:30   request.type = ORDER_TYPE_SELL
2020.03.18 16:00:27.122 2019.12.23 00:00:30   request.volume = 0.1
2020.03.18 16:00:27.122 2019.12.23 00:00:30   request.price = 1.1075
2020.03.18 16:00:27.122 2019.12.23 00:00:30   request.sl = 1.1095
2020.03.18 16:00:27.122 2019.12.23 00:00:30   request.tp = 1.1055
2020.03.18 16:00:27.122 2019.12.23 00:00:30   trans.type - TRADE_TRANSACTION_REQUEST
2020.03.18 16:00:27.122 2019.12.23 00:00:30   result.retcode = 10009
2020.03.18 16:00:27.122 2019.12.23 00:00:30   result.order = 3
2020.03.18 16:00:27.122 2019.12.23 00:00:30   result.deal = 3
2020.03.18 16:01:54.947 2019.12.23 00:00:30   position = 2
2020.03.18 16:01:55.381 2019.12.23 00:00:30   request.magic = 3
2020.03.18 16:01:55.706 2019.12.23 00:00:30   request.symbol = EURUSD
2020.03.18 16:01:56.025 2019.12.23 00:00:30   request.type = ORDER_TYPE_SELL
2020.03.18 16:01:56.332 2019.12.23 00:00:30   request.volume = 0.1
2020.03.18 16:01:56.666 2019.12.23 00:00:30   request.price = 1.1075
2020.03.18 16:01:56.970 2019.12.23 00:00:30   request.sl = 1.1095
2020.03.18 16:01:57.291 2019.12.23 00:00:30   request.tp = 1.1055

Никаких трудностей с получением тикета позиции не вижу.