{{tag>mql5 mql4 csymbolinfo}}
====== MQL5 標準ライブラリのラッパークラスを作ろう (CSymbolInfo 編) ======
{{INLINETOC}}
===== ラッパークラスを作る目的 =====
MQL5 にはカスタムインジケーターや EA のコード記述を簡易化するための標準ライブラリが用意されている。\\
標準ライブラリは、''<データフォルダ>\MQL5\Include'' フォルダー配下に .mqh ファイルで格納されている。
MQL4 にも同様に標準ライブラリは存在するが公式ドキュメントがなく、また MQL5 標準ライブラリに用意されているクラスが MQL4 標準ライブラリにないといったことがある。\\
MT5 がそれほど普及せず MT4 がいまだに使われ続けている現状では、MQL5 標準ライブラリを使って開発したものを MQL4 に移植したいことがあるかもしれない。\\
というより管理人は実際にこの状況に直面し、ラッパークラスを作ることで MQL5、MQL4 に両対応した。\\
他にも方法はあるかもしれないが、同じような境遇に直面している方の道しるべとなればよいと思い記事にする。
参考:MQL5 リファレンス [[https://www.mql5.com/ja/docs/standardlibrary|標準ライブラリ]]
===== MQL4 / MQL5 標準ライブラリの違い =====
実際に標準ライブラリのフォルダーやファイルを見比べてみると、MQL5 にあって MQL4 にないクラスがあることがよくわかる。
__「<データフォルダ>\MQL4\Include」フォルダーの例__\\
{{gallery>:metatrader:mql:pasted:20230807-110014.png?lightbox}}
__「<データフォルダ>\MQL5\Include」フォルダーの例__\\
{{gallery>:metatrader:mql:pasted:20230807-110028.png?lightbox}}
カスタムインジケーターや EA を実装する上で大きく影響があったのは、MQL4 標準ライブラリには取引操作に関する機能 ([[https://www.mql5.com/ja/docs/standardlibrary/tradeclasses|取引クラス Trade Classes]]) がないことだった。\\
自作のカスタムインジケーターや EA の MQL5 ソースは、取引クラスの機能を多く参照していたため、それらの機能を MQL4 側にも実装する必要があった。\\
目標は、**MQL5 ソースで参照している取引クラスのメソッドをラップしてMQL5 / MQL4 両方で使えるようにすること**になった。
===== どこから手を付ける? =====
なにごとも簡単そうなところから手をつけるのがいい。\\
この記事では比較的単純な[[https://www.mql5.com/ja/docs/standardlibrary/tradeclasses/csymbolinfo|CSymbolInfo]] のラッパークラスを紹介する。\\
管理人の事例では最終的に下記のクラスのラッパーが必要になった。
* CAccountInfo
* CDealInfo
* CSymbolInfo
* CPositionInfo
* CTrade
===== CSymbolInfo ラッパークラスの作成 =====
==== ラップしたメソッド ====
CSymbolInfo のメソッド全部を使えるようにするのが理想だが、それでは手間がかかりすぎる。\\
MQL5 ソースで使っている必要最小限のメソッドだけラップすることにした。\\
ほかのメソッドが必要になった場合は後から足していけばよい。
* Refresh
* RefreshRates
* Name
* Spread
* Bid
* Ask
* Digits
* Point
* LotsMin
* LotsMax
* LotsStep
* SwapLong
* SwapShort
==== MQL4 と MQL5 のコードの切り分け方法 ====
プリプロセッサでコードを切り分ける。\\
MQL4 のソースをビルドするとき、ソースに ''#define _MQL4'' を定義すれば MQL4 版のコードがビルドされる。\\
定義しなければ MQL5 版のコードがビルドされる。
#ifdef _MQL4
// MQL4 版のコードをここに書く
#else // _MQL4
// MQL5 版のコードをここに書く
#endif // _MQL4
==== CSymbolInfo のソースを確認して MQL4 に移植していく ====
MQL5 版のラップメソッドのコードは ''CSymbolInfo'' の該当メソッドを呼ぶだけでよい。\\
MQL4 版のほうはわりと大変💦 だが、''SymbolInfoDouble()'' や ''SymbolInfoInteger()'' を使って対応する値がおおむね取得できる。\\
MQL5 標準ライブラリ CSymbolInfo のソース (''<データフォルダ>\MQL5\Include\Trade\SymbolInfo.mqh'') を確認しながら同等な処理を行うように移植していく。
==== ラッパークラス ClSymbolInfoHelper 完成! ====
3 分クッキング方式( ^ω^)・・・
=== コード ===
//+------------------------------------------------------------------+
//| SymbolInfoHelper.mqh |
//+------------------------------------------------------------------+
#ifndef _SYMBOLINFOHELPER_MQH_
#define _SYMBOLINFOHELPER_MQH_
//+------------------------------------------------------------------+
//| インクルード |
//+------------------------------------------------------------------+
#ifdef _MQL4
#include
#else // _MQL4
#include
#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);
}
// (以下、略)