Lite_EXPERT2.mqh: Expert Advisor 开发人员的功能套件

Nikolay Kositsin | 11 四月, 2016


简介

在一系列“基于常见交易系统的 Expert Advisor 和交易机器人优化的惊人作用”的1234567 文章中,我向 EA 开发人员新手介绍了我的 Expert Advisor 编写方法,通过此方法,使用 Lite_EXPERT1.mqh 现成的自定义交易函数,可以轻松地将交易策略转换为非常简单且快速实现的程序代码。 很自然,为了确保信息的最大简明性,此文件中使用的自定义函数数量是最小的,可以这么说足以能够毫无困难的理解信息。

然而,当在更广的范围内使用程序时,此文件提供的这项功能还不是很充分。 Lite_EXPERT2.mqh 就是针对此目标编写而成,它包含一个更为通用的自定义函数集,但对于第一次使用的用户而言稍显复杂。 本文假设读者已经熟悉“基于常见交易系统的 Expert Advisor 和交易机器人优化的惊人作用 1”文章中提供的信息,将本文作为进一步培养和提升使用此类自定义函数所需的技能过程中的下一步。


Lite_EXPERT2.mqh 文件内容

一般来说,Lite_EXPERT2.mqh 中的所有可用函数均可按以下流程图形式显示:

函数描述

除函数以外,Lite_EXPERT2.mqh 还包含整数变量 int LastTime 和整数变量 extern int Slippage_,前者已在全局范围中声明并用于所有交易函数,后者用于滑点(用点数表示),两者均可从 Expert Advisor 的外部参数中看见。


1. 建仓函数

此块的所有函数可分为两个大组。 第一组函数的名称以”Open”开头,第二组函数的名称以“dOpen”开头。 第一组函数将与建仓价格之间的相对距离(用点表示)作为止损位和获利位的外部变量,即它们由整数变量表示。

bool OpenBuyOrder1_
        (bool& BUY_Signal, int MagicNumber, datetime TimeLevel,
                           double Money_Management, int Margin_Mode, 
                                         int STOPLOSS, int TAKEPROFIT)
                                         
bool OpenSellOrder1_
        (bool& SELL_Signal, int MagicNumber, datetime TimeLevel, 
                            double Money_Management, int Margin_Mode, 
                                          int STOPLOSS, int TAKEPROFIT)
                                          
bool OpenBuyOrder2_
        (bool& BUY_Signal, int MagicNumber, datetime TimeLevel,
                           double Money_Management, int Margin_Mode, 
                                          int STOPLOSS, int TAKEPROFIT)
                                          
bool OpenSellOrder2_
        (bool& SELL_Signal, int MagicNumber, datetime TimeLevel, 
                            double Money_Management, int Margin_Mode, 
                                          int STOPLOSS, int TAKEPROFIT)

第二组函数中的止损位和获利位由浮点变量表示。 在这种情况下,这些变量的值是从价格图表中获得的相应订单的绝对值。 这样的话,编写用于特定目的的 Expert Advisor 的代码会方便得多。

bool dOpenBuyOrder1_
        (bool& BUY_Signal, int MagicNumber, datetime TimeLevel,
                           double Money_Management, int Margin_Mode, 
                                  double dSTOPLOSS, double dTAKEPROFIT)
                                  
bool dpenSellOrder1_
        (bool& SELL_Signal, int MagicNumber, datetime TimeLevel,
                           double Money_Management, int Margin_Mode, 
                                  double dSTOPLOSS, double dTAKEPROFIT)
                                  
bool dOpenBuyOrder2_
        (bool& BUY_Signal, int MagicNumber, datetime TimeLevel,
                           double Money_Management, int Margin_Mode, 
                                   double dSTOPLOSS, double dTAKEPROFIT)
                                   
bool dOpenSellOrder2_
        (bool& SELL_Signal, int MagicNumber, datetime TimeLevel, 
                            double Money_Management, int Margin_Mode, 
                                   double dSTOPLOSS, double dTAKEPROFIT)

所有名称以“1_”结尾的函数适用于允许在执行交易时设置止损位和获利位的经纪人。 所有名称包含“2_”的函数适用于允许为已经建立的仓设置订单的经纪人。

与 Lite_EXPERT1.mqh 中具有的函数相比,这八个函数有两个新变量(datetime TimeLevel 和 Margin_Mode)。 因此,我们应做的第一件事情是更近距离地了解它们。 TimeLevel 变量的值表示在执行当前交易之后的一定时间限制。 此文件的所有交易函数不会新建任何使用当前幻数的仓位或订单,直至到达指定时间限制。 在执行交易时,此外部变量的值被保存在计算机硬盘上的全局变量中,因此,在重新启动交易终端或 Windows 操作系统时,此值不会丢失。 此变量的最基本用法是阻止在同一个条柱上重新建仓或设置挂订单。

//----
   static datetime TimeLevel;
   TimeLevel = Time[0] + Period() * 60;
   
   //---- 
   if (!OpenBuyOrder1_
        (BUY_Signal, MagicNumber, TimeLevel,
                           Money_Management, Margin_Mode, 
                                         STOPLOSS, TAKEPROFIT))
                                                          return(-1);

假设 将在与 BUY_Signal 变量所处的同一个块中对 TimeLevel static 变量进行初始化。 现在,如果 OpenBuyOrder1_() 函数建立了一个仓,它将在硬盘上的全局变量中保存 TimeLevel 变量值。 用于建仓位或挂单的 Lite_EXPERT2.mqh 文件的函数不会建立任何使用相应幻数的订单,直至最后一个报价的时间大于或等于此值。

因此,基本上,此文件的交易函数不会根据参考将 bool& BUY_Signal 类型的外部变量清零,因为这没有必要。 只有当 TimeLevel 变量已被初始化为“-1”时,才会进行清零! 如果我们将此变量值设置为等于零,则当没有使用等于 int MagicNumber 外部变量值的幻数的订单时,建仓函数将不会对任何内容信息清零或保存,而将基于 BUY_Signal 外部变量的信号建仓。

将使用以下公式生成硬盘上保存的全局变量所使用的字符串名称,用于进行测试和优化:

string G_Name_ = "TimeLevel", "_", AccountNumber(), "_", 
                                "_Test_", OrderSymbol(), "_", OrderMagicNumber());

在其他情况下:

string G_Name_ = "TimeLevel", "_", AccountNumber(), 
                                     "_", OrderSymbol(), "_", OrderMagicNumber());

此名称不得用于其他全局变量!

Lite_EXPERT1.mqh 中可用的交易函数仅使用一种手数计算方法(基于可用保证金的 MM)。 此手数大小计算方法可能不适用于所有策略。 Lite_EXPERT2.mqh 中提供的交易函数已考虑到了此问题, 将使用 int Margin_Mode 外部变量确定手数大小计算方法。 在确定手数大小计算方法的过程中,Margin_Mode 外部变量可能会取从 0 到 5 范围内的值:

  • 0 - 基于可用保证金的 MM
  • 1 - 基于帐户余额的 MM
  • 2 - 基于可用保证金的亏损的 MM
  • 3 - 基于帐户余额的亏损的 MM
  • 4 - 0 与 2 之间的手数
  • 5 - 1 与 3 之间的手数
  • 默认值 - 基于可用保证金的 MM

请记住,如果您使用了第二或第三种手数大小计算方法,同时您的止损位为动态变化的,即它将因交易的不同而不同,您应考虑止损位和 MM 的边界值。 例如,变量使用以下值: Money_Management = 0.1,Margin_Mode = 3,int STOPLOSS = 100(5 个小数位),Expert Advisor 将在建仓时使用整个保证金!

为了以防万一,我将再次介绍 Margin_Mode 变量的两个变式(2 和 3)。 在这里,手数大小计算函数不使用可用保证金或帐户余额值,而是乘以 Money_Management 变量值。 产生的值表示当在止损位平仓时可能会发生的亏损金额! 这些亏损不取决于止损位大小。 因此,手数大小计算函数基于止损位大小确定仓交易量,以便在止损位处发生的亏损保持相对固定!

当 Margin_Mode 变量值等于 4 或 5 时,交易函数可同时使用两种变式计算手数大小,然后选择最小值。 例如,如果 Margin_Mode = 5,交易函数将基于帐户余额同时基于帐户余额的亏损计算手数大小,然后选择最小值。

我想在这里再说明一次,如果 Money_Management 变量值为负数,所有这些函数将忽略 Margin_Mode 变量值,而将 Money_Management 变量值用作为手数大小。 使用这些值时,将忽略负号,值本身将被四舍五入为最接近的标准值,但此值不能大于可用值。 在计算手数大小的过程中,这八个函数始终会检查可用保证金是否足以执行某个交易,必要时将计算出的手数大小减小为允许的值。


2. 用于下挂单的函数

这是函数被分成的两个大组中的第二个大组,与上面的函数类似。

bool OpenBuyLimitOrder1_
        (bool& Order_Signal, int MagicNumber, datetime TimeLevel,
                            double Money_Management, int Margin_Mode, 
                                           int STOPLOSS, int TAKEPROFIT,
                                              int LEVEL, datetime Expiration) 
bool OpenBuyStopOrder1_
        (bool& Order_Signal, int MagicNumber, datetime TimeLevel, 
                             double Money_Management, int Margin_Mode, 
                                           int STOPLOSS, int TAKEPROFIT,
                                               int LEVEL, datetime Expiration)
bool OpenSellLimitOrder1_
        (bool& Order_Signal, int MagicNumber, datetime TimeLevel, 
                            double Money_Management, int Margin_Mode, 
                                           int STOPLOSS, int TAKEPROFIT,
                                               int LEVEL, datetime Expiration)
bool OpenSellStopOrder1_
        (bool& Order_Signal, int MagicNumber, datetime TimeLevel,
                            double Money_Management, int Margin_Mode,
                                           int STOPLOSS, int TAKEPROFIT,
                                               int LEVEL, datetime Expiration)
bool dOpenBuyLimitOrder1_
        (bool& Order_Signal, int MagicNumber, datetime TimeLevel,
                            double Money_Management, int Margin_Mode, 
                                    double dSTOPLOSS, double dTAKEPROFIT,
                                           double dLEVEL, datetime Expiration)
bool dOpenBuyStopOrder1_
        (bool& Order_Signal, int MagicNumber, datetime TimeLevel, 
                             double Money_Management, int Margin_Mode, 
                                    double dSTOPLOSS, double dTAKEPROFIT,
                                            double dLEVEL, datetime Expiration)
bool dOpenSellLimitOrder1_
        (bool& Order_Signal, int MagicNumber, datetime TimeLevel, 
                            double Money_Management, int Margin_Mode, 
                                    double dSTOPLOSS, double dTAKEPROFIT,
                                            double dLEVEL, datetime Expiration)
bool dOpenSellStopOrder1_
        (bool& Order_Signal, int MagicNumber, datetime TimeLevel,
                            double Money_Management, int Margin_Mode,
                                    double dSTOPLOSS, double dTAKEPROFIT,
                                            double dLEVEL, datetime Expiration)

上文针对之前函数的说明同样适用于这八个函数。 只有两种情况可作为例外情况。 基本上,无法基于下订单时所发生的事情计算手数大小。 如果手数大小是在下订单时计算的,可能很难期望严格按照 Money_Management 变量值进行计算。 由于同样的原因,这些函数不会为手数大小检查是否有足够的资金。


3. 用于关闭持仓的函数

此块仅包含四个函数

bool CloseBuyOrder1_(bool& CloseStop, int MagicNumber)

bool CloseSellOrder1_(bool& CloseStop, int MagicNumber)

bool CloseAllBuyOrders1_(bool CloseStop)

bool CloseAllSellOrders1_(bool CloseStop)

这些函数相当简单,不需要任何额外的解释。 前两个函数按照正在运行 Expert Advisor 的图表中的符号关闭具有指定幻数的仓。 另外两个函数关闭所有可用的持仓。


4. 用于删除挂单的函数

本节中的函数列表仅包含两个基本函数

bool CloseOrder1_(bool& CloseStop, int cmd, int MagicNumber)
bool CloseAllOrders1_(bool CloseStop, int cmd)

它们的外部变量包含新变量 - int cmd。 下面提供了此变量的值:

  • OP_BUYLIMIT 2 挂单买入限价
  • OP_SELLLIMIT 3 挂单卖出限价
  • OP_BUYSTOP 4 挂单买入止损
  • OP_SELLSTOP 5 挂单卖出止损


5. 仓位修改函数和跟踪止损位函数

此块包含三个函数组:

1) 仓位修改函数

bool dModifyOpenBuyOrder_
       (bool& Modify_Signal, int MagicNumber, 
                     datetime ModifyTimeLevel_, double dSTOPLOSS, double dTAKEPROFIT)
                         
bool dModifyOpenSellOrder_
       (bool& Modify_Signal, int MagicNumber, 
                     datetime ModifyTimeLevel_, double dSTOPLOSS, double dTAKEPROFIT)
                         
bool dModifyOpenBuyOrderS (bool& Modify_Signal, double dSTOPLOSS, double dTAKEPROFIT)
                         
bool dModifyOpenSellOrderS(bool& Modify_Signal, double dSTOPLOSS, double dTAKEPROFIT)

此组的所有四个函数使用价格图表比例上的止损位和获利位的绝对值,并使用浮点变量表示。 针对第一和第二组变量所编写的说明同样适用于此组的变量。

请注意,datetime ModifyTimeLevel_ 变量仅用于此组的前两个函数,不适用于任何其他函数! 后两个函数中完全没有此变量。 为了能够在 Expert Advisor 的代码中调用后两个函数中的一个函数,您需要首先使用 OrderSelect() 函数选择 OP_BUY 或 OP_SELL 类型的订单,才能继续使用它们! 这些函数用于处理没有幻数的持仓。 用于在硬盘上保存 ModifyTimeLevel_ 变量值的全局变量的字符串名称的公式如下:

string G_Name_ = "ModifyTimeLevel_", "_", AccountNumber(), 
                                          "_", "_Test_", Symbol(), "_", MagicNumber;
string G_Name_ = "ModifyTimeLevel_", "_", AccountNumber(),
                                                    "_", Symbol(), "_", MagicNumber;  

2) 跟踪止损位

bool Make_BuyTrailingStop_
             (bool& TreilSignal, int MagicNumber, datetime TrailTimeLevel, int TRAILINGSTOP)

bool Make_SellTrailingStop_
             (bool& TreilSignal, int MagicNumber, datetime TrailTimeLevel, int TRAILINGSTOP)
        
bool dMake_BuyTrailingStop_
        (bool& TreilSignal, int MagicNumber, datetime TrailTimeLevel_, double dTRAILINGSTOP)
        
bool dMake_SellTrailingStop_
        (bool& TreilSignal, int MagicNumber, datetime TrailTimeLevel_, double dTRAILINGSTOP)

此组包含四个用于根据当前价格强制执行止损位的经典跟踪止损位。 其中两个将相对于当前价格的跟踪止损位值(用点数表示)作为外部参数,另外两个使用跟踪止损位绝对值用于相同目的。 与之前函数类似,这四个函数以具有类似值的 TrailTimeLevel 和 TrailTimeLevel_ 变量形式使用时间限制。 如果您打算在每次变动时更改跟踪止损位,应将这些变量设置为零。

3) 另一个包含四个仓位修改函数的组

bool BuyStoplossCorrect
           (int MagicNumber, int ExtPointProfit, int StoplossProfit)
           
bool SellStoplossCorrect
           (int MagicNumber, int ExtPointProfit, int StoplossProfit)
           
bool AllBuyStoplossCorrects (int ExtPointProfit, int StoplossProfit)
           
bool AllSellStoplossCorrects(int ExtPointProfit, int StoplossProfit)

它们对止损位级别执行一次性更改。 前两个函数检查具有固定幻数的持仓的获利(用点数表示),如果获利不小于 ExtPointProfit 变量值,则将止损位移至当前价格,此处与建仓价相距 StoplossProfit 距离。

此组的后两个函数跟踪当前交易品种的所有具有任何幻数的持仓,并对它们的相应止损位执行一次性更改。


6. 附加函数

这是 Lite_EXPERT2.mqh 文件中的最后一个并且可能是最大的函数组。 此组的很多函数被用作为上述其他函数的代码中的附加函数,因此在很多情况下,可能没有实际的意义。 因此,我在这里仅限于回顾其中最基本的函数。

首先,我想让您关注 TimeLevelGlobalVariableDel() 函数

void TimeLevelGlobalVariableDel(string symbol, int MagicNumber)

在测试和优化之后,此函数将删除由此文件中包含的交易函数生成的并保存在计算机硬盘上的所有全局变量。 应在 Expert Advisor 取消初始化块中调用此函数,例如,如下所示:

//+X================================================================X+
//| Custom Expert deinitialization function                          |
//+X================================================================X+  
int deinit()
  {
//----+
    TimeLevelGlobalVariableDel(Symbol(), 1);
    TimeLevelGlobalVariableDel(Symbol(), 2);
    TimeLevelGlobalVariableDel(Symbol(), 3);
    TimeLevelGlobalVariableDel(Symbol(), 4);
    //---- Completing deinitialization of the Expert Advisor
    return(0);
//----+ 
  }

如果未执行此操作,则在测试或优化之后,遗留有最新时间值的全局变量将阻止 Expert Advisor 的测试和优化!!!

经常出现的情况时,仅可从标准值系列中选择的图表时间范围被用作为 Expert Advisor 的外部变量。 在手动操作时,难免会犯错,设置错误的值。 在这种情况下,可使用 TimeframeCheck() 函数进行检查,

void TimeframeCheck(string TimeframeName, int Timeframe)

此函数在初始化 Expert Advisor 时检查外部变量中的时间范围值。

在 Expert Advisor 中针对任何价位执行的所有计算最初都是基于价格图表值的。 可能不是总能够非常明确图表中显示是什么价位 - 买入价还是卖价。 因此,在交易函数中使用从价格图表中获得的值之前,需要针对这种差异对这些值进行调整。 可使用 dGhartVelueCorrect() 函数执行此操作:

double dGhartVelueCorrect(int cmd, double& GhartVelue)

Cmd 变量的用途与 MetaEditor Help 中提供的说明完全相同。 调用此函数时,外部参数 GhartVelue 只能由一个变量来表示,可根据参考将此变量的值更改为一个调整后的值。 例如:

//---- get the GhartVelue_ price level
   double GhartVelue_ = Low[0];
   //---- correct the GhartVelue_ price level
   dGhartVelueCorrect(OP_BUY, GhartVelue_);

还有两个用于选择订单的参数,以便能够继续进一步使用函数:

bool OrderSelect_(string symbol, int cmd, int MagicNumber, int pool)
bool Order_Select(string symbol, int MagicNumber)

pool 变量只能取以下两个值: MODE_TRADES - 从持单和挂单中选择订单,MODE_HISTORY - 从关闭和删除的订单中选择订单。 如果成功,此函数将返回 true,否则返回 false。

在某些情况下,您可能需要使用 MarginCheck(),此函数用于根据帐户中可用的资金检查手数大小,必要时将减小手数大小以适合可用资金:

bool MarginCheck(string symbol, int Cmd, double& Lot)

此参数中的 Cmd 参数只能取以下两个值: OP_BUY 和 OP_SELL。 如果计算成功,此函数将返回 true。 如果在函数操作过程中出现错误,此函数将返回 false。

有时,您可能需要计算手数大小。 为此,我们还有两个函数:

double BuyLotCount(double Money_Management, int Margin_Mode, int STOPLOSS)
double SellLotCount(double Money_Management, int Margin_Mode, int STOPLOSS)

如果没有错误,此函数将返回手数值,否则返回 -1。

还有三个函数适用于那些希望在 Expert Advisor 的代码中包含指标代码的人员。

int IndicatorCounted_(int Number, string symbol, int timeframe)

int ReSetAsIndexBuffer(string symbol, int timeframe, double& array[])

int ReSetAsIndexBuffer_(string symbol, int timeframe, double& Array[], int ArrSize)

第一个函数相当于 EA 中操作的 IndicatorCounted() 函数。 外部变量 Number 表示在 Expert Advisor 代码中的函数调用编号(一个指标一个编号)。

第二个函数的用途是将全局范围中声明的数据数组转换成指标缓冲区类似项。 因此,此函数使名称表示变量 double& array[] 的数组的元素与相应时间序列数组同步。 在 start() function 中,必须在图表条柱中迭代的循环运算符的范围以外调用此函数。

第三个函数完全类似于第二个函数,但 Array[] 数组中的元素数是从 ArrSize 变量衍生的。 换言之,在这种情况下,此数组仅包含最近的条柱的 ArrSize 数字。 在很多情况下,在指标代码中,值仅添加到此数组中,且 Expert Advisor 代码中仅使用最后一批值的 ArrSize 数字,此函数可能比上一个函数更有用。

最后两个函数:

bool IsNewBar(int Number, string symbol, int timeframe)

bool MinBarCheck(string symbol, int timeframe, int MinBarTotal)

当相应时间序列数组中的条柱发生更改时,IsNewBar() 函数将返回 True。 在所有其他情况下,此函数返回 False。 Number 变量表示在 Expert Advisor 代码中的函数调用编号。 MinBarCheck() 函数比较相关图表的条柱数量与 MinBarTotal 变量值。 如果条柱数量小,此函数返回 False。 此函数用于在计算所需的条柱数量不足时不允许 Expert Advisor 进行交易。


总结

上文基本列出了所有最需要的 Lite_EXPERT2.mqh 函数,这些函数代表了能够在 MQL4 中方便高效地编写策略测试程序所需的的最少量的函数。 在本系列的后续文章中,我将提供说明 EA 中的上述文件函数的使用的具体示例。