![インディケータコードの Expert Advisor コードへの変換おわりに](https://c.mql5.com/i/articles/overlay.png)
インディケータコードの Expert Advisor コードへの変換おわりに
はじめに
先行記事(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 向けクラスターインディケータ構築の理論的基礎](https://c.mql5.com/2/14/330_3.png)
![MQL 4 で信頼性ある安全な売買ロボットを作成する方法](https://c.mql5.com/2/14/327_2.png)
![マーケットトレンドの明確化に役立つピボットポイント](https://c.mql5.com/2/14/333_1.png)
![4 とおりのタイムフレームからの複数インディケータシグナルの同時表示](https://c.mql5.com/2/14/325_3.png)
![MQL5 - MetaTrader 5クライアントターミナルにビルトインされたトレードストラテジーの言語](https://c.mql5.com/i/registerlandings/logo-2.png)
- 無料取引アプリ
- 8千を超えるシグナルをコピー
- 金融ニュースで金融マーケットを探索