MT4 (Metatrader 4)上で動くEA (Expert Advisor) は通常MQL4と呼ばれる言語で記述されます。機械学習を用いたアプローチでEAを作成する場合、既存の機械学習のライブラリやコードを用いるためにも、ロジックをPythonで記述できれば便利です。
MT5では公式にPythonとの連携機能が用意されているのですが、残念ながら国内業者での採用が多いMT4では用意されていません。そこで今回は、MT4/MQL4とPythonの連携方法について解説します。
MT4/MQL4とPythonの連携方法
MT4/MQL4からPythonのコードを実行、またはMT4/MQL4とPythonの間で情報を受け渡すにはさまざまな方法が考えられます。
- ソケット (この記事で解説)
 - 名前付きパイプ
 - ファイル
 - 共有メモリ
 - Pythonの埋め込み機能
 
第1回として本記事ではソケットを用いた方法を解説します。ソケットのメリットとしては比較的難易度が低いことと、MT4/MQL4とPythonをそれぞれ別のコンピュータ上で動作可能であることなどがあります。
全体構成
MT4でレートの取得と売買を行い、Python側では売買の判定のみを行うという構成にする場合、MT4とPythonの間で最低でも以下の3種類のデータをやりとりする必要があります。
- レート(MT4→Python)
 - 売買や情報取得命令(Python→MT4)
 - 売買や情報取得命令の結果を返す(MT4→Python)
 
今回は簡単のため、Python側でソケットサーバーを作成して接続を待ち受け、MT4のOnTick()のタイミングごとにPythonのソケットサーバーに接続し、データを送受信する構成をとります。
MT4の準備
まず、MT4のオプション→エキスパートアドバイザで、「WebRequestを許可するURLリスト」に「http://localhost」を追加しておきます。

Pythonの準備
Pythonのセットアップは完了しているものとします。Flask をインストールしておきます。pipの場合次のようにします。
pip install FlaskFlaskの動作確認のため、Pythonで次のコードを実行します。
# -*- coding: utf-8 -*-
from flask import Flask, request
app = Flask(__name__)
@app.route("/")
def root():
    return 'OK'
if __name__ == '__main__':
    app.run(debug=False, host='127.0.0.1', port=8080)Webブラウザで http://localhost:8080 にアクセスし、OKと表示されたら、Flaskは正常に動いています。

MT4とPythonで通信する
Pythonで次のコードを実行します。
# -*- coding: utf-8 -*-
from flask import Flask, request
app = Flask(__name__)
prev_close = -1
@app.route("/")
def root():
    return 'OK'
@app.route("/reset")
def reset():
    global prev_close
    prev_close = -1
    return ""
@app.route("/ontick")
def ontick():
    open  = float(request.args.get('open'))
    high  = float(request.args.get('high'))
    low   = float(request.args.get('low'))
    close = float(request.args.get('close'))
    global prev_close
    if prev_close == -1:
        prev_close = close
    print(open, high, low, close, prev_close)
    prev_close = close
    return 'OK'
if __name__ == '__main__':
    app.run(debug=False, host='127.0.0.1', port=8080)
次に、MetaEditorで次の内容のEAを作成し、実行します。
void OnTick()
{
   static datetime prev_time = Time[0];
   
   // 新しい足ができていないときはなにもせずに抜ける   
   if(Time[0] == prev_time) {
      return;
   }
   
   prev_time = Time[0];
   string cookie=NULL, headers;
   char   post[], result[];
   string format = "http://localhost:8080/ontick?symbol=%s&time=%s&open=%f&high=%f&low=%f&close=%f";
   string request = StringFormat(format, Symbol(), TimeToStr(Time[0] , TIME_DATE | TIME_SECONDS), Open[0], High[0], Low[0], Close[0]);
   int response = WebRequest("GET", request, cookie, NULL, 500, post, 0, result, headers);
   if (response == -1) {
     Print("Error in WebRequest. Error code  =",GetLastError());
   } else {
      if (response == 200) {
         // サーバーから返ってきた値を表示する
         Print("Return from python: " + CharArrayToString(result));
      } else {
         PrintFormat("Request '%s' failed, error code %d", url, response);
      }
    }
}上記のプログラムは、MT4からPythonに時刻と四本値を送信し、PythonからMT4には”OK”という文字列を返しています。こうして、MT4とPythonの間で情報をやりとりすることができます。注意点としては、実際に上記のプログラムを動かしてみるとわかるのですが、ときどきエラーで止まってしまいます。このため、実運用する際には、エラー処理なども作り込む必要があります。
次回は、名前付きパイプを利用してPythonとMT4を連携する方法を紹介します。
  
  
  
  


コメント
大変参考になりました。ありがとうございます。
ただ、MetaEditorのEAの内容についてコピペではうまくいかなかった箇所があったので
コメントに残させていただきます。
・14行目
string format = “http://localhost:8080/tick?symbol=%s&time=%s&open=%f&high=%f&low=%f&close=%f”;
URL部について、sample2.pyでは、ポートを「port=80」と定義しているため、
http://localhost:8080ではなくhttp://localhost:80だと思われます。
また、route以降を「tick」としていますが、sample2.pyでは、
routeを「/ontick」と定義しているため、http://localhost:80/ontick
だと思われます。
pythonを使ってEAやインジケータを作るにあたり大変参考にさせていただきましたので
差し出がましいようであれば、申し訳ございません。
ありがとうございました。
間違いの指摘ありがとうございました。
今後ともよろしくお願いいたします。