MQL5 にはカスタムインジケーターや EA のコード記述を簡易化するための標準ライブラリが用意されている。
標準ライブラリは、<データフォルダ>\MQL5\Include
フォルダー配下に .mqh ファイルで格納されている。
MQL4 にも同様に標準ライブラリは存在するが公式ドキュメントがなく、また MQL5 標準ライブラリに用意されているクラスが MQL4 標準ライブラリにないといったことがある。
MT5 がそれほど普及せず MT4 がいまだに使われ続けている現状では、MQL5 標準ライブラリを使って開発したものを MQL4 に移植したいことがあるかもしれない。
というより管理人は実際にこの状況に直面し、ラッパークラスを作ることで MQL5、MQL4 に両対応した。
他にも方法はあるかもしれないが、同じような境遇に直面している方の道しるべとなればよいと思い記事にする。
参考:MQL5 リファレンス 標準ライブラリ
実際に標準ライブラリのフォルダーやファイルを見比べてみると、MQL5 にあって MQL4 にないクラスがあることがよくわかる。
「<データフォルダ>\MQL4\Include」フォルダーの例
「<データフォルダ>\MQL5\Include」フォルダーの例
カスタムインジケーターや EA を実装する上で大きく影響があったのは、MQL4 標準ライブラリには取引操作に関する機能 (取引クラス Trade Classes) がないことだった。
自作のカスタムインジケーターや EA の MQL5 ソースは、取引クラスの機能を多く参照していたため、それらの機能を MQL4 側にも実装する必要があった。
目標は、MQL5 ソースで参照している取引クラスのメソッドをラップしてMQL5 / MQL4 両方で使えるようにすることになった。
なにごとも簡単そうなところから手をつけるのがいい。
この記事では比較的単純なCSymbolInfo のラッパークラスを紹介する。
管理人の事例では最終的に下記のクラスのラッパーが必要になった。
CSymbolInfo のメソッド全部を使えるようにするのが理想だが、それでは手間がかかりすぎる。
MQL5 ソースで使っている必要最小限のメソッドだけラップすることにした。
ほかのメソッドが必要になった場合は後から足していけばよい。
プリプロセッサでコードを切り分ける。
MQL4 のソースをビルドするとき、ソースに #define _MQL4
を定義すれば MQL4 版のコードがビルドされる。
定義しなければ MQL5 版のコードがビルドされる。
#ifdef _MQL4 // MQL4 版のコードをここに書く #else // _MQL4 // MQL5 版のコードをここに書く #endif // _MQL4
MQL5 版のラップメソッドのコードは CSymbolInfo
の該当メソッドを呼ぶだけでよい。
MQL4 版のほうはわりと大変💦 だが、SymbolInfoDouble()
や SymbolInfoInteger()
を使って対応する値がおおむね取得できる。
MQL5 標準ライブラリ CSymbolInfo のソース (<データフォルダ>\MQL5\Include\Trade\SymbolInfo.mqh
) を確認しながら同等な処理を行うように移植していく。
3 分クッキング方式( ^ω^)・・・
//+------------------------------------------------------------------+ //| SymbolInfoHelper.mqh | //+------------------------------------------------------------------+ #ifndef _SYMBOLINFOHELPER_MQH_ #define _SYMBOLINFOHELPER_MQH_ //+------------------------------------------------------------------+ //| インクルード | //+------------------------------------------------------------------+ #ifdef _MQL4 #include <Object.mqh> #else // _MQL4 #include <Trade\SymbolInfo.mqh> #endif // _MQL4 //+------------------------------------------------------------------+ //| ClSymbolInfoHelperクラス | //+------------------------------------------------------------------+ class ClSymbolInfoHelper : public CObject { public: ClSymbolInfoHelper(void); virtual ~ClSymbolInfoHelper(void); bool Refresh(void); bool RefreshRates(); bool Name(const string name); // シンボル設定 string Name(void); int Spread(void) const; double Bid(void); double Ask(void); int Digits(void); double Point(void); double LotsMin(void) const; double LotsMax(void) const; double LotsStep(void) const; double SwapLong(void) const; double SwapShort(void) const; protected: #ifdef _MQL4 string l_name; // シンボル MqlTick l_tick; // ティック double l_point; // ポイント double l_lots_min; // 最小ロット double l_lots_max; // 最大ロット double l_lots_step; // ロットステップ double l_swap_long; // スワップ(ロング) double l_swap_short; // スワップ(ショート) int l_digits; // 桁 #else // _MQL4 CSymbolInfo l_SymbolInfo; // シンボル情報 #endif // _MQL4 }; //+------------------------------------------------------------------+ //| コンストラクター | //+------------------------------------------------------------------+ ClSymbolInfoHelper::ClSymbolInfoHelper(void) { #ifdef _MQL4 l_name = ""; ZeroMemory(l_tick); l_point = 0.0; l_lots_min = 0.0; l_lots_max = 0.0; l_lots_step = 0.0; l_swap_long = 0.0; l_swap_short = 0.0; l_digits = 0; #else #endif } //+------------------------------------------------------------------+ //| デストラクター | //+------------------------------------------------------------------+ ClSymbolInfoHelper::~ClSymbolInfoHelper(void) { } //+------------------------------------------------------------------+ //| Refresh | //+------------------------------------------------------------------+ bool ClSymbolInfoHelper::Refresh(void) { #ifdef _MQL4 // クラス変数の設定 long tmp_long = 0; if(!SymbolInfoDouble(l_name, SYMBOL_POINT, l_point)) return(false); if(!SymbolInfoDouble(l_name, SYMBOL_VOLUME_MIN, l_lots_min)) return(false); if(!SymbolInfoDouble(l_name, SYMBOL_VOLUME_MAX, l_lots_max)) return(false); if(!SymbolInfoDouble(l_name, SYMBOL_VOLUME_STEP, l_lots_step)) return(false); if(!SymbolInfoDouble(l_name, SYMBOL_SWAP_LONG, l_swap_long)) return(false); if(!SymbolInfoDouble(l_name, SYMBOL_SWAP_SHORT, l_swap_short)) return(false); if(!SymbolInfoInteger(l_name, SYMBOL_DIGITS, tmp_long)) return(false); l_digits = (int)tmp_long; return(true); #else return(l_SymbolInfo.Refresh()); #endif } //+------------------------------------------------------------------+ //| レート更新 | //+------------------------------------------------------------------+ bool ClSymbolInfoHelper::RefreshRates(void) { #ifdef _MQL4 return(SymbolInfoTick(l_name, l_tick)); #else return(l_SymbolInfo.RefreshRates()); #endif } //+------------------------------------------------------------------+ //| シンボル設定 | //+------------------------------------------------------------------+ bool ClSymbolInfoHelper::Name(const string name) { #ifdef _MQL4 l_name = name; if(!Refresh()) { return(false); } return(true); #else return(l_SymbolInfo.Name(name)); #endif } //+------------------------------------------------------------------+ //| シンボル | //+------------------------------------------------------------------+ string ClSymbolInfoHelper::Name(void) { #ifdef _MQL4 return(l_name); #else return(l_SymbolInfo.Name()); #endif } //+------------------------------------------------------------------+ //| スプレッド | //+------------------------------------------------------------------+ int ClSymbolInfoHelper::Spread(void) const { #ifdef _MQL4 return((int)SymbolInfoInteger(l_name, SYMBOL_SPREAD)); #else return(l_SymbolInfo.Spread()); #endif } //+------------------------------------------------------------------+ //| 売値 | //+------------------------------------------------------------------+ double ClSymbolInfoHelper::Bid(void) { #ifdef _MQL4 return(l_tick.bid); #else return(l_SymbolInfo.Bid()); #endif } //+------------------------------------------------------------------+ //| 買値 | //+------------------------------------------------------------------+ double ClSymbolInfoHelper::Ask(void) { #ifdef _MQL4 return(l_tick.ask); #else return(l_SymbolInfo.Ask()); #endif } //+------------------------------------------------------------------+ //| 通貨ペアの小数点以下桁数 | //+------------------------------------------------------------------+ int ClSymbolInfoHelper::Digits(void) { #ifdef _MQL4 return(l_digits); #else return(l_SymbolInfo.Digits()); #endif } //+------------------------------------------------------------------+ //| 通貨ペアのポイント | //+------------------------------------------------------------------+ double ClSymbolInfoHelper::Point(void) { #ifdef _MQL4 return(l_point); #else return(l_SymbolInfo.Point()); #endif } //+------------------------------------------------------------------+ //| 最小ロット | //+------------------------------------------------------------------+ double ClSymbolInfoHelper::LotsMin(void) const { #ifdef _MQL4 return(l_lots_min); #else return(l_SymbolInfo.LotsMin()); #endif } //+------------------------------------------------------------------+ //| 最大ロット | //+------------------------------------------------------------------+ double ClSymbolInfoHelper::LotsMax(void) const { #ifdef _MQL4 return(l_lots_max); #else return(l_SymbolInfo.LotsMax()); #endif } //+------------------------------------------------------------------+ //| 最大ロット | //+------------------------------------------------------------------+ double ClSymbolInfoHelper::LotsStep(void) const { #ifdef _MQL4 return(l_lots_step); #else return(l_SymbolInfo.LotsStep()); #endif } //+------------------------------------------------------------------+ //| スワップ(ロング) | //+------------------------------------------------------------------+ double ClSymbolInfoHelper::SwapLong(void) const { #ifdef _MQL4 return(l_swap_long); #else return(l_SymbolInfo.SwapLong()); #endif } //+------------------------------------------------------------------+ //| スワップ(ショート) | //+------------------------------------------------------------------+ double ClSymbolInfoHelper::SwapShort(void) const { #ifdef _MQL4 return(l_swap_short); #else return(l_SymbolInfo.SwapShort()); #endif } #endif // _SYMBOLINFOHELPER_MQH_ //+------------------------------------------------------------------+
プリプロセッサでコードを切り分けたので、MQL4 のソースをビルドするときはソースに #define _MQL4 を定義することに留意する。
//+------------------------------------------------------------------+ //| Sample.mq4 | //+------------------------------------------------------------------+ #property strict #property copyright "" #property version "1.0" // MQL4用ソースを使用 #define _MQL4 // インクルード #include "SymbolInfoHelper.mqh" //+------------------------------------------------------------------+ //| Custom indicator initialization function | //+------------------------------------------------------------------+ int OnInit() { ClSymbolInfoHelper symbolInfo; if(!symbolInfo.Name(Symbol())) { Print("Error initializing symbol."); return(INIT_FAILED); } if(!symbolInfo.RefreshRates()) { Print("Error initializing symbol."); return(INIT_FAILED); } PrintFormat("シンボル = %s", symbolInfo.Name()); PrintFormat("スプレッド = %d", symbolInfo.Spread()); PrintFormat("Ask = %s", DoubleToString(symbolInfo.Ask(), symbolInfo.Digits())); PrintFormat("Bid = %s", DoubleToString(symbolInfo.Bid(), symbolInfo.Digits())); return(INIT_SUCCEEDED); } // (以下、略)