mt5-bridge 1.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- mt5_bridge-1.1.0/.github/FUNDING.yml +2 -0
- mt5_bridge-1.1.0/.github/copilot-instructions.md +12 -0
- mt5_bridge-1.1.0/.gitignore +1 -0
- mt5_bridge-1.1.0/LICENSE +21 -0
- mt5_bridge-1.1.0/PKG-INFO +105 -0
- mt5_bridge-1.1.0/README.ja.md +96 -0
- mt5_bridge-1.1.0/README.md +89 -0
- mt5_bridge-1.1.0/mt5_bridge/__init__.py +0 -0
- mt5_bridge-1.1.0/mt5_bridge/client.py +46 -0
- mt5_bridge-1.1.0/mt5_bridge/main.py +265 -0
- mt5_bridge-1.1.0/mt5_bridge/mcp_server.py +124 -0
- mt5_bridge-1.1.0/mt5_bridge/mt5_handler.py +481 -0
- mt5_bridge-1.1.0/pyproject.toml +25 -0
- mt5_bridge-1.1.0/uv.lock +2037 -0
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
```instructions
|
|
2
|
+
# コードコメントに関する基本ルール
|
|
3
|
+
|
|
4
|
+
- すべてのコード変更では、処理の意図や背景が分かるように可能な限りきめ細かくコメントを追加してください。
|
|
5
|
+
- コメントは原則として英語と日本語を併記します(例: `# Fetch latest rates / 最新レートを取得`).
|
|
6
|
+
- 既存コードにコメントが不足している場合も、関連箇所に同様の英日併記コメントを追記してください。
|
|
7
|
+
|
|
8
|
+
# 応答言語に関する基本ルール
|
|
9
|
+
|
|
10
|
+
- すべての応答は日本語で記述してください。ユーザーが明示的に他言語での回答を求めた場合のみ、その指示に従います。
|
|
11
|
+
|
|
12
|
+
```
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__pycache__/
|
mt5_bridge-1.1.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Akiva Miura
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: mt5-bridge
|
|
3
|
+
Version: 1.1.0
|
|
4
|
+
Summary: MT5 Bridge API - REST API and CLI for MetaTrader5
|
|
5
|
+
Author-email: Akiva Miura <akiva.miura@gmail.com>
|
|
6
|
+
License-File: LICENSE
|
|
7
|
+
Requires-Python: >=3.11
|
|
8
|
+
Requires-Dist: fastapi>=0.110.0
|
|
9
|
+
Requires-Dist: fastmcp>=0.4.1
|
|
10
|
+
Requires-Dist: httpx>=0.27.0
|
|
11
|
+
Requires-Dist: metatrader5>=5.0.0; sys_platform == 'win32'
|
|
12
|
+
Requires-Dist: pandas>=2.0.0
|
|
13
|
+
Requires-Dist: pydantic>=2.6.0
|
|
14
|
+
Requires-Dist: uvicorn[standard]>=0.29.0
|
|
15
|
+
Description-Content-Type: text/markdown
|
|
16
|
+
|
|
17
|
+
# MT5 Bridge API
|
|
18
|
+
|
|
19
|
+
## Overview
|
|
20
|
+
`mt5-bridge` is a FastAPI service and CLI tool that mediates market data access and order execution between a MetaTrader 5 terminal and external applications.
|
|
21
|
+
|
|
22
|
+
- **Server**: Runs on Windows (where MT5 is installed) and exposes a REST API.
|
|
23
|
+
- **Client**: Runs on any platform (Windows, Linux, macOS) and communicates with the Server to fetch data or execute trades.
|
|
24
|
+
|
|
25
|
+
## Prerequisites
|
|
26
|
+
- Python 3.11+
|
|
27
|
+
- **Server Mode**: Windows environment with MetaTrader 5 terminal installed.
|
|
28
|
+
- **Client Mode**: Any OS.
|
|
29
|
+
|
|
30
|
+
## Installation (uv)
|
|
31
|
+
|
|
32
|
+
This project uses [uv](https://github.com/astral-sh/uv) for package management.
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
# Install dependencies
|
|
36
|
+
uv sync
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
On Linux/macOS, the `MetaTrader5` package will be skipped automatically, allowing you to use the client functionality without issues.
|
|
40
|
+
|
|
41
|
+
## Usage
|
|
42
|
+
|
|
43
|
+
The package installs a CLI command `mt5-bridge`.
|
|
44
|
+
|
|
45
|
+
### 1. Start the Server (Windows Only)
|
|
46
|
+
|
|
47
|
+
On your Windows machine with MT5:
|
|
48
|
+
|
|
49
|
+
```powershell
|
|
50
|
+
# Default (localhost:8000)
|
|
51
|
+
uv run mt5-bridge server
|
|
52
|
+
|
|
53
|
+
# Custom host/port
|
|
54
|
+
uv run mt5-bridge server --host 0.0.0.0 --port 8000
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
> **Note**: If you are using WSL, please checkout this repository on the **Windows file system** (e.g., `C:\Work\mt5-bridge`) and run the command from PowerShell/Command Prompt. Running Windows Python directly against a directory inside WSL (UNC path like `\\wsl.localhost\Ubuntu\...`) often causes `DLL load failed` errors with libraries like NumPy.
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
Additional options:
|
|
61
|
+
- `--mt5-path "C:\Path\To\terminal64.exe"`: proper initialization
|
|
62
|
+
- `--no-utc`: Disable Server Time -> UTC conversion
|
|
63
|
+
|
|
64
|
+
### 2. Use the Client (Any Platform)
|
|
65
|
+
|
|
66
|
+
From another machine (or the same one), use the client command to interact with the server.
|
|
67
|
+
|
|
68
|
+
```bash
|
|
69
|
+
# Check connection health
|
|
70
|
+
uv run mt5-bridge client --url http://192.168.1.10:8000 health
|
|
71
|
+
|
|
72
|
+
# Get historical rates (M1, last 1000 bars) for XAUUSD
|
|
73
|
+
uv run mt5-bridge client --url http://192.168.1.10:8000 rates XAUUSD
|
|
74
|
+
|
|
75
|
+
# Get latest tick
|
|
76
|
+
uv run mt5-bridge client --url http://192.168.1.10:8000 tick XAUUSD
|
|
77
|
+
|
|
78
|
+
# List open positions
|
|
79
|
+
uv run mt5-bridge client --url http://192.168.1.10:8000 positions
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
### JSON API
|
|
83
|
+
|
|
84
|
+
You can also access the API directly via generic HTTP clients (curl, Postman, specific libraries).
|
|
85
|
+
|
|
86
|
+
- `GET /health`
|
|
87
|
+
- `GET /rates/{symbol}?timeframe=M1&count=1000`
|
|
88
|
+
- `GET /tick/{symbol}`
|
|
89
|
+
- `GET /positions`
|
|
90
|
+
- `POST /order`
|
|
91
|
+
- `POST /close`
|
|
92
|
+
- `POST /modify`
|
|
93
|
+
|
|
94
|
+
## Architecture
|
|
95
|
+
- `mt5_bridge/main.py`: CLI entry point and FastAPI server definition.
|
|
96
|
+
- `mt5_bridge/mt5_handler.py`: Wrapper for MetaTrader5 package (guarded imports).
|
|
97
|
+
- `mt5_bridge/client.py`: HTTP client implementation.
|
|
98
|
+
|
|
99
|
+
## MCP (Copilot CLI) Integration
|
|
100
|
+
- Purpose: expose the MT5 Bridge API to Copilot CLI (MCP).
|
|
101
|
+
- Run MCP server:
|
|
102
|
+
- `python mt5_bridge/mcp_server.py --api-base http://localhost:8000`
|
|
103
|
+
|
|
104
|
+
## License
|
|
105
|
+
MIT License.
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
# MT5 Bridge API
|
|
2
|
+
|
|
3
|
+
## 概要
|
|
4
|
+
`mt5-bridge`はMetaTrader 5ターミナルと外部アプリケーションの間で市場データの取得や注文実行を仲介するためのFastAPIサービス兼CLIツールです。
|
|
5
|
+
|
|
6
|
+
- **サーバー機能**: MetaTrader 5がインストールされたWindows環境で動作し、REST API機能を提供します。
|
|
7
|
+
- **クライアント機能**: Windows、Linux、macOSなど任意のOSで動作し、サーバーと通信してデータの取得や注文を行います。
|
|
8
|
+
|
|
9
|
+
## 前提条件
|
|
10
|
+
- Python 3.11以上
|
|
11
|
+
- **サーバーモード**: MetaTrader 5ターミナルがインストールされたWindows環境。
|
|
12
|
+
- **クライアントモード**: 任意のOS環境。
|
|
13
|
+
|
|
14
|
+
## インストール (uv)
|
|
15
|
+
|
|
16
|
+
本プロジェクトはパッケージ管理に [uv](https://github.com/astral-sh/uv) を使用しています。
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
# 依存関係のインストール
|
|
20
|
+
uv sync
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
LinuxやmacOS環境では、Windows専用の `MetaTrader5` パッケージは自動的にスキップされ、クライアント機能のみが利用可能な状態でインストールされます。
|
|
24
|
+
|
|
25
|
+
## 使い方
|
|
26
|
+
|
|
27
|
+
インストールすると `mt5-bridge` コマンドが利用可能になります。
|
|
28
|
+
|
|
29
|
+
### 1. サーバーの起動 (Windowsのみ)
|
|
30
|
+
|
|
31
|
+
MT5がインストールされているWindowsマシンで実行します:
|
|
32
|
+
|
|
33
|
+
```powershell
|
|
34
|
+
# デフォルト設定 (localhost:8000) で起動
|
|
35
|
+
uv run mt5-bridge server
|
|
36
|
+
|
|
37
|
+
# ホストやポートを指定
|
|
38
|
+
uv run mt5-bridge server --host 0.0.0.0 --port 8000
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
> **注意**: WSL上のディレクトリ(`\\wsl.localhost\...`)にあるソースコードをWindows側のPythonで直接実行すると、NumPyなどのライブラリ読み込み時(DLLロード)にエラーが発生する場合があります。サーバー機能を利用する際は、必ず**Windows側のローカルフォルダ**(例: `C:\Work\mt5-bridge`)にリポジトリを配置して実行してください。
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
主なオプション:
|
|
45
|
+
- `--mt5-path "C:\Path\To\terminal64.exe"`: MT5のパスを明示的に指定して初期化
|
|
46
|
+
- `--no-utc`: サーバー時間をUTCに変換せず、そのまま返す(デフォルトはUTC変換有効)
|
|
47
|
+
|
|
48
|
+
### 2. クライアントの利用 (任意のプラットフォーム)
|
|
49
|
+
|
|
50
|
+
別のマシン(または同一マシン)からクライアントコマンドでサーバーを操作できます。
|
|
51
|
+
|
|
52
|
+
```bash
|
|
53
|
+
# サーバーのヘルスチェック
|
|
54
|
+
uv run mt5-bridge client --url http://192.168.1.10:8000 health
|
|
55
|
+
|
|
56
|
+
# 過去レートの取得 (M1, 最新1000本) - シンボル: XAUUSD
|
|
57
|
+
uv run mt5-bridge client --url http://192.168.1.10:8000 rates XAUUSD
|
|
58
|
+
|
|
59
|
+
# 最新ティックの取得
|
|
60
|
+
uv run mt5-bridge client --url http://192.168.1.10:8000 tick XAUUSD
|
|
61
|
+
|
|
62
|
+
# 保有ポジションの一覧表示
|
|
63
|
+
uv run mt5-bridge client --url http://192.168.1.10:8000 positions
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
### JSON API
|
|
67
|
+
|
|
68
|
+
汎用的なHTTPクライアントやライブラリから直接APIを利用することも可能です。
|
|
69
|
+
|
|
70
|
+
- `GET /health`
|
|
71
|
+
- `GET /rates/{symbol}?timeframe=M1&count=1000`
|
|
72
|
+
- `GET /tick/{symbol}`
|
|
73
|
+
- `GET /positions`
|
|
74
|
+
- `POST /order`
|
|
75
|
+
- `POST /close`
|
|
76
|
+
- `POST /modify`
|
|
77
|
+
|
|
78
|
+
## 構成
|
|
79
|
+
- `mt5_bridge/main.py`: CLIエントリポイントおよびFastAPIサーバー定義。
|
|
80
|
+
- `mt5_bridge/mt5_handler.py`: MetaTrader5パッケージのラッパー(条件付きインポートにより非Windows環境でもエラーになりません)。
|
|
81
|
+
- `mt5_bridge/client.py`: HTTPクライアントの実装。
|
|
82
|
+
|
|
83
|
+
## MCP (Copilot CLI) 連携
|
|
84
|
+
- 目的: MT5 Bridge APIをCopilot CLI (MCP) として公開します。
|
|
85
|
+
- 実行方法:
|
|
86
|
+
- `python mt5_bridge/mcp_server.py --api-base http://localhost:8000`
|
|
87
|
+
|
|
88
|
+
## サポート・寄付
|
|
89
|
+
- <a href="https://github.com/sponsors/akivajp" style="vertical-align: middle;"><img src="https://github.githubassets.com/assets/GitHub-Mark-ea2971cee799.png" alt="GitHub Sponsors" height="32" /></a> GitHub Sponsors: [https://github.com/sponsors/akivajp](https://github.com/sponsors/akivajp)
|
|
90
|
+
- <a href="https://buymeacoffee.com/akivajp" style="vertical-align: middle;"><img src="https://github.githubassets.com/assets/buy_me_a_coffee-63ed78263f6e.svg" alt="Buy Me a Coffee" height="32" /></a> Buy Me a Coffee: [https://buymeacoffee.com/akivajp](https://buymeacoffee.com/akivajp)
|
|
91
|
+
|
|
92
|
+
上記以外の支援方法を希望される場合は Issue や Discussions でご相談ください。
|
|
93
|
+
少額の寄付・サポートはいつでも大歓迎です。お気持ちだけでも大きな励みになります。
|
|
94
|
+
|
|
95
|
+
## ライセンス
|
|
96
|
+
本プロジェクトは MIT License の下で提供されます。詳細は `LICENSE` を参照してください。
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
# MT5 Bridge API
|
|
2
|
+
|
|
3
|
+
## Overview
|
|
4
|
+
`mt5-bridge` is a FastAPI service and CLI tool that mediates market data access and order execution between a MetaTrader 5 terminal and external applications.
|
|
5
|
+
|
|
6
|
+
- **Server**: Runs on Windows (where MT5 is installed) and exposes a REST API.
|
|
7
|
+
- **Client**: Runs on any platform (Windows, Linux, macOS) and communicates with the Server to fetch data or execute trades.
|
|
8
|
+
|
|
9
|
+
## Prerequisites
|
|
10
|
+
- Python 3.11+
|
|
11
|
+
- **Server Mode**: Windows environment with MetaTrader 5 terminal installed.
|
|
12
|
+
- **Client Mode**: Any OS.
|
|
13
|
+
|
|
14
|
+
## Installation (uv)
|
|
15
|
+
|
|
16
|
+
This project uses [uv](https://github.com/astral-sh/uv) for package management.
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
# Install dependencies
|
|
20
|
+
uv sync
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
On Linux/macOS, the `MetaTrader5` package will be skipped automatically, allowing you to use the client functionality without issues.
|
|
24
|
+
|
|
25
|
+
## Usage
|
|
26
|
+
|
|
27
|
+
The package installs a CLI command `mt5-bridge`.
|
|
28
|
+
|
|
29
|
+
### 1. Start the Server (Windows Only)
|
|
30
|
+
|
|
31
|
+
On your Windows machine with MT5:
|
|
32
|
+
|
|
33
|
+
```powershell
|
|
34
|
+
# Default (localhost:8000)
|
|
35
|
+
uv run mt5-bridge server
|
|
36
|
+
|
|
37
|
+
# Custom host/port
|
|
38
|
+
uv run mt5-bridge server --host 0.0.0.0 --port 8000
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
> **Note**: If you are using WSL, please checkout this repository on the **Windows file system** (e.g., `C:\Work\mt5-bridge`) and run the command from PowerShell/Command Prompt. Running Windows Python directly against a directory inside WSL (UNC path like `\\wsl.localhost\Ubuntu\...`) often causes `DLL load failed` errors with libraries like NumPy.
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
Additional options:
|
|
45
|
+
- `--mt5-path "C:\Path\To\terminal64.exe"`: proper initialization
|
|
46
|
+
- `--no-utc`: Disable Server Time -> UTC conversion
|
|
47
|
+
|
|
48
|
+
### 2. Use the Client (Any Platform)
|
|
49
|
+
|
|
50
|
+
From another machine (or the same one), use the client command to interact with the server.
|
|
51
|
+
|
|
52
|
+
```bash
|
|
53
|
+
# Check connection health
|
|
54
|
+
uv run mt5-bridge client --url http://192.168.1.10:8000 health
|
|
55
|
+
|
|
56
|
+
# Get historical rates (M1, last 1000 bars) for XAUUSD
|
|
57
|
+
uv run mt5-bridge client --url http://192.168.1.10:8000 rates XAUUSD
|
|
58
|
+
|
|
59
|
+
# Get latest tick
|
|
60
|
+
uv run mt5-bridge client --url http://192.168.1.10:8000 tick XAUUSD
|
|
61
|
+
|
|
62
|
+
# List open positions
|
|
63
|
+
uv run mt5-bridge client --url http://192.168.1.10:8000 positions
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
### JSON API
|
|
67
|
+
|
|
68
|
+
You can also access the API directly via generic HTTP clients (curl, Postman, specific libraries).
|
|
69
|
+
|
|
70
|
+
- `GET /health`
|
|
71
|
+
- `GET /rates/{symbol}?timeframe=M1&count=1000`
|
|
72
|
+
- `GET /tick/{symbol}`
|
|
73
|
+
- `GET /positions`
|
|
74
|
+
- `POST /order`
|
|
75
|
+
- `POST /close`
|
|
76
|
+
- `POST /modify`
|
|
77
|
+
|
|
78
|
+
## Architecture
|
|
79
|
+
- `mt5_bridge/main.py`: CLI entry point and FastAPI server definition.
|
|
80
|
+
- `mt5_bridge/mt5_handler.py`: Wrapper for MetaTrader5 package (guarded imports).
|
|
81
|
+
- `mt5_bridge/client.py`: HTTP client implementation.
|
|
82
|
+
|
|
83
|
+
## MCP (Copilot CLI) Integration
|
|
84
|
+
- Purpose: expose the MT5 Bridge API to Copilot CLI (MCP).
|
|
85
|
+
- Run MCP server:
|
|
86
|
+
- `python mt5_bridge/mcp_server.py --api-base http://localhost:8000`
|
|
87
|
+
|
|
88
|
+
## License
|
|
89
|
+
MIT License.
|
|
File without changes
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import httpx
|
|
2
|
+
from typing import List, Dict, Optional, Any
|
|
3
|
+
|
|
4
|
+
class BridgeClient:
|
|
5
|
+
def __init__(self, base_url: str = "http://localhost:8000"):
|
|
6
|
+
self.base_url = base_url.rstrip("/")
|
|
7
|
+
|
|
8
|
+
def get_rates(self, symbol: str, timeframe: str = "M1", count: int = 1000) -> List[Dict[str, Any]]:
|
|
9
|
+
url = f"{self.base_url}/rates/{symbol}"
|
|
10
|
+
params = {"timeframe": timeframe, "count": count}
|
|
11
|
+
try:
|
|
12
|
+
resp = httpx.get(url, params=params, timeout=10.0)
|
|
13
|
+
resp.raise_for_status()
|
|
14
|
+
return resp.json()
|
|
15
|
+
except httpx.HTTPError as e:
|
|
16
|
+
print(f"Error fetching rates: {e}")
|
|
17
|
+
return []
|
|
18
|
+
|
|
19
|
+
def get_tick(self, symbol: str) -> Optional[Dict[str, Any]]:
|
|
20
|
+
url = f"{self.base_url}/tick/{symbol}"
|
|
21
|
+
try:
|
|
22
|
+
resp = httpx.get(url, timeout=5.0)
|
|
23
|
+
resp.raise_for_status()
|
|
24
|
+
return resp.json()
|
|
25
|
+
except httpx.HTTPError as e:
|
|
26
|
+
print(f"Error fetching tick: {e}")
|
|
27
|
+
return None
|
|
28
|
+
|
|
29
|
+
def get_positions(self) -> List[Dict[str, Any]]:
|
|
30
|
+
url = f"{self.base_url}/positions"
|
|
31
|
+
try:
|
|
32
|
+
resp = httpx.get(url, timeout=5.0)
|
|
33
|
+
resp.raise_for_status()
|
|
34
|
+
return resp.json()
|
|
35
|
+
except httpx.HTTPError as e:
|
|
36
|
+
print(f"Error fetching positions: {e}")
|
|
37
|
+
return []
|
|
38
|
+
|
|
39
|
+
def check_health(self) -> Dict[str, Any]:
|
|
40
|
+
url = f"{self.base_url}/health"
|
|
41
|
+
try:
|
|
42
|
+
resp = httpx.get(url, timeout=5.0)
|
|
43
|
+
resp.raise_for_status()
|
|
44
|
+
return resp.json()
|
|
45
|
+
except httpx.HTTPError as e:
|
|
46
|
+
return {"status": "error", "detail": str(e)}
|
|
@@ -0,0 +1,265 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
|
|
3
|
+
from fastapi import FastAPI, HTTPException, Query
|
|
4
|
+
from pydantic import BaseModel
|
|
5
|
+
from typing import List, Optional
|
|
6
|
+
import uvicorn
|
|
7
|
+
import asyncio
|
|
8
|
+
import argparse
|
|
9
|
+
import os
|
|
10
|
+
import sys
|
|
11
|
+
import json
|
|
12
|
+
from importlib.metadata import version, PackageNotFoundError
|
|
13
|
+
|
|
14
|
+
# Try relative imports (package mode), fallback to path manipulation (script mode)
|
|
15
|
+
try:
|
|
16
|
+
from .mt5_handler import MT5Handler
|
|
17
|
+
from .client import BridgeClient
|
|
18
|
+
except ImportError:
|
|
19
|
+
sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
|
20
|
+
from mt5_bridge.mt5_handler import MT5Handler
|
|
21
|
+
from mt5_bridge.client import BridgeClient
|
|
22
|
+
|
|
23
|
+
app = FastAPI(title="MT5 Bridge API")
|
|
24
|
+
mt5_handler = MT5Handler()
|
|
25
|
+
|
|
26
|
+
class Rate(BaseModel):
|
|
27
|
+
time: int
|
|
28
|
+
open: float
|
|
29
|
+
high: float
|
|
30
|
+
low: float
|
|
31
|
+
close: float
|
|
32
|
+
tick_volume: int
|
|
33
|
+
spread: int
|
|
34
|
+
real_volume: int
|
|
35
|
+
|
|
36
|
+
class Tick(BaseModel):
|
|
37
|
+
time: int
|
|
38
|
+
bid: float
|
|
39
|
+
ask: float
|
|
40
|
+
last: float
|
|
41
|
+
volume: int
|
|
42
|
+
|
|
43
|
+
class Position(BaseModel):
|
|
44
|
+
ticket: int
|
|
45
|
+
symbol: str
|
|
46
|
+
type: str
|
|
47
|
+
volume: float
|
|
48
|
+
price_open: float
|
|
49
|
+
comment: str
|
|
50
|
+
magic: int
|
|
51
|
+
sl: float
|
|
52
|
+
tp: float
|
|
53
|
+
price_current: float
|
|
54
|
+
profit: float
|
|
55
|
+
time: int
|
|
56
|
+
|
|
57
|
+
async def monitor_connection():
|
|
58
|
+
"""Periodically check MT5 connection and reconnect if needed."""
|
|
59
|
+
while True:
|
|
60
|
+
try:
|
|
61
|
+
if not mt5_handler.check_connection():
|
|
62
|
+
print("WARNING: MT5 connection lost. Reconnecting...")
|
|
63
|
+
await asyncio.sleep(5) # Check every 5 seconds
|
|
64
|
+
except Exception as e:
|
|
65
|
+
print(f"Error in connection monitor: {e}")
|
|
66
|
+
await asyncio.sleep(5)
|
|
67
|
+
|
|
68
|
+
@app.on_event("startup")
|
|
69
|
+
async def startup_event():
|
|
70
|
+
"""Initialize MT5 connection on startup."""
|
|
71
|
+
# Only try to initialize if we are on Windows (checked in main types, but safe here too)
|
|
72
|
+
if sys.platform == "win32":
|
|
73
|
+
if not mt5_handler.initialize():
|
|
74
|
+
print("WARNING: Failed to initialize MT5 on startup. Will retry in background.")
|
|
75
|
+
|
|
76
|
+
# Start connection monitor
|
|
77
|
+
asyncio.create_task(monitor_connection())
|
|
78
|
+
else:
|
|
79
|
+
print("Non-Windows platform detected: MT5 connection disabled.")
|
|
80
|
+
|
|
81
|
+
@app.on_event("shutdown")
|
|
82
|
+
async def shutdown_event():
|
|
83
|
+
"""Shutdown MT5 connection."""
|
|
84
|
+
mt5_handler.shutdown()
|
|
85
|
+
|
|
86
|
+
@app.get("/health")
|
|
87
|
+
def health_check():
|
|
88
|
+
return {"status": "ok", "mt5_connected": mt5_handler.connected}
|
|
89
|
+
|
|
90
|
+
@app.get("/rates/{symbol}", response_model=List[Rate])
|
|
91
|
+
def get_rates(
|
|
92
|
+
symbol: str,
|
|
93
|
+
timeframe: str = Query(..., description="Timeframe (e.g., M1, H1)"),
|
|
94
|
+
count: int = Query(1000, description="Number of bars")
|
|
95
|
+
):
|
|
96
|
+
rates = mt5_handler.get_rates(symbol, timeframe, count)
|
|
97
|
+
if rates is None:
|
|
98
|
+
raise HTTPException(status_code=500, detail=f"Failed to get rates for {symbol}")
|
|
99
|
+
return rates
|
|
100
|
+
|
|
101
|
+
@app.get("/tick/{symbol}", response_model=Tick)
|
|
102
|
+
def get_tick(symbol: str):
|
|
103
|
+
tick = mt5_handler.get_tick(symbol)
|
|
104
|
+
if tick is None:
|
|
105
|
+
raise HTTPException(status_code=500, detail=f"Failed to get tick for {symbol}")
|
|
106
|
+
return tick
|
|
107
|
+
|
|
108
|
+
@app.get("/positions", response_model=List[Position])
|
|
109
|
+
def get_positions():
|
|
110
|
+
positions = mt5_handler.get_positions()
|
|
111
|
+
if positions is None:
|
|
112
|
+
raise HTTPException(status_code=500, detail="Failed to get positions")
|
|
113
|
+
return positions
|
|
114
|
+
|
|
115
|
+
class OrderRequest(BaseModel):
|
|
116
|
+
symbol: str
|
|
117
|
+
type: str # "BUY" or "SELL"
|
|
118
|
+
volume: float
|
|
119
|
+
sl: float = 0.0
|
|
120
|
+
tp: float = 0.0
|
|
121
|
+
comment: str = ""
|
|
122
|
+
magic: int = 123456
|
|
123
|
+
|
|
124
|
+
class CloseRequest(BaseModel):
|
|
125
|
+
ticket: int
|
|
126
|
+
|
|
127
|
+
class ModifyRequest(BaseModel):
|
|
128
|
+
ticket: int
|
|
129
|
+
sl: Optional[float] = None
|
|
130
|
+
tp: Optional[float] = None
|
|
131
|
+
update_sl: bool = False
|
|
132
|
+
update_tp: bool = False
|
|
133
|
+
|
|
134
|
+
@app.post("/order")
|
|
135
|
+
def send_order(order: OrderRequest):
|
|
136
|
+
ticket, error = mt5_handler.send_order(
|
|
137
|
+
order.symbol,
|
|
138
|
+
order.type,
|
|
139
|
+
order.volume,
|
|
140
|
+
order.sl,
|
|
141
|
+
order.tp,
|
|
142
|
+
order.comment,
|
|
143
|
+
magic=order.magic
|
|
144
|
+
)
|
|
145
|
+
if ticket is None:
|
|
146
|
+
detail = error or "Failed to send order"
|
|
147
|
+
raise HTTPException(status_code=500, detail=detail)
|
|
148
|
+
return {"status": "ok", "ticket": ticket}
|
|
149
|
+
|
|
150
|
+
@app.post("/close")
|
|
151
|
+
def close_position(req: CloseRequest):
|
|
152
|
+
success, message = mt5_handler.close_position(req.ticket)
|
|
153
|
+
if not success:
|
|
154
|
+
raise HTTPException(status_code=500, detail=f"Failed to close position: {message}")
|
|
155
|
+
return {"status": "ok"}
|
|
156
|
+
|
|
157
|
+
@app.post("/modify")
|
|
158
|
+
def modify_position(req: ModifyRequest):
|
|
159
|
+
success, message = mt5_handler.modify_position(
|
|
160
|
+
req.ticket,
|
|
161
|
+
req.sl,
|
|
162
|
+
req.tp,
|
|
163
|
+
req.update_sl,
|
|
164
|
+
req.update_tp,
|
|
165
|
+
)
|
|
166
|
+
if not success:
|
|
167
|
+
raise HTTPException(status_code=500, detail=f"Failed to modify position: {message}")
|
|
168
|
+
return {"status": "ok"}
|
|
169
|
+
|
|
170
|
+
def main():
|
|
171
|
+
parser = argparse.ArgumentParser(description="MT5 Bridge CLI")
|
|
172
|
+
|
|
173
|
+
try:
|
|
174
|
+
app_version = version("mt5-bridge")
|
|
175
|
+
except PackageNotFoundError:
|
|
176
|
+
app_version = "unknown"
|
|
177
|
+
|
|
178
|
+
parser.add_argument(
|
|
179
|
+
"--version",
|
|
180
|
+
action="version",
|
|
181
|
+
version=f"mt5-bridge version: {app_version}\nPython version: {sys.version}"
|
|
182
|
+
)
|
|
183
|
+
|
|
184
|
+
subparsers = parser.add_subparsers(dest="command", help="Command to run")
|
|
185
|
+
|
|
186
|
+
# Serve command
|
|
187
|
+
server_parser = subparsers.add_parser("server", help="Run MT5 Bridge Server (Windows Only)")
|
|
188
|
+
server_parser.add_argument(
|
|
189
|
+
"--host",
|
|
190
|
+
default="0.0.0.0",
|
|
191
|
+
help="Host interface to bind (default: 0.0.0.0)",
|
|
192
|
+
)
|
|
193
|
+
server_parser.add_argument(
|
|
194
|
+
"--port",
|
|
195
|
+
type=int,
|
|
196
|
+
default=8000,
|
|
197
|
+
help="Port to listen on (default: 8000)",
|
|
198
|
+
)
|
|
199
|
+
server_parser.add_argument("--mt5-path", default=None, help="Path to MT5 executable")
|
|
200
|
+
server_parser.add_argument("--mt5-login", type=int, default=None, help="MT5 Login ID")
|
|
201
|
+
server_parser.add_argument("--mt5-password", default=None, help="MT5 Password")
|
|
202
|
+
server_parser.add_argument("--mt5-server", default=None, help="MT5 Server Name")
|
|
203
|
+
server_parser.add_argument("--no-utc", action="store_true", help="Disable UTC conversion")
|
|
204
|
+
|
|
205
|
+
# Client command
|
|
206
|
+
client_parser = subparsers.add_parser("client", help="Run MT5 Bridge Client")
|
|
207
|
+
client_parser.add_argument("--url", default="http://localhost:8000", help="Server URL")
|
|
208
|
+
|
|
209
|
+
client_subs = client_parser.add_subparsers(dest="client_command", help="Client command", required=True)
|
|
210
|
+
|
|
211
|
+
# Client Subcommands
|
|
212
|
+
client_subs.add_parser("health", help="Check server health")
|
|
213
|
+
|
|
214
|
+
rates_p = client_subs.add_parser("rates", help="Get historical rates")
|
|
215
|
+
rates_p.add_argument("symbol", type=str)
|
|
216
|
+
rates_p.add_argument("--timeframe", default="M1")
|
|
217
|
+
rates_p.add_argument("--count", type=int, default=1000)
|
|
218
|
+
|
|
219
|
+
tick_p = client_subs.add_parser("tick", help="Get latest tick")
|
|
220
|
+
tick_p.add_argument("symbol", type=str)
|
|
221
|
+
|
|
222
|
+
client_subs.add_parser("positions", help="Get open positions")
|
|
223
|
+
|
|
224
|
+
args = parser.parse_args()
|
|
225
|
+
|
|
226
|
+
if args.command == "server":
|
|
227
|
+
if sys.platform != "win32":
|
|
228
|
+
print("Error: Server functionality is only supported on Windows.")
|
|
229
|
+
sys.exit(1)
|
|
230
|
+
|
|
231
|
+
# Configure MT5 handler with CLI args
|
|
232
|
+
if args.mt5_path:
|
|
233
|
+
mt5_handler.program_path = args.mt5_path
|
|
234
|
+
if args.mt5_login:
|
|
235
|
+
mt5_handler.login = args.mt5_login
|
|
236
|
+
if args.mt5_password:
|
|
237
|
+
mt5_handler.password = args.mt5_password
|
|
238
|
+
if args.mt5_server:
|
|
239
|
+
mt5_handler.server = args.mt5_server
|
|
240
|
+
|
|
241
|
+
# Configure UTC conversion
|
|
242
|
+
mt5_handler.use_utc = not args.no_utc
|
|
243
|
+
if mt5_handler.use_utc:
|
|
244
|
+
print("UTC conversion enabled (Server Time -> UTC)")
|
|
245
|
+
else:
|
|
246
|
+
print("UTC conversion disabled (Raw Server Time)")
|
|
247
|
+
|
|
248
|
+
# Run Server
|
|
249
|
+
uvicorn.run(app, host=args.host, port=args.port)
|
|
250
|
+
|
|
251
|
+
elif args.command == "client":
|
|
252
|
+
client = BridgeClient(base_url=args.url)
|
|
253
|
+
if args.client_command == "health":
|
|
254
|
+
print(json.dumps(client.check_health(), indent=2))
|
|
255
|
+
elif args.client_command == "rates":
|
|
256
|
+
print(json.dumps(client.get_rates(args.symbol, args.timeframe, args.count), indent=2))
|
|
257
|
+
elif args.client_command == "tick":
|
|
258
|
+
print(json.dumps(client.get_tick(args.symbol), indent=2))
|
|
259
|
+
elif args.client_command == "positions":
|
|
260
|
+
print(json.dumps(client.get_positions(), indent=2))
|
|
261
|
+
else:
|
|
262
|
+
parser.print_help()
|
|
263
|
+
|
|
264
|
+
if __name__ == "__main__":
|
|
265
|
+
main()
|