MT5自動売買ソフトの作成6 RCIを作る

RCIとは

RCIとは「Rank Correlation Index」(順位相関指数)の略で、計算式はこんな感じです。

 RCI=(1-(6×d)÷(nの3乗-n))×100
 d:「日付の順位」と「価格の順位」の差を2乗し、合計した数値
 n:期間
 日付の順位:当日(最新の日付)=1、として遡りながら番号付け
 価格の順位:期間中の最高値=1、として、高い順に順位付け

MACDストキャスティクスとは違ってチャートの数値を直接計算に使いません。

RCIは何故か日本のトレーダーに人気があるそうです。

将来作ろうと思っている自動売買ソフトにも使いたいと考えているインジケータの一つです。

これで何がわかるかというと、なんと「売られすぎ」や「買われすぎ」といった相場の過熱度が判断できます。

ストキャスティクスと同じです・・・まぁオシレータ系インジケータなのでそうなりますね。

ただ「番号付け」「順位付け」という部分が曲者でプログラムは難しくなります。

計算式が「「日付の順位」と「価格の順位」の差を2乗し、合計した数値」ですから順位付けしないわけにはいきません。

さてこの「順位付け」ですが、前回のストキャスティクスのプログラムを見たらピンとくると思います。

順位付け=ソートが必要、ならばmapを使ったら良いのではないかということに。

DLLのプログラム

// dllmain.cpp : DLL アプリケーションのエントリ ポイントを定義します。
#include "pch.h"

#include <memory>
#include <vector>
#include <map>

using namespace std;

BOOL APIENTRY DllMain( HMODULE hModule,
                       DWORD  ul_reason_for_call,
                       LPVOID lpReserved
                     )
{
    switch (ul_reason_for_call)
    {
    case DLL_PROCESS_ATTACH:
    case DLL_THREAD_ATTACH:
    case DLL_THREAD_DETACH:
    case DLL_PROCESS_DETACH:
        break;
    }
    return TRUE;
}

extern "C" __declspec(dllexport) void dllRciArray(const int size, const int period, double* close, double* out)
{
    for (int i = period; i <= size; i++) {
        // RCI=(1-(6×d)÷(nの3乗-n))×100
        // d:「日付の順位」と「価格の順位」の差を2乗し、合計した数値
        // n:期間
        // 日付の順位:当日(最新の日付)=1、として遡りながら、2,3,4・・・と順位付け
        // 価格の順位:期間中の最高値=1、として、高い順に2,3,4・・・と順位付け
        //
        // ・RCIがプラスのゾーンにある時:上昇局面
        // ・RCIがマイナスのゾーンにある時:下落局面
        // 
        std::map<double, int> rciMap;
        for (int loop = 0; loop < period; loop++) {
            rciMap[close[i+loop-period+1] + static_cast<double>(loop + 1) / 10000000.0] = period - loop;
        }
        auto    n = period;
        double d = 0;
        for (auto it = rciMap.begin(); it != rciMap.end(); ++it) {
            d += (it->second - n) * (it->second - n);
            n--;
        }
        out[i] = (1 - ((6 * d) / (period * (period * period - 1)))) * 100.0;
        out[i] = (out[i] > 100.0) ? 100.0 : (out[i] < -100.0) ? -100.0 : out[i];
    }
}

このmap(std::map<double, int> rciMap)がRCIの順位付けを行います。

キーにclose値を、値に日付の順位を登録しています。

日付の順位はperiod - loopを登録しています。古い順にするために期間からループカウントを引いています。

キーはrciMap[close[i+loop-period+1] + static_cast<double>(loop + 1) / 10000000.0]です。

close[i+loop-period+1]はclose値ですが、static_cast<double>(loop + 1) / 10000000.0は何でしょう?

苦肉の策です・・・

mapは同じキー値を登録すると、キー値は2つにならないで1つのまま値が書き換えられます。

期間中のclose値が同じ場合があると・・まずいですよね。1つ減ってしまいます、日付の順位がなくなるということです。

そのため日付の番号=loop+1(逆順ですが気にしない)をソートに影響がないように、大きな値で割って小さくして足しておきます。これでキー値の重複は起きません。

ちなみにstatic_cast<double>(loop + 1)は、C言語では(double)(loop + 1)と書く、ただのキャストです。

もう一つ初見があると思います、for (auto it = rciMap.begin(); it != rciMap.end(); ++it)ですね。

このfor文ではmapの最初から最後までループしています。

auto it = rciMap.begin()で「it」に最初の項目を、it != rciMap.end()で最後(項目がなくなるまで)の項目まで回します。「it」をイテレータと言います。

イテレータの変数型は「auto」になっています。autoは左辺を右辺の式から推定される型にしますので、mapのイテレータ型が返されます、

mapのイテレータでは、first=キー、second=値がポインタでアクセスできます。

mapはソートされますので、it->second - nでは「値=日付の順位 ー キーの順:価格の(高い方からの)順位」が計算されます。

      out[i] = (1 - ((6 * d) / (period * (period * period - 1)))) * 100.0;
        out[i] = (out[i] > 100.0) ? 100.0 : (out[i] < -100.0) ? -100.0 : out[i];

あとは計算結果を出力エリアに入れるだけですね。

インジケータのプログラム

//+------------------------------------------------------------------+
//|                                                         rciChart.mq5 |
//|                                      Copyright 2023, MetaQuotes Ltd. |
//|                                                 https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2023, MetaQuotes Ltd."
#property link      "https://www.mql5.com"
#property version   "1.00"
#property indicator_separate_window
#property indicator_buffers 2
#property indicator_plots   2
//--- plot rciChart
#property indicator_label1  "rciChart"
#property indicator_type1   DRAW_LINE
#property indicator_color1  clrRoyalBlue
#property indicator_style1  STYLE_SOLID
#property indicator_width1  1

#property indicator_label2  "rciChart"
#property indicator_type2   DRAW_LINE
#property indicator_color2  clrOrange
#property indicator_style2  STYLE_SOLID
#property indicator_width2  1

#define barCount    10000

//--- indicator buffers
double  rciChartBuffer1[];
double  rciChartBuffer2[];

double  closeBuffer[barCount];
double  middleBuffer[barCount];
double  shortBuffer[barCount];

#import  "D:\\Projects\\Dll_test\\x64\\Debug\\Dll_test.dll"
    void dllRciArray(const int size, const int period, const double& close[], double& out[]);
#import  

//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int OnInit()
{
//--- indicator buffers mapping
    SetIndexBuffer(0, rciChartBuffer1, INDICATOR_DATA);
    SetIndexBuffer(1, rciChartBuffer2, INDICATOR_DATA);

    ArraySetAsSeries(rciChartBuffer1, true);
    ArraySetAsSeries(rciChartBuffer2, true);
//---
    return(INIT_SUCCEEDED);
}

//+------------------------------------------------------------------+
//| Custom indicator iteration function                             |
//+------------------------------------------------------------------+
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[],
                 const long &volume[],
                 const int &spread[])
  {
//---
    ArraySetAsSeries(close, true);

    int size = rates_total - prev_calculated;
    size = (size > barCount) ? barCount : size;
    ArrayCopy(closeBuffer, close, 0, 0, size);

    dllRciArray(size, 24, closeBuffer, middleBuffer);
    ArrayCopy(rciChartBuffer1, middleBuffer, 0, 0, size);

    dllRciArray(size, 9, closeBuffer, shortBuffer);
    ArrayCopy(rciChartBuffer2, shortBuffer, 0, 0, size);

//--- return value of prev_calculated for next call
    return(rates_total);
}

//+------------------------------------------------------------------+

もうMQL5のプログラムの説明はいらないと思います。どれも同じ感じですからね。

ビルドして実行しましょう。

RCIはMT5に最初から入っていませんので、MQL5 communityなどで探してみてください。

比べているRCIは3本線のものですが、期間24と期間9にして一緒に表示しています。

どうでしょうか、合っていますでしょうか?

「売られすぎ」や「買われすぎ」の仲間、ストキャスティクスと比べてみましょう。

期間9(オレンジ)は似ている動きをしていますが、RCIの方が滑らかに動く感じです。

次回

次回は、MQL5中心になりますが、パラメータの追加についてです。