mt5cli 1.6.0__tar.gz → 1.7.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.
- {mt5cli-1.6.0 → mt5cli-1.7.1}/.agents/skills/local-qa/scripts/qa.sh +2 -2
- {mt5cli-1.6.0 → mt5cli-1.7.1}/.github/dependabot.yml +11 -3
- {mt5cli-1.6.0 → mt5cli-1.7.1}/.github/renovate.json +1 -1
- {mt5cli-1.6.0 → mt5cli-1.7.1}/.github/workflows/ci.yml +0 -13
- {mt5cli-1.6.0 → mt5cli-1.7.1}/.github/workflows/claude.yml +1 -1
- mt5cli-1.7.1/.github/workflows/codeql.yml +42 -0
- {mt5cli-1.6.0 → mt5cli-1.7.1}/.github/workflows/release.yml +2 -2
- mt5cli-1.6.0/README.md → mt5cli-1.7.1/PKG-INFO +37 -59
- mt5cli-1.6.0/PKG-INFO → mt5cli-1.7.1/README.md +8 -88
- mt5cli-1.7.1/docs/api/analytics.md +99 -0
- {mt5cli-1.6.0 → mt5cli-1.7.1}/docs/api/history.md +36 -10
- {mt5cli-1.6.0 → mt5cli-1.7.1}/docs/api/index.md +1 -3
- {mt5cli-1.6.0 → mt5cli-1.7.1}/docs/api/observability.md +7 -10
- {mt5cli-1.6.0 → mt5cli-1.7.1}/docs/api/public-contract.md +30 -56
- {mt5cli-1.6.0 → mt5cli-1.7.1}/mkdocs.yml +1 -1
- {mt5cli-1.6.0 → mt5cli-1.7.1}/mt5cli/__init__.py +2 -2
- mt5cli-1.7.1/mt5cli/analytics.py +405 -0
- {mt5cli-1.6.0 → mt5cli-1.7.1}/mt5cli/cli.py +33 -53
- {mt5cli-1.6.0 → mt5cli-1.7.1}/mt5cli/client.py +7 -7
- {mt5cli-1.6.0 → mt5cli-1.7.1}/mt5cli/history.py +190 -32
- {mt5cli-1.6.0 → mt5cli-1.7.1}/mt5cli/lifecycle.py +1 -1
- {mt5cli-1.6.0 → mt5cli-1.7.1}/mt5cli/observability.py +245 -34
- {mt5cli-1.6.0 → mt5cli-1.7.1}/mt5cli/rates.py +6 -3
- {mt5cli-1.6.0 → mt5cli-1.7.1}/mt5cli/schemas.py +13 -9
- {mt5cli-1.6.0 → mt5cli-1.7.1}/mt5cli/sdk.py +2 -2
- {mt5cli-1.6.0 → mt5cli-1.7.1}/mt5cli/telemetry.py +6 -6
- {mt5cli-1.6.0 → mt5cli-1.7.1}/mt5cli/trading.py +123 -64
- {mt5cli-1.6.0 → mt5cli-1.7.1}/mt5cli/utils.py +2 -2
- mt5cli-1.7.1/pyproject.toml +142 -0
- mt5cli-1.7.1/tests/architecture/__init__.py +1 -0
- mt5cli-1.7.1/tests/architecture/test_unit_module_correspondence.py +37 -0
- {mt5cli-1.6.0 → mt5cli-1.7.1}/tests/cli/test_cli.bats +2 -19
- mt5cli-1.7.1/tests/conftest.py +47 -0
- mt5cli-1.7.1/tests/contracts/__init__.py +1 -0
- mt5cli-1.7.1/tests/contracts/test_public_api.py +163 -0
- mt5cli-1.7.1/tests/docs/__init__.py +1 -0
- mt5cli-1.7.1/tests/docs/test_public_contract.py +21 -0
- mt5cli-1.7.1/tests/integration/__init__.py +1 -0
- mt5cli-1.6.0/tests/unit/test_packaging.py → mt5cli-1.7.1/tests/packaging/test_metadata.py +1 -0
- {mt5cli-1.6.0 → mt5cli-1.7.1}/tests/unit/conftest.py +2 -38
- mt5cli-1.7.1/tests/unit/test_analytics.py +1016 -0
- {mt5cli-1.6.0 → mt5cli-1.7.1}/tests/unit/test_cli.py +102 -98
- {mt5cli-1.6.0 → mt5cli-1.7.1}/tests/unit/test_client.py +60 -1
- mt5cli-1.7.1/tests/unit/test_contract.py +13 -0
- mt5cli-1.7.1/tests/unit/test_converters.py +153 -0
- mt5cli-1.7.1/tests/unit/test_exceptions.py +56 -0
- {mt5cli-1.6.0 → mt5cli-1.7.1}/tests/unit/test_history.py +693 -5
- {mt5cli-1.6.0 → mt5cli-1.7.1}/tests/unit/test_marketdata.py +1 -1
- {mt5cli-1.6.0 → mt5cli-1.7.1}/tests/unit/test_observability.py +260 -28
- mt5cli-1.7.1/tests/unit/test_rates.py +885 -0
- mt5cli-1.7.1/tests/unit/test_retry.py +42 -0
- mt5cli-1.7.1/tests/unit/test_schemas.py +327 -0
- mt5cli-1.7.1/tests/unit/test_sdk.py +23 -0
- {mt5cli-1.6.0 → mt5cli-1.7.1}/tests/unit/test_trading.py +1 -1
- mt5cli-1.7.1/uv.lock +1570 -0
- mt5cli-1.6.0/docs/api/grafana.md +0 -25
- mt5cli-1.6.0/examples/grafana/README.md +0 -95
- mt5cli-1.6.0/examples/grafana/compose.yml +0 -26
- mt5cli-1.6.0/examples/grafana/dashboards/mt5cli-market.json +0 -98
- mt5cli-1.6.0/examples/grafana/dashboards/mt5cli-overview.json +0 -254
- mt5cli-1.6.0/examples/grafana/dashboards/mt5cli-trades.json +0 -167
- mt5cli-1.6.0/examples/grafana/provisioning/dashboards/mt5cli.yml +0 -13
- mt5cli-1.6.0/examples/grafana/provisioning/datasources/mt5cli-sqlite.yml +0 -16
- mt5cli-1.6.0/mt5cli/grafana.py +0 -633
- mt5cli-1.6.0/pyproject.toml +0 -202
- mt5cli-1.6.0/tests/unit/test_contracts.py +0 -768
- mt5cli-1.6.0/tests/unit/test_examples.py +0 -88
- mt5cli-1.6.0/tests/unit/test_grafana.py +0 -930
- mt5cli-1.6.0/tests/unit/test_history_contract.py +0 -508
- mt5cli-1.6.0/tests/unit/test_history_edge_contract.py +0 -122
- mt5cli-1.6.0/tests/unit/test_rate_timestamp_contract.py +0 -291
- mt5cli-1.6.0/tests/unit/test_rates.py +0 -408
- mt5cli-1.6.0/tests/unit/test_rates_contract.py +0 -220
- mt5cli-1.6.0/tests/unit/test_stable_sdk_cleanup.py +0 -52
- mt5cli-1.6.0/uv.lock +0 -1504
- {mt5cli-1.6.0 → mt5cli-1.7.1}/.agents/skills/local-qa/SKILL.md +0 -0
- {mt5cli-1.6.0 → mt5cli-1.7.1}/.agents/skills/mt5cli/SKILL.md +0 -0
- {mt5cli-1.6.0 → mt5cli-1.7.1}/.claude/agents/codex.md +0 -0
- {mt5cli-1.6.0 → mt5cli-1.7.1}/.claude/settings.json +0 -0
- {mt5cli-1.6.0 → mt5cli-1.7.1}/.github/CODEOWNERS +0 -0
- {mt5cli-1.6.0 → mt5cli-1.7.1}/.github/FUNDING.yml +0 -0
- {mt5cli-1.6.0 → mt5cli-1.7.1}/.gitignore +0 -0
- {mt5cli-1.6.0 → mt5cli-1.7.1}/LICENSE +0 -0
- {mt5cli-1.6.0 → mt5cli-1.7.1}/docs/api/cli.md +0 -0
- {mt5cli-1.6.0 → mt5cli-1.7.1}/docs/api/client.md +0 -0
- {mt5cli-1.6.0 → mt5cli-1.7.1}/docs/api/converters.md +0 -0
- {mt5cli-1.6.0 → mt5cli-1.7.1}/docs/api/exceptions.md +0 -0
- {mt5cli-1.6.0 → mt5cli-1.7.1}/docs/api/marketdata.md +0 -0
- {mt5cli-1.6.0 → mt5cli-1.7.1}/docs/api/schemas.md +0 -0
- {mt5cli-1.6.0 → mt5cli-1.7.1}/docs/api/telemetry.md +0 -0
- {mt5cli-1.6.0 → mt5cli-1.7.1}/docs/api/trading.md +0 -0
- {mt5cli-1.6.0 → mt5cli-1.7.1}/docs/api/utils.md +0 -0
- {mt5cli-1.6.0 → mt5cli-1.7.1}/docs/index.md +0 -0
- {mt5cli-1.6.0 → mt5cli-1.7.1}/mt5cli/__main__.py +0 -0
- {mt5cli-1.6.0 → mt5cli-1.7.1}/mt5cli/contract.py +0 -0
- {mt5cli-1.6.0 → mt5cli-1.7.1}/mt5cli/converters.py +0 -0
- {mt5cli-1.6.0 → mt5cli-1.7.1}/mt5cli/exceptions.py +0 -0
- {mt5cli-1.6.0 → mt5cli-1.7.1}/mt5cli/marketdata.py +0 -0
- {mt5cli-1.6.0 → mt5cli-1.7.1}/mt5cli/retry.py +0 -0
- /mt5cli-1.6.0/tests/unit/test_docs_contract.py → /mt5cli-1.7.1/tests/docs/test_rate_storage_contract.py +0 -0
- {mt5cli-1.6.0/tests/unit → mt5cli-1.7.1/tests/integration}/test_history_capture_persist.py +0 -0
- {mt5cli-1.6.0 → mt5cli-1.7.1}/tests/unit/__init__.py +0 -0
- {mt5cli-1.6.0 → mt5cli-1.7.1}/tests/unit/test_lifecycle.py +0 -0
- {mt5cli-1.6.0 → mt5cli-1.7.1}/tests/unit/test_telemetry.py +0 -0
- {mt5cli-1.6.0 → mt5cli-1.7.1}/tests/unit/test_utils.py +0 -0
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
set -euox pipefail
|
|
4
4
|
cd "$(git rev-parse --show-toplevel)"
|
|
5
5
|
|
|
6
|
-
COOLDOWN_DAYS=
|
|
6
|
+
COOLDOWN_DAYS=1
|
|
7
7
|
export UV_EXCLUDE_NEWER="${COOLDOWN_DAYS} days"
|
|
8
8
|
export NPM_CONFIG_MIN_RELEASE_AGE="${COOLDOWN_DAYS}"
|
|
9
9
|
export PNPM_CONFIG_MINIMUM_RELEASE_AGE=$((COOLDOWN_DAYS * 24 * 60))
|
|
@@ -16,7 +16,7 @@ uv run pyright .
|
|
|
16
16
|
uv run pytest
|
|
17
17
|
|
|
18
18
|
# Markdown and JSON
|
|
19
|
-
|
|
19
|
+
pnpm dlx oxfmt --write './**/*.{md,json}'
|
|
20
20
|
|
|
21
21
|
# YAML
|
|
22
22
|
git ls-files -z -- '*.yml' \
|
|
@@ -6,12 +6,20 @@ updates:
|
|
|
6
6
|
schedule:
|
|
7
7
|
interval: daily
|
|
8
8
|
cooldown:
|
|
9
|
-
default-days:
|
|
9
|
+
default-days: 1
|
|
10
10
|
open-pull-requests-limit: 10
|
|
11
|
-
|
|
11
|
+
groups:
|
|
12
|
+
github-actions-dependencies:
|
|
13
|
+
patterns:
|
|
14
|
+
- '*'
|
|
15
|
+
- package-ecosystem: uv
|
|
12
16
|
directory: /
|
|
13
17
|
schedule:
|
|
14
18
|
interval: daily
|
|
15
19
|
cooldown:
|
|
16
|
-
default-days:
|
|
20
|
+
default-days: 1
|
|
17
21
|
open-pull-requests-limit: 10
|
|
22
|
+
groups:
|
|
23
|
+
uv-dependencies:
|
|
24
|
+
patterns:
|
|
25
|
+
- '*'
|
|
@@ -73,19 +73,6 @@ jobs:
|
|
|
73
73
|
runs-on: ubuntu-slim
|
|
74
74
|
secrets:
|
|
75
75
|
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
76
|
-
github-codeql-analysis:
|
|
77
|
-
if: >
|
|
78
|
-
github.event_name == 'push'
|
|
79
|
-
|| github.event_name == 'pull_request'
|
|
80
|
-
|| (github.event_name == 'workflow_dispatch' && inputs.workflow == 'lint-and-test')
|
|
81
|
-
permissions:
|
|
82
|
-
contents: read
|
|
83
|
-
security-events: write
|
|
84
|
-
actions: read
|
|
85
|
-
uses: dceoy/gha-for-devops/.github/workflows/github-codeql-analysis.yml@main # zizmor: ignore[unpinned-uses]
|
|
86
|
-
with:
|
|
87
|
-
language: >
|
|
88
|
-
["python"]
|
|
89
76
|
dependabot-auto-merge:
|
|
90
77
|
if: >
|
|
91
78
|
github.event_name == 'pull_request' && github.actor == 'dependabot[bot]'
|
|
@@ -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 }}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: CodeQL
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches:
|
|
6
|
+
- main
|
|
7
|
+
paths:
|
|
8
|
+
- '.github/workflows/**'
|
|
9
|
+
- '.github/actions/**'
|
|
10
|
+
- '**/action.yml'
|
|
11
|
+
- '**/action.yaml'
|
|
12
|
+
- '**/*.py'
|
|
13
|
+
- '**/*.pyi'
|
|
14
|
+
- '**/pyproject.toml'
|
|
15
|
+
- '**/uv.lock'
|
|
16
|
+
- '**/requirements*.txt'
|
|
17
|
+
pull_request:
|
|
18
|
+
branches:
|
|
19
|
+
- main
|
|
20
|
+
paths:
|
|
21
|
+
- '.github/workflows/**'
|
|
22
|
+
- '.github/actions/**'
|
|
23
|
+
- '**/action.yml'
|
|
24
|
+
- '**/action.yaml'
|
|
25
|
+
- '**/*.py'
|
|
26
|
+
- '**/*.pyi'
|
|
27
|
+
- '**/pyproject.toml'
|
|
28
|
+
- '**/uv.lock'
|
|
29
|
+
- '**/requirements*.txt'
|
|
30
|
+
workflow_dispatch:
|
|
31
|
+
permissions:
|
|
32
|
+
contents: read
|
|
33
|
+
jobs:
|
|
34
|
+
github-codeql-analysis:
|
|
35
|
+
permissions:
|
|
36
|
+
contents: read
|
|
37
|
+
security-events: write
|
|
38
|
+
actions: read
|
|
39
|
+
uses: dceoy/gha-for-devops/.github/workflows/github-codeql-analysis.yml@main # zizmor: ignore[unpinned-uses]
|
|
40
|
+
with:
|
|
41
|
+
language: >
|
|
42
|
+
["actions","python"]
|
|
@@ -34,11 +34,11 @@ jobs:
|
|
|
34
34
|
id-token: write # IMPORTANT: mandatory for trusted publishing
|
|
35
35
|
steps:
|
|
36
36
|
- name: Download all the dists
|
|
37
|
-
uses: actions/download-artifact@
|
|
37
|
+
uses: actions/download-artifact@9000827ccba6bdab643e8b6fd33ac0654aef8333 # v8.0.2
|
|
38
38
|
with:
|
|
39
39
|
name: ${{ needs.build-and-release.outputs.distribution-artifact-name }}
|
|
40
40
|
path: dist/
|
|
41
41
|
- name: Publish distribution 📦 to PyPI
|
|
42
|
-
uses: pypa/gh-action-pypi-publish@
|
|
42
|
+
uses: pypa/gh-action-pypi-publish@7245906fb61fa33a13554f9d5fb856fe61758086 # v1.14.3
|
|
43
43
|
with:
|
|
44
44
|
verbose: true
|
|
@@ -1,3 +1,32 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: mt5cli
|
|
3
|
+
Version: 1.7.1
|
|
4
|
+
Summary: Generic MT5 data and execution infrastructure for Python applications
|
|
5
|
+
Project-URL: Repository, https://github.com/dceoy/mt5cli.git
|
|
6
|
+
Author-email: dceoy <dceoy@users.noreply.github.com>
|
|
7
|
+
Maintainer-email: dceoy <dceoy@users.noreply.github.com>
|
|
8
|
+
License-Expression: MIT
|
|
9
|
+
License-File: LICENSE
|
|
10
|
+
Classifier: Development Status :: 3 - Alpha
|
|
11
|
+
Classifier: Environment :: Console
|
|
12
|
+
Classifier: Intended Audience :: Financial and Insurance Industry
|
|
13
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
14
|
+
Classifier: Operating System :: Microsoft :: Windows
|
|
15
|
+
Classifier: Programming Language :: Python
|
|
16
|
+
Classifier: Programming Language :: Python :: 3
|
|
17
|
+
Classifier: Topic :: Office/Business :: Financial :: Investment
|
|
18
|
+
Requires-Python: <3.15,>=3.11
|
|
19
|
+
Requires-Dist: pandas>=2.2.2
|
|
20
|
+
Requires-Dist: pdmt5>=1.2.0
|
|
21
|
+
Requires-Dist: typer>=0.26.0
|
|
22
|
+
Provides-Extra: otel
|
|
23
|
+
Requires-Dist: opentelemetry-api>=1.23.0; extra == 'otel'
|
|
24
|
+
Requires-Dist: opentelemetry-exporter-otlp-proto-http>=1.23.0; extra == 'otel'
|
|
25
|
+
Requires-Dist: opentelemetry-sdk>=1.23.0; extra == 'otel'
|
|
26
|
+
Provides-Extra: parquet
|
|
27
|
+
Requires-Dist: pyarrow>=19.0.0; extra == 'parquet'
|
|
28
|
+
Description-Content-Type: text/markdown
|
|
29
|
+
|
|
1
30
|
# mt5cli
|
|
2
31
|
|
|
3
32
|
[](https://github.com/dceoy/mt5cli/actions/workflows/ci.yml)
|
|
@@ -183,8 +212,8 @@ python -m mt5cli -o account.csv account-info
|
|
|
183
212
|
| `close-positions` | Close open positions by `--symbol` or `--ticket` (`--yes` required for live; `--dry-run` available; optional `--filling-mode` / `--deviation` / `--comment` / `--magic`) |
|
|
184
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`) |
|
|
185
214
|
| `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
|
|
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 |
|
|
188
217
|
|
|
189
218
|
Use `order-check` to validate a request payload before running `order-send --yes`.
|
|
190
219
|
`close-positions` is the safer high-level alternative that builds correct close
|
|
@@ -217,25 +246,20 @@ mt5cli -o history.db collect-history \
|
|
|
217
246
|
|
|
218
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.
|
|
219
248
|
|
|
220
|
-
###
|
|
221
|
-
|
|
222
|
-
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.
|
|
249
|
+
### Static dashboard publication
|
|
223
250
|
|
|
224
|
-
|
|
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`.
|
|
225
252
|
|
|
226
|
-
|
|
227
|
-
mt5cli -o history.db grafana-schema
|
|
228
|
-
```
|
|
253
|
+
### Account snapshots (SQLite)
|
|
229
254
|
|
|
230
|
-
|
|
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.
|
|
231
256
|
|
|
232
257
|
#### Snapshot current account state
|
|
233
258
|
|
|
234
259
|
```bash
|
|
235
260
|
mt5cli -o history.db snapshot \
|
|
236
261
|
--symbol JP225 --symbol HK50 --symbol NL25 \
|
|
237
|
-
--with-account --with-positions --with-orders --with-terminal
|
|
238
|
-
--with-grafana-schema
|
|
262
|
+
--with-account --with-positions --with-orders --with-terminal
|
|
239
263
|
```
|
|
240
264
|
|
|
241
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.
|
|
@@ -255,56 +279,10 @@ update_observability_with_config(
|
|
|
255
279
|
include_positions=True,
|
|
256
280
|
include_orders=True,
|
|
257
281
|
include_terminal=True,
|
|
258
|
-
with_grafana_schema=True,
|
|
259
282
|
)
|
|
260
283
|
```
|
|
261
284
|
|
|
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.
|
|
285
|
+
#### Telemetry API docs
|
|
308
286
|
|
|
309
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`.
|
|
310
288
|
|
|
@@ -1,32 +1,3 @@
|
|
|
1
|
-
Metadata-Version: 2.5
|
|
2
|
-
Name: mt5cli
|
|
3
|
-
Version: 1.6.0
|
|
4
|
-
Summary: Generic MT5 data and execution infrastructure for Python applications
|
|
5
|
-
Project-URL: Repository, https://github.com/dceoy/mt5cli.git
|
|
6
|
-
Author-email: dceoy <dceoy@users.noreply.github.com>
|
|
7
|
-
Maintainer-email: dceoy <dceoy@users.noreply.github.com>
|
|
8
|
-
License-Expression: MIT
|
|
9
|
-
License-File: LICENSE
|
|
10
|
-
Classifier: Development Status :: 3 - Alpha
|
|
11
|
-
Classifier: Environment :: Console
|
|
12
|
-
Classifier: Intended Audience :: Financial and Insurance Industry
|
|
13
|
-
Classifier: License :: OSI Approved :: MIT License
|
|
14
|
-
Classifier: Operating System :: Microsoft :: Windows
|
|
15
|
-
Classifier: Programming Language :: Python
|
|
16
|
-
Classifier: Programming Language :: Python :: 3
|
|
17
|
-
Classifier: Topic :: Office/Business :: Financial :: Investment
|
|
18
|
-
Requires-Python: <3.15,>=3.11
|
|
19
|
-
Requires-Dist: pandas>=2.2.2
|
|
20
|
-
Requires-Dist: pdmt5>=1.2.0
|
|
21
|
-
Requires-Dist: typer>=0.26.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'
|
|
26
|
-
Provides-Extra: parquet
|
|
27
|
-
Requires-Dist: pyarrow>=19.0.0; extra == 'parquet'
|
|
28
|
-
Description-Content-Type: text/markdown
|
|
29
|
-
|
|
30
1
|
# mt5cli
|
|
31
2
|
|
|
32
3
|
[](https://github.com/dceoy/mt5cli/actions/workflows/ci.yml)
|
|
@@ -212,8 +183,8 @@ python -m mt5cli -o account.csv account-info
|
|
|
212
183
|
| `close-positions` | Close open positions by `--symbol` or `--ticket` (`--yes` required for live; `--dry-run` available; optional `--filling-mode` / `--deviation` / `--comment` / `--magic`) |
|
|
213
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`) |
|
|
214
185
|
| `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
|
|
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 |
|
|
217
188
|
|
|
218
189
|
Use `order-check` to validate a request payload before running `order-send --yes`.
|
|
219
190
|
`close-positions` is the safer high-level alternative that builds correct close
|
|
@@ -246,25 +217,20 @@ mt5cli -o history.db collect-history \
|
|
|
246
217
|
|
|
247
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.
|
|
248
219
|
|
|
249
|
-
###
|
|
250
|
-
|
|
251
|
-
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.
|
|
220
|
+
### Static dashboard publication
|
|
252
221
|
|
|
253
|
-
|
|
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`.
|
|
254
223
|
|
|
255
|
-
|
|
256
|
-
mt5cli -o history.db grafana-schema
|
|
257
|
-
```
|
|
224
|
+
### Account snapshots (SQLite)
|
|
258
225
|
|
|
259
|
-
|
|
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.
|
|
260
227
|
|
|
261
228
|
#### Snapshot current account state
|
|
262
229
|
|
|
263
230
|
```bash
|
|
264
231
|
mt5cli -o history.db snapshot \
|
|
265
232
|
--symbol JP225 --symbol HK50 --symbol NL25 \
|
|
266
|
-
--with-account --with-positions --with-orders --with-terminal
|
|
267
|
-
--with-grafana-schema
|
|
233
|
+
--with-account --with-positions --with-orders --with-terminal
|
|
268
234
|
```
|
|
269
235
|
|
|
270
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.
|
|
@@ -284,56 +250,10 @@ update_observability_with_config(
|
|
|
284
250
|
include_positions=True,
|
|
285
251
|
include_orders=True,
|
|
286
252
|
include_terminal=True,
|
|
287
|
-
with_grafana_schema=True,
|
|
288
253
|
)
|
|
289
254
|
```
|
|
290
255
|
|
|
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.
|
|
256
|
+
#### Telemetry API docs
|
|
337
257
|
|
|
338
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`.
|
|
339
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.
|