mt5cli 1.0.1__tar.gz → 1.0.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.
- {mt5cli-1.0.1 → mt5cli-1.0.3}/PKG-INFO +30 -30
- {mt5cli-1.0.1 → mt5cli-1.0.3}/README.md +29 -29
- {mt5cli-1.0.1 → mt5cli-1.0.3}/docs/index.md +22 -22
- {mt5cli-1.0.1 → mt5cli-1.0.3}/mt5cli/cli.py +9 -6
- {mt5cli-1.0.1 → mt5cli-1.0.3}/mt5cli/history.py +9 -3
- {mt5cli-1.0.1 → mt5cli-1.0.3}/mt5cli/sdk.py +7 -4
- {mt5cli-1.0.1 → mt5cli-1.0.3}/mt5cli/trading.py +10 -2
- {mt5cli-1.0.1 → mt5cli-1.0.3}/pyproject.toml +1 -1
- {mt5cli-1.0.1 → mt5cli-1.0.3}/tests/test_cli.py +43 -6
- {mt5cli-1.0.1 → mt5cli-1.0.3}/tests/test_history.py +16 -2
- {mt5cli-1.0.1 → mt5cli-1.0.3}/tests/test_sdk.py +64 -3
- {mt5cli-1.0.1 → mt5cli-1.0.3}/tests/test_trading.py +88 -0
- {mt5cli-1.0.1 → mt5cli-1.0.3}/uv.lock +2 -2
- {mt5cli-1.0.1 → mt5cli-1.0.3}/.agents/skills/local-qa/SKILL.md +0 -0
- {mt5cli-1.0.1 → mt5cli-1.0.3}/.agents/skills/local-qa/scripts/qa.sh +0 -0
- {mt5cli-1.0.1 → mt5cli-1.0.3}/.agents/skills/mt5cli/SKILL.md +0 -0
- {mt5cli-1.0.1 → mt5cli-1.0.3}/.agents/skills/pr-feedback-triage/SKILL.md +0 -0
- {mt5cli-1.0.1 → mt5cli-1.0.3}/.claude/agents/codex.md +0 -0
- {mt5cli-1.0.1 → mt5cli-1.0.3}/.claude/settings.json +0 -0
- {mt5cli-1.0.1 → mt5cli-1.0.3}/.github/FUNDING.yml +0 -0
- {mt5cli-1.0.1 → mt5cli-1.0.3}/.github/dependabot.yml +0 -0
- {mt5cli-1.0.1 → mt5cli-1.0.3}/.github/renovate.json +0 -0
- {mt5cli-1.0.1 → mt5cli-1.0.3}/.github/workflows/ci.yml +0 -0
- {mt5cli-1.0.1 → mt5cli-1.0.3}/.github/workflows/claude.yml +0 -0
- {mt5cli-1.0.1 → mt5cli-1.0.3}/.github/workflows/release.yml +0 -0
- {mt5cli-1.0.1 → mt5cli-1.0.3}/.gitignore +0 -0
- {mt5cli-1.0.1 → mt5cli-1.0.3}/AGENTS.md +0 -0
- {mt5cli-1.0.1 → mt5cli-1.0.3}/CLAUDE.md +0 -0
- {mt5cli-1.0.1 → mt5cli-1.0.3}/LICENSE +0 -0
- {mt5cli-1.0.1 → mt5cli-1.0.3}/docs/api/cli.md +0 -0
- {mt5cli-1.0.1 → mt5cli-1.0.3}/docs/api/client.md +0 -0
- {mt5cli-1.0.1 → mt5cli-1.0.3}/docs/api/converters.md +0 -0
- {mt5cli-1.0.1 → mt5cli-1.0.3}/docs/api/exceptions.md +0 -0
- {mt5cli-1.0.1 → mt5cli-1.0.3}/docs/api/history.md +0 -0
- {mt5cli-1.0.1 → mt5cli-1.0.3}/docs/api/index.md +0 -0
- {mt5cli-1.0.1 → mt5cli-1.0.3}/docs/api/public-contract.md +0 -0
- {mt5cli-1.0.1 → mt5cli-1.0.3}/docs/api/schemas.md +0 -0
- {mt5cli-1.0.1 → mt5cli-1.0.3}/docs/api/sdk.md +0 -0
- {mt5cli-1.0.1 → mt5cli-1.0.3}/docs/api/trading.md +0 -0
- {mt5cli-1.0.1 → mt5cli-1.0.3}/docs/api/utils.md +0 -0
- {mt5cli-1.0.1 → mt5cli-1.0.3}/mkdocs.yml +0 -0
- {mt5cli-1.0.1 → mt5cli-1.0.3}/mt5cli/__init__.py +0 -0
- {mt5cli-1.0.1 → mt5cli-1.0.3}/mt5cli/__main__.py +0 -0
- {mt5cli-1.0.1 → mt5cli-1.0.3}/mt5cli/client.py +0 -0
- {mt5cli-1.0.1 → mt5cli-1.0.3}/mt5cli/contract.py +0 -0
- {mt5cli-1.0.1 → mt5cli-1.0.3}/mt5cli/converters.py +0 -0
- {mt5cli-1.0.1 → mt5cli-1.0.3}/mt5cli/exceptions.py +0 -0
- {mt5cli-1.0.1 → mt5cli-1.0.3}/mt5cli/retry.py +0 -0
- {mt5cli-1.0.1 → mt5cli-1.0.3}/mt5cli/schemas.py +0 -0
- {mt5cli-1.0.1 → mt5cli-1.0.3}/mt5cli/utils.py +0 -0
- {mt5cli-1.0.1 → mt5cli-1.0.3}/tests/__init__.py +0 -0
- {mt5cli-1.0.1 → mt5cli-1.0.3}/tests/conftest.py +0 -0
- {mt5cli-1.0.1 → mt5cli-1.0.3}/tests/test_contracts.py +0 -0
- {mt5cli-1.0.1 → mt5cli-1.0.3}/tests/test_utils.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: mt5cli
|
|
3
|
-
Version: 1.0.
|
|
3
|
+
Version: 1.0.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>
|
|
@@ -182,34 +182,34 @@ python -m mt5cli -o account.csv account-info
|
|
|
182
182
|
|
|
183
183
|
## Commands
|
|
184
184
|
|
|
185
|
-
| Command | Description
|
|
186
|
-
| ---------------------- |
|
|
187
|
-
| `rates-from` | Export rates from a start date
|
|
188
|
-
| `rates-from-pos` | Export rates from a start position
|
|
189
|
-
| `latest-rates` | Export latest rates from a start position
|
|
190
|
-
| `rates-range` | Export rates for a date range
|
|
191
|
-
| `ticks-from` | Export ticks from a start date
|
|
192
|
-
| `ticks-range` | Export ticks for a date range
|
|
193
|
-
| `ticks-recent` | Export ticks from a recent trailing window
|
|
194
|
-
| `account-info` | Export account information
|
|
195
|
-
| `terminal-info` | Export terminal information
|
|
196
|
-
| `version` | Export MetaTrader 5 version information
|
|
197
|
-
| `last-error` | Export the last error information
|
|
198
|
-
| `symbols` | Export symbol list
|
|
199
|
-
| `symbol-info` | Export symbol details
|
|
200
|
-
| `symbol-info-tick` | Export the last tick for a symbol
|
|
201
|
-
| `minimum-margins` | Export minimum-volume buy and sell margin requirements
|
|
202
|
-
| `market-book` | Export market depth (order book)
|
|
203
|
-
| `orders` | Export active orders
|
|
204
|
-
| `positions` | Export open positions
|
|
205
|
-
| `history-orders` | Export historical orders
|
|
206
|
-
| `history-deals` | Export historical deals
|
|
207
|
-
| `recent-history-deals` | Export historical deals from a recent trailing window
|
|
208
|
-
| `mt5-summary` | Export terminal/account status summary
|
|
209
|
-
| `order-check` | Check funds sufficiency for a trade request
|
|
210
|
-
| `order-send` | Send a raw trade request to the trade server (`--yes` required; expert path)
|
|
211
|
-
| `close-positions` | Close open positions by `--symbol` or `--ticket` (`--yes` required for live; `--dry-run` available)
|
|
212
|
-
| `collect-history` |
|
|
185
|
+
| Command | Description |
|
|
186
|
+
| ---------------------- | ------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
187
|
+
| `rates-from` | Export rates from a start date |
|
|
188
|
+
| `rates-from-pos` | Export rates from a start position |
|
|
189
|
+
| `latest-rates` | Export latest rates from a start position |
|
|
190
|
+
| `rates-range` | Export rates for a date range |
|
|
191
|
+
| `ticks-from` | Export ticks from a start date |
|
|
192
|
+
| `ticks-range` | Export ticks for a date range |
|
|
193
|
+
| `ticks-recent` | Export ticks from a recent trailing window |
|
|
194
|
+
| `account-info` | Export account information |
|
|
195
|
+
| `terminal-info` | Export terminal information |
|
|
196
|
+
| `version` | Export MetaTrader 5 version information |
|
|
197
|
+
| `last-error` | Export the last error information |
|
|
198
|
+
| `symbols` | Export symbol list |
|
|
199
|
+
| `symbol-info` | Export symbol details |
|
|
200
|
+
| `symbol-info-tick` | Export the last tick for a symbol |
|
|
201
|
+
| `minimum-margins` | Export minimum-volume buy and sell margin requirements |
|
|
202
|
+
| `market-book` | Export market depth (order book) |
|
|
203
|
+
| `orders` | Export active orders |
|
|
204
|
+
| `positions` | Export open positions |
|
|
205
|
+
| `history-orders` | Export historical orders |
|
|
206
|
+
| `history-deals` | Export historical deals |
|
|
207
|
+
| `recent-history-deals` | Export historical deals from a recent trailing window |
|
|
208
|
+
| `mt5-summary` | Export terminal/account status summary |
|
|
209
|
+
| `order-check` | Check funds sufficiency for a trade request |
|
|
210
|
+
| `order-send` | Send a raw trade request to the trade server (`--yes` required; expert path) |
|
|
211
|
+
| `close-positions` | Close open positions by `--symbol` or `--ticket` (`--yes` required for live; `--dry-run` available) |
|
|
212
|
+
| `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`) |
|
|
213
213
|
|
|
214
214
|
Use `order-check` to validate a request payload before running `order-send --yes`.
|
|
215
215
|
`close-positions` is the safer high-level alternative that builds correct close
|
|
@@ -217,7 +217,7 @@ requests automatically. At least one `--symbol` or `--ticket` must be provided.
|
|
|
217
217
|
|
|
218
218
|
### `collect-history`
|
|
219
219
|
|
|
220
|
-
Collect several historical datasets per symbol into one SQLite database in a single MT5 session. Pick datasets with repeatable `--dataset` (default:
|
|
220
|
+
Collect several historical datasets per symbol into one SQLite database in a single MT5 session. Pick datasets with repeatable `--dataset` (default: `rates`, `history-orders`, `history-deals`; add `--dataset ticks` when tick-level history is required — tick data can grow the SQLite database quickly), choose conflict behavior with `--if-exists append|replace|fail` (default: `fail`), and optionally derive `cash_events` / `positions_reconstructed` views from `history_deals` via `--with-views`.
|
|
221
221
|
|
|
222
222
|
```bash
|
|
223
223
|
mt5cli -o history.db collect-history \
|
|
@@ -157,34 +157,34 @@ python -m mt5cli -o account.csv account-info
|
|
|
157
157
|
|
|
158
158
|
## Commands
|
|
159
159
|
|
|
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` |
|
|
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
188
|
|
|
189
189
|
Use `order-check` to validate a request payload before running `order-send --yes`.
|
|
190
190
|
`close-positions` is the safer high-level alternative that builds correct close
|
|
@@ -192,7 +192,7 @@ requests automatically. At least one `--symbol` or `--ticket` must be provided.
|
|
|
192
192
|
|
|
193
193
|
### `collect-history`
|
|
194
194
|
|
|
195
|
-
Collect several historical datasets per symbol into one SQLite database in a single MT5 session. Pick datasets with repeatable `--dataset` (default:
|
|
195
|
+
Collect several historical datasets per symbol into one SQLite database in a single MT5 session. Pick datasets with repeatable `--dataset` (default: `rates`, `history-orders`, `history-deals`; add `--dataset ticks` when tick-level history is required — tick data can grow the SQLite database quickly), choose conflict behavior with `--if-exists append|replace|fail` (default: `fail`), and optionally derive `cash_events` / `positions_reconstructed` views from `history_deals` via `--with-views`.
|
|
196
196
|
|
|
197
197
|
```bash
|
|
198
198
|
mt5cli -o history.db collect-history \
|
|
@@ -149,15 +149,15 @@ mt5cli --login 12345 --password mypass --server MyBroker-Demo \
|
|
|
149
149
|
|
|
150
150
|
### Trading State
|
|
151
151
|
|
|
152
|
-
| Command | Description
|
|
153
|
-
| ---------------------- |
|
|
154
|
-
| `orders` | Export active orders
|
|
155
|
-
| `positions` | Export open positions
|
|
156
|
-
| `history-orders` | Export historical orders
|
|
157
|
-
| `history-deals` | Export historical deals
|
|
158
|
-
| `recent-history-deals` | Export historical deals from a trailing window
|
|
159
|
-
| `mt5-summary` | Export terminal/account status summary
|
|
160
|
-
| `order-check` | Check funds sufficiency for a trade request (read-only, no `--yes`)
|
|
152
|
+
| Command | Description |
|
|
153
|
+
| ---------------------- | ------------------------------------------------------------------- |
|
|
154
|
+
| `orders` | Export active orders |
|
|
155
|
+
| `positions` | Export open positions |
|
|
156
|
+
| `history-orders` | Export historical orders |
|
|
157
|
+
| `history-deals` | Export historical deals |
|
|
158
|
+
| `recent-history-deals` | Export historical deals from a trailing window |
|
|
159
|
+
| `mt5-summary` | Export terminal/account status summary |
|
|
160
|
+
| `order-check` | Check funds sufficiency for a trade request (read-only, no `--yes`) |
|
|
161
161
|
|
|
162
162
|
### Execution (live / mutating)
|
|
163
163
|
|
|
@@ -176,9 +176,9 @@ applications should prefer dedicated closing helpers or their own risk controls.
|
|
|
176
176
|
|
|
177
177
|
### Bulk Collection
|
|
178
178
|
|
|
179
|
-
| Command | Description
|
|
180
|
-
| ----------------- |
|
|
181
|
-
| `collect-history` | Collect rates,
|
|
179
|
+
| Command | Description |
|
|
180
|
+
| ----------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
181
|
+
| `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) |
|
|
182
182
|
|
|
183
183
|
```bash
|
|
184
184
|
mt5cli -o history.db collect-history \
|
|
@@ -190,16 +190,16 @@ mt5cli -o history.db collect-history \
|
|
|
190
190
|
|
|
191
191
|
`collect-history` options:
|
|
192
192
|
|
|
193
|
-
| Option | Default
|
|
194
|
-
| -------------- |
|
|
195
|
-
| `--symbol/-s` | _required_
|
|
196
|
-
| `--date-from` | _required_
|
|
197
|
-
| `--date-to` | _required_
|
|
198
|
-
| `--dataset` |
|
|
199
|
-
| `--timeframe` | `M1`
|
|
200
|
-
| `--flags` | `ALL`
|
|
201
|
-
| `--if-exists` | `fail`
|
|
202
|
-
| `--with-views` | off
|
|
193
|
+
| Option | Default | Description |
|
|
194
|
+
| -------------- | ------------------------------------ | -------------------------------------------------------------------------------------------------------------------------- |
|
|
195
|
+
| `--symbol/-s` | _required_ | Symbol to collect (repeat for multiple). |
|
|
196
|
+
| `--date-from` | _required_ | Start date in ISO 8601. |
|
|
197
|
+
| `--date-to` | _required_ | End date in ISO 8601. |
|
|
198
|
+
| `--dataset` | rates, history-orders, history-deals | Repeatable: `rates`, `ticks`, `history-orders`, `history-deals`. Ticks are opt-in: pass `--dataset ticks` to include them. |
|
|
199
|
+
| `--timeframe` | `M1` | Rates timeframe; recorded in a `timeframe` column on the `rates` table. |
|
|
200
|
+
| `--flags` | `ALL` | Tick copy flags forwarded to `copy_ticks_range`. |
|
|
201
|
+
| `--if-exists` | `fail` | `append`, `replace`, or `fail` when a target table already exists. |
|
|
202
|
+
| `--with-views` | off | Add `cash_events` and `positions_reconstructed` views (requires the `history-deals` dataset). |
|
|
203
203
|
|
|
204
204
|
History orders and deals are fetched per symbol and concatenated, so the symbol filter is applied consistently across all datasets. The `cash_events` view is derived from symbol-filtered `history_deals`, so account-level cash events with empty or non-matching symbols may be excluded. The `positions_reconstructed` view excludes positions with no closing deal, uses volume-weighted open/close prices, and reports reversal deals (`DEAL_ENTRY_INOUT`) via `volume_reversal` / `reversal_count`.
|
|
205
205
|
|
|
@@ -727,7 +727,8 @@ def collect_history(
|
|
|
727
727
|
"--dataset",
|
|
728
728
|
help=(
|
|
729
729
|
"Dataset to include (repeat for multiple)."
|
|
730
|
-
" Defaults to
|
|
730
|
+
" Defaults to rates, history-orders, history-deals."
|
|
731
|
+
" Ticks are opt-in: pass --dataset ticks to include them."
|
|
731
732
|
),
|
|
732
733
|
),
|
|
733
734
|
] = None,
|
|
@@ -765,10 +766,12 @@ def collect_history(
|
|
|
765
766
|
) -> None:
|
|
766
767
|
"""Collect historical datasets into a single SQLite database.
|
|
767
768
|
|
|
768
|
-
Tables written depend on ``--dataset``: ``rates``, ``
|
|
769
|
-
``
|
|
770
|
-
|
|
771
|
-
|
|
769
|
+
Tables written depend on ``--dataset``: ``rates``, ``history_orders``,
|
|
770
|
+
``history_deals`` by default. ``ticks`` are opt-in: pass
|
|
771
|
+
``--dataset ticks`` to include them (tick data grows the database quickly).
|
|
772
|
+
History datasets are fetched per symbol and concatenated. Rates rows carry
|
|
773
|
+
the requested ``timeframe`` so appended runs at different timeframes remain
|
|
774
|
+
distinguishable.
|
|
772
775
|
|
|
773
776
|
With ``--with-views`` (requires the ``history-deals`` dataset), optional
|
|
774
777
|
views ``cash_events`` and ``positions_reconstructed`` are derived from
|
|
@@ -784,7 +787,7 @@ def collect_history(
|
|
|
784
787
|
" Use a .db/.sqlite/.sqlite3 extension or --format sqlite3."
|
|
785
788
|
)
|
|
786
789
|
raise typer.BadParameter(msg)
|
|
787
|
-
datasets = set(dataset) if dataset else
|
|
790
|
+
datasets = set(dataset) if dataset is not None else None
|
|
788
791
|
sdk.collect_history(
|
|
789
792
|
output=export_ctx.output,
|
|
790
793
|
symbols=symbol,
|
|
@@ -30,6 +30,11 @@ if TYPE_CHECKING:
|
|
|
30
30
|
logger = logging.getLogger(__name__)
|
|
31
31
|
|
|
32
32
|
DEFAULT_HISTORY_TIMEFRAMES: tuple[str, ...] = TIMEFRAME_NAMES
|
|
33
|
+
DEFAULT_HISTORY_DATASETS: frozenset[Dataset] = frozenset({
|
|
34
|
+
Dataset.rates,
|
|
35
|
+
Dataset.history_orders,
|
|
36
|
+
Dataset.history_deals,
|
|
37
|
+
})
|
|
33
38
|
|
|
34
39
|
_HISTORY_DEDUP_KEYS: dict[Dataset, tuple[tuple[str, ...], ...]] = {
|
|
35
40
|
Dataset.rates: DEDUP_KEYS[DataKind.rates],
|
|
@@ -62,11 +67,12 @@ def resolve_history_datasets(datasets: set[Dataset] | None) -> set[Dataset]:
|
|
|
62
67
|
"""Resolve configured history datasets.
|
|
63
68
|
|
|
64
69
|
Returns:
|
|
65
|
-
|
|
66
|
-
configured selection (which
|
|
70
|
+
``DEFAULT_HISTORY_DATASETS`` (rates, history-orders, history-deals)
|
|
71
|
+
when ``datasets`` is None, otherwise the configured selection (which
|
|
72
|
+
may be empty or explicitly include ``Dataset.ticks``).
|
|
67
73
|
"""
|
|
68
74
|
if datasets is None:
|
|
69
|
-
return set(
|
|
75
|
+
return set(DEFAULT_HISTORY_DATASETS)
|
|
70
76
|
return set(datasets)
|
|
71
77
|
|
|
72
78
|
|
|
@@ -981,7 +981,8 @@ def update_history( # noqa: PLR0913
|
|
|
981
981
|
client: Connected MT5 data client.
|
|
982
982
|
output: SQLite database path.
|
|
983
983
|
symbols: Symbols to update.
|
|
984
|
-
datasets: Datasets to include (defaults to
|
|
984
|
+
datasets: Datasets to include (defaults to rates, history-orders,
|
|
985
|
+
history-deals; pass ``{Dataset.ticks}`` to opt in to ticks).
|
|
985
986
|
timeframes: Rate timeframes to update (defaults to all fixed MT5
|
|
986
987
|
timeframes when None).
|
|
987
988
|
flags: Tick copy flags as integer or name (e.g. ``ALL``).
|
|
@@ -1110,7 +1111,8 @@ class ThrottledHistoryUpdater:
|
|
|
1110
1111
|
|
|
1111
1112
|
Args:
|
|
1112
1113
|
output: SQLite database path.
|
|
1113
|
-
datasets: Datasets to include (defaults to
|
|
1114
|
+
datasets: Datasets to include (defaults to rates, history-orders,
|
|
1115
|
+
history-deals; pass ``{Dataset.ticks}`` to opt in to ticks).
|
|
1114
1116
|
timeframes: Rate timeframes to update (defaults to all fixed MT5
|
|
1115
1117
|
timeframes).
|
|
1116
1118
|
flags: Tick copy flags as integer or name (e.g. ``ALL``).
|
|
@@ -1242,7 +1244,8 @@ def collect_history(
|
|
|
1242
1244
|
symbols: Symbols to collect.
|
|
1243
1245
|
date_from: Start date.
|
|
1244
1246
|
date_to: End date.
|
|
1245
|
-
datasets: Datasets to include (defaults to
|
|
1247
|
+
datasets: Datasets to include (defaults to rates, history-orders,
|
|
1248
|
+
history-deals; pass ``{Dataset.ticks}`` to opt in to ticks).
|
|
1246
1249
|
timeframe: Rates timeframe as integer or name (e.g. ``M1``).
|
|
1247
1250
|
flags: Tick copy flags as integer or name (e.g. ``ALL``).
|
|
1248
1251
|
if_exists: Behavior when a target table already exists.
|
|
@@ -1251,7 +1254,7 @@ def collect_history(
|
|
|
1251
1254
|
"""
|
|
1252
1255
|
start = _require_datetime(date_from)
|
|
1253
1256
|
end = _require_datetime(date_to)
|
|
1254
|
-
selected = datasets
|
|
1257
|
+
selected = resolve_history_datasets(datasets)
|
|
1255
1258
|
tf = _coerce_timeframe(timeframe)
|
|
1256
1259
|
tick_flags = _coerce_tick_flags(flags)
|
|
1257
1260
|
mt5_config = config or build_config()
|
|
@@ -15,6 +15,7 @@ from .exceptions import Mt5OperationError
|
|
|
15
15
|
from .history import drop_forming_rate_bar
|
|
16
16
|
from .sdk import build_config
|
|
17
17
|
from .utils import coerce_login as _coerce_login
|
|
18
|
+
from .utils import parse_timeframe
|
|
18
19
|
|
|
19
20
|
if TYPE_CHECKING:
|
|
20
21
|
from collections.abc import Iterator, Mapping, Sequence
|
|
@@ -1624,11 +1625,18 @@ def fetch_latest_closed_rates_for_trading_client(
|
|
|
1624
1625
|
if count <= 0:
|
|
1625
1626
|
msg = "count must be positive."
|
|
1626
1627
|
raise ValueError(msg)
|
|
1628
|
+
timeframe = parse_timeframe(granularity)
|
|
1627
1629
|
fetch_method = getattr(client, "fetch_latest_rates_as_df", None)
|
|
1628
|
-
|
|
1630
|
+
copy_method = getattr(client, "copy_rates_from_pos_as_df", None)
|
|
1631
|
+
if callable(fetch_method):
|
|
1632
|
+
fetched = fetch_method(symbol, granularity, count + 1)
|
|
1633
|
+
elif callable(copy_method):
|
|
1634
|
+
fetched = copy_method(
|
|
1635
|
+
symbol=symbol, timeframe=timeframe, start_pos=0, count=count + 1
|
|
1636
|
+
)
|
|
1637
|
+
else:
|
|
1629
1638
|
msg = "MT5 trading client cannot fetch rate data."
|
|
1630
1639
|
raise Mt5OperationError(msg)
|
|
1631
|
-
fetched = fetch_method(symbol, granularity, count + 1)
|
|
1632
1640
|
if not isinstance(fetched, pd.DataFrame):
|
|
1633
1641
|
msg = (
|
|
1634
1642
|
f"Malformed rate data for {symbol!r} at granularity {granularity!r}: "
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "mt5cli"
|
|
3
|
-
version = "1.0.
|
|
3
|
+
version = "1.0.3"
|
|
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"}]
|
|
@@ -1298,12 +1298,12 @@ class TestCollectHistory:
|
|
|
1298
1298
|
"""Create a mocked Mt5DataClient with history-style DataFrames."""
|
|
1299
1299
|
return _build_history_client(mocker)
|
|
1300
1300
|
|
|
1301
|
-
def
|
|
1301
|
+
def test_collect_history_writes_default_tables(
|
|
1302
1302
|
self,
|
|
1303
1303
|
tmp_path: Path,
|
|
1304
1304
|
history_client: MagicMock,
|
|
1305
1305
|
) -> None:
|
|
1306
|
-
"""Test that collect-history
|
|
1306
|
+
"""Test that collect-history default excludes ticks."""
|
|
1307
1307
|
output = tmp_path / "history.db"
|
|
1308
1308
|
result = runner.invoke(
|
|
1309
1309
|
app,
|
|
@@ -1323,8 +1323,42 @@ class TestCollectHistory:
|
|
|
1323
1323
|
)
|
|
1324
1324
|
assert result.exit_code == 0, result.output
|
|
1325
1325
|
assert history_client.copy_rates_range_as_df.call_count == 2
|
|
1326
|
-
assert history_client.copy_ticks_range_as_df.call_count ==
|
|
1327
|
-
|
|
1326
|
+
assert history_client.copy_ticks_range_as_df.call_count == 0
|
|
1327
|
+
with sqlite3.connect(output) as conn:
|
|
1328
|
+
tables = {
|
|
1329
|
+
row[0]
|
|
1330
|
+
for row in conn.execute(
|
|
1331
|
+
"SELECT name FROM sqlite_master WHERE type='table'",
|
|
1332
|
+
).fetchall()
|
|
1333
|
+
}
|
|
1334
|
+
assert {"rates", "history_orders", "history_deals"} <= tables
|
|
1335
|
+
assert "ticks" not in tables
|
|
1336
|
+
|
|
1337
|
+
def test_collect_history_explicit_ticks_dataset(
|
|
1338
|
+
self,
|
|
1339
|
+
tmp_path: Path,
|
|
1340
|
+
history_client: MagicMock,
|
|
1341
|
+
) -> None:
|
|
1342
|
+
"""Test that --dataset ticks writes the ticks table with the correct flags."""
|
|
1343
|
+
output = tmp_path / "history.db"
|
|
1344
|
+
result = runner.invoke(
|
|
1345
|
+
app,
|
|
1346
|
+
[
|
|
1347
|
+
"-o",
|
|
1348
|
+
str(output),
|
|
1349
|
+
"collect-history",
|
|
1350
|
+
"--symbol",
|
|
1351
|
+
"EURUSD",
|
|
1352
|
+
"--date-from",
|
|
1353
|
+
"2024-01-01",
|
|
1354
|
+
"--date-to",
|
|
1355
|
+
"2024-02-01",
|
|
1356
|
+
"--dataset",
|
|
1357
|
+
"ticks",
|
|
1358
|
+
],
|
|
1359
|
+
)
|
|
1360
|
+
assert result.exit_code == 0, result.output
|
|
1361
|
+
history_client.copy_ticks_range_as_df.assert_called_once_with(
|
|
1328
1362
|
symbol="EURUSD",
|
|
1329
1363
|
date_from=datetime(2024, 1, 1, tzinfo=UTC),
|
|
1330
1364
|
date_to=datetime(2024, 2, 1, tzinfo=UTC),
|
|
@@ -1337,7 +1371,8 @@ class TestCollectHistory:
|
|
|
1337
1371
|
"SELECT name FROM sqlite_master WHERE type='table'",
|
|
1338
1372
|
).fetchall()
|
|
1339
1373
|
}
|
|
1340
|
-
assert
|
|
1374
|
+
assert "ticks" in tables
|
|
1375
|
+
assert "rates" not in tables
|
|
1341
1376
|
|
|
1342
1377
|
def test_collect_history_history_fetched_per_symbol(
|
|
1343
1378
|
self,
|
|
@@ -1520,7 +1555,7 @@ class TestCollectHistory:
|
|
|
1520
1555
|
tmp_path: Path,
|
|
1521
1556
|
history_client: MagicMock,
|
|
1522
1557
|
) -> None:
|
|
1523
|
-
"""Test that --flags defaults to ALL
|
|
1558
|
+
"""Test that --flags defaults to ALL when --dataset ticks is explicit."""
|
|
1524
1559
|
output = tmp_path / "history.db"
|
|
1525
1560
|
result = runner.invoke(
|
|
1526
1561
|
app,
|
|
@@ -1534,6 +1569,8 @@ class TestCollectHistory:
|
|
|
1534
1569
|
"2024-01-01",
|
|
1535
1570
|
"--date-to",
|
|
1536
1571
|
"2024-02-01",
|
|
1572
|
+
"--dataset",
|
|
1573
|
+
"ticks",
|
|
1537
1574
|
],
|
|
1538
1575
|
)
|
|
1539
1576
|
assert result.exit_code == 0, result.output
|
|
@@ -19,6 +19,7 @@ from pdmt5 import TIMEFRAME_MAP
|
|
|
19
19
|
|
|
20
20
|
from mt5cli import history
|
|
21
21
|
from mt5cli.history import (
|
|
22
|
+
DEFAULT_HISTORY_DATASETS,
|
|
22
23
|
DEFAULT_HISTORY_TIMEFRAMES,
|
|
23
24
|
DedupScope,
|
|
24
25
|
RateTarget,
|
|
@@ -547,10 +548,23 @@ class TestResolveHistorySettings:
|
|
|
547
548
|
"""Tests for history dataset and timeframe resolution."""
|
|
548
549
|
|
|
549
550
|
def test_resolve_history_datasets_defaults_and_empty(self) -> None:
|
|
550
|
-
"""Test dataset resolution
|
|
551
|
-
|
|
551
|
+
"""Test dataset resolution excludes ticks by default."""
|
|
552
|
+
resolved = resolve_history_datasets(None)
|
|
553
|
+
assert resolved == set(DEFAULT_HISTORY_DATASETS)
|
|
554
|
+
assert Dataset.ticks not in resolved
|
|
555
|
+
assert {
|
|
556
|
+
Dataset.rates,
|
|
557
|
+
Dataset.history_orders,
|
|
558
|
+
Dataset.history_deals,
|
|
559
|
+
} == resolved
|
|
552
560
|
assert resolve_history_datasets(set()) == set()
|
|
553
561
|
|
|
562
|
+
def test_resolve_history_datasets_explicit_ticks(self) -> None:
|
|
563
|
+
"""Test that explicit ticks selection is honored."""
|
|
564
|
+
assert resolve_history_datasets({Dataset.ticks}) == {Dataset.ticks}
|
|
565
|
+
all_ds = resolve_history_datasets(set(Dataset))
|
|
566
|
+
assert Dataset.ticks in all_ds
|
|
567
|
+
|
|
554
568
|
def test_resolve_history_timeframes_defaults(self) -> None:
|
|
555
569
|
"""Test default timeframes include all fixed MT5 values."""
|
|
556
570
|
resolved = resolve_history_timeframes(None)
|
|
@@ -618,12 +618,12 @@ class TestCollectHistory:
|
|
|
618
618
|
"""Create a mocked Mt5DataClient with history-style DataFrames."""
|
|
619
619
|
return _build_history_client(mocker)
|
|
620
620
|
|
|
621
|
-
def
|
|
621
|
+
def test_collect_history_writes_default_tables(
|
|
622
622
|
self,
|
|
623
623
|
tmp_path: Path,
|
|
624
624
|
history_client: MagicMock,
|
|
625
625
|
) -> None:
|
|
626
|
-
"""Test that collect_history
|
|
626
|
+
"""Test that collect_history default excludes ticks."""
|
|
627
627
|
output = tmp_path / "history.db"
|
|
628
628
|
collect_history(
|
|
629
629
|
output,
|
|
@@ -632,7 +632,33 @@ class TestCollectHistory:
|
|
|
632
632
|
"2024-02-01",
|
|
633
633
|
)
|
|
634
634
|
assert history_client.copy_rates_range_as_df.call_count == 2
|
|
635
|
+
assert history_client.copy_ticks_range_as_df.call_count == 0
|
|
636
|
+
with sqlite3.connect(output) as conn:
|
|
637
|
+
tables = {
|
|
638
|
+
row[0]
|
|
639
|
+
for row in conn.execute(
|
|
640
|
+
"SELECT name FROM sqlite_master WHERE type='table'",
|
|
641
|
+
).fetchall()
|
|
642
|
+
}
|
|
643
|
+
assert {"rates", "history_orders", "history_deals"} <= tables
|
|
644
|
+
assert "ticks" not in tables
|
|
645
|
+
|
|
646
|
+
def test_collect_history_explicit_ticks_dataset(
|
|
647
|
+
self,
|
|
648
|
+
tmp_path: Path,
|
|
649
|
+
history_client: MagicMock,
|
|
650
|
+
) -> None:
|
|
651
|
+
"""Test that explicit datasets={Dataset.ticks} writes the ticks table."""
|
|
652
|
+
output = tmp_path / "history.db"
|
|
653
|
+
collect_history(
|
|
654
|
+
output,
|
|
655
|
+
["EURUSD", "GBPUSD"],
|
|
656
|
+
"2024-01-01",
|
|
657
|
+
"2024-02-01",
|
|
658
|
+
datasets={Dataset.ticks},
|
|
659
|
+
)
|
|
635
660
|
assert history_client.copy_ticks_range_as_df.call_count == 2
|
|
661
|
+
assert history_client.copy_rates_range_as_df.call_count == 0
|
|
636
662
|
with sqlite3.connect(output) as conn:
|
|
637
663
|
tables = {
|
|
638
664
|
row[0]
|
|
@@ -640,7 +666,8 @@ class TestCollectHistory:
|
|
|
640
666
|
"SELECT name FROM sqlite_master WHERE type='table'",
|
|
641
667
|
).fetchall()
|
|
642
668
|
}
|
|
643
|
-
assert
|
|
669
|
+
assert "ticks" in tables
|
|
670
|
+
assert "rates" not in tables
|
|
644
671
|
|
|
645
672
|
def test_collect_history_with_views(
|
|
646
673
|
self,
|
|
@@ -1066,6 +1093,40 @@ class TestUpdateHistory:
|
|
|
1066
1093
|
after = datetime.now(UTC)
|
|
1067
1094
|
assert before <= captured["end"] <= after
|
|
1068
1095
|
|
|
1096
|
+
def test_update_history_default_datasets_exclude_ticks(
|
|
1097
|
+
self,
|
|
1098
|
+
connected_client: MagicMock,
|
|
1099
|
+
mocker: MockerFixture,
|
|
1100
|
+
tmp_path: Path,
|
|
1101
|
+
) -> None:
|
|
1102
|
+
"""Test update_history with datasets=None does not collect ticks."""
|
|
1103
|
+
datasets_written: list[set[Dataset]] = []
|
|
1104
|
+
|
|
1105
|
+
def capture(
|
|
1106
|
+
*args: object,
|
|
1107
|
+
**_kwargs: object,
|
|
1108
|
+
) -> tuple[set[Dataset], dict[Dataset, set[str]]]:
|
|
1109
|
+
datasets_written.append(args[3]) # type: ignore[arg-type]
|
|
1110
|
+
return set(), {}
|
|
1111
|
+
|
|
1112
|
+
mocker.patch("mt5cli.sdk.write_incremental_datasets", side_effect=capture)
|
|
1113
|
+
update_history(
|
|
1114
|
+
client=connected_client,
|
|
1115
|
+
output=tmp_path / "default-datasets.db",
|
|
1116
|
+
symbols=["EURUSD"],
|
|
1117
|
+
datasets=None,
|
|
1118
|
+
timeframes=["M1"],
|
|
1119
|
+
lookback_hours=1,
|
|
1120
|
+
date_to=datetime(2024, 1, 1, tzinfo=UTC),
|
|
1121
|
+
)
|
|
1122
|
+
assert len(datasets_written) == 1
|
|
1123
|
+
assert Dataset.ticks not in datasets_written[0]
|
|
1124
|
+
assert {
|
|
1125
|
+
Dataset.rates,
|
|
1126
|
+
Dataset.history_orders,
|
|
1127
|
+
Dataset.history_deals,
|
|
1128
|
+
} == datasets_written[0]
|
|
1129
|
+
|
|
1069
1130
|
|
|
1070
1131
|
class TestRecentTicks:
|
|
1071
1132
|
"""Tests for recent_ticks helper."""
|
|
@@ -3198,6 +3198,71 @@ class TestFetchLatestClosedRatesForTradingClient:
|
|
|
3198
3198
|
count=2,
|
|
3199
3199
|
)
|
|
3200
3200
|
|
|
3201
|
+
def test_copy_rates_from_pos_fallback_drops_forming_bar(self) -> None:
|
|
3202
|
+
"""Regression: client with only copy_rates_from_pos_as_df works end-to-end."""
|
|
3203
|
+
client = MagicMock(spec=["copy_rates_from_pos_as_df"])
|
|
3204
|
+
client.copy_rates_from_pos_as_df.return_value = pd.DataFrame(
|
|
3205
|
+
{
|
|
3206
|
+
"time": [1700000000, 1700000060, 1700000120],
|
|
3207
|
+
"close": [1.0, 1.1, 1.2],
|
|
3208
|
+
},
|
|
3209
|
+
)
|
|
3210
|
+
|
|
3211
|
+
result = fetch_latest_closed_rates_for_trading_client(
|
|
3212
|
+
client,
|
|
3213
|
+
symbol="EURUSD",
|
|
3214
|
+
granularity="M1",
|
|
3215
|
+
count=2,
|
|
3216
|
+
)
|
|
3217
|
+
|
|
3218
|
+
client.copy_rates_from_pos_as_df.assert_called_once_with(
|
|
3219
|
+
symbol="EURUSD", timeframe=1, start_pos=0, count=3
|
|
3220
|
+
)
|
|
3221
|
+
assert list(result["close"]) == [1.0, 1.1]
|
|
3222
|
+
assert list(result["time"]) == [1700000000, 1700000060]
|
|
3223
|
+
|
|
3224
|
+
def test_copy_rates_from_pos_fallback_resolves_granularity(self) -> None:
|
|
3225
|
+
"""Fallback path resolves granularity string to integer timeframe."""
|
|
3226
|
+
client = MagicMock(spec=["copy_rates_from_pos_as_df"])
|
|
3227
|
+
client.copy_rates_from_pos_as_df.return_value = pd.DataFrame(
|
|
3228
|
+
{"time": [1, 2, 3, 4], "close": [1.0, 1.1, 1.2, 1.3]},
|
|
3229
|
+
)
|
|
3230
|
+
|
|
3231
|
+
fetch_latest_closed_rates_for_trading_client(
|
|
3232
|
+
client, symbol="USDJPY", granularity="H1", count=3
|
|
3233
|
+
)
|
|
3234
|
+
|
|
3235
|
+
call_kwargs = client.copy_rates_from_pos_as_df.call_args.kwargs
|
|
3236
|
+
assert call_kwargs["symbol"] == "USDJPY"
|
|
3237
|
+
assert call_kwargs["timeframe"] == 16385
|
|
3238
|
+
assert call_kwargs["start_pos"] == 0
|
|
3239
|
+
assert call_kwargs["count"] == 4
|
|
3240
|
+
|
|
3241
|
+
def test_copy_rates_from_pos_fallback_returns_count_closed_rows(self) -> None:
|
|
3242
|
+
"""Fallback path trims to exactly count closed rows after forming-bar drop."""
|
|
3243
|
+
client = MagicMock(spec=["copy_rates_from_pos_as_df"])
|
|
3244
|
+
client.copy_rates_from_pos_as_df.return_value = pd.DataFrame(
|
|
3245
|
+
{"time": list(range(6)), "close": [float(i) for i in range(6)]},
|
|
3246
|
+
)
|
|
3247
|
+
|
|
3248
|
+
result = fetch_latest_closed_rates_for_trading_client(
|
|
3249
|
+
client, symbol="EURUSD", granularity="M1", count=4
|
|
3250
|
+
)
|
|
3251
|
+
|
|
3252
|
+
assert len(result) == 4
|
|
3253
|
+
assert list(result["close"]) == [1.0, 2.0, 3.0, 4.0]
|
|
3254
|
+
|
|
3255
|
+
def test_copy_rates_from_pos_fallback_raises_on_invalid_granularity(self) -> None:
|
|
3256
|
+
"""Invalid granularity raises ValueError before calling the fallback method."""
|
|
3257
|
+
client = MagicMock(spec=["copy_rates_from_pos_as_df"])
|
|
3258
|
+
|
|
3259
|
+
with pytest.raises(ValueError, match="Invalid timeframe"):
|
|
3260
|
+
fetch_latest_closed_rates_for_trading_client(
|
|
3261
|
+
client, symbol="EURUSD", granularity="BADGRAN", count=1
|
|
3262
|
+
)
|
|
3263
|
+
|
|
3264
|
+
client.copy_rates_from_pos_as_df.assert_not_called()
|
|
3265
|
+
|
|
3201
3266
|
def test_raises_when_trading_client_cannot_fetch_rates(self) -> None:
|
|
3202
3267
|
"""Test missing rate-fetch methods raise Mt5TradingError."""
|
|
3203
3268
|
client = MagicMock(spec=[])
|
|
@@ -3528,6 +3593,29 @@ class TestFetchLatestClosedRatesIndexed:
|
|
|
3528
3593
|
assert "close" in result.columns
|
|
3529
3594
|
assert isinstance(result.index, pd.DatetimeIndex)
|
|
3530
3595
|
|
|
3596
|
+
def test_copy_rates_from_pos_fallback_produces_utc_datetime_index(self) -> None:
|
|
3597
|
+
"""Fallback path via copy_rates_from_pos_as_df produces a UTC DatetimeIndex."""
|
|
3598
|
+
client = MagicMock(spec=["copy_rates_from_pos_as_df"])
|
|
3599
|
+
client.copy_rates_from_pos_as_df.return_value = pd.DataFrame(
|
|
3600
|
+
{
|
|
3601
|
+
"time": [1700000000, 1700003600, 1700007200],
|
|
3602
|
+
"close": [1.1, 1.2, 1.3],
|
|
3603
|
+
},
|
|
3604
|
+
)
|
|
3605
|
+
|
|
3606
|
+
result = fetch_latest_closed_rates_indexed(
|
|
3607
|
+
client,
|
|
3608
|
+
symbol="EURUSD",
|
|
3609
|
+
granularity="M1",
|
|
3610
|
+
count=2,
|
|
3611
|
+
)
|
|
3612
|
+
|
|
3613
|
+
assert isinstance(result.index, pd.DatetimeIndex)
|
|
3614
|
+
assert result.index.name == "time"
|
|
3615
|
+
assert str(result.index.tz) == "UTC"
|
|
3616
|
+
assert "time" not in result.columns
|
|
3617
|
+
assert list(result["close"]) == [1.1, 1.2]
|
|
3618
|
+
|
|
3531
3619
|
|
|
3532
3620
|
class TestExtractTickPrice:
|
|
3533
3621
|
"""Tests for the public extract_tick_price helper."""
|
|
@@ -358,7 +358,7 @@ name = "metatrader5"
|
|
|
358
358
|
version = "5.0.5640"
|
|
359
359
|
source = { registry = "https://pypi.org/simple" }
|
|
360
360
|
dependencies = [
|
|
361
|
-
{ name = "numpy"
|
|
361
|
+
{ name = "numpy" },
|
|
362
362
|
]
|
|
363
363
|
wheels = [
|
|
364
364
|
{ url = "https://files.pythonhosted.org/packages/ef/a0/3b764c6743ef601ff12f7d8d62ca5768eb25e90d3758ac7e1d08e667af85/metatrader5-5.0.5640-cp311-cp311-win_amd64.whl", hash = "sha256:4057255f2d63138a3ea1a5d492715038a71d3177cad793fca97a5c58771b4eb1", size = 48091, upload-time = "2026-02-20T23:31:12.289Z" },
|
|
@@ -487,7 +487,7 @@ wheels = [
|
|
|
487
487
|
|
|
488
488
|
[[package]]
|
|
489
489
|
name = "mt5cli"
|
|
490
|
-
version = "1.0.
|
|
490
|
+
version = "1.0.3"
|
|
491
491
|
source = { editable = "." }
|
|
492
492
|
dependencies = [
|
|
493
493
|
{ name = "click" },
|
|
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
|