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

C++というより、ほとんどC言語ですが、少し解説します。

#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のプログラミングコードと下の図を参考にしてください。

次回

次回はオシレータ系のインジケータを作ります。