661 lines
		
	
	
		
			No EOL
		
	
	
		
			34 KiB
		
	
	
	
		
			MQL5
		
	
	
	
	
	
			
		
		
	
	
			661 lines
		
	
	
		
			No EOL
		
	
	
		
			34 KiB
		
	
	
	
		
			MQL5
		
	
	
	
	
	
#property strict
 | 
						|
 | 
						|
#include "Order_Base.mqh"
 | 
						|
 | 
						|
// #define ORDER_COMMISSION 0 // Задание комиссии (OrderCommission() = OrderLots() * (ORDER_COMMISSION)), включая динамический вариант.
 | 
						|
// #define ORDER_CURRENCY_DIGITS 2 // Задание Digits для вычисления профита/комиссии/свопа при помещении в историю торгов.
 | 
						|
 | 
						|
struct ORDER : public ORDER_BASE
 | 
						|
{
 | 
						|
#ifdef __MQL5__
 | 
						|
  using ORDER_BASE::operator=; // https://www.mql5.com/ru/forum/492248/page23#comment_57855997
 | 
						|
#endif // #ifdef __MQL5__
 | 
						|
 | 
						|
private:
 | 
						|
  static int TicketCount;
 | 
						|
 | 
						|
  int GetNewTicket( void ) const
 | 
						|
  {
 | 
						|
    return(++ORDER::TicketCount);
 | 
						|
  }
 | 
						|
 | 
						|
  bool Reset( void )
 | 
						|
  {
 | 
						|
    this.StopLoss = -1; // Чтобы при создании ордера сработало условие (this.StopLoss != SL) в Check.
 | 
						|
 | 
						|
    return(true);
 | 
						|
  }
 | 
						|
 | 
						|
  bool Check( double &Price, double &SL, double &TP, const MqlTick &Tick ) const
 | 
						|
  {
 | 
						|
    _VC
 | 
						|
    bool Res = false;
 | 
						|
 | 
						|
    if (SL && (this.StopLoss != SL))
 | 
						|
      SL = ::NormalizeDouble(SL, this.Digits);
 | 
						|
 | 
						|
    if (TP && (this.TakeProfit != TP))
 | 
						|
      TP = ::NormalizeDouble(TP, this.Digits);
 | 
						|
 | 
						|
    switch (this.Type)
 | 
						|
    {
 | 
						|
    case ORDER_TYPE_BUY:
 | 
						|
      Res = ((this.StopLoss != SL) || (this.TakeProfit != TP)) &&
 | 
						|
            (SL >= 0) && (SL <= Tick.bid) &&
 | 
						|
            ((!TP) || (TP >= Tick.bid));
 | 
						|
 | 
						|
      break;
 | 
						|
 | 
						|
    case ORDER_TYPE_SELL:
 | 
						|
      Res = ((this.StopLoss != SL) || (this.TakeProfit != TP)) &&
 | 
						|
            (TP >= 0) && (TP <= Tick.ask) &&
 | 
						|
            (!SL || (SL >= Tick.ask));
 | 
						|
 | 
						|
      break;
 | 
						|
 | 
						|
    case ORDER_TYPE_BUY_LIMIT:
 | 
						|
      if (Price != this.OpenPrice)
 | 
						|
        Price = ::NormalizeDouble(Price, this.Digits);
 | 
						|
/*
 | 
						|
    #ifdef BESTINTERVAL_ONTESTER
 | 
						|
      if (Price == Tick.ask) // Для применения BestInterval пока нельзя выставлять лимитник по текущей цене.
 | 
						|
        Price = Tick.ask - _Point;
 | 
						|
    #endif // BESTINTERVAL_ONTESTER
 | 
						|
*/
 | 
						|
      Res = ((this.OpenPrice != Price) || (this.StopLoss != SL) || (this.TakeProfit != TP)) &&
 | 
						|
            (Price > 0) && (Price <= Tick.ask) &&
 | 
						|
            (SL >= 0) && (SL <= Price) &&
 | 
						|
            (!TP || (TP >= Price));
 | 
						|
 | 
						|
      break;
 | 
						|
 | 
						|
    case ORDER_TYPE_SELL_LIMIT:
 | 
						|
      if (Price != this.OpenPrice)
 | 
						|
        Price = ::NormalizeDouble(Price, this.Digits);
 | 
						|
/*
 | 
						|
    #ifdef BESTINTERVAL_ONTESTER
 | 
						|
      if (Price == Tick.bid) // Для применения BestInterval пока нельзя выставлять лимитник по текущей цене.
 | 
						|
        Price = Tick.bid + _Point;
 | 
						|
    #endif // BESTINTERVAL_ONTESTER
 | 
						|
*/
 | 
						|
      Res = ((this.OpenPrice != Price) || (this.StopLoss != SL) || (this.TakeProfit != TP)) &&
 | 
						|
            (Price >= Tick.bid) &&
 | 
						|
            (TP >= 0) && (TP <= Price) &&
 | 
						|
            (!SL || (SL >= Price));
 | 
						|
 | 
						|
      break;
 | 
						|
 | 
						|
    case ORDER_TYPE_BUY_STOP:
 | 
						|
      if (Price != this.OpenPrice)
 | 
						|
        Price = ::NormalizeDouble(Price, this.Digits);
 | 
						|
 | 
						|
      Res = ((this.OpenPrice != Price) || (this.StopLoss != SL) || (this.TakeProfit != TP)) &&
 | 
						|
            (Price >= Tick.ask) &&
 | 
						|
            (SL >= 0) && (SL <= Price) &&
 | 
						|
            (!TP || (TP >= Price));
 | 
						|
 | 
						|
      break;
 | 
						|
 | 
						|
    case ORDER_TYPE_SELL_STOP:
 | 
						|
      if (Price != this.OpenPrice)
 | 
						|
        Price = ::NormalizeDouble(Price, this.Digits);
 | 
						|
 | 
						|
      Res = ((this.OpenPrice != Price) || (this.StopLoss != SL) || (this.TakeProfit != TP)) &&
 | 
						|
            (Price > 0) && (Price <= Tick.bid) &&
 | 
						|
            (TP >= 0) && (TP <= Price) &&
 | 
						|
            (!SL || (SL >= Price));
 | 
						|
      ;
 | 
						|
    }
 | 
						|
 | 
						|
    return(Res);
 | 
						|
  }
 | 
						|
 | 
						|
  void ToClose( const MqlTick &Tick )
 | 
						|
  {
 | 
						|
    _VC
 | 
						|
    if (!this.IsClosed())
 | 
						|
    {
 | 
						|
      this.Ticket = -this.Ticket;
 | 
						|
 | 
						|
      this.CloseTimeMsc = Tick.time_msc;
 | 
						|
 | 
						|
      if (this.IsPosition())
 | 
						|
        this.Profit = this.GetCalcProfit();
 | 
						|
    }
 | 
						|
 | 
						|
    return;
 | 
						|
  }
 | 
						|
 | 
						|
  void ToNull( void )
 | 
						|
  {
 | 
						|
    _VC
 | 
						|
    this.Ticket = 0;
 | 
						|
 | 
						|
    // Обнуление нужно для ORDERS::IsChange: this.Balance += this.Orders[i].GetProfit() + this.Orders[i].GetCommission();
 | 
						|
    this.Profit = 0;
 | 
						|
    this.Commission = 0;
 | 
						|
 | 
						|
    return;
 | 
						|
  }
 | 
						|
 | 
						|
  double GetCalcClosePrice( const MqlTick &Tick ) const
 | 
						|
  {
 | 
						|
    _VC
 | 
						|
    double Price = this.ClosePrice;
 | 
						|
 | 
						|
    if (!this.IsClosed())
 | 
						|
//      Price = !((this.Type ^ 2) % 3) ? Tick.ask : Tick.bid;
 | 
						|
      switch (this.Type)
 | 
						|
      {
 | 
						|
      case ORDER_TYPE_BUY:
 | 
						|
      case ORDER_TYPE_SELL_LIMIT:
 | 
						|
      case ORDER_TYPE_SELL_STOP:
 | 
						|
        Price = Tick.bid;
 | 
						|
 | 
						|
        break;
 | 
						|
      case ORDER_TYPE_SELL:
 | 
						|
      case ORDER_TYPE_BUY_LIMIT:
 | 
						|
      case ORDER_TYPE_BUY_STOP:
 | 
						|
        Price = Tick.ask;
 | 
						|
      }
 | 
						|
 | 
						|
    return(Price);
 | 
						|
  }
 | 
						|
 | 
						|
  double GetCalcProfit( void ) const
 | 
						|
  {
 | 
						|
    _VC
 | 
						|
 | 
						|
    // https://www.mql5.com/ru/forum/474452/page19#comment_55099014
 | 
						|
    return((this.Type <= ORDER_TYPE_SELL) ? (this.ClosePrice - this.OpenPrice) * this.TickValue : 0); // bool Create(...)
 | 
						|
//    return(this.IsPosition() ? (this.ClosePrice - this.OpenPrice) * this.TickValue : 0); // bool Create(...)
 | 
						|
//    return((this.Type == ORDER_TYPE_BUY) ? /*this.Lots * */(this.ClosePrice - this.OpenPrice) * this.TickValue
 | 
						|
//                                         : ((this.Type == ORDER_TYPE_SELL) ? /*this.Lots * */(this.OpenPrice - this.ClosePrice) * this.TickValue : 0));
 | 
						|
  }
 | 
						|
 | 
						|
  static void CloseBy( ORDER &Order1, ORDER &Order2, const MqlTick &Tick )
 | 
						|
  {
 | 
						|
    Order2.ClosePrice = Order1.OpenPrice;
 | 
						|
 | 
						|
    Order2.CloseReason = Order2.OpenReason;
 | 
						|
    Order2.ClosePriceRequest = Order1.OpenPriceRequest;
 | 
						|
 | 
						|
    Order2.ToClose(Tick);
 | 
						|
 | 
						|
    Order1.ChangeLots(::NormalizeDouble(Order1.Lots - Order2.Lots, 8));
 | 
						|
    Order1.Profit = Order1.GetCalcProfit();
 | 
						|
 | 
						|
  #ifdef ORDER_COMMISSION
 | 
						|
    Order1.Commission = Order1.Lots * (ORDER_COMMISSION);
 | 
						|
  #endif // ORDER_COMMISSION
 | 
						|
 | 
						|
    return;
 | 
						|
  }
 | 
						|
 | 
						|
public:
 | 
						|
  bool Modify( double Price, double SL, double TP, const MqlTick &Tick )
 | 
						|
  {
 | 
						|
    _VC
 | 
						|
    const bool Res = this.Check(Price, SL, TP, Tick);
 | 
						|
 | 
						|
    if (Res)
 | 
						|
    {
 | 
						|
      this.TakeProfit = TP;
 | 
						|
      this.StopLoss = SL;
 | 
						|
 | 
						|
      if (!this.IsPosition())
 | 
						|
      {
 | 
						|
        this.OpenPrice = Price;
 | 
						|
 | 
						|
        this.OpenPriceRequest = this.OpenPrice;
 | 
						|
      }
 | 
						|
    }
 | 
						|
 | 
						|
    return(Res);
 | 
						|
  }
 | 
						|
 | 
						|
  bool IsClosed( void ) const
 | 
						|
  {
 | 
						|
    _VC
 | 
						|
    return(this.Ticket <= 0); // ClosTimeMsc нельзя, т.к. есть Null-ордера.
 | 
						|
  }
 | 
						|
 | 
						|
  bool IsNotNull( void ) const
 | 
						|
  {
 | 
						|
    _VC
 | 
						|
    return((bool)this.Ticket);
 | 
						|
  }
 | 
						|
 | 
						|
  bool IsChange( const MqlTick &Tick )
 | 
						|
  {
 | 
						|
    _VC
 | 
						|
    bool Res = false;
 | 
						|
    const ENUM_ORDER_TYPE PrevType = this.Type;
 | 
						|
 | 
						|
//    if (!this.IsClosed())
 | 
						|
    if (this.Ticket > 0)
 | 
						|
    {
 | 
						|
      switch (this.Type)
 | 
						|
      {
 | 
						|
      case ORDER_TYPE_BUY:
 | 
						|
        if ((Tick.bid <= this.StopLoss) && Tick.bid) // https://www.mql5.com/ru/forum/360210/page41#comment_20741563
 | 
						|
        {
 | 
						|
          this.CloseReason = DEAL_REASON_SL;
 | 
						|
          this.ClosePriceRequest = this.StopLoss;
 | 
						|
 | 
						|
          this.ClosePrice = Tick.bid;
 | 
						|
 | 
						|
          Res = true;
 | 
						|
        }
 | 
						|
        else if (this.TakeProfit && (Tick.bid >= this.TakeProfit))
 | 
						|
        {
 | 
						|
          this.CloseReason = DEAL_REASON_TP;
 | 
						|
          this.ClosePriceRequest = this.TakeProfit;
 | 
						|
 | 
						|
        #ifdef VIRTUAL_LIMITS_TP_SLIPPAGE
 | 
						|
          this.ClosePrice = Tick.bid;
 | 
						|
        #else //  VIRTUAL_LIMITS_TP_SLIPPAGE
 | 
						|
          this.ClosePrice = this.TakeProfit;
 | 
						|
        #endif //  VIRTUAL_LIMITS_TP_SLIPPAGE
 | 
						|
 | 
						|
          Res = true;
 | 
						|
        }
 | 
						|
 | 
						|
        break;
 | 
						|
 | 
						|
      case ORDER_TYPE_SELL:
 | 
						|
        if (this.StopLoss && (Tick.ask >= this.StopLoss))
 | 
						|
        {
 | 
						|
          this.CloseReason = DEAL_REASON_SL;
 | 
						|
          this.ClosePriceRequest = this.StopLoss;
 | 
						|
 | 
						|
          this.ClosePrice = Tick.ask;
 | 
						|
 | 
						|
          Res = true;
 | 
						|
        }
 | 
						|
        else if ((Tick.ask <= this.TakeProfit) && Tick.ask) // https://www.mql5.com/ru/forum/360210/page41#comment_20741563
 | 
						|
        {
 | 
						|
          this.CloseReason = DEAL_REASON_TP;
 | 
						|
          this.ClosePriceRequest = this.TakeProfit;
 | 
						|
 | 
						|
        #ifdef VIRTUAL_LIMITS_TP_SLIPPAGE
 | 
						|
          this.ClosePrice = Tick.ask;
 | 
						|
        #else //  VIRTUAL_LIMITS_TP_SLIPPAGE
 | 
						|
          this.ClosePrice = this.TakeProfit;
 | 
						|
        #endif //  VIRTUAL_LIMITS_TP_SLIPPAGE
 | 
						|
 | 
						|
          Res = true;
 | 
						|
        }
 | 
						|
 | 
						|
        break;
 | 
						|
 | 
						|
      case ORDER_TYPE_BUY_LIMIT:
 | 
						|
        if (Res = (Tick.ask <= this.OpenPrice) && Tick.ask) // https://www.mql5.com/ru/forum/360210/page41#comment_20741563
 | 
						|
//        if (Res = (::NormalizeDouble(Tick.ask - this.OpenPrice, _Digits) <= 0)) // https://www.mql5.com/ru/forum/222141/page22#comment_14210601
 | 
						|
        {
 | 
						|
          this.Type = ORDER_TYPE_BUY;
 | 
						|
 | 
						|
        #ifdef VIRTUAL_LIMITS_TP_SLIPPAGE
 | 
						|
          this.OpenPrice = Tick.ask;
 | 
						|
        #endif //  VIRTUAL_LIMITS_TP_SLIPPAGE
 | 
						|
        }
 | 
						|
 | 
						|
        break;
 | 
						|
 | 
						|
      case ORDER_TYPE_SELL_LIMIT:
 | 
						|
        if (Res = (Tick.bid >= this.OpenPrice))
 | 
						|
//        if (Res = (::NormalizeDouble(Tick.bid - this.OpenPrice, _Digits) >= 0)) // https://www.mql5.com/ru/forum/222141/page22#comment_14210601
 | 
						|
        {
 | 
						|
          this.Type = ORDER_TYPE_SELL;
 | 
						|
 | 
						|
        #ifdef VIRTUAL_LIMITS_TP_SLIPPAGE
 | 
						|
          this.OpenPrice = Tick.bid;
 | 
						|
        #endif //  VIRTUAL_LIMITS_TP_SLIPPAGE
 | 
						|
        }
 | 
						|
 | 
						|
        break;
 | 
						|
 | 
						|
      case ORDER_TYPE_BUY_STOP:
 | 
						|
        if (Res = (Tick.ask >= this.OpenPrice))
 | 
						|
        {
 | 
						|
          this.Type = ORDER_TYPE_BUY;
 | 
						|
 | 
						|
          this.OpenPrice = Tick.ask;
 | 
						|
        }
 | 
						|
 | 
						|
        break;
 | 
						|
 | 
						|
      case ORDER_TYPE_SELL_STOP:
 | 
						|
        if (Res = (Tick.bid <= this.OpenPrice) && Tick.bid) // https://www.mql5.com/ru/forum/360210/page41#comment_20741563
 | 
						|
        {
 | 
						|
          this.Type = ORDER_TYPE_SELL;
 | 
						|
 | 
						|
          this.OpenPrice = Tick.bid;
 | 
						|
        }
 | 
						|
      }
 | 
						|
 | 
						|
      if (Res)
 | 
						|
      {
 | 
						|
        if (this.Type == PrevType)
 | 
						|
          this.ToClose(Tick);
 | 
						|
        else
 | 
						|
        {
 | 
						|
          this.OpenTimeMsc = Tick.time_msc;
 | 
						|
 | 
						|
          this.ClosePrice = this.GetCalcClosePrice(Tick);
 | 
						|
 | 
						|
          this.Profit = this.GetCalcProfit();
 | 
						|
 | 
						|
        #ifdef ORDER_COMMISSION
 | 
						|
          this.Commission = this.Lots * (ORDER_COMMISSION);
 | 
						|
        #endif // ORDER_COMMISSION
 | 
						|
        }
 | 
						|
      }
 | 
						|
      else
 | 
						|
      {
 | 
						|
        this.ClosePrice = this.GetCalcClosePrice(Tick);
 | 
						|
 | 
						|
        this.Profit = this.GetCalcProfit();
 | 
						|
      }
 | 
						|
    }
 | 
						|
 | 
						|
    return(Res);
 | 
						|
  }
 | 
						|
 | 
						|
  bool Delete(  const MqlTick &Tick  )
 | 
						|
  {
 | 
						|
    _VC
 | 
						|
    const bool Res = !this.IsPosition();
 | 
						|
 | 
						|
    if (Res)
 | 
						|
    {
 | 
						|
      this.ClosePrice = this.GetCalcClosePrice(Tick);
 | 
						|
      this.CloseReason = this.OpenReason;
 | 
						|
 | 
						|
      this.ToClose(Tick);
 | 
						|
    }
 | 
						|
 | 
						|
    return(Res);
 | 
						|
  }
 | 
						|
 | 
						|
  bool Create( const SYMBOL_BASE &SymbolBase, const ENUM_ORDER_TYPE inType, const double &dLots,
 | 
						|
               double inPrice, double dSL, double dTP, const MAGIC_TYPE &iMagicNumber,
 | 
						|
               const string &sComment, const MqlTick &Tick )
 | 
						|
  {
 | 
						|
    this = SymbolBase;
 | 
						|
 | 
						|
    this.Type = inType;
 | 
						|
 | 
						|
    this.TickValue *= (this.Lots = ::NormalizeDouble(dLots, 8));
 | 
						|
 | 
						|
    if ((bool)(this.Type & 1))
 | 
						|
      this.TickValue = -this.TickValue;
 | 
						|
 | 
						|
    const bool Res = (this.Lots > 0) && this.Reset() && this.Check(inPrice, dSL, dTP, Tick);
 | 
						|
 | 
						|
    if (Res)
 | 
						|
    {
 | 
						|
      this.Ticket = this.GetNewTicket();
 | 
						|
 | 
						|
      this.OpenPrice = (this.Type == ORDER_TYPE_BUY) ? Tick.ask : ((this.Type == ORDER_TYPE_SELL) ? Tick.bid : inPrice);
 | 
						|
      this.OpenTimeMsc = Tick.time_msc;
 | 
						|
 | 
						|
      this.StopLoss = dSL;
 | 
						|
      this.TakeProfit = dTP;
 | 
						|
 | 
						|
      this.MagicNumber = iMagicNumber;
 | 
						|
 | 
						|
      this.ClosePrice = this.GetCalcClosePrice(Tick);
 | 
						|
      this.Profit = this.GetCalcProfit();
 | 
						|
 | 
						|
      this.CloseTimeMsc = 0;
 | 
						|
 | 
						|
      this.comment = sComment;
 | 
						|
 | 
						|
      this.OpenReason = DEAL_REASON_EXPERT;
 | 
						|
      this.CloseReason = DEAL_REASON_CLIENT;
 | 
						|
 | 
						|
      this.OpenPriceRequest = this.OpenPrice;
 | 
						|
//      this.ClosePriceRequest = this.ClosePrice;
 | 
						|
 | 
						|
      #ifdef ORDER_COMMISSION
 | 
						|
      if (this.IsPosition())
 | 
						|
        this.Commission = this.Lots * (ORDER_COMMISSION);
 | 
						|
      else
 | 
						|
      #endif // #ifdef ORDER_COMMISSION
 | 
						|
      this.Commission = 0;
 | 
						|
    }
 | 
						|
 | 
						|
    return(Res);
 | 
						|
  }
 | 
						|
 | 
						|
  void Deposit( const double dDeposit, const MqlTick &Tick )
 | 
						|
  {
 | 
						|
    this.Ticket = this.GetNewTicket();
 | 
						|
    this.Type = OP_BALANCE;
 | 
						|
 | 
						|
    this.Lots = 0;
 | 
						|
 | 
						|
  //  this.Symbol = NULL;
 | 
						|
    const string Str = NULL;
 | 
						|
    this.comment = Str;
 | 
						|
    this.Symbol = Str;
 | 
						|
 | 
						|
    this.OpenPrice = 0;
 | 
						|
 | 
						|
    this.OpenTimeMsc = Tick.time_msc;
 | 
						|
 | 
						|
    this.StopLoss = 0;
 | 
						|
    this.TakeProfit = 0;
 | 
						|
 | 
						|
    this.ClosePrice = this.OpenPrice;
 | 
						|
 | 
						|
  //  this.Expiration = 0;
 | 
						|
 | 
						|
    this.MagicNumber = 0;
 | 
						|
 | 
						|
    this.Profit = dDeposit;
 | 
						|
 | 
						|
    this.Commission = 0;
 | 
						|
    this.Swap = 0;
 | 
						|
 | 
						|
    this.OpenReason = DEAL_REASON_CLIENT;
 | 
						|
    this.CloseReason = this.OpenReason;
 | 
						|
 | 
						|
    this.OpenPriceRequest = this.OpenPrice;
 | 
						|
    this.ClosePriceRequest = this.OpenPriceRequest;
 | 
						|
 | 
						|
    this.ToClose(Tick);
 | 
						|
 | 
						|
    return;
 | 
						|
  }
 | 
						|
 | 
						|
  static void CloseByNetting( ORDER &Order1, ORDER &Order2, const MqlTick &Tick )
 | 
						|
  {
 | 
						|
    Order1.ClosePrice = Order2.OpenPrice;
 | 
						|
    Order1.CloseReason = Order2.OpenReason;
 | 
						|
 | 
						|
    Order1.ClosePriceRequest = Order2.OpenPriceRequest;
 | 
						|
 | 
						|
    Order1.ToClose(Tick);
 | 
						|
 | 
						|
    Order2.ToNull();
 | 
						|
 | 
						|
    return;
 | 
						|
  }
 | 
						|
 | 
						|
  bool CloseBy( ORDER &Order, const MqlTick &Tick, const bool Netting = false )
 | 
						|
  {
 | 
						|
    const bool Res = (this.Ticket != Order.Ticket) && (this.Type == ORDER_TYPE_SELL - Order.Type) && !this.IsClosed() && !Order.IsClosed();
 | 
						|
 | 
						|
    if (Res)
 | 
						|
    {
 | 
						|
      if (this.Lots == Order.Lots)
 | 
						|
      {
 | 
						|
        if (!Netting)
 | 
						|
        {
 | 
						|
          this.ClosePrice = Order.OpenPrice;
 | 
						|
          this.CloseReason = this.OpenReason;
 | 
						|
 | 
						|
          this.ClosePriceRequest = Order.OpenPriceRequest;
 | 
						|
 | 
						|
          this.ToClose(Tick);
 | 
						|
 | 
						|
          Order.ClosePrice = Order.OpenPrice;
 | 
						|
          Order.CloseReason = Order.OpenReason;
 | 
						|
 | 
						|
          Order.OpenPriceRequest = Order.OpenPrice;
 | 
						|
          Order.ClosePriceRequest = Order.ClosePrice;
 | 
						|
 | 
						|
//          Order.ChangeLots();
 | 
						|
          Order.Lots = 0; // Не должен влиять на подсчет оборота.
 | 
						|
          Order.Commission = 0;
 | 
						|
 | 
						|
          Order.ToClose(Tick);
 | 
						|
        }
 | 
						|
        else if (this.OpenTimeMsc <= Order.OpenTimeMsc)
 | 
						|
          ORDER::CloseByNetting(this, Order, Tick);
 | 
						|
        else
 | 
						|
          ORDER::CloseByNetting(Order, this, Tick);
 | 
						|
      }
 | 
						|
      else if (this.Lots > Order.Lots)
 | 
						|
        ORDER::CloseBy(this, Order, Tick);
 | 
						|
      else
 | 
						|
        ORDER::CloseBy(Order, this, Tick);
 | 
						|
    }
 | 
						|
 | 
						|
    return(Res);
 | 
						|
  }
 | 
						|
 | 
						|
  double Close( double dLots, const MqlTick &Tick )
 | 
						|
  {
 | 
						|
    _VC
 | 
						|
    double Res = 0;
 | 
						|
 | 
						|
    if (this.IsPosition() && !this.IsClosed())
 | 
						|
    {
 | 
						|
      dLots = ::NormalizeDouble(dLots, 8);
 | 
						|
 | 
						|
      if ((dLots > 0) && (dLots <= this.Lots))
 | 
						|
      {
 | 
						|
        Res = this.Lots - dLots;
 | 
						|
 | 
						|
        if (Res)
 | 
						|
          this.ChangeLots(dLots);
 | 
						|
 | 
						|
        this.CloseReason = this.OpenReason;
 | 
						|
        this.ClosePriceRequest = this.ClosePrice;
 | 
						|
 | 
						|
      #ifdef ORDER_COMMISSION
 | 
						|
        this.Commission = this.Lots * (ORDER_COMMISSION);
 | 
						|
      #endif // ORDER_COMMISSION
 | 
						|
 | 
						|
        this.ToClose(Tick);
 | 
						|
      }
 | 
						|
    }
 | 
						|
 | 
						|
    return(Res);
 | 
						|
  }
 | 
						|
 | 
						|
  void ChangeLots( const double dLots )
 | 
						|
  {
 | 
						|
    this.TickValue *= dLots / this.Lots;
 | 
						|
 | 
						|
    this.Lots = dLots;
 | 
						|
 | 
						|
    return;
 | 
						|
  }
 | 
						|
 | 
						|
  void SetLots( const double &dLots )
 | 
						|
  {
 | 
						|
    this.Ticket = this.GetNewTicket();
 | 
						|
 | 
						|
    this.ChangeLots(::NormalizeDouble(dLots, 8));
 | 
						|
 | 
						|
    #ifdef ORDER_COMMISSION
 | 
						|
      this.Commission = this.Lots * (ORDER_COMMISSION);
 | 
						|
    #else // ORDER_COMMISSION
 | 
						|
      this.Commission = 0;
 | 
						|
    #endif // ORDER_COMMISSION
 | 
						|
 | 
						|
    this.CloseTimeMsc = 0;
 | 
						|
    this.Profit = this.GetCalcProfit();
 | 
						|
 | 
						|
    return;
 | 
						|
  }
 | 
						|
 | 
						|
  void ToHistory( void )
 | 
						|
  {
 | 
						|
    _VC
 | 
						|
    if (this.IsClosed())
 | 
						|
    {
 | 
						|
      this.Ticket = -this.Ticket;
 | 
						|
      this.TickValue = ::MathAbs(this.TickValue) * this.TickSize /
 | 
						|
                       (this.Lots ? this.Lots : 1); // Для CloseBy TickValue учитывает Lots.
 | 
						|
 | 
						|
      // https://www.mql5.com/ru/forum/282062/page70#comment_56002732
 | 
						|
      // Чтобы было совпадение с реальным окружением. Важно для BestInterval.
 | 
						|
      this.Profit = ::NormalizeDouble(this.Profit, SYMBOL_BASE::DigitsCurrency);
 | 
						|
      this.Commission = ::NormalizeDouble(this.Commission, SYMBOL_BASE::DigitsCurrency);
 | 
						|
      this.Swap = ::NormalizeDouble(this.Swap, SYMBOL_BASE::DigitsCurrency);
 | 
						|
    }
 | 
						|
 | 
						|
    return;
 | 
						|
  }
 | 
						|
 | 
						|
  void Stop( const MqlTick &Tick )
 | 
						|
  {
 | 
						|
    if (!this.Delete(Tick) && !this.Close(this.Lots, Tick))
 | 
						|
    {
 | 
						|
      const string Str = this.comment.Get() + "[end of test]";
 | 
						|
 | 
						|
      this.comment = Str;
 | 
						|
    }
 | 
						|
 | 
						|
    return;
 | 
						|
  }
 | 
						|
 | 
						|
  static void SetTicketCount( const int NewValue )
 | 
						|
  {
 | 
						|
    if (NewValue > ORDER::TicketCount)
 | 
						|
      ORDER::TicketCount = NewValue;
 | 
						|
 | 
						|
    return;
 | 
						|
  }
 | 
						|
 | 
						|
  // Искусственная техническая конструкция по этой причине:
 | 
						|
  // https://www.mql5.com/ru/forum/464362/page3#comment_52856376
 | 
						|
  template <typename T>
 | 
						|
  void IsChange2( bool &Res, T &Orders2, const int &i, int &j, double &Profit2 )
 | 
						|
  {
 | 
						|
    Res |= this.IsChange(Orders2.CurrentTick) && !this.IsClosed();
 | 
						|
 | 
						|
    if (this.IsClosed())
 | 
						|
    {
 | 
						|
    #ifdef ORDER_COMMISSION
 | 
						|
      Orders2.Balance += this.GetProfit() + this.GetCommission();
 | 
						|
    #else // ORDER_COMMISSION
 | 
						|
      Orders2.Balance += this.GetProfit();
 | 
						|
    #endif // ORDER_COMMISSION
 | 
						|
 | 
						|
      Orders2.AddHistoryOrder(this);
 | 
						|
    }
 | 
						|
    else
 | 
						|
    {
 | 
						|
    #ifdef ORDER_COMMISSION
 | 
						|
      Profit2 += this.GetFullProfit();
 | 
						|
    #else // ORDER_COMMISSION
 | 
						|
      Profit2 += this.GetProfit();
 | 
						|
    #endif // ORDER_COMMISSION
 | 
						|
 | 
						|
      if (i != j)
 | 
						|
        Orders2.Orders[j] = this;
 | 
						|
 | 
						|
      j++;
 | 
						|
    }
 | 
						|
 | 
						|
    return;
 | 
						|
  }
 | 
						|
};
 | 
						|
 | 
						|
static int ORDER::TicketCount = 0; // https://www.mql5.com/ru/forum/1111/page2302#comment_8891928 |