Virtual_by_fxsaber/Include/fxsaber/Virtual/Orders.mqh

1920 lines
No EOL
106 KiB
MQL5

#ifndef MAX_ORDERS
#define DELETE_MACRO_MAX_ORDERS
#define MAX_ORDERS 200 // https://www.mql5.com/ru/forum/282062/page66#comment_55114313
#endif // #ifndef MAX_ORDERS
#ifdef VIRTUAL_ORDERSELECT_WITHOUT_COPY
#define VIRTUAL_ORDERSELECT_MACROS(A, B) B
#else // #ifdef VIRTUAL_ORDERSELECT_WITHOUT_COPY
#define VIRTUAL_ORDERSELECT_MACROS(A, B) A
#endif // #ifdef VIRTUAL_ORDERSELECT_WITHOUT_COPY #else
// https://www.mql5.com/ru/forum/1111/page2336#comment_9660624
// Нужно для BestInterval
// #define TICKS_CORRECT_TIME // время тиков, кратное секунде, будет скорректировано на миллисекунду вперед
// https://www.mql5.com/ru/forum/321656/page34#comment_14192799
// #define TICKS_FORCE_NORMALIZE // Принудительная нормализация цен входящего тика
#include "HistoryOrders.mqh"
#ifdef __MQL5__
#ifndef SELECT_BY_POS
#define SELECT_BY_POS 0
#endif // SELECT_BY_POS
#ifndef SELECT_BY_TICKET
#define SELECT_BY_TICKET 1
#endif // SELECT_BY_TICKET
#ifndef MODE_TRADES
#define MODE_TRADES 0
#endif // MODE_TRADES
#ifndef MODE_HISTORY
#define MODE_HISTORY 1
#endif // MODE_HISTORY
#ifndef OP_SELL
#define OP_SELL ORDER_TYPE_SELL
#endif // OP_SELL
#endif // __MQL5__
typedef void (*STRATEGY)( void );
typedef void (*STRATEGY_MULTI)( const string &Symb, const int &Index );
class ORDERS : public HISTORYORDERS
{
private:
bool Netting;
protected: // SNAPSHOT::Snaphot - VIRTUAL_ORDERSELECT_WITHOUT_COPY
int SelectIndex;
private:
int PrevAmountOrders;
string ID;
#ifdef VIRTUAL_ALTERNATIVE
// На предыдущем тике была вызвана функция из этого списка:
// -OrderProfit, -OrderClosePrice, -AccountEquity, -AccountProfit, -OrderPrint
// +OrderSend, +OrderModify, +OrderClose, +OrderDelete, +OrderCloseBy.
bool CallFunc;
bool CallChange; // На текущем тике вызван this.IsChange().
struct PRICEBORDERS
{
private:
int Flag;
struct MINMAX
{
double Min;
double Max;
void SetMax( const double &Price )
{
if (Price && (Price < this.Max))
this.Max = Price;
return;
}
void SetMin(const double &Price )
{
if (Price > this.Min)
this.Min = Price;
return;
}
bool IsAccept( const double &Price ) const
{
return((Price <= this.Min) || (Price >= this.Max));
}
void Init()
{
this.Min = 0;
this.Max = DBL_MAX;
return;
}
} bid, ask;
void Calc( const ORDER &Order )
{
switch (Order.Type)
{
case ORDER_TYPE_BUY:
this.bid.SetMax(Order.TakeProfit);
this.bid.SetMin(Order.StopLoss);
break;
case ORDER_TYPE_SELL:
this.ask.SetMin(Order.TakeProfit);
this.ask.SetMax(Order.StopLoss);
break;
case ORDER_TYPE_BUY_LIMIT:
this.ask.SetMin(Order.OpenPrice);
break;
case ORDER_TYPE_SELL_LIMIT:
this.bid.SetMax(Order.OpenPrice);
break;
case ORDER_TYPE_BUY_STOP:
this.ask.SetMax(Order.OpenPrice);
break;
case ORDER_TYPE_SELL_STOP:
this.bid.SetMin(Order.OpenPrice);
}
return;
}
void Init()
{
this.bid.Init();
this.ask.Init();
return;
}
void SetFlag( void )
{
// Приоритет на лимитники и TP.
// static const string Condition[] = {"(Tick.bid >= this.bid.Max)", "(Tick.ask <= this.ask.Min)",
// "(Tick.bid <= this.bid.Min)", "(Tick.ask >= this.ask.Max)"};
// https://www.mql5.com/ru/forum/170952/page238#comment_51197092
// Приоритет на стоповые и SL.
// static const string Condition[] = {"(Tick.bid <= this.bid.Min)", "(Tick.ask >= this.ask.Max)",
// "(Tick.bid >= this.bid.Max)", "(Tick.ask <= this.ask.Min)"};
// Приоритет на лимитники и TP.
this.Flag = (this.bid.Max != DBL_MAX) | ((this.ask.Min != 0) << 1) |
((this.bid.Min != 0) << 2) | ((this.ask.Max != DBL_MAX) << 3);
return;
}
bool IsAccept2( const MqlTick &Tick ) const
{
// Приоритет на лимитники и TP.
switch (this.Flag)
{
case 0:
return(false);
case 1:
return(Tick.bid >= this.bid.Max);
case 2:
return(Tick.ask <= this.ask.Min);
case 3:
return((Tick.bid >= this.bid.Max) || (Tick.ask <= this.ask.Min));
case 4:
return(Tick.bid <= this.bid.Min);
case 5:
return((Tick.bid >= this.bid.Max) || (Tick.bid <= this.bid.Min));
case 6:
return((Tick.ask <= this.ask.Min) || (Tick.bid <= this.bid.Min));
case 7:
return((Tick.bid >= this.bid.Max) || (Tick.ask <= this.ask.Min) || (Tick.bid <= this.bid.Min));
case 8:
return(Tick.ask >= this.ask.Max);
case 9:
return((Tick.bid >= this.bid.Max) || (Tick.ask >= this.ask.Max));
case 10:
return((Tick.ask <= this.ask.Min) || (Tick.ask >= this.ask.Max));
case 11:
return((Tick.bid >= this.bid.Max) || (Tick.ask <= this.ask.Min) || (Tick.ask >= this.ask.Max));
case 12:
return((Tick.bid <= this.bid.Min) || (Tick.ask >= this.ask.Max));
case 13:
return((Tick.bid >= this.bid.Max) || (Tick.bid <= this.bid.Min) || (Tick.ask >= this.ask.Max));
case 14:
return((Tick.ask <= this.ask.Min) || (Tick.bid <= this.bid.Min) || (Tick.ask >= this.ask.Max));
case 15:
return((Tick.bid >= this.bid.Max) || (Tick.ask <= this.ask.Min) || (Tick.bid <= this.bid.Min) || (Tick.ask >= this.ask.Max));
}
return(false);
}
public:
bool Ready;
void Set( const ORDER &Orders[], const int &Total )
{
_VC
this.Init();
for (uint i = Total; (bool)i--;)
this.Calc(Orders[i]);
this.SetFlag(); // См. this.IsAccept()
this.Ready = true;
return;
}
bool IsAccept( const MqlTick &Tick )
{
_VC
// const bool Res = this.bid.IsAccept(Tick.bid) || this.ask.IsAccept(Tick.ask); // this.SetFlag() не нужен.
const bool Res = this.IsAccept2(Tick);
if (Res)
this.Ready = false;
return(Res);
}
} PriceBorders;
#endif // #ifdef VIRTUAL_ALTERNATIVE
static string GetTickFlag( uint tickflag )
{
string flag = " " + (string)tickflag;
#define TICKFLAG_MACRO(A) flag += ((bool)(tickflag & TICK_FLAG_##A)) ? " TICK_FLAG_" + #A : ""; \
tickflag -= tickflag & TICK_FLAG_##A;
TICKFLAG_MACRO(BID)
TICKFLAG_MACRO(ASK)
TICKFLAG_MACRO(LAST)
TICKFLAG_MACRO(VOLUME)
TICKFLAG_MACRO(BUY)
TICKFLAG_MACRO(SELL)
#undef TICKFLAG_MACRO
if (tickflag)
flag += " FLAG_UNKNOWN (" + (string)tickflag + ")";
return(flag);
}
#define TOSTR(A) " " + #A + " = " + (string)Tick.A
#define TOSTR2(A) " " + #A + " = " + ::DoubleToString(Tick.A, _Digits)
static string TickToString( const MqlTick &Tick )
{
// Нет проверки, что Tick.time == Tick.time_msc / 1000
return(TOSTR(time) + "." + ::IntegerToString(Tick.time_msc % 1000, 3, '0') +
TOSTR2(bid) + TOSTR2(ask) + TOSTR2(last)+ TOSTR(volume) + ORDERS::GetTickFlag(Tick.flags));
}
#undef TOSTR2
#undef TOSTR
bool IsChange( void )
{
_VC
bool Res = false;
double Profit = 0;
int j = 0;
for (int i = 0; i < this.AmountOrders; i++)
// this.Orders[i].IsChange2(Res, this, i, j, Profit) ;
{
Res |= this.Orders[i].IsChange(this.CurrentTick) && !this.Orders[i].IsClosed();
// if (this.Orders[i].IsClosed())
if (this.Orders[i].Ticket <= 0)
{
// https://www.mql5.com/ru/forum/282062/page70#comment_56002732
#ifdef ORDER_COMMISSION
this.Balance += this.Orders[i].GetProfit() + this.Orders[i].GetCommission();
#else // ORDER_COMMISSION
this.Balance += this.Orders[i].GetProfit();
#endif // ORDER_COMMISSION
this.AddHistoryOrder(this.Orders[i]);
#ifdef VIRTUAL_ORDERSELECT_WITHOUT_COPY
if (this.SelectIndex == i)
this.Orders[this.SelectIndex = MAX_ORDERS - 1] = this.HistoryOrders[this.AmountHistoryOrders - 1];
else if (this.SelectIndex > i)
this.SelectIndex--;
#endif // #ifdef VIRTUAL_ORDERSELECT_WITHOUT_COPY
}
else
{
#ifdef ORDER_COMMISSION
Profit += this.Orders[i].GetFullProfit();
#else // ORDER_COMMISSION
Profit += this.Orders[i].GetProfit();
#endif // ORDER_COMMISSION
if (i != j)
this.Orders[j] = this.Orders[i];
j++;
}
}
this.AmountOrders = j;
// https://www.mql5.com/ru/forum/282062/page70#comment_56002732
this.Equity = this.Balance + Profit;
this.FlagChange |= Res;
#ifdef VIRTUAL_ALTERNATIVE
this.CallChange = true;
#endif // #ifdef VIRTUAL_ALTERNATIVE
return(Res);
}
void CloseBy( void )
{
int j = -1;
for (int i = 0; i < this.AmountOrders; i++)
if (!this.Orders[i].IsClosed() && this.Orders[i].IsPosition())
{
if (j < 0)
j = i;
else if (this.Orders[j].CloseBy(this.Orders[i], this.CurrentTick, this.Netting) && this.Orders[j].IsClosed())
{
i = j;
j = -1;
}
}
return;
}
void Check( void )
{
_VC
while (this.IsChange())
if (this.Netting)
this.CloseBy();
return;
}
int GetIndex( const long &Ticket ) const
{
_VC
int Res = -1;
if ((this.SelectIndex < this.AmountOrders) && (this.Orders[this.SelectIndex].GetTicket() == Ticket))
Res = this.SelectIndex;
else
for (int i = 0; i < this.AmountOrders; i++)
if (this.Orders[i].GetTicket() == Ticket)
{
Res = i;
break;
}
return(Res);
}
// Нужно перебор менять на Generic-реализацию.
bool SelectByTicketHistory( const long &Ticket )
{
_VC
bool Res = false;
for (int i = 0; i < this.AmountHistoryOrders; i++)
// for (int i = this.AmountHistoryOrders - 1; i >= 0; i--) // Чаще ищутся позиции, что закрылись недавно.
if (this.HistoryOrders[i].GetTicket() == Ticket)
{
VIRTUAL_ORDERSELECT_MACROS(this.SelectOrder, this.Orders[this.SelectIndex = MAX_ORDERS - 1]) = this.HistoryOrders[i];
Res = true;
break;
}
return(Res);
}
bool SelectByTicket( const long &Ticket, const int Pool )
{
_VC
return((VIRTUAL_ORDERSELECT_MACROS(this.SelectOrder.GetCloseTimeMsc() && this.SelectOrder,
this.Orders[this.SelectIndex]).GetTicket() == Ticket) ||
((Pool == MODE_TRADES) ? this.SelectByTicketTrades(Ticket) || this.SelectByTicketHistory(Ticket)
: this.SelectByTicketHistory(Ticket) || this.SelectByTicketTrades(Ticket)));
}
bool SelectByPosHistory( const int Index )
{
_VC
const bool Res = (Index >= 0) && (Index < this.AmountHistoryOrders);
if (Res)
VIRTUAL_ORDERSELECT_MACROS(this.SelectOrder, this.Orders[this.SelectIndex = MAX_ORDERS - 1]) = this.HistoryOrders[Index];
return(Res);
}
void Recalculate( void )
{
this.Balance = 0;
for (int i = this.AmountHistoryOrders - 1; i >= 0; i--)
this.Balance += this.HistoryOrders[i].GetFullProfit();
this.Equity = this.Balance;
for (int i = this.AmountOrders - 1; i >= 0; i--)
this.Equity += this.Orders[i].GetFullProfit();
this.ReserveHistoryOrders = this.AmountHistoryOrders;
this.InitStat();
#ifdef VIRTUAL_ORDERSELECT_WITHOUT_COPY
this.Orders[this.SelectIndex = MAX_ORDERS - 1].SetTicket(0);
#else // #ifdef VIRTUAL_ORDERSELECT_WITHOUT_COPY
this.SelectIndex = 0;
this.SelectOrder.SetTicket(0);
#endif // #ifdef VIRTUAL_ORDERSELECT_WITHOUT_COPY #else
ORDER::SetTicketCount((int)this.GetMaxTicket());
#ifdef VIRTUAL_ALTERNATIVE
this.CallChange = false;
this.CallFunc = true;
#endif // #ifdef VIRTUAL_ALTERNATIVE
return;
}
double GetMargin() const
{
double result = 0;
for (int i = 0; i < this.AmountOrders; i++)
{
if (!this.Orders[i].IsClosed() && this.Orders[i].IsPosition())
{
result += this.Orders[i].GetMargin();
}
}
return result;
}
TICKET_TYPE GetMaxTicket( void ) const
{
TICKET_TYPE MaxTicket = this.GetMaxHistoryTicket();
for (int i = AmountOrders - 1; i >= 0; i--)
{
const TICKET_TYPE Ticket = this.Orders[i].GetTicket();
if (Ticket > MaxTicket)
MaxTicket = Ticket;
}
return(MaxTicket);
}
protected:
#ifdef __MQL5__
bool Swap( ORDERS &OtherOrders )
{
const bool Res = this.HISTORYORDERS::Swap(OtherOrders);
if (Res)
{
::ArrayCopy(this.Orders, OtherOrders.Orders, 0, 0, OtherOrders.AmountOrders);
#ifdef VIRTUAL_ORDERSELECT_WITHOUT_COPY
if (OtherOrders.SelectIndex == MAX_ORDERS - 1)
this.Orders[MAX_ORDERS - 1] = OtherOrders.Orders[MAX_ORDERS - 1];
#endif // #ifdef VIRTUAL_ORDERSELECT_WITHOUT_COPY
this.Netting = OtherOrders.Netting;
this.SelectIndex = OtherOrders.SelectIndex;
this.PrevAmountOrders = OtherOrders.PrevAmountOrders;
this.ID = OtherOrders.ID;
#ifdef VIRTUAL_ALTERNATIVE
this.CallFunc = OtherOrders.CallFunc;
this.CallChange = OtherOrders.CallChange; // На текущем тике вызван this.IsChange().
this.PriceBorders = OtherOrders.PriceBorders;
#ifndef VIRTUAL_ORDERSELECT_WITHOUT_COPY
this.SelectTick = OtherOrders.SelectTick;
#endif // #ifndef VIRTUAL_ORDERSELECT_WITHOUT_COPY
#endif // #ifdef VIRTUAL_ALTERNATIVE
this.SymbolBase = OtherOrders.SymbolBase;
this.CurrentTick = OtherOrders.CurrentTick;
this.AmountOrders = OtherOrders.AmountOrders;
this.SymbolOrders = OtherOrders.SymbolOrders;
this.Handle = OtherOrders.Handle;
#ifndef VIRTUAL_ORDERSELECT_WITHOUT_COPY
this.SelectOrder = OtherOrders.SelectOrder;
#endif // #ifndef VIRTUAL_ORDERSELECT_WITHOUT_COPY
this.Balance = OtherOrders.Balance;
this.Equity = OtherOrders.Equity;
// this.MyPointer = OtherOrders.MyPointer;
}
return(Res);
}
#endif // #ifdef __MQL5__
public: // ORDER::IsChange2
ORDERS* MyPointer;
ORDER Orders[MAX_ORDERS]; // sizeof(this.Orders) - только для статик.
protected:
SYMBOL_BASE SymbolBase;
public:
MqlTick CurrentTick;
#ifdef VIRTUAL_ALTERNATIVE
MqlTick SelectTick;
#endif // #ifdef VIRTUAL_ALTERNATIVE
int AmountOrders;
string SymbolOrders;
/*const */int Handle; // VIRTUAL_SELECTORDERS_OBJECT: ORDERS::Swap.
ORDER SelectOrder;
double Balance;
double Equity;
ORDERS( const int iHandle, const datetime StartTime = 0, const string sSymbol = NULL ) :
Handle(iHandle), SelectIndex(0), PrevAmountOrders(0), AmountOrders(0)
{
this.SymbolBase = sSymbol;
this.SymbolOrders = this.SymbolBase.GetSymbol();
this.MyPointer = this.Handle ? &this : NULL;
if (this.Handle < 0) // default SymbolID = 0.
this.SymbolBase.SymbolID = -this.Handle;
this.ID = NULL;
MqlTick Tick = {};
Tick.time_msc = (long)StartTime * 1000;
#ifdef VIRTUAL_ALTERNATIVE
this.CallChange = false;
this.CallFunc = true;
this.SelectTick = Tick;
#endif // #ifdef VIRTUAL_ALTERNATIVE
this.NewTick(Tick);
#ifdef VIRTUAL_ALTERNATIVE
this.CallChange = true; // Для SNAPSHOT::SNAPSHOT.
#endif // #ifdef VIRTUAL_ALTERNATIVE
}
ORDERS( const int iHandle, const datetime StartTime, const SYMBOL_BASE &sSymbol ) :
Handle(iHandle), SelectIndex(0), PrevAmountOrders(0), AmountOrders(0)
{
this.SymbolBase = sSymbol;
this.SymbolOrders = this.SymbolBase.GetSymbol();
if (this.Handle < 0) // default SymbolID = 0.
this.SymbolBase.SymbolID = -this.Handle;
this.ID = NULL;
MqlTick Tick = {};
Tick.time_msc = (long)StartTime * 1000;
#ifdef VIRTUAL_ALTERNATIVE
this.CallChange = false;
this.CallFunc = true;
this.SelectTick = Tick;
#endif // #ifdef VIRTUAL_ALTERNATIVE
this.NewTick(Tick);
}
// DBL_MIN - тип Неттинга и размер баланса берутся с текущего счета.
void Set( const double dBalance = DBL_MIN, const bool bNetting = false )
{
this.Balance = 0;
this.Equity = 0;
this.Netting = (dBalance != DBL_MIN) ? bNetting :
#ifdef __MQL5__
!((ENUM_ACCOUNT_MARGIN_MODE)::AccountInfoInteger(ACCOUNT_MARGIN_MODE) == ACCOUNT_MARGIN_MODE_RETAIL_HEDGING)
#else // __MQL5__
bNetting
#endif //__MQL5__
;
this.AmountOrders = 0;
#ifdef VIRTUAL_ORDERSELECT_WITHOUT_COPY
this.Orders[this.SelectIndex = MAX_ORDERS - 1].SetTicket(0);
#else // #ifdef VIRTUAL_ORDERSELECT_WITHOUT_COPY
this.SelectIndex = 0;
this.SelectOrder.SetTicket(0); // Обходим отсутствие конструктора: ORDER_BASE::ORDER_BASE( void ) : Ticket(0).
#endif // #ifdef VIRTUAL_ORDERSELECT_WITHOUT_COPY #else
this.AmountHistoryOrders = 0;
this.ReserveHistoryOrders = 0; // ::ArrayResize(this.HistoryOrders, RESERVE_HISTORY_ORDERS);
if (dBalance > 0) // Не всегда создание виртуального окружения требует депозита (увеличения счетчика тикетов) - CopyTo, например.
this.OrderDeposit((dBalance != DBL_MIN) ? dBalance : ::AccountInfoDouble(ACCOUNT_BALANCE));
return;
}
bool CopyFrom( const ORDERS* SourceOrders )
{
bool Res = (::CheckPointer(SourceOrders) != POINTER_INVALID);
if (Res)
{
// this = SourceOrders; // https://www.mql5.com/ru/forum/1111/page2480#comment_12038312
this.Netting = SourceOrders.Netting;
this.Balance = SourceOrders.Balance;
this.Equity = SourceOrders.Equity;
this.AmountOrders = ::ArrayCopy(this.Orders, SourceOrders.Orders, 0, 0, SourceOrders.AmountOrders); // Гарантированное копирование - static[].
this.CurrentTick = SourceOrders.CurrentTick;
this.SelectIndex = SourceOrders.SelectIndex;
this.SelectOrder = SourceOrders.SelectOrder;
#ifdef VIRTUAL_ALTERNATIVE
this.SelectTick = this.CurrentTick;
this.CallChange = false;
this.CallFunc = true;
#endif // #ifdef VIRTUAL_ALTERNATIVE
// https://www.mql5.com/ru/forum/331186/page12#comment_17360688
#ifdef __MQL5__
Res = this.HISTORYORDERS::CopyFrom(SourceOrders);
#else // __MQL5__
Res = this.CopyFrom((const HISTORYORDERS*)SourceOrders); // https://www.mql5.com/ru/forum/170953/page33#comment_19199513
#endif // __MQL5__
this.FlagChange |= Res;
ORDER::SetTicketCount((int)SourceOrders.GetMaxTicket());
}
return(Res);
}
bool CopyOrder( const bool WithoutHistory = false )
{
return(VIRTUAL_ORDERSELECT_MACROS(this.SelectOrder, this.Orders[this.SelectIndex = MAX_ORDERS - 1]).Copy(WithoutHistory));
}
// Не желательно объединять клонированные (CopyFrom) виртуальные окружения, т.к. будет повторение тикетов.
// Для Single-варианта.
void operator +=( const ORDERS* SourceOrders )
{
if (::CheckPointer(SourceOrders) != POINTER_INVALID)
{
// SelectIndex и SelectOrder трогать не нужно.
this.Balance += SourceOrders.Balance;
this.Equity += SourceOrders.Equity;
if (SourceOrders.AmountOrders)// https://www.mql5.com/ru/forum/170952/page167#comment_15258160
this.AmountOrders += ::ArrayCopy(this.Orders, SourceOrders.Orders, this.AmountOrders, 0, SourceOrders.AmountOrders);
#ifdef VIRTUAL_ALTERNATIVE
this.CallChange = false;
this.CallFunc = true;
#endif // #ifdef VIRTUAL_ALTERNATIVE
ORDER_BASE NewHistoryOrders[];
const int Size = ::ArrayResize(NewHistoryOrders, this.AmountHistoryOrders + SourceOrders.AmountHistoryOrders);
for (int i = 0, j = 0, k = 0; k < Size; k++)
NewHistoryOrders[k] = (i < this.AmountHistoryOrders) ? (((j == SourceOrders.AmountHistoryOrders) ||
(this.HistoryOrders[i].GetCloseTimeMsc() < SourceOrders.HistoryOrders[j].GetCloseTimeMsc()))
? this.HistoryOrders[i++] : SourceOrders.HistoryOrders[j++])
: SourceOrders.HistoryOrders[j++];
#ifdef __MQL5__
::ArraySwap(this.HistoryOrders, NewHistoryOrders);
#else // __MQL5__
::ArrayCopy(this.HistoryOrders, NewHistoryOrders);
#endif // __MQL5__
this.AmountHistoryOrders = Size;
this.ReserveHistoryOrders = Size;
this.FlagChange = true;
ORDER::SetTicketCount((int)SourceOrders.GetMaxTicket());
this.InitStat();
return;
}
return;
}
bool IsNetting( void ) const
{
_VC
return(this.Netting);
}
void NewTick( const MqlTick &Tick ) // Нет проверки, что Tick.time == Tick.time_msc / 1000
{
_VC
this.CurrentTick = Tick;
#ifndef VIRTUAL_TESTER
#ifndef VIRTUAL_TESTER_MULTI
// https://www.mql5.com/ru/forum/282062/page56#comment_53793273
this.CurrentTick.flags |= this.Handle << 16; // Метка виртуального тика.
#endif // #ifndef VIRTUAL_TESTER_MULTI
#endif // #ifndef VIRTUAL_TESTER
#ifdef TICKS_FORCE_NORMALIZE
this.CurrentTick.bid = ::NormalizeDouble(this.CurrentTick.bid, 8);
this.CurrentTick.ask = ::NormalizeDouble(this.CurrentTick.ask, 8);
#endif // TICKS_FORCE_NORMALIZE
#ifdef TICKS_CORRECT_TIME
if (!(this.CurrentTick.time_msc % 1000))
this.CurrentTick.time_msc++;
#endif // TICKS_CORRECT_TIME
#ifdef VIRTUAL_ALTERNATIVE
this.CallChange = false;
if (this.CallFunc)
{
this.Check();
this.CallFunc = false;
this.PriceBorders.Ready = false;
}
else if (!this.PriceBorders.Ready)
{
this.PriceBorders.Set(this.Orders, this.AmountOrders);
if (this.AmountOrders && this.PriceBorders.IsAccept(this.CurrentTick))
// this.PriceBorders.Ready = !this.Check();
this.Check();
// else
// this.CallChange = false;
}
else if (this.AmountOrders && this.PriceBorders.IsAccept(this.CurrentTick))
// this.PriceBorders.Ready = !
#endif // #ifdef VIRTUAL_ALTERNATIVE
this.Check();
return;
}
void NewTick( const MqlTick &Ticks[], const STRATEGY Strategy = NULL )
{
const int Size = ::ArraySize(Ticks);
if (Strategy != NULL)
for (int i = 0; (i < Size) && (!::IsStopped()); i++)
{
this.NewTick(Ticks[i]);
Strategy();
}
else
for (int i = 0; i < Size; i++)
this.NewTick(Ticks[i]);
return;
}
// Должна вызываться только так: VIRTUAL::SelectOrders.NewTickMulti(Ticks, StrategyMulti)
// Только для Single-окружения.
// Причина написания: https://www.mql5.com/ru/forum/462835/page24#comment_52772777
void NewTickMulti( const MqlTick &Ticks[], const STRATEGY_MULTI StrategyMulti = NULL )
{
if (this.IsSingle())
{
const int Size = ::ArraySize(Ticks);
if (StrategyMulti != NULL)
for (int i = 0; (i < Size) && (!::IsStopped()); i++)
{
this.NewTick(Ticks[i]);
StrategyMulti(this.SymbolOrders, this.SymbolBase.SymbolID); // Предполагается, что VIRTUAL::SelectOrders == &this.
}
else
for (int i = 0; i < Size; i++)
this.NewTick(Ticks[i]);
}
return;
}
bool OrderDeposit( const double Deposit )
{
if (Deposit)
{
ORDER Order;
Order.Deposit(Deposit, this.CurrentTick);
this.AddHistoryOrder(Order);
this.Balance += Deposit;
this.Equity += Deposit;
}
return((bool)Deposit);
}
virtual TICKET_TYPE OrderSend( const string&, const int &Type, const double &dVolume, const double &Price,
const int &SlipPage, const double &SL, const double &TP,
const string &comment, const MAGIC_TYPE &magic, const datetime &dExpiration )
{
_VC
TICKET_TYPE Res = -1;
if (this.Orders[this.AmountOrders].Create(this.SymbolBase, (ENUM_ORDER_TYPE)Type, dVolume,
Price, SL, TP, magic, comment, this.CurrentTick))
{
Res = this.Orders[this.AmountOrders].GetTicket();
this.FlagChange = true; // https://www.mql5.com/ru/forum/282062/page72#comment_56065971
#ifdef VIRTUAL_ALTERNATIVE
this.CallFunc = true;
#endif // #ifdef VIRTUAL_ALTERNATIVE
if (this.Orders[this.AmountOrders++].IsPosition())
{
if (this.Netting)
this.CloseBy();
this.IsChange();
}
else
this.Check();
}
return(Res);
}
virtual bool Stop( void )
{
#ifdef VIRTUAL_ALTERNATIVE
if (!this.CallChange)
this.IsChange();
#endif // #ifdef VIRTUAL_ALTERNATIVE
for (int i = 0; i < this.AmountOrders; i++)
this.Orders[i].Stop(this.CurrentTick);
this.IsChange();
#ifdef VIRTUAL_ALTERNATIVE
this.CallFunc = true;
#endif // #ifdef VIRTUAL_ALTERNATIVE
// Здесь можно ужать массив истории торгов для экономии памяти.
return(true);
}
bool OrderSelect( const long Index, const int &Select, const int &Pool )
{
_VC
return((Select == SELECT_BY_POS) ?
((Pool == MODE_TRADES) ? this.SelectByPos((int)Index) : this.SelectByPosHistory((int)Index)) :
this.SelectByTicket(Index, Pool));
}
bool OrderSelect( const long Index, const int &Select )
{
_VC
return((Select == SELECT_BY_POS) ? this.SelectByPos((int)Index) : this.SelectByTicket(Index, MODE_TRADES));
}
virtual bool OrderClose( const long Ticket, const double &dLots, const double &Price )
{
_VC
bool Res = false;
const int Pos = this.GetIndex(Ticket);
if (Pos >= 0)
{
#ifdef VIRTUAL_ALTERNATIVE
if (!this.CallChange)
this.IsChange();
this.CallFunc = true;
#endif // #ifdef VIRTUAL_ALTERNATIVE
const double LeftLots = this.Orders[Pos].Close(dLots, this.CurrentTick);
Res = this.Orders[Pos].IsClosed();
if (Res)
{
if (LeftLots)
{
this.Orders[this.AmountOrders] = this.Orders[Pos];
this.Orders[this.AmountOrders++].SetLots(LeftLots);
}
this.IsChange();
}
}
return(Res);
}
virtual bool OrderModify( const long Ticket, const double &Price, const double &SL, const double &TP, const datetime &Expiration )
{
_VC
const int Pos = this.GetIndex(Ticket);
const bool Res = (Pos >= 0) ? this.Orders[Pos].Modify(Price, SL, TP, this.CurrentTick) : false;
if (Res)
{
this.FlagChange = true;
this.Check();
#ifdef VIRTUAL_ALTERNATIVE
this.CallFunc = true;
#endif // #ifdef VIRTUAL_ALTERNATIVE
}
return(Res);
}
virtual bool OrderCloseBy( const long Ticket, const long Opposite )
{
bool Res = false;
const int Pos1 = (Ticket != Opposite) ? this.GetIndex(Ticket) : -1;
if (Pos1 >= 0)
{
const int Pos2 = this.GetIndex(Opposite);
if (Pos2 >= 0)
{
Res = this.Orders[Pos1].CloseBy(this.Orders[Pos2], this.CurrentTick);
if (Res)
{
this.IsChange();
#ifdef VIRTUAL_ALTERNATIVE
this.CallFunc = true;
#endif // #ifdef VIRTUAL_ALTERNATIVE
}
}
}
return(Res);
}
virtual bool OrderDelete( const long Ticket )
{
_VC
bool Res = false;
const int Pos = this.GetIndex(Ticket);
if (Pos >= 0)
{
Res = this.Orders[Pos].Delete(this.CurrentTick);
if (Res)
{
this.IsChange();
#ifdef VIRTUAL_ALTERNATIVE
this.CallFunc = true;
#endif // #ifdef VIRTUAL_ALTERNATIVE
}
}
return(Res);
}
int OrdersTotal2( void ) const // 2 - MT4Orders
{
return(this.AmountOrders);
}
int OrdersHistoryTotal( void ) const
{
return(this.AmountHistoryOrders);
}
#define ORDERFUNCTION(NAME,T) \
T Order##NAME( void ) const \
{ \
_VC return(VIRTUAL_ORDERSELECT_MACROS(this.SelectOrder, \
this.Orders[this.SelectIndex]).Get##NAME()); \
}
ORDERFUNCTION(Ticket, TICKET_TYPE)
ORDERFUNCTION(Type, int)
ORDERFUNCTION(Lots, double)
ORDERFUNCTION(OpenPrice, double)
ORDERFUNCTION(OpenTimeMsc, long)
ORDERFUNCTION(OpenTime, datetime)
ORDERFUNCTION(StopLoss, double)
ORDERFUNCTION(TakeProfit, double)
ORDERFUNCTION(CloseTimeMsc, long)
ORDERFUNCTION(CloseTime, datetime)
ORDERFUNCTION(Expiration, datetime)
ORDERFUNCTION(MagicNumber, MAGIC_TYPE)
ORDERFUNCTION(Commission, double)
ORDERFUNCTION(Swap, double)
ORDERFUNCTION(SymbolID, int)
// ORDERFUNCTION(Symbol, string)
ORDERFUNCTION(Comment, string)
ORDERFUNCTION(OpenPriceRequest, double)
ORDERFUNCTION(ClosePriceRequest, double)
ORDERFUNCTION(OpenReason, ENUM_DEAL_REASON)
ORDERFUNCTION(CloseReason, ENUM_DEAL_REASON)
#undef ORDERFUNCTION
double OrderProfit( void )
{
_VC
#ifdef VIRTUAL_ALTERNATIVE
if (!VIRTUAL_ORDERSELECT_MACROS(this.SelectOrder, this.Orders[this.SelectIndex]).CloseTimeMsc)
{
if (!this.CallChange)
this.IsChange();
// this.CallFunc = true;
#ifndef VIRTUAL_ORDERSELECT_WITHOUT_COPY
if (this.SelectTick.time)
{
this.SelectOrder.IsChange(this.SelectTick);
this.SelectTick.time = 0;
}
#endif // #ifndef VIRTUAL_ORDERSELECT_WITHOUT_COPY
}
#endif // #ifdef VIRTUAL_ALTERNATIVE
return(VIRTUAL_ORDERSELECT_MACROS(this.SelectOrder, this.Orders[this.SelectIndex]).GetProfit());
}
double OrderClosePrice( void )
{
_VC
#ifdef VIRTUAL_ALTERNATIVE
if (!VIRTUAL_ORDERSELECT_MACROS(this.SelectOrder, this.Orders[this.SelectIndex]).CloseTimeMsc)
{
if (!this.CallChange)
this.IsChange();
// this.CallFunc = true;
#ifndef VIRTUAL_ORDERSELECT_WITHOUT_COPY
if (this.SelectTick.time)
{
this.SelectOrder.IsChange(this.SelectTick);
this.SelectTick.time = 0;
}
#endif // #ifndef VIRTUAL_ORDERSELECT_WITHOUT_COPY
}
#endif // #ifdef VIRTUAL_ALTERNATIVE
return(VIRTUAL_ORDERSELECT_MACROS(this.SelectOrder, this.Orders[this.SelectIndex]).GetClosePrice());
}
virtual string OrderSymbol( void ) const
{
_VC return(VIRTUAL_ORDERSELECT_MACROS(this.SelectOrder, this.Orders[this.SelectIndex]).GetCloseTimeMsc()
? VIRTUAL_ORDERSELECT_MACROS(this.SelectOrder, this.Orders[this.SelectIndex]).GetSymbol()
: this.SymbolOrders);
}
TICKET_TYPE OrderTicketOpen( void ) const
{
return(VIRTUAL_ORDERSELECT_MACROS(this.SelectOrder, this.Orders[this.SelectIndex]).GetTicket());
}
TICKET_TYPE OrderTicketID( void ) const
{
return(VIRTUAL_ORDERSELECT_MACROS(this.SelectOrder, this.Orders[this.SelectIndex]).GetTicket());
}
int OrderDealsAmount( void ) const
{
return(0);
}
double OrderLotsOpen( void ) const
{
return(VIRTUAL_ORDERSELECT_MACROS(this.SelectOrder, this.Orders[this.SelectIndex]).GetLots());
}
void OrderPrint( void )
{
#ifdef VIRTUAL_ALTERNATIVE
if (!VIRTUAL_ORDERSELECT_MACROS(this.SelectOrder, this.Orders[this.SelectIndex]).CloseTimeMsc)
{
if (!this.CallChange)
this.IsChange();
// this.CallFunc = true;
#ifndef VIRTUAL_ORDERSELECT_WITHOUT_COPY
if (this.SelectTick.time)
{
this.SelectOrder.IsChange(this.SelectTick);
this.SelectTick.time = 0;
}
#endif // #ifndef VIRTUAL_ORDERSELECT_WITHOUT_COPY
}
#endif // #ifdef VIRTUAL_ALTERNATIVE
VIRTUAL_ORDERSELECT_MACROS(this.SelectOrder, this.Orders[this.SelectIndex]).GetPrint(this.CurrentTick.time);
return;
}
bool OrderComment( const string &NewComment )
{
_VC
const long Ticket = VIRTUAL_ORDERSELECT_MACROS(this.SelectOrder, this.Orders[this.SelectIndex]).GetTicket();
const int Index = this.GetIndex(Ticket);
const bool Res = (Index >= 0);
if (Res)
this.Orders[this.SelectIndex].SetComment(NewComment);
return(Res);
}
bool TesterDeposit( const double Deposit )
{
return(this.OrderDeposit(Deposit));
}
bool TesterWithdrawal( const double Withdraw )
{
return(this.TesterDeposit(-Withdraw));
}
virtual double AccountBalance( void ) const
{
return(::NormalizeDouble(this.Balance, SYMBOL_BASE::DigitsCurrency));
}
virtual double AccountEquity( void )
{
#ifdef VIRTUAL_ALTERNATIVE
if (!this.CallChange)
this.IsChange();
// this.CallFunc = true;
#endif // #ifdef VIRTUAL_ALTERNATIVE
return(::NormalizeDouble(this.Equity, SYMBOL_BASE::DigitsCurrency));
}
virtual double AccountProfit( void )
{
#ifdef VIRTUAL_ALTERNATIVE
if (!this.CallChange)
this.IsChange();
// this.CallFunc = true;
#endif // #ifdef VIRTUAL_ALTERNATIVE
return(::NormalizeDouble(this.Equity - this.Balance, SYMBOL_BASE::DigitsCurrency));
}
datetime TimeCurrent( void ) const
{
return(this.CurrentTick.time);
}
datetime TimeCurrent( MqlDateTime &StructTime ) const
{
::TimeToStruct(this.CurrentTick.time, StructTime);
return(this.CurrentTick.time);
}
datetime TimeTradeServer( void ) const
{
return(this.CurrentTick.time);
}
datetime TimeTradeServer( MqlDateTime &StructTime ) const
{
::TimeToStruct(this.CurrentTick.time, StructTime);
return(this.CurrentTick.time);
}
virtual bool SymbolInfoTick( const string &Symb, MqlTick &Tick )
{
_VC
Tick = this.CurrentTick;
return(true);
}
// Где-то логично этому находиться в SYMBOL_BASE.
virtual double SymbolInfoDouble( const string &Symb, const ENUM_SYMBOL_INFO_DOUBLE Property )
{
_VC
// Если OrderSymbol() != SymbolBase (в Virtual-историю можно писать любые символы), то будут неправильные данные.
switch (Property)
{
case SYMBOL_BID:
return(this.CurrentTick.bid);
case SYMBOL_ASK:
return(this.CurrentTick.ask);
case SYMBOL_POINT:
return(this.SymbolBase.Point);
case SYMBOL_TRADE_TICK_VALUE:
case SYMBOL_TRADE_TICK_VALUE_LOSS:
case SYMBOL_TRADE_TICK_VALUE_PROFIT:
return(this.SymbolBase.TickValue * this.SymbolBase.TickSize);
case SYMBOL_TRADE_TICK_SIZE:
return(this.SymbolBase.TickSize);
}
return(::SymbolInfoDouble(Symb, Property));
/*
return((Property == SYMBOL_BID) ? this.CurrentTick.bid
: ((Property == SYMBOL_ASK) ? this.CurrentTick.ask : ::SymbolInfoDouble(Symb, Property)));
*/
}
virtual bool SymbolInfoDouble( const string &Symb, const ENUM_SYMBOL_INFO_DOUBLE Property, double &Value )
{
_VC
switch (Property)
{
case SYMBOL_BID:
case SYMBOL_ASK:
case SYMBOL_POINT:
case SYMBOL_TRADE_TICK_VALUE:
case SYMBOL_TRADE_TICK_VALUE_LOSS:
case SYMBOL_TRADE_TICK_VALUE_PROFIT:
case SYMBOL_TRADE_TICK_SIZE:
Value = ORDERS::SymbolInfoDouble(Symb, Property); // https://www.mql5.com/ru/forum/478178/page23#comment_56068453
return(true);
}
return(::SymbolInfoDouble(Symb, Property, Value));
/*
const bool Res = (Property == SYMBOL_BID) || (Property == SYMBOL_ASK);
if (Res)
{
if (Property == SYMBOL_BID)
Value = this.CurrentTick.bid;
else
Value = this.CurrentTick.ask;
}
return(Res ? true : ::SymbolInfoDouble(Symb, Property, Value));
*/
}
virtual long SymbolInfoInteger( const string &Symb, const ENUM_SYMBOL_INFO_INTEGER Property )
{
_VC
// Если OrderSymbol() != SymbolBase (в Virtual-историю можно писать любые символы), то будут неправильные данные.
switch (Property)
{
case SYMBOL_TIME:
return(this.CurrentTick.time);
case SYMBOL_TIME_MSC:
return(this.CurrentTick.time_msc);
case SYMBOL_DIGITS:
return(this.SymbolBase.Digits);
case SYMBOL_SPREAD:
return((long)((this.CurrentTick.ask - this.CurrentTick.bid) / this.SymbolBase.Point + 0.1));
case SYMBOL_TRADE_MODE:
return(SYMBOL_TRADE_MODE_FULL);
}
return(::SymbolInfoInteger(Symb, Property));
/*
return((Property == SYMBOL_TIME) ? this.CurrentTick.time
: ((Property == SYMBOL_SPREAD) ? (long)((this.CurrentTick.ask - this.CurrentTick.bid) / this.SymbolBase.Point + 0.1) : ::SymbolInfoInteger(Symb, Property)));
*/
}
virtual bool SymbolInfoInteger( const string &Symb, const ENUM_SYMBOL_INFO_INTEGER Property, long &Value )
{
_VC
switch (Property)
{
case SYMBOL_TIME:
case SYMBOL_TIME_MSC:
case SYMBOL_DIGITS:
case SYMBOL_SPREAD:
case SYMBOL_TRADE_MODE:
Value = ORDERS::SymbolInfoInteger(Symb, Property); // https://www.mql5.com/ru/forum/478178/page23#comment_56068453
return(true);
}
return(::SymbolInfoInteger(Symb, Property, Value));
/*
const bool Res = (Property == SYMBOL_TIME) || (Property == SYMBOL_SPREAD);
if (Res)
{
if (Property == SYMBOL_TIME) // SYMBOL_TIME_MSC специально берется с реального окружения - нужно для ByPass.mqh.
Value = this.CurrentTick.time;
else
Value = (long)((this.CurrentTick.ask - this.CurrentTick.bid) / this.SymbolBase.Point + 0.1);
}
return(Res ? true : ::SymbolInfoInteger(Symb, Property, Value));
*/
}
long AccountInfoInteger( const ENUM_ACCOUNT_INFO_INTEGER Property ) const
{
#ifdef __MQL5__
return((Property == ACCOUNT_MARGIN_MODE) ? (this.Netting ? ACCOUNT_MARGIN_MODE_RETAIL_NETTING : ACCOUNT_MARGIN_MODE_RETAIL_HEDGING) : ::AccountInfoInteger(Property));
#else // __MQL5__
return(::AccountInfoInteger(Property));
#endif // __MQL5__
}
double AccountInfoDouble( const ENUM_ACCOUNT_INFO_DOUBLE Property )
{
double Res;
switch (Property)
{
case ACCOUNT_BALANCE:
Res = this.AccountBalance();
break;
case ACCOUNT_PROFIT:
Res = this.AccountProfit();
break;
case ACCOUNT_EQUITY:
Res = this.AccountEquity();
break;
case ACCOUNT_MARGIN:
Res = this.GetMargin();
break;
case ACCOUNT_MARGIN_FREE:
Res = this.AccountEquity() - this.GetMargin();
break;
default:
Res = ::AccountInfoDouble(Property);
}
return(Res);
}
string ToString( int LastHistoryOrders = 0, const bool Pending = true )
{
string Str = ORDERS::TickToString(this.CurrentTick) + "\n\n";
#ifdef VIRTUAL_ALTERNATIVE
if (!this.CallChange)
this.IsChange();
this.CallFunc = true;
#endif // #ifdef VIRTUAL_ALTERNATIVE
for (int i = 0; i < this.AmountOrders; i++)
Str += this.Orders[i].ToString(this.CurrentTick.time) + "\n";
Str += (this.AmountOrders ? "\n" : NULL) + "Balance = " + ::DoubleToString(this.Balance, 2) +
", Equity = " + ::DoubleToString(this.Equity, 2) +
(this.AmountHistoryOrders && (this.HistoryOrders[0].GetType() == OP_BALANCE)
? ", Profit = " + ::DoubleToString(this.Equity - this.HistoryOrders[0].GetProfit(), 2) : NULL) +
(this.AmountHistoryOrders ? ", " + ::TimeToString(this.HistoryOrders[0].GetOpenTime(), TIME_DATE) + " - " + (string)this.TimeCurrent() : NULL);
if (LastHistoryOrders > 0)
for (int i = this.AmountHistoryOrders - 1; (i >= 0) && LastHistoryOrders; i--)
if (Pending || (this.HistoryOrders[i].GetType() <= OP_SELL) || (this.HistoryOrders[i].GetType() == OP_BALANCE))
{
Str += "\n" + this.HistoryOrders[i].ToString(this.CurrentTick.time);
LastHistoryOrders--;
}
return(Str);
}
int GetMemoryUsed( void ) const
{
return(sizeof(this) + ::ArraySize(this.HistoryOrders) * sizeof(ORDER_BASE) + sizeof(this.Orders)); // sizeof(this.Orders) - только для статик.
}
const ORDER_BASE GetOrder( void ) const
{
return(VIRTUAL_ORDERSELECT_MACROS(this.SelectOrder, this.Orders[this.SelectIndex]));
}
bool AddOrder( const ORDER_BASE &Order )
{
if (Order.GetCloseTime())
{
this.AddHistoryOrder(Order);
this.Balance += Order.GetProfit();
this.Equity += Order.GetProfit();
}
else
{
this.Orders[this.AmountOrders] = Order;
if (this.Orders[this.AmountOrders++].IsPosition())
{
if (this.Netting)
this.CloseBy();
this.IsChange();
}
else
this.Check();
}
ORDER::SetTicketCount((int)Order.GetTicket());
return(true);
}
bool CalcSwaps( const double &SwapShort, const double &SwapLong, const datetime &RolloverTime, const int &Rollover3Days )
{
const double ChangeSwaps = HISTORYORDERS::CalcSwaps(SwapShort, SwapLong, RolloverTime, Rollover3Days);
this.Balance += ChangeSwaps;
this.Equity += ChangeSwaps;
this.FlagChange |= (bool)ChangeSwaps;
return(true);
}
#ifndef __MQL5__
// sizeof(MqlTick) в MT4/5 отличается
struct MqlTickMT5 : public MqlTick
{
double volume_real; // https://www.mql5.com/ru/forum/373986/page7#comment_24982259
};
#endif // #ifndef __MQL5__
uint Save ( const int FileHandle )
{
uint Res = 0;
if (FileHandle != INVALID_HANDLE)
{
#ifdef VIRTUAL_ALTERNATIVE
if (!this.CallChange)
this.IsChange();
this.CallFunc = true;
#endif // #ifdef VIRTUAL_ALTERNATIVE
Res += ::FileWriteInteger(FileHandle, this.AmountOrders * sizeof(ORDER));
if (this.AmountOrders)
Res += ::FileWriteArray(FileHandle, this.Orders, 0, this.AmountOrders) * sizeof(ORDER);
Res += ::FileWriteInteger(FileHandle, this.AmountHistoryOrders * sizeof(ORDER_BASE));
if (this.AmountHistoryOrders)
Res += ::FileWriteArray(FileHandle, this.HistoryOrders, 0, this.AmountHistoryOrders) * sizeof(ORDER_BASE);
Res += ::FileWriteInteger(FileHandle, sizeof(this.Netting));
Res += ::FileWriteInteger(FileHandle, this.Netting, sizeof(this.Netting));
#ifdef __MQL5__
Res += ::FileWriteInteger(FileHandle, sizeof(this.CurrentTick));
Res += ::FileWriteStruct(FileHandle, this.CurrentTick);
#else // #ifdef __MQL5__
MqlTickMT5 CurrentTickTmp = {};
CurrentTickTmp = this.CurrentTick;
Res += ::FileWriteInteger(FileHandle, sizeof(CurrentTickTmp));
Res += ::FileWriteStruct(FileHandle, CurrentTickTmp);
#endif // #ifdef __MQL5__ #else
uchar Bytes[];
Res += ::FileWriteInteger(FileHandle, ::StringToCharArray(this.ID, Bytes));
Res += ::FileWriteArray(FileHandle, Bytes);
Res += ::FileWriteInteger(FileHandle, sizeof(this.SymbolBase));
Res += ::FileWriteStruct(FileHandle, this.SymbolBase);
}
return(Res);
}
uint Save( const string FileName, const bool FileCommon = false )
{
uint Res = 0;
const int FileHandle = ::FileOpen(FileName, FILE_WRITE | FILE_BIN | (FileCommon ? FILE_COMMON : 0));
if (FileHandle != INVALID_HANDLE)
{
Res = this.Save(FileHandle);
::FileClose(FileHandle);
}
return(Res);
}
bool Load( const int FileHandle )
{
const bool Res = (FileHandle != INVALID_HANDLE);
if (Res)
{
if ((bool)(this.AmountOrders = ::FileReadInteger(FileHandle) / sizeof(ORDER)))
::FileReadArray(FileHandle, this.Orders, 0, this.AmountOrders);
if ((bool)(this.AmountHistoryOrders = ::FileReadInteger(FileHandle) / sizeof(ORDER_BASE)))
::FileReadArray(FileHandle, this.HistoryOrders, 0, this.AmountHistoryOrders);
::FileReadInteger(FileHandle);
this.Netting = ::FileReadInteger(FileHandle, sizeof(this.Netting));
::FileReadInteger(FileHandle);
#ifdef __MQL5__
::FileReadStruct(FileHandle, this.CurrentTick);
#else // #ifdef __MQL5__
MqlTickMT5 CurrentTickTmp;
::FileReadStruct(FileHandle, CurrentTickTmp);
this.CurrentTick = CurrentTickTmp;
#endif // #ifdef __MQL5__ #else
const int AmountBytes = ::FileReadInteger(FileHandle);
uchar Bytes[];
if (AmountBytes)
::FileReadArray(FileHandle, Bytes, 0, AmountBytes);
this.ID = AmountBytes ? ::CharArrayToString(Bytes) : NULL; // https://www.mql5.com/ru/forum/1111/page2323#comment_9414672
::FileReadInteger(FileHandle);
::FileReadStruct(FileHandle, this.SymbolBase);
this.SymbolOrders = this.SymbolBase.GetSymbol();
this.Recalculate();
}
return(Res);
}
bool Load( const string FileName, const bool FileCommon = false )
{
const int FileHandle = ::FileOpen(FileName, FILE_READ | FILE_BIN | (FileCommon ? FILE_COMMON : 0));
const bool Res = ORDERS::Load(FileHandle);
if (Res)
::FileClose(FileHandle);
return(Res);
}
#ifdef __TYPETOBYTES__
template <typename T>
uint Save( T &Array[], const int LastHistoryOrders = INT_MAX ) const
{
#ifdef VIRTUAL_ALTERNATIVE
if (!this.CallChange)
this.IsChange();
this.CallFunc = true;
#endif // #ifdef VIRTUAL_ALTERNATIVE
::ArrayFree(Array);
CONTAINER<T> Container;
ORDER OrdersTmp[]; // Чтобы не записывать весь массив, а только полезный кусок
::ArrayCopy(OrdersTmp, this.Orders, 0, 0, this.AmountOrders);
Container[0] = OrdersTmp;
::ArrayFree(OrdersTmp);
ORDER_BASE HistoryOrdersTmp[]; // Чтобы не записывать весь массив, а только полезный кусок
if ((LastHistoryOrders >= 0) && (LastHistoryOrders < this.AmountHistoryOrders - 1) &&
::ArrayResize(HistoryOrdersTmp, LastHistoryOrders + 1) &&
this.GetReduceOrder(HistoryOrdersTmp[0], this.AmountHistoryOrders - LastHistoryOrders - 1))
::ArrayCopy(HistoryOrdersTmp, this.HistoryOrders, 1, this.AmountHistoryOrders - LastHistoryOrders, LastHistoryOrders);
else
::ArrayCopy(HistoryOrdersTmp, this.HistoryOrders, 0, 0, this.AmountHistoryOrders);
Container[1] = HistoryOrdersTmp;
::ArrayFree(HistoryOrdersTmp);
bool NettingTmp = this.Netting; // Метод обязан быть const, а переменная - не const. Чтобы добиться однозначной сигнатуры в CONTAINER.
Container[2] = NettingTmp;
#ifdef __MQL5__
MqlTick CurrentTickTmp = this.CurrentTick; // Метод обязан быть const, а переменная - не const. Чтобы добиться однозначной сигнатуры в CONTAINER.
#else // #ifdef __MQL5__
MqlTickMT5 CurrentTickTmp = this.CurrentTick;
#endif // #ifdef __MQL5__ #else
Container[3] = CurrentTickTmp;
Container[4] = this.ID; // Метод обязан быть const, а переменная - не const. Чтобы добиться однозначной сигнатуры в CONTAINER.
Container[5] = this.SymbolBase; // Метод обязан быть const, а переменная - не const. Чтобы добиться однозначной сигнатуры в CONTAINER.
#ifdef __MQL5__
return(::ArraySwap(Array, Container.Data) ? ::ArraySize(Array) : 0);
#else // #ifdef __MQL5__
return(::ArrayCopy(Array, Container.Data));
#endif // #ifdef __MQL5__ #else
}
template <typename T>
bool Load( const T &Array[] )
{
CONTAINER<T> Container;
const bool Res = (::ArrayCopy(Container.Data, Array) > 0);
if (Res)
{
ORDER OrdersTmp[];
this.AmountOrders = Container[0].Get(OrdersTmp); // Статический this.Orders будет возвращать неправильный this.AmountOrders.
::ArrayCopy(this.Orders, OrdersTmp);
::ArrayFree(OrdersTmp);
::ArrayFree(this.HistoryOrders);
this.AmountHistoryOrders = Container[1].Get(this.HistoryOrders);
Container[2].Get(this.Netting);
#ifdef __MQL5__
Container[3].Get(this.CurrentTick);
#else // #ifdef __MQL5__
this.CurrentTick = Container[3].Get<MqlTickMT5>();
#endif // #ifdef __MQL5__ #else
this.ID = Container[4].Get<string>();
this.SymbolBase = Container[5].Get<SYMBOL_BASE>();
this.SymbolOrders = this.SymbolBase.GetSymbol();
this.Recalculate();
}
return(Res);
}
#endif // #ifdef __TYPETOBYTES__
bool SetID( const string NewID )
{
this.ID = NewID;
return(true);
}
string GetID( void ) const
{
return(this.ID);
}
bool ReduceHistory( const int LastHistoryOrders, const bool ChangeHistory = false )
{
return(this.GetReduceOrder(VIRTUAL_ORDERSELECT_MACROS(this.SelectOrder, this.Orders[this.SelectIndex = MAX_ORDERS - 1]),
this.AmountHistoryOrders - LastHistoryOrders - 1, ChangeHistory));
}
virtual void MultiTick( const MqlTick &Ticks[] )
{
this.NewTick(Ticks[0]);
return;
}
virtual void MultiTick( void )
{
// static MqlTick Tick = {}; // https://www.mql5.com/ru/forum/170952/page250#comment_52908635
MqlTick Tick;
if (::SymbolInfoTick(this.SymbolOrders, Tick))
this.NewTick(Tick);
return;
}
virtual void MultiTick( const uint &Index, const MqlTick &Tick )
{
if (!Index)
this.NewTick(Tick);
return;
}
virtual void MultiTick( const uint &Index )
{
if (!Index)
this.MultiTick();
return;
}
// https://www.mql5.com/ru/forum/455977/page29#comment_51010450
bool IsAmountChanged( void )
{
const bool Res = (this.AmountOrders != this.PrevAmountOrders);
if (Res)
this.PrevAmountOrders = this.AmountOrders;
return(Res);
}
// https://www.mql5.com/ru/forum/455977/page29#comment_51010450
bool TickCalc( const MqlTick &Tick, ORDERS &objOrders )
{
this.NewTick(Tick);
if (this.AmountHistoryOrders)
this.MoveHistory(objOrders);
return(this.IsAmountChanged());
}
// https://www.mql5.com/ru/forum/455977/page29#comment_51010450
bool TickCalc( ORDERS &objOrders )
{
// static MqlTick Tick; // https://www.mql5.com/ru/forum/170952/page250#comment_52908635
MqlTick Tick;
return(::SymbolInfoTick(this.SymbolOrders, Tick) && this.TickCalc(Tick, objOrders));
}
// https://www.mql5.com/ru/forum/455977/page29#comment_51010450
virtual bool SelectByPos( const int Index )
{
_VC
const bool Res = (Index >= 0) && (Index < this.AmountOrders);
if (Res)
{
this.SelectIndex = Index;
#ifndef VIRTUAL_ORDERSELECT_WITHOUT_COPY
this.SelectOrder = this.Orders[this.SelectIndex];
#ifdef VIRTUAL_ALTERNATIVE
if (this.CallChange)
this.SelectTick.time = 0;
else
this.SelectTick = this.CurrentTick;
#endif // #ifdef VIRTUAL_ALTERNATIVE
#endif // #ifndef VIRTUAL_ORDERSELECT_WITHOUT_COPY
}
return(Res);
}
virtual bool SelectByTicketTrades( const long &Ticket )
{
_VC
const int Index = this.GetIndex(Ticket);
const bool Res = (Index >= 0);
if (Res)
{
this.SelectIndex = Index;
#ifndef VIRTUAL_ORDERSELECT_WITHOUT_COPY
this.SelectOrder = this.Orders[this.SelectIndex];
#ifdef VIRTUAL_ALTERNATIVE
if (this.CallChange)
this.SelectTick.time = 0;
else
this.SelectTick = this.CurrentTick;
#endif // #ifdef VIRTUAL_ALTERNATIVE
#endif // #ifndef VIRTUAL_ORDERSELECT_WITHOUT_COPY
}
return(Res);
}
bool IsSingle( void ) const
{
return(this.GetAmountSymbols() == 1);
}
virtual int GetAmountSymbols( void ) const
{
return(1);
}
virtual int GetSymbols( string &Symbols[] ) const
{
const int Res = ::ArrayResize(Symbols, 1);
Symbols[0] = this.SymbolOrders;
return(Res);
}
virtual string GetSymbolByIndex( const int &Index ) const
{
return(!Index ? this.SymbolOrders : NULL);
}
virtual ORDERS* GetSubOrders( const int &Index )
{
return(!Index ? &this : NULL);
}
void ToNull( void )
{
this.Handle = 0;
this.MyPointer = NULL;
return;
}
void operator =( ORDERS* OtherOrders )
{
if ((OtherOrders != this.MyPointer) && (OtherOrders != &this))
{
if (this.Handle)
this.MyPointer.Swap(this);
if (OtherOrders == NULL)
this.ToNull();
#ifdef __MQL5__
else
this.Swap(this.MyPointer = OtherOrders);
#endif // #ifdef __MQL5__
}
return;
}
};
#undef VIRTUAL_ORDERSELECT_MACROS