mt5cli 1.4.2__tar.gz → 1.5.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.
Files changed (85) hide show
  1. {mt5cli-1.4.2 → mt5cli-1.5.0}/.agents/skills/local-qa/scripts/qa.sh +2 -2
  2. {mt5cli-1.4.2 → mt5cli-1.5.0}/.github/workflows/claude.yml +2 -3
  3. {mt5cli-1.4.2 → mt5cli-1.5.0}/PKG-INFO +2 -2
  4. {mt5cli-1.4.2 → mt5cli-1.5.0}/docs/api/public-contract.md +3 -1
  5. mt5cli-1.5.0/mt5cli/lifecycle.py +87 -0
  6. {mt5cli-1.4.2 → mt5cli-1.5.0}/mt5cli/sdk.py +1 -0
  7. {mt5cli-1.4.2 → mt5cli-1.5.0}/pyproject.toml +1 -1
  8. mt5cli-1.5.0/tests/unit/test_lifecycle.py +145 -0
  9. {mt5cli-1.4.2 → mt5cli-1.5.0}/uv.lock +1 -1
  10. mt5cli-1.4.2/AGENTS.md +0 -29
  11. mt5cli-1.4.2/CLAUDE.md +0 -1
  12. {mt5cli-1.4.2 → mt5cli-1.5.0}/.agents/skills/local-qa/SKILL.md +0 -0
  13. {mt5cli-1.4.2 → mt5cli-1.5.0}/.agents/skills/mt5cli/SKILL.md +0 -0
  14. {mt5cli-1.4.2 → mt5cli-1.5.0}/.claude/agents/codex.md +0 -0
  15. {mt5cli-1.4.2 → mt5cli-1.5.0}/.claude/settings.json +0 -0
  16. {mt5cli-1.4.2 → mt5cli-1.5.0}/.github/CODEOWNERS +0 -0
  17. {mt5cli-1.4.2 → mt5cli-1.5.0}/.github/FUNDING.yml +0 -0
  18. {mt5cli-1.4.2 → mt5cli-1.5.0}/.github/dependabot.yml +0 -0
  19. {mt5cli-1.4.2 → mt5cli-1.5.0}/.github/renovate.json +0 -0
  20. {mt5cli-1.4.2 → mt5cli-1.5.0}/.github/workflows/ci.yml +0 -0
  21. {mt5cli-1.4.2 → mt5cli-1.5.0}/.github/workflows/release.yml +0 -0
  22. {mt5cli-1.4.2 → mt5cli-1.5.0}/.gitignore +0 -0
  23. {mt5cli-1.4.2 → mt5cli-1.5.0}/LICENSE +0 -0
  24. {mt5cli-1.4.2 → mt5cli-1.5.0}/README.md +0 -0
  25. {mt5cli-1.4.2 → mt5cli-1.5.0}/docs/api/cli.md +0 -0
  26. {mt5cli-1.4.2 → mt5cli-1.5.0}/docs/api/client.md +0 -0
  27. {mt5cli-1.4.2 → mt5cli-1.5.0}/docs/api/converters.md +0 -0
  28. {mt5cli-1.4.2 → mt5cli-1.5.0}/docs/api/exceptions.md +0 -0
  29. {mt5cli-1.4.2 → mt5cli-1.5.0}/docs/api/grafana.md +0 -0
  30. {mt5cli-1.4.2 → mt5cli-1.5.0}/docs/api/history.md +0 -0
  31. {mt5cli-1.4.2 → mt5cli-1.5.0}/docs/api/index.md +0 -0
  32. {mt5cli-1.4.2 → mt5cli-1.5.0}/docs/api/marketdata.md +0 -0
  33. {mt5cli-1.4.2 → mt5cli-1.5.0}/docs/api/observability.md +0 -0
  34. {mt5cli-1.4.2 → mt5cli-1.5.0}/docs/api/schemas.md +0 -0
  35. {mt5cli-1.4.2 → mt5cli-1.5.0}/docs/api/telemetry.md +0 -0
  36. {mt5cli-1.4.2 → mt5cli-1.5.0}/docs/api/trading.md +0 -0
  37. {mt5cli-1.4.2 → mt5cli-1.5.0}/docs/api/utils.md +0 -0
  38. {mt5cli-1.4.2 → mt5cli-1.5.0}/docs/index.md +0 -0
  39. {mt5cli-1.4.2 → mt5cli-1.5.0}/examples/grafana/README.md +0 -0
  40. {mt5cli-1.4.2 → mt5cli-1.5.0}/examples/grafana/compose.yml +0 -0
  41. {mt5cli-1.4.2 → mt5cli-1.5.0}/examples/grafana/dashboards/mt5cli-market.json +0 -0
  42. {mt5cli-1.4.2 → mt5cli-1.5.0}/examples/grafana/dashboards/mt5cli-overview.json +0 -0
  43. {mt5cli-1.4.2 → mt5cli-1.5.0}/examples/grafana/dashboards/mt5cli-trades.json +0 -0
  44. {mt5cli-1.4.2 → mt5cli-1.5.0}/examples/grafana/provisioning/dashboards/mt5cli.yml +0 -0
  45. {mt5cli-1.4.2 → mt5cli-1.5.0}/examples/grafana/provisioning/datasources/mt5cli-sqlite.yml +0 -0
  46. {mt5cli-1.4.2 → mt5cli-1.5.0}/mkdocs.yml +0 -0
  47. {mt5cli-1.4.2 → mt5cli-1.5.0}/mt5cli/__init__.py +0 -0
  48. {mt5cli-1.4.2 → mt5cli-1.5.0}/mt5cli/__main__.py +0 -0
  49. {mt5cli-1.4.2 → mt5cli-1.5.0}/mt5cli/cli.py +0 -0
  50. {mt5cli-1.4.2 → mt5cli-1.5.0}/mt5cli/client.py +0 -0
  51. {mt5cli-1.4.2 → mt5cli-1.5.0}/mt5cli/contract.py +0 -0
  52. {mt5cli-1.4.2 → mt5cli-1.5.0}/mt5cli/converters.py +0 -0
  53. {mt5cli-1.4.2 → mt5cli-1.5.0}/mt5cli/exceptions.py +0 -0
  54. {mt5cli-1.4.2 → mt5cli-1.5.0}/mt5cli/grafana.py +0 -0
  55. {mt5cli-1.4.2 → mt5cli-1.5.0}/mt5cli/history.py +0 -0
  56. {mt5cli-1.4.2 → mt5cli-1.5.0}/mt5cli/marketdata.py +0 -0
  57. {mt5cli-1.4.2 → mt5cli-1.5.0}/mt5cli/observability.py +0 -0
  58. {mt5cli-1.4.2 → mt5cli-1.5.0}/mt5cli/rates.py +0 -0
  59. {mt5cli-1.4.2 → mt5cli-1.5.0}/mt5cli/retry.py +0 -0
  60. {mt5cli-1.4.2 → mt5cli-1.5.0}/mt5cli/schemas.py +0 -0
  61. {mt5cli-1.4.2 → mt5cli-1.5.0}/mt5cli/telemetry.py +0 -0
  62. {mt5cli-1.4.2 → mt5cli-1.5.0}/mt5cli/trading.py +0 -0
  63. {mt5cli-1.4.2 → mt5cli-1.5.0}/mt5cli/utils.py +0 -0
  64. {mt5cli-1.4.2 → mt5cli-1.5.0}/tests/cli/test_cli.bats +0 -0
  65. {mt5cli-1.4.2 → mt5cli-1.5.0}/tests/unit/__init__.py +0 -0
  66. {mt5cli-1.4.2 → mt5cli-1.5.0}/tests/unit/conftest.py +0 -0
  67. {mt5cli-1.4.2 → mt5cli-1.5.0}/tests/unit/test_cli.py +0 -0
  68. {mt5cli-1.4.2 → mt5cli-1.5.0}/tests/unit/test_client.py +0 -0
  69. {mt5cli-1.4.2 → mt5cli-1.5.0}/tests/unit/test_contracts.py +0 -0
  70. {mt5cli-1.4.2 → mt5cli-1.5.0}/tests/unit/test_docs_contract.py +0 -0
  71. {mt5cli-1.4.2 → mt5cli-1.5.0}/tests/unit/test_examples.py +0 -0
  72. {mt5cli-1.4.2 → mt5cli-1.5.0}/tests/unit/test_grafana.py +0 -0
  73. {mt5cli-1.4.2 → mt5cli-1.5.0}/tests/unit/test_history.py +0 -0
  74. {mt5cli-1.4.2 → mt5cli-1.5.0}/tests/unit/test_history_contract.py +0 -0
  75. {mt5cli-1.4.2 → mt5cli-1.5.0}/tests/unit/test_history_edge_contract.py +0 -0
  76. {mt5cli-1.4.2 → mt5cli-1.5.0}/tests/unit/test_marketdata.py +0 -0
  77. {mt5cli-1.4.2 → mt5cli-1.5.0}/tests/unit/test_observability.py +0 -0
  78. {mt5cli-1.4.2 → mt5cli-1.5.0}/tests/unit/test_packaging.py +0 -0
  79. {mt5cli-1.4.2 → mt5cli-1.5.0}/tests/unit/test_rate_timestamp_contract.py +0 -0
  80. {mt5cli-1.4.2 → mt5cli-1.5.0}/tests/unit/test_rates.py +0 -0
  81. {mt5cli-1.4.2 → mt5cli-1.5.0}/tests/unit/test_rates_contract.py +0 -0
  82. {mt5cli-1.4.2 → mt5cli-1.5.0}/tests/unit/test_stable_sdk_cleanup.py +0 -0
  83. {mt5cli-1.4.2 → mt5cli-1.5.0}/tests/unit/test_telemetry.py +0 -0
  84. {mt5cli-1.4.2 → mt5cli-1.5.0}/tests/unit/test_trading.py +0 -0
  85. {mt5cli-1.4.2 → mt5cli-1.5.0}/tests/unit/test_utils.py +0 -0
@@ -32,9 +32,9 @@ case "${OSTYPE}" in
32
32
  | xargs -0 -t shellcheck
33
33
 
34
34
  # GitHub Actions
35
- zizmor --fix=safe .github/workflows
35
+ uvx zizmor --fix=safe .github/workflows
36
36
  git ls-files -z -- '.github/workflows/*.yml' | xargs -0 -t actionlint
37
- checkov --framework=all --output=github_failed_only --directory=.
37
+ uvx checkov --framework=all --output=github_failed_only --directory=.
38
38
  ;;
39
39
  *)
40
40
  echo 'GitHub Actions and shell script linting is only supported on Linux and macOS.'
@@ -27,8 +27,7 @@ jobs:
27
27
  if: >
28
28
  github.event_name == 'pull_request'
29
29
  && (! github.event.pull_request.draft)
30
- && (! startsWith(github.head_ref, 'dependabot/'))
31
- && (! startsWith(github.head_ref, 'renovate/'))
30
+ && github.event.pull_request.user.type != 'Bot'
32
31
  permissions:
33
32
  contents: read
34
33
  pull-requests: write
@@ -46,7 +45,7 @@ jobs:
46
45
  || (github.event_name == 'pull_request_review' && contains(github.event.review.body, '@claude'))
47
46
  || (github.event_name == 'issues' && (contains(github.event.issue.body, '@claude') || contains(github.event.issue.title, '@claude')))
48
47
  permissions:
49
- contents: read
48
+ contents: write
50
49
  pull-requests: write
51
50
  issues: write
52
51
  id-token: write
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.4
1
+ Metadata-Version: 2.5
2
2
  Name: mt5cli
3
- Version: 1.4.2
3
+ Version: 1.5.0
4
4
  Summary: Generic MT5 data and execution infrastructure for Python applications
5
5
  Project-URL: Repository, https://github.com/dceoy/mt5cli.git
6
6
  Author-email: dceoy <dceoy@users.noreply.github.com>
@@ -39,6 +39,7 @@ operational capability still has exactly one owning module:
39
39
  | Module | Owns |
40
40
  | ---------------------- | ------------------------------------------------------------------------------------------------------------------------------------------- |
41
41
  | `mt5cli.client` | Connection lifecycle (`build_config`, `mt5_session`, `MT5Client`); the sole session factory |
42
+ | `mt5cli.lifecycle` | Account selection within an already-active persistent MT5 session (`switch_account`) |
42
43
  | `mt5cli.marketdata` | Stateless one-off market-data reads and multi-account rate collection (`AccountSpec`, `collect_latest_rates_for_accounts`, etc.) |
43
44
  | `mt5cli.history` | Legacy history collection, incremental updates, and SQLite storage (`collect_history`, `update_history`, `write_*_dataset`) |
44
45
  | `mt5cli.rates` | Canonical rate persistence/loading and stable update wrappers (`load_rate_series_from_sqlite`, `update_history`, `ThrottledHistoryUpdater`) |
@@ -66,6 +67,7 @@ These names are exported from `mt5cli` and enumerated in
66
67
  | `build_config` | Build the underlying MT5 connection config from connection fields; `login` accepts `int \| str \| None` — numeric strings are coerced to `int`, blank strings are treated as unset, and `${ENV_VAR}` / `$ENV_NAME` placeholders in string parameters are expanded when `allow_whole_dollar_env=True` |
67
68
  | `substitute_mapping_values` | Generic selected-key environment substitution for downstream configuration adapters |
68
69
  | `mt5_session` | Canonical context manager: initialize/login once, yield `MT5Client`, shut down once; a supplied `client=` remains caller-owned. |
70
+ | `switch_account` | Change the account selected by an already-active persistent `MT5Client` with MT5 `login()` only; verifies the requested login/server and does not reinitialize or shut down the terminal |
69
71
  | `Mt5Config` | Canonical pdmt5 connection configuration type returned by `build_config` and accepted by managed-session wrappers |
70
72
  | `AccountSpec` | Generic account group: symbols plus optional credentials |
71
73
  | `resolve_account_spec`, `resolve_account_specs` | Merge overrides and expand `${ENV_VAR}` placeholders; opt-in `allow_whole_dollar_env` for bare `$NAME` |
@@ -374,4 +376,4 @@ their own adapter layer.
374
376
  `tests/unit/test_contracts.py` asserts that every name in `STABLE_SDK_EXPORTS` is
375
377
  importable from `mt5cli`, that all package-root exports are covered by the
376
378
  stable set, and documents key closed-bar, SQLite loading, account-resolution,
377
- and trading-session behaviors.
379
+ and trading-session behaviors.
@@ -0,0 +1,87 @@
1
+ """Persistent MT5 session lifecycle helpers."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from typing import TYPE_CHECKING, Any, cast
6
+
7
+ from pdmt5 import Mt5RuntimeError
8
+
9
+ from .exceptions import Mt5ConnectionError, normalize_mt5_exception
10
+
11
+ if TYPE_CHECKING:
12
+ from pdmt5 import Mt5DataClient
13
+
14
+ from .client import MT5Client
15
+
16
+
17
+ def _account_matches(account: Any, *, login: int, server: str | None) -> bool: # noqa: ANN401
18
+ """Return whether an MT5 account snapshot matches the requested account."""
19
+ if getattr(account, "login", None) != login:
20
+ return False
21
+ return server is None or getattr(account, "server", None) == server
22
+
23
+
24
+ def switch_account(
25
+ client: MT5Client,
26
+ *,
27
+ login: int,
28
+ password: str | None = None,
29
+ server: str | None = None,
30
+ timeout: int | None = None,
31
+ ) -> None:
32
+ """Switch an active persistent MT5 session without reinitializing the terminal.
33
+
34
+ The configured account on ``client`` is intentionally left unchanged, but this
35
+ does not restore the prior account. For a self-owned client
36
+ (``with MT5Client(...) as client:``), exiting and re-entering reconnects using
37
+ the original configuration. For clients obtained via ``mt5_session()`` or
38
+ ``from_connected_client()`` (the common case), exiting and re-entering ``client``
39
+ has no effect on the connection at all, so the switched account persists for the
40
+ life of the terminal session.
41
+
42
+ Args:
43
+ client: An already-entered ``MT5Client`` with a persistent connection and
44
+ an already-active account.
45
+ login: Target MT5 account login.
46
+ password: Optional target account password.
47
+ server: Optional target trade server.
48
+ timeout: Optional MT5 login timeout in milliseconds.
49
+
50
+ Raises:
51
+ Mt5ConnectionError: If the client is not active, has no active account,
52
+ or the account switch fails.
53
+ """
54
+ connected = cast("Mt5DataClient | None", getattr(client, "_client", None))
55
+ if connected is None or not getattr(connected, "_is_initialized", True):
56
+ # pdmt5 auto-initializes on the first call, so an externally-bound
57
+ # inactive client must be rejected here.
58
+ msg = "MT5 account switching requires an active persistent session."
59
+ raise Mt5ConnectionError(msg)
60
+
61
+ try:
62
+ account = connected.account_info()
63
+ if _account_matches(account, login=login, server=server):
64
+ return
65
+ login_succeeded = connected.login(
66
+ login=login,
67
+ password=password,
68
+ server=server,
69
+ timeout=timeout,
70
+ )
71
+ last_error = None if login_succeeded else connected.last_error()
72
+ active_account = connected.account_info() if login_succeeded else None
73
+ except Mt5RuntimeError as exc:
74
+ normalized = normalize_mt5_exception(exc)
75
+ raise normalized from exc
76
+
77
+ if not login_succeeded:
78
+ msg = f"MT5 account switch failed: {last_error}"
79
+ raise Mt5ConnectionError(msg)
80
+ if not _account_matches(active_account, login=login, server=server):
81
+ msg = (
82
+ "MT5 account switch did not activate the requested account: "
83
+ f"requested login={login}, server={server!r}, "
84
+ f"active login={getattr(active_account, 'login', None)!r}, "
85
+ f"server={getattr(active_account, 'server', None)!r}"
86
+ )
87
+ raise Mt5ConnectionError(msg)
@@ -20,6 +20,7 @@ from .history import (
20
20
  report_rate_gaps,
21
21
  resolve_history_timeframes,
22
22
  )
23
+ from .lifecycle import switch_account
23
24
  from .marketdata import (
24
25
  AccountSpec,
25
26
  collect_latest_closed_rates_by_granularity,
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "mt5cli"
3
- version = "1.4.2"
3
+ version = "1.5.0"
4
4
  description = "Generic MT5 data and execution infrastructure for Python applications"
5
5
  authors = [{name = "dceoy", email = "dceoy@users.noreply.github.com"}]
6
6
  maintainers = [{name = "dceoy", email = "dceoy@users.noreply.github.com"}]
@@ -0,0 +1,145 @@
1
+ """Tests for persistent MT5 session lifecycle helpers."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from types import ModuleType, SimpleNamespace
6
+ from unittest.mock import MagicMock
7
+
8
+ import pytest
9
+ from pdmt5 import Mt5DataClient, Mt5RuntimeError
10
+
11
+ from mt5cli import MT5Client, Mt5ConnectionError, switch_account
12
+
13
+
14
+ def _connected_client(raw_client: MagicMock) -> MT5Client:
15
+ """Return an MT5Client facade bound to a caller-owned active client."""
16
+ return MT5Client.from_connected_client(raw_client)
17
+
18
+
19
+ @pytest.mark.parametrize(
20
+ ("active_login", "active_server", "requested_server", "expect_login"),
21
+ [
22
+ pytest.param(222, "Demo", "Demo", False, id="same-login-same-server"),
23
+ pytest.param(222, "Demo", None, False, id="same-login-no-server-constraint"),
24
+ pytest.param(111, "Demo", "Demo", True, id="different-login"),
25
+ pytest.param(222, "Other", "Demo", True, id="same-login-different-server"),
26
+ ],
27
+ )
28
+ def test_switch_account_pre_switch_match_decision(
29
+ active_login: int,
30
+ active_server: str | None,
31
+ requested_server: str | None,
32
+ expect_login: bool,
33
+ ) -> None:
34
+ """The pre-switch account-match check decides whether login() runs."""
35
+ raw_client = MagicMock()
36
+ active_account = SimpleNamespace(login=active_login, server=active_server)
37
+ raw_client.account_info.side_effect = [
38
+ active_account,
39
+ SimpleNamespace(login=222, server=requested_server or active_server),
40
+ ]
41
+ raw_client.login.return_value = True
42
+ client = _connected_client(raw_client)
43
+
44
+ switch_account(client, login=222, server=requested_server)
45
+
46
+ assert raw_client.login.called is expect_login
47
+ raw_client.shutdown.assert_not_called()
48
+
49
+
50
+ def test_switch_account_uses_login_without_reinitializing_terminal() -> None:
51
+ """Changing accounts calls login only and verifies the resulting account."""
52
+ raw_client = MagicMock()
53
+ raw_client.account_info.side_effect = [
54
+ SimpleNamespace(login=111, server="Demo"),
55
+ SimpleNamespace(login=222, server="Demo"),
56
+ ]
57
+ raw_client.login.return_value = True
58
+ client = _connected_client(raw_client)
59
+
60
+ switch_account(
61
+ client,
62
+ login=222,
63
+ password="secret",
64
+ server="Demo",
65
+ timeout=60_000,
66
+ )
67
+
68
+ raw_client.login.assert_called_once_with(
69
+ login=222,
70
+ password="secret",
71
+ server="Demo",
72
+ timeout=60_000,
73
+ )
74
+ raw_client.initialize_and_login_mt5.assert_not_called()
75
+ raw_client.shutdown.assert_not_called()
76
+
77
+
78
+ def test_switch_account_requires_active_persistent_session() -> None:
79
+ """Transient clients cannot switch an account without opening a session."""
80
+ client = MT5Client()
81
+
82
+ with pytest.raises(Mt5ConnectionError, match="active persistent session"):
83
+ switch_account(client, login=222)
84
+
85
+
86
+ def test_switch_account_rejects_inactive_externally_bound_client() -> None:
87
+ """An uninitialized real client cannot trigger pdmt5's auto-initialization.
88
+
89
+ ``MT5Client.from_connected_client()`` accepts any raw client object without
90
+ validating that it is already connected. pdmt5's ``account_info()``/``login()``
91
+ silently call ``initialize()`` on an uninitialized client, so this guards the
92
+ guarantee that ``switch_account`` never (re)initializes the terminal.
93
+ """
94
+ mock_mt5 = ModuleType("mock_mt5")
95
+ mock_mt5.initialize = MagicMock() # type: ignore[attr-defined]
96
+ raw_client = Mt5DataClient(mt5=mock_mt5)
97
+ client = MT5Client.from_connected_client(raw_client)
98
+
99
+ with pytest.raises(Mt5ConnectionError, match="active persistent session"):
100
+ switch_account(client, login=222)
101
+
102
+ mock_mt5.initialize.assert_not_called() # type: ignore[attr-defined]
103
+
104
+
105
+ def test_switch_account_normalizes_login_failure() -> None:
106
+ """A failed MT5 login becomes the stable connection exception."""
107
+ raw_client = MagicMock()
108
+ raw_client.account_info.return_value = SimpleNamespace(login=111, server="Demo")
109
+ raw_client.login.return_value = False
110
+ raw_client.last_error.return_value = (-6, "authorization failed")
111
+ client = _connected_client(raw_client)
112
+
113
+ with pytest.raises(Mt5ConnectionError, match="authorization failed"):
114
+ switch_account(client, login=222, server="Demo")
115
+
116
+
117
+ def test_switch_account_normalizes_runtime_error() -> None:
118
+ """Underlying pdmt5 runtime errors use the stable connection exception.
119
+
120
+ This also covers the no-active-account case: pdmt5's
121
+ ``Mt5DataClient.account_info()`` raises ``Mt5RuntimeError`` rather than
122
+ returning ``None`` when MT5 has no active account, so ``switch_account``
123
+ requires an already-active account and surfaces that failure the same way
124
+ as any other runtime error.
125
+ """
126
+ raw_client = MagicMock()
127
+ raw_client.account_info.side_effect = Mt5RuntimeError("connection lost")
128
+ client = _connected_client(raw_client)
129
+
130
+ with pytest.raises(Mt5ConnectionError, match="connection lost"):
131
+ switch_account(client, login=222, server="Demo")
132
+
133
+
134
+ def test_switch_account_fails_if_requested_account_is_not_active_after_login() -> None:
135
+ """A successful login return cannot bypass active-account verification."""
136
+ raw_client = MagicMock()
137
+ raw_client.account_info.side_effect = [
138
+ SimpleNamespace(login=111, server="Demo"),
139
+ SimpleNamespace(login=222, server="Other"),
140
+ ]
141
+ raw_client.login.return_value = True
142
+ client = _connected_client(raw_client)
143
+
144
+ with pytest.raises(Mt5ConnectionError, match="did not activate"):
145
+ switch_account(client, login=222, server="Demo")
@@ -595,7 +595,7 @@ wheels = [
595
595
 
596
596
  [[package]]
597
597
  name = "mt5cli"
598
- version = "1.4.2"
598
+ version = "1.5.0"
599
599
  source = { editable = "." }
600
600
  dependencies = [
601
601
  { name = "pandas" },
mt5cli-1.4.2/AGENTS.md DELETED
@@ -1,29 +0,0 @@
1
- # Repository Guidelines
2
-
3
- ## Build, Test, and Development Commands
4
-
5
- Use `.agents/skills/local-qa/SKILL.md` for pre-handoff QA. It runs `.agents/skills/local-qa/scripts/qa.sh`, which formats, lints, type-checks, tests, formats Markdown, and checks GitHub workflows.
6
-
7
- `bats --verbose-run ./tests/cli/` runs the Windows/MT5-oriented CLI tests against the installed `mt5cli` entry point. These require a Windows environment where `uv sync` can install MetaTrader5; state clearly when they were not run.
8
-
9
- `skills/mt5cli/SKILL.md` documents the mt5cli agent skill.
10
-
11
- ## Coding Style & Naming Conventions
12
-
13
- Preserve the package’s small, typed helper style rather than adding broad abstractions.
14
-
15
- ## Design Principles
16
-
17
- Apply KISS, DRY, and YAGNI when changing code. Prefer the simplest implementation that satisfies the current CLI/API contract. Remove duplication when shared behavior is already proven by at least two concrete call sites, but avoid generic helpers for speculative reuse. Do not add configuration flags, extension hooks, or alternate backends until a real repository use case requires them.
18
-
19
- ## Testing Guidelines
20
-
21
- Parametrize unit tests with `pytest.mark.parametrize` when the same behavior needs coverage across multiple inputs. Mock MT5/pdmt5 boundaries; do not require a live MetaTrader terminal in unit tests.
22
-
23
- ## Commit & Pull Request Guidelines
24
-
25
- Recent history uses concise imperative commits, sometimes with conventional prefixes such as `feat:` or `chore:` and PR numbers appended by GitHub. Keep commits scoped to one logical change. Pull requests should describe behavior changes, note tests run, link related issues, and call out MT5/live-trading risk where relevant.
26
-
27
- ## Security & Configuration Tips
28
-
29
- Never commit account credentials, broker passwords, exported private data, or local `.venv` contents. Treat `order_send` and CLI `order-send --yes` as live execution paths; gate examples and tests so they cannot place real trades accidentally.
mt5cli-1.4.2/CLAUDE.md DELETED
@@ -1 +0,0 @@
1
- AGENTS.md
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes