mt5cli 1.0.2__tar.gz → 1.1.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.
Files changed (66) hide show
  1. {mt5cli-1.0.2 → mt5cli-1.1.0}/.agents/skills/local-qa/scripts/qa.sh +1 -1
  2. {mt5cli-1.0.2 → mt5cli-1.1.0}/PKG-INFO +132 -28
  3. {mt5cli-1.0.2 → mt5cli-1.1.0}/README.md +127 -27
  4. {mt5cli-1.0.2 → mt5cli-1.1.0}/docs/api/public-contract.md +75 -8
  5. mt5cli-1.1.0/examples/grafana/README.md +95 -0
  6. mt5cli-1.1.0/examples/grafana/compose.yml +26 -0
  7. mt5cli-1.1.0/examples/grafana/dashboards/mt5cli-market.json +98 -0
  8. mt5cli-1.1.0/examples/grafana/dashboards/mt5cli-overview.json +254 -0
  9. mt5cli-1.1.0/examples/grafana/dashboards/mt5cli-trades.json +167 -0
  10. mt5cli-1.1.0/examples/grafana/provisioning/dashboards/mt5cli.yml +13 -0
  11. mt5cli-1.1.0/examples/grafana/provisioning/datasources/mt5cli-sqlite.yml +16 -0
  12. {mt5cli-1.0.2 → mt5cli-1.1.0}/mt5cli/__init__.py +4 -0
  13. {mt5cli-1.0.2 → mt5cli-1.1.0}/mt5cli/cli.py +125 -0
  14. {mt5cli-1.0.2 → mt5cli-1.1.0}/mt5cli/contract.py +2 -0
  15. mt5cli-1.1.0/mt5cli/grafana.py +682 -0
  16. {mt5cli-1.0.2 → mt5cli-1.1.0}/mt5cli/sdk.py +255 -17
  17. mt5cli-1.1.0/mt5cli/telemetry.py +354 -0
  18. {mt5cli-1.0.2 → mt5cli-1.1.0}/mt5cli/trading.py +10 -2
  19. {mt5cli-1.0.2 → mt5cli-1.1.0}/mt5cli/utils.py +2 -1
  20. {mt5cli-1.0.2 → mt5cli-1.1.0}/pyproject.toml +8 -1
  21. {mt5cli-1.0.2 → mt5cli-1.1.0}/tests/conftest.py +38 -0
  22. {mt5cli-1.0.2 → mt5cli-1.1.0}/tests/test_cli.py +239 -0
  23. mt5cli-1.1.0/tests/test_examples.py +75 -0
  24. mt5cli-1.1.0/tests/test_grafana.py +991 -0
  25. {mt5cli-1.0.2 → mt5cli-1.1.0}/tests/test_sdk.py +515 -0
  26. mt5cli-1.1.0/tests/test_telemetry.py +249 -0
  27. {mt5cli-1.0.2 → mt5cli-1.1.0}/tests/test_trading.py +88 -0
  28. {mt5cli-1.0.2 → mt5cli-1.1.0}/uv.lock +123 -3
  29. {mt5cli-1.0.2 → mt5cli-1.1.0}/.agents/skills/local-qa/SKILL.md +0 -0
  30. {mt5cli-1.0.2 → mt5cli-1.1.0}/.agents/skills/mt5cli/SKILL.md +0 -0
  31. {mt5cli-1.0.2 → mt5cli-1.1.0}/.agents/skills/pr-feedback-triage/SKILL.md +0 -0
  32. {mt5cli-1.0.2 → mt5cli-1.1.0}/.claude/agents/codex.md +0 -0
  33. {mt5cli-1.0.2 → mt5cli-1.1.0}/.claude/settings.json +0 -0
  34. {mt5cli-1.0.2 → mt5cli-1.1.0}/.github/FUNDING.yml +0 -0
  35. {mt5cli-1.0.2 → mt5cli-1.1.0}/.github/dependabot.yml +0 -0
  36. {mt5cli-1.0.2 → mt5cli-1.1.0}/.github/renovate.json +0 -0
  37. {mt5cli-1.0.2 → mt5cli-1.1.0}/.github/workflows/ci.yml +0 -0
  38. {mt5cli-1.0.2 → mt5cli-1.1.0}/.github/workflows/claude.yml +0 -0
  39. {mt5cli-1.0.2 → mt5cli-1.1.0}/.github/workflows/release.yml +0 -0
  40. {mt5cli-1.0.2 → mt5cli-1.1.0}/.gitignore +0 -0
  41. {mt5cli-1.0.2 → mt5cli-1.1.0}/AGENTS.md +0 -0
  42. {mt5cli-1.0.2 → mt5cli-1.1.0}/CLAUDE.md +0 -0
  43. {mt5cli-1.0.2 → mt5cli-1.1.0}/LICENSE +0 -0
  44. {mt5cli-1.0.2 → mt5cli-1.1.0}/docs/api/cli.md +0 -0
  45. {mt5cli-1.0.2 → mt5cli-1.1.0}/docs/api/client.md +0 -0
  46. {mt5cli-1.0.2 → mt5cli-1.1.0}/docs/api/converters.md +0 -0
  47. {mt5cli-1.0.2 → mt5cli-1.1.0}/docs/api/exceptions.md +0 -0
  48. {mt5cli-1.0.2 → mt5cli-1.1.0}/docs/api/history.md +0 -0
  49. {mt5cli-1.0.2 → mt5cli-1.1.0}/docs/api/index.md +0 -0
  50. {mt5cli-1.0.2 → mt5cli-1.1.0}/docs/api/schemas.md +0 -0
  51. {mt5cli-1.0.2 → mt5cli-1.1.0}/docs/api/sdk.md +0 -0
  52. {mt5cli-1.0.2 → mt5cli-1.1.0}/docs/api/trading.md +0 -0
  53. {mt5cli-1.0.2 → mt5cli-1.1.0}/docs/api/utils.md +0 -0
  54. {mt5cli-1.0.2 → mt5cli-1.1.0}/docs/index.md +0 -0
  55. {mt5cli-1.0.2 → mt5cli-1.1.0}/mkdocs.yml +0 -0
  56. {mt5cli-1.0.2 → mt5cli-1.1.0}/mt5cli/__main__.py +0 -0
  57. {mt5cli-1.0.2 → mt5cli-1.1.0}/mt5cli/client.py +0 -0
  58. {mt5cli-1.0.2 → mt5cli-1.1.0}/mt5cli/converters.py +0 -0
  59. {mt5cli-1.0.2 → mt5cli-1.1.0}/mt5cli/exceptions.py +0 -0
  60. {mt5cli-1.0.2 → mt5cli-1.1.0}/mt5cli/history.py +0 -0
  61. {mt5cli-1.0.2 → mt5cli-1.1.0}/mt5cli/retry.py +0 -0
  62. {mt5cli-1.0.2 → mt5cli-1.1.0}/mt5cli/schemas.py +0 -0
  63. {mt5cli-1.0.2 → mt5cli-1.1.0}/tests/__init__.py +0 -0
  64. {mt5cli-1.0.2 → mt5cli-1.1.0}/tests/test_contracts.py +0 -0
  65. {mt5cli-1.0.2 → mt5cli-1.1.0}/tests/test_history.py +0 -0
  66. {mt5cli-1.0.2 → mt5cli-1.1.0}/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.2
3
+ Version: 1.1.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>
@@ -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
@@ -182,34 +186,36 @@ python -m mt5cli -o account.csv account-info
182
186
 
183
187
  ## Commands
184
188
 
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) |
189
+ | Command | Description |
190
+ | ---------------------- | ------------------------------------------------------------------------------------------------------------------------------------------- |
191
+ | `rates-from` | Export rates from a start date |
192
+ | `rates-from-pos` | Export rates from a start position |
193
+ | `latest-rates` | Export latest rates from a start position |
194
+ | `rates-range` | Export rates for a date range |
195
+ | `ticks-from` | Export ticks from a start date |
196
+ | `ticks-range` | Export ticks for a date range |
197
+ | `ticks-recent` | Export ticks from a recent trailing window |
198
+ | `account-info` | Export account information |
199
+ | `terminal-info` | Export terminal information |
200
+ | `version` | Export MetaTrader 5 version information |
201
+ | `last-error` | Export the last error information |
202
+ | `symbols` | Export symbol list |
203
+ | `symbol-info` | Export symbol details |
204
+ | `symbol-info-tick` | Export the last tick for a symbol |
205
+ | `minimum-margins` | Export minimum-volume buy and sell margin requirements |
206
+ | `market-book` | Export market depth (order book) |
207
+ | `orders` | Export active orders |
208
+ | `positions` | Export open positions |
209
+ | `history-orders` | Export historical orders |
210
+ | `history-deals` | Export historical deals |
211
+ | `recent-history-deals` | Export historical deals from a recent trailing window |
212
+ | `mt5-summary` | Export terminal/account status summary |
213
+ | `order-check` | Check funds sufficiency for a trade request |
214
+ | `order-send` | Send a raw trade request to the trade server (`--yes` required; expert path) |
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:
@@ -157,34 +157,36 @@ 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) |
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
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:
@@ -124,6 +124,63 @@ sending requests. Failed, malformed, or unknown broker retcodes are fail-closed
124
124
  and returned as `status="failed"` with normalized `request` / `response` details;
125
125
  `dry_run=True` never calls `ensure_symbol_selected()` or `order_send()`.
126
126
 
127
+ ### Grafana observability (SQLite read model)
128
+
129
+ These helpers prepare a SQLite database as a Grafana datasource. All DDL is
130
+ idempotent (`CREATE TABLE IF NOT EXISTS`, `DROP VIEW IF EXISTS` + `CREATE
131
+ VIEW`, `CREATE INDEX IF NOT EXISTS`). Missing source tables are skipped with a
132
+ warning rather than raising an error.
133
+
134
+ | Symbol | Role |
135
+ | ---------------------------------- | ----------------------------------------------------------------------------------------------- |
136
+ | `update_observability` | Append one timestamped snapshot row per data type; accepts an already-connected `Mt5DataClient` |
137
+ | `update_observability_with_config` | Standalone wrapper: opens/closes MT5 connection automatically around `update_observability` |
138
+
139
+ Both functions write to the SQLite path given by `output=`. The optional
140
+ `symbols` parameter filters `positions_get` / `orders_get` by symbol.
141
+ `with_grafana_schema=False` (default) skips Grafana view/index setup; run
142
+ `grafana-schema` once to set up the schema, then call `snapshot` repeatedly
143
+ without this flag.
144
+
145
+ **Snapshot tables** (created by `create_snapshot_tables` in `mt5cli.grafana`):
146
+
147
+ | Table | Content |
148
+ | -------------------- | ----------------------------------------- |
149
+ | `account_snapshots` | Balance, equity, margin, free-margin, P&L |
150
+ | `position_snapshots` | Open positions: symbol, volume, profit, … |
151
+ | `order_snapshots` | Active orders: symbol, type, price, … |
152
+ | `terminal_snapshots` | Terminal connectivity and build info |
153
+ | `snapshot_runs` | Per-run status (`ok` / `error`) timestamp |
154
+
155
+ **Grafana time-series views** (integer epoch-second `time` column; snapshot views also expose `run_id`):
156
+
157
+ | View | Source |
158
+ | ---------------------------- | -------------------------------- |
159
+ | `grafana_rates` | `rates` table |
160
+ | `grafana_ticks` | `ticks` table |
161
+ | `grafana_history_deals` | `history_deals` |
162
+ | `grafana_history_orders` | `history_orders` |
163
+ | `grafana_trade_deals` | `history_deals` trade types only |
164
+ | `grafana_cash_events` | `history_deals` non-trade events |
165
+ | `grafana_symbol_pnl` | Per-close-deal P&L per symbol |
166
+ | `grafana_account_snapshots` | `account_snapshots` |
167
+ | `grafana_position_snapshots` | `position_snapshots` |
168
+ | `grafana_order_snapshots` | `order_snapshots` |
169
+ | `grafana_terminal_snapshots` | `terminal_snapshots` |
170
+
171
+ **Grafana static summary views** (no `time` column; use for table/stat panels, not time-series):
172
+
173
+ | View | Source |
174
+ | ---------------------- | ------------------------------------- |
175
+ | `grafana_realized_pnl` | Cumulative realized PnL per symbol |
176
+ | `grafana_trade_stats` | Win/loss counts and profit per symbol |
177
+
178
+ Lower-level helpers (`ensure_grafana_schema`, `create_grafana_views`,
179
+ `create_grafana_indexes`, `create_snapshot_tables`, `start_snapshot_run`,
180
+ `insert_account_snapshot`, `insert_position_snapshots`, `insert_order_snapshots`,
181
+ `insert_terminal_snapshot`, `record_snapshot_run`) are available directly from
182
+ `mt5cli.grafana` and are not part of the package-root stable surface.
183
+
127
184
  ### Errors
128
185
 
129
186
  | Symbol | Role |
@@ -135,14 +192,15 @@ and returned as `status="failed"` with normalized `request` / `response` details
135
192
  Lower-level helpers are available from their owning modules and are not part
136
193
  of the package-root stable surface. Import them directly when needed:
137
194
 
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` |
195
+ | Module | Examples |
196
+ | ------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
197
+ | `mt5cli.grafana` | `ensure_grafana_schema`, `create_grafana_views`, `create_grafana_indexes`, `create_snapshot_tables`, `start_snapshot_run`, `insert_account_snapshot`, `record_snapshot_run` |
198
+ | `mt5cli.history` | `resolve_rate_view_name`, `resolve_rate_tables`, `load_rate_data`, `build_rate_view_name` |
199
+ | `mt5cli.sdk` | `copy_rates_from`, `copy_ticks_from`, `account_info`, `symbols`, `mt5_summary`, `latest_rates` |
200
+ | `mt5cli.schemas` | `DataKind`, `normalize_dataframe`, `validate_schema`, `DEDUP_KEYS` |
201
+ | `mt5cli.utils` | `Dataset`, `IfExists`, `detect_format`, `export_dataframe`, `export_dataframe_to_sqlite` |
202
+ | `mt5cli.converters` | `normalize_symbol`, `ensure_utc`, `parse_date_range`, `granularity_name` |
203
+ | `mt5cli.exceptions` | `normalize_mt5_exception`, `call_with_normalized_errors`, `is_recoverable_mt5_error` |
146
204
 
147
205
  ## CLI commands
148
206
 
@@ -155,6 +213,15 @@ The Typer application in `mt5cli.cli` exposes file-export commands documented in
155
213
  - Delegate to the same Python APIs described here; they are not duplicated
156
214
  business logic.
157
215
 
216
+ `grafana-schema` initializes Grafana views, indexes, and snapshot tables in the
217
+ target SQLite database without connecting to MT5. It is idempotent and safe to
218
+ run repeatedly.
219
+
220
+ `snapshot` appends one timestamped row per enabled data type
221
+ (`--with-account`, `--with-positions`, `--with-orders`, `--with-terminal`) and
222
+ never places orders or modifies trading state. Both commands require
223
+ `-o/--output` to point at a `.db` / SQLite file.
224
+
158
225
  `order-send` is the expert raw-request path; it requires `--yes` and a fully
159
226
  constructed request payload. `close-positions` is the safer high-level helper
160
227
  that closes open positions by `--symbol` or `--ticket` using
@@ -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: