一からの取引エキスパートアドバイザーの開発(第10部):カスタムインジケータへのアクセス
はじめに
取引EAが本当に役立つのは、カスタムインジケータを使用できる場合のみです。それ以外の場合、取引EAは適切に設計され、ポジションの管理や市場取引の実行を支援するただのコードと命令のセットであり、おそらくそれだけです。
さて、MetaTrader 5チャートにインジケータを追加するのは最も難しい部分ではありませんが、EAでこれらのインジケータによって計算されたデータに直接アクセスすることは、適切な計画がなければほとんど不可能な作業になります。また、その方法がわからない場合は、標準インジケータのみに制限されますが、取引にはもっと必要です。良い例は、VWAP(ボリューム加重平均価格)インジケータです。これは、ブラジル証券取引所で先物を取引するトレーダーにとって非常に重要な移動平均です。このMAはMetaTraderの標準インジケータではありませんが、VWAPを計算するカスタムインジケータを作成して画面に表示することができます。ただし、EAで分析されるシステムで同じインジケータを使用することにした場合、状況はさらに複雑になります。関連する知識がなければ、EA内でこのカスタムインジケータを使用することはできません。この記事では、この制限を回避してこの問題を解決する方法を説明します。
計画
まず、カスタムインジケータで使用する計算を作成してみましょう。幸い、例として使用するVWAPの計算式は非常に単純です。
プログラミング言語に変換すると、MQL5では次のようになります。
int OnCalculate(const int rates_total, const int prev_calculated, const datetime &time[], const double &open[], const double &high[], const double &low[], const double &close[], const long &tick_volume[], const long &volume[], const int &spread[]) { double Price = 0; ulong Volume = 0; static int siPos = 0; if (macroGetDate(time[rates_total - 1]) != macroGetDate(time[siPos])) { for (int c0 = rates_total - 1; macroGetDate(time[siPos]) != macroGetDate(time[c0]); siPos++); ArrayInitialize(VWAP_Buff, EMPTY_VALUE); } for (int c0 = siPos; c0 < rates_total; c0++) { Price += ((high[c0] + low[c0] + close[c0]) / 3) * volume[c0]; Volume += volume[c0]; VWAP_Buff[c0] = Price / Volume; } return rates_total; }
計算の行は強調表示されています。関数の残りはDAILY VWAPの適切な初期化に使用されます。ただし、インジケータはチャート上で実行できないため、コードにさらに追加が必要です。残りのコードは以下のとおりです。
#property copyright "Daniel Jose - Indicador VWAP ( IntraDay )" #property version "1.01" #property indicator_chart_window #property indicator_buffers 1 #property indicator_plots 1 #property indicator_width1 2 #property indicator_type1 DRAW_LINE #property indicator_color1 clrBlack //+------------------------------------------------------------------+ #define macroGetDate(A) (A - (A % 86400)) //+------------------------------------------------------------------+ double VWAP_Buff[]; //+------------------------------------------------------------------+ int OnInit() { SetIndexBuffer(0, VWAP_Buff, INDICATOR_DATA); return INIT_SUCCEEDED; }
これにより、前に示したようにチャートにVWAPが表示される可能性が生まれます。
この部分はそれほど複雑ではありませんでした。ここで、EAが特定の方法でインジケータを分析するように、EAにVWAPを認識させる方法を見つける必要があります。取引でインジケータから利益を得ることが可能になります。
インジケータの操作を簡単にするために、VWAPを保存して、簡単にアクセスできるようにします。
その後は、新しい投影方法に飛び込むことができます。VWAPインジケータは基本的に正しいですが、EAで使用するためには正しくプログラムされていません。なぜでしょうか。問題は、EAがインジケータがチャート上にあるかどうかを知ることができないことです。これを知らなければ、インジケータを読み取ることができません。
問題は、ファイル名はシステムにとってほとんど重要ではないということです。ファイルには任意の名前をつけられますが、インジケータ名は計算内容を反映している必要があります。私たちのインジケータには、それを反映する名前がまだありません。VWAPと呼ばれたとしても、システムには何の意味もありません。このため、EAはインジケータがチャートに存在するかどうかを知ることができません。
インジケータに計算内容を反映させるには、コードでこれを示す必要があります。必ずしもファイル名にリンクされるとは限らない一意の名前を作成します。この場合、インジケータ初期化コードは次のようになります。
int OnInit() { SetIndexBuffer(0, VWAP_Buff, INDICATOR_DATA); IndicatorSetString(INDICATOR_SHORTNAME, "VWAP"); return INIT_SUCCEEDED; }
問題は強調表示された行を追加するだけで解決します。場合によっては、もっと難しいこともあります。これには後で戻ります。まず、MetaTrader 5ライブラリのCUSTOM MOVING AVERAGEインジケータのコードを例として使用しましょう。そのコードは次のとおりです。
void OnInit() { SetIndexBuffer(0,ExtLineBuffer,INDICATOR_DATA); IndicatorSetInteger(INDICATOR_DIGITS,_Digits+1); PlotIndexSetInteger(0,PLOT_DRAW_BEGIN,InpMAPeriod); PlotIndexSetInteger(0,PLOT_SHIFT,InpMAShift); string short_name; switch(InpMAMethod) { case MODE_EMA : short_name="EMA"; break; case MODE_LWMA : short_name="LWMA"; break; case MODE_SMA : short_name="SMA"; break; case MODE_SMMA : short_name="SMMA"; break; default : short_name="unknown ma"; } IndicatorSetString(INDICATOR_SHORTNAME, short_name + "(" + string(InpMAPeriod) + ")"); PlotIndexSetDouble(0,PLOT_EMPTY_VALUE,0.0); }
強調表示された部分が必要な名前を示していますが、ファイル名とは関係がありません。ただし、これはカスタムインジケータ内で正確に実行する必要があります。
これが完了し、EAがカスタムインジケータがチャートで実行されているかどうかを確認できることがわかったので、次の手順に進むことができます。
EAを介したインジケータへのアクセス
以前と同じように続けられますが、理想的には、何が起こっているのかを本当に理解するには、完全に新しいコードを作成する必要があります。取引EAを一から開発することを学ぶのが目的なので、この段階を通過しましょう。したがって、私たちの旅の続きでは、孤立したEAを作成します。その後で、それを最終的なコードに含めるかどうかを決められます。それでは、コードを書き始めましょう。以下でお分かりのように、EAはクリーンなコードから始まります。
//+------------------------------------------------------------------+ int OnInit() { EventSetTimer(1); return INIT_SUCCEEDED; } //+------------------------------------------------------------------+ void OnDeinit(const int reason) { EventKillTimer(); } //+------------------------------------------------------------------+ void OnTick(){} //+------------------------------------------------------------------+ void OnTimer(){} //+------------------------------------------------------------------+
次のことをおこないましょう。まず、VWAPインジケータがチャート上にあると想定し、インジケータによって計算された最後の値をEAに読み込みます。これを毎秒繰り返します。それをおこなう方法は単純です。変更後のEAコードがどのようになるかをご覧ください。
#property copyright "Daniel Jose" #property version "1.00" //+------------------------------------------------------------------+ int handle; double Buff[]; //+------------------------------------------------------------------+ int OnInit() { handle = ChartIndicatorGet(ChartID(), 0, "VWAP"); SetIndexBuffer(0, Buff, INDICATOR_DATA); EventSetTimer(1); return INIT_SUCCEEDED; } //+------------------------------------------------------------------+ void OnDeinit(const int reason) { EventKillTimer(); } //+------------------------------------------------------------------+ void OnTick() { } //+------------------------------------------------------------------+ void OnTimer() { int i; if (handle != INVALID_HANDLE) { i = CopyBuffer(handle, 0, 0, 1, Buff); Print(Buff[0]); } } //+------------------------------------------------------------------+
強調表示されている部分は、クリーンなコードに追加した部分です。結果は以下のとおりです。
なぜ機能したのでしょうか。MQL5がシステム間でデータを読み書きする手段を提供するためです。読み取る方法の1つは、CopyBuffer関数を使用することです。これは以下のように機能します。
したがって、標準のMetaTrader 5インジケータに限られず、任意のカスタムインジケータからデータを読み取ることができます。つまり、任意のインジケータを作成して機能させることができます。
次に、別のシナリオを考えてみます。今回はVWAPはチャートに存在しませんが、EAがそれを必要としているので、チャートに読み込む必要があります。どうすればできるでしょうか。これもかなり簡単です。さらに、以前に他の目的(EAのサブウィンドウを作成)で使用したことがあります。ここでおこなうのは、iCustom関数を使用することです。ただし、今回はカスタムインジケータを読み込みます。その場合、EAコードは次のようになります。
#property copyright "Daniel Jose" #property version "1.00" //+------------------------------------------------------------------+ int handle; double Buff[]; //+------------------------------------------------------------------+ int OnInit() { handle = ChartIndicatorGet(ChartID(), 0, "VWAP"); SetIndexBuffer(0, Buff, INDICATOR_DATA); EventSetTimer(1); return INIT_SUCCEEDED; } //+------------------------------------------------------------------+ void OnDeinit(const int reason) { EventKillTimer(); } //+------------------------------------------------------------------+ void OnTick() { } //+------------------------------------------------------------------+ void OnTimer() { int i; if (handle == INVALID_HANDLE) handle = iCustom(NULL, PERIOD_CURRENT, "VWAP.EX5");else { i = CopyBuffer(handle, 0, 0, 1, Buff); Print(Buff[0]); } } //+------------------------------------------------------------------+
強調表示されたコードが、元のシステムに追加した唯一のコードです。EAを実行すると、次の結果が生成されます。
次の図は、実装した内容を示しています。
最も基本的なレベルで必要なのはこれだけですが、よく見ると、VWAPがチャートに表示されていないことがわかります。EAがそれを使用しても、何が起こっているのかがユーザーにはわかりません。これも簡単に修正できます。最終的なコードは次のようになります。これを覚えておいてください。EAが何をしているのかを分析して観察できることは常に良いことです。完全な自由を与えることは安全ではないので、お勧めしません。
#property copyright "Daniel Jose" #property version "1.00" //+------------------------------------------------------------------+ int handle; long id; double Buff[]; string szCmd; //+------------------------------------------------------------------+ int OnInit() { szCmd = "VWAP"; handle = ChartIndicatorGet(id = ChartID(), 0, szCmd); SetIndexBuffer(0, Buff, INDICATOR_DATA); EventSetTimer(1); return INIT_SUCCEEDED; } //+------------------------------------------------------------------+ void OnDeinit(const int reason) { ChartIndicatorDelete(id, 0, szCmd); IndicatorRelease(handle); EventKillTimer(); } //+------------------------------------------------------------------+ void OnTick() { } //+------------------------------------------------------------------+ void OnTimer() { int i; if (handle == INVALID_HANDLE) { if ((handle = iCustom(NULL, PERIOD_CURRENT, "VWAP.EX5")) != INVALID_HANDLE) ChartIndicatorAdd(id, 0, handle); }else { i = CopyBuffer(handle, 0, 0, 1, Buff); Print(Buff[0]); } } //+------------------------------------------------------------------+
上記のEAコードででは、VWAPによって計算された最後の値を読み取り、それを画面に表示します。インジケータがチャートにない場合は、読み込まれて表示されます。チャートからEAを削除すると、VWAPも画面から削除されます。したがって、EAは常に計算を実行するために必要なものを持っています。説明したことの結果を以下に示します。
明らかにインジケータに変更を加えていないため、これはあまり実現可能ではないと思われるかもしれません。ただし、上記の手順を使用しても、カスタムインジケータに関連するものはすべて実装できます。最後の説明として、別の例を見てみましょう。移動平均を適用し、VWAPでおこなったのと同じ方法でEAを使用してみましょう。ここでのみ、平均のパラメータを指定します。
2番目のケース:移動平均の使用
ここでは、パラメータをカスタムインジケータに渡す方法に焦点を当てるため、移動平均の計算は重要ではありません。新しいカスタムインジケータは次のとおりです。
#property copyright "Daniel Jose 16.05.2021" #property description "Basic Moving Averages (Optimizes Calculation)" #property indicator_chart_window //+------------------------------------------------------------------+ enum eTypeMedia { MME, //Exponential moving average MMA //Arithmetic moving average }; //+------------------------------------------------------------------+ #property indicator_buffers 1 #property indicator_plots 1 #property indicator_type1 DRAW_LINE #property indicator_width1 2 #property indicator_applied_price PRICE_CLOSE //+------------------------------------------------------------------+ input color user00 = clrRoyalBlue; //Cor input int user01 = 9; //Periods input eTypeMedia user02 = MME; //MA type input int user03 = 0; //Displacement //+------------------------------------------------------------------+ double Buff[], f_Expo; //+------------------------------------------------------------------+ int OnInit() { string sz0 = "MM" + (user02 == MME ? "E": (user02 == MMA ? "A" : "_")) + (string)user01; f_Expo = (double) (2.0 / (1.0 + user01)); ArrayInitialize(Buff, EMPTY_VALUE); SetIndexBuffer(0, Buff, INDICATOR_DATA); PlotIndexSetInteger(0, PLOT_LINE_COLOR, user00); PlotIndexSetInteger(0, PLOT_DRAW_BEGIN, user01); PlotIndexSetInteger(0, PLOT_SHIFT, user03); IndicatorSetString(INDICATOR_SHORTNAME, sz0); return INIT_SUCCEEDED; } //+------------------------------------------------------------------+ int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[]) { double Value; int c0; switch (user02) { case MME: if (user01 < rates_total) { for (c0 = (prev_calculated > 0 ? prev_calculated - 1 : 0); c0 < rates_total - user03; c0++) Buff[c0] = (c0 > 0? ((price[c0] - Buff[c0 - 1]) * f_Expo) + Buff[c0 - 1] : price[c0] * f_Expo); for (; c0 < rates_total; c0++) Buff[c0] = EMPTY_VALUE; } break; case MMA: if (user01 < rates_total) { if (prev_calculated == 0) { Value = 0; for (int c1 = 0; c1 < user01; c1++) Value += price[user01 - c1]; Buff[user01] = Value / user01; } for (c0 = (prev_calculated > 0 ? prev_calculated - 1 : user01 + 1); c0 < rates_total - user03; c0++) Buff[c0] = ((Buff[c0 - 1] * user01) - price[c0 - user01] + price[c0]) / user01; for (; c0 < rates_total; c0++) Buff[c0] = EMPTY_VALUE; } break; } return rates_total; } //+------------------------------------------------------------------+
これで、インジケータ名はいくつかの要因に依存します。後でEAでチェックして、それぞれの状況に適応させることができます。たとえば、EAが2つの移動平均を使用し、それらをチャートに表示するとします。上記のコードで強調表示されている部分に注意してください。EAを有効にし、この場合はiCustom関数を使用して、インジケータパラメータを変更および構成します。必要に応じて実装できるようになるためには、これを理解することが重要です。平均の1つは17期間の指数MAであり、もう1つは52期間の算術MAです。17期間のMAは緑になり、52期間のMAは赤になります。EAは、インジケータを次の形式の関数として認識します。
平均(色、期間、タイプ、シフト) なので、インジケータは個別のファイルではなく、EA関数になります。これはプログラミングでは非常に一般的です。特定のタスクを実行するために関連するパラメータを使用してプログラムを呼び出し、最終的に結果を簡単に取得できるためです。しかし、問題は、VWAPでおこなったのと同じ方法でEAにこのシナリオを作成および管理させるにはどうすればよいかということです。
このためには、EAコードを変更する必要があります。新しいEAの完全なコードを以下に示します。
#property copyright "Daniel Jose" #property version "1.00" //+------------------------------------------------------------------+ long id; int handle1, handle2; double Buff1[], Buff2[]; string szCmd1, szCmd2; //+------------------------------------------------------------------+ int OnInit() { szCmd1 = "MME17"; szCmd2 = "MMA52"; id = ChartID(); handle1 = ChartIndicatorGet(id, 0, szCmd1); handle2 = ChartIndicatorGet(id, 0, szCmd2); SetIndexBuffer(0, Buff1, INDICATOR_DATA); SetIndexBuffer(0, Buff2, INDICATOR_DATA); EventSetTimer(1); return INIT_SUCCEEDED; } //+------------------------------------------------------------------+ void OnDeinit(const int reason) { ChartIndicatorDelete(id, 0, szCmd1); ChartIndicatorDelete(id, 0, szCmd2); IndicatorRelease(handle1); IndicatorRelease(handle2); EventKillTimer(); } //+------------------------------------------------------------------+ void OnTick() { } //+------------------------------------------------------------------+ void OnTimer() { int i1, i2; if (handle1 == INVALID_HANDLE) { if ((handle1 = iCustom(NULL, PERIOD_CURRENT, "Media Movel.EX5", clrGreen, 17, 0)) != INVALID_HANDLE) ChartIndicatorAdd(id, 0, handle1); }; if (handle2 == INVALID_HANDLE) { if ((handle2 = iCustom(NULL, PERIOD_CURRENT, "Media Movel.EX5", clrRed, 52, 1)) != INVALID_HANDLE) ChartIndicatorAdd(id, 0, handle2); }; if ((handle1 != INVALID_HANDLE) && (handle2 != INVALID_HANDLE)) { i1 = CopyBuffer(handle1, 0, 0, 1, Buff1); i2 = CopyBuffer(handle2, 0, 0, 1, Buff2); Print(Buff1[0], "<< --- >>", Buff2[0]); } } //+------------------------------------------------------------------+
これが結果です。
EAコードの強調表示された部分に注意してください。これはまさに私たちが必要としているものです。VWAPで使用したのと同じメカニズムを使用して、インジケータにパラメータを渡します。ただし、VWAPの場合、渡されるパラメータがある移動平均とは対照的に、パラメータを渡す必要はありませんでした。これらすべてが非常に大きな自由度を提供します。
結論
この記事には汎用コードは含まれていません。とにかく、より複雑で思慮深いEAでこの種のシステムを使用する方法を理解するために、2つの異なるEAと2つの異なるカスタムインジケータについて詳しく説明しました。この知識があれば、独自のカスタムインジケータを使用できると思います。私たちのEAでさえ、非常に興味深い分析を提供できます。これらすべては、MetaTrader 5が、トレーダーが望むことができる最も用途の広いプラットフォームであることを証明しています。これを理解していない人は単にそれを最後まで研究していないだけです。
MetaTrader 5では、これまで多くの人ができていたよりもはるかに前進することができます。この記事で提示された知識を使用してください。
次の記事でお会いしましょう。
リンク
初心者のためのMQL5:EXPERT ADVISORでのテクニカルインディケーター使用ガイド
MetaQuotes Ltdによりポルトガル語から翻訳されました。
元の記事: https://www.mql5.com/pt/articles/10329
- 無料取引アプリ
- 8千を超えるシグナルをコピー
- 金融ニュースで金融マーケットを探索