#include "Orders.mqh" #ifdef __MQL5__ #ifdef __MT4ORDERS__ #define MACROS_HISTORY #endif // #ifdef __MT4ORDERS__ #else //#ifdef __MQL5__ #define MACROS_HISTORY #endif // #ifdef __MQL5__ #else class SNAPSHOT : public ORDERS { private: ulong TimeData; int PrevHistoryTotal; int PrevPositionsTotal; int PrevOrdersTotal; MAGIC_TYPE PrevHistoryMagic; string PrevHistorySymb; bool PrevHistoryCloseBy; datetime PrevHistoryFrom; #ifdef __MQL5__ #ifdef __MT4ORDERS__ // Взято из MT4Orders.mqh static bool OrderVisible( void ) { // Если позиция закрылась при живой частично исполненной отложке, что ее породила. // А после оставшаяся часть отложки полностью исполнилась, но не успела исчезнуть. // То будет видна и новая позиция (правильно) и не исчезнувшая отложка (неправильно). const ulong PositionID = ::OrderGetInteger(ORDER_POSITION_ID); const ENUM_ORDER_TYPE Type = (ENUM_ORDER_TYPE)::OrderGetInteger(ORDER_TYPE); ulong Ticket = 0; return(!((Type == ORDER_TYPE_CLOSE_BY) || (PositionID && // Partial-отложенник имеет ненулевой PositionID. (Type <= ORDER_TYPE_SELL) && // Закрывающие маркет-ордера игнорируем ((Ticket = ::OrderGetInteger(ORDER_TICKET)) != PositionID))) && // Открывающие частично исполненные маркет-ордера не игнорируем. // Открывающий/доливающий позицию ордер может не успеть исчезнуть. (!::PositionsTotal() || !(::PositionSelectByTicket(Ticket ? Ticket : ::OrderGetInteger(ORDER_TICKET)) && // (::PositionGetInteger(POSITION_TYPE) == (::OrderGetInteger(ORDER_TYPE) & 1)) && // (::PositionGetInteger(POSITION_TIME_MSC) >= ::OrderGetInteger(ORDER_TIME_SETUP_MSC)) && (::PositionGetDouble(POSITION_VOLUME) == ::OrderGetDouble(ORDER_VOLUME_INITIAL))))); } #ifdef VIRTUAL_ORDERSELECT_WITHOUT_COPY void SetOrders( const MAGIC_TYPE &Magic, const string &Symb, const bool &AllSymbols, const TICKET_TYPE &SelectTicket, const int &SelectType ) #else // #ifdef VIRTUAL_ORDERSELECT_WITHOUT_COPY void SetOrders( const MAGIC_TYPE &Magic, const string &Symb, const bool &AllSymbols ) #endif // #ifdef VIRTUAL_ORDERSELECT_WITHOUT_COPY #else { static const bool IsTester = ::MQLInfoInteger(MQL_TESTER); const int StartPos2 = this.AmountOrders; int PrevTotal = -1; int Total = ::OrdersTotal(true); int Count = 0; /* int Index[]; ::ArrayResize(Index, ArraySize(this.Orders)); */ while (Total && (PrevTotal != Total)) { ulong PrevTicket = 0; PrevTotal = Total; this.AmountOrders = StartPos2; for (int i = 0; i < PrevTotal; i++) { const ulong Ticket = ::OrderGetTicket(i); if (!IsTester && (!Ticket || (Ticket == PrevTicket) || (PrevTotal != (Total = ::OrdersTotal(true))))) { /* ::Print("Ticket = " + (string)Ticket); ::Print("PrevTicket = " + (string)PrevTicket); ::Print("PrevTotal = " + (string)PrevTotal); ::Print("Total = " + (string)Total); ::Print("this.AmountOrders = " + (string)this.AmountOrders); ::Print("i = " + (string)i); ::Print("StartPos2 = " + (string)StartPos2); */ PrevTotal = -1; this.AmountOrders = StartPos2; break; } else if (((Magic == -1) || (::OrderGetInteger(ORDER_MAGIC) == Magic)) && (IsTester || SNAPSHOT::OrderVisible()) && (AllSymbols || (::OrderGetString(ORDER_SYMBOL) == _Symbol)) && ::OrderSelect(INT_MIN, SELECT_BY_POS)) { /* if ((this.AmountOrders >= ::ArraySize(this.Orders)) || ((this.AmountOrders > StartPos2) && (Ticket == this.Orders[StartPos2].GetTicket()))) { ::Alert(__FILE__ + ": " + (string)__LINE__ + " - this.AmountOrders >= ::ArraySize(this.Orders)"); ::Print("this.AmountOrders = " + (string)this.AmountOrders); ::Print("::ArraySize(this.Orders) = " + (string)::ArraySize(this.Orders)); ::Print("StartPos2 = " + (string)StartPos2); ::Print("Ticket = " + (string)Ticket); ::Print("Count = " + (string)Count); ::Print("i = " + (string)i); const datetime tTimeCurrent = ::TimeCurrent(); for (int j = 0; j < ::ArraySize(this.Orders); j++) ::Print(::IntegerToString(j, 2, '0') + ": " + this.Orders[j].ToString(tTimeCurrent) + ", Index = " + (string)Index[j] + ", NowTicket = " + (string)::OrderGetTicket(Index[j])); } Index[this.AmountOrders] = i; */ #ifdef VIRTUAL_SNAPSHOT_WITHOUT_HISTORY this.Orders[this.AmountOrders++].Copy(true); // Чтобы уменьшить тормоза работы со строками. #else // #ifdef VIRTUAL_SNAPSHOT_WITHOUT_HISTORY this.Orders[this.AmountOrders++].Copy(); // Данные MT5-ордеров не используют историю торгов в MT4Orders. #endif // #ifdef VIRTUAL_SNAPSHOT_WITHOUT_HISTORY #else #ifdef VIRTUAL_ORDERSELECT_WITHOUT_COPY if (SelectTicket && (this.Orders[this.AmountOrders - 1].GetTicket() == SelectTicket) && (this.Orders[this.AmountOrders - 1].GetType() == SelectType)) this.SelectIndex = this.AmountOrders - 1; #endif // #ifdef VIRTUAL_ORDERSELECT_WITHOUT_COPY } PrevTicket = Ticket; } #ifdef MT4ORDERS_BYPASS_MAXTIME MT4ORDERS::ByPass.Waiting(); #endif // #ifdef MT4ORDERS_BYPASS_MAXTIME if (!IsTester) { Total = ::OrdersTotal(true); Count++; } } this.PrevOrdersTotal = Total; if (Count > 1) ::Alert(__FILE__ + " " + (string)__LINE__ + ": Count = " + (string)Count); } #endif // #ifdef __MT4ORDERS__ #endif // #ifdef __MQL5__ #ifdef MACROS_HISTORY #ifdef __TRADESID__ // TicketOrder - CloseBy-ордер, PositionID - ID-сделки, для которой ищем противоположную. static ulong GetDealCloseBy( const ulong TicketOrder, const ulong PositionID ) { static TRADESID SnapTradesID; ulong Res = 0; ulong Deals[]; const ulong PositionByID = ::HistoryOrderGetInteger(TicketOrder, (::HistoryOrderGetInteger(TicketOrder, ORDER_POSITION_ID) == PositionID) ? ORDER_POSITION_BY_ID : ORDER_POSITION_ID); for (uint i = SnapTradesID.GetDealsByID(PositionByID, Deals); (bool)i--;) if (::HistoryDealGetInteger(Deals[i], DEAL_ORDER) == TicketOrder) { Res = Deals[i]; break; } return(Res); } // https://www.mql5.com/ru/forum/93352/page79#comment_54482424 // Возвращает позицию тикета в таблице исторических ордеров. static int OrderSelectPos( const long Ticket = -1 ) { #ifdef MT4ORDERS_ORDERS_SORT #ifdef __MQL5__ #ifdef __VIRTUAL__ if (!VIRTUAL::GetHandle()) #endif // #ifdef __VIRTUAL__ return(-1); #endif // #ifdef __MQL5__ #else // #ifdef MT4ORDERS_ORDERS_SORT #ifdef __VIRTUAL__ if (VIRTUAL::GetHandle()) return(-1); #endif // #ifdef __VIRTUAL__ #endif // #ifdef MT4ORDERS_ORDERS_SORT #else int Pos = -1; if (::OrderSelect(Ticket, SELECT_BY_TICKET, MODE_HISTORY) && ::OrderCloseTime()) { static int PrevHistoryTotal2 = 0; static CHashMap Tickets; const long SearchTicket = (::OrderType() <= OP_SELL) ? ::OrderTicket() : -::OrderTicket(); const int Total = ::OrdersHistoryTotal(); while (PrevHistoryTotal2 < Total) { if (::OrderSelect(PrevHistoryTotal2, SELECT_BY_POS, MODE_HISTORY)) { const long NewTicket = (::OrderType() <= OP_SELL) ? ::OrderTicket() : -::OrderTicket(); if (NewTicket == SearchTicket) Pos = PrevHistoryTotal2; Tickets.Add(NewTicket, PrevHistoryTotal2); } PrevHistoryTotal2++; } if ((Pos == -1) && !Tickets.TryGetValue(SearchTicket, Pos)) Pos = -1; } return(Pos); } #endif // #ifdef __TRADESID__ static int GetDealTimePos( const datetime SearchTime ) { datetime LeftTime, RightTime, PosTime; int Left, Right, Pos = 0; Left = 0; Right = ::HistoryDealsTotal() - 1; #ifdef __MQL5__ LeftTime = (datetime)::HistoryDealGetInteger(::HistoryDealGetTicket(Left), DEAL_TIME); RightTime = (datetime)::HistoryDealGetInteger(::HistoryDealGetTicket(Right), DEAL_TIME); #else // #ifdef __MQL5__ LeftTime = ::OrderSelect(Left, SELECT_BY_POS, MODE_HISTORY) ? ::OrderCloseTime() : 0; RightTime = ::OrderSelect(Left, SELECT_BY_POS, MODE_HISTORY) ? ::OrderCloseTime() : INT_MAX; #endif // #ifdef __MQL5__ #else if (SearchTime >= RightTime) Pos = Right; while ((LeftTime < SearchTime) && (SearchTime < RightTime)) { Pos = (Left + Right) >> 1; #ifdef __MQL5__ PosTime = (datetime)::HistoryDealGetInteger(::HistoryDealGetTicket(Pos), DEAL_TIME); #else // #ifdef __MQL5__ PosTime = ::OrderSelect(Pos, SELECT_BY_POS, MODE_HISTORY) ? ::OrderCloseTime() : LeftTime; #endif // #ifdef __MQL5__ #else if (Pos == Left) break; if (SearchTime >= PosTime) { Left = Pos; LeftTime = PosTime; } else // if (SearchTime < PosTime) { Right = Pos; RightTime = PosTime; } } return(Pos); } static bool SetOrder( ORDER_BASE &Order, const bool &CloseBy ) { bool Res = Order.Copy(); #ifdef __TRADESID__ if (Res && !CloseBy) { const ulong TicketOrder = ::HistoryDealGetInteger(::OrderTicket(), DEAL_ORDER); const ulong DealBy = (TicketOrder && ::HistoryOrderGetInteger(TicketOrder, ORDER_TYPE) == ORDER_TYPE_CLOSE_BY) ? SNAPSHOT::GetDealCloseBy(TicketOrder, ::OrderTicketID()) : 0; if (DealBy) if (Res = ::OrderSelect(DealBy, SELECT_BY_TICKET, MODE_HISTORY) && (Order.GetOpenTimeMsc() < ::OrderOpenTimeMsc())) { const ulong TicketOpen = ::HistoryDealGetInteger(::OrderTicketOpen(), DEAL_ORDER); const ENUM_ORDER_TYPE Type = (ENUM_ORDER_TYPE)::HistoryOrderGetInteger(TicketOpen, ORDER_TYPE); // Может быть среди живых. const ENUM_DEAL_REASON Reason = (Type <= ORDER_TYPE_SELL) ? DEAL_REASON_CLIENT : ((Type <= ORDER_TYPE_SELL_LIMIT) ? DEAL_REASON_TP : DEAL_REASON_SL); Order.Set(Order.GetTicket(), (ENUM_ORDER_TYPE)Order.GetType(), ::MathMax(::OrderLots(), Order.GetLots()), ::OrderSymbol(), Order.GetComment(), Order.GetOpenPrice(), Order.GetOpenTimeMsc(), (Reason == DEAL_REASON_SL) ? ::OrderOpenPriceRequest() : Order.GetStopLoss(), (Reason == DEAL_REASON_TP) ? ::OrderOpenPriceRequest() : Order.GetTakeProfit(), ::OrderOpenPrice(), ::OrderOpenTimeMsc(), Order.GetMagicNumber(), ::OrderProfit() + Order.GetProfit(), ::OrderCommission() + Order.GetCommission(), ::OrderSwap() + Order.GetSwap(), Order.GetOpenReason(), Reason, Order.GetOpenPriceRequest(), ::OrderOpenPriceRequest() ); } } #endif // #ifdef __TRADESID__ return(Res); } // Symb = "" - все символы. void SetHistoryOrders( const MAGIC_TYPE &Magic, const bool &HistoryInit, string Symb = "", const bool CloseBy = true, const datetime From = 0 ) { const bool AllSymbols = (Symb == ""); if (!AllSymbols && (Symb == NULL)) Symb = _Symbol; const int Total = ::OrdersHistoryTotal(); const bool ChangeFlag = this.IsChangeHistory(Magic, Symb, CloseBy, From); if (HistoryInit && ChangeFlag) { #ifdef __MQL5__ // undeclared identifier - #include ? this.PrevHistoryTotal = From ? SNAPSHOT::OrderSelectPos(::HistoryDealGetTicket(SNAPSHOT::GetDealTimePos(From))) : 0; #else // #ifdef __MQL5__ this.PrevHistoryTotal = From ? SNAPSHOT::GetDealTimePos(From) : 0; #endif // #ifdef __MQL5__ #else this.AmountHistoryOrders = 0; } for (int i = this.PrevHistoryTotal; i < Total; i++) // ::Order... - :: if (::OrderSelect(i, SELECT_BY_POS, MODE_HISTORY) && (::OrderOpenTime() >= From) && ((::OrderType() >= OP_BALANCE) || (((Magic == -1) || (::OrderMagicNumber() == Magic)) && (AllSymbols || (::OrderSymbol() == Symb))))) { ORDER_BASE Order; if (SNAPSHOT::SetOrder(Order, CloseBy)) this.AddHistoryOrder(Order); } this.PrevHistoryTotal = Total; return; } #undef MACROS_HISTORY #endif // #ifdef MACROS_HISTORY bool IsChangeHistory( const MAGIC_TYPE &Magic, const string &Symb, const bool &CloseBy, const datetime &From ) { const bool Res = (Magic != this.PrevHistoryMagic) || (CloseBy != this.PrevHistoryCloseBy) || (From != this.PrevHistoryFrom) || (Symb != this.PrevHistorySymb); if (Res) { this.PrevHistoryMagic = Magic; this.PrevHistoryCloseBy = CloseBy; this.PrevHistoryFrom = From; this.PrevHistorySymb = Symb; } return(Res); } public: SNAPSHOT() : ORDERS(0), TimeData(ULONG_MAX >> 1), // Половина ULONG_MAX, чтобы первый вызов SnapshotLifeTime // гарантированно дал большое время (при малых GetMicrosecondCount). PrevHistoryTotal(0), PrevPositionsTotal(-1), PrevOrdersTotal(-1), PrevHistoryMagic(INT_MAX), PrevHistorySymb(NULL), PrevHistoryCloseBy(false), PrevHistoryFrom(INT_MAX) { this.SetID(__FUNCTION__); } // Symb = "" - все символы. ulong Snapshot( const uint &RefreshTime, const MAGIC_TYPE &Magic, bool HistoryInit = false, string Symb = "", const bool CloseBy = true ) { static const bool IsTester = ::MQLInfoInteger(MQL_TESTER); if (!IsTester && #ifdef __MQL5__ #ifdef __MT4ORDERS__ #ifdef MT4ORDERS_BYPASS_MAXTIME MT4ORDERS::ByPass.Waiting() && #endif // #ifdef MT4ORDERS_BYPASS_MAXTIME (this.PrevPositionsTotal == ::PositionsTotal()) && (this.PrevOrdersTotal == ::OrdersTotal(true)) && #endif // #ifdef __MT4ORDERS__ #else // #ifdef __MQL5__ (this.PrevOrdersTotal == ::OrdersTotal()) && #endif // #ifdef __MQL5__ #else SNAPSHOT::SnapshotLifeTime() < ::MathMin(RefreshTime, ::TerminalInfoInteger(TERMINAL_PING_LAST) >> 1)) return(0); const bool AllSymbols = IsTester || (Symb == ""); if (!AllSymbols && (Symb == NULL)) Symb = _Symbol; #ifdef __MQL5__ #ifdef __MT4ORDERS__ int PrevTotal = -1; int Total = 0; ulong StartTime = 0; int Count = 0; #ifdef VIRTUAL_ORDERSELECT_WITHOUT_COPY const TICKET_TYPE SelectTicket = (this.SelectIndex == MAX_ORDERS - 1) ? 0 : this.Orders[this.SelectIndex].GetTicket(); const int SelectType = (this.SelectIndex == MAX_ORDERS - 1) ? 0 : this.Orders[this.SelectIndex].GetType(); #endif // #ifdef VIRTUAL_ORDERSELECT_WITHOUT_COPY while (PrevTotal != Total) { #ifndef VIRTUAL_SNAPSHOT_WITHOUT_HISTORY this.SetHistoryOrders(Magic, HistoryInit, Symb, CloseBy); HistoryInit = false; #ifdef MT4ORDERS_BYPASS_MAXTIME MT4ORDERS::ByPass.Waiting(); #endif // #ifdef MT4ORDERS_BYPASS_MAXTIME #endif // #ifndef VIRTUAL_SNAPSHOT_WITHOUT_HISTORY StartTime = ::GetMicrosecondCount(); this.AmountOrders = 0; ulong PrevTicket = 0; Total = ::PositionsTotal(); PrevTotal = Total; for (int i = 0; i < PrevTotal; i++) { const ulong Ticket = ::PositionGetTicket(i); if (!Ticket || (Ticket == PrevTicket) || (PrevTotal != (Total = ::PositionsTotal()))) { PrevTotal = -1; break; } else if (((Magic == -1) || (::PositionGetInteger(POSITION_MAGIC) == Magic)) && (AllSymbols || (::PositionGetString(POSITION_SYMBOL) == Symb)) && ::OrderSelect(INT_MAX, SELECT_BY_POS)) { #ifdef VIRTUAL_SNAPSHOT_WITHOUT_HISTORY this.Orders[this.AmountOrders++].Copy(true); #else // #ifdef VIRTUAL_SNAPSHOT_WITHOUT_HISTORY this.Orders[this.AmountOrders++].Copy(); #endif // #ifdef VIRTUAL_SNAPSHOT_WITHOUT_HISTORY #else #ifdef VIRTUAL_ORDERSELECT_WITHOUT_COPY if (SelectTicket && (this.Orders[this.AmountOrders - 1].GetTicket() == SelectTicket)) this.SelectIndex = this.AmountOrders - 1; #endif // #ifdef VIRTUAL_ORDERSELECT_WITHOUT_COPY } PrevTicket = Ticket; } #ifdef MT4ORDERS_BYPASS_MAXTIME MT4ORDERS::ByPass.Waiting(); #endif // #ifdef MT4ORDERS_BYPASS_MAXTIME if ((PrevTotal == (Total = ::PositionsTotal()))) { #ifdef VIRTUAL_ORDERSELECT_WITHOUT_COPY this.SetOrders(Magic, Symb, AllSymbols, SelectTicket, SelectType); #else // #ifdef VIRTUAL_ORDERSELECT_WITHOUT_COPY this.SetOrders(Magic, Symb, AllSymbols); #endif // #ifdef VIRTUAL_ORDERSELECT_WITHOUT_COPY #else Total = ::PositionsTotal(); } Count++; } if (Count > 1) ::Alert(__FILE__ + " " + (string)__LINE__ + ": Count = " + (string)Count); this.CurrentTick.time = ::TimeCurrent(); // Для определения длительности жизни ордеров. this.PrevPositionsTotal = Total; this.TimeData = ::GetMicrosecondCount(); return(Total ? this.TimeData - StartTime : 0); #else // #ifdef __MT4ORDERS__ return(ULONG_MAX); #endif // #ifdef __MT4ORDERS__ #else #else // #ifdef __MQL5__ int PrevTotal = -1; int Total = 0; ulong StartTime = 0; while (PrevTotal != Total) { #ifndef VIRTUAL_SNAPSHOT_WITHOUT_HISTORY this.SetHistoryOrders(Magic, HistoryInit); HistoryInit = false; #endif // #ifndef VIRTUAL_SNAPSHOT_WITHOUT_HISTORY StartTime = ::GetMicrosecondCount(); this.AmountOrders = 0; int PrevTicket = 0; Total = ::OrdersTotal(); PrevTotal = Total; for (int i = 0; i < PrevTotal; i++) { if (!::OrderSelect(i, SELECT_BY_POS) || (::OrderTicket() == PrevTicket) || (PrevTotal != (Total = ::OrdersTotal()))) { PrevTotal = -1; break; } else if (((Magic == -1) || (::OrderMagicNumber() == Magic)) && (AllSymbols || (::OrderSymbol() == Symb))) this.Orders[this.AmountOrders++].Copy(); PrevTicket = ::OrderTicket(); } } this.CurrentTick.time = ::TimeCurrent(); // Для определения длительности жизни ордеров. this.PrevOrdersTotal = Total; this.TimeData = ::GetMicrosecondCount(); return(Total ? this.TimeData - StartTime : 0); #endif // #ifdef __MQL5__ #else } ulong SnapshotLifeTime( void ) const { static const bool IsTester = ::MQLInfoInteger(MQL_TESTER); return(IsTester ? ULONG_MAX : (::GetMicrosecondCount() - this.TimeData)); // Обязуем любой вызов снепшота в Тестере делать полноценным. } bool SnapshotHistory( const MAGIC_TYPE Magic = -1, const bool HistoryInit = false, const string Symb = NULL, const bool CloseBy = true, const datetime From = 0 ) { #ifndef VIRTUAL_SNAPSHOT_WITHOUT_HISTORY return(false); #else // #ifndef VIRTUAL_SNAPSHOT_WITHOUT_HISTORY #ifndef __TRADESID__ if (!CloseBy) return(false); #endif // #ifdef __TRADESID__ #else this.SetHistoryOrders(Magic, HistoryInit, Symb, CloseBy, From); // undeclared identifier - #include ? return(true); #endif // #ifndef VIRTUAL_SNAPSHOT_WITHOUT_HISTORY #else } };