English Русский 中文 Español Deutsch Português
インディケータコードの Expert Advisor コードへの変換おわりに

インディケータコードの Expert Advisor コードへの変換おわりに

MetaTrader 4 | 17 2月 2016, 16:23
694 0
Nikolay Kositsin
Nikolay Kositsin

はじめに

先行記事(Transferring an Indicator Code into an Expert Advisor Code. Indicator Structure および Transferring an Indicator Code into an Expert Advisor Code. General Structural Schemes of an Expert Advisor and Indicator Functions) で、既製のインディケータコードを基にインディケータ関数を書く一般的スキームを分析し、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 は基本4時間、トレーダーが選択する通貨ペアに関する日次チャートに基づいてトレーディングを行います。EA はこのトレーディング方向においては 1 組の通貨ペアについてポジションを 1 つだけオープンします。EA には売りと買いのトレードを実行する計算アルゴリズムが独立して2つあります。それは逆方向のポジションをオープンするためです。

ストラテジーテスタで EA を迅速に最適化するために、外部変数 Buy_Sell_Custom を持ちます。この変数がゼロであれば EA は「買い」シグナルのみ計算します。変数 Buy_Sell_Custom が1であれば「売り」シグナルのみ計算されます。両方を計算する必要がある場合、外部変数 Buy_Sell_Custom は2となります。その他の外部変数は2つのグループに分けられます。「買い」トレードと「売り」トレードです。

トレードシグナルはカスタムインディケータ RAVI.mq4、フィルターの変動方向の変化で、偽シグナルの切り捨てはトレンド方向でこれはカスタムインディケータ ASCTrend1.mq4 によって決定されるものです。Expert Advisor にはゼロと最初のインディケータバッファソース値から受け取るためにインディケータ ASCTrend1.mq4 を2度呼び出します。それらの差の符号がトレンド方向を決定します。EA もインディケータ RAVI.mq4 を 2 度呼び出します。2 つの計算アルゴリズムに対するソース値の 2 つのバリアント(「買い」と「売り」)を受け取るためです。

EA の入力パラメータの多くの意味は名前から解るようになっているようです。変数 MA_Metod_Up および MA_Metod_Dn は平均化の手法を決定します。それらはゼロから 3 までの異なる値を持ちます。変数 PRICE_Up および PRICE_Dn は価格定数値を持ち、変化の範囲はゼロから 6 です。

この Expert Advisor は明確に損失1ではなく、シンプルでありながら良好な最適化におけるきわめて良好な結果を出します。そのため、1組のファイルから単一の自己充足的ファイルに変換する労力に値します。EA コードはファイル Lite_EXPERT.mqh を使用します。それは実際2つのカスタム関数で、それを呼びだすことで 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= 真であれば、関数 OpenBuyOrder() がロングポジションをオープンし、SELL_Sign= 真であれば関数 OpenSellOrder() がショートポジションをオープンします。パラメータの意味:Money_Management_Up、STOPLOSS_Up、 TAKEPROFIT_Up、Money_Management_Dn、STOPLOSS_Dn、 TAKEPROFIT_Dn な名前から理解することができます。

インディケータ関数 Get_ASCTrend1Series() の記述

先行記事(Transferring an Indicator Code into an Expert Advisor Code. General Structural Schemes of an Expert Advisor and Indicator Functions)の末尾で、インディケータ関数 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. |
//|                                        http://www.metaquotes.net/ |
//+------------------------------------------------------------------+
#property copyright "Copyright © 2007, MetaQuotes Software Corp."
#property link      "http://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 コードの変換は関数 start() 内部の "DEFINING SIGNALS FOR TRADES" 部分に若干の変更が必要でした。というのもソースバッファと比較してエミュレートされたインディケータバッファで使用されるセルの位置変化があったためです。

最終およびソース Expert Advisor の検証結果

ここで最終的な EA をストラテジーテスタにロードし、履歴上の処理結果を変換前の EA の同じ結果と比較します。そしてどちらの場合においても履歴における EA 検証でのトレーディング結果に差はまったくないことがわかります。どちらの EA バリアントも検証期間が同一で EA に外部変数の同一値がロードされ、同一の検証手法が用いられていれば、まったく同じ検証結果を示しています。

ただ、一見して明らかなのは、あらゆる状況において、最終的な Expert Advisor はソース EA よりも動作が遅いことです。







より長い EA の検証期間に対する理由はなんでしょうか?バッファ処理のインディケータモードのエミュレーションを使用すること、またバッファへのインディケータ関数による値を戻るのが参照によることにいくばくかの関連があると考えます。もちろんこれはすべてバッファ処理のインディケータモードのエミュレーションを省略することでさらに発展させることが可能です。EA の FastNewASCTrend1RAVI_Expert mq4 を見て検証するのもよいでしょう。ただこの場合も状況は同じです。- この EA は前のものと同じように処理がゆっくりしています。そのため、次の結論が明らかです。リソースを大量消費しないインディケータを利用したわれわれのきわめてシンプルなコード変換ケースでは、 EA がより迅速な処理を行うためにカスタム関数を置換することは絶対に不合理です。

おわりに

リソースをあまり大量消費しないインディケータを参照する Expert Advisors にとって、カスタムインディケータ呼び出しをカスタム関数呼び出しに置き換えることでもっと迅速にするタスクは一般的に遂行不能です。ただし、完全自立 Expert Advisor を開発したいという願望が大きければ、単一ファイルで構成し、カスタムインディケータ呼び出しを省略することでこれは可能となります。しかし、このタスクは単純化できないため、多くの労力が必要です。そのためこの欲求にはひじょうに深刻な理由が必要です。


MetaQuotes Ltdによってロシア語から翻訳されました。
元の記事: https://www.mql5.com/ru/articles/1463

FOREX 向けクラスターインディケータ構築の理論的基礎 FOREX 向けクラスターインディケータ構築の理論的基礎
クラスターインディケータは通貨ペアを個別の通貨に分けるインディケータセットです。インディケータは相対通貨変動をトレースし、新しい通貨トレンドのフォーマット可能性を判断し、トレードシグナルを受信し、中長期ポジションをフォローします。
MQL 4 で信頼性ある安全な売買ロボットを作成する方法 MQL 4 で信頼性ある安全な売買ロボットを作成する方法
本稿では、Expert Advisor を作成し使用する場面で生じるごく一般的なエラーについてお話します。典型的な安全な自動売買システムについてもご説明します。
マーケットトレンドの明確化に役立つピボットポイント マーケットトレンドの明確化に役立つピボットポイント
ピボットポイントはある通貨ペアのその後のトレンドを表示する価格チャート上の線です。価格がこの線より上にあると、トレンドは上昇しています。したがって価格がこの線より下だとトレンドは下降します。
4 とおりのタイムフレームからの複数インディケータシグナルの同時表示 4 とおりのタイムフレームからの複数インディケータシグナルの同時表示
マニュアルトレーディングでは、複数インディケータの値から目を離すわけにはいきません。メカニカルトレーディングではそこは少しばかり異なります。2~3個のインディケータがあると、トレーディングにはタイムフレームを1つ選ぶ必要があり、それはむつかしい仕事ではありません。しかし5~6個ものインディケータがあり、トレーディング戦略が複数のタイムフレームについてのシグナルを考慮することを要求すればどうなるのでしょうか?