English Русский 中文 Español 日本語 Português
Übertragen eines Indikator Code in einen Expert Advisor Code. Abschluss

Übertragen eines Indikator Code in einen Expert Advisor Code. Abschluss

MetaTrader 4Beispiele | 28 Januar 2016, 15:13
1 633 0
Nikolay Kositsin
Nikolay Kositsin

Einführung

In den vorherigen Artikeln (Übertragen eines Indikator Code in einen Expert Advisor Code. Indikator-Strukturund Übertragen eines Indikator Code in einen Expert Advisor Code. Allgemeine Strukturelle Regelungen von Expert Advisor und Anzeigefunktionen) haben wir das allgemeine Schema des Schreibens einer Anzeigefunktion auf der Basis eines fertigen Indikator Codes analysiert und seine Korrelation mit einem Expert Advisor definiert. Nun ist es an der Zeit, den Code eines richtigen Expert Advisor zu schreiben. Also, lassen Sie uns beginnen.


EA Quellcode

Also, wir haben den folgenden Expert Advisor Code:

//+------------------------------------------------------------------+
//|                                         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);
  }
//+------------------------------------------------------------------+

Zunächst müssen wir diesen Expert Advisor analysieren. Der Expert Advisor ist für den Handel auf Basis von 4-Stunden und Tages-Chart gedacht, auf einen Währungspaar, das von dem Trader ausgewählt wird. Der EA kann nur eine Position von einem Währungspaar in eine Handelsrichtung öffnen. Der EA verfügt über zwei unabhängige Berechnungsalgorithmen zur Ausführung von Sell und Buy Trades, weshalb er entgegengesetzte Positionen öffnen kann.

Für eine schnellere Optimierung des EA im Strategietester, enthält er eine externe Variable Buy_Sell_Custom - wen sie gleich Null ist, berechnet der EA nur Buy-Signale. Wenn die Variable Buy_Sell_Custom gleich Eins ist, werden nur Sell-Signale berechnet. Wenn wir beide Berechnungen benötigen, muss die externe Variable Buy_Sell_Custom gleich Zwei sein. Andere externe Variablen sind in zwei Gruppen unterteilt: für Buy und Sell Trades.

Die Handelssignale sind Änderungen in der Bewegungsrichtung des benutzerdefinierten Indikators RAVI.mq4, der Filter für falsche Signale ist die Trendrichtung, bestimmt durch den benutzerdefinierten Indikator ASCTrend1.mq4. Der Expert Advisor verfügt über zwei Aufrufe des Indikators ASCTrend1.mq4 für den Erhalt des Null- und der ersten Anzeigepuffer Quellwerte. Das Zeichen ihrer Differenz bestimmt die Trendrichtung. Der EA verfügt außerdem über zwei Aufrufe des Indikators RAVI.mq4, für den Empfang von zwei Varianten (Buy und Sell) von Quellwerten für die zwei Berechnungsalgorithmen.

Ich nehme an, die Mehrheit der Eingabeparameter sollte durch ihre Namen verständlich sein. Die Variablen MA_Metod_Up und MA_Metod_Dn bestimmen die Methode der Mittelwertbestimmung. Sie müssen unterschiedliche Werte von Null bis Drei haben. Die Variablen Price_Up und Price_Dn haben Werte von Preis-Konstanten, Grenzen ihrer Änderung sind von Null bis Sechs.

Dieser Expert Advisor ist eindeutig kein verlierender, und trotz seiner Einfachheit, zeigt er ziemlich gute Ergebnisse bei einer guten Optimierung. Also ist es etwas Mühe wert, ihn von einem Satz an Dateien in eine einzelne autarke Datei zu wandeln. Der Ea Code verwendet die Datei Lite_Expert.mqh, die eigentlich zwei benutzerdefinierte Funktionen sind, durch deren Aufruf der EA Trades ausführt:

//+------------------------------------------------------------------+
//|                                                  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);
  }              
//+------------------------------------------------------------------+

Ich nehme an, diese Funktionen sind ziemlich einfach, und es sollte keine Schwierigkeiten mit ihnen geben. Hier ist der Algorithmus, der auf die Funktionen verweist:

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

Wenn hier BUY_Sign=true öffnet die Funktion OpenBuyOrder() eine Long-Position, wenn SELL_Sign=true, öffnet die Funktion OpenSellOrder() eine Short-Position. Bedeutung von Parametern: Money_Management_Up, STOPLOSS_Up, TAKEPROFIT_Up, Money_Management_Dn, STOPLOSS_Dn, TAKEPROFIT_Dn ist verständlich durch deren Namen!


Schreiben einer Anzeigefunktion Get_ASCTrend1Series()

Am Ende des vorherigen Artikels (Übertragen eines Indikator Code in einen Expert Advisor Code. Allgemeine Strukturelle Regelungen von Expert Advisor und Anzeigefunktionen) stellte ich die Anzeigefunktion Get_RAVISeries() vor. Sie wäre jetzt ziemlich nützlich. Alles, was wir brauchen, ist eine analoge Funktion für den Indikator ASCTrend1.mq4 zu schreiben. Nun werden wir auf der Basis des Indikator-Codes die Funktion Get_ASCTrend1Series() schreiben. Werfen wir also einen Blick auf den Indikator-Code:

//+------------------------------------------------------------------+
//|                                                        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);
  }
//+------------------------------------------------------------------+

Ich habe diesen Indikator optimiert. Die Originalversion ist in der angehängten Datei ASCTrend1_Old!. mq4. Nun wollen wir die Funktion Get_ASCTrend1Series() vorbereiten. Zu diesem Zweck verändern wir den Code des Indikators ASCTrend1.mq4 entsprechend dem Schema seiner Code-Verbesserungen, beschrieben in dem vorherigen Artikel. Als Ergebnis erhalten wir die folgende benutzerdefinierte Funktion:

//+------------------------------------------------------------------+
//|                                          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);
  }
//+------------------------------------------------------------------+

Natürlich ist diese Funktion schwieriger als die am Ende des vorherigen Artikels beschriebene. Aber während der Indikator-Quellcode geschrieben und ohne Fehler optimiert wurde, ist der Vorgang der Erstellung dieser Funktion nicht sehr problematisch!

Danach muss die Funktion auf die Übereinstimmung ihrer Werte mit den Indikatorwerten getestet werden. Zu diese Zweck sollten wir noch einmal einen Tester Expert Advisor für die Funktion Get_ASCTrend1Series() erstellen:

//+------------------------------------------------------------------+
//|                                      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);
  }
//+------------------------------------------------------------------+

Starten Sie das Testen dieses Expert Advisors im Strategietester und danach prüfen Sie die Protokolldatei, um sicherzustellen, dass die Werte unserer Funktion vollständig identisch mit denen des Indikators sind, in Bezug auf das Füllen der emulierten Anzeigepuffer-Werte!


Letzte Änderung des anfänglichen Expert Advisor Code

Und jetzt können wir endlich den EA-Code umwandeln, ersetzen benutzerdefinierte Indikator-Aufrufe durch benutzerdefinierte Anzeigefunktion-Aufrufe:

//+------------------------------------------------------------------+
//|                                      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);
  }
//+------------------------------------------------------------------+

Natürlich benötigt die letzte Umwandlung des EA-Code einige Änderungen innerhalb der Funktion start() in dem Teil "DEFINING SIGNALS FOR TRADES" wegen der verwendeten Änderungen in den Positionen von Zellen in emulierten Anzeigepuffern, im Vergleich zu Quellpuffern.


Testergebnisse des Endgültigen und des Quell-Expert-Advisor

Jetzt können wir den endgültigen EA in den Strategietester laden, und seine Ergebnisse des Betriebs mit historischen Daten mit den Ergebnissen des EA vor der Umwandlung vergleichen. Und wir sehen in beiden Fällen keinen Unterschied in den Handelsergebnissen beim Testen des EA mit der Historie. Beide EA Varianten erzielen absolut identische Testergebnisse, wenn der Testzeitraum identisch ist, die gleichen Werte der externen Variablen in die EAs geladen werden und die gleiche Testmethode verwendet wird.

Was aber auf den ersten Blick klar ist, dass in allen Situationen der endgültige Expert Advisor langsamer arbeitet als der Quell-EA!

Was ist der Grund für die längere EA-Testzeit? Wir können annehmen, dass es irgendwie mit der verwendeten Emulation des Anzeigemodus des Pufferbetriebs und zurückgegebenen Werten durch Anzeigefunktionen in Puffer durch Verweis verbunden ist. Natürlich kann all dies weiterentwickelt werden, durch Weglassen der Emulation des Anzeigemodus des Pufferbetriebs. Sie können einen Blick auf den EA FastNewASCTrend1RAVI_Expert. mq4 werfen und ihn testen. Aber in diesem Fall sind die Situationen absolut die gleichen - dieser EA arbeitet so langsam wie der vorherige. Also, das folgende Fazit ist offensichtlich: in unserem ziemlich einfachen Fall der Code-Umwandlung unter Verwendung absolut Ressourcen-schonender Indikatoren, ist ihr Ersatz durch benutzerdefinierte Funktionen für schnelleren EA-Betrieb absolut unvernünftig!


Fazit

Für Expert Advisors die Verweise zu sehr Ressourcen-schonenden Indikatoren, ist die Aufgabe sie schneller zu machen mit dem Ersatz von benutzerdefinierten Indikator-Aufrufen durch benutzerdefinierte Funktionsaufrufe in der Regel unlösbar! Allerdings, wenn jemand das große Verlangen hat einen absolut unabhängigen Expert Advisor zu entwickeln, bestehend aus einer einzigen Datei und dem Weglassen von Aufrufen benutzerdefinierter Indikatoren, kann man dies machen. Es wird aber viel Mühe erfordern, weil diese Aufgabe nicht vereinfacht werden kann. Also muss dieses Verlangen von sehr schwerwiegenden Gründen unterstützt werden.

Übersetzt aus dem Russischen von MetaQuotes Ltd.
Originalartikel: https://www.mql5.com/ru/articles/1463

Theoretische Grundlagen zum Aufbau von Cluster Indikatoren für Devisenhandel Theoretische Grundlagen zum Aufbau von Cluster Indikatoren für Devisenhandel
Cluster Indikatoren sind Indikatoren, die Währungspaare in einzelne Währungen aufteilen. Indikatoren ermöglichen das Verfolgen der relativen Währungsfluktuation, bestimmen das Potential der Bildung eines neuen Trends, erhalten Handelssignale und folgen mittelfristigen und langfristigen Positionen.
Wie man einen Zuverlässigen und Sicheren Handelsroboter in MQL4 entwickelt Wie man einen Zuverlässigen und Sicheren Handelsroboter in MQL4 entwickelt
Der Artikel befasst sich mit den häufigsten Fehlern, die Auftreten bei der Entwicklung und Verwendung eines Expert Advisor. Ein beispielhaftes sicheres automatisches Handelssystem wird auch beschrieben.
Handelsstrategie basierend auf Pivot Punkte Analyse Handelsstrategie basierend auf Pivot Punkte Analyse
Pivot Punkte (PP) Analyse ist eine der einfachsten und effektivsten Strategien für hoch volatile Intraday Märkte. Sie wurde bereits in der Zeit vor den Computern verwendet, als Trader, die mit Aktien arbeiteten, keine ADP verwenden konnten, ausgenommen Zählrahmen und Arithmometer.
Ein Expert Advisor auf Bestellung gemacht. Handbuch für einen Trader Ein Expert Advisor auf Bestellung gemacht. Handbuch für einen Trader
Nicht alle Trader sind Programmierer. Und nicht alle Programmierer sind wirklich gut. Also, was sollte getan werden, wenn Sie Ihr System automatisieren müssen und keine Zeit und Lust haben MQL4 zu studieren?