Результат запуска приложенного скрипта на реальном счете.
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.
Библиотека быстрее штатной реализации в ~2500 раз. Только альтернативный подход позволяет делать некоторые вычисления за приемлемое время.
Один из вариантов таких вычислений.
Форум по трейдингу, автоматическим торговым системам и тестированию торговых стратегий
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.
ЗЫ Если кто будет смотреть, попробуйте еще вариант без красного условия. Хотелось бы, чтобы без него работало.
Предполагается, что библиотека в дальнейшем будет адаптироваться для вариантов широкого применения. В частности, для MT4-style.
В таком виде смогу сразу проверить на боевых роботах.
Не связано ли решение с тем, что пока получится выполнить ByPass.Is(), все списки и количества естественным образом нормализуются?
А если взять и сравнить эффективность данного решения с выполнением любого другого кода, выполняющегося такое же время?
Функция не выглядит как быстрая, чтобы ее на каждом тике выполнять.
Вообще код интересный, много непонятно, предстоит потратить время на его понимание)
Пока вот интересное место:
else if (::HistoryOrderGetInteger(this.Orders[i], ORDER_TICKET) != this.Orders[i])
Когда и почему может возникнуть это неравенство?
Не связано ли решение с тем, что пока получится выполнить ByPass.Is(), все списки и количества естественным образом нормализуются?
А если взять и сравнить эффективность данного решения с выполнением любого другого кода, выполняющегося такое же время?
Функция не выглядит как быстрая, чтобы ее на каждом тике выполнять.
Замерял выполнение: от единиц микросекунд до пяти миллисекунд (редкие всплески). Как с Ренатом выяснили в ветке по скоростям, это значение колоссально зависит от внешних факторов, а не самого кода.
Из-за микровсплесков лага всего Терминала в коде всюду стоят проверки на изменение OrdersTotal, PositionsTotal. Потому что стресс-тесты показали, что, например, в момент прохода по циклу текущих ордеров можно нарваться на изменение их таблицы. При этом цикл проходит не за микросекунды, а за миллисекунды - тупо лаг на весь Терминал от ОС пришел. Т.е. полное осознание, что Терминал - ни разу не система реального времени, и в любой момент могут быть тормоза, даже когда CPU = 0%.
Вообще код интересный, много непонятно, предстоит потратить время на его понимание)
Пока вот интересное место:
Когда и почему может возникнуть это неравенство?
Если ордер отсутствует в текущей таблице (не всей, а той, что через HistorySelect-функции создается) истории, будет неравенство.

- 2020.05.28
- www.mql5.com
В таком виде смогу сразу проверить на боевых роботах.
В принципе, должно изначально работать с любой торговой библиотекой, если инклудить в самом начале - чтобы чьи-то дефайны не изменили код.
Но код нужно сделать кроссплатформенным (под MT4 просто пустые заглушки).
Правильно ли я понял, что 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.

- Бесплатные приложения для трейдинга
- 8 000+ сигналов для копирования
- Экономические новости для анализа финансовых рынков
Вы принимаете политику сайта и условия использования
TradesID:
Быстрая работа с POSITION_ID
Автор: fxsaber