mt5cli 1.1.2__tar.gz → 1.1.3__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (69) hide show
  1. {mt5cli-1.1.2 → mt5cli-1.1.3}/.agents/skills/mt5cli/SKILL.md +6 -5
  2. {mt5cli-1.1.2 → mt5cli-1.1.3}/PKG-INFO +50 -35
  3. {mt5cli-1.1.2 → mt5cli-1.1.3}/README.md +48 -33
  4. {mt5cli-1.1.2 → mt5cli-1.1.3}/docs/api/public-contract.md +13 -1
  5. {mt5cli-1.1.2 → mt5cli-1.1.3}/docs/index.md +10 -7
  6. {mt5cli-1.1.2 → mt5cli-1.1.3}/mt5cli/__init__.py +4 -0
  7. {mt5cli-1.1.2 → mt5cli-1.1.3}/mt5cli/cli.py +166 -13
  8. {mt5cli-1.1.2 → mt5cli-1.1.3}/mt5cli/contract.py +2 -0
  9. {mt5cli-1.1.2 → mt5cli-1.1.3}/mt5cli/history.py +166 -2
  10. {mt5cli-1.1.2 → mt5cli-1.1.3}/mt5cli/sdk.py +2 -0
  11. {mt5cli-1.1.2 → mt5cli-1.1.3}/mt5cli/trading.py +119 -3
  12. {mt5cli-1.1.2 → mt5cli-1.1.3}/pyproject.toml +2 -2
  13. {mt5cli-1.1.2 → mt5cli-1.1.3}/tests/test_cli.py +289 -10
  14. {mt5cli-1.1.2 → mt5cli-1.1.3}/tests/test_history.py +294 -0
  15. {mt5cli-1.1.2 → mt5cli-1.1.3}/tests/test_trading.py +247 -0
  16. {mt5cli-1.1.2 → mt5cli-1.1.3}/uv.lock +6 -6
  17. {mt5cli-1.1.2 → mt5cli-1.1.3}/.agents/skills/local-qa/SKILL.md +0 -0
  18. {mt5cli-1.1.2 → mt5cli-1.1.3}/.agents/skills/local-qa/scripts/qa.sh +0 -0
  19. {mt5cli-1.1.2 → mt5cli-1.1.3}/.agents/skills/pr-feedback-triage/SKILL.md +0 -0
  20. {mt5cli-1.1.2 → mt5cli-1.1.3}/.claude/agents/codex.md +0 -0
  21. {mt5cli-1.1.2 → mt5cli-1.1.3}/.claude/settings.json +0 -0
  22. {mt5cli-1.1.2 → mt5cli-1.1.3}/.github/FUNDING.yml +0 -0
  23. {mt5cli-1.1.2 → mt5cli-1.1.3}/.github/dependabot.yml +0 -0
  24. {mt5cli-1.1.2 → mt5cli-1.1.3}/.github/renovate.json +0 -0
  25. {mt5cli-1.1.2 → mt5cli-1.1.3}/.github/workflows/ci.yml +0 -0
  26. {mt5cli-1.1.2 → mt5cli-1.1.3}/.github/workflows/claude.yml +0 -0
  27. {mt5cli-1.1.2 → mt5cli-1.1.3}/.github/workflows/opencode.yml +0 -0
  28. {mt5cli-1.1.2 → mt5cli-1.1.3}/.github/workflows/release.yml +0 -0
  29. {mt5cli-1.1.2 → mt5cli-1.1.3}/.gitignore +0 -0
  30. {mt5cli-1.1.2 → mt5cli-1.1.3}/AGENTS.md +0 -0
  31. {mt5cli-1.1.2 → mt5cli-1.1.3}/CLAUDE.md +0 -0
  32. {mt5cli-1.1.2 → mt5cli-1.1.3}/LICENSE +0 -0
  33. {mt5cli-1.1.2 → mt5cli-1.1.3}/docs/api/cli.md +0 -0
  34. {mt5cli-1.1.2 → mt5cli-1.1.3}/docs/api/client.md +0 -0
  35. {mt5cli-1.1.2 → mt5cli-1.1.3}/docs/api/converters.md +0 -0
  36. {mt5cli-1.1.2 → mt5cli-1.1.3}/docs/api/exceptions.md +0 -0
  37. {mt5cli-1.1.2 → mt5cli-1.1.3}/docs/api/grafana.md +0 -0
  38. {mt5cli-1.1.2 → mt5cli-1.1.3}/docs/api/history.md +0 -0
  39. {mt5cli-1.1.2 → mt5cli-1.1.3}/docs/api/index.md +0 -0
  40. {mt5cli-1.1.2 → mt5cli-1.1.3}/docs/api/schemas.md +0 -0
  41. {mt5cli-1.1.2 → mt5cli-1.1.3}/docs/api/sdk.md +0 -0
  42. {mt5cli-1.1.2 → mt5cli-1.1.3}/docs/api/telemetry.md +0 -0
  43. {mt5cli-1.1.2 → mt5cli-1.1.3}/docs/api/trading.md +0 -0
  44. {mt5cli-1.1.2 → mt5cli-1.1.3}/docs/api/utils.md +0 -0
  45. {mt5cli-1.1.2 → mt5cli-1.1.3}/examples/grafana/README.md +0 -0
  46. {mt5cli-1.1.2 → mt5cli-1.1.3}/examples/grafana/compose.yml +0 -0
  47. {mt5cli-1.1.2 → mt5cli-1.1.3}/examples/grafana/dashboards/mt5cli-market.json +0 -0
  48. {mt5cli-1.1.2 → mt5cli-1.1.3}/examples/grafana/dashboards/mt5cli-overview.json +0 -0
  49. {mt5cli-1.1.2 → mt5cli-1.1.3}/examples/grafana/dashboards/mt5cli-trades.json +0 -0
  50. {mt5cli-1.1.2 → mt5cli-1.1.3}/examples/grafana/provisioning/dashboards/mt5cli.yml +0 -0
  51. {mt5cli-1.1.2 → mt5cli-1.1.3}/examples/grafana/provisioning/datasources/mt5cli-sqlite.yml +0 -0
  52. {mt5cli-1.1.2 → mt5cli-1.1.3}/mkdocs.yml +0 -0
  53. {mt5cli-1.1.2 → mt5cli-1.1.3}/mt5cli/__main__.py +0 -0
  54. {mt5cli-1.1.2 → mt5cli-1.1.3}/mt5cli/client.py +0 -0
  55. {mt5cli-1.1.2 → mt5cli-1.1.3}/mt5cli/converters.py +0 -0
  56. {mt5cli-1.1.2 → mt5cli-1.1.3}/mt5cli/exceptions.py +0 -0
  57. {mt5cli-1.1.2 → mt5cli-1.1.3}/mt5cli/grafana.py +0 -0
  58. {mt5cli-1.1.2 → mt5cli-1.1.3}/mt5cli/retry.py +0 -0
  59. {mt5cli-1.1.2 → mt5cli-1.1.3}/mt5cli/schemas.py +0 -0
  60. {mt5cli-1.1.2 → mt5cli-1.1.3}/mt5cli/telemetry.py +0 -0
  61. {mt5cli-1.1.2 → mt5cli-1.1.3}/mt5cli/utils.py +0 -0
  62. {mt5cli-1.1.2 → mt5cli-1.1.3}/tests/__init__.py +0 -0
  63. {mt5cli-1.1.2 → mt5cli-1.1.3}/tests/conftest.py +0 -0
  64. {mt5cli-1.1.2 → mt5cli-1.1.3}/tests/test_contracts.py +0 -0
  65. {mt5cli-1.1.2 → mt5cli-1.1.3}/tests/test_examples.py +0 -0
  66. {mt5cli-1.1.2 → mt5cli-1.1.3}/tests/test_grafana.py +0 -0
  67. {mt5cli-1.1.2 → mt5cli-1.1.3}/tests/test_sdk.py +0 -0
  68. {mt5cli-1.1.2 → mt5cli-1.1.3}/tests/test_telemetry.py +0 -0
  69. {mt5cli-1.1.2 → mt5cli-1.1.3}/tests/test_utils.py +0 -0
@@ -32,10 +32,10 @@ Global options MUST precede the subcommand.
32
32
  | `-o, --output PATH` | Output file path (required). |
33
33
  | `-f, --format FORMAT` | `csv`, `json`, `parquet`, or `sqlite3` (auto from extension). |
34
34
  | `--table NAME` | Table name for SQLite3 output (default: `data`). |
35
- | `--login INT` | MT5 trading account login. |
36
- | `--password TEXT` | MT5 trading account password. |
37
- | `--server TEXT` | MT5 trading server name. |
38
- | `--path TEXT` | Path to MetaTrader 5 terminal EXE. |
35
+ | `--login INT` | MT5 trading account login (`MT5_LOGIN`). |
36
+ | `--password TEXT` | MT5 trading account password (`MT5_PASSWORD`). |
37
+ | `--server TEXT` | MT5 trading server name (`MT5_SERVER`). |
38
+ | `--path TEXT` | Path to MetaTrader 5 terminal EXE (`MT5_PATH`). |
39
39
  | `--timeout INT` | Connection timeout in milliseconds. |
40
40
  | `--log-level LEVEL` | `DEBUG`, `INFO`, `WARNING` (default), `ERROR`. |
41
41
 
@@ -106,7 +106,8 @@ mt5cli -o history.db collect-history \
106
106
  local MT5 terminal is already logged in.
107
107
  - Avoid passing `--password` on the command line in shared or logged
108
108
  environments — it is visible in `ps`, shell history, and CI logs. Prefer
109
- logging in through the MT5 terminal first, then omit credentials here.
109
+ `MT5_PASSWORD`/`MT5_LOGIN`/`MT5_SERVER`/`MT5_PATH` environment variables or a
110
+ pre-authenticated local terminal session.
110
111
  - Reach for `--log-level DEBUG` when a command fails silently — MT5
111
112
  connection errors surface there.
112
113
  - If the user asks to run from source in this repo, prefix with `uv run`
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: mt5cli
3
- Version: 1.1.2
3
+ Version: 1.1.3
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>
@@ -18,7 +18,7 @@ Classifier: Topic :: Office/Business :: Financial :: Investment
18
18
  Requires-Python: <3.14,>=3.11
19
19
  Requires-Dist: click>=8.1.0
20
20
  Requires-Dist: pandas>=2.2.2
21
- Requires-Dist: pdmt5>=1.0.4
21
+ Requires-Dist: pdmt5>=1.1.0
22
22
  Requires-Dist: pydantic>=2.13.4
23
23
  Requires-Dist: typer>=0.15.0
24
24
  Provides-Extra: otel
@@ -175,9 +175,10 @@ mt5cli -o ticks.json ticks-from --symbol EURUSD \
175
175
  # Export symbols to SQLite3 with custom table name
176
176
  mt5cli -o data.db --table symbols symbols --group "*USD*"
177
177
 
178
- # Export with connection credentials
179
- mt5cli --login 12345 --password mypass --server MyBroker-Demo \
180
- -o positions.csv positions
178
+ # Export with connection credentials from env or placeholders
179
+ MT5_LOGIN=12345 MT5_PASSWORD=secret MT5_SERVER=MyBroker-Demo \
180
+ mt5cli -o positions.csv positions
181
+ MT5_PATH="/path/to/terminal64.exe" mt5cli -o positions.csv positions
181
182
  ```
182
183
 
183
184
  Run as a Python module:
@@ -188,40 +189,54 @@ python -m mt5cli -o account.csv account-info
188
189
 
189
190
  ## Commands
190
191
 
191
- | Command | Description |
192
- | ---------------------- | ------------------------------------------------------------------------------------------------------------------------------------------- |
193
- | `rates-from` | Export rates from a start date |
194
- | `rates-from-pos` | Export rates from a start position |
195
- | `latest-rates` | Export latest rates from a start position |
196
- | `rates-range` | Export rates for a date range |
197
- | `ticks-from` | Export ticks from a start date |
198
- | `ticks-range` | Export ticks for a date range |
199
- | `ticks-recent` | Export ticks from a recent trailing window |
200
- | `account-info` | Export account information |
201
- | `terminal-info` | Export terminal information |
202
- | `version` | Export MetaTrader 5 version information |
203
- | `last-error` | Export the last error information |
204
- | `symbols` | Export symbol list |
205
- | `symbol-info` | Export symbol details |
206
- | `symbol-info-tick` | Export the last tick for a symbol |
207
- | `minimum-margins` | Export minimum-volume buy and sell margin requirements |
208
- | `market-book` | Export market depth (order book) |
209
- | `orders` | Export active orders |
210
- | `positions` | Export open positions |
211
- | `history-orders` | Export historical orders |
212
- | `history-deals` | Export historical deals |
213
- | `recent-history-deals` | Export historical deals from a recent trailing window |
214
- | `mt5-summary` | Export terminal/account status summary |
215
- | `order-check` | Check funds sufficiency for a trade request |
216
- | `order-send` | Send a raw trade request to the trade server (`--yes` required; expert path) |
217
- | `close-positions` | Close open positions by `--symbol` or `--ticket` (`--yes` required for live; `--dry-run` available) |
218
- | `collect-history` | Collect rates, history-orders, and history-deals for one or more symbols into a single SQLite database (ticks opt-in via `--dataset ticks`) |
219
- | `grafana-schema` | Create or refresh Grafana-ready views and indexes in an existing SQLite database (idempotent, no MT5 connection) |
220
- | `snapshot` | Snapshot current account, position, order, and terminal state into SQLite for live Grafana dashboards |
192
+ | Command | Description |
193
+ | ---------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------- |
194
+ | `rates-from` | Export rates from a start date |
195
+ | `rates-from-pos` | Export rates from a start position |
196
+ | `latest-rates` | Export latest rates from a start position |
197
+ | `rates-range` | Export rates for a date range |
198
+ | `ticks-from` | Export ticks from a start date |
199
+ | `ticks-range` | Export ticks for a date range |
200
+ | `ticks-recent` | Export ticks from a recent trailing window |
201
+ | `account-info` | Export account information |
202
+ | `terminal-info` | Export terminal information |
203
+ | `version` | Export MetaTrader 5 version information |
204
+ | `last-error` | Export the last error information |
205
+ | `symbols` | Export symbol list |
206
+ | `symbol-info` | Export symbol details |
207
+ | `symbol-info-tick` | Export the last tick for a symbol |
208
+ | `minimum-margins` | Export minimum-volume buy and sell margin requirements |
209
+ | `market-book` | Export market depth (order book) |
210
+ | `orders` | Export active orders |
211
+ | `positions` | Export open positions |
212
+ | `history-orders` | Export historical orders |
213
+ | `history-deals` | Export historical deals |
214
+ | `recent-history-deals` | Export historical deals from a recent trailing window |
215
+ | `mt5-summary` | Export terminal/account status summary |
216
+ | `order-check` | Check funds sufficiency for a trade request |
217
+ | `order-send` | Send a raw trade request to the trade server (`--yes` required; expert path) |
218
+ | `close-positions` | Close open positions by `--symbol` or `--ticket` (`--yes` required for live; `--dry-run` available; optional `--deviation` / `--comment` / `--magic`) |
219
+ | `collect-history` | Collect rates, history-orders, and history-deals for one or more symbols into a single SQLite database (ticks opt-in via `--dataset ticks`) |
220
+ | `history-gaps` | Export a SQLite-only one-row-per-gap report from managed rate compatibility views without connecting to MT5 |
221
+ | `grafana-schema` | Create or refresh Grafana-ready views and indexes in an existing SQLite database (idempotent, no MT5 connection) |
222
+ | `snapshot` | Snapshot current account, position, order, and terminal state into SQLite for live Grafana dashboards |
221
223
 
222
224
  Use `order-check` to validate a request payload before running `order-send --yes`.
223
225
  `close-positions` is the safer high-level alternative that builds correct close
224
226
  requests automatically. At least one `--symbol` or `--ticket` must be provided.
227
+ CLI connection flags fall back to `MT5_LOGIN`, `MT5_PASSWORD`, `MT5_SERVER`,
228
+ and `MT5_PATH` when unset, and explicit CLI values still win.
229
+
230
+ ### `history-gaps`
231
+
232
+ Inspect collected SQLite rate views offline and export one row per detected gap.
233
+ For managed compatibility views, the command infers bar spacing from the view
234
+ name. Use `--granularity-seconds` for custom tables or views.
235
+
236
+ ```bash
237
+ mt5cli -o gaps.json history-gaps --sqlite3 history.db
238
+ mt5cli -o eurusd.csv history-gaps --sqlite3 history.db --table rate_EURUSD__M1_1
239
+ ```
225
240
 
226
241
  ### `collect-history`
227
242
 
@@ -144,9 +144,10 @@ mt5cli -o ticks.json ticks-from --symbol EURUSD \
144
144
  # Export symbols to SQLite3 with custom table name
145
145
  mt5cli -o data.db --table symbols symbols --group "*USD*"
146
146
 
147
- # Export with connection credentials
148
- mt5cli --login 12345 --password mypass --server MyBroker-Demo \
149
- -o positions.csv positions
147
+ # Export with connection credentials from env or placeholders
148
+ MT5_LOGIN=12345 MT5_PASSWORD=secret MT5_SERVER=MyBroker-Demo \
149
+ mt5cli -o positions.csv positions
150
+ MT5_PATH="/path/to/terminal64.exe" mt5cli -o positions.csv positions
150
151
  ```
151
152
 
152
153
  Run as a Python module:
@@ -157,40 +158,54 @@ python -m mt5cli -o account.csv account-info
157
158
 
158
159
  ## Commands
159
160
 
160
- | Command | Description |
161
- | ---------------------- | ------------------------------------------------------------------------------------------------------------------------------------------- |
162
- | `rates-from` | Export rates from a start date |
163
- | `rates-from-pos` | Export rates from a start position |
164
- | `latest-rates` | Export latest rates from a start position |
165
- | `rates-range` | Export rates for a date range |
166
- | `ticks-from` | Export ticks from a start date |
167
- | `ticks-range` | Export ticks for a date range |
168
- | `ticks-recent` | Export ticks from a recent trailing window |
169
- | `account-info` | Export account information |
170
- | `terminal-info` | Export terminal information |
171
- | `version` | Export MetaTrader 5 version information |
172
- | `last-error` | Export the last error information |
173
- | `symbols` | Export symbol list |
174
- | `symbol-info` | Export symbol details |
175
- | `symbol-info-tick` | Export the last tick for a symbol |
176
- | `minimum-margins` | Export minimum-volume buy and sell margin requirements |
177
- | `market-book` | Export market depth (order book) |
178
- | `orders` | Export active orders |
179
- | `positions` | Export open positions |
180
- | `history-orders` | Export historical orders |
181
- | `history-deals` | Export historical deals |
182
- | `recent-history-deals` | Export historical deals from a recent trailing window |
183
- | `mt5-summary` | Export terminal/account status summary |
184
- | `order-check` | Check funds sufficiency for a trade request |
185
- | `order-send` | Send a raw trade request to the trade server (`--yes` required; expert path) |
186
- | `close-positions` | Close open positions by `--symbol` or `--ticket` (`--yes` required for live; `--dry-run` available) |
187
- | `collect-history` | Collect rates, history-orders, and history-deals for one or more symbols into a single SQLite database (ticks opt-in via `--dataset ticks`) |
188
- | `grafana-schema` | Create or refresh Grafana-ready views and indexes in an existing SQLite database (idempotent, no MT5 connection) |
189
- | `snapshot` | Snapshot current account, position, order, and terminal state into SQLite for live Grafana dashboards |
161
+ | Command | Description |
162
+ | ---------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------- |
163
+ | `rates-from` | Export rates from a start date |
164
+ | `rates-from-pos` | Export rates from a start position |
165
+ | `latest-rates` | Export latest rates from a start position |
166
+ | `rates-range` | Export rates for a date range |
167
+ | `ticks-from` | Export ticks from a start date |
168
+ | `ticks-range` | Export ticks for a date range |
169
+ | `ticks-recent` | Export ticks from a recent trailing window |
170
+ | `account-info` | Export account information |
171
+ | `terminal-info` | Export terminal information |
172
+ | `version` | Export MetaTrader 5 version information |
173
+ | `last-error` | Export the last error information |
174
+ | `symbols` | Export symbol list |
175
+ | `symbol-info` | Export symbol details |
176
+ | `symbol-info-tick` | Export the last tick for a symbol |
177
+ | `minimum-margins` | Export minimum-volume buy and sell margin requirements |
178
+ | `market-book` | Export market depth (order book) |
179
+ | `orders` | Export active orders |
180
+ | `positions` | Export open positions |
181
+ | `history-orders` | Export historical orders |
182
+ | `history-deals` | Export historical deals |
183
+ | `recent-history-deals` | Export historical deals from a recent trailing window |
184
+ | `mt5-summary` | Export terminal/account status summary |
185
+ | `order-check` | Check funds sufficiency for a trade request |
186
+ | `order-send` | Send a raw trade request to the trade server (`--yes` required; expert path) |
187
+ | `close-positions` | Close open positions by `--symbol` or `--ticket` (`--yes` required for live; `--dry-run` available; optional `--deviation` / `--comment` / `--magic`) |
188
+ | `collect-history` | Collect rates, history-orders, and history-deals for one or more symbols into a single SQLite database (ticks opt-in via `--dataset ticks`) |
189
+ | `history-gaps` | Export a SQLite-only one-row-per-gap report from managed rate compatibility views without connecting to MT5 |
190
+ | `grafana-schema` | Create or refresh Grafana-ready views and indexes in an existing SQLite database (idempotent, no MT5 connection) |
191
+ | `snapshot` | Snapshot current account, position, order, and terminal state into SQLite for live Grafana dashboards |
190
192
 
191
193
  Use `order-check` to validate a request payload before running `order-send --yes`.
192
194
  `close-positions` is the safer high-level alternative that builds correct close
193
195
  requests automatically. At least one `--symbol` or `--ticket` must be provided.
196
+ CLI connection flags fall back to `MT5_LOGIN`, `MT5_PASSWORD`, `MT5_SERVER`,
197
+ and `MT5_PATH` when unset, and explicit CLI values still win.
198
+
199
+ ### `history-gaps`
200
+
201
+ Inspect collected SQLite rate views offline and export one row per detected gap.
202
+ For managed compatibility views, the command infers bar spacing from the view
203
+ name. Use `--granularity-seconds` for custom tables or views.
204
+
205
+ ```bash
206
+ mt5cli -o gaps.json history-gaps --sqlite3 history.db
207
+ mt5cli -o eurusd.csv history-gaps --sqlite3 history.db --table rate_EURUSD__M1_1
208
+ ```
194
209
 
195
210
  ### `collect-history`
196
211
 
@@ -70,6 +70,7 @@ timestamp normalization in downstream apps.
70
70
  | Symbol | Role |
71
71
  | ----------------------------------------------------------------- | -------------------------------------------------------------------------------------------- |
72
72
  | `collect_history` | One-shot date-range export into SQLite |
73
+ | `report_rate_gaps` | SQLite-only one-row-per-gap report for a rate table or compatibility view |
73
74
  | `update_history`, `update_history_with_config` | Incremental append from `MAX(time)` cursors |
74
75
  | `ThrottledHistoryUpdater` | Minimum interval between successful incremental updates; optional `update_backend` injection |
75
76
  | `RateTarget`, `build_rate_targets` | Neutral `(symbol, timeframe)` series descriptors |
@@ -98,6 +99,7 @@ strategy entries, exits, Kelly sizing, or signal logic.
98
99
  | `calculate_symbol_group_margin_ratio` | Estimated symbol-group margin/equity with optional exposure |
99
100
  | `determine_order_limits` | SL/TP price levels from ratios |
100
101
  | `calculate_trailing_stop_updates` | Per-ticket generic trailing stop-loss update plan |
102
+ | `resolve_broker_filling_mode` | Broker-supported filling-mode selection helper |
101
103
  | `ensure_symbol_selected` | Select/verify Market Watch visibility |
102
104
  | `fetch_recent_history_deals_for_trading_client` | Recent deal history from a connected trading client |
103
105
  | `place_market_order`, `close_open_positions`, `update_sltp_for_open_positions`, `update_trailing_stop_loss_for_open_positions` | Order execution helpers (`dry_run` supported) |
@@ -211,6 +213,9 @@ The Typer application in `mt5cli.cli` exposes file-export commands documented in
211
213
  - Require `-o/--output` and write CSV, JSON, Parquet, or SQLite.
212
214
  - Accept global MT5 connection options (`--login`, `--password`, `--server`,
213
215
  `--path`, `--timeout`).
216
+ - Resolve unset CLI connection options from `MT5_LOGIN`, `MT5_PASSWORD`,
217
+ `MT5_SERVER`, and `MT5_PATH`, and expand `${ENV_VAR}` placeholders in CLI
218
+ string fields before building the MT5 config.
214
219
  - Delegate to the same Python APIs described here; they are not duplicated
215
220
  business logic.
216
221
 
@@ -228,7 +233,14 @@ constructed request payload. `close-positions` is the safer high-level helper
228
233
  that closes open positions by `--symbol` or `--ticket` using
229
234
  `close_open_positions()`. Both `order-send --yes` and `close-positions --yes`
230
235
  are live execution paths. `close-positions --dry-run` previews close orders
231
- without placing them and does not require `--yes`.
236
+ without placing them and does not require `--yes`. `close-positions` also
237
+ accepts optional `--deviation`, `--comment`, and `--magic`; `--magic` scopes
238
+ the selected open positions fail-closed when position magic metadata is absent.
239
+
240
+ `history-gaps` reads an existing SQLite history database and exports one row
241
+ per detected gap from managed rate compatibility views. It never initializes
242
+ MT5. Pass `--granularity-seconds` for custom tables or views whose bar spacing
243
+ cannot be inferred from the name.
232
244
 
233
245
  ## Internal helpers (not stable)
234
246
 
@@ -109,8 +109,10 @@ mt5cli -o ticks.json ticks-from --symbol EURUSD \
109
109
  # Export symbols to SQLite3 with custom table name
110
110
  mt5cli -o data.db --table symbols symbols --group "*USD*"
111
111
 
112
- # Export with connection credentials
113
- mt5cli --login 12345 --password mypass --server MyBroker-Demo \
112
+ # Export with connection credentials from env or placeholders
113
+ MT5_LOGIN=12345 MT5_PASSWORD=secret MT5_SERVER=MyBroker-Demo \
114
+ mt5cli -o positions.csv positions
115
+ mt5cli --login '${MT5_LOGIN}' --password '${MT5_PASSWORD}' --server '${MT5_SERVER}' \
114
116
  -o positions.csv positions
115
117
  ```
116
118
 
@@ -164,10 +166,10 @@ mt5cli --login 12345 --password mypass --server MyBroker-Demo \
164
166
  These commands send requests to the live trade server and can place or close
165
167
  real trades. Both require `--yes` for live execution.
166
168
 
167
- | Command | Description |
168
- | ----------------- | ---------------------------------------------------------------------------------------------------- |
169
- | `order-send` | Send a **raw** trade request directly to MT5 (`--yes` required; expert path — no extra validation) |
170
- | `close-positions` | Close open positions by `--symbol` or `--ticket` (`--yes` required for live; `--dry-run` to preview) |
169
+ | Command | Description |
170
+ | ----------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------ |
171
+ | `order-send` | Send a **raw** trade request directly to MT5 (`--yes` required; expert path — no extra validation) |
172
+ | `close-positions` | Close open positions by `--symbol` or `--ticket` (`--yes` required for live; `--dry-run` to preview; optional `--deviation` / `--comment` / `--magic`) |
171
173
 
172
174
  Use `order-check` (Trading State) to validate funds before running `order-send --yes`.
173
175
  `close-positions` is the safer high-level alternative that builds correct close
@@ -179,6 +181,7 @@ applications should prefer dedicated closing helpers or their own risk controls.
179
181
  | Command | Description |
180
182
  | ----------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
181
183
  | `collect-history` | Collect rates, history-orders, and history-deals (ticks opt-in via `--dataset ticks`) for one or more symbols into a single SQLite database (optional cash-event/position views) |
184
+ | `history-gaps` | Export a SQLite-only one-row-per-gap report from managed rate compatibility views without connecting to MT5 |
182
185
 
183
186
  ```bash
184
187
  mt5cli -o history.db collect-history \
@@ -213,7 +216,7 @@ See the [History schema diagram](api/history.md#entity-relationship-diagram) for
213
216
  | `-f, --format` | Output format (auto-detected from extension if omitted) |
214
217
  | `--table` | Table name for SQLite3 output (default: "data") |
215
218
  | `--login` | Trading account login |
216
- | `--password` | Trading account password |
219
+ | `--password` | Trading account password (`MT5_PASSWORD`) |
217
220
  | `--server` | Trading server name |
218
221
  | `--path` | Path to MetaTrader5 terminal EXE file |
219
222
  | `--timeout` | Connection timeout in milliseconds |
@@ -22,6 +22,7 @@ from .history import (
22
22
  drop_forming_rate_bar,
23
23
  load_rate_series_by_granularity,
24
24
  load_rate_series_from_sqlite,
25
+ report_rate_gaps,
25
26
  )
26
27
  from .sdk import (
27
28
  AccountSpec,
@@ -76,6 +77,7 @@ from .trading import (
76
77
  mt5_trading_session,
77
78
  normalize_order_volume,
78
79
  place_market_order,
80
+ resolve_broker_filling_mode,
79
81
  update_sltp_for_open_positions,
80
82
  update_trailing_stop_loss_for_open_positions,
81
83
  )
@@ -140,8 +142,10 @@ __all__ = [
140
142
  "mt5_trading_session",
141
143
  "normalize_order_volume",
142
144
  "place_market_order",
145
+ "report_rate_gaps",
143
146
  "resolve_account_spec",
144
147
  "resolve_account_specs",
148
+ "resolve_broker_filling_mode",
145
149
  "update_history",
146
150
  "update_history_with_config",
147
151
  "update_observability",
@@ -4,6 +4,9 @@ from __future__ import annotations
4
4
 
5
5
  import json
6
6
  import logging
7
+ import os
8
+ import re
9
+ import sqlite3
7
10
  from dataclasses import dataclass
8
11
  from datetime import datetime # noqa: TC003
9
12
  from pathlib import Path # noqa: TC003
@@ -11,10 +14,10 @@ from typing import TYPE_CHECKING, Annotated, Any, cast
11
14
 
12
15
  import pandas as pd
13
16
  import typer
14
- from pdmt5 import Mt5Config
15
17
 
16
18
  from . import sdk
17
19
  from .client import MT5Client
20
+ from .history import report_rate_gaps, resolve_granularity_name
18
21
  from .trading import OrderExecutionResult, close_open_positions, create_trading_client
19
22
  from .utils import (
20
23
  DATETIME_TYPE,
@@ -32,6 +35,8 @@ from .utils import (
32
35
  if TYPE_CHECKING:
33
36
  from collections.abc import Callable
34
37
 
38
+ from pdmt5 import Mt5Config
39
+
35
40
  logger = logging.getLogger(__name__)
36
41
 
37
42
  # ---------------------------------------------------------------------------
@@ -66,12 +71,55 @@ app = typer.Typer(
66
71
  _REQUEST_OPTION_HELP = (
67
72
  "Order request as a JSON object string, or '@path' to load JSON from a file."
68
73
  )
74
+ _CLI_ENV_DEFAULTS: dict[str, str] = {
75
+ "path": "MT5_PATH",
76
+ "login": "MT5_LOGIN",
77
+ "password": "MT5_PASSWORD",
78
+ "server": "MT5_SERVER",
79
+ }
80
+ _RATE_VIEW_NAME_RE = re.compile(
81
+ r"^rate_(?P<symbol>.+)__(?:(?P<granularity>[A-Z0-9]+)_)?(?P<timeframe>\d+)$",
82
+ )
69
83
 
70
84
 
71
85
  def _get_export_context(ctx: typer.Context) -> _ExportContext:
72
86
  return cast("_ExportContext", ctx.obj)
73
87
 
74
88
 
89
+ def _resolve_cli_option(value: str | None, env_name: str) -> str | None:
90
+ return value if value is not None else os.environ.get(env_name)
91
+
92
+
93
+ def _timeframe_interval_seconds(timeframe: int) -> int | None:
94
+ granularity = resolve_granularity_name(timeframe)
95
+ units = {
96
+ "M": 60,
97
+ "H": 3600,
98
+ "D": 86400,
99
+ "W": 604800,
100
+ }
101
+ for prefix, seconds in units.items():
102
+ suffix = granularity.removeprefix(prefix)
103
+ if granularity.startswith(prefix) and suffix.isdigit():
104
+ return int(suffix) * seconds
105
+ return None
106
+
107
+
108
+ def _infer_gap_table_granularity_seconds(table: str) -> int | None:
109
+ if (match := _RATE_VIEW_NAME_RE.fullmatch(table)) is None:
110
+ return None
111
+ return _timeframe_interval_seconds(int(match.group("timeframe")))
112
+
113
+
114
+ def _default_gap_tables(conn: sqlite3.Connection) -> list[str]:
115
+ rows = conn.execute(
116
+ "SELECT name FROM sqlite_master"
117
+ " WHERE type IN ('table', 'view') AND name GLOB 'rate_*__*'"
118
+ " ORDER BY name",
119
+ ).fetchall()
120
+ return [str(row[0]) for row in rows]
121
+
122
+
75
123
  def _execute_export(
76
124
  ctx: typer.Context,
77
125
  fetch_fn: Callable[[], pd.DataFrame],
@@ -132,20 +180,36 @@ def _callback( # pyright: ignore[reportUnusedFunction]
132
180
  typer.Option(help="Table name for SQLite3 output."),
133
181
  ] = "data",
134
182
  login: Annotated[
135
- int | None,
136
- typer.Option(help="Trading account login."),
183
+ str | None,
184
+ typer.Option(
185
+ help="Trading account login.",
186
+ envvar=_CLI_ENV_DEFAULTS["login"],
187
+ show_envvar=True,
188
+ ),
137
189
  ] = None,
138
190
  password: Annotated[
139
191
  str | None,
140
- typer.Option(help="Trading account password."),
192
+ typer.Option(
193
+ help="Trading account password.",
194
+ envvar=_CLI_ENV_DEFAULTS["password"],
195
+ show_envvar=True,
196
+ ),
141
197
  ] = None,
142
198
  server: Annotated[
143
199
  str | None,
144
- typer.Option(help="Trading server name."),
200
+ typer.Option(
201
+ help="Trading server name.",
202
+ envvar=_CLI_ENV_DEFAULTS["server"],
203
+ show_envvar=True,
204
+ ),
145
205
  ] = None,
146
206
  path: Annotated[
147
207
  str | None,
148
- typer.Option(help="Path to MetaTrader5 terminal EXE file."),
208
+ typer.Option(
209
+ help="Path to MetaTrader5 terminal EXE file.",
210
+ envvar=_CLI_ENV_DEFAULTS["path"],
211
+ show_envvar=True,
212
+ ),
149
213
  ] = None,
150
214
  timeout: Annotated[
151
215
  int | None,
@@ -169,17 +233,22 @@ def _callback( # pyright: ignore[reportUnusedFunction]
169
233
  )
170
234
  except ValueError as exc:
171
235
  raise typer.BadParameter(str(exc)) from exc
236
+ try:
237
+ config = sdk.build_config(
238
+ path=_resolve_cli_option(path, _CLI_ENV_DEFAULTS["path"]),
239
+ login=_resolve_cli_option(login, _CLI_ENV_DEFAULTS["login"]),
240
+ password=_resolve_cli_option(password, _CLI_ENV_DEFAULTS["password"]),
241
+ server=_resolve_cli_option(server, _CLI_ENV_DEFAULTS["server"]),
242
+ timeout=timeout,
243
+ allow_whole_dollar_env=True,
244
+ )
245
+ except ValueError as exc:
246
+ raise typer.BadParameter(str(exc)) from exc
172
247
  ctx.obj = _ExportContext(
173
248
  output=output,
174
249
  output_format=output_format,
175
250
  table=table,
176
- config=Mt5Config(
177
- path=path,
178
- login=login,
179
- password=password,
180
- server=server,
181
- timeout=timeout,
182
- ),
251
+ config=config,
183
252
  )
184
253
 
185
254
 
@@ -658,6 +727,20 @@ def close_positions(
658
727
  help="Position ticket to close (repeat for multiple tickets).",
659
728
  ),
660
729
  ] = None,
730
+ deviation: Annotated[
731
+ int | None,
732
+ typer.Option(help="Optional slippage/deviation for each close request."),
733
+ ] = None,
734
+ comment: Annotated[
735
+ str | None,
736
+ typer.Option(help="Optional comment attached to each close request."),
737
+ ] = None,
738
+ magic: Annotated[
739
+ int | None,
740
+ typer.Option(
741
+ help="Optional magic tag for close requests and position filtering.",
742
+ ),
743
+ ] = None,
661
744
  dry_run: Annotated[
662
745
  bool,
663
746
  typer.Option("--dry-run", help="Preview close orders without executing them."),
@@ -694,6 +777,9 @@ def close_positions(
694
777
  client,
695
778
  symbols=list(symbol) if symbol else None,
696
779
  tickets=list(ticket) if ticket else None,
780
+ deviation=deviation,
781
+ comment=comment,
782
+ magic=magic,
697
783
  dry_run=dry_run,
698
784
  )
699
785
  finally:
@@ -702,6 +788,73 @@ def close_positions(
702
788
  _execute_export(ctx, lambda: df)
703
789
 
704
790
 
791
+ @app.command("history-gaps", rich_help_panel="Collection")
792
+ def history_gaps(
793
+ ctx: typer.Context,
794
+ sqlite3_path: Annotated[
795
+ Path,
796
+ typer.Option(
797
+ "--sqlite3",
798
+ help="Source SQLite history database to analyze.",
799
+ ),
800
+ ],
801
+ table: Annotated[
802
+ list[str] | None,
803
+ typer.Option(
804
+ "--table",
805
+ help="Rate table or compatibility view to inspect (repeat for multiple).",
806
+ ),
807
+ ] = None,
808
+ granularity_seconds: Annotated[
809
+ int | None,
810
+ typer.Option(help="Explicit bar interval in seconds for custom tables/views."),
811
+ ] = None,
812
+ min_gap_intervals: Annotated[
813
+ int,
814
+ typer.Option(help="Minimum missing-bar count required to emit a gap row."),
815
+ ] = 1,
816
+ ) -> None:
817
+ """Export SQLite rate gaps without connecting to MT5.
818
+
819
+ Raises:
820
+ typer.BadParameter: If no compatible rate view is available and no
821
+ explicit table is provided, or if granularity inference fails.
822
+ """
823
+ with sqlite3.connect(sqlite3_path) as conn:
824
+ tables = list(table) if table else _default_gap_tables(conn)
825
+ if not tables:
826
+ msg = (
827
+ "No managed rate compatibility views found; pass --table for a rate "
828
+ "table or view."
829
+ )
830
+ raise typer.BadParameter(msg, param_hint="--table")
831
+ frames: list[pd.DataFrame] = []
832
+ for table_name in tables:
833
+ interval_seconds = (
834
+ granularity_seconds or _infer_gap_table_granularity_seconds(table_name)
835
+ )
836
+ if interval_seconds is None:
837
+ msg = (
838
+ f"Could not infer granularity for {table_name!r}; pass "
839
+ "--granularity-seconds."
840
+ )
841
+ raise typer.BadParameter(msg, param_hint="--granularity-seconds")
842
+ frames.append(
843
+ report_rate_gaps(
844
+ conn,
845
+ table_name,
846
+ granularity_seconds=interval_seconds,
847
+ min_gap_intervals=min_gap_intervals,
848
+ )
849
+ )
850
+ df = (
851
+ pd.concat(frames, ignore_index=True)
852
+ if frames
853
+ else pd.DataFrame(columns=["table"])
854
+ )
855
+ _execute_export(ctx, lambda: df)
856
+
857
+
705
858
  @app.command(rich_help_panel="Collection")
706
859
  def collect_history(
707
860
  ctx: typer.Context,
@@ -59,6 +59,8 @@ STABLE_SDK_EXPORTS: frozenset[str] = frozenset({
59
59
  "mt5_trading_session",
60
60
  "normalize_order_volume",
61
61
  "place_market_order",
62
+ "report_rate_gaps",
63
+ "resolve_broker_filling_mode",
62
64
  "resolve_account_spec",
63
65
  "resolve_account_specs",
64
66
  "update_history",