Библиотеки: TradesID

 

TradesID:

Быстрая работа с POSITION_ID

Автор: fxsaber

 

Результат запуска приложенного скрипта на реальном счете.

2021.03.29 17:18:55.280 TradesID_Example (EURUSD,M1)    HistoryDealsTotal() = 99663
2021.03.29 17:18:55.280 TradesID_Example (EURUSD,M1)    HistoryOrdersTotal() = 174307

2021.03.29 17:24:15.862 TradesID_Example (EURUSD,M1)    GetSumByPositionsID(PositionsID,GetSumByPositionID) = 29906412.60837016
2021.03.29 17:24:15.862 TradesID_Example (EURUSD,M1)    Alert: Bench_Stack = 0, 1 <= Time[GetSumByPositionsID(PositionsID,GetSumByPositionID))] = 320581370 mcs.

2021.03.29 17:24:16.057 TradesID_Example (EURUSD,M1)    GetSumByPositionsID(PositionsID,GetSumByPositionID2) = 29906412.60837016
2021.03.29 17:24:16.057 TradesID_Example (EURUSD,M1)    Alert: Bench_Stack = 0, 1 <= Time[GetSumByPositionsID(PositionsID,GetSumByPositionID2))] = 195526 mcs.
2021.03.29 17:24:16.170 TradesID_Example (EURUSD,M1)    GetSumByPositionsID(PositionsID,GetSumByPositionID2) = 29906412.60837016
2021.03.29 17:24:16.170 TradesID_Example (EURUSD,M1)    Alert: Bench_Stack = 0, 1 <= Time[GetSumByPositionsID(PositionsID,GetSumByPositionID2))] = 112839 mcs.

Библиотека быстрее штатной реализации в ~2500 раз. Только альтернативный подход позволяет делать некоторые вычисления за приемлемое время.

 

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

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


Ниже демонстрация.

#include <MT4Orders.mqh> // https://www.mql5.com/ru/code/16006

#define Bid SymbolInfoDouble(_Symbol, SYMBOL_BID)
#define Ask SymbolInfoDouble(_Symbol, SYMBOL_ASK)

// Возвращает ордера с заданным PositionID
void GetOrders( const long PositionID, ulong &Orders[] )
{
  if (HistorySelectByPosition(PositionID))
  {
    const int Size = ArrayResize(Orders, HistoryOrdersTotal());
    
    for (int i = 0; i < Size; i++)
      Orders[i] = HistoryOrderGetTicket(i);
  }
}

void OnStart()
{  
  // Открыли противоположные позиции
  const TICKET_TYPE Ticket1 = OrderSend(_Symbol, OP_BUY, 0.1, Ask, 0, 0, 0);
  const TICKET_TYPE Ticket2 = OrderSend(_Symbol, OP_SELL, 0.1, Bid, 0, 0, 0);
  
  // и схлопнули их друг с другом.
  if (OrderCloseBy(Ticket1, Ticket2))
  {
    long Orders1[];
    long Orders2[];
    
    // Возвращает ордера с заданным PositionID
    GetOrders(Ticket1, Orders1);
    GetOrders(Ticket2, Orders2);
    
    TRADESID TradesID;
    
    long OrdersID1[];
    long OrdersID2[];
    
    // Возвращает ордера, связанные с PositionID
    TradesID.GetOrdersByID(Ticket1, OrdersID1);
    TradesID.GetOrdersByID(Ticket2, OrdersID2);
    
    // Распечатали штатный вариант.
    ArrayPrint(Orders1);
    ArrayPrint(Orders2);
    
    // Распечатали альтернативный вариант.
    ArrayPrint(OrdersID1);
    ArrayPrint(OrdersID2);    
  }
}


Результат.

2377425 2377427
2377426

2377425 2377427
2377426 2377427

Пустота во второй строке - результат HistorySelectByPosition.

 
fxsaber:

Библиотека быстрее штатной реализации в ~2500 раз. Только альтернативный подход позволяет делать некоторые вычисления за приемлемое время.

Один из вариантов таких вычислений.

Форум по трейдингу, автоматическим торговым системам и тестированию торговых стратегий

Библиотеки: Report

fxsaber, 2021.03.31 14:15

#define REPORT_REJECTS            // Анализ качества исполнения торговых ордеров.

Теперь виден уровень FillRate.


На кухнях он, конечно, равен 100%.

 

Добавлен ByPass.mqh. Это производный класс от TradesID.mqh, который позволяет идентифицировать корректность торгового окружения MT5: таблицы текущих позиций/ордеров и закрытых сделок/ордеров.


Формулировка, конечно, условная. В качестве примера использования попробуем решить следующую проблему.

Форум по трейдингу, автоматическим торговым системам и тестированию торговых стратегий

Великий и ужасный МТ4 навсегда (или как грамотно выработать стратегию перехода)

fxsaber, 2021.05.05 02:04

// Демонстрация открытия дубля позиции в MT5.

#include <Trade\Trade.mqh>

void OnStart()
{
  CTrade Trade;
  
  while (!IsStopped() && (PositionsTotal() <= 1)) // Закончим, когда появится более одной позиции.
    if (PositionsTotal() == 1)
      Trade.PositionClose(PositionGetTicket(0)); // Если есть позиция - закрываем.
    else if (!OrdersTotal())
      Trade.Buy(0.01); // Если нет позиции и ордера - открываем позицию.
}

Запустите этот код на пустом демо-счете (ForexTimeFXTM-Demo01) и убедитесь в открытии двух позиций через несколько секунд.


Вот такие изменения в коде понадобилось сделать, чтобы избавиться от дубля позиции.

// Демонстрация РЕШЕНИЯ открытия дубля позиции в MT5.
// https://www.mql5.com/ru/forum/368178/page13#comment_22171524

#include <fxsaber\TradesID\ByPass.mqh> // https://www.mql5.com/ru/code/34173

#include <Trade\Trade.mqh>

void OnStart()
{
  CTrade Trade;

  BYPASS ByPass; // Объект обхода рассинхронизации торгового окружения

  while (!IsStopped() && (PositionsTotal() <= 1)) // Закончим, когда появится более одной позиции.
    if (ByPass.Is()) // Если торговое окружение синхронизировано
    {
      if (PositionsTotal() == 1)
        Trade.PositionClose(PositionGetTicket(0)); // Если есть позиция - закрываем.
      else if (!OrdersTotal() &&
               (OrdersTotal() == ByPass.OrdersTotal())) // После проверки синхронизации исключаем доп. затык.
      {
        if (Trade.Buy(0.01)) // Если нет позиции и ордера - открываем позицию.
          ByPass += Trade.ResultOrder(); // Добавили открывающий ордер в БД синхронизации.
      }
    }
}


Предполагается, что библиотека в дальнейшем будет адаптироваться для вариантов широкого применения. В частности, для MT4-style.


ЗЫ Если кто будет смотреть, попробуйте еще вариант без красного условия. Хотелось бы, чтобы без него работало.

 
fxsaber:

Предполагается, что библиотека в дальнейшем будет адаптироваться для вариантов широкого применения. В частности, для MT4-style.

В таком виде смогу сразу проверить на боевых роботах.

 

Не связано ли решение с тем, что пока получится выполнить ByPass.Is(), все списки и количества естественным образом нормализуются?

А если взять и сравнить эффективность данного решения с выполнением любого другого кода, выполняющегося такое же время?

Функция не выглядит как быстрая, чтобы ее на каждом тике выполнять.

Вообще код интересный, много непонятно, предстоит потратить время на его понимание)

Пока вот интересное место:

else if (::HistoryOrderGetInteger(this.Orders[i], ORDER_TICKET) != this.Orders[i])

Когда и почему может возникнуть это неравенство?

 
Dmitry Fedoseev:

Не связано ли решение с тем, что пока получится выполнить ByPass.Is(), все списки и количества естественным образом нормализуются?

А если взять и сравнить эффективность данного решения с выполнением любого другого кода, выполняющегося такое же время?

Функция не выглядит как быстрая, чтобы ее на каждом тике выполнять.

Замерял выполнение: от единиц микросекунд до пяти миллисекунд (редкие всплески). Как с Ренатом выяснили в ветке по скоростям, это значение колоссально зависит от внешних факторов, а не самого кода.

Из-за микровсплесков лага всего Терминала в коде всюду стоят проверки на изменение OrdersTotal, PositionsTotal. Потому что стресс-тесты показали, что, например, в момент прохода по циклу текущих ордеров можно нарваться на изменение их таблицы. При этом цикл проходит не за микросекунды, а за миллисекунды - тупо лаг на весь Терминал от ОС пришел. Т.е. полное осознание, что Терминал - ни разу не система реального времени, и в любой момент могут быть тормоза, даже когда CPU = 0%.

Вообще код интересный, много непонятно, предстоит потратить время на его понимание)

Пока вот интересное место:

Когда и почему может возникнуть это неравенство?

Если ордер отсутствует в текущей таблице (не всей, а той, что через HistorySelect-функции создается) истории, будет неравенство.

MT5 и скорость в боевом исполнении
MT5 и скорость в боевом исполнении
  • 2020.05.28
  • www.mql5.com
MT5 - шустрая платформа. Но есть узкие горлышки, которые сводят на нет все старания быстрой торговли...
 
Andrey Khatimlianskii:

В таком виде смогу сразу проверить на боевых роботах.

В принципе, должно изначально работать с любой торговой библиотекой, если инклудить в самом начале - чтобы чьи-то дефайны не изменили код.

Но код нужно сделать кроссплатформенным (под MT4 просто пустые заглушки).

 
Обновлен ByPass.mqh. Только косметика и совместимость с другими библиотеками. Сам механизм идентификации рассинхрона не тронут.
 

Правильно ли я понял, что BYPASS.CheckHistory (и, соотв-но BYPASS.Is) определяют рассинхронизацию только на ордерах, открывающих позиции?

Я смотрю этот код:

bool IsPosDeal( const ulong Ticket )
  {
    bool Res = true;
    const ulong PositionID = ::HistoryDealGetInteger(Ticket, DEAL_POSITION_ID);
    if (PositionID)
    {
      ulong Deals[];
      const int Size = this.GetDealsByID(PositionID, Deals);
      Res = !Size || (::HistoryDealGetInteger(Deals[Size - 1], DEAL_ENTRY) != DEAL_ENTRY_IN) || ::PositionSelectByTicket(PositionID);
    }
    return(Res);
  }

  bool IsPosOrder( const ulong Ticket )
  {
    bool Res = true;
    const ulong PositionID = ::HistoryOrderGetInteger(Ticket, ORDER_POSITION_ID);
    if (PositionID)
    {
      ulong OldOrders[];
      const int Size = this.GetOrdersByID(PositionID, OldOrders);
      Res = !Size || (OldOrders[Size - 1] != PositionID) || ::PositionSelectByTicket(PositionID);
    }
    return(Res);
  }


1. Например, если есть 1 поза, я ее закрываю. Ордер на закрытия исполнился, появился в истории. По нему прошла сделка, появилась в истории. Но PositionsTotal пока =1, т.е. окружение не синхронизировано.

Тогда (OldOrders[Size - 1] != PositionID)   == true

          (::HistoryDealGetInteger(Deals[Size - 1], DEAL_ENTRY) != DEAL_ENTRY_IN)   == true

         соотв-но .CheckHistory и .Is дадут true, чего быть не должно.

2. Даже если  сделки пока нет в истории, это условие

Res = !Size || (::HistoryDealGetInteger(Deals[Size - 1], DEAL_ENTRY) != DEAL_ENTRY_IN) || ::PositionSelectByTicket(PositionID);

будут выполняться, т.к.  PositionSelectByTicket(PositionID)   еще истинно. IsPosDeal  выдаст true.

Причина обращения: