
初心者向け MQL4 言語カスタムインディケータ(パート 1)
はじめに
本稿は『初心者向け MQL4 言語』シリーズの第4 弾です。今日はカスタムインディケータの書き方を学習します。インディケータを特徴で分類する知識を得、この特徴がインディケータにどのように影響するかを確認し、新しい関数や最適化について学び、最後に自分のインディケータを書きます。また、本稿末尾にはプログラミングスタイルのアドバイスもあります。『初心者の方にとって』本稿が最初に読む記事であれば、先行記事を読むのが良いかもしれません。また、この記事では基礎は説明していないので、前の資料を正しく理解できていることを確認してください。
インディケータタイプ
ここからどんな種類のインディケータがあるか説明していきます。もちろんその多くを見たことがあるでしょうが、ここではインディケータの特徴 および パラメータに注目していただき、特徴 とパラメータによる分類 を少し行います。それがあとでカスタムインディケータを書くのに役立つのです。ではまず簡単なインディケータから始めます。
これは移動平均、MA で、広く利用されるテクニカルインディケータです。次の重要な事項に注意を払います。
- インディケータはチャートウィンドウに描画される
- インディケータは値を1つ
だけ表示する - インディケータ値の取る範囲は無限であり、現行価格によって決まる
- 線は一定の色、幅、スタイル(実線)で描かれる
ここで別のインディケータを検討します。
それはウィリアムズの範囲率、%Rです。次の重要な事項に注意を払います。
- インディケータは個別のサブウィンドウに描画される
- 前出のケース同様、インディケータは値を1つだけ表示する
- インディケータ値の取る範囲は厳密に制限されている
- 描かれる線は別のスタイル、色、幅を持つ
よって、以下のインディケータ特性があります。
- インディケータはチャートウィンドウまたは個別のサブウィンドウに描かれる。ここで、なぜ移動平均がチャートに描かれ、ウィリアムズの範囲率、%R が別のウィンドウに描かれるのか理解しておきます。違いは 表示される値範囲です。2番目のインディケータは範囲 0 ~-100 の値を表示することに注意します。ここでチャートウィンドウでこの値が表示されることを想像してください。そうすると何が起こるでしょうか?この線は見えないのではないでしょうか。というのも価格はひじょうに狭い範囲を持つことになるからです。われわれの場合、それは 0.6805~0.7495 です。しかしそればかりではありません。実際、価格は正の数字で、ここでの値は負です。インディケータは、値がアクティブなチャートの価格範囲の外にある場合別のサブウィンドウに描かれるのです。そして範囲がほとんど同じであれば(たとえば異なる種類の移動平均)、インディケータはチャートウィンドウに描かれます。のちに、このインディケータのパラメータは、この簡単なロジックに従って設定します。以下がその画像です。

- 別のサブウィンドウに描かれるインディケータは厳密な範囲に制限されているのです。それは、ターミナルがインディケータ値を表示するのに固定のスケールを設定することを意味します。そして値がその範囲を超えても表示されることはありません。このパラメータを無効にすると、インディケータ値をすべて持つように、ターミナルは自動的にスケールを変更します。その画像を確認します。
- あるインディケータが異なる色、スタイル、幅で値を表示します。ターミナルにインディケータ描画を設定する場合、それを頻繁に見てきました。ここに一つ制限があります。線幅を 1 より大きくすると、線種は実線のみとなります。
ここにもう一つインディケータがあります。

ごらんのように、インディケータ ボリュームはヒストグラムの形式で描かれています。よって、インディケータ値の表示タイプは複数あるのです。以下は別のタイプの例です。

インディケータ フラクタルは特殊なシンボルの形式で描かれます。次のインディケータを見ます。

これはアリゲータです。このインディケータは同時に3つの値(残高曲線)を描くことに注意します。どのように動作するのでしょうか?実質的に、あらゆるインディケータ(例外はいくつかありますが、それについてはのちほどお話しします)はデータバッファを使用します。
データバッファとはほとんどシンプルな配列です。この配列は部分的にターミナルによって管理される点で特殊です。ターミナルは、新規バーが受信されるたびに移動が起こるよう配列を変更します。その目的は配列のエレメントがそれぞれ特定のバーに対応するようにすることです。1つのインディケータに表示されるデータバッファの最大値は 8 です。今はそれは奇妙に思えるかもしれませんが、すぐにそうでなければならないことが理解できます。アリゲータには線ごとに個別のデータバッファがあることだけ覚えておいてください。各データバッファには独自のパラメータがあり、それに従ってターミナルは描画します。われわれの場合、以下の方法で記述できるバッファが3つあります。
- 第1バッファは幅3のグリーンの実線で描かれます。
- 第2のバッファは幅1で赤の破線で描かれます。
- 第3のバッファは幅2のブルーの実線で描かれます。
インディケータがバッファを描く必要はありません。バッファは中間計算に使用されます。それがバッファ数が表示より大きくなるかもしれない理由です。ただ、データバッファの最も重要な特性は、各バッファエレメントがチャート上の特定のバーに対応する必要があることです。このことだけ覚えておいてください。すぐにこれがどう動作するかコードで確認します。
これまで少し見てきたものについて結論を出します。インディケータにはすべて以下のパラメータがあります。
- 値を表示したり中間計算を行うための1つ以上のデータバッファ(必ずしもというわけではありません)バッファにはすべて、どのように描かれるか、そして描かれるかどうか決定する独自のパラメータがあります。たとえば、ヒストグラム、シンボル、線の形式で、何色で、どんな線種値を描画するか、などです。
- インディケータがどこに描かれるか(チャートウィンドウ、またはサブウィンドウ)
- インディケータがサブウィンドウに描かれる場合、範囲を限定するかまたはスケーリングは自動で行われるか
こういったパラメータをすべて明確に理解していることを確認します。そしてこれからカスタムインディケータを作成するためウィザードを利用します。
カスタムインディケータの作成
MetaEditor を起動し、File → New規を選択します。

そうすると、ウィンドウ Expert Advisor ウィザードが表示されます。Custom Indicator を選択し、Next をクリックします。

フィールド名前、著者、リンクを記入します。ここではすべていつもどおりですが、パラメータを追加します。パラメータとは何でしょうか?
パラメータはユーザーによって設定される一般変数です。重要なことは、これら変数はインディケータ コードで使用されるということです。パラメータの利用は明らかで、ユーザーがインディケータの操作においていくつかの特徴を設定できるようにするのです。希望通りの設定が可能です。たとえば、対象とするタイムフレーム、操作モード、平均化に使用するバー数、等です。
例として、インディケータ値を計算するために処理するバー本数を表示するパラエータを追加してみます。それはどこで使用可能でしょうか?みなさんのインディケータがあまりに多くの計算のためにプロセッサに深刻な負荷をかけると想像します。そしてチャートのタイムフレームを頻繁に変更し、最後の100~200本のバーしか閲覧しません。そうなると、時間を無駄にするその他の計算は必要なくなります。このパラメータはそのような場面で役に立つのです。もちろん、コンピュータのリソースを無駄にするインディケータは簡単です。これは単にインディケータパラメータを使用するバリアントです。
よってパラメータを追加するにはAdd(1)をクリックします。その後、変数名(2)を変更します。われわれの場合は barsToProcess と置き換えます。また初期値 (3)、すなわちデフォルト値も変更します。それを 100 に変更します。また、変数タイプを変更することがありますが、ここでの場合は何も変更する必要がありません。タイプ int は今回の目的に完全に適しているためです。必要な変更がすべて終われば、Nextをクリックします。

ほぼ準備ができました。インディケータがどのように描画されるか指示します。別のウィンドウまたはチャートウィンドウです。範囲制限もする可能性があります。別ウィンドウのインディケータにチェックを入れます。以下は空のフィールドのインデックス(データバッファ)です。ここで必要な数のデータバッファを追加します(最大 8)。そのうえ、つねにコード変更し、のちにバッファを追加、または削除します。バッファを追加するにはAddをクリックします。バッファの描画方法を変更することもあります。線、 ヒストグラム、セクション、矢印です。われわれは何も変更しませんので、われわれのタイプは線です。色を設定し、OK をクリックします。
ついにはじめてのインディケータが準備できました!まあ、何も描画しませんが、それはコードです。ソースコードの入ったファイルは、インディケータのある次のフォルダにあります:MetaTrader4\experts\indicators。
1行ずつ分析しましょう
メタエディタ― が作成したものを確認します。
//+------------------------------------------------------------------+ //| myFirstIndicator.mq4 | //| Antonuk Oleg | //| banderass@i.ua | //+------------------------------------------------------------------+
通常どおり、1 行コメントで構成されている冒頭には以前に書いた情報が入っています。次に
#property copyright "Antonuk Oleg"
2番目の記事にあるプロセッサ命令 #define をまだ覚えていますか?定数を宣言するために使用したものです。インディケータの特定プロパティを表記するために使用される命令がもう一つあります。われわれの場合、それは原作者を指定するために使用されます。それは特別な記号 # で始まり、キーワードプロパティ(スペースなし)につながることにご注意ください。それから設定したい具体的なプロパティが続きます。われわれの場合、それは著作権で、そのあとこのプロパティの値です。ここではそれはみなさんの名前の行です。命令 #property を使用してインディケータの数多くの特徴を設定することができます。ここでそれを確認します。これらプロパティはすべてデフォルトで設定されます。先へ進みます。
#property link "banderass@i.ua"
この命令は著者との連絡方法を示します。この情報(著者名および連絡情報)がどこにあるか尋ねることができます。というのもそれはどこにも表示されないからです。ですがそれは実行可能ファイルに含まれています。共通テキストとして実行可能ファイルを閲覧する場合、この情報が表示されます。
次に
#property indicator_separate_window
この命令は、インディケータが別のサブウィンドウに描画される必要があることを示します。ごらんのように、前の命令とは異なりそれ以上のパラメータはありません。
#property indicator_buffers 1
この命令は、インディケータによってデータバッファがいくつ使われるかを示します。命令はなんとなく一般関数に似ていることにお気づきかもしれません。それらもまたパラメータをいくつか受け取り、それに対応して何かを行うものです。ただ、一つ重要な違いがあります。命令は 1番目のインスタンスで(コンパイル前に)実行される、ということです。
#property indicator_color1 DarkOrchid
1番目のバッファの デフォルト色を示します。バッファの列挙はゼロではなく 1 から始まることに注意が必要です。のちに混乱しないようそのことを覚えておくようにします。色は数多くの事前に決められた名前の一つで示されます。ヘルプで使用可能な色すべてのキーワードを確認することができます。:MQL4 参照 → 標準定数 → ウェブカラー。同様にその他のバッファ色を指定することができます。ただバッファ番号を変更するだけです。
extern int barsToProcess=100;
これはインディケータのパラメータです。ウィザードで設定済みです。一般関数との唯一の違いは、変数タイプの前のキーワード extern です。以下はインディケータ開始時、ユーザーにとってパラメータがどんなふうに見えるかを示しています。

次に
double ExtMapBuffer1[];
これは通常の配列です。ただ、次元が指定されておらず、初期化は行われません。この配列はあとでデータバッファとして設定します。
それから関数を宣言し、記述します。通常のスクリプトと異なり、インディケータそれぞれには関数が 1 つではなく 3 つあります。
- init() -インディケータ起動時、一度のみターミナルから呼ばれます。目的は処理用インディケータを準備し、データバッファを設定し、パラメータ(ユーザーが書いたもの)および他の準備処理を確認することです。この関数は必須ではありません。その中でコードを処理しなければ、削除してもかまいません。
- deinit() -これもチャートからインディケータを削除する際、一度だけ呼び出されます。処理を終了するためにインディケータを準備します。たとえば、開いているファイルを閉じる、ファイルからグラフィカルオブジェクトを削除する(心配しなくてもやり方は学習します)、など。この関数も必須ではありません。
- start() -スクリプトとは異なり、インディケータではこの関数はティックごとに呼ばれます。すなわち、通貨ペアから新規クオートが出ると、インディケータをアタッチしたチャートに対してこの関数が呼ばれるのです。また、この関数はインディケータ起動時、すなわち関数 init() の後、にも呼ばれます。
各関数で何が起こっているか確認します。
int init() { SetIndexStyle(0,DRAW_LINE); SetIndexBuffer(0,ExtMapBuffer1); return(0); }
ここでデータバッファを設定するのに重要な2つの関数呼び出しを確認します。
SetIndexStyle(0,DRAW_LINE);
この関数はデータバッファをどのように描画するか設定します。最初のパラメータはどのバッファに対して 変更が加えられるか、を指示します。この関数(および類似した関数)では、バッファの列挙は命令でのように1からではなくゼロからであることに注意が必要です。重要なところですから注意してください。2番目のパラメータは、選択されたバッファをどのように描くかを指示します。われわれの場合、定数 DRAW_LINE を使用します。それはバッファが線として描かれることを示します。もちろんほかにも定数はありますが、それについてはのちほどお話します。
SetIndexBuffer(0,ExtMapBuffer1);
この関数はバッファ番号に対して配列を『バインド』します 。すなわち、指示された番号のバッファがデータ格納に指示された配列を使うことを示します。よってこの配列のエレメントを変更することは、バッファの値を変更することとなります。実質的に配列はデータバッファなのです。1番目の引数はバインドされる配列の名前です。
return(0);
関数の最後でゼロを返します。これで初期化は正常に行われました。
int deinit() { //---- //---- return(0); }
デフォルトでは初期化関数は空です。
int start() { int counted_bars=IndicatorCounted(); //---- //---- return(0); }
ここからもっとも重要な関数です。主要なコードはここにあります。変数 counted_bars は事前に宣言され、関数 IndicatorCounted() によって初期化されることに注意が必要です。この変数は通常、最適化とインディケータ処理のスピードアップに使用されます。これについてはのちに分析します。そしてここではインディケータウィンドウに何かを描画します。
インディケータの終了
何を表示するか決めます。インディケータは何を表示してくれるのでしょうか?何かシンプルなものです。まず乱数を描きます。当然ですね?これは利益シグナルの 50% を保証するのです。
関数 init() に乱数作成の初期化を行うコードを書きます。
int init() { SetIndexStyle(0,DRAW_LINE); SetIndexBuffer(0,ExtMapBuffer1); // initialization of the generator of random numbers MathSrand(TimeLocal()); return(0); }
初期化は準備できました。ここで来るのが関数 start() です。
int start() { int counted_bars=IndicatorCounted(); for(int i=0;i<Bars;i++) { ExtMapBuffer1[i]=MathRand()%1001; } return(0); }
コンパイル-F7。ターミナルを起動し、パネルナビゲータを検索し、Custom Indicators の項目を選択したら、インディケータ名をダブルクリックします。
インディケータがアクティブチャートにアタッチされます。
おわかりですね。すべて動作します。ではコードが何をするか見ます。
for(int i=0;i<Bars;i++)
データバッファの全エレメントを処理するには、for サイクルを使用します。特定のバーがバッファの各エレメントに対応するため、ゼロバー(有効な直近バー)から開始するサイクルを用い、連続する中で変数 Bars より1小さい1番目のバーで終了します(バーをゼロからカウントするため)。
{ ExtMapBuffer1[i]=MathRand()%1001; }
各反復ではカウンターは1ずつ増え、直近の 有効なバーから 最初のバーまで移動し、同時に各バッファエレメント(特定バーに対応している)に 0 から 1000 のランダムな数を割り当てます。どのように特定バッファエレメントが特定バーに対応するか理解するのがむつかしければ、次の方法でサイクルを変更してみて、ターミナルでその結果を確認してください。
for(int i=0;i<Bars;i++) { ExtMapBuffer1[i]=i; }
これでインディケータは各バーの番号を表示します。それが以下です。

バー番号は直近バーから1番目のバーまで増加しています(0から Bars へ)。バッファエレメントがチャート上のバーに対応することを、もうご理解いただけているとよいのですが。
ここで『ランダムな』インディケータのコードに戻ります。少なくとも数分それを使用したら、すべてのインディケータのティックがまったく異なるチャートを描くのがおわかりでしょう。すなわち、ティックはすべて前回計算されたものを計算するのです。1ティック前に何が起こったか知ることができないため、これは都合の悪いことです。ただ、これは問題にはなりません。というのも、だれもそのようなインディケータは使用しないからです。ここでは書き方を学習しているにすぎません。もう一つあります。みなさんのインディケータが数多くの複雑な計算を行い、あるバーの計算が大量のプロセッサリソースを消費する場合を想像します。そのような場合、新しい価格が表示されれば、インディケータは前にすでに計算済みであっても有効なバーすべての値を計算するのです。はっきりしましたか?一度だけ計算する代わりに、何度も繰り返し計算してしまうのです。不当にリソースを無駄にすることにつながるそのような問題を排除することは最適化と呼ばれます。
ではこの問題をどのように解決することができるのでしょうか?通常、それは以下の方法で行われます。まず、インディケータは有効なろうそく足すべてにおいて計算を行います。そしてクオートが受け取られるときだけ、直近のろうそく足に対してのみ値を再計算します。これは合理的です。不要な処理がありません。関数 start() が次のように動作するよう、それを最適化します。
int start() { int counted_bars=IndicatorCounted(), limit; if(counted_bars>0) counted_bars--; limit=Bars-counted_bars; for(int i=0;i<limit;i++) { ExtMapBuffer1[i]=MathRand()%1001; } return(0); }
それでは行をそれぞれ分析します。
int counted_bars=IndicatorCounted(),
インディケータによって計算されたバー数を格納する変数 counted_bars を宣言します。実質的には関数 IndicatorCounted() は、関数start() が前回呼ばれた後変更のないバーの数を返します。よって、それが start() の最初の呼び出しである場合、バーはわれわれにとってすべて新規であるため、IndicatorBars() は 0 を返します。最初の呼び出しでなければ、変更は直近バーだけで、IndicatorBars() は Bars-1 に等しい数を返します。
limit;
もう一つリミターとして使用される変数があります。すなわち、それはすでに計算済みのろうそく足は省略してサイクルが早く完了するのに役立つものです。
if(counted_bars>0) counted_bars--;
すでにお話しずみですが、IndicatorCounted() が 0 を返せば、関数 start() は初めて呼ばれ、バーはすべて『新規』ということになります(インディケータはそういったバーに対して計算されませんでした)。ただ、start() の呼び出しがはじめてでなければ、Bars-1 に等しい値が返されます。よって、この条件はそのような状況を探知します。そのあと、変数 counted_bars を1ずつ減らしていきます。直近バーのみ変更されるのですが、なぜこれを行うのでしょうか?実際にはいくつか状況があります。最終ティックがきたとき、最後から2番目のティックが処理されたため、前回バーの最終ティックが未処理のまま、という場合があります。そしてカスタムインディケータが呼ばれず、計算も行われなかったのです。このような状況を排除するため、変数を 1 ずつ減らすのはこのためです。
limit=Bars-counted_bars;
変数 limit(リミター)に再計算が必要な直近バーの数を割り当てます。変数 counted_bars がすでに計算されたろうそく足の数を格納している間、Bars(有効なバーの合計数)とろうそく足がいくつ計算される必要があるかを定義する counted_bars の間の差を求めます。
for(int i=0;i<limit;i++) { ExtMapBuffer1[i]=MathRand()%1001; }
サイクル自体はほとんど変わりません。実装条件を変更しただけです。ここではカウンターが limit より小さい間、サイクルは稼働します。
これで最適化は終了です。インディケータの改定バージョンを見ると、新規ティックが受信されるとき、最終バーの値だけ変化しているのがわかります。インディケータが何も複雑な計算をしなくても、そのような最適化を絶えず利用するようにします。これはオートンです。
「ウィザード」に追加したインディケータパラメータ ToProcess を覚えていますか?今がそれを使用するのに最適なタイミングです。それにはサイクルの前に数行を追加するだけです。
int start() { int counted_bars=IndicatorCounted(), limit; if(counted_bars>0) counted_bars--; limit=Bars-counted_bars; if(limit>barsToProcess) limit=barsToProcess; for(int i=0;i<limit;i++) { ExtMapBuffer1[i]=MathRand()%1001; } return(0); }
おわかりですね。すべてひじょうに簡単です。limit が barsToProcess より大きければチェックします。はい、なら割り当てによってリミターを減らします。結果、barsToProcess=100 と設定すれば、以下のような絵が表示されます。
ごらんのように、われわれが設定したバー数のみ計算されます。
インディケータはほとんど準備完了です。ただ、市場参入への明確なシグナルがまだありません。よって、確実性をもっと追加する必要があります。このため、レベルを使用します。
レベル は一定のスタイル、色、幅でインディケータが描く横線です。バー1本の最大レベル数は 8 であることに留意します。また、命令や関数によっても levels を設定することができます。デフォルトで levels を設定したければ、1番目のバリアントを使用するのが好都合です。インディケータ処理中にレベルを動的に変更するには関数を用います。そこで、レベルを2つ使います。1番目はポイント800で、2番目はポイント200です。このため、インディケータコードの始めに命令をいくつか追加します。
//+------------------------------------------------------------------+ //| myFirstIndicator.mq4 | //| Antonuk Oleg | //| banderass@i.ua | //+------------------------------------------------------------------+ #property copyright "Antonuk Oleg" #property link "banderass@i.ua" #property indicator_level1 800.0 #property indicator_level2 200.0 #property indicator_levelcolor LimeGreen #property indicator_levelwidth 2 #property indicator_levelstyle 0 #property indicator_separate_window
新しい命令を分析します。
#property indicator_level1 800.0
この命令はレベル 1 をポイント 800.0 に設定する必要があることを示しています。バッファ設定命令同様、バッファ列挙は 1 から開始されることに留意が必要です。別のレベルを設定するには、命令の最後でレベル数を変更すればよいだけです。
#property indicator_level2 200.0
レベルの外部形式設定には重要な制限があります。レベルをそれぞれ個別に設定することはできません。全設定は全レベルに例外なく適用されます。レベルをそれぞれ個別に設定する必要があれば、次稿で説明されるオブジェクトを使用する必要があります(そしてレベルはまったく使用しません)。
#property indicator_levelcolor LimeGreen
この命令は全レベルを描画するための色を設定します。
#property indicator_levelwidth 2
この命令は全レベルを描画するための幅を設定します。幅は 1 ~5 で設定します。幅が 1 より大きい場合、レベルは実線で描かれることを忘れないでください。レベル描画に別のスタイルが必要であれば、幅1のみ使用します。
#property indicator_levelstyle STYLE_SOLID
この命令は線を描画するためのスタイルを設定します。以下の設定済み定数があります。
- STYLE_SOLID -実線
- STYLE_DASH -破線
- STYLE_DOT -点線
- STYLE_DASHDOT -一点鎖線
- STYLE_DASHDOTDOT -二点鎖線

『ランダム』インディケータの作成が終了しました。より適切な名前-randomIndicator.mq4、でソースファイルを保存します。再びソースファイルを再コンパイルします。このインディケータコードのも次の部分で使用可能です。以下が最終バージョンです。
関数 iCustom
ここからひじょうに便利な関数- iCustom、の詳細説明をします。それは任意のカスタムインディケータ値を取得するのに使用されます。組み込みインディケータに対しては、先行記事で説明されているテクニカルインディケータを処理する関数(たとえば、 iADX()、iMACD など)を使用することを思い出してください。その他インディケータすべて(カスタムインディケータ)に対しては関数 iCustom を使用します。この関数はユニバーサルで、以下の要件を満たすカスタムインディケータであればなんにでも適用可能です。
- インディケータはコンパイルされており、実行可能なファイル形式(*.ex4)である。
- インディケータはフォルダMetaTrader 4\experts\indicators 内にある。
関数プロトタイプは以下の形式です。
double iCustom( string symbol, int timeframe, string name, ..., int mode, int shift);
パラメータ:
- シンボル-カスタムインディケータ値の計算にどの銘柄(通貨ペア)が使われるか定義します。現行(アクティブな)銘柄(チャート)が必要な場合は、NULL(または 0)を使用します。
- タイムフレーム -どのタイムフレームでインディケータが使用されるか定義します。現期間に対しては0 または次の定数のひとつを使用します。:PERIOD_M1、PERIOD_M5、PERIOD_M15、 PERIOD_M30、PERIOD_H1、PERIOD_H4、PERIOD_D1、 PERIOD_W1、PERIOD_MN1。
- 名前 -カスタムインディケータの実行可能ファイル名。名前だけが指示されます。拡張子(.ex4)やファイルへのバス(experts/indicators/)は書きません。たとえば、カスタムインディケータの実行可能ファイル名が "RandomIndicator.ex4" であれば、"RandomIndicator" と書きます。ここでレジスタは関係ありません。"RANDOMindicator" と書けば動作するということです。
- ... -ここでカスタムインディケータパラメータの値をすべて指定する必要があります。たとえば、われわれのインディケータ RandomIndicator には、パラメータは barsToProcess の一つしかありません。すなわち、われわれの場合、ここでは100(またはみなさんの値に適したもの)を書きます。パラメータ数が1より大きい場合、カスタムインディケータで宣言されるように同一連続内に指定し、コンマで区切ります。この関数を基にインディケータを書いてみます。そうすればよりよく理解できるでしょう。
- モード -カスタムインディケータの動作モード。実質上、それはデータバッファの数で、その値が取得したい値です。列挙はゼロから開始します(命令とは異なります)。カスタムインディケータにデータバッファが一つしかなければ、このパラメータは 0 になります。
- シフト -どのバーでカスタムインディケータが使用されるかを定義します。
使用例:
ExtMapBuffer[0]=iCustom(NULL,PERIOD_H1,"Momentum",14,0,0); // assign to the first element of the array ExtMapBuffer the value of the custom // indicator Momentum on the last available bar. We use here the active // security on hour chart. The name of the executable file: Momentum. // This indicator has only one parameter - period. In our case the period // is equal to 14. This indicator has only one data buffer, so we use zero, // in order to get access to its values.
double signalLast=iCustom("EURUSD",PERIOD_D1,"MACD",12,26,9,1,0); // declare a new variable signalLast and assign to it the value of the custom // indicator индикатора MACD on the last available bar. We use the pair EURUSD on // a daily chart. The name of the executable file: MACD. This indicator has 3 parameters: // period for quick average, period for slow average and period for a signal line. // This indicator also has 2 data buffers. The first one is with values of the main line. The second one // with values of a signal line. In our case we take the value of the signal line.
シグナルインディケータ
もう一つシンプルなインディケータを書きます。そこで、次の状況を想像してください。数多くのデータバッファを持つひじょうに複雑なインディケータを書きました。その多くは、個別のウィンドウに表示され、その他は中間計算に使用されます。買いと売りのシグナルは正確にわかっています。ただ、問題はシグナルの追跡がひじょうに困難なことです。レベルの上か下にある交差している線を見つけようと、画面を絶えず見守る必要があります。そのため、それを代わってやってくれ、エントリーシグナルのみ表示するインディケータをもう1件書くことにしました。たとえば、これらはどの方向でポジションをオープンするか表示する矢印であったりします。これはシグナルインディケータがどの場面で適切であるかを示すというのは、空想です。われわれの状況はもっと簡単なものですが、初めの状況と似てはいます。
前出のインディケータ RandomIndicator を基にシグナルインディケータを書きます。まず、エントリー条件を決定する必要があります。ここでわれわれのレベルが必要となります。よって以下がその条件です。
- 線が上側のレベル(800.0)を上回れば買い
- 線が下側のレベル(200.0)を下回れば買い
今が新しいインディケータを書くのに最適なときです。新しいカスタムインディケータを作成するのに Expert Advisor ウィザードを利用します。前回のように追加のパラメータを1つ付け加えます。
最後のステップ(カスタムインディケータプログラムの特性を描画する)は以下です。

まず、買いと売りのシグナルを矢印として描くのに使用するデータバッファを 2 つ追加します。データバッファのタイプを 矢印に変更します。色とシンボルコードを変更します。以下が利用可能なシンボルコードです。
シグナルをチャートウィンドウに描こうとしているので、インディケータを別のウィンドウに描画する必要はありません。
データバッファは2つ使用します。それはバッファ1つだけでは異なる矢印(シンボル)を描くことはできないからです。シンボル形式で表示されるデータバッファはそれぞれ1つのシンボルによってのみ描画されます。ここで、インディケータ初期化コードを細心の注意を払って分析します。
int init() { //---- indicators SetIndexStyle(0,DRAW_ARROW); SetIndexArrow(0,236); SetIndexBuffer(0,ExtMapBuffer1); SetIndexEmptyValue(0,0.0); SetIndexStyle(1,DRAW_ARROW); SetIndexArrow(1,238); SetIndexBuffer(1,ExtMapBuffer2); SetIndexEmptyValue(1,0.0); //---- return(0); }
データバッファ描画のために別の定数-DRAW_ARROW、が用いられていることに注意します。
SetIndexStyle(0,DRAW_ARROW);
またシンボル描画を設定するのに用いられる新しい関数が2つあるのがわかります。SetIndexArrow はどのシンボルがバッファを表すか設定するのに使われます。1番目の引数はバッファ番号で、2番目の引数はインディケータを表す シンボルコードです。
SetIndexArrow(0,236);
SetIndexEmptyValue は『空の』値を指示するのに使用されます。それは、何も描かない値を指示することを意味します。われわれの場合、それは非常に都合のよいものです。というのも、シグナルはすべてのバーで生成されるとは限らないためです。それは以下のように行われます。:現行バーで配列を描く必要がない場合、対応するデータバッファに『空の』値、われわれの場合は0、を割り当てます。関数の最初の引数データバッファ番号です。2番目の引数は 『空の』値です。
SetIndexEmptyValue(0,0.0);
残りの初期化コードは、バッファの類似体を以前に分析済みの『ランダム』インディケータに設定します。それでは、関数 start() 内のコードを完成させます。
int start() { int counted_bars=IndicatorCounted(), limit; if(counted_bars>0) counted_bars--; limit=Bars-counted_bars; if(limit>barsToProcess) limit=barsToProcess; for(int i=0;i<limit;i++) { double randomValue=iCustom(NULL,0,"RandomIndicator",barsToProcess,0,i); if(randomValue>800.0) ExtMapBuffer1[i]=High[i]+5*Point; else ExtMapBuffer1[i]=0.0; if(randomValue<200.0) ExtMapBuffer2[i]=Low[i]-5*Point; else ExtMapBuffer2[i]=0.0; } return(0); }
サイクルまでのコード全体は『ランダム』インディケータから反復されます。このコードはすべてのインディケータの標準で、小さな変更をすることで繰り返します。それでは、サイクルを詳しく分析します。
まず、変数 randomValue(ランダムな値)を宣言し、そこに現行バーの『ランダム』インディケータ値を割り当てます。このために関数 iCustomを使用します。
double randomValue=iCustom(NULL,0,"RandomIndicator",barsToProcess,0,i); // get the value of the "random" indicator on the i-th bar. Use the active chart on the current period. // The name of the executable file of indicator: RandomIndicator. Single parameter of "random" indicator // is number of bars for calculation. In our indicator there is also analogous variable, that is why // we use it. In "random" indicator only 1 data buffer, so we use 0, for getting // access to its values.
『ランダム』インディケータの値が、上部レベル(800)を上回れば、これは「買い」のシグナルです。
if(randomValue>800.0) ExtMapBuffer1[i]=High[i]+5*Point; // if there is signal to buy, assign to current element of data buffer the highest // value of the current bar. Besides add 5 points, so that the arrow were a little higher // than the current price. The predetermined variable Point is used to get automatically // a multiplier for presenting points. Otherwise we would have to write something like // this: ExtMapBuffer1[i]=High[i]+0.0005;
それ以外、「買い」シグナルが一つもなければ、以下のようになります。
else ExtMapBuffer1[i]=0.0; // if no Buy signal, assign to the current element of datafor(int i=0;i<limit;i++) { double randomValue=iCustom(NULL,0,"RandomIndicator",barsToProcess,0,i); if(randomValue>800.0) ExtMapBuffer1[i]=High[i]+5*Point; else ExtMapBuffer1[i]=0.0; if(randomValue<200.0) ExtMapBuffer2[i]=Low[i]-5*Point; else ExtMapBuffer2[i]=0.0; }// buffer "empty" value, which is equal to 0.0. // Now no symbol will be shown on this bar.
『ランダム』インディケータの値が、下部レベル(200)を下回れば、これは「売り」のシグナルです。
if(randomValue<200.0) ExtMapBuffer2[i]=Low[i]-5*Point; // if it is signal to sell, assign to the current element of data buffer the lowest // value of the current bar. Besides diminish the value by 5 points, so that the arrow were // a little lower than the current price.
それ以外、「売り」シグナルが一つもなければ、以下のようになります。
else ExtMapBuffer2[i]=0.0; // if no Sell signal, assign to the current element of data // buffer "empty" value. Now no symbol will be shown on this bar.
以上がサイクルでした。インディケータをコンパイルし、ターミナルで起動します。

スタイルについて
常にタイムリーではありますが、これはコートやシャツに合うネクタイを選ぶルールとは違います。自分のためだけにプログラムを書くのでない場合、プログラミングのスタイルはひじょうに重要なものです。実質上、開発者はだれにも自分自身のプログラミングスタイルがあります。各人が自分の方法でサイクルをデザインし、異なるインデントを作成し(またはインデントはなしにし)、変数を宣言する、などします。みなさんもご自身のプログラムスタイルを見つける必要があります。そしてそれを今後常に使っていくのです。みなさんのコードを読みやすく理解しやすいものとするのに役立つ推奨事項をいくつか提供したいと思います。
- 1行にセミコロン(;)で区切って多くの処理を書かない。
- 変数名や関数名は英語で書く。
- 変数名ではデリミターとして大文字を使用する。
- 変数名や関数名に過度の略語や短縮形を用いない。
- コードブロックには一定長のインデントを設ける。
- 新規の本文(サイクルや条件の)には追加のインデントを付ける。
- 1タイプの変数はグループ化する。
- 大きなコードブロックやむつかしいコードブロックには適切なコメントを付ける。
- ご自身が書いた関数(割り当て、パラメータ)には適切なコメントを付ける。
おわりに
今日は新しい事柄を学びました。シンプルなインディケータを2つ書きました。まあ、それらは役には立ちませんが、ここではトレードを成功する方法をお教えしているのではありません。インディケータがどのように動作するか、それがどんなパラメータとプロパティを持つか見てきました。バッファを設定してそれらを処理する方法を学習しました。いくつか新たな関数の知識を得ました。関数 iCustom はひじょうに重要で、のちにExpert Advisor でも使用されます。何か問題があれば、本稿を読み返し、理解するようにしてください。まだ質問があるようでしたら、遠慮なくフォーラムを利用するか、本稿へのコメントをお寄せください。
MetaQuotes Ltdにより英語から翻訳されました。
元の記事: https://www.mql5.com/en/articles/1500



- 無料取引アプリ
- 8千を超えるシグナルをコピー
- 金融ニュースで金融マーケットを探索