![インディケータコードから Expert Advisor コードへの変換インディケータストラクチャ](https://c.mql5.com/i/articles/overlay.png)
インディケータコードから Expert Advisor コードへの変換インディケータストラクチャ
はじめに
よりよくご理解いただくために、以下の資料をご一読ください。
1. MetaQuotes Software Corp. Features of Custom Indicators Creation.
2. Nikolay Kositsin. Multiple Null Bar Re-Count in Some Indicators.
タイトルで示されている本稿のテーマに入る前に、次の質問がふさわしいかもしれません。:「大半の場合、カスタムインディケータ処理を行うEA が、それ自体のコード内でカスタムインディケータを処理するのに必要なものをすべて備えている類似体よりもずっと簡単に思えるなら、インディケータコードを EA コードに転換する目的はなにでしょうか?特に、コードが正確に描かれていることを考慮するとすれば、どちらの場合も結果はまったく同じになるのです。
私見ですが、必要になるのは次の2つの場合です。
1. ゼロバーで計算される EA 計算値が全く使用されなければ、当然ゼロバーや最初のバーでの不要な再計算は省略したいと思います。これによりそのような EA 処理時間を短縮することができます。それは複雑でリソース消費が多いコードに適しています。
2. デコンパイル対策のコード保護を最大限ほどこしたExpert Advisor の商用利用
インディケータ最適化例
まず、以下のカスタムインディケータのコードフラグメントに注目していただきたいと思います。
int start() { int limit; int counted_bars = IndicatorCounted(); //---- the last calculated bar will be recalculated if(counted_bars > 0) counted_bars--; limit = Bars - counted_bars - 1; //---- the main cycle for(int i = limit; i >= 0; i--) { //---- ExtBlueBuffer[i] = iMA(NULL, 0, JawsPeriod, 0, MODE_SMMA, PRICE_MEDIAN, i); ExtRedBuffer[i] = iMA(NULL, 0, TeethPeriod, 0, MODE_SMMA, PRICE_MEDIAN, i); ExtLimeBuffer[i] = iMA(NULL, 0, LipsPeriod, 0, MODE_SMMA, PRICE_MEDIAN, i); } //---- return(0); }
この場合、われわれには次の行が適しています。
if(counted_bars > 0) counted_bars--;
'counted_bars' 変数の値の減少を1ずつ行うこのチェックの意味は、カスタムインディケータにこの行がなければ、ゼロバーが変更されるときバッファから EA に対して誤った値が送信されてしまうの、というものです。EA のインディケータ曲線はきわめて『しわくちゃな』形をしています。
ただ、ここで注意が必要なのは、平滑化アルゴリズムの中には、平滑化が開始される参照ポイントに敏感なものもあるということです。すなわち、同じ値を持つために、インディケータと EA 内のインディケータコードのサイクルにおいてすべてのバーの再計算を開始する一番古いバーの番号は一致している必要があります。
ここに EA での処理迅速化のためのインディケータコード最適化方法を説明する例があります。. インディケータがゼロバーでの値の再計算を中断したのち、メインのインディケータでサイクルがゼロを1に変えます。
// instead of for(int i = limit; i >= 0; i--) // write for(int i = limit; i >= 1; i--)
結果、ソースコードは次のようになります。
int start() { int limit; int counted_bars = IndicatorCounted(); //---- the last bar will be recalculated if(counted_bars > 0) counted_bars--; limit = Bars - counted_bars - 1; //---- the main cycle //now the cycle ends in 1 for(int i = limit; i >= 1; i--) { //---- ExtBlueBuffer[i] = iMA(NULL, 0, JawsPeriod, 0, MODE_SMMA, PRICE_MEDIAN, i); ExtRedBuffer[i] = iMA(NULL, 0, TeethPeriod, 0, MODE_SMMA, PRICE_MEDIAN, i); ExtLimeBuffer[i] = iMA(NULL, 0, LipsPeriod, 0, MODE_SMMA, PRICE_MEDIAN, i); } //---- return(0); }
実トレーディングで本格的にお手持ちの EA を使用しようとしていてご自身の資金を信用されているなら、お手持ちの Expert Advisor および EA が処理を行うインディケータを細かい部分まで注意して確認する必要があります。それに、その確認作業はご自身でおこなわなければならないのです。インディケータストラクチャとそのコードの最適化方法を完全に理解するために数日をそっくり費やすのが、おおざっぱに書かれたインディケータから値を受信する EA を我慢して数か月使用するよりはずっと簡単で賢いやり方だと思います。
そのため、インディケータコードを EA コードに変換するにはそれなりの理由が必要なのは明らかです、インディケータが正確に書かれて入れば、EA の処理はそれなしでもそれほど遅いものにはなりません。最初の EA コードはカスタムインディケータを使ってこの形式でチェックアウトを作成するのが簡単です。And if the EA がreally 完璧な結果を出したら、順番にカスタムインディケータの呼び出しをインディケータコードのフラグメントに変えていき、コードはさらに最適化することが可能です。
また、検証済みの損失および収益値は EA コード変更後修正してはいけないことに注意が必要です。
既存のインディケータ数はひじょうに大きいもので、それぞれが即時のコードを持っています。すべてのインディケータに変換するコードのユニバーサルメソッドを作成することはほどんど不可能なのはこの理由によります。1つあるいはそれおと同じカスタムインディケータが EA コードで複数回表されることで問題はより深刻なものとなります。インディケータコードが多少シンプルであれば、それはカスタム関数に書くことができ、そのような場合にカスタムインディケータを関数に変えることはかなり容易です。ただ、EA コードはひじょうに頻繁にそのようなディメンションを達成するため、そこのエラーを検出することはほとんど不可能です。そうするとすべての努力はむなしくなります。
インディケータストラクチャの一般的スキーム
//+------------------------------------------------------------------+ //| IndicatorPlan.mq4 | //| Copyright © 2007, MetaQuotes Software Corp. | //| http://www.metaquotes.net/ | //+------------------------------------------------------------------+ #property copyright "Copyright © 2007, MetaQuotes Software Corp." #property link "http://www.metaquotes.net/" //---- drawing the indicator in the main window #property indicator_chart_window //---- number of indicator buffers #property indicator_buffers 1 //---- indicator color #property indicator_color1 Gold //---- INPUT PARAMETERS OF THE INDICATOR extern int period0 = 15; extern int period1 = 15; extern int period2 = 15; //---- indicator buffers double Ind_Buffer0[]; double Ind_Buffer1[]; double Ind_Buffer2[]; //+------------------------------------------------------------------+ //| Custom indicator initialization function | //+------------------------------------------------------------------+ int init() { //---- defining the graph execution style SetIndexStyle(0, DRAW_LINE); //---- 3 indicator buffers are used for calculation IndicatorBuffers(3); SetIndexBuffer(0, Ind_Buffer0); SetIndexBuffer(1, Ind_Buffer1); SetIndexBuffer(2, Ind_Buffer2); //---- end of initialization return(0); } //+------------------------------------------------------------------+ //| Custom indicator iteration function | //+------------------------------------------------------------------+ int start() { //---- Checking whether the bars number is enough for further calculation if(Bars < period0 + period1 + period2) return(0); //----+ Insertion of variables with a floating point double Resalt0, Resalt1, Resalt2; //----+ Insertion of integer variables and getting calculated bars int limit, MaxBar,bar, 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--; //---- defining the number of the oldest bar, // starting from which new bars will be recalculated limit = Bars - counted_bars - 1; //---- defining the number of the oldest bar, // starting from which new bars will be recalculated MaxBar = Bars - 1 - (period0 + period1 + period2); //---- initialization of zero if(limit > MaxBar) { limit = MaxBar; for(bar = Bars - 1; bar >= MaxBar; bar--) { Ind_Buffer0[bar] = 0.0; Ind_Buffer1[bar] = 0.0; Ind_Buffer2[bar] = 0.0; } } //----+ THE FIRST CYCLE OF INDICATOR CALCULATION for(bar = limit; bar >= 0; bar--) { // Here code of the variable Resalt1 calculation based on the external // variable period1 Ind_Buffer1[bar] = Resalt1; } //----+ THE SECOND CYCLE OF INDICATOR CALCULATION for(bar = limit; bar >= 0; bar--) { // Here code of the variable Resalt2 calculation // based on the values of the buffer Ind_Buffer1[] // and external variable period2 Ind_Buffer2[bar] = Resalt2; } //----+ THE MAIN CYCLE OF INDICATOR CALCULATION for(bar = limit; bar >= 0; bar--) { // Here code of the variable Resalt0 calculation // based on the values of the buffer Ind_Buffer2[] // and external variable0 Ind_Buffer0[bar] = Resalt0; } return(0); } //+------------------------------------------------------------------+
ここで、今回のコンテキストでは興味の対象でなく Expert Advisor に不要なエレメントをスキームから除外します。:
//+------------------------------------------------------------------+ //| IndicatorPlan1.mq4 | //| Copyright © 2007, MetaQuotes Software Corp. | //| http://www.metaquotes.net/ | //+------------------------------------------------------------------+ //---- INPUT PARAMETERS OF THE INDICATOR extern int period0 = 15; extern int period1 = 15; extern int period2 = 15; //---- indicator buffers double Ind_Buffer0[]; double Ind_Buffer1[]; double Ind_Buffer2[]; //+------------------------------------------------------------------+ //| Custom indicator initialization function | //+------------------------------------------------------------------+ int init() { //---- end of initialization return(0); } //+------------------------------------------------------------------+ //| Custom indicator iteration function | //+------------------------------------------------------------------+ int start() { //---- Checking whether the bars number is enough for further calculation if(Bars < period0 + period1 + period2) return(0); //----+ Insertion of variables with a floating point double Resalt0, Resalt1, Resalt2; //----+ Insertion of integer variables and getting calculated bars int limit, MaxBar, bar, 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--; //---- defining the number of the oldest bar, // starting from which new bars will be recalculated limit = Bars - counted_bars - 1; //---- defining the number of the oldest bar, // starting from which new bars will be recalculated MaxBar = Bars - 1 - (period0 + period1 + period2); //---- initialization of zero if(limit > MaxBar) { limit = MaxBar; for(bar = Bars - 1; bar >= MaxBar; bar--) { Ind_Buffer0[bar] = 0.0; Ind_Buffer1[bar] = 0.0; Ind_Buffer2[bar] = 0.0; } } //----+ THE FIRST CYCLE OF INDICATOR CALCULATION for(bar = limit; bar >= 0; bar--) { // Here code of the variable Resalt1 calculation // based on the external variable period1 Ind_Buffer1[bar]= Resalt1; } //----+ THE SECOND CYCLE OF INDICATOR CALCULATION for(bar = limit; bar >= 0; bar--) { // Here code of the variable Resalt2 calculation // based on the values of the buffer Ind_Buffer1[] // and external variable period2 Ind_Buffer2[bar] = Resalt2; } //----+ THE MAIN CYCLE OF INDICATOR CALCULATION for(bar = limit; bar >= 0; bar--) { // Here code of the variable Resalt0 // based on the values of the buffer Ind_Buffer2[] // and external variable period0 Ind_Buffer0[bar] = Resalt0; } return(0); } //+------------------------------------------------------------------+
残念なエラーがなければ、このコードは簡単に Expert Advisor コードに入れることができたでしょう。
1. まず忘れてはならないことは、関数 IndicatorCounted() は Expert Advisors では動作しないということです。
2. また初期化ブロックではカスタム配列をインディケータ配列に変えることはできません。
インディケータコードを完全に保存するためには、最初に関数 IndicatorCounted() の類似体を作成し、Expert Advisor でインディケータバッファの類似隊をいくらかエミュレートする必要があります。残念ながら、標準的な関数によって EA でインディケータバッファを直接エミュレートすることはできません。これまでに Expert Advisor 向けの SetIndexBuffer() および IndicatorBuffers() の類似体はありません。そのためこの問題を解決するには別の方法が必要です。MQL4 にはそのためのオプションが十分あるのです。
Expert Advisor におけるインディケータバッファのエミュレーション
1. インディケータバッファ変数に割り当てられる値は、インディケータがチャートにアタッチされており、ターミナルが動作していれば、サイクル間で失われることはありません。
2. チャート上でゼロバー(直近バー)が変更されると、インディケータバッファのエレメントはすべて移動sれます。
3. 新規バーが来ると、インディケータ内で変数の限界値が1から2に代わります。そしてバッファエレメントはすべて1移動します。すなわち、ゼロバーが1番目のバーとなり、1番目のバーが2番目となる、などです。
4. 当然インディケータバッファのディメンションも変化します。次のティックでバーに変化がなければ、バッファエレメントは元の場所のままです。
それではインディケータバッファのエミュレーションです。これには、次に挙げる MQL4 の標準関数を使用します。 ArraySize()、 ArrayResize()、ArraySetAsSeries() です。インディケータバッファのエミュレーションコードはひじょうにシンプルで、その処理の原理は記述可能です。ゼロバーが変化するとき、バッファ内のエレメント定義の直接的順序は復元され、関数 ArrayResize() によって新規バーからバッファに新しいセルが追加されます。その後、バッファ内エレメント定義の逆順が設定され、エミュレート済みのインディケータバッファ内の最初のセルの間に空のセルが出現します。
//---- INDICATOR BUFFERS EMULATION int NewSize = iBars(symbol, timeframe); //---- Checking the change of the zero bar if(ArraySize(Ind_Buffer0) < NewSize) { //---- Set the direct indexing direction in the array ArraySetAsSeries(Ind_Buffer0, false); ArraySetAsSeries(Ind_Buffer1, false); ArraySetAsSeries(Ind_Buffer2, false); //---- Change the size of the emulated indicator buffers ArrayResize(Ind_Buffer0, NewSize); ArrayResize(Ind_Buffer1, NewSize); ArrayResize(Ind_Buffer2, NewSize); //---- Set the reverse indexing direction in the array ArraySetAsSeries(Ind_Buffer0, true); ArraySetAsSeries(Ind_Buffer1, true); ArraySetAsSeries(Ind_Buffer2, true); } //----
ところでmこのインディケータバッファのエミュレーション方法は、中間計算に対して8つのインディケータバッファが十分でなければ、インディケータでも使用可能です。この例は添付ファイル SMI.mq4 and SMI_New.mq4 にあります。
![](https://c.mql5.com/2/15/smi.gif)
関数 IndicatorCounted() の代用
//+------------------------------------------------------------------+ //| NewIndicatorPlan1.mqh | //| Copyright © 2007, MetaQuotes Software Corp. | //| http://www.metaquotes.net/ | //+------------------------------------------------------------------+ //---- INPUT INDICATOR PARAMETERS extern int period0 = 15; extern int period1 = 15; extern int period2 = 15; //---- indicator buffers double Ind_Buffer0[]; double Ind_Buffer1[]; double Ind_Buffer2[]; //+------------------------------------------------------------------+ //| Custom indicator initialization function | //+------------------------------------------------------------------+ int init() { //---- initialization end return(0); } //+------------------------------------------------------------------+ //| Custom indicator iteration function | //+------------------------------------------------------------------+ int start() { //---- Checking whether the bars number is enough for further calculation if(Bars < period0 + period1 + period2) return(0); //---- INDICATOR BUFFERS EMULATION if(ArraySize(Ind_Buffer0) < Bars) { ArraySetAsSeries(Ind_Buffer0, false); ArraySetAsSeries(Ind_Buffer1, false); ArraySetAsSeries(Ind_Buffer2, false); //---- ArrayResize(Ind_Buffer0, Bars); ArrayResize(Ind_Buffer1, Bars); ArrayResize(Ind_Buffer2, Bars); //---- ArraySetAsSeries(Ind_Buffer0, true); ArraySetAsSeries(Ind_Buffer1, true); ArraySetAsSeries(Ind_Buffer2, true); } //----+ INSERTION OF A STATIC INTEGER MEMORY VARIABLE static int IndCounted; //----+ Insertion of variables with a floating point double Resalt0, Resalt1, Resalt2; //----+ Insertion of integer variables and getting calculated bars int limit, MaxBar, bar, counted_bars = IndCounted; //---- checking for possible errors if(counted_bars < 0) return(-1); //---- the last calculated bar must be recalculated if(counted_bars > 0) counted_bars--; //----+ REMEMBERING THE AMOUNT OF ALL BARS OF THE CHART IndCounted = Bars - 1; //---- defining the number of the oldest bar, // starting from which new bars will be recalculated limit = Bars - counted_bars - 1; //---- defining the number of the oldest bar, // starting from which new bars will be recalculated MaxBar = Bars - 1 - (period0 + period1 + period2); //---- initialization of zero if(limit > MaxBar) { limit = MaxBar; for(bar = Bars - 1; bar >= 0; bar--) { Ind_Buffer0[bar] = 0.0; Ind_Buffer1[bar] = 0.0; Ind_Buffer2[bar] = 0.0; } } //----+ THE FIRST CYCLE OF INDICATOR CALCULATION for(bar = limit; bar >= 0; bar--) { // Here code of the variable Resalt1 calculation // based on the external variable period1 Ind_Buffer1[bar] = Resalt1; } //----+ THE SECOND CYCLE OF INDICATOR CALCULATION for(bar = limit; bar >= 0; bar--) { // Here code of the variable Resalt2 calculation // based on the values of the buffer Ind_Buffer1[] and external variable period2 Ind_Buffer2[bar] = Resalt2; } //----+ THE MAIN CYCLE OF INDICATOR CALCULATION for(bar = limit; bar >= 0; bar--) { // Here code of the variable Resalt0 calculation // based on the values of the buffer Ind_Buffer2[] and external variable period0 Ind_Buffer0[bar] = Resalt0; } return(0); } //+------------------------------------------------------------------+
インディケータコードのさらなる変換とそのストラクチャの最終スキーム
別のタイムフレームでのデータ処理タスクも簡単に遂行されます。タイプの時系列に対する Bars タイプの事前決定変数を置き換えます。
iBars(string symbol, int timeframe);
NULL -文字列シンボル用;0 (時系列で)- int タイムフレーム用;Close[bar] - for
iClose(string symbol, int timeframe, bar);
などです。
ここからインディケータ行を分析します。
//---- checking for possible errors if(counted_bars < 0) return(-1); //---- the last calculated bar must be recalculated if(counted_bars > 0) counted_bars--;
われわれのインディケータストラクチャ内の関数 IndicatorCounted() の置換については、インディケータcounted_bars はゼロより小さくなることは決してありません。よって Expert Advisor コード内の行
//---- checking for possible errors if(counted_bars < 0) return(-1);
は省略可能です。次の2行
//---- the last calculated bar must be recalculated if(counted_bars > 0) counted_bars--;
でも同様です。最初のバーの不要な再計算によって EA の動作速度を落とす場合、そのような確認は EA 処理にはまったく無駄なことなので、それらは削除します。それから EA への変換用最終コードは以下のようなものとなります。
//+------------------------------------------------------------------+ //| NewIndicatorPlan2.mqh | //| Copyright © 2007, MetaQuotes Software Corp. | //| http://www.metaquotes.net/ | //+------------------------------------------------------------------+ //---- INPUT INDICATOR PARAMETERS extern int period0 = 15; extern int period1 = 15; extern int period2 = 15; //---- indicator buffers double Ind_Buffer0[]; double Ind_Buffer1[]; double Ind_Buffer2[]; //---- DECLARING VARIABLES FOR CHOOSING A CHART string symbol; int timeframe; //+------------------------------------------------------------------+ //| Custom indicator initialization function | //+------------------------------------------------------------------+ int init() { //---- CHOOSING A CHART FRO INDICATOR CALCULATION symbol = Symbol();//INITIALIZATION OF THE VARIABLE symbol; timeframe =240;//INITIALIZATION OF THE VARIABLE timeframe; //---- end of initialization return(0); } //+------------------------------------------------------------------+ //| Custom indicator iteration function | //+------------------------------------------------------------------+ int start() { // GETTING THE AMOUNT OF ALL BARS OF THE CHART int IBARS = iBars(symbol, timeframe); //---- Checking whether the bars number is enough for further calculation if(IBARS < period0 + period1 + period2) return(0); // INDICATOR BUFFERS EMULATION if(ArraySize(Ind_Buffer0) < IBARS) { ArraySetAsSeries(Ind_Buffer0, false); ArraySetAsSeries(Ind_Buffer1, false); ArraySetAsSeries(Ind_Buffer2, false); //---- ArrayResize(Ind_Buffer0, IBARS); ArrayResize(Ind_Buffer1, IBARS); ArrayResize(Ind_Buffer2, IBARS); //---- ArraySetAsSeries(Ind_Buffer0, true); ArraySetAsSeries(Ind_Buffer1, true); ArraySetAsSeries(Ind_Buffer2, true); } // INSERTION OF A STATIC INTEGER MEMORY VARIABLE static int IndCounted; //----+ Insertion of variables with a floating point double Resalt0, Resalt1, Resalt2; //----+ Insertion of integer variables and GETTING ALREADY CALCULATED BARS int limit, MaxBar, bar, counted_bars = IndCounted; //----+ REMEMBERING THE AMOUNT OF ALL BARS OF THE CHART IndCounted = 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--) { Ind_Buffer0[bar] = 0.0; Ind_Buffer1[bar] = 0.0; Ind_Buffer2[bar] = 0.0; } } //----+ THE FIRST CYCLE OF INDICATOR CALCULATION for(bar = limit; bar >= 0; bar--) { // Here code of the variable Resalt1 calculation based on the external // variable period1 Ind_Buffer1[bar] = Resalt1; } //----+ THE SECOND CYCLE OF INDICATOR CALCULATION for(bar = limit; bar >= 0; bar--) { // Here code of the variable Resalt2 calculation // based on the values of the buffer Ind_Buffer1[] and the external variable period2 Ind_Buffer2[bar] = Resalt2; } //----+ THE MAIN CYCLE OF INDICATOR CALCULATION for(bar = limit; bar >= 0; bar--) { // Here code of the variable Resalt0 calculation // based on the values of the buffer Ind_Buffer2[] and the external variable period0 Ind_Buffer0[bar] = Resalt0; } return(0); } //+------------------------------------------------------------------+
// Saving the variables values if(bar == 1) if(((limit == 1) && (time == Time[2])) || (limit > 1)) { time = Time[2]; // These variables are not interesting for us PRICE = price; TREND = trend; RESALT = Resalt; } //+------------------------------------------------------------------+
このフラグメントは最初のバーではなくゼロバーで変数値を保存するために変更します。'1' はすべて '0'、'2' -'1'ずつ、で置き換えます。そしてコードが現チャートで処理をするのでなければ、時系列配列への参照も変更します
time = Time[1];
into
time = iTime(symbol, timeframe, 1);
結果、得るのが以下のフラグメントです。
// Saving variables values if(bar == 0) if(((limit == 0) && (time == iTime(symbol, timeframe, 1))) || (limit > 0)) { time = iTime(symbol, timeframe, 1); PRICE = price; TREND = trend; RESALT = Resalt; } //+------------------------------------------------------------------+
インディケータコードには私が作成した XXXSeries() のような平滑化関数が入っています。そういった関数を持つフラグメントをEA コードで使用するには、その関数をそれらの EA 類似体と置き換えます。
おわりに
MetaQuotes Ltdによってロシア語から翻訳されました。
元の記事: https://www.mql5.com/ru/articles/1456
![インディケータコードから Expert Advisor コードへの変換Expert Advisor およびインディケータ関数の一般的スキーム](https://c.mql5.com/2/14/311_2.gif)
![Forex トレーディングのイロハ](https://c.mql5.com/2/14/304_1.png)
![Expert Advisorのオーダーメード - トレーダー向けマニュアル](https://c.mql5.com/2/14/324_26.jpg)
![トリコロールインディケータとインディケータを書くことを最大限シンプル化するいくつかの機会](https://c.mql5.com/2/14/298_1.png)
![MQL5 - MetaTrader 5クライアントターミナルにビルトインされたトレードストラテジーの言語](https://c.mql5.com/i/registerlandings/logo-2.png)
- 無料取引アプリ
- 8千を超えるシグナルをコピー
- 金融ニュースで金融マーケットを探索