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」と「ストキャスティクス」です。
次回は、ちょっと変わったストキャスティクスを作ってみましょう。