初級から中級まで:インジケーター(V)
はじめに
前回の「初級から中級まで:継承」では、構造化プログラミングの文脈で継承をどのように活用できるかについて解説しました。 情報を詰め込みすぎないよう、今回はもう少しシンプルなトピックを取り上げます。少なくとも、これまでの記事で扱った内容と並行して学習や実践を進められるトピックです。
このトピックは、最初は少し理解しにくいかもしれません。また、これまでにかなり内容の濃い記事が6本連続で掲載されていたことを踏まえると、ひとまずリラックスして、もっとシンプルで実用的な内容に目を向けてみることをお勧めします。
インジケーターに関する記事のうち最新の「初級から中級まで:インジケーター(IV)」では、完全に静的で非常にシンプルな実装を使用してチャート上に直接表示する方法について解説し、インジケーターを直接作成する方法を紹介しました。 これらの記事で紹介した形式は、実際、ほとんどあらゆるインジケーターの作成に適しています。しかし、実質的にはその形式だけに縛られているわけではありません。 動的なインジケーターと分類できるものを作成することも可能です。
ただし、なぜ私がこれまで紹介してきた形式を「静的」と考えているのか、また、それが動的に作成する形式とどのように異なるのかを理解していただくには、いくつか説明が必要です。 そこで、まずは新しいトピックに進み、これらの違いについて詳しく見ていきましょう。
静的インジケーターと動的インジケーターの違い
これから述べることについては、おそらく多くのプログラマーが異論を唱えるでしょう。しかし、誰もが自分の意見を持つ権利があるのですから、それ自体は大した問題ではありません。 また、ある意見の根拠が明確に示され、きちんと説明されていれば、その意見が必ずしも他の意見を否定することにはなりません。
これまで紹介してきたインジケーターを、なぜ私が「静的」と呼ぶのかを理解するには、まず、そのコードを確認する必要があります。 実際、当時は細かい部分まで説明しませんでした。その理由の一つは、このような実装は比較的理解しやすいものだったからです。 しかし、今回取り上げる内容を理解するには、まず、これまでに紹介したコードを理解しておく必要があります。
それでは、これまで紹介したコードの一つを例に見てみましょう。 以下のコードを、説明の出発点として使用します。
01. //+------------------------------------------------------------------+ 02. #property copyright "Daniel Jose" 03. //+------------------------------------------------------------------+ 04. #property indicator_type1 DRAW_COLOR_CANDLES 05. #property indicator_color1 clrRed, clrRoyalBlue, clrGreen 06. //+----------------+ 07. #property indicator_buffers 5 08. #property indicator_plots 1 09. //+----------------+ 10. double gl_Buff_High[], 11. gl_Buff_Open[], 12. gl_Buff_Close[], 13. gl_Buff_Low[], 14. gl_Buff_Color[]; 15. //+------------------------------------------------------------------+ 16. int OnInit() 17. { 18. SetIndexBuffer(1, gl_Buff_High, INDICATOR_DATA); 19. SetIndexBuffer(0, gl_Buff_Open, INDICATOR_DATA); 20. SetIndexBuffer(3, gl_Buff_Close, INDICATOR_DATA); 21. SetIndexBuffer(2, gl_Buff_Low, INDICATOR_DATA); 22. SetIndexBuffer(4, gl_Buff_Color, INDICATOR_COLOR_INDEX); 23. 24. return INIT_SUCCEEDED; 25. }; 26. //+------------------------------------------------------------------+ 27. 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 &TickVolume[], const long &Volume[], const int &Spread[]) 28. { 29. static double high = DBL_MIN, 30. low = DBL_MAX; 31. 32. for (int c = (prev_calculated > 0 ? prev_calculated - 1 : 0); c < rates_total; c++) 33. { 34. gl_Buff_High[c] = High[c]; 35. gl_Buff_Open[c] = Open[c]; 36. gl_Buff_Close[c] = Close[c]; 37. gl_Buff_Low[c] = Low[c]; 38. 39. gl_Buff_Color[c] = (Open[c] > Close[c] ? 0 : 2); 40. if ((c - 1) > 0) 41. { 42. high = (High[c - 1] > high ? High[c - 1] : high); 43. low = (Low[c - 1] < low ? Low[c - 1] : low); 44. gl_Buff_Color[c] = ((high > High[c]) && (low < Low[c]) ? 1 : gl_Buff_Color[c]); 45. if (gl_Buff_Color[c] != 1) 46. { 47. high = DBL_MIN; 48. low = DBL_MAX; 49. } 50. } 51. } 52. 53. return rates_total; 54. }; 55. //+------------------------------------------------------------------+
コード01
コード01を実行すると、チャート表示が、インジケーターによる描画形式に変わります。 ここでは、下の図に示すように、インサイドバー・インジケーターとして表示されます。

図01
コード01の仕組みについては、少なくとも基本的な部分は理解できているはずです。というのも、このコードを初めて紹介した際に、特定のパラメータをどのように宣言するかについて説明したからです。 しかし、私の考えでは、コード01に示したこのインジケーターは静的インジケーターに分類されます。 その理由は、まさに04行目と05行目の宣言にあります。 ここで、誤解しないでいただきたいのですが、 コード01のインジケーターを静的と呼ぶのは、コンパイル後にインジケーター自体を変更できないためです。これは、先ほど触れた2行を含むMQL5のコンパイル指令を使って宣言しているからです。
とはいえ、本当に重要なのは、インジケーターが正しく動作し、設計された目的を果たせるかどうかです。 その目的は比較的簡単に達成できるため、この静的な実装を使用しても何ら問題はありません。 それでも、まったく同じコード、あるいは、より正確に言えば、実装方法や表示方法の点で非常によく似たインジケーターを、動的な特性を備えた形で作成することもできます。
では、動的インジケーターはどのように動作するのでしょうか。 また、動的インジケーターを実装することには、どのような利点があるのでしょうか。 順を追って見ていきましょう。 まず、動的インジケーターのコードには、コード01の04行目と05行目にある記述を含めません。 これが最初の、そして最も重要な違いです。 動的に実装する利点は、まさにこの2行が不要になることにあります。 最初は奇妙に思えるかもしれませんが、後でより複雑なモデルに移ると、その意味がはっきりしてくるでしょう。
この2行をコード内で宣言しないからこそ、表示方法を変更するたびにコードを再コンパイルすることなく、インジケーターの情報表示方法を変更できるのです。 さらに、おそらく最大の利点は、表示方法を選ぶための明確なロジック構成を、インジケーターコード内にわざわざ作る必要がないということです。
ここで、よく注意してください。 今回行うことによって、コード01のインジケーターの動作原理そのものが変わるわけではありません。 単に、これまでの処理をコードから実行できるようにするだけです。 言い換えれば、通常はコンパイル指令によって行われる処理の一部を、MQL5標準ライブラリの関数呼び出しによって実行するということです。 まず、コード01のインジケーターを静的にしている記述を削除します。 静的な部分を取り除くと、次のようになります。
01. //+------------------------------------------------------------------+ 02. #property copyright "Daniel Jose" 03. //+------------------------------------------------------------------+ 04. #property indicator_buffers 5 05. #property indicator_plots 1 06. //+----------------+ 07. double gl_Buff_High[], 08. gl_Buff_Open[], 09. gl_Buff_Close[], 10. gl_Buff_Low[], 11. gl_Buff_Color[]; 12. //+------------------------------------------------------------------+ 13. int OnInit() 14. { 15. SetIndexBuffer(1, gl_Buff_High, INDICATOR_DATA); 16. SetIndexBuffer(0, gl_Buff_Open, INDICATOR_DATA); 17. SetIndexBuffer(3, gl_Buff_Close, INDICATOR_DATA); 18. SetIndexBuffer(2, gl_Buff_Low, INDICATOR_DATA); 19. SetIndexBuffer(4, gl_Buff_Color, INDICATOR_COLOR_INDEX); 20. 21. return INIT_SUCCEEDED; 22. }; 23. //+------------------------------------------------------------------+ 24. 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 &TickVolume[], const long &Volume[], const int &Spread[]) 25. { 26. static double high = DBL_MIN, 27. low = DBL_MAX; 28. 29. for (int c = (prev_calculated > 0 ? prev_calculated - 1 : 0); c < rates_total; c++) 30. { 31. gl_Buff_High[c] = High[c]; 32. gl_Buff_Open[c] = Open[c]; 33. gl_Buff_Close[c] = Close[c]; 34. gl_Buff_Low[c] = Low[c]; 35. 36. gl_Buff_Color[c] = (Open[c] > Close[c] ? 0 : 2); 37. if ((c - 1) > 0) 38. { 39. high = (High[c - 1] > high ? High[c - 1] : high); 40. low = (Low[c - 1] < low ? Low[c - 1] : low); 41. gl_Buff_Color[c] = ((high > High[c]) && (low < Low[c]) ? 1 : gl_Buff_Color[c]); 42. if (gl_Buff_Color[c] != 1) 43. { 44. high = DBL_MIN; 45. low = DBL_MAX; 46. } 47. } 48. } 49. 50. return rates_total; 51. }; 52. //+------------------------------------------------------------------+
コード02
コード02は、コンパイルはできるものの、動作しません。 少なくとも、適用したチャートには何の変化も起こりません。 しかし、これは本質的な問題ではありません。 MetaTrader 5に対して、インジケーターをどのように使用するかを明示する部分を削除したために起こったことです。 その結果、MetaTrader 5が認識しているのは、データバッファが4つ、カラーバッファが1つあり、プロットが1つだけ描画されるということだけです。 しかし、各バッファには何らかの情報を表示するための設定がされているにもかかわらず、MetaTrader 5はそれらのバッファをどのように使用すればよいのか、分かっていません。 そのため、チャート上では何も起こらないのです。
では、この問題をどう解決すればよいのでしょうか。 コード02で宣言したバッファの使い方を、MetaTrader 5にどう伝えればよいのでしょうか。 ここが最も興味深いところです。というのも、ここからはMetaTrader 5におけるバッファの使用方法を、動的に設定していくからです。 具体的には、以下に示すように、コードにいくつかの行を追加します。
01. //+------------------------------------------------------------------+ 02. #property copyright "Daniel Jose" 03. //+------------------------------------------------------------------+ 04. #property indicator_buffers 5 05. #property indicator_plots 1 06. //+----------------+ 07. double gl_Buff_High[], 08. gl_Buff_Open[], 09. gl_Buff_Close[], 10. gl_Buff_Low[], 11. gl_Buff_Color[]; 12. //+------------------------------------------------------------------+ 13. int OnInit() 14. { 15. const color cor[] = {clrRed, clrRoyalBlue, clrGreen}; 16. 17. PlotIndexSetInteger(0, PLOT_DRAW_TYPE, DRAW_COLOR_CANDLES); 18. PlotIndexSetInteger(0, PLOT_COLOR_INDEXES, cor.Size()); 19. for (uint c = 0; c < cor.Size(); c++) 20. PlotIndexSetInteger(c, PLOT_LINE_COLOR, c, cor[c]); 21. 22. SetIndexBuffer(1, gl_Buff_High, INDICATOR_DATA); 23. SetIndexBuffer(0, gl_Buff_Open, INDICATOR_DATA); 24. SetIndexBuffer(3, gl_Buff_Close, INDICATOR_DATA); 25. SetIndexBuffer(2, gl_Buff_Low, INDICATOR_DATA); 26. SetIndexBuffer(4, gl_Buff_Color, INDICATOR_COLOR_INDEX); 27. 28. return INIT_SUCCEEDED; 29. }; 30. //+------------------------------------------------------------------+ . . .
コード03
最終的な結果は図01に示したものと同じなので、ここでは繰り返しません。 ただし、コード03の断片に示されている動的なインジケーターを使用した場合、コード01の静的なインジケーターとは、動作が少し異なります。
先に進む前に、コード03で何が行われているのかを確認しておきましょう。ここでは、説明に必要な部分だけを抜粋しています。 コード全体については、添付ファイルを参照してください。 まず、15行目では、色を格納した定数配列を定義しています。 この配列の色の並びは、コード01の05行目で定義したものと同じです。 コードの仕組みを理解するうえで、この点は重要です。 続いて、コード03の17行目を実行すると、コード01の04行目をコンパイラが読み込んだときと同じ効果が得られます。 ただし、ここには微妙な違いがあります。 コード01では、コンパイル時にインジケータの表示方法をあらかじめ指定していました。一方、コード03では、そのような指定を行いません。 ここではコンパイル時に見た目を固定するのではなく、生成されたコードの実行時にその値を設定します。 しかし、この違いは、後ほど説明するように、重要な影響をもたらします。
その直後の18行目では、カラーバッファで使用する色の数をMetaTrader 5に指定しています。 ここで注意してほしいのは、色の数を指定しただけでは、それぞれの色の値まで指定したことにはならない、という点です。 色を初期化しなければ、MetaTrader 5によって何らかの値が設定され、意図しない配色になってしまう可能性があります。 そこで、19行目のループを使い、20行目の関数を呼び出して、各色を初期化しています。 これにより、コード01の05行目と同じ処理を実現しています。
しかし、両者の共通点はここまでです。 コード03に示されたインジケータをチャートに適用すると、少なくとも一見すると、かなり奇妙な状況が生じます。 その様子を図02で確認してみましょう。

図02
「おや? ちょっと待ってください。 インジケーターに適用する色は、いったいどこにあるのでしょうか?」あなたも、そして図02を見たユーザーも、きっと驚くことでしょう。これでは、何が起きているのか分からず、戸惑ってしまっても無理はありません。 では、このインジケーターを実際にチャートに適用すると、どうなるのでしょうか。 以下のアニメーション01をご覧ください。

アニメーション01
「これは何とも奇妙です。 さっぱり理解できません。 図02では色を設定できないように見えるのに、なぜインジケーターは図01と同じ結果を表示できるのでしょうか。 不思議です。」
実のところ、この動作はそれほど不思議なものではありません。 その理由は、パラメータを動的に定義していることにあります。 MetaTrader 5は、インジケーターで使用する色の数や具体的な色がまだ確定していない段階では、それらを設定項目として表示できません。つまり、色を設定するための情報がまだそろっていないため、設定画面に色を表示できないのです。 これは確かに興味深く、今後、さまざまな角度から検討することもできます。 ただし、今は本題に集中しましょう。
配色が図01と同じになっていることから、このインジケーターが正常に動作していることは確認できます。 また、コードを記述した段階で指定したテンプレートを、ユーザーに示すこともできます。 「では、ユーザー自身が好きな色を選べるようにすることもできるのではないでしょうか。」もちろん、可能です。インジケーターがチャートに表示された後であれば、ユーザーはプロパティ画面を開き、色を変更できます。 実際にどうなるか、アニメーション02で確認してみましょう。

アニメーション02
なるほど、興味深いですね。これで、インジケーターで使用する色がプロパティ画面に表示されるようになりました。 「ちょっと待ってください。 先ほど、色を変更できると言いましたよね。 でも、気になることがあります。 コード03の15行目では、アニメーション02に表示されている色を定数として定義しています。 定数であるにもかかわらず、ユーザーが配色を変更できるというのは、 矛盾しているように思えます。」
この点については、MetaTrader 5の仕組みがうまく働きます。ユーザーが変更するのは、インジケーターそのものではなく、その引数やパラメータだからです。 そのため、MetaTrader 5はインジケーターをチャートから完全に取り除くことなく、 必要な部分だけを変更できます。 その様子を、以下のアニメーションに示します。

アニメーション03
ご覧のとおり、この方法は実際に機能します。 ただし、この方法には小さな欠点があります。 ユーザーがインジケーターをチャートから削除した場合や、より正確には、MetaTrader 5がチャートを再構築する場合、15行目で宣言した値が再び使用されます。その結果、ユーザーが変更した設定は失われ、もう一度設定し直さなければなりません。
チャートが再構築されるきっかけの一例として、チャートの時間足の変更が挙げられます。 少し奇妙に聞こえるかもしれませんが、たとえば5分足でインジケーターの設定を変更した後、すぐに10分足へ切り替えるだけで、インジケーターの作成時に定義された設定が再び適用され、ユーザーが行った変更が失われてしまうことがあります。 こうした状況を回避する方法については、別の記事ですでに説明しています。 そのため、ここでは詳しく取り上げません。
ここまでの簡単な説明で、静的な実装と動的な実装の違いは、ある程度ご理解いただけたと思います。 さらに詳しく知りたい方は、MQL5の公式ドキュメントにある「 インジケーターのプロパティと対応する関数の関係」という表を参照してください。この表を見れば、どのプロパティをコンパイル時に指定する必要があり、どのプロパティを関数によって動的に設定できるのかを確認できます。
「なるほど。でも、率直に言って、動的な仕組みを使うメリットがまだよく分かりません。 これが自分にとって、どのように役立つのでしょうか。」 その疑問ももっともです。このような実装には、あまり魅力を感じないかもしれません。 実際、動的な実装には面倒な点もあり、場合によっては、わざわざ採用するほどの利点がないように思えるでしょう。
しかし、個人的な用途など、ごく限られた特定の状況では、この方法が役立つこともあります。 その利点を具体的に示すため、 次のトピックでは、ある特殊なケースを取り上げ、実際の実装例を見ていくことにしましょう。
さまざまなチャートタイプに対応するインサイドバー・インジケーター
前回のトピックで取り上げたインジケーターは、主にローソク足チャートでの使用を想定しています。 しかし、ご存じのとおり、MetaTrader 5 ではローソク足チャートだけでなく、バーチャートやラインチャートも利用できます。 ユーザーが誤ってローソク足チャート以外の表示形式に切り替えてしまうと、チャートの表示に思わぬ問題が生じます。 以下をご覧ください。

図03
図03は、バーチャート上でインサイドバー・インジケーターを使用した例です。 次の図に示すように、情報が通常とは大きく異なる形で表示されていることがわかります。

図04
こちらも、図03とよく似た問題です。 ただし、図04では、ラインチャートとインサイドバー・インジケーターを組み合わせています。 ラインチャートを使用する場合、OHLC(始値・高値・安値・終値)のうち、通常は終値など、特定の価格データに注目するため、このような使い方はあまり意味がないと考える人も多いでしょう。 しかし、問題の本質はそこではありません。 チャートの表示形式を変更しても、インジケーターによってローソク足が表示されてしまうことが問題なのです。 この挙動は修正する必要があります。
この問題を解決する方法として、ここでは2つのアプローチを取り上げます。 もちろん、チャートの表示形式ごとに専用のインジケーターを作成する方法もあります。 しかし、それではユーザーにとって使い勝手が悪くなり、操作も煩雑になってしまいます。 残りの2つの方法に注目してみましょう。 1つ目は、コンパイラーが生成するコードを踏まえて、適切な実行方式を構築する方法です。 もう1つは、監視対象となる情報を動的に処理する仕組みを構築する方法です。
ここでは、不要な複雑さを避けるため、前回のトピックで取り上げたコード03を修正し、インジケーターの色をあらかじめ指定できるようにします。 ただし、チャート上にどの種類の表示を行うかについては、この段階では指定しません。 その結果、コード03は次のようになります。
01. //+------------------------------------------------------------------+ 02. #property copyright "Daniel Jose" 03. //+------------------------------------------------------------------+ 04. #property indicator_color1 clrRed, clrRoyalBlue, clrGreen 05. //+----------------+ 06. #property indicator_buffers 5 07. #property indicator_plots 1 08. //+----------------+ 09. double gl_Buff_High[], 10. gl_Buff_Open[], 11. gl_Buff_Close[], 12. gl_Buff_Low[], 13. gl_Buff_Color[]; 14. //+------------------------------------------------------------------+ 15. int OnInit() 16. { 17. PlotIndexSetInteger(0, PLOT_DRAW_TYPE, DRAW_COLOR_CANDLES); 18. 19. SetIndexBuffer(1, gl_Buff_High, INDICATOR_DATA); 20. SetIndexBuffer(0, gl_Buff_Open, INDICATOR_DATA); 21. SetIndexBuffer(3, gl_Buff_Close, INDICATOR_DATA); 22. SetIndexBuffer(2, gl_Buff_Low, INDICATOR_DATA); 23. SetIndexBuffer(4, gl_Buff_Color, INDICATOR_COLOR_INDEX); 24. 25. return INIT_SUCCEEDED; 26. }; 27. //+------------------------------------------------------------------+ 28. 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 &TickVolume[], const long &Volume[], const int &Spread[]) 29. { 30. static double high = DBL_MIN, 31. low = DBL_MAX; 32. 33. for (int c = (prev_calculated > 0 ? prev_calculated - 1 : 0); c < rates_total; c++) 34. { 35. gl_Buff_High[c] = High[c]; 36. gl_Buff_Open[c] = Open[c]; 37. gl_Buff_Close[c] = Close[c]; 38. gl_Buff_Low[c] = Low[c]; 39. 40. gl_Buff_Color[c] = (Open[c] > Close[c] ? 0 : 2); 41. if ((c - 1) > 0) 42. { 43. high = (High[c - 1] > high ? High[c - 1] : high); 44. low = (Low[c - 1] < low ? Low[c - 1] : low); 45. gl_Buff_Color[c] = ((high > High[c]) && (low < Low[c]) ? 1 : gl_Buff_Color[c]); 46. if (gl_Buff_Color[c] != 1) 47. { 48. high = DBL_MIN; 49. low = DBL_MAX; 50. } 51. } 52. } 53. 54. return rates_total; 55. }; 56. //+------------------------------------------------------------------+
コード04
コード04では、インジケーターの色を最初に指定できるようになったため、ユーザーにとって、より使いやすいものになりました。 これは、コード04の4行目でインジケーターのプロパティを定義しているためです。 これに伴い、OnInitイベントハンドラーから、色の読み込みを行うコードを削除しました。 こうして、これから手を加えていくための基本となるインジケーターができました。
さて、ここからが最も興味深いところです。コード04のインジケーターは、動的に動作する一方で、カラーテンプレートを表示するための静的な部分も備えています。では、これをどのように動作させ、現在表示しているチャートの種類に応じて自動的に適応させればよいのでしょうか。
私のコードを見た人は、たいていこのあたりで驚くようです。 コード04の17行目では、インジケーターをローソク足として描画するように指定しています。 そのため、チャートをバーチャートに切り替えると、前の図で見たような問題が発生します。 ところが、ドキュメントを詳しく調べてみると、標準ライブラリには興味深い機能が用意されていることがわかります。 現在、どの種類のチャートが使用されているかを取得できる機能です。 ChartGetInteger関数のことです。 この関数を使って現在のチャートの種類を取得すると、列挙型の値が返されます。今回の場合、それがENUM_CHART_MODEです。
まさに、これが私たちの知りたい情報です。 ただし、実際にこの機能を利用する前に、まずは現段階のインジケーターがどのように動作するのかを確認しておきましょう。 そのためにコードを修正しました。修正後のコードは、次のとおりです。
. . . 14. //+------------------------------------------------------------------+ 15. #define PrintX(X) Print(__FUNCTION__, " => ",#X, " :: ",X); 16. //+------------------------------------------------------------------+ 17. int OnInit() 18. { 19. PrintX(EnumToString((ENUM_CHART_MODE)ChartGetInteger(0, CHART_MODE))); 20. PlotIndexSetInteger(0, PLOT_DRAW_TYPE, DRAW_COLOR_CANDLES); 21. 22. SetIndexBuffer(1, gl_Buff_High, INDICATOR_DATA); 23. SetIndexBuffer(0, gl_Buff_Open, INDICATOR_DATA); 24. SetIndexBuffer(3, gl_Buff_Close, INDICATOR_DATA); 25. SetIndexBuffer(2, gl_Buff_Low, INDICATOR_DATA); 26. SetIndexBuffer(4, gl_Buff_Color, INDICATOR_COLOR_INDEX); 27. 28. return INIT_SUCCEEDED; 29. }; 30. //+------------------------------------------------------------------+ . . .
コード05
ここで注意して説明を追ってください。そうしないと、これから行う処理についての説明がわかりにくくなってしまいます。 コード05は、現在使用されているチャートの種類を判別するために、コード04に加える必要があった変更を示しています。 そして、19行目で関数を呼び出します。この呼び出しは、最終的に15行目の定義に従って、MetaTrader 5 のターミナルに表示する情報へと変換されます。 その情報は、以下の図で確認できます。

図05
図05から、使用中のチャートがCHART_CANDLESであることがわかります。

図06
図06では、現在のチャートモードがCHART_BARSであることがはっきりと確認できます。

図07
そして最後に、図07では、現在のチャートモードがCHART_LINEであることがわかります。 では、これをどのように活用すればよいのでしょうか。 実は、やり方は非常に簡単です。ただし、CHART_LINE チャートについては例外で、その解決方法は後ほど説明します。 インジケーターで適切な描画モードを使用するには、コード05に示した20行目の関数呼び出しで、ローソク足チャートの場合は DRAW_COLOR_CANDLESを、バーチャートの場合はDRAW_COLOR_BARSを指定するだけです。
ここまで来れば、あとは難しくありません。 誰でも容易に実装でき、MetaTrader 5がInitイベントからインジケーターを適用する場合には、問題なく動作します。 つまり、最もシンプルな方法で実装するなら、コードは以下のようになります。
. . . 14. //+------------------------------------------------------------------+ 15. int OnInit() 16. { 17. switch ((ENUM_CHART_MODE)ChartGetInteger(0, CHART_MODE)) 18. { 19. case CHART_BARS: 20. PlotIndexSetInteger(0, PLOT_DRAW_TYPE, DRAW_COLOR_BARS); 21. break; 22. case CHART_CANDLES: 23. PlotIndexSetInteger(0, PLOT_DRAW_TYPE, DRAW_COLOR_CANDLES); 24. break; 25. case CHART_LINE: 26. break; 27. } 28. 29. SetIndexBuffer(1, gl_Buff_High, INDICATOR_DATA); 30. SetIndexBuffer(0, gl_Buff_Open, INDICATOR_DATA); 31. SetIndexBuffer(3, gl_Buff_Close, INDICATOR_DATA); 32. SetIndexBuffer(2, gl_Buff_Low, INDICATOR_DATA); 33. SetIndexBuffer(4, gl_Buff_Color, INDICATOR_COLOR_INDEX); 34. 35. return INIT_SUCCEEDED; 36. }; 37. //+------------------------------------------------------------------+ . . .
コード06
なお、このコード06では、ラインチャート描画モードを考慮していない点に注意してください。 この点については、後ほど明らかになります。 ただし、ほかの描画モードをどのように表示しているかに注目してください。 コード06の方法は機能しますが、少し問題があります。 以下のアニメーションで、その問題を確認できます。

アニメーション04
アニメーション04を見ると、チャート描画モードを切り替えた際に、インジケーターがMetaTrader 5の動作に追従できなくなることがわかります。 このような状況に直面すると、インジケータを正しく作成するのは不可能だと考え、諦めてしまう人も多いでしょう。 しかし、そうした人たちはMetaTrader 5が実際にどのように動作するのかを理解していません。 何らかの操作が行われると、MetaTrader 5は、それに対応する特定のイベントを発生させます。通常、こうしたイベントはチャート上での操作に伴って発生し、そのイベントには何が起きたのかを判断するための情報が含まれています。 イベントの中には、有効化または無効化できるものもあれば、そうできないものもあります。 イベントを有効化または無効化するたびに、MetaTrader 5全体のパフォーマンスに影響が生じます。これは、システムに負荷がかかり、本来なら監視する必要のないイベントまで処理しなければならなくなるためです。
高いパフォーマンスを求める人にとって、これはごく基本的なことです。 MetaTrader 5で取引を行う際、インジケータやEAなどの追加アプリケーションを一切使用しないトレーダーも少なくありません。 こうしたアプリケーションは、ボラティリティが高い局面でMetaTrader 5の動作を遅くしてしまうことがあるためです。 まさにそのような局面こそ、彼らが利益を得たいと考える場面です。そのため、MetaTrader 5の基本機能だけを使用し、それ以外のものは何も追加しないのです。
さて、ここではMetaTrader 5を最も基本的な構成で使用して取引することについては扱いません。 私たちは、何らかの目的に役立てるために、チャートにアプリケーションを追加したいと考えています。 そのためには、チャート上で何が行われたかにかかわらず、何らかの操作が行われるたびにMetaTrader 5が発生させる特定のイベントを捕捉する必要があります。
今回捕捉したいのは、ChartEventイベントです。 コードでは、これをOnChartEventプロシージャで処理します。 ここでよく注意してください。 ChartEventイベントは、チャート上で何らかの変化が起こるたびに発生します。 ただし、チャートに加えられた変更をすべて捕捉することがないよう、特定のパラメータを有効化または無効化できます。 とはいえ、チャートにインジケータを追加すると、MetaTrader 5は2つのイベントを連続して発生させます。 場合によっては3つ目のイベントも発生しますが、ここでは最初の2つに注目します。
最初に発生するのは、初期化(Init)イベントです。このイベントは、インジケーターについての解説を始めた当初から取り上げてきたOnInit関数によって捕捉され、処理されます。 Initイベントの処理が完了すると、すぐに次のイベントが発生します。まだ何も操作していなくても同じです。 このイベントがChartEventです。そして、まさにこの時点で、インジケータが実際にチャート上に配置されます。
さて、この後何が起こるのかを理解するために、ChartEventイベントを捕捉するプロシージャの宣言を確認しましょう。 以下に示します。
void OnChartEvent(const int id, // Event ID const long& lparam, // Parameter of type long event const double& dparam, // Parameter of type double event const string& sparam // Parameter of type string events );
コード07
ここで OnChartEventドキュメントを確認すると、小さな表が掲載されています。 その表の中で、今回注目するのは次の値です。CHARTEVENT_CHART_CHANGE — チャートが変更されたときに発生するイベントです。 つまり、ChartEventイベントが最初に発生した際に現れるのが、この値です。 これがわかれば、コードに興味深い工夫を加えることができます。 コードを更新すると、次に示すようになります。
01. //+------------------------------------------------------------------+ 02. #property copyright "Daniel Jose" 03. //+------------------------------------------------------------------+ 04. #property indicator_color1 clrRed, clrRoyalBlue, clrGreen 05. //+----------------+ 06. #property indicator_buffers 5 07. #property indicator_plots 1 08. //+----------------+ 09. double gl_Buff_High[], 10. gl_Buff_Open[], 11. gl_Buff_Close[], 12. gl_Buff_Low[], 13. gl_Buff_Color[]; 14. //+------------------------------------------------------------------+ 15. int OnInit() 16. { 17. SetIndexBuffer(0, gl_Buff_Open, INDICATOR_DATA); 18. SetIndexBuffer(1, gl_Buff_High, INDICATOR_DATA); 19. SetIndexBuffer(2, gl_Buff_Low, INDICATOR_DATA); 20. SetIndexBuffer(3, gl_Buff_Close, INDICATOR_DATA); 21. SetIndexBuffer(4, gl_Buff_Color, INDICATOR_COLOR_INDEX); 22. 23. return INIT_SUCCEEDED; 24. }; 25. //+------------------------------------------------------------------+ 26. 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 &TickVolume[], const long &Volume[], const int &Spread[]) 27. { 28. static double high = DBL_MIN, 29. low = DBL_MAX; 30. 31. for (int c = (prev_calculated > 0 ? prev_calculated - 1 : 0); c < rates_total; c++) 32. { 33. gl_Buff_High[c] = High[c]; 34. gl_Buff_Open[c] = Open[c]; 35. gl_Buff_Close[c] = Close[c]; 36. gl_Buff_Low[c] = Low[c]; 37. 38. gl_Buff_Color[c] = (Open[c] > Close[c] ? 0 : 2); 39. if ((c - 1) > 0) 40. { 41. high = (High[c - 1] > high ? High[c - 1] : high); 42. low = (Low[c - 1] < low ? Low[c - 1] : low); 43. gl_Buff_Color[c] = ((high > High[c]) && (low < Low[c]) ? 1 : gl_Buff_Color[c]); 44. if (gl_Buff_Color[c] != 1) 45. { 46. high = DBL_MIN; 47. low = DBL_MAX; 48. } 49. } 50. } 51. 52. return rates_total; 53. }; 54. //+------------------------------------------------------------------+ 55. void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam) 56. { 57. switch (id) 58. { 59. case CHARTEVENT_CHART_CHANGE: 60. switch ((ENUM_CHART_MODE)ChartGetInteger(0, CHART_MODE)) 61. { 62. case CHART_BARS: 63. PlotIndexSetInteger(0, PLOT_DRAW_TYPE, DRAW_COLOR_BARS); 64. break; 65. case CHART_CANDLES: 66. PlotIndexSetInteger(0, PLOT_DRAW_TYPE, DRAW_COLOR_CANDLES); 67. break; 68. case CHART_LINE: 69. break; 70. } 71. break; 72. } 73. ChartRedraw(); 74. }; 75. //+------------------------------------------------------------------+
コード08
「うーん。 どうでしょう。 OnInit関数が実行される際に、どのチャート描画モードを使用するのかをMetaTrader 5に指定していないので、このコードは動作しないと思います。 わかりません。 コード02でエラーが発生したときと同じ結果になるはずです。ほぼ間違いないと思います。」
さて、ここに懐疑的な読者が一人いるようです。 そこで、世界は想像しているよりもずっと広いのだということを示してみましょう。 コード08が実際に機能することを確かめるために、以下のアニメーションを見てみましょう。

アニメーション05
MetaTrader 5のチャート上での動作に追従できないのは、CHART_LINEモードへの切り替えだけであることに注目してください。 この場合、チャートの描画モデルを使用していないため、私たちのインジケーターはMetaTrader 5上でのユーザーの操作に追従できません。
まとめ
本日の記事では、インジケーターを扱うための、基本的でありながら非常に興味深い原則を紹介しました。 インジケーターを使ってチャートを描画し、MetaTrader 5のすべての要件を満たす方法までは説明しませんでした。しかし、シンプルなインジケーターに、チャート描画モードに対応するコマンドを実行させることはできました。 これは、プラットフォーム上でユーザーが選択した描画モードに応じて、インジケーターにラインチャートを表示させる方法を、まだ説明していないことにも関係しています。
ただし、ローソク足チャートやバーチャートの描画から、色付きのラインチャートの描画へ切り替える方法については、現時点で実演するのがかなり難しいため、後ほど取り上げることにします。ローソク足チャートやバーチャートでは4つのデータバッファと1つのカラーバッファが必要ですが、色付きのラインチャートでは1つのデータバッファと1つのカラーバッファが必要です。 これを実現するには、標準ライブラリの呼び出しを通じてインジケーターを読み込んだり、アンロードしたりするという、少し異なるアプローチを採用する必要があります。 現時点では、私にとっても説明するのが少々難しく、読者の皆さんにとっても理解するのが難しい内容だと思います。
いずれにしても、さらに複雑な内容へと進んでいく中で、今回取り上げた問題の解決方法をいずれ紹介します。 それまでは、この記事の内容をしっかりと学んでおくことをお勧めします。そして何よりも、これまでの記事で説明した内容を読み返すことを忘れないでください。 次の記事では、いくつかの概念を今回のインジケーターのテーマと結び付けていきます。きっと興味深い内容になるでしょう。 ぜひお見逃しなく。
MetaQuotes Ltdによりポルトガル語から翻訳されました。
元の記事: https://www.mql5.com/pt/articles/15932
警告: これらの資料についてのすべての権利はMetaQuotes Ltd.が保有しています。これらの資料の全部または一部の複製や再プリントは禁じられています。
この記事はサイトのユーザーによって執筆されたものであり、著者の個人的な見解を反映しています。MetaQuotes Ltdは、提示された情報の正確性や、記載されているソリューション、戦略、または推奨事項の使用によって生じたいかなる結果についても責任を負いません。
市場シミュレーション(第21回):SQL入門(IV)
市場シミュレーション:ポジション表示(V)
エラー 146 (「トレードコンテキスト ビジー」) と、その対処方法
選択的状態空間モデルを採用したMambaベースのニューラルネットワーク自動売買ロボットの開発
- 無料取引アプリ
- 8千を超えるシグナルをコピー
- 金融ニュースで金融マーケットを探索