基于大众交易系统和交易机器人优化点金术的 Expert Advisor(续)

Nikolay Kositsin | 17 三月, 2016

简介

交易系统的优化已被各种文献资料广泛讨论,在这里没有必要详细介绍可在互联网上轻松找到的资料。因此,这里我们只详细讨论最基本的简单理念,这些理念是你理解使用自动系统优化结果的整体意义以及优化的实用价值的特殊意义的逻辑基础。

假设你了解,将基于相对简单的交易系统构建的 Expert Advisor 上传到策略测试程序的操作非常简单,在进行优化之后,你几乎可以基于任何历史数据获得极佳的测试结果,这些结果与历史数据的优化不谋而合。但在这里,不禁会提出这样的问题:“尽管结果非常惊人,但它已在根据历史调整系统之后获得,结果与预测自动系统的行为有什么关系呢?”

此时,这个问题相当尖锐。其中一个原因是,在第一次成功优化之后,EA 编写新手会产生在实时交易中轻率地使用优化结果的错误想法,带来的后果可能具有严重破坏性。EA 编写新手可能会损失自己或者使用现成的 Expert Advisor 的人员的金钱。因此,我将围绕此主题展开本文。



非狂热地回测或测试优化结果

通常,在最初使用 EA 优化之后,可能会决定在交易过程中构建一个使用具有最大获利和最小亏损的优化结果的策略,希望具有类似参数集合的系统不仅在优化期间可获利,而且在不久的将来也可获利。它的示意图如下:

这就是很多 EA 编写新手在刚开始熟悉交易策略测试程序时所使用的逻辑。但遗憾的是,我们不能使用此逻辑预测未来和定义交易系统的效率,对于实时市场,在实时模式下,在模拟帐户上测试策略以定期记录其操作结果,这项工作非常累人!

如果任何人都有足够的耐心花数年时间等待结果,那没有什么问题!然而,当系统变得完全没有用处之后,浪费这么多时间和互联网流量会让人非常失望的。而且,这种方法不能测试很多交易策略。一个或者最多两个!最后,此类交易策略的测试需要投入如此多的工作量,以致于其创建者无法严格地评估操作结果!

从心理学的角度看,在这种情况下,会很难面对真相和承认所产生的策略仅仅是浪费时间和精力。自然,这种精力和时间的浪费几乎无法创建可获利的 Expert Advisor。

因此,在这种情况下,唯一可行的方式是,基于历史数据对未来情形进行建模。这相当简单。我们需要做的是将时间边界从之前的预定值沿时间轴向左移动。在这种情况下,在优化之后,你将有足够的时间来评估比优化数据更接近当前数据的最新数据的优化结果。

在很多情况下,包括上面介绍的那种情况,对于某个已优化参数的 Expert Advisor,这种方法能够评估使用它的优化结果的类似策略的实际效果。下表以图示的方式显示了这种优化策略分析的含义:

我认为此图表很清晰。例如,我们将 2007 年作为分析期间。对于第一次优化,优化期间从从 01.01.2007 到 31.03.2007,测试期间从从 01.04.2007 到 31.05.2007。在测试和记录结果之后,我们将优化期间和测试期间向前移动一个月:优化期间从 01.02.2007 到 30.04.2007,测试期间从 01.05.2007 到 30.06.2007。以此类推。于是,我们拥有了一个记录每个优化结果的表格,例如下表:

最终测试参数/运行 01 02 03 04 05 06 07 08 09 10 11 12
净利润
总利润
总亏损
收益率
绝对亏损
相对亏损
总交易次数

 

当然,你需要在每次优化运行后填写表格单元。分析此类表格和处理其中包含的信息不会有任何很大的问题,因此你可以轻松地得出这种分析的结论。我认为,这种基于历史数据对 EA 行为进行的分析有助于准确地评估 EA 优化结果,避免对此流程产生错觉。而且,与在模拟帐户上进行测试相比,省时效果尤为显著!

回溯测试技术对 EA 编写新手而言很容易理解,但此调查也需要时间和精力。即使你的 EA 回溯测试结果不佳,也无需沮丧:虚拟损失总比现实中使用看似极具盈利能力的亏损策略好得多。

这就是我要告诉你关于回溯测试的全部内容。请注意,选择具有最小亏损的最大盈余不是唯一一个可行的系统优化策略。提供策略只是为了向你介绍回溯测试程序。通常,在回溯测试过程中,大部分时间都是用于进行优化,而优化策略的测试需要极少的时间,所以,一次测试多个策略以拥有更多的统计资料用于得出进一步结论,这样做更为合理。因此,测试结果的表格会大得多。



振荡指标交易策略

基于振荡指标,可以构建很多不同的交易策略。在本文中,我将基于在超买和超买区域执行的进场和退场介绍最常见的系统:

大多数振荡指标的值都是从某个最小值变成某个最大值的,每个振荡指标都有自己的值。在距极限值的一定距离处,设置 UpLevel 和 DownLevel 位。

在此类系统中,如果振荡指标离开非趋势区域进入超买区域,出现买入信号:

如果振荡指标离开非趋势区域进入超卖区域,出现卖出信号:

除上述主要信号外,当振荡指标离开超卖和超买区域进入非趋势区域时,系统也会出现其他信号,这些信号是所谓的修正信号。

当振荡指标离开超卖区域进入非趋势区域(修正下跌趋势)时,出现买入信号:

当振荡指标离开超买区域进入非趋势区域(修正上涨趋势)时,出现卖出信号:



振荡指标交易系统的 Expert Advisor 代码

因此,我们有四个进入市场的算法。观察多头仓位的两个算法变体,我们可以得出结论:它们完全相同,唯一的区别是突破位的仓位,从程序代码编写的角度看,这完全无关紧要。空头仓位的情形与此类似。下面是我用于实现振荡指标交易系统的形式:

//+==================================================================+
//|                                                        Exp_3.mq4 |
//|                             Copyright © 2007,   Nikolay Kositsin | 
//|                              Khabarovsk,   farria@mail.redcom.ru | 
//+==================================================================+
#property copyright "Copyright © 2007, Nikolay Kositsin"
#property link "farria@mail.redcom.ru"
//----+ +--------------------------------------------------------------------------+
//---- EA INPUT PARAMETERS FOR BUY TRADES 
extern bool   Test_Up1 = true;//filter of trade calculations direction
extern int    Timeframe_Up1 = 240;
extern double Money_Management_Up1 = 0.1;
extern double IndLevel_Up1 = 0.8; // breakout level of the indicator
extern int    JLength_Up1 = 8;  // depth of JJMA smoothing of entering price
extern int    XLength_Up1 = 8;  // depth of JurX smoothing of obtained indicator 
extern int    Phase_Up1 = 100; // parameter changing in the range -100 ... +100, 
                              //influences the quality of transient processes of smoothing
extern int    IPC_Up1 = 0;/* Selecting prices on which the indicator will be calculated
(0-CLOSE, 1-OPEN, 2-HIGH, 3-LOW, 4-MEDIAN, 5-TYPICAL, 6-WEIGHTED, 7-Heiken Ashi Close, 
8-SIMPL, 9-TRENDFOLLOW, 10-0.5*TRENDFOLLOW, 11-Heiken Ashi Low, 12-Heiken Ashi High,  
13-Heiken Ashi Open, 14-Heiken Ashi Close.) */
extern int    STOPLOSS_Up1 = 50;  // stoploss
extern int    TAKEPROFIT_Up1 = 100; // takeprofit
extern int    TRAILINGSTOP_Up1 = 0; // trailing stop
extern bool   ClosePos_Up1 = true; // forced position closing allowed
//----+ +--------------------------------------------------------------------------+
///---- EA INPUT PARAMETERS FOR BUY TRADES 
extern bool   Test_Up2 = true;//filter of trade calculations direction
extern int    Timeframe_Up2 = 240;
extern double Money_Management_Up2 = 0.1;
extern double IndLevel_Up2 = -0.8; // breakout level of the indicator
extern int    JLength_Up2 = 8;  // depth of JJMA smoothing of entering price
extern int    XLength_Up2 = 8;  // depth of JurX smoothing of obtained indicator
extern int    Phase_Up2 = 100; // parameter changing in the range -100 ... +100, 
                              //influences the quality of transient processes of smoothing
extern int    IPC_Up2 = 0;/* Selecting prices on which the indicator will be calculated 
(0-CLOSE, 1-OPEN, 2-HIGH, 3-LOW, 4-MEDIAN, 5-TYPICAL, 6-WEIGHTED, 7-Heiken Ashi Close, 
8-SIMPL, 9-TRENDFOLLOW, 10-0.5*TRENDFOLLOW, 11-Heiken Ashi Low, 12-Heiken Ashi High,  
13-Heiken Ashi Open, 14-Heiken Ashi Close.) */
extern int    STOPLOSS_Up2 = 50;  // stoploss
extern int    TAKEPROFIT_Up2 = 100; // takeprofit
extern int    TRAILINGSTOP_Up2 = 0; // trailing stop
extern bool   ClosePos_Up2 = true; // forced position closing allowed
//----+ +--------------------------------------------------------------------------+
//---- EA INPUT PARAMETERS FOR SELL TRADES 
extern bool   Test_Dn1 = true;//filter of trade calculations direction
extern int    Timeframe_Dn1 = 240;
extern double Money_Management_Dn1 = 0.1;
extern double IndLevel_Dn1 = 0.8; // breakout level of the indicator
extern int    JLength_Dn1 = 8;  // depth of JJMA smoothing of entering price
extern int    XLength_Dn1 = 8;  // depth of JurX smoothing of obtained indicator 
extern int    Phase_Dn1 = 100; // parameter changing in the range -100 ... +100, 
                              //influences the quality of transient processes of smoothing
extern int    IPC_Dn1 = 0;/* Selecting prices on which the indicator will be calculated 
(0-CLOSE, 1-OPEN, 2-HIGH, 3-LOW, 4-MEDIAN, 5-TYPICAL, 6-WEIGHTED, 7-Heiken Ashi Close, 
8-SIMPL, 9-TRENDFOLLOW, 10-0.5*TRENDFOLLOW, 11-Heiken Ashi Low, 12-Heiken Ashi High,  
13-Heiken Ashi Open, 14-Heiken Ashi Close.) */
extern int    STOPLOSS_Dn1 = 50;  // stoploss
extern int    TAKEPROFIT_Dn1 = 100; // takeprofit
extern int    TRAILINGSTOP_Dn1 = 0; // trailing stop
extern bool   ClosePos_Dn1 = true; // forced position closing allowed
//----+ +--------------------------------------------------------------------------+
//---- EA INPUT PARAMETERS FOR SELL TRADES
extern bool   Test_Dn2 = true;//filter of trade calculations direction
extern int    Timeframe_Dn2 = 240;
extern double Money_Management_Dn2 = 0.1;
extern double IndLevel_Dn2 = -0.8; // breakout level of the indicator
extern int    JLength_Dn2 = 8;  // depth of JJMA smoothing of entering price
extern int    XLength_Dn2 = 8;  // depth of JurX smoothing of obtained indicator 
extern int    Phase_Dn2 = 100; // parameter changing in the range -100 ... +100, 
                              //influences the quality of transient processes of smoothing
extern int    IPC_Dn2 = 0;/* Selecting prices on which the indicator will be calculated 
(0-CLOSE, 1-OPEN, 2-HIGH, 3-LOW, 4-MEDIAN, 5-TYPICAL, 6-WEIGHTED, 7-Heiken Ashi Close, 
8-SIMPL, 9-TRENDFOLLOW, 10-0.5*TRENDFOLLOW, 11-Heiken Ashi Low, 12-Heiken Ashi High,  
13-Heiken Ashi Open, 14-Heiken Ashi Close.) */
extern int    STOPLOSS_Dn2 = 50;  // stoploss
extern int    TAKEPROFIT_Dn2 = 100; // takeprofit
extern int    TRAILINGSTOP_Dn2 = 0; // trailing stop
extern bool   ClosePos_Dn2 = true; // forced position closing allowed
//----+ +--------------------------------------------------------------------------+
//---- Integer variables for the minimum of calculation bars
int MinBar_Up1, MinBar_Dn1;
int MinBar_Up2, MinBar_Dn2;
//+==================================================================+
//| TimeframeCheck() functions                                       |
//+==================================================================+
void TimeframeCheck(string Name, int Timeframe)
  {
//----+
   //---- Checking the correctness of Timeframe variable value
   if (Timeframe != 1)
    if (Timeframe != 5)
     if (Timeframe != 15)
      if (Timeframe != 30)
       if (Timeframe != 60)
        if (Timeframe != 240)
         if (Timeframe != 1440)
           Print(StringConcatenate("Parameter ",Name,
                     " cannot ", "be equal to ", Timeframe, "!!!"));    
//----+ 
  }
//+==================================================================+
//| Custom Expert functions                                          |
//+==================================================================+
#include <Lite_EXPERT1.mqh>
//+==================================================================+
//| Custom Expert initialization function                            |
//+==================================================================+
int init()
  {
//---- Checking the correctness of Timeframe_Up1 variable value
   TimeframeCheck("Timeframe_Up1", Timeframe_Up1);                             
//---- Checking the correctness of Timeframe_Up2 variable value
   TimeframeCheck("Timeframe_Up2", Timeframe_Up2);                              
//---- Checking the correctness of Timeframe_Dn1 variable value 
   TimeframeCheck("Timeframe_Dn1", Timeframe_Dn1);  
//---- Checking the correctness of Timeframe_Dn2 variable value 
   TimeframeCheck("Timeframe_Dn2", Timeframe_Dn2);  
//---- Initialization of variables
   MinBar_Up1 = 3 + 3 * XLength_Up1 + 30;
   MinBar_Up2 = 3 + 3 * XLength_Up2 + 30;
   MinBar_Dn1 = 3 + 3 * XLength_Dn1 + 30;
   MinBar_Dn2 = 3 + 3 * XLength_Dn2 + 30;                                     
//---- end of initialization
   return(0);
  }
//+==================================================================+
//| expert deinitialization function                                 |
//+==================================================================+  
int deinit()
  {
//----+
   
    //---- End of EA deinitialization
    return(0);
//----+ 
  }
//+==================================================================+
//| Custom Expert iteration function                                 |
//+==================================================================+
int start()
  {
   //----+ Declaring local variables
   double Osc1, Osc2;
   //----+ Declaring static variables
   //----+ +---------------------------------------------------------------+
   static int LastBars_Up1, LastBars_Dn1;
   static bool BUY_Sign1, BUY_Stop1, SELL_Sign1, SELL_Stop1;
   //----+ +---------------------------------------------------------------+
   static int LastBars_Up2, LastBars_Dn2;
   static bool BUY_Sign2, BUY_Stop2, SELL_Sign2, SELL_Stop2;
   //----+ +---------------------------------------------------------------+
   //----++ CODE FOR LONG POSITIONS 1
   if (Test_Up1) 
    {
      int IBARS_Up1 = iBars(NULL, Timeframe_Up1);
      
      if (IBARS_Up1 >= MinBar_Up1)
       {
         if (LastBars_Up1 != IBARS_Up1)
          {
           //----+ Initialization of variables 
           BUY_Sign1 = false;
           BUY_Stop1 = false;
           LastBars_Up1 = IBARS_Up1; 
           
           //----+ CALCULATING INDICATOR VALUES AND UPLOADING THEM TO BUFFERS        
           Osc1 = iCustom(NULL, Timeframe_Up1, 
                 "JCCIX", JLength_Up1, XLength_Up1,
                                Phase_Up1, IPC_Up1, 0, 1);
                                
           Osc2 = iCustom(NULL, Timeframe_Up1, 
                 "JCCIX", JLength_Up1, XLength_Up1,
                                Phase_Up1, IPC_Up1, 0, 2);
           
           //----+ DEFINING SIGNALS FOR TRADES                                           
           if (Osc2 < IndLevel_Up1)
             if (Osc1 > IndLevel_Up1)
                          BUY_Sign1 = true;
                          
           if (Osc1 < IndLevel_Up1)
                          BUY_Stop1 = true;                                           
          }
          //----+ EXECUTION OF TRADES
          if (!OpenBuyOrder1(BUY_Sign1, 1, Money_Management_Up1, 
                                          STOPLOSS_Up1, TAKEPROFIT_Up1))
                                                                 return(-1);
          if (ClosePos_Up1)
                if (!CloseOrder1(BUY_Stop1, 1))
                                        return(-1);
                                        
          if (!Make_TreilingStop(1, TRAILINGSTOP_Up1))
                                                  return(-1);
        }
     }
   //----+ +---------------------------------------------------------------+
   //----++ CODE FOR LONG POSITIONS 2
   if (Test_Up2) 
    {
      int IBARS_Up2 = iBars(NULL, Timeframe_Up2);
      
      if (IBARS_Up2 >= MinBar_Up2)
       {
         if (LastBars_Up2 != IBARS_Up2)
          {
           //----+ Initialization of variables 
           BUY_Sign2 = false;
           BUY_Stop2 = false;
           LastBars_Up2 = IBARS_Up2; 
           
           //----+ CALCULATING INDICATOR VALUES AND UPLOADING THEM TO BUFFERS        
           Osc1 = iCustom(NULL, Timeframe_Up2, 
                 "JCCIX", JLength_Up2, XLength_Up2,
                                Phase_Up2, IPC_Up2, 0, 1);
                                
           Osc2 = iCustom(NULL, Timeframe_Up2, 
                 "JCCIX", JLength_Up2, XLength_Up2,
                                Phase_Up2, IPC_Up2, 0, 2);
           
           //----+ DEFINING SIGNALS FOR TRADES                                           
           if (Osc2 < IndLevel_Up2)
             if (Osc1 > IndLevel_Up2)
                          BUY_Sign2 = true;
                          
           if (Osc1 < IndLevel_Up2)
                          BUY_Stop2 = true;                                           
          }
          //----+ EXECUTION OF TRADES
          if (!OpenBuyOrder1(BUY_Sign2, 2, Money_Management_Up2, 
                                          STOPLOSS_Up2, TAKEPROFIT_Up2))
                                                                 return(-1);
          if (ClosePos_Up2)
                if (!CloseOrder1(BUY_Stop2, 2))
                                        return(-1);
                                        
          if (!Make_TreilingStop(2, TRAILINGSTOP_Up2))
                                                  return(-1);
        }
     }
   //----+ +---------------------------------------------------------------+ 
   //----++ CODE FOR SHORT POSITIONS 1
   if (Test_Dn1) 
    {
      int IBARS_Dn1 = iBars(NULL, Timeframe_Dn1);
      
      if (IBARS_Dn1 >= MinBar_Dn1)
       {
         if (LastBars_Dn1 != IBARS_Dn1)
          {
           //----+ Initialization of variables 
           SELL_Sign1 = false;
           SELL_Stop1 = false;
           LastBars_Dn1 = IBARS_Dn1; 
           
           //----+ CALCULATING INDICATOR VALUES AND UPLOADING THEM TO BUFFERS        
           Osc1 = iCustom(NULL, Timeframe_Dn1, 
                 "JCCIX", JLength_Dn1, XLength_Dn1,
                                Phase_Dn1, IPC_Dn1, 0, 1);
                                
           Osc2 = iCustom(NULL, Timeframe_Dn1, 
                 "JCCIX", JLength_Dn1, XLength_Dn1,
                                Phase_Dn1, IPC_Dn1, 0, 2);
           
           //----+ DEFINING SIGNALS FOR TRADES                                           
           if (Osc2 > IndLevel_Dn1)
             if (Osc1 < IndLevel_Dn1)
                          SELL_Sign1 = true;
                          
           if (Osc1 > IndLevel_Dn1)
                          SELL_Stop1 = true;                                           
          }
          //----+ EXECUTION OF TRADES
          if (!OpenSellOrder1(SELL_Sign1, 3, Money_Management_Dn1, 
                                          STOPLOSS_Dn1, TAKEPROFIT_Dn1))
                                                                 return(-1);
          if (ClosePos_Dn1)
                if (!CloseOrder1(SELL_Stop1, 3))
                                        return(-1);
                                        
          if (!Make_TreilingStop(3, TRAILINGSTOP_Dn1))
                                                  return(-1);
        }
     }
   //----+ +---------------------------------------------------------------+
   //----++ CODE FOR SHORT POSITIONS 2
   if (Test_Dn2) 
    {
      int IBARS_Dn2 = iBars(NULL, Timeframe_Dn2);
      
      if (IBARS_Dn2 >= MinBar_Dn2)
       {
         if (LastBars_Dn2 != IBARS_Dn2)
          {
           //----+ Initialization of variables 
           SELL_Sign2 = false;
           SELL_Stop2 = false;
           LastBars_Dn2 = IBARS_Dn2; 
           
           //----+ CALCULATING INDICATOR VALUES AND UPLOADING THEM TO BUFFERS        
           Osc1 = iCustom(NULL, Timeframe_Dn2, 
                 "JCCIX", JLength_Dn2, XLength_Dn2,
                                Phase_Dn2, IPC_Dn2, 0, 1);
                                
           Osc2 = iCustom(NULL, Timeframe_Dn2, 
                 "JCCIX", JLength_Dn2, XLength_Dn2,
                                Phase_Dn2, IPC_Dn2, 0, 2);
           
           //----+ DEFINING SIGNALS FOR TRADES                                           
           if (Osc2 > IndLevel_Dn2)
             if (Osc1 < IndLevel_Dn2)
                          SELL_Sign2 = true;
                          
           if (Osc1 > IndLevel_Dn2)
                          SELL_Stop2 = true;                                           
          }
          //----+ EXECUTION OF TRADES
          if (!OpenSellOrder1(SELL_Sign2, 4, Money_Management_Dn2, 
                                          STOPLOSS_Dn2, TAKEPROFIT_Dn2))
                                                                 return(-1);
          if (ClosePos_Dn2)
                if (!CloseOrder1(SELL_Stop2, 4))
                                        return(-1);
                                        
           if (!Make_TreilingStop(4, TRAILINGSTOP_Dn2))
                                                  return(-1);
        }
     }
   //----+ +---------------------------------------------------------------+ 
//----+ 
    
    return(0);
  }
//+------------------------------------------------------------------+

此代码比上一篇文章提供的代码长两倍。顺便提一下,如果 EA 代码包含完整程序代码,而不是订单管理函数的调用,可以想象一下此 EA 代码会有多长!在这里,应注意此 EA 包含新的输入参数:IndLevel_Up1、IndLevel_Up2、IndLevel_Dn1 和 IndLevel_Dn2。IndLevel_Up1 和 IndLevel_Up2 定义两个多头仓位算法的 Uplevel 和 DownLevel 的值,IndLevel_Dn1 和 IndLevel_Dn2 定义两个空头仓位算法的 DownLevel 和 Uplevel 的值。

在此 Expert Advisor 的优化过程中,应考虑到,这些位的值可在 -1.0 到 +1.0 之间变动。如果你要使用任何其他振荡指标替代 JCCIX 振荡指标,应考虑到这些位的最大值和最小值可以不同。源指标 JCCIX 是 CCI 指标的类似项,在此指标中,常见移动平均线执行的平滑算法被 JMA 和超线性平滑所替代。EA 使用追踪止损位,它们的值由 EA 输入参数定义,例如 TRAILINGSTOP_Up1、TRAILINGSTOP_Up2、TRAILINGSTOP_Dn1、TRAILINGSTOP_Dn2。至于所有其他方面,此 EA 完全类似于上一篇文章中介绍的 EA。



将即时入市更改为挂单

在很多情况下,在类似于上述 EA 的 EA 中,将即时入市更改为挂单可以更准确地把握进入市场时机,并能够获得更高的利润和降低到达止损位的概率。来自文件 Lite_EXPERT1.mqh 的一组函数能够轻松地实现这种替代。

我们需要的是分别用 OpenBuyLimitOrder1() 和 OpenSellLimitOrder1() 替代函数 OpenBuyOrder1() 和 OpenSellOrder1()。在替代这些函数的过程中,这些函数的新输入变量必须进行初始化:位和到期。例如,我们可以构建一个交易策略,在此策略中,“位”变量将由 EA 输入参数定义。挂单取消的日期可以设置为当前柱的下一个更改的日期。

我在这里不重复这个同样的代码。本文已附上上述 EA 的已更改代码 (Exp_4.mq4)。举个使用挂单的例子,我将介绍使用 OSMA 振荡指标的振荡指标系统:

//For the EA operation the Metatrader\EXPERTS\indicators folder 
//must contain the 5c_OsMA.mq4 indicator
//+==================================================================+
//|                                                        Exp_5.mq4 |
//|                             Copyright © 2008,   Nikolay Kositsin | 
//|                              Khabarovsk,   farria@mail.redcom.ru | 
//+==================================================================+
#property copyright "Copyright © 2008, Nikolay Kositsin"
#property link "farria@mail.redcom.ru"
//----+ +--------------------------------------------------------------------------+
//---- EA INPUT PARAMETERS FOR BUY TRADES 
extern bool   Test_Up = true;//filter of trade calculations direction
extern int    Timeframe_Up = 240;
extern double Money_Management_Up = 0.1;
extern double IndLevel_Up = 0; // breakout level of the indicator
extern int    FastEMA_Up = 12;  // period of quick EMA
extern int    SlowEMA_Up = 26;  // period of slow EMA
extern int    SignalSMA_Up = 9;  // period of signal SMA
extern int    STOPLOSS_Up = 50;  // stoploss
extern int    TAKEPROFIT_Up = 100; // takeprofit
extern int    TRAILINGSTOP_Up = 0; // trailing stop
extern int    PriceLevel_Up =40; // difference between the current price and
                                         // the price of pending order triggering
extern bool   ClosePos_Up = true; // forced position closing allowed
//----+ +--------------------------------------------------------------------------+
//---- EA INPUT PARAMETERS FOR SELL TRADES 
extern bool   Test_Dn = true;//filter of trade calculations direction
extern int    Timeframe_Dn = 240;
extern double Money_Management_Dn = 0.1;
extern double IndLevel_Dn = 0; // breakout level of the indicator
extern int    FastEMA_Dn = 12;  // period of quick EMA
extern int    SlowEMA_Dn = 26;  // period of slow EMA
extern int    SignalSMA_Dn = 9;  // period of signal SMA
extern int    STOPLOSS_Dn = 50;  // stoploss
extern int    TAKEPROFIT_Dn = 100; // takeprofit
extern int    TRAILINGSTOP_Dn = 0; // trailing stop
extern int    PriceLevel_Dn = 40; // difference between the current price and 
                                         // the price of pending order triggering
extern bool   ClosePos_Dn = true; // forced position closing allowed
//----+ +--------------------------------------------------------------------------+
//---- Integer variables for the minimum of calculation bars
int MinBar_Up, MinBar_Dn;
//+==================================================================+
//| TimeframeCheck() functions                                       |
//+==================================================================+
void TimeframeCheck(string Name, int Timeframe)
  {
//----+
   //---- Checking the correctness of Timeframe variable value
   if (Timeframe != 1)
    if (Timeframe != 5)
     if (Timeframe != 15)
      if (Timeframe != 30)
       if (Timeframe != 60)
        if (Timeframe != 240)
         if (Timeframe != 1440)
           Print(StringConcatenate("Parameter ",Name,
                     " cannot ", "be equal to ", Timeframe, "!!!"));    
//----+ 
  }
//+==================================================================+
//| Custom Expert functions                                          |
//+==================================================================+
#include <Lite_EXPERT1.mqh>
//+==================================================================+
//| Custom Expert initialization function                            |
//+==================================================================+
int init()
  {
//---- Checking the correctness of Timeframe_Up variable value
   TimeframeCheck("Timeframe_Up", Timeframe_Up);                                                          
//---- Checking the correctness of Timeframe_Dn variable value 
   TimeframeCheck("Timeframe_Dn", Timeframe_Dn);  
//---- Initialization of variables
   MinBar_Up  = 3 + MathMax(FastEMA_Up, SlowEMA_Up) + SignalSMA_Up;
   MinBar_Dn  = 3 + MathMax(FastEMA_Dn, SlowEMA_Dn) + SignalSMA_Dn;                               
//---- end of initialization
   return(0);
  }
//+==================================================================+
//| expert deinitialization function                                 |
//+==================================================================+  
int deinit()
  {
//----+
   
    //---- End of EA deinitialization
    return(0);
//----+ 
  }
//+==================================================================+
//| Custom Expert iteration function                                 |
//+==================================================================+
int start()
  {
   //----+ Declaring local variables
   double Osc1, Osc2;
   //----+ Declaring static variables
   //----+ +---------------------------------------------------------------+
   static datetime StopTime_Up, StopTime_Dn; 
   static int LastBars_Up, LastBars_Dn;
   static bool BUY_Sign1, BUY_Stop1, SELL_Sign1, SELL_Stop1;
   //----+ +---------------------------------------------------------------+
   //----++ CODE FOR LONG POSITIONS 1
   if (Test_Up) 
    {
      int IBARS_Up = iBars(NULL, Timeframe_Up);
      
      if (IBARS_Up >= MinBar_Up)
       {
         if (LastBars_Up != IBARS_Up)
          {
           //----+ Initialization of variables 
           BUY_Sign1 = false;
           BUY_Stop1 = false;
           LastBars_Up = IBARS_Up;
           StopTime_Up = iTime(NULL, Timeframe_Up, 0)
                                            + 60 * Timeframe_Up;
           
           //----+ CALCULATING INDICATOR VALUES AND UPLOADING THEM TO BUFFERS        
           Osc1 = iCustom(NULL, Timeframe_Up, 
                         "5c_OsMA", FastEMA_Up, SlowEMA_Up,
                                               SignalSMA_Up, 5, 1);
                                
           Osc2 = iCustom(NULL, Timeframe_Up, 
                         "5c_OsMA", FastEMA_Up, SlowEMA_Up,
                                               SignalSMA_Up, 5, 2);
           
           //----+ DEFINING SIGNALS FOR TRADES                                           
           if (Osc2 < IndLevel_Up)
             if (Osc1 > IndLevel_Up)
                          BUY_Sign1 = true;
                          
           if (Osc1 < IndLevel_Up)
                          BUY_Stop1 = true;                                           
          }
          //----+ EXECUTION OF TRADES
          if (!OpenBuyLimitOrder1(BUY_Sign1, 1, 
              Money_Management_Up, STOPLOSS_Up, TAKEPROFIT_Up,
                                            PriceLevel_Up, StopTime_Up))
                                                                 return(-1);
          if (ClosePos_Up)
                if (!CloseOrder1(BUY_Stop1, 1))
                                        return(-1);
                                        
          if (!Make_TreilingStop(1, TRAILINGSTOP_Up))
                                                  return(-1);
        }
     }
   //----+ +---------------------------------------------------------------+
   //----++ CODE FOR SHORT POSITIONS 1
   if (Test_Dn) 
    {
      int IBARS_Dn = iBars(NULL, Timeframe_Dn);
      
      if (IBARS_Dn >= MinBar_Dn)
       {
         if (LastBars_Dn != IBARS_Dn)
          {
           //----+ Initialization of variables 
           SELL_Sign1 = false;
           SELL_Stop1 = false;
           LastBars_Dn = IBARS_Dn;
           StopTime_Dn = iTime(NULL, Timeframe_Dn, 0) 
                                            + 60 * Timeframe_Dn; 
           
           //----+ CALCULATING INDICATOR VALUES AND UPLOADING THEM TO BUFFERS        
           Osc1 = iCustom(NULL, Timeframe_Dn, 
                         "5c_OsMA", FastEMA_Dn, SlowEMA_Dn,
                                               SignalSMA_Dn, 5, 1);
                                
           Osc2 = iCustom(NULL, Timeframe_Dn, 
                         "5c_OsMA", FastEMA_Dn, SlowEMA_Dn,
                                               SignalSMA_Dn, 5, 2);
           
           //----+ DEFINING SIGNALS FOR TRADES                                           
           if (Osc2 > IndLevel_Dn)
             if (Osc1 < IndLevel_Dn)
                          SELL_Sign1 = true;
                          
           if (Osc1 > IndLevel_Dn)
                          SELL_Stop1 = true;                                           
          }
          //----+ EXECUTION OF TRADES
          if (!OpenSellLimitOrder1(SELL_Sign1, 2, 
              Money_Management_Dn, STOPLOSS_Dn, TAKEPROFIT_Dn,
                                            PriceLevel_Dn, StopTime_Dn))
                                                                 return(-1);
          if (ClosePos_Dn)
                if (!CloseOrder1(SELL_Stop1, 2))
                                        return(-1);
                                        
          if (!Make_TreilingStop(2, TRAILINGSTOP_Dn))
                                                  return(-1);
        }
     }
   //----+ +---------------------------------------------------------------+
//----+ 
    
    return(0);
  }
//+------------------------------------------------------------------+

没有使用两个突破位 UpLevel 和 DownLevel,只使用了一个,这就是为什么 EA 仅有两个仓位管理算法的原因。通常,在与 OsMA 相关的交易系统中,此位选择等于零,但我决定将其留在 EA 的外部变量中,因此它可进行更改。换言之,应考虑到 OSMA 指标没有变化的最大和最小值,因此突破位对此值没有限制。尽管如此,正如我之前所说的,它通常等于零。为了定义挂单取消的时间,将使用静态变量StopTime_Up和StopTime_Dn are used,一旦柱发生更改,它们将在下一次柱更改时进行初始化。



总结

在总结中,我想补充的是,振荡指标交易系统会相对当前市场趋势给出很多错误的信号。因此,此类 EA 最好在市场盘整期纳入操作,或者用于仅根据趋势建仓。

对于回溯测试,我可以重申一遍,对于 EA 编写新手而言,这可能是正确评估优化结果的最佳方法。当根据历史进行调整后,EA 能够展示出惊人的结果,这完全没有问题。然而,要了解应如何使用现成的 EA 以避免出现市场远离优化的 EA 参数的情况要难得多。