English Русский Español Deutsch 日本語 Português
将指标代码转移至 Expert Advisor 代码。总结

将指标代码转移至 Expert Advisor 代码。总结

MetaTrader 4示例 | 26 二月 2016, 10:51
1 405 0
Nikolay Kositsin
Nikolay Kositsin

简介

在前几篇文章中(将指标代码转移到 Expert Advisor 代码中。指标结构 将指标代码转移到 Expert Advisor 代码中。Expert Advisor 的一般结构方案和指标函数)中,我们分析了一个根据现有指标代码编写指标函数的一般方案,并定义了其与 Expert Advisor 的关联。现在是时候重写一个真实 Expert Advisor 的代码了。那么,让我们开始吧。


EA 源代码

我们拥有以下 Expert Advisor 代码:

//+------------------------------------------------------------------+
//|                                         ASCTrend1RAVI_Expert.mq4 |
//|                             Copyright © 2006,   Nikolay Kositsin | 
//|                              Khabarovsk,   farria@mail.redcom.ru | 
//+------------------------------------------------------------------+
#property copyright "Copyright © 2006, Nikolay Kositsin"
#property link "farria@mail.redcom.ru"
//---- INPUT PARAMETERS
extern int RAVI_Timeframe = 240;
extern int ASCT_Timeframe = 1440;
//---- FILTER OF TRADE CALCULATION DIRECTION
extern int Buy_Sell_Custom = 2; //0-Buy, 1-Sell, 2-Buy+Sell
//---- INPUT PARAMETERS OF EA FOR BUY TRADES 
extern double Money_Management_Up=0.1;
extern int RISK_Up = 3;
extern int Period1_Up = 7; 
extern int Period2_Up = 65; 
extern int MA_Metod_Up = 0;
extern int PRICE_Up = 0;
extern int STOPLOSS_Up = 50;
extern int TAKEPROFIT_Up = 100;
//---- INPUT PARAMETERS OF EA FOR SELL TRADES 
extern double Money_Management_Dn = 0.1;
extern int RISK_Dn = 3;
extern int Period1_Dn = 7; 
extern int Period2_Dn = 65; 
extern int MA_Metod_Dn = 0;
extern int PRICE_Dn = 0;
extern int STOPLOSS_Dn = 50;
extern int TAKEPROFIT_Dn = 100;
//---- Emulated indicator buffers
double RAVI_Up[3];
double RAVI_Dn[3];
double ASCTrend1_Up[2];
double ASCTrend1_Dn[2];
//+------------------------------------------------------------------+
//| Custom Expert functions                                          |
//+------------------------------------------------------------------+
#include <Lite_EXPERT.mqh>
//+------------------------------------------------------------------+
//| Custom Expert initialization function                            |
//+------------------------------------------------------------------+
int init()
  {
//---- 
   if(RAVI_Timeframe != 1)
       if(RAVI_Timeframe != 5)
           if(RAVI_Timeframe != 15)
               if(RAVI_Timeframe != 30)
                   if(RAVI_Timeframe != 60)
                       if(RAVI_Timeframe != 240)
                           if(RAVI_Timeframe != 1440)
                               Print("Parameter RAVI_Timeframe cannot" + 
                                     " be equal to " + RAVI_Timeframe+"!!!");
//---- 
   if(ASCT_Timeframe != 1)
       if(ASCT_Timeframe != 5)
           if(ASCT_Timeframe != 15)
               if(ASCT_Timeframe != 30)
                   if(ASCT_Timeframe != 60)
                       if(ASCT_Timeframe != 240)
                           if(ASCT_Timeframe != 1440)
                               Print("Parameter ASCT_Timeframe cannot" + 
                                     " be equal "+ASCT_Timeframe+"!!!");
//----                      
   if(RAVI_Timeframe > ASCT_Timeframe) 
       Print("Parameter ASCT_Timeframe should not be less" + 
             " than RAVI_Timeframe");                   
//---- initialization end
   return(0);
  }
//+------------------------------------------------------------------+
//| Custom Expert iteration function                                 |
//+------------------------------------------------------------------+
int start()
  {
//---- Checking whether the bars number is enough for further calculation  
   if(iBars(NULL, ASCT_Timeframe) < 3 + RISK_Up*2 + 1 + 1)
       return(0);
   if(iBars(NULL, ASCT_Timeframe) < 3 + RISK_Dn*2 + 1 + 1)
       return(0);
//----
   if(iBars(NULL, RAVI_Timeframe) < MathMax(Period1_Up, Period2_Up + 4))
       return(0);
   if(iBars(NULL, RAVI_Timeframe) < MathMax(Period1_Dn, Period2_Dn + 4))
       return(0);
//---- Declaration of variables
   static double ASCTrend1_Trend_Up, ASCTrend1_Trend_Dn;
   static int LastBars;
   int    bar;
   bool   BUY_Sign, SELL_Sign; 
//---- CALCULATION OF INDICATOR VALUES AND LOADING THEM TO BUFFERS
   if(LastBars != iBars(NULL, RAVI_Timeframe))
       switch(Buy_Sell_Custom)
         {
           case 0: 
             {           
               for(bar = 1; bar <= 3; bar++)
                   RAVI_Up[bar-1] = iCustom(NULL, RAVI_Timeframe, 
                                            "RAVI", Period1_Up, Period2_Up,
                                            MA_Metod_Up, PRICE_Up, 0, bar); 
               //----+
               ASCTrend1_Up[0] = iCustom(NULL, ASCT_Timeframe, 
                                         "ASCTrend1", RISK_Up, 0, 1); 
               ASCTrend1_Up[1] = iCustom(NULL, ASCT_Timeframe, 
                                         "ASCTrend1", RISK_Up, 1, 1); 
               //----+
               ASCTrend1_Trend_Up = ASCTrend1_Up[1] - ASCTrend1_Up[0];       
               break;
             }
           //---- 
           case 1: 
             {            
               for(bar = 1; bar <= 3; bar++)
                   RAVI_Dn[bar-1] = iCustom(NULL, RAVI_Timeframe, 
                                            "RAVI", Period1_Dn, 
                                            Period2_Dn, MA_Metod_Dn, 
                                            PRICE_Dn, 0, bar);
               //----+              
               ASCTrend1_Dn[0] = iCustom(NULL, ASCT_Timeframe, 
                                         "ASCTrend1", RISK_Dn, 0, 1); 
               ASCTrend1_Dn[1] = iCustom(NULL, ASCT_Timeframe, 
                                         "ASCTrend1", RISK_Dn, 1, 1); 
               //----+
               ASCTrend1_Trend_Dn =ASCTrend1_Dn[1] - ASCTrend1_Dn[0];
               break;
             }
           //----
           default:
             {
               for(bar = 1; bar <= 3; bar++)
                   RAVI_Up[bar-1] = iCustom(NULL, RAVI_Timeframe, 
                                            "RAVI", Period1_Up, 
                                            Period2_Up, MA_Metod_Up, 
                                            PRICE_Up, 0, bar); 
               //----+
               ASCTrend1_Up[0] = iCustom(NULL, ASCT_Timeframe, 
                                         "ASCTrend1", RISK_Up, 0, 1); 
               ASCTrend1_Up[1] = iCustom(NULL, ASCT_Timeframe, 
                                         "ASCTrend1", RISK_Up, 1, 1);
               //----+ 
               ASCTrend1_Trend_Up = ASCTrend1_Up[1] - ASCTrend1_Up[0];
               //----+             
               for(bar = 1; bar <= 3; bar++)
                   RAVI_Dn[bar-1] = iCustom(NULL, RAVI_Timeframe, 
                                            "RAVI", Period1_Dn, 
                                            Period2_Dn, MA_Metod_Dn,
                                            PRICE_Dn, 0, bar);
               //----+             
               ASCTrend1_Dn[0] = iCustom(NULL, ASCT_Timeframe, 
                                         "ASCTrend1", RISK_Dn, 0, 1); 
               ASCTrend1_Dn[1] = iCustom(NULL, ASCT_Timeframe, 
                                         "ASCTrend1", RISK_Dn, 1, 1); 
               //----+
               ASCTrend1_Trend_Dn = ASCTrend1_Dn[1] - ASCTrend1_Dn[0];
             }
         }
//---- Variable initialization
   LastBars = iBars(NULL, RAVI_Timeframe);
//---- DEFINING SIGNALS FOR TRADES
   switch(Buy_Sell_Custom)
     {
       //----
       case 0: 
         {
           if(RAVI_Up[1] - RAVI_Up[2] < 0)
               if(RAVI_Up[0] - RAVI_Up[1] > 0)
                   if(ASCTrend1_Trend_Up > 0)
                       BUY_Sign = true;
           break;
         }
       //----+ +---------------------------+ 
       case 1: 
         {
           if(RAVI_Dn[1] - RAVI_Dn[2] > 0)
               if(RAVI_Dn[0] - RAVI_Dn[1] < 0)
                   if(ASCTrend1_Trend_Dn < 0)
                       SELL_Sign = true;
           break;
         }
       //----
       default:
         {
           if(RAVI_Up[1] - RAVI_Up[2] < 0)
               if(RAVI_Up[0] - RAVI_Up[1] > 0)
                   if(ASCTrend1_Trend_Up > 0)
                       BUY_Sign = true;
           //----                 
           if(RAVI_Dn[1] - RAVI_Dn[2] > 0)
               if(RAVI_Dn[0] - RAVI_Dn[1] < 0)
                   if(ASCTrend1_Trend_Dn < 0)
                       SELL_Sign = true;
         }
     }
//---- EXECUTING TRADES
   switch(Buy_Sell_Custom)
     {
       //----
       case 0: 
         {
           OpenBuyOrder(BUY_Sign, Money_Management_Up, STOPLOSS_Up, 
                        TAKEPROFIT_Up);
           break;
         }
       //----
       case 1: 
         {
           OpenSellOrder(SELL_Sign, Money_Management_Dn, STOPLOSS_Dn,
                         TAKEPROFIT_Dn);
           break;
         }
       //----
       default:
         {
           OpenBuyOrder(BUY_Sign, Money_Management_Up, STOPLOSS_Up, 
                        TAKEPROFIT_Up);
           OpenSellOrder(SELL_Sign, Money_Management_Dn, STOPLOSS_Dn,
                         TAKEPROFIT_Dn);
         }
     }
   return(0);
  }
//+------------------------------------------------------------------+

首先我们需要分析一下这个 Expert Advisor。Expert Advisor 主要用于根据四小时制的日线图以交易者所选货币对进行交易。EA 仅可对一个货币对在此交易方向上开立头寸。EA 有两种独立的计算算法分别用于执行买入和卖出交易,因此它可开立相反的头寸。

为了能在策略测试程序中快速优化 EA,代码中包含着一个外部变量 Buy_Sell_Custom - 如果该变量值等于 0,EA 仅计算 Buy 信号。如果该变量 Buy_Sell_Custom 等于 1,则仅计算 Sell 信号。如果两种信号都要计算,此外部变量 Buy_Sell_Custom 应等于 2。其他外部变量分为两组,一组用于 Buy(买入)交易,一组用于 Sell(卖出)交易。

交易信号反映了自定义指标 RAVI.mq4 的移动方向的变化,用于切断假信号的过滤器反映了由自定义指标 ASCTrend1.mq4 所决定的趋势方向。Expert Advisor 两次调用指标 ASCTrend1.mq4 以从零指标缓冲区和第一个指标缓冲区接收源值;这些值之间呈现的差异决定了趋势的方向。EA 还两次调用指标 RAVI.mq4 以接收源值的两个变体(Buy 和 Sell),以用于两个计算算法。

我认为大部分 EA 输入参数的含义已很直白地在其名称上体现出来了。变量 MA_Metod_Up 和 MA_Metod_Dn 确定了平均法。它们可能拥有不同的值,范围介于 0 到 3 之间。变量 PRICE_Up 和 PRICE_Dn 的值是价格常数,范围介于 0 到 6 之间。

此 Expert Advisor 显然不是会亏损的那一种,尽管其结构非常简单,但其优化程度高,展现出来的成果相当优秀。所以还是值得投入一些精力将其从一组文件转换成单个的可自给自足文件。EA 代码使用文件 Lite_EXPERT.mqh,此文件实际上是两个自定义函数,EA 通过调用此文件执行交易:

//+------------------------------------------------------------------+
//|                                                  Lite_EXPERT.mqh |
//|                                   Version  January 7, 2007 Final |
//|                             Copyright © 2006,   Nikolay Kositsin | 
//|                              Khabarovsk,   farria@mail.redcom.ru | 
//+------------------------------------------------------------------+
//---- Declaring a global variable for remembering the time 
//     of the last reference to a server
int LastTime; 
//+------------------------------------------------------------------+
//| OpenBuyOrder()                                                   |
//+------------------------------------------------------------------+
int OpenBuyOrder(bool BUY_Signal, double Money_Management, int STOPLOSS, 
                 int TAKEPROFIT)
  {
   if(!BUY_Signal)
       return(0); 
   if(TimeLocal() - LastTime < 11)
       return(0); 
   int total = OrdersTotal();
//---- Checking if there is an open position on this trading 
//     pair in Buy direction
   for(int ttt = total - 1; ttt >= 0; ttt--)     
       if(OrderSelect(ttt, SELECT_BY_POS, MODE_TRADES))
           if((OrderSymbol() == Symbol()) && (OrderType() == 0))
               return(0); 
//---- 
   double ask = NormalizeDouble(Ask, Digits);              
   double bid = NormalizeDouble(Bid, Digits);
//----
   if(ask == 0.0)
       return(-1);  
   if(bid == 0.0)
       return(-1);  
//----  
   double LotVel;      
   double tickVel = MarketInfo(Symbol(), MODE_TICKVALUE);    
   if(tickVel == 0)
       return(-1); 
//----
   if(Money_Management > 0)
       LotVel = tickVel*AccountEquity()*Money_Management / 10000.0;
   else 
       LotVel = -tickVel*10000*Money_Management / 10000.0;
//----
   double Lot = NormalizeDouble(LotVel, 1);  
   if(Lot < 0.1)
       return(-1);     
//----+ Open Buy position
   double Stoploss = NormalizeDouble(bid - STOPLOSS*Point, Digits);  
   double TakeProfit = NormalizeDouble(ask + TAKEPROFIT*Point, Digits);
//----
   int ticket = OrderSend(Symbol(), OP_BUY, Lot, ask, 3, Stoploss, 
                          TakeProfit, NULL, 0, 0, CLR_NONE); 
   //----
   LastTime = TimeLocal(); 
   //----
   if(ticket > 0)
       return(1);
   else 
       return(-1);
  }  
//+------------------------------------------------------------------+
//|  OpenSellOrder()                                                 |
//+------------------------------------------------------------------+
int OpenSellOrder(bool SELL_Signal, double Money_Management, int STOPLOSS, 
                  int TAKEPROFIT)
  { 
   if(!SELL_Signal)
       return(0); 
   if(TimeLocal() - LastTime < 11)
       return(0); 
   int total = OrdersTotal();
//---- Checking if there is an open position on this trading 
//     pair in Sell direction 
   for(int kkk = total - 1; kkk >= 0; kkk--)      
       if(OrderSelect(kkk, SELECT_BY_POS, MODE_TRADES))
           if((OrderSymbol() == Symbol()) && (OrderType() == 1))
               return(0); 
//---- 
   double bid = NormalizeDouble(Bid, Digits); 
   double ask = NormalizeDouble(Ask, Digits);
//----
   if(bid == 0.0)
       return(-1); 
   if(ask == 0.0)
       return(-1); 
//----      
   double LotVel;      
   double tickVel = MarketInfo(Symbol(), MODE_TICKVALUE);
   if(tickVel == 0.0)
       return(-1);   
//----   
   if(Money_Management > 0)
       LotVel = tickVel*AccountEquity()*Money_Management / 10000.0;
   else 
       LotVel = -tickVel*10000*Money_Management / 10000.0;
//----
   double Lot = NormalizeDouble(LotVel, 1); 
   if(Lot < 0.1)
       return(-1);       
//----+ Open Sell position 
   double Stoploss = NormalizeDouble(ask + STOPLOSS*Point, Digits);         
   double TakeProfit = NormalizeDouble(bid - TAKEPROFIT*Point, Digits);
//----   
   int ticket = OrderSend(Symbol(), OP_SELL, Lot, bid, 3, Stoploss, 
                          TakeProfit, NULL, 0, 0,CLR_NONE);
//----   
   LastTime = TimeLocal(); 
//----
   if(ticket > 0)
       return(1);
   else 
       return(-1);
  }              
//+------------------------------------------------------------------+

我认为这些函数都相当简单,应该不会有什么难点。以下是这些函数的参考算法:

OpenBuyOrder(BUY_Sign, Money_Management_Up, STOPLOSS_Up, TAKEPROFIT_Up);
OpenSellOrder(SELL_Sign, Money_Management_Dn, STOPLOSS_Dn, TAKEPROFIT_Dn);

这里,如果 BUY_Sign=true,函数 OpenBuyOrder() 将开立一个多头头寸,如果 SELL_Sign=true,函数 OpenSellOrder() 将开立一个空头头寸。参数含义:Money_Management_Up、STOPLOSS_Up、TAKEPROFIT_Up、Money_Management_Dn、STOPLOSS_Dn 和 TAKEPROFIT_Dn 这些函数可以很容易从其名称上理解含义!


编写指标函数 Get_ASCTrend1Series()

在上一篇文章的结尾处(将指标代码转移到 Expert Advisor 代码中。Expert Advisor 的一般结构方案和指标函数),我提到了指标函数 Get_RAVISeries()。现在它就相当有用。我们所要做的就是为指标 ASCTrend1.mq4 编写一个类似函数。现在,根据这个指标代码,我们将编写函数 Get_ASCTrend1Series()。那么,让我们看一看这个指标代码:

//+------------------------------------------------------------------+
//|                                                        ASCTrend1 |
//|                                        Ramdass - Conversion only |
//+------------------------------------------------------------------+
//---- drawing the indicator in the main window
#property indicator_chart_window 
//---- number of indicator buffers
#property indicator_buffers 2 
//---- indicator color
#property indicator_color1 Magenta
#property indicator_color2 Aqua
//---- width of indicator lines
#property indicator_width1 2
#property indicator_width2 2
//---- INPUT PARAMETERS OF THE INDICATOR
extern int RISK = 3;
//---- indicator buffers
double val1[];
double val2[];
//+------------------------------------------------------------------+
//| ASCTrend1 initialization function                                |
//+------------------------------------------------------------------+
int init()
  {
//---- Chart drawing style
   SetIndexStyle(0, DRAW_HISTOGRAM, 0, 2);
   SetIndexStyle(1, DRAW_HISTOGRAM, 0, 2);
//---- 2 indicator buffers are used for calculation
   SetIndexBuffer(0, val1);
   SetIndexBuffer(1, val2);
//---- setting indicator values that will be unseen on the chart
   SetIndexEmptyValue(0, 0.0);
   SetIndexEmptyValue(1, 0.0);
//---- name for data windows and labels for subwindows
   IndicatorShortName("ASCTrend1");
   SetIndexLabel(0, "DownASCTrend1");
   SetIndexLabel(1, "UpASCTrend1");
//----
   return(0);
  }

//+------------------------------------------------------------------+
//| ASCTrend1                                                        |
//+------------------------------------------------------------------+
int start()
  {
//---- introducing memory variables  
   static double x1, x2;
//---- Introducing floating point variables
   double value1, value2, value3, TrueCount, Range, AvgRange, MRO1, MRO2;
//---- Introducing integer variables and getting already calculated bars
   int MaxBar, iii, kkk, bar, value10, value11, 
       counted_bars = IndicatorCounted();
//---- checking for possible errors
   if(counted_bars < 0)
       return(-1);
//---- the last calculated bar must be recalculated 
   if(counted_bars > 0)
       counted_bars--;
//---- variable initialization
   value10 = 3 + RISK*2;
//---- checking whether the bars number is enough for further calculation
   if((Bars <= value10) || (Bars < 10)) 
       return(0);
//---- defining the number of the oldest bar, 
//     starting from which all bars will be fully recalculated 
   MaxBar = Bars - 1 - value10;
//---- defining the number of the oldest bar, 
//     starting from which only new bars will be recalculated 
   bar = Bars - 1 - counted_bars;
//---- zero initialization
   if(bar >= MaxBar)
     {
       x1 = 67 + RISK;
       x2 = 33 - RISK;
       bar = MaxBar;
       //----
       for(kkk = Bars - 1; kkk >= MaxBar; kkk--) 
         {
           val1[kkk] = 0.0;
           val2[kkk] = 0.0;
         }
     }
//---- THE MAIN CYCLE OF INDICATOR CALCULATION
   while(bar >= 0)
     {  
       Range = 0.0;
       AvgRange = 0.0;
       for(iii = 0; iii <= 9; iii++) 
       AvgRange += MathAbs(High[bar+iii] - Low[bar+iii]);
       //----
       Range = AvgRange / 10;
       iii = 0;
       TrueCount = 0;
       while(iii < 9 && TrueCount < 1)
         {
           if(MathAbs(Open[bar+iii] - Close[bar+iii]) >= Range*2.0) 
           TrueCount++;
           //----
           iii++;
         }
       if(TrueCount >= 1)
           MRO1 = bar + iii; 
       else 
           MRO1 = -1;
       //----
       iii = 0;
       TrueCount = 0;
       while(iii < 6 && TrueCount < 1)
         {
           if(MathAbs(Close[bar+iii+3] - Close[bar+iii]) >= Range*4.6) 
           TrueCount++;
           //----
           iii++;
         }
       if(TrueCount >= 1)
           MRO2 = bar + iii; 
       else 
           MRO2 = -1;
       //----
       if(MRO1 > -1)
           value11 = 3; 
       else 
           value11 = value10;
       //----
       if(MRO2 > -1)
           value11 = 4; 
       else 
           value11 = value10;
       //----
       value2 = 100 - MathAbs(iWPR(NULL, 0, value11, bar));
       //---- 
       val1[bar] = 0;
       val2[bar] = 0;
       //---- 
       if(value2 > x1)
         {
           val1[bar] = Low [bar];
           val2[bar] = High[bar];
         }
       //---- 
       if(value2 < x2)
         {
           val1[bar] = High[bar]; 
           val2[bar] = Low [bar];
         }
       //---- 
       bar--;
     }
   return(0);
  }
//+------------------------------------------------------------------+

我优化了这个指标。原始版本在随附文件 ASCTrend1_Old!. mq4 中。现在让我们准备函数 Get_ASCTrend1Series()。为此,我们将根据上一篇文章中所述的相关代码改进方案,转换指标 ASCTrend1.mq4 的代码。结果,获得的自定义函数如下:

//+------------------------------------------------------------------+
//|                                          Get_ASCTrend1Series.mqh |
//|                        Copyright © 2006,        Nikolay Kositsin | 
//|                              Khabarovsk,   farria@mail.redcom.ru |
//+------------------------------------------------------------------+
bool Get_ASCTrend1Series(int Number, string symbol,int timeframe, 
                         bool NullBarRecount, int RISK, 
                         double& InputBuffer[])    
  {
//---- getting the number of all bars of a chart
   int IBARS = iBars(symbol, timeframe);  
//---- Checking whether the bars number is enough for further calculation
   if((IBARS < 3 + RISK*2 + 1)||(IBARS < 10))
       return(false);
//---- EMULATION OF INDICATOR BUFFERS
   if(ArraySize(InputBuffer) < IBARS)
     {
       ArraySetAsSeries(InputBuffer, false);
       //----  
       ArrayResize(InputBuffer, IBARS); 
       //----
       ArraySetAsSeries(InputBuffer, true);
     } 
//----+  introducing static memory variables
   static double x1[], x2[];
   static int IndCounted[]; 
//----+ changing size of static variables
   if(ArraySize(IndCounted) < Number + 1)
     {
       ArrayResize(x1, Number + 1); 
       ArrayResize(x2, Number + 1); 
       ArrayResize(IndCounted, Number + 1); 
     }
 //----+ introducing integer variable
   int LastCountBar;
//----+ Checking whether the zero bar recalculation is allowed
   if(!NullBarRecount)
       LastCountBar = 1;
//----+ Introducing floating point variables 
   double value1, value2, value3, val1, val2;
   double TrueCount, Range, AvgRange, MRO1, MRO2;
//----+ Introducing integer variables and getting already calculated bars
   int MaxBar, iii, kkk, bar, value10, value11,
       counted_bars = IndCounted[Number];
//----+ Remembering the number of all bars of the chart
   IndCounted[Number] = IBARS - 1;
//---- variable initialization
   value10 = 3 + RISK*2;
//---- defining the number of the oldest bar, 
//     starting from which new bars will be recalculated
   bar = IBARS - counted_bars - 1; 
//---- defining the number of the oldest bar, 
//     starting from which all bars will be recalculated
   MaxBar = IBARS - 1 - value10;
//---- zero initialization 
   if(bar > MaxBar)
     {
       bar = MaxBar;
       x1[Number] = 67 + RISK;
       x2[Number] = 33 - RISK;
       //----
       ArrayInitialize(InputBuffer, 0.0); 
     } 
//---- THE MAIN CYCLE OF INDICATOR CALCULATION
   while(bar >= LastCountBar)
     {  
       Range = 0.0;
       AvgRange = 0.0;
       for(iii = 0; iii <= 9; iii++) 
           AvgRange += MathAbs(iHigh(symbol, timeframe, bar + iii) -
                               iLow(symbol, timeframe, bar + iii));
       //----
       Range = AvgRange / 10;
       iii = 0;
       TrueCount = 0;
       while(iii < 9 && TrueCount < 1)
         {
           if(MathAbs(iOpen(symbol, timeframe, bar + iii) -
                      iClose(symbol, timeframe, bar + iii)) >= Range*2.0) 
               TrueCount++;
           //----
           iii++;
         }
       if(TrueCount >= 1)
           MRO1 = bar + iii; 
       else 
           MRO1 = -1;
       //----
       iii = 0;
       TrueCount = 0;
       while(iii < 6 && TrueCount < 1)
         {
           if(MathAbs(iClose(symbol, timeframe, bar + iii + 3) -
                      iClose(symbol, timeframe, bar + iii)) >= Range*4.6) 
               TrueCount++;
           //----
           iii++;
         }
       if(TrueCount >= 1)
           MRO2 = bar + iii; 
       else 
           MRO2 = -1;
       //----
       if(MRO1 > -1)
           value11 = 3; 
       else 
           value11 = value10;
       if(MRO2 > -1)
           value11 = 4; 
       else 
           value11 = value10;
       //----
       value2 = 100 - MathAbs(iWPR(symbol, timeframe, value11, bar));
       //---- 
       val1 = 0;
       val2 = 0;
       InputBuffer[bar] = 0;
       //---- 
       if(value2 > x1[Number])
         {
           val1 = iLow (symbol, timeframe, bar);
           val2 = iHigh(symbol, timeframe, bar);
         }
       if(value2 < x2[Number])
         {
           val1 = iHigh(symbol, timeframe, bar);
           val2 = iLow(symbol, timeframe, bar);
         } 
       InputBuffer[bar]=val2-val1;
       bar--;
     }
   //----+  
   return(true);
  }
//+------------------------------------------------------------------+

当然,这个函数比上一篇文章结尾处所述的那个函数更难。但既然指标源代码编写和优化时并未出错,创建此函数的流程也不会有很大的问题!

之后需要测试此函数,以了解此函数值与作为此函数开发依据的指标值之间的对应关系。为此我们应再次为函数 Get_ASCTrend1Series() 创建一个测试用 Expert Advisor:

//+------------------------------------------------------------------+
//|                                      Get_ASCTrend1SeriesTest.mq4 |
//|                      Copyright © 2007, MetaQuotes Software Corp. |
//|                                        https://www.metaquotes.net/ |
//+------------------------------------------------------------------+
#property copyright "Copyright © 2007, MetaQuotes Software Corp."
#property link      "https://www.metaquotes.net/"
//---- INPUT PARAMETERS OF THE EA
extern bool NullBarRecount = true;
//---- indicator buffers
double IndBuffer0[];
double IndBuffer1[];
double IndBuffer2[];
//+------------------------------------------------------------------+
//| Get_ASCTrend1Series() functions                                  |
//+------------------------------------------------------------------+
#include <Get_ASCTrend1Series.mqh>
//+------------------------------------------------------------------+
//| Custom Expert initialization function                            |
//+------------------------------------------------------------------+
int init()
  {
//---- initialization end
   return(0);
  }
//+------------------------------------------------------------------+
//| Custom Expert iteration function                                 |
//+------------------------------------------------------------------+
int start()
  {
//---- 
   double Ind_Velue,Resalt;
//---- 
   if(!Get_ASCTrend1Series(0, Symbol(), 0, NullBarRecount, 1, IndBuffer0))
       return(0);
   if(!Get_ASCTrend1Series(1, Symbol(), 240, NullBarRecount, 3, IndBuffer1))
       return(0);
   if(!Get_ASCTrend1Series(2, Symbol(), 1440, NullBarRecount, 5, IndBuffer2))
       return(0);
//---- getting indicator values for the test 0
   Ind_Velue = iCustom(NULL, 0, "ASCTrend1", 1, 1, 2) - 
               iCustom(NULL, 0, "ASCTrend1", 1, 0, 2); 
   Resalt = IndBuffer0[2] - Ind_Velue; 
   Print("0:     " + Ind_Velue + "    " + IndBuffer0[2] + 
         "    " + Resalt + "");
//---- getting indicator values for the test 1
   Ind_Velue = iCustom(NULL, 240, "ASCTrend1", 3, 1, 2) - 
               iCustom(NULL, 240, "ASCTrend1", 3, 0, 2); 
   Resalt = IndBuffer1[2] - Ind_Velue; 
   Print("H4:    " + Ind_Velue + "    " + IndBuffer1[2] + 
         "    " + Resalt + "");
//---- getting indicator values for the test 2
   Ind_Velue = iCustom(NULL, 1440, "ASCTrend1", 5, 1, 2) - 
               iCustom(NULL, 1440, "ASCTrend1", 5, 0, 2); 
   Resalt = IndBuffer2[2] - Ind_Velue; 
   Print("Daily: " + Ind_Velue + "    " + IndBuffer2[2] + 
         "    " + Resalt + "");
//----
   return(0);
  }
//+------------------------------------------------------------------+

在策略测试程序中开始测试此 Expert Advisor,之后检查日志文件,确保函数值与用模拟指标缓冲区值填充的指标值完全相同!


初始 Expert Advisor 代码的最终转换

现在是最后一步,我们可以转换 EA 代码,将自定义指标调用替代为自定义指标函数调用:

//+------------------------------------------------------------------+
//|                                      NewASCTrend1RAVI_Expert.mq4 |
//|                             Copyright © 2006,   Nikolay Kositsin | 
//|                              Khabarovsk,   farria@mail.redcom.ru | 
//+------------------------------------------------------------------+
#property copyright "Copyright © 2006, Nikolay Kositsin"
#property link "farria@mail.redcom.ru"
//---- INPUT PARAMETERS
extern int RAVI_Timeframe = 240;
extern int ASCT_Timeframe = 1440;
//---- FILTER OF TRADE CALCULATION DIRECTION
extern int Buy_Sell_Custom = 2; // 0-Buy, 1-Sell, 2-Buy+Sell
//---- INPUT PARAMETERS OF EA FOR BUY TRADES 
extern double Money_Management_Up = 0.1;
extern int RISK_Up = 3;
extern int Period1_Up = 7; 
extern int Period2_Up = 65; 
extern int MA_Metod_Up = 0;
extern int PRICE_Up = 0;
extern int STOPLOSS_Up = 50;
extern int TAKEPROFIT_Up = 100;
//---- INPUT PARAMETERS OF EA FOR SELL TRADES 
extern double Money_Management_Dn = 0.1;
extern int RISK_Dn = 3;
extern int Period1_Dn = 7; 
extern int Period2_Dn = 65; 
extern int MA_Metod_Dn = 0;
extern int PRICE_Dn = 0;
extern int STOPLOSS_Dn = 50;
extern int TAKEPROFIT_Dn = 100;
//---- Emulated indicator buffers
double RAVI_Up[];
double RAVI_Dn[];
double ASCTrend1_Up[];
double ASCTrend1_Dn[];
//+------------------------------------------------------------------+
//| Get_ASCTrend1Series functions()                                  |
//+------------------------------------------------------------------+
#include <Get_ASCTrend1Series.mqh>
//+------------------------------------------------------------------+
//| Get_RAVISeries functions()                                       |
//+------------------------------------------------------------------+
#include <Get_RAVISeries.mqh>
//+------------------------------------------------------------------+
//| Custom Expert functions                                          |
//+------------------------------------------------------------------+
#include <Lite_EXPERT.mqh>
//+------------------------------------------------------------------+
//| Custom Expert initialization function                            |
//+------------------------------------------------------------------+
int init()
  {
//---- 
   if(RAVI_Timeframe != 1)
       if(RAVI_Timeframe != 5)
           if(RAVI_Timeframe != 15)
               if(RAVI_Timeframe != 30)
                   if(RAVI_Timeframe != 60)
                       if(RAVI_Timeframe != 240)
                           if(RAVI_Timeframe != 1440)
                               Print("Parameter RAVI_Timeframe cannot" + 
                                     " be equal to " +
                                     RAVI_Timeframe + "!!!");
//---- 
   if(ASCT_Timeframe != 1)
       if(ASCT_Timeframe != 5)
           if(ASCT_Timeframe != 15)
               if(ASCT_Timeframe != 30)
                   if(ASCT_Timeframe != 60)
                       if(ASCT_Timeframe != 240)
                           if(ASCT_Timeframe != 1440)
                               Print("Parameter ASCT_Timeframe cannot" + 
                                     " be equal to "+
                                     ASCT_Timeframe+"!!!");
//----                      
   if(RAVI_Timeframe > ASCT_Timeframe) 
       Print("Parameter ASCT_Timeframe should not be " + 
             "less than RAVI_Timeframe");                   
//---- initialization end
   return(0);
  }
//+------------------------------------------------------------------+
//| Custom Expert iteration function                                 |
//+------------------------------------------------------------------+
int start()
  {
//---- Checking whether the bars number is enough for further calculation  
   if(iBars(NULL, ASCT_Timeframe) < 3 + RISK_Up*2 + 1 + 1)
       return(0);
   if(iBars(NULL, ASCT_Timeframe) < 3 + RISK_Dn*2 + 1 + 1)
       return(0);
//----
   if(iBars(NULL, RAVI_Timeframe) < MathMax(Period1_Up, Period2_Up + 4))
       return(0);
   if(iBars(NULL, RAVI_Timeframe) < MathMax(Period1_Dn, Period2_Dn + 4))
       return(0);
//---- Declaring variables for blocking zero bar recalculation 
   static int LastBars;
//---- Declaring logic variables for trend signals
   bool   BUY_Sign, SELL_Sign;
//---- CALCULATION OF INDICATOR VALUES AND LOADING THEM TO BUFFERS 
   if(LastBars != iBars(NULL, RAVI_Timeframe))
       switch(Buy_Sell_Custom)
         {
           case 0: 
             {
               Get_RAVISeries(0, Symbol(), RAVI_Timeframe, false, 
                              Period1_Up, Period2_Up, MA_Metod_Up,
                              PRICE_Up, RAVI_Up);
               //----
               Get_ASCTrend1Series(0, Symbol(), ASCT_Timeframe, 
                                   false, RISK_Up, ASCTrend1_Up);  
               break;
             }
           case 1: 
             {            
               Get_RAVISeries(1, Symbol(), RAVI_Timeframe, false, 
                              Period1_Dn, Period2_Dn, MA_Metod_Dn, 
                              PRICE_Dn, RAVI_Dn);
               //----
               Get_ASCTrend1Series(1, Symbol(), ASCT_Timeframe, 
                                   false, RISK_Dn, ASCTrend1_Dn); 
               break;
             }
           default:
             {
               Get_RAVISeries(0, Symbol(), RAVI_Timeframe, false,
                              Period1_Up, Period2_Up, MA_Metod_Up,
                              PRICE_Up, RAVI_Up);
               //----
               Get_ASCTrend1Series(0, Symbol(), ASCT_Timeframe, 
                                   false, RISK_Up, ASCTrend1_Up); 
               //----              
               Get_RAVISeries(1, Symbol(), RAVI_Timeframe, false,
                              Period1_Dn, Period2_Dn,MA_Metod_Dn,
                              PRICE_Dn,RAVI_Dn);
               //----
               Get_ASCTrend1Series(1, Symbol(), ASCT_Timeframe, 
                                   false, RISK_Dn, ASCTrend1_Dn); 
             }
         }
       //---- Variable initialization
       LastBars = iBars(NULL, RAVI_Timeframe);
       //---- DEFINING SIGNALS FOR TRADES 
       switch(Buy_Sell_Custom)
         {
       case 0: 
         {
           if(RAVI_Up[2] - RAVI_Up[3] < 0)
               if(RAVI_Up[1] - RAVI_Up[2] > 0)
                   if(ASCTrend1_Up[1] > 0)
                       BUY_Sign = true;
           break;
         }
       case 1: 
         {
           if(RAVI_Dn[2] - RAVI_Dn[3] > 0)
               if(RAVI_Dn[1] - RAVI_Dn[2] < 0)
                   if(ASCTrend1_Dn[1] < 0)
                       SELL_Sign = true;
           break;
         }
       default:
         {
           if(RAVI_Up[2] - RAVI_Up[3] < 0)
               if(RAVI_Up[1] - RAVI_Up[2] > 0)
                   if(ASCTrend1_Up[1] > 0)
                       BUY_Sign = true;
           //----+                  
           if(RAVI_Dn[2] - RAVI_Dn[3] > 0)
               if(RAVI_Dn[1] - RAVI_Dn[2] < 0)
                   if(ASCTrend1_Dn[1] < 0)
                       SELL_Sign = true;
         }
     }
//---- EXECUTING TRADES
   switch(Buy_Sell_Custom)
     {
       case 0: 
         {
           OpenBuyOrder(BUY_Sign, Money_Management_Up, STOPLOSS_Up, 
                        TAKEPROFIT_Up);
           break;
         }
       case 1: 
         {
           OpenSellOrder(SELL_Sign, Money_Management_Dn, STOPLOSS_Dn, 
                         TAKEPROFIT_Dn);
           break;
         }
       default:
         {
           OpenBuyOrder(BUY_Sign, Money_Management_Up, STOPLOSS_Up, 
                        TAKEPROFIT_Up);
           OpenSellOrder(SELL_Sign, Money_Management_Dn, STOPLOSS_Dn, 
                         TAKEPROFIT_Dn);
         }
     }
   return(0);
  }
//+------------------------------------------------------------------+

当然,由于模拟指标缓冲区中使用的单元头寸比起源缓冲区有所变化,EA 代码的最后转换需要在 "DEFINING SIGNALS FOR TRADES" 部分中的函数 start() 内做一些改动。


最终 Expert Advisor 和源 Expert Advisor 的测试结果

现在我们可以将最终 EA 加载到策略测试程序中,将其针对历史记录的操作结果与转换前的相应 EA 操作结果进行比较。我们看到,两种情况下按历史记录测试 EA 产生的交易结果并无差别。如果测试时段相同,加载到 EA 中的外部变量值相同,所用测试方法也相同,那么这两种方法产生的测试结果是完全相同的。

但在所有情况下,最终 Expert Advisor 的操作速度都慢于源 EA,这一点是显而易见的!







更长 EA 测试时间的原因何在?我们可以假设它与使用缓冲区操作的指标模拟模式以及将依据指标函数的值返回到依据参考的缓冲区有着某种关联。当然,可以通过忽略缓冲区操作的指标模拟模式来进一步探究原因。您可看一看 EA FastNewASCTrend1RAVI_Expert. mq4 并测试它。但在这种情况下,发生的情况是完全相同的 - 此 EA 与上一个 EA 运行地一样缓慢。因此,我们明显可以得出以下结论:在我们这个使用绝对的非资源密集型指标进行的相当简单的代码转换案例中,为加快 EA 操作而采用自定义函数的替代项完全是不合理的!


总结

对于使用非资源密集型指标的参考的 Expert Advisor,通过将自定义指标调用替换为自定义函数调用来加快 Expert Advisor 操作的任务通常是无法实现的!但如果有人非常想要开发一个包含单个文件且完全独立自主的 Expert Advisor,同时忽略自定义指标调用步骤,也可以去做。不过这会花很多精力,因为这个任务无法简化。所以必须有非常正当的理由才能这么做。

本文由MetaQuotes Ltd译自俄文
原文地址: https://www.mql5.com/ru/articles/1463

为嘉盛开发群集指标的理论基础 为嘉盛开发群集指标的理论基础
群集指标是一系列将货币对分成独立的货币的指标。指标允许跟踪相对货币波动,确定形成新的货币趋势的潜能,接收交易信号,以及追踪中期和长期仓位。
如何在 MQL 4 中开发可靠安全的交易机器人 如何在 MQL 4 中开发可靠安全的交易机器人
本文旨在处理开发和使用 Expert Advisor 过程中出现的最常见的错误。还将介绍一个安全的自动交易系统的示例。
基于枢纽点分析的交易策略 基于枢纽点分析的交易策略
枢轴点(PP)分析是对于日内大幅波动市场最简单和最为有效的策略之一。它早在计算机出现之前已经投入使用,那时证券交易者除了算盘和计数器之外,还无法使用任何自动数据处理设备。
四个时间范围的多个指标信号的同步显示 四个时间范围的多个指标信号的同步显示
手动交易时,你必须留意多个指标的值。这跟机械化交易略有不同。如果你有两个或三个指标,且已选择了一个时间范围进行交易,则任务并不复杂。但如果你有五个或六个指标,且交易策略要求考虑多个时间范围上的信号,你将如何做呢?