mt5cli 1.3.3__tar.gz → 1.4.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.4.1/.agents/skills/local-qa/scripts/qa.sh +41 -0
- {mt5cli-1.3.3 → mt5cli-1.4.1}/.agents/skills/mt5cli/SKILL.md +2 -2
- mt5cli-1.4.1/.github/CODEOWNERS +2 -0
- {mt5cli-1.3.3 → mt5cli-1.4.1}/.github/workflows/ci.yml +11 -14
- {mt5cli-1.3.3 → mt5cli-1.4.1}/.github/workflows/claude.yml +2 -2
- {mt5cli-1.3.3 → mt5cli-1.4.1}/.github/workflows/release.yml +2 -2
- mt5cli-1.4.1/AGENTS.md +29 -0
- {mt5cli-1.3.3 → mt5cli-1.4.1}/PKG-INFO +2 -3
- {mt5cli-1.3.3 → mt5cli-1.4.1}/README.md +0 -1
- {mt5cli-1.3.3 → mt5cli-1.4.1}/docs/api/public-contract.md +23 -15
- {mt5cli-1.3.3 → mt5cli-1.4.1}/docs/api/trading.md +54 -8
- {mt5cli-1.3.3 → mt5cli-1.4.1}/docs/index.md +0 -1
- {mt5cli-1.3.3 → mt5cli-1.4.1}/mt5cli/__init__.py +4 -2
- {mt5cli-1.3.3 → mt5cli-1.4.1}/mt5cli/contract.py +5 -1
- {mt5cli-1.3.3 → mt5cli-1.4.1}/mt5cli/trading.py +315 -81
- {mt5cli-1.3.3 → mt5cli-1.4.1}/pyproject.toml +3 -3
- mt5cli-1.4.1/tests/cli/test_cli.bats +122 -0
- {mt5cli-1.3.3/tests → mt5cli-1.4.1/tests/unit}/test_contracts.py +2 -2
- {mt5cli-1.3.3/tests → mt5cli-1.4.1/tests/unit}/test_examples.py +1 -1
- mt5cli-1.4.1/tests/unit/test_stable_sdk_cleanup.py +31 -0
- {mt5cli-1.3.3/tests → mt5cli-1.4.1/tests/unit}/test_trading.py +913 -86
- {mt5cli-1.3.3 → mt5cli-1.4.1}/uv.lock +220 -14
- mt5cli-1.3.3/.agents/skills/local-qa/scripts/qa.sh +0 -26
- mt5cli-1.3.3/.agents/skills/pr-feedback-triage/SKILL.md +0 -211
- mt5cli-1.3.3/AGENTS.md +0 -37
- {mt5cli-1.3.3 → mt5cli-1.4.1}/.agents/skills/local-qa/SKILL.md +0 -0
- {mt5cli-1.3.3 → mt5cli-1.4.1}/.claude/agents/codex.md +0 -0
- {mt5cli-1.3.3 → mt5cli-1.4.1}/.claude/settings.json +0 -0
- {mt5cli-1.3.3 → mt5cli-1.4.1}/.github/FUNDING.yml +0 -0
- {mt5cli-1.3.3 → mt5cli-1.4.1}/.github/dependabot.yml +0 -0
- {mt5cli-1.3.3 → mt5cli-1.4.1}/.github/renovate.json +0 -0
- {mt5cli-1.3.3 → mt5cli-1.4.1}/.gitignore +0 -0
- {mt5cli-1.3.3 → mt5cli-1.4.1}/CLAUDE.md +0 -0
- {mt5cli-1.3.3 → mt5cli-1.4.1}/LICENSE +0 -0
- {mt5cli-1.3.3 → mt5cli-1.4.1}/docs/api/cli.md +0 -0
- {mt5cli-1.3.3 → mt5cli-1.4.1}/docs/api/client.md +0 -0
- {mt5cli-1.3.3 → mt5cli-1.4.1}/docs/api/converters.md +0 -0
- {mt5cli-1.3.3 → mt5cli-1.4.1}/docs/api/exceptions.md +0 -0
- {mt5cli-1.3.3 → mt5cli-1.4.1}/docs/api/grafana.md +0 -0
- {mt5cli-1.3.3 → mt5cli-1.4.1}/docs/api/history.md +0 -0
- {mt5cli-1.3.3 → mt5cli-1.4.1}/docs/api/index.md +0 -0
- {mt5cli-1.3.3 → mt5cli-1.4.1}/docs/api/marketdata.md +0 -0
- {mt5cli-1.3.3 → mt5cli-1.4.1}/docs/api/observability.md +0 -0
- {mt5cli-1.3.3 → mt5cli-1.4.1}/docs/api/schemas.md +0 -0
- {mt5cli-1.3.3 → mt5cli-1.4.1}/docs/api/telemetry.md +0 -0
- {mt5cli-1.3.3 → mt5cli-1.4.1}/docs/api/utils.md +0 -0
- {mt5cli-1.3.3 → mt5cli-1.4.1}/examples/grafana/README.md +0 -0
- {mt5cli-1.3.3 → mt5cli-1.4.1}/examples/grafana/compose.yml +0 -0
- {mt5cli-1.3.3 → mt5cli-1.4.1}/examples/grafana/dashboards/mt5cli-market.json +0 -0
- {mt5cli-1.3.3 → mt5cli-1.4.1}/examples/grafana/dashboards/mt5cli-overview.json +0 -0
- {mt5cli-1.3.3 → mt5cli-1.4.1}/examples/grafana/dashboards/mt5cli-trades.json +0 -0
- {mt5cli-1.3.3 → mt5cli-1.4.1}/examples/grafana/provisioning/dashboards/mt5cli.yml +0 -0
- {mt5cli-1.3.3 → mt5cli-1.4.1}/examples/grafana/provisioning/datasources/mt5cli-sqlite.yml +0 -0
- {mt5cli-1.3.3 → mt5cli-1.4.1}/mkdocs.yml +0 -0
- {mt5cli-1.3.3 → mt5cli-1.4.1}/mt5cli/__main__.py +0 -0
- {mt5cli-1.3.3 → mt5cli-1.4.1}/mt5cli/cli.py +0 -0
- {mt5cli-1.3.3 → mt5cli-1.4.1}/mt5cli/client.py +0 -0
- {mt5cli-1.3.3 → mt5cli-1.4.1}/mt5cli/converters.py +0 -0
- {mt5cli-1.3.3 → mt5cli-1.4.1}/mt5cli/exceptions.py +0 -0
- {mt5cli-1.3.3 → mt5cli-1.4.1}/mt5cli/grafana.py +0 -0
- {mt5cli-1.3.3 → mt5cli-1.4.1}/mt5cli/history.py +0 -0
- {mt5cli-1.3.3 → mt5cli-1.4.1}/mt5cli/marketdata.py +0 -0
- {mt5cli-1.3.3 → mt5cli-1.4.1}/mt5cli/observability.py +0 -0
- {mt5cli-1.3.3 → mt5cli-1.4.1}/mt5cli/retry.py +0 -0
- {mt5cli-1.3.3 → mt5cli-1.4.1}/mt5cli/schemas.py +0 -0
- {mt5cli-1.3.3 → mt5cli-1.4.1}/mt5cli/telemetry.py +0 -0
- {mt5cli-1.3.3 → mt5cli-1.4.1}/mt5cli/utils.py +0 -0
- {mt5cli-1.3.3/tests → mt5cli-1.4.1/tests/unit}/__init__.py +0 -0
- {mt5cli-1.3.3/tests → mt5cli-1.4.1/tests/unit}/conftest.py +0 -0
- {mt5cli-1.3.3/tests → mt5cli-1.4.1/tests/unit}/test_cli.py +0 -0
- {mt5cli-1.3.3/tests → mt5cli-1.4.1/tests/unit}/test_client.py +0 -0
- {mt5cli-1.3.3/tests → mt5cli-1.4.1/tests/unit}/test_grafana.py +0 -0
- {mt5cli-1.3.3/tests → mt5cli-1.4.1/tests/unit}/test_history.py +0 -0
- {mt5cli-1.3.3/tests → mt5cli-1.4.1/tests/unit}/test_marketdata.py +0 -0
- {mt5cli-1.3.3/tests → mt5cli-1.4.1/tests/unit}/test_observability.py +0 -0
- {mt5cli-1.3.3/tests → mt5cli-1.4.1/tests/unit}/test_packaging.py +0 -0
- {mt5cli-1.3.3/tests → mt5cli-1.4.1/tests/unit}/test_telemetry.py +0 -0
- {mt5cli-1.3.3/tests → mt5cli-1.4.1/tests/unit}/test_utils.py +0 -0
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
|
|
3
|
+
set -euox pipefail
|
|
4
|
+
cd "$(git rev-parse --show-toplevel)"
|
|
5
|
+
|
|
6
|
+
COOLDOWN_DAYS=7
|
|
7
|
+
export UV_EXCLUDE_NEWER="${COOLDOWN_DAYS} days"
|
|
8
|
+
export NPM_CONFIG_MIN_RELEASE_AGE="${COOLDOWN_DAYS}"
|
|
9
|
+
export PNPM_CONFIG_MINIMUM_RELEASE_AGE=$((COOLDOWN_DAYS * 24 * 60))
|
|
10
|
+
|
|
11
|
+
# Python
|
|
12
|
+
uv run ruff format .
|
|
13
|
+
uv run ruff check --fix .
|
|
14
|
+
uv run pyright .
|
|
15
|
+
uv run pytest
|
|
16
|
+
|
|
17
|
+
# Markdown and JSON
|
|
18
|
+
npx -y prettier --write './**/*.{md,json}'
|
|
19
|
+
|
|
20
|
+
# YAML
|
|
21
|
+
git ls-files -z -- '*.yml' \
|
|
22
|
+
| xargs -0 -t uvx yamllint -d '{"extends": "relaxed", "rules": {"line-length": "disable"}}'
|
|
23
|
+
|
|
24
|
+
# GitHub Actions
|
|
25
|
+
case "${OSTYPE}" in
|
|
26
|
+
darwin* | linux*)
|
|
27
|
+
# Shell scripts
|
|
28
|
+
git ls-files -z -- '*.sh' '*.bash' '*.bats' \
|
|
29
|
+
| xargs -0 -t shfmt --write --indent=2 --binary-next-line --case-indent --space-redirects
|
|
30
|
+
git ls-files -z -- '*.sh' '*.bash' '*.bats' \
|
|
31
|
+
| xargs -0 -t shellcheck
|
|
32
|
+
|
|
33
|
+
# GitHub Actions
|
|
34
|
+
zizmor --fix=safe .github/workflows
|
|
35
|
+
git ls-files -z -- '.github/workflows/*.yml' | xargs -0 -t actionlint
|
|
36
|
+
checkov --framework=all --output=github_failed_only --directory=.
|
|
37
|
+
;;
|
|
38
|
+
*)
|
|
39
|
+
echo 'GitHub Actions and shell script linting is only supported on Linux and macOS.'
|
|
40
|
+
;;
|
|
41
|
+
esac
|
|
@@ -12,8 +12,8 @@ command. Output format is auto-detected from the file extension (`.csv`,
|
|
|
12
12
|
|
|
13
13
|
## Requirements
|
|
14
14
|
|
|
15
|
-
|
|
16
|
-
|
|
15
|
+
MT5 terminal).
|
|
16
|
+
|
|
17
17
|
- Install: `pip install -U mt5cli MetaTrader5`.
|
|
18
18
|
- In this repo, run via `uv run mt5cli ...` or `uv run python -m mt5cli ...`.
|
|
19
19
|
|
|
@@ -35,7 +35,7 @@ jobs:
|
|
|
35
35
|
|| (github.event_name == 'workflow_dispatch' && inputs.workflow == 'lint-and-test')
|
|
36
36
|
permissions:
|
|
37
37
|
contents: read
|
|
38
|
-
uses: dceoy/
|
|
38
|
+
uses: dceoy/gha-for-devops/.github/workflows/python-package-lint-and-scan.yml@main # zizmor: ignore[unpinned-uses]
|
|
39
39
|
with:
|
|
40
40
|
package-path: .
|
|
41
41
|
runs-on: windows-latest
|
|
@@ -46,32 +46,28 @@ jobs:
|
|
|
46
46
|
|| (github.event_name == 'workflow_dispatch' && inputs.workflow == 'lint-and-test')
|
|
47
47
|
permissions:
|
|
48
48
|
contents: read
|
|
49
|
-
uses: dceoy/
|
|
49
|
+
uses: dceoy/gha-for-devops/.github/workflows/python-package-test.yml@main # zizmor: ignore[unpinned-uses]
|
|
50
50
|
with:
|
|
51
51
|
package-path: .
|
|
52
52
|
runs-on: windows-latest
|
|
53
|
-
|
|
53
|
+
bats-test:
|
|
54
54
|
if: >
|
|
55
55
|
github.event_name == 'push'
|
|
56
56
|
|| github.event_name == 'pull_request'
|
|
57
57
|
|| (github.event_name == 'workflow_dispatch' && inputs.workflow == 'lint-and-test')
|
|
58
58
|
permissions:
|
|
59
59
|
contents: read
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
-
|
|
63
|
-
|
|
64
|
-
persist-credentials: false
|
|
65
|
-
- uses: astral-sh/setup-uv@11f9893b081a58869d3b5fccaea48c9e9e46f990 # v8.3.2
|
|
66
|
-
- name: Check CLI help with minimum Click and Typer versions
|
|
67
|
-
run: uv run --with click==8.4.0 --with typer==0.26.0 mt5cli --help
|
|
60
|
+
uses: dceoy/gha-for-devops/.github/workflows/bats-test.yml@main # zizmor: ignore[unpinned-uses]
|
|
61
|
+
with:
|
|
62
|
+
search-path: ./tests/cli/
|
|
63
|
+
runs-on: windows-latest
|
|
68
64
|
python-docs-deploy:
|
|
69
65
|
if: >
|
|
70
66
|
github.event_name == 'push'
|
|
71
67
|
|| (github.event_name == 'workflow_dispatch' && inputs.workflow == 'docs-deploy')
|
|
72
68
|
permissions:
|
|
73
69
|
contents: write
|
|
74
|
-
uses: dceoy/
|
|
70
|
+
uses: dceoy/gha-for-devops/.github/workflows/python-package-mkdocs-gh-deploy.yml@main # zizmor: ignore[unpinned-uses]
|
|
75
71
|
with:
|
|
76
72
|
package-path: .
|
|
77
73
|
runs-on: ubuntu-slim
|
|
@@ -86,7 +82,7 @@ jobs:
|
|
|
86
82
|
contents: read
|
|
87
83
|
security-events: write
|
|
88
84
|
actions: read
|
|
89
|
-
uses: dceoy/
|
|
85
|
+
uses: dceoy/gha-for-devops/.github/workflows/github-codeql-analysis.yml@main # zizmor: ignore[unpinned-uses]
|
|
90
86
|
with:
|
|
91
87
|
language: >
|
|
92
88
|
["python"]
|
|
@@ -96,7 +92,8 @@ jobs:
|
|
|
96
92
|
needs:
|
|
97
93
|
- python-lint-and-scan
|
|
98
94
|
- python-test
|
|
99
|
-
|
|
95
|
+
- bats-test
|
|
96
|
+
uses: dceoy/gha-for-devops/.github/workflows/dependabot-auto-merge.yml@main # zizmor: ignore[unpinned-uses]
|
|
100
97
|
permissions:
|
|
101
98
|
contents: write
|
|
102
99
|
pull-requests: write
|
|
@@ -35,7 +35,7 @@ jobs:
|
|
|
35
35
|
issues: write
|
|
36
36
|
id-token: write
|
|
37
37
|
actions: read
|
|
38
|
-
uses: dceoy/
|
|
38
|
+
uses: dceoy/gha-for-devops/.github/workflows/claude-code-review.yml@main # zizmor: ignore[unpinned-uses]
|
|
39
39
|
secrets:
|
|
40
40
|
CLAUDE_CODE_OAUTH_TOKEN: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }} # zizmor: ignore[secrets-outside-env]
|
|
41
41
|
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
@@ -51,7 +51,7 @@ jobs:
|
|
|
51
51
|
issues: write
|
|
52
52
|
id-token: write
|
|
53
53
|
actions: read
|
|
54
|
-
uses: dceoy/
|
|
54
|
+
uses: dceoy/gha-for-devops/.github/workflows/claude-code-bot.yml@main # zizmor: ignore[unpinned-uses]
|
|
55
55
|
secrets:
|
|
56
56
|
CLAUDE_CODE_OAUTH_TOKEN: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }} # zizmor: ignore[secrets-outside-env]
|
|
57
57
|
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
@@ -13,7 +13,7 @@ jobs:
|
|
|
13
13
|
permissions:
|
|
14
14
|
contents: write
|
|
15
15
|
id-token: write
|
|
16
|
-
uses: dceoy/
|
|
16
|
+
uses: dceoy/gha-for-devops/.github/workflows/python-package-release-on-pypi-and-github.yml@main # zizmor: ignore[unpinned-uses]
|
|
17
17
|
with:
|
|
18
18
|
package-path: .
|
|
19
19
|
create-github-release: true
|
|
@@ -39,6 +39,6 @@ jobs:
|
|
|
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@dc37677b2e1c63e2034f94d8a5b11f265b73ba33 # v1.14.2
|
|
43
43
|
with:
|
|
44
44
|
verbose: true
|
mt5cli-1.4.1/AGENTS.md
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# Repository Guidelines
|
|
2
|
+
|
|
3
|
+
## Build, Test, and Development Commands
|
|
4
|
+
|
|
5
|
+
Use `.agents/skills/local-qa/SKILL.md` for pre-handoff QA. It runs `.agents/skills/local-qa/scripts/qa.sh`, which formats, lints, type-checks, tests, formats Markdown, and checks GitHub workflows.
|
|
6
|
+
|
|
7
|
+
`bats --verbose-run ./tests/cli/` runs the Windows/MT5-oriented CLI tests against the installed `mt5cli` entry point. These require a Windows environment where `uv sync` can install MetaTrader5; state clearly when they were not run.
|
|
8
|
+
|
|
9
|
+
`skills/mt5cli/SKILL.md` documents the mt5cli agent skill.
|
|
10
|
+
|
|
11
|
+
## Coding Style & Naming Conventions
|
|
12
|
+
|
|
13
|
+
Preserve the package’s small, typed helper style rather than adding broad abstractions.
|
|
14
|
+
|
|
15
|
+
## Design Principles
|
|
16
|
+
|
|
17
|
+
Apply KISS, DRY, and YAGNI when changing code. Prefer the simplest implementation that satisfies the current CLI/API contract. Remove duplication when shared behavior is already proven by at least two concrete call sites, but avoid generic helpers for speculative reuse. Do not add configuration flags, extension hooks, or alternate backends until a real repository use case requires them.
|
|
18
|
+
|
|
19
|
+
## Testing Guidelines
|
|
20
|
+
|
|
21
|
+
Parametrize unit tests with `pytest.mark.parametrize` when the same behavior needs coverage across multiple inputs. Mock MT5/pdmt5 boundaries; do not require a live MetaTrader terminal in unit tests.
|
|
22
|
+
|
|
23
|
+
## Commit & Pull Request Guidelines
|
|
24
|
+
|
|
25
|
+
Recent history uses concise imperative commits, sometimes with conventional prefixes such as `feat:` or `chore:` and PR numbers appended by GitHub. Keep commits scoped to one logical change. Pull requests should describe behavior changes, note tests run, link related issues, and call out MT5/live-trading risk where relevant.
|
|
26
|
+
|
|
27
|
+
## Security & Configuration Tips
|
|
28
|
+
|
|
29
|
+
Never commit account credentials, broker passwords, exported private data, or local `.venv` contents. Treat `order_send` and CLI `order-send --yes` as live execution paths; gate examples and tests so they cannot place real trades accidentally.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: mt5cli
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.4.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>
|
|
@@ -15,7 +15,7 @@ Classifier: Operating System :: Microsoft :: Windows
|
|
|
15
15
|
Classifier: Programming Language :: Python
|
|
16
16
|
Classifier: Programming Language :: Python :: 3
|
|
17
17
|
Classifier: Topic :: Office/Business :: Financial :: Investment
|
|
18
|
-
Requires-Python: <3.
|
|
18
|
+
Requires-Python: <3.15,>=3.11
|
|
19
19
|
Requires-Dist: pandas>=2.2.2
|
|
20
20
|
Requires-Dist: pdmt5>=1.2.0
|
|
21
21
|
Requires-Dist: typer>=0.26.0
|
|
@@ -385,7 +385,6 @@ eurusd_m1 = rates["EURUSD", "M1"] # closed bars only
|
|
|
385
385
|
|
|
386
386
|
## Requirements
|
|
387
387
|
|
|
388
|
-
- Python 3.11+
|
|
389
388
|
- Windows OS (MetaTrader 5 requirement)
|
|
390
389
|
- MetaTrader 5 platform installed
|
|
391
390
|
|
|
@@ -24,10 +24,11 @@ in pdmt5 — the name changed, it was not simply moved.
|
|
|
24
24
|
|
|
25
25
|
Downstream packages should import from the package root (`from mt5cli import
|
|
26
26
|
...`). The contract set `STABLE_SDK_EXPORTS` in `mt5cli.contract` enumerates
|
|
27
|
-
every package-root symbol.
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
27
|
+
every package-root symbol. Helpers promoted for downstream use, including selected configuration and
|
|
28
|
+
parsing utilities, history timeframe resolution, and Grafana schema setup, are
|
|
29
|
+
part of that root contract. Other low-level
|
|
30
|
+
schema, export, conversion, and implementation helpers remain available only
|
|
31
|
+
from their owning modules.
|
|
31
32
|
|
|
32
33
|
### Module responsibility map
|
|
33
34
|
|
|
@@ -56,13 +57,14 @@ These names are exported from `mt5cli` and enumerated in
|
|
|
56
57
|
|
|
57
58
|
### Session lifecycle and configuration
|
|
58
59
|
|
|
59
|
-
| Symbol | Role
|
|
60
|
-
| ----------------------------------------------- |
|
|
61
|
-
| `MT5Client` | The single connected client for data, history, calculations, order checking, and order sending
|
|
62
|
-
| `build_config` | Build
|
|
63
|
-
| `
|
|
64
|
-
| `
|
|
65
|
-
| `
|
|
60
|
+
| Symbol | Role |
|
|
61
|
+
| ----------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
62
|
+
| `MT5Client` | The single connected client for data, history, calculations, order checking, and order sending |
|
|
63
|
+
| `build_config` | Build the underlying MT5 connection config 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` |
|
|
64
|
+
| `substitute_mapping_values` | Generic selected-key environment substitution for downstream configuration adapters |
|
|
65
|
+
| `mt5_session` | Canonical context manager: initialize/login once, yield `MT5Client`, shut down once; a supplied `client=` remains caller-owned. |
|
|
66
|
+
| `AccountSpec` | Generic account group: symbols plus optional credentials |
|
|
67
|
+
| `resolve_account_spec`, `resolve_account_specs` | Merge overrides and expand `${ENV_VAR}` placeholders; opt-in `allow_whole_dollar_env` for bare `$NAME` |
|
|
66
68
|
|
|
67
69
|
### Closed-bar rate helpers
|
|
68
70
|
|
|
@@ -93,6 +95,11 @@ timestamp normalization in downstream apps.
|
|
|
93
95
|
See [History Collection (SQLite)](history.md) for schema, view naming, and ER
|
|
94
96
|
diagrams.
|
|
95
97
|
|
|
98
|
+
Shared package-root helpers used by downstream adapters include `Dataset`,
|
|
99
|
+
`parse_timeframe`, and `resolve_history_timeframes`. These are stable exports;
|
|
100
|
+
downstream applications should not import them from `mt5cli.utils` or
|
|
101
|
+
`mt5cli.history`.
|
|
102
|
+
|
|
96
103
|
### Trading and sizing primitives (generic)
|
|
97
104
|
|
|
98
105
|
These helpers implement broker-facing calculations only. They do not encode
|
|
@@ -108,7 +115,7 @@ strategy entries, exits, Kelly sizing, or signal logic.
|
|
|
108
115
|
| `CalibrationStatus`, `ClockStatus` | Literal status types for clock calibration and normalization |
|
|
109
116
|
| `detect_position_side` | Net long / short / flat from open positions |
|
|
110
117
|
| `calculate_spread_ratio` | Relative bid-ask spread |
|
|
111
|
-
| `calculate_margin_and_volume`, `calculate_volume_by_margin`, `
|
|
118
|
+
| `calculate_margin_and_volume`, `calculate_volume_by_margin`, `calculate_account_projected_margin_ratio` | Margin budget and volume sizing |
|
|
112
119
|
| `normalize_order_volume`, `estimate_order_margin`, `calculate_positions_margin` | Broker volume normalization and margin totals |
|
|
113
120
|
| `calculate_positions_margin_by_symbol` | Per-symbol margin map (resilient, first-seen order) |
|
|
114
121
|
| `calculate_positions_margin_safe` | Summed total margin across symbols (failed symbols skipped) |
|
|
@@ -230,8 +237,9 @@ workflows call the facade's canonical data methods (`account_info`, `positions`,
|
|
|
230
237
|
| `grafana_realized_pnl` | Cumulative realized PnL per symbol |
|
|
231
238
|
| `grafana_trade_stats` | Win/loss counts and profit per symbol |
|
|
232
239
|
|
|
233
|
-
|
|
234
|
-
|
|
240
|
+
`ensure_grafana_schema` is a stable package-root helper for downstream setup.
|
|
241
|
+
Other lower-level helpers (`create_grafana_views`, `create_grafana_indexes`,
|
|
242
|
+
`create_snapshot_tables`, `start_snapshot_run`,
|
|
235
243
|
`insert_account_snapshot`, `insert_position_snapshots`, `insert_order_snapshots`,
|
|
236
244
|
`insert_terminal_snapshot`, `record_snapshot_run`) are available directly from
|
|
237
245
|
`mt5cli.grafana` and are not part of the package-root stable surface.
|
|
@@ -367,7 +375,7 @@ their own adapter layer.
|
|
|
367
375
|
|
|
368
376
|
## Contract verification
|
|
369
377
|
|
|
370
|
-
`tests/test_contracts.py` asserts that every name in `STABLE_SDK_EXPORTS` is
|
|
378
|
+
`tests/unit/test_contracts.py` asserts that every name in `STABLE_SDK_EXPORTS` is
|
|
371
379
|
importable from `mt5cli`, that all package-root exports are covered by the
|
|
372
380
|
stable set, and documents key closed-bar, SQLite loading, account-resolution,
|
|
373
381
|
and trading-session behaviors.
|
|
@@ -264,7 +264,9 @@ A normalized snapshot keeps the raw/UTC distinction explicit:
|
|
|
264
264
|
|
|
265
265
|
`copy_ticks_range()` is not used for calibration: its documented UTC contract
|
|
266
266
|
is broker/terminal-dependent and not independently verified here (see
|
|
267
|
-
above), so
|
|
267
|
+
above), so neither the query timestamps nor the returned historical tick
|
|
268
|
+
timestamps can serve as an out-of-band UTC reference, and historical copied
|
|
269
|
+
rows are never used to infer the server offset. Instead, the only
|
|
268
270
|
independent reference available is **this process's own clock**: every
|
|
269
271
|
calibration poll
|
|
270
272
|
fetches one live `symbol_info_tick()` value and stamps it with
|
|
@@ -303,7 +305,11 @@ to a plausible bucket (`unstable_offset`), an unrealistic rounded delta
|
|
|
303
305
|
missing live data (`no_live_tick`) — snapshots fail closed: `clock_status` is
|
|
304
306
|
`"uncalibrated"`, `time_utc` and `server_clock_offset_seconds` are `None`,
|
|
305
307
|
and the raw fields remain available. A raw timestamp is never silently
|
|
306
|
-
treated as UTC.
|
|
308
|
+
treated as UTC. The same fail-closed rule applies after a successful
|
|
309
|
+
calibration: once periodic revalidation invalidates the cached offset (see
|
|
310
|
+
below), subsequent snapshots are normalized only by a successful full
|
|
311
|
+
recalibration — never by the refuted cache. `normalizer.calibration`
|
|
312
|
+
exposes the full
|
|
307
313
|
`TickClockCalibration` diagnostics: status, offset, distinct sample count,
|
|
308
314
|
evidence symbols, last calibration time, and (for troubleshooting a failed
|
|
309
315
|
attempt) the most recently considered sample's symbol, raw server-labeled
|
|
@@ -323,15 +329,55 @@ symbols and calls. A cached offset is recomputed in three cases:
|
|
|
323
329
|
future — evidence that the broker offset grew, e.g. a UTC+2 to UTC+3
|
|
324
330
|
transition.
|
|
325
331
|
- Periodically, at most once per `revalidation_interval_seconds` (default 5
|
|
326
|
-
minutes),
|
|
327
|
-
symbol
|
|
328
|
-
|
|
329
|
-
inconclusive symbols are skipped; a detected disagreement forces full
|
|
330
|
-
recalibration. This is what catches a broker offset _decrease_ (e.g. UTC+3
|
|
331
|
-
to UTC+2): the resulting normalized time looks stale rather than future,
|
|
332
|
+
minutes), through one full revalidation round over every configured
|
|
333
|
+
symbol. This is what catches a broker offset _decrease_ (e.g. UTC+3 to
|
|
334
|
+
UTC+2): the resulting normalized time looks stale rather than future,
|
|
332
335
|
which a future-skew check alone cannot distinguish from ordinary
|
|
333
336
|
quiet-market staleness.
|
|
334
337
|
|
|
338
|
+
In a revalidation round each configured symbol is polled once and compared
|
|
339
|
+
against that symbol's last known observation (from the initial calibration
|
|
340
|
+
or a prior revalidation), and the whole round is evaluated before any
|
|
341
|
+
decision is made — revalidation never stops at the first confirming symbol.
|
|
342
|
+
A symbol is inconclusive — skipped, neither confirming nor refuting the
|
|
343
|
+
cache — when its fetch is unusable, when it has no prior observation to
|
|
344
|
+
compare against, or when its tick epoch is unchanged since that observation
|
|
345
|
+
(ordinary tick age, e.g. a closed or illiquid symbol). Every other advancing
|
|
346
|
+
sample is evidence the cache must be able to explain:
|
|
347
|
+
|
|
348
|
+
- an accepted offset equal to the cached one confirms the cache, but never
|
|
349
|
+
cancels another symbol's refuting evidence from the same round;
|
|
350
|
+
- an accepted offset that differs from the cached one refutes the cache (a
|
|
351
|
+
clean broker offset change) — unless the cached offset, widened by the
|
|
352
|
+
elapsed time since that symbol's prior observation plus that prior
|
|
353
|
+
observation's own inferred delay under the cached offset, still explains
|
|
354
|
+
the disagreeing raw offset. That case is forgiven once per symbol and held as
|
|
355
|
+
a pending disagreement instead of refuting immediately, since it cannot
|
|
356
|
+
yet be told apart from a maximally delayed tick under the cached offset.
|
|
357
|
+
A pending disagreement escalates to a refutation only if that same
|
|
358
|
+
symbol's next conclusive round rounds to the identical bucket again; any
|
|
359
|
+
inconclusive, differently-bucketed, or unusable round in between clears
|
|
360
|
+
it instead of letting unrelated samples accumulate into false
|
|
361
|
+
corroboration;
|
|
362
|
+
- an advancing sample with no usable offset (`unstable_offset` or
|
|
363
|
+
`implausible_offset`) also refutes the cache, subject to the same
|
|
364
|
+
once-per-symbol forgiveness above — but an unusable sample can never
|
|
365
|
+
itself be held as, or match, a pending disagreement, since it carries no
|
|
366
|
+
specific offset to corroborate. A fresh market event whose raw offset is
|
|
367
|
+
neither reconciled with any plausible 30-minute bucket nor forgiven this
|
|
368
|
+
way is contradictory evidence the cached calibration cannot explain.
|
|
369
|
+
|
|
370
|
+
Any refuting evidence invalidates the cached calibration and forces full
|
|
371
|
+
recalibration; when several symbols refute the cache in one round, the
|
|
372
|
+
reported evidence is deterministic (`implausible_offset` first, then
|
|
373
|
+
`unstable_offset`, then a clean disagreement). The recalibration either
|
|
374
|
+
re-establishes a validated offset or fails closed — a raw tick is never
|
|
375
|
+
normalized with a cached offset that advancing evidence has just refuted. A
|
|
376
|
+
wholly inconclusive round keeps the cache, and a round in which no symbol
|
|
377
|
+
had a comparable baseline does not consume the interval, so the next call
|
|
378
|
+
retries immediately instead of waiting a full
|
|
379
|
+
`revalidation_interval_seconds`.
|
|
380
|
+
|
|
335
381
|
If recalibration still cannot validate the timestamp, the snapshot fails
|
|
336
382
|
closed. Failed calibrations are recorded for diagnostics but never reused;
|
|
337
383
|
retries are bounded to at most once per `failed_calibration_retry_seconds`
|
|
@@ -9,7 +9,7 @@ strategy responsibilities.
|
|
|
9
9
|
|
|
10
10
|
from importlib.metadata import version
|
|
11
11
|
|
|
12
|
-
from .client import MT5Client, build_config, mt5_session
|
|
12
|
+
from .client import MT5Client, build_config, mt5_session, substitute_mapping_values
|
|
13
13
|
from .contract import STABLE_SDK_EXPORTS
|
|
14
14
|
from .exceptions import (
|
|
15
15
|
Mt5CliError,
|
|
@@ -17,6 +17,7 @@ from .exceptions import (
|
|
|
17
17
|
Mt5OperationError,
|
|
18
18
|
Mt5SchemaError,
|
|
19
19
|
)
|
|
20
|
+
from .grafana import ensure_grafana_schema
|
|
20
21
|
from .history import (
|
|
21
22
|
RateTarget,
|
|
22
23
|
ThrottledHistoryUpdater,
|
|
@@ -26,6 +27,7 @@ from .history import (
|
|
|
26
27
|
load_rate_series_by_granularity,
|
|
27
28
|
load_rate_series_from_sqlite,
|
|
28
29
|
report_rate_gaps,
|
|
30
|
+
resolve_history_timeframes,
|
|
29
31
|
update_history,
|
|
30
32
|
update_history_with_config,
|
|
31
33
|
)
|
|
@@ -56,7 +58,6 @@ from .trading import (
|
|
|
56
58
|
TickClockNormalizer,
|
|
57
59
|
calculate_account_projected_margin_ratio,
|
|
58
60
|
calculate_margin_and_volume,
|
|
59
|
-
calculate_new_position_margin_ratio,
|
|
60
61
|
calculate_positions_margin,
|
|
61
62
|
calculate_positions_margin_by_symbol,
|
|
62
63
|
calculate_positions_margin_safe,
|
|
@@ -82,6 +83,7 @@ from .trading import (
|
|
|
82
83
|
update_sltp_for_open_positions,
|
|
83
84
|
update_trailing_stop_loss_for_open_positions,
|
|
84
85
|
)
|
|
86
|
+
from .utils import Dataset, parse_timeframe
|
|
85
87
|
|
|
86
88
|
__version__ = version(__package__) if __package__ else None
|
|
87
89
|
|
|
@@ -123,11 +123,11 @@ STABLE_SDK_EXPORTS: frozenset[str] = frozenset({
|
|
|
123
123
|
"TickClockNormalizer",
|
|
124
124
|
"RateTarget",
|
|
125
125
|
"ThrottledHistoryUpdater",
|
|
126
|
+
"Dataset",
|
|
126
127
|
"build_config",
|
|
127
128
|
"build_rate_targets",
|
|
128
129
|
"calculate_account_projected_margin_ratio",
|
|
129
130
|
"calculate_margin_and_volume",
|
|
130
|
-
"calculate_new_position_margin_ratio",
|
|
131
131
|
"calculate_projected_margin_ratio",
|
|
132
132
|
"calculate_positions_margin",
|
|
133
133
|
"calculate_positions_margin_by_symbol",
|
|
@@ -144,6 +144,7 @@ STABLE_SDK_EXPORTS: frozenset[str] = frozenset({
|
|
|
144
144
|
"detect_position_side",
|
|
145
145
|
"determine_order_limits",
|
|
146
146
|
"drop_forming_rate_bar",
|
|
147
|
+
"ensure_grafana_schema",
|
|
147
148
|
"ensure_symbol_selected",
|
|
148
149
|
"estimate_order_margin",
|
|
149
150
|
"extract_tick_price",
|
|
@@ -157,11 +158,14 @@ STABLE_SDK_EXPORTS: frozenset[str] = frozenset({
|
|
|
157
158
|
"load_rate_series_from_sqlite",
|
|
158
159
|
"mt5_session",
|
|
159
160
|
"normalize_order_volume",
|
|
161
|
+
"parse_timeframe",
|
|
160
162
|
"place_market_order",
|
|
161
163
|
"report_rate_gaps",
|
|
162
164
|
"resolve_broker_filling_mode",
|
|
163
165
|
"resolve_account_spec",
|
|
164
166
|
"resolve_account_specs",
|
|
167
|
+
"resolve_history_timeframes",
|
|
168
|
+
"substitute_mapping_values",
|
|
165
169
|
"update_history",
|
|
166
170
|
"update_history_with_config",
|
|
167
171
|
"update_observability",
|