初級から中級まで:オブジェクト(I)
はじめに
前回の「初級から中級まで:インジケーター(V)」では、MetaTrader 5のチャート表示モードに合わせて、インジケーターを簡単に調整する方法を解説しました。ただし、調整できるのは一部に限られる場合もあります。 また、ローソク足、バーチャート、ラインチャートという3つの表示モードの使い分けについては説明しませんでした。これについて解説するには、本連載でまだ十分に説明していない知識が必要になるためです。
一定の範囲内であれば、ローソク足モードとバーチャートモードには、同じインジケーターをそのまま対応させることができ、特に大きな問題はありません。 一方、ラインチャートモードに対応させるには、別のインジケーターを作成し、チャート上に手動で追加する必要があります。 少々不便ではありますが、いずれはこの制約を回避する方法も紹介する予定です。
必要な知識を本連載で順を追って学んでいく間は、こうした制約にひとまず対処しながら先に進めばよいことを、理解していただけたでしょう。すべての実装の詳細をその都度説明しなくても、現段階でどのように作業を進めればよいか、分かっていただけたのではないでしょうか。
とはいえ、現時点ですでに取り上げられるテーマもあります。ただし、その面白さや意義を感じるかどうかは、読者によって異なるかもしれません。 今回から扱うテーマについて、その奥にある意味まで理解できる読者は、必ずしも多くないだろうと思うからです。 一方で、今回の記事と今後の記事で紹介する内容に大きな興味を持ち、実際に価値ある目的に役立てられると感じる読者もいるでしょう。
それでは、このテーマを掘り下げるための出発点となる事柄から見ていきましょう。ここでは詳しく解説しませんが、まずはその基本となる部分を押さえておきます。
特に注意すべき点
チャート上に表示したいデータは、トレーダーによってさまざまです。 その有用性も、状況によって異なります。 そうしたデータの一つに、ローソク足やバーの本数を数えるカウンターがあります。 これをまったく必要としない人もいれば、市場をどのように分析・評価しているかについて、ほかのトレーダーと意見を交換する際の、手軽で効率的なコミュニケーション手段として活用する人もいます。
ここまでは、ユーザーの立場から、チャート上に何を表示するかという話でした。 しかし、プログラマーの立場から見ると、事情は少し異なります。
プログラマーであれば、チャート上に情報を表示する方法を工夫し、さまざまな見せ方を実現できます。 ただし、今回の簡単な例では、表示方法にそれほど多くの選択肢はありません。目的はあくまでチャート上にテキストを表示することであり、それ以上のことをするわけではないからです。 重要なのはテキストそのものではなく、それを表の形でどう配置し、定期的に更新するかです。
趣味でプログラミングをしている人の多くは、プログラミングに真剣に取り組み、継続的に学んでいる人ほど、問題を先まで見通せていません。 ただし、誤解しないでください。特定の目的のために、ときどきプログラムを作成する人が、自分のしていることを理解していないと言いたいわけではありません。 問題なのは、プログラムの最適化が不十分な場合に、MetaTrader 5上でどのような問題が起こり得るかを、必ずしも把握できていないことです。最適化されていないプログラムをチャート上で多数動かすと、プラットフォームのパフォーマンスが徐々に低下し、最終的には正常に使用できなくなるおそれがあります。
チャート上にテキストを表示するだけの単純なローソク足カウンターであっても、最適化が不十分であれば、必要以上に多くのリソースを消費する可能性があります。 本連載では、あくまで学習を目的としながらも、MetaTrader 5のパフォーマンスに大きな悪影響を与えないコードを紹介するよう努めています。 それでも、意図せず問題が生じる可能性はあります。記事で紹介するコードを実際に使用する際には、不要なトラブルを避けるためにも、十分に注意してください。
それでは本題に戻りましょう。ローソク足カウンターを作るとしたら、どのような方法が考えられるでしょうか。 目盛り上に数値を表示するようなアプリケーションを思い浮かべるかもしれません。 しかし、まだ具体的な答えは出ていません。 では、どのような仕組みを採用すればよいのでしょうか。 スクリプトとインジケーターの作成方法については、すでに説明しました。そのため、選択肢はおそらくこの2つのいずれかになるでしょう。 ただし、スクリプトにはいくつかの制約があるため、最終的にはインジケーターを使うことになります。
では、スクリプトにはどのような制限があるのでしょうか。 すでに試したことがある方なら、スクリプトをチャートに追加しても、時間軸を変更すると削除されてしまうことに気づいたはずです。 スクリプト内でループを使って削除を防ごうとしても、同じことが起こります。 そのため、時間軸を変更するたびに、スクリプトをチャート上に再配置する仕組みが必要になります。 こうしたスクリプトの実行上の問題を回避する方法もあります。 しかし、その方法ではサービスを利用する必要があり、サービスとチャートを連携させる仕組みもかなり複雑になります。そのため、今回の目的には適していません。
この連携の仕組みがどれほど複雑なのか興味がある方は、レプリケーション/モデリングシステムの構築に関する私の記事をご覧ください。 ただし、それらの記事は、MQL5プログラミングの基礎を十分に理解している読者を対象としています。今回はまだ学習を始めたばかりの段階ですので、サービスを利用してチャートを操作する方法について、ここで詳しく説明することは控えます。
そうなると、残る選択肢はインジケーターです。 もっとも、インジケーターも、あらゆる種類の処理に適しているわけではありません。 MetaTrader 5では、インジケーターはチャート上で動作する単一のアプリケーションのように、一体の処理ブロックとして実行されます。 これは、特定の値を算出するための処理を高速化するためです。 こうして得られる値は、インジケーターをチャートに適用する際に選択できる入力パラメータ群の一部となります。 この点については、以前の記事ですでに説明しました。
とはいえ、バーのカウンターが処理をおこなうのは、チャート上に新しいバーが出現したときだけです。そのため、タイマーを使って処理のタイミングを同期させればよいのではないか、と考えるかもしれません。 しかし、ここに問題があります。 インジケーターでタイマーを使用することは、基本的に推奨されません。 タイマーの処理に不具合が発生すると、ほかのインジケーターにも影響が及ぶ可能性があり、場合によってはMetaTrader 5が一時的に応答しなくなることさえあるからです。そのわずかな停止時間が、重大な結果につながる場合もあります。
もちろん、チャート上に新しいバーが出現したことを検知する仕組みを実現する方法は、他にもあります。 そして、新しいローソク足が出現したタイミングを把握し、その本数を数えてからテキストを表示するという目的であれば、インジケーターを使うのが最も簡単で実用的な方法です。 それでは、ローソク足のカウントを実装するために、まずはその第一歩から始めましょう。 以下のコードを出発点とします。
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 datetime &Time[], const double &Open[], const double &High[], const double &Low[], const double &Close[], const long &TickVolume[], const long &Volume[], const int &Spread[]) 10. { 11. return rates_total; 12. }; 13. //+------------------------------------------------------------------+
コード01
ここで、重要な点に注意してください。チャートに表示されている銘柄の価格が変動するたびに、MetaTrader 5からすべてのインジケーターにCalculateイベントが送られます。 MetaTrader 5では、Calculateイベントは一定期間のアクティビティに基づいてトリガーされるわけではありません。 約10分が経過して新しいバーが形成されるタイミングになっても、価格が実際に変動しなければCalculateイベントは発生しません。 つまり、価格に変動がない限り、Calculateイベントは発生しないのです。
ボラティリティの高い銘柄では、Calculateイベントがミリ秒単位という非常に短い間隔で発生することもあります。そのため、このイベントの処理は可能な限り高速におこなう必要があります。 言い換えれば、不必要な計算や情報の検索に時間を費やしてはいけません。そうした処理は、イベントの処理速度を大幅に低下させる原因となるからです。
その影響は、同じチャートに関連付けられているほかのアプリケーションにも及びます。 しかも、ここではまだエキスパートアドバイザー(EA)についてすら触れていません。 そうです。最適化が不十分なインジケーターは、MetaTrader 5のパフォーマンスを著しく損なう可能性があるのです。 ただし、もうすぐ状況は改善します。
まず理解しておいてほしいのは、処理速度は情報の検索方法に左右されるということです。 コード01を見てください。ここには、各バーの時刻を取得するための配列が用意されています。 ただし、Calculateイベントでは、気配値が実際に変化した正確なタイミングを把握できません。 ここで把握できるのは、そのバーがいつ形成されたかという情報です。 また、チャートの時間軸をもとに、そのバーが確定するまでにどれくらい時間が必要かを見積もることもできます この情報は、後ほど取り上げる別の種類のアプリケーションを作成する際に重要になります。
しかし、今回の目的はローソク足カウンターを作成すること、あるいは少なくとも、チャート上に新しいバーが出現したかどうかを検知することです。 したがって、9行目に示されているTime配列の値が変化したことを確認できれば、新しいバーがチャート上に出現したと判断できます。
これとは別に、私としては、prev_calculatedとrates_totalの値を確認する方法のほうが、はるかに簡単だと考えています。 なぜでしょうか。 Time配列の値はdatetime型です。 一方、prev_calculatedとrates_totalはMetaTrader 5から渡される整数型の値です。そのため、これらの値を比較するほうが、処理を簡単かつ高速におこなえます。 また、今回検知したいのは、チャート上に新しいバーが出現したことによる変化です。この点を踏まえれば、確認すべき情報も明確になり、処理をより簡潔にできます。
さて、まもなく説明する別の問題についてはさておき、ここまでの話で、私が何を伝えようとしているのかおわかりでしょうか。 重要なのは、何かをどのように実装するかということではありません。 目標を達成するために、どのような情報を利用できるのかを理解することです。 このことを確かめるために、次に示すように、コード01をコード02へと変更してみましょう。
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. //+------------------------------------------------------------------+
コード02
ここまでで、重要なのは状況を正しく理解し、手元にある情報を適切に活用することだとおわかりいただけたと思います。 何かを実現する方法に、唯一の「正解」や「不正解」があるわけではありません。同じ目標を達成し、同じ結果を得るためにも、より簡単な方法があるということです。 では、チャート上に新しいバーが出現したかどうかは、どのように判断すればよいのでしょうか。 これは最も簡単な部分です。 それでは、以下に示すようにコード02を修正してみましょう。
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. if ((prev_calculated > 0) && (prev_calculated != rates_total)) 12. Print("New Bar..."); 13. 14. return rates_total; 15. }; 16. //+------------------------------------------------------------------+
コード03
コード03は、先ほど説明した内容と、今回実証したかったことをそのまま示しています。 つまり、現在のバーがいつ終了し、新しいバーがいつ出現したかを、簡単な方法で検知できるということです。 実際に確認するために、以下のアニメーションをご覧ください。

アニメーション01
コード03もアニメーションも非常にシンプルなので、詳しい説明は省き、いよいよこの記事の最も興味深い部分に進みましょう。 ただし、その前に、新しいテーマについて説明する必要があります。
オブジェクトとチャート
一般的な認識とは異なり、チャート上に表示されるものは、すべてオブジェクトです。 もちろん、何を作成するかによって、オブジェクトの用途はまったく異なります。しかし、用途が違っても、オブジェクトであることに変わりはありません。 もう一つ、あらかじめ断っておきます。ここでいう「オブジェクト」は、オブジェクト指向プログラミング(OOP)とは関係ありません。 ここから少し複雑な話に入ります。 オブジェクトには2種類あります。より正確に言えば、チャート上にオブジェクトを配置する方法には2つあります。 MetaTrader 5のチャートは、このような仕組みになっています。
1つ目は、座標系がチャートに連動するオブジェクトです。2つ目は、画面座標を使って配置するオブジェクトです。 この2種類には、画面上での表示方法だけでなく、操作方法にも違いがあります。 一方で、種類によっては互いに関連する部分もあります。 目的に合ったオブジェクトを選べるようになれば、実装が大幅に簡単になり、コードの実行速度も向上します。
さて、こうした仕組みを扱うのは今回が初めてです。そこで、ローソク足カウンターの話はいったん脇に置き、まずはオブジェクトの扱い方を理解することから始めましょう。
最初に、MQL5のドキュメントにある「オブジェクト型」のセクションを参照してください。 そこには、さまざまなオブジェクトの一覧が表示されています。 一覧を見て、種類が少なく、何かが足りないのではないかと思うかもしれません。しかし、そうではありません。 MQL5にはチャート上で利用できるオブジェクトが十分すぎるほど用意されていると思います。 実際、これらすべてを使う必要はありません。 しかし、すでに実装されていて利用できるのであれば、それらを有効に活用しない手はありません。 しかも、その中には工夫次第で非常に便利な使い方ができるものも多く、開発にかかる時間を大幅に節約できます。
これを説明するために、チャート上のオブジェクトをどのように扱えるかを示す簡単なコードを書いてみましょう。 以下が、そのコードです。
01. //+------------------------------------------------------------------+ 02. #property copyright "Daniel Jose" 03. //+------------------------------------------------------------------+ 04. #define def_NameChannel "Demo" 05. //+------------------------------------------------------------------+ 06. int OnInit() 07. { 08. ObjectCreate(0, def_NameChannel, OBJ_REGRESSION, 0, 0, 0); 09. 10. return INIT_SUCCEEDED; 11. }; 12. //+------------------------------------------------------------------+ 13. 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[]) 14. { 15. const int n = 20, 16. a = 2; 17. 18. if ((prev_calculated > (n + a)) && (prev_calculated != rates_total)) 19. { 20. ObjectMove(0, def_NameChannel, 0, Time[rates_total - (n + a)], Close[rates_total - (n + a)]); 21. ObjectMove(0, def_NameChannel, 1, Time[rates_total - a], Close[rates_total - a]); 22. } 23. 24. return rates_total; 25. }; 26. //+------------------------------------------------------------------+ 27. void OnDeinit(const int reason) 28. { 29. ObjectDelete(0, def_NameChannel); 30. ChartRedraw(); 31. }; 32. //+------------------------------------------------------------------+
コード04
コード04では、インジケーター内で最初に作成するオブジェクトがどのようなものになるかを示しています。 これを見て、「これまでの記事で学んだ構造が含まれていないのだから、これはインジケーターではないのでは」と思うかもしれません。しかし、コード04は間違いなくインジケーターです。 MetaTrader 5が発生させるCalculateイベントを処理するOnCalculate関数が含まれていることを確認すれば、それがわかります。
ただし、コード04のインジケーターは、一般的なインジケーターとは少し異なります。 より明確な目的を持ち、特定の処理を実行するために作られているのです。 コード04の仕組みを説明する前に、まずは以下のアニメーションで、チャート上でどのように動作するのかを見てみましょう。

アニメーション02
これは、アニメーションで確認する必要があります。静止画だけでは、この後に見る動作を十分に理解できないからです。 さて、コードにあれこれ突っ込みを入れる前に、まずはコード04の仕組みを理解しておきましょう。 04行目には、これから作成するオブジェクトに名前を付けるための定義があります。 そうです。チャート上に配置するオブジェクトには、名前を付ける必要があります。 まずは、この点に今から慣れておきましょう。
08行目では、単にオブジェクトを作成しようとしています。 ここで作成するのは、線形回帰チャネルです。 ただし、この時点ではチャネルをどこに表示するかがまだ決まっていないため、それ以上の処理はおこないません。 その後、インジケーターは、MetaTrader 5から特定の条件を満たすCalculateイベントが送られてくるのを待ちます。 待機する条件は18行目で定義されています。 コード04の18行目は、コード03の11行目とよく似ています。どちらも、新しいバーが出現するのを待つという同じ目的を持っています。 新しいバーが出現するまでは、08行目で作成したオブジェクトはチャート上に表示されません。
新しいバーが出現すると、20行目と21行目の処理によって、線形回帰チャネルが描画されます。 どのように描画するのかについては、 少し後で詳しく説明します。 ここでは、特に難しい仕組みではないということだけ覚えておいてください。 20行目ではチャネルの描画開始位置を、21行目では描画終了位置を指定しています。 こうすることで、直近20本のバーを対象に線形回帰を行い、現在のバーを計算対象から除外したチャネルが得られます。
また、アプリケーションでオブジェクトを作成してチャートに追加する場合は、適切な実装方法として、アプリケーションの終了時にそのオブジェクトをチャートから削除するようにしておくべきです。 そのために、29行目ではOnDeinitイベントハンドラ内でオブジェクトを削除しています。
さて、ここからが最も興味深いところです。 線形回帰の分析方法や、その結果の表示方法を変更したら、どうなるでしょうか。 インジケーターの動作はどのように変わるのでしょうか。 それを確認するために、次に示すようにコード04を大きく変更してみましょう。
01. //+------------------------------------------------------------------+ 02. #property copyright "Daniel Jose" 03. //+------------------------------------------------------------------+ 04. #define def_NameChannel "Demo" 05. //+------------------------------------------------------------------+ 06. int OnInit() 07. { 08. ObjectCreate(0, def_NameChannel, OBJ_REGRESSION, 0, 0, 0); 09. 10. return INIT_SUCCEEDED; 11. }; 12. //+------------------------------------------------------------------+ 13. 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[]) 14. { 15. const int n = 20, 16. a = 1; 17. 18. if (prev_calculated > (n + a)) 19. { 20. ObjectMove(0, def_NameChannel, 0, Time[rates_total - (n + a)], Close[rates_total - (n + a)]); 21. ObjectMove(0, def_NameChannel, 1, Time[rates_total - a], Close[rates_total - a]); 22. } 23. 24. return rates_total; 25. }; 26. //+------------------------------------------------------------------+ 27. void OnDeinit(const int reason) 28. { 29. ObjectDelete(0, def_NameChannel); 30. ChartRedraw(); 31. }; 32. //+------------------------------------------------------------------+
コード05
コード04と比べて、コード05ではOnCalculateイベントハンドラだけを変更している点に注目してください。 それだけの変更ですが、その結果は非常に興味深いものになっています。 以下のアニメーションで確認してみましょう。

アニメーション03
アニメーション03で見られる動きは、実に驚くべきものです。特に、線形回帰チャネルがリアルタイムで生成されていることを考えると、その驚きもいっそう大きいでしょう。 つまり、価格が更新されるたびに、線形回帰チャネルも変化するのです。 設定次第では、指数移動平均(EMA)よりも反応の速いトレンドインジケーターを作ることもできます。 ただし、自己調整型の線形回帰チャネルを実現する方法を探し求めているトレーダーの皆さんに、その可能性を追求するのはお任せしましょう。
もしかすると、皆さんはこのインジケーターに何の意味があるのかと疑問に思われているかもしれません。正直なところ、私にもわかりません。 非常に興味深く、試してみるだけでも楽しいことは確かです。 しかし、もっと具体的なアイデアを持つ人なら、これを別の用途に使う方法を思いついているかもしれません。というのも、作成するオブジェクトの種類を変更し、そのアンカーポイントを指定するだけでよいからです。 いったんオブジェクトをチャート上に配置すれば、あらかじめ定義したルールが自動的に適用され、必要に応じてオブジェクトの位置などが更新されます。手動で調整することを覚えておく必要はありません。
では、もうひとつ例を見てみましょう。 この場合は、別の種類のオブジェクトを使用します。 これから示す内容はすべて、あくまで教育目的に限ったものであることをご留意ください。 以下が、そのコードです。
01. //+------------------------------------------------------------------+ 02. #property copyright "Daniel Jose" 03. //+------------------------------------------------------------------+ 04. #define def_NameChannel "Demo" 05. //+------------------------------------------------------------------+ 06. int OnInit() 07. { 08. ObjectCreate(0, def_NameChannel, OBJ_TREND, 0, 0, 0); 09. ObjectSetInteger(0, def_NameChannel, OBJPROP_RAY_RIGHT, true); 10. 11. return INIT_SUCCEEDED; 12. }; 13. //+------------------------------------------------------------------+ 14. 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[]) 15. { 16. const int n = 60; 17. double p = DBL_MIN; 18. int t = 0; 19. 20. for (int c = rates_total - (n + 1); c < rates_total; c++) 21. p = (High[c] > p ? High[t = c] : p); 22. ObjectMove(0, def_NameChannel, 0, Time[t], p); 23. p = DBL_MIN; 24. for (int c = t + 1; c < rates_total; c++) 25. p = ((High[c] > p) && (Open[c] < Close[c]) ? High[t = c] : p); 26. ObjectMove(0, def_NameChannel, 1, Time[t], p); 27. 28. return rates_total; 29. }; 30. //+------------------------------------------------------------------+ 31. void OnDeinit(const int reason) 32. { 33. ObjectDelete(0, def_NameChannel); 34. ChartRedraw(); 35. }; 36. //+------------------------------------------------------------------+
コード06
コード06を実行すると、以下のような結果が得られます。

アニメーション04
表示された結果を見て、あまり期待しすぎないでください。これはあくまでデモンストレーションにすぎません。 決して、完成したアプリケーションとして捉えてはいけません。 とはいえ、アニメーション04では、コード06に組み込まれた基準に従って、下降トレンドラインが描画される様子を確認できます。 「でも、どうすればそんなことができるのでしょうか」簡単に言えば、私が指定した基準に従って下降トレンドラインを探すよう、コードに指示しただけです。
それでは、何が起きているのかを詳しく見ていきましょう。 コード03と同様に、08行目では、チャート上に配置するオブジェクトの種類を指定しています。 続く09行目では、そのオブジェクトのプロパティの1つを変更しています。 このプロパティをコード06のように設定していなければ、結果は下の図のようになります。

図01
図01を見ると、トレンドラインがどの点からどの点まで引かれているかがわかります。 今回は下降トレンドラインを探しています。 上昇トレンドラインを定義するには、別の基準を使用します。 ただし、ここでの目的は、トレンドラインを自動的に検出して描画する方法そのものを説明することではありません。そのため、ここで使用している基準や手法が実際に機能することだけを押さえておきましょう。 では、インジケーターはトレンドラインを描画するための2つの点を、どのように見つけたのでしょうか。 まるで魔法のように思えるかもしれません。
もちろん、魔法ではありません。 基本的な基準を適用しているだけです。 Calculateイベントハンドラに注目してください。 ここには2つのループがあります。1つ目のループでは、トレンドラインの始点を探します。2つ目のループでは、終点を探します。 始点が見つかったら、22行目でオブジェクトの最初のアンカーポイントの位置を指定します。 続いて、終点が見つかった時点で、26行目により2つ目のアンカーポイントの位置を指定します。 後はMetaTrader 5が処理してくれます。
ただし、このインジケーターを称賛に値するような優れたものだと決して考えないでください。 ここで作成したのは、図01とアニメーション04に示したものを検出するためのものにすぎません。 先ほど示した例のように、他のケースでも同じようにうまく機能するとは限りません。
「なるほど、わかりました。 でも、ほかのプロパティも変更すれば、もっと自分好みのオブジェクトを作れるのではありませんか?」もちろん可能です。 もう1つ付け加えておきましょう。チャート上で直接プロパティを変更する場合とは異なり、オブジェクトによっては、コードからしか変更できないプロパティもあります。
それでは、プロパティを変更する簡単な例を見てみましょう。 以下をご覧ください。
01. //+------------------------------------------------------------------+ 02. #property copyright "Daniel Jose" 03. //+------------------------------------------------------------------+ 04. #define def_NameChannel "Demo" 05. //+------------------------------------------------------------------+ 06. int OnInit() 07. { 08. ObjectCreate(0, def_NameChannel, OBJ_TREND, 0, 0, 0); 09. ObjectSetInteger(0, def_NameChannel, OBJPROP_COLOR, clrLawnGreen); 10. ObjectSetInteger(0, def_NameChannel, OBJPROP_WIDTH, 4); 11. 12. return INIT_SUCCEEDED; 13. }; 14. //+------------------------------------------------------------------+ . . .
コード07
この場合、コード07では、コード06から変更が必要な部分だけを示しています。 ここで注目してほしいのは、09行目でオブジェクトの色を、10行目で線の太さを指定している点です。 このコードを実行すると、下の図のような結果が得られます。

図02
それでは、読者の皆さん、よく聞いてください。 現在のコードでは、下の図に示すように、インジケーターの情報を表示するためのキーボードショートカットから設定画面を開いても、オブジェクトの色や線の太さを変更することはできません。

図03
これらのオブジェクトのプロパティを変更するには、別のダイアログボックスを使用する必要があります。 つまり、下図に示すダイアログボックスを使わなければなりません。

図04
少し面倒に感じるかもしれませんが、これがMetaTrader 5での典型的な使い方です。 とはいえ、このような状況を回避する方法もあります。インジケーター側で設定した内容に応じて、ユーザーがオブジェクトのプロパティを変更できるようにすることも可能です。 ただし、オブジェクトに触れるのはこれが初めてに過ぎないので、焦らないようにしましょう。 今回の内容だけでも、いろいろと試してみる余地は十分にあるはずです。そうして経験を積めば、次の記事に進む準備も整うでしょう。
まとめ
今回の記事では、チャート上のオブジェクトを直接操作する方法について、その仕組みを示すために用意したコードを使いながら、基本的なところから見てきました。 最初の説明はやや表面的なものだったかもしれません。しかし、目的を落ち着いて考え、作業をきちんと計画すれば、非常に興味深いことを実現できるとわかっていただけたと思います。 そのためには、実際に手を動かしながら、MQL5が提供する各オブジェクトの仕組みや、MetaTrader 5での使い方を学ぶ必要があります。 利用できる機能を十分に理解していなければ、自分のアイデアを実現することはできません。
いつものように、この連載記事の完全なソースコードは添付ファイルに含まれています。 ただし、特に今回の記事で紹介したコードについては、学習と、記事で解説した内容を実践することだけを目的としている点に注意してください。 ここで紹介したコードを、完成したアプリケーションとして、あるいは学習とは無関係な目的で使用しないでください。資金面で大きな損失につながるおそれがあります。
MetaQuotes Ltdによりポルトガル語から翻訳されました。
元の記事: https://www.mql5.com/pt/articles/15975
警告: これらの資料についてのすべての権利はMetaQuotes Ltd.が保有しています。これらの資料の全部または一部の複製や再プリントは禁じられています。
この記事はサイトのユーザーによって執筆されたものであり、著者の個人的な見解を反映しています。MetaQuotes Ltdは、提示された情報の正確性や、記載されているソリューション、戦略、または推奨事項の使用によって生じたいかなる結果についても責任を負いません。
市場シミュレーション(第22回):SQL入門(V)
取引におけるニューラルネットワーク:データ固有の依存性なしに時系列を一般化する(結論)
エラー 146 (「トレードコンテキスト ビジー」) と、その対処方法
市場シミュレーション(第21回):SQL入門(IV)
- 無料取引アプリ
- 8千を超えるシグナルをコピー
- 金融ニュースで金融マーケットを探索