English Русский Español Português
preview
初級から中級まで:インジケーター(I)

初級から中級まで:インジケーター(I)

MetaTrader 5 |
26 0
CODE X
CODE X

前回の「初級から中級まで:イベント(II)」では、MetaTrader 5プラットフォームの異なるセッション間で、情報を半永久的に保存する方法について解説しました。中には、そのような内容は不要だと感じる方もいらっしゃるかもしれません。しかし、いざセッション間で情報を保持する必要が生じたとき、きっとこう思われるはずです。「あのときのアドバイスに感謝します。本当にありがとう」と。いずれにせよ、

私たちの探求はまだ始まったばかりです。すでに小規模なプログラムを作成できるだけの基本概念を身につけ、イベント駆動型プログラミングの仕組みも確認しました。そこで次のステップとして、多くの方が自分で実装しようと試みるものの、実際には既に設計されており、実装するのではなく必要に応じて利用すればよい基本概念について理解していきます。

ここで指しているのは、MetaTrader 5のほぼすべてのアプリケーションにデフォルトで備わっている「タブ」のことです。アプリケーションによって数は異なりますが、これらのタブをどのように活用するかをプログラマとして理解すれば、他の開発者が提供するインターフェースに対して、より注意深く、より高い基準を持つユーザーになることができます。

それでは、本日から新しいテーマを始めていきましょう。


デフォルトタブ

MQL5で完全に記述されたコードには、基本的な設定ウィンドウを持たせることができます。このウィンドウはすべてのアプリケーションに必ず表示されるわけではありません。スクリプトやサービスのようなアプリケーションでは、表示される場合とされない場合があります。しかし、インジケーターやエキスパートアドバイザー(EA)では、必ずいくつかの要素、つまりデフォルトタブが表示されます。これらのタブはプログラマが作成するものではなく、MetaTrader 5の環境内で定義されています。つまり、インジケーターやEAの設定に関しては、標準的な構成がすでに用意されているということです。

しかしながら、さまざまな理由から、多くのプログラマが不要な設定要素を追加してしまうことがあります。本来であれば、それらの要素はデフォルトでユーザーに表示されるタブ内に収めることが可能です。ここでお聞きしたいのは、デフォルトタブに定義されている要素へのアクセスのしかたをご存じかという点です。これはユーザーとしてではなく、プログラマとしての問いです。

ここでいう「デフォルトタブ」とは、以下の図(図01~04)に示されているものを指します。

図01

図02

図03

図04

これらはインジケーターのデフォルトタブです。現在はインジケーターにおけるイベントの仕組みを学習している段階です。インジケーターの方が比較的シンプルであるため、より複雑な内容へ進む前に、できる限り多くのことをここで学ぶべきです。このデフォルトタブの仕組みは非常に興味深く、よく設計されています。多様なケースに適合するようになっており、実際のところ、ほとんどの場合は追加の入力パラメータを実装する必要はありません。それにもかかわらず、多くのプログラマが追加の入力パラメータを作成してしまうのはなぜでしょうか。その理由は、MetaTrader 5が提供するデフォルトの構成では要件を満たせない場合があるということです。しかし、不必要な要素を次々と追加する前に、まずはこれらのタブの使い方を正しく理解することが重要です。そうすることで、アプリケーションがMetaTrader 5の標準から逸脱することを防ぐことができます。

基本的に、管理が必要となるのは図01に示されているタブのみです。このタブでは、アプリケーションの目的をより分かりやすく説明するための要素を追加することが可能です。そして、その方法は非常にシンプルです。ただし、ここでは引き続き教育的側面に焦点を当てていくので、このタブに要素を追加することはしません。それでも、特にアプリケーションをより多くの人に利用してもらいたいと考えているのであれば、この最初のタブの扱い方についてしっかりと検討しておくべきです。

図01のタブを追加または変更するには、コンパイラに対して何を表示するのかを指示する必要があります。そのために使用するのがコンパイラディレクティブです。このディレクティブには#propertyを使用します。ディレクティブを用いることで、アプリケーションのバージョン定義から、特定のインターネット上のリンク指定まで、さまざまな情報を定義することができます。詳細については公式ドキュメントを参照してください。おそらく、これまでの記事内のコードで、このディレクティブがどのように使われているかをご覧になっているはずです。基本的に、私はデフォルトでは著作権情報のみを定義しています。それでは、どのような定義が可能なのかイメージしていただくために、別の例を見ていきましょう。以下のコードを使用します。

01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. #property version "1.0"
04. #property icon "Example.ico"
05. #property description "This is a demonstration application whose purpose is only didactic.\nIt is part of a sequence of articles published in the MQL5 community."
06. #property link "https://www.mql5.com/pt/articles/15794"
07. //+------------------------------------------------------------------+
08. int OnInit()
09. {
10.    return INIT_SUCCEEDED;
11. };
12. //+------------------------------------------------------------------+
13. int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[])
14. {
15.    return rates_total;
16. };
17. //+------------------------------------------------------------------+

コード01

このように、コード01を実行すると、図01の表示内容が変更されます。その変更は、以下の図05に示されているとおりです。

図05

ご覧のとおり、標準とは異なるアイコンが表示され、さらにアプリケーションの目的を説明する記述も追加されています。これらはすべて、コード01の02行目から05行目の間で定義されています。アイコンとして使用する画像ファイルと、そのディレクトリを明示的に指定する必要がある点に注意してください。付録では、皆さんが実際に試せるように環境を整えてありますので、希望するアイコンをどのように配置すればよいかを確認してみてください。現時点では、コード01に示されているように、アイコンファイルはインジケーターのコードが配置されているフォルダ内に置かれている必要があります。ただし、必ずしも同じフォルダである必要はありません。適切なパスを指定すれば、コンパイラは該当ファイルを正しく見つけることができます。

「少し待ってください。コード01の06行目で宣言した情報が表示されていませんが、どこに行ったのでしょうか?」実際には、その情報は消えたわけではありません。以前タブ上に表示されていた情報は、別の情報と関連付けられています。たとえば、著作権情報が表示されている部分にカーソルを合わせると、以下の図06のような表示が確認できます。

図06

つまり、図06に示されているとおり、すべての情報は正しく保持されています。また、リンクの起点となっている箇所をクリックすると、ブラウザが起動し、コード01の06行目で指定した場所へ移動します。多くの場合、皆さんは個人利用を目的として開発をおこなっていると思いますので、これらの設定は必ずしも実装する必要はありません。しかしながら、コンパイラに対してコード生成の方法を指示する必要があるケースも存在します。言い換えると、個人用途であっても、特定の状況ではこれらのコンパイルプロパティを設定する必要が生じるということです。

その代表的な例が、チャート上に何らかの描画をおこなうインジケーターを作成する場合です。なお、本トピックの冒頭で触れた他のタブについての説明を省略している点については、どうかご安心ください。すぐにそれらに戻ります。ただしその前に、まずはこれらのタブに意味を持たせるための作業をおこなう必要があります。


基本的なインジケーター

これまで私たちは、多くの情報を生成し、それをターミナルに出力して表示させることだけをおこなってきました。いよいよ、チャート上に実際に何かを描画する段階に進みます。より分かりやすく、視覚的にも理解しやすくするために、今回はMetaTrader 5の標準スタイルのチャートを使用します。これはあくまでデモンストレーション目的ですので、皆さんはご自身にとって最も扱いやすい、あるいは好みに合った表示設定を使用してください。

さて、前回および前々回の記事では、MetaTrader 5が生成する2つの主要イベント、すなわちDeinitイベントとInitイベントについて説明しました。これらはいずれも、アプリケーションの動作を制御するためのものです。しかし、すべてのインジケーターには2つのイベントが必要です。1つはすでに説明したInitイベント、そしてもう1つがCalculateイベントです。

ここは非常に重要なポイントですので、十分に注意してください。OnCalculate関数はCalculateイベントを捕捉しなければなりません。このイベントはインジケーターのコードに存在している必要があります。しかし、同じ関数には2つのバージョンがあります。つまり、デフォルトでMQL5はオーバーロード関数を使用できるようにしています。次に1つのバージョンを示し、もう1つのバージョンはその下に示します。

int  OnCalculate(
   const int        rates_total,       // price[] array size
   const int        prev_calculated,   // number of handled bars at the previous call
   const int        begin,             // index number in the price[] array meaningful data starts from
   const double&    price[]            // array of values for calculation
   );
int  OnCalculate(
   const int        rates_total,       // size of input time series
   const int        prev_calculated,   // number of handled bars at the previous call
   const datetime&  time[],            // Time array
   const double&    open[],            // Open array
   const double&    high[],            // High array
   const double&    low[],             // Low array
   const double&    close[],           // Close array
   const long&      tick_volume[],     // Tick Volume array
   const long&      volume[],          // Real Volume array
   const int&       spread[]           // Spread array
   );

しかし、なぜMQL5の開発者はこのようにしたのでしょうか。主な理由はインジケーターの動作をより効率的かつ安定させることかと思います。実際、計算方法はいくつか存在し、とくにインジケーターは多くの場合、平均値を算出するための計算をおこないます。そのため、開発者はMetaTrader 5プラットフォームのパフォーマンスを向上させられることに気づき、インジケーターが一部の計算を省略できるように設計しました。プラットフォーム自体があらかじめ結果を提供してくれるためです。今は少しピンとこないかもしれませんが、実際に手を動かして学ぶうちに、その価値の大きさを理解できるようになるでしょう。

では、どちらのオーバーロードバージョンを使えばよいのでしょうか。答えは、状況によって完全に異なります。どちらか一方を必ず使わなければならないということではありません。両方のバージョンは本質的には同じで、Calculateイベントを受け取るという点に違いはありません。それだけです。その先に何が起こるかは、私たちが何を計画し、何を実行するかによって決まります。

しかし、ここで興味深い点があります。これはプラットフォーム開発者による巧みな改良として認めざるを得ません。使用するOnCalculateのバージョンによって、ユーザーは図02に示されているタブにアクセスできる場合と、できない場合があります。古いバージョンのMetaTrader 5を使用しているときの画像やアニメーション、動画が含まれる記事を探すと、この図02のタブはほとんど常に表示されており、まったく必要のない場合でも出現していたことに気づくでしょう。

しかし、ある時点でこの挙動はすべて消えました。引数の少ないバージョンを使用すれば、図02のタブにアクセスできます。引数の多いバージョンを使用すれば、このタブは利用できません。だからこそ、前もって説明しても意味がなかったのです。今だからこそ理解でき、その意義は、インジケーターを実装する際にさらに明確になります。

まずは、ほとんどすべてを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 int begin, const double &price[])
10. {
11.    return rates_total;
12. };
13. //+------------------------------------------------------------------+

コード02

なお、現時点ではまだ何も実装されていません。コードには基本的な部分しか含まれておらず、コンパイラに対して目的のアプリケーション、つまり本記事の冒頭で説明したすべてのタブを表示するインジケーターを作成する方法を知らせるだけの内容です。次に、私たちのインジケーターについて説明します。そのためには、コンパイラに何を作成すべきか指示する必要があります。作成方法には2通りあります。動的に作成する方法は、はるかに複雑で、別の機会に詳しく説明します。一方、静的に作成する方法はかなり簡単で直接的です。小規模なコード向けで、初心者や、シンプルなインジケーターを素早く作りたい方に最適です。今回は後者の静的手法を使うため、コード02を修正して以下のバージョンを作成します。

01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. //+------------------------------------------------------------------+
04. #property indicator_type1           DRAW_LINE
05. #property indicator_color1          clrBlack
06. //+------------------------------------------------------------------+
07. int OnInit()
08. {
09.    return INIT_SUCCEEDED;
10. };
11. //+------------------------------------------------------------------+
12. int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[])
13. {
14.    return rates_total;
15. };
16. //+------------------------------------------------------------------+

コード03

これは私たちが取るべき2つ目のステップです。05行目の記述は、標準のMetaTrader 5環境で使用する際に残る小さな問題に関するものです。この行がなければ、たとえ非常にシンプルなインジケーターであっても、インジケーターのプロットの色を変更できません。しかし、最も重要な文は04行目にあります。この行で、チャート上に正確に何を表示したいかを指定します。ここでは価格ラインを表示しますが、どの価格を示すのか、また、ラインの定義やプロパティをどのように設定するのか。これらについては心配いりません。後で一緒に確認していきますので、気長に進めてください。

インジケーターの種類を定義した後は、さらにいくつかの特性を定義する必要があります。そのためには、MQL5のドキュメントを参照して、次の項目をどのように宣言するかを学びます。そして再びドキュメントに戻り、#propertyという用語を検索して、04行目で宣言された内容、すなわちインジケーターの種類に関連する情報を確認します。検索すると、以下の図のように目立つ項目を確認できます。

図07

図07に示されている項目は非常に重要です。これがなければ、MetaTrader 5に対してインジケーターをどのように表示するかを指示することができません。そのため、列挙型ENUM_DRAW_TYPEを確認します。ドキュメントでこれを調べると、以下のような表を確認できます。

図08

必要な情報は、図08の緑でハイライトされている部分にあります。つまり、データバッファが必要です。インジケーターの種類によって必要な内容は異なりますが、今回作成するプログラムは非常にシンプルなので、バッファだけで十分です。この情報を基に、コード03にいくつかの要素を追加することができます。ただし、その前に追加の情報が必要です。これは、インジケーターが画面上に描画する要素の数に関するものです。これを調べるには、#propertyを参照します。以下の図のとおりです。

図09

次に、同じ表を参照して2つ目の情報を取得します。ここでは、図10に示された2つの値の合計と同じ値を使用する必要があります。

図10

必要な情報をすべて揃えたら、コード03に戻り、さらにいくつかの要素を追加します。コードは次のようになります。

01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. //+------------------------------------------------------------------+
04. #property indicator_type1           DRAW_LINE
05. #property indicator_color1          clrBlack
06. //+----------------+
07. #property indicator_buffers         1
08. #property indicator_plots           1
09. //+------------------------------------------------------------------+
10. int OnInit()
11. {
12.    return INIT_SUCCEEDED;
13. };
14. //+------------------------------------------------------------------+
15. int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[])
16. {
17.    return rates_total;
18. };
19. //+------------------------------------------------------------------+

コード04

コード04を見ると、07行目に私たちが探していた宣言があり、これは図08で確認したものです。08行目では別の情報が指定されており、これは図10の値の合計と同じです。これでインジケーターを機能させる準備が整いました。最後のステップは以下のとおりです。

01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. //+------------------------------------------------------------------+
04. #property indicator_type1           DRAW_LINE
05. #property indicator_color1          clrBlack
06. //+----------------+
07. #property indicator_buffers         1
08. #property indicator_plots           1
09. //+----------------+
10. double   gl_buffer[];
11. //+------------------------------------------------------------------+
12. int OnInit()
13. {
14.    SetIndexBuffer(0, gl_buffer, INDICATOR_DATA);
15. 
16.    return INIT_SUCCEEDED;
17. };
18. //+------------------------------------------------------------------+
19. int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[])
20. {
21.    for (int c = prev_calculated; c < rates_total; c++)
22.       gl_buffer[c] = price[c];
23. 
24.    return rates_total;
25. };
26. //+------------------------------------------------------------------+

コード05

これが、完全に機能するインジケーターです。MetaTrader 5が提供するあらゆる情報を表示でき、しかも正しく動作します。「でも、どうしてこんなことが可能なのですか?他のインジケーターのコードはもっと大きいのを見たことがあります。信じられません。自分でもインジケーターをいくつか作ったことがありますが、どれもここに示されているものよりずっと多くのコードが必要でした。本当に動作するのか確認したいです。」

読者の皆さんの驚きは理解できます。これまで学んだインジケーターのコードには、私たちがここでおこなっているよりも多くの要素が含まれていることがほとんどだからです。しかし、このコードが実際に動作することを示すために、次のアニメーションをご覧ください。

アニメーション01

アニメーション01では、インジケーターを配置し、その色を変更する手順を示しています。非常にシンプルで直感的であることに注目してください。

アニメーション02

アニメーション02では、インジケーターのラインスタイルを変更する方法を示しています。こちらも非常に直感的です。

アニメーション03

アニメーション03では、インジケーターのラインの太さを変更する方法を示しています。この場合、MetaTrader 5が異なる太さのラインスタイルに対応してくれると便利ですが、開発者がこの機能を実装するまでは、現在のMetaTrader 5の仕様に合わせて対応する必要があります。

アニメーション04

最後に、アニメーション04では、インジケーターでおこなわれる計算の種類を変更する方法を確認できます。ご覧の通り、すべて非常にシンプルで直感的であり、インジケーターの動作に影響を与えることはありません。図04のタブがどのように機能するかは推測するしかありませんが、非常に直感的で簡単なので、好奇心を持って使えば十分です。

ここで詳しく説明する必要はありません。このタブは主に動作環境の設定に使われるもので、初心者トレーダーが触ることはほとんどありません。しかし、プログラマとしては、このタブがどのように動作し、コード上でどのように実装できるかを理解する必要があります。もちろん、このタブを使わずに純粋にMQL5だけでインジケーターの動作を設定することも可能ですが、その方法は後ほど、より適切なタイミングで解説します。

コードの動作を確認したところで、最後の実装ステップで追加された内容を見ていきましょう。ここで説明する内容は、後ほどより高度な形で扱いますが、まずはなぜ、どのようにして最後のステップで行の実装が必要なのかを理解しておくことが重要です。具体的には、10行目からの部分です。

連載最初の記事でも説明した通り、グローバル変数の使用は原則推奨されません。しかし、10行目で宣言された変数は特別なケースです。後ほど詳しく扱ういくつかのポイントを実装するために、グローバル変数が本当に必要だからです。現時点では、この変数がチャート上にプロットされるデータを保持することにより、アニメーションで示したインジケーターを作成するということを理解しておけば十分です。

そのためには、複数の変数がコード上で宣言されている可能性がある中で、MetaTrader 5にどの変数を使用するかを指示する必要があります。この情報は、14行目で確認できます。基本的にはここまでです。その後、Calculateイベントを処理し、MetaTrader 5が提供するデータを使って10行目で宣言した変数に値を代入していきます。 

これは難しいものではありません。ほとんどの場合、非常に簡単です。必要なのは、ある値を計算し、10行目で宣言した配列の特定の位置に代入することだけです。ここで注意してください。配列について説明した際に、配列には動的型と静的型があるとお伝えしました。しかし、インジケーターで描画用のデータを格納する配列は、すべて動的配列でなければなりません。メモリを自分で確保する必要はありません。必要に応じてMetaTrader 5が自動的に割り当てます。したがって、22行目のコードを使用できます。14行目で、この変数がチャート上に描画するインジケーターデータを保持することを指定しているわけです。その後は、MetaTrader 5がメモリ割り当てを完全に管理します。

さらに重要な点があります。理想的には、MetaTrader 5のアプリケーションは可能な限り効率的であるべきです。これは、処理に時間をかけすぎず、プラットフォームの安定性を維持するために必要です。そのため、21行目のループの作り方に注目してください。一見すると、Calculateイベントが発生するたびに、価格配列の全データを最初から最後まで読み込むように見えます。しかし、実際にはそうではありません。少なくとも正しく実装されていれば、そのようなことは起こりません。

本質的に、MetaTrader 5内部ではアプリケーションの動作を管理するための値が保持されています。そして、インジケーターがCalculateイベントを初めて処理した際、rates_totalは価格配列に存在するデータの量を指します。prev_calculatedはゼロの場合もあればそうでない場合もありますが、初回呼び出し時にはシリーズの最初のデータポイントを指します。後で見るように、この値は変化することがあります。現時点では、ここで説明した条件は、コードが正しく実装されルールに従っていれば、最初の一回だけ適用されることを理解してください。

次回の呼び出しでは、prev_calculatedがrates_totalと同じ場合もあれば、異なる場合もあります。同じままであれば、特に変更はありません。小さい場合は、描画する値を更新するためにループが実行されます。

したがって、コードを正しく実装すれば、最初の実行は遅くなります。しかし、その後のCalculateイベントでは最小限の処理しかおこなわれず、プラットフォームは迅速に分析をおこない、結果を画面に表示できます。

「なるほど、でもひとつ理解できません。prev_calculatedとrates_totalが同じ場合に、Calculateイベントはなぜ発生するのですか?21行目のループが何も実行されないのでは、時間の無駄では?」確かにその通りです。しかし、MetaTrader 5はCalculateイベントを任意に発生させるわけではありません。銘柄の価格が変化したときのみイベントを発生させます。そのため、市場のボラティリティが高い期間には、膨大な数のイベントが発生します。コードが最適化されていなければ、プラットフォームの動作が徐々に遅くなります。しかし、その責任はプラットフォームではなく、時間とリソースを消費する最適化されていないアプリケーションにあります。


まとめ

本記事では、初めてとなる完全に機能する実用的なインジケーターを作成しました。目的はアプリケーションの作り方そのものを示すことではなく、読者の皆さんがご自身のアイデアをどのように安全かつシンプル、そして実践的な方法で開発できるのかを理解し、実際に安全でシンプルかつ実践的な方法でそれを適用できるようにすることにあります。

このテーマは十分に理解し、しっかりと学習すべき内容であるため、必要以上に話を広げることはしません。内容を詰め込みすぎると、落ち着いて学習する妨げになるからです。付録には、本記事で紹介したコードのうち2つを収録しています。それらを使用して、記事内で示した手順を段階的に追うことができます。

次回の記事では、本日のテーマを継続します。OnCalculate関数のもう一つの宣言形式について、まだ検討する必要があるためです。

MetaQuotes Ltdによりポルトガル語から翻訳されました。
元の記事: https://www.mql5.com/pt/articles/15794

添付されたファイル |
Anexo.zip (6.43 KB)
市場シミュレーション(第10回):ソケット(V) 市場シミュレーション(第10回):ソケット(V)
これからExcelとMetaTrader 5の接続の実装を始めますが、その前にいくつか押さえておくべき重要なポイントがあります。これを理解しておくことで、なぜ動くのか、なぜ動かないのかで悩む必要がなくなります。そして、PythonとExcelを組み合わせることに尻込みする前に、xlwingsを使ってExcelからMetaTrader 5をある程度操作できる方法を見てみましょう。ここで紹介する内容は主に教育目的ですが、もちろん、ここで取り上げることだけに制限されるわけではありません。
初心者からエキスパートへ:市場構造を認識したRSI取引 初心者からエキスパートへ:市場構造を認識したRSI取引
本記事では、相対力指数(RSI)オシレーターを市場構造と組み合わせて取引するための実践的な手法を解説します。特に、チャネル型のプライスアクションパターンに焦点を当て、それらが一般的にどのように取引されているか、そしてMQL5をどのように活用してこのプロセスを強化できるかを説明します。最終的には、トレンド継続の機会をより高い精度と一貫性で捉えることを目的とした、ルールベースの自動チャネル取引システムを構築できるようになるでしょう。
初級から中級まで:インジケーター(II) 初級から中級まで:インジケーター(II)
本記事では、移動平均の計算をどのように実装するか、またその計算をおこなう際にどのような点に注意すべきかを確認します。さらに、OnCalculate関数のオーバーロードについても取り上げ、どのバージョンをいつ、どのように扱うべきかを理解していきます。
MQL5 MVCパラダイムにおけるテーブルのビューおよびコントローラーコンポーネント:サイズ変更可能な要素 MQL5 MVCパラダイムにおけるテーブルのビューおよびコントローラーコンポーネント:サイズ変更可能な要素
本記事では、要素の端や角をマウスでドラッグしてコントロールをサイズ変更する機能を追加します。