1920 lines
		
	
	
		
			No EOL
		
	
	
		
			106 KiB
		
	
	
	
		
			MQL5
		
	
	
	
	
	
			
		
		
	
	
			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 |