mt5cli 1.1.3__tar.gz → 1.1.4__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-1.1.3 → mt5cli-1.1.4}/PKG-INFO +33 -33
- {mt5cli-1.1.3 → mt5cli-1.1.4}/README.md +31 -31
- {mt5cli-1.1.3 → mt5cli-1.1.4}/docs/api/public-contract.md +6 -2
- {mt5cli-1.1.3 → mt5cli-1.1.4}/docs/index.md +4 -4
- {mt5cli-1.1.3 → mt5cli-1.1.4}/mt5cli/cli.py +22 -1
- {mt5cli-1.1.3 → mt5cli-1.1.4}/mt5cli/trading.py +30 -1
- {mt5cli-1.1.3 → mt5cli-1.1.4}/pyproject.toml +2 -2
- {mt5cli-1.1.3 → mt5cli-1.1.4}/tests/test_cli.py +87 -0
- {mt5cli-1.1.3 → mt5cli-1.1.4}/tests/test_trading.py +152 -0
- {mt5cli-1.1.3 → mt5cli-1.1.4}/uv.lock +5 -5
- {mt5cli-1.1.3 → mt5cli-1.1.4}/.agents/skills/local-qa/SKILL.md +0 -0
- {mt5cli-1.1.3 → mt5cli-1.1.4}/.agents/skills/local-qa/scripts/qa.sh +0 -0
- {mt5cli-1.1.3 → mt5cli-1.1.4}/.agents/skills/mt5cli/SKILL.md +0 -0
- {mt5cli-1.1.3 → mt5cli-1.1.4}/.agents/skills/pr-feedback-triage/SKILL.md +0 -0
- {mt5cli-1.1.3 → mt5cli-1.1.4}/.claude/agents/codex.md +0 -0
- {mt5cli-1.1.3 → mt5cli-1.1.4}/.claude/settings.json +0 -0
- {mt5cli-1.1.3 → mt5cli-1.1.4}/.github/FUNDING.yml +0 -0
- {mt5cli-1.1.3 → mt5cli-1.1.4}/.github/dependabot.yml +0 -0
- {mt5cli-1.1.3 → mt5cli-1.1.4}/.github/renovate.json +0 -0
- {mt5cli-1.1.3 → mt5cli-1.1.4}/.github/workflows/ci.yml +0 -0
- {mt5cli-1.1.3 → mt5cli-1.1.4}/.github/workflows/claude.yml +0 -0
- {mt5cli-1.1.3 → mt5cli-1.1.4}/.github/workflows/opencode.yml +0 -0
- {mt5cli-1.1.3 → mt5cli-1.1.4}/.github/workflows/release.yml +0 -0
- {mt5cli-1.1.3 → mt5cli-1.1.4}/.gitignore +0 -0
- {mt5cli-1.1.3 → mt5cli-1.1.4}/AGENTS.md +0 -0
- {mt5cli-1.1.3 → mt5cli-1.1.4}/CLAUDE.md +0 -0
- {mt5cli-1.1.3 → mt5cli-1.1.4}/LICENSE +0 -0
- {mt5cli-1.1.3 → mt5cli-1.1.4}/docs/api/cli.md +0 -0
- {mt5cli-1.1.3 → mt5cli-1.1.4}/docs/api/client.md +0 -0
- {mt5cli-1.1.3 → mt5cli-1.1.4}/docs/api/converters.md +0 -0
- {mt5cli-1.1.3 → mt5cli-1.1.4}/docs/api/exceptions.md +0 -0
- {mt5cli-1.1.3 → mt5cli-1.1.4}/docs/api/grafana.md +0 -0
- {mt5cli-1.1.3 → mt5cli-1.1.4}/docs/api/history.md +0 -0
- {mt5cli-1.1.3 → mt5cli-1.1.4}/docs/api/index.md +0 -0
- {mt5cli-1.1.3 → mt5cli-1.1.4}/docs/api/schemas.md +0 -0
- {mt5cli-1.1.3 → mt5cli-1.1.4}/docs/api/sdk.md +0 -0
- {mt5cli-1.1.3 → mt5cli-1.1.4}/docs/api/telemetry.md +0 -0
- {mt5cli-1.1.3 → mt5cli-1.1.4}/docs/api/trading.md +0 -0
- {mt5cli-1.1.3 → mt5cli-1.1.4}/docs/api/utils.md +0 -0
- {mt5cli-1.1.3 → mt5cli-1.1.4}/examples/grafana/README.md +0 -0
- {mt5cli-1.1.3 → mt5cli-1.1.4}/examples/grafana/compose.yml +0 -0
- {mt5cli-1.1.3 → mt5cli-1.1.4}/examples/grafana/dashboards/mt5cli-market.json +0 -0
- {mt5cli-1.1.3 → mt5cli-1.1.4}/examples/grafana/dashboards/mt5cli-overview.json +0 -0
- {mt5cli-1.1.3 → mt5cli-1.1.4}/examples/grafana/dashboards/mt5cli-trades.json +0 -0
- {mt5cli-1.1.3 → mt5cli-1.1.4}/examples/grafana/provisioning/dashboards/mt5cli.yml +0 -0
- {mt5cli-1.1.3 → mt5cli-1.1.4}/examples/grafana/provisioning/datasources/mt5cli-sqlite.yml +0 -0
- {mt5cli-1.1.3 → mt5cli-1.1.4}/mkdocs.yml +0 -0
- {mt5cli-1.1.3 → mt5cli-1.1.4}/mt5cli/__init__.py +0 -0
- {mt5cli-1.1.3 → mt5cli-1.1.4}/mt5cli/__main__.py +0 -0
- {mt5cli-1.1.3 → mt5cli-1.1.4}/mt5cli/client.py +0 -0
- {mt5cli-1.1.3 → mt5cli-1.1.4}/mt5cli/contract.py +0 -0
- {mt5cli-1.1.3 → mt5cli-1.1.4}/mt5cli/converters.py +0 -0
- {mt5cli-1.1.3 → mt5cli-1.1.4}/mt5cli/exceptions.py +0 -0
- {mt5cli-1.1.3 → mt5cli-1.1.4}/mt5cli/grafana.py +0 -0
- {mt5cli-1.1.3 → mt5cli-1.1.4}/mt5cli/history.py +0 -0
- {mt5cli-1.1.3 → mt5cli-1.1.4}/mt5cli/retry.py +0 -0
- {mt5cli-1.1.3 → mt5cli-1.1.4}/mt5cli/schemas.py +0 -0
- {mt5cli-1.1.3 → mt5cli-1.1.4}/mt5cli/sdk.py +0 -0
- {mt5cli-1.1.3 → mt5cli-1.1.4}/mt5cli/telemetry.py +0 -0
- {mt5cli-1.1.3 → mt5cli-1.1.4}/mt5cli/utils.py +0 -0
- {mt5cli-1.1.3 → mt5cli-1.1.4}/tests/__init__.py +0 -0
- {mt5cli-1.1.3 → mt5cli-1.1.4}/tests/conftest.py +0 -0
- {mt5cli-1.1.3 → mt5cli-1.1.4}/tests/test_contracts.py +0 -0
- {mt5cli-1.1.3 → mt5cli-1.1.4}/tests/test_examples.py +0 -0
- {mt5cli-1.1.3 → mt5cli-1.1.4}/tests/test_grafana.py +0 -0
- {mt5cli-1.1.3 → mt5cli-1.1.4}/tests/test_history.py +0 -0
- {mt5cli-1.1.3 → mt5cli-1.1.4}/tests/test_sdk.py +0 -0
- {mt5cli-1.1.3 → mt5cli-1.1.4}/tests/test_telemetry.py +0 -0
- {mt5cli-1.1.3 → mt5cli-1.1.4}/tests/test_utils.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: mt5cli
|
|
3
|
-
Version: 1.1.
|
|
3
|
+
Version: 1.1.4
|
|
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.1.
|
|
21
|
+
Requires-Dist: pdmt5>=1.1.1
|
|
22
22
|
Requires-Dist: pydantic>=2.13.4
|
|
23
23
|
Requires-Dist: typer>=0.15.0
|
|
24
24
|
Provides-Extra: otel
|
|
@@ -189,37 +189,37 @@ python -m mt5cli -o account.csv account-info
|
|
|
189
189
|
|
|
190
190
|
## Commands
|
|
191
191
|
|
|
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
|
|
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 `--filling-mode` / `--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 |
|
|
223
223
|
|
|
224
224
|
Use `order-check` to validate a request payload before running `order-send --yes`.
|
|
225
225
|
`close-positions` is the safer high-level alternative that builds correct close
|
|
@@ -158,37 +158,37 @@ python -m mt5cli -o account.csv account-info
|
|
|
158
158
|
|
|
159
159
|
## Commands
|
|
160
160
|
|
|
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
|
|
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 `--filling-mode` / `--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 |
|
|
192
192
|
|
|
193
193
|
Use `order-check` to validate a request payload before running `order-send --yes`.
|
|
194
194
|
`close-positions` is the safer high-level alternative that builds correct close
|
|
@@ -234,8 +234,12 @@ that closes open positions by `--symbol` or `--ticket` using
|
|
|
234
234
|
`close_open_positions()`. Both `order-send --yes` and `close-positions --yes`
|
|
235
235
|
are live execution paths. `close-positions --dry-run` previews close orders
|
|
236
236
|
without placing them and does not require `--yes`. `close-positions` also
|
|
237
|
-
accepts optional `--deviation`, `--comment`, and `--magic`;
|
|
238
|
-
the selected open positions fail-closed when position magic
|
|
237
|
+
accepts optional `--filling-mode`, `--deviation`, `--comment`, and `--magic`;
|
|
238
|
+
`--magic` scopes the selected open positions fail-closed when position magic
|
|
239
|
+
metadata is absent. When `--filling-mode` (or the `order_filling_mode`
|
|
240
|
+
parameter of `close_open_positions()`) is omitted, the filling mode is
|
|
241
|
+
resolved per symbol with `resolve_broker_filling_mode()` so closes are not
|
|
242
|
+
rejected on brokers whose symbols do not support IOC.
|
|
239
243
|
|
|
240
244
|
`history-gaps` reads an existing SQLite history database and exports one row
|
|
241
245
|
per detected gap from managed rate compatibility views. It never initializes
|
|
@@ -166,10 +166,10 @@ mt5cli --login '${MT5_LOGIN}' --password '${MT5_PASSWORD}' --server '${MT5_SERVE
|
|
|
166
166
|
These commands send requests to the live trade server and can place or close
|
|
167
167
|
real trades. Both require `--yes` for live execution.
|
|
168
168
|
|
|
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`) |
|
|
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 `--filling-mode` / `--deviation` / `--comment` / `--magic`) |
|
|
173
173
|
|
|
174
174
|
Use `order-check` (Trading State) to validate funds before running `order-send --yes`.
|
|
175
175
|
`close-positions` is the safer high-level alternative that builds correct close
|
|
@@ -37,6 +37,8 @@ if TYPE_CHECKING:
|
|
|
37
37
|
|
|
38
38
|
from pdmt5 import Mt5Config
|
|
39
39
|
|
|
40
|
+
from .trading import OrderFillingMode
|
|
41
|
+
|
|
40
42
|
logger = logging.getLogger(__name__)
|
|
41
43
|
|
|
42
44
|
# ---------------------------------------------------------------------------
|
|
@@ -727,6 +729,16 @@ def close_positions(
|
|
|
727
729
|
help="Position ticket to close (repeat for multiple tickets).",
|
|
728
730
|
),
|
|
729
731
|
] = None,
|
|
732
|
+
filling_mode: Annotated[
|
|
733
|
+
str | None,
|
|
734
|
+
typer.Option(
|
|
735
|
+
"--filling-mode",
|
|
736
|
+
help=(
|
|
737
|
+
"Order filling mode for close requests (IOC, FOK, or RETURN)."
|
|
738
|
+
" Defaults to per-symbol broker resolution."
|
|
739
|
+
),
|
|
740
|
+
),
|
|
741
|
+
] = None,
|
|
730
742
|
deviation: Annotated[
|
|
731
743
|
int | None,
|
|
732
744
|
typer.Option(help="Optional slippage/deviation for each close request."),
|
|
@@ -762,11 +774,19 @@ def close_positions(
|
|
|
762
774
|
|
|
763
775
|
Raises:
|
|
764
776
|
typer.BadParameter: If neither ``--symbol`` nor ``--ticket`` is given,
|
|
765
|
-
|
|
777
|
+
if ``--filling-mode`` is not IOC, FOK, or RETURN, or if ``--yes``
|
|
778
|
+
is missing for a live (non-dry-run) run.
|
|
766
779
|
"""
|
|
767
780
|
if not symbol and not ticket:
|
|
768
781
|
msg = "Provide at least one --symbol or --ticket to close positions."
|
|
769
782
|
raise typer.BadParameter(msg)
|
|
783
|
+
order_filling_mode: OrderFillingMode | None = None
|
|
784
|
+
if filling_mode is not None:
|
|
785
|
+
normalized_filling_mode = filling_mode.upper()
|
|
786
|
+
if normalized_filling_mode not in {"IOC", "FOK", "RETURN"}:
|
|
787
|
+
msg = f"Unsupported filling mode: {filling_mode!r}."
|
|
788
|
+
raise typer.BadParameter(msg, param_hint="--filling-mode")
|
|
789
|
+
order_filling_mode = cast("OrderFillingMode", normalized_filling_mode)
|
|
770
790
|
if not dry_run and not yes:
|
|
771
791
|
msg = "Pass --yes to close live positions."
|
|
772
792
|
raise typer.BadParameter(msg, param_hint="--yes")
|
|
@@ -777,6 +797,7 @@ def close_positions(
|
|
|
777
797
|
client,
|
|
778
798
|
symbols=list(symbol) if symbol else None,
|
|
779
799
|
tickets=list(ticket) if ticket else None,
|
|
800
|
+
order_filling_mode=order_filling_mode,
|
|
780
801
|
deviation=deviation,
|
|
781
802
|
comment=comment,
|
|
782
803
|
magic=magic,
|
|
@@ -767,6 +767,18 @@ def _supported_filling_modes(
|
|
|
767
767
|
supported.add("FOK")
|
|
768
768
|
if isinstance(ioc_flag, int) and filling_mode & ioc_flag:
|
|
769
769
|
supported.add("IOC")
|
|
770
|
+
# MQL5 permits IOC/FOK for Request and Instant execution regardless of the
|
|
771
|
+
# SYMBOL_FILLING_MODE bitmask, which only governs Market/Exchange execution.
|
|
772
|
+
implicit_ioc_fok_modes = {
|
|
773
|
+
mode
|
|
774
|
+
for mode in (
|
|
775
|
+
getattr(client.mt5, "SYMBOL_TRADE_EXECUTION_REQUEST", None),
|
|
776
|
+
getattr(client.mt5, "SYMBOL_TRADE_EXECUTION_INSTANT", None),
|
|
777
|
+
)
|
|
778
|
+
if isinstance(mode, int)
|
|
779
|
+
}
|
|
780
|
+
if trade_exemode is not None and trade_exemode in implicit_ioc_fok_modes:
|
|
781
|
+
supported.update({"IOC", "FOK"})
|
|
770
782
|
market_execution = getattr(client.mt5, "SYMBOL_TRADE_EXECUTION_MARKET", None)
|
|
771
783
|
if trade_exemode is not None and not (
|
|
772
784
|
isinstance(market_execution, int) and trade_exemode == market_execution
|
|
@@ -1548,6 +1560,7 @@ def close_open_positions(
|
|
|
1548
1560
|
*,
|
|
1549
1561
|
symbols: str | list[str] | None = None,
|
|
1550
1562
|
tickets: list[int] | None = None,
|
|
1563
|
+
order_filling_mode: OrderFillingMode | None = None,
|
|
1551
1564
|
deviation: int | None = None,
|
|
1552
1565
|
comment: str | None = None,
|
|
1553
1566
|
magic: int | None = None,
|
|
@@ -1555,6 +1568,10 @@ def close_open_positions(
|
|
|
1555
1568
|
) -> list[OrderExecutionResult]:
|
|
1556
1569
|
"""Close matching open positions.
|
|
1557
1570
|
|
|
1571
|
+
When ``order_filling_mode`` is ``None``, the filling mode is resolved per
|
|
1572
|
+
symbol with :func:`resolve_broker_filling_mode` so closes are not rejected
|
|
1573
|
+
on brokers whose symbols do not support IOC.
|
|
1574
|
+
|
|
1558
1575
|
Returns:
|
|
1559
1576
|
Normalized execution results for matching positions.
|
|
1560
1577
|
"""
|
|
@@ -1565,14 +1582,26 @@ def close_open_positions(
|
|
|
1565
1582
|
magic=magic,
|
|
1566
1583
|
)
|
|
1567
1584
|
results: list[OrderExecutionResult] = []
|
|
1585
|
+
resolved_filling_modes: dict[str, OrderFillingMode] = {}
|
|
1568
1586
|
for row in positions.to_dict("records"):
|
|
1569
1587
|
pos_type = row["type"]
|
|
1570
1588
|
side: OrderSide = "SELL" if pos_type == client.mt5.POSITION_TYPE_BUY else "BUY"
|
|
1589
|
+
symbol = str(row["symbol"])
|
|
1590
|
+
if order_filling_mode is None:
|
|
1591
|
+
if symbol not in resolved_filling_modes:
|
|
1592
|
+
resolved_filling_modes[symbol] = resolve_broker_filling_mode(
|
|
1593
|
+
client,
|
|
1594
|
+
symbol=symbol,
|
|
1595
|
+
)
|
|
1596
|
+
filling_mode = resolved_filling_modes[symbol]
|
|
1597
|
+
else:
|
|
1598
|
+
filling_mode = order_filling_mode
|
|
1571
1599
|
result = place_market_order(
|
|
1572
1600
|
client,
|
|
1573
|
-
symbol=
|
|
1601
|
+
symbol=symbol,
|
|
1574
1602
|
volume=float(row["volume"]),
|
|
1575
1603
|
order_side=side,
|
|
1604
|
+
order_filling_mode=filling_mode,
|
|
1576
1605
|
position=int(row["ticket"]),
|
|
1577
1606
|
deviation=deviation,
|
|
1578
1607
|
comment=comment,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "mt5cli"
|
|
3
|
-
version = "1.1.
|
|
3
|
+
version = "1.1.4"
|
|
4
4
|
description = "Generic MT5 data and execution infrastructure for Python applications"
|
|
5
5
|
authors = [{name = "dceoy", email = "dceoy@users.noreply.github.com"}]
|
|
6
6
|
maintainers = [{name = "dceoy", email = "dceoy@users.noreply.github.com"}]
|
|
@@ -9,7 +9,7 @@ license-files = ["LICENSE"]
|
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">= 3.11, < 3.14"
|
|
11
11
|
dependencies = [
|
|
12
|
-
"pdmt5 >= 1.1.
|
|
12
|
+
"pdmt5 >= 1.1.1",
|
|
13
13
|
"pandas >= 2.2.2",
|
|
14
14
|
"pydantic >= 2.13.4",
|
|
15
15
|
"click >= 8.1.0",
|
|
@@ -603,6 +603,15 @@ class TestHelpText:
|
|
|
603
603
|
assert "--dry-run" in output
|
|
604
604
|
assert "--yes" in output
|
|
605
605
|
|
|
606
|
+
def test_close_positions_help_mentions_filling_mode(self) -> None:
|
|
607
|
+
"""close-positions help must document the --filling-mode option."""
|
|
608
|
+
result = runner.invoke(
|
|
609
|
+
app,
|
|
610
|
+
["-o", "out.csv", "close-positions", "--help"],
|
|
611
|
+
)
|
|
612
|
+
assert result.exit_code == 0
|
|
613
|
+
assert "--filling-mode" in normalize_cli_output(result.output)
|
|
614
|
+
|
|
606
615
|
|
|
607
616
|
# ---------------------------------------------------------------------------
|
|
608
617
|
# close-positions command
|
|
@@ -798,6 +807,84 @@ class TestClosePositions:
|
|
|
798
807
|
assert request["comment"] == "close-me"
|
|
799
808
|
assert request["magic"] == 7
|
|
800
809
|
|
|
810
|
+
@pytest.mark.parametrize(
|
|
811
|
+
("cli_value", "expected"),
|
|
812
|
+
[
|
|
813
|
+
pytest.param("ioc", 30, id="lowercase-ioc"),
|
|
814
|
+
pytest.param("IOC", 30, id="uppercase-ioc"),
|
|
815
|
+
pytest.param("fok", 31, id="lowercase-fok"),
|
|
816
|
+
pytest.param("FOK", 31, id="uppercase-fok"),
|
|
817
|
+
pytest.param("return", 32, id="lowercase-return"),
|
|
818
|
+
pytest.param("RETURN", 32, id="uppercase-return"),
|
|
819
|
+
pytest.param("Return", 32, id="mixed-case-return"),
|
|
820
|
+
],
|
|
821
|
+
)
|
|
822
|
+
def test_close_positions_forwards_explicit_filling_mode(
|
|
823
|
+
self,
|
|
824
|
+
tmp_path: Path,
|
|
825
|
+
trading_client: MagicMock,
|
|
826
|
+
cli_value: str,
|
|
827
|
+
expected: int,
|
|
828
|
+
) -> None:
|
|
829
|
+
"""--filling-mode is normalized and forwarded without broker resolution."""
|
|
830
|
+
trading_client.mt5.ORDER_FILLING_FOK = 31
|
|
831
|
+
trading_client.mt5.ORDER_FILLING_RETURN = 32
|
|
832
|
+
output = tmp_path / "close.json"
|
|
833
|
+
result = runner.invoke(
|
|
834
|
+
app,
|
|
835
|
+
[
|
|
836
|
+
"-o",
|
|
837
|
+
str(output),
|
|
838
|
+
"close-positions",
|
|
839
|
+
"--symbol",
|
|
840
|
+
"JP225",
|
|
841
|
+
"--filling-mode",
|
|
842
|
+
cli_value,
|
|
843
|
+
"--dry-run",
|
|
844
|
+
],
|
|
845
|
+
)
|
|
846
|
+
assert result.exit_code == 0, result.output
|
|
847
|
+
data = json.loads(output.read_text())
|
|
848
|
+
request = json.loads(data[0]["request"])
|
|
849
|
+
assert request["type_filling"] == expected
|
|
850
|
+
trading_client.symbol_info_as_dict.assert_not_called()
|
|
851
|
+
|
|
852
|
+
@pytest.mark.parametrize(
|
|
853
|
+
"cli_value",
|
|
854
|
+
["BOGUS", "ioc ", " FOK", "GTC", ""],
|
|
855
|
+
ids=[
|
|
856
|
+
"unknown-word",
|
|
857
|
+
"trailing-space",
|
|
858
|
+
"leading-space",
|
|
859
|
+
"order-time-mode",
|
|
860
|
+
"empty",
|
|
861
|
+
],
|
|
862
|
+
)
|
|
863
|
+
def test_close_positions_rejects_unknown_filling_mode(
|
|
864
|
+
self,
|
|
865
|
+
tmp_path: Path,
|
|
866
|
+
trading_client: MagicMock,
|
|
867
|
+
cli_value: str,
|
|
868
|
+
) -> None:
|
|
869
|
+
"""Unknown --filling-mode values fail closed before any close request."""
|
|
870
|
+
output = tmp_path / "close.json"
|
|
871
|
+
result = runner.invoke(
|
|
872
|
+
app,
|
|
873
|
+
[
|
|
874
|
+
"-o",
|
|
875
|
+
str(output),
|
|
876
|
+
"close-positions",
|
|
877
|
+
"--symbol",
|
|
878
|
+
"JP225",
|
|
879
|
+
"--filling-mode",
|
|
880
|
+
cli_value,
|
|
881
|
+
"--dry-run",
|
|
882
|
+
],
|
|
883
|
+
)
|
|
884
|
+
assert result.exit_code != 0
|
|
885
|
+
assert "filling mode" in normalize_cli_output(result.output).lower()
|
|
886
|
+
trading_client.order_send.assert_not_called()
|
|
887
|
+
|
|
801
888
|
def test_order_send_unchanged(
|
|
802
889
|
self,
|
|
803
890
|
tmp_path: Path,
|
|
@@ -73,6 +73,7 @@ def _mock_trade_client() -> MagicMock:
|
|
|
73
73
|
client.mt5.ORDER_TIME_GTC = 40
|
|
74
74
|
client.mt5.SYMBOL_TRADE_EXECUTION_MARKET = 3
|
|
75
75
|
client.mt5.SYMBOL_TRADE_EXECUTION_REQUEST = 4
|
|
76
|
+
client.mt5.SYMBOL_TRADE_EXECUTION_INSTANT = 5
|
|
76
77
|
client.mt5.TRADE_RETCODE_PLACED = 10008
|
|
77
78
|
client.mt5.TRADE_RETCODE_DONE = 10009
|
|
78
79
|
client.mt5.TRADE_RETCODE_DONE_PARTIAL = 10010
|
|
@@ -2434,6 +2435,49 @@ class TestVolumeAndExecution:
|
|
|
2434
2435
|
|
|
2435
2436
|
assert result == "RETURN"
|
|
2436
2437
|
|
|
2438
|
+
@pytest.mark.parametrize(
|
|
2439
|
+
"execution_attr",
|
|
2440
|
+
["SYMBOL_TRADE_EXECUTION_REQUEST", "SYMBOL_TRADE_EXECUTION_INSTANT"],
|
|
2441
|
+
ids=["request-execution", "instant-execution"],
|
|
2442
|
+
)
|
|
2443
|
+
@pytest.mark.parametrize(
|
|
2444
|
+
"filling_mode", [0, None], ids=["filling-mode-zero", "filling-mode-none"]
|
|
2445
|
+
)
|
|
2446
|
+
@pytest.mark.parametrize(
|
|
2447
|
+
("preferred_modes", "expected"),
|
|
2448
|
+
[
|
|
2449
|
+
pytest.param(("IOC", "FOK"), "IOC", id="prefers-ioc"),
|
|
2450
|
+
pytest.param(("FOK", "IOC"), "FOK", id="prefers-fok"),
|
|
2451
|
+
],
|
|
2452
|
+
)
|
|
2453
|
+
def test_resolve_broker_filling_mode_request_instant_execution_implies_ioc_fok(
|
|
2454
|
+
self,
|
|
2455
|
+
execution_attr: str,
|
|
2456
|
+
filling_mode: int | None,
|
|
2457
|
+
preferred_modes: tuple[str, str],
|
|
2458
|
+
expected: str,
|
|
2459
|
+
) -> None:
|
|
2460
|
+
"""Request/Instant execution supports IOC/FOK regardless of the mask.
|
|
2461
|
+
|
|
2462
|
+
MQL5 permits IOC and FOK for Request and Instant execution even when
|
|
2463
|
+
``SYMBOL_FILLING_MODE`` carries no matching bits (that bitmask only
|
|
2464
|
+
governs Market/Exchange execution), so the resolver must honor
|
|
2465
|
+
whichever of IOC/FOK is preferred without requiring mask bits.
|
|
2466
|
+
"""
|
|
2467
|
+
client = _mock_trade_client()
|
|
2468
|
+
client.symbol_info_as_dict.return_value = {
|
|
2469
|
+
"filling_mode": filling_mode,
|
|
2470
|
+
"trade_exemode": getattr(client.mt5, execution_attr),
|
|
2471
|
+
}
|
|
2472
|
+
|
|
2473
|
+
result = resolve_broker_filling_mode(
|
|
2474
|
+
client,
|
|
2475
|
+
symbol="EURUSD",
|
|
2476
|
+
preferred_modes=cast("Any", preferred_modes),
|
|
2477
|
+
)
|
|
2478
|
+
|
|
2479
|
+
assert result == expected
|
|
2480
|
+
|
|
2437
2481
|
def test_resolve_broker_filling_mode_keeps_preferred_when_metadata_unparseable(
|
|
2438
2482
|
self,
|
|
2439
2483
|
caplog: pytest.LogCaptureFixture,
|
|
@@ -2545,6 +2589,114 @@ class TestVolumeAndExecution:
|
|
|
2545
2589
|
assert result == []
|
|
2546
2590
|
client.order_send.assert_not_called()
|
|
2547
2591
|
|
|
2592
|
+
def test_close_open_positions_resolves_filling_mode_per_symbol(self) -> None:
|
|
2593
|
+
"""Default closes resolve the broker filling mode instead of assuming IOC."""
|
|
2594
|
+
client = _mock_trade_client()
|
|
2595
|
+
client.mt5.ORDER_FILLING_FOK = 31
|
|
2596
|
+
client.positions_get_as_df.return_value = pd.DataFrame(
|
|
2597
|
+
[
|
|
2598
|
+
{"ticket": 1, "symbol": "EURUSD", "type": 0, "volume": 0.1},
|
|
2599
|
+
{"ticket": 2, "symbol": "EURUSD", "type": 0, "volume": 0.2},
|
|
2600
|
+
],
|
|
2601
|
+
)
|
|
2602
|
+
client.symbol_info_as_dict.return_value = {
|
|
2603
|
+
"filling_mode": client.mt5.SYMBOL_FILLING_FOK,
|
|
2604
|
+
"trade_exemode": client.mt5.SYMBOL_TRADE_EXECUTION_MARKET,
|
|
2605
|
+
}
|
|
2606
|
+
client.symbol_info_tick_as_dict.return_value = {"ask": 1.2, "bid": 1.1}
|
|
2607
|
+
|
|
2608
|
+
result = close_open_positions(client, symbols="EURUSD", dry_run=True)
|
|
2609
|
+
|
|
2610
|
+
assert [_request_from_result(r)["type_filling"] for r in result] == [31, 31]
|
|
2611
|
+
client.symbol_info_as_dict.assert_called_once()
|
|
2612
|
+
|
|
2613
|
+
def test_close_open_positions_resolves_filling_mode_per_distinct_symbol(
|
|
2614
|
+
self,
|
|
2615
|
+
) -> None:
|
|
2616
|
+
"""Each distinct symbol gets its own resolved mode with one lookup each."""
|
|
2617
|
+
client = _mock_trade_client()
|
|
2618
|
+
client.mt5.ORDER_FILLING_FOK = 31
|
|
2619
|
+
client.positions_get_as_df.return_value = pd.DataFrame(
|
|
2620
|
+
[
|
|
2621
|
+
{"ticket": 1, "symbol": "EURUSD", "type": 0, "volume": 0.1},
|
|
2622
|
+
{"ticket": 2, "symbol": "USDJPY", "type": 0, "volume": 0.2},
|
|
2623
|
+
{"ticket": 3, "symbol": "EURUSD", "type": 0, "volume": 0.3},
|
|
2624
|
+
],
|
|
2625
|
+
)
|
|
2626
|
+
snapshots = {
|
|
2627
|
+
"EURUSD": {
|
|
2628
|
+
"filling_mode": client.mt5.SYMBOL_FILLING_FOK,
|
|
2629
|
+
"trade_exemode": client.mt5.SYMBOL_TRADE_EXECUTION_MARKET,
|
|
2630
|
+
},
|
|
2631
|
+
"USDJPY": {
|
|
2632
|
+
"filling_mode": (
|
|
2633
|
+
client.mt5.SYMBOL_FILLING_FOK | client.mt5.SYMBOL_FILLING_IOC
|
|
2634
|
+
),
|
|
2635
|
+
"trade_exemode": client.mt5.SYMBOL_TRADE_EXECUTION_MARKET,
|
|
2636
|
+
},
|
|
2637
|
+
}
|
|
2638
|
+
client.symbol_info_as_dict.side_effect = (
|
|
2639
|
+
lambda symbol: snapshots[symbol] # pyright: ignore[reportUnknownLambdaType]
|
|
2640
|
+
)
|
|
2641
|
+
client.symbol_info_tick_as_dict.return_value = {"ask": 1.2, "bid": 1.1}
|
|
2642
|
+
|
|
2643
|
+
result = close_open_positions(client, dry_run=True)
|
|
2644
|
+
|
|
2645
|
+
# Order matches the source positions frame: EURUSD, USDJPY, EURUSD.
|
|
2646
|
+
type_fillings = [_request_from_result(r)["type_filling"] for r in result]
|
|
2647
|
+
assert [r["symbol"] for r in result] == ["EURUSD", "USDJPY", "EURUSD"]
|
|
2648
|
+
assert type_fillings == [31, 30, 31]
|
|
2649
|
+
# One symbol_info lookup per unique symbol, not per position.
|
|
2650
|
+
assert client.symbol_info_as_dict.call_count == 2
|
|
2651
|
+
|
|
2652
|
+
def test_close_open_positions_keeps_ioc_when_supported(self) -> None:
|
|
2653
|
+
"""Symbols advertising IOC support keep the IOC close default."""
|
|
2654
|
+
client = _mock_trade_client()
|
|
2655
|
+
client.positions_get_as_df.return_value = pd.DataFrame(
|
|
2656
|
+
[{"ticket": 1, "symbol": "EURUSD", "type": 0, "volume": 0.1}],
|
|
2657
|
+
)
|
|
2658
|
+
client.symbol_info_as_dict.return_value = {
|
|
2659
|
+
"filling_mode": (
|
|
2660
|
+
client.mt5.SYMBOL_FILLING_FOK | client.mt5.SYMBOL_FILLING_IOC
|
|
2661
|
+
),
|
|
2662
|
+
"trade_exemode": client.mt5.SYMBOL_TRADE_EXECUTION_MARKET,
|
|
2663
|
+
}
|
|
2664
|
+
client.symbol_info_tick_as_dict.return_value = {"ask": 1.2, "bid": 1.1}
|
|
2665
|
+
|
|
2666
|
+
result = close_open_positions(client, symbols="EURUSD", dry_run=True)
|
|
2667
|
+
|
|
2668
|
+
assert _request_from_result(result[0])["type_filling"] == 30
|
|
2669
|
+
|
|
2670
|
+
@pytest.mark.parametrize(
|
|
2671
|
+
("order_filling_mode", "expected"),
|
|
2672
|
+
[
|
|
2673
|
+
pytest.param("IOC", 30, id="ioc"),
|
|
2674
|
+
pytest.param("FOK", 31, id="fok"),
|
|
2675
|
+
pytest.param("RETURN", 32, id="return"),
|
|
2676
|
+
],
|
|
2677
|
+
)
|
|
2678
|
+
def test_close_open_positions_forwards_explicit_filling_mode(
|
|
2679
|
+
self, order_filling_mode: str, expected: int
|
|
2680
|
+
) -> None:
|
|
2681
|
+
"""An explicit order_filling_mode bypasses broker resolution."""
|
|
2682
|
+
client = _mock_trade_client()
|
|
2683
|
+
client.mt5.ORDER_FILLING_FOK = 31
|
|
2684
|
+
client.mt5.ORDER_FILLING_RETURN = 32
|
|
2685
|
+
client.positions_get_as_df.return_value = pd.DataFrame(
|
|
2686
|
+
[{"ticket": 1, "symbol": "EURUSD", "type": 0, "volume": 0.1}],
|
|
2687
|
+
)
|
|
2688
|
+
client.symbol_info_tick_as_dict.return_value = {"ask": 1.2, "bid": 1.1}
|
|
2689
|
+
|
|
2690
|
+
result = close_open_positions(
|
|
2691
|
+
client,
|
|
2692
|
+
symbols="EURUSD",
|
|
2693
|
+
order_filling_mode=cast("Any", order_filling_mode),
|
|
2694
|
+
dry_run=True,
|
|
2695
|
+
)
|
|
2696
|
+
|
|
2697
|
+
assert _request_from_result(result[0])["type_filling"] == expected
|
|
2698
|
+
client.symbol_info_as_dict.assert_not_called()
|
|
2699
|
+
|
|
2548
2700
|
def test_filter_positions_magic_is_fail_closed_without_magic_column(self) -> None:
|
|
2549
2701
|
"""Test direct magic filtering fails closed when the DataFrame lacks magic."""
|
|
2550
2702
|
positions = pd.DataFrame([{"ticket": 1, "symbol": "EURUSD"}])
|
|
@@ -499,7 +499,7 @@ wheels = [
|
|
|
499
499
|
|
|
500
500
|
[[package]]
|
|
501
501
|
name = "mt5cli"
|
|
502
|
-
version = "1.1.
|
|
502
|
+
version = "1.1.4"
|
|
503
503
|
source = { editable = "." }
|
|
504
504
|
dependencies = [
|
|
505
505
|
{ name = "click" },
|
|
@@ -543,7 +543,7 @@ requires-dist = [
|
|
|
543
543
|
{ name = "opentelemetry-exporter-otlp-proto-http", marker = "extra == 'otel'" },
|
|
544
544
|
{ name = "opentelemetry-sdk", marker = "extra == 'otel'" },
|
|
545
545
|
{ name = "pandas", specifier = ">=2.2.2" },
|
|
546
|
-
{ name = "pdmt5", specifier = ">=1.1.
|
|
546
|
+
{ name = "pdmt5", specifier = ">=1.1.1" },
|
|
547
547
|
{ name = "pyarrow", marker = "extra == 'parquet'", specifier = ">=19.0.0" },
|
|
548
548
|
{ name = "pydantic", specifier = ">=2.13.4" },
|
|
549
549
|
{ name = "typer", specifier = ">=0.15.0" },
|
|
@@ -800,16 +800,16 @@ wheels = [
|
|
|
800
800
|
|
|
801
801
|
[[package]]
|
|
802
802
|
name = "pdmt5"
|
|
803
|
-
version = "1.1.
|
|
803
|
+
version = "1.1.1"
|
|
804
804
|
source = { registry = "https://pypi.org/simple" }
|
|
805
805
|
dependencies = [
|
|
806
806
|
{ name = "metatrader5", marker = "sys_platform == 'win32'" },
|
|
807
807
|
{ name = "pandas" },
|
|
808
808
|
{ name = "pydantic" },
|
|
809
809
|
]
|
|
810
|
-
sdist = { url = "https://files.pythonhosted.org/packages/
|
|
810
|
+
sdist = { url = "https://files.pythonhosted.org/packages/4d/9c/4c51e9b109440629762a5ca719c367798469bb590b8ce99aa0652c467549/pdmt5-1.1.1.tar.gz", hash = "sha256:2d8e4e9b87d237793f8534813b373545eadb5c0e1ced13ee8be8dc473c77190d", size = 23879, upload-time = "2026-07-07T15:50:19.604Z" }
|
|
811
811
|
wheels = [
|
|
812
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
812
|
+
{ url = "https://files.pythonhosted.org/packages/8f/18/4221deef65dc320da9b6299e1914c36c1e81b29a5557f0c1083718b36545/pdmt5-1.1.1-py3-none-any.whl", hash = "sha256:9df16148ebec754c15ce3d1b02af82aaeec3c5cb1603a47258fa68c74f45b453", size = 22274, upload-time = "2026-07-07T15:50:18.489Z" },
|
|
813
813
|
]
|
|
814
814
|
|
|
815
815
|
[[package]]
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|