MT5自動売買ソフトの作成4 オシレータ系インジケータ

まずポインタとは

SMA(単純移動平均)を前々回に作ってみました。

また前回の「データを見る」でもDLL側でポインタを使っています。

C++なのでポインタを普通に書いていましたが、もしかしたらポインタでC、C++をあきらめた人もいるかもしれませんので、少し解説します。

・・・ポインタは何故か嫌われ者で悲しいです。

SMAの回のMQL5側のインポート文では「double& close[]」で呼び出します。

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

DLL側では「double* close」で受けています。

extern "C" __declspec(dllexport) void dllSmaArray(const int size, const int period, double* close, double* out)
{
    for (int i=0; i <= size - period; i++) {
        double  total = 0.0;
        for (int n = 0; n < period; n++) {
            total += close[i+n];
        }
        out[i+period-1] = total / period;
    }
}

DLL受け渡しバッファは下図でしたね。DLL受け渡しバッファは「double& 変数」→「double* 変数」となっていることになります。

「double& 変数」=double変数配列は、「double*」=double変数配列のポインタということになりますね。

ポインタはメモリの位置を指す変数ですが、ここでは簡単にバッファの先頭(位置)を指す変数と考えてください。配列変数と同様の扱いができます。

オシレータ系インジケータ

今回はMACDを作成します。

MACDとは「Moving Average Convergence Divergence」の略ですが、「マックディー」としか呼ばれませんし、正式名称を知っている人はほとんどいないと思います。

MACDは短期(9か12期間)と長期(26期間)の指数移動平均(EMA)の差分をヒストグラム表示して、その差分をSMA(9期間)したシグナルを破線で表示します。(上図下側)

見るべきポイントはゴールデンクロスデッドクロスヒストグラム表示とシグナルの交差です。

ゴールデンクロスデッドクロスでは判断が遅くなることが多いので、ヒストグラム表示とシグナルの交差を見るようです。(ピンクの矢印ですが、騙しが多い気もしますね)

上図を見てもシグナル交差のほうが判断が早いです。

DLLのプログラム

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

#include <memory>

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

// EMA= 前日EMA+平滑化定数α×(当日終値-前日EMA)  平滑化定数α=2÷(n+1)
static void emaArray(const int size, const int period, const double* close, std::unique_ptr<double[]>& out) {
    double  alpha = 2.0 / ((double)period + 1.0);
    double  total = 0.0;
    for (int n = 0; n < period; n++) {
        total += close[n];
    }
    out[period-1] = total / period;

    for (int i = period; i < size; i++) {
        out[i] = out[i - 1] + (close[i] - out[i - 1]) * alpha;
    }
}

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

// MACD
extern "C" __declspec(dllexport) void dllMacdArray(const int size, const int periodS, const int periodL, const int periodG, double* close, double* outM, double* outS)
{
    std::unique_ptr<double[]>   maS(new double[size + 1]);
    std::unique_ptr<double[]>   maL(new double[size + 1]);
    // MACDの計算
    emaArray(size, periodS, close, maS);
    emaArray(size, periodL, close, maL);
    for (int i = periodL; i < size; i++) {
        outM[i] = maS[i] - maL[i];
    }
    // シグナルの計算
    smaArray(size, periodG, outM, outS);
}

EMAの計算、SMAの計算、MACDの計算の関数を作成します。

新しいのはEMA、MACDです。EMAはSMAと同じ移動平均の「指数移動平均」です。

SMAは二重ループで計算していますが、EMAはループが一重のため計算が早いのが特徴で、最新終値を2回使うので最新値を重視する移動平均です。

EMAの計算式は、

EMA = 前日EMA+平滑化定数α×(当日終値-前日EMA)  ※ただし、平滑化定数α=2÷(n+1)

MACDは2期間EMAの差分をヒストグラム表示して、その差分のSMAをシグナルとして破線表示します。

上記、dllMacdArray関数は見れば一目瞭然ですね。

2期間のEMA計算をするため、2期間分のバッファが必要になります。

C++では、newで領域確保して deleteで領域開放します。ですが、領域開放って面倒ですよね。他の言語のようにガベージコレクションに任せてしまいましょう。

C++11から実装されている「unique_ptr」で領域確保すると、なんと開放しなくてもスコープから抜けると解放されます。

楽ですね。

    std::unique_ptr<double[]>   maS(new double[size + 1]);
    std::unique_ptr<double[]>   maL(new double[size + 1]);

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

//+------------------------------------------------------------------+
//|                                                         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_buffers 2
#property indicator_plots   2
//--- plot dll_test
#property indicator_label1  "MACD"
#property indicator_type1   DRAW_HISTOGRAM
#property indicator_color1  Silver
#property indicator_style1  STYLE_SOLID
#property indicator_width1  1

#property indicator_label2  "signal"
#property indicator_type2   DRAW_LINE
#property indicator_color2  Red
#property indicator_style2  STYLE_DOT
#property indicator_width2  1

#define barCount    20000

//--- indicator buffers
double  shortChart[];
double  longChart[];

double  closeBuffer[barCount];
double  longBuffer[barCount];
double  shortBuffer[barCount];

#import  "D:\\Projects\\Dll_test\\x64\\Debug\\Dll_test.dll"
    void dllMacdArray(const int size, const int periodS, const int periodL, const int periodG, const double& close[], double& outM[], double& outS[]);
#import  

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

    ArraySetAsSeries(shortChart, true);
    ArraySetAsSeries(longChart, 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);

    dllMacdArray(size, 9, 26, 9, closeBuffer, shortBuffer, longBuffer);
    ArrayCopy(shortChart, shortBuffer, 0, 0, size);
    ArrayCopy(longChart, longBuffer, 0, 0, size);
//--- return value of prev_calculated for next call
    return(rates_total);
}

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

今回はMACDとシグナル、2つのグラフを表示します。

#property indicator_separate_windowはチャート上に表示しないで、Window分割したグラフに表示する指定です。

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

#property indicator_separate_window
#property indicator_buffers 2
#property indicator_plots   2
//--- plot dll_test
#property indicator_label1  "MACD"
#property indicator_type1   DRAW_HISTOGRAM
#property indicator_color1  Silver
#property indicator_style1  STYLE_SOLID
#property indicator_width1  1

#property indicator_label2  "signal"
#property indicator_type2   DRAW_LINE
#property indicator_color2  Red
#property indicator_style2  STYLE_DOT
#property indicator_width2  1

そしてDLLとの受け渡しバッファはCloseと合わせて3つ用意します。

double    closeBuffer[barCount];
double  longBuffer[barCount];
double  shortBuffer[barCount];

DLLの呼び出しは、短い期間と長い期間、シグナルの期間を指定して、バッファを受け渡します。

dllMacdArray(size, 9, 26, 9, closeBuffer, shortBuffer, longBuffer);

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

MT5に最初からインストールされているMACDと表示を比べてみましょう。

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

次回

オシレータといえば「MACD」と「ストキャスティクス」です。

次回は、ちょっと変わったストキャスティクスを作ってみましょう。