下载MetaTrader 5

MQL5 Cookbook - 以 MQL5 编写的多币种 EA,利用限价订单工作

29 十月 2014, 09:08
Anatoli Kazharski
0
1 369

介绍

这次,我们将要创建一款多币种 EA,交易算法基于限价订单 Buy Stop(高买) 和 Sell Stop(低卖)。我们打算创建的范式,将被设计为日内交易/测试。本文讨论下列事项:

  • 在规定时间范围内进行交易。让我们来创建一个功能,可以设置交易的起止时间。例如,该时间可以是欧洲或美国的交易时段。这样可以确保在优化 EA 参数时,有机会发现更合适的时间范围。
  • 布置/修改/删除限价订单。
  • 处理交易事件: 检查最后一个持仓是否在止盈或止损位置平仓,以及在成交历史中控制每个品种。


开发 EA

我们打算利用来自文章 MQL5 Cookbook: 多币种EA交易 - 简洁而快速的途径 的代码作为模板。虽然该范式的基本结构将维持不变,但也会引入一些明显的变化。该 EA 将会被设计为日内交易,然而,这种模式可以在必要时切断。当新柱线事件出现时,如果仓位已平,则限价订单将会被即刻布置。

让我们从 EA 的外部参数开始。首先,我们在包含文件 Enums.mqh 中创建一个枚举 ENUM_HOURS。在枚举中标识符的数量等于一天中的小时数:

//--- Hours Enumeration
enum ENUM_HOURS
  {
   h00 = 0,  // 00 : 00
   h01 = 1,  // 01 : 00
   h02 = 2,  // 02 : 00
   h03 = 3,  // 03 : 00
   h04 = 4,  // 04 : 00
   h05 = 5,  // 05 : 00
   h06 = 6,  // 06 : 00
   h07 = 7,  // 07 : 00
   h08 = 8,  // 08 : 00
   h09 = 9,  // 09 : 00
   h10 = 10, // 10 : 00
   h11 = 11, // 11 : 00
   h12 = 12, // 12 : 00
   h13 = 13, // 13 : 00
   h14 = 14, // 14 : 00
   h15 = 15, // 15 : 00
   h16 = 16, // 16 : 00
   h17 = 17, // 17 : 00
   h18 = 18, // 18 : 00
   h19 = 19, // 19 : 00
   h20 = 20, // 20 : 00
   h21 = 21, // 21 : 00
   h22 = 22, // 22 : 00
   h23 = 23  // 23 : 00
  };

之后,在外部参数列表中我们将创建四个与交易时间范围相关的参数:

  • TradeInTimeRange - 启用/禁止该模式。正如已经提到的那样,我们即将制作的 EA,不仅可以在确定的时间范围内工作,也可以不间断工作,即连续模式。
  • StartTrade - 交易时段的开始小时。若 TradeInTimeRange 模式为开,一旦服务器时间等于该值时,EA 将布置限价订单。
  • StopOpenOrders - 布置订单的结束时间。当服务器时间等于该数值时,即使仓位已平,EA 都将停止布置限价订单。
  • EndTrade - 交易时段停止的小时。一旦服务器时间等于该数值,EA 停止交易。指定品种的所有持仓都将被平仓,并且所有挂单被删除。

外部参数的列表如下所示。给出的例程用于两个品种。在参数 PendingOrder 中我们设置了一个距当前价位的点数。

//--- External parameters of the Expert Advisor 
sinput long       MagicNumber       = 777;      // Magic number
sinput int        Deviation         = 10;       // Slippage
//---
sinput string delimeter_00=""; // --------------------------------
sinput string     Symbol_01            ="EURUSD";  // Symbol 1
input  bool       TradeInTimeRange_01  =true;      // |     Trading in a time range
input  ENUM_HOURS StartTrade_01        = h10;      // |     The hour of the beginning of a trading session
input  ENUM_HOURS StopOpenOrders_01    = h17;      // |     The hour  of the end of placing orders
input  ENUM_HOURS EndTrade_01          = h22;      // |     The hour of the end of a trading session
input  double     PendingOrder_01      = 50;       // |     Pending order
input  double     TakeProfit_01        = 100;      // |     Take Profit
input  double     StopLoss_01          = 50;       // |     Stop Loss
input  double     TrailingStop_01      = 10;       // |     Trailing Stop
input  bool       Reverse_01           = true;     // |     Position reversal
input  double     Lot_01               = 0.1;      // |     Lot
//---
sinput string delimeter_01=""; // --------------------------------
sinput string     Symbol_02            ="AUDUSD";  // Symbol 2
input  bool       TradeInTimeRange_02  =true;      // |     Trading in a time range
input  ENUM_HOURS StartTrade_02        = h10;      // |     The hour of the beginning of a trading session
input  ENUM_HOURS StopOpenOrders_02    = h17;      // |     The hour  of the end of placing orders
input  ENUM_HOURS EndTrade_02          = h22;      // |     The hour of the end of a trading session
input  double     PendingOrder_02      = 50;       // |     Pending order
input  double     TakeProfit_02        = 100;      // |     Take Profit
input  double     StopLoss_02          = 50;       // |     Stop Loss
input  double     TrailingStop_02      = 10;       // |     Trailing Stop
input  bool       Reverse_02           = true;     // |     Position reversal
input  double     Lot_02               = 0.1;      // |     Lot

而且相应变化也在数组列表中进行,即填充外部参数的数值:

//--- Arrays for storing external parameters
string     Symbols[NUMBER_OF_SYMBOLS];          // Symbol
bool       TradeInTimeRange[NUMBER_OF_SYMBOLS]; // Trading in a time range
ENUM_HOURS StartTrade[NUMBER_OF_SYMBOLS];       // The hour of the beginning of a trading session
ENUM_HOURS StopOpenOrders[NUMBER_OF_SYMBOLS];   // The hour  of the end of placing orders
ENUM_HOURS EndTrade[NUMBER_OF_SYMBOLS];         // The hour of the end of a trading session
double     PendingOrder[NUMBER_OF_SYMBOLS];     // Pending order
double     TakeProfit[NUMBER_OF_SYMBOLS];       // Take Profit
double     StopLoss[NUMBER_OF_SYMBOLS];         // Stop Loss
double     TrailingStop[NUMBER_OF_SYMBOLS];     // Trailing Stop
bool       Reverse[NUMBER_OF_SYMBOLS];          // Position Reversal
double     Lot[NUMBER_OF_SYMBOLS];              // Lot

现在我们打算安排为反向模式 (即 Reverse 参数值为 true),当挂单被触发,反向挂单将被删除,并且布置新的。我们不能像改变挂单的价位(价格,止损,止盈)那样来修改它的手数。因此,我们必须将其删除,并在新的挂单中使用所期望的手数。

此外,如果反向模式被启用,且在同一时间设置追踪止损位,则挂单将追随价格。在此基础之上,如果设置了止损位,则将基于挂单计算它的价值。

让我们在全局范围创建两个字符串变量用于挂单注释:

//--- Pending order comments 
string comment_top_order    ="top_order";
string comment_bottom_order ="bottom_order";

在 EA 加载期间,函数 OnInit() 进行初始化,我们将检查外部参数正确与否。评估标准如下。当 TradeInTimeRange 模式启用,交易时段的开始小时不能小于布置限价订单的结束小时。布置限价订单的结束小时,反之,也不能小于交易时段的结束小时。让我们来编写函数 CheckInputParameters() 进行这样的检查:

//+------------------------------------------------------------------+
//| Checks external parameters                                       |
//+------------------------------------------------------------------+
bool CheckInputParameters()
  {
//--- Loop through the specified symbols
   for(int s=0; s<NUMBER_OF_SYMBOLS; s++)
     {
      //--- If there is no symbol and the TradeInTimeRange mode is disabled, move on to the following symbol.
      if(Symbols[s]=="" || !TradeInTimeRange[s])
         continue;
      //--- Check the accuracy of the start and the end of a trade session time
      if(StartTrade[s]>=EndTrade[s])
        {
         Print(Symbols[s],
               ": The hour of the beginning of a trade session("+IntegerToString(StartTrade[s])+") "
               "must be less than the hour of the end of a trade session"("+IntegerToString(EndTrade[s])+")!");
         return(false);
        }
      //--- A trading session is to start no later that one hour before the hour of placing pending orders.
      //    Pending orders are to be placed no later than one hour before the hour of the end  of a trading session.
      if(StopOpenOrders[s]>=EndTrade[s] ||
         StopOpenOrders[s]<=StartTrade[s])
        {
         Print(Symbols[s],
               ": The hour of the end of placing orders ("+IntegerToString(StopOpenOrders[s])+") "
               "is to be less than the hour of the end ("+IntegerToString(EndTrade[s])+") and "
               "greater than the hour of the beginning of a trading session  ("+IntegerToString(StartTrade[s])+")!");
         return(false);
        }
     }
//--- Parameters are correct
   return(true);
  }

为了实现这个范式,我们需要函数检查是否处于交易和布置挂单的指定时间范围。我们将命名这些函数 IsInTradeTimeRange()IsInOpenOrdersTimeRange()。它们的工作都相同,唯一的区别是检查范围的上限。再往前,我们会看到这些函数将被用于何处。

//+------------------------------------------------------------------+
//| Checks if we are within the time range for trade                 |
//+------------------------------------------------------------------+
bool IsInTradeTimeRange(int symbol_number)
  {
//--- If TradeInTimeRange mode is enabled
   if(TradeInTimeRange[symbol_number])
     {
      //--- Structure of the date and time
      MqlDateTime last_date;
      //--- Get the last value of the date and time data set
      TimeTradeServer(last_date);
      //--- Outside of the allowed time range
      if(last_date.hour<StartTrade[symbol_number] ||
         last_date.hour>=EndTrade[symbol_number])
         return(false);
     }
//--- Within the allowed time range
   return(true);
  }
//+------------------------------------------------------------------+
//| Checks if we are within the time range for placing orders        |
//+------------------------------------------------------------------+
bool IsInOpenOrdersTimeRange(int symbol_number)
  {
//--- If the TradeInTimeRange mode if enabled
   if(TradeInTimeRange[symbol_number])
     {
      //--- Structure of the date and time
      MqlDateTime last_date; 
      //--- Get the last value of the date and time data set
      TimeTradeServer(last_date);
      //--- Outside the allowed time range
      if(last_date.hour<StartTrade[symbol_number] ||
         last_date.hour>=StopOpenOrders[symbol_number])
         return(false);
     }
//--- Within the allowed time range
   return(true);
  }

前面的文章中已经分析了用于接收仓位,品种和成交历史的函数。在本文中我们需要一个类似的函数用于获取限价订单的属性。在包含文件 Enums.mqh 中我们打算创建一个带有限价订单属性的枚举:

//--- Enumeration of the properties of a pending order 
enum ENUM_ORDER_PROPERTIES
  {
   O_SYMBOL          = 0,
   O_MAGIC           = 1,
   O_COMMENT         = 2,
   O_PRICE_OPEN      = 3,
   O_PRICE_CURRENT   = 4,
   O_PRICE_STOPLIMIT = 5,
   O_VOLUME_INITIAL  = 6,
   O_VOLUME_CURRENT  = 7,
   O_SL              = 8,
   O_TP              = 9,
   O_TIME_SETUP      = 10,
   O_TIME_EXPIRATION = 11,
   O_TIME_SETUP_MSC  = 12,
   O_TYPE_TIME       = 13,
   O_TYPE            = 14,
   O_ALL             = 15
  };

那么在包含文件 TradeFunctions.mqh 中我们需要编写一个带有限价订单属性的结构,并安装它:

//-- Properties of a pending order
struct pending_order_properties
  {
   string            symbol;          // Symbol
   long              magic;           // Magic number
   string            comment;         // Comment
   double            price_open;      // Price specified in the order
   double            price_current;   // Current price of the order symbol
   double            price_stoplimit; // Limit order price for the Stop Limit order
   double            volume_initial;  // Initial order volume
   double            volume_current;  // Current order volume
   double            sl;              // Stop Loss level
   double            tp;              // Take Profit level
   datetime          time_setup;      // Order placement time
   datetime          time_expiration; // Order expiration time
   datetime          time_setup_msc;  // The time of placing an order for execution in milliseconds since 01.01.1970
   datetime          type_time;       // Order lifetime
   ENUM_ORDER_TYPE   type;            // Position type
  };
//--- Variable of the order features
pending_order_properties ord;

为了得到一个甚或所有挂单的属性,我们要编写一个函数 GetPendingOrderProperties()。挂单被选中之后,我们可以利用这个功能来检索订单的属性。完成这点的方法将在下面描述。

//+------------------------------------------------------------------+
//| Retrieves the properties of the previously selected pending order|
//+------------------------------------------------------------------+
void GetPendingOrderProperties(ENUM_ORDER_PROPERTIES order_property)
  {
   switch(order_property)
     {
      case O_SYMBOL          : ord.symbol=OrderGetString(ORDER_SYMBOL);                              break;
      case O_MAGIC           : ord.magic=OrderGetInteger(ORDER_MAGIC);                               break;
      case O_COMMENT         : ord.comment=OrderGetString(ORDER_COMMENT);                            break;
      case O_PRICE_OPEN      : ord.price_open=OrderGetDouble(ORDER_PRICE_OPEN);                      break;
      case O_PRICE_CURRENT   : ord.price_current=OrderGetDouble(ORDER_PRICE_CURRENT);                break;
      case O_PRICE_STOPLIMIT : ord.price_stoplimit=OrderGetDouble(ORDER_PRICE_STOPLIMIT);            break;
      case O_VOLUME_INITIAL  : ord.volume_initial=OrderGetDouble(ORDER_VOLUME_INITIAL);              break;
      case O_VOLUME_CURRENT  : ord.volume_current=OrderGetDouble(ORDER_VOLUME_CURRENT);              break;
      case O_SL              : ord.sl=OrderGetDouble(ORDER_SL);                                      break;
      case O_TP              : ord.tp=OrderGetDouble(ORDER_TP);                                      break;
      case O_TIME_SETUP      : ord.time_setup=(datetime)OrderGetInteger(ORDER_TIME_SETUP);           break;
      case O_TIME_EXPIRATION : ord.time_expiration=(datetime)OrderGetInteger(ORDER_TIME_EXPIRATION); break;
      case O_TIME_SETUP_MSC  : ord.time_setup_msc=(datetime)OrderGetInteger(ORDER_TIME_SETUP_MSC);   break;
      case O_TYPE_TIME       : ord.type_time=(datetime)OrderGetInteger(ORDER_TYPE_TIME);             break;
      case O_TYPE            : ord.type=(ENUM_ORDER_TYPE)OrderGetInteger(ORDER_TYPE);                break;
      case O_ALL             :
         ord.symbol=OrderGetString(ORDER_SYMBOL);
         ord.magic=OrderGetInteger(ORDER_MAGIC);
         ord.comment=OrderGetString(ORDER_COMMENT);
         ord.price_open=OrderGetDouble(ORDER_PRICE_OPEN);
         ord.price_current=OrderGetDouble(ORDER_PRICE_CURRENT);
         ord.price_stoplimit=OrderGetDouble(ORDER_PRICE_STOPLIMIT);
         ord.volume_initial=OrderGetDouble(ORDER_VOLUME_INITIAL);
         ord.volume_current=OrderGetDouble(ORDER_VOLUME_CURRENT);
         ord.sl=OrderGetDouble(ORDER_SL);
         ord.tp=OrderGetDouble(ORDER_TP);
         ord.time_setup=(datetime)OrderGetInteger(ORDER_TIME_SETUP);
         ord.time_expiration=(datetime)OrderGetInteger(ORDER_TIME_EXPIRATION);
         ord.time_setup_msc=(datetime)OrderGetInteger(ORDER_TIME_SETUP_MSC);
         ord.type_time=(datetime)OrderGetInteger(ORDER_TYPE_TIME);
         ord.type=(ENUM_ORDER_TYPE)OrderGetInteger(ORDER_TYPE);                                      break;
         //---
     default: Print("Retrieved feature of the pending order was not taken into account in the enumeration ");             return;
     }
  }

现在,我们将要编写基本的布置,修改和删除挂单函数。函数 SetPendingOrder() 布置一个限价订单。如果限价订单布置失败,函数在日志中加入一条,并有错误代码和描述:

//+------------------------------------------------------------------+
//| Places a pending order                                           |
//+------------------------------------------------------------------+
void SetPendingOrder(int                  symbol_number,   // Symbol number
                     ENUM_ORDER_TYPE      order_type,      // Order type
                     double               lot,             // Volume
                     double               stoplimit_price, // Level of the StopLimit order 
                     double               price,           // Price
                     double               sl,              // Stop Loss
                     double               tp,              // Take Profit
                     ENUM_ORDER_TYPE_TIME type_time,       // Order Expiration
                     string               comment)         // Comment
//--- Set magic number in the trade structure
   trade.SetExpertMagicNumber(MagicNumber);
//--- If a pending order failed to be placed, print an error message  
   if(!trade.OrderOpen(Symbols[symbol_number],
                       order_type,lot,stoplimit_price,price,sl,tp,type_time,0,comment))
      Print("Error when placing a pending order: ",GetLastError()," - ",ErrorDescription(GetLastError()));
  }

函数 ModifyPendingOrder() 修改一个限价订单。我们这样安排,不仅能修改订单价格,而且可以修改手数,还能将它作为最后传递给函数的参数。如果传递的手数值大于零,则意味着挂单已被删除,并布置一个新的所需手数的订单。在所有其余情况下,我们只需简单地修改已存在订单的价位。

//+------------------------------------------------------------------+
//| Modifies a pending order                                         |
//+------------------------------------------------------------------+
void ModifyPendingOrder(int                  symbol_number,   //Symbol number
                        ulong                ticket,          // Order ticket
                        ENUM_ORDER_TYPE      type,            // Order type
                        double               price,           // Order price
                        double               sl,              // Stop Loss of the order
                        double               tp,              // Take Profit of the order
                        ENUM_ORDER_TYPE_TIME type_time,       // Order expiration
                        datetime             time_expiration, // Order expiration time
                        double               stoplimit_price, // Price
                        string               comment,         // Comment
                        double               volume)          // Volume
  {
//--- If the passed volume value is non-zero, delete the order and place it again
   if(volume>0)
     {
      //--- If the order failed to be deleted, exit
      if(!DeletePendingOrder(ticket))
         return;
      //--- Place a pending order
      SetPendingOrder(symbol_number,type,volume,0,price,sl,tp,type_time,comment);
      //--- Adjust Stop Loss positions as related to the order
      CorrectStopLossByOrder(symbol_number,price,type);
     }
//--- If the passed volume value is zero, modify the order
   else
     {
      //--- If the pending order failed to be modified, print a relevant message
      if(!trade.OrderModify(ticket,price,sl,tp,type_time,time_expiration,stoplimit_price))
         Print("Error when modifying the pending order price: ",
         GetLastError()," - ",ErrorDescription(GetLastError()));
      //--- Otherwise adjust Stop Loss positions as related to the order
      else
         CorrectStopLossByOrder(symbol_number,price,type);
     }
  }

在上面的代码中突显了两个新函数 DeletePendingOrder()CorrectStopLossByOrder()。第一个是删除挂单,第二个是调整与挂单相关的仓位的止损和止盈。

//+------------------------------------------------------------------+
//| Deletes a pending order                                          | 
//+------------------------------------------------------------------+
bool DeletePendingOrder(ulong ticket)
  {

//--- If a pending order failed to get deleted, print a relevant message
   if(!trade.OrderDelete(ticket))
     {
      Print("Error when deleting a pending order: ",GetLastError()," - ",ErrorDescription(GetLastError()));
      return(false);
     }
//---
   return(true);
  }
//+------------------------------------------------------------------+
//| Modifies StopLoss of the position as related to the pending order|
//+------------------------------------------------------------------+
void CorrectStopLossByOrder(int             symbol_number, // Symbol number
                            double          price,         // Order Price
                            ENUM_ORDER_TYPE type)          // Order Type
  {
//--- If Stop Loss disabled, exit
   if(StopLoss[symbol_number]==0)
      return;
//--- If Stop Loss enabled
   double new_sl=0.0; // New Stop Loss value
//--- Get a Point value
   GetSymbolProperties(symbol_number,S_POINT);
//--- Number of decimal places
   GetSymbolProperties(symbol_number,S_DIGITS);
//--- Get Take Profit positions
   GetPositionProperties(symbol_number,P_TP);
//--- Calculate as related to the order type
   switch(type)
     {
      case ORDER_TYPE_BUY_STOP  :
         new_sl=NormalizeDouble(price+CorrectValueBySymbolDigits(StopLoss[symbol_number]*symb.point),symb.digits);
         break;
      case ORDER_TYPE_SELL_STOP :
         new_sl=NormalizeDouble(price-CorrectValueBySymbolDigits(StopLoss[symbol_number]*symb.point),symb.digits);
         break;
     }
//--- Modify the position
   if(!trade.PositionModify(Symbols[symbol_number],new_sl,pos.tp))
      Print("Error when modifying position: ",GetLastError()," - ",ErrorDescription(GetLastError()));
  }

在布置限价订单之前,有必要检查一下是否存在相同注释的挂单。如同本文开始提到的那样,我们将布置一个注释为 "top_order" 的 Buy Stop 订单和一个注释为 "bottom_order" 的 Sell Stop 订单。为了方便检查让我们写一个名为 CheckPendingOrderByComment() 的函数:

//+------------------------------------------------------------------+
//| Checks existence of a pending order by a comment                 |
//+------------------------------------------------------------------+
bool CheckPendingOrderByComment(int symbol_number,string comment)
  {
   int    total_orders  =0;  // Total number of pending orders
   string order_symbol  =""; // Order Symbol
   string order_comment =""; // Order Comment
//--- Get the total number of pending orders
   total_orders=OrdersTotal();
//--- Loop through the total orders
   for(int i=total_orders-1; i>=0; i--)
     {
      //---Select the order by the ticket
      if(OrderGetTicket(i)>0)
        {
         //--- Get the symbol name
         order_symbol=OrderGetString(ORDER_SYMBOL);
         //--- If the symbols are equal
         if(order_symbol==Symbols[symbol_number])
           {
            //--- Get the order comment
            order_comment=OrderGetString(ORDER_COMMENT);
            //--- If the comments are equal
            if(order_comment==comment)
               return(true);
           }
        }
     }
//--- Order with a specified comment not found
   return(false);
  }

以上代码显示订单总计数量,可以使用系统函数 OrdersTotal() 获得。但是,要获得指定品种的挂单总数,我们要编写一个用户自定义的函数。我们将它命名为 OrdersTotalBySymbol():

//+------------------------------------------------------------------+
//| Returns the total number of orders for the specified symbol      |
//+------------------------------------------------------------------+
int OrdersTotalBySymbol(string symbol)
  {
   int   count        =0; // Order counter
   int   total_orders =0; // Total number of pending orders
//--- Get the total number of pending orders
   total_orders=OrdersTotal();
//--- Loop through the total number of orders
   for(int i=total_orders-1; i>=0; i--)
     {
      //--- If an order has been selected
      if(OrderGetTicket(i)>0)
        {
         //--- Get the order symbol
         GetOrderProperties(O_SYMBOL);
         //--- If the order symbol and the specified symbol are equal
         if(ord.symbol==symbol)
            //--- Increase the counter
            count++;
        }
     }
//--- Return the total number of orders
   return(count);
  }

在布置限价订单之前,有必要计算一下是否需要止损和止盈位。如果反向模式启用,我们需要单独的用户自定义函数来重新计算和修改追随止损位。

为了计算限价订单价格,让我们来编写函数 CalculatePendingOrder():

//+------------------------------------------------------------------+
//| Calculates the pending order level(price)                        |
//+------------------------------------------------------------------+
double CalculatePendingOrder(int symbol_number,ENUM_ORDER_TYPE order_type)
  {
//--- For the calculated pending order value
   double price=0.0;
//--- If the value for SELL STOP order is to be calculated
   if(order_type==ORDER_TYPE_SELL_STOP)
     {
      //--- Calculate level
      price=NormalizeDouble(symb.bid-CorrectValueBySymbolDigits(PendingOrder[symbol_number]*symb.point),symb.digits);
      //--- Return calculated value if it is less than the lower limit of Stops level
      //    If the value is equal or greater, return the adjusted value
      return(price<symb.down_level ? price : symb.down_level-symb.offset);
     }
//--- If the value for BUY STOP order is to be calculated
   if(order_type==ORDER_TYPE_BUY_STOP)
     {
      //--- Calculate level
      price=NormalizeDouble(symb.ask+CorrectValueBySymbolDigits(PendingOrder[symbol_number]*symb.point),symb.digits);
      //--- Return the calculated value if it is greater than the upper limit of Stops level
      //    If the value is equal or less, return the adjusted value
      return(price>symb.up_level ? price : symb.up_level+symb.offset);
     }
//---
   return(0.0);
  }

以下函数代码计算挂单的止损和止盈位。

//+------------------------------------------------------------------+
//| Calculates Stop Loss level for a pending order                   |
//+------------------------------------------------------------------+
double CalculatePendingOrderStopLoss(int symbol_number,ENUM_ORDER_TYPE order_type,double price)
  {
//--- If Stop Loss is required
   if(StopLoss[symbol_number]>0)
     {
      double sl         =0.0; // For the Stop Loss calculated value
      double up_level   =0.0; // Upper limit of Stop Levels
      double down_level =0.0; // Lower limit of Stop Levels
      //--- If the value for BUY STOP order is to be calculated
      if(order_type==ORDER_TYPE_BUY_STOP)
        {
         //--- Define lower threshold
         down_level=NormalizeDouble(price-symb.stops_level*symb.point,symb.digits);
         //--- Calculate level
         sl=NormalizeDouble(price-CorrectValueBySymbolDigits(StopLoss[symbol_number]*symb.point),symb.digits);
         //--- Return the calculated value if it is less than the lower limit of Stop level
         //    If the value is equal or greater, return the adjusted value
         return(sl<down_level ? sl : NormalizeDouble(down_level-symb.offset,symb.digits));
        }
      //--- If the value for the SELL STOP order is to be calculated
      if(order_type==ORDER_TYPE_SELL_STOP)
        {
         //--- Define the upper threshold
         up_level=NormalizeDouble(price+symb.stops_level*symb.point,symb.digits);
         //--- Calculate the level
         sl=NormalizeDouble(price+CorrectValueBySymbolDigits(StopLoss[symbol_number]*symb.point),symb.digits);
         //--- Return the calculated value if it is greater than the upper limit of the Stops level
         //    If the value is less or equal, return the adjusted value.
         return(sl>up_level ? sl : NormalizeDouble(up_level+symb.offset,symb.digits));
        }
     }
//---
   return(0.0);
  }
//+------------------------------------------------------------------+
//| Calculates the Take Profit level for a pending order             |
//+------------------------------------------------------------------+
double CalculatePendingOrderTakeProfit(int symbol_number,ENUM_ORDER_TYPE order_type,double price)
  {
//--- If Take Profit is required
   if(TakeProfit[symbol_number]>0)
     {
      double tp         =0.0; // For the calculated Take Profit value
      double up_level   =0.0; // Upper limit of Stop Levels
      double down_level =0.0; // Lower limit of Stop Levels
      //--- If the value for SELL STOP order is to be calculated
      if(order_type==ORDER_TYPE_SELL_STOP)
        {
         //--- Define lower threshold
         down_level=NormalizeDouble(price-symb.stops_level*symb.point,symb.digits);
         //--- Calculate the level
         tp=NormalizeDouble(price-CorrectValueBySymbolDigits(TakeProfit[symbol_number]*symb.point),symb.digits);
         //--- Return the calculated value if it is less than the below limit of the Stops level
         //    If the value is greater or equal, return the adjusted value
         return(tp<down_level ? tp : NormalizeDouble(down_level-symb.offset,symb.digits));
        }
      //--- If the value for the BUY STOP order is to be calculated
      if(order_type==ORDER_TYPE_BUY_STOP)
        {
         //--- Define the upper threshold
         up_level=NormalizeDouble(price+symb.stops_level*symb.point,symb.digits);
         //--- Calculate the level
         tp=NormalizeDouble(price+CorrectValueBySymbolDigits(TakeProfit[symbol_number]*symb.point),symb.digits);
         //--- Return the calculated value if it is greater than the upper limit of the Stops level
         //    If the value is less or equal, return the adjusted value
         return(tp>up_level ? tp : NormalizeDouble(up_level+symb.offset,symb.digits));
        }
     }
//---
   return(0.0);
  }

为了计算一个反向挂单的停止位 (价格),我们要编写以下函数 CalculateReverseOrderTrailingStop()ModifyPendingOrderTrailingStop()。您可以在下面找到函数代码。

函数代码 CalculateReverseOrderTrailingStop():

//+----------------------------------------------------------------------------+
//| Calculates the Trailing Stop level for the reversed order                  |
//+----------------------------------------------------------------------------+
double CalculateReverseOrderTrailingStop(int symbol_number,ENUM_POSITION_TYPE position_type)
  {
//--- Variables for calculation
   double    level       =0.0;
   double    buy_point   =low[symbol_number].value[1];  // Low value for Buy
   double    sell_point  =high[symbol_number].value[1]; // High value for Sell
//--- Calculate the level for the BUY position
   if(position_type==POSITION_TYPE_BUY)
     {
      //--- Bar's low minus the specified number of points
      level=NormalizeDouble(buy_point-CorrectValueBySymbolDigits(PendingOrder[symbol_number]*symb.point),symb.digits);
      //---  If the calculated level is lower than the lower limit of the Stops level, 
      //    the calculation is complete, return the current value of the level
      if(level<symb.down_level)
         return(level);
      //--- If it is not lower, try to calculate based on the bid price
      else
        {
         level=NormalizeDouble(symb.bid-CorrectValueBySymbolDigits(PendingOrder[symbol_number]*symb.point),symb.digits);
         //--- If the calculated level is lower than the limit, return the current value of the level
         //    otherwise set the nearest possible value
         return(level<symb.down_level ? level : symb.down_level-symb.offset);
        }
     }
//--- Calculate the level for the SELL position
   if(position_type==POSITION_TYPE_SELL)
     {
      // Bar's high plus the specified number of points
      level=NormalizeDouble(sell_point+CorrectValueBySymbolDigits(PendingOrder[symbol_number]*symb.point),symb.digits);
      //--- If the calculated level is higher than the upper limit of the Stops level, 
      //    then the calculation is complete, return the current value of the level
      if(level>symb.up_level)
         return(level);
      //--- If it is not higher, try to calculate based on the ask price
      else
        {
         level=NormalizeDouble(symb.ask+CorrectValueBySymbolDigits(PendingOrder[symbol_number]*symb.point),symb.digits);
         //--- If the calculated level is higher than the limit, return the current value of the level
         //    Otherwise set the nearest possible value
         return(level>symb.up_level ? level : symb.up_level+symb.offset);
        }
     }
//---
   return(0.0);
  }

函数代码 ModifyPendingOrderTrailingStop():

//+------------------------------------------------------------------+
//| Modifying the Trailing Stop level for a pending order            |
//+------------------------------------------------------------------+
void ModifyPendingOrderTrailingStop(int symbol_number)
  {
//--- Exit, if the reverse position mode is disabled and Trailing Stop is not set
   if(!Reverse[symbol_number] || TrailingStop[symbol_number]==0)
      return;
//--- 
   double          new_level              =0.0;         // For calculating a new level for a pending order
   bool            condition              =false;       // For checking the modificating condition
   int             total_orders           =0;           // Total number of pending orders
   ulong           order_ticket           =0;           // Order ticket
   string          opposite_order_comment ="";          // Opposite order comment
   ENUM_ORDER_TYPE opposite_order_type    =WRONG_VALUE; // Order type

//--- Get the flag of presence/absence of a position
   pos.exists=PositionSelect(Symbols[symbol_number]);
//--- If a position is absent
   if(!pos.exists)
      return;
//--- Get a total number of pending orders
   total_orders=OrdersTotal();
//--- Get the symbol properties
   GetSymbolProperties(symbol_number,S_ALL);
//--- Get the position properties
   GetPositionProperties(symbol_number,P_ALL);
//--- Get the level for Stop Loss
   new_level=CalculateReverseOrderTrailingStop(symbol_number,pos.type);
//--- Loop through the orders from the last to the first one
   for(int i=total_orders-1; i>=0; i--)
     {
      //--- If the order selected
      if((order_ticket=OrderGetTicket(i))>0)
        {
         //--- Get the order symbol
         GetPendingOrderProperties(O_SYMBOL);
         //--- Get the order comment
         GetPendingOrderProperties(O_COMMENT);
         //--- Get the order price
         GetPendingOrderProperties(O_PRICE_OPEN);
         //--- Depending on the position type, check the relevant condition for the Trailing Stop modification
         switch(pos.type)
           {
            case POSITION_TYPE_BUY  :
               //---If the new order value is greater than the current value plus set step then condition fulfilled 
               condition=new_level>ord.price_open+CorrectValueBySymbolDigits(TrailingStop[symbol_number]*symb.point);
               //--- Define the type and comment of the reversed pending order for check.
               opposite_order_type    =ORDER_TYPE_SELL_STOP;
               opposite_order_comment =comment_bottom_order;
               break;
            case POSITION_TYPE_SELL :
               //--- If the new value for the order if less than the current value minus a set step then condition fulfilled
               condition=new_level<ord.price_open-CorrectValueBySymbolDigits(TrailingStop[symbol_number]*symb.point);
               //--- Define the type and comment of the reversed pending order for check
               opposite_order_type    =ORDER_TYPE_BUY_STOP;
               opposite_order_comment =comment_top_order;
               break;
           }
         //--- If condition fulfilled, the order symbol and positions are equal
         //    and order comment and the reversed order comment are equal
         if(condition && 
            ord.symbol==Symbols[symbol_number] && 
            ord.comment==opposite_order_comment)
           {
            double sl=0.0; // Stop Loss
            double tp=0.0; // Take Profit
            //--- Get Take Profit and Stop Loss levels
            sl=CalculatePendingOrderStopLoss(symbol_number,opposite_order_type,new_level);
            tp=CalculatePendingOrderTakeProfit(symbol_number,opposite_order_type,new_level);
            //--- Modify order
            ModifyPendingOrder(symbol_number,order_ticket,opposite_order_type,new_level,sl,tp,
                               ORDER_TIME_GTC,ord.time_expiration,ord.price_stoplimit,ord.comment,0);
            return;
           }
        }
     }
  }

某些时候,有必要找出一个仓位是否在止损或止盈位置平仓。在这种特殊情况下,我们将遇到这样的需求。所以让我们编写函数,通过最后的成交注释来识别这个事件。要获取指定品种的最后成交注释,我们要编写一个单独的函数,名为 GetLastDealComment():

//+------------------------------------------------------------------+
//| Returns a the last deal comment for a specified symbol           |
//+------------------------------------------------------------------+
string GetLastDealComment(int symbol_number)
  {
   int    total_deals  =0;  // Total number of deals in the selected history
   string deal_symbol  =""; // Deal symbol 
   string deal_comment =""; // Deal comment
//--- If the deals history retrieved
   if(HistorySelect(0,TimeCurrent()))
     {
      //--- Receive the number of deals in the retrieved list
      total_deals=HistoryDealsTotal();
      //--- Loop though the total number of deals in the retrieved list from the last deal to the first one.
      for(int i=total_deals-1; i>=0; i--)
        {
         //--- Receive the deal comment
         deal_comment=HistoryDealGetString(HistoryDealGetTicket(i),DEAL_COMMENT);
         //--- Receive the deal symbol
         deal_symbol=HistoryDealGetString(HistoryDealGetTicket(i),DEAL_SYMBOL);
         //--- If the deal symbol and the current symbol are equal, stop the loop
         if(deal_symbol==Symbols[symbol_number])
            break;
        }
     }
//---
   return(deal_comment);
  }

现在,很容易编写函数来检测指定品种的最后仓位的平仓原因。以下代码是这些函数 IsClosedByTakeProfit()IsClosedByStopLoss():

//+------------------------------------------------------------------+
//| Returns the reason for closing position at Take Profit           |
//+------------------------------------------------------------------+
bool IsClosedByTakeProfit(int symbol_number)
  {
   string last_comment="";
//--- Get the last deal comment for the specified symbol
   last_comment=GetLastDealComment(symbol_number);
//--- If the comment contain a string "tp"
   if(StringFind(last_comment,"tp",0)>-1)
      return(true);
//--- If the comment does not contain a string "tp"
   return(false);
  }
//+------------------------------------------------------------------+
//| Returns the reason for closing position at Stop Loss             |
//+------------------------------------------------------------------+
bool IsClosedByStopLoss(int symbol_number)
  {
   string last_comment="";
//--- Get the last deal comment for the specified symbol
   last_comment=GetLastDealComment(symbol_number);
//--- If the comment contains the string "sl"
   if(StringFind(last_comment,"sl",0)>-1)
      return(true);
//--- If the comment does not contain the string "sl"
   return(false);
  }

我们将进行另一次检查,以确定指定品种在成交历史中的最后一笔记录是否为真实的成交。我们要在内存中保留最后一笔成交的单号。要实现这个目的,我们在全局范围内添加一个数组:

//--- Array for checking the ticket of the last deal for each symbol.
ulong last_deal_ticket[NUMBER_OF_SYMBOLS];

函数 IsLastDealTicket() 用于检查最后成交的单号,其代码如下:

//+------------------------------------------------------------------+
//| Returns the event of the last deal for the specified symbol      |
//+------------------------------------------------------------------+
bool IsLastDealTicket(int symbol_number)
  {
   int    total_deals =0;  // Total number of deals in the selected history list
   string deal_symbol =""; // Deal symbol
   ulong  deal_ticket =0;  // Deal ticket
//--- If the deal history was received
   if(HistorySelect(0,TimeCurrent()))
     {
      //--- Get the total number of deals in the received list
      total_deals=HistoryDealsTotal();
      //--- Loop through the total number of deals from the last deal to the first one
      for(int i=total_deals-1; i>=0; i--)
        {
         //--- Get deal ticket
         deal_ticket=HistoryDealGetTicket(i);
         //--- Get deal symbol
         deal_symbol=HistoryDealGetString(deal_ticket,DEAL_SYMBOL);
         //--- If deal symbol and the current one are equal, stop the loop
         if(deal_symbol==Symbols[symbol_number])
           {
            //--- If the tickets are equal, exit
            if(deal_ticket==last_deal_ticket[symbol_number])
               return(false);
            //--- If the tickets are not equal report it
            else
              {
               //--- Save the last deal ticket
               last_deal_ticket[symbol_number]=deal_ticket;
               return(true);
              }
           }
        }
     }
//---
   return(false);
  }

如果当前时间超出指定交易范围,无论盈亏与否,持仓将被强制平仓。让我们编写这个函数 ClosePosition() 来平仓:

//+------------------------------------------------------------------+
//| Closes position                                                  |
//+------------------------------------------------------------------+
void ClosePosition(int symbol_number)
  {
//--- Check if position exists  
   pos.exists=PositionSelect(Symbols[symbol_number]);
//--- If there is no position, exit
   if(!pos.exists)
      return;
//--- Set the slippage value in points
   trade.SetDeviationInPoints(CorrectValueBySymbolDigits(Deviation));
//--- If the position was not closed, print the relevant message
   if(!trade.PositionClose(Symbols[symbol_number]))
      Print("Error when closing position: ",GetLastError()," - ",ErrorDescription(GetLastError()));
  }

当超出交易时间范围后,持仓被平仓,所有挂单也必须一并删除。函数 DeleteAllPendingOrders() 就是为此编写的,它删除所有指定品种的挂单:

//+------------------------------------------------------------------+
//| Deletes all pending orders                                       |
//+------------------------------------------------------------------+
void DeleteAllPendingOrders(int symbol_number)
  {
   int   total_orders =0; // Total number of pending orders
   ulong order_ticket =0; // Order ticket
//--- Get the total number of pending orders
   total_orders=OrdersTotal();
//--- Loop through the total number of pending orders
   for(int i=total_orders-1; i>=0; i--)
     {
      //--- If the order selected
      if((order_ticket=OrderGetTicket(i))>0)
        {
         //--- Get the order symbol
         GetOrderProperties(O_SYMBOL);
         //--- If the order symbol and the current symbol are equal
         if(ord.symbol==Symbols[symbol_number])
            //--- Delete the order
            DeletePendingOrder(order_ticket);
        }
     }
  }

所以,我们现在拥有了支持结构化方案的所有必要函数。让我们来看看,经过一些明显变化并增加用于管理挂单的新函数 ManagePendingOrders(),曾经熟悉的函数 TradingBlock() 不见了。在此,将依照挂单的当前状况进行全面控制。

函数 TradingBlock() 用于当前范式,如下所示:

//+------------------------------------------------------------------+
//| Trade block                                                      |
//+------------------------------------------------------------------+
void TradingBlock(int symbol_number)
  {
   double          tp=0.0;                 // Take Profit
   double          sl=0.0;                 // Stop Loss
   double          lot=0.0;                // Volume for position calculation in case of reversed position
   double          order_price=0.0;        // Price for placing the order
   ENUM_ORDER_TYPE order_type=WRONG_VALUE; // Order type for opening position
//--- If outside of the time range for placing pending orders
   if(!IsInOpenOrdersTimeRange(symbol_number))
      return;
//--- Find out if there is an open position for the symbol
   pos.exists=PositionSelect(Symbols[symbol_number]);
//--- If there is no position
   if(!pos.exists)
     {
      //--- Get symbol properties
      GetSymbolProperties(symbol_number,S_ALL);
      //--- Adjust the volume
      lot=CalculateLot(symbol_number,Lot[symbol_number]);
      //--- If there is no upper pending order
      if(!CheckPendingOrderByComment(symbol_number,comment_top_order))
        {
         //--- Get the price for placing a pending order
         order_price=CalculatePendingOrder(symbol_number,ORDER_TYPE_BUY_STOP);
         //--- Get Take Profit and Stop Loss levels
         sl=CalculatePendingOrderStopLoss(symbol_number,ORDER_TYPE_BUY_STOP,order_price);
         tp=CalculatePendingOrderTakeProfit(symbol_number,ORDER_TYPE_BUY_STOP,order_price);
         //--- Place a pending order
         SetPendingOrder(symbol_number,ORDER_TYPE_BUY_STOP,lot,0,order_price,sl,tp,ORDER_TIME_GTC,comment_top_order);
        }
      //--- If there is no lower pending order
      if(!CheckPendingOrderByComment(symbol_number,comment_bottom_order))
        {
         //--- Get the price for placing the pending order
         order_price=CalculatePendingOrder(symbol_number,ORDER_TYPE_SELL_STOP);
         //--- Get Take Profit and Stop Loss levels
         sl=CalculatePendingOrderStopLoss(symbol_number,ORDER_TYPE_SELL_STOP,order_price);
         tp=CalculatePendingOrderTakeProfit(symbol_number,ORDER_TYPE_SELL_STOP,order_price);
         //--- Place a pending order
         SetPendingOrder(symbol_number,ORDER_TYPE_SELL_STOP,lot,0,order_price,sl,tp,ORDER_TIME_GTC,comment_bottom_order);
        }
     }
  }

函数代码 ManagePendingOrders() 用于管理挂单:

//+------------------------------------------------------------------+
//| Manages pending orders                                           |
//+------------------------------------------------------------------+
void ManagePendingOrders()
  {
//--- Loop through the total number of symbols
   for(int s=0; s<NUMBER_OF_SYMBOLS; s++)
     {
      //--- If trading this symbol is forbidden, go to the following one
      if(Symbols[s]=="")
         continue;
      //--- Find out if there is an open position for the symbol
      pos.exists=PositionSelect(Symbols[s]);
      //--- If there is no position
      if(!pos.exists)
        {
         //--- If the last deal on current symbol and
         //    position  was exited on Take Profit or Stop Loss
         if(IsLastDealTicket(s) && 
            (IsClosedByStopLoss(s) || IsClosedByTakeProfit(s)))
            //--- Delete all pending orders for the symbol
            DeleteAllPendingOrders(s);
         //--- Go to the following symbol
         continue;
        }
      //--- If there is a position
      ulong           order_ticket           =0;           // Order ticket
      int             total_orders           =0;           // Total number of pending orders
      int             symbol_total_orders    =0;           // Number of pending orders for the specified symbol
      string          opposite_order_comment ="";          // Opposite order comment
      ENUM_ORDER_TYPE opposite_order_type    =WRONG_VALUE; // Order type
      //--- Get the total number of pending orders
      total_orders=OrdersTotal();
      //--- Get the total number of pending orders for the specified symbol
      symbol_total_orders=OrdersTotalBySymbol(Symbols[s]);
      //--- Get symbol properties
      GetSymbolProperties(s,S_ASK);
      GetSymbolProperties(s,S_BID);
      //--- Get the comment for the selected position
      GetPositionProperties(s,P_COMMENT);
      //--- If the position comment belongs to the upper order,
      //    then the lower order is to be deleted, modified/placed
      if(pos.comment==comment_top_order)
        {
         opposite_order_type    =ORDER_TYPE_SELL_STOP;
         opposite_order_comment =comment_bottom_order;
        }
      //--- If the position comment belongs to the lower order,
      //    then the upper order is to be deleted/modified/placed
      if(pos.comment==comment_bottom_order)
        {
         opposite_order_type    =ORDER_TYPE_BUY_STOP;
         opposite_order_comment =comment_top_order;
        }
      //--- If there are no pending orders for the specified symbol
      if(symbol_total_orders==0)
        {
         //--- If the position reversal is enabled, place a reversed order
         if(Reverse[s])
           {
            double tp=0.0;          // Take Profit
            double sl=0.0;          // Stop Loss
            double lot=0.0;         // Volume for position calculation in case of reversed positio
            double order_price=0.0; // Price for placing the order
            //--- Get the price for placing a pending order
            order_price=CalculatePendingOrder(s,opposite_order_type);
            //---Get Take Profit и Stop Loss levels
            sl=CalculatePendingOrderStopLoss(s,opposite_order_type,order_price);
            tp=CalculatePendingOrderTakeProfit(s,opposite_order_type,order_price);
            //--- Calculate double volume
            lot=CalculateLot(s,pos.volume*2);
            //--- Place the pending order
            SetPendingOrder(s,opposite_order_type,lot,0,order_price,sl,tp,ORDER_TIME_GTC,opposite_order_comment);
            //--- Adjust Stop Loss as related to the order
            CorrectStopLossByOrder(s,order_price,opposite_order_type);
           }
         return;
        }
      //--- If there are pending orders for this symbol, then depending on the circumstances delete or
      //    modify the reversed order
      if(symbol_total_orders>0)
        {
         //--- Loop through the total number of orders from the last one to the first one
         for(int i=total_orders-1; i>=0; i--)
           {
            //--- If the order chosen
            if((order_ticket=OrderGetTicket(i))>0)
              {
               //--- Get the order symbol
               GetPendingOrderProperties(O_SYMBOL);
               //--- Get the order comment
               GetPendingOrderProperties(O_COMMENT);
               //--- If order symbol and position symbol are equal,
               //    and order comment and the reversed order comment are equal
               if(ord.symbol==Symbols[s] && 
                  ord.comment==opposite_order_comment)
                 {
                  //--- If position reversal is disabled
                  if(!Reverse[s])
                     //--- Delete order
                     DeletePendingOrder(order_ticket);
                  //--- If position reversal is enabled
                  else
                    {
                     double lot=0.0;
                     //--- Get the current order properties
                     GetPendingOrderProperties(O_ALL);
                     //--- Get the current position volume
                     GetPositionProperties(s,P_VOLUME);
                     //--- If the order has been modified already, exit the loop.
                     if(ord.volume_initial>pos.volume)
                        break;
                     //--- Calculate double volume
                     lot=CalculateLot(s,pos.volume*2);
                     //--- Modify (delete and place again) the order
                     ModifyPendingOrder(s,order_ticket,opposite_order_type,
                                        ord.price_open,ord.sl,ord.tp,
                                        ORDER_TIME_GTC,ord.time_expiration,
                                        ord.price_stoplimit,opposite_order_comment,lot);
                    }
                 }
              }
           }
        }
     }
  }

现在我们只需要在主程序文件中进行微调。我们将添加交易事件处理器 OnTrade()。在此函数中,将针对与交易事件相关的挂单进行当前状况评估。

//+------------------------------------------------------------------+
//| Processing of trade events                                       |
//+------------------------------------------------------------------+
void OnTrade()
  {
//--- Check the state of pending orders
   ManagePendingOrders();
  }

函数 ManagePendingOrders() 也同样用于用户事件处理器 OnChartEvent():

//+------------------------------------------------------------------+
//| User events and chart events handler                             |
//+------------------------------------------------------------------+
void OnChartEvent(const int id,         // Event identifier
                  const long &lparam,   // Parameter of long event type
                  const double &dparam, // Parameter of double event type
                  const string &sparam) // Parameter of string event type
  {
//--- If it is a user event
   if(id>=CHARTEVENT_CUSTOM)
     {
      //--- Exit, if trade is prohibited
      if(CheckTradingPermission()>0)
         return;
      //--- If it is a tick event
      if(lparam==CHARTEVENT_TICK)
        {
         //--- Check the state of pending orders
         ManagePendingOrders();
         //--- Check signals and trade according to them
         CheckSignalsAndTrade();
         return;
        }
     }
  }

一些变化也同样在函数 CheckSignalsAndTrade() 里产生。在以下代码里,突显的字符串是本文中分析的新函数。

//+------------------------------------------------------------------+
//| Checks signals and trades based on New Bar event                 |
//+------------------------------------------------------------------+
void CheckSignalsAndTrade()
  {
//--- Loop through all specified signals
   for(int s=0; s<NUMBER_OF_SYMBOLS; s++)
     {
      //--- If trading this symbol is prohibited, exit
      if(Symbols[s]=="")
         continue;
      //--- If the bar is not new, move on to the following symbol
      if(!CheckNewBar(s))
         continue;
      //--- If there is a new bar
      else
        {
         //--- If outwith the time range
         if(!IsInTradeTimeRange(s))
           {
            //--- Close position
            ClosePosition(s);
            //--- Delete all pending orders
            DeleteAllPendingOrders(s);
            //--- Move on to the following symbol
            continue;
           }
         //--- Get bars data
         GetBarsData(s);
         //--- Check conditions and trade
         TradingBlock(s);
         //--- If position reversal if enabled
         if(Reverse[s])
            //--- Pull up Stop Loss for pending order
            ModifyPendingOrderTrailingStop(s);
         //--- If position reversal is disabled
         else
         //--- Pull up Stop Loss
            ModifyTrailingStop(s);
        }
     }
  

现在万事俱备,我们可以尝试优化这个多币种 EA 的参数,让我们按照如下所示来设置策略测试:

图例. 1 - 参数优化测试设置。

图例. 1 - 参数优化测试设置。

首先我们针对当前货币对 EURUSD 优化参数,之后是 AUDUSD。以下屏幕截图示意我们将选择 EURUSD 的哪些参数进行优化:

图例. 2 - 设置多币种 EA 的优化参数

图例. 2 - 设置多币种 EA 的优化参数

在货币对 EURUSD 的参数进行优化以后,同样对 AUDUSD 的参数进行优化。以下是两个品种一并测试的结果。选择最大恢复因子的结果。为了进行测试,两个品种的手数值均设为 1

图例. 3 - 两个品种一并测试的结果。

图例. 3 - 两个品种一并测试的结果。


结论

有关东西就这么多了。有了现成函数在手,您可以集中精力发展制定交易决策的思路。在此情况下,变化将在函数 TradingBlock()ManagePendingOrders() 里实现。对于那些最近开始学习 MQL5 的人,我们建议在练习时增加更多的品种并改变交易算法方案。

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

附加的文件 |
755.set (1.59 KB)
如何从 MQL5 (MQL4) 访问 MySQL 数据库 如何从 MQL5 (MQL4) 访问 MySQL 数据库

本文描述开发一个在 MQL 与 MySQL 之间的接口。它讨论了现有的可行解决方案,并采用更便捷的途径来实现与数据库协同工作的链接库。本文包括功能的详尽描述,接口结构,例程,以及一些使用 MySQL 时的特性。作为软件解决方案,本文附件中包含了用于 MQL4 和 MQL5 语言的动态库,文档和脚本例程。

MQL5 Cookbook: 处理自定义图表事件 MQL5 Cookbook: 处理自定义图表事件

本文研究在 MQL5 环境里设计和开发自定义图表事件的内容。一种事件分类的例程也可以在这里找到,当然,还有事件类,以及自定义事件处理器类的程序代码。

为何在 MetaTrader 4 与 MetaTrader 5 上的虚拟托管优于一般的 VPS 为何在 MetaTrader 4 与 MetaTrader 5 上的虚拟托管优于一般的 VPS

虚拟托管云网络是专为 MetaTrader 4 和 MetaTrader 5 平台研发的,并拥有许多本地解决方案。获得我们的 24 小时免费服务 - 现在即可测试一台虚拟服务器。

MQL5 编程基础: 终端的全局变量 MQL5 编程基础: 终端的全局变量

本文重点介绍了面向对象的 MQL5 语言,用于创建对象,并与终端的全局变量协同工作。作为一个实际的例子,我考虑这样一种情况,以全局变量作为控制点实现程序分段。