轻松快捷开发 MetaTrader 程序的函数库(第 三十四部分):延后交易请求 - 在特定条件下删除和修改订单与持仓

11 五月 2020, 09:49
Artyom Trishkin
0
1 461

内容

概念

本文是致力于运用延后请求进行交易的终结章节。 我们将开发删除挂单、修改止损和止盈价位、挂单参数的功能。

由此,我们将拥有完整的功能,令我们能够开发简单的自定义策略,或者根据用户定义的条件激活 EA 行为逻辑。 在准备好图形外壳之后,此预备工作将为我们提供合适的工具,例如,直接在 EA 工作期间直接从本身创建其行为的可视构造函数(大概,在函数库的图形外壳就绪之后,我将会完成一个简单的示例)。

此刻,我们能够创建挂单的附加类型。 例如,可以为 MQL4 创建 StopLimit 挂单。 在拥有足够的函数库功能之后,我会做这件事。
结果就是,它能够创建全新的挂单类型,例如 BuyTime、SellTime、BuyTimeStop、SellTimeStop等。
还仍然缺少一些图形结构,令我们无法创建完整的自定义订单。 一旦有了相应的函数库功能之后,我们将返回该任务。

同时,我将修复抽象延后请求对象后代类中的一些瑕疵,并依据目前计划创建缺少的交易功能。

实现

正如事实证明,我们没有简单显示挂单名称的函数。 但我们有 OrderTypeDescription() 函数可以显示其说明+名称。 这意味着,我们可简单地从函数返回的结果里删除描述文本,仅保留订单名称。
我们在服务函数 \MQL5\Include\DoEasy\Services\DELib.mqh 文件中改进返回订单名称的函数:

//+------------------------------------------------------------------+
//| Return the order name                                            |
//+------------------------------------------------------------------+
string OrderTypeDescription(const ENUM_ORDER_TYPE type,bool as_order=true,bool prefix_for_market_order=true,bool descr=true)
  {
   string pref=
     (
      !prefix_for_market_order ? "" :
      #ifdef __MQL5__ CMessage::Text(MSG_ORD_MARKET) 
      #else/*__MQL4__*/(as_order ? CMessage::Text(MSG_ORD_MARKET) : CMessage::Text(MSG_ORD_POSITION)) #endif 
     );
   return
     (
      type==ORDER_TYPE_BUY_LIMIT       ?  (descr ? CMessage::Text(MSG_ORD_PENDING) : "")+" Buy Limit"       :
      type==ORDER_TYPE_BUY_STOP        ?  (descr ? CMessage::Text(MSG_ORD_PENDING) : "")+" Buy Stop"        :
      type==ORDER_TYPE_SELL_LIMIT      ?  (descr ? CMessage::Text(MSG_ORD_PENDING) : "")+" Sell Limit"      :
      type==ORDER_TYPE_SELL_STOP       ?  (descr ? CMessage::Text(MSG_ORD_PENDING) : "")+" Sell Stop"       :
   #ifdef __MQL5__
      type==ORDER_TYPE_BUY_STOP_LIMIT  ?  (descr ? CMessage::Text(MSG_ORD_PENDING) : "")+" Buy Stop Limit"  :
      type==ORDER_TYPE_SELL_STOP_LIMIT ?  (descr ? CMessage::Text(MSG_ORD_PENDING) : "")+" Sell Stop Limit" :
      type==ORDER_TYPE_CLOSE_BY        ?  CMessage::Text(MSG_ORD_CLOSE_BY)                                  :  
   #else 
      type==ORDER_TYPE_BALANCE         ?  CMessage::Text(MSG_LIB_PROP_BALANCE)                              :
      type==ORDER_TYPE_CREDIT          ?  CMessage::Text(MSG_LIB_PROP_CREDIT)                               :
   #endif 
      type==ORDER_TYPE_BUY             ?  pref+" Buy"                                                       :
      type==ORDER_TYPE_SELL            ?  pref+" Sell"                                                      :  
      CMessage::Text(MSG_ORD_UNKNOWN_TYPE)
     );
  }
//+------------------------------------------------------------------+

该函数始终返回在挂单类型之前的“挂单”文本
该标记表示必须显示“挂单”文本已实现,从而该函数能够显示订单类型描述而无需初级文本。 因此,如果禁用了该标志(值为 false),则不会显示“挂单”的初级文本。


在基准延后请求后代对象的所有文件中,即在显示摘要请求名称的方法中,修复显示延后请求的摘要描述 — 在请求描述中添加订单/持仓类型文本。 之后,添加单据(如果该类中可用),后跟一个以逗号分隔的延后请求 ID。 当前的结构并不完美,因为首先显示请求描述,然后再显示 ID 和单据。

修改开仓的延后请求对象类:

//+------------------------------------------------------------------+
//| Return the short request name                                    |
//+------------------------------------------------------------------+
string CPendReqOpen::Header(void)
  {
   string type=PositionTypeDescription((ENUM_POSITION_TYPE)this.GetProperty(PEND_REQ_PROP_MQL_REQ_TYPE));
   return CMessage::Text(MSG_LIB_TEXT_PEND_REQUEST_STATUS_OPEN)+" "+type+", ID #"+(string)this.GetProperty(PEND_REQ_PROP_ID);
  }
//+------------------------------------------------------------------+

修改 平仓的延后请求对象类:

//+------------------------------------------------------------------+
//| Return the short request name                                    |
//+------------------------------------------------------------------+
string CPendReqClose::Header(void)
  {
   string type=PositionTypeDescription((ENUM_POSITION_TYPE)this.GetProperty(PEND_REQ_PROP_MQL_REQ_TYPE));
   return CMessage::Text(MSG_LIB_TEXT_PEND_REQUEST_STATUS_CLOSE)+" "+type+" #"+(string)this.GetProperty(PEND_REQ_PROP_MQL_REQ_POSITION)+", ID #"+(string)this.GetProperty(PEND_REQ_PROP_ID);
  }
//+------------------------------------------------------------------+

修改持仓止损和/或止盈价位的延后请求对象类:

//+------------------------------------------------------------------+
//| Return the short request name                                    |
//+------------------------------------------------------------------+
string CPendReqSLTP::Header(void)
  {
   string type=PositionTypeDescription((ENUM_POSITION_TYPE)this.GetProperty(PEND_REQ_PROP_MQL_REQ_TYPE));
   return CMessage::Text(MSG_LIB_TEXT_PEND_REQUEST_STATUS_SLTP)+" "+type+" #"+(string)this.GetProperty(PEND_REQ_PROP_MQL_REQ_POSITION)+", ID #"+(string)this.GetProperty(PEND_REQ_PROP_ID);
  }
//+------------------------------------------------------------------+

修改下挂单的延后请求对象类:

//+------------------------------------------------------------------+
//| Return the short request name                                    |
//+------------------------------------------------------------------+
string CPendReqPlace::Header(void)
  {
   string type=OrderTypeDescription((ENUM_ORDER_TYPE)this.GetProperty(PEND_REQ_PROP_MQL_REQ_TYPE),true,false,false);
   return CMessage::Text(MSG_LIB_TEXT_PEND_REQUEST_STATUS_PLACE)+type+", ID #"+(string)this.GetProperty(PEND_REQ_PROP_ID);
  }
//+------------------------------------------------------------------+

修改删除挂单的延后请求对象类:

//+------------------------------------------------------------------+
//| Return the short request name                                    |
//+------------------------------------------------------------------+
string CPendReqRemove::Header(void)
  {
   string type=OrderTypeDescription((ENUM_ORDER_TYPE)this.GetProperty(PEND_REQ_PROP_MQL_REQ_TYPE),true,false,false);
   return CMessage::Text(MSG_LIB_TEXT_PEND_REQUEST_STATUS_REMOVE)+type+" #"+(string)this.GetProperty(PEND_REQ_PROP_MQL_REQ_ORDER)+", ID #"+(string)this.GetProperty(PEND_REQ_PROP_ID);
  }
//+------------------------------------------------------------------+

修改挂单属性的延后请求对象类:

//+------------------------------------------------------------------+
//| Return the short request name                                    |
//+------------------------------------------------------------------+
string CPendReqModify::Header(void)
  {
   string type=OrderTypeDescription((ENUM_ORDER_TYPE)this.GetProperty(PEND_REQ_PROP_MQL_REQ_TYPE),true,false,false);
   return CMessage::Text(MSG_LIB_TEXT_PEND_REQUEST_STATUS_MODIFY)+type+", #"+(string)this.GetProperty(PEND_REQ_PROP_MQL_REQ_ORDER)+" ID #"+(string)this.GetProperty(PEND_REQ_PROP_ID);
  }
//+------------------------------------------------------------------+

CTradingControl 交易管理类的 TradingControl.mqh 文件中(即在其公开部分),为删除挂单修改持仓的止损/止盈修改挂单参数等,声明延后请求创建方法:

//--- Create a pending request (1) for full and partial position closure, (2) for closing a position by an opposite one, (3) for removing an order
   int                  CreatePReqClose(const ulong ticket,const double volume=WRONG_VALUE,const string comment=NULL,const ulong deviation=ULONG_MAX);
   int                  CreatePReqCloseBy(const ulong ticket,const ulong ticket_by);
   int                  CreatePreqDelete(const ulong ticket);

//--- Create a pending request to modify (1) position's stop orders, (2) an order
   template<typename SL,typename TP> 
   int                  CreatePReqModifyPosition(const ulong ticket,const SL sl=WRONG_VALUE,const TP tp=WRONG_VALUE);
   template<typename PS,typename PL,typename SL,typename TP>
   int                  CreatePReqModifyOrder(const ulong ticket,
                                              const PS price=WRONG_VALUE,
                                              const SL sl=WRONG_VALUE,
                                              const TP tp=WRONG_VALUE,
                                              const PL limit=WRONG_VALUE,
                                              datetime expiration=WRONG_VALUE,
                                              const ENUM_ORDER_TYPE_TIME type_time=WRONG_VALUE,
                                              const ENUM_ORDER_TYPE_FILLING type_filling=WRONG_VALUE);
   
//--- Set pending request activation criteria
   bool                 SetNewActivationProperties(const uchar id,
                                                   const ENUM_PEND_REQ_ACTIVATION_SOURCE source,
                                                   const int property,
                                                   const double control_value,
                                                   const ENUM_COMPARER_TYPE comparer_type,
                                                   const double actual_value);
  };
//+------------------------------------------------------------------+

在类主体之外实现它们:

//+------------------------------------------------------------------+
//| Create a pending request to remove a pending order               |
//+------------------------------------------------------------------+
int CTradingControl::CreatePreqDelete(const ulong ticket)
  {
//--- If the global trading ban flag is set, exit and return WRONG_VALUE
   if(this.IsTradingDisable())
     {
      if(this.m_log_level>LOG_LEVEL_NO_MSG)
         ::Print(DFUN,CMessage::Text(MSG_LIB_TEXT_TRADING_DISABLE));
      return WRONG_VALUE;
     }
//--- Set the error flag as "no errors"
   this.m_error_reason_flags=TRADE_REQUEST_ERR_FLAG_NO_ERROR;
   ENUM_ACTION_TYPE action=ACTION_TYPE_CLOSE;
//--- Get an order object by ticket
   COrder *order=this.GetOrderObjByTicket(ticket);
   if(order==NULL)
     {
      this.m_error_reason_flags=TRADE_REQUEST_ERR_FLAG_INTERNAL_ERR;
      if(this.m_log_level>LOG_LEVEL_NO_MSG)
         ::Print(DFUN,CMessage::Text(MSG_LIB_SYS_ERROR_FAILED_GET_ORD_OBJ));
      return WRONG_VALUE;
     }
   ENUM_ORDER_TYPE order_type=(ENUM_ORDER_TYPE)order.TypeOrder();
//--- Get a symbol object by an order ticket
   CSymbol *symbol_obj=this.GetSymbolObjByOrder(ticket,DFUN);
   if(symbol_obj==NULL)
     {
      this.m_error_reason_flags=TRADE_REQUEST_ERR_FLAG_INTERNAL_ERR;
      if(this.m_log_level>LOG_LEVEL_NO_MSG)
         ::Print(DFUN,CMessage::Text(MSG_LIB_SYS_ERROR_FAILED_GET_SYM_OBJ));
      return WRONG_VALUE;
     }
//--- get a trading object from a symbol object
   CTradeObj *trade_obj=symbol_obj.GetTradeObj();
   if(trade_obj==NULL)
     {
      this.m_error_reason_flags=TRADE_REQUEST_ERR_FLAG_INTERNAL_ERR;
      if(this.m_log_level>LOG_LEVEL_NO_MSG)
         ::Print(DFUN,CMessage::Text(MSG_LIB_SYS_ERROR_FAILED_GET_TRADE_OBJ));
      return WRONG_VALUE;
     }
//--- Update symbol quotes
   if(!symbol_obj.RefreshRates())
     {
      trade_obj.SetResultRetcode(10021);
      trade_obj.SetResultComment(CMessage::Text(trade_obj.GetResultRetcode()));
      this.AddErrorCodeToList(10021);  // No quotes to handle the request
      if(this.m_log_level>LOG_LEVEL_NO_MSG)
         ::Print(DFUN,CMessage::Text(10021));
      return WRONG_VALUE;
     }
     
//--- Look for the least of the possible IDs. If failed to find, return WRONG_VALUE
   int id=this.GetFreeID();
   if(id<1)
     {
      //--- No free IDs to create a pending request
      if(this.m_log_level>LOG_LEVEL_NO_MSG)
         ::Print(DFUN,CMessage::Text(MSG_LIB_TEXT_PEND_REQUEST_NO_FREE_IDS));
      return WRONG_VALUE;
     }

   //--- Set the trading operation type, as well as deleted order's symbol and ticket in the request structure
   this.m_request.action=TRADE_ACTION_REMOVE;
   this.m_request.symbol=symbol_obj.Name();
   this.m_request.order=ticket;
   this.m_request.type=order_type;
   this.m_request.volume=order.Volume();
   this.m_request.price=order.PriceOpen();
//--- As a result of creating a pending trading request, return either its ID or -1 if unsuccessful
   if(this.CreatePendingRequest(PEND_REQ_STATUS_REMOVE,(uchar)id,1,ulong(END_TIME-(ulong)::TimeCurrent()),this.m_request,0,symbol_obj,order))
      return id;
   return WRONG_VALUE;
  }
//+------------------------------------------------------------------+
//| Create a pending request to modify position's stop orders        |
//+------------------------------------------------------------------+
template<typename SL,typename TP> 
int CTradingControl::CreatePReqModifyPosition(const ulong ticket,const SL sl=WRONG_VALUE,const TP tp=WRONG_VALUE)
  {
//--- If the global trading ban flag is set, exit and return WRONG_VALUE
   if(this.IsTradingDisable())
     {
      if(this.m_log_level>LOG_LEVEL_NO_MSG)
         ::Print(DFUN,CMessage::Text(MSG_LIB_TEXT_TRADING_DISABLE));
      return WRONG_VALUE;
     }
//--- Set the error flag as "no errors"
   this.m_error_reason_flags=TRADE_REQUEST_ERR_FLAG_NO_ERROR;
   ENUM_ACTION_TYPE action=ACTION_TYPE_MODIFY;
//--- Get an order object by ticket
   COrder *order=this.GetOrderObjByTicket(ticket);
   if(order==NULL)
     {
      this.m_error_reason_flags=TRADE_REQUEST_ERR_FLAG_INTERNAL_ERR;
      if(this.m_log_level>LOG_LEVEL_NO_MSG)
         ::Print(DFUN,CMessage::Text(MSG_LIB_SYS_ERROR_FAILED_GET_ORD_OBJ));
      return WRONG_VALUE;
     }
   ENUM_ORDER_TYPE order_type=(ENUM_ORDER_TYPE)order.TypeOrder();
//--- Get a symbol object by a position ticket
   CSymbol *symbol_obj=this.GetSymbolObjByPosition(ticket,DFUN);
   if(symbol_obj==NULL)
     {
      this.m_error_reason_flags=TRADE_REQUEST_ERR_FLAG_INTERNAL_ERR;
      if(this.m_log_level>LOG_LEVEL_NO_MSG)
         ::Print(DFUN,CMessage::Text(MSG_LIB_SYS_ERROR_FAILED_GET_SYM_OBJ));
      return WRONG_VALUE;
     }
//--- Get a trading object from a symbol object
   CTradeObj *trade_obj=symbol_obj.GetTradeObj();
   if(trade_obj==NULL)
     {
      this.m_error_reason_flags=TRADE_REQUEST_ERR_FLAG_INTERNAL_ERR;
      if(this.m_log_level>LOG_LEVEL_NO_MSG)
         ::Print(DFUN,CMessage::Text(MSG_LIB_SYS_ERROR_FAILED_GET_TRADE_OBJ));
      return WRONG_VALUE;
     }
//--- Set the prices
//--- If failed to set - write the "internal error" flag, set the error code in the return structure,
//--- display the message in the journal and return 'false'
   if(!this.SetPrices(order_type,0,(sl==WRONG_VALUE ? order.StopLoss() : sl),(tp==WRONG_VALUE ? order.TakeProfit() : tp),0,DFUN,symbol_obj))
     {
      this.m_error_reason_flags=TRADE_REQUEST_ERR_FLAG_INTERNAL_ERR;
      trade_obj.SetResultRetcode(10021);
      trade_obj.SetResultComment(CMessage::Text(trade_obj.GetResultRetcode()));
      if(this.m_log_level>LOG_LEVEL_NO_MSG)
         ::Print(DFUN,CMessage::Text(10021));   // No quotes to process the request
      return WRONG_VALUE;
     }
//--- Look for the least of the possible IDs. If failed to find, return 'false'
   int id=this.GetFreeID();
   if(id<1)
      return WRONG_VALUE;
            
//--- Write a type of a conducted operation, as well as a symbol and a ticket of a modified position to the request structure
   this.m_request.action=TRADE_ACTION_SLTP;
   this.m_request.symbol=symbol_obj.Name();
   this.m_request.position=ticket;
   this.m_request.type=order_type;
   this.m_request.volume=order.Volume();
//--- As a result of creating a pending trading request, return either its ID or -1 if unsuccessful
   if(this.CreatePendingRequest(PEND_REQ_STATUS_SLTP,(uchar)id,1,ulong(END_TIME-(ulong)::TimeCurrent()),this.m_request,0,symbol_obj,order))
      return id;
   return WRONG_VALUE;
  }
//+------------------------------------------------------------------+
//| Create a pending request to modify a pending order               |
//+------------------------------------------------------------------+
template<typename PS,typename PL,typename SL,typename TP>
int CTradingControl::CreatePReqModifyOrder(const ulong ticket,
                                           const PS price=WRONG_VALUE,
                                           const SL sl=WRONG_VALUE,
                                           const TP tp=WRONG_VALUE,
                                           const PL limit=WRONG_VALUE,
                                           datetime expiration=WRONG_VALUE,
                                           const ENUM_ORDER_TYPE_TIME type_time=WRONG_VALUE,
                                           const ENUM_ORDER_TYPE_FILLING type_filling=WRONG_VALUE)
  {
//--- If the global trading ban flag is set, exit and return WRONG_VALUE
   if(this.IsTradingDisable())
     {
      if(this.m_log_level>LOG_LEVEL_NO_MSG)
         ::Print(DFUN,CMessage::Text(MSG_LIB_TEXT_TRADING_DISABLE));
      return WRONG_VALUE;
     }
//--- Set the error flag as "no errors"
   this.m_error_reason_flags=TRADE_REQUEST_ERR_FLAG_NO_ERROR;
   ENUM_ACTION_TYPE action=ACTION_TYPE_MODIFY;
//--- Get an order object by ticket
   COrder *order=this.GetOrderObjByTicket(ticket);
   if(order==NULL)
     {
      this.m_error_reason_flags=TRADE_REQUEST_ERR_FLAG_INTERNAL_ERR;
      if(this.m_log_level>LOG_LEVEL_NO_MSG)
         ::Print(DFUN,CMessage::Text(MSG_LIB_SYS_ERROR_FAILED_GET_ORD_OBJ));
      return false;
     }
   ENUM_ORDER_TYPE order_type=(ENUM_ORDER_TYPE)order.TypeOrder();
//--- Get a symbol object by an order ticket
   CSymbol *symbol_obj=this.GetSymbolObjByOrder(ticket,DFUN);
   if(symbol_obj==NULL)
     {
      this.m_error_reason_flags=TRADE_REQUEST_ERR_FLAG_INTERNAL_ERR;
      if(this.m_log_level>LOG_LEVEL_NO_MSG)
         ::Print(DFUN,CMessage::Text(MSG_LIB_SYS_ERROR_FAILED_GET_SYM_OBJ));
      return false;
     }
//--- get a trading object from a symbol object
   CTradeObj *trade_obj=symbol_obj.GetTradeObj();
   if(trade_obj==NULL)
     {
      this.m_error_reason_flags=TRADE_REQUEST_ERR_FLAG_INTERNAL_ERR;
      if(this.m_log_level>LOG_LEVEL_NO_MSG)
         ::Print(DFUN,CMessage::Text(MSG_LIB_SYS_ERROR_FAILED_GET_TRADE_OBJ));
      return false;
     }
//--- Set the prices
//--- If failed to set - write the "internal error" flag, set the error code in the return structure,
//--- display the message in the journal and return 'false'
   if(!this.SetPrices(order_type,
                      (price>0 ? price : order.PriceOpen()),
                      (sl>0 ? sl : sl<0 ? order.StopLoss() : 0),
                      (tp>0 ? tp : tp<0 ? order.TakeProfit() : 0),
                      (limit>0 ? limit : order.PriceStopLimit()),
                      DFUN,symbol_obj))
     {
      this.m_error_reason_flags=TRADE_REQUEST_ERR_FLAG_INTERNAL_ERR;
      trade_obj.SetResultRetcode(10021);
      trade_obj.SetResultComment(CMessage::Text(trade_obj.GetResultRetcode()));
      if(this.m_log_level>LOG_LEVEL_NO_MSG)
         ::Print(DFUN,CMessage::Text(10021));   // No quotes to process the request
      return false;
     }
//--- Look for the least of the possible IDs. If failed to find, return 'false'
   int id=this.GetFreeID();
   if(id<1)
      return WRONG_VALUE;
            
//--- Write the magic number, volume, filling type, as well as expiration date and type to the request structure
   this.m_request.magic=order.GetMagicID((uint)order.Magic());
   this.m_request.volume=order.Volume();
   this.m_request.type_filling=(type_filling>WRONG_VALUE ? type_filling : order.TypeFilling());
   this.m_request.expiration=(expiration>WRONG_VALUE ? expiration : order.TimeExpiration());
   this.m_request.type_time=(type_time>WRONG_VALUE ? type_time : order.TypeTime());

   //--- Set the trading operation type, as well as modified order's symbol and ticket in the request structure
   this.m_request.action=TRADE_ACTION_MODIFY;
   this.m_request.symbol=symbol_obj.Name();
   this.m_request.order=ticket;
   this.m_request.type=order_type;
//--- As a result of creating a pending trading request, return either its ID or -1 if unsuccessful
   if(this.CreatePendingRequest(PEND_REQ_STATUS_MODIFY,(uchar)id,1,ulong(END_TIME-(ulong)::TimeCurrent()),this.m_request,0,symbol_obj,order))
      return id;
   return WRONG_VALUE;
  }
//+------------------------------------------------------------------+

方法的逻辑与先前创建的在特定条件下开仓/平仓/下订单等延后请求的方法完全相同。 在代码里已进行了详细注释,因此勿需再关注这些方法。

现在,我们加入从程序访问所创建方法。 为此,编写从函数库基准对象类的方法里调用这些方法。
CEngine 类里,添加为删除挂单修改持仓止损/止盈价位修改挂单参数等创建延后请求的方法声明:

//--- Create a pending request for closing a position (1) fully, (2) partially, (3) by an opposite one, (4) for removing an order
   int                  ClosePositionPending(const ulong ticket,const string comment=NULL,const ulong deviation=ULONG_MAX);
   int                  ClosePositionPartiallyPending(const ulong ticket,const double volume,const string comment=NULL,const ulong deviation=ULONG_MAX);
   int                  ClosePositionByPending(const ulong ticket,const ulong ticket_by);
   int                  DeleteOrderPending(const ulong ticket);

//--- Create a pending request to modify (1) a position, (2) an order
   template<typename SL,typename TP> 
   int                  ModifyPositionPending(const ulong ticket,const SL sl=WRONG_VALUE,const TP tp=WRONG_VALUE,const string comment=NULL);
   template<typename PR,typename SL,typename TP,typename PL>
   int                  ModifyOrderPending(const ulong ticket,
                                           const PR price=WRONG_VALUE,
                                           const SL sl=WRONG_VALUE,
                                           const TP tp=WRONG_VALUE,
                                           const PL stoplimit=WRONG_VALUE,
                                           datetime expiration=WRONG_VALUE,
                                           const ENUM_ORDER_TYPE_TIME type_time=WRONG_VALUE,
                                           const ENUM_ORDER_TYPE_FILLING type_filling=WRONG_VALUE);
                                   

在类的实体之外实现声明的方法:

//+------------------------------------------------------------------+
//| Create a pending request to remove a pending order               |
//+------------------------------------------------------------------+
int CEngine::DeleteOrderPending(const ulong ticket)
  {
   return this.m_trading.CreatePreqDelete(ticket);
  }
//+------------------------------------------------------------------+
//| Create a pending request to modify a position                    |
//+------------------------------------------------------------------+
template<typename SL,typename TP>
int CEngine::ModifyPositionPending(const ulong ticket,const SL sl=WRONG_VALUE,const TP tp=WRONG_VALUE,const string comment=NULL)
  {
   return this.m_trading.CreatePReqModifyPosition(ticket,sl,tp);
  }
//+------------------------------------------------------------------+
//| Create a pending request to modify an order                      |
//+------------------------------------------------------------------+
template<typename PR,typename SL,typename TP,typename PL>
int CEngine::ModifyOrderPending(const ulong ticket,
                                const PR price=WRONG_VALUE,
                                const SL sl=WRONG_VALUE,
                                const TP tp=WRONG_VALUE,
                                const PL stoplimit=WRONG_VALUE,
                                datetime expiration=WRONG_VALUE,
                                const ENUM_ORDER_TYPE_TIME type_time=WRONG_VALUE,
                                const ENUM_ORDER_TYPE_FILLING type_filling=WRONG_VALUE)
  {
   return this.m_trading.CreatePReqModifyOrder(ticket,price,sl,tp,stoplimit,expiration,type_time,type_filling);
  }
//+------------------------------------------------------------------+

这些方法仅调用我们之前在 CTradingControl 类里编写的创建延后请求的相应方法,并返回结果。

这些就是为删除订单、修改订单和持仓属性等操控延后请求所需的全部改进。
此外,出于类的改进,代码(模板参数的名称)也做了一些小的修改。 不过,它们仅与方法代码的可视化感知有关,因此没有必要在此研究它们。

测试

为了执行所创建的功能,利用上篇文章中的 EA,并将其保存在 \MQL5\Experts\TestDoEasy\Part34\ 之下,命名为 TestDoEasyPart34.mq5

如同以前测试延后请求的 EA 一样,我们将创建激活延后请求模式的按钮 — 删除所有挂单(Delete pending),所有持仓平仓(Close all)以及为无止价位的挂单和持仓设置止损和止盈价位的按钮(Set StopLoss 和 Set TakeProfit)。

鉴于按下这些按钮会导致针对所有现有订单和持仓的批处理,因此启用相应的激活按钮将令我们能够一次性检查多笔挂单和持仓的延后请求。

在当前版本中,测试 EA 交易面板里的交易管理按钮不是很方便 — 出金按钮位于撤单、平仓和设置止价位按钮之间。 我们将此按钮设置到较低位置在设置止盈按钮之后。 为此,只需在测试 EA 交易面板所有按钮的枚举中更改常量的位置即可:

//+------------------------------------------------------------------+
//|                                             TestDoEasyPart34.mq5 |
//|                        Copyright 2018, MetaQuotes Software Corp. |
//|                             https://mql5.com/en/users/artmedia70 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2019, MetaQuotes Software Corp."
#property link      "https://mql5.com/en/users/artmedia70"
#property version   "1.00"
//--- includes
#include <DoEasy\Engine.mqh>
//--- enums
enum ENUM_BUTTONS
  {
   BUTT_BUY,
   BUTT_BUY_LIMIT,
   BUTT_BUY_STOP,
   BUTT_BUY_STOP_LIMIT,
   BUTT_CLOSE_BUY,
   BUTT_CLOSE_BUY2,
   BUTT_CLOSE_BUY_BY_SELL,
   BUTT_SELL,
   BUTT_SELL_LIMIT,
   BUTT_SELL_STOP,
   BUTT_SELL_STOP_LIMIT,
   BUTT_CLOSE_SELL,
   BUTT_CLOSE_SELL2,
   BUTT_CLOSE_SELL_BY_BUY,
   BUTT_DELETE_PENDING,
   BUTT_CLOSE_ALL,
   BUTT_SET_STOP_LOSS,
   BUTT_SET_TAKE_PROFIT,
   BUTT_PROFIT_WITHDRAWAL,
   BUTT_TRAILING_ALL
  };
#define TOTAL_BUTT   (20)

全局变量列表接收按钮状态标志,这些标志代表所激活的延后请求模式,依此删除挂单、平仓并修改挂单和持仓的止价位,以及加入两个存储当前品种 Point 和 Digits 值的变量

//--- global variables
CEngine        engine;
SDataButt      butt_data[TOTAL_BUTT];
string         prefix;
double         lot;
double         withdrawal=(InpWithdrawal<0.1 ? 0.1 : InpWithdrawal);
ushort         magic_number;
uint           stoploss;
uint           takeprofit;
uint           distance_pending;
uint           distance_stoplimit;
uint           distance_pending_request;
uint           bars_delay_pending_request;
uint           slippage;
bool           trailing_on;
bool           pressed_pending_buy;
bool           pressed_pending_buy_limit;
bool           pressed_pending_buy_stop;
bool           pressed_pending_buy_stoplimit;
bool           pressed_pending_close_buy;
bool           pressed_pending_close_buy2;
bool           pressed_pending_close_buy_by_sell;
bool           pressed_pending_sell;
bool           pressed_pending_sell_limit;
bool           pressed_pending_sell_stop;
bool           pressed_pending_sell_stoplimit;
bool           pressed_pending_close_sell;
bool           pressed_pending_close_sell2;
bool           pressed_pending_close_sell_by_buy;
bool           pressed_pending_delete_all;
bool           pressed_pending_close_all;
bool           pressed_pending_sl;
bool           pressed_pending_tp;
double         trailing_stop;
double         trailing_step;
uint           trailing_start;
uint           stoploss_to_modify;
uint           takeprofit_to_modify;
int            used_symbols_mode;
string         used_symbols;
string         array_used_symbols[];
bool           testing;
uchar          group1;
uchar          group2;
double         g_point;
int            g_digits;
//+------------------------------------------------------------------+

在 EA 的 OnInit() 处理程序里,将当前品种的 Point 和 Digits 值赋值给相应的变量

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- Calling the function displays the list of enumeration constants in the journal 
//--- (the list is set in the strings 22 and 25 of the DELib.mqh file) for checking the constants validity
   //EnumNumbersTest();

//--- Set EA global variables
   prefix=MQLInfoString(MQL_PROGRAM_NAME)+"_";
   testing=engine.IsTester();
   for(int i=0;i<TOTAL_BUTT;i++)
     {
      butt_data[i].name=prefix+EnumToString((ENUM_BUTTONS)i);
      butt_data[i].text=EnumToButtText((ENUM_BUTTONS)i);
     }
   lot=NormalizeLot(Symbol(),fmax(InpLots,MinimumLots(Symbol())*2.0));
   magic_number=InpMagic;
   stoploss=InpStopLoss;
   takeprofit=InpTakeProfit;
   distance_pending=InpDistance;
   distance_stoplimit=InpDistanceSL;
   slippage=InpSlippage;
   trailing_stop=InpTrailingStop*Point();
   trailing_step=InpTrailingStep*Point();
   trailing_start=InpTrailingStart;
   stoploss_to_modify=InpStopLossModify;
   takeprofit_to_modify=InpTakeProfitModify;
   distance_pending_request=(InpDistancePReq<5 ? 5 : InpDistancePReq);
   bars_delay_pending_request=(InpBarsDelayPReq<1 ? 1 : InpBarsDelayPReq);
   g_point=SymbolInfoDouble(NULL,SYMBOL_POINT);
   g_digits=(int)SymbolInfoInteger(NULL,SYMBOL_DIGITS);
//--- Initialize random group numbers
   group1=0;
   group2=0;
   srand(GetTickCount());
   
//--- Initialize DoEasy library
   OnInitDoEasy();
   
//--- Check and remove remaining EA graphical objects
   if(IsPresentObects(prefix))
      ObjectsDeleteAll(0,prefix);

//--- Create the button panel
   if(!CreateButtons(InpButtShiftX,InpButtShiftY))
      return INIT_FAILED;
//--- Set trailing activation button status
   ButtonState(butt_data[TOTAL_BUTT-1].name,trailing_on);
//--- Reset states of the buttons for working using pending requests
   for(int i=0;i<14;i++)
     {
      ButtonState(butt_data[i].name+"_PRICE",false);
      ButtonState(butt_data[i].name+"_TIME",false);
     }

//--- Check playing a standard sound by macro substitution and a custom sound by description
   engine.PlaySoundByDescription(SND_OK);
   Sleep(600);
   engine.PlaySoundByDescription(TextByLanguage("Звук упавшей монетки 2","Falling coin 2"));

//---
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+

在按钮生成函数里,改进代码,从而创建激活延后请求其他模式的按钮:

//+------------------------------------------------------------------+
//| Create the buttons panel                                         |
//+------------------------------------------------------------------+
bool CreateButtons(const int shift_x=20,const int shift_y=0)
  {
   int h=18,w=82,offset=2,wpt=14;
   int cx=offset+shift_x+wpt*2+2,cy=offset+shift_y+(h+1)*(TOTAL_BUTT/2)+3*h+1;
   int x=cx,y=cy;
   int shift=0;
   for(int i=0;i<TOTAL_BUTT;i++)
     {
      x=x+(i==7 ? w+2 : 0);
      if(i==TOTAL_BUTT-6) x=cx;
      y=(cy-(i-(i>6 ? 7 : 0))*(h+1));
      if(!ButtonCreate(butt_data[i].name,x,y,(i<TOTAL_BUTT-6 ? w : w*2+2),h,butt_data[i].text,(i<4 ? clrGreen : i>6 && i<11 ? clrRed : clrBlue)))
        {
         Alert(TextByLanguage("Не удалось создать кнопку \"","Could not create button \""),butt_data[i].text);
         return false;
        }
     }
   
   h=18; offset=2;
   cx=offset+shift_x; cy=offset+shift_y+(h+1)*(TOTAL_BUTT/2)+3*h+1;
   x=cx; y=cy;
   shift=0;
   for(int i=0;i<18;i++)
     {
      y=(cy-(i-(i>6 ? 7 : 0))*(h+1));
      if(!ButtonCreate(butt_data[i].name+"_PRICE",((i>6 && i<14) || i>17 ? x+wpt*2+w*2+5 : x),y,wpt,h,"P",(i<4 ? clrGreen : i>6 && i<11 ? clrChocolate : clrBlue)))
        {
         Alert(TextByLanguage("Не удалось создать кнопку \"","Could not create button \""),butt_data[i].text+" \"P\"");
         return false;
        }
      if(!ButtonCreate(butt_data[i].name+"_TIME",((i>6 && i<14) || i>17 ? x+wpt*2+w*2+5+wpt+1 : x+wpt+1),y,wpt,h,"T",(i<4 ? clrGreen : i>6 && i<11 ? clrChocolate : clrBlue)))
        {
         Alert(TextByLanguage("Не удалось создать кнопку \"","Could not create button \""),butt_data[i].text+" \"T\"");
         return false;
        }
     }
   ChartRedraw(0);
   return true;
  }
//+------------------------------------------------------------------+

所有修改仅影响已创建按钮的顺序编号管理,为其设置必要的坐标。

EA 的主要变化在于 EA 交易面板按钮的按下函数。
添加交易面板上的按钮处理代码

//+------------------------------------------------------------------+
//| Handle pressing the buttons                                      |
//+------------------------------------------------------------------+
void PressButtonEvents(const string button_name)
  {
   bool comp_magic=true;   // Temporary variable selecting the composite magic number with random group IDs
   string comment="";
   //--- Convert button name into its string ID
   string button=StringSubstr(button_name,StringLen(prefix));
   //--- Random group 1 and 2 numbers within the range of 0 - 15
   group1=(uchar)Rand();
   group2=(uchar)Rand();
   uint magic=(comp_magic ? engine.SetCompositeMagicNumber(magic_number,group1,group2) : magic_number);
   //--- If the button is pressed
   if(ButtonState(button_name))
     {
      //--- If the BUTT_BUY button is pressed: Open Buy position
      if(button==EnumToString(BUTT_BUY))
        {
         //--- If the pending request creation buttons are not pressed, open Buy 
         if(!pressed_pending_buy)
            engine.OpenBuy(lot,Symbol(),magic,stoploss,takeprofit);   // No comment - the default comment is to be set
         //--- Otherwise, create a pending request for opening a Buy position
         else
           {
            int id=engine.OpenBuyPending(lot,Symbol(),magic,stoploss,takeprofit);
            if(id>0)
              {
               //--- set the pending request activation price and time, as well as activation parameters
               double ask=SymbolInfoDouble(NULL,SYMBOL_ASK);
               double price_activation=NormalizeDouble(ask-distance_pending_request*g_point,g_digits);
               ulong  time_activation=TimeCurrent()+bars_delay_pending_request*PeriodSeconds();
               SetPReqCriterion((uchar)id,price_activation,time_activation,BUTT_BUY,EQUAL_OR_LESS,ask,TimeCurrent());
              }
           }
        }
      //--- If the BUTT_BUY_LIMIT button is pressed: Place BuyLimit
      else if(button==EnumToString(BUTT_BUY_LIMIT))
        {
         //--- If the pending request creation buttons are not pressed, set BuyLimit
         if(!pressed_pending_buy_limit)
            engine.PlaceBuyLimit(lot,Symbol(),distance_pending,stoploss,takeprofit,magic,TextByLanguage("Отложенный BuyLimit","Pending BuyLimit order"));
         //--- Otherwise, create a pending request to place a BuyLimit order with the placement distance
         //--- and set the conditions depending on active buttons
         else
           {
            int id=engine.PlaceBuyLimitPending(lot,Symbol(),distance_pending,stoploss,takeprofit,magic);
            if(id>0)
              {
               //--- set the pending request activation price and time, as well as activation parameters
               double ask=SymbolInfoDouble(NULL,SYMBOL_ASK);
               double price_activation=NormalizeDouble(ask-distance_pending_request*g_point,g_digits);
               ulong  time_activation=TimeCurrent()+bars_delay_pending_request*PeriodSeconds();
               SetPReqCriterion((uchar)id,price_activation,time_activation,BUTT_BUY_LIMIT,EQUAL_OR_LESS,ask,TimeCurrent());
              }
           }
        }
      //--- If the BUTT_BUY_STOP button is pressed: Set BuyStop
      else if(button==EnumToString(BUTT_BUY_STOP))
        {
         //--- If the pending request creation buttons are not pressed, set BuyStop
         if(!pressed_pending_buy_stop)
            engine.PlaceBuyStop(lot,Symbol(),distance_pending,stoploss,takeprofit,magic,TextByLanguage("Отложенный BuyStop","Pending BuyStop order"));
         //--- Otherwise, create a pending request to place a BuyStop order with the placement distance
         //--- and set the conditions depending on active buttons
         else
           {
            int id=engine.PlaceBuyStopPending(lot,Symbol(),distance_pending,stoploss,takeprofit,magic);
            if(id>0)
              {
               //--- set the pending request activation price and time, as well as activation parameters
               double ask=SymbolInfoDouble(NULL,SYMBOL_ASK);
               double price_activation=NormalizeDouble(ask-distance_pending_request*g_point,g_digits);
               ulong  time_activation=TimeCurrent()+bars_delay_pending_request*PeriodSeconds();
               SetPReqCriterion((uchar)id,price_activation,time_activation,BUTT_BUY_STOP,EQUAL_OR_LESS,ask,TimeCurrent());
              }
           }
        }
      //--- If the BUTT_BUY_STOP_LIMIT button is pressed: Set BuyStopLimit
      else if(button==EnumToString(BUTT_BUY_STOP_LIMIT))
        {
         //--- If the pending request creation buttons are not pressed, set BuyStopLimit
         if(!pressed_pending_buy_stoplimit)
            engine.PlaceBuyStopLimit(lot,Symbol(),distance_pending,distance_stoplimit,stoploss,takeprofit,magic,TextByLanguage("Отложенный BuyStopLimit","Pending BuyStopLimit order"));
         //--- Otherwise, create a pending request to place a BuyStopLimit order with the placement distances
         //--- and set the conditions depending on active buttons
         else
           {
            int id=engine.PlaceBuyStopLimitPending(lot,Symbol(),distance_pending,distance_stoplimit,stoploss,takeprofit,magic);
            if(id>0)
              {
               //--- set the pending request activation price and time, as well as activation parameters
               double ask=SymbolInfoDouble(NULL,SYMBOL_ASK);
               double price_activation=NormalizeDouble(ask-distance_pending_request*g_point,g_digits);
               ulong  time_activation=TimeCurrent()+bars_delay_pending_request*PeriodSeconds();
               SetPReqCriterion((uchar)id,price_activation,time_activation,BUTT_BUY_STOP_LIMIT,EQUAL_OR_LESS,ask,TimeCurrent());
              }
           }
        }
      //--- If the BUTT_SELL button is pressed: Open Sell position
      else if(button==EnumToString(BUTT_SELL))
        {
         //--- If the pending request creation buttons are not pressed, open Sell
         if(!pressed_pending_sell)
            engine.OpenSell(lot,Symbol(),magic,stoploss,takeprofit);  // No comment - the default comment is to be set
         //--- Otherwise, create a pending request for opening a Sell position
         else
           {
            int id=engine.OpenSellPending(lot,Symbol(),magic,stoploss,takeprofit);
            if(id>0)
              {
               //--- set the pending request activation price and time, as well as activation parameters
               double bid=SymbolInfoDouble(NULL,SYMBOL_BID);
               double price_activation=NormalizeDouble(bid+distance_pending_request*g_point,g_digits);
               ulong  time_activation=TimeCurrent()+bars_delay_pending_request*PeriodSeconds();
               SetPReqCriterion((uchar)id,price_activation,time_activation,BUTT_SELL,EQUAL_OR_MORE,bid,TimeCurrent());
              }
           }
        }
      //--- If the BUTT_SELL_LIMIT button is pressed: Set SellLimit
      else if(button==EnumToString(BUTT_SELL_LIMIT))
        {
         //--- If the pending request creation buttons are not pressed, set SellLimit
         if(!pressed_pending_sell_limit)
            engine.PlaceSellLimit(lot,Symbol(),distance_pending,stoploss,takeprofit,magic,TextByLanguage("Отложенный SellLimit","Pending SellLimit order"));
         //--- Otherwise, create a pending request to place a SellLimit order with the placement distance
         //--- and set the conditions depending on active buttons
         else
           {
            int id=engine.PlaceSellLimitPending(lot,Symbol(),distance_pending,stoploss,takeprofit,magic);
            if(id>0)
              {
               //--- set the pending request activation price and time, as well as activation parameters
               double bid=SymbolInfoDouble(NULL,SYMBOL_BID);
               double price_activation=NormalizeDouble(bid+distance_pending_request*g_point,g_digits);
               ulong  time_activation=TimeCurrent()+bars_delay_pending_request*PeriodSeconds();
               SetPReqCriterion((uchar)id,price_activation,time_activation,BUTT_SELL_LIMIT,EQUAL_OR_MORE,bid,TimeCurrent());
              }
           }
        }
      //--- If the BUTT_SELL_STOP button is pressed: Set SellStop
      else if(button==EnumToString(BUTT_SELL_STOP))
        {
         //--- If the pending request creation buttons are not pressed, set SellStop
         if(!pressed_pending_sell_stop)
            engine.PlaceSellStop(lot,Symbol(),distance_pending,stoploss,takeprofit,magic,TextByLanguage("Отложенный SellStop","Pending SellStop order"));
         //--- Otherwise, create a pending request to place a SellStop order with the placement distance
         //--- and set the conditions depending on active buttons
         else
           {
            int id=engine.PlaceSellStopPending(lot,Symbol(),distance_pending,stoploss,takeprofit,magic);
            if(id>0)
              {
               //--- set the pending request activation price and time, as well as activation parameters
               double bid=SymbolInfoDouble(NULL,SYMBOL_BID);
               double price_activation=NormalizeDouble(bid+distance_pending_request*g_point,g_digits);
               ulong  time_activation=TimeCurrent()+bars_delay_pending_request*PeriodSeconds();
               SetPReqCriterion((uchar)id,price_activation,time_activation,BUTT_SELL_STOP,EQUAL_OR_MORE,bid,TimeCurrent());
              }
           }
        }
      //--- If the BUTT_SELL_STOP_LIMIT button is pressed: Set SellStopLimit
      else if(button==EnumToString(BUTT_SELL_STOP_LIMIT))
        {
         //--- If the pending request creation buttons are not pressed, set SellStopLimit
         if(!pressed_pending_sell_stoplimit)
            engine.PlaceSellStopLimit(lot,Symbol(),distance_pending,distance_stoplimit,stoploss,takeprofit,magic,TextByLanguage("Отложенный SellStopLimit","Pending SellStopLimit order"));
         //--- Otherwise, create a pending request to place a SellStopLimit order with the placement distances
         //--- and set the conditions depending on active buttons
         else
           {
            int id=engine.PlaceSellStopLimitPending(lot,Symbol(),distance_pending,distance_stoplimit,stoploss,takeprofit,magic);
            if(id>0)
              {
               //--- set the pending request activation price and time, as well as activation parameters
               double bid=SymbolInfoDouble(NULL,SYMBOL_BID);
               double price_activation=NormalizeDouble(bid+distance_pending_request*g_point,g_digits);
               ulong  time_activation=TimeCurrent()+bars_delay_pending_request*PeriodSeconds();
               SetPReqCriterion((uchar)id,price_activation,time_activation,BUTT_SELL_STOP_LIMIT,EQUAL_OR_MORE,bid,TimeCurrent());
              }
           }
        }
      //--- If the BUTT_CLOSE_BUY button is pressed: Close Buy with the maximum profit
      else if(button==EnumToString(BUTT_CLOSE_BUY))
        {
         //--- Get the list of all open positions
         CArrayObj* list=engine.GetListMarketPosition();
         //--- Select only Buy positions from the list and for the current symbol only
         list=CSelect::ByOrderProperty(list,ORDER_PROP_SYMBOL,Symbol(),EQUAL);
         list=CSelect::ByOrderProperty(list,ORDER_PROP_TYPE,POSITION_TYPE_BUY,EQUAL);
         //--- Sort the list by profit considering commission and swap
         list.Sort(SORT_BY_ORDER_PROFIT_FULL);
         //--- Get the index of the Buy position with the maximum profit
         int index=CSelect::FindOrderMax(list,ORDER_PROP_PROFIT_FULL);
         if(index>WRONG_VALUE)
           {
            //--- Get the Buy position object and close a position by ticket
            COrder* position=list.At(index);
            if(position!=NULL)
              {
               //--- If the pending request creation buttons are not pressed, close a position
               if(!pressed_pending_close_buy)
                  engine.ClosePosition((ulong)position.Ticket());
               //--- Otherwise, create a pending request for closing a position by ticket
               //--- and set the conditions depending on active buttons
               else
                 {
                  int id=engine.ClosePositionPending(position.Ticket());
                  if(id>0)
                    {
                     //--- set the pending request activation price and time, as well as activation parameters
                     double bid=SymbolInfoDouble(NULL,SYMBOL_BID);
                     double price_activation=NormalizeDouble(bid+distance_pending_request*g_point,g_digits);
                     ulong  time_activation=TimeCurrent()+bars_delay_pending_request*PeriodSeconds();
                     SetPReqCriterion((uchar)id,price_activation,time_activation,BUTT_CLOSE_BUY,EQUAL_OR_MORE,bid,TimeCurrent());
                    }
                 }
              }
           }
        }
      //--- If the BUTT_CLOSE_BUY2 button is pressed: Close the half of the Buy with the maximum profit
      else if(button==EnumToString(BUTT_CLOSE_BUY2))
        {
         //--- Get the list of all open positions
         CArrayObj* list=engine.GetListMarketPosition();
         //--- Select only Buy positions from the list and for the current symbol only
         list=CSelect::ByOrderProperty(list,ORDER_PROP_SYMBOL,Symbol(),EQUAL);
         list=CSelect::ByOrderProperty(list,ORDER_PROP_TYPE,POSITION_TYPE_BUY,EQUAL);
         //--- Sort the list by profit considering commission and swap
         list.Sort(SORT_BY_ORDER_PROFIT_FULL);
         //--- Get the index of the Buy position with the maximum profit
         int index=CSelect::FindOrderMax(list,ORDER_PROP_PROFIT_FULL);
         if(index>WRONG_VALUE)
           {
            //--- Get the Buy position object and close a position by ticket
            COrder* position=list.At(index);
            if(position!=NULL)
              {
               //--- If the pending request creation buttons are not pressed, close a position by ticket
               if(!pressed_pending_close_buy2)
                  engine.ClosePositionPartially((ulong)position.Ticket(),position.Volume()/2.0);
               //--- Otherwise, create a pending request for closing a position partially by ticket
               //--- and set the conditions depending on active buttons
               else
                 {
                  int id=engine.ClosePositionPartiallyPending(position.Ticket(),position.Volume()/2.0);
                  if(id>0)
                    {
                     //--- set the pending request activation price and time, as well as activation parameters
                     double bid=SymbolInfoDouble(NULL,SYMBOL_BID);
                     double price_activation=NormalizeDouble(bid+distance_pending_request*g_point,g_digits);
                     ulong  time_activation=TimeCurrent()+bars_delay_pending_request*PeriodSeconds();
                     SetPReqCriterion((uchar)id,price_activation,time_activation,BUTT_CLOSE_BUY2,EQUAL_OR_MORE,bid,TimeCurrent());
                    }
                 }
              }
           }
        }
      //--- If the BUTT_CLOSE_BUY_BY_SELL button is pressed: Close Buy with the maximum profit by the opposite Sell with the maximum profit
      else if(button==EnumToString(BUTT_CLOSE_BUY_BY_SELL))
        {
         //--- In case of a hedging account
         if(engine.IsHedge())
           {
            CArrayObj *list_buy=NULL, *list_sell=NULL;
            //--- Get the list of all open positions
            CArrayObj* list=engine.GetListMarketPosition();
            if(list==NULL)
               return;
            //--- Select only current symbol positions from the list
            list=CSelect::ByOrderProperty(list,ORDER_PROP_SYMBOL,Symbol(),EQUAL);
            
            //--- Select only Buy positions from the list
            list_buy=CSelect::ByOrderProperty(list,ORDER_PROP_TYPE,POSITION_TYPE_BUY,EQUAL);
            if(list_buy==NULL)
               return;
            //--- Sort the list by profit considering commission and swap
            list_buy.Sort(SORT_BY_ORDER_PROFIT_FULL);
            //--- Get the index of the Buy position with the maximum profit
            int index_buy=CSelect::FindOrderMax(list_buy,ORDER_PROP_PROFIT_FULL);
            
            //--- Select only Sell positions from the list
            list_sell=CSelect::ByOrderProperty(list,ORDER_PROP_TYPE,POSITION_TYPE_SELL,EQUAL);
            if(list_sell==NULL)
               return;
            //--- Sort the list by profit considering commission and swap
            list_sell.Sort(SORT_BY_ORDER_PROFIT_FULL);
            //--- Get the index of the Sell position with the maximum profit
            int index_sell=CSelect::FindOrderMax(list_sell,ORDER_PROP_PROFIT_FULL);
            if(index_buy>WRONG_VALUE && index_sell>WRONG_VALUE)
              {
               //--- Select the Buy position with the maximum profit
               COrder* position_buy=list_buy.At(index_buy);
               //--- Select the Sell position with the maximum profit
               COrder* position_sell=list_sell.At(index_sell);
               if(position_buy!=NULL && position_sell!=NULL)
                 {
                  //--- If the pending request creation buttons are not pressed, close positions by ticket
                  if(!pressed_pending_close_buy_by_sell)
                     engine.ClosePositionBy((ulong)position_buy.Ticket(),(ulong)position_sell.Ticket());
                  //--- Otherwise, create a pending request for closing a Buy position by an opposite Sell one
                  //--- and set the conditions depending on active buttons
                  else
                    {
                     int id=engine.ClosePositionByPending(position_buy.Ticket(),position_sell.Ticket());
                     if(id>0)
                       {
                        //--- set the pending request activation price and time, as well as activation parameters
                        double bid=SymbolInfoDouble(NULL,SYMBOL_BID);
                        double price_activation=NormalizeDouble(bid+distance_pending_request*g_point,g_digits);
                        ulong  time_activation=TimeCurrent()+bars_delay_pending_request*PeriodSeconds();
                        SetPReqCriterion((uchar)id,price_activation,time_activation,BUTT_CLOSE_BUY_BY_SELL,EQUAL_OR_MORE,bid,TimeCurrent());
                       }
                    }
                 }
              }
           }
        }
        
      //--- If the BUTT_CLOSE_SELL button is pressed: Close Sell with the maximum profit
      else if(button==EnumToString(BUTT_CLOSE_SELL))
        {
         //--- Get the list of all open positions
         CArrayObj* list=engine.GetListMarketPosition();
         //--- Select only Sell positions from the list and for the current symbol only
         list=CSelect::ByOrderProperty(list,ORDER_PROP_SYMBOL,Symbol(),EQUAL);
         list=CSelect::ByOrderProperty(list,ORDER_PROP_TYPE,POSITION_TYPE_SELL,EQUAL);
         //--- Sort the list by profit considering commission and swap
         list.Sort(SORT_BY_ORDER_PROFIT_FULL);
         //--- Get the index of the Sell position with the maximum profit
         int index=CSelect::FindOrderMax(list,ORDER_PROP_PROFIT_FULL);
         if(index>WRONG_VALUE)
           {
            //--- Get the Sell position object and close a position by ticket
            COrder* position=list.At(index);
            if(position!=NULL)
              {
               //--- If the pending request creation buttons are not pressed, close a position
               if(!pressed_pending_close_sell)
                  engine.ClosePosition((ulong)position.Ticket());
               //--- Otherwise, create a pending request for closing a position by ticket
               //--- and set the conditions depending on active buttons
               else
                 {
                  int id=engine.ClosePositionPending(position.Ticket());
                  if(id>0)
                    {
                     //--- set the pending request activation price and time, as well as activation parameters
                     double ask=SymbolInfoDouble(NULL,SYMBOL_ASK);
                     double price_activation=NormalizeDouble(ask-distance_pending_request*g_point,g_digits);
                     ulong  time_activation=TimeCurrent()+bars_delay_pending_request*PeriodSeconds();
                     SetPReqCriterion((uchar)id,price_activation,time_activation,BUTT_CLOSE_SELL,EQUAL_OR_LESS,ask,TimeCurrent());
                    }
                 }
              }
           }
        }
      //--- If the BUTT_CLOSE_SELL2 button is pressed: Close the half of the Sell with the maximum profit
      else if(button==EnumToString(BUTT_CLOSE_SELL2))
        {
         //--- Get the list of all open positions
         CArrayObj* list=engine.GetListMarketPosition();
         //--- Select only Sell positions from the list and for the current symbol only
         list=CSelect::ByOrderProperty(list,ORDER_PROP_SYMBOL,Symbol(),EQUAL);
         list=CSelect::ByOrderProperty(list,ORDER_PROP_TYPE,POSITION_TYPE_SELL,EQUAL);
         //--- Sort the list by profit considering commission and swap
         list.Sort(SORT_BY_ORDER_PROFIT_FULL);
         //--- Get the index of the Sell position with the maximum profit
         int index=CSelect::FindOrderMax(list,ORDER_PROP_PROFIT_FULL);
         if(index>WRONG_VALUE)
           {
            //--- Get the Sell position object and close a position by ticket
            COrder* position=list.At(index);
            if(position!=NULL)
              {
               //--- If the pending request creation buttons are not pressed, close a position by ticket
               if(!pressed_pending_close_sell2)
                  engine.ClosePositionPartially((ulong)position.Ticket(),position.Volume()/2.0);
               //--- Otherwise, create a pending request for closing a position partially by ticket
               //--- and set the conditions depending on active buttons
               else
                 {
                  int id=engine.ClosePositionPartiallyPending(position.Ticket(),position.Volume()/2.0);
                  if(id>0)
                    {
                     //--- set the pending request activation price and time, as well as activation parameters
                     double ask=SymbolInfoDouble(NULL,SYMBOL_ASK);
                     double price_activation=NormalizeDouble(ask-distance_pending_request*g_point,g_digits);
                     ulong  time_activation=TimeCurrent()+bars_delay_pending_request*PeriodSeconds();
                     SetPReqCriterion((uchar)id,price_activation,time_activation,BUTT_CLOSE_SELL2,EQUAL_OR_LESS,ask,TimeCurrent());
                    }
                 }
              }
           }
        }
      //--- If the BUTT_CLOSE_SELL_BY_BUY button is pressed: Close Sell with the maximum profit by the opposite Buy with the maximum profit
      else if(button==EnumToString(BUTT_CLOSE_SELL_BY_BUY))
        {
         //--- In case of a hedging account
         if(engine.IsHedge())
           {
            CArrayObj *list_buy=NULL, *list_sell=NULL;
            //--- Get the list of all open positions
            CArrayObj* list=engine.GetListMarketPosition();
            if(list==NULL)
               return;
            //--- Select only current symbol positions from the list
            list=CSelect::ByOrderProperty(list,ORDER_PROP_SYMBOL,Symbol(),EQUAL);
            
            //--- Select only Sell positions from the list
            list_sell=CSelect::ByOrderProperty(list,ORDER_PROP_TYPE,POSITION_TYPE_SELL,EQUAL);
            if(list_sell==NULL)
               return;
            //--- Sort the list by profit considering commission and swap
            list_sell.Sort(SORT_BY_ORDER_PROFIT_FULL);
            //--- Get the index of the Sell position with the maximum profit
            int index_sell=CSelect::FindOrderMax(list_sell,ORDER_PROP_PROFIT_FULL);
            
            //--- Select only Buy positions from the list
            list_buy=CSelect::ByOrderProperty(list,ORDER_PROP_TYPE,POSITION_TYPE_BUY,EQUAL);
            if(list_buy==NULL)
               return;
            //--- Sort the list by profit considering commission and swap
            list_buy.Sort(SORT_BY_ORDER_PROFIT_FULL);
            //--- Get the index of the Buy position with the maximum profit
            int index_buy=CSelect::FindOrderMax(list_buy,ORDER_PROP_PROFIT_FULL);
            if(index_sell>WRONG_VALUE && index_buy>WRONG_VALUE)
              {
               //--- Select the Sell position with the maximum profit
               COrder* position_sell=list_sell.At(index_sell);
               //--- Select the Buy position with the maximum profit
               COrder* position_buy=list_buy.At(index_buy);
               if(position_sell!=NULL && position_buy!=NULL)
                 {
                  //--- If the pending request creation buttons are not pressed, close positions by ticket
                  if(!pressed_pending_close_sell_by_buy)
                     engine.ClosePositionBy((ulong)position_sell.Ticket(),(ulong)position_buy.Ticket());
                  //--- Otherwise, create a pending request for closing a Sell position by an opposite Buy one
                  //--- and set the conditions depending on active buttons
                  else
                    {
                     int id=engine.ClosePositionByPending(position_sell.Ticket(),position_buy.Ticket());
                     if(id>0)
                       {
                        //--- set the pending request activation price and time, as well as activation parameters
                        double ask=SymbolInfoDouble(NULL,SYMBOL_ASK);
                        double price_activation=NormalizeDouble(ask-distance_pending_request*g_point,g_digits);
                        ulong  time_activation=TimeCurrent()+bars_delay_pending_request*PeriodSeconds();
                        SetPReqCriterion((uchar)id,price_activation,time_activation,BUTT_CLOSE_SELL_BY_BUY,EQUAL_OR_LESS,ask,TimeCurrent());
                       }
                    }
                 }
              }
           }
        }
      //--- If the BUTT_CLOSE_ALL is pressed: Close all positions starting with the one with the least profit
      else if(button==EnumToString(BUTT_CLOSE_ALL))
        {
         //--- Get the list of all open positions
         CArrayObj* list=engine.GetListMarketPosition();
         //--- Select only current symbol positions from the list
         list=CSelect::ByOrderProperty(list,ORDER_PROP_SYMBOL,Symbol(),EQUAL);
         if(list!=NULL)
           {
            //--- Sort the list by profit considering commission and swap
            list.Sort(SORT_BY_ORDER_PROFIT_FULL);
            int total=list.Total();
            //--- In the loop from the position with the least profit
            for(int i=0;i<total;i++)
              {
               COrder* position=list.At(i);
               if(position==NULL)
                  continue;
               //--- If the pending request creation buttons are not pressed, close each position by its ticket
               if(!pressed_pending_close_all)
                  engine.ClosePosition((ulong)position.Ticket());
               //--- Otherwise, create a pending request for closing each position
               //--- and set the conditions depending on active buttons
               else
                 {
                  int id=engine.ClosePositionPending(position.Ticket());
                  if(id>0)
                    {
                     //--- set the pending request activation price and time, as well as activation parameters
                     double price=SymbolInfoDouble(NULL,SYMBOL_BID);
                     double price_activation=NormalizeDouble(position.PriceOpen()+distance_pending_request*g_point,g_digits);
                     ENUM_COMPARER_TYPE comparer=EQUAL_OR_MORE;
                     if(position.TypeOrder()==POSITION_TYPE_SELL)
                       {
                        price=SymbolInfoDouble(NULL,SYMBOL_ASK);
                        price_activation=NormalizeDouble(position.PriceOpen()-distance_pending_request*g_point,g_digits);
                        comparer=EQUAL_OR_LESS;
                       }
                     ulong  time_activation=TimeCurrent()+bars_delay_pending_request*PeriodSeconds();
                     SetPReqCriterion((uchar)id,price_activation,time_activation,BUTT_CLOSE_ALL,comparer,price,TimeCurrent());
                    }
                 }
              }
           }
        }
      //--- If the BUTT_DELETE_PENDING button is pressed: Remove pending orders starting from the oldest one
      else if(button==EnumToString(BUTT_DELETE_PENDING))
        {
         //--- Get the list of all orders
         CArrayObj* list=engine.GetListMarketPendings();
         //--- Select only current symbol orders from the list
         list=CSelect::ByOrderProperty(list,ORDER_PROP_SYMBOL,Symbol(),EQUAL);
         if(list!=NULL)
           {
            //--- Sort the list by placement time
            list.Sort(SORT_BY_ORDER_TIME_OPEN);
            int total=list.Total();
            //--- In a loop from an order with the longest time
            for(int i=total-1;i>=0;i--)
              {
               COrder* order=list.At(i);
               if(order==NULL)
                  continue;
               //--- If the pending request creation buttons are not pressed, remove each order by its ticket
               if(!pressed_pending_delete_all)
                  engine.DeleteOrder((ulong)order.Ticket());
               //--- Otherwise, create a pending request for removing each order
               //--- and set the conditions depending on active buttons
               else
                 {
                  int id=engine.DeleteOrderPending(order.Ticket());
                  if(id>0)
                    {
                     //--- set the pending request activation price and time, as well as activation parameters
                     double price=SymbolInfoDouble(NULL,SYMBOL_ASK);
                     double price_activation=NormalizeDouble(order.PriceOpen()+(distance_pending+distance_pending_request)*g_point,g_digits);
                     ENUM_COMPARER_TYPE comparer=EQUAL_OR_MORE;
                     if(order.TypeByDirection()==ORDER_TYPE_SELL)
                       {
                        price=SymbolInfoDouble(NULL,SYMBOL_BID);
                        price_activation=NormalizeDouble(order.PriceOpen()-(distance_pending+distance_pending_request)*g_point,g_digits);
                        comparer=EQUAL_OR_LESS;
                       }
                     ulong  time_activation=TimeCurrent()+bars_delay_pending_request*PeriodSeconds();
                     SetPReqCriterion((uchar)id,price_activation,time_activation,BUTT_DELETE_PENDING,comparer,price,TimeCurrent());
                    }
                 }
              }
           }
        }
               
      //--- If the BUTT_SET_STOP_LOSS button is pressed: Place StopLoss to all orders and positions where it is not present
      if(button==EnumToString(BUTT_SET_STOP_LOSS))
        {
         SetStopLoss();
        }
      //--- If the BUTT_SET_TAKE_PROFIT button is pressed: Place TakeProfit to all orders and positions where it is not present
      if(button==EnumToString(BUTT_SET_TAKE_PROFIT))
        {
         SetTakeProfit();
        }
      //--- If the BUTT_PROFIT_WITHDRAWAL button is pressed: Withdraw funds from the account
      if(button==EnumToString(BUTT_PROFIT_WITHDRAWAL))
        {
         //--- If the program is launched in the tester
         if(MQLInfoInteger(MQL_TESTER))
           {
            //--- Emulate funds withdrawal
            TesterWithdrawal(withdrawal);
           }
        }

      //--- Wait for 1/10 of a second
      Sleep(100);
      //--- "Unpress" the button (if this is neither a trailing button, nor the buttons enabling pending requests)
      if(button!=EnumToString(BUTT_TRAILING_ALL) && StringFind(button,"_PRICE")<0 && StringFind(button,"_TIME")<0)
         ButtonState(button_name,false);
      //--- If the BUTT_TRAILING_ALL button or the buttons enabling pending requests are pressed
      else
        {
         //--- Set the active button color for the button enabling trailing
         if(button==EnumToString(BUTT_TRAILING_ALL))
           {
            ButtonState(button_name,true);
            trailing_on=true;
           }
         
         //--- Buying
         //--- Set the active button color for the button enabling pending requests for opening Buy by price or time
         if(button==EnumToString(BUTT_BUY)+"_PRICE" || button==EnumToString(BUTT_BUY)+"_TIME")
           {
            ButtonState(button_name,true);
            pressed_pending_buy=true;
           }
         //--- Set the active button color for the button enabling pending requests for placing BuyLimit by price or time
         if(button==EnumToString(BUTT_BUY_LIMIT)+"_PRICE" || button==EnumToString(BUTT_BUY_LIMIT)+"_TIME")
           {
            ButtonState(button_name,true);
            pressed_pending_buy_limit=true;
           }
         //--- Set the active button color for the button enabling pending requests for placing BuyStop by price or time
         if(button==EnumToString(BUTT_BUY_STOP)+"_PRICE" || button==EnumToString(BUTT_BUY_STOP)+"_TIME")
           {
            ButtonState(button_name,true);
            pressed_pending_buy_stop=true;
           }
         //--- Set the active button color for the button enabling pending requests for placing BuyStopLimit by price or time
         if(button==EnumToString(BUTT_BUY_STOP_LIMIT)+"_PRICE" || button==EnumToString(BUTT_BUY_STOP_LIMIT)+"_TIME")
           {
            ButtonState(button_name,true);
            pressed_pending_buy_stoplimit=true;
           }
         //--- Set the active button color for the button enabling pending requests for closing Buy by price or time
         if(button==EnumToString(BUTT_CLOSE_BUY)+"_PRICE" || button==EnumToString(BUTT_CLOSE_BUY)+"_TIME")
           {
            ButtonState(button_name,true);
            pressed_pending_close_buy=true;
           }
         //--- Set the active button color for the button enabling pending requests for closing 1/2 Buy by price or time
         if(button==EnumToString(BUTT_CLOSE_BUY2)+"_PRICE" || button==EnumToString(BUTT_CLOSE_BUY2)+"_TIME")
           {
            ButtonState(button_name,true);
            pressed_pending_close_buy2=true;
           }
         //--- Set the active button color for the button enabling pending requests for closing Buy by an opposite Sell by price or time
         if(button==EnumToString(BUTT_CLOSE_BUY_BY_SELL)+"_PRICE" || button==EnumToString(BUTT_CLOSE_BUY_BY_SELL)+"_TIME")
           {
            ButtonState(button_name,true);
            pressed_pending_close_buy_by_sell=true;
           }
         
         //--- Selling
         //--- Set the active button color for the button enabling pending requests for opening Sell by price or time
         if(button==EnumToString(BUTT_SELL)+"_PRICE" || button==EnumToString(BUTT_SELL)+"_TIME")
           {
            ButtonState(button_name,true);
            pressed_pending_sell=true;
           }
         //--- Set the active button color for the button enabling pending requests for placing SellLimit by price or time
         if(button==EnumToString(BUTT_SELL_LIMIT)+"_PRICE" || button==EnumToString(BUTT_SELL_LIMIT)+"_TIME")
           {
            ButtonState(button_name,true);
            pressed_pending_sell_limit=true;
           }
         //--- Set the active button color for the button enabling pending requests for placing SellStop by price or time
         if(button==EnumToString(BUTT_SELL_STOP)+"_PRICE" || button==EnumToString(BUTT_SELL_STOP)+"_TIME")
           {
            ButtonState(button_name,true);
            pressed_pending_sell_stop=true;
           }
         //--- Set the active button color for the button enabling pending requests for placing SellStopLimit by price or time
         if(button==EnumToString(BUTT_SELL_STOP_LIMIT)+"_PRICE" || button==EnumToString(BUTT_SELL_STOP_LIMIT)+"_TIME")
           {
            ButtonState(button_name,true);
            pressed_pending_sell_stoplimit=true;
           }
         //--- Set the active button color for the button enabling pending requests for closing Sell by price or time
         if(button==EnumToString(BUTT_CLOSE_SELL)+"_PRICE" || button==EnumToString(BUTT_CLOSE_SELL)+"_TIME")
           {
            ButtonState(button_name,true);
            pressed_pending_close_sell=true;
           }
         //--- Set the active button color for the button enabling pending requests for closing 1/2 Sell by price or time
         if(button==EnumToString(BUTT_CLOSE_SELL2)+"_PRICE" || button==EnumToString(BUTT_CLOSE_SELL2)+"_TIME")
           {
            ButtonState(button_name,true);
            pressed_pending_close_sell2=true;
           }
         //--- Set the active button color for the button enabling pending requests for closing Sell by an opposite Buy by price or time
         if(button==EnumToString(BUTT_CLOSE_SELL_BY_BUY)+"_PRICE" || button==EnumToString(BUTT_CLOSE_SELL_BY_BUY)+"_TIME")
           {
            ButtonState(button_name,true);
            pressed_pending_close_sell_by_buy=true;
           }
         //--- Set the active button color for the button enabling pending requests for removing orders by price or time
         if(button==EnumToString(BUTT_DELETE_PENDING)+"_PRICE" || button==EnumToString(BUTT_DELETE_PENDING)+"_TIME")
           {
            ButtonState(button_name,true);
            pressed_pending_delete_all=true;
           }
         //--- Set the active button color for the button enabling pending requests for closing positions by price or time
         if(button==EnumToString(BUTT_CLOSE_ALL)+"_PRICE" || button==EnumToString(BUTT_CLOSE_ALL)+"_TIME")
           {
            ButtonState(button_name,true);
            pressed_pending_close_all=true;
           }
         //--- Set the active button color for the button enabling pending requests for placing StopLoss by price or time
         if(button==EnumToString(BUTT_SET_STOP_LOSS)+"_PRICE" || button==EnumToString(BUTT_SET_STOP_LOSS)+"_TIME")
           {
            ButtonState(button_name,true);
            pressed_pending_sl=true;
           }
         //--- Set the active button color for the button enabling pending requests for placing TakeProfit by price or time
         if(button==EnumToString(BUTT_SET_TAKE_PROFIT)+"_PRICE" || button==EnumToString(BUTT_SET_TAKE_PROFIT)+"_TIME")
           {
            ButtonState(button_name,true);
            pressed_pending_tp=true;
           }
        }
      //--- re-draw the chart
      ChartRedraw();
     }
   //--- Return a color for the inactive buttons
   else 
     {
      //--- trailing button
      if(button==EnumToString(BUTT_TRAILING_ALL))
        {
         ButtonState(button_name,false);
         trailing_on=false;
        }
      
      //--- Buying
      //--- the button enabling pending requests for opening Buy by price
      if(button==EnumToString(BUTT_BUY)+"_PRICE")
        {
         ButtonState(button_name,false);
         pressed_pending_buy=(ButtonState(button_name) | ButtonState(prefix+EnumToString(BUTT_BUY)+"_TIME"));
        }
      //--- the button enabling pending requests for opening Buy by time
      if(button==EnumToString(BUTT_BUY)+"_TIME")
        {
         ButtonState(button_name,false);
         pressed_pending_buy=(ButtonState(button_name) | ButtonState(prefix+EnumToString(BUTT_BUY)+"_PRICE"));
        }
      
      //--- the button enabling pending requests for placing BuyLimit by price
      if(button==EnumToString(BUTT_BUY_LIMIT)+"_PRICE")
        {
         ButtonState(button_name,false);
         pressed_pending_buy_limit=(ButtonState(button_name) | ButtonState(prefix+EnumToString(BUTT_BUY_LIMIT)+"_TIME"));
        }
      //--- the button enabling pending requests for placing BuyLimit by time
      if(button==EnumToString(BUTT_BUY_LIMIT)+"_TIME")
        {
         ButtonState(button_name,false);
         pressed_pending_buy_limit=(ButtonState(button_name) | ButtonState(prefix+EnumToString(BUTT_BUY_LIMIT)+"_PRICE"));
        }
      
      //--- the button enabling pending requests for placing BuyStop by price
      if(button==EnumToString(BUTT_BUY_STOP)+"_PRICE")
        {
         ButtonState(button_name,false);
         pressed_pending_buy_stop=(ButtonState(button_name) | ButtonState(prefix+EnumToString(BUTT_BUY_STOP)+"_TIME"));
        }
      //--- the button enabling pending requests for placing BuyStop by time
      if(button==EnumToString(BUTT_BUY_STOP)+"_TIME")
        {
         ButtonState(button_name,false);
         pressed_pending_buy_stop=(ButtonState(button_name) | ButtonState(prefix+EnumToString(BUTT_BUY_STOP)+"_PRICE"));
        }
      
      //--- the button enabling pending requests for placing BuyStopLimit by price
      if(button==EnumToString(BUTT_BUY_STOP_LIMIT)+"_PRICE")
        {
         ButtonState(button_name,false);
         pressed_pending_buy_stoplimit=(ButtonState(button_name) | ButtonState(prefix+EnumToString(BUTT_BUY_STOP_LIMIT)+"_TIME"));
        }
      //--- the button enabling pending requests for placing BuyStopLimit by time
      if(button==EnumToString(BUTT_BUY_STOP_LIMIT)+"_TIME")
        {
         ButtonState(button_name,false);
         pressed_pending_buy_stoplimit=(ButtonState(button_name) | ButtonState(prefix+EnumToString(BUTT_BUY_STOP_LIMIT)+"_PRICE"));
        }
      
      //--- the button enabling pending requests for closing Buy by price
      if(button==EnumToString(BUTT_CLOSE_BUY)+"_PRICE")
        {
         ButtonState(button_name,false);
         pressed_pending_close_buy=(ButtonState(button_name) | ButtonState(prefix+EnumToString(BUTT_CLOSE_BUY)+"_TIME"));
        }
      //--- the button enabling pending requests for closing Buy by time
      if(button==EnumToString(BUTT_CLOSE_BUY)+"_TIME")
        {
         ButtonState(button_name,false);
         pressed_pending_close_buy=(ButtonState(button_name) | ButtonState(prefix+EnumToString(BUTT_CLOSE_BUY)+"_PRICE"));
        }
      
      //--- the button enabling pending requests for closing 1/2 Buy by price
      if(button==EnumToString(BUTT_CLOSE_BUY2)+"_PRICE")
        {
         ButtonState(button_name,false);
         pressed_pending_close_buy2=(ButtonState(button_name) | ButtonState(prefix+EnumToString(BUTT_CLOSE_BUY2)+"_TIME"));
        }
      //--- the button enabling pending requests for closing 1/2 Buy by time
      if(button==EnumToString(BUTT_CLOSE_BUY2)+"_TIME")
        {
         ButtonState(button_name,false);
         pressed_pending_close_buy2=(ButtonState(button_name) | ButtonState(prefix+EnumToString(BUTT_CLOSE_BUY2)+"_PRICE"));
        }
      
      //--- the button enabling pending requests for closing Buy by an opposite Sell by price
      if(button==EnumToString(BUTT_CLOSE_BUY_BY_SELL)+"_PRICE")
        {
         ButtonState(button_name,false);
         pressed_pending_close_buy_by_sell=(ButtonState(button_name) | ButtonState(prefix+EnumToString(BUTT_CLOSE_BUY_BY_SELL)+"_TIME"));
        }
      //--- the button enabling pending requests for closing Buy by an opposite Sell by time
      if(button==EnumToString(BUTT_CLOSE_BUY_BY_SELL)+"_TIME")
        {
         ButtonState(button_name,false);
         pressed_pending_close_buy_by_sell=(ButtonState(button_name) | ButtonState(prefix+EnumToString(BUTT_CLOSE_BUY_BY_SELL)+"_PRICE"));
        }

      //--- Selling
      //--- the button enabling pending requests for opening Sell by price
      if(button==EnumToString(BUTT_SELL)+"_PRICE")
        {
         ButtonState(button_name,false);
         pressed_pending_sell=(ButtonState(button_name) | ButtonState(prefix+EnumToString(BUTT_SELL)+"_TIME"));
        }
      //--- the button enabling pending requests for opening Sell by time
      if(button==EnumToString(BUTT_SELL)+"_TIME")
        {
         ButtonState(button_name,false);
         pressed_pending_sell=(ButtonState(button_name) | ButtonState(prefix+EnumToString(BUTT_SELL)+"_PRICE"));
        }
      
      //--- the button enabling pending requests for placing SellLimit by price
      if(button==EnumToString(BUTT_SELL_LIMIT)+"_PRICE")
        {
         ButtonState(button_name,false);
         pressed_pending_sell_limit=(ButtonState(button_name) | ButtonState(prefix+EnumToString(BUTT_SELL_LIMIT)+"_TIME"));
        }
      //--- the button enabling pending requests for placing SellLimit by time
      if(button==EnumToString(BUTT_SELL_LIMIT)+"_TIME")
        {
         ButtonState(button_name,false);
         pressed_pending_sell_limit=(ButtonState(button_name) | ButtonState(prefix+EnumToString(BUTT_SELL_LIMIT)+"_PRICE"));
        }
      
      //--- the button enabling pending requests for placing SellStop by price
      if(button==EnumToString(BUTT_SELL_STOP)+"_PRICE")
        {
         ButtonState(button_name,false);
         pressed_pending_sell_stop=(ButtonState(button_name) | ButtonState(prefix+EnumToString(BUTT_SELL_STOP)+"_TIME"));
        }
      //--- the button enabling pending requests for placing SellStop by time
      if(button==EnumToString(BUTT_SELL_STOP)+"_TIME")
        {
         ButtonState(button_name,false);
         pressed_pending_sell_stop=(ButtonState(button_name) | ButtonState(prefix+EnumToString(BUTT_SELL_STOP)+"_PRICE"));
        }
      
      //--- the button enabling pending requests for placing SellStopLimit by price
      if(button==EnumToString(BUTT_SELL_STOP_LIMIT)+"_PRICE")
        {
         ButtonState(button_name,false);
         pressed_pending_sell_stoplimit=(ButtonState(button_name) | ButtonState(prefix+EnumToString(BUTT_SELL_STOP_LIMIT)+"_TIME"));
        }
      //--- the button enabling pending requests for placing SellStopLimit by time
      if(button==EnumToString(BUTT_SELL_STOP_LIMIT)+"_TIME")
        {
         ButtonState(button_name,false);
         pressed_pending_sell_stoplimit=(ButtonState(button_name) | ButtonState(prefix+EnumToString(BUTT_SELL_STOP_LIMIT)+"_PRICE"));
        }
      
      //--- the button enabling pending requests for closing Sell by price
      if(button==EnumToString(BUTT_CLOSE_SELL)+"_PRICE")
        {
         ButtonState(button_name,false);
         pressed_pending_close_sell=(ButtonState(button_name) | ButtonState(prefix+EnumToString(BUTT_CLOSE_SELL)+"_TIME"));
        }
      //--- the button enabling pending requests for closing Sell by time
      if(button==EnumToString(BUTT_CLOSE_SELL)+"_TIME")
        {
         ButtonState(button_name,false);
         pressed_pending_close_sell=(ButtonState(button_name) | ButtonState(prefix+EnumToString(BUTT_CLOSE_SELL)+"_PRICE"));
        }
      
      //--- the button enabling pending requests for closing 1/2 Sell by price
      if(button==EnumToString(BUTT_CLOSE_SELL2)+"_PRICE")
        {
         ButtonState(button_name,false);
         pressed_pending_close_sell2=(ButtonState(button_name) | ButtonState(prefix+EnumToString(BUTT_CLOSE_SELL2)+"_TIME"));
        }
      //--- the button enabling pending requests for closing 1/2 Sell by time
      if(button==EnumToString(BUTT_CLOSE_SELL2)+"_TIME")
        {
         ButtonState(button_name,false);
         pressed_pending_close_sell2=(ButtonState(button_name) | ButtonState(prefix+EnumToString(BUTT_CLOSE_SELL2)+"_PRICE"));
        }
      
      //--- the button enabling pending requests for closing Sell by an opposite Buy by price
      if(button==EnumToString(BUTT_CLOSE_SELL_BY_BUY)+"_PRICE")
        {
         ButtonState(button_name,false);
         pressed_pending_close_sell_by_buy=(ButtonState(button_name) | ButtonState(prefix+EnumToString(BUTT_CLOSE_SELL_BY_BUY)+"_TIME"));
        }
      //--- the button enabling pending requests for closing Sell by an opposite Buy by time
      if(button==EnumToString(BUTT_CLOSE_SELL_BY_BUY)+"_TIME")
        {
         ButtonState(button_name,false);
         pressed_pending_close_sell_by_buy=(ButtonState(button_name) | ButtonState(prefix+EnumToString(BUTT_CLOSE_SELL_BY_BUY)+"_PRICE"));
        }
      
      //--- the button enabling pending requests for removing orders by price
      if(button==EnumToString(BUTT_DELETE_PENDING)+"_PRICE")
        {
         ButtonState(button_name,false);
         pressed_pending_delete_all=(ButtonState(button_name) | ButtonState(prefix+EnumToString(BUTT_DELETE_PENDING)+"_TIME"));
        }
      //--- the button enabling pending requests for removing orders by time
      if(button==EnumToString(BUTT_DELETE_PENDING)+"_TIME")
        {
         ButtonState(button_name,false);
         pressed_pending_delete_all=(ButtonState(button_name) | ButtonState(prefix+EnumToString(BUTT_DELETE_PENDING)+"_PRICE"));
        }
      
      //--- the button enabling pending requests for closing positions by price
      if(button==EnumToString(BUTT_CLOSE_ALL)+"_PRICE")
        {
         ButtonState(button_name,false);
         pressed_pending_close_all=(ButtonState(button_name) | ButtonState(prefix+EnumToString(BUTT_CLOSE_ALL)+"_TIME"));
        }
      //--- the button enabling pending requests for closing positions by time
      if(button==EnumToString(BUTT_CLOSE_ALL)+"_TIME")
        {
         ButtonState(button_name,false);
         pressed_pending_close_all=(ButtonState(button_name) | ButtonState(prefix+EnumToString(BUTT_CLOSE_ALL)+"_PRICE"));
        }
      
      //--- the button enabling pending requests for placing StopLoss by price
      if(button==EnumToString(BUTT_SET_STOP_LOSS)+"_PRICE")
        {
         ButtonState(button_name,false);
         pressed_pending_sl=(ButtonState(button_name) | ButtonState(prefix+EnumToString(BUTT_SET_STOP_LOSS)+"_TIME"));
        }
      //--- the button enabling pending requests for placing StopLoss by time
      if(button==EnumToString(BUTT_SET_STOP_LOSS)+"_TIME")
        {
         ButtonState(button_name,false);
         pressed_pending_sl=(ButtonState(button_name) | ButtonState(prefix+EnumToString(BUTT_SET_STOP_LOSS)+"_PRICE"));
        }
      
      //--- the button enabling pending requests for placing TakeProfit by price
      if(button==EnumToString(BUTT_SET_TAKE_PROFIT)+"_PRICE")
        {
         ButtonState(button_name,false);
         pressed_pending_tp=(ButtonState(button_name) | ButtonState(prefix+EnumToString(BUTT_SET_TAKE_PROFIT)+"_TIME"));
        }
      //--- the button enabling pending requests for placing TakeProfit by time
      if(button==EnumToString(BUTT_SET_TAKE_PROFIT)+"_TIME")
        {
         ButtonState(button_name,false);
         pressed_pending_tp=(ButtonState(button_name) | ButtonState(prefix+EnumToString(BUTT_SET_TAKE_PROFIT)+"_PRICE"));
        }
      //--- re-draw the chart
      ChartRedraw();
     }
  }
//+------------------------------------------------------------------+

在设置止损止盈的函数中,添加创建延后请求的代码模块,从而能在所有挂单和持仓里设置止损/止盈

//+------------------------------------------------------------------+
//| Set StopLoss to all orders and positions                         |
//+------------------------------------------------------------------+
void SetStopLoss(void)
  {
   if(stoploss_to_modify==0)
      return;
//--- Set StopLoss to all positions where it is absent
   //--- Get the list of all positions
   CArrayObj* list=engine.GetListMarketPosition();
   //--- Select only current symbol positions from the list
   list=CSelect::ByOrderProperty(list,ORDER_PROP_SYMBOL,Symbol(),EQUAL);
   //--- select positions with zero StopLoss from the list
   list=CSelect::ByOrderProperty(list,ORDER_PROP_SL,0,EQUAL);
   if(list==NULL)
      return;
   int total=list.Total();
   for(int i=total-1;i>=0;i--)
     {
      COrder* position=list.At(i);
      if(position==NULL)
         continue;
      double sl=CorrectStopLoss(position.Symbol(),position.TypeByDirection(),0,stoploss_to_modify);
      //--- If the pending request creation buttons are not pressed, set StopLoss for each position by its ticket
      if(!pressed_pending_sl)
         engine.ModifyPosition((ulong)position.Ticket(),sl,-1);
      //--- Otherwise, create a pending request for setting StopLoss for each position
      //--- and set the conditions depending on active buttons
      else
        {
         int id=engine.ModifyPositionPending(position.Ticket(),sl,-1);
         if(id>0)
           {
            //--- set the pending request activation price and time, as well as activation parameters
            double price=SymbolInfoDouble(NULL,SYMBOL_BID);
            double price_activation=NormalizeDouble(position.PriceOpen()+distance_pending_request*g_point,g_digits);
            ENUM_COMPARER_TYPE comparer=EQUAL_OR_MORE;
            if(position.TypeByDirection()==ORDER_TYPE_SELL)
              {
               price=SymbolInfoDouble(NULL,SYMBOL_ASK);
               price_activation=NormalizeDouble(position.PriceOpen()-distance_pending_request*g_point,g_digits);
               comparer=EQUAL_OR_LESS;
              }
            ulong  time_activation=TimeCurrent()+bars_delay_pending_request*PeriodSeconds();
            SetPReqCriterion((uchar)id,price_activation,time_activation,BUTT_SET_STOP_LOSS,comparer,price,TimeCurrent());
           }
        }
     }
//--- Set StopLoss to all pending orders where it is absent
   //--- Get the list of all orders
   list=engine.GetListMarketPendings();
   //--- Select only current symbol positions from the list
   list=CSelect::ByOrderProperty(list,ORDER_PROP_SYMBOL,Symbol(),EQUAL);
   //--- select orders with zero StopLoss from the list
   list=CSelect::ByOrderProperty(list,ORDER_PROP_SL,0,EQUAL);
   if(list==NULL)
      return;
   total=list.Total();
   for(int i=total-1;i>=0;i--)
     {
      COrder* order=list.At(i);
      if(order==NULL)
         continue;
      double sl=CorrectStopLoss(order.Symbol(),(ENUM_ORDER_TYPE)order.TypeOrder(),order.PriceOpen(),stoploss_to_modify);
      //--- If the pending request creation buttons are not pressed, set StopLoss for each order by its ticket
      if(!pressed_pending_sl)
         engine.ModifyOrder((ulong)order.Ticket(),-1,sl,-1,-1);
      //--- Otherwise, create a pending request for setting StopLoss for each order
      //--- and set the conditions depending on active buttons
      else
        {
         int id=engine.ModifyOrderPending(order.Ticket(),-1,sl,-1,-1);
         if(id>0)
           {
            //--- set the pending request activation price and time, as well as activation parameters
            double price=SymbolInfoDouble(NULL,SYMBOL_ASK);
            double price_activation=NormalizeDouble(order.PriceOpen()+(distance_pending+distance_pending_request)*g_point,g_digits);
            ENUM_COMPARER_TYPE comparer=EQUAL_OR_MORE;
            if(order.TypeByDirection()==ORDER_TYPE_SELL)
              {
               price=SymbolInfoDouble(NULL,SYMBOL_BID);
               price_activation=NormalizeDouble(order.PriceOpen()-(distance_pending+distance_pending_request)*g_point,g_digits);
               comparer=EQUAL_OR_LESS;
              }
            ulong  time_activation=TimeCurrent()+bars_delay_pending_request*PeriodSeconds();
            SetPReqCriterion((uchar)id,price_activation,time_activation,BUTT_SET_STOP_LOSS,comparer,price,TimeCurrent());
           }
        }
     }
  }
//+------------------------------------------------------------------+
//| Set TakeProfit to all orders and positions                       |
//+------------------------------------------------------------------+
void SetTakeProfit(void)
  {
   if(takeprofit_to_modify==0)
      return;
//--- Set TakeProfit to all positions where it is absent
   //--- Get the list of all positions
   CArrayObj* list=engine.GetListMarketPosition();
   //--- Select only current symbol positions from the list
   list=CSelect::ByOrderProperty(list,ORDER_PROP_SYMBOL,Symbol(),EQUAL);
   //--- select positions with zero TakeProfit from the list
   list=CSelect::ByOrderProperty(list,ORDER_PROP_TP,0,EQUAL);
   if(list==NULL)
      return;
   int total=list.Total();
   for(int i=total-1;i>=0;i--)
     {
      COrder* position=list.At(i);
      if(position==NULL)
         continue;
      double tp=CorrectTakeProfit(position.Symbol(),position.TypeByDirection(),0,takeprofit_to_modify);
      //--- If the pending request creation buttons are not pressed, set TakeProfit for each position by its ticket
      if(!pressed_pending_tp)
         engine.ModifyPosition((ulong)position.Ticket(),-1,tp);
      //--- Otherwise, create a pending request for setting TakeProfit for each position
      //--- and set the conditions depending on active buttons
      else
        {
         int id=engine.ModifyPositionPending(position.Ticket(),-1,tp);
         if(id>0)
           {
            //--- set the pending request activation price and time, as well as activation parameters
            double price=SymbolInfoDouble(NULL,SYMBOL_BID);
            double price_activation=NormalizeDouble(position.PriceOpen()+distance_pending_request*g_point,g_digits);
            ENUM_COMPARER_TYPE comparer=EQUAL_OR_MORE;
            if(position.TypeByDirection()==ORDER_TYPE_SELL)
              {
               price=SymbolInfoDouble(NULL,SYMBOL_ASK);
               price_activation=NormalizeDouble(position.PriceOpen()-distance_pending_request*g_point,g_digits);
               comparer=EQUAL_OR_LESS;
              }
            ulong  time_activation=TimeCurrent()+bars_delay_pending_request*PeriodSeconds();
            SetPReqCriterion((uchar)id,price_activation,time_activation,BUTT_SET_TAKE_PROFIT,comparer,price,TimeCurrent());
           }
        }
     }
//--- Set TakeProfit to all pending orders where it is absent
   //--- Get the list of all orders
   list=engine.GetListMarketPendings();
   //--- Select only current symbol orders from the list
   list=CSelect::ByOrderProperty(list,ORDER_PROP_SYMBOL,Symbol(),EQUAL);
   //--- select orders with zero TakeProfit from the list
   list=CSelect::ByOrderProperty(list,ORDER_PROP_TP,0,EQUAL);
   if(list==NULL)
      return;
   total=list.Total();
   for(int i=total-1;i>=0;i--)
     {
      COrder* order=list.At(i);
      if(order==NULL)
         continue;
      double tp=CorrectTakeProfit(order.Symbol(),(ENUM_ORDER_TYPE)order.TypeOrder(),order.PriceOpen(),takeprofit_to_modify);
      //--- If the pending request creation buttons are not pressed, set TakeProfit for each order by its ticket
      if(!pressed_pending_sl)
         engine.ModifyOrder((ulong)order.Ticket(),-1,-1,tp,-1);
      //--- Otherwise, create a pending request for setting TakeProfit for each order
      //--- and set the conditions depending on active buttons
      else
        {
         int id=engine.ModifyOrderPending(order.Ticket(),-1,-1,tp,-1);
         if(id>0)
           {
            //--- set the pending request activation price and time, as well as activation parameters
            double price=SymbolInfoDouble(NULL,SYMBOL_ASK);
            double price_activation=NormalizeDouble(order.PriceOpen()+(distance_pending+distance_pending_request)*g_point,g_digits);
            ENUM_COMPARER_TYPE comparer=EQUAL_OR_MORE;
            if(order.TypeByDirection()==ORDER_TYPE_SELL)
              {
               price=SymbolInfoDouble(NULL,SYMBOL_BID);
               price_activation=NormalizeDouble(order.PriceOpen()-(distance_pending+distance_pending_request)*g_point,g_digits);
               comparer=EQUAL_OR_LESS;
              }
            ulong  time_activation=TimeCurrent()+bars_delay_pending_request*PeriodSeconds();
            SetPReqCriterion((uchar)id,price_activation,time_activation,BUTT_SET_TAKE_PROFIT,comparer,price,TimeCurrent());
           }
        }
     }
  }
//+------------------------------------------------------------------+

在全部研究过的函数中,就逻辑而言,创建延后请求的代码模块均相似。 在代码中已进行了详细注释,故此请自行独立研究它们。 如果您有任何疑问,请随时在评论中提问。

编译 EA,并在测试器中以可视化模式启动它。 若要检验管单删除、以及挂单和持仓修改,请开立两笔空头仓位,并下一笔空头挂单,不要设置止损和止盈价位。 接下来,创建延后请求,按价格修改挂单和持仓的止价位。 等待激活延后请求,并设置指定的止价位,然后删除挂单和持仓。

然后开立两笔多头仓位,并下一笔多头挂单。 之后,创建删除挂单、和按时间平仓的延后请求。


正如我们所见,止价位设置在给定延后请求激活价位的交点处。 持仓在指定时间后平仓,订单被删除。

操作尚未磨合。 同时,针对同一个票据创建多个延后请求时会出现问题,因为这些请求并不总能正确工作。 当前,每笔持仓或挂单仅有一个延后请求时,该逻辑才能正常操作。 在延后请求激活之后,执行并删除,能够针对持仓或挂单(如果它们仍在场内)创建新的延后请求。

我计划一旦拥有了图形库对象后,随着函数库功能的深入发展,逐步解决此问题。

下一步是什么?

在下一篇文章中,我们将着手开发存储、处理和接收价格数据的函数库功能。 

文后附有当前版本函数库的所有文件,以及测试 EA 文件,供您测试和下载。
请在评论中留下您的问题、意见和建议。

返回目录

系列中的前几篇文章:

第一部分 概念,数据管理
第二部分 历史订单和成交集合
第三部分 在场订单和持仓集合,安排搜索
第四部分 交易事件, 概念
第五部分 交易事件类和集合。 将事件发送至程序
第六部分 净持帐户事件
第七部分 StopLimit 挂单激活事件,为订单和持仓修改事件准备功能
第八部分 订单和持仓修改事件
第九部分 与 MQL4 的兼容性 — 准备数据
第十部分 与 MQL4 的兼容性 - 开仓和激活挂单事件
第十一部分 与 MQL4 的兼容性 - 平仓事件
第十二部分 帐户对象类和帐户对象集合
第十三部分 账户对象事件
第十四部分 品种对象
第十五部份 品种对象集合
第十六部分 品种集合事件
第十七部分 函数库对象之间的交互
第十八部分 帐户与任意其他函数库对象的交互
第十九部分 函数库消息类
第二十部分 创建和存储程序资源
第二十一部分 交易类 - 基准跨平台交易对象
第二十二部分 交易类 - 基准交易类,限制验证
第二十三部分 交易类 - 基准交易类,有效参数验证
第二十四部分 交易类 - 基准交易类,无效参数的自动纠正
第二十五部分 交易类 - 基准交易类,处理交易服务器返回的错误
第二十六部分 操控延后交易请求 - 首次实现(开仓)
第二十七部分 操控延后交易请求 - 下挂单
第二十八部分 操控延后交易请求 - 平仓、删除和修改
第二十九部分 操控延后交易请求 - 请求对象类
第三十部分 延后交易请求 - 管理请求对象
第三十一部分 延后交易请求 - 在特定条件下开仓
第三十二部分 延后交易请求 - 在特定条件下放置挂单
第三十三部分 延后交易请求 - 在特定条件下平仓(全部、部分和逆向平仓)


本文译自 MetaQuotes Software Corp. 撰写的俄文原文
原文地址: https://www.mql5.com/ru/articles/7569

附加的文件 |
MQL5.zip (3666.8 KB)
MQL4.zip (3666.79 KB)
应用网络函数,或无需 DLL 的 MySQL:第 I 部分 - 连通器 应用网络函数,或无需 DLL 的 MySQL:第 I 部分 - 连通器

MetaTrader 5 最近已获增网络函数。 这为程序员开发市场所需产品提供了巨大的机遇。 如今,他们能够实现以前需要动态库支持的功能。 在本文中,我们将以 MySQL 为例研究所有的实现。

如何在 MetaTrader 5 中利用 DirectX 创建 3D 图形 如何在 MetaTrader 5 中利用 DirectX 创建 3D 图形

3D 图形为大数据分析提供了完美的方案,它可以直观透视隐藏的形态。 这些任务能以 MQL5 直接解决,而 DireсtX 函数允许创建三维物体。 故其能够为 MetaTrader 5 创建任意复杂度的程序,甚至 3D 游戏。 学习 3D 图形,从绘制简单的三维形状开始。

应用网络函数,或无需 DLL 的 MySQL:第 II 部分 - 监视信号属性变化的程序 应用网络函数,或无需 DLL 的 MySQL:第 II 部分 - 监视信号属性变化的程序

在前一部分当中,我们研究了 MySQL 连通器的实现。 在本文中,我们将研究如何实现收集信号属性的服务应用,和观察其随时间变化的程序。 如果用户需要观察并未显示在信号网页上的属性变化,则所实现的示例具有重大实际意义。

监视多币种的交易信号(第二部分):应用程序可视部分的实现 监视多币种的交易信号(第二部分):应用程序可视部分的实现

在上一篇文章中,我们已创建了应用程序框架,其可作为进一步操作的基础。 在这一部分中,我们将继续开发:创建应用程序的可视部分,并配置界面元素的基本交互。