mt5cli 1.5.0__tar.gz → 1.7.0__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.5.0 → mt5cli-1.7.0}/.github/workflows/claude.yml +1 -1
- {mt5cli-1.5.0 → mt5cli-1.7.0}/PKG-INFO +9 -60
- {mt5cli-1.5.0 → mt5cli-1.7.0}/README.md +8 -59
- mt5cli-1.7.0/docs/api/analytics.md +99 -0
- {mt5cli-1.5.0 → mt5cli-1.7.0}/docs/api/history.md +36 -10
- {mt5cli-1.5.0 → mt5cli-1.7.0}/docs/api/index.md +1 -3
- {mt5cli-1.5.0 → mt5cli-1.7.0}/docs/api/observability.md +7 -10
- {mt5cli-1.5.0 → mt5cli-1.7.0}/docs/api/public-contract.md +76 -73
- {mt5cli-1.5.0 → mt5cli-1.7.0}/mkdocs.yml +1 -1
- mt5cli-1.7.0/mt5cli/analytics.py +399 -0
- {mt5cli-1.5.0 → mt5cli-1.7.0}/mt5cli/cli.py +31 -51
- {mt5cli-1.5.0 → mt5cli-1.7.0}/mt5cli/history.py +951 -240
- mt5cli-1.7.0/mt5cli/observability.py +563 -0
- {mt5cli-1.5.0 → mt5cli-1.7.0}/mt5cli/rates.py +60 -0
- {mt5cli-1.5.0 → mt5cli-1.7.0}/mt5cli/sdk.py +11 -2
- {mt5cli-1.5.0 → mt5cli-1.7.0}/pyproject.toml +2 -2
- mt5cli-1.7.0/tests/architecture/__init__.py +1 -0
- mt5cli-1.7.0/tests/architecture/test_unit_module_correspondence.py +37 -0
- {mt5cli-1.5.0 → mt5cli-1.7.0}/tests/cli/test_cli.bats +2 -19
- mt5cli-1.7.0/tests/conftest.py +47 -0
- mt5cli-1.7.0/tests/contracts/__init__.py +1 -0
- mt5cli-1.7.0/tests/contracts/test_public_api.py +163 -0
- mt5cli-1.7.0/tests/docs/__init__.py +1 -0
- mt5cli-1.7.0/tests/docs/test_public_contract.py +21 -0
- mt5cli-1.7.0/tests/integration/__init__.py +1 -0
- mt5cli-1.7.0/tests/integration/test_history_capture_persist.py +211 -0
- mt5cli-1.5.0/tests/unit/test_packaging.py → mt5cli-1.7.0/tests/packaging/test_metadata.py +1 -0
- {mt5cli-1.5.0 → mt5cli-1.7.0}/tests/unit/conftest.py +2 -38
- mt5cli-1.7.0/tests/unit/test_analytics.py +1016 -0
- {mt5cli-1.5.0 → mt5cli-1.7.0}/tests/unit/test_cli.py +86 -85
- {mt5cli-1.5.0 → mt5cli-1.7.0}/tests/unit/test_client.py +59 -0
- mt5cli-1.7.0/tests/unit/test_contract.py +13 -0
- mt5cli-1.7.0/tests/unit/test_converters.py +153 -0
- mt5cli-1.7.0/tests/unit/test_exceptions.py +56 -0
- {mt5cli-1.5.0 → mt5cli-1.7.0}/tests/unit/test_history.py +646 -3
- {mt5cli-1.5.0 → mt5cli-1.7.0}/tests/unit/test_observability.py +277 -24
- mt5cli-1.7.0/tests/unit/test_rates.py +885 -0
- mt5cli-1.7.0/tests/unit/test_retry.py +42 -0
- mt5cli-1.7.0/tests/unit/test_schemas.py +327 -0
- mt5cli-1.7.0/tests/unit/test_sdk.py +23 -0
- {mt5cli-1.5.0 → mt5cli-1.7.0}/uv.lock +8 -8
- mt5cli-1.5.0/docs/api/grafana.md +0 -25
- mt5cli-1.5.0/examples/grafana/README.md +0 -95
- mt5cli-1.5.0/examples/grafana/compose.yml +0 -26
- mt5cli-1.5.0/examples/grafana/dashboards/mt5cli-market.json +0 -98
- mt5cli-1.5.0/examples/grafana/dashboards/mt5cli-overview.json +0 -254
- mt5cli-1.5.0/examples/grafana/dashboards/mt5cli-trades.json +0 -167
- mt5cli-1.5.0/examples/grafana/provisioning/dashboards/mt5cli.yml +0 -13
- mt5cli-1.5.0/examples/grafana/provisioning/datasources/mt5cli-sqlite.yml +0 -16
- mt5cli-1.5.0/mt5cli/grafana.py +0 -633
- mt5cli-1.5.0/mt5cli/observability.py +0 -250
- mt5cli-1.5.0/tests/unit/test_contracts.py +0 -768
- mt5cli-1.5.0/tests/unit/test_examples.py +0 -88
- mt5cli-1.5.0/tests/unit/test_grafana.py +0 -930
- mt5cli-1.5.0/tests/unit/test_history_contract.py +0 -371
- mt5cli-1.5.0/tests/unit/test_history_edge_contract.py +0 -122
- mt5cli-1.5.0/tests/unit/test_rate_timestamp_contract.py +0 -291
- mt5cli-1.5.0/tests/unit/test_rates.py +0 -380
- mt5cli-1.5.0/tests/unit/test_rates_contract.py +0 -220
- mt5cli-1.5.0/tests/unit/test_stable_sdk_cleanup.py +0 -52
- {mt5cli-1.5.0 → mt5cli-1.7.0}/.agents/skills/local-qa/SKILL.md +0 -0
- {mt5cli-1.5.0 → mt5cli-1.7.0}/.agents/skills/local-qa/scripts/qa.sh +0 -0
- {mt5cli-1.5.0 → mt5cli-1.7.0}/.agents/skills/mt5cli/SKILL.md +0 -0
- {mt5cli-1.5.0 → mt5cli-1.7.0}/.claude/agents/codex.md +0 -0
- {mt5cli-1.5.0 → mt5cli-1.7.0}/.claude/settings.json +0 -0
- {mt5cli-1.5.0 → mt5cli-1.7.0}/.github/CODEOWNERS +0 -0
- {mt5cli-1.5.0 → mt5cli-1.7.0}/.github/FUNDING.yml +0 -0
- {mt5cli-1.5.0 → mt5cli-1.7.0}/.github/dependabot.yml +0 -0
- {mt5cli-1.5.0 → mt5cli-1.7.0}/.github/renovate.json +0 -0
- {mt5cli-1.5.0 → mt5cli-1.7.0}/.github/workflows/ci.yml +0 -0
- {mt5cli-1.5.0 → mt5cli-1.7.0}/.github/workflows/release.yml +0 -0
- {mt5cli-1.5.0 → mt5cli-1.7.0}/.gitignore +0 -0
- {mt5cli-1.5.0 → mt5cli-1.7.0}/LICENSE +0 -0
- {mt5cli-1.5.0 → mt5cli-1.7.0}/docs/api/cli.md +0 -0
- {mt5cli-1.5.0 → mt5cli-1.7.0}/docs/api/client.md +0 -0
- {mt5cli-1.5.0 → mt5cli-1.7.0}/docs/api/converters.md +0 -0
- {mt5cli-1.5.0 → mt5cli-1.7.0}/docs/api/exceptions.md +0 -0
- {mt5cli-1.5.0 → mt5cli-1.7.0}/docs/api/marketdata.md +0 -0
- {mt5cli-1.5.0 → mt5cli-1.7.0}/docs/api/schemas.md +0 -0
- {mt5cli-1.5.0 → mt5cli-1.7.0}/docs/api/telemetry.md +0 -0
- {mt5cli-1.5.0 → mt5cli-1.7.0}/docs/api/trading.md +0 -0
- {mt5cli-1.5.0 → mt5cli-1.7.0}/docs/api/utils.md +0 -0
- {mt5cli-1.5.0 → mt5cli-1.7.0}/docs/index.md +0 -0
- {mt5cli-1.5.0 → mt5cli-1.7.0}/mt5cli/__init__.py +0 -0
- {mt5cli-1.5.0 → mt5cli-1.7.0}/mt5cli/__main__.py +0 -0
- {mt5cli-1.5.0 → mt5cli-1.7.0}/mt5cli/client.py +0 -0
- {mt5cli-1.5.0 → mt5cli-1.7.0}/mt5cli/contract.py +0 -0
- {mt5cli-1.5.0 → mt5cli-1.7.0}/mt5cli/converters.py +0 -0
- {mt5cli-1.5.0 → mt5cli-1.7.0}/mt5cli/exceptions.py +0 -0
- {mt5cli-1.5.0 → mt5cli-1.7.0}/mt5cli/lifecycle.py +0 -0
- {mt5cli-1.5.0 → mt5cli-1.7.0}/mt5cli/marketdata.py +0 -0
- {mt5cli-1.5.0 → mt5cli-1.7.0}/mt5cli/retry.py +0 -0
- {mt5cli-1.5.0 → mt5cli-1.7.0}/mt5cli/schemas.py +0 -0
- {mt5cli-1.5.0 → mt5cli-1.7.0}/mt5cli/telemetry.py +0 -0
- {mt5cli-1.5.0 → mt5cli-1.7.0}/mt5cli/trading.py +0 -0
- {mt5cli-1.5.0 → mt5cli-1.7.0}/mt5cli/utils.py +0 -0
- /mt5cli-1.5.0/tests/unit/test_docs_contract.py → /mt5cli-1.7.0/tests/docs/test_rate_storage_contract.py +0 -0
- {mt5cli-1.5.0 → mt5cli-1.7.0}/tests/unit/__init__.py +0 -0
- {mt5cli-1.5.0 → mt5cli-1.7.0}/tests/unit/test_lifecycle.py +0 -0
- {mt5cli-1.5.0 → mt5cli-1.7.0}/tests/unit/test_marketdata.py +0 -0
- {mt5cli-1.5.0 → mt5cli-1.7.0}/tests/unit/test_telemetry.py +0 -0
- {mt5cli-1.5.0 → mt5cli-1.7.0}/tests/unit/test_trading.py +0 -0
- {mt5cli-1.5.0 → mt5cli-1.7.0}/tests/unit/test_utils.py +0 -0
|
@@ -34,7 +34,7 @@ jobs:
|
|
|
34
34
|
issues: write
|
|
35
35
|
id-token: write
|
|
36
36
|
actions: read
|
|
37
|
-
uses: dceoy/
|
|
37
|
+
uses: dceoy/pr-loop/.github/workflows/claude-code-review.yml@main # zizmor: ignore[unpinned-uses]
|
|
38
38
|
secrets:
|
|
39
39
|
CLAUDE_CODE_OAUTH_TOKEN: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }} # zizmor: ignore[secrets-outside-env]
|
|
40
40
|
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: mt5cli
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.7.0
|
|
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>
|
|
@@ -212,8 +212,8 @@ python -m mt5cli -o account.csv account-info
|
|
|
212
212
|
| `close-positions` | Close open positions by `--symbol` or `--ticket` (`--yes` required for live; `--dry-run` available; optional `--filling-mode` / `--deviation` / `--comment` / `--magic`) |
|
|
213
213
|
| `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`) |
|
|
214
214
|
| `history-gaps` | Export a SQLite-only one-row-per-gap report from managed rate compatibility views without connecting to MT5 |
|
|
215
|
-
| `
|
|
216
|
-
| `snapshot` | Snapshot current account, position, order, and terminal state into SQLite
|
|
215
|
+
| `publish-dashboard` | Publish Parquet analytics datasets and a manifest from a SQLite history database for static dashboards (`--sqlite3`; `-o` is the manifest path; no MT5) |
|
|
216
|
+
| `snapshot` | Snapshot current account, position, order, and terminal state into SQLite |
|
|
217
217
|
|
|
218
218
|
Use `order-check` to validate a request payload before running `order-send --yes`.
|
|
219
219
|
`close-positions` is the safer high-level alternative that builds correct close
|
|
@@ -246,25 +246,20 @@ mt5cli -o history.db collect-history \
|
|
|
246
246
|
|
|
247
247
|
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 `rates` table records the requested `timeframe` so appended runs at different timeframes remain distinguishable. The `positions_reconstructed` view aggregates trade deals by `position_id`, excludes positions without closing-side entries, and uses volume-weighted open/close prices; reversal deals (`DEAL_ENTRY_INOUT`) are reported via `volume_reversal` / `reversal_count` columns.
|
|
248
248
|
|
|
249
|
-
###
|
|
249
|
+
### Static dashboard publication
|
|
250
250
|
|
|
251
|
-
mt5cli
|
|
251
|
+
`mt5cli -o dist/data/manifest.json publish-dashboard --sqlite3 history.db` (or `mt5cli.publish_dashboard("history.db", "dist/data")`) writes `trades`, `daily_pnl`, `strategy_stats`, `equity`, and `account_snapshots` Parquet files plus a `manifest.json` from canonical `analytics_*` views, ready for static hosting and DuckDB-Wasm. It never connects to MT5 and requires the `parquet` extra. See `docs/api/analytics.md`.
|
|
252
252
|
|
|
253
|
-
|
|
253
|
+
### Account snapshots (SQLite)
|
|
254
254
|
|
|
255
|
-
|
|
256
|
-
mt5cli -o history.db grafana-schema
|
|
257
|
-
```
|
|
258
|
-
|
|
259
|
-
This creates snapshot tables (`account_snapshots`, `position_snapshots`, `order_snapshots`, `terminal_snapshots`, `snapshot_runs`) and all `grafana_*` views and indexes in the SQLite database. Safe to run repeatedly — all operations are idempotent.
|
|
255
|
+
mt5cli can append timestamped account, position, order, and terminal snapshots to a SQLite database. Snapshot tables (`account_snapshots`, `position_snapshots`, `order_snapshots`, `terminal_snapshots`, `snapshot_runs`) are created automatically and idempotently.
|
|
260
256
|
|
|
261
257
|
#### Snapshot current account state
|
|
262
258
|
|
|
263
259
|
```bash
|
|
264
260
|
mt5cli -o history.db snapshot \
|
|
265
261
|
--symbol JP225 --symbol HK50 --symbol NL25 \
|
|
266
|
-
--with-account --with-positions --with-orders --with-terminal
|
|
267
|
-
--with-grafana-schema
|
|
262
|
+
--with-account --with-positions --with-orders --with-terminal
|
|
268
263
|
```
|
|
269
264
|
|
|
270
265
|
Appends one timestamped row per data type. Never places orders or modifies trading state. Run periodically (e.g. from a cron job or a loop) to build a time-series account history.
|
|
@@ -284,56 +279,10 @@ update_observability_with_config(
|
|
|
284
279
|
include_positions=True,
|
|
285
280
|
include_orders=True,
|
|
286
281
|
include_terminal=True,
|
|
287
|
-
with_grafana_schema=True,
|
|
288
282
|
)
|
|
289
283
|
```
|
|
290
284
|
|
|
291
|
-
####
|
|
292
|
-
|
|
293
|
-
**Time-series views** (integer epoch-second `time` column; snapshot views also expose `run_id`):
|
|
294
|
-
|
|
295
|
-
| View | Source | Description |
|
|
296
|
-
| ---------------------------- | -------------------- | ---------------------------------------------------------- |
|
|
297
|
-
| `grafana_rates` | `rates` | OHLCV bars with integer epoch `time` |
|
|
298
|
-
| `grafana_ticks` | `ticks` | Tick data with integer epoch `time` |
|
|
299
|
-
| `grafana_history_deals` | `history_deals` | All deals with epoch `time` |
|
|
300
|
-
| `grafana_history_orders` | `history_orders` | All historical orders; adds epoch `time` from `time_setup` |
|
|
301
|
-
| `grafana_trade_deals` | `history_deals` | Trade deals only (`type IN (0,1)`) |
|
|
302
|
-
| `grafana_cash_events` | `history_deals` | Non-trade deals (deposits, dividends, etc.) |
|
|
303
|
-
| `grafana_symbol_pnl` | `history_deals` | Per-close-deal profit/loss per symbol |
|
|
304
|
-
| `grafana_account_snapshots` | `account_snapshots` | Account balance/equity/margin time series |
|
|
305
|
-
| `grafana_position_snapshots` | `position_snapshots` | Open position snapshots over time |
|
|
306
|
-
| `grafana_order_snapshots` | `order_snapshots` | Active order snapshots over time |
|
|
307
|
-
| `grafana_terminal_snapshots` | `terminal_snapshots` | Terminal connectivity snapshots |
|
|
308
|
-
|
|
309
|
-
**Static summary views** (no `time` column; use in table or stat panels, not time-series):
|
|
310
|
-
|
|
311
|
-
| View | Source | Description |
|
|
312
|
-
| ---------------------- | --------------- | ------------------------------------- |
|
|
313
|
-
| `grafana_realized_pnl` | `history_deals` | Cumulative realized PnL per symbol |
|
|
314
|
-
| `grafana_trade_stats` | `history_deals` | Win/loss counts and profit per symbol |
|
|
315
|
-
|
|
316
|
-
#### Example Grafana queries
|
|
317
|
-
|
|
318
|
-
```sql
|
|
319
|
-
-- Equity curve over time
|
|
320
|
-
SELECT time, equity FROM grafana_account_snapshots ORDER BY time;
|
|
321
|
-
|
|
322
|
-
-- Rolling balance by account login
|
|
323
|
-
SELECT time, login, balance FROM grafana_account_snapshots
|
|
324
|
-
WHERE login = $login ORDER BY time;
|
|
325
|
-
|
|
326
|
-
-- Open positions at latest successful snapshot
|
|
327
|
-
SELECT symbol, volume, profit FROM grafana_position_snapshots
|
|
328
|
-
WHERE run_id = (SELECT MAX(run_id) FROM snapshot_runs WHERE status = 'ok');
|
|
329
|
-
|
|
330
|
-
-- Realized PnL by symbol
|
|
331
|
-
SELECT symbol, total_profit FROM grafana_trade_stats ORDER BY total_profit DESC;
|
|
332
|
-
```
|
|
333
|
-
|
|
334
|
-
#### Grafana and telemetry API docs
|
|
335
|
-
|
|
336
|
-
The shipped Grafana helpers are documented in [`docs/api/grafana.md`](docs/api/grafana.md), including `publish_grafana_copy()` for creating a WAL-safe published SQLite copy for Grafana.
|
|
285
|
+
#### Telemetry API docs
|
|
337
286
|
|
|
338
287
|
OpenTelemetry metrics are documented in [`docs/api/telemetry.md`](docs/api/telemetry.md), including `enable_otel_metrics()`, `configure_metrics()`, the `mt5cli[otel]` extra, and `OTEL_EXPORTER_OTLP_ENDPOINT`.
|
|
339
288
|
|
|
@@ -183,8 +183,8 @@ python -m mt5cli -o account.csv account-info
|
|
|
183
183
|
| `close-positions` | Close open positions by `--symbol` or `--ticket` (`--yes` required for live; `--dry-run` available; optional `--filling-mode` / `--deviation` / `--comment` / `--magic`) |
|
|
184
184
|
| `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`) |
|
|
185
185
|
| `history-gaps` | Export a SQLite-only one-row-per-gap report from managed rate compatibility views without connecting to MT5 |
|
|
186
|
-
| `
|
|
187
|
-
| `snapshot` | Snapshot current account, position, order, and terminal state into SQLite
|
|
186
|
+
| `publish-dashboard` | Publish Parquet analytics datasets and a manifest from a SQLite history database for static dashboards (`--sqlite3`; `-o` is the manifest path; no MT5) |
|
|
187
|
+
| `snapshot` | Snapshot current account, position, order, and terminal state into SQLite |
|
|
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
|
|
@@ -217,25 +217,20 @@ mt5cli -o history.db collect-history \
|
|
|
217
217
|
|
|
218
218
|
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 `rates` table records the requested `timeframe` so appended runs at different timeframes remain distinguishable. The `positions_reconstructed` view aggregates trade deals by `position_id`, excludes positions without closing-side entries, and uses volume-weighted open/close prices; reversal deals (`DEAL_ENTRY_INOUT`) are reported via `volume_reversal` / `reversal_count` columns.
|
|
219
219
|
|
|
220
|
-
###
|
|
220
|
+
### Static dashboard publication
|
|
221
221
|
|
|
222
|
-
mt5cli
|
|
222
|
+
`mt5cli -o dist/data/manifest.json publish-dashboard --sqlite3 history.db` (or `mt5cli.publish_dashboard("history.db", "dist/data")`) writes `trades`, `daily_pnl`, `strategy_stats`, `equity`, and `account_snapshots` Parquet files plus a `manifest.json` from canonical `analytics_*` views, ready for static hosting and DuckDB-Wasm. It never connects to MT5 and requires the `parquet` extra. See `docs/api/analytics.md`.
|
|
223
223
|
|
|
224
|
-
|
|
224
|
+
### Account snapshots (SQLite)
|
|
225
225
|
|
|
226
|
-
|
|
227
|
-
mt5cli -o history.db grafana-schema
|
|
228
|
-
```
|
|
229
|
-
|
|
230
|
-
This creates snapshot tables (`account_snapshots`, `position_snapshots`, `order_snapshots`, `terminal_snapshots`, `snapshot_runs`) and all `grafana_*` views and indexes in the SQLite database. Safe to run repeatedly — all operations are idempotent.
|
|
226
|
+
mt5cli can append timestamped account, position, order, and terminal snapshots to a SQLite database. Snapshot tables (`account_snapshots`, `position_snapshots`, `order_snapshots`, `terminal_snapshots`, `snapshot_runs`) are created automatically and idempotently.
|
|
231
227
|
|
|
232
228
|
#### Snapshot current account state
|
|
233
229
|
|
|
234
230
|
```bash
|
|
235
231
|
mt5cli -o history.db snapshot \
|
|
236
232
|
--symbol JP225 --symbol HK50 --symbol NL25 \
|
|
237
|
-
--with-account --with-positions --with-orders --with-terminal
|
|
238
|
-
--with-grafana-schema
|
|
233
|
+
--with-account --with-positions --with-orders --with-terminal
|
|
239
234
|
```
|
|
240
235
|
|
|
241
236
|
Appends one timestamped row per data type. Never places orders or modifies trading state. Run periodically (e.g. from a cron job or a loop) to build a time-series account history.
|
|
@@ -255,56 +250,10 @@ update_observability_with_config(
|
|
|
255
250
|
include_positions=True,
|
|
256
251
|
include_orders=True,
|
|
257
252
|
include_terminal=True,
|
|
258
|
-
with_grafana_schema=True,
|
|
259
253
|
)
|
|
260
254
|
```
|
|
261
255
|
|
|
262
|
-
####
|
|
263
|
-
|
|
264
|
-
**Time-series views** (integer epoch-second `time` column; snapshot views also expose `run_id`):
|
|
265
|
-
|
|
266
|
-
| View | Source | Description |
|
|
267
|
-
| ---------------------------- | -------------------- | ---------------------------------------------------------- |
|
|
268
|
-
| `grafana_rates` | `rates` | OHLCV bars with integer epoch `time` |
|
|
269
|
-
| `grafana_ticks` | `ticks` | Tick data with integer epoch `time` |
|
|
270
|
-
| `grafana_history_deals` | `history_deals` | All deals with epoch `time` |
|
|
271
|
-
| `grafana_history_orders` | `history_orders` | All historical orders; adds epoch `time` from `time_setup` |
|
|
272
|
-
| `grafana_trade_deals` | `history_deals` | Trade deals only (`type IN (0,1)`) |
|
|
273
|
-
| `grafana_cash_events` | `history_deals` | Non-trade deals (deposits, dividends, etc.) |
|
|
274
|
-
| `grafana_symbol_pnl` | `history_deals` | Per-close-deal profit/loss per symbol |
|
|
275
|
-
| `grafana_account_snapshots` | `account_snapshots` | Account balance/equity/margin time series |
|
|
276
|
-
| `grafana_position_snapshots` | `position_snapshots` | Open position snapshots over time |
|
|
277
|
-
| `grafana_order_snapshots` | `order_snapshots` | Active order snapshots over time |
|
|
278
|
-
| `grafana_terminal_snapshots` | `terminal_snapshots` | Terminal connectivity snapshots |
|
|
279
|
-
|
|
280
|
-
**Static summary views** (no `time` column; use in table or stat panels, not time-series):
|
|
281
|
-
|
|
282
|
-
| View | Source | Description |
|
|
283
|
-
| ---------------------- | --------------- | ------------------------------------- |
|
|
284
|
-
| `grafana_realized_pnl` | `history_deals` | Cumulative realized PnL per symbol |
|
|
285
|
-
| `grafana_trade_stats` | `history_deals` | Win/loss counts and profit per symbol |
|
|
286
|
-
|
|
287
|
-
#### Example Grafana queries
|
|
288
|
-
|
|
289
|
-
```sql
|
|
290
|
-
-- Equity curve over time
|
|
291
|
-
SELECT time, equity FROM grafana_account_snapshots ORDER BY time;
|
|
292
|
-
|
|
293
|
-
-- Rolling balance by account login
|
|
294
|
-
SELECT time, login, balance FROM grafana_account_snapshots
|
|
295
|
-
WHERE login = $login ORDER BY time;
|
|
296
|
-
|
|
297
|
-
-- Open positions at latest successful snapshot
|
|
298
|
-
SELECT symbol, volume, profit FROM grafana_position_snapshots
|
|
299
|
-
WHERE run_id = (SELECT MAX(run_id) FROM snapshot_runs WHERE status = 'ok');
|
|
300
|
-
|
|
301
|
-
-- Realized PnL by symbol
|
|
302
|
-
SELECT symbol, total_profit FROM grafana_trade_stats ORDER BY total_profit DESC;
|
|
303
|
-
```
|
|
304
|
-
|
|
305
|
-
#### Grafana and telemetry API docs
|
|
306
|
-
|
|
307
|
-
The shipped Grafana helpers are documented in [`docs/api/grafana.md`](docs/api/grafana.md), including `publish_grafana_copy()` for creating a WAL-safe published SQLite copy for Grafana.
|
|
256
|
+
#### Telemetry API docs
|
|
308
257
|
|
|
309
258
|
OpenTelemetry metrics are documented in [`docs/api/telemetry.md`](docs/api/telemetry.md), including `enable_otel_metrics()`, `configure_metrics()`, the `mt5cli[otel]` extra, and `OTEL_EXPORTER_OTLP_ENDPOINT`.
|
|
310
259
|
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
# Analytics
|
|
2
|
+
|
|
3
|
+
::: mt5cli.analytics
|
|
4
|
+
|
|
5
|
+
## Static dashboard workflow
|
|
6
|
+
|
|
7
|
+
mt5cli keeps SQLite as the canonical local store and publishes browser-facing
|
|
8
|
+
Parquet datasets from it, so a static site (for example DuckDB-Wasm) can query
|
|
9
|
+
the data without a live API or direct SQLite access. Visualization stays
|
|
10
|
+
outside mt5cli.
|
|
11
|
+
|
|
12
|
+
```bash
|
|
13
|
+
mt5cli -o dist/data/manifest.json publish-dashboard --sqlite3 history.db
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
```python
|
|
17
|
+
from mt5cli import publish_dashboard
|
|
18
|
+
|
|
19
|
+
manifest = publish_dashboard("history.db", "dist/data")
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
Publication never connects to MetaTrader 5 and never modifies the source
|
|
23
|
+
database (analytics views are created as connection-local TEMP views). It
|
|
24
|
+
requires the `parquet` extra (`pip install "mt5cli[parquet]"`).
|
|
25
|
+
|
|
26
|
+
| File | Source view |
|
|
27
|
+
| --------------------------- | ----------------------------------------------------------------------------------------------------- |
|
|
28
|
+
| `trades.parquet` | `analytics_trades` |
|
|
29
|
+
| `daily_pnl.parquet` | `analytics_daily_pnl` |
|
|
30
|
+
| `strategy_stats.parquet` | `analytics_strategy_stats` |
|
|
31
|
+
| `equity.parquet` | `analytics_equity` |
|
|
32
|
+
| `account_snapshots.parquet` | `account_snapshots` joined to successful runs |
|
|
33
|
+
| `manifest.json` | `schema_version`, `generated_at`, `mt5cli_version`, and per dataset `name`, `file`, `rows`, `columns` |
|
|
34
|
+
|
|
35
|
+
Datasets whose source tables are missing are skipped and omitted from the
|
|
36
|
+
manifest. The manifest is written last. `manifest_name` must be a plain file name (no `/`, `\\`, drive prefix or `..`) that is not the name of a dataset file; otherwise `ValueError` is raised before anything is written (the CLI reports it as a usage error).
|
|
37
|
+
|
|
38
|
+
## Analytics views
|
|
39
|
+
|
|
40
|
+
`create_analytics_views(conn)` creates the views idempotently in an existing
|
|
41
|
+
history database (`temporary=True` creates TEMP views for read-only databases).
|
|
42
|
+
|
|
43
|
+
- `analytics_trades`: one row per completed trade leg (built on the history layer's `deal_portions` view, the same source as `positions_reconstructed`). A leg is the stretch of
|
|
44
|
+
a `position_id` between reversals: a `DEAL_ENTRY_INOUT` deal closes the
|
|
45
|
+
current leg and opens the next one (`leg_index`). Its volume, commission and
|
|
46
|
+
fee are split pro rata between the two legs; its swap goes wholly to the
|
|
47
|
+
closing leg (MT5 reports `DEAL_SWAP` for the position being closed), and
|
|
48
|
+
profit is realized on the closed part only. A leg is complete once its closing volume covers its opening
|
|
49
|
+
volume (1e-9 lot tolerance), so partially closed legs and the still-open
|
|
50
|
+
leg after a reversal are excluded. For positions without reversals, leg 0
|
|
51
|
+
matches `positions_reconstructed`. Columns: `position_id`, `symbol`,
|
|
52
|
+
`leg_index`, `magic`, `magic_count`, `side`, `open_time`, `close_time`, `close_date`,
|
|
53
|
+
`holding_seconds`, `volume`, `entry_price`, `exit_price` (volume-weighted),
|
|
54
|
+
`reversal_count`, `deals_count`, `profit`, `commission`, `swap`, `fee`,
|
|
55
|
+
`net_profit`. Numeric timestamps stay numeric epoch values; textual
|
|
56
|
+
timestamps remain timezone-naive MT5 trade-server wall-clock values. No
|
|
57
|
+
implicit UTC conversion is performed.
|
|
58
|
+
- `analytics_realized_events`: every deal portion, each at its own timestamp
|
|
59
|
+
(cash basis: an entry's commission lands on the entry date and a partial
|
|
60
|
+
close's profit on the day it is realized), including partially closed and
|
|
61
|
+
still-open legs. Events of a completed leg sum to that leg's `net_profit`,
|
|
62
|
+
so cumulative equity also reflects entry costs and partial-close profit of
|
|
63
|
+
legs that have not completed yet.
|
|
64
|
+
Each event carries its own deal's `magic`. A leg needs a visible opening
|
|
65
|
+
volume to count as a trade, and its position must start inside the captured
|
|
66
|
+
history (its first visible deal is an entry-in, since the reversal split cannot
|
|
67
|
+
be reconstructed otherwise), so positions opened before the captured history
|
|
68
|
+
appear only in the events, daily P/L and equity. This check is best-effort: a
|
|
69
|
+
scale-in cut by the capture window looks like a position start and cannot be told
|
|
70
|
+
apart from one using the deals alone, so collect history from before the oldest
|
|
71
|
+
open position when exact trade legs matter.
|
|
72
|
+
- `analytics_daily_pnl`: per `date`, `symbol`, `magic` (the deal-level magic)
|
|
73
|
+
from the realized events: `event_count`, `profit`, `commission`, `swap`, `fee`, `net_profit`.
|
|
74
|
+
- `analytics_strategy_stats`: per `symbol`, `magic` from the trade legs (trade
|
|
75
|
+
count, wins, losses, win rate, gross profit/loss, profit factor), plus
|
|
76
|
+
`avg_holding_seconds`, `first_open_time`, `last_close_time`. A leg's `magic` is
|
|
77
|
+
its entry magic; legs whose entry deals mix magics have a NULL `magic` with
|
|
78
|
+
`magic_count > 1`, so for such legs daily P/L by magic and the strategy stats
|
|
79
|
+
by magic can differ.
|
|
80
|
+
- `analytics_equity`: one row per realized event in time order with
|
|
81
|
+
`cumulative_net_profit` (realized P&L only).
|
|
82
|
+
|
|
83
|
+
Net P/L is:
|
|
84
|
+
|
|
85
|
+
```text
|
|
86
|
+
net_profit = COALESCE(profit, 0) + COALESCE(commission, 0)
|
|
87
|
+
+ COALESCE(swap, 0) + COALESCE(fee, 0)
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
Missing optional deal columns are treated as zero (or NULL for `magic`). A trade
|
|
91
|
+
is a win when `net_profit > 0` and a loss when `net_profit < 0`; break-even
|
|
92
|
+
trades count as neither. `gross_profit` and `gross_loss` sum positive and
|
|
93
|
+
negative trade `net_profit`, and `profit_factor = gross_profit / ABS(gross_loss)`
|
|
94
|
+
is NULL without losses. Metrics needing unavailable state (such as MAE/MFE) are
|
|
95
|
+
out of scope.
|
|
96
|
+
|
|
97
|
+
Parquet columns with a fixed meaning are written with stable types (`Int64`,
|
|
98
|
+
`float64`, `string`) even for empty tables; time columns keep the stored
|
|
99
|
+
representation.
|
|
@@ -123,6 +123,20 @@ erDiagram
|
|
|
123
123
|
REAL profit
|
|
124
124
|
}
|
|
125
125
|
|
|
126
|
+
deal_portions {
|
|
127
|
+
INTEGER ticket
|
|
128
|
+
INTEGER position_id
|
|
129
|
+
TEXT symbol
|
|
130
|
+
INTEGER leg
|
|
131
|
+
TEXT role
|
|
132
|
+
INTEGER position_start_known
|
|
133
|
+
REAL volume
|
|
134
|
+
REAL profit
|
|
135
|
+
REAL commission
|
|
136
|
+
REAL swap
|
|
137
|
+
REAL fee
|
|
138
|
+
}
|
|
139
|
+
|
|
126
140
|
positions_reconstructed {
|
|
127
141
|
INTEGER position_id
|
|
128
142
|
TEXT symbol
|
|
@@ -144,20 +158,32 @@ erDiagram
|
|
|
144
158
|
history_orders ||--o{ history_deals : "order ~ ticket (logical)"
|
|
145
159
|
symbols ||--o{ rates : "symbol (logical)"
|
|
146
160
|
history_deals ||--|| cash_events : "VIEW: type NOT IN (0,1)"
|
|
147
|
-
history_deals ||--o{
|
|
161
|
+
history_deals ||--o{ deal_portions : "VIEW: deals split at reversals"
|
|
162
|
+
deal_portions ||--o{ positions_reconstructed : "VIEW: GROUP BY position_id"
|
|
148
163
|
```
|
|
149
164
|
|
|
150
165
|
### Tables and views
|
|
151
166
|
|
|
152
|
-
| Object | Kind | Source | Notes
|
|
153
|
-
| ------------------------- | ----- | -------------------- |
|
|
154
|
-
| `rates` | table | `copy_rates_range` | Indexed on `(symbol, timeframe, time)` when columns exist.
|
|
155
|
-
| `ticks` | table | `copy_ticks_range` | Indexed on `(symbol, time)` when columns exist.
|
|
156
|
-
| `history_orders` | table | `history_orders_get` | Fetched per `--symbol`, then concatenated.
|
|
157
|
-
| `history_deals` | table | `history_deals_get` | Fetched per `--symbol`, then concatenated. Indexed on `(position_id, symbol)` when present.
|
|
158
|
-
| `symbols` | table | `symbol_info` | Opt-in. One row per symbol per collection/update, snapshotted at `date_to` / update end.
|
|
159
|
-
| `cash_events` | view | `history_deals` | Non-trade deal types (deposits, balance ops, etc.). Requires `type` column.
|
|
160
|
-
| `
|
|
167
|
+
| Object | Kind | Source | Notes |
|
|
168
|
+
| ------------------------- | ----- | -------------------- | -------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
169
|
+
| `rates` | table | `copy_rates_range` | Indexed on `(symbol, timeframe, time)` when columns exist. |
|
|
170
|
+
| `ticks` | table | `copy_ticks_range` | Indexed on `(symbol, time)` when columns exist. |
|
|
171
|
+
| `history_orders` | table | `history_orders_get` | Fetched per `--symbol`, then concatenated. |
|
|
172
|
+
| `history_deals` | table | `history_deals_get` | Fetched per `--symbol`, then concatenated. Indexed on `(position_id, symbol)` when present. |
|
|
173
|
+
| `symbols` | table | `symbol_info` | Opt-in. One row per symbol per collection/update, snapshotted at `date_to` / update end. |
|
|
174
|
+
| `cash_events` | view | `history_deals` | Non-trade deal types (deposits, balance ops, etc.). Requires `type` column. |
|
|
175
|
+
| `deal_portions` | view | `history_deals` | Canonical leg-aware layer: trade deals, with each reversal (`DEAL_ENTRY_INOUT`) split into a closing and an opening portion (`leg`, `role`). |
|
|
176
|
+
| `positions_reconstructed` | view | `deal_portions` | One row per closed `position_id`; volume-weighted prices and reversal stats (reversal volume counted whole). |
|
|
177
|
+
|
|
178
|
+
The reversal split in `deal_portions` uses the balance of the visible deals, so it
|
|
179
|
+
is reliable only for positions whose first visible deal is an entry-in
|
|
180
|
+
(`position_start_known = 1`). When the captured history starts mid-position
|
|
181
|
+
(`position_start_known = 0`), the `leg` numbering and the closing/opening split of a
|
|
182
|
+
reversal are approximate, though every deal's cash totals are preserved and
|
|
183
|
+
`positions_reconstructed` is unaffected. The flag is best-effort: a scale-in is also
|
|
184
|
+
an entry-in deal, so if the capture window cuts a position and a later scale-in is
|
|
185
|
+
the first visible deal, the missing earlier exposure cannot be detected from the
|
|
186
|
+
deals alone.
|
|
161
187
|
|
|
162
188
|
Column sets can vary with terminal and pdmt5 version. Views are skipped with a warning
|
|
163
189
|
when required columns are missing.
|
|
@@ -18,9 +18,8 @@ responsibilities.
|
|
|
18
18
|
| [Exceptions](exceptions.md) | Stable mt5cli exception types and MT5 error normalization |
|
|
19
19
|
| [Trading](trading.md) | Trading-capable sessions and operational helpers |
|
|
20
20
|
| [History Collection (SQLite)](history.md) | SQLite schema, incremental writes, dedup, and rate views |
|
|
21
|
-
| [Observability](observability.md) | Account/position/order/terminal snapshot orchestration
|
|
21
|
+
| [Observability](observability.md) | Account/position/order/terminal snapshot orchestration and SQLite snapshot tables |
|
|
22
22
|
| [Telemetry](telemetry.md) | OpenTelemetry metrics setup, meters, and emitted metric names |
|
|
23
|
-
| [Grafana](grafana.md) | Grafana-ready SQLite schema, views, snapshots, and published copies |
|
|
24
23
|
| [CLI](cli.md) | Typer commands that delegate to the Python API |
|
|
25
24
|
| [Utils](utils.md) | Generic parsing, conversion, and export helpers |
|
|
26
25
|
|
|
@@ -34,7 +33,6 @@ flowchart TD
|
|
|
34
33
|
MarketData["marketdata"] --> Client
|
|
35
34
|
History["history"] --> Client
|
|
36
35
|
Observability["observability"] --> Client
|
|
37
|
-
Observability --> Grafana["grafana"]
|
|
38
36
|
History --> Utils["utils export"]
|
|
39
37
|
```
|
|
40
38
|
|
|
@@ -2,13 +2,11 @@
|
|
|
2
2
|
|
|
3
3
|
::: mt5cli.observability
|
|
4
4
|
|
|
5
|
-
##
|
|
5
|
+
## Snapshot persistence (SQLite)
|
|
6
6
|
|
|
7
7
|
These helpers append timestamped account/position/order/terminal snapshot
|
|
8
|
-
rows into a SQLite database
|
|
9
|
-
idempotent (`CREATE TABLE IF NOT EXISTS
|
|
10
|
-
VIEW`, `CREATE INDEX IF NOT EXISTS`). Missing source tables are skipped with a
|
|
11
|
-
warning rather than raising an error.
|
|
8
|
+
rows into a SQLite database. The snapshot tables are created on demand with
|
|
9
|
+
idempotent DDL (`CREATE TABLE IF NOT EXISTS`).
|
|
12
10
|
|
|
13
11
|
| Symbol | Role |
|
|
14
12
|
| ---------------------------------- | ------------------------------------------------------------------------------------------- |
|
|
@@ -17,9 +15,6 @@ warning rather than raising an error.
|
|
|
17
15
|
|
|
18
16
|
Both functions write to the SQLite path given by `output=`. The optional
|
|
19
17
|
`symbols` parameter filters `positions` / `orders` by symbol.
|
|
20
|
-
`with_grafana_schema=False` (default) skips Grafana view/index setup; run
|
|
21
|
-
`grafana-schema` once to set up the schema, then call `snapshot` repeatedly
|
|
22
|
-
without this flag.
|
|
23
18
|
|
|
24
19
|
Pass the `MT5Client` yielded by `mt5_session()` directly to
|
|
25
20
|
`update_observability(client=...)`. This workflow calls the facade's canonical
|
|
@@ -34,5 +29,7 @@ with mt5_session() as client:
|
|
|
34
29
|
update_observability(client=client, output="observability.db")
|
|
35
30
|
```
|
|
36
31
|
|
|
37
|
-
|
|
38
|
-
|
|
32
|
+
The snapshot table DDL and row inserts (`create_snapshot_tables`,
|
|
33
|
+
`start_snapshot_run`, `insert_*_snapshot(s)`, `record_snapshot_run`) live in
|
|
34
|
+
this module alongside the orchestration that decides _when_ and _what_ to
|
|
35
|
+
snapshot.
|