mt5cli 0.5.3__tar.gz → 0.6.1__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.
- {mt5cli-0.5.3 → mt5cli-0.6.1}/PKG-INFO +74 -2
- {mt5cli-0.5.3 → mt5cli-0.6.1}/README.md +73 -1
- {mt5cli-0.5.3 → mt5cli-0.6.1}/docs/api/index.md +7 -2
- {mt5cli-0.5.3 → mt5cli-0.6.1}/docs/api/sdk.md +34 -3
- mt5cli-0.6.1/docs/api/trading.md +70 -0
- {mt5cli-0.5.3 → mt5cli-0.6.1}/mkdocs.yml +1 -0
- {mt5cli-0.5.3 → mt5cli-0.6.1}/mt5cli/__init__.py +16 -0
- {mt5cli-0.5.3 → mt5cli-0.6.1}/mt5cli/history.py +17 -0
- {mt5cli-0.5.3 → mt5cli-0.6.1}/mt5cli/sdk.py +204 -9
- mt5cli-0.6.1/mt5cli/trading.py +210 -0
- {mt5cli-0.5.3 → mt5cli-0.6.1}/pyproject.toml +1 -1
- {mt5cli-0.5.3 → mt5cli-0.6.1}/tests/test_history.py +41 -0
- {mt5cli-0.5.3 → mt5cli-0.6.1}/tests/test_sdk.py +334 -2
- mt5cli-0.6.1/tests/test_trading.py +356 -0
- {mt5cli-0.5.3 → mt5cli-0.6.1}/uv.lock +4 -4
- {mt5cli-0.5.3 → mt5cli-0.6.1}/.agents/skills/local-qa/SKILL.md +0 -0
- {mt5cli-0.5.3 → mt5cli-0.6.1}/.agents/skills/local-qa/scripts/qa.sh +0 -0
- {mt5cli-0.5.3 → mt5cli-0.6.1}/.agents/skills/mt5cli/SKILL.md +0 -0
- {mt5cli-0.5.3 → mt5cli-0.6.1}/.claude/agents/codex.md +0 -0
- {mt5cli-0.5.3 → mt5cli-0.6.1}/.claude/settings.json +0 -0
- {mt5cli-0.5.3 → mt5cli-0.6.1}/.github/FUNDING.yml +0 -0
- {mt5cli-0.5.3 → mt5cli-0.6.1}/.github/dependabot.yml +0 -0
- {mt5cli-0.5.3 → mt5cli-0.6.1}/.github/renovate.json +0 -0
- {mt5cli-0.5.3 → mt5cli-0.6.1}/.github/workflows/ci.yml +0 -0
- {mt5cli-0.5.3 → mt5cli-0.6.1}/.github/workflows/claude.yml +0 -0
- {mt5cli-0.5.3 → mt5cli-0.6.1}/.github/workflows/release.yml +0 -0
- {mt5cli-0.5.3 → mt5cli-0.6.1}/.gitignore +0 -0
- {mt5cli-0.5.3 → mt5cli-0.6.1}/AGENTS.md +0 -0
- {mt5cli-0.5.3 → mt5cli-0.6.1}/CLAUDE.md +0 -0
- {mt5cli-0.5.3 → mt5cli-0.6.1}/LICENSE +0 -0
- {mt5cli-0.5.3 → mt5cli-0.6.1}/docs/api/cli.md +0 -0
- {mt5cli-0.5.3 → mt5cli-0.6.1}/docs/api/history.md +0 -0
- {mt5cli-0.5.3 → mt5cli-0.6.1}/docs/api/utils.md +0 -0
- {mt5cli-0.5.3 → mt5cli-0.6.1}/docs/index.md +0 -0
- {mt5cli-0.5.3 → mt5cli-0.6.1}/mt5cli/__main__.py +0 -0
- {mt5cli-0.5.3 → mt5cli-0.6.1}/mt5cli/cli.py +0 -0
- {mt5cli-0.5.3 → mt5cli-0.6.1}/mt5cli/utils.py +0 -0
- {mt5cli-0.5.3 → mt5cli-0.6.1}/tests/__init__.py +0 -0
- {mt5cli-0.5.3 → mt5cli-0.6.1}/tests/test_cli.py +0 -0
- {mt5cli-0.5.3 → mt5cli-0.6.1}/tests/test_utils.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: mt5cli
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.6.1
|
|
4
4
|
Summary: Command-line tool for MetaTrader 5
|
|
5
5
|
Project-URL: Repository, https://github.com/dceoy/mt5cli.git
|
|
6
6
|
Author-email: dceoy <dceoy@users.noreply.github.com>
|
|
@@ -161,8 +161,23 @@ update_history_with_config(
|
|
|
161
161
|
- **Rate view loading**: use `load_rate_data()` / `load_rate_data_from_connection()` to load a SQLite rate table or view into a `DatetimeIndex` DataFrame.
|
|
162
162
|
- **Multi-series rate loading**: use `build_rate_targets()` to build neutral `RateTarget(symbol, timeframe)` pairs, `resolve_rate_tables()` to map them to table/view names (pass `require_existing=True` for strict resolution), and `load_rate_series_from_sqlite()` to load them into a mapping keyed by `(symbol, integer timeframe)`. The loader requires existing managed views unless `explicit_tables` is supplied, and rejects duplicate `(symbol, timeframe)` targets.
|
|
163
163
|
- **Multi-account latest rates**: use `collect_latest_rates_for_accounts()` with `AccountSpec` to read the latest bars for several account groups, merged into a `(symbol, integer timeframe)` mapping. For long-running pollers, `collect_latest_rates_for_accounts_with_retries()` adds bounded exponential backoff that retries only `pdmt5.Mt5TradingError` / `pdmt5.Mt5RuntimeError` and re-raises once `retry_count` is exhausted.
|
|
164
|
+
- **Latest closed bars**: use `collect_latest_closed_rates_for_accounts()` when downstream logic must exclude the still-forming current bar. It fetches `count + 1` bars at `start_pos=0`, drops the last row with `drop_forming_rate_bar()`, and validates each series is non-empty. `collect_latest_closed_rates_by_granularity()` returns the same data keyed by `(symbol, granularity_name)` such as `("EURUSD", "M1")`.
|
|
165
|
+
|
|
166
|
+
```python
|
|
167
|
+
from mt5cli import AccountSpec, collect_latest_closed_rates_by_granularity
|
|
168
|
+
|
|
169
|
+
rates = collect_latest_closed_rates_by_granularity(
|
|
170
|
+
[AccountSpec(symbols=["EURUSD", "GBPUSD"], login=12345)],
|
|
171
|
+
["M1", "H1"],
|
|
172
|
+
count=500,
|
|
173
|
+
retry_count=3,
|
|
174
|
+
)
|
|
175
|
+
eurusd_m1 = rates["EURUSD", "M1"] # closed bars only
|
|
176
|
+
```
|
|
177
|
+
|
|
164
178
|
- **Credential resolution**: use `resolve_account_spec()` / `resolve_account_specs()` to merge explicit override values over `AccountSpec` fields and expand `${ENV_VAR}` placeholders (via `substitute_env_placeholders()`), raising `ValueError` for missing variables. This keeps secrets out of plan/config files without coupling to any strategy code.
|
|
165
|
-
- **Throttled history updates**: use `ThrottledHistoryUpdater` to wrap `update_history()` with a minimum `interval_seconds` between successful runs (monotonic clock). Call `should_update()` / `update(client, symbols)` from an application loop; errors propagate by default, or pass `suppress_errors=True` to swallow recoverable `Mt5*Error
|
|
179
|
+
- **Throttled history updates**: use `ThrottledHistoryUpdater` to wrap `update_history()` with a minimum `interval_seconds` between successful runs (monotonic clock). Call `should_update()` / `update(client, symbols)` from an application loop; errors propagate by default, or pass `suppress_errors=True` to swallow recoverable `Mt5*Error`, `sqlite3.Error`, `ValueError`, `OSError`, and MT5 client capability errors for history API methods without advancing the throttle (other `AttributeError` / `TypeError` values always propagate).
|
|
180
|
+
- **Trading session helpers**: use `mt5_trading_session()` for a trading-capable `pdmt5.Mt5TradingClient` that initializes/logs in via `Mt5Config.path` and always shuts down safely. Pair with `detect_position_side()`, `calculate_margin_and_volume()`, and `determine_order_limits()` for generic position and sizing utilities. The read-only `mt5_session()` / `Mt5CliClient` SDK is unchanged.
|
|
166
181
|
- **Granularity-keyed rate loading**: `load_rate_series_by_granularity()` builds targets with `build_rate_targets()`, loads them with `load_rate_series_from_sqlite()`, and returns a mapping keyed by `(symbol | None, granularity_name)` such as `("EURUSD", "M1")` to reduce downstream boilerplate.
|
|
167
182
|
- **MT5 session helper**: use the `mt5_session()` context manager to attach to (or, when `Mt5Config.path` is set, launch) an MT5 terminal, log in, and yield a connected `Mt5CliClient` that shuts down on exit.
|
|
168
183
|
- **SQLite export helpers**: use `export_dataframe_to_sqlite()` for append mode, optional index export, and post-write deduplication by key columns.
|
|
@@ -174,6 +189,63 @@ update_history_with_config(
|
|
|
174
189
|
- Windows OS (MetaTrader 5 requirement)
|
|
175
190
|
- MetaTrader 5 platform installed
|
|
176
191
|
|
|
192
|
+
### Migration note for mteor
|
|
193
|
+
|
|
194
|
+
Replace local MT5 lifecycle and trading helper code with mt5cli imports:
|
|
195
|
+
|
|
196
|
+
```python
|
|
197
|
+
# Before (local mteor helpers)
|
|
198
|
+
# with local_mt5_trading_session(config) as client:
|
|
199
|
+
# side = local_detect_position_side(client, symbol)
|
|
200
|
+
# sizing = local_calculate_margin_and_volume(client, symbol, unit_ratio, preserved_ratio)
|
|
201
|
+
# limits = local_determine_order_limits(client, symbol, side, sl_ratio, tp_ratio)
|
|
202
|
+
|
|
203
|
+
# After (mt5cli shared layer)
|
|
204
|
+
from pdmt5 import Mt5Config
|
|
205
|
+
from mt5cli import (
|
|
206
|
+
calculate_margin_and_volume,
|
|
207
|
+
detect_position_side,
|
|
208
|
+
determine_order_limits,
|
|
209
|
+
mt5_trading_session,
|
|
210
|
+
)
|
|
211
|
+
|
|
212
|
+
with mt5_trading_session(
|
|
213
|
+
Mt5Config(path=terminal_path, login=login), retry_count=2
|
|
214
|
+
) as client:
|
|
215
|
+
side = detect_position_side(client, symbol)
|
|
216
|
+
sizing = calculate_margin_and_volume(
|
|
217
|
+
client, symbol, unit_margin_ratio=0.5, preserved_margin_ratio=0.2
|
|
218
|
+
)
|
|
219
|
+
if side is not None:
|
|
220
|
+
limits = determine_order_limits(
|
|
221
|
+
client,
|
|
222
|
+
symbol,
|
|
223
|
+
side,
|
|
224
|
+
stop_loss_limit_ratio=0.01,
|
|
225
|
+
take_profit_limit_ratio=0.02,
|
|
226
|
+
)
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
Throttled history updates use a separate read-only session:
|
|
230
|
+
|
|
231
|
+
```python
|
|
232
|
+
from pdmt5 import Mt5Config, Mt5DataClient
|
|
233
|
+
|
|
234
|
+
from mt5cli import ThrottledHistoryUpdater
|
|
235
|
+
|
|
236
|
+
updater = ThrottledHistoryUpdater(
|
|
237
|
+
output="history.db", interval_seconds=60, suppress_errors=True
|
|
238
|
+
)
|
|
239
|
+
client = Mt5DataClient(config=Mt5Config(login=login))
|
|
240
|
+
client.initialize_and_login_mt5()
|
|
241
|
+
try:
|
|
242
|
+
updater.update(client, ["EURUSD"])
|
|
243
|
+
finally:
|
|
244
|
+
client.shutdown()
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
Read-only collectors can keep using `mt5_session()` and `Mt5CliClient` without changes.
|
|
248
|
+
|
|
177
249
|
## Development
|
|
178
250
|
|
|
179
251
|
```bash
|
|
@@ -137,8 +137,23 @@ update_history_with_config(
|
|
|
137
137
|
- **Rate view loading**: use `load_rate_data()` / `load_rate_data_from_connection()` to load a SQLite rate table or view into a `DatetimeIndex` DataFrame.
|
|
138
138
|
- **Multi-series rate loading**: use `build_rate_targets()` to build neutral `RateTarget(symbol, timeframe)` pairs, `resolve_rate_tables()` to map them to table/view names (pass `require_existing=True` for strict resolution), and `load_rate_series_from_sqlite()` to load them into a mapping keyed by `(symbol, integer timeframe)`. The loader requires existing managed views unless `explicit_tables` is supplied, and rejects duplicate `(symbol, timeframe)` targets.
|
|
139
139
|
- **Multi-account latest rates**: use `collect_latest_rates_for_accounts()` with `AccountSpec` to read the latest bars for several account groups, merged into a `(symbol, integer timeframe)` mapping. For long-running pollers, `collect_latest_rates_for_accounts_with_retries()` adds bounded exponential backoff that retries only `pdmt5.Mt5TradingError` / `pdmt5.Mt5RuntimeError` and re-raises once `retry_count` is exhausted.
|
|
140
|
+
- **Latest closed bars**: use `collect_latest_closed_rates_for_accounts()` when downstream logic must exclude the still-forming current bar. It fetches `count + 1` bars at `start_pos=0`, drops the last row with `drop_forming_rate_bar()`, and validates each series is non-empty. `collect_latest_closed_rates_by_granularity()` returns the same data keyed by `(symbol, granularity_name)` such as `("EURUSD", "M1")`.
|
|
141
|
+
|
|
142
|
+
```python
|
|
143
|
+
from mt5cli import AccountSpec, collect_latest_closed_rates_by_granularity
|
|
144
|
+
|
|
145
|
+
rates = collect_latest_closed_rates_by_granularity(
|
|
146
|
+
[AccountSpec(symbols=["EURUSD", "GBPUSD"], login=12345)],
|
|
147
|
+
["M1", "H1"],
|
|
148
|
+
count=500,
|
|
149
|
+
retry_count=3,
|
|
150
|
+
)
|
|
151
|
+
eurusd_m1 = rates["EURUSD", "M1"] # closed bars only
|
|
152
|
+
```
|
|
153
|
+
|
|
140
154
|
- **Credential resolution**: use `resolve_account_spec()` / `resolve_account_specs()` to merge explicit override values over `AccountSpec` fields and expand `${ENV_VAR}` placeholders (via `substitute_env_placeholders()`), raising `ValueError` for missing variables. This keeps secrets out of plan/config files without coupling to any strategy code.
|
|
141
|
-
- **Throttled history updates**: use `ThrottledHistoryUpdater` to wrap `update_history()` with a minimum `interval_seconds` between successful runs (monotonic clock). Call `should_update()` / `update(client, symbols)` from an application loop; errors propagate by default, or pass `suppress_errors=True` to swallow recoverable `Mt5*Error
|
|
155
|
+
- **Throttled history updates**: use `ThrottledHistoryUpdater` to wrap `update_history()` with a minimum `interval_seconds` between successful runs (monotonic clock). Call `should_update()` / `update(client, symbols)` from an application loop; errors propagate by default, or pass `suppress_errors=True` to swallow recoverable `Mt5*Error`, `sqlite3.Error`, `ValueError`, `OSError`, and MT5 client capability errors for history API methods without advancing the throttle (other `AttributeError` / `TypeError` values always propagate).
|
|
156
|
+
- **Trading session helpers**: use `mt5_trading_session()` for a trading-capable `pdmt5.Mt5TradingClient` that initializes/logs in via `Mt5Config.path` and always shuts down safely. Pair with `detect_position_side()`, `calculate_margin_and_volume()`, and `determine_order_limits()` for generic position and sizing utilities. The read-only `mt5_session()` / `Mt5CliClient` SDK is unchanged.
|
|
142
157
|
- **Granularity-keyed rate loading**: `load_rate_series_by_granularity()` builds targets with `build_rate_targets()`, loads them with `load_rate_series_from_sqlite()`, and returns a mapping keyed by `(symbol | None, granularity_name)` such as `("EURUSD", "M1")` to reduce downstream boilerplate.
|
|
143
158
|
- **MT5 session helper**: use the `mt5_session()` context manager to attach to (or, when `Mt5Config.path` is set, launch) an MT5 terminal, log in, and yield a connected `Mt5CliClient` that shuts down on exit.
|
|
144
159
|
- **SQLite export helpers**: use `export_dataframe_to_sqlite()` for append mode, optional index export, and post-write deduplication by key columns.
|
|
@@ -150,6 +165,63 @@ update_history_with_config(
|
|
|
150
165
|
- Windows OS (MetaTrader 5 requirement)
|
|
151
166
|
- MetaTrader 5 platform installed
|
|
152
167
|
|
|
168
|
+
### Migration note for mteor
|
|
169
|
+
|
|
170
|
+
Replace local MT5 lifecycle and trading helper code with mt5cli imports:
|
|
171
|
+
|
|
172
|
+
```python
|
|
173
|
+
# Before (local mteor helpers)
|
|
174
|
+
# with local_mt5_trading_session(config) as client:
|
|
175
|
+
# side = local_detect_position_side(client, symbol)
|
|
176
|
+
# sizing = local_calculate_margin_and_volume(client, symbol, unit_ratio, preserved_ratio)
|
|
177
|
+
# limits = local_determine_order_limits(client, symbol, side, sl_ratio, tp_ratio)
|
|
178
|
+
|
|
179
|
+
# After (mt5cli shared layer)
|
|
180
|
+
from pdmt5 import Mt5Config
|
|
181
|
+
from mt5cli import (
|
|
182
|
+
calculate_margin_and_volume,
|
|
183
|
+
detect_position_side,
|
|
184
|
+
determine_order_limits,
|
|
185
|
+
mt5_trading_session,
|
|
186
|
+
)
|
|
187
|
+
|
|
188
|
+
with mt5_trading_session(
|
|
189
|
+
Mt5Config(path=terminal_path, login=login), retry_count=2
|
|
190
|
+
) as client:
|
|
191
|
+
side = detect_position_side(client, symbol)
|
|
192
|
+
sizing = calculate_margin_and_volume(
|
|
193
|
+
client, symbol, unit_margin_ratio=0.5, preserved_margin_ratio=0.2
|
|
194
|
+
)
|
|
195
|
+
if side is not None:
|
|
196
|
+
limits = determine_order_limits(
|
|
197
|
+
client,
|
|
198
|
+
symbol,
|
|
199
|
+
side,
|
|
200
|
+
stop_loss_limit_ratio=0.01,
|
|
201
|
+
take_profit_limit_ratio=0.02,
|
|
202
|
+
)
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
Throttled history updates use a separate read-only session:
|
|
206
|
+
|
|
207
|
+
```python
|
|
208
|
+
from pdmt5 import Mt5Config, Mt5DataClient
|
|
209
|
+
|
|
210
|
+
from mt5cli import ThrottledHistoryUpdater
|
|
211
|
+
|
|
212
|
+
updater = ThrottledHistoryUpdater(
|
|
213
|
+
output="history.db", interval_seconds=60, suppress_errors=True
|
|
214
|
+
)
|
|
215
|
+
client = Mt5DataClient(config=Mt5Config(login=login))
|
|
216
|
+
client.initialize_and_login_mt5()
|
|
217
|
+
try:
|
|
218
|
+
updater.update(client, ["EURUSD"])
|
|
219
|
+
finally:
|
|
220
|
+
client.shutdown()
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
Read-only collectors can keep using `mt5_session()` and `Mt5CliClient` without changes.
|
|
224
|
+
|
|
153
225
|
## Development
|
|
154
226
|
|
|
155
227
|
```bash
|
|
@@ -18,6 +18,10 @@ Utility module providing constants, enums, Click parameter types, and helper fun
|
|
|
18
18
|
|
|
19
19
|
Programmatic SDK for read-only MetaTrader 5 data collection. Returns pandas DataFrames and provides `collect_history` for SQLite bulk collection.
|
|
20
20
|
|
|
21
|
+
### [Trading](trading.md)
|
|
22
|
+
|
|
23
|
+
Trading-capable session management and operational helpers built on `pdmt5.Mt5TradingClient`. Complements the read-only SDK without changing existing `Mt5CliClient` behavior.
|
|
24
|
+
|
|
21
25
|
### [History Collection (SQLite)](history.md)
|
|
22
26
|
|
|
23
27
|
SQLite storage helpers for the `collect-history` command schema, incremental updates, deduplication, indexes, and optional views.
|
|
@@ -28,8 +32,9 @@ The package follows a simple architecture built on top of pdmt5:
|
|
|
28
32
|
|
|
29
33
|
1. **CLI Layer** (`cli.py`): Typer application with subcommands that delegate to the SDK and export results.
|
|
30
34
|
2. **SDK Layer** (`sdk.py`): Read-only data access functions, `Mt5CliClient`, and `collect_history` orchestration.
|
|
31
|
-
3. **
|
|
32
|
-
4. **
|
|
35
|
+
3. **Trading Layer** (`trading.py`): Trading-capable sessions and operational helpers on `Mt5TradingClient`.
|
|
36
|
+
4. **Utils Layer** (`utils.py`): Constants, enums, custom Click parameter types, parsing helpers, and format detection/export utilities.
|
|
37
|
+
5. **Data Layer** (via `pdmt5`): Uses `Mt5DataClient`, `Mt5TradingClient`, and `Mt5Config` from the pdmt5 package for MetaTrader 5 access.
|
|
33
38
|
|
|
34
39
|
## Usage Guidelines
|
|
35
40
|
|
|
@@ -28,6 +28,26 @@ rates = collect_latest_rates_for_accounts_with_retries(
|
|
|
28
28
|
)
|
|
29
29
|
```
|
|
30
30
|
|
|
31
|
+
### Latest closed rate bars
|
|
32
|
+
|
|
33
|
+
MetaTrader 5 `start_pos=0` includes the still-forming current bar as the last
|
|
34
|
+
row. `collect_latest_closed_rates_for_accounts()` fetches `count + 1` bars,
|
|
35
|
+
drops that row with `drop_forming_rate_bar()`, and validates each series is
|
|
36
|
+
non-empty. Use `collect_latest_closed_rates_by_granularity()` when callers
|
|
37
|
+
prefer keys such as `("EURUSD", "M1")` instead of integer timeframes.
|
|
38
|
+
|
|
39
|
+
```python
|
|
40
|
+
from mt5cli import AccountSpec, collect_latest_closed_rates_by_granularity
|
|
41
|
+
|
|
42
|
+
rates = collect_latest_closed_rates_by_granularity(
|
|
43
|
+
[AccountSpec(symbols=["EURUSD"], login=12345)],
|
|
44
|
+
["M1", "H1"],
|
|
45
|
+
count=500,
|
|
46
|
+
retry_count=3,
|
|
47
|
+
)
|
|
48
|
+
closed_m1 = rates["EURUSD", "M1"]
|
|
49
|
+
```
|
|
50
|
+
|
|
31
51
|
### Resolving credentials and `${ENV_VAR}` placeholders
|
|
32
52
|
|
|
33
53
|
`resolve_account_spec()` / `resolve_account_specs()` merge explicit override
|
|
@@ -78,6 +98,17 @@ finally:
|
|
|
78
98
|
client.shutdown()
|
|
79
99
|
```
|
|
80
100
|
|
|
81
|
-
By default `Mt5TradingError`, `Mt5RuntimeError`,
|
|
82
|
-
|
|
83
|
-
|
|
101
|
+
By default recoverable errors (`Mt5TradingError`, `Mt5RuntimeError`,
|
|
102
|
+
`sqlite3.Error`, `ValueError`, `OSError`, and MT5 client capability
|
|
103
|
+
`AttributeError` / `TypeError` for history API methods) propagate so the caller
|
|
104
|
+
controls logging; pass `suppress_errors=True` to swallow them and return
|
|
105
|
+
`False` without advancing the throttle. Other `AttributeError` / `TypeError`
|
|
106
|
+
values always propagate. Input validation (`_resolve_update_history_request`)
|
|
107
|
+
runs before any MT5 or SQLite calls, but when `suppress_errors=True` the
|
|
108
|
+
resulting `ValueError` is suppressed along with other recoverable errors.
|
|
109
|
+
|
|
110
|
+
## Trading-capable sessions
|
|
111
|
+
|
|
112
|
+
For order placement and trading calculations, use the dedicated
|
|
113
|
+
[Trading module](trading.md). The read-only `Mt5CliClient` and `mt5_session()`
|
|
114
|
+
helpers in this module are unchanged.
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
# Trading Module
|
|
2
|
+
|
|
3
|
+
::: mt5cli.trading
|
|
4
|
+
|
|
5
|
+
## Trading-capable MT5 sessions
|
|
6
|
+
|
|
7
|
+
`mt5_trading_session()` complements the read-only `mt5_session()` helper in
|
|
8
|
+
`sdk.py`. It yields a connected `pdmt5.Mt5TradingClient`, uses
|
|
9
|
+
`Mt5Config.path` to launch the terminal when configured, and always calls
|
|
10
|
+
`shutdown()` on exit.
|
|
11
|
+
|
|
12
|
+
```python
|
|
13
|
+
from pdmt5 import Mt5Config
|
|
14
|
+
|
|
15
|
+
from mt5cli import mt5_trading_session
|
|
16
|
+
|
|
17
|
+
with mt5_trading_session(
|
|
18
|
+
Mt5Config(path=r"C:\Program Files\MetaTrader 5\terminal64.exe", login=12345),
|
|
19
|
+
retry_count=2,
|
|
20
|
+
) as client:
|
|
21
|
+
positions = client.positions_get_as_df(symbol="EURUSD")
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
The read-only `Mt5CliClient` / `mt5_session()` API is unchanged.
|
|
25
|
+
|
|
26
|
+
## Operational trading helpers
|
|
27
|
+
|
|
28
|
+
These helpers are strategy-agnostic and do not depend on signal detection,
|
|
29
|
+
betting logic, or scheduling code in downstream applications.
|
|
30
|
+
|
|
31
|
+
```python
|
|
32
|
+
from mt5cli import (
|
|
33
|
+
calculate_margin_and_volume,
|
|
34
|
+
detect_position_side,
|
|
35
|
+
determine_order_limits,
|
|
36
|
+
)
|
|
37
|
+
|
|
38
|
+
side = detect_position_side(client, "EURUSD")
|
|
39
|
+
sizing = calculate_margin_and_volume(
|
|
40
|
+
client,
|
|
41
|
+
"EURUSD",
|
|
42
|
+
unit_margin_ratio=0.5,
|
|
43
|
+
preserved_margin_ratio=0.2,
|
|
44
|
+
)
|
|
45
|
+
limits = determine_order_limits(
|
|
46
|
+
client,
|
|
47
|
+
"EURUSD",
|
|
48
|
+
side="long",
|
|
49
|
+
stop_loss_limit_ratio=0.01,
|
|
50
|
+
take_profit_limit_ratio=0.02,
|
|
51
|
+
)
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
Protective ratios must satisfy `0 <= ratio < 1`; `0` omits that level.
|
|
55
|
+
`calculate_margin_and_volume()` clamps negative `margin_free` to `0.0`
|
|
56
|
+
before sizing.
|
|
57
|
+
|
|
58
|
+
## Migration from mteor-local helpers
|
|
59
|
+
|
|
60
|
+
| mteor-local concern | mt5cli replacement |
|
|
61
|
+
| -------------------------------------------------------- | ----------------------------------------------- |
|
|
62
|
+
| Manual terminal spawn/kill around trading code | `mt5_trading_session()` |
|
|
63
|
+
| Local position-side detection | `detect_position_side()` |
|
|
64
|
+
| Local margin/volume sizing | `calculate_margin_and_volume()` |
|
|
65
|
+
| Local SL/TP price derivation | `determine_order_limits()` |
|
|
66
|
+
| Throttled SQLite history loop with ad-hoc error handling | `ThrottledHistoryUpdater(suppress_errors=True)` |
|
|
67
|
+
|
|
68
|
+
Keep read-only data collection on `mt5_session()` / `Mt5CliClient`; use
|
|
69
|
+
`mt5_trading_session()` only where order placement or trading calculations are
|
|
70
|
+
required.
|
|
@@ -6,6 +6,7 @@ from .history import (
|
|
|
6
6
|
RateTarget,
|
|
7
7
|
build_rate_targets,
|
|
8
8
|
build_rate_view_name,
|
|
9
|
+
drop_forming_rate_bar,
|
|
9
10
|
load_rate_data,
|
|
10
11
|
load_rate_data_from_connection,
|
|
11
12
|
load_rate_series_by_granularity,
|
|
@@ -24,6 +25,8 @@ from .sdk import (
|
|
|
24
25
|
account_info,
|
|
25
26
|
build_config,
|
|
26
27
|
collect_history,
|
|
28
|
+
collect_latest_closed_rates_by_granularity,
|
|
29
|
+
collect_latest_closed_rates_for_accounts,
|
|
27
30
|
collect_latest_rates,
|
|
28
31
|
collect_latest_rates_for_accounts,
|
|
29
32
|
collect_latest_rates_for_accounts_with_retries,
|
|
@@ -58,6 +61,12 @@ from .sdk import (
|
|
|
58
61
|
from .sdk import (
|
|
59
62
|
version as mt5_version,
|
|
60
63
|
)
|
|
64
|
+
from .trading import (
|
|
65
|
+
calculate_margin_and_volume,
|
|
66
|
+
detect_position_side,
|
|
67
|
+
determine_order_limits,
|
|
68
|
+
mt5_trading_session,
|
|
69
|
+
)
|
|
61
70
|
from .utils import (
|
|
62
71
|
TICK_FLAG_MAP,
|
|
63
72
|
TIMEFRAME_MAP,
|
|
@@ -86,7 +95,10 @@ __all__ = [
|
|
|
86
95
|
"build_config",
|
|
87
96
|
"build_rate_targets",
|
|
88
97
|
"build_rate_view_name",
|
|
98
|
+
"calculate_margin_and_volume",
|
|
89
99
|
"collect_history",
|
|
100
|
+
"collect_latest_closed_rates_by_granularity",
|
|
101
|
+
"collect_latest_closed_rates_for_accounts",
|
|
90
102
|
"collect_latest_rates",
|
|
91
103
|
"collect_latest_rates_for_accounts",
|
|
92
104
|
"collect_latest_rates_for_accounts_with_retries",
|
|
@@ -96,6 +108,9 @@ __all__ = [
|
|
|
96
108
|
"copy_ticks_from",
|
|
97
109
|
"copy_ticks_range",
|
|
98
110
|
"detect_format",
|
|
111
|
+
"detect_position_side",
|
|
112
|
+
"determine_order_limits",
|
|
113
|
+
"drop_forming_rate_bar",
|
|
99
114
|
"export_dataframe",
|
|
100
115
|
"export_dataframe_to_sqlite",
|
|
101
116
|
"history_deals",
|
|
@@ -111,6 +126,7 @@ __all__ = [
|
|
|
111
126
|
"mt5_session",
|
|
112
127
|
"mt5_summary",
|
|
113
128
|
"mt5_summary_as_df",
|
|
129
|
+
"mt5_trading_session",
|
|
114
130
|
"mt5_version",
|
|
115
131
|
"orders",
|
|
116
132
|
"parse_datetime",
|
|
@@ -106,6 +106,23 @@ def resolve_granularity_name(timeframe: int) -> str:
|
|
|
106
106
|
return str(timeframe)
|
|
107
107
|
|
|
108
108
|
|
|
109
|
+
def drop_forming_rate_bar(df_rate: pd.DataFrame) -> pd.DataFrame:
|
|
110
|
+
"""Return closed bars from chronologically ordered MT5 rate data.
|
|
111
|
+
|
|
112
|
+
MetaTrader 5 ``copy_rates_from_pos(start_pos=0)`` includes the still-forming
|
|
113
|
+
current bar as the last row. Slice it off so downstream logic only sees
|
|
114
|
+
completed bars. Empty frames and single-row frames return empty results.
|
|
115
|
+
|
|
116
|
+
Args:
|
|
117
|
+
df_rate: Rate data ordered oldest-to-newest with the forming bar last.
|
|
118
|
+
|
|
119
|
+
Returns:
|
|
120
|
+
A new DataFrame with all rows except the last. Index and columns are
|
|
121
|
+
preserved. The input frame is not modified.
|
|
122
|
+
"""
|
|
123
|
+
return df_rate.iloc[:-1].copy()
|
|
124
|
+
|
|
125
|
+
|
|
109
126
|
def build_rate_view_name(
|
|
110
127
|
*,
|
|
111
128
|
symbol: str,
|