MT5自動売買ソフトの作成5 ストキャスティクスを作る

ストキャスティクスとは

通常のストキャスティクスは下記の線で構成されています。

%K =(当日終値-n日間の最安値)÷(n日間の最高値-n日間の最安値)×100
%D = %Kの単純移動平均
Slow%D = %Dの単純移動平均

当日の終値が過去数日間のどの位置にあるのかがわかります。

ストキャスティクスでは「売られすぎ」や「買われすぎ」といった相場の過熱度が判断できます。オシレーター系の基本ですね。

オシレーター系インジケータの特徴ですが、トレンドに入るとずっと買われすぎ、ずっと売られすぎ状態になってしまいます。なのでレンジ状態(行ったり来たりの状態)でのみ参考になります。

前回のMACDはオシレータ系に入っていますが、指数移動平均がベースなのでトレンド系に近いとインジケータです。そのため、ストキャスティクスMACDと組み合わせて使われることがあります。

今回作るストキャスティクス

%D(Slow%Kとも言います)のみを使います。ただし1本ではなく4期間の4本編みにします。

4本のストキャスティクスが纏まるところがシグナルになる感じですね。

想像つかないと思いますので画像を。

4本の各期間は、9、14、38、62にしています。(通貨ペアで有効な期間が変わると思いますので、いろいろ試してみましょう)

DLLのプログラム

普通の計算式

extern "C" __declspec(dllexport) void DllStochasArray(const int size, const int period, const int slow, const double* close, const double* lowArg, const double* highArg, double* out) {
    auto pStoch = make_unique<double[]>(size);

    for (int i = period; i <= size; i++) {
        double  high = 0.0;
        double  low = 100000.0;
        for (int n = 0; n < period; n++) {
            if (high < highArg[i - period + n]) {
                high = highArg[i - period + n];
            }
            if (low > lowArg[i - period + n]) {
                low = lowArg[i - period + n];
            }
        }
        pStoch[i - 1] = (close[i - 1] - low) * 100.0 / (high - low);
    }
    smaArray(size, slow, pStoch.get(), out);
}

period期間の高値・安値を調べて、%K(pStoch)を計算します。%Kをslow期間で単純移動平均して%Dを算出します。

このストキャスティクス関数を4本分呼び出せば良いのですが、せっかくC++でDLLを作成するのですから、今回はC++要素を入れてみましょう。

4本全部計算してみましょう。

extern "C" __declspec(dllexport) void DllStochasBraid(const int size, const int slow,
    const int period1, const int period2, const int period3, const int period4,
    const double* close, const double* lowArg, const double* highArg,
    double* out1, double* out2, double* out3, double* out4) {

    vector<int>   periods = { period1, period2, period3, period4 };
    vector<map<double, int>>    mapHighs = {
            map<double, int>(),
            map<double, int>(),
            map<double, int>(),
            map<double, int>() };
    vector<map<double, int>>    mapLows = {
            map<double, int>(),
            map<double, int>(),
            map<double, int>(),
            map<double, int>() };

    auto ptr1 = make_unique<double[]>(size);
    auto ptr2 = make_unique<double[]>(size);
    auto ptr3 = make_unique<double[]>(size);
    auto ptr4 = make_unique<double[]>(size);
    vector<unique_ptr<double[]>*>   stocks = { &ptr1, &ptr2, &ptr3, &ptr4 };

    for (int i = 0; i < size; i++) {
        for (int n = 0; n < periods.size(); n++) {
            // HIGH. LOW値をmapに入れておく
            mapHighs[n][highArg[i]] = mapLows[n][lowArg[i]] = 1;
            
            if (i >= periods[n]) {
                // 期間が過ぎたら入れ替える
                auto ith = mapHighs[n].find(highArg[i - periods[n]]);
                if (ith != mapHighs[n].end()) mapHighs[n].erase(ith);

                auto itl = mapLows[n].find(lowArg[i - periods[n]]);
                if (itl != mapLows[n].end()) mapLows[n].erase(itl);

                // 期間が来たらストキャスティクスを計算する
                (*stocks[n])[i] = (close[i] - mapLows[n].begin()->first) * 100.0
                    / (mapHighs[n].rbegin()->first - mapLows[n].begin()->first);
                (*stocks[n])[i] = ((*stocks[n])[i] > 100.0) ? 100.0 : ((*stocks[n])[i] < 0.0) ? 0.0 : (*stocks[n])[i];
            }
        }
    }
    smaArray(size, slow, stocks[0]->get(), out1);
    smaArray(size, slow, stocks[1]->get(), out2);
    smaArray(size, slow, stocks[2]->get(), out3);
    smaArray(size, slow, stocks[3]->get(), out4);
}

初見のキーワードがあると思います。(初見でない人は飛ばしてください)

  • vector

    動的な配列と考えてください。[数値]で各要素にアクセスできます。

  • map

    連想配列と考えてください。[文字列・数値]で各要素にアクセスできます。

    例えばmap<string, string> aであれば、a["abc"] = "def";のように"abc"の要素に"def"を登録できます。

("abc"をキー、"def"を値と言います)

また map は登録されたら自動的にキーでソートします。

今回はこのmapはキーでソートするという機能を使って高値の最大値、安値の最小値を取り出します。 (キーをソートするので、値には適当な"1"を固定で入れています・・・あまり良いプログラムとは言えませんので参考程度で)

(*stocks[n])[i] = (close[i] - mapLows[n].begin()->first) * 100.0
                    / (mapHighs[n].rbegin()->first - mapLows[n].begin()->first);

.begin()->firstがmapの最初の要素、.rbegin()->firstがmapの最後の要素です。

mapHighs、mapLowsは、各期間分の高値・安値を登録しています。

periods期間が過ぎたら.erase(ith)で要素から削除します。

stocksにはperiods期間の%Kが計算されます。

最後に smaArray でslow期間で単純移動平均を行いout1~4に出力します。

全体のコード

// 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;
}

static void smaArray(const int size, const int period, const double* close, double* out) {
    for (int i = period; i <= size; i++) {
        double  total = 0.0;
        for (int n = 0; n < period; n++) {
            total += close[i - period + n];
        }
        out[i - 1] = total / period;
    }
}

extern "C" __declspec(dllexport) void DllStochasBraid(const int size, const int slow,
    const int period1, const int period2, const int period3, const int period4,
    const double* close, const double* lowArg, const double* highArg,
    double* out1, double* out2, double* out3, double* out4) {

    vector<int>   periods = { period1, period2, period3, period4 };
    vector<map<double, int>>    mapHighs = {
            map<double, int>(),
            map<double, int>(),
            map<double, int>(),
            map<double, int>() };
    vector<map<double, int>>    mapLows = {
            map<double, int>(),
            map<double, int>(),
            map<double, int>(),
            map<double, int>() };

    auto ptr1 = make_unique<double[]>(size);
    auto ptr2 = make_unique<double[]>(size);
    auto ptr3 = make_unique<double[]>(size);
    auto ptr4 = make_unique<double[]>(size);
    vector<unique_ptr<double[]>*>   stocks = { &ptr1, &ptr2, &ptr3, &ptr4 };

    for (int i = 0; i < size; i++) {
        for (int n = 0; n < periods.size(); n++) {
            // HIGH. LOW値をmapに入れておく
            mapHighs[n][highArg[i]] = mapLows[n][lowArg[i]] = 1;
            
            if (i >= periods[n]) {
                // 期間が過ぎたら入れ替える
                auto ith = mapHighs[n].find(highArg[i - periods[n]]);
                if (ith != mapHighs[n].end()) mapHighs[n].erase(ith);

                auto itl = mapLows[n].find(lowArg[i - periods[n]]);
                if (itl != mapLows[n].end()) mapLows[n].erase(itl);

                // 期間が来たらストキャスティクスを計算する
                (*stocks[n])[i] = (close[i] - mapLows[n].begin()->first) * 100.0
                    / (mapHighs[n].rbegin()->first - mapLows[n].begin()->first);
                (*stocks[n])[i] = ((*stocks[n])[i] > 100.0) ? 100.0 : ((*stocks[n])[i] < 0.0) ? 0.0 : (*stocks[n])[i];
            }
        }
    }
    smaArray(size, slow, stocks[0]->get(), out1);
    smaArray(size, slow, stocks[1]->get(), out2);
    smaArray(size, slow, stocks[2]->get(), out3);
    smaArray(size, slow, stocks[3]->get(), out4);
}

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

//+------------------------------------------------------------------+
//|                                                         dll_test.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_minimum 0
#property indicator_maximum 100
#property indicator_buffers 4
#property indicator_plots   4
//--- plot memConf
#property indicator_label1  "stoch1"
#property indicator_type1   DRAW_LINE
#property indicator_color1  Orange
#property indicator_style1  STYLE_SOLID
#property indicator_width1  1

#property indicator_label2  "stoch2"
#property indicator_type2   DRAW_LINE
#property indicator_color2  Chocolate
#property indicator_style2  STYLE_SOLID
#property indicator_width2  1

#property indicator_label3  "stoch3"
#property indicator_type3   DRAW_LINE
#property indicator_color3  Turquoise
#property indicator_style3  STYLE_SOLID
#property indicator_width3  1

#property indicator_label4  "stoch4"
#property indicator_type4   DRAW_LINE
#property indicator_color4  LimeGreen
#property indicator_style4  STYLE_SOLID
#property indicator_width4  1

#define barCount    20000

//--- indicator buffers
double  stochasChart1[];
double  stochasChart2[];
double  stochasChart3[];
double  stochasChart4[];

double  closeBuffer[barCount];
double  lowBuffer[barCount];
double  highBuffer[barCount];
double  out1Buffer[barCount];
double  out2Buffer[barCount];
double  out3Buffer[barCount];
double  out4Buffer[barCount];


#import  "D:\\Projects\\Dll_test\\x64\\Debug\\Dll_test.dll"
    void DllStochasBraid(const int size, const int slow, 
                const int period1, const int period2, const int period3, const int period4, 
                const double& close[], const double& low[], double& high[], 
                double& out1[], double& out2[], double& out3[], double& out4[]);
#import  

//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int OnInit()
{
//--- indicator buffers mapping
    SetIndexBuffer(0, stochasChart1, INDICATOR_DATA);
    SetIndexBuffer(1, stochasChart2, INDICATOR_DATA);
    SetIndexBuffer(2, stochasChart3, INDICATOR_DATA);
    SetIndexBuffer(3, stochasChart4, INDICATOR_DATA);

    ArraySetAsSeries(stochasChart1, true);
    ArraySetAsSeries(stochasChart2, true);
    ArraySetAsSeries(stochasChart3, true);
    ArraySetAsSeries(stochasChart4, 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);
    ArraySetAsSeries(high, true);
    ArraySetAsSeries(low, true);

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

    DllStochasBraid(size, 3, 9, 14, 38, 62, 
                    closeBuffer, lowBuffer, highBuffer, 
                    out1Buffer, out2Buffer, out3Buffer, out4Buffer);
    ArrayCopy(stochasChart1, out1Buffer, 0, 0, size);
    ArrayCopy(stochasChart2, out2Buffer, 0, 0, size);
    ArrayCopy(stochasChart3, out3Buffer, 0, 0, size);
    ArrayCopy(stochasChart4, out4Buffer, 0, 0, size);

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

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

今回はストキャスティクス4本を表示します。

#property indicator_minimum 0はチャート表示の最小値、#property indicator_maximum 100は最大値を指定します。

また、インジケータバッファ、プロット数を4にして、各インジケータの色や種類を指定しています。

#property indicator_separate_window
#property indicator_minimum 0
#property indicator_maximum 100
#property indicator_buffers 4
#property indicator_plots   4
//--- plot memConf
#property indicator_label1  "stoch1"
#property indicator_type1   DRAW_LINE
#property indicator_color1  Orange
#property indicator_style1  STYLE_SOLID
#property indicator_width1  1

そしてDLLとの受け渡しバッファはClose、High、Lowと出力用合わせて7つ用意します。

double    closeBuffer[barCount];
double  lowBuffer[barCount];
double  highBuffer[barCount];
double  out1Buffer[barCount];
double  out2Buffer[barCount];
double  out3Buffer[barCount];
double  out4Buffer[barCount];

DLLの呼び出しは、4本分のストキャスティクス用パラメータを指定します。

  void DllStochasBraid(const int size, const int slow, 
                const int period1, const int period2, const int period3, const int period4, 
                const double& close[], const double& low[], double& high[], 
                double& out1[], double& out2[], double& out3[], double& out4[]);

MSCodeにコピーして、ビルドをします。

ストキャスティクス4本表示して比べてみましょう。

次回

次回は、またオシレータ系インジケータのRCIを作ってみましょう。