MT5自動売買ソフトの作成3 データを見る
今回はMQL5とDLL(C++)間でのデータ受け渡しがどうなっているのか見ていきます。
受け渡しのデータ内容がわからないとプログラムが作れませんので、調べておくのが基本です。
DLLのプログラム
// dllmain.cpp : DLL アプリケーションのエントリ ポイントを定義します。 #include "pch.h" #include <fstream> 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; } const char* LOG_FILE = "D:\\dll_test.log"; static void write(int i, double value) { std::ofstream oss; oss.open(LOG_FILE, std::ios::app); oss << " i= " << i << ", value= " << value << std::endl; oss.close(); } static void writeStr(const char* str) { std::ofstream oss; oss.open(LOG_FILE, std::ios::app); oss << str << std::endl; oss.close(); } extern "C" __declspec(dllexport) void dllDumpTest(const int size, double* value) { writeStr("dllDumpTest ------------------------------------"); int len = (size < 10) ? size : 10; for (int i = 0; i < size; i++) { if (i < len) { write(i, value[i]); } if (i > size - len) { write(i, value[i]); } } }
#include <fstream>
ofstreamでファイルを書き込むため、includeしておきます。
const char* LOG_FILE = "D:\\dll_test.log"; static void write(int i, double value) { std::ofstream oss; oss.open(LOG_FILE, std::ios::app); oss << " i= " << i << ", value= " << value << std::endl; oss.close(); } static void writeStr(const char* str) { std::ofstream oss; oss.open(LOG_FILE, std::ios::app); oss << str << std::endl; oss.close(); }
ファイルにデータを書き込みます。今回は受け渡されたデータと、文字列を書き込むプログラムです。
extern "C" __declspec(dllexport) void dllDumpTest(const int size, double* value) { writeStr("dllDumpTest ------------------------------------"); int len = (size < 10) ? size : 10; for (int i = 0; i < size; i++) { if (i < len) { write(i, value[i]); } if (i > size - len) { write(i, value[i]); } } }
いよいよメインのMT5との受け渡し関数です。
渡されたデータの前後10データをファイルに書き込みます。
MetaEditorでインジケータ作成
VSCodeでインジケータの編集とMT5で実行
VSCodeのエクスプローラーで「Dll_test.mq5」を開き下記のように変更します。
(同じ名称で説明していますが、前のプログラムを残しておきたい場合は、Gitで履歴管理するか、別の名称にコピーして取っておいてください)
//+------------------------------------------------------------------+ //| 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_chart_window #property indicator_buffers 1 #property indicator_plots 1 #property indicator_label1 "dll_test" #property indicator_type1 DRAW_LINE #property indicator_color1 Orange #property indicator_style1 STYLE_SOLID #property indicator_width1 1 #import "D:\\Projects\\Dll_test\\x64\\Debug\\Dll_test.dll" void dllDumpTest(const int size, const double& value[]); #import //+------------------------------------------------------------------+ //| Custom indicator initialization function | //+------------------------------------------------------------------+ int OnInit() { //--- indicator buffers mapping //--- 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; dllDumpTest(size, close); //--- return value of prev_calculated for next call return(rates_total); } //+------------------------------------------------------------------+
OnCalculateイベントは価格が変動するごとに呼び出されますので、平日はガンガン呼び出されます。開発するのは休日のほうが良いかもしれません。
一度動けばいいので、すぐに右クリックしてインディケータリストを開きます。
「Dll_test」を選択して「削除」し、閉じます。
ファイルの内容
まずはCloseデータとArraySetAsSeriesを確認します。
ArraySetAsSeriesはMQL5のライブラリ関数で、trueにするとインデックス0が最新データで、falseにすると最大インデックスが最新データになるという機能です。
dllDumpTest ------------------------------------ i= 0, value= 97.327 i= 1, value= 97.385 i= 2, value= 97.686 i= 3, value= 98.162 i= 4, value= 98.514 i= 5, value= 98.352 i= 6, value= 98.719 i= 7, value= 98.655 i= 8, value= 98.537 i= 9, value= 98.645 i= 40391, value= 131.497 i= 40392, value= 131.46 i= 40393, value= 131.551 i= 40394, value= 131.482 i= 40395, value= 131.51 i= 40396, value= 131.43 i= 40397, value= 131.412 i= 40398, value= 131.425 i= 40399, value= 131.411
「ArraySetAsSeries」をコメントにします。
//ArraySetAsSeries(close, true);
dllDumpTest ------------------------------------ i= 0, value= 97.327 i= 1, value= 97.385 i= 2, value= 97.686 i= 3, value= 98.162 i= 4, value= 98.514 i= 5, value= 98.352 i= 6, value= 98.719 i= 7, value= 98.655 i= 8, value= 98.537 i= 9, value= 98.645 i= 40391, value= 131.497 i= 40392, value= 131.46 i= 40393, value= 131.551 i= 40394, value= 131.482 i= 40395, value= 131.51 i= 40396, value= 131.43 i= 40397, value= 131.412 i= 40398, value= 131.425 i= 40399, value= 131.411
変わりませんね。
DLLに渡される時にはその機能は有効にならないことがわかりました。
//+------------------------------------------------------------------+ //| 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_chart_window #property indicator_buffers 1 #property indicator_plots 1 #property indicator_label1 "dll_test" #property indicator_type1 DRAW_LINE #property indicator_color1 Orange #property indicator_style1 STYLE_SOLID #property indicator_width1 1 #import "D:\\Projects\\Dll_test\\x64\\Debug\\Dll_test.dll" void dllDumpTest(const int size, const double& value[]); #import #define barCount 10000 //+------------------------------------------------------------------+ //| Custom indicator initialization function | //+------------------------------------------------------------------+ int OnInit() { //--- indicator buffers mapping //--- return(INIT_SUCCEEDED); } double closeBuffer[barCount+1]; //+------------------------------------------------------------------+ //| 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); ArrayCopy(closeBuffer, close, 0, 0, barCount); dllDumpTest(barCount, closeBuffer); //--- return value of prev_calculated for next call return(rates_total); } //+------------------------------------------------------------------+
次はバッファにコピー(ArrayCopy)してからDLLにデータを渡します。
チャートのデータが全部渡されてしまうと処理時間がかかるのでレスポンスが遅れます。自動売買はできるだけ早く判断したほうが良いので100程度のデータ渡しで済ませたいです。CloseをそのままDLLに渡すと全データを受け渡さないと最新データが受け取れませんね。
ただ100程度では、インジケータの描画が直近のチャートにしか反映しなくなるので、数千から1万弱くらいが良いと思います。
今回はcloseデータを1万渡すことにします。
i= 0, value= 127.659 i= 1, value= 127.912 i= 2, value= 127.86 i= 3, value= 127.669 i= 4, value= 127.672 i= 5, value= 127.736 i= 6, value= 127.694 i= 7, value= 127.85 i= 8, value= 127.851 i= 9, value= 127.947 i= 9991, value= 131.497 i= 9992, value= 131.46 i= 9993, value= 131.551 i= 9994, value= 131.482 i= 9995, value= 131.51 i= 9996, value= 131.43 i= 9997, value= 131.412 i= 9998, value= 131.425 i= 9999, value= 131.411
指定したバッファのインデックス最大が最新データになっています。
ArraySetAsSeriesをfalseにしてみた場合はやってみてください。
インジケータ作成の方針としては、MQL5からArraySetAsSeriesはTrueでバッファにコピーして、DLLにデータを渡す形にしましょう。
前回のSMAのプログラミングコードと下の図を参考にしてください。
次回
次回はオシレータ系のインジケータを作ります。