トップ  >  自作してみる >  MQL4未経験者向け(インジケータ作成の基本)  >  サブウインドウ  >  S2.オシレーター系インジケータ作成(自作RCI3本表示)
S2.オシレーター系インジケータ作成(自作RCI3本表示)


今回はサブウインドウに表示するオシレーター系インジケータのRCIを作成します。

いつも通りに今回も他カスタムインジケータをベースにして新しいカスタムインジケータを作成します。
それでは以前作成したRSIのカスタムインジケータのソースコードのTestSubRSI.mq4をコピペして、
ファイル名をTestSubSelfRCI.mq4に変更しましょう。

RCIの計算式からロジックを考える

RCIは事前定義されているテクニカルインジケータ関数に無いので、自作する必要があります。
前回自作した時は単純移動平均だったので、そんなに考える事無く作る事が出来ました。

RCI(Rank Correlation Index)のように考えて分かるような類では無い場合は、まずは計算式を調べる必要があります。

インジケータRCI

d:日付順位と価格順位の順位差を2乗し、合計した数値
n:算出期間


この計算式からまずどんなデータが必要かを考えます。

①日付データ
②価格データ
③日付順位(ランク)
④価格順位(ランク)

まずはこの4つのデータが必要です。
データは算出期間分取得しなければなりません。
更にランク付けしなければならない為、データをソートしなければなりません。
また価格データで同値が複数ある場合、価格順位(ランク)は中間値になります。
(中間値の情報提供ありがとうございました。)

ソートしてランク付けしたデータを取得出来なければRCIを算出する事が出来ません。

struct(構造体)型を宣言する

4種類のデータ配列が必要で、それをソートしなければならない事が分かっているので、 データ取得用の変数はstruct(構造体)を使用します。

struct(構造体)はデータ管理し易くなる非常に便利なものですので、 プログラミング未経験者はなるべく使うようにしましょう。
構造体は簡単に言うと、複数のデータをひとまとめにして取り扱う事が出来る型です。

構造体を使うにはまず、構造体の型を宣言する必要があります。

必要なデータは4つですが、価格順位は同値調整が必要なので価格順位(調整後)を含めて5つデータを用意します。

ソースコード:
// 構造体型宣言
struct struct_rci_data {                         // RCI算出用構造体
    datetime date_value;                        // 日付
    double   rate_value;                        // 価格
    int      rank_date;                         // 日付順位
    int      rank_rate;                         // 価格順位
    double   rank_adjust_rate;                  // 価格順位(調整後)
};

これは関数の外を書きます。
struct_rci_dataという名の構造体型が宣言されました。

このstruct_rci_dataという型を使って変数を宣言すると使えるようになります。
変数宣言方法は、他変数宣言とやり方は一緒です。
実際の使い方は、RCIを算出する時に説明します。


enum列挙型を宣言する

RCIは3本表示して使う人が多いので、最初から3本表示出来るように作ってしまいます。
1本表示にして、チャートにRCIインジケータを3回適用すれば済む話ですが、
せっかく自作するのなら使い勝手が良いように最初から3本表示させます。
ついでに表示本数を1~3本から選択出来るようにします。

このように指定したデータを選択して使うようにさせるにはenum列挙型を使います。
enum列挙型も既に紹介済みなので、詳細説明は割愛します。
今までは事前に用意されたenum列挙型を使用していましたが、
今回は上記構造体と同様に、enum列挙型を自作して宣言します。

ソースコード:
// enum列挙型宣言
enum ENUM_LINECOUNT {                           // RCI表示本数
    RCI_DISPLINE_1 = 1,                         // 1本(短期のみ)
    RCI_DISPLINE_2,                             // 2本(短期,中期)
    RCI_DISPLINE_3                              // 3本(短期,中期,長期)
};

これは関数の外にソースコードを書きます。
ENUM_LINECOUNT という名のenum列挙型が宣言されました。

インプットパラメータを追加する

インプットパラメータは短期・中期・長期の算出期間設定と、
RCI表示本数設定にします。

ソースコード:
// インプットパラメータ
input  int              _InputCalPeriod_S      = 9;                 // 算出期間[短期]
input  int              _InputCalPeriod_M      = 26;                // 算出期間[中期]
input  int              _InputCalPeriod_L      = 52;                // 算出期間[長期]
sinput ENUM_LINECOUNT _InputLineCount        = RCI_DISPLINE_3;  // RCI表示本数

RCI表示本数の型は、先程宣言した自作enum列挙型を使用しています。
enum列挙型の変数を宣言した場合、設定出来る値はそのenum列挙の定数リストしか使えません。
なので初期値はENUM_LINECOUNT列挙の定数リストのRCI_DISPLINE_3を設定しています。

インプットパラメータを変えただけなので、今時点でコンパイルをすると_InputCalPeriodを使用している箇所でエラーになります。
なるべくコンパイルが通る状態を保っていたいので、_InputCalPeriodを使用している部分を削除しましょう。

ソースコード:
    // temp_indname = StringFormat( "%s(%d)" , file_name , _InputCalPeriod ); // インジケータ短縮名

まずはOnInit()関数内で使用している部分を削除します。

ソースコード:
/*
        double get_value = iRSI (           // RSI算出
                             Symbol(),      // 通貨ペア
                             Period(),      // 時間軸
                             _InputCalPeriod, // 算出期間
                             PRICE_CLOSE,   // 適用価格
                             icount          // シフト
                        );
        _IndBuffer1[icount] = get_value;       // インジケータ1に算出結果を設定
*/

続いてOnCalculate()関数内で使用している部分を丸ごと削除します。


これでコンパイルが通るようになった筈ですので、動かしてプロパティを確認してみましょう。

インジケータRCI

パラメータの入力画面はこのようになります。

それから_InputLineCountだけinputでは無く、sinputを使用していますが、
これはストラテジーテスターでパラメータの最適化する必要が無いものに対して使います。

インジケータRCI

パラメータの最適化については別途説明しますが、基本的にEAで行う作業です。
今はカスタムインジケータ作成をしているので、特別気にする必要はありません。


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

インジケータが最大3本表示されるので、インジケータプロパティを調整します。

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

#property  indicator_color2     clrYellow      // インジケータ2の色
#property  indicator_width2     1               // インジケータ2の太さ

#property  indicator_color3     clrMagenta     // インジケータ3の色
#property  indicator_width3     1               // インジケータ3の太さ

#property  indicator_minimum  -100               // サブウインドウスケール下限値設定
#property  indicator_maximum   100               // サブウインドウスケール上限値設定

// レベルライン設定
#property  indicator_level1     0                 // レベルライン1
#property  indicator_level2    80                 // レベルライン2
#property  indicator_level3   -80                 // レベルライン3

カスタムインジケータのバッファ数(indicator_buffers)の値はプログラムの処理の途中で変更する事は出来ない為、
最大表示数の3を設定します。

RCIの範囲は -100 ~ 100 なので、サブウインドウスケール下限値設定(indicator_minimum)は-100を設定します。

レベルライン(indicator_level1~3)は、中心値と80%の閾値用に設定します。

コンパイルして、動作チェックしてみましょう。

インジケータRCI

サブウインドウの範囲が -100 ~ 100になって、レベルラインが3本表示されていますね。

インジケータRCI

プロパティの"色の設定"で、インジケータが3本になって各種デフォルト色が設定されている事を確認します。

インジケータ短縮名設定

今回はインプットパラメータが多い為、インジケータの短縮名を作成する処理が長くなります。
処理が長いので、短縮名を作成する関数を作成しましょう。

ソースコード:
//+------------------------------------------------------------------+
//| インジケータ短縮名を取得する
//+------------------------------------------------------------------+
string GetShortName()
{
    string file_name;                           // ファイル名(拡張子無し)
    string str_array[];                         // 文字列分離取得用動的配列
    string input_str;                           // インプットパラメータ用文字列
    string ret_name;                            // インジケータ短縮名
    int    get_arraycount;                      // 分割配列数
    
    get_arraycount = StringSplit( __FILE__ , '.' , str_array );  // ファイル名を拡張子で分離
    
    if ( get_arraycount > 0 ) {                 // 文字列分離成功していた場合
        file_name = str_array[0];               // ファイル名に分離した最初の文字列を設定
    }
    
    // インプットパラメータ値を結合した文字列を作成
    input_str = (string)_InputCalPeriod_S;

    if ( _InputLineCount >= RCI_DISPLINE_2 ) {
        input_str += StringFormat( ",%d" , _InputCalPeriod_M );
    }

    if ( _InputLineCount >= RCI_DISPLINE_3 ) {
        input_str += StringFormat( ",%d" , _InputCalPeriod_L );
    }
    
    ret_name = StringFormat( "%s(%s)" , file_name , input_str ); // インジケータ短縮名

    return ret_name;

}


GetShortName()関数を定義して、OnInit()関数内の処理を持ってきました。
少し処理を増やしていますが、これはインプットパラメータでRCI表示本数によって、短縮名が変わるようにしています。

次はOnInit()の処理を変更して、GetShortName()関数を呼ぶようにしましょう。

ソースコード:
//+------------------------------------------------------------------+
//| OnInit(初期化)イベント
//+------------------------------------------------------------------+
int OnInit()
{
    IndicatorSetString( INDICATOR_SHORTNAME  , GetShortName() );// インジケータ短縮名設定

    SetIndexBuffer( 0, _IndBuffer1 );     // インジケータ1表示用動的配列をインジケータ1にバインドする

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


OnInit()関数内がだいぶスッキリしましたね。

それではコンパイルして動作チェックしましょう、インプットパラメータでRCI表示本数を変えると短縮名が変わります。

■RCI3本表示設定時
インジケータRCI


■RCI1本表示設定時
インジケータRCI

カスタムインジケータ用動的配列追加

表示するインジケータが最大3本になったので、カスタムインジケータ用動的配列追加を追加します。

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


動的配列を追加したら、バインドする事も忘れずに行います。
OnInit()関数内で処理を追加します。

ソースコード:
    SetIndexBuffer( 0, _IndBuffer1 );     // インジケータ1表示用動的配列をインジケータ1にバインドする
    SetIndexBuffer( 1, _IndBuffer2 );     // インジケータ2表示用動的配列をインジケータ2にバインドする
    SetIndexBuffer( 2, _IndBuffer3 );     // インジケータ3表示用動的配列をインジケータ3にバインドする


コンパイルしても動作確認出来る内容はありませんが、コンパイルが通る事だけ確認しておきます。


RCI算出関数を定義する

以前紹介した自作SMA()関数と同様に、RCI算出するCalRCI()関数を定義します。

ソースコード:
//+------------------------------------------------------------------+
//| RCI(Rank Correlation Index)を算出する[配列ベース]
//+------------------------------------------------------------------+
double CalRCI( 
                const double &in_array[],         // インプット変数配列アドレス
                       int     in_time_period ,    // 算出期間
                       int     in_index            // インデックス
                       )
{
    double ret = 0;   // 戻り値

    int    array_count = ArraySize(in_array);       // 配列要素数取得
    int    end_index = in_index + in_time_period;    // ループエンド

    if ( end_index < array_count ) {                  // 配列範囲内チェック

        //ここに処理を追加する

    }

    return ret;       // 戻り値を返す
}


引数と戻り値は自作SMA()関数と全く一緒です。
インプット変数配列と算出期間から配列範囲外にアクセスさせない処理も全く一緒です。
ここからRCI算出処理を追加していきます。

構造体配列を宣言する

まずこのページ序盤で宣言した自作構造体のstruct_rci_data型の変数を宣言します。

ソースコード:
    struct_rci_data  temp_st_rci[];                                         // RCI算出用構造体動的配列
    int arrayst_count = ArrayResize(                    // 動的配列サイズ変更
                                       temp_st_rci ,      // 変更する配列
                                       in_time_period ,   // 新しい配列サイズ
                                       0                  // 予備サイズ
                                      );


CalRCI()関数内の配列範囲内チェック処理内にこの追加します。

このページの序盤でも説明しましたが、RCIを算出するには

①日付データ
②価格データ
③日付順位(ランク)
④価格順位(ランク)

上記データを算出期間分取得しなければなりません。
その為、構造体型変数のtemp_st_rciは動的配列で宣言しています。

必要な配列サイズは算出期間分なので、ArrayResize()関数で算出期間分の配列を確保しています。

インジケータRCI

算出期間のin_time_periodが5の場合、動的配列が5つ確保されるので、上記イメージのような配列が確保されます。

構造体メンバにデータ設定する

構造体内の変数を構造体メンバと呼びます。
構造体メンバにアクセスするには構造体型変数のあとにピリオド(.)を付けてアクセスします。

データを取得してtemp_st_rciの各メンバにデータを設定します。

ソースコード:
        // データ設定
        int temp_rank = 1;
        int arr_count = 0;
        for ( int icount = in_index ; icount < end_index ; icount++  ) {
            temp_st_rci[arr_count].date_value = Time[icount];        // 日付データ設定
            temp_st_rci[arr_count].rate_value = in_array[icount];    // 価格データ設定
            temp_st_rci[arr_count].rank_date  = temp_rank;           // 日付順位(ランク)設定
            temp_rank++;
            arr_count++;
        }


先程追加した処理の後に、上記処理を追加します。

日付データは定義済み変数のTime[]の値を設定します。
価格データは以前紹介した自作SMA()関数と同様に、引数の変数配列から設定します。
日付順位(ランク)は、インデックスの小さい方が新しい日付になるので、設定順の順番でランク付けします。

インジケータRCI

仮に1時間足のドル円で動作させた場合、上記のようになります
(まだCalRCI()関数の呼び出す処理がない為、あくまで例です)。


構造体型変数配列をソートする

価格順位(ランク)は価格データを基にランク付けしなければならない為、まずは価格データで配列をソートします。

ソースコード:
        // 価格順ソート
        for ( int main_count = 0; main_count < arrayst_count - 1 ; main_count++ ) {
            for ( int sub_count = main_count + 1; sub_count < arrayst_count ; sub_count++ ) {

                // 次の配列メンバと比較して小さい場合
                if ( temp_st_rci[main_count].rate_value < temp_st_rci[sub_count].rate_value ) {

                    // 構造体配列を入れ替える
                    struct_rci_data temp_swap  = temp_st_rci[main_count];      // 比較元のデータ退避
                    temp_st_rci[main_count]    = temp_st_rci[sub_count];       // 比較元のデータ入れ替え
                    temp_st_rci[sub_count]     = temp_swap;                    // 比較先に退避データをセット
                }
            }
        }


価格データのrate_valueを比較して、比較先よりも値が小さい場合、構造体型変数配列をスワップしています。

インジケータRCI

これを配列全てに対して行っています。

(ちなみに余談ですが、構造体では無く多次元配列でデータを持っていた場合、ArraySort()関数で簡単にソート出来ます。)



価格ソートが終わったら、ソートされた順番でランク付けをします。
価格順位(調整後ランク)は後で調整するので、今は価格順位(ランク)と同じ値を設定します。

ソースコード:
        // 価格RANK設定
        for( int main_count = 0; main_count < arrayst_count ; main_count++ ) {
            int temp_set_rank = main_count + 1;
            temp_st_rci[main_count].rank_rate        = temp_set_rank;
            temp_st_rci[main_count].rank_adjust_rate = (double)temp_set_rank;
        }


価格順位(ランク)は、価格ソート後なのでインデックスの小さい方が高い価格になるので、設定順の順番でランク付けします。

インジケータRCI


同値ランクの中間値を設定する

価格データで同値が複数ある場合、価格順位(ランク)は中間値に調整しなければならないので、
価格順位でソートしたデータを上からチェックして、同値の価格データの価格順位を調整します。

ソースコード:
        // 価格RANKの同値調整
        for ( int main_count = 0 ; main_count < arrayst_count - 1 ; main_count++ ) {
            double sum_rank   = (double)temp_st_rci[main_count].rank_rate;     // ランクサマリー
            int    same_count = 0;                                              // 同値検出カウント

            for ( int sub_count = main_count + 1 ; sub_count < arrayst_count ; sub_count++ ) {

                if ( temp_st_rci[main_count].rate_value == temp_st_rci[sub_count].rate_value ) { // 同値の場合
                    sum_rank += (double)temp_st_rci[sub_count].rank_rate;       // ランクサマリーにランクを加算
                    same_count++;                                                // 同値検出カウントをインクリメント
                } else {                                                        // 同値以外の場合

                    break;                                                      // 同値の場合forループから抜ける
                }
            }

            if ( same_count >= 1 ) {                                             // 同値価格が1つ以上ある場合
                double set_adjust_rank = sum_rank / ((double)same_count + 1);   // ランクの中間値を算出

                for( int ad_count = 0 ; ad_count <= same_count; ad_count++ ) {  // 同値検出カウント分ループ
                    // 価格順位(調整後)に中間値を設定
                    temp_st_rci[ad_count + main_count].rank_adjust_rate = set_adjust_rank; 
                }

                main_count += same_count;                         // メインループを同値検出カウント分スキップさせる
            }
        }


インジケータRCI


これでRCI算出に必要なデータが揃いました。
やっとRCIを算出する事が出来ますw


上記ソースコードのif文判定で注意しなければならない処理があります。
    if ( temp_st_rci[main_count].rate_value == temp_st_rci[sub_count].rate_value ) { // 同値の場合


一見すると何も問題なさそうですが、double型の値の同値(完全一致)判定は本来であれば使うべきではありません。
今回はrate_valueに設定される値が、小数点第5位までの精度という事が分かっているので完全一致判定を使っても問題が発生しませんが、 判定する値が除算によって求めれらた値の場合、除算結果が割り切れない値の時に完全一致判定がうまく動作しない事があります。
詳細はdoubleのリファレンスを見て下さい。


またbreak処理は最も近いループから抜けます。
ループを一つ抜けるだけで、全てのループから抜ける訳ではないので注意して下さい。

インジケータRCI


RCIを算出する

もう特に説明は要らないと思いますが、RCIの計算式通りにプログラムを書くだけです。


インジケータRCI

d:日付順位と価格順位の順位差を2乗し、合計した数値
n:算出期間
価格順位は、価格順位(調整後)の値を使用します。

ソースコード:
        // RCIのd算出
        double sum_d     = 0;
        double temp_diff = 0;
        for( int main_count = 0; main_count < arrayst_count ; main_count++ ) {
            temp_diff = (double)temp_st_rci[main_count].rank_date - temp_st_rci[main_count].rank_adjust_rate;
            sum_d += MathPow( temp_diff , 2 );
        }

        // RCIのn(n^2 - 1)算出
        int temp_div = in_time_period * ( (int)MathPow( in_time_period , 2 ) - 1 );

        // RCIを算出
        if ( temp_div > 0) {            // 0除算対策
            ret = 100 * ( 1 - ( 6 * sum_d / (double)temp_div ) );
        }


もうこれぐらいのプログラムであれば説明不要ですよね??
MathPow()関数が出てきましたが、これは数学関数べき乗の計算を行ってくれます。

RCI算出部分で、sum_dとtemp_divをdouble型キャストしているのは、
int型変数を除算すると小数点以下が丸められてしまうからです。



CalRCI()関数はこれでやっと完成です。

RCIラインを表示する

完成したCalRCI()関数を呼び出してRCIラインを表示させましょう。
OnCalculate()関数内のiRSI()関数呼び出しを削除した所に以下ソースコードを追加します。

ソースコード:
         double get_value;

         // 短期RCIを算出してインジケータバッファに設定
         get_value = CalRCI (                   // RCI算出
                             Close,            // 変数配列アドレス
                             _InputCalPeriod_S, // 算出期間
                             icount             // シフト
                        );

        _IndBuffer1[icount] = get_value;       // インジケータ1に算出結果を設定

        // 中期RCIを算出してインジケータバッファに設定
        if ( _InputLineCount >= RCI_DISPLINE_2 ) {
             get_value = CalRCI (                   // RCI算出
                                 Close,            // 変数配列アドレス
                                 _InputCalPeriod_M, // 算出期間
                                 icount             // シフト
                            );

            _IndBuffer2[icount] = get_value;       // インジケータ2に算出結果を設定
        }

        // 長期RCIを算出してインジケータバッファに設定
        if ( _InputLineCount >= RCI_DISPLINE_3 ) {
             get_value = CalRCI (                   // RCI算出
                                 Close,            // 変数配列アドレス
                                 _InputCalPeriod_L, // 算出期間
                                 icount             // シフト
                            );

            _IndBuffer3[icount] = get_value;       // インジケータ3に算出結果を設定
        }


コンパイルして動作チェックしてみましょう。

インジケータRCI

RCI3本表示と、2本表示と、1本表示の3パターンでチェックしてインプットパラメータが機能しているか確認します。

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


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

ソースコード:
//+------------------------------------------------------------------+
//|                                               TestSubSelfRCI.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_separate_window // カスタムインジケータをサブウインドウに表示する

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

#property  indicator_color2     clrYellow     // インジケータ2の色
#property  indicator_width2     1               // インジケータ2の太さ

#property  indicator_color3     clrMagenta    // インジケータ3の色
#property  indicator_width3     1               // インジケータ3の太さ

#property  indicator_minimum  -100              // サブウインドウスケール下限値設定
#property  indicator_maximum   100              // サブウインドウスケール上限値設定

// レベルライン設定
#property  indicator_level1     0               // レベルライン1
#property  indicator_level2    80               // レベルライン2
#property  indicator_level3   -80               // レベルライン3

#property  indicator_levelcolor   clrGray     // レベルラインの色
#property  indicator_levelstyle STYLE_DOT     // レベルラインの種類
#property  indicator_levelwidth         1      // レベルラインの太さ


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

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

// 構造体型宣言
struct struct_rci_data {                         // RCI算出用構造体
    datetime date_value;                        // 日付
    double   rate_value;                         // 価格
    int      rank_date;                          // 日付順位
    int      rank_rate;                          // 価格順位
    double   rank_adjust_rate;                  // 価格順位(調整後)
};

// enum列挙型宣言
enum ENUM_LINECOUNT {                           // RCI表示本数
    RCI_DISPLINE_1 = 1,                         // 1本(短期のみ)
    RCI_DISPLINE_2,                             // 2本(短期,中期)
    RCI_DISPLINE_3                              // 3本(短期,中期,長期)
};

// インプットパラメータ
input  int              _InputCalPeriod_S      = 9;                  // 算出期間[短期]
input  int              _InputCalPeriod_M      = 26;                 // 算出期間[中期]
input  int              _InputCalPeriod_L      = 52;                 // 算出期間[長期]
sinput ENUM_LINECOUNT _InputLineCount        = RCI_DISPLINE_3;   // RCI表示本数

//+------------------------------------------------------------------+
//| OnInit(初期化)イベント
//+------------------------------------------------------------------+
int OnInit()
{
    IndicatorSetString( INDICATOR_SHORTNAME  , GetShortName() );// インジケータ短縮名設定

    SetIndexBuffer( 0, _IndBuffer1 );     // インジケータ1表示用動的配列をインジケータ1にバインドする
    SetIndexBuffer( 1, _IndBuffer2 );     // インジケータ2表示用動的配列をインジケータ2にバインドする
    SetIndexBuffer( 2, _IndBuffer3 );     // インジケータ3表示用動的配列をインジケータ3にバインドする

    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++ ) {
         double get_value;

         // 短期RCIを算出してインジケータバッファに設定
         get_value = CalRCI (                   // RCI算出
                             Close,            // 変数配列アドレス
                             _InputCalPeriod_S, // 算出期間
                             icount             // シフト
                        );

        _IndBuffer1[icount] = get_value;       // インジケータ1に算出結果を設定

        // 中期RCIを算出してインジケータバッファに設定
        if ( _InputLineCount >= RCI_DISPLINE_2 ) {
             get_value = CalRCI (                   // RCI算出
                                 Close,            // 変数配列アドレス
                                 _InputCalPeriod_M, // 算出期間
                                 icount             // シフト
                            );

            _IndBuffer2[icount] = get_value;       // インジケータ2に算出結果を設定
        }

        // 長期RCIを算出してインジケータバッファに設定
        if ( _InputLineCount >= RCI_DISPLINE_3 ) {
             get_value = CalRCI (                   // RCI算出
                                 Close,            // 変数配列アドレス
                                 _InputCalPeriod_L, // 算出期間
                                 icount             // シフト
                            );

            _IndBuffer3[icount] = get_value;       // インジケータ3に算出結果を設定
        }
    }

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

//+------------------------------------------------------------------+
//| RCI(Rank Correlation Index)を算出する[配列ベース]
//+------------------------------------------------------------------+
double CalRCI( 
                const double &in_array[],         // インプット変数配列アドレス
                       int     in_time_period ,    // 算出期間
                       int     in_index            // インデックス
                       )
{
    double ret = 0;   // 戻り値

    int    array_count = ArraySize(in_array);       // 配列要素数取得
    int    end_index = in_index + in_time_period;    // ループエンド

    if ( end_index < array_count ) {                  // 配列範囲内チェック

        struct_rci_data  temp_st_rci[];                       // RCI算出用構造体動的配列
        int arrayst_count = ArrayResize(                    // 動的配列サイズ変更
                                           temp_st_rci ,      // 変更する配列
                                           in_time_period ,   // 新しい配列サイズ
                                           0                  // 予備サイズ
                                          );

        // データ設定
        int temp_rank = 1;
        int arr_count = 0;
        for ( int icount = in_index ; icount < end_index ; icount++  ) {
            temp_st_rci[arr_count].date_value = Time[icount];        // 日付データ設定
            temp_st_rci[arr_count].rate_value = in_array[icount];    // 価格データ設定
            temp_st_rci[arr_count].rank_date  = temp_rank;           // 日付順位(ランク)設定
            temp_rank++;
            arr_count++;
        }


        // 価格順ソート
        for ( int main_count = 0; main_count < arrayst_count - 1 ; main_count++ ) {
            for ( int sub_count = main_count + 1; sub_count < arrayst_count ; sub_count++ ) {

                // 次の配列メンバと比較して小さい場合
                if ( temp_st_rci[main_count].rate_value < temp_st_rci[sub_count].rate_value ) {

                    // 構造体配列を入れ替える
                    struct_rci_data temp_swap  = temp_st_rci[main_count];      // 比較元のデータ退避
                    temp_st_rci[main_count]    = temp_st_rci[sub_count];       // 比較元のデータ入れ替え
                    temp_st_rci[sub_count]     = temp_swap;                    // 比較先に退避データをセット
                }
            }
        }


        // 価格RANK設定
        for( int main_count = 0; main_count < arrayst_count ; main_count++ ) {
            int temp_set_rank = main_count + 1;
            temp_st_rci[main_count].rank_rate = temp_set_rank;
            temp_st_rci[main_count].rank_adjust_rate = (double)temp_set_rank;
        }


        // 価格RANKの同値調整
        for ( int main_count = 0 ; main_count < arrayst_count - 1 ; main_count++ ) {
            double sum_rank   = (double)temp_st_rci[main_count].rank_rate;     // ランクサマリー
            int    same_count = 0;                                              // 同値検出カウント

            for ( int sub_count = main_count + 1 ; sub_count < arrayst_count ; sub_count++ ) {

                if ( temp_st_rci[main_count].rate_value == temp_st_rci[sub_count].rate_value ) { // 同値の場合
                    sum_rank += (double)temp_st_rci[sub_count].rank_rate;       // ランクサマリーにランクを加算
                    same_count++;                                                // 同値検出カウントをインクリメント
                } else {                                                        // 同値以外の場合

                    break;                                                      // 同値の場合forループから抜ける
                }
            }

            if ( same_count >= 1 ) {                                             // 同値価格が1つ以上ある場合
                double set_adjust_rank = sum_rank / ((double)same_count + 1);   // ランクの中間値を算出

                for( int ad_count = 0 ; ad_count <= same_count; ad_count++ ) {  // 同値検出カウント分ループ
                    // 価格順位(調整後)に中間値を設定
                    temp_st_rci[ad_count + main_count].rank_adjust_rate = set_adjust_rank; 
                }

                main_count += same_count;                         // メインループを同値検出カウント分スキップさせる
            }
        }


        // RCIのd算出
        double sum_d = 0;
        double temp_diff = 0;
        for( int main_count = 0; main_count < arrayst_count ; main_count++ ) {
            temp_diff = (double)temp_st_rci[main_count].rank_date - temp_st_rci[main_count].rank_adjust_rate;
            sum_d += MathPow( temp_diff , 2 );
        }

        // RCIのn(n^2 - 1)算出
        int temp_div = in_time_period * ( (int)MathPow( in_time_period , 2 ) - 1 );

        // RCIを算出
        if ( temp_div > 0) {            // 0除算対策
            ret = 100 * ( 1 - ( 6 * sum_d / (double)temp_div ) );
        }


    }

    return ret;       // 戻り値を返す
}

//+------------------------------------------------------------------+
//| インジケータ短縮名を取得する
//+------------------------------------------------------------------+
string GetShortName()
{
    string file_name;                           // ファイル名(拡張子無し)
    string str_array[];                         // 文字列分離取得用動的配列
    string input_str;                           // インプットパラメータ用文字列
    string ret_name;                            // インジケータ短縮名
    int    get_arraycount;                      // 分割配列数
    
    get_arraycount = StringSplit( __FILE__ , '.' , str_array );  // ファイル名を拡張子で分離
    
    if ( get_arraycount > 0 ) {                 // 文字列分離成功していた場合
        file_name = str_array[0];               // ファイル名に分離した最初の文字列を設定
    }
    
    // インプットパラメータ値を結合した文字列を作成
    input_str = (string)_InputCalPeriod_S;

    if ( _InputLineCount >= RCI_DISPLINE_2 ) {
        input_str += StringFormat( ",%d" , _InputCalPeriod_M );
    }

    if ( _InputLineCount >= RCI_DISPLINE_3 ) {
        input_str += StringFormat( ",%d" , _InputCalPeriod_L );
    }
    
    ret_name = StringFormat( "%s(%s)" , file_name , input_str ); // インジケータ短縮名

    return ret_name;

}




スポンサーリンク
検索

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


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


Top

inserted by FC2 system