MT5自動売買ソフトの作成2 DLLを作る

初めてプログラミングをする人や経験の少ない人は「DLLって難しいのでは?」と思いますよね。少しだけ不便なことはありますが、難しいことはありません。

基本的に自分でプログラムを作るといっても、ほとんどはライブラリを呼び出すだけです。

例えば、初めてのC言語ではこんな感じに

printf("Hello world\n");

書くのが定番です。"Hello world\n"という文字列を「printf」という、ライブラリ関数を呼び出して画面に表示しています。

DLLとは「ダイナミックリンクライブラリ」、動的にリンクするライブラリです。

「printf」というライブラリ関数を作る感じですね。

「動的にリンク」とはプログラム実行にメモリにロードして呼び出される仕組みです。

少し手伝う程度の手間でWindowsが勝手にやることなので気にしなくても大丈夫です。

VS 2022でプロジェクトを作成する

VS 2022を立ち上げましょう。

まず「新しいプロジェクトの作成」を選びます。

C++ / Windows / ライブラリ」を右上で選択します。

「ダイナミックリンクライブラリ(DLL)」を選んで「次へ」をクリックします。

プロジェクト名は「 Dll_text」にします。

「作成」をクリックするとプロジェクトが作成されます。

DLLのプログラム

今回のプログラムは単純移動平均を作ってみます。

まずはチャートに表示されるインジケータを作ってみましょう。

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

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

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

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

プロジェクトを作成すると、DllMainまでは自動で生成されています。

初めに「extern "C" __declspec(dllexport)」とは、これがDLLライブラリだよ、呼び出せるよ、という「おまじない」です。定型なので覚える必要もありません。(コピペで十分)

「dllSmaArray」が今回作成したプログラムですね。SMA=単純な平均を移動しながら計算するので「単純移動平均」です。

(次回にデータの受け渡しについて調べますので、今回はさっくりと説明します)

下図をプログラミングしたのが dllSmaArray関数です。MQL5から渡されるclose(終値)のデータを1つずつずらして、period(期間)分合計しperiodで割って平均値を算出していきます。

「dllSmaArray」を作成したら CTRL+Shift+B でビルドします。

次はMQL5でMT5のインジケータを作って「dllSmaArray」を呼び出します。

MetaEditorでインジケータ作成

MT5のインジケータをMetaQuotes言語エディタ(MetaEditor)で作成します。

MT5の「ツール」→「MetaQuotes言語エディタ」で立ち上げます。

「ファイル」→「新しいファイル」でカスタムインジケータを選択し、次へ進みます。

名前に「Dll_test.mq5」を追加します。

今回はパラメータなどは追加しませんので、次へ進みます。

「OnCliculate(...open,high,low,close)」を選択して次に進みます。

プロットに「chart」を追加して「完了」すると、スケルトンが生成されます。

VSCodeでインジケータの編集とMT5で実行

VSCodeでMQL5のディレクトリ("C:\Users\[ユーザ名]\AppData\Roaming\MetaQuotes\Terminal\[シリアル番号のような英数字]\MQL5)を開きます。

VSCodeエクスプローラーで「Dll_test.mq5」を開き下記のように変更します。

//+------------------------------------------------------------------+
//|                                                     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
//--- indicator buffers
double         chartBuffer[];

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

#define barCount    10000

double  closeBuffer[barCount+1];
double  smaBuffer[barCount+1];

//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- indicator buffers mapping
    SetIndexBuffer(0, chartBuffer, INDICATOR_DATA);
    ArraySetAsSeries(chartBuffer, 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);

    dllSmaArray(size, 200, closeBuffer, smaBuffer);
    ArrayCopy(chartBuffer, smaBuffer, 0, 0, size);
//--- return value of prev_calculated for next call
    return(rates_total);
  }
//+------------------------------------------------------------------+

DLLを使うときに必要なのは、DLLのインポート文です。

「"D:\Projects\Dll_test\x64\Debug\Dll_test.dll"」はVSで作成したDLLのフルパスにしてください。

#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との受け渡しバッファです。

#define   barCount    10000

double  closeBuffer[barCount+1];
double  smaBuffer[barCount+1];

チャートを表示するためのバッファは、DLLとの受け渡しバッファからコピーするので下準備をします。

int OnInit()
  {
//--- indicator buffers mapping
    SetIndexBuffer(0, chartBuffer, INDICATOR_DATA);
    ArraySetAsSeries(chartBuffer, true);
//---

そしてチャートを表示する関数OnCalculateでは、DLLを呼び出します。

「dllSmaArray」の呼び出しは、(サイズ、期間、Close、SMA)です。期間はSMAの合計期間です。

今回は 200期間にしています。

  ArraySetAsSeries(close, true);

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

    dllSmaArray(size, 200, closeBuffer, smaBuffer);
    ArrayCopy(chartBuffer, smaBuffer, 0, 0, size);

OnCalculateの解説

sizeは処理していないデータの数とbarCountの少ない方を計算しています。計算しなければいけない処理数ですね。

barCountを指定しない場合はsize = (size > barCount) ? barCount : size;が無くなります。

Dll_test.mq5の編集が終わったらビルドしましょう。

左から2番目の矢印アイコン(Compile MQL file)をクリックするとMQL5がビルドされて「ex5」ファイルが生成されます。

完成した「Dll_test」はMT5の指標を更新すると表示されます。

「Dll_test」をチャートにドラッグドロップすると、DLLの使用を許可するダイアログが表示されますので「OK」ボタンを押すと、SMAがチャートに表示されます。

どうでしょう、表示されましたか?簡単だったでしょうか、難しかったでしょうか?

環境の構築と作り方を覚えてしまえば後は作るだけです。ここまでの作業は繰り返しすることですので自然と覚えてしまうでしょう。

今回のプログラムでは、ArraySetAsSeries、ArrayCopyという関数を使用しています。

次回はこの関数を含めて、MQL5とC++のデータ受け渡しがどうなっているのか見てみます。

次回

DLLに受け渡されるデータを見てみましょう。