feat: stable MT5Client public API and infrastructure layer (#30)

* feat: add stable MT5Client public API and infrastructure layer

Introduce a reusable public API for downstream trading applications:

- MT5Client as the primary client abstraction with order_check/order_send
- schemas module with DataKind contracts, validation, and normalization
- converters, exceptions, retry, and storage facade modules
- CLI order commands now route through MT5Client
- connected_client made public; retry logic centralized
- Contract tests for API surface, schemas, and storage round-trips
- README and docs updated with Python API usage examples

Co-authored-by: Daichi Narushima <dceoy@users.noreply.github.com>

* fix: correct time coercion, broker-safe symbols, and execution docs

- Normalize MT5 time columns with correct second/millisecond units
- Coerce all present known MT5 time fields, including optional order times
- Preserve broker symbol casing in normalize_symbol()
- Document order_send() as a live execution primitive with clear scope boundaries
- Add contract tests for timestamp and symbol normalization behavior

Co-authored-by: Daichi Narushima <dceoy@users.noreply.github.com>

---------

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: Daichi Narushima <dceoy@users.noreply.github.com>
This commit is contained in:
Daichi Narushima
2026-06-13 01:32:03 +09:00
committed by GitHub
co-authored by Daichi Narushima Cursor Agent
parent 9356d5dcdf
commit 78c49238cf
22 changed files with 1506 additions and 207 deletions
+3 -3
View File
@@ -175,7 +175,7 @@ class TestConnectionLifecycle:
mock_client = MagicMock()
mocker.patch("mt5cli.sdk.Mt5DataClient", return_value=mock_client)
config = MagicMock()
with sdk._connected_client(config): # type: ignore[reportPrivateUsage]
with sdk.connected_client(config): # type: ignore[reportPrivateUsage]
mock_client.initialize_and_login_mt5.assert_called_once()
mock_client.shutdown.assert_called_once()
@@ -191,7 +191,7 @@ class TestConnectionLifecycle:
mocker.patch("mt5cli.sdk.Mt5DataClient", return_value=mock_client)
with (
pytest.raises(RuntimeError, match="login failed"),
sdk._connected_client(MagicMock()), # type: ignore[reportPrivateUsage]
sdk.connected_client(MagicMock()), # type: ignore[reportPrivateUsage]
):
pass
mock_client.shutdown.assert_called_once()
@@ -1342,7 +1342,7 @@ class TestCollectLatestRatesForAccounts:
"""Test account fields override base_config, empty login falls back."""
configs: list[object] = []
def _record_config(*, config: object) -> MagicMock:
def _record_config(*, config: object, **_: object) -> MagicMock:
configs.append(config)
return mock_client