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.
@@ -0,0 +1,2 @@
1
+ github: akivajp
2
+ buy_me_a_coffee: akivajp
@@ -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__/
@@ -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()