2012/01/12

GSLをMT4で使う■■■

再修正しました。2012/1/13
R以外で計算ライブラリーをMT4に導入する方法がないか調べてる内に、GSL(GNU Scientific Library)と言うものを発見しました。と言うことで今回は、GSLをMT4で使うことに挑戦してみました。

何はともあれまずこちらを・・

まずは、このサイトをご確認ください。 慣れた人ならこれでほぼ完了します。
1.GSLをWindowsのVisual C++で使う(ごんざぶログ)を熟読
2.産業技術総合研究所 富永氏によるマニュアルの和訳GSL リファレンス・マニュアルの日本語訳を確認
3.MQL4(MT4):DLLの作り方を公開していますアメンボのブログ2)内の記事および
記事の置き場所アメンボのWEB―記事:・MT4(MQL4)用DLLの作り方.pdf
4.初めてのDLL(2)/関数作成篇(ご存じfaiさんの備忘秘録)

組込方法

上記の記事を参考(ほぼそのまま^^;)に中継用のDLLを作成し、GSLをMT4から使用する事にしました。

手順1

今回は、VC++2010を使用してみたので、まずは、Visual C++ 2010 Express(無料)をDL+インストールします。
そして、VC++向けにコンパイルされたGSLをDLします。今回は、GSL_1.8__LCG_win32_vc71.tar.gzをDLしました。

手順2

まずは、GSL リファレンス・マニュアルの日本語訳を参考に取り込む関数を選択します。
今回は、第31章ウェーブレット変換P461~ を選択しました。
※参考コードも記載されていますので、導入しやすいと思います。
※注意:GSL リファレンス・マニュアルの日本語訳に記載されているサンプルコードは、C語で記載されていますが、VC++用のコードではありません。その為、VC++で使用する場合は、一部変更する必要があります。
例)
double *abs = malloc(10* sizeof (double));//というコードは、エラーが出ます。
double *abs = (double *)malloc(10 * sizeof (double));//という型宣言を追加する必要があります。

手順3

 ※ここに記載してあるのは、あくまでもDLL作成の最低設定です。詳細については、上記参照サイトを確認してください。
新規プロジェクトの作成
VC++2010を起動し【ファイル(F)】―【新規作成(N)】-【プロジェクト(P)】
Win32プロジェクトを選択後、名前(N)欄にプロジェクト名を記入-【OK】-【次へ】
※必要に応じ保存場所を変更。保存場所を覚えておく。
1
2
Win32アプリケーションウィザードにて(DLL)(空のプロジェクト)を選択-【OK】
3
プロジェクトが、立上ったらソリューションエクスプロラー内ソースファイルを右クリック-【追加】-【新しい項目】を選択。
【C++ファイル(.cpp)】-名前欄にファイル名を記入
※本来は、ヘッダーファイルなどに分割するが、今回はcppファイルのみを使用する。
4

GSLのインクルード
ここで一度VC++2010を終了後、先ほどDLしてきた、GSLファイルに移動し、解凍後最下層にある《includeフォルダ》及び《libフォルダ》をコピーし、VC++2010で作成したプロジェクトフォルダのC++ファイル(.ccp)があるフォルダ内にペーストする。
13

DEFファイルの作成
とりあえず、テキストエディタで空のファイルをプロジェクト名.defとし、C++ファイルがあるフォルダに作成する。
15

プロパティページの設定
※本来は、Debugモードで作成していくが、今回は説明短縮のため初めからReleaseで設定する。
ソリューション構成をReleaseに設定し、【プロジェクト(P)】-【プロパティ】を選択する。
【構成プロパティ】
【全般】━【文字セット】━(マルチバイド文字セットを使用する)
【C/C++】┳【プリプロセッサ】━【プリプロセッサの定義】━【(編集)】(GSL_DLL)を追加
             ┗【コード生成】━(マルチスレッド(/MT)に設定
6
【リンカー】┳【追加依存ファイル】━【(編集)】(.\lib\gsl.lib:.\lib\gslcblas.lib)を追加
              ┗【モジュール定義ファイル】━【(編集)】(先ほど作成したdefファイル名)を追加
7.6
7.3

コード記入
今回テスト用に記入したコードは、以下のとおりです。
※サンプルコードを少しいじっています。
#pragma once
#define WIN32_LEAN_AND_MEAN 
#include <Windows.h>
#include <stdlib.h>
#include <stdio.h>
#include <math.h>
#include ".\include\gsl\gsl_sort.h"
#include ".\include\gsl\gsl_wavelet.h"

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);
}
//-----------------------------------------------------------------------------
__declspec(dllexport) void __stdcall DWT(double *input,double *out,int Ns,int D,int fs){
 int i;
 int siz = (int)pow(2.0,Ns);
 if(D%2==1)D++;
 if(D>20)D=20;
 
 //配列のコピー
 memcpy(out,input,sizeof(double)*siz); 
 
 //ウェーブレットの初期設定(ウェーブレット名,D値)
 gsl_wavelet *w;
 if(D==2){
  w = gsl_wavelet_alloc(gsl_wavelet_haar,D);
 }else{
  w = gsl_wavelet_alloc(gsl_wavelet_daubechies, D);
   }
  
 //作業領域の確保(数量)
 gsl_wavelet_workspace *work;
 work = gsl_wavelet_workspace_alloc(siz);
 size_t *p = (size_t *)malloc(siz * sizeof(size_t));
 double *abs = (double *)malloc(siz * sizeof (double));
 if (abs == NULL || p== NULL){printf("メモリを確保できません"); exit(0);}
 
 //変換(初期設定,データ,進み幅,作業エリア)
 gsl_wavelet_transform_forward(w, out, 1, siz,work);
    
 //フィルタ作業
 for(i = 0; i < siz; i++) abs[i] = fabs(out[i]);
 gsl_sort_index(p, abs, 1, siz);
 for(i = 0; (i + fs) < siz; i++) out[p[i]] = 0;
    
 //逆変換
 gsl_wavelet_transform_inverse(w, out, 1, siz, work);
}
//----------------------------------------------------------------------------

DEFファイルの内容を記入
以下の様に作成しました。
LIBRARY GSLwrapper

EXPORTS
 DWT
 
ビルドと配置
ビルドに成功したら、Releaseフォルダ内のプロジェクト名.dllとlibuフォルダ内のgel.dllおよびgslcblas.dllをコピーし、MT4のlibrariesフォルダにプロジェクト.dllをペーストterminal.exe と同じフォルダ内にgel.dllおよびgslcblas.dllをペーストします。(追記)※教えて頂いた方ありがとうございます。


※再追記
上記コードをそのまま作動させるとメモリリークが発生するそうです。
修正方法は、コメント欄をご覧ください。


呼び出し側のMQL4のコードを作成
以下の様なサンプルコードを作成しました。
//+------------------------------------------------------------------+
//|                                                      DWT_GSL.mq4 |
//|                                        Copyright ゥ 2012, bighope |
//|                       http://expertadviser-bighope.blogspot.com/ |
//+------------------------------------------------------------------+
#property copyright "Copyright ゥ 2012, bighope"
#property link      "http://expertadviser-bighope.blogspot.com/"

#import "GSLwrapper.dll"
   void DWT(double& input[],double& out[],int Ns,int k,int fs);
#import

#property indicator_chart_window
#property indicator_buffers 1
#property indicator_color1 Yellow

extern int       N = 10;//対象データ数(2のN乗)
extern int       D = 4;//ウェーブレットD数※2は、Harr
extern int       max = 50;//最大値からどれだけ残すか
extern int       Shift = 0;//データのシフト
double IDWT[];
double in[];
double out[];
int    DWTPeriod ;
//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int init()
  {
//---- indicators
   SetIndexStyle(0,DRAW_LINE);
   SetIndexBuffer(0,IDWT);
  
   DWTPeriod = MathPow(2,N);
   
   ArrayResize(in,DWTPeriod);
   ArrayResize(out,DWTPeriod);
//----
   return(0);
  }
//+------------------------------------------------------------------+
//| Custom indicator deinitialization function                       |
//+------------------------------------------------------------------+
int deinit()
  {
//----
//----
   return(0);
  }
//+------------------------------------------------------------------+
//| Custom indicator iteration function                              |
//+------------------------------------------------------------------+
int start()
  {
   int counted_bars=IndicatorCounted();
   if(counted_bars>0)return(0);
   ArrayInitialize(in,0.0);
   ArrayInitialize(out,0.0);
   for(int k=0;k<DWTPeriod;k++) in[k]=Open[k+Shift];
   
   DWT(in,out,N,D,max);
   
   for(k=0;k<DWTPeriod;k++)IDWT[Shift+k] = out[k];

   return(0);
  }
//+------------------------------------------------------------------+


手順4

MT4を作動させて、DWT_GSLを作動させてみる。
※インジケータのパラメータ【全般】の(ALL DLL imports) のチェックをいれること。
 16
テストの結果、Experts欄に【 DWT_GSL USDJPY,M5: cannot load library 'GSLwrapper.dll' (error 126)】と表示されインジケータが作動しなかった。ちなみに、XP32bit、MT4.00 Build409 を使用しています。
上記のようにエラーが発生した場合は、librariesフォルダにPathを通すことで対応します。
【マイコンピュータ】右クリック―【プロパティ(R)】―【詳細設定】―【環境変数(N)】―【システム環境変数(S)】
【Pathを選択】―【編集】―一番最後に【;C:\Program Files\MetaTrader 4\experts\libraries】と記入

※デフォルト設定の場合です。各MT4によって調整が必要です。
※\は、¥マークの半角です。
※Pathを追加するだなので、既存の情報を変更しないように注意してください。
作業を終了後MT4を立ち上げると、インジケータが表示されます。
18

サンプル

今回は、VC++2010で説明しましたが、汎用性を考慮してVC++2008で作成した物を添付しておきます。
ご興味のある方は、こちら よりDLしてください。

まとめ

GSLを取り込むことで、作業の低減とバグの低減が可能になると思います。
今回、一番手こずった点は、cannot load library でことごとくDLLを読み取ることが出来なかったところです。
筆者自身、いろいろとOSをいじりすぎているのでもしかしたら、Pathを通す必要がないかもししれません。
もし、Pathを通すことなくDLLが読み込めたとしたらコメントが頂けると幸いです。