open-api-mt5 0.7.2__tar.gz → 0.7.3__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.
Files changed (38) hide show
  1. {open_api_mt5-0.7.2 → open_api_mt5-0.7.3}/PKG-INFO +1 -1
  2. open_api_mt5-0.7.3/app/services/connection_service.py +72 -0
  3. {open_api_mt5-0.7.2 → open_api_mt5-0.7.3}/open_api_mt5.egg-info/PKG-INFO +1 -1
  4. {open_api_mt5-0.7.2 → open_api_mt5-0.7.3}/pyproject.toml +1 -1
  5. open_api_mt5-0.7.3/tests/test_connection_service.py +100 -0
  6. open_api_mt5-0.7.2/app/services/connection_service.py +0 -41
  7. open_api_mt5-0.7.2/tests/test_connection_service.py +0 -50
  8. {open_api_mt5-0.7.2 → open_api_mt5-0.7.3}/README.md +0 -0
  9. {open_api_mt5-0.7.2 → open_api_mt5-0.7.3}/app/__init__.py +0 -0
  10. {open_api_mt5-0.7.2 → open_api_mt5-0.7.3}/app/cli.py +0 -0
  11. {open_api_mt5-0.7.2 → open_api_mt5-0.7.3}/app/main.py +0 -0
  12. {open_api_mt5-0.7.2 → open_api_mt5-0.7.3}/app/services/__init__.py +0 -0
  13. {open_api_mt5-0.7.2 → open_api_mt5-0.7.3}/app/services/account_service.py +0 -0
  14. {open_api_mt5-0.7.2 → open_api_mt5-0.7.3}/app/services/bar_service.py +0 -0
  15. {open_api_mt5-0.7.2 → open_api_mt5-0.7.3}/app/services/calendar_service.py +0 -0
  16. {open_api_mt5-0.7.2 → open_api_mt5-0.7.3}/app/services/config_service.py +0 -0
  17. {open_api_mt5-0.7.2 → open_api_mt5-0.7.3}/app/services/exposure_service.py +0 -0
  18. {open_api_mt5-0.7.2 → open_api_mt5-0.7.3}/app/services/market_data_service.py +0 -0
  19. {open_api_mt5-0.7.2 → open_api_mt5-0.7.3}/app/services/news_close_store.py +0 -0
  20. {open_api_mt5-0.7.2 → open_api_mt5-0.7.3}/app/services/registry_service.py +0 -0
  21. {open_api_mt5-0.7.2 → open_api_mt5-0.7.3}/app/services/serialization.py +0 -0
  22. {open_api_mt5-0.7.2 → open_api_mt5-0.7.3}/app/services/stream_service.py +0 -0
  23. {open_api_mt5-0.7.2 → open_api_mt5-0.7.3}/app/services/symbol_service.py +0 -0
  24. {open_api_mt5-0.7.2 → open_api_mt5-0.7.3}/app/services/ticket_service.py +0 -0
  25. {open_api_mt5-0.7.2 → open_api_mt5-0.7.3}/app/services/trade_service.py +0 -0
  26. {open_api_mt5-0.7.2 → open_api_mt5-0.7.3}/open_api_mt5.egg-info/SOURCES.txt +0 -0
  27. {open_api_mt5-0.7.2 → open_api_mt5-0.7.3}/open_api_mt5.egg-info/dependency_links.txt +0 -0
  28. {open_api_mt5-0.7.2 → open_api_mt5-0.7.3}/open_api_mt5.egg-info/entry_points.txt +0 -0
  29. {open_api_mt5-0.7.2 → open_api_mt5-0.7.3}/open_api_mt5.egg-info/requires.txt +0 -0
  30. {open_api_mt5-0.7.2 → open_api_mt5-0.7.3}/open_api_mt5.egg-info/top_level.txt +0 -0
  31. {open_api_mt5-0.7.2 → open_api_mt5-0.7.3}/setup.cfg +0 -0
  32. {open_api_mt5-0.7.2 → open_api_mt5-0.7.3}/tests/test_bar_service.py +0 -0
  33. {open_api_mt5-0.7.2 → open_api_mt5-0.7.3}/tests/test_bars_api.py +0 -0
  34. {open_api_mt5-0.7.2 → open_api_mt5-0.7.3}/tests/test_calendar_api.py +0 -0
  35. {open_api_mt5-0.7.2 → open_api_mt5-0.7.3}/tests/test_calendar_service.py +0 -0
  36. {open_api_mt5-0.7.2 → open_api_mt5-0.7.3}/tests/test_config_service.py +0 -0
  37. {open_api_mt5-0.7.2 → open_api_mt5-0.7.3}/tests/test_trade_api.py +0 -0
  38. {open_api_mt5-0.7.2 → open_api_mt5-0.7.3}/tests/test_trade_service.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: open-api-mt5
3
- Version: 0.7.2
3
+ Version: 0.7.3
4
4
  Summary: REST and WebSocket API project for MetaTrader 5
5
5
  Requires-Python: >=3.11
6
6
  Description-Content-Type: text/markdown
@@ -0,0 +1,72 @@
1
+ from __future__ import annotations
2
+
3
+ from pathlib import PurePath, PureWindowsPath
4
+ from typing import Any
5
+
6
+ import MetaTrader5 as mt5
7
+
8
+
9
+ def _path_parts(value: str) -> PurePath:
10
+ if "\\" in value or ":" in value:
11
+ return PureWindowsPath(value)
12
+ return PurePath(value)
13
+
14
+
15
+ def _normalize_terminal_dir(value: str | None) -> str | None:
16
+ if not value:
17
+ return None
18
+
19
+ cleaned = value.strip().strip('"')
20
+ if not cleaned:
21
+ return None
22
+
23
+ parsed = _path_parts(cleaned)
24
+ if parsed.name.lower() in {"terminal.exe", "terminal64.exe"}:
25
+ parsed = parsed.parent
26
+ return str(parsed).replace("/", "\\").rstrip("\\").casefold()
27
+
28
+
29
+ def _connected_to_requested_terminal(path: str) -> bool:
30
+ terminal_info = mt5.terminal_info()
31
+ connected_path = _normalize_terminal_dir(getattr(terminal_info, "path", None))
32
+ requested_path = _normalize_terminal_dir(path)
33
+ return connected_path is None or requested_path is None or connected_path == requested_path
34
+
35
+
36
+ def initialize_connection(
37
+ username: str | int | None,
38
+ password: str | None,
39
+ server: str | None,
40
+ path: str | None = None,
41
+ portable: bool | None = None,
42
+ ) -> tuple[bool, str]:
43
+ init_kwargs: dict[str, Any] = {}
44
+ if path:
45
+ init_kwargs["portable"] = True if portable is None else portable
46
+ elif portable is not None:
47
+ init_kwargs["portable"] = portable
48
+ if username not in (None, ""):
49
+ try:
50
+ init_kwargs["login"] = int(username)
51
+ except ValueError:
52
+ return False, "failed: username/login must be a number"
53
+ if password:
54
+ init_kwargs["password"] = password
55
+ if server:
56
+ init_kwargs["server"] = server
57
+
58
+ initialized = mt5.initialize(path, **init_kwargs) if path else mt5.initialize(**init_kwargs)
59
+ if initialized:
60
+ if path and not _connected_to_requested_terminal(path):
61
+ connected_path = getattr(mt5.terminal_info(), "path", "unknown")
62
+ mt5.shutdown()
63
+ return False, f"failed: connected to terminal at '{connected_path}' instead of requested path '{path}'"
64
+ return True, "connected"
65
+ return False, f"failed: {mt5.last_error()}"
66
+
67
+
68
+ def shutdown_connection() -> tuple[bool, str]:
69
+ mt5.shutdown()
70
+ if mt5.terminal_info() is None:
71
+ return True, "disconnected"
72
+ return False, "failed: terminal still connected"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: open-api-mt5
3
- Version: 0.7.2
3
+ Version: 0.7.3
4
4
  Summary: REST and WebSocket API project for MetaTrader 5
5
5
  Requires-Python: >=3.11
6
6
  Description-Content-Type: text/markdown
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "open-api-mt5"
3
- version = "0.7.2"
3
+ version = "0.7.3"
4
4
  description = "REST and WebSocket API project for MetaTrader 5"
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.11"
@@ -0,0 +1,100 @@
1
+ from __future__ import annotations
2
+
3
+ from types import SimpleNamespace
4
+
5
+ from app.services import connection_service
6
+
7
+
8
+ def test_initialize_connection_uses_portable_mode_with_terminal_path(monkeypatch):
9
+ captured = {"args": None, "kwargs": None}
10
+
11
+ def initialize(*args, **kwargs):
12
+ captured["args"] = args
13
+ captured["kwargs"] = kwargs
14
+ return True
15
+
16
+ monkeypatch.setattr(connection_service.mt5, "initialize", initialize, raising=False)
17
+ monkeypatch.setattr(
18
+ connection_service.mt5,
19
+ "terminal_info",
20
+ lambda: SimpleNamespace(path=r"C:\MT5-A"),
21
+ raising=False,
22
+ )
23
+
24
+ assert connection_service.initialize_connection(
25
+ username="123",
26
+ password="secret",
27
+ server="Broker-Server",
28
+ path=r"C:\MT5-A\terminal64.exe",
29
+ ) == (True, "connected")
30
+ assert captured["args"] == (r"C:\MT5-A\terminal64.exe",)
31
+ assert captured["kwargs"] == {
32
+ "portable": True,
33
+ "login": 123,
34
+ "password": "secret",
35
+ "server": "Broker-Server",
36
+ }
37
+
38
+
39
+ def test_initialize_connection_allows_disabling_portable_mode(monkeypatch):
40
+ captured = {"args": None, "kwargs": None}
41
+
42
+ def initialize(*args, **kwargs):
43
+ captured["args"] = args
44
+ captured["kwargs"] = kwargs
45
+ return True
46
+
47
+ monkeypatch.setattr(
48
+ connection_service.mt5,
49
+ "initialize",
50
+ initialize,
51
+ raising=False,
52
+ )
53
+ monkeypatch.setattr(
54
+ connection_service.mt5,
55
+ "terminal_info",
56
+ lambda: SimpleNamespace(path=r"C:\MT5-A"),
57
+ raising=False,
58
+ )
59
+
60
+ assert connection_service.initialize_connection(
61
+ username="123",
62
+ password=None,
63
+ server=None,
64
+ path=r"C:\MT5-A\terminal64.exe",
65
+ portable=False,
66
+ ) == (True, "connected")
67
+ assert captured["args"] == (r"C:\MT5-A\terminal64.exe",)
68
+ assert captured["kwargs"] == {
69
+ "portable": False,
70
+ "login": 123,
71
+ }
72
+
73
+
74
+ def test_initialize_connection_rejects_wrong_running_terminal(monkeypatch):
75
+ shutdown_called = False
76
+
77
+ monkeypatch.setattr(connection_service.mt5, "initialize", lambda *_args, **_kwargs: True, raising=False)
78
+ monkeypatch.setattr(
79
+ connection_service.mt5,
80
+ "terminal_info",
81
+ lambda: SimpleNamespace(path=r"C:\MT5-B"),
82
+ raising=False,
83
+ )
84
+
85
+ def shutdown():
86
+ nonlocal shutdown_called
87
+ shutdown_called = True
88
+
89
+ monkeypatch.setattr(connection_service.mt5, "shutdown", shutdown, raising=False)
90
+
91
+ ok, message = connection_service.initialize_connection(
92
+ username="123",
93
+ password=None,
94
+ server=None,
95
+ path=r"C:\MT5-A\terminal64.exe",
96
+ )
97
+
98
+ assert ok is False
99
+ assert "instead of requested path" in message
100
+ assert shutdown_called is True
@@ -1,41 +0,0 @@
1
- from __future__ import annotations
2
-
3
- from typing import Any
4
-
5
- import MetaTrader5 as mt5
6
-
7
-
8
- def initialize_connection(
9
- username: str | int | None,
10
- password: str | None,
11
- server: str | None,
12
- path: str | None = None,
13
- portable: bool | None = None,
14
- ) -> tuple[bool, str]:
15
- init_kwargs: dict[str, Any] = {}
16
- if path:
17
- init_kwargs["path"] = path
18
- init_kwargs["portable"] = True if portable is None else portable
19
- elif portable is not None:
20
- init_kwargs["portable"] = portable
21
- if username not in (None, ""):
22
- try:
23
- init_kwargs["login"] = int(username)
24
- except ValueError:
25
- return False, "failed: username/login must be a number"
26
- if password:
27
- init_kwargs["password"] = password
28
- if server:
29
- init_kwargs["server"] = server
30
-
31
- initialized = mt5.initialize(**init_kwargs)
32
- if initialized:
33
- return True, "connected"
34
- return False, f"failed: {mt5.last_error()}"
35
-
36
-
37
- def shutdown_connection() -> tuple[bool, str]:
38
- mt5.shutdown()
39
- if mt5.terminal_info() is None:
40
- return True, "disconnected"
41
- return False, "failed: terminal still connected"
@@ -1,50 +0,0 @@
1
- from __future__ import annotations
2
-
3
- from app.services import connection_service
4
-
5
-
6
- def test_initialize_connection_uses_portable_mode_with_terminal_path(monkeypatch):
7
- captured = {}
8
-
9
- def initialize(**kwargs):
10
- captured.update(kwargs)
11
- return True
12
-
13
- monkeypatch.setattr(connection_service.mt5, "initialize", initialize, raising=False)
14
-
15
- assert connection_service.initialize_connection(
16
- username="123",
17
- password="secret",
18
- server="Broker-Server",
19
- path=r"C:\MT5-A\terminal64.exe",
20
- ) == (True, "connected")
21
- assert captured == {
22
- "path": r"C:\MT5-A\terminal64.exe",
23
- "portable": True,
24
- "login": 123,
25
- "password": "secret",
26
- "server": "Broker-Server",
27
- }
28
-
29
-
30
- def test_initialize_connection_allows_disabling_portable_mode(monkeypatch):
31
- captured = {}
32
- monkeypatch.setattr(
33
- connection_service.mt5,
34
- "initialize",
35
- lambda **kwargs: captured.update(kwargs) or True,
36
- raising=False,
37
- )
38
-
39
- assert connection_service.initialize_connection(
40
- username="123",
41
- password=None,
42
- server=None,
43
- path=r"C:\MT5-A\terminal64.exe",
44
- portable=False,
45
- ) == (True, "connected")
46
- assert captured == {
47
- "path": r"C:\MT5-A\terminal64.exe",
48
- "portable": False,
49
- "login": 123,
50
- }
File without changes
File without changes
File without changes
File without changes