トップ  >  自作してみる >  MQL4未経験者向け(インジケータ作成の基本)  >  メインウインドウ  >  M1-3.トレンド系インジケータ作成(終値ライン)作成編
M1-3.トレンド系インジケータ作成(終値ライン)作成編


前回、1-2.トレンド系インジケータ作成(終値ライン)初期コード解説編 のページの続きになります。
カスタムインジケータの初期コードの内容を全て理解した所で、ようやく本題に入れます。
随分と長くなってしまったので、3ページを渡って説明する羽目になってしまいました。

ソースコード:
//+------------------------------------------------------------------+
//|                                                TestCloseLine.mq4 |
//|                                                             yuki |
//|                                      https://yukifx.web.fc2.com/ |
//+------------------------------------------------------------------+
#property copyright "yuki"
#property link      "https://yukifx.web.fc2.com/"
#property version   "1.00"
#property strict // strictは絶対に削除しない事
#property indicator_chart_window // カスタムインジケータをチャートウインドウに表示する

//+------------------------------------------------------------------+
//| OnInit(初期化)イベント
//+------------------------------------------------------------------+
int OnInit()
{
   

   return( INIT_SUCCEEDED );      // 戻り値:初期化成功
}

//+------------------------------------------------------------------+
//| OnCalculate(tick受信)イベント
//| カスタムインジケータ専用のイベント関数
//+------------------------------------------------------------------+
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     &tick_volume[],   // Tick出来高
                const long     &volume[],        // Real出来高
                const int      &spread[])        // スプレッド
{


   return( rates_total ); // 戻り値設定:次回OnCalculate関数が呼ばれた時のprev_calculatedの値に渡される
}



前回説明済みなので、上記の初期コードで分からない所は無い筈ですよね??

カスタムインジケータプロパティ設定


まずはカスタムインジケータのプロパティ設定をします。
この設定をしないとカスタムインジケータを表示させる事が出来ません。

ソースコード:
// インジケータプロパティ設定
#property  indicator_buffers    1               // カスタムインジケータのバッファ数
#property  indicator_color1     clrWhite      // インジケータ1の色
#property  indicator_width1     2               // インジケータ1の太さ

このプロパティ設定は、他プロパティ設定と同様に関数の外に書きます。

終値インジケータ

まだインジケータプロパティ設定しかしていないので、チャート上には何も表示されませんが、
インジケータのプロパティを開くとカスタムインジケータが追加され、デフォルト値が変化していると思います。

今回は使用するカスタムインジケータの本数は1本だけなので、indicator_buffersの値を1にしています。
終値インジケータ

カスタムインジケータ用動的配列作成とバインド

次に、カスタムインジケータ表示用の動的配列を宣言します。

ソースコード:
// インジケータ表示用動的配列
double     _IndBuffer1[];        // インジケータ1表示用動的配列

この動的配列も関数外に書きます。
この変数配列の値がインジケータ表示で使われる事になります。

そして先程プロパティ設定で用意したインジケータバッファに、この変数配列をバインド(紐付け)します。
これはプログラム実行中に1度だけ行う必要があるので、OnInitイベント関数内で行います。

ソースコード:
//+------------------------------------------------------------------+
//| OnInit(初期化)イベント
//+------------------------------------------------------------------+
int OnInit()
{
   SetIndexBuffer( 0, _IndBuffer1 );     // インジケータ1表示用動的配列をインジケータ1にバインドする

   return( INIT_SUCCEEDED );            // 戻り値:初期化成功
}


終値インジケータ

SetIndexBuffer関数を使って動的配列をバインドするのですが、
インジケータのプロパティ画面を見てわかる通り、最初のインジケータバッファの番号が0から始まっています。
なのでSetIndexBufferの第1引数で指定するインジケータバッファインデックスは0を指定します。

またインジケータバッファにバインドされた動的配列は、定義済み変数の動的配列と同様に、
自動的に動的配列の領域が確保されるので、自分で動的配列のサイズ変更する処理は必要ありません。
終値インジケータ

終値ラインインジケータ表示

カスタムインジケータの最低限の設定が終わったので、インジケータ表示用動的配列に終値を設定してみましょう。
終値を設定すると終値ラインが表示されます。
これもプログラム実行中に処理しなければならない内容なので、OnCalculateイベント関数内にプログラムを書きます。

ソースコード:
//+------------------------------------------------------------------+
//| OnCalculate(tick受信)イベント
//| カスタムインジケータ専用のイベント関数
//+------------------------------------------------------------------+
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     &tick_volume[],   // Tick出来高
                const long     &volume[],        // Real出来高
                const int      &spread[])        // スプレッド
{
   _IndBuffer1[0] = Close[0];
   _IndBuffer1[1] = Close[1];
   _IndBuffer1[2] = Close[2];
   _IndBuffer1[3] = Close[3];

   return( rates_total ); // 戻り値設定:次回OnCalculate関数が呼ばれた時のprev_calculatedの値に渡される
}

終値インジケータ

直近4つの終値を設定したので、4本分の終値ラインが表示されました。


では、forループでバーの本数分の設定を行いましょう。

ソースコード:
    int end_index = Bars;  // バー数取得
    for( int icount = 0 ; icount < end_index ; icount++ ) {
       _IndBuffer1[icount] = Close[icount];
    }

終値インジケータ

終値インジケータ

終値ラインが全て表示されるようになりましたが、実は未だ未完成です。
現状では、確定済みのバーに対するインジケータが毎回更新されています。
このカスタムインジケータは確定済みのバーに対しては1度だけデータを設定し、未確定の先頭のバーだけ毎回更新すれば良いので、
先頭のバーだけ常時更新するようにします。
ただ、OnCalculateイベント関数はtick更新時に処理を取りこぼす事がある為、一つ前のバーのインジケータも常時更新した方が無難です。
なので先頭と一つ前のバーだけ常時更新させるようにします。

終値インジケータ

ソースコード:
    int end_index = Bars - prev_calculated;  // バー数取得(未計算分)
    if ( end_index <= 2) {                    // 直近2本は常時更新
        end_index = 2;
    }

    for( int icount = 0 ; icount < end_index ; icount++ ) {
       _IndBuffer1[icount] = Close[icount];
    }


これで無駄な再計算は行われなくなりましたが、最後に不具合対策を行います。

不具合対策(バッファオーバーフロー対策)


インジケータバッファにバインドした動的配列は自動的に動的配列の領域が確保されると言いましたが、
動的配列が確保されない時もあります

それはヒストリカルデータが無い時です。
終値インジケータ

ヒストリカルデータが無い場合、自動で直近のデータが取得されますが、データ取得完了する前にOnCalculateイベント関数が呼ばれた場合、
動的配列が自動確保されていない状態で動的配列にアクセスする事になります。
その結果バッファオーバーフローによる不具合が発生します。

ソースコード:
    int end_index = Bars - prev_calculated;  // バー数取得(未計算分)
    if ( end_index <= 2) {                    // 直近2本は常時更新
        end_index = 2;
    }

    if ( Bars <= 2 ) {                       // ヒストリカルデータ不足時
        return 0;                            // 全て再計算が必要なので、計算済みバー数を0にして終了する
    }

    for( int icount = 0 ; icount < end_index ; icount++ ) {
       _IndBuffer1[icount] = Close[icount];   // インジケータ1に終値を設定
    }

少し雑な不具合対策処理ですが、配列アクセスする前にArraySize関数を使って配列要素数をチェックする方法等もあります。
一般的には配列要素数をチェックして、配列外アクセスさせない処理を入れますが、今回のケースでは動的配列のサイズとBarsが一緒である事を知っていて、 尚且再計算させない配列があるのと、分かり易いプログラムにしたかったので、このような形になりました。
やり方は人それぞれですので、プログラミング未経験者の方はこのやり方に拘る必要はありません、ある程度理解してきたらもっとスマートなコードを書いても問題ありません。


ソースコードのハードコーディングを整理(defineマクロ)


(この項目にはプログラミング未経験者向けの説明が含まれています)

上の項目のソースコードで、インデックス判定に2という数値を使用しています。
ちなみに2という数値を直接書く行為はマジックナンバーと呼ばれます。ただ、EAのマジックナンバーと勘違いしてしまう可能性があるのでハードコーディングと呼びます。
ハードコーディングは良くないコードの書き方ですので整理しましょう。
(とはいえ、適当に作った時はついハードコーディングしてしまい、そのまま放置して忘れてしまう事があります。仕事でやる時はそんな事はありませんが)
ハードコーディングはコードを書いた人にしか意味と影響範囲が分からないので、他人が見たり製作者が数カ月後に見直した時に何の数値が分からなくなってしまいます。
影響範囲が分からない為、変更した時に変更漏れが発生する事もあります。

ちなみに紛らわしいのですが、EAの識別用マジックナンバーとは無関係です

defineマクロを使ってマジックマンバーを置き換えましょう。
まずはdefineを定義をします。

ソースコード:
// マクロ定義
#define  IND_MIN_INDEX    2               // 最小バー数

このマクロは、プロパティ設定と同様に関数の外に書きます。
IND_MIN_INDEXというマクロを定義しましたが、この名称は作成者が自由に名付けて下さい。
それではハードコーディングの2を、IND_MIN_INDEXに置き換えましょう。

ソースコード:
    int end_index = Bars - prev_calculated;  // バー数取得(未計算分)
    if ( end_index <= IND_MIN_INDEX ) {    // 直近数本は常時更新
        end_index = IND_MIN_INDEX;
    }

    if ( Bars <= IND_MIN_INDEX ) {         // ヒストリカルデータ不足時
        return 0;                            // 全て再計算が必要なので、計算済みバー数を0にして終了する
    }

    for( int icount = 0 ; icount < end_index ; icount++ ) {
       _IndBuffer1[icount] = Close[icount];   // インジケータ1に終値を設定
    }


このIND_MIN_INDEXは、コンパイル時(正確にはコンパイルの直前)に2へ置き換えられます。
つまりソースコード上からハードコーディングは無くなりましたが、翻訳された後の機械語には全く影響を与えません。


完成したサンプルソースコード


最後に出来上がったソースコードをまとめると、

ソースコード:
//+------------------------------------------------------------------+
//|                                                TestCloseLine.mq4 |
//|                                                             yuki |
//|                                      https://yukifx.web.fc2.com/ |
//+------------------------------------------------------------------+
#property copyright "yuki"
#property link      "https://yukifx.web.fc2.com/"
#property version   "1.00"
#property strict // strictは絶対に削除しない事
#property indicator_chart_window // カスタムインジケータをチャートウインドウに表示する

// インジケータプロパティ設定
#property  indicator_buffers    1               // カスタムインジケータのバッファ数
#property  indicator_color1     clrWhite      // インジケータ1の色
#property  indicator_width1     2               // インジケータ1の太さ

// マクロ定義
#define    IND_MIN_INDEX         2               // 最小バー数

// インジケータ表示用動的配列
double     _IndBuffer1[];                          // インジケータ1表示用動的配列

//+------------------------------------------------------------------+
//| OnInit(初期化)イベント
//+------------------------------------------------------------------+
int OnInit()
{
   SetIndexBuffer( 0, _IndBuffer1 );     // インジケータ1表示用動的配列をインジケータ1にバインドする

   return( INIT_SUCCEEDED );      // 戻り値:初期化成功
}

//+------------------------------------------------------------------+
//| OnCalculate(tick受信)イベント
//| カスタムインジケータ専用のイベント関数
//+------------------------------------------------------------------+
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     &tick_volume[],   // Tick出来高
                const long     &volume[],        // Real出来高
                const int      &spread[])        // スプレッド
{
    int end_index = Bars - prev_calculated;  // バー数取得(未計算分)
    if ( end_index <= IND_MIN_INDEX ) {    // 直近数本は常時更新
        end_index = IND_MIN_INDEX;
    }

    if ( Bars <= IND_MIN_INDEX ) {         // ヒストリカルデータ不足時
        return 0;                            // 全て再計算が必要なので、計算済みバー数を0にして終了する
    }

    for( int icount = 0 ; icount < end_index ; icount++ ) {
       _IndBuffer1[icount] = Close[icount];   // インジケータ1に終値を設定
    }

   return( rates_total ); // 戻り値設定:次回OnCalculate関数が呼ばれた時のprev_calculatedの値に渡される
}



実際に手を加えたのは19行だけですので非常に短いソースコードです。
これは最低限の内容だけですので、ここから好きなようにカスタマイズしていく事になります。

課題


上記サンプルソースコード変更して、高値ラインのカスタムインジケータを作成して下さい。

■ヒント
High[]




スポンサーリンク
検索

↑の検索エンジンが表示されない人は、
↓の古い検索エンジンを使用して下さい。
カスタム検索
MQL4リファレンスツリー
スポンサーリンク


Copyright ©2015 MT4でEA自作しちゃお~ All Rights Reserved.


Top

inserted by FC2 system