見出し画像

MQL4とMQL5のソースコード共通化

作成日:2025.02.21
更新日:2025.02.27

はじめに

Macで開発を行う際はMetaTrader5を主に使用していますが、本番運用とフォワードテストはWindows PC上のMetaTrader4で行っています。開発環境が異なることから、スムーズに作業を進めるためにMQL4とMQL5のソースコードの共通化を図っています。

この記事では、MQL4で記述したコードをMetaTrader5でコンパイル・実行できるようにすることを目標に、具体的な方法を紹介します。

MQLとは「MetaQuotes Language」の略で、MetaTraderのプログラミング言語です。(「MetaTrader」を「MT」と記載することがあります。「MT4」「MT5」のようにバージョンを付けることもあります。)

この方法はまだ完成度が低く、改良の余地がたくさんあります。しかし、MQL4で作成されたプログラムをMQL5に移行する際に、少しでもお役に立てればと思い、公開することにしました。

ソースコードは、この記事の一番下に置いてあります。

ソースコード共通化について

本番運用はMT4であるので、ソースコードの言語仕様はMQL4をベースにして記述します。MQL4とMQL5は文法が似ていますが、多くの関数は互換性があります。

開発環境のMT5で、MQL4のソースコードを利用できるようにするため、MQL4で使用されていた定義や関数の互換機能をインクルードファイルとして実装しています。

MQL4とMQL5で同じ名前の関数がある場合、オーバーロードによって対応しています。オーバーロードで対応できない場合や、実装が難しい場合は、MQL5の関数仕様をMQL4の関数仕様に置き換えたり、対応を見送ったりしています。

ソースコードの書き方

ソースコードは、以下の手順で記述します。

  1. 「#property strict」を追加
    MQL5の言語仕様はMQL4よりも厳格化されています。したがってプリプロセッサディレクティブ「#property strict」を追加します。MQL5上では無視されます。

  2. 「#include <mql4to5.mqh>」を追加
    「mql4to5.mqh」は、MQL4で使われていた定義や関数に対応する機能をMQL5で利用できるようにした互換ライブラリです。MQL4互換ライブラリ「mql4to5.mqh」の詳細については後述します。

  3. パラメーターの入力にはextern変数ではなくinput変数を使う
    MT5ではextern変数はプロパティウィンドウに表示されないので、extern変数はinput変数に置き換えます。但し、input変数は値の変更ができません。したがって、変数の値をプログラム中で変更する処理があった場合は、以下のように対処します。(別の方法でも問題ありません。値を変更しない場合は、input 変数の置き換えだけで済みます。)

    1. input変数名はextern変数名と違う名称にします。例えばサフィックス「_」をつけます。元のextern変数はグローバル変数として宣言します。
      extern int HogeHoge = 0;
           ↓
      input int HogeHoge_ = 0;
      int HogeHoge = 0;

    2. OnInit関数の中でグローバル変数にinput数を代入するようにします。
      int OnInit() {
           ︙
       HogeHoge = HogeHoge_;
           ︙
      }

  4. イベント関数
    イベント処理関数は、init、deinit、startを使わず、OnInit、OnDeinit、OnTick、OnStart、OnCalculateを使います。

  5. MQL4の定義済み変数Bars
    MQL4の定義済み変数Barsは使わず、関数のBars(_symbol, _period)を使います。なお、Barsは明示的に宣言していません。コンパイル時にエラーとして検出されるようにしています。

上記の手順でEAを作成する場合、テンプレートは次のようになります。

#property strict

#include <mql4to5.mqh>

input int HogeHoge_ = 0;  // Input Parameter
int HogeHoge;             // I want to change the value of this variable.

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit() {
  // Initialize HogeHoge.
  HogeHoge = HogeHoge_;

  return INIT_SUCCEEDED;
}

//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason) {
}

//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick() {
  // The predefined variable Bars should be replaced with the Bars function.
  int bars = Bars(_symbol, _period);
}

以下は、ティックごとに買いと売りのポジション数をコメントで表示するサンプルです。ポイントはMQL5でもMQL4互換のOrderSelect関数が使えるところです。

#property strict

#include <mql4to5.mqh>

input bool UseComment = true;  // Use Comment

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit() {
  Print("### OnInit finished.");
  return INIT_SUCCEEDED;
}
//+------------------------------------------------------------------+

//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason) {
  Comment("");
  Print("### OnDeinit finished.");
}
//+------------------------------------------------------------------+

//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick() {
  int orders_total = OrdersTotal();
  int pos_b = 0, pos_s = 0;
  for (int i = 0; i < orders_total; i++) {
    if (!OrderSelect(i, SELECT_BY_POS)) continue;
    int order_type = OrderType();
    if (order_type == OP_BUY)
      pos_b++;
    else if (order_type == OP_SELL)
      pos_s++;
  }
  if (UseComment) Comment("BUY=", pos_b, ", SELL=", pos_s);
}
//+------------------------------------------------------------------+

このように、手順にしたがってMQL4ベースのソースコードを記述すれば、MT5でコンパイルして動かすことができます。

MQL4互換ライブラリ「mql4to5.mqh」について

このインクルードファイルは、MQL4で使われていた定義や関数をMQL5で利用できるようにした互換ライブラリです。

大きく分けて、以下の 4つで構成されています。

  1. MQL4トレード関数への対応

  2. MQL4のその他の関数と定義への対応

  3. iCustom関数の対応

  4. その他

それでは、それぞれの項目について説明していきます。

1. MQL4トレード関数への対応

MQL4のトレード関数については、以下のMQL5コミュニティのコードベースを参考にしました。この無料の記事に添付されている「MT4Orders.mqh」を修正して利用しています。(驚きました。世の中にはすごい人がいます。)

「MT4Orders.mqh」は、このサイトからダウンロードできます。MQL5コミュニティのアカウントを登録しなくても、ソースコード表示だけならファイル名の横の「view」のリンクをクリックすれば可能です。

「mql4to5.mqh」は、修正したファイル「MT4Orders_mod.mqh」をインクルードして、MQL4のトレード関数をMQL5でも利用できるようにしています。

修正した「MT4Orders_mod.mqh」を公開しようと思いましたが、ライセンスが不明なため修正方法を説明します。

ticketの型がMQL4とMQL5で異なるので、MQL4に合わせます。

188行目「#ifdef __MQL5__」と189行目「#ifndef __MT4ORDERS__」の間に「#define MT4_TICKET_TYPE」を挿入します。

 ︙          ︙
186    //   Add: OrderLots(true) - synchronized size of the selected position, taking into account all orders which close this position.
187
188    #ifdef __MQL5__
189    #ifndef __MT4ORDERS__
 ︙          ︙

 ︙          ︙
186    //   Add: OrderLots(true) - synchronized size of the selected position, taking into account all orders which close this position.
187
188    #ifdef __MQL5__
189    #define MT4_TICKET_TYPE
190    #ifndef __MT4ORDERS__
 ︙          ︙

修正した「MT4Orders.mqh」は「MT4Orders_mod.mqh」として保存します。

このファイルで使われている一部の変数名や引数名は、グローバル変数に使われやすい名前なので、衝突することがあると思います。すべてを修正するのは大変な作業なので、コンパイルでエラーになったときに、エラーになった変数をリネームしていけばよいかと思います。

私は衝突した変数名は、前に「__」を付け足してリネームしました。変更した変数は、
Price」「Magic」「StartTime」「Expiration」「SL」「TP」「StopLevel
です。

なお、「MT4Orders.mqh」をそのまま使う場合は、「mql4to5.mqh」を修正します。具体的には、「mql4to5.mqh」の中で、
「#include <MT4Orders_mod.mqh>」
の部分を、以下のように書き換えてください。

#include <MT4Orders_mod.mqh>

#ifdef __MQL5__
#define MT4_TICKET_TYPE
#endif
#include <MT4Orders.mqh>

2. MQL4のその他の関数と定義への対応

MQL4のその他の関数と定義をMQL5に移植する方法は、以下のサイトを参考にしています。

このサイトの情報によって大部分を網羅できていますが、完全なMQL4互換は難しく、一部、次のような対応となっています。

  • MQL5の関数仕様をMQL4の関数仕様に変更
    MQL4とMQL5で同一関数名を持つ関数において、引数の数や型が異なる場合、関数のオーバーロードによってMQL4の関数仕様を追加しています。オーバーロードで対応できない場合は、MQL5の関数仕様をMQL4の関数仕様に変更しています(以下「MQL4優先」)。

  • 影響が少ない範囲で仕様変更

  • 未実装

  • MQL4互換関数を別関数名で実装
    テクニカル指標関数はMQL4優先にしていません。オーバーロードで対応できない場合は、MQL4の互換関数を別の関数名で実装しています。

MQL4互換で制限のある関数一覧を以下に示します。

MQL4関数          MQL5での対応
------------------------------------------------------------
AccountFreeMarginMode   必ず1を返却
ArrayDimension          未実装
ArrayMaximum            MQL4優先
ArrayMinimum            MQL4優先
ArraySort               MQL4優先(制限あり)
IsTradeContextBusy      必ずfalseを返却
IndicatorBuffers        何も処理しない
IndicatorCounted        OnCalculate内でしか使用できない
FileOpenHistory         未実装
FileReadDouble          sizeパラメーターは無視(DOUBLE_SIZE固定)
StringTrimLeft          MQL4優先
StringTrimRight         MQL4優先
StringConcatenate       MQL4優先(63個まで結合)
iAD                     MQL4版「iAD」は「iADMQL4」
iBandsOnArray           未実装
iBWMFI                  MQL4版「iBWMFI」は「iBWMFIMQL4」
iCCIOnArray             未実装
iEnvelopesOnArray       未実装
iMomentumOnArray        未実装
iMFI                    MQL4版「iMFI」は「iMFIMQL4」
iRSIOnArray             未実装
iStdDevOnArray          未実装
RefreshRates            必ずtrueを返却

未実装とMQL4優先以外で、上記の関数のリストを補足説明します。

AccountFreeMarginMode

必ず1を返却します。余剰証拠金は、評価損益を含めて計算するというモードです。(未実装にした方がよかったかもしれません。)

ArraySort

ArraySortはMQL4優先です。しかし、完全にはトレースできていません。MQL4の関数仕様は以下になりますが、MQL5の関数仕様では、第2パラメーター以降がありません。

bool ArraySort(
  void& array[],              // array for sorting
  int count = WHOLE_ARRAY,    // count
  int start = 0,              // starting index
  int direction = MODE_ASCEND // sort direction
);

第1引数の配列は、1次元の場合、MQL4仕様と互換性があります。2次元以上の場合は、以下の条件を満たしていれば問題ありません。

第2引数「count」が「WHOLE_ARRAY」であり、かつ第3引数「start」が「0」である。

これらの条件を満たさない場合は、マクロを追加する必要があります。このマクロは、コンパイル時にArraySortの互換関数を生成します。

引数arrayの配列が2次元で、2次元目の要素数が2個(例えば、array[, 2])の場合、「GENERATE_ARRAYSORTMQL4_2D(2)」というマクロを追加してください。マクロの「(2)」の部分は、2次元目の要素数を表します。以下のファイルの5行目が該当する記述です。

引数arrayの配列が3次元で、2次元目の要素数が3個、3次元目の要素数が4個(例えば、array[, 3, 4])なら、「GENERATE_ARRAYSORTMQL4_3D(3,4)」というマクロを追加してください。ファイルの6行目が該当する記述です。

引数arrayの配列が4次元で、2次元目の要素数が2、3次元目の要素数が3、4次元目の要素数が4(例えば、array[, 2, 3, 4])なら、「GENERATE_ARRAYSORTMQL4_4D(2,3,4)」というマクロを追加してください。ファイルの7行目が該当する記述です。

MQL5で許可される最大配列次元数は4です。したがって5次元以上は対応していません。

IsTradeContextBusy

MQL5には対応する関数はありません。しかし、MQL4ではこの関数が頻繁に利用されていた背景を踏まえ、MQL5への移植を円滑にする目的で、常にfalseを返す仕様で実装しています。

IndicatorBuffers

MQL5には対応する関数はありません。この関数は空の関数が実装されています。呼び出しても何も実行されません。

IndicatorCounted

イベント関数のOnCalculate内でしか記述できないという制限があります。

FileReadDouble

sizeパラメーターは無視されます。

StringConcatenate

63個まで結合可能です。

RefreshRates

「mql4to5.mqh」では、AskやBidなどの気配値は最新の値を返却するので、この関数は常にtrueを返す仕様で実装しています。

MQL4互換関数を別関数名で用意した関数「iAD」「iBWMFI」「iMFI」

これらは、関数名も引数も衝突していますが、MQL4優先にはしていません。MQL4互換関数は別の関数名で用意しています。

もしMQL5上でMQL4と同じ動作をさせたい場合は、「mql4to5.mqh」をインルードした後にマクロで関数名変換してください。

#include <mql4to5.mqh>

#ifdef __MQL5__
#define iAD iADMQL4
#define iBWMFI iBWMFIMQL4
#define iMFI iMFIMQL4
#endif

3. iCustom関数の対応

iCustom関数のMQL4とMQL5間の互換性については、現在、完全な解決策を提供できていません。カスタムインジケーターごとにMQL4とMQL5両対応のラッパー関数を用意するという方法で対応しています。つまり、現状ではカスタムインジケーターごとにラッパー関数を記述する必要があります。

カスタムインジケーターのラッパー関数は、「mql4to5ic.mqh」というファイルに記述します。「mql4to5.mqh」はこのファイルをインクルードしています。

参考までに、「mql4to5ic.mqh」ファイルには、ZigZagと平均足のラッパー関数のサンプルコードが含まれています。これらの関数は、MQL4/MQL5の両方で共通して利用できます。

平均足用の関数
double iCustom_HeikenAshi(string symbol, int timeframe, int mode, int shift)

ZigZag用の関数
double iCustom_ZigZag(string symbol, int timeframe, int depth, int deviation, int backstep, int mode, int shift)

これらの関数を呼び出した場合、OnDeinit関数で「Release_All_iCustom」関数を呼んで後始末してください。

これが現状の方法ですが、より良い解決策がある場合は、「mql4to5.mqh」の一番下のインクルードの部分を削除して、その解決策に変更してください。

//+-------------------------------------------------------------------------------------+
//| Include for iCustiom().                                                             |
//+-------------------------------------------------------------------------------------+
#include <mql4to5ic.mqh>
//+-------------------------------------------------------------------------------------+

カスタムインジケーターのラッパー関数について

MQL4用のラッパー関数は、単純にiCustom関数を呼び出すだけなので、説明は省略します。

MQL5では指標関数の仕様が大きく変更され、呼び出すとハンドルを返却するようになりました。このハンドルを使ってインジケーターの値を取り出します。

テクニカル指標関数(iMAやiATRなど)を呼び出すとハンドルが取得できます。同一引数で呼び出しを繰り返しても、同一ハンドル値が返されるようです。(iATR関数で検証してみました。)

ところが、カスタムインジケーター用のiCustom関数の場合は、同一引数での呼び出しでも別のハンドル値が返されるようです。(ZigZagで検証してみました。)

これは、呼び出す度に内部で別メモリが割り当てられていると考えられます。つまり使用メモリの肥大化に繋がってしまいます。(これは私の推測であり、間違っているかもしれません。)

MQL5の一般的な記述方法では、利用する指標関数はOnInit内で呼び出して、そのハンドルを保持するようにします。OnTickやOnCalculateでは、このハンドルを利用して指標の値を取り出します。つまり、MQL4のように指標関数を何度も呼び出す使い方ではないので、問題になっていないのかもしれません。

横道にそれましたが、つまり、ラッパー関数の実装では、同一引数でiCustom関数が重複して呼び出されるのを防ぐため、呼び出し状況の管理が必要です。「mql4to5ic.mqh」では、ラッパー関数ごとにハッシュマップを持ち、関数名と引数をキーにしてハンドルを管理しています。

OnDeinit関数でRelease_All_iCustom関数を呼ぶ必要があるのは、このハッシュマップを解放するためです。

4. その他

MQL5では削除された「Time[]」「 Open[]」「 Close[]」「 High[]」「 Low[]」「 Volume[]」の時系列アクセスついては、以下のURLを参考にしています。

ErrorDescription関数については、以下のURLを参考にしています。

最後に

このように成果物を整理してドキュメントにまとめると、様々な不備が見えてきます。とは言え、このMQL4互換ライブラリは、私的には十分役立っています。MQL4のソースコードを少し手直しするだけで、MQL5でもOKなわけですから。

拙いサンプルコードですが、ご参考になれば幸いです。

それでは、また。

ソースコードについて

「mql4to5.mqh」は「mql4to5ic.mqh」と「MT4Orders_mod.mqh」をインクルードしています。

「MT4Orders_mod.mqh」は前述の通り、ライセンスが不明なため、「MT4Orders.mqh」を以下のサイトからダウンロードして修正してください。(MQL5コミュニティのアカウント登録しなくても、ファイル名の横の「view」のリンクをクリックすればソースコードが表示されます。)

「mql4to5.mqh」と「mql4to5ic.mqh」は、以下からダウンロードしてください。

「mql4to5.mqh」「mql4to5ic.mqh」「MT4Orders_mod.mqh」は、以下のフォルダの直下に置いてください。

 MT4 → データフォルダ\MQL4\Include
 MT5 → データフォルダ\MQL5\Include

いいなと思ったら応援しよう!

SORA PAPA
よろしければ応援お願いします!

ピックアップされています

MetaTraderの備忘録

  • 10本

コメント

ログイン または 会員登録 するとコメントできます。
FXに興味を持ち、MetaTrader、TradingView、JForex、cTraderなどを模索している元ソフトウェア開発者です。
MQL4とMQL5のソースコード共通化|SORA PAPA
word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word

mmMwWLliI0fiflO&1
mmMwWLliI0fiflO&1
mmMwWLliI0fiflO&1
mmMwWLliI0fiflO&1
mmMwWLliI0fiflO&1
mmMwWLliI0fiflO&1
mmMwWLliI0fiflO&1