2010年12月19日日曜日

EAはC++で作ろう! MinGWを使って、MQL4からリンク可能なdllをLinux上でクロス開発

Ubuntu(Linux)上にインストールしたMinGWを使って、ダイナミックリンクライブラリ(dll)を作成し、MQL4からリンクすることによって、MetaTrader4上で実行できることが確認できたので報告する。

例えばJForexはJava環境での自動売買プログラムの開発が可能である。オブジェクト指向型のJava言語を使った開発は、C言語ライクなMQL4を使ったプログラムよりも高度な記述に適している。しかも、Javaは生来クロスプラットフォーム環境を強く意識しており、Windows環境に強く依存しているMetaTraderではプラットフォームに対する融通が効かなかった。
今回、Linux環境上でMQL4用のdllをクロス開発できることが確認できたことにより、Windowsに一切頼ることなく、C++などのオブジェクト指向型言語を使った、JForexよりも柔軟なEAの開発が可能になる。

まず、MinGWについて理解しておく必要がある。
MinGWとは、Minimalist GNU for Windowsの略で、様々な読み方があるが、ミン・ジー・ダブリューと読むのが正しいようだ。
もともとはGNUのCコンパイラGCCをWindowsプログラムの開発に使えるようWindows環境に移植し、Windows APIのためのヘッダファイルを追加したものだ。更にこれをLinux上でWindowsプログラムがクロス開発できるよう、Windows APIのヘッダファイル等も含めてLinux向けに移植したものが今回使うUbuntu上のMinGWである。
つまり、Linux上のMinGWとは、GCCを用いたクロス開発環境である。したがって、g++を使うことによってC++での開発が可能になるし、gfortranを使えばFORTRANでの開発も可能である。また、インターネット上で公開されている多くのGCC向けのテクニックが応用可能である。
今回は、C++での開発を前提とする。(うまくすれば、gcc-javaやadaなども使えるかもしれない)
コンソール上で
% sudo apt-get install gcc-mingw32
とすれば、Cコンパイラ、C++コンパイラ、FORTRANコンパイラ他、アセンブラ、リンカ等のユーティリティも一緒にインストールできる。

テスト用のプログラムとして、「FXメタトレーダー入門」(豊嶋久道 : PanRolling)から、ソースファイルをお借りしよう。
この本の144ページ、リスト4.1 Ex1.mq4 を使わせていただこうと思う。(http://www.panrolling.com/books/gr/gr56.htmlから、ダウンロード可能)このサンプルプログラムは純粋にMQL4のみで書かれており、wine上で走っているMetaTrader4でも、メタエディターを使って普通にコンパイルできる。まずは、これを書籍内で示されている通りにコンパイルしてみて、環境が正しく設定されていることと、結果のイメージを確認する。

これを、下記のように改変して "DllTest.mq4" とする。(ブログのフォーマット上、長い行が折り返されて見にくい。テキストエディタ等にコピー&ペーストすれば見やすくなる)
----- DllTest.mq4 ここから -----
//+------------------------------------------------------------------+
//|                                                      DllTest.mq4 |
//|                                Copyright (c) 2010, FX-renshucho. |
//|                                http://fxmetatrader.blogspot.com/ |
//+------------------------------------------------------------------+
#property copyright "Copyright (c) 2010, FX-renshucho."
#property link      "http://fxmetatrader.blogspot.com/"

#property indicator_chart_window
#property indicator_buffers 1

#import "libDllTest.dll"
double movingAverages(double,double,double,double);

// 指標バッファ
double Buf[];


//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int init()
  {
//---- indicators

   //指標バッファの割り当て
   SetIndexBuffer(0,Buf);

//----
   return(0);
  }
//+------------------------------------------------------------------+
//| Custom indicator deinitialization function                       |
//+------------------------------------------------------------------+
int deinit()
  {
//----
   
//----
   return(0);
  }
//+------------------------------------------------------------------+
//| Custom indicator iteration function                              |
//+------------------------------------------------------------------+
int start()
  {
//----
   //指標の計算範囲
   int counted_bars = Bars-IndicatorCounted();

   //指標の計算
   for(int i=counted_bars-1; i>=0; i--)
   {
      Buf[i] = movingAverages(Close[i], Close[i+1], Close[i+2], Close[i+3]);
//      Buf[i] = (Close[i]+Close[i+1]+Close[i+2]+Close[i+3])/4;

   }
   
//----
   return(0);
  }
//+------------------------------------------------------------------+
----- DllTest.mq4 ここまで -----

つまり、移動平均を計算する部分だけdllに切り出してみることにする。
コード中、注目すべき箇所を赤色で示した。
このままメタエディター上でコンパイルしてしまう。コンパイルはエラー、ウォーニング共に0で通るはずだ。

次に、このコードに合う、movingAverages()関数を含んだlibDllTest.dllを作成すれば良い。
dllを作るためのC++のソースファイルは"<インストールディレクトリ>/experts/samples/DLLSample"にある、ExpertSample.cppを参考に、下記のようなソースファイルを作成し、"DllTest.cpp" とした。

----- DllTest.cpp ここから -----
//+------------------------------------------------------------------+
//|                                                      DllTest.cpp |
//|                                                   libDllTest.dll |
//|                                Copyright (c) 2010, FX-renshucho. |
//|                                http://fxmetatrader.blogspot.com/ |
//+------------------------------------------------------------------+
#define WIN32_LEAN_AND_MEAN  // Exclude rarely-used stuff from Windows headers
#include <windows.h>
#include <stdlib.h>
#include <stdio.h>
//----
#define MT4_EXPFUNC __declspec(dllexport)

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
BOOL APIENTRY DllMain(HANDLE 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"
MT4_EXPFUNC double __stdcall movingAverages(double a, double b, double c, double d)
  {
   return((a+b+c+d)/4);
  }

----- DllTest.cpp ここまで -----

簡単に解説しておくと、MT4_EXPFUNCとしてdefineされているが、「__declspec(dllexport)」を関数宣言の先頭に記述すると、gcc(g++)コンパイラはこれを外部から呼ばれる関数だと認識する。また、C++では、同じ関数名でも引数の種類の違う関数が複数定義できる。実行時にこれらが衝突することを避けるため、関数名は独自のシンボルに変換される。しかし、それではMQL4から呼び出すときの名前がわからなくなるので、「extern "C"」を付けて、movingAveragesという名前が変更されないようにしている。(参照:「BINARY HACKS」オライリー・ジャパン,P65,CからC++の関数を呼び出す)
実際には、これだけでは不十分で、コンパイル時にもケアしている。(後述)

これを、以下のコマンドでコンパイルする。
$ i586-mingw32msvc-g++ -shared -o libDllTest.dll DllTest.cpp -Wl,-k,--output-def,DllTest.def,--out-implib,libDllTest.a
(これも、ブログのフォーマットにより細かく改行されているが1行のコマンドである)

個々のオプションの詳細については、インターネット上のg++やMinGWの解説を参照してほしい。概略的な説明であれば、「C++クックブック」(オライリージャパン)がわかりやすい。P26から、「レシピ1.4 コマンドラインからの動的ライブラリのビルド」などが参考になる。
ただし、"-k"オプションについてはかなり重要であるにも関わらず、書籍等で取り上げられていない。
-kオプションを付けずにコンパイルすると、movingAveragesというシンボル名が生成される代わりに、movingAverages@32というシンボル名が作られる。
もちろん、MQL4上で、関数名をmovingAverages@32()に書き換えてやれば問題なく動作する。しかし、毎回、コンパイルのたびにシンボル名を確認してMQL4のソースを書き換えていたのでは開発効率が悪すぎる。
"-k"は、リンカに@以下を捨てろと指示するオプションである。

完成したlibDllTest.dllを、"<インストールディレクトリ>/experts/libraries"にコピーする。
その後、MetaTrader4の画面から、コンパイル済みのカスタムインジケーターDllTestを実行すれば、Ex1を実行した時と同じ結果が得られるはずである。

どうだろうか。ポイントさえ押さえておけば、普通のプログラム開発と何も変わらない。C++なら、UML関連のツールを使って、ある程度のソースを自動生成することも可能だし、JavaのようにVMのオーバーヘッドを気にする必要もない。安価で非力なPCでより高度なEAを走らせることが可能になる。また、最近はやりのマルチコアプロセッサを有効に活用することもできるだろう。
これらの開発環境がOSまで含めて全てフリーで構築できるというところがおもしろい。

3 件のコメント:

  1. Linuxユーザーではありませんが勉強になりました。
    私は、現在MT4とMT5を並行して開発を進めていますが互換性がなく不便に感じています。C++によるDLL活用が今後の開発に重要であることを知りました。
    今後のご活躍に期待いたします。

    返信削除
  2. コメントありがとうございます。
    簡単にできると思ってはじめたMinGWでのdllのクロス開発ですが、MinGWに関するまとまった日本語の文献がなかなか見つからず、今回のテストをするだけで意外に手間取ってしまいました。
    とりあえず、手順だけは確認できましたので、似たようなことで困っておられる方の参考になればと思い公開しました。
    bighopeさんはLinuxユーザーではないとのことですが、少しでもお役に立てればうれしいです。

    返信削除
  3. -kオプション、助かりました。ありがとうございます

    返信削除