インディケータコードから Expert Advisor コードへの変換Expert Advisor およびインディケータ関数の一般的スキーム
はじめに
前稿(Transferring an Indicator Code into an Expert Advisor Code. Indicator Structure) では、インディケータの一般的ストラクチャ、Expert Advisor コードに変換するコードを分析し、インディケータコードの事前調整の主要な考えを記述しました。ここでは取得したコードをカスタム関数に変換してみます。というのもこれはおそらくExpert Advisor でインディケータコードを 表現するのにもっとも便利な方法だからです。カスタム関数は mqh-file として示され、命令を用いて Expert Advisor 内でそれを宣言するのに場所はほとんどいりません。またこの関数の呼び出しはカスタムインディケータの呼び出しほどむつかしくありません。より重要なのは、そのようなカスタム関数はあらゆる Expert Advisors でさらに使用するにあたりどんな状況にも対応することが可能であることです。
その関数を書く前に、内部構造にかかわりなく、この関数がの他 Expert Advisor の部分とどのように連携するか分析します。
カスタムインディケータ呼び出しを伴う EA のストラクチャ
まずカスタムインディケータからデータを受信する EA のストラクチャの概要を調べます。この EA では、最初にカスタムインディケータからデータを受信する部分のみ注目します。今までのところ、EA による受信したデータの処理方法、それのシグナルへの変換、EA の実行箇所のストラクチャについては述べません。この EA のカスタムインディケータを前稿で述べたものとして分析します。以下は Expert Advisor のストラクチャ例です。
//+------------------------------------------------------------------+ //| ExpertIndPlan0.mqh | //| Copyright © 2007, MetaQuotes Software Corp. | //| http://www.metaquotes.net/ | //+------------------------------------------------------------------+ #property copyright "Copyright © 2007, MetaQuotes Software Corp." #property link "http://www.metaquotes.net/" //---- EA input parameters extern int period10 = 15; extern int period11 = 15; extern int period12 = 15; //---- EA input parameters extern int period20 = 15; extern int period21 = 15; extern int period22 = 15; //---- Declaring buffers for indicator values double Ind_Buffer1[6]; double Ind_Buffer2[6]; //+------------------------------------------------------------------+ //| Custom Expert initialization function | //+------------------------------------------------------------------+ int init() { // Here is the code of EA initialization //---- initialization end return(0); } //+------------------------------------------------------------------+ //| Custom Expert iteration function | //+------------------------------------------------------------------+ int start() { //---- Checking whether the bars number is enough for further calculation if(iBars(Symbol(), 0) < period10 + period11 + period12 + 10) return(0); if(iBars(Symbol(), 0) < period20 + period21 + period22 + 10) return(0); //---- getting indicator values for further calculation for(int bar = 5; bar >= 1; bar--) { Ind_Buffer1[bar] = iCustom("IndicatorPlan", Symbol(), 0, period10, period11, period12, 0, bar); Ind_Buffer2[bar] = iCustom("IndicatorPlan", Symbol(), 0, period20, period21, period22, 0, bar); } // Here is the EA code, forming trade signals // based on the values of indicator buffer cells //---- // here is the EA executive part code, // requesting for placing an order //---- return(0); } //+------------------------------------------------------------------+
このスキームでは、各ティックでカスタムインディケータ IndicatorPlan mq4 の2回の呼び出しのカウント値のゼロバッファを取り、それを共通配列 Ind_Buffer1[] および Ind_Buffer2[] に入れます。インディケータ呼び出しのスキームはさらなる計算のために、ゼロ値以外、最後の5個のインディケータ値が必要になることを考慮します。
カスタムインディケータ呼び出しを伴う EA のストラクチャ
あらゆる Expert Advisor に適したユニバーサルなインディケータ関数を作成しているとき、それは受信値をインディケータバッファの類似体に送信します。それはチャートのバーすべてに対してインディケータ値を格納するのです。まちろん、その呼び出しが完全にカスタムインディケータ呼び出しの類似体であるインディケータ関数を作成することはできますが、書くのには時間がかかりすぎ、コードはひじょうに長いものとなってしまうことでしょう。
もっと簡単にする方法があるのです。この関数は入力パラメータをカスタムインディケータやバッファのパラメータとして受け取り、セルが計算済みのインディケータ値を書き込まれたインディケータモードのエミュレーションを持つ同じバッファとして返します。われわれの関数内にインディケータバッファに対応する関数に対する参照にリンクした外部変数を宣言することで簡単に行えます。MQL4 言語では、それは次のようなものです。:double& InputBuffer。インディケータ関数は論理関数として宣言します。計算が正常に行われれば『真』を返し、チャート上に適切なバー番号がないため計算が正常に行われなければ『偽』を返します。この説明の後、インディケータスキームから構築され、前稿でお話ししたインディケータ関数は以下のような形になるはずです。
bool Get_IndSeries(int Number,string symbol, int timeframe, bool NullBarRecount, int period0, int period1, int period2, double& InputBuffer0[], double& InputBuffer1[], double& InputBuffer2[])
インディケータバッファ以外、外部変数が中間計算用バッファ InputBuffer1 および InputBuffer2 も持つのは当然のことです。なぜなら関数内にこういったバッファを作成するのはひじょうに面倒だからです。関数内にこれらバッファ処理のインディケータモードをエミュレートするほうが好ましいです。それにより問題が発生することはありません。ここから外部変数 NullBarRecount の意味を詳しく説明します。実際、の大半はゼロバーでの計算を必要としません。そして、ユニバーサルなインディケータ関数を書いている間、それは当然のごとくゼロバーでインディケータ値の再計算を行います。それは実質上実行時間をに増やす可能性のあるものです。 関数 NullBarRecount の外部パラメータを『偽』と指摘することで、必要なければ関数がゼロバーで計算するのを禁止します。
ここで関数呼び出しを持つバリアントでインディケータ呼び出し用 EA ストラクチャのスキームを示すことができます。
//+------------------------------------------------------------------+ //| ExpertIndPlan1.mqh | //| Copyright © 2007, MetaQuotes Software Corp. | //| http://www.metaquotes.net/ | //+------------------------------------------------------------------+ #property copyright "Copyright © 2007, MetaQuotes Software Corp." #property link "http://www.metaquotes.net/" //---- EA input parameters extern int period10 = 15; extern int period11 = 15; extern int period12 = 15; //---- EA input parameters extern int period20 = 15; extern int period21 = 15; extern int period22 = 15; //---- Indicator buffers declaration double Ind_Buffer10[], Ind_Buffer11[], Ind_Buffer12[]; double Ind_Buffer20[], Ind_Buffer21[], Ind_Buffer22[]; //+------------------------------------------------------------------+ //| Get_IndSeries() function | //+------------------------------------------------------------------+ //---- Declaration of the function Get_IndSeries() bool Get_IndSeries(int Number,string symbol, int timeframe, bool NullBarRecount, int period0, int period1, int period2, double& InputBuffer0[], double& InputBuffer1[], double& InputBuffer2[]) { //---- // Here is the code of the function GetIdicator() //---- return(true); } //+------------------------------------------------------------------+ //| Custom Expert initialization function | //+------------------------------------------------------------------+ int init() { //---- // Here is the code of the EA initialization //---- initialization end return(0); } //+------------------------------------------------------------------+ //| Custom Expert iteration function | //+------------------------------------------------------------------+ int start() { //---- Checking whether the bars number is enough for further calculation if(iBars(Symbol(), 0) < period10 + period11 + period12 + 10) return(0); if(iBars(Symbol(), 0) < period20 + period21 + period22 + 10) return(0); //---- getting indicator values for further calculation if(!Get_IndSeries(0,Symbol(), 0, false, period10, period11, period12, Ind_Buffer10, Ind_Buffer11, Ind_Buffer12)) return(0); if(!Get_IndSeries(1, Symbol(), 0, false, period20, period21, period22, Ind_Buffer20, Ind_Buffer21,Ind_Buffer22)) return(0); //---- // Here is the EA code, forming trade signals // based on the values of indicator buffer cells //---- // here is the EA executive part code, // requesting for placing an order //---- return(0); } //+------------------------------------------------------------------+
インディケータコードをカスタム関数に変換する一般的スキーム
この初期作業後、インディケータ関数の内部ストラクチャの一般的スキームに移ることができます。前稿の最後のインディケータスキームを基本にします。むつかしいことはないはずです。
1. 関数 int start() の内容だけを取ります。
2. 関数 Get_IndSeries() の宣言を追加します。
bool Get_IndSeries(string symbol, int timeframe, bool NullBarRecount, int period0, int period1, int period2, double& InputBuffer0, double& InputBuffer1, double& InputBuffer2)
3. 関数 Get_IndSeries() の外部変数のバッファ名(InputBuffer)に従いコード(Ind_Buffer)内のインディケータバッファ名を変更します。
4. 変数 LastCountBar の宣言を追加します。
5. 変数 NullBarRecount が真であることを確認します。
if(!NullBarRecount) LastCountBar = 1;
7. バー数がのちの計算に十分か確認する際、コードの一番最初に変更を加えます。: return(0) を return(false) に。
8. コードの最後で return(0) を return(true) に変更します。
インディケータ関数は準備できました。
//+------------------------------------------------------------------+ //| Get_IndSeries.mqh | //| Copyright © 2007, MetaQuotes Software Corp. | //| http://www.metaquotes.net/ | //+------------------------------------------------------------------+ bool Get_IndSeries(int Number, string symbol,int timeframe, bool NullBarRecount, int period0, int period1, int period2, double& InputBuffer0[], double& InputBuffer1[], double& InputBuffer2[]) { //---- 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 < period0 + period1 + period2) return(false); //---- EMULATION OF INDICATOR BUFFERS if(ArraySize(InputBuffer0) < IBARS) { ArraySetAsSeries(InputBuffer0, false); ArraySetAsSeries(InputBuffer1, false); ArraySetAsSeries(InputBuffer2, false); //---- ArrayResize(InputBuffer0, IBARS); ArrayResize(InputBuffer1, IBARS); ArrayResize(InputBuffer2, IBARS); //---- ArraySetAsSeries(InputBuffer0, true); ArraySetAsSeries(InputBuffer1, true); ArraySetAsSeries(InputBuffer2, true); } //----+ introducing static memory variables static int IndCounted[]; //----+ changing the size of static variables if(ArraySize(IndCounted) < Number + 1) ArrayResize(IndCounted, Number + 1); //----+ introducing an integer variable int LastCountBar; //----+ Checking if the recalculation of the zero bar is allowed if(!NullBarRecount) LastCountBar = 1; else LastCountBar = 0; //----+ Inserting a variable with a floating point double Resalt0, Resalt1, Resalt2; //----+ Inserting integer variables and getting already calculated bars int limit, MaxBar, bar, counted_bars = IndCounted[Number]; //----+ Remembering the number of all bars of a chart (we do not count the zero bar!) IndCounted[Number] = IBARS - 1; //---- defining the number of the oldest bar, // starting from which new bars will be recalculated limit = IBARS - counted_bars - 1; //---- defining the number of the oldest bar, // starting from which new bars will be recalculated MaxBar = IBARS - 1 - (period0 + period1 + period2); //---- initialization of zero if(limit > MaxBar) { limit = MaxBar; for(bar = IBARS - 1; bar >= 0; bar--) { InputBuffer0[bar] = 0.0; InputBuffer1[bar] = 0.0; InputBuffer2[bar] = 0.0; } } //----+ THE FIRST CYCLE OF INDICATOR CALCULATION for(bar = limit; bar >= LastCountBar; bar--) { // Here code of the variable Resalt1 calculation // based on the external variable period1 InputBuffer1[bar] = Resalt1; } //----+ THE SECOND CYCLE OF INDICATOR CALCULATION for(bar = limit; bar >= LastCountBar; bar--) { // Here code of the variable Resalt2 calculation // based on the values of the buffer Ind_Buffer1[] // and external variable period2 InputBuffer2[bar] = Resalt2; } //----+ THE MAIN CYCLE OF INDICATOR CALCULATION for(bar = limit; bar >= LastCountBar; bar--) { // Here code of the variable Resalt0 calculation // based on the values of the buffer Ind_Buffer2[] // and external variable period0 InputBuffer0[bar] = Resalt0; } return(true); } //+------------------------------------------------------------------+
読者のみなさんが MQL4 をひじょうにうまくご利用なら、上述のアクションを読んだあと、与えられたスキームに従ってインディケータ関数を書くのになんの問題もないだろうと思います。
カスタムインディケータ関数を書く例
ここからインディケータ関数を書きます。最大限シンプルなインディケータを取ります。
//+------------------------------------------------------------------+ //| RAVI.mq4 | //| Copyright © 2005, MetaQuotes Software Corp. | //| http://www.metaquotes.net/ | //+------------------------------------------------------------------+ #property copyright "Copyright © 2005, MetaQuotes Software Corp." #property link "http://www.metaquotes.net/" //---- drawing the indicator in a separate window #property indicator_separate_window //---- number of indicator buffers #property indicator_buffers 1 //---- indicator color #property indicator_color1 Red //---- INPUT PARAMETERS OF THE INDICATOR extern int Period1 = 7; extern int Period2 = 65; extern int MA_Metod = 0; extern int PRICE = 0; //---- indicator buffers double ExtBuffer[]; //+------------------------------------------------------------------+ //| RAVI initialization function | //+------------------------------------------------------------------+ int init() { //---- indicator drawing style SetIndexStyle(0, DRAW_LINE); //---- indicator buffers SetIndexBuffer(0,ExtBuffer); //---- indicator name and labels for subwindows IndicatorShortName("RAVI (" + Period1+ ", " + Period2 + ")"); SetIndexLabel(0, "RAVI"); //---- initialization end return(0); } //+------------------------------------------------------------------+ //| RAVI iteration function | //+------------------------------------------------------------------+ int start() { int MinBars = MathMax(Period1, Period2); //---- checking whether the bars number is enough for further calculation if(Bars < MinBars) return(0); //----+ Introducing variables with a floating point double MA1, MA2, result; //----+ Introducing integer variables and getting already calculated bars int MaxBar, bar, limit, counted_bars = IndicatorCounted(); //---- checking for possible errors if(counted_bars < 0) return(-1); //---- the last calculated bar should be recalculated if(counted_bars > 0) counted_bars--; //---- defining the number of the oldest bar, // starting from which all bars will be recalculated MaxBar = Bars - 1 - MinBars; //---- defining the number of the oldest bar, // starting from which new bars will be recalculated limit = Bars - counted_bars - 1; //---- zero initialization if(limit > MaxBar) { for(int ii = Bars - 1; ii >= MaxBar; ii--) ExtBuffer[ii] = 0.0; limit = MaxBar; } //---- main cycle for(bar = 0; bar <= limit; bar++) { MA1 = iMA(NULL, 0, Period1, 0, MA_Metod, PRICE,bar); MA2 = iMA(NULL, 0, Period2, 0, MA_Metod, PRICE,bar); //---- result = ((MA1 - MA2) / MA2)*100; ExtBuffer[bar] = result; } //---- return(0); } //+------------------------------------------------------------------+
アルゴリズムの固定
1. インディケータコードにある不要なエレメントをすべて消去します。
2. シングルバッファ ExtBuffer[] 用インディケータバッファのエミュレーションコードを書きます。
3. 関数 IndicatorCounted() を変数 IndCounted に置き換えます。
4. 変数 IndCounted をチャートバー数-1で初期化します。
5. 事前決定された変数 Bars を時系列呼び出しiBars(シンボル、タイムフレーム)に変更します。
6. counted_bars に対する不要な確認を削除します。
//---- checking possible errors if(counted_bars < 0) return(-1); //---- the last calculated bar must be recalculated if(counted_bars > 0) counted_bars--;
7. 関数 int start() のコンテンツのみ残します。
8. 関数 Get_RAVISeries() の宣言を追加します。
bool Get_RAVISeries(int Number, string symbol,int timeframe, bool NullBarRecount, int Period1, int Period2, int MA_Metod, int PRICE, double& InputBuffer[])
9. コード内のインディケータバッファ名(ExtBuffer)を関数Substitute Get_RAVISeries() の外部変数のバッファ名(InputBuffer)に従い置き換えます。
10. 変数 LastCountBar の宣言を追加します。
11. 静的変数 IndCounted を配列 IndCounted[Number] に変え、関数 Get_RAVISeries の呼び出し数に応じて変数サイズを変更するためのコードを追加します。mqh:
//----+ changing the size of static variables if(ArraySize(IndCounted) < Number + 1) { ArrayResize(IndCounted, Number + 1); }
12. 変数 NullBarRecount が真であることを確認します。
if(!NullBarRecount) LastCountBar = 1;
13. インディケータ計算のすべてのサイクルでゼロを LastCountBar に変更します。
for(bar = limit; bar >= LastCountBar; bar--)
14. バー数が十分か確認する際、コードの冒頭に変更を加えます。: return(0) を return(false) に。
15. 末尾で return(0) を return(true) に置き換えます。
すべてのコードを変更したら、インディケータ関数 Get_RAVISeries() を取得します
//+------------------------------------------------------------------+ //| Get_RAVISeries.mqh | //| Copyright © 2007, MetaQuotes Software Corp. | //| http://www.metaquotes.net/ | //+------------------------------------------------------------------+ bool Get_RAVISeries(int Number, string symbol,int timeframe, bool NullBarRecount, int Period1, int Period2, int MA_Metod, int PRICE, 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 < MathMax(Period1, Period2)) return(false); //---- EMULATION OF INDICATOR BUFFERS if(ArraySize(InputBuffer) < IBARS) { ArraySetAsSeries(InputBuffer, false); //---- ArrayResize(InputBuffer, IBARS); //---- ArraySetAsSeries(InputBuffer, true); } //----+ inserting static variables of memory static int IndCounted[]; //----+ changing the size of static variables if(ArraySize(IndCounted) < Number + 1) { ArrayResize(IndCounted, Number + 1); } //----+ Introducing an integer variable int LastCountBar; //----+ Checking whether the recalculation of the zero bar is allowed if(!NullBarRecount) LastCountBar = 1; //----+ Introducing floating point variables double MA1,MA2,result; //----+ Introducing integer variables and getting alreadu calculated bars int MaxBar, bar, limit, counted_bars = IndCounted[Number]; //----+ Remembering the amount of all chart bars IndCounted[Number] = IBARS - 1; //---- determining the number of the oldest bar, // starting from which new bars will be recalculated limit = IBARS - counted_bars - 1; // Print(IBARS - counted_bars); //---- determining the number of the oldest bar, // starting from which all bars will be recalculated MaxBar = IBARS - 1 - MathMax(Period1, Period2); //---- zero initialization if(limit > MaxBar) { limit = MaxBar; for(bar = IBARS - 1; bar >= 0; bar--) { InputBuffer[bar] = 0.0; } } //----+ THE FIRST CYCLE OF INDICATOR CALCULATION for(bar = limit; bar >= LastCountBar; bar--) { MA1 = iMA(symbol, timeframe, Period1, 0, MA_Metod, PRICE, bar); MA2 = iMA(symbol, timeframe, Period2, 0, MA_Metod, PRICE, bar); //---- result = ((MA1 - MA2) / MA2)*100; InputBuffer[bar] = result; } //----+ return(true); } //+------------------------------------------------------------------+
確かにこれはすべてすばらしくできています。Competently, シンプルすぎることはありません。ただ、疑問が一つ浮かびます。このインディケータ関数はカスタムインディケータと同じように計算するのだろうか?
計算精度についてのカスタムインディケータ関数検証
関数計算結果がカスタムインディケータ関数の計算結果に等しいか確認する必要があります。このために最適な Expert Advisor はトレードを行わず、カスタムインディケータ RAVI.mq4 およびカスタム関数 Get_RAVISeries() から値を受け取り、差を見つけ、その後インディエータ値、カスタム関数値、両者の差分をログファイルに送信するだけのものです。必要なことは、カスタム関数 Get_RAVISeries() およびインディケータ RAVI.mq4 のアルゴリズムの対応に関する最終結論を出すためのログファイルの内容を分析することだけです。
//+------------------------------------------------------------------+ //| Get_RAVISeriesTest.mq4 | //| Copyright © 2007, MetaQuotes Software Corp. | //| http://www.metaquotes.net/ | //+------------------------------------------------------------------+ #property copyright "Copyright © 2007, MetaQuotes Software Corp." #property link "http://www.metaquotes.net/" //---- INPUT EA PARAMETERS extern bool NullBarRecount = true; //---- indicator buffers double RAVI_Buffer0[]; double RAVI_Buffer1[]; double RAVI_Buffer2[]; //+------------------------------------------------------------------+ //| Get_RAVISeries() function | //+------------------------------------------------------------------+ #include <Get_RAVISeries.mqh> //+------------------------------------------------------------------+ //| Custom Expert initialization function | //+------------------------------------------------------------------+ int init() { //---- initialization end return(0); } //+------------------------------------------------------------------+ //| Custom Expert iteration function | //+------------------------------------------------------------------+ int start() { //---- double Ind_Velue, Resalt; //---- if(!Get_RAVISeries(0, Symbol(), 0, NullBarRecount, 10, 20, 1, 0, RAVI_Buffer0)) return(0); if(!Get_RAVISeries(1, Symbol(), 240, NullBarRecount, 25, 66, 2, 1, RAVI_Buffer1)) return(0); if(!Get_RAVISeries(2, Symbol(), 1440, NullBarRecount, 30, 70, 3, 3, RAVI_Buffer2)) return(0); //---- getting indicator values for the test 0 Ind_Velue = iCustom(NULL, 0, "RAVI", 10, 20, 1, 0, 0, 2); Resalt = RAVI_Buffer0[2] - Ind_Velue; Print(" " + Ind_Velue + " " + RAVI_Buffer0[2] + " " + Resalt+""); //---- getting indicator values for the test 1 Ind_Velue = iCustom(NULL, 240, "RAVI", 25, 66, 2, 1, 0, 2); Resalt = RAVI_Buffer1[2] - Ind_Velue; Print(" " + Ind_Velue + " " + RAVI_Buffer1[2] + " " + Resalt+"" ); //---- getting indicator values for the test 2 Ind_Velue = iCustom(NULL, 1440, "RAVI", 30, 70, 3, 3, 0, 2); Resalt = RAVI_Buffer2[2] - Ind_Velue; Print(" " + Ind_Velue + " " + RAVI_Buffer2[2] + " " + Resalt + ""); //---- return(0); } //+------------------------------------------------------------------+
ストラテジーテスタで Expert Advisor の Get_RAVISeries 検証を起動します。当然、コンパイルされたファイル RAVI.ex4 はすでにMetaTrader クライアントターミナルのフォルダ \expert\indicators に、ファイル Get_RAVISeries.mqh はフォルダ \expert \include にあります。ストラテジーテスタのジャーナルおよびログファイルでは、インディケータ値と関数形式をしたその類似体の入った列を2列確認できます。3番目の列はこれらの値の差分が示されます。最後の列の値はすべてゼロです。それは値がどちらの場合にも等しいことを意味します。インディケータカスタム関数の書き込みタスクは正常に遂行された、と結論づけることができます。
おわりに
次の記事では、このテーマに限定し、この類の関数を書き、その関数を基にシンプルな Expert Advisor を実装するもっとむつかしい例を分析しようと思います、
MetaQuotes Ltdによってロシア語から翻訳されました。
元の記事: https://www.mql5.com/ru/articles/1457
- 無料取引アプリ
- 8千を超えるシグナルをコピー
- 金融ニュースで金融マーケットを探索