MT4用EAからMT5用EAを作るには?

 

「ZigzagBreakoutのMT4用EAのMT5版を開発する予定はありませんか?」

そのようなお話を以前頂いておりまして、その時には「開発の予定はありません」とお答えしたのですが、ちょっと時間があったのでMT5用のEAに改修してみようと、思いつきではじめました。

※MT4はMetaTrader4、MT5はMetaTrader5の略称として使用しております。

※ZigzaBreakoutについては下記のページを御覧ください。

当ブログ人気記事「zigzagブレイクアウト」のEA販売開始です!

MT4用EAの開発につきましては簡単なEAを例にした「ゼロから始めるEA開発」というシリーズ記事もございますので、ご興味がございましたらそちらも御覧ください。

前準備:ファイル名(拡張子)を変更してMT5に設置

拡張子.mq4を複製して、複製したファイルをmq5に変更します。ZigzagBreakoutの場合ですと「ZigZagBreakout.mq4」を「ZigZagBreakout.mq5」に変更です。

これでシステム上はMT5のEAとして認識されます。(当然ながら、中身はMT4のEAですのでそのままでは動作しません。)

このmq5ファイルをMT5のデータフォルダの下、MQL5¥Experts¥Advisorsに移動します。(Expertsフォルダの下であればどこに入れても問題ありません。)

これで改修前の準備は完了です。

改修作業:MT4を起動して1行1行チェック‥!

いよいよ作業開始です。MT5を起動してキーボードのF4を押すとMetaEditorが起動、左のナビゲーターからZigzagBreakuot.mq5を選択するとソースコードが表示されます。

1行1行チェックして修正していきます。コンパイルボタンを押してエラーになる行をチェック、修正をしていっても良いのですが、MQL4とMQL5では名前が同じでも動作が異なる関数があるためコンパイルエラーを全て修正しても期待した動作になるとは限りません。

面倒でも「これはMQL5でも同じ書き方で問題無いか?」を確認していった方が近道ではないかと思います。(と、今回の修正作業をしてみて感じました)

以下に私が実施した修正点と修正方法を記載していきます。なお、ある程度、MQL4のプログラミングができる方向けに書いています。(初心者向けの記事ではございません)

オブジェクトの生成方法

オブジェクトの生成方法が少し変わっています。MT4のソースコード(以下MQL4)が上、MT5のソースコード(以下MQL5)が下です。(特に記載が無い場合にはこの後の記事も上がMQL4、下がMQL5になっています)

ObjectCreate("SpreadLabel", OBJ_LABEL, 0, 0, 0);
ObjectSet("SpreadLabel", OBJPROP_CORNER, 1);
ObjectSet("SpreadLabel", OBJPROP_XDISTANCE, 10);
ObjectSet("SpreadLabel", OBJPROP_YDISTANCE, 15);
ObjectCreate(0,"SpreadLabel", OBJ_LABEL, 0, 0, 0);
ObjectSetInteger(0, "SpreadLabel", OBJPROP_CORNER, 1);
ObjectSetInteger(0, "SpreadLabel", OBJPROP_XDISTANCE, 10);
ObjectSetInteger(0, "SpreadLabel", OBJPROP_YDISTANCE, 15);

引数が1つ増えています。追加されたのはチャートIDで、0が「現在のチャート」になります。

自身のチャートにオブジェクトを作りたいので0を指定しています。

また、ObjectSetがObjectSetIntegerになっています。MQL5では数字の場合にはObjectSetIntegerを使い、文字列の場合にはObjectSetStringを使います。

ポジションの取得
for(int i=0; i<=OrdersTotal()-1; i++)
for(int i=0; i<=PositionsTotal()-1; i++)

OrdersTotal関数がPositionsTotal関数になっています。

ですが、関数の名前が変わったのではありません‥!

MQL4でのOrdersTotal関数は「発注が通って既に持っているポジション」と「指値・逆指値などの待機状態の注文(待機注文)」もまとめて取得できましたが、MQL5ではポジションと待機注文は「別」になりました。

PositionsTotal関数はその名の通り、現在持っているポジションだけを対象とします。

待機注文を取得したい場合にはOrdersTotal関数を使用します。MQL4の関数名と同じで引数も変わらないためコンパイルエラーにはならないのですが、MQL5では待機注文しか取得できないため気をつけないとバグを生み出す原因となります。

「待機注文」と「ポジション」はMQL5では「完全に分離している」ため、他の関数もすべて同様です。(待機注文用の関数とポジション用の関数が用意されています。)

if(OrderSelect(i,SELECT_BY_POS,MODE_TRADES) == false) break;
if(OrderMagicNumber() != Magic || OrderSymbol() !=Symbol()) continue;
if(OrderType() == OP_BUY || OrderType() == OP_SELL) 
if(PositionSelectByTicket(PositionGetTicket(i)) == false) break;
if(PositionGetInteger(POSITION_MAGIC) != Magic || PositionGetString(POSITION_SYMBOL) !=Symbol()) continue;
if(PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY || PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_SELL)

注文を選択、その注文のマジックナンバーが自身(今回はZigzagBreakuout)のものかどうか、通貨ペアは同じかどうかをチェックし、自身の注文で、現在稼働している通貨ペアと同じものであれば、それが「買い」か「売り」かを調べています。

「待機注文」ではなく「ポジション」を調べたい場合にはOrderSelectではなくPositionSelectByTicketを使います。

MQL5の注文選択はチケット番号が必要になるためPositionGetTiketを使ってチケット番号を取得、そのチケット番号を使って注文を選択しています。

マジックナンバーやシンボル、タイプを取得する関数は専用の関数ではなくPositionGetInteger(数値用)、PositionGetString(文字列用)になりました。

これらの関数に引数で指定すると必要な値が取得できます。(指定する値はポジションプロパティのページを御覧ください。)

現在のチャート情報取得
double nearestOrderPoint = FindNearestOrder() * Point;
double nearestOrderPoint = FindNearestOrder() * Point();

少しの違いですがポイントの取得がPointからPoint()という関数になりました。

同様に、DigitsはDigits()で取得できます。

現在の足の時刻の取得方法
if(Time[0] != BarTime)
if(iTime(Symbol(),_Period,0) != BarTime)

MQL4ではTime[0]で「最新の足の時刻」を取得できましたが、MQL5ではiTime関数にシンボル(通貨ペア)と時間軸、バーの位置(シフト)を指定する必要があります。

「実行している通貨ペア、時間軸の最新の足」であれば上記のようになります。

※ 実行している通貨ペアの取得にSymbol()を使用していますが、_Symbolでも取得できます。

CSymbolInfoを使って簡易化することもできるようです。 試したことはありませんが‥。

スプレッドの取得
Spread[0] = MarketInfo(Symbol(),MODE_SPREAD);
Spread[0] = (double)SymbolInfoInteger(Symbol(), SYMBOL_SPREAD);

スプレッドの取得にはMarketInfoという関数を使っていましたが、SymbolInfoIntegerを使って取得するようになりました。

インジケーターから値を取得する

Zigzagインジケーターからの値取得についてはかなり大きく変更されました。

Zigzag=iCustom(Symbol(),0,"ZigZag",ZigzagDepth,ZigzagDeviation,ZigzagBackstep,0,i);
Zigzag=ZigzagArray[i];

MQL4ではiCunstom関数に引数を渡すことでZigzagの値を取得できましたが、MQL5からインジケーターを呼び出すのはそれだけでは足りません。上記で配列から取得するように作っているのですが、当然、このままでは値は空です。

以下のコードを追加しました。

double ZigzagArray[]; 
int OnInit()
{
   ZigzagHandle = iCustom(Symbol(),PERIOD_CURRENT,"Examples\\ZigZag",ZigzagDepth,ZigzagDeviation,ZigzagBackstep);

変数を宣言し、初期化(OnInit)でZigzagのハンドルをiCustomで取得しています。

MQL5ではiCunstom関数を呼び出すだけではZigzagの値は取得できないのです。

void OnTick()
{
   if(CopyBuffer(ZigzagHandle,0,0,iBars(Symbol(),PERIOD_CURRENT),ZigzagArray)<=0)
   {
      Print(EAComment + " Zigzag Buufer Error :" + IntegerToString(GetLastError())); 
   }
   ArraySetAsSeries(ZigzagArray,true);

そしてOnTickでZigzagインジケーターの値を配列にコピーし、ArraySetAsSeriesで「動的配列オブジェクトに AS_SERIES フラグを設定し 時系列のように索引付け」をしています。「最新データから古いデータの順番に取得できるようにする」というものです。

これで配列に値が入りましたので、ZigzagArray[i] のような形でZigzagの値が取得できるようになります。

現在価格の取得(Ask / Bid)

MQL4まではBidやAskと書くだけで取得できましたが、MQL5ではSymbolInfoDoubleで取得する必要があります。

Bidの場合は以下のように記載します。

bid = SymbolInfoDouble(Symbol(), SYMBOL_BID);

Askの場合にはSYMBOL_BIDをSYMBOL_ASKにします。(Symbol()は_SymbolでもOKです)

※Time[0]同様、AskやBidもCSymbolInfoを使って簡易化することもできるようです。試したことはありません。

発注処理
ticket = OrderSend(Symbol(),OP_BUYLIMIT,lots,StopOrderPrice,Slippage,sl,tp,EAComment,Magic,0,Blue);
ticket = OrderSend(Symbol(),ORDER_TYPE_BUY_LIMIT,lots,StopOrderPrice,Slippage,sl,tp,EAComment,Magic,0);

ZigzagBreakout(MT5版)ではOrderSend関数を定義し、MQL4とほぼ同じ形で発注できるように作っています。

OrderDeleteやOrderModifyも関数を作成して、MQL4と似たような形で実行できるようにしました。

申し訳ありませんが、内容については記載・解説をいたしません。(オブジェクト指向で記載されており、MQL4とはかなり違います。)

ZigzagBreakoutをご購入のお客様はソースコードをダウンロード、関数をコピーして他のEAにお使いいただいて問題ありません。(もちろん、関数の内容を解析、改修されたい方もご自由に変更していただいて問題ありません。)

なお、発注処理の関数を使うためにはTrade.mqhをインクルードする必要があります。(ファイルの最初の方で定義します。Trade.mqhはMT5に標準で入っているファイルです。)

#include <Trade\Trade.mqh>
日時取得処理
if(StringToInteger(StopHourList[i]) == Hour())
MqlDateTime mdt;
TimeCurrent(mdt);
if(StringToInteger(StopHourList[i]) == mdt.hour)

MQL4では現在時刻の取得はHour()を使用しますが、MQL5ではMqlDateTimeという構造体を使うようです。(MQL4でもこの構造体は使用できるようです。私は使ったことがありません‥。)

構造体を宣言後、TimeCurrent関数に構造体を引数で渡すと現在日時が格納されます。

時刻の場合にはその構造体のhourを取得します。年月日時分秒と曜日なども取得できるので、日時の取得をする場合にはこの構造体を使用します。

詳細はMqlDateTimeを御覧ください。

口座情報取得処理
if(AccountCurrency() == "JPY") basicEquity *= 100;
lots = AccountEquity() / basicEquity / 10;
if(AccountInfoString(ACCOUNT_CURRENCY) == "JPY") basicEquity *= 100;
lots = AccountInfoDouble(ACCOUNT_EQUITY) / basicEquity / 10;

MQL4では口座の通貨はAccountCrrency()、有効証拠金はAccountEquity()で取得していました。

MQL5の場合、AccountInfoStringやAccountInfDoubleで取得します。

AccountInfoStringで取得できるものはENUM_ACCOUNT_INFO_STRINGを指定、AccountInfoDoubleで取得できるものはENUM_ACCOUNT_INFO_DOUBLEを指定します。(それぞれ何が取得できるかはリンク先を御覧ください。STRINGとDOUBLEの他に、ENUM_ACCOUNT_INFO_INTEGERもあります。その場合にはAccountInfoIntegerで取得します。)

取引履歴の取得
for(int i=OrdersHistoryTotal()-1; i>=0; i--)
{
   if(OrderSelect(i,SELECT_BY_POS,MODE_HISTORY))
HistorySelect(TimeCurrent()-PauseAfterOrdering,TimeCurrent());
for(int i=HistoryDealsTotal()-1; i>=0; i--)
{
   ticket = HistoryDealGetTicket(i);

MQL4で取引履歴を参照する方法はポジションの取得とほぼ同様で、OrderTotalの代わりにOrderHistoryTotal、OrderSelectにMODE_HISTORYを指定することで取得できました。

MQL5ではまず最初にHistorySelectで履歴を取得したい範囲を指定する必要があります。(指定しないと取得できません)

次にHistoryDealsTotalで取得した履歴の合計数が取得できます。(待機注文の場合にはHistoryOrdersTotalです。履歴の場合も待機注文と区別されているのでご注意ください。)

マージンチェック
double freeMargin=AccountFreeMarginCheck(symbol, type, lots);
if(freeMargin<0)
if(type == ORDER_TYPE_BUY || type == ORDER_TYPE_BUY_LIMIT || type == ORDER_TYPE_BUY_STOP)
   price = SymbolInfoDouble(symbol, SYMBOL_ASK);
else
   price = SymbolInfoDouble(symbol, SYMBOL_BID);

if(OrderCalcMargin(type, symbol, lots, price, freeMargin))
   if(freeMargin<0)

MQL4ではAccountFreemarginCheckにシンボル(通貨ペア)、オーダータイプ、ロットを渡せばフリーマージンを取得できましたが、MQL5ではOrderCalcMarginにオーダータイプ、シンボル(通貨ペア)、ロット、価格を渡すと最後の引数(freeMargin)に値が返るようになりました。

価格を渡さなければいけないため、買いの場合にはAsk、売りの場合にはBidを取得して渡しています。

改修が終わったらコンパイル&実行

コンパイルエラーがすべて無くなったらバックテスト実行してみます。間違いがなければ取引が実行され、結果が表示されるはずです。

※パラメーター調整等は行っておりません。動作の確認だけ実施しました。

主な修正点は解説しましたが、すべてではありません。詳しく知りたい方はZigzagBreakoutのソースコードを比較してみてください。(ソースコードの比較にはWinMergeを好んで使っています。)

※改修したソースコードはZigzagBreakoutの最新版に入れておきますので、ご購入のお客様でMQL5版が必要な方はFX研究日記別館-ショップから最新版をダウンロードしてください。

FX研究日記別館-ショップ

最後に

実は「MQL4→MQL5 変換ライブラリ」とでもいうようなライブラリを持っているのですが、私が作ったものではなく、しかもバグが多いため、「自分が使ってみて、見つけたバグだけは自分で修正した」だけの不完全なものでしたので、今回は全部自分で手直ししてみました。

「こんなのは簡単にできるだろう」と思って気楽に着手してみたところ、意外と修正箇所が多く、苦労しました‥。

コンパイルエラーになってくれる場合には調べて置き換えるだけなので問題ありません。しかしに「コンパイルエラーにはならない(MQL5でも動作する)」が「動作がMQL4とMQL5では異なる」という箇所は問題点の洗い出しに時間がかかってしまいました。途中で「もうやめようかな」と思った程です!(笑)

MQL5の知識が少ない中で「コンパイルエラーの修正だけをして、動作確認しながら、問題があれば修正しよう」という考えだと逆に苦労すると思ったので、周り道にも思えますが冒頭の方で書いたように「1行1行、確認しながら修正していったほうが近道ではないか」と思います。

本記事やソースコードがご購入のお客様や、「これからMQL4のソースコードをMQL5に書き換えよう」としている方の一助になりましたら幸いです。

  • このエントリーをはてなブックマークに追加
  • Pocket

この記事へのコメントはこちら

メールアドレスは公開されませんのでご安心ください。
また、* が付いている欄は必須項目となりますので、必ずご記入をお願いします。

内容に問題なければ、下記の「コメント送信」ボタンを押してください。