Shrink public API surface and remove storage re-export module (#74)
This commit is contained in:
@@ -44,16 +44,14 @@ from datetime import UTC, datetime
|
|||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
from mt5cli import (
|
from mt5cli import (
|
||||||
DataKind,
|
|
||||||
Dataset,
|
|
||||||
MT5Client,
|
MT5Client,
|
||||||
build_config,
|
build_config,
|
||||||
collect_history,
|
collect_history,
|
||||||
export_dataframe,
|
|
||||||
mt5_session,
|
mt5_session,
|
||||||
normalize_dataframe,
|
|
||||||
update_history_with_config,
|
update_history_with_config,
|
||||||
)
|
)
|
||||||
|
from mt5cli.schemas import DataKind, normalize_dataframe
|
||||||
|
from mt5cli.utils import Dataset, export_dataframe
|
||||||
|
|
||||||
# Persistent session for multiple calls
|
# Persistent session for multiple calls
|
||||||
with mt5_session(build_config(login=12345, server="Broker-Demo")) as client:
|
with mt5_session(build_config(login=12345, server="Broker-Demo")) as client:
|
||||||
@@ -89,7 +87,7 @@ update_history_with_config(
|
|||||||
)
|
)
|
||||||
```
|
```
|
||||||
|
|
||||||
Schema contracts live in `mt5cli.schemas` (`DataKind`, `validate_schema`, `normalize_dataframe`). Storage helpers are re-exported from `mt5cli.storage` and the package root.
|
Schema contracts live in `mt5cli.schemas` (`DataKind`, `validate_schema`, `normalize_dataframe`). Export and storage helpers are in `mt5cli.utils` (`Dataset`, `export_dataframe`) and `mt5cli.history`.
|
||||||
|
|
||||||
`MT5Client.order_send()` is a live execution primitive: it can place real trades on the connected account. mt5cli does not implement strategy logic, signal generation, backtesting, or optimization — downstream applications must gate live execution explicitly.
|
`MT5Client.order_send()` is a live execution primitive: it can place real trades on the connected account. mt5cli does not implement strategy logic, signal generation, backtesting, or optimization — downstream applications must gate live execution explicitly.
|
||||||
|
|
||||||
@@ -212,7 +210,8 @@ For automated pipelines, use the importable incremental API instead of re-fetchi
|
|||||||
|
|
||||||
```python
|
```python
|
||||||
from pdmt5 import Mt5Config, Mt5DataClient
|
from pdmt5 import Mt5Config, Mt5DataClient
|
||||||
from mt5cli import Dataset, update_history, update_history_with_config
|
from mt5cli import update_history, update_history_with_config
|
||||||
|
from mt5cli.utils import Dataset
|
||||||
|
|
||||||
# Reuse an already-connected pdmt5 client (does not open/close MT5)
|
# Reuse an already-connected pdmt5 client (does not open/close MT5)
|
||||||
client = Mt5DataClient(config=Mt5Config(login=12345))
|
client = Mt5DataClient(config=Mt5Config(login=12345))
|
||||||
|
|||||||
+5
-3
@@ -182,12 +182,14 @@ targets without hard-coding view names:
|
|||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
from mt5cli import (
|
from mt5cli import (
|
||||||
load_rate_data,
|
|
||||||
load_rate_series_by_granularity,
|
load_rate_series_by_granularity,
|
||||||
load_rate_series_from_sqlite,
|
load_rate_series_from_sqlite,
|
||||||
resolve_rate_table_name,
|
|
||||||
)
|
)
|
||||||
from mt5cli.history import resolve_rate_view_name
|
from mt5cli.history import (
|
||||||
|
load_rate_data,
|
||||||
|
resolve_rate_table_name,
|
||||||
|
resolve_rate_view_name,
|
||||||
|
)
|
||||||
|
|
||||||
view = resolve_rate_view_name(Path("history.db"), "EURUSD", "M1", require_existing=True)
|
view = resolve_rate_view_name(Path("history.db"), "EURUSD", "M1", require_existing=True)
|
||||||
rates = load_rate_data(Path("history.db"), view, count=1000)
|
rates = load_rate_data(Path("history.db"), view, count=1000)
|
||||||
|
|||||||
+3
-5
@@ -13,7 +13,6 @@ responsibilities.
|
|||||||
| [Public API Contract](public-contract.md) | Stable downstream SDK exports, CLI boundary, and out-of-scope items |
|
| [Public API Contract](public-contract.md) | Stable downstream SDK exports, CLI boundary, and out-of-scope items |
|
||||||
| [Client](client.md) | `MT5Client` session abstraction for data access and order primitives |
|
| [Client](client.md) | `MT5Client` session abstraction for data access and order primitives |
|
||||||
| [Schemas](schemas.md) | Canonical DataFrame contracts and normalization helpers |
|
| [Schemas](schemas.md) | Canonical DataFrame contracts and normalization helpers |
|
||||||
| [Storage](storage.md) | CSV/JSON/Parquet/SQLite export and history collection helpers |
|
|
||||||
| [Converters](converters.md) | Symbol, timeframe, timezone, and date-range utilities |
|
| [Converters](converters.md) | Symbol, timeframe, timezone, and date-range utilities |
|
||||||
| [Exceptions](exceptions.md) | Stable mt5cli exception types and MT5 error normalization |
|
| [Exceptions](exceptions.md) | Stable mt5cli exception types and MT5 error normalization |
|
||||||
| [SDK](sdk.md) | Module-level fetch helpers, multi-account collectors, incremental history |
|
| [SDK](sdk.md) | Module-level fetch helpers, multi-account collectors, incremental history |
|
||||||
@@ -30,15 +29,14 @@ flowchart TD
|
|||||||
CLI["mt5cli CLI"] --> Client
|
CLI["mt5cli CLI"] --> Client
|
||||||
Client --> SDK["sdk / pdmt5"]
|
Client --> SDK["sdk / pdmt5"]
|
||||||
Client --> Schemas["schemas"]
|
Client --> Schemas["schemas"]
|
||||||
Storage["storage"] --> History["history SQLite"]
|
History["history SQLite"] --> Utils["utils export"]
|
||||||
Storage --> Utils["utils export"]
|
|
||||||
SDK --> PDMT5["pdmt5.Mt5DataClient"]
|
SDK --> PDMT5["pdmt5.Mt5DataClient"]
|
||||||
```
|
```
|
||||||
|
|
||||||
Downstream packages should depend on the package root exports documented in the
|
Downstream packages should depend on the package root exports documented in the
|
||||||
[Public API Contract](public-contract.md) (`MT5Client`,
|
[Public API Contract](public-contract.md) (`MT5Client`,
|
||||||
`DataKind`, `normalize_dataframe`, `collect_history`, `load_rate_data`,
|
`collect_history`, `load_rate_series_from_sqlite`, etc.) rather than private
|
||||||
`resolve_rate_view_name`, etc.) rather than private modules.
|
modules. Lower-level helpers are accessible directly from their owning modules.
|
||||||
|
|
||||||
`MT5Client.order_send()` is a live execution primitive that can place real trades. mt5cli exposes minimal execution helpers only; strategy logic, signals, backtests, and optimization remain out of scope and must be implemented downstream with explicit execution gating.
|
`MT5Client.order_send()` is a live execution primitive that can place real trades. mt5cli exposes minimal execution helpers only; strategy logic, signals, backtests, and optimization remain out of scope and must be implemented downstream with explicit execution gating.
|
||||||
|
|
||||||
|
|||||||
+31
-84
@@ -24,22 +24,15 @@ Note: the former `mt5cli` re-export `TICK_FLAG_MAP` corresponds to `COPY_TICKS_M
|
|||||||
in pdmt5 — the name changed, it was not simply moved.
|
in pdmt5 — the name changed, it was not simply moved.
|
||||||
|
|
||||||
Downstream packages should import from the package root (`from mt5cli import
|
Downstream packages should import from the package root (`from mt5cli import
|
||||||
...`) and use the public tier sets in `mt5cli.contract` to distinguish API
|
...`). The contract set `STABLE_SDK_EXPORTS` in `mt5cli.contract` enumerates
|
||||||
stability. CLI commands mirror the same behavior but are not importable Python
|
every package-root symbol. Lower-level helpers (schema utilities, export
|
||||||
APIs.
|
functions, parser helpers, low-level MT5 wrappers) are available directly from
|
||||||
|
their owning modules (`mt5cli.schemas`, `mt5cli.utils`, `mt5cli.converters`,
|
||||||
## Public API tiers
|
`mt5cli.sdk`, etc.) and are not part of the root SDK surface.
|
||||||
|
|
||||||
mt5cli classifies package-root imports by intended downstream use:
|
|
||||||
|
|
||||||
| Tier | Contract set | Meaning |
|
|
||||||
| ---------------- | -------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
||||||
| Stable core | `STABLE_SDK_EXPORTS` | Preferred SDK surface for downstream MT5 infrastructure adapters. Changes require a deliberate compatibility path. |
|
|
||||||
| Secondary public | `SECONDARY_PUBLIC_EXPORTS` | Public helpers for CLI/export/schema integrations and lower-level MT5 wrappers. Importable, but less central to the downstream trading SDK. |
|
|
||||||
|
|
||||||
## Stable downstream SDK API
|
## Stable downstream SDK API
|
||||||
|
|
||||||
These names are exported from `mt5cli` and covered by the contract in
|
These names are exported from `mt5cli` and enumerated in
|
||||||
`mt5cli.STABLE_SDK_EXPORTS` (defined in `mt5cli.contract`).
|
`mt5cli.STABLE_SDK_EXPORTS` (defined in `mt5cli.contract`).
|
||||||
|
|
||||||
### Session lifecycle and configuration
|
### Session lifecycle and configuration
|
||||||
@@ -52,20 +45,6 @@ These names are exported from `mt5cli` and covered by the contract in
|
|||||||
| `create_trading_client`, `mt5_trading_session` | Trading-capable `pdmt5.Mt5TradingClient` lifecycle |
|
| `create_trading_client`, `mt5_trading_session` | Trading-capable `pdmt5.Mt5TradingClient` lifecycle |
|
||||||
| `AccountSpec` | Generic account group: symbols plus optional credentials |
|
| `AccountSpec` | Generic account group: symbols plus optional credentials |
|
||||||
| `resolve_account_spec`, `resolve_account_specs` | Merge overrides and expand `${ENV_VAR}` placeholders; opt-in `allow_whole_dollar_env` for bare `$NAME` |
|
| `resolve_account_spec`, `resolve_account_specs` | Merge overrides and expand `${ENV_VAR}` placeholders; opt-in `allow_whole_dollar_env` for bare `$NAME` |
|
||||||
| `substitute_env_placeholders` | Replace `${NAME}` substrings from the environment; opt-in `allow_whole_dollar_env` for whole-value `$NAME` |
|
|
||||||
| `substitute_mapping_values` | Recursively traverse a dict/list/scalar structure and substitute `${ENV_VAR}` placeholders for caller-selected mapping keys only; optionally normalise blank strings to `None` for a separate caller-selected key set; does not hard-code any application-specific key names |
|
|
||||||
|
|
||||||
Credential resolution is generic: any environment variable name may appear inside
|
|
||||||
`${...}`. mt5cli does not hard-code application-specific keys such as
|
|
||||||
`mt5_login` or `mt5_exe`.
|
|
||||||
|
|
||||||
Pass `allow_whole_dollar_env=True` to `substitute_env_placeholders()`,
|
|
||||||
`substitute_mapping_values()`, `resolve_account_spec()`, `resolve_account_specs()`,
|
|
||||||
and `build_config()` to additionally expand strings whose entire value is a bare
|
|
||||||
`$ENV_NAME` identifier.
|
|
||||||
Partial strings such as `"plan$pass"`, `"abc$ENV"`, or `"$ENV-suffix"` are
|
|
||||||
**never** expanded — only an exact `$IDENTIFIER` whole-string match qualifies.
|
|
||||||
Default is `False` to preserve backward compatibility.
|
|
||||||
|
|
||||||
### Closed-bar rate helpers
|
### Closed-bar rate helpers
|
||||||
|
|
||||||
@@ -85,20 +64,13 @@ timestamp normalization in downstream apps.
|
|||||||
|
|
||||||
### SQLite history collection and rate loading
|
### SQLite history collection and rate loading
|
||||||
|
|
||||||
| Symbol | Role |
|
| Symbol | Role |
|
||||||
| ----------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------- |
|
| ----------------------------------------------------------------- | -------------------------------------------------------------------------------------------- |
|
||||||
| `collect_history` | One-shot date-range export into SQLite |
|
| `collect_history` | One-shot date-range export into SQLite |
|
||||||
| `update_history`, `update_history_with_config` | Incremental append from `MAX(time)` cursors |
|
| `update_history`, `update_history_with_config` | Incremental append from `MAX(time)` cursors |
|
||||||
| `ThrottledHistoryUpdater` | Minimum interval between successful incremental updates; optional `update_backend` injection |
|
| `ThrottledHistoryUpdater` | Minimum interval between successful incremental updates; optional `update_backend` injection |
|
||||||
| `resolve_history_datasets`, `resolve_history_timeframes`, `resolve_history_tick_flags` | History pipeline configuration |
|
| `RateTarget`, `build_rate_targets` | Neutral `(symbol, timeframe)` series descriptors |
|
||||||
| `build_rate_view_name`, `resolve_rate_table_name`, `resolve_rate_view_name`, `resolve_rate_view_names`, `resolve_rate_tables` | Map symbols/timeframes to mt5cli-managed table or view names |
|
| `load_rate_series_from_sqlite`, `load_rate_series_by_granularity` | Load one or many series; fail clearly when managed views are missing |
|
||||||
| `RateTarget`, `build_rate_targets` | Neutral `(symbol, timeframe)` series descriptors |
|
|
||||||
| `load_rate_data`, `load_rate_data_from_connection` | Load one table/view into a time-indexed DataFrame |
|
|
||||||
| `load_rate_series_from_sqlite`, `load_rate_series_by_granularity` | Load one or many series; fail clearly when managed views are missing |
|
|
||||||
|
|
||||||
Pass `require_existing=True` to rate view resolution helpers when downstream
|
|
||||||
code must fail instead of receiving a best-guess view name. Multi-series loaders
|
|
||||||
require existing managed `rate_*__*` views unless `explicit_tables` is supplied.
|
|
||||||
|
|
||||||
See [History Collection (SQLite)](history.md) for schema, view naming, and ER
|
See [History Collection (SQLite)](history.md) for schema, view naming, and ER
|
||||||
diagrams.
|
diagrams.
|
||||||
@@ -151,48 +123,23 @@ and returned as `status="failed"` with normalized `request` / `response` details
|
|||||||
|
|
||||||
### Errors
|
### Errors
|
||||||
|
|
||||||
| Symbol | Role |
|
| Symbol | Role |
|
||||||
| ------------------------------------------------------------------------------------ | -------------------------------------------- |
|
| -------------------------------------------------------------------------- | ----------------------------- |
|
||||||
| `Mt5CliError`, `Mt5ConnectionError`, `Mt5OperationError`, `Mt5SchemaError` | Stable mt5cli exception types |
|
| `Mt5CliError`, `Mt5ConnectionError`, `Mt5OperationError`, `Mt5SchemaError` | Stable mt5cli exception types |
|
||||||
| `normalize_mt5_exception`, `call_with_normalized_errors`, `is_recoverable_mt5_error` | Error normalization and retry classification |
|
|
||||||
|
|
||||||
## Secondary public exports
|
## Module-scoped helpers
|
||||||
|
|
||||||
These names remain importable from `mt5cli` and are covered by
|
Lower-level helpers are available from their owning modules and are not part
|
||||||
`SECONDARY_PUBLIC_EXPORTS`, but they are oriented toward CLI/export/schema
|
of the package-root stable surface. Import them directly when needed:
|
||||||
integrations, parsing, and lower-level MT5 access rather than the stable core
|
|
||||||
SDK surface. Prefer the stable symbols above for downstream infrastructure
|
|
||||||
adapters.
|
|
||||||
|
|
||||||
### Read-only MT5 data wrappers
|
| Module | Examples |
|
||||||
|
| ------------------- | ---------------------------------------------------------------------------------------------- |
|
||||||
Module-level helpers open a transient connection per call. Prefer `mt5_session`
|
| `mt5cli.history` | `resolve_rate_view_name`, `resolve_rate_tables`, `load_rate_data`, `build_rate_view_name` |
|
||||||
or `MT5Client` when making many requests in one process.
|
| `mt5cli.sdk` | `copy_rates_from`, `copy_ticks_from`, `account_info`, `symbols`, `mt5_summary`, `latest_rates` |
|
||||||
|
| `mt5cli.schemas` | `DataKind`, `normalize_dataframe`, `validate_schema`, `DEDUP_KEYS` |
|
||||||
| Area | Symbols |
|
| `mt5cli.utils` | `Dataset`, `IfExists`, `detect_format`, `export_dataframe`, `export_dataframe_to_sqlite` |
|
||||||
| -------------------- | ---------------------------------------------------------------------------------------------------- |
|
| `mt5cli.converters` | `normalize_symbol`, `ensure_utc`, `parse_date_range`, `granularity_name` |
|
||||||
| Rates | `copy_rates_from`, `copy_rates_from_pos`, `copy_rates_range`, `latest_rates`, `collect_latest_rates` |
|
| `mt5cli.exceptions` | `normalize_mt5_exception`, `call_with_normalized_errors`, `is_recoverable_mt5_error` |
|
||||||
| Ticks | `copy_ticks_from`, `copy_ticks_range`, `recent_ticks` |
|
|
||||||
| Account / terminal | `account_info`, `terminal_info`, `mt5_version`, `last_error`, `mt5_summary`, `mt5_summary_as_df` |
|
|
||||||
| Symbols / market | `symbols`, `symbol_info`, `symbol_info_tick`, `market_book`, `minimum_margins` |
|
|
||||||
| Trading state (read) | `orders`, `positions`, `history_orders`, `history_deals`, `recent_history_deals` |
|
|
||||||
| Multi-account rates | `collect_latest_rates_for_accounts` |
|
|
||||||
|
|
||||||
Use `mt5_version` for MetaTrader 5 terminal version data. The name `version` at
|
|
||||||
the package root refers to `importlib.metadata.version` (package metadata), not
|
|
||||||
the MT5 SDK helper.
|
|
||||||
|
|
||||||
### Schema, export, and parser helpers
|
|
||||||
|
|
||||||
| Area | Symbols |
|
|
||||||
| -------------------- | ------------------------------------------------------------------------------------------------------------- |
|
|
||||||
| Dataset contracts | `DataKind`, `Dataset`, `IfExists`, `DEDUP_KEYS`, `REQUIRED_COLUMNS`, `TIME_COLUMNS`, `KNOWN_MT5_TIME_COLUMNS` |
|
|
||||||
| Schema normalization | `normalize_dataframe`, `normalize_time_columns`, `schema_columns`, `validate_schema` |
|
|
||||||
| Export helpers | `detect_format`, `export_dataframe`, `export_dataframe_to_sqlite` |
|
|
||||||
| Symbol parsing | `normalize_symbol`, `normalize_symbols` |
|
|
||||||
| Time parsing | `ensure_utc`, `parse_date_range`, `parse_datetime`, `recent_window` |
|
|
||||||
| MT5 parsing maps | `granularity_name`, `parse_tick_flags`, `parse_timeframe` |
|
|
||||||
| Trading data shapes | `POSITION_COLUMNS` |
|
|
||||||
|
|
||||||
## CLI commands
|
## CLI commands
|
||||||
|
|
||||||
@@ -246,7 +193,7 @@ their own adapter layer.
|
|||||||
|
|
||||||
## Contract verification
|
## Contract verification
|
||||||
|
|
||||||
`tests/test_contracts.py` asserts that every name in the stable and secondary
|
`tests/test_contracts.py` asserts that every name in `STABLE_SDK_EXPORTS` is
|
||||||
tier sets is importable from `mt5cli`, documents key closed-bar, rate-view,
|
importable from `mt5cli`, that all package-root exports are covered by the
|
||||||
SQLite loading, account-resolution, and trading-session behaviors, and keeps the
|
stable set, and documents key closed-bar, SQLite loading, account-resolution,
|
||||||
tier sets aligned with `__all__`.
|
and trading-session behaviors.
|
||||||
|
|||||||
+2
-1
@@ -117,7 +117,8 @@ call it every iteration without over-fetching.
|
|||||||
```python
|
```python
|
||||||
from pdmt5 import Mt5Config, Mt5DataClient
|
from pdmt5 import Mt5Config, Mt5DataClient
|
||||||
|
|
||||||
from mt5cli import Dataset, ThrottledHistoryUpdater
|
from mt5cli import ThrottledHistoryUpdater
|
||||||
|
from mt5cli.utils import Dataset
|
||||||
|
|
||||||
updater = ThrottledHistoryUpdater(
|
updater = ThrottledHistoryUpdater(
|
||||||
output="history.db",
|
output="history.db",
|
||||||
|
|||||||
@@ -1,3 +0,0 @@
|
|||||||
# Storage
|
|
||||||
|
|
||||||
::: mt5cli.storage
|
|
||||||
+5
-9
@@ -42,19 +42,15 @@ from datetime import UTC, datetime
|
|||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
from mt5cli import (
|
from mt5cli import (
|
||||||
DataKind,
|
|
||||||
Dataset,
|
|
||||||
MT5Client,
|
MT5Client,
|
||||||
build_config,
|
build_config,
|
||||||
collect_history,
|
collect_history,
|
||||||
export_dataframe,
|
|
||||||
load_rate_data,
|
|
||||||
minimum_margins,
|
|
||||||
mt5_session,
|
mt5_session,
|
||||||
normalize_dataframe,
|
|
||||||
recent_ticks,
|
|
||||||
resolve_rate_view_name,
|
|
||||||
)
|
)
|
||||||
|
from mt5cli.history import load_rate_data, resolve_rate_view_name
|
||||||
|
from mt5cli.schemas import DataKind, normalize_dataframe
|
||||||
|
from mt5cli.sdk import minimum_margins, recent_ticks
|
||||||
|
from mt5cli.utils import Dataset, export_dataframe
|
||||||
|
|
||||||
# Persistent session for multiple calls
|
# Persistent session for multiple calls
|
||||||
with mt5_session(build_config(login=12345, server="Broker-Demo")) as client:
|
with mt5_session(build_config(login=12345, server="Broker-Demo")) as client:
|
||||||
@@ -90,7 +86,7 @@ collect_history(
|
|||||||
)
|
)
|
||||||
```
|
```
|
||||||
|
|
||||||
Schema contracts live in `mt5cli.schemas` (`DataKind`, `validate_schema`, `normalize_dataframe`). Storage helpers are re-exported from `mt5cli.storage` and the package root.
|
Schema contracts live in `mt5cli.schemas` (`DataKind`, `validate_schema`, `normalize_dataframe`). Export and storage helpers are in `mt5cli.utils` (`Dataset`, `export_dataframe`) and `mt5cli.history`.
|
||||||
|
|
||||||
`MT5Client.order_send()` is a live execution primitive: it can place real trades on the connected account. mt5cli does not implement strategy logic, signal generation, backtesting, or optimization — downstream applications must gate live execution explicitly (the CLI requires `--yes` for `order-send`).
|
`MT5Client.order_send()` is a live execution primitive: it can place real trades on the connected account. mt5cli does not implement strategy logic, signal generation, backtesting, or optimization — downstream applications must gate live execution explicitly (the CLI requires `--yes` for `order-send`).
|
||||||
|
|
||||||
|
|||||||
@@ -59,7 +59,6 @@ nav:
|
|||||||
- Public API Contract: api/public-contract.md
|
- Public API Contract: api/public-contract.md
|
||||||
- Client: api/client.md
|
- Client: api/client.md
|
||||||
- Schemas: api/schemas.md
|
- Schemas: api/schemas.md
|
||||||
- Storage: api/storage.md
|
|
||||||
- Converters: api/converters.md
|
- Converters: api/converters.md
|
||||||
- Exceptions: api/exceptions.md
|
- Exceptions: api/exceptions.md
|
||||||
- CLI: api/cli.md
|
- CLI: api/cli.md
|
||||||
|
|||||||
+1
-145
@@ -9,107 +9,34 @@ strategy responsibilities.
|
|||||||
from importlib.metadata import version
|
from importlib.metadata import version
|
||||||
|
|
||||||
from .client import MT5Client, build_config, mt5_session
|
from .client import MT5Client, build_config, mt5_session
|
||||||
from .contract import (
|
from .contract import STABLE_SDK_EXPORTS
|
||||||
PUBLIC_EXPORT_TIERS,
|
|
||||||
SECONDARY_PUBLIC_EXPORTS,
|
|
||||||
STABLE_SDK_EXPORTS,
|
|
||||||
)
|
|
||||||
from .converters import (
|
|
||||||
ensure_utc,
|
|
||||||
granularity_name,
|
|
||||||
normalize_symbol,
|
|
||||||
normalize_symbols,
|
|
||||||
parse_date_range,
|
|
||||||
recent_window,
|
|
||||||
)
|
|
||||||
from .exceptions import (
|
from .exceptions import (
|
||||||
Mt5CliError,
|
Mt5CliError,
|
||||||
Mt5ConnectionError,
|
Mt5ConnectionError,
|
||||||
Mt5OperationError,
|
Mt5OperationError,
|
||||||
Mt5SchemaError,
|
Mt5SchemaError,
|
||||||
call_with_normalized_errors,
|
|
||||||
is_recoverable_mt5_error,
|
|
||||||
normalize_mt5_exception,
|
|
||||||
)
|
)
|
||||||
from .history import (
|
from .history import (
|
||||||
RateTarget,
|
RateTarget,
|
||||||
build_rate_targets,
|
build_rate_targets,
|
||||||
build_rate_view_name,
|
|
||||||
drop_forming_rate_bar,
|
drop_forming_rate_bar,
|
||||||
load_rate_data,
|
|
||||||
load_rate_data_from_connection,
|
|
||||||
load_rate_series_by_granularity,
|
load_rate_series_by_granularity,
|
||||||
load_rate_series_from_sqlite,
|
load_rate_series_from_sqlite,
|
||||||
resolve_history_datasets,
|
|
||||||
resolve_history_tick_flags,
|
|
||||||
resolve_history_timeframes,
|
|
||||||
resolve_rate_table_name,
|
|
||||||
resolve_rate_tables,
|
|
||||||
resolve_rate_view_name,
|
|
||||||
resolve_rate_view_names,
|
|
||||||
)
|
|
||||||
from .schemas import (
|
|
||||||
DEDUP_KEYS,
|
|
||||||
KNOWN_MT5_TIME_COLUMNS,
|
|
||||||
REQUIRED_COLUMNS,
|
|
||||||
TIME_COLUMNS,
|
|
||||||
DataKind,
|
|
||||||
normalize_dataframe,
|
|
||||||
normalize_time_columns,
|
|
||||||
schema_columns,
|
|
||||||
validate_schema,
|
|
||||||
)
|
)
|
||||||
from .sdk import (
|
from .sdk import (
|
||||||
AccountSpec,
|
AccountSpec,
|
||||||
ThrottledHistoryUpdater,
|
ThrottledHistoryUpdater,
|
||||||
account_info,
|
|
||||||
collect_history,
|
collect_history,
|
||||||
collect_latest_closed_rates_by_granularity,
|
collect_latest_closed_rates_by_granularity,
|
||||||
collect_latest_closed_rates_for_accounts,
|
collect_latest_closed_rates_for_accounts,
|
||||||
collect_latest_rates,
|
|
||||||
collect_latest_rates_for_accounts,
|
|
||||||
collect_latest_rates_for_accounts_with_retries,
|
collect_latest_rates_for_accounts_with_retries,
|
||||||
copy_rates_from,
|
|
||||||
copy_rates_from_pos,
|
|
||||||
copy_rates_range,
|
|
||||||
copy_ticks_from,
|
|
||||||
copy_ticks_range,
|
|
||||||
fetch_latest_closed_rates,
|
fetch_latest_closed_rates,
|
||||||
history_deals,
|
|
||||||
history_orders,
|
|
||||||
last_error,
|
|
||||||
latest_rates,
|
|
||||||
market_book,
|
|
||||||
minimum_margins,
|
|
||||||
mt5_summary,
|
|
||||||
mt5_summary_as_df,
|
|
||||||
orders,
|
|
||||||
positions,
|
|
||||||
recent_history_deals,
|
|
||||||
recent_ticks,
|
|
||||||
resolve_account_spec,
|
resolve_account_spec,
|
||||||
resolve_account_specs,
|
resolve_account_specs,
|
||||||
substitute_env_placeholders,
|
|
||||||
substitute_mapping_values,
|
|
||||||
symbol_info,
|
|
||||||
symbol_info_tick,
|
|
||||||
symbols,
|
|
||||||
terminal_info,
|
|
||||||
update_history,
|
update_history,
|
||||||
update_history_with_config,
|
update_history_with_config,
|
||||||
)
|
)
|
||||||
from .sdk import (
|
|
||||||
version as mt5_version,
|
|
||||||
)
|
|
||||||
from .storage import (
|
|
||||||
Dataset,
|
|
||||||
IfExists,
|
|
||||||
detect_format,
|
|
||||||
export_dataframe,
|
|
||||||
export_dataframe_to_sqlite,
|
|
||||||
)
|
|
||||||
from .trading import (
|
from .trading import (
|
||||||
POSITION_COLUMNS,
|
|
||||||
ExecutionStatus,
|
ExecutionStatus,
|
||||||
MarginVolume,
|
MarginVolume,
|
||||||
OrderExecutionResult,
|
OrderExecutionResult,
|
||||||
@@ -149,28 +76,13 @@ from .trading import (
|
|||||||
update_sltp_for_open_positions,
|
update_sltp_for_open_positions,
|
||||||
update_trailing_stop_loss_for_open_positions,
|
update_trailing_stop_loss_for_open_positions,
|
||||||
)
|
)
|
||||||
from .utils import (
|
|
||||||
parse_datetime,
|
|
||||||
parse_tick_flags,
|
|
||||||
parse_timeframe,
|
|
||||||
)
|
|
||||||
|
|
||||||
__version__ = version(__package__) if __package__ else None
|
__version__ = version(__package__) if __package__ else None
|
||||||
|
|
||||||
__all__ = [
|
__all__ = [
|
||||||
"DEDUP_KEYS",
|
|
||||||
"KNOWN_MT5_TIME_COLUMNS",
|
|
||||||
"POSITION_COLUMNS",
|
|
||||||
"PUBLIC_EXPORT_TIERS",
|
|
||||||
"REQUIRED_COLUMNS",
|
|
||||||
"SECONDARY_PUBLIC_EXPORTS",
|
|
||||||
"STABLE_SDK_EXPORTS",
|
"STABLE_SDK_EXPORTS",
|
||||||
"TIME_COLUMNS",
|
|
||||||
"AccountSpec",
|
"AccountSpec",
|
||||||
"DataKind",
|
|
||||||
"Dataset",
|
|
||||||
"ExecutionStatus",
|
"ExecutionStatus",
|
||||||
"IfExists",
|
|
||||||
"MT5Client",
|
"MT5Client",
|
||||||
"MarginVolume",
|
"MarginVolume",
|
||||||
"Mt5CliError",
|
"Mt5CliError",
|
||||||
@@ -186,10 +98,8 @@ __all__ = [
|
|||||||
"ProjectionMode",
|
"ProjectionMode",
|
||||||
"RateTarget",
|
"RateTarget",
|
||||||
"ThrottledHistoryUpdater",
|
"ThrottledHistoryUpdater",
|
||||||
"account_info",
|
|
||||||
"build_config",
|
"build_config",
|
||||||
"build_rate_targets",
|
"build_rate_targets",
|
||||||
"build_rate_view_name",
|
|
||||||
"calculate_account_projected_margin_ratio",
|
"calculate_account_projected_margin_ratio",
|
||||||
"calculate_margin_and_volume",
|
"calculate_margin_and_volume",
|
||||||
"calculate_new_position_margin_ratio",
|
"calculate_new_position_margin_ratio",
|
||||||
@@ -201,29 +111,17 @@ __all__ = [
|
|||||||
"calculate_symbol_group_margin_ratio",
|
"calculate_symbol_group_margin_ratio",
|
||||||
"calculate_trailing_stop_updates",
|
"calculate_trailing_stop_updates",
|
||||||
"calculate_volume_by_margin",
|
"calculate_volume_by_margin",
|
||||||
"call_with_normalized_errors",
|
|
||||||
"close_open_positions",
|
"close_open_positions",
|
||||||
"collect_history",
|
"collect_history",
|
||||||
"collect_latest_closed_rates_by_granularity",
|
"collect_latest_closed_rates_by_granularity",
|
||||||
"collect_latest_closed_rates_for_accounts",
|
"collect_latest_closed_rates_for_accounts",
|
||||||
"collect_latest_rates",
|
|
||||||
"collect_latest_rates_for_accounts",
|
|
||||||
"collect_latest_rates_for_accounts_with_retries",
|
"collect_latest_rates_for_accounts_with_retries",
|
||||||
"copy_rates_from",
|
|
||||||
"copy_rates_from_pos",
|
|
||||||
"copy_rates_range",
|
|
||||||
"copy_ticks_from",
|
|
||||||
"copy_ticks_range",
|
|
||||||
"create_trading_client",
|
"create_trading_client",
|
||||||
"detect_format",
|
|
||||||
"detect_position_side",
|
"detect_position_side",
|
||||||
"determine_order_limits",
|
"determine_order_limits",
|
||||||
"drop_forming_rate_bar",
|
"drop_forming_rate_bar",
|
||||||
"ensure_symbol_selected",
|
"ensure_symbol_selected",
|
||||||
"ensure_utc",
|
|
||||||
"estimate_order_margin",
|
"estimate_order_margin",
|
||||||
"export_dataframe",
|
|
||||||
"export_dataframe_to_sqlite",
|
|
||||||
"extract_tick_price",
|
"extract_tick_price",
|
||||||
"fetch_latest_closed_rates",
|
"fetch_latest_closed_rates",
|
||||||
"fetch_latest_closed_rates_for_trading_client",
|
"fetch_latest_closed_rates_for_trading_client",
|
||||||
@@ -232,58 +130,16 @@ __all__ = [
|
|||||||
"get_positions_frame",
|
"get_positions_frame",
|
||||||
"get_symbol_snapshot",
|
"get_symbol_snapshot",
|
||||||
"get_tick_snapshot",
|
"get_tick_snapshot",
|
||||||
"granularity_name",
|
|
||||||
"history_deals",
|
|
||||||
"history_orders",
|
|
||||||
"is_recoverable_mt5_error",
|
|
||||||
"last_error",
|
|
||||||
"latest_rates",
|
|
||||||
"load_rate_data",
|
|
||||||
"load_rate_data_from_connection",
|
|
||||||
"load_rate_series_by_granularity",
|
"load_rate_series_by_granularity",
|
||||||
"load_rate_series_from_sqlite",
|
"load_rate_series_from_sqlite",
|
||||||
"market_book",
|
|
||||||
"minimum_margins",
|
|
||||||
"mt5_session",
|
"mt5_session",
|
||||||
"mt5_summary",
|
|
||||||
"mt5_summary_as_df",
|
|
||||||
"mt5_trading_session",
|
"mt5_trading_session",
|
||||||
"mt5_version",
|
|
||||||
"normalize_dataframe",
|
|
||||||
"normalize_mt5_exception",
|
|
||||||
"normalize_order_volume",
|
"normalize_order_volume",
|
||||||
"normalize_symbol",
|
|
||||||
"normalize_symbols",
|
|
||||||
"normalize_time_columns",
|
|
||||||
"orders",
|
|
||||||
"parse_date_range",
|
|
||||||
"parse_datetime",
|
|
||||||
"parse_tick_flags",
|
|
||||||
"parse_timeframe",
|
|
||||||
"place_market_order",
|
"place_market_order",
|
||||||
"positions",
|
|
||||||
"recent_history_deals",
|
|
||||||
"recent_ticks",
|
|
||||||
"recent_window",
|
|
||||||
"resolve_account_spec",
|
"resolve_account_spec",
|
||||||
"resolve_account_specs",
|
"resolve_account_specs",
|
||||||
"resolve_history_datasets",
|
|
||||||
"resolve_history_tick_flags",
|
|
||||||
"resolve_history_timeframes",
|
|
||||||
"resolve_rate_table_name",
|
|
||||||
"resolve_rate_tables",
|
|
||||||
"resolve_rate_view_name",
|
|
||||||
"resolve_rate_view_names",
|
|
||||||
"schema_columns",
|
|
||||||
"substitute_env_placeholders",
|
|
||||||
"substitute_mapping_values",
|
|
||||||
"symbol_info",
|
|
||||||
"symbol_info_tick",
|
|
||||||
"symbols",
|
|
||||||
"terminal_info",
|
|
||||||
"update_history",
|
"update_history",
|
||||||
"update_history_with_config",
|
"update_history_with_config",
|
||||||
"update_sltp_for_open_positions",
|
"update_sltp_for_open_positions",
|
||||||
"update_trailing_stop_loss_for_open_positions",
|
"update_trailing_stop_loss_for_open_positions",
|
||||||
"validate_schema",
|
|
||||||
]
|
]
|
||||||
|
|||||||
+2
-78
@@ -1,4 +1,4 @@
|
|||||||
"""Downstream SDK export tiers for mt5cli."""
|
"""Downstream SDK export tier for mt5cli."""
|
||||||
|
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
@@ -22,7 +22,6 @@ STABLE_SDK_EXPORTS: frozenset[str] = frozenset({
|
|||||||
"ThrottledHistoryUpdater",
|
"ThrottledHistoryUpdater",
|
||||||
"build_config",
|
"build_config",
|
||||||
"build_rate_targets",
|
"build_rate_targets",
|
||||||
"build_rate_view_name",
|
|
||||||
"calculate_account_projected_margin_ratio",
|
"calculate_account_projected_margin_ratio",
|
||||||
"calculate_margin_and_volume",
|
"calculate_margin_and_volume",
|
||||||
"calculate_new_position_margin_ratio",
|
"calculate_new_position_margin_ratio",
|
||||||
@@ -34,7 +33,6 @@ STABLE_SDK_EXPORTS: frozenset[str] = frozenset({
|
|||||||
"calculate_symbol_group_margin_ratio",
|
"calculate_symbol_group_margin_ratio",
|
||||||
"calculate_trailing_stop_updates",
|
"calculate_trailing_stop_updates",
|
||||||
"calculate_volume_by_margin",
|
"calculate_volume_by_margin",
|
||||||
"call_with_normalized_errors",
|
|
||||||
"close_open_positions",
|
"close_open_positions",
|
||||||
"collect_history",
|
"collect_history",
|
||||||
"collect_latest_closed_rates_by_granularity",
|
"collect_latest_closed_rates_by_granularity",
|
||||||
@@ -54,92 +52,18 @@ STABLE_SDK_EXPORTS: frozenset[str] = frozenset({
|
|||||||
"get_positions_frame",
|
"get_positions_frame",
|
||||||
"get_symbol_snapshot",
|
"get_symbol_snapshot",
|
||||||
"get_tick_snapshot",
|
"get_tick_snapshot",
|
||||||
"is_recoverable_mt5_error",
|
|
||||||
"load_rate_data",
|
|
||||||
"load_rate_data_from_connection",
|
|
||||||
"load_rate_series_by_granularity",
|
"load_rate_series_by_granularity",
|
||||||
"load_rate_series_from_sqlite",
|
"load_rate_series_from_sqlite",
|
||||||
"mt5_session",
|
"mt5_session",
|
||||||
"mt5_trading_session",
|
"mt5_trading_session",
|
||||||
"normalize_mt5_exception",
|
|
||||||
"normalize_order_volume",
|
"normalize_order_volume",
|
||||||
"place_market_order",
|
"place_market_order",
|
||||||
"resolve_account_spec",
|
"resolve_account_spec",
|
||||||
"resolve_account_specs",
|
"resolve_account_specs",
|
||||||
"resolve_history_datasets",
|
|
||||||
"resolve_history_tick_flags",
|
|
||||||
"resolve_history_timeframes",
|
|
||||||
"resolve_rate_table_name",
|
|
||||||
"resolve_rate_tables",
|
|
||||||
"resolve_rate_view_name",
|
|
||||||
"resolve_rate_view_names",
|
|
||||||
"substitute_env_placeholders",
|
|
||||||
"substitute_mapping_values",
|
|
||||||
"update_history",
|
"update_history",
|
||||||
"update_history_with_config",
|
"update_history_with_config",
|
||||||
"update_sltp_for_open_positions",
|
"update_sltp_for_open_positions",
|
||||||
"update_trailing_stop_loss_for_open_positions",
|
"update_trailing_stop_loss_for_open_positions",
|
||||||
})
|
})
|
||||||
|
|
||||||
SECONDARY_PUBLIC_EXPORTS: frozenset[str] = frozenset({
|
__all__ = ["STABLE_SDK_EXPORTS"]
|
||||||
"DEDUP_KEYS",
|
|
||||||
"DataKind",
|
|
||||||
"Dataset",
|
|
||||||
"IfExists",
|
|
||||||
"KNOWN_MT5_TIME_COLUMNS",
|
|
||||||
"POSITION_COLUMNS",
|
|
||||||
"REQUIRED_COLUMNS",
|
|
||||||
"TIME_COLUMNS",
|
|
||||||
"account_info",
|
|
||||||
"collect_latest_rates",
|
|
||||||
"collect_latest_rates_for_accounts",
|
|
||||||
"copy_rates_from",
|
|
||||||
"copy_rates_from_pos",
|
|
||||||
"copy_rates_range",
|
|
||||||
"copy_ticks_from",
|
|
||||||
"copy_ticks_range",
|
|
||||||
"detect_format",
|
|
||||||
"ensure_utc",
|
|
||||||
"export_dataframe",
|
|
||||||
"export_dataframe_to_sqlite",
|
|
||||||
"granularity_name",
|
|
||||||
"history_deals",
|
|
||||||
"history_orders",
|
|
||||||
"last_error",
|
|
||||||
"latest_rates",
|
|
||||||
"market_book",
|
|
||||||
"minimum_margins",
|
|
||||||
"mt5_summary",
|
|
||||||
"mt5_summary_as_df",
|
|
||||||
"mt5_version",
|
|
||||||
"normalize_dataframe",
|
|
||||||
"normalize_symbol",
|
|
||||||
"normalize_symbols",
|
|
||||||
"normalize_time_columns",
|
|
||||||
"orders",
|
|
||||||
"parse_date_range",
|
|
||||||
"parse_datetime",
|
|
||||||
"parse_tick_flags",
|
|
||||||
"parse_timeframe",
|
|
||||||
"positions",
|
|
||||||
"recent_history_deals",
|
|
||||||
"recent_ticks",
|
|
||||||
"recent_window",
|
|
||||||
"schema_columns",
|
|
||||||
"symbol_info",
|
|
||||||
"symbol_info_tick",
|
|
||||||
"symbols",
|
|
||||||
"terminal_info",
|
|
||||||
"validate_schema",
|
|
||||||
})
|
|
||||||
|
|
||||||
PUBLIC_EXPORT_TIERS: dict[str, frozenset[str]] = {
|
|
||||||
"stable": STABLE_SDK_EXPORTS,
|
|
||||||
"secondary": SECONDARY_PUBLIC_EXPORTS,
|
|
||||||
}
|
|
||||||
|
|
||||||
__all__ = [
|
|
||||||
"PUBLIC_EXPORT_TIERS",
|
|
||||||
"SECONDARY_PUBLIC_EXPORTS",
|
|
||||||
"STABLE_SDK_EXPORTS",
|
|
||||||
]
|
|
||||||
|
|||||||
@@ -1,49 +0,0 @@
|
|||||||
"""Generic storage helpers for MT5 market and account history."""
|
|
||||||
|
|
||||||
from __future__ import annotations
|
|
||||||
|
|
||||||
from .history import (
|
|
||||||
RateTarget,
|
|
||||||
build_rate_targets,
|
|
||||||
build_rate_view_name,
|
|
||||||
drop_forming_rate_bar,
|
|
||||||
load_rate_data,
|
|
||||||
load_rate_data_from_connection,
|
|
||||||
load_rate_series_by_granularity,
|
|
||||||
load_rate_series_from_sqlite,
|
|
||||||
resolve_rate_tables,
|
|
||||||
resolve_rate_view_name,
|
|
||||||
resolve_rate_view_names,
|
|
||||||
)
|
|
||||||
from .sdk import collect_history, update_history, update_history_with_config
|
|
||||||
from .utils import (
|
|
||||||
Dataset,
|
|
||||||
IfExists,
|
|
||||||
OutputFormat,
|
|
||||||
detect_format,
|
|
||||||
export_dataframe,
|
|
||||||
export_dataframe_to_sqlite,
|
|
||||||
)
|
|
||||||
|
|
||||||
__all__ = [
|
|
||||||
"Dataset",
|
|
||||||
"IfExists",
|
|
||||||
"OutputFormat",
|
|
||||||
"RateTarget",
|
|
||||||
"build_rate_targets",
|
|
||||||
"build_rate_view_name",
|
|
||||||
"collect_history",
|
|
||||||
"detect_format",
|
|
||||||
"drop_forming_rate_bar",
|
|
||||||
"export_dataframe",
|
|
||||||
"export_dataframe_to_sqlite",
|
|
||||||
"load_rate_data",
|
|
||||||
"load_rate_data_from_connection",
|
|
||||||
"load_rate_series_by_granularity",
|
|
||||||
"load_rate_series_from_sqlite",
|
|
||||||
"resolve_rate_tables",
|
|
||||||
"resolve_rate_view_name",
|
|
||||||
"resolve_rate_view_names",
|
|
||||||
"update_history",
|
|
||||||
"update_history_with_config",
|
|
||||||
]
|
|
||||||
+83
-112
@@ -2,14 +2,16 @@
|
|||||||
|
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
import re
|
import importlib
|
||||||
import sqlite3
|
import sqlite3
|
||||||
from datetime import UTC, datetime
|
from datetime import UTC, datetime
|
||||||
from importlib.metadata import requires
|
from importlib.metadata import requires
|
||||||
from pathlib import Path
|
from typing import TYPE_CHECKING, get_type_hints
|
||||||
from typing import get_type_hints
|
|
||||||
from unittest.mock import MagicMock
|
from unittest.mock import MagicMock
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
import pandas as pd
|
import pandas as pd
|
||||||
import pytest
|
import pytest
|
||||||
from pdmt5 import Mt5RuntimeError, Mt5TradingError
|
from pdmt5 import Mt5RuntimeError, Mt5TradingError
|
||||||
@@ -17,15 +19,8 @@ from pytest_mock import MockerFixture # noqa: TC002
|
|||||||
|
|
||||||
import mt5cli
|
import mt5cli
|
||||||
from mt5cli import (
|
from mt5cli import (
|
||||||
DEDUP_KEYS,
|
|
||||||
PUBLIC_EXPORT_TIERS,
|
|
||||||
REQUIRED_COLUMNS,
|
|
||||||
SECONDARY_PUBLIC_EXPORTS,
|
|
||||||
STABLE_SDK_EXPORTS,
|
STABLE_SDK_EXPORTS,
|
||||||
TIME_COLUMNS,
|
|
||||||
AccountSpec,
|
AccountSpec,
|
||||||
DataKind,
|
|
||||||
Dataset,
|
|
||||||
ExecutionStatus,
|
ExecutionStatus,
|
||||||
MarginVolume,
|
MarginVolume,
|
||||||
MT5Client,
|
MT5Client,
|
||||||
@@ -44,40 +39,56 @@ from mt5cli import (
|
|||||||
calculate_projected_margin_ratio,
|
calculate_projected_margin_ratio,
|
||||||
calculate_symbol_group_margin_ratio,
|
calculate_symbol_group_margin_ratio,
|
||||||
calculate_trailing_stop_updates,
|
calculate_trailing_stop_updates,
|
||||||
call_with_normalized_errors,
|
|
||||||
detect_format,
|
|
||||||
drop_forming_rate_bar,
|
drop_forming_rate_bar,
|
||||||
ensure_symbol_selected,
|
ensure_symbol_selected,
|
||||||
ensure_utc,
|
|
||||||
export_dataframe,
|
|
||||||
export_dataframe_to_sqlite,
|
|
||||||
extract_tick_price,
|
extract_tick_price,
|
||||||
fetch_latest_closed_rates,
|
fetch_latest_closed_rates,
|
||||||
fetch_latest_closed_rates_for_trading_client,
|
fetch_latest_closed_rates_for_trading_client,
|
||||||
fetch_latest_closed_rates_indexed,
|
fetch_latest_closed_rates_indexed,
|
||||||
granularity_name,
|
|
||||||
is_recoverable_mt5_error,
|
|
||||||
load_rate_data,
|
|
||||||
load_rate_series_from_sqlite,
|
load_rate_series_from_sqlite,
|
||||||
mt5_session,
|
mt5_session,
|
||||||
mt5_trading_session,
|
mt5_trading_session,
|
||||||
normalize_dataframe,
|
|
||||||
normalize_mt5_exception,
|
|
||||||
normalize_order_volume,
|
normalize_order_volume,
|
||||||
|
place_market_order,
|
||||||
|
resolve_account_spec,
|
||||||
|
resolve_account_specs,
|
||||||
|
)
|
||||||
|
from mt5cli.converters import (
|
||||||
|
ensure_utc,
|
||||||
|
granularity_name,
|
||||||
normalize_symbol,
|
normalize_symbol,
|
||||||
normalize_symbols,
|
normalize_symbols,
|
||||||
parse_date_range,
|
parse_date_range,
|
||||||
place_market_order,
|
|
||||||
recent_window,
|
recent_window,
|
||||||
resolve_account_spec,
|
)
|
||||||
resolve_account_specs,
|
from mt5cli.exceptions import (
|
||||||
|
call_with_normalized_errors,
|
||||||
|
is_recoverable_mt5_error,
|
||||||
|
normalize_mt5_exception,
|
||||||
|
)
|
||||||
|
from mt5cli.history import (
|
||||||
|
create_rate_compatibility_views,
|
||||||
|
load_rate_data,
|
||||||
resolve_rate_view_name,
|
resolve_rate_view_name,
|
||||||
|
)
|
||||||
|
from mt5cli.retry import retry_with_backoff
|
||||||
|
from mt5cli.schemas import (
|
||||||
|
DEDUP_KEYS,
|
||||||
|
REQUIRED_COLUMNS,
|
||||||
|
TIME_COLUMNS,
|
||||||
|
DataKind,
|
||||||
|
ensure_utc_columns,
|
||||||
|
normalize_dataframe,
|
||||||
|
normalize_time_columns,
|
||||||
schema_columns,
|
schema_columns,
|
||||||
validate_schema,
|
validate_schema,
|
||||||
)
|
)
|
||||||
from mt5cli.history import create_rate_compatibility_views
|
from mt5cli.utils import (
|
||||||
from mt5cli.retry import retry_with_backoff
|
Dataset,
|
||||||
from mt5cli.schemas import ensure_utc_columns, normalize_time_columns
|
detect_format,
|
||||||
|
export_dataframe,
|
||||||
|
export_dataframe_to_sqlite,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def _sample_frame(kind: DataKind) -> pd.DataFrame:
|
def _sample_frame(kind: DataKind) -> pd.DataFrame:
|
||||||
@@ -540,6 +551,12 @@ def test_storage_export_round_trip_sqlite(tmp_path: Path) -> None:
|
|||||||
assert count == 1
|
assert count == 1
|
||||||
|
|
||||||
|
|
||||||
|
def test_storage_module_does_not_exist() -> None:
|
||||||
|
"""mt5cli.storage re-export module has been removed."""
|
||||||
|
with pytest.raises(ModuleNotFoundError):
|
||||||
|
importlib.import_module("mt5cli.storage")
|
||||||
|
|
||||||
|
|
||||||
class TestStableSdkContract:
|
class TestStableSdkContract:
|
||||||
"""Tests for the documented stable downstream SDK contract."""
|
"""Tests for the documented stable downstream SDK contract."""
|
||||||
|
|
||||||
@@ -548,51 +565,19 @@ class TestStableSdkContract:
|
|||||||
missing = sorted(STABLE_SDK_EXPORTS - set(mt5cli.__all__))
|
missing = sorted(STABLE_SDK_EXPORTS - set(mt5cli.__all__))
|
||||||
assert not missing, f"STABLE_SDK_EXPORTS missing from __all__: {missing}"
|
assert not missing, f"STABLE_SDK_EXPORTS missing from __all__: {missing}"
|
||||||
|
|
||||||
def test_public_export_tiers_are_disjoint_and_complete(self) -> None:
|
def test_stable_exports_cover_root_api(self) -> None:
|
||||||
"""Documented public tiers do not overlap and classify root exports."""
|
"""STABLE_SDK_EXPORTS classifies every package-root symbol."""
|
||||||
assert PUBLIC_EXPORT_TIERS == {
|
tier_metadata = {"STABLE_SDK_EXPORTS"}
|
||||||
"stable": STABLE_SDK_EXPORTS,
|
|
||||||
"secondary": SECONDARY_PUBLIC_EXPORTS,
|
|
||||||
}
|
|
||||||
assert not (STABLE_SDK_EXPORTS & SECONDARY_PUBLIC_EXPORTS)
|
|
||||||
tiered_exports = STABLE_SDK_EXPORTS | SECONDARY_PUBLIC_EXPORTS
|
|
||||||
root_exports = set(mt5cli.__all__)
|
root_exports = set(mt5cli.__all__)
|
||||||
|
|
||||||
missing_from_root = sorted(tiered_exports - root_exports)
|
missing_from_root = sorted(STABLE_SDK_EXPORTS - root_exports)
|
||||||
assert not missing_from_root, (
|
assert not missing_from_root, (
|
||||||
f"Tiered exports missing from __all__: {missing_from_root}"
|
f"STABLE_SDK_EXPORTS missing from __all__: {missing_from_root}"
|
||||||
)
|
)
|
||||||
|
|
||||||
tier_metadata_exports = {
|
unclassified = sorted(root_exports - STABLE_SDK_EXPORTS - tier_metadata)
|
||||||
"PUBLIC_EXPORT_TIERS",
|
assert not unclassified, (
|
||||||
"SECONDARY_PUBLIC_EXPORTS",
|
f"Root exports not in STABLE_SDK_EXPORTS: {unclassified}"
|
||||||
"STABLE_SDK_EXPORTS",
|
|
||||||
}
|
|
||||||
unclassified_root_exports = sorted(
|
|
||||||
root_exports - tiered_exports - tier_metadata_exports,
|
|
||||||
)
|
|
||||||
assert not unclassified_root_exports, (
|
|
||||||
f"Root exports missing from public API tiers: {unclassified_root_exports}"
|
|
||||||
)
|
|
||||||
|
|
||||||
def test_stable_docs_do_not_document_nonstable_exports(self) -> None:
|
|
||||||
"""Stable docs do not promote secondary root exports."""
|
|
||||||
docs_path = Path("docs/api/public-contract.md")
|
|
||||||
docs = docs_path.read_text(encoding="utf-8")
|
|
||||||
stable_section = docs.split("## Stable downstream SDK API", maxsplit=1)[
|
|
||||||
1
|
|
||||||
].split(
|
|
||||||
"## Secondary public exports",
|
|
||||||
maxsplit=1,
|
|
||||||
)[0]
|
|
||||||
documented_symbols = set(
|
|
||||||
re.findall(r"`([A-Za-z_][A-Za-z0-9_]*)`", stable_section)
|
|
||||||
)
|
|
||||||
nonstable_exports = SECONDARY_PUBLIC_EXPORTS
|
|
||||||
|
|
||||||
wrongly_stable = sorted(documented_symbols & nonstable_exports)
|
|
||||||
assert not wrongly_stable, (
|
|
||||||
f"Non-stable exports documented in stable section: {wrongly_stable}"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
@pytest.mark.parametrize("name", sorted(STABLE_SDK_EXPORTS))
|
@pytest.mark.parametrize("name", sorted(STABLE_SDK_EXPORTS))
|
||||||
@@ -600,17 +585,6 @@ class TestStableSdkContract:
|
|||||||
"""Stable SDK names resolve through ``from mt5cli import ...``."""
|
"""Stable SDK names resolve through ``from mt5cli import ...``."""
|
||||||
assert hasattr(mt5cli, name), f"{name!r} missing from mt5cli package root"
|
assert hasattr(mt5cli, name), f"{name!r} missing from mt5cli package root"
|
||||||
|
|
||||||
@pytest.mark.parametrize(
|
|
||||||
"name",
|
|
||||||
sorted(SECONDARY_PUBLIC_EXPORTS),
|
|
||||||
)
|
|
||||||
def test_secondary_exports_are_importable(
|
|
||||||
self,
|
|
||||||
name: str,
|
|
||||||
) -> None:
|
|
||||||
"""Non-stable public names remain available from the package root."""
|
|
||||||
assert hasattr(mt5cli, name), f"{name!r} missing from mt5cli package root"
|
|
||||||
|
|
||||||
def test_drop_forming_rate_bar_from_package_root(self) -> None:
|
def test_drop_forming_rate_bar_from_package_root(self) -> None:
|
||||||
"""Closed-bar trimming is available from the stable package surface."""
|
"""Closed-bar trimming is available from the stable package surface."""
|
||||||
frame = pd.DataFrame({"time": [1, 2, 3], "close": [1.0, 1.1, 1.2]})
|
frame = pd.DataFrame({"time": [1, 2, 3], "close": [1.0, 1.1, 1.2]})
|
||||||
@@ -684,38 +658,6 @@ class TestStableSdkContract:
|
|||||||
assert callable(calculate_projected_margin_ratio)
|
assert callable(calculate_projected_margin_ratio)
|
||||||
assert callable(calculate_symbol_group_margin_ratio)
|
assert callable(calculate_symbol_group_margin_ratio)
|
||||||
|
|
||||||
def test_resolve_rate_view_name_from_package_root(self, tmp_path: Path) -> None:
|
|
||||||
"""Rate view resolution is importable and honors require_existing."""
|
|
||||||
db_path = tmp_path / "rates.db"
|
|
||||||
with sqlite3.connect(db_path) as conn:
|
|
||||||
conn.execute(
|
|
||||||
"CREATE TABLE rates("
|
|
||||||
" symbol TEXT, timeframe INTEGER, time TEXT, close REAL)",
|
|
||||||
)
|
|
||||||
conn.execute(
|
|
||||||
"INSERT INTO rates(symbol, timeframe, time, close) VALUES (?, ?, ?, ?)",
|
|
||||||
("EURUSD", 1, "2024-01-01T00:00:00+00:00", 1.0),
|
|
||||||
)
|
|
||||||
create_rate_compatibility_views(conn)
|
|
||||||
|
|
||||||
assert resolve_rate_view_name(db_path, "EURUSD", "M1") == "rate_EURUSD__1"
|
|
||||||
missing = tmp_path / "missing.db"
|
|
||||||
with pytest.raises(ValueError, match="SQLite database not found"):
|
|
||||||
resolve_rate_view_name(missing, "EURUSD", "M1", require_existing=True)
|
|
||||||
|
|
||||||
def test_load_rate_data_from_package_root(self, tmp_path: Path) -> None:
|
|
||||||
"""SQLite rate loading normalizes timestamps through the stable API."""
|
|
||||||
db_path = tmp_path / "view.db"
|
|
||||||
with sqlite3.connect(db_path) as conn:
|
|
||||||
conn.execute(
|
|
||||||
'CREATE VIEW "rate_EURUSD__1" AS'
|
|
||||||
" SELECT '2024-01-01T00:00:00+00:00' AS time, 1.1 AS close",
|
|
||||||
)
|
|
||||||
|
|
||||||
frame = load_rate_data(db_path, "rate_EURUSD__1")
|
|
||||||
assert frame.index.name == "time"
|
|
||||||
assert abs(float(frame.iloc[0]["close"]) - 1.1) < 1e-9
|
|
||||||
|
|
||||||
def test_load_rate_series_from_sqlite_requires_managed_views(
|
def test_load_rate_series_from_sqlite_requires_managed_views(
|
||||||
self,
|
self,
|
||||||
tmp_path: Path,
|
tmp_path: Path,
|
||||||
@@ -834,6 +776,38 @@ class TestStableSdkContract:
|
|||||||
assert "time" not in result.columns
|
assert "time" not in result.columns
|
||||||
assert "close" in result.columns
|
assert "close" in result.columns
|
||||||
|
|
||||||
|
def test_rate_view_helpers_in_history_module(self, tmp_path: Path) -> None:
|
||||||
|
"""Rate view helpers are available from mt5cli.history."""
|
||||||
|
db_path = tmp_path / "rates.db"
|
||||||
|
with sqlite3.connect(db_path) as conn:
|
||||||
|
conn.execute(
|
||||||
|
"CREATE TABLE rates("
|
||||||
|
" symbol TEXT, timeframe INTEGER, time TEXT, close REAL)",
|
||||||
|
)
|
||||||
|
conn.execute(
|
||||||
|
"INSERT INTO rates(symbol, timeframe, time, close) VALUES (?, ?, ?, ?)",
|
||||||
|
("EURUSD", 1, "2024-01-01T00:00:00+00:00", 1.0),
|
||||||
|
)
|
||||||
|
create_rate_compatibility_views(conn)
|
||||||
|
|
||||||
|
assert resolve_rate_view_name(db_path, "EURUSD", "M1") == "rate_EURUSD__1"
|
||||||
|
missing = tmp_path / "missing.db"
|
||||||
|
with pytest.raises(ValueError, match="SQLite database not found"):
|
||||||
|
resolve_rate_view_name(missing, "EURUSD", "M1", require_existing=True)
|
||||||
|
|
||||||
|
def test_load_rate_data_in_history_module(self, tmp_path: Path) -> None:
|
||||||
|
"""SQLite rate loading normalizes timestamps through mt5cli.history."""
|
||||||
|
db_path = tmp_path / "view.db"
|
||||||
|
with sqlite3.connect(db_path) as conn:
|
||||||
|
conn.execute(
|
||||||
|
'CREATE VIEW "rate_EURUSD__1" AS'
|
||||||
|
" SELECT '2024-01-01T00:00:00+00:00' AS time, 1.1 AS close",
|
||||||
|
)
|
||||||
|
|
||||||
|
frame = load_rate_data(db_path, "rate_EURUSD__1")
|
||||||
|
assert frame.index.name == "time"
|
||||||
|
assert abs(float(frame.iloc[0]["close"]) - 1.1) < 1e-9
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize(
|
@pytest.mark.parametrize(
|
||||||
"name",
|
"name",
|
||||||
@@ -851,9 +825,6 @@ def test_pdmt5_pass_through_names_removed_from_public_contract(name: str) -> Non
|
|||||||
assert name not in STABLE_SDK_EXPORTS, (
|
assert name not in STABLE_SDK_EXPORTS, (
|
||||||
f"{name!r} should not be in STABLE_SDK_EXPORTS"
|
f"{name!r} should not be in STABLE_SDK_EXPORTS"
|
||||||
)
|
)
|
||||||
assert name not in SECONDARY_PUBLIC_EXPORTS, (
|
|
||||||
f"{name!r} should not be in SECONDARY_PUBLIC_EXPORTS"
|
|
||||||
)
|
|
||||||
assert name not in mt5cli.__all__, f"{name!r} should not be in mt5cli.__all__"
|
assert name not in mt5cli.__all__, f"{name!r} should not be in mt5cli.__all__"
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user