MT5 API(MetaTrader5モジュール)を使用して、Pythonから現在PCで起動しているMT5へ接続、操作する

　FX戦略は証券会社よって違う為替レート、ティックボリューム、スプレッドなどの影響もあって、実際に取引する業者のヒストリカルデータを使って検証したほうがいい場合もあるかもしれません(とくに短期戦略)。MT5では接続先業者からリアルに基づくヒストリカルデータを取得できます。(期間は業者よってことなり、どの程度信頼できる精度なのかはわかりません。)

　この記事ではMT5 APIを使用して、Pythonから現在PCで起動しているMT5へ接続、操作する方法を解説します。

例えば、以下のような操作が可能です。
- 接続先のアカウント情報、ターミナル情報を取得
- 取扱い銘柄のレートを取得
- 注文
- 取引履歴の取得

# 準備と基本コード
　MetaTrader5モジュールをインストールしていない場合は、以下のコマンドでインストールを済ませてください。
```
pip install MetaTrader5
```
　Python側でプログラムを実行しているタイミングで、同PC上にMT5が起動している必要があります。起動しているMT5の操作をPythonで行うためのものという認識で良さそうです。

基本的にはこのような形式でコード書きます。
```py
import MetaTrader5 as mt5

# MetaTrader5に接続
if not mt5.initialize():
    print("MetaTrader5 initialize failed")
    mt5.shutdown()

#################################
#  ここにやりたいこと書く       #
#################################

# MetaTrader5との接続を切断
mt5.shutdown()
```
MetaTrader5への接続と切断はセットで記述しましょう。

# MT5の情報を取得する
```py
import MetaTrader5 as mt5

# MetaTrader 5に接続
if not mt5.initialize():
    print("MetaTrader5 initialize failed")
    mt5.shutdown()

# MetaTrader 5バージョンについてのデータを表示する
print(f'MetaTrader5 version : {mt5.version()}')
 
terminal_info_dict = mt5.terminal_info()._asdict() # ターミナルの設定とステータスに関する情報を取得
account_info_dict = mt5.account_info()._asdict() # アカウント情報を取得

# MetaTrader 5との接続を切断
mt5.shutdown()

print(f'\nターミナルの設定とステータスに関する情報\n{terminal_info_dict}')
print(f'\nアカウント情報\n{account_info_dict}')
```
コードを実行するとMT5に接続しているアカウントなど個人情報が出力されます。

取得したデータがどのような形式か確認します。

```py
print(type(terminal_info_dict))
print(type(account_info_dict))
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1882169/33e56796-c99b-8adb-4bca-6238078475f3.png)
取得したデータは辞書(dict)型ですので以下のように個別で中身を確認できます。
```py
print(terminal_info_dict['company'])
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1882169/2cf2a62f-93fd-fba5-f477-91dc577fbd43.png)

# レート情報を取得する
まずは取得可能な銘柄コードを確認していきます。
```py
import MetaTrader5 as mt5

# MetaTrader 5に接続
if not mt5.initialize():
    print("MetaTrader5 initialize failed")
    mt5.shutdown()

# 銘柄コードのリストを取得と表示
symbols = mt5.symbols_get()

for symbol in symbols:
    print(symbol.name)

# MetaTrader 5との接続を切断
mt5.shutdown()
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1882169/02235ef2-4d67-817a-f816-afba3c2192d1.png)

## OHLCを取得する
では取得する銘柄コードを使ってレート情報を取得します。
```py
import MetaTrader5 as mt5
from datetime import datetime

# MetaTrader 5に接続
if not mt5.initialize():
    print("MetaTrader5 initialize failed")
    mt5.shutdown()

# 銘柄、時間軸、期間を指定
symbol = "USDJPY"
timeframe = mt5.TIMEFRAME_H1
start_date = datetime(2023, 1, 1)
end_date = datetime(2023, 3, 31)

# レート情報を取得
rates = mt5.copy_rates_range(symbol, timeframe, start_date, end_date)

# MetaTrader 5との接続を切断
mt5.shutdown()
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1882169/52ca1807-9d25-c028-b40c-34ccb7108b58.png)
レート情報の取得はできましたが、このままでは扱いにくいのでPandasデータフレームに変換していきます。
```py
# 取得したレート情報をPandasデータフレームに変換
rates_df = pd.DataFrame(rates, columns=["time", "open", "high", "low", "close", "tick_volume", "spread", "real_volume"])
rates_df['time'] = pd.to_datetime(rates_df['time'], unit='s')
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1882169/7710e670-38e9-a59e-efcb-f10594e219e1.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1882169/0c64c6b9-4bc9-2c80-8e92-46b3d3074a06.png)
タイムゾーンにずれが生じるかと思いますが、扱いやすい形式で取得できました。

## ティックを取得する
次はティック(AskとBid)を取得してみます。
```py
import MetaTrader5 as mt5
import pandas as pd
from datetime import datetime

# MetaTrader 5に接続
if not mt5.initialize():
    print("MetaTrader5 initialize failed")
    mt5.shutdown()

# 銘柄、期間を指定
symbol = "USDJPY"
start_date = datetime(2023, 5, 2, 12, 45)
end_date = datetime(2023, 5, 2, 13, 0)

# ティックデータを取得
ticks = mt5.copy_ticks_range(symbol, start_date, end_date, mt5.COPY_TICKS_ALL)

# 取得したティックデータをPandasデータフレームに変換
ticks_df = pd.DataFrame(ticks, columns=["time", "bid", "ask", "last", "volume", "flags"])
ticks_df['time'] = pd.to_datetime(ticks_df['time'], unit='s')

# MetaTrader 5との接続を切断
mt5.shutdown()
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1882169/a8d61b39-d8ef-5b4f-5019-c9cb01db3f66.png)
GW連休前の仲値前後のティックを取得してみました。そのままグラフ化してみましょう。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1882169/0b039862-d902-7011-28c6-02b7842b534d.png)

# 注文を出す
Python側から注文を出すことができます。MT5側で予め設定する必要があるので「ツール」→「オプション」→「エキスパートアドバイザー」からPython APIの無効を解除してください。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1882169/b201e431-1ab9-9f26-b929-fcee8dc4715d.png)

注文を出すコードです。
__※注意：実行すると実際にポジションを持ちます！！__
```py
import MetaTrader5 as mt5

# MetaTrader 5に接続
if not mt5.initialize():
    print("MetaTrader5 initialize failed")
    mt5.shutdown()

# 注文のシンボル、数量（ロット）、注文タイプを設定
symbol = "EURUSD"
lot = 0.07
order_type = mt5.ORDER_TYPE_SELL
price = mt5.symbol_info_tick(symbol).bid

# 成行注文用のトレードリクエストを作成
trade_request = {
    "action": mt5.TRADE_ACTION_DEAL,
    "symbol": symbol,
    "volume": lot,
    "type": order_type,
    "price": price,
    "deviation": 20,
    "magic": 234000,
    "comment": "Python MT5 sell order",
    "type_time": mt5.ORDER_TIME_GTC,
    "type_filling": mt5.ORDER_FILLING_FOK,
}

# 注文を送信
result = mt5.order_send(trade_request)

if result.retcode != mt5.TRADE_RETCODE_DONE:
    print("Order failed, retcode =", result.retcode)
else:
    print("Order successfully placed with ticket #", result.order)

# MetaTrader 5との接続を切断
mt5.shutdown()
```
__※注意：実行すると実際にポジションを持ちます！！__

trade_requestの中身はFX業者や銘柄の設定に合わせてください。仕様はMQL5のMqlTradeRequestと同じです。

「Order successfully placed with ticket # (チケット番号)」
となれば成功です。

では保有しているポジション情報を取得してみましょう
```py
import MetaTrader5 as mt5
import pandas as pd

# MT5に接続
if not mt5.initialize():
    print("initialize() failed, error code =", mt5.last_error())
    quit()

# ポジション情報を取得
positions = mt5.positions_get()

# ポジション情報をPandas DataFrameに変換
positions_dict = []
for position in positions:
    positions_dict.append({
        'ticket': position.ticket,
        'time': pd.to_datetime(position.time, unit='s'),
        'symbol': position.symbol,
        'type': position.type,
        'volume': position.volume,
        'price_open': position.price_open,
        'sl': position.sl,
        'tp': position.tp,
        'price_current': position.price_current,
        'profit': position.profit,
        'swap': position.swap,
        'comment': position.comment,
        'magic': position.magic
    })

positions_df = pd.DataFrame(positions_dict)

# MT5を終了
mt5.shutdown()

positions_df
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1882169/25633f83-dfba-54be-39d6-5ce4f105c7c1.png)

## 注意点
　PC上でMT5を複数起動している場合は意図しない口座に接続することがあります。事前にMT5のログインIDを確認しておいて、目的の口座かどうかを確認してから動作させるような制御が望ましいと思います。
```py
account_info_dict = mt5.account_info()._asdict() # アカウント情報を取得

if account_info_dict['login'] == 9999: # MT5ログインID
    print("目的の口座です。")
else:
    print("目的の口座ではありません。")
```

# おわり
　このようにAPIを使って様々な操作をPython上から行えます。その他の機能は公式サイトを確認してください。

https://www.mql5.com/ja/docs/python_metatrader5