Virtual_by_fxsaber/Include/fxsaber/Virtual/Snapshot.mqh

588 lines
No EOL
41 KiB
MQL5

#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<long, int> 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 <fxsaber\TradesID\TradesID.mqh> ?
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 <MT4Orders.mqh> ?
return(true);
#endif // #ifndef VIRTUAL_SNAPSHOT_WITHOUT_HISTORY #else
}
};