mt5cli 1.0.3__tar.gz → 1.1.1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (66) hide show
  1. {mt5cli-1.0.3 → mt5cli-1.1.1}/.agents/skills/local-qa/scripts/qa.sh +1 -1
  2. {mt5cli-1.0.3 → mt5cli-1.1.1}/PKG-INFO +105 -1
  3. {mt5cli-1.0.3 → mt5cli-1.1.1}/README.md +100 -0
  4. {mt5cli-1.0.3 → mt5cli-1.1.1}/docs/api/public-contract.md +80 -9
  5. {mt5cli-1.0.3 → mt5cli-1.1.1}/docs/api/trading.md +55 -0
  6. mt5cli-1.1.1/examples/grafana/README.md +95 -0
  7. mt5cli-1.1.1/examples/grafana/compose.yml +26 -0
  8. mt5cli-1.1.1/examples/grafana/dashboards/mt5cli-market.json +98 -0
  9. mt5cli-1.1.1/examples/grafana/dashboards/mt5cli-overview.json +254 -0
  10. mt5cli-1.1.1/examples/grafana/dashboards/mt5cli-trades.json +167 -0
  11. mt5cli-1.1.1/examples/grafana/provisioning/dashboards/mt5cli.yml +13 -0
  12. mt5cli-1.1.1/examples/grafana/provisioning/datasources/mt5cli-sqlite.yml +16 -0
  13. {mt5cli-1.0.3 → mt5cli-1.1.1}/mt5cli/__init__.py +6 -0
  14. {mt5cli-1.0.3 → mt5cli-1.1.1}/mt5cli/cli.py +125 -0
  15. {mt5cli-1.0.3 → mt5cli-1.1.1}/mt5cli/contract.py +3 -0
  16. mt5cli-1.1.1/mt5cli/grafana.py +682 -0
  17. {mt5cli-1.0.3 → mt5cli-1.1.1}/mt5cli/sdk.py +255 -17
  18. mt5cli-1.1.1/mt5cli/telemetry.py +354 -0
  19. {mt5cli-1.0.3 → mt5cli-1.1.1}/mt5cli/trading.py +136 -3
  20. {mt5cli-1.0.3 → mt5cli-1.1.1}/mt5cli/utils.py +2 -1
  21. {mt5cli-1.0.3 → mt5cli-1.1.1}/pyproject.toml +8 -1
  22. {mt5cli-1.0.3 → mt5cli-1.1.1}/tests/conftest.py +38 -0
  23. {mt5cli-1.0.3 → mt5cli-1.1.1}/tests/test_cli.py +220 -85
  24. mt5cli-1.1.1/tests/test_examples.py +75 -0
  25. mt5cli-1.1.1/tests/test_grafana.py +991 -0
  26. {mt5cli-1.0.3 → mt5cli-1.1.1}/tests/test_sdk.py +487 -0
  27. mt5cli-1.1.1/tests/test_telemetry.py +249 -0
  28. {mt5cli-1.0.3 → mt5cli-1.1.1}/tests/test_trading.py +176 -0
  29. {mt5cli-1.0.3 → mt5cli-1.1.1}/uv.lock +122 -2
  30. {mt5cli-1.0.3 → mt5cli-1.1.1}/.agents/skills/local-qa/SKILL.md +0 -0
  31. {mt5cli-1.0.3 → mt5cli-1.1.1}/.agents/skills/mt5cli/SKILL.md +0 -0
  32. {mt5cli-1.0.3 → mt5cli-1.1.1}/.agents/skills/pr-feedback-triage/SKILL.md +0 -0
  33. {mt5cli-1.0.3 → mt5cli-1.1.1}/.claude/agents/codex.md +0 -0
  34. {mt5cli-1.0.3 → mt5cli-1.1.1}/.claude/settings.json +0 -0
  35. {mt5cli-1.0.3 → mt5cli-1.1.1}/.github/FUNDING.yml +0 -0
  36. {mt5cli-1.0.3 → mt5cli-1.1.1}/.github/dependabot.yml +0 -0
  37. {mt5cli-1.0.3 → mt5cli-1.1.1}/.github/renovate.json +0 -0
  38. {mt5cli-1.0.3 → mt5cli-1.1.1}/.github/workflows/ci.yml +0 -0
  39. {mt5cli-1.0.3 → mt5cli-1.1.1}/.github/workflows/claude.yml +0 -0
  40. {mt5cli-1.0.3 → mt5cli-1.1.1}/.github/workflows/release.yml +0 -0
  41. {mt5cli-1.0.3 → mt5cli-1.1.1}/.gitignore +0 -0
  42. {mt5cli-1.0.3 → mt5cli-1.1.1}/AGENTS.md +0 -0
  43. {mt5cli-1.0.3 → mt5cli-1.1.1}/CLAUDE.md +0 -0
  44. {mt5cli-1.0.3 → mt5cli-1.1.1}/LICENSE +0 -0
  45. {mt5cli-1.0.3 → mt5cli-1.1.1}/docs/api/cli.md +0 -0
  46. {mt5cli-1.0.3 → mt5cli-1.1.1}/docs/api/client.md +0 -0
  47. {mt5cli-1.0.3 → mt5cli-1.1.1}/docs/api/converters.md +0 -0
  48. {mt5cli-1.0.3 → mt5cli-1.1.1}/docs/api/exceptions.md +0 -0
  49. {mt5cli-1.0.3 → mt5cli-1.1.1}/docs/api/history.md +0 -0
  50. {mt5cli-1.0.3 → mt5cli-1.1.1}/docs/api/index.md +0 -0
  51. {mt5cli-1.0.3 → mt5cli-1.1.1}/docs/api/schemas.md +0 -0
  52. {mt5cli-1.0.3 → mt5cli-1.1.1}/docs/api/sdk.md +0 -0
  53. {mt5cli-1.0.3 → mt5cli-1.1.1}/docs/api/utils.md +0 -0
  54. {mt5cli-1.0.3 → mt5cli-1.1.1}/docs/index.md +0 -0
  55. {mt5cli-1.0.3 → mt5cli-1.1.1}/mkdocs.yml +0 -0
  56. {mt5cli-1.0.3 → mt5cli-1.1.1}/mt5cli/__main__.py +0 -0
  57. {mt5cli-1.0.3 → mt5cli-1.1.1}/mt5cli/client.py +0 -0
  58. {mt5cli-1.0.3 → mt5cli-1.1.1}/mt5cli/converters.py +0 -0
  59. {mt5cli-1.0.3 → mt5cli-1.1.1}/mt5cli/exceptions.py +0 -0
  60. {mt5cli-1.0.3 → mt5cli-1.1.1}/mt5cli/history.py +0 -0
  61. {mt5cli-1.0.3 → mt5cli-1.1.1}/mt5cli/retry.py +0 -0
  62. {mt5cli-1.0.3 → mt5cli-1.1.1}/mt5cli/schemas.py +0 -0
  63. {mt5cli-1.0.3 → mt5cli-1.1.1}/tests/__init__.py +0 -0
  64. {mt5cli-1.0.3 → mt5cli-1.1.1}/tests/test_contracts.py +0 -0
  65. {mt5cli-1.0.3 → mt5cli-1.1.1}/tests/test_history.py +0 -0
  66. {mt5cli-1.0.3 → mt5cli-1.1.1}/tests/test_utils.py +0 -0
@@ -10,7 +10,7 @@ uv run pyright .
10
10
  uv run pytest
11
11
 
12
12
  # Markdown
13
- npx -y prettier --write './**/*.md'
13
+ npx -y prettier --write './**/*.{md,json}'
14
14
 
15
15
  # GitHub Actions
16
16
  case "${OSTYPE}" in
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: mt5cli
3
- Version: 1.0.3
3
+ Version: 1.1.1
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>
@@ -19,6 +19,10 @@ Requires-Python: <3.14,>=3.11
19
19
  Requires-Dist: click>=8.1.0
20
20
  Requires-Dist: pdmt5>=1.0.0
21
21
  Requires-Dist: typer>=0.15.0
22
+ Provides-Extra: otel
23
+ Requires-Dist: opentelemetry-api; extra == 'otel'
24
+ Requires-Dist: opentelemetry-exporter-otlp-proto-http; extra == 'otel'
25
+ Requires-Dist: opentelemetry-sdk; extra == 'otel'
22
26
  Provides-Extra: parquet
23
27
  Requires-Dist: pyarrow>=19.0.0; extra == 'parquet'
24
28
  Description-Content-Type: text/markdown
@@ -210,6 +214,8 @@ python -m mt5cli -o account.csv account-info
210
214
  | `order-send` | Send a raw trade request to the trade server (`--yes` required; expert path) |
211
215
  | `close-positions` | Close open positions by `--symbol` or `--ticket` (`--yes` required for live; `--dry-run` available) |
212
216
  | `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`) |
217
+ | `grafana-schema` | Create or refresh Grafana-ready views and indexes in an existing SQLite database (idempotent, no MT5 connection) |
218
+ | `snapshot` | Snapshot current account, position, order, and terminal state into SQLite for live Grafana dashboards |
213
219
 
214
220
  Use `order-check` to validate a request payload before running `order-send --yes`.
215
221
  `close-positions` is the safer high-level alternative that builds correct close
@@ -229,6 +235,104 @@ mt5cli -o history.db collect-history \
229
235
 
230
236
  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.
231
237
 
238
+ ### Grafana-ready SQLite dashboards
239
+
240
+ mt5cli can prepare a SQLite database for use as a Grafana datasource (via the [SQLite plugin](https://grafana.com/grafana/plugins/frser-sqlite-datasource/) or similar). Most `grafana_*` views expose an integer epoch-second `time` column for use in Grafana time-series panels. Two views (`grafana_realized_pnl`, `grafana_trade_stats`) are static symbol-level summaries with no `time` column — use them in table or stat panels.
241
+
242
+ #### Prepare the schema (idempotent, no MT5 connection needed)
243
+
244
+ ```bash
245
+ mt5cli -o history.db grafana-schema
246
+ ```
247
+
248
+ 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.
249
+
250
+ #### Snapshot current account state
251
+
252
+ ```bash
253
+ mt5cli -o history.db snapshot \
254
+ --symbol JP225 --symbol HK50 --symbol NL25 \
255
+ --with-account --with-positions --with-orders --with-terminal \
256
+ --with-grafana-schema
257
+ ```
258
+
259
+ 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.
260
+
261
+ #### SDK usage
262
+
263
+ ```python
264
+ from pdmt5 import Mt5DataClient, Mt5Config
265
+ from mt5cli import update_observability, update_observability_with_config
266
+
267
+ # Reuse an already-connected client
268
+ client = Mt5DataClient(config=Mt5Config(login=12345))
269
+ client.initialize_and_login_mt5()
270
+ try:
271
+ update_observability(
272
+ client=client,
273
+ output="history.db",
274
+ symbols=["EURUSD", "GBPUSD"], # optional position/order filter
275
+ include_account=True,
276
+ include_positions=True,
277
+ include_orders=True,
278
+ include_terminal=True,
279
+ with_grafana_schema=True,
280
+ )
281
+ finally:
282
+ client.shutdown()
283
+
284
+ # Standalone wrapper that opens/closes MT5 automatically
285
+ update_observability_with_config(
286
+ output="history.db",
287
+ config=Mt5Config(login=12345),
288
+ )
289
+ ```
290
+
291
+ #### Available Grafana views
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
+ > **Note**: OpenTelemetry integration is intentionally not part of this release and is tracked separately.
335
+
232
336
  ### Incremental history SDK
233
337
 
234
338
  For automated pipelines, use the importable incremental API instead of re-fetching fixed date ranges:
@@ -185,6 +185,8 @@ python -m mt5cli -o account.csv account-info
185
185
  | `order-send` | Send a raw trade request to the trade server (`--yes` required; expert path) |
186
186
  | `close-positions` | Close open positions by `--symbol` or `--ticket` (`--yes` required for live; `--dry-run` available) |
187
187
  | `collect-history` | Collect rates, history-orders, and history-deals for one or more symbols into a single SQLite database (ticks opt-in via `--dataset ticks`) |
188
+ | `grafana-schema` | Create or refresh Grafana-ready views and indexes in an existing SQLite database (idempotent, no MT5 connection) |
189
+ | `snapshot` | Snapshot current account, position, order, and terminal state into SQLite for live Grafana dashboards |
188
190
 
189
191
  Use `order-check` to validate a request payload before running `order-send --yes`.
190
192
  `close-positions` is the safer high-level alternative that builds correct close
@@ -204,6 +206,104 @@ mt5cli -o history.db collect-history \
204
206
 
205
207
  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.
206
208
 
209
+ ### Grafana-ready SQLite dashboards
210
+
211
+ mt5cli can prepare a SQLite database for use as a Grafana datasource (via the [SQLite plugin](https://grafana.com/grafana/plugins/frser-sqlite-datasource/) or similar). Most `grafana_*` views expose an integer epoch-second `time` column for use in Grafana time-series panels. Two views (`grafana_realized_pnl`, `grafana_trade_stats`) are static symbol-level summaries with no `time` column — use them in table or stat panels.
212
+
213
+ #### Prepare the schema (idempotent, no MT5 connection needed)
214
+
215
+ ```bash
216
+ mt5cli -o history.db grafana-schema
217
+ ```
218
+
219
+ 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.
220
+
221
+ #### Snapshot current account state
222
+
223
+ ```bash
224
+ mt5cli -o history.db snapshot \
225
+ --symbol JP225 --symbol HK50 --symbol NL25 \
226
+ --with-account --with-positions --with-orders --with-terminal \
227
+ --with-grafana-schema
228
+ ```
229
+
230
+ 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.
231
+
232
+ #### SDK usage
233
+
234
+ ```python
235
+ from pdmt5 import Mt5DataClient, Mt5Config
236
+ from mt5cli import update_observability, update_observability_with_config
237
+
238
+ # Reuse an already-connected client
239
+ client = Mt5DataClient(config=Mt5Config(login=12345))
240
+ client.initialize_and_login_mt5()
241
+ try:
242
+ update_observability(
243
+ client=client,
244
+ output="history.db",
245
+ symbols=["EURUSD", "GBPUSD"], # optional position/order filter
246
+ include_account=True,
247
+ include_positions=True,
248
+ include_orders=True,
249
+ include_terminal=True,
250
+ with_grafana_schema=True,
251
+ )
252
+ finally:
253
+ client.shutdown()
254
+
255
+ # Standalone wrapper that opens/closes MT5 automatically
256
+ update_observability_with_config(
257
+ output="history.db",
258
+ config=Mt5Config(login=12345),
259
+ )
260
+ ```
261
+
262
+ #### Available Grafana views
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
+ > **Note**: OpenTelemetry integration is intentionally not part of this release and is tracked separately.
306
+
207
307
  ### Incremental history SDK
208
308
 
209
309
  For automated pipelines, use the importable incremental API instead of re-fetching fixed date ranges:
@@ -45,7 +45,7 @@ These names are exported from `mt5cli` and enumerated in
45
45
  | `MT5Client` | Read-only data client with optional `order_check` / `order_send` |
46
46
  | `build_config` | Build `pdmt5.Mt5Config` from connection fields; `login` accepts `int \| str \| None` — numeric strings are coerced to `int`, blank strings are treated as unset, and `${ENV_VAR}` / `$ENV_NAME` placeholders in string parameters are expanded when `allow_whole_dollar_env=True` |
47
47
  | `mt5_session` | Context manager: initialize, login, yield client, shutdown |
48
- | `create_trading_client`, `mt5_trading_session` | Trading-capable MT5 client lifecycle; returns a client supporting order execution and account management |
48
+ | `create_trading_client`, `mt5_trading_session` | Trading-capable MT5 client lifecycle; returns a raw `pdmt5.Mt5DataClient` (not `MT5Client`) supporting order execution, account management, and history deal retrieval |
49
49
  | `AccountSpec` | Generic account group: symbols plus optional credentials |
50
50
  | `resolve_account_spec`, `resolve_account_specs` | Merge overrides and expand `${ENV_VAR}` placeholders; opt-in `allow_whole_dollar_env` for bare `$NAME` |
51
51
 
@@ -99,6 +99,7 @@ strategy entries, exits, Kelly sizing, or signal logic.
99
99
  | `determine_order_limits` | SL/TP price levels from ratios |
100
100
  | `calculate_trailing_stop_updates` | Per-ticket generic trailing stop-loss update plan |
101
101
  | `ensure_symbol_selected` | Select/verify Market Watch visibility |
102
+ | `fetch_recent_history_deals_for_trading_client` | Recent deal history from a connected trading client |
102
103
  | `place_market_order`, `close_open_positions`, `update_sltp_for_open_positions`, `update_trailing_stop_loss_for_open_positions` | Order execution helpers (`dry_run` supported) |
103
104
  | `MarginVolume`, `OrderLimits`, `OrderExecutionResult` | Typed return contracts for order helpers |
104
105
  | `OrderSide`, `OrderFillingMode`, `OrderTimeMode`, `PositionSide`, `ExecutionStatus` | Typed enums for order helpers |
@@ -124,6 +125,63 @@ sending requests. Failed, malformed, or unknown broker retcodes are fail-closed
124
125
  and returned as `status="failed"` with normalized `request` / `response` details;
125
126
  `dry_run=True` never calls `ensure_symbol_selected()` or `order_send()`.
126
127
 
128
+ ### Grafana observability (SQLite read model)
129
+
130
+ These helpers prepare a SQLite database as a Grafana datasource. All DDL is
131
+ idempotent (`CREATE TABLE IF NOT EXISTS`, `DROP VIEW IF EXISTS` + `CREATE
132
+ VIEW`, `CREATE INDEX IF NOT EXISTS`). Missing source tables are skipped with a
133
+ warning rather than raising an error.
134
+
135
+ | Symbol | Role |
136
+ | ---------------------------------- | ----------------------------------------------------------------------------------------------- |
137
+ | `update_observability` | Append one timestamped snapshot row per data type; accepts an already-connected `Mt5DataClient` |
138
+ | `update_observability_with_config` | Standalone wrapper: opens/closes MT5 connection automatically around `update_observability` |
139
+
140
+ Both functions write to the SQLite path given by `output=`. The optional
141
+ `symbols` parameter filters `positions_get` / `orders_get` by symbol.
142
+ `with_grafana_schema=False` (default) skips Grafana view/index setup; run
143
+ `grafana-schema` once to set up the schema, then call `snapshot` repeatedly
144
+ without this flag.
145
+
146
+ **Snapshot tables** (created by `create_snapshot_tables` in `mt5cli.grafana`):
147
+
148
+ | Table | Content |
149
+ | -------------------- | ----------------------------------------- |
150
+ | `account_snapshots` | Balance, equity, margin, free-margin, P&L |
151
+ | `position_snapshots` | Open positions: symbol, volume, profit, … |
152
+ | `order_snapshots` | Active orders: symbol, type, price, … |
153
+ | `terminal_snapshots` | Terminal connectivity and build info |
154
+ | `snapshot_runs` | Per-run status (`ok` / `error`) timestamp |
155
+
156
+ **Grafana time-series views** (integer epoch-second `time` column; snapshot views also expose `run_id`):
157
+
158
+ | View | Source |
159
+ | ---------------------------- | -------------------------------- |
160
+ | `grafana_rates` | `rates` table |
161
+ | `grafana_ticks` | `ticks` table |
162
+ | `grafana_history_deals` | `history_deals` |
163
+ | `grafana_history_orders` | `history_orders` |
164
+ | `grafana_trade_deals` | `history_deals` trade types only |
165
+ | `grafana_cash_events` | `history_deals` non-trade events |
166
+ | `grafana_symbol_pnl` | Per-close-deal P&L per symbol |
167
+ | `grafana_account_snapshots` | `account_snapshots` |
168
+ | `grafana_position_snapshots` | `position_snapshots` |
169
+ | `grafana_order_snapshots` | `order_snapshots` |
170
+ | `grafana_terminal_snapshots` | `terminal_snapshots` |
171
+
172
+ **Grafana static summary views** (no `time` column; use for table/stat panels, not time-series):
173
+
174
+ | View | Source |
175
+ | ---------------------- | ------------------------------------- |
176
+ | `grafana_realized_pnl` | Cumulative realized PnL per symbol |
177
+ | `grafana_trade_stats` | Win/loss counts and profit per symbol |
178
+
179
+ Lower-level helpers (`ensure_grafana_schema`, `create_grafana_views`,
180
+ `create_grafana_indexes`, `create_snapshot_tables`, `start_snapshot_run`,
181
+ `insert_account_snapshot`, `insert_position_snapshots`, `insert_order_snapshots`,
182
+ `insert_terminal_snapshot`, `record_snapshot_run`) are available directly from
183
+ `mt5cli.grafana` and are not part of the package-root stable surface.
184
+
127
185
  ### Errors
128
186
 
129
187
  | Symbol | Role |
@@ -135,14 +193,15 @@ and returned as `status="failed"` with normalized `request` / `response` details
135
193
  Lower-level helpers are available from their owning modules and are not part
136
194
  of the package-root stable surface. Import them directly when needed:
137
195
 
138
- | Module | Examples |
139
- | ------------------- | ---------------------------------------------------------------------------------------------- |
140
- | `mt5cli.history` | `resolve_rate_view_name`, `resolve_rate_tables`, `load_rate_data`, `build_rate_view_name` |
141
- | `mt5cli.sdk` | `copy_rates_from`, `copy_ticks_from`, `account_info`, `symbols`, `mt5_summary`, `latest_rates` |
142
- | `mt5cli.schemas` | `DataKind`, `normalize_dataframe`, `validate_schema`, `DEDUP_KEYS` |
143
- | `mt5cli.utils` | `Dataset`, `IfExists`, `detect_format`, `export_dataframe`, `export_dataframe_to_sqlite` |
144
- | `mt5cli.converters` | `normalize_symbol`, `ensure_utc`, `parse_date_range`, `granularity_name` |
145
- | `mt5cli.exceptions` | `normalize_mt5_exception`, `call_with_normalized_errors`, `is_recoverable_mt5_error` |
196
+ | Module | Examples |
197
+ | ------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
198
+ | `mt5cli.grafana` | `ensure_grafana_schema`, `create_grafana_views`, `create_grafana_indexes`, `create_snapshot_tables`, `start_snapshot_run`, `insert_account_snapshot`, `record_snapshot_run` |
199
+ | `mt5cli.history` | `resolve_rate_view_name`, `resolve_rate_tables`, `load_rate_data`, `build_rate_view_name` |
200
+ | `mt5cli.sdk` | `copy_rates_from`, `copy_ticks_from`, `account_info`, `symbols`, `mt5_summary`, `latest_rates` |
201
+ | `mt5cli.schemas` | `DataKind`, `normalize_dataframe`, `validate_schema`, `DEDUP_KEYS` |
202
+ | `mt5cli.utils` | `Dataset`, `IfExists`, `detect_format`, `export_dataframe`, `export_dataframe_to_sqlite` |
203
+ | `mt5cli.converters` | `normalize_symbol`, `ensure_utc`, `parse_date_range`, `granularity_name` |
204
+ | `mt5cli.exceptions` | `normalize_mt5_exception`, `call_with_normalized_errors`, `is_recoverable_mt5_error` |
146
205
 
147
206
  ## CLI commands
148
207
 
@@ -155,6 +214,15 @@ The Typer application in `mt5cli.cli` exposes file-export commands documented in
155
214
  - Delegate to the same Python APIs described here; they are not duplicated
156
215
  business logic.
157
216
 
217
+ `grafana-schema` initializes Grafana views, indexes, and snapshot tables in the
218
+ target SQLite database without connecting to MT5. It is idempotent and safe to
219
+ run repeatedly.
220
+
221
+ `snapshot` appends one timestamped row per enabled data type
222
+ (`--with-account`, `--with-positions`, `--with-orders`, `--with-terminal`) and
223
+ never places orders or modifies trading state. Both commands require
224
+ `-o/--output` to point at a `.db` / SQLite file.
225
+
158
226
  `order-send` is the expert raw-request path; it requires `--yes` and a fully
159
227
  constructed request payload. `close-positions` is the safer high-level helper
160
228
  that closes open positions by `--symbol` or `--ticket` using
@@ -187,6 +255,9 @@ The following belong in consuming applications, not in mt5cli:
187
255
  - Backtesting, walk-forward analysis, or parameter optimization
188
256
  - Strategy-specific risk policy, position sizing systems, or Kelly fractions
189
257
  - Entry/exit decision logic or YAML strategy semantics
258
+ - Entry-deal classification, Kelly fractions, or betting-specific deal transformations
259
+ (use `fetch_recent_history_deals_for_trading_client` to retrieve raw deal data, then
260
+ apply downstream transformations in your own adapter layer)
190
261
  - Application-specific credential schema keys wired into mt5cli internals
191
262
 
192
263
  mt5cli provides connection lifecycle, normalized data access, SQLite history
@@ -10,6 +10,11 @@ client supporting order execution and account management, use `Mt5Config.path`
10
10
  to launch the terminal when configured, and `mt5_trading_session()` always
11
11
  calls `shutdown()` on exit.
12
12
 
13
+ `create_trading_client()` returns a raw `pdmt5.Mt5DataClient` instance, not the
14
+ higher-level `MT5Client` wrapper. Use `mt5_session()` / `MT5Client` for
15
+ read-only data collection; use `mt5_trading_session()` only where order
16
+ placement or trading calculations are required.
17
+
13
18
  ```python
14
19
  from mt5cli import create_trading_client, mt5_trading_session
15
20
 
@@ -182,6 +187,55 @@ updates: list[OrderExecutionResult] = update_sltp_for_open_positions(
182
187
  Closes issue #33: strategy-neutral order planning and execution helpers exposed
183
188
  through the stable package root without embedding entry/exit policy.
184
189
 
190
+ ## Retrieving recent history deals
191
+
192
+ `fetch_recent_history_deals_for_trading_client()` fetches history deals from an
193
+ already-connected trading client over a trailing time window. It works directly
194
+ with the object returned by `create_trading_client()` (a raw
195
+ `pdmt5.Mt5DataClient`) without requiring any additional wrapping.
196
+
197
+ The helper returns a chronologically sorted DataFrame with a `RangeIndex` and
198
+ all columns from the underlying client (`time`, `symbol`, `type`, `entry`,
199
+ `volume`, `profit`, `position_id`, etc.). It does **not** apply any
200
+ strategy-specific transformations — entry/exit classification, Kelly fractions,
201
+ and betting semantics belong in downstream applications.
202
+
203
+ ```python
204
+ from mt5cli import (
205
+ create_trading_client,
206
+ fetch_recent_history_deals_for_trading_client,
207
+ )
208
+
209
+ client = create_trading_client(login=12345, server="Broker-Demo")
210
+ try:
211
+ deals_df = fetch_recent_history_deals_for_trading_client(
212
+ client,
213
+ symbol="JP225",
214
+ hours=24,
215
+ )
216
+ finally:
217
+ client.shutdown()
218
+ ```
219
+
220
+ Or inside a managed session:
221
+
222
+ ```python
223
+ from mt5cli import fetch_recent_history_deals_for_trading_client, mt5_trading_session
224
+
225
+ with mt5_trading_session(login=12345, server="Broker-Demo") as client:
226
+ deals_df = fetch_recent_history_deals_for_trading_client(
227
+ client,
228
+ symbol="JP225",
229
+ hours=48,
230
+ )
231
+ ```
232
+
233
+ `hours` must be positive; `date_to` defaults to `datetime.now(UTC)`. An empty
234
+ or `None` result from the underlying client is normalized to an empty DataFrame.
235
+
236
+ Downstream packages own all strategy-specific transformations. mt5cli does not
237
+ provide entry-deal classification, Kelly sizing, or any betting-specific helpers.
238
+
185
239
  ## Migration from application-local helpers
186
240
 
187
241
  | Application-local concern | mt5cli replacement |
@@ -192,6 +246,7 @@ through the stable package root without embedding entry/exit policy.
192
246
  | Local broker volume step normalization | `normalize_order_volume()` |
193
247
  | Local order or position margin estimation | `estimate_order_margin()`, `calculate_positions_margin()` |
194
248
  | Local closed-bar fetch from a trading session | `fetch_latest_closed_rates_for_trading_client()`, `fetch_latest_closed_rates_indexed()` |
249
+ | Local recent deal history fetch from a trading session | `fetch_recent_history_deals_for_trading_client()` |
195
250
  | Local SL/TP price derivation | `determine_order_limits()` |
196
251
  | Throttled SQLite history loop with ad-hoc error handling | `ThrottledHistoryUpdater(suppress_errors=True)` |
197
252
 
@@ -0,0 +1,95 @@
1
+ # Grafana Integration for mt5cli
2
+
3
+ This directory contains example configuration and dashboard files for visualising
4
+ mt5cli SQLite data in [Grafana](https://grafana.com/) using the
5
+ [Grafana SQLite datasource plugin](https://grafana.com/grafana/plugins/frser-sqlite-datasource/).
6
+
7
+ ## Prerequisites
8
+
9
+ - mt5cli installed and able to connect to MetaTrader 5
10
+ - Grafana 10+ with the `frser-sqlite-datasource` plugin installed
11
+ - (Optional) Docker and Docker Compose for the containerised setup
12
+
13
+ ## Generating the SQLite database
14
+
15
+ Collect historical data and snapshot current account state:
16
+
17
+ ```sh
18
+ # Collect OHLCV history
19
+ mt5cli -o history.db collect-history --symbol EURUSD --date-from 2024-01-01 --date-to 2024-12-31
20
+
21
+ # Create Grafana-ready views and indexes
22
+ mt5cli -o history.db grafana-schema
23
+
24
+ # Snapshot current account, positions, and orders
25
+ mt5cli -o history.db snapshot --with-grafana-schema
26
+ ```
27
+
28
+ ## Publishing a Grafana-readable copy
29
+
30
+ Grafana reads the SQLite file directly. To avoid read/write conflicts, publish
31
+ a consistent copy after each update:
32
+
33
+ ```sh
34
+ mt5cli -o history.db grafana-schema --publish-copy history.mt5cli.db
35
+ mt5cli -o history.db snapshot --publish-copy history.mt5cli.db
36
+ ```
37
+
38
+ The `--publish-copy` option uses the SQLite online backup API, which is safe
39
+ even when the source database uses WAL journal mode.
40
+
41
+ ## Configuring the datasource path
42
+
43
+ Edit `provisioning/datasources/mt5cli-sqlite.yml` and set the `path` field
44
+ to the absolute path of your published `.db` file:
45
+
46
+ ```yaml
47
+ jsonData:
48
+ path: /absolute/path/to/history.mt5cli.db
49
+ ```
50
+
51
+ ## Running Grafana on Windows (native)
52
+
53
+ 1. Download and install Grafana from <https://grafana.com/grafana/download/>.
54
+ 2. Install the SQLite plugin: `grafana-cli plugins install frser-sqlite-datasource`.
55
+ 3. Copy `provisioning/datasources/mt5cli-sqlite.yml` into
56
+ `%ProgramFiles%\GrafanaLabs\grafana\conf\provisioning\datasources\`.
57
+ Do not copy `provisioning/dashboards/mt5cli.yml` — it contains a
58
+ Docker-specific dashboard path that is not valid on Windows.
59
+ 4. Import the dashboards from `dashboards/` via the Grafana UI
60
+ (Dashboards → Import → Upload JSON file).
61
+
62
+ ## Running with Docker Compose
63
+
64
+ Set `MT5CLI_DB_PATH` to the absolute path of your published `.db` file, then
65
+ start the stack:
66
+
67
+ ```sh
68
+ # From the examples/grafana directory
69
+ MT5CLI_DB_PATH=/absolute/path/to/history.mt5cli.db docker compose up -d
70
+ ```
71
+
72
+ Alternatively, create a `.env` file in `examples/grafana/` containing
73
+ `MT5CLI_DB_PATH=/absolute/path/to/history.mt5cli.db` and run
74
+ `docker compose up -d`. Compose refuses to start if the variable is unset or
75
+ empty.
76
+
77
+ Then open <http://localhost:3000> (default credentials: admin / admin).
78
+
79
+ ## Dashboard overview
80
+
81
+ | Dashboard | Description |
82
+ | ---------------------- | ------------------------------------------------------- |
83
+ | `mt5cli-overview.json` | Account balance, equity, margin, and snapshot freshness |
84
+ | `mt5cli-trades.json` | Trade P/L, win rate, symbol breakdown |
85
+ | `mt5cli-market.json` | OHLCV rates, spreads, and tick volume |
86
+
87
+ All panel queries use the `grafana_*` views; they do not read internal storage
88
+ tables directly.
89
+
90
+ ## Importing dashboards
91
+
92
+ 1. Open Grafana and navigate to **Dashboards → Import**.
93
+ 2. Click **Upload JSON file** and select one of the files in `dashboards/`.
94
+ 3. Select the `mt5cli-SQLite` datasource when prompted.
95
+ 4. Click **Import**.
@@ -0,0 +1,26 @@
1
+ # Docker Compose for Grafana with mt5cli SQLite datasource.
2
+ #
3
+ # MT5CLI_DB_PATH must be set to the absolute host path of your published .db
4
+ # file before running `docker compose up -d`. Compose will refuse to start if
5
+ # the variable is missing or empty.
6
+ #
7
+ # Example:
8
+ # MT5CLI_DB_PATH=/home/user/history.mt5cli.db docker compose up -d
9
+
10
+ services:
11
+ grafana:
12
+ image: grafana/grafana:latest
13
+ ports:
14
+ - "3000:3000"
15
+ environment:
16
+ GF_PATHS_PROVISIONING: /etc/grafana/provisioning
17
+ GF_INSTALL_PLUGINS: frser-sqlite-datasource
18
+ volumes:
19
+ - ./provisioning:/etc/grafana/provisioning:ro
20
+ - ./dashboards:/var/lib/grafana/dashboards:ro
21
+ - grafana-storage:/var/lib/grafana
22
+ - ${MT5CLI_DB_PATH:?Set MT5CLI_DB_PATH to the path of your published mt5cli SQLite DB}:/data/mt5cli.db:ro
23
+ user: "472"
24
+
25
+ volumes:
26
+ grafana-storage: