初級から中級まで:オブジェクト(II)
はじめに
前回の「初級から中級へ:オブジェクト(I)」では、MQL5で利用できるグラフィカルオブジェクトを初めて紹介し、それらをチャート上に配置し、比較的簡単な方法で操作する手順も示しました。ただし、この方法は、一般にあまり効率的とはいえません。 というのも、現段階ではアプリケーションを本格的に構築することが目的ではなく、コードをできるだけシンプルにし、理解しやすくすることを優先しているからです。
前回の記事も、なかなか興味深い内容だったと思います。 しかし、今回取り組む内容は、それをはるかに上回る複雑さを持っています。 コードを使ってオブジェクトを操作・制御するようになると、それまでは不可能に思えたことも、次第に当たり前のように実現できるようになっていくのです。
とはいえ、私たちはまだ学習の入り口に立ったばかりです。想像力を存分に発揮し、プログラマーの視点から見てもMetaTrader 5を本当に興味深いものへと発展させていくには、その前にいくつか押さえておくべき点があります。 これから取り組む内容は、ユーザーの立場から見ると、何の役にも立たないように思えるかもしれません。 それでは、いつものように、新しいトピックに取りかかりましょう。
初期的な操作
前回の記事で紹介した内容は、オブジェクトに対する本格的な操作の第一歩とは、まだいえません。 というのも、そこで行ったのは、オブジェクトをチャート上に配置することだけだったからです。 それ以外の操作をおこなうには、ダイアログボックスを開き、オブジェクトのプロパティにアクセスして変更する必要がありました。 しかし、プログラミングを使えば、さらに多くのことができます。理論上、できることに限界があるとすれば、それは私たちの想像力くらいでしょう。 もちろん、MQL5はMetaTrader 5上で動作するアプリケーションを開発するために設計されており、MetaTrader 5自体も価格チャートを表示することを主な目的としたプラットフォームです。そのため、MQL5を単独で直接利用する場合にできることには、確かに一定の制約があります。
しかし、読者の皆さん、信じてください。その制約は、ほとんどの人が想像するよりも、はるか先にあります。 そして、その限界に達したとしても、CやC++といったプログラミング言語を使えば、さらに限界を押し広げることができます。 ただし、それはすでに別のレベルの話であり、この連載では扱いません。私の考えでは、本連載で扱うのは中級レベルと上級レベルを分ける境界までです。 CやC++などの言語を使ってMQL5の機能を拡張する必要が出てくる段階から、上級レベルの領域に入っていきます。
さて、本題に戻りましょう。オブジェクトを操作するには、ユーザーからの入力を受け付け、ユーザーとのやり取りを処理する仕組みが必要です。 こうした仕組みでは、キーボードやマウスを利用して、ユーザーの入力を簡単に処理できます。 ただし、皆さんもすでにお気づきかもしれませんが、こうした仕組みを利用するには、イベントを受け取って処理する必要があります。Windows、Linuxなど、どのオペレーティングシステムでも、ユーザーとアプリケーションの間のやり取りには、基本的にイベントが使われているからです。 この点については、「初級から中級へ:イベント(I)」でも紹介しました。 ただし、そこでは表面的に触れただけでした。 今回は、このテーマについてもう少し詳しく掘り下げていきます。
まずは、詳しい説明がなくても誰もが理解できるような、簡単なコード例を作成してみましょう。 最初のコードを以下に示します。
01. //+------------------------------------------------------------------+ 02. #property copyright "Daniel Jose" 03. //+------------------------------------------------------------------+ 04. #define def_NameObj "Demo" 05. //+------------------------------------------------------------------+ 06. int OnInit() 07. { 08. ObjectCreate(0, def_NameObj, OBJ_HLINE, 0, 0, 0); 09. ObjectSetInteger(0, def_NameObj, OBJPROP_COLOR, clrPurple); 10. 11. return INIT_SUCCEEDED; 12. }; 13. //+------------------------------------------------------------------+ 14. int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[]) 15. { 16. static double p = 0; 17. 18. ObjectMove(0, def_NameObj, 0, 0, p); 19. 20. if (prev_calculated > 0) 21. p = price[prev_calculated - 1]; 22. 23. return rates_total; 24. }; 25. //+------------------------------------------------------------------+ 26. void OnDeinit(const int reason) 27. { 28. ObjectDelete(0, def_NameObj); 29. ChartRedraw(); 30. }; 31. //+------------------------------------------------------------------+
コード01
これまでの記事で紹介してきた内容をしっかり学んでいれば、このコードも完全に理解できるはずです。実際、このコードは非常にシンプルでありながら、興味深い動作を実現しています。 その動作そのものが興味深いのです。 もしかすると、皆さんはこう思っているかもしれません。「このコードのどこがそんなに興味深いのだろう。 それに、14行目のOnCalculate関数の中で、なぜこのような処理を行うのだろうか。 ここで何が起きているのか、説明してもらえないだろうか?」もしこのコードの動作が理解できないのであれば、それは、これまでの記事で紹介した内容を実際に試してみていないからかもしれません。
このコードを実行すると、以下のアニメーションのような結果が得られます。

アニメーション01
この場合、1枚の画像だけでは十分ではありません。アニメーションを見ると、紫色のラインが価格ラインより常に常に1ティック分遅れて見えることがわかります。 ただし、ときには紫色のラインが現在の価格ラインと重なることもあります。 では、なぜこのようなことが起こるのでしょうか。ここが最も興味深い点です。 ほとんどの場合、紫色のラインは直前の気配値の価格を示しています。 しかし、その価格が現在の価格と一致することもあります。 そのため、コード01のインジケーターに何か問題があるように見えるかもしれません。
しかし、コードにもプラットフォームにも問題はありません。 紫色のラインは、意図された役割を正確に果たしています。つまり、直前の気配値の価格を示しているのです。 ただし、新しい気配値を受信した後、MetaTrader 5がその価格に変化がないまま新たなCalculateイベントを発生させると、紫色のラインは最新の価格ラインと重なります。 これこそが、アニメーション01で確認できる現象です。 その理由は18行目にあります。 直前の価格と新たに受信した気配値の価格を比較すると、両者が異なる場合があります。 その場合、紫色のラインは価格ラインより遅れた位置に表示されます。 一方、新しい気配値の価格が直前の価格と同じであれば、紫色のラインは最新の価格ラインと重なります。
なかなか便利ではないでしょうか。 多くのトレーダーやプログラマーは、今説明した考え方を理解しておらず、Calculateイベントが発生するのは、必ず価格が変化したときだと考えています。 しかし、ここまで見てきたように、実際には必ずしもそうではありません。 だからこそ、コードの最適化について十分に理解しておくことが重要です。そうしなければ、MetaTrader 5上で動作するアプリケーションのパフォーマンスを大きく低下させてしまうおそれがあります。
さて、シンプルで一見すると些細な例を見てきたところで、次はイベントを使った実験に進みましょう。 まずは、最も簡単なものとして、キーボードイベントの処理から始めます。 キーボードイベントが比較的扱いやすいのは、イベントを有効化したり無効化したりするための設定が必要ないからです。 キーが押されると、MetaTrader 5は自動的にイベントを発生させます。 チャート上で動作しているアプリケーションにChartEventイベントハンドラが実装されていれば、キーの押下を受け取り、それに応じた処理を実行できます。 この仕組みを最初に体験するために、キーボードイベントに反応するコードを新たに作成しましょう。初心者にとって目的がわかりやすく、なかなか興味深いコードです。 以下に示します。
01. //+------------------------------------------------------------------+ 02. #property copyright "Daniel Jose" 03. //+------------------------------------------------------------------+ 04. int OnInit() 05. { 06. return INIT_SUCCEEDED; 07. }; 08. //+------------------------------------------------------------------+ 09. int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[]) 10. { 11. return rates_total; 12. }; 13. //+------------------------------------------------------------------+ 14. void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam) 15. { 16. switch(id) 17. { 18. case CHARTEVENT_KEYDOWN: 19. Print(lparam); 20. break; 21. } 22. }; 23. //+------------------------------------------------------------------+
コード02
プログラマーが、どのキーが押されたのかをどのように判別するのか、疑問に思ったことはありませんか。 矢印キーが押されたのか、それともHomeキーやEndキーなどが押されたのかを、どうやって見分けるのでしょうか。 英数字キーについて考えると、まるで魔法のように思えるかもしれません。 しかし、それ以外のキーについてはどうでしょうか。 実は、プログラマーは押されたキーを直接把握しているわけではありません。ただし、どのように調べればよいかはわかっています。 そのために使うのが、先ほど紹介したようなコードです。 このコードを実行すれば、特定のキーを押したときに、どの数値コードが生成されるのかを確認できます。
実際には、キーを押したときに生成されるキーコードそのものは、ここに表示される値とは異なります。 しかし、オペレーティングシステムが、いわば押されたキーの情報を「変換」しているため、コード02のようなアプリケーションを実行すると、MetaTrader 5が、そのキーを識別するために使用すべき値を教えてくれます。 以下のアニメーションはその結果を示しています。

アニメーション02
この仕組みは、直感的に理解できると思います。 キーを押すと、そのキーに対応する値がターミナルに表示されます。 つまり、この値を利用すれば、どのキーが押されたのかを判別できるのです。 なお、MQL5の標準ライブラリには、特定の特殊キーが押されているかどうかを確認するための関数も用意されています。 ただし、その関数は、今学んだ仕組みを抽象化したものです。 なかなか便利ではないでしょうか。
では、これをどう活用すればよいのでしょうか。 あまり意味がないように思えるかもしれません。 そこで、コード02で取得した情報をどのように活用できるのか、実例を見てみましょう。 そのために、コード01を修正し、今度は別の動作をするようにします。 変更後のコードを以下に示します。
01. //+------------------------------------------------------------------+ 02. #property copyright "Daniel Jose" 03. //+------------------------------------------------------------------+ 04. #define def_NameObj "Demo" 05. #define def_KEY_UP 38 06. #define def_KEY_DOWN 40 07. //+------------------------------------------------------------------+ 08. int OnInit() 09. { 10. ObjectCreate(0, def_NameObj, OBJ_HLINE, 0, 0, 0); 11. ObjectSetInteger(0, def_NameObj, OBJPROP_COLOR, clrPurple); 12. 13. return INIT_SUCCEEDED; 14. }; 15. //+------------------------------------------------------------------+ 16. int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[]) 17. { 18. return rates_total; 19. }; 20. //+------------------------------------------------------------------+ 21. void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam) 22. { 23. static int p = 0; 24. MqlRates rate[1]; 25. 26. switch(id) 27. { 28. case CHARTEVENT_KEYDOWN: 29. switch ((int)lparam) 30. { 31. case def_KEY_DOWN: 32. p = (p < Bars(_Symbol, _Period) ? p + 1 : p); 33. break; 34. case def_KEY_UP: 35. p = (p > 0 ? p - 1 : p); 36. break; 37. default: 38. return; 39. } 40. Comment(StringFormat("Current bar analyzed: %d", p)); 41. CopyRates(_Symbol, _Period, p, rate.Size(), rate); 42. ObjectMove(0, def_NameObj, 0, rate[0].time, rate[0].close); 43. break; 44. } 45. ChartRedraw(); 46. }; 47. //+------------------------------------------------------------------+ 48. void OnDeinit(const int reason) 49. { 50. Comment(""); 51. ObjectDelete(0, def_NameObj); 52. ChartRedraw(); 53. }; 54. //+------------------------------------------------------------------+
コード03
コード03は、今回の記事で取り上げた他のコードよりも少し複雑です。 とはいえ、新たに登場する要素はそれほど多くありません。 そこで、ここでは新しい点に絞って説明し、それ以外の部分については、理解しやすいものとして省略します。
まず、23行目でローカルカウンタを初期化します。 このカウンタによって、チャート上の最新のバーから何本分ずれた位置にいるのかを把握できます。 これを分かりやすくするため、40行目では、特定の情報をチャート上に直接表示しています。 29~39行目のコードは、あらかじめ定義したキーだけを捕捉し、処理するためのものです。 41行目では、カウンタ「p」が示すバーの価格データを取得します。 ここで取得するのは、1本分のバーのデータだけです。 続く42行目では、チャート上のオブジェクトを移動させます。 このコードを実行すると、以下のアニメーションのような動作になります。

アニメーション03
興味深い結果ですが、現在どのバーが対象になっているのかを把握するために、バーを数える必要があり、少し分かりにくい面もありました。 ところで、50行目の処理について説明するのを忘れるところでした。 チャートからインジケーターを削除するとき、50行目のコードで、それまで表示していたテキストも削除します。 これは非常に簡単な処理ですが、意外と忘れられがちです。削除を忘れると、チャート上に不要な表示が残り、見づらくなってしまいます。
ここまでは順調です。 オブジェクトに対して、簡単な操作を行う方法はすでにわかりました。 しかし、次に覚えておくべきことがあります。お気づきかもしれませんが、これまで作成したオブジェクトには、すべて同じ名前が付けられていました。これでは、あまり実用的とはいえません。 チャート上の各オブジェクトには、それぞれ一意の名前を付ける必要があります。そのため、複数のオブジェクトを同じチャート上に同時に配置する方法を考える必要があります。 この方法を説明するために、次のトピックに進みましょう。
同じチャート上の複数のオブジェクト
初心者の多くが頭を悩ませる問題があります。コードの仕組みを十分に理解していなくても、コードを使ってみたいと考える人は少なくありません。 そして、複数のコードを組み合わせたり、同じチャート上で複数のプログラムを使用したりすると、それぞれのプログラムがチャート上にオブジェクトを作成しようとするため、やがて問題が発生します。 ここでは、ユーザーがオブジェクトを手動でチャートに追加するケースについては、あえて考えません。 あくまでも、オブジェクトの作成を自動で行うコードをユーザーが利用するケースに話を限定します。
問題の原因は、開発者とMetaTrader 5プラットフォーム双方の処理を簡素化するため、チャート上のすべてのオブジェクトに一意の名前を付けなければならないことにあります。 すでにチャート上に存在するオブジェクトと同じ名前で別のオブジェクトを作成しようとすると、問題が発生します。 その結果、実際にはチャート上に存在しない対象を操作しようとしても、同じ名前を持つ別の既存オブジェクトを誤って操作してしまうことがあります。 これは、プログラミング初心者だけでなく、経験の浅いユーザーにとっても、大きな混乱の原因になります。
一般に、プログラムやアプリケーションは特定の目的に合わせて開発されます。そのため開発者は、チャート上のオブジェクト名の扱いに注意するよう、ユーザーにわざわざ説明することはあまりありません。 基本的には、ユーザーが複数のアプリケーションを組み合わせたり、異なるアプリケーションを同じチャート上で同時に使用したりすることは想定していないのです。
しかし、プログラミングの観点から見ると、根本的な問題は別のところにあります。多くの初心者は、コードの中で何をしているのか、また、その操作がプログラムの実行時にどのような結果をもたらすのかを十分に理解しないまま、単純にコピー&ペーストを繰り返してしまいます。 このようなコピー&ペーストの使い方が、どのような問題を引き起こすのかを示すために、簡単なコード例を作成してみましょう。 以下をご覧ください。
01. //+------------------------------------------------------------------+ 02. #property copyright "Daniel Jose" 03. //+------------------------------------------------------------------+ 04. #define def_NameObj "Demo" 05. #define def_KEY_UP 38 06. #define def_KEY_DOWN 40 07. //+------------------------------------------------------------------+ 08. int OnInit() 09. { 10. ObjectCreate(0, def_NameObj, OBJ_VLINE, 0, 0, 0); 11. ObjectSetInteger(0, def_NameObj, OBJPROP_COLOR, clrRoyalBlue); 12. ObjectCreate(0, def_NameObj, OBJ_HLINE, 0, 0, 0); 13. ObjectSetInteger(0, def_NameObj, OBJPROP_COLOR, clrPurple); 14. 15. return INIT_SUCCEEDED; 16. }; 17. //+------------------------------------------------------------------+ 18. int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[]) 19. { 20. return rates_total; 21. }; 22. //+------------------------------------------------------------------+ 23. void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam) 24. { 25. static int p = 0; 26. MqlRates rate[1]; 27. 28. switch(id) 29. { 30. case CHARTEVENT_KEYDOWN: 31. switch ((int)lparam) 32. { 33. case def_KEY_DOWN: 34. p = (p < Bars(_Symbol, _Period) ? p + 1 : p); 35. break; 36. case def_KEY_UP: 37. p = (p > 0 ? p - 1 : p); 38. break; 39. default: 40. return; 41. } 42. Comment(StringFormat("Current bar analyzed: %d", p)); 43. CopyRates(_Symbol, _Period, p, rate.Size(), rate); 44. ObjectMove(0, def_NameObj, 0, rate[0].time, rate[0].close); 45. ObjectMove(0, def_NameObj, 0, rate[0].time, rate[0].close); 46. break; 47. } 48. ChartRedraw(); 49. }; 50. //+------------------------------------------------------------------+ 51. void OnDeinit(const int reason) 52. { 53. Comment(""); 54. ObjectDelete(0, def_NameObj); 55. ChartRedraw(); 56. }; 57. //+------------------------------------------------------------------+
コード04
ここでは、プログラミング初心者によく見られる、かなり初歩的ではあるものの、非常によくある間違いを取り上げます。目的は、チャート上に十字を描くことのようです。 しかし、このコードを実行すると、次のような結果になります。

アニメーション04
あれ、おかしいですね。 OnInitイベントハンドラでは、2つのオブジェクトを作成しています。1つは水平線、もう1つは垂直線です。 それなのに、なぜ垂直線しか表示されないのでしょうか。 さらに、 よく見ると、アニメーション04に表示された垂直線の色も、本来意図していたものとは異なっていることがわかります。 いったい何が起きているのでしょうか。
問題は、10行目と12行目でそれぞれオブジェクトを作成するように指定しているにもかかわらず、実際に作成されるのは10行目で宣言されたオブジェクトだけだという点です。 これは、12行目が実行されたときに、MetaTrader 5が、同じ名前のオブジェクトがすでにチャート上に存在すると判断するためです。 実際には、そのオブジェクトはまだチャート上には配置されていません。しかし、すでに実行キューに入っており、できるだけ早くチャート上に配置されるのを待っている状態なのです。
そのため、12行目で宣言されたオブジェクトの作成は拒否されます。 コードの大部分は比較的単純なので、多くの人はこうした細かな点を見落としてしまいます。さらに、ドキュメントを調べようともしないことがあります。 しかし、これは主に経験不足によるものです。プログラミング初心者は、エラーの原因が別の場所にあるのではないかと考えてしまいがちです。 同じ現象が、同一のチャート上で2つ以上のアプリケーションを同時に使用しようとしているユーザーに発生し、しかもオブジェクト名が重複していると、状況はさらに混乱します。 このようなケースは非常にまれですが、実際に遭遇する可能性はあります。
チャート上で複数のオブジェクトを扱う方法はいくつかあります。しかし、いずれの方法を使う場合でも、すでにチャート上に存在するオブジェクトと同じ名前で別のオブジェクトを作成しようとすると、発生したエラーへの対処が難しくなることがあります。 場合によっては、同じ名前を持つ異なるオブジェクトを利用できることもありますが、それらを同時に使用することはできません。 これは非常に特殊なケースです。正しい実装方法を学び、原因不明に見えるエラーを避けようとしている方々を混乱させないため、ここでは詳しい説明を省略します。
いずれにしても、ほぼあらゆる状況で使えるシンプルな解決策の一つは、オブジェクトの配列を作成し、チャート上に配置する各オブジェクトを、その配列の要素として管理することです。 これは現時点で考えられる最もシンプルで汎用性の高い解決策の一つだと思います。 そこで、先ほどのコード04を、次のように変更してみましょう。
01. //+------------------------------------------------------------------+ 02. #property copyright "Daniel Jose" 03. //+------------------------------------------------------------------+ 04. #define def_KEY_UP 38 05. #define def_KEY_DOWN 40 06. //+----------------+ 07. #define macro_NameObject "Demo" + (string)ObjectsTotal(0) 08. //+------------------------------------------------------------------+ 09. string gl_Objs[2]; 10. //+------------------------------------------------------------------+ 11. int OnInit() 12. { 13. ObjectCreate(0, gl_Objs[0] = macro_NameObject, OBJ_VLINE, 0, 0, 0); 14. ObjectSetInteger(0, gl_Objs[0], OBJPROP_COLOR, clrRoyalBlue); 15. ObjectCreate(0, gl_Objs[1] = macro_NameObject, OBJ_HLINE, 0, 0, 0); 16. ObjectSetInteger(0, gl_Objs[1], OBJPROP_COLOR, clrPurple); 17. 18. return INIT_SUCCEEDED; 19. }; 20. //+------------------------------------------------------------------+ 21. int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[]) 22. { 23. return rates_total; 24. }; 25. //+------------------------------------------------------------------+ 26. void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam) 27. { 28. static int p = 0; 29. MqlRates rate[1]; 30. 31. switch(id) 32. { 33. case CHARTEVENT_KEYDOWN: 34. switch ((int)lparam) 35. { 36. case def_KEY_DOWN: 37. p = (p < Bars(_Symbol, _Period) ? p + 1 : p); 38. break; 39. case def_KEY_UP: 40. p = (p > 0 ? p - 1 : p); 41. break; 42. default: 43. return; 44. } 45. Comment(StringFormat("Current bar analyzed: %d", p)); 46. CopyRates(_Symbol, _Period, p, rate.Size(), rate); 47. ObjectMove(0, gl_Objs[0], 0, rate[0].time, rate[0].close); 48. ObjectMove(0, gl_Objs[1], 0, rate[0].time, rate[0].close); 49. break; 50. } 51. ChartRedraw(); 52. }; 53. //+------------------------------------------------------------------+ 54. void OnDeinit(const int reason) 55. { 56. Comment(""); 57. for (uint c = 0; c < gl_Objs.Size(); c++) 58. ObjectDelete(0, gl_Objs[c]); 59. ChartRedraw(); 60. }; 61. //+------------------------------------------------------------------+
コード05
もちろん、ここではすべてをできるだけシンプルな方法で実装しています。 これまでの記事を読み、実際に試してきた方なら、現在の知識だけでも、もっと複雑な仕組みを構築できることがおわかりでしょう。 しかし、私たちが本当に注目したいのは、最終的にどのような結果が得られるかという点です。 以下のアニメーションをご覧ください。

アニメーション05
アニメーション04で示した結果とは、まったく異なることがわかります。 ここで注目していただきたいのは、コード05とコード04を比較すると、コード04で発生した問題を解決するために、実際にはコードをほとんど変更していないという点です。 最も目立つ変更は、コード05の09行目にある宣言です。 しかし、それだけではすべての問題を解決できないため、もう一つ変更を加えています。 それが、コード05の07行目です。
この例では、オブジェクト名の定義をマクロに変更しています。 このマクロで重要な役割を果たしているのが、ObjectsTotal関数です。 MQL5標準ライブラリに用意されているこの関数は、チャート上に存在するオブジェクトの数を取得します。また、チャートに配置されるのを待っているオブジェクトも、その対象に含まれます。 この関数を利用すれば、チャート上にいくつオブジェクトを配置しても、異なる作者が作成した複数のプログラムを使用する場合であっても、それぞれのオブジェクトに一意の名前を付けることができます。 オブジェクトを削除してから新しいオブジェクトを作成する場合も、名前がよく似ていたとしても、マクロ内でObjectsTotal関数を使用することで、オブジェクトごとに異なる名前を付けられます。
非常にシンプルですが、効果的な方法です。 それ以外の変更点は、これまでの記事を学んできた方なら容易に理解できるはずなので、詳しい説明は省略します。 ただし、これだけシンプルな仕組みであっても、忘れてはならないことがあります。マクロによって毎回一意の名前が生成されるため、作成したオブジェクトの名前をどこかに保存しておく必要があるのです。 この例では、09行目の配列に名前を格納しています。 そうしておかなければ、チャート上にすでに配置されているオブジェクトを特定できません。
この記事を終える前に、今後の記事でも頻繁に使用することになる、もう一つの重要な点について説明したいと思います。 ただし、トピックを明確に分けるため、次のセクションに進みましょう。
短縮名
ここまでに紹介したコードは、実質的に一つのまとまったコードを構成するものであり、基本的には順番に使用することを想定しています。 しかし、実際の開発環境では、いつもこのように単純に進められるとは限りません。 そのため、プログラマーとしては、いくつかの点に注意し、必要な操作を適切に行う方法を知っておく必要があります。 その一つが、チャート上に表示されているインジケーターにアクセスする方法です。 なぜこれが必要なのかについては、後ほど、もう少し複雑な機能を開発する段階で説明します。
ユーザーの立場からすれば、こう思うかもしれません。「インジケーターにアクセスする方法なら、もちろん知っている。CTRL + Iを押せば、チャート上のインジケーターを一覧できるウィンドウが開く。 チャートにインジケーターを追加したいときも、ナビゲータで目的のインジケーターを探せばよい。 後はチャートにドラッグするだけで、MetaTrader 5がすべて処理してくれる。 簡単だ。」
確かに、これはユーザーの視点から見た場合の話です。 しかし、プログラマーは異なる方法で作業するため、こうした仕組みを理解するには、別の視点が必要になります。 この場合、ユーザーインターフェースはほとんど役に立ちません。それどころか、後ほど説明するいくつかの仕組みについて、理解を妨げることさえあります。
そこで、皆さんにもプログラマーの視点で考えることに慣れていただくため、MetaTrader 5で何ができ、何ができないのかについて、もう少し掘り下げてみましょう。 また、MQL5のコードに記述した内容を、MetaTrader 5がどのように認識するのかも理解する必要があります。 そもそもMQL5は、MetaTrader 5に対して、実行したい処理を指示するための言語なのです。
ユーザーは、MetaTrader 5のチャート上で動作しているアプリケーションを画面上で確認します。一方、プログラマーは、現在どのアプリケーションが動作しているのかをMetaTrader 5に問い合わせることができます。 これにより、実行中のアプリケーションを検出し、それに応じた追加・削除の処理を自動化できます。 多くの場合、ここで問題になるのは、チャート上に表示されているインジケーターを特定することです。 一般的な認識とは異なり、インジケーターには、コード内で内部的に使用される名前があり、その名前が実行ファイルの名前とまったく異なる場合があるためです。
また、コード内で使用するこの名前は、ユーザーが変更できないようにする必要があります。 厳密に言えば、プログラマーがユーザーによる変更を許可することも可能です。 しかし、今の段階では詳しく説明しにくい問題を避けるため、そのような設計はお勧めしません。
つまり、インジケーターを扱う際には、そのインジケーターに名前を設定できるということです。 では、具体的にはどうすればよいのでしょうか。 そのためには、MQL5標準ライブラリのIndicatorSetStringを使用します。 一見すると些細なこと、あるいは必要のないことのように思えるかもしれません。しかし、インジケーターに短縮名を設定しておくと、さまざまな処理を大幅に簡略化できます。 さらに、この関数の用途はインジケーター名の設定だけではありません。 他にも、より重要な役割があります。 ただし、その説明に入る前に、まずはインジケーターの内部名(短縮名)をどのように設定するのかを確認しましょう。 基本的には、次のように記述します。
01. //+------------------------------------------------------------------+ 02. #property copyright "Daniel Jose" 03. //+------------------------------------------------------------------+ 04. int OnInit() 05. { 06. IndicatorSetString(INDICATOR_SHORTNAME, "Version Demo"); 07. 08. return INIT_SUCCEEDED; 09. }; 10. //+------------------------------------------------------------------+ 11. int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[]) 12. { 13. return rates_total; 14. }; 15. //+------------------------------------------------------------------+
コード06
コード06の実行ファイルに、ユーザーがどのような名前を付けるかは問題ではありません。 私たちが注目すべきなのは、06行目で指定されている名前です。 インジケーター名には、自由に名前を指定できます。 ただし、インジケーターの目的を簡潔に表す、適切な名前を選ぶことをお勧めします。 これを「短縮名」と呼びます。
では、なぜわざわざこのような名前を設定する必要があるのでしょうか。 どのようなメリットがあるのでしょうか。 この段階で、その理由を明確に説明するのは少し難しいところです。 インジケーターに短縮名を設定する必要性を本当に理解してもらうには、今の段階では説明をわかりやすくするどころか、かえって話を複雑にしてしまう内容まで紹介しなければなりません。 それでも、これから実装するインジケーターには、常に短く、意味のわかる名前を付けるようにしてください。そうしておけば、後になってチャート上のインジケーターを操作する必要が生じたときに、改めて名前を考えずに済みます。 いずれにしても、たった1行のコードを追加するだけで、将来起こり得る多くの問題を防ぐことができます。
まとめ
今回の記事では、オブジェクトを簡単な方法で制御する方法をいくつか紹介しました。 また、カスタムアプリケーションを利用することで、同じチャート上に複数のオブジェクトを配置できることも確認しました。 さらに、これから実装するインジケーターには、あらかじめ短縮名を設定しておくことが重要だと学び始めました。
一見すると、これらの概念にはあまり関連性がないように思えるかもしれません。しかし、それぞれを個別に捉えるのではなく、一連の仕組みとして理解することが大切です。 いずれにせよ、今回の記事の目的は、さまざまな問題を解決するための簡単な方法を紹介することでした。学習を始めたばかりの頃は、単純で、少し注意すれば防げるようなミスを何度も犯してしまい、それによってすっかり自信を失ってしまうことがあるからです。 その結果、プログラミングは一部の限られた人にしかできないものだと思い込んでしまいがちです。しかし、実際には誰もがプログラミングを学び、自分のアイデアを形にできるはずです。
念のため、練習や学習に活用できるよう、今回紹介したコードを添付しておきます。実際に試してみることで、何かを学ぶ過程で誰もが経験するようなミスを繰り返さずに済むようになるでしょう。
MetaQuotes Ltdによりポルトガル語から翻訳されました。
元の記事: https://www.mql5.com/pt/articles/15987
警告: これらの資料についてのすべての権利はMetaQuotes Ltd.が保有しています。これらの資料の全部または一部の複製や再プリントは禁じられています。
この記事はサイトのユーザーによって執筆されたものであり、著者の個人的な見解を反映しています。MetaQuotes Ltdは、提示された情報の正確性や、記載されているソリューション、戦略、または推奨事項の使用によって生じたいかなる結果についても責任を負いません。
取引におけるニューラルネットワーク:高精度分類のための効果的な特徴抽出(オブジェクトの実装)
市場シミュレーション(第22回):SQL入門(V)
取引におけるニューラルネットワーク:クロスドメイン時系列予測フレームワーク「TimeFound」
初級から中級まで:オブジェクト(I)
- 無料取引アプリ
- 8千を超えるシグナルをコピー
- 金融ニュースで金融マーケットを探索