#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 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 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 bool Load( const T &Array[] ) { CONTAINER 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(); #endif // #ifdef __MQL5__ #else this.ID = Container[4].Get(); this.SymbolBase = Container[5].Get(); 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